ebike-ui/ebike-user/utils/deviceInfo.js
2025-08-01 09:00:22 +08:00

170 lines
7.5 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 };