170 lines
7.5 KiB
JavaScript
170 lines
7.5 KiB
JavaScript
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 }; |