2025-11-08 11:30:06 +08:00
|
|
|
|
<template>
|
2026-04-28 13:55:17 +08:00
|
|
|
|
<view />
|
2025-11-08 11:30:06 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
|
|
|
import i18n from '@/common/i18n/index.js'
|
2026-04-28 13:55:17 +08:00
|
|
|
|
import { connectIfLoggedIn, disconnect, setAppActive } from '@/common/utils/appUserWs.js'
|
|
|
|
|
|
import { bindAppWsTopNoticeListenerOnce } from '@/common/utils/noticeTopShared.js'
|
|
|
|
|
|
import { syncGlobalVersionToLocalStorage } from '@/common/config/appVersionConfig.js'
|
2025-11-08 11:30:06 +08:00
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
|
onLaunch: function() {
|
|
|
|
|
|
console.log('App Launch')
|
2026-04-28 13:55:17 +08:00
|
|
|
|
bindAppWsTopNoticeListenerOnce()
|
|
|
|
|
|
this._syncUserWebSocket()
|
2025-11-08 11:30:06 +08:00
|
|
|
|
// 延迟检查安全验证,确保plus对象已初始化
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
this.checkAppSecurity()
|
|
|
|
|
|
}, 1000)
|
|
|
|
|
|
},
|
|
|
|
|
|
onShow: function() {
|
|
|
|
|
|
console.log('App Show')
|
2026-04-28 13:55:17 +08:00
|
|
|
|
// 每次进入前台:重新获取当前运行版本并写入本地(冷启动也会触发 onShow)
|
|
|
|
|
|
syncGlobalVersionToLocalStorage()
|
|
|
|
|
|
setAppActive(true)
|
|
|
|
|
|
this._syncUserWebSocket()
|
2025-11-08 11:30:06 +08:00
|
|
|
|
// 每次应用显示时检查安全验证,延迟执行确保plus对象可用
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
this.checkAppSecurity()
|
|
|
|
|
|
}, 500)
|
|
|
|
|
|
},
|
|
|
|
|
|
// 添加一个全局方法用于测试
|
|
|
|
|
|
onReady: function() {
|
|
|
|
|
|
// 将验证方法挂载到全局,方便测试
|
|
|
|
|
|
// #ifdef APP-PLUS
|
|
|
|
|
|
if (typeof plus !== 'undefined') {
|
|
|
|
|
|
plus.webview.currentWebview().evalJS(`
|
|
|
|
|
|
if (typeof window.testAuth === 'undefined') {
|
|
|
|
|
|
window.testAuth = function() {
|
|
|
|
|
|
console.log('手动触发验证测试');
|
|
|
|
|
|
// 这里可以手动触发验证逻辑
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
`)
|
|
|
|
|
|
}
|
|
|
|
|
|
// #endif
|
|
|
|
|
|
},
|
|
|
|
|
|
onHide: function() {
|
|
|
|
|
|
console.log('App Hide')
|
2026-04-28 13:55:17 +08:00
|
|
|
|
setAppActive(false)
|
2025-11-08 11:30:06 +08:00
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
2026-04-28 13:55:17 +08:00
|
|
|
|
/** 有 token 则建立用户长连接,否则断开 */
|
|
|
|
|
|
_syncUserWebSocket() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
if (uni.getStorageSync('token')) {
|
|
|
|
|
|
connectIfLoggedIn()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
disconnect()
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.warn('_syncUserWebSocket', e)
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2025-11-08 11:30:06 +08:00
|
|
|
|
// 检查应用安全设置
|
|
|
|
|
|
checkAppSecurity() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const securityEnabled = uni.getStorageSync('appSecurityEnabled')
|
|
|
|
|
|
console.log('安全设置状态:', securityEnabled)
|
|
|
|
|
|
if (securityEnabled) {
|
|
|
|
|
|
// 延迟一点时间确保页面完全加载
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
this.performBiometricAuth()
|
|
|
|
|
|
}, 100)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 添加调试信息到页面
|
|
|
|
|
|
this.showDebugInfo(securityEnabled)
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.error('检查安全设置失败:', e)
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 显示调试信息
|
|
|
|
|
|
showDebugInfo(securityEnabled) {
|
|
|
|
|
|
// console.log('当前时间:', new Date().toLocaleString())
|
|
|
|
|
|
// console.log('应用状态:', 'onShow')
|
|
|
|
|
|
console.log('========================')
|
|
|
|
|
|
|
|
|
|
|
|
if (securityEnabled) {
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
// uni.showToast({
|
|
|
|
|
|
// icon: 'none',
|
|
|
|
|
|
// duration: 2000
|
|
|
|
|
|
// })
|
|
|
|
|
|
}, 500)
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 执行生物识别验证
|
|
|
|
|
|
performBiometricAuth() {
|
|
|
|
|
|
console.log('开始执行生物识别验证')
|
|
|
|
|
|
|
|
|
|
|
|
// 先尝试使用原生生物识别,如果失败则使用模拟验证
|
|
|
|
|
|
let useNativeAuth = false
|
|
|
|
|
|
|
|
|
|
|
|
// #ifdef APP-PLUS
|
|
|
|
|
|
// 检查plus对象是否可用
|
|
|
|
|
|
if (typeof plus !== 'undefined' && plus.fingerprint) {
|
|
|
|
|
|
console.log('检测到plus.fingerprint,尝试使用原生验证')
|
|
|
|
|
|
useNativeAuth = true
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否支持生物识别
|
|
|
|
|
|
plus.fingerprint.isKeyguardSecure((secureResult) => {
|
|
|
|
|
|
console.log('生物识别支持检查结果:', secureResult)
|
|
|
|
|
|
if (secureResult) {
|
|
|
|
|
|
console.log('设备支持生物识别,开始原生验证')
|
|
|
|
|
|
// 执行生物识别验证
|
|
|
|
|
|
plus.fingerprint.authenticate((result) => {
|
|
|
|
|
|
console.log('生物识别验证成功:', result)
|
2026-01-17 17:34:06 +08:00
|
|
|
|
// 验证成功,继续正常流程
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: i18n.t('verifySuccess'),
|
|
|
|
|
|
icon: 'success'
|
|
|
|
|
|
})
|
2025-11-08 11:30:06 +08:00
|
|
|
|
}, (error) => {
|
|
|
|
|
|
console.log('生物识别验证失败:', error)
|
|
|
|
|
|
// 验证失败,显示错误信息
|
|
|
|
|
|
uni.showModal({
|
|
|
|
|
|
title: i18n.t('biometricAuthFailed'),
|
|
|
|
|
|
content: i18n.t('biometricAuthCancel'),
|
|
|
|
|
|
showCancel: false,
|
|
|
|
|
|
confirmText: i18n.t('modalConfirm'),
|
|
|
|
|
|
success: (res) => {
|
|
|
|
|
|
if (res.confirm) {
|
|
|
|
|
|
// 用户确认后关闭应用
|
|
|
|
|
|
// #ifdef APP-PLUS
|
|
|
|
|
|
plus.runtime.quit()
|
|
|
|
|
|
// #endif
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}, {
|
|
|
|
|
|
message: i18n.t('biometricPrompt')
|
|
|
|
|
|
})
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.log('设备不支持生物识别,使用模拟验证')
|
|
|
|
|
|
this.showMockAuth()
|
|
|
|
|
|
}
|
|
|
|
|
|
}, (error) => {
|
|
|
|
|
|
console.log('检查生物识别支持时出错:', error)
|
|
|
|
|
|
// 检查失败时也显示模拟验证
|
|
|
|
|
|
this.showMockAuth()
|
|
|
|
|
|
})
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.log('plus对象或plus.fingerprint不可用,使用模拟验证')
|
|
|
|
|
|
useNativeAuth = false
|
|
|
|
|
|
}
|
|
|
|
|
|
// #endif
|
|
|
|
|
|
|
|
|
|
|
|
// 如果不是APP环境或原生验证不可用,使用模拟验证
|
|
|
|
|
|
if (!useNativeAuth) {
|
|
|
|
|
|
console.log('使用模拟验证')
|
|
|
|
|
|
// 延迟一点时间确保界面完全加载
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
this.showMockAuth()
|
|
|
|
|
|
}, 200)
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 显示模拟验证(用于基座或不支持生物识别的情况)
|
|
|
|
|
|
showMockAuth() {
|
|
|
|
|
|
console.log('显示模拟验证界面')
|
|
|
|
|
|
uni.showModal({
|
2026-01-17 17:34:06 +08:00
|
|
|
|
title: i18n.t('identityVerification'),
|
|
|
|
|
|
content: i18n.t('pleaseEnterPasswordOrBiometric'),
|
2025-11-08 11:30:06 +08:00
|
|
|
|
editable: true,
|
2026-01-17 17:34:06 +08:00
|
|
|
|
placeholderText: i18n.t('pleaseEnterPassword'),
|
|
|
|
|
|
confirmText: i18n.t('verify'),
|
|
|
|
|
|
cancelText: i18n.t('cancel'),
|
2025-11-08 11:30:06 +08:00
|
|
|
|
success: (res) => {
|
|
|
|
|
|
if (res.confirm) {
|
|
|
|
|
|
if (res.content && res.content.length > 0) {
|
|
|
|
|
|
console.log('模拟验证成功')
|
|
|
|
|
|
uni.showToast({
|
2026-01-17 17:34:06 +08:00
|
|
|
|
title: i18n.t('verifySuccess'),
|
2025-11-08 11:30:06 +08:00
|
|
|
|
icon: 'success'
|
|
|
|
|
|
})
|
|
|
|
|
|
} else {
|
|
|
|
|
|
uni.showToast({
|
2026-01-17 17:34:06 +08:00
|
|
|
|
title: i18n.t('pleaseEnterPassword'),
|
2025-11-08 11:30:06 +08:00
|
|
|
|
icon: 'none'
|
|
|
|
|
|
})
|
|
|
|
|
|
// 重新显示验证
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
this.showMockAuth()
|
|
|
|
|
|
}, 1000)
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.log('用户取消验证')
|
|
|
|
|
|
// 用户取消验证,可以选择关闭应用或允许继续
|
|
|
|
|
|
uni.showModal({
|
2026-01-17 17:34:06 +08:00
|
|
|
|
title: i18n.t('authFailed'),
|
|
|
|
|
|
content: i18n.t('authFailedContent'),
|
2025-11-08 11:30:06 +08:00
|
|
|
|
showCancel: false,
|
2026-01-17 17:34:06 +08:00
|
|
|
|
confirmText: i18n.t('confirm'),
|
2025-11-08 11:30:06 +08:00
|
|
|
|
success: (result) => {
|
|
|
|
|
|
// #ifdef APP-PLUS
|
|
|
|
|
|
if (typeof plus !== 'undefined') {
|
|
|
|
|
|
plus.runtime.quit()
|
|
|
|
|
|
}
|
|
|
|
|
|
// #endif
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
|
/*每个页面公共css */
|
2026-04-28 13:55:17 +08:00
|
|
|
|
/* 全站统一缩小原生 <switch>(无 size 属性,用 transform;origin 靠右以适配常见列表行尾开关) */
|
|
|
|
|
|
switch {
|
|
|
|
|
|
transform: scale(0.78);
|
|
|
|
|
|
transform-origin: 100% 50%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 设备详情:数据字纯黑,与灰色标题区分(不加粗) */
|
|
|
|
|
|
.device-detail .info-card .info-content .info-status-row .info-status:not(.red):not(.green) {
|
|
|
|
|
|
color: #000000 !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
.device-detail .info-card .info-content .info-status-row .info-status.red {
|
|
|
|
|
|
color: #e74c3c !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
.device-detail .info-card .info-content .info-status-row .info-status.green {
|
|
|
|
|
|
color: #27ae60 !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
.device-detail .onebox .devicename .cu,
|
|
|
|
|
|
.device-detail .moshi .lt .cu {
|
|
|
|
|
|
color: #000000;
|
|
|
|
|
|
}
|
|
|
|
|
|
.device-detail .onebox .room .lt > view:not(.xi) {
|
|
|
|
|
|
color: #000000;
|
|
|
|
|
|
}
|
|
|
|
|
|
.device-detail .dlyanchi .lt > view + view,
|
|
|
|
|
|
.device-detail .yanchi .bot .lt > view + view {
|
|
|
|
|
|
color: #000000 !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
.device-detail .accel-indicator {
|
|
|
|
|
|
color: #000000 !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
.device-detail .device-info .value {
|
|
|
|
|
|
color: #000000 !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 设备详情:说明/标题/行标签 #787878,与纯黑数据区分 */
|
|
|
|
|
|
.device-detail .info-card .info-content .info-title,
|
|
|
|
|
|
.device-detail .onebox .xi,
|
|
|
|
|
|
.device-detail .moshi .lt .xi,
|
|
|
|
|
|
.device-detail .onebox .devicename .xi,
|
|
|
|
|
|
.device-detail .onebox .room .lt .xi,
|
|
|
|
|
|
.device-detail .mcname,
|
|
|
|
|
|
.device-detail .mckaiguan .lt,
|
|
|
|
|
|
.device-detail .tongzhi .lt,
|
|
|
|
|
|
.device-detail .yanchi .top .lt,
|
|
|
|
|
|
.device-detail .dlyanchi .lt > view:first-child,
|
|
|
|
|
|
.device-detail .yanchi .bot .lt > view:first-child,
|
|
|
|
|
|
.device-detail .accel-title,
|
|
|
|
|
|
.device-detail .accel-desc {
|
|
|
|
|
|
color: #787878 !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 全站底部弹层自下滑入(子组件中写:animation: appSheetSlideUp 0.42s cubic-bezier(0.25, 0.8, 0.25, 1);) */
|
|
|
|
|
|
@keyframes appSheetSlideUp {
|
|
|
|
|
|
from {
|
|
|
|
|
|
transform: translate3d(0, 100%, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
to {
|
|
|
|
|
|
transform: translate3d(0, 0, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-08 11:30:06 +08:00
|
|
|
|
</style>
|