let deviceInfoCache = null; // 缓存设备信息,避免重复调用原生 API /** * 获取设备信息的主函数 * @returns {Promise} 包含各种设备信息的 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 };