ebike-ui/ebike-user/utils/deviceInfo.js

170 lines
7.5 KiB
JavaScript
Raw Normal View History

2025-08-01 09:00:22 +08:00
let deviceInfoCache = null; // 缓存设备信息,避免重复调用原生 API
/**
* 获取设备信息的主函数
* @returns {Promise<Object>} 包含各种设备信息的 Promise
*/
async function getDeviceInfo() {
// 如果缓存存在,直接返回缓存数据
if (deviceInfoCache) {
return Promise.resolve(deviceInfoCache);
}
try {
// 使用 UniApp 的 uni.getSystemInfo 或 uni.getSystemInfoSync 获取系统信息
const info = await new Promise((resolve, reject) => {
uni.getSystemInfo({
success: resolve,
fail: reject
});
});
// --- 基础信息 ---
const baseInfo = {
// 设备品牌
brand: info.brand,
// 设备型号
model: info.model,
// 设备像素比
pixelRatio: info.pixelRatio,
// 屏幕宽度 (px)
screenWidth: info.screenWidth,
// 屏幕高度 (px)
screenHeight: info.screenHeight,
// 状态栏高度 (px) - 重要!
statusBarHeight: info.statusBarHeight || 0, // 大部分平台都提供
// 窗口宽度 (px)
windowWidth: info.windowWidth,
// 窗口高度 (px)
windowHeight: info.windowHeight,
// 语言
language: info.language,
// 系统版本
system: info.system,
// 平台 (ios/android/devtools)
platform: info.platform,
// App 版本号
appVersion: info.appVersion,
// SDK 版本
SDKVersion: info.SDKVersion,
// 微信小程序环境下的基础库版本 (如果适用)
version: info.version,
};
// --- 安全区域信息 (Safe Area) ---
// 注意uni.getSystemInfo 返回的 safeArea 对象是可选的,取决于平台和版本
let safeAreaInfo = {
safeAreaTop: 0,
safeAreaBottom: 0,
safeAreaLeft: 0,
safeAreaRight: 0,
safeAreaWidth: info.screenWidth, // 默认为全屏
safeAreaHeight: info.screenHeight,
};
if (info.safeArea) {
// 如果原生 API 提供了 safeArea 对象,则使用它
safeAreaInfo = {
safeAreaTop: info.safeArea.top,
safeAreaBottom: info.safeArea.bottom,
safeAreaLeft: info.safeArea.left,
safeAreaRight: info.safeArea.right,
safeAreaWidth: info.safeArea.width,
safeAreaHeight: info.safeArea.height,
};
} else {
// 如果没有 safeArea 对象,尝试通过其他方式估算 (例如,仅处理 iOS 底部)
// 这种方法不精确,仅作降级处理
// iOS 通常有底部安全区域Android 大部分情况为 0
if (info.platform.toLowerCase() === 'ios') {
// 这是一个非常粗略的估计,实际值应通过 safeArea 或 CSS 环境变量获取
// 这里仅为演示,**实际项目中应避免这种硬编码估算**
// 更好的做法是依赖 CSS env() 或在特定页面通过 DOM 计算
safeAreaInfo.safeAreaBottom = 34; // 典型的 iPhone X+ 底部指示条高度
}
// 计算安全区域的宽高 (基于状态栏和估算的底部)
safeAreaInfo.safeAreaHeight = info.screenHeight - baseInfo.statusBarHeight - safeAreaInfo.safeAreaBottom;
safeAreaInfo.safeAreaWidth = info.screenWidth; // 通常左右没有 insets
}
// --- 顶部导航栏相关信息 ---
// 注意uni-app 的 navigationBar 高度需要计算或通过配置获取,原生 API 不直接提供
// 这里提供一个计算逻辑,但实际值可能因配置而异
let navigationBarHeight = 0;
// 通常,导航栏高度 = 状态栏高度 + 标题栏高度 (44px 是常见标题栏高度)
// **注意:这只是一个常见假设,实际高度取决于您的页面配置!**
// 更准确的方式是在页面 onLoad 时通过 this.$u?.getMenuButtonBoundingClientRect() (如果使用 uView) 或 uni.createSelectorQuery 获取 .uni-page-head 的高度
// 这里我们只提供一个估算值作为参考
const typicalTitleBarHeight = 44; // 常见标题栏高度 (px)
navigationBarHeight = baseInfo.statusBarHeight + typicalTitleBarHeight;
// --- CSS 环境变量对应的值 (用于指导 CSS 设计) ---
// 这些值通常在 CSS 中用 env(safe-area-inset-*) 使用JS 中获取它们比较困难且不标准
// 我们可以根据 safeArea 信息推断出 *可能* 的 CSS insets
// 注意JS 获取的 safeArea 值和 CSS env() 的值在某些平台/情况下可能不完全一致
const cssEnvValues = {
// CSS 中 env(safe-area-inset-top) 的值,通常包含状态栏和可能的刘海
cssSafeAreaInsetTop: safeAreaInfo.safeAreaTop > 0 ? safeAreaInfo.safeAreaTop : baseInfo.statusBarHeight,
// CSS 中 env(safe-area-inset-bottom) 的值
cssSafeAreaInsetBottom: info.screenHeight - safeAreaInfo.safeAreaBottom <= 0 ? 34 : info.screenHeight - safeAreaInfo.safeAreaBottom,
// CSS 中 env(safe-area-inset-left) 和 env(safe-area-inset-right) 的值
cssSafeAreaInsetLeft: safeAreaInfo.safeAreaLeft,
cssSafeAreaInsetRight: safeAreaInfo.safeAreaRight,
};
// --- 合并所有信息 ---
const fullDeviceInfo = {
...baseInfo,
...safeAreaInfo,
// 导航栏高度 (估算)
navigationBarHeight: navigationBarHeight,
// CSS 环境变量值 (估算/推断)
...cssEnvValues,
// 常用的便捷计算值
// 可用内容区域高度 (屏幕高 - 状态栏 - 导航栏 - 底部安全区)
usableContentHeight: info.screenHeight - baseInfo.statusBarHeight - navigationBarHeight - safeAreaInfo.safeAreaBottom,
};
// 缓存结果
deviceInfoCache = fullDeviceInfo;
return fullDeviceInfo;
} catch (error) {
console.error('获取设备信息失败:', error);
// 返回一个包含部分已知信息或默认值的对象
return {
...deviceInfoCache, // 如果之前缓存过部分信息
error: error.message || 'Unknown error'
};
}
}
/**
* 同步获取设备信息 (注意仅在首次调用后有缓存否则可能不准确)
* @returns {Object|null} 设备信息对象或 null
*/
function getDeviceInfoSync() {
if (deviceInfoCache) {
return deviceInfoCache;
}
// 如果没有缓存,尝试同步获取 (uni.getSystemInfoSync 可能抛出异常)
try {
const info = uni.getSystemInfoSync();
// 这里可以复制上面的部分逻辑进行同步处理,但为了简洁和避免重复,通常建议使用异步版本
// 或者,首次调用异步后,后续可用同步获取缓存
console.warn('getDeviceInfoSync 调用时缓存为空,建议先调用异步版本');
return null;
} catch (error) {
console.error('同步获取设备信息失败:', error);
return null;
}
}
/**
* 清除设备信息缓存 (例如当需要强制刷新时)
*/
function clearDeviceInfoCache() {
deviceInfoCache = null;
}
export { getDeviceInfo, getDeviceInfoSync, clearDeviceInfoCache };