diff --git a/src/App.vue b/src/App.vue
index 49782b9..f12cad8 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,6 +1,9 @@
diff --git a/src/components/home/construction.vue b/src/components/home/construction.vue
index 9730414..fae3975 100644
--- a/src/components/home/construction.vue
+++ b/src/components/home/construction.vue
@@ -3,7 +3,7 @@ import { useCurrentProject } from '@/composables/useCurrentProject'
import { useDeviceAuth } from '@/composables/useDeviceAuth'
const { hasCurrentProject, currentProject } = useCurrentProject()
-const { isActivated } = useDeviceAuth()
+const { needAuthPrompt } = useDeviceAuth()
@@ -14,7 +14,7 @@ const { isActivated } = useDeviceAuth()
-
+
diff --git a/src/components/home/progress.vue b/src/components/home/progress.vue
index b2f8af6..c324beb 100644
--- a/src/components/home/progress.vue
+++ b/src/components/home/progress.vue
@@ -7,7 +7,7 @@ import { useDbStore } from '@/store/modules/db'
const db = useDbStore()
const { hasCurrentProject, currentProjectId } = useCurrentProject()
-const { isActivated } = useDeviceAuth()
+const { isActivated, needAuthPrompt } = useDeviceAuth()
const currentProject = computed(() => {
if (!hasCurrentProject.value || currentProjectId.value == null)
@@ -99,7 +99,7 @@ onMounted(async () => {
-
+
diff --git a/src/components/home/projectList.vue b/src/components/home/projectList.vue
index a2863bf..1af7ae8 100644
--- a/src/components/home/projectList.vue
+++ b/src/components/home/projectList.vue
@@ -5,7 +5,7 @@ import { useDeviceAuth } from '@/composables/useDeviceAuth'
import { useDbStore } from '@/store/modules/db'
const db = useDbStore()
-const { isActivated } = useDeviceAuth()
+const { isActivated, needAuthPrompt } = useDeviceAuth()
/** 首页展示最近 2 条,按 id 倒序 */
const list = computed(() => (db.project || []).slice().sort((a, b) => (b.id ?? 0) - (a.id ?? 0)).slice(0, 2))
@@ -41,7 +41,7 @@ function goToDetail(item: Project) {
-
+
暂无项目
diff --git a/src/components/home/status.vue b/src/components/home/status.vue
index 98a90b8..514528a 100644
--- a/src/components/home/status.vue
+++ b/src/components/home/status.vue
@@ -4,7 +4,7 @@ import { useDeviceAuth } from '@/composables/useDeviceAuth'
import { useRtkLive } from '@/composables/useRtkLive'
const { hasCurrentProject } = useCurrentProject()
-const { isActivated } = useDeviceAuth()
+const { needAuthPrompt } = useDeviceAuth()
const { speedText, tempText } = useRtkLive()
function goToWork() {
@@ -22,7 +22,7 @@ function goToWork() {
-
+
--
diff --git a/src/components/home/warning.vue b/src/components/home/warning.vue
index 2bfb73a..721024a 100644
--- a/src/components/home/warning.vue
+++ b/src/components/home/warning.vue
@@ -7,7 +7,7 @@ import { useDbStore } from '@/store/modules/db'
const db = useDbStore()
const { hasCurrentProject, currentProjectId } = useCurrentProject()
-const { isActivated } = useDeviceAuth()
+const { isActivated, needAuthPrompt } = useDeviceAuth()
/** 首页只展示当前项目最近几条 */
const list = computed(() => {
@@ -50,7 +50,7 @@ function goToDetail(item: WarningRecord) {
-
+
--
diff --git a/src/composables/useDeviceAuth.ts b/src/composables/useDeviceAuth.ts
index cd909da..3e50a33 100644
--- a/src/composables/useDeviceAuth.ts
+++ b/src/composables/useDeviceAuth.ts
@@ -1,3 +1,4 @@
+import type { DeviceAuthStatus } from '@/api/device'
import type { AuthStatus } from '@/api/types'
import { onShow } from '@dcloudio/uni-app'
import { computed, ref } from 'vue'
@@ -5,63 +6,192 @@ import { activateDevice, getDeviceStatus } from '@/api/device'
import { getDeviceId } from '@/utils/device'
import storage from '@/utils/storage'
-const AUTH_STATUS_KEY = 'device_auth_status'
+const AUTH_INFO_KEY = 'device_auth_info'
+const ACTIVATION_CODE_KEY = 'device_activation_code'
-/** 模块级共享,保证各页面读到同一授权状态 */
-const authStatus = ref(storage.get(AUTH_STATUS_KEY, '0') ?? '0')
+function readCachedAuthInfo(): DeviceAuthStatus | null {
+ return storage.get(AUTH_INFO_KEY) ?? null
+}
+
+function toAuthStatus(value: AuthStatus | string | number | null | undefined): AuthStatus | null {
+ if (value == null || value === '')
+ return null
+ const status = String(value) as AuthStatus
+ if (status === '0' || status === '1' || status === '2' || status === '3')
+ return status
+ return null
+}
+
+/** 兼容 data 嵌套 / 扁平结构 / authStatus 为数字 */
+function pickAuthPayload(res: unknown): Partial | null {
+ if (!res || typeof res !== 'object')
+ return null
+ const raw = res as Record
+ const nested = raw.data
+ if (nested && typeof nested === 'object')
+ return nested as Partial
+ if ('authStatus' in raw)
+ return raw as Partial
+ return null
+}
+
+function normalizeAuthInfo(payload: Partial | null | undefined): DeviceAuthStatus | null {
+ if (!payload)
+ return null
+ const status = toAuthStatus(payload.authStatus as AuthStatus | string | number | undefined)
+ if (!status)
+ return null
+ return {
+ deviceId: payload.deviceId ?? getDeviceId(),
+ deviceName: payload.deviceName ?? '',
+ authStatus: status,
+ startDate: payload.startDate ?? '',
+ endDate: payload.endDate ?? '',
+ remainingDays: Number(payload.remainingDays ?? 0),
+ }
+}
+
+function createUnauthorizedInfo(): DeviceAuthStatus {
+ return {
+ deviceId: getDeviceId(),
+ deviceName: '',
+ authStatus: '0',
+ startDate: '',
+ endDate: '',
+ remainingDays: 0,
+ }
+}
+
+/** 模块级共享,保证各页面读到同一授权信息 */
+const authInfo = ref(readCachedAuthInfo())
+const activationCode = ref(storage.get(ACTIVATION_CODE_KEY, '') ?? '')
const loading = ref(false)
+const authReady = ref(false)
/**
- * 设备授权状态:仅 authStatus === '1' 视为已激活
- * 未激活时首页相关模块保留标题,内容展示未授权提示
- *
- * 注意:onShow 仅在页面级调用时注册,避免子组件重复监听
+ * 进入软件:先查本地激活码 → 无码再查授权状态并写入 storage
+ * 激活成功后再查一次状态写入 storage,形成闭环
+ * 仅未授权(0)或已过期(2)时展示 unauthorized / activateNotice
*/
export function useDeviceAuth(options?: { watchShow?: boolean }) {
+ const authStatus = computed(() => toAuthStatus(authInfo.value?.authStatus))
const isActivated = computed(() => authStatus.value === '1')
- const deviceId = computed(() => getDeviceId())
+ /** 是否需要展示未授权提示组件 */
+ const needAuthPrompt = computed(() => authStatus.value === '0' || authStatus.value === '2')
+ const hasActivationCode = computed(() => !!activationCode.value.trim())
+ const deviceId = computed(() => authInfo.value?.deviceId || getDeviceId())
- function setAuthStatus(status: AuthStatus) {
- authStatus.value = status
- storage.set(AUTH_STATUS_KEY, status)
+ /** 将完整授权信息写入内存 + storage */
+ function saveAuthInfo(info: DeviceAuthStatus) {
+ authInfo.value = info
+ storage.set(AUTH_INFO_KEY, info)
}
+ function setActivationCode(code: string) {
+ const trimmed = code.trim()
+ activationCode.value = trimmed
+ if (trimmed)
+ storage.set(ACTIVATION_CODE_KEY, trimmed)
+ else
+ storage.remove(ACTIVATION_CODE_KEY)
+ }
+
+ /** 查询设备授权状态,并完整落库;失败时返回 null */
async function fetchAuthStatus() {
loading.value = true
try {
- const res = await getDeviceStatus(deviceId.value)
- const status = res.data?.authStatus
- if (status != null && status !== '')
- setAuthStatus(String(status) as AuthStatus)
+ const res = await getDeviceStatus(getDeviceId())
+ const info = normalizeAuthInfo(pickAuthPayload(res))
+ if (info) {
+ saveAuthInfo(info)
+ return info
+ }
+ console.warn('[deviceAuth] 授权状态响应无法解析:', res)
+ return null
}
- catch {
- // 接口失败时沿用本地缓存状态
+ catch (err) {
+ console.warn('[deviceAuth] 查询授权状态失败:', err)
+ return null
}
finally {
loading.value = false
}
}
- /** 提交授权码激活设备,成功后更新本地状态 */
- async function activate(activationCode: string) {
- const code = activationCode.trim()
- if (!code)
+ /**
+ * 启动时初始化授权:
+ * 1. 本地已有激活码 → 读取 storage 中的授权信息
+ * 2. 无激活码 → 查询设备授权状态;查不到则按未授权处理并展示提示
+ */
+ async function initAuth() {
+ loading.value = true
+ try {
+ const localCode = storage.get(ACTIVATION_CODE_KEY, '') ?? ''
+ if (localCode.trim()) {
+ activationCode.value = localCode.trim()
+ const cached = readCachedAuthInfo()
+ if (cached) {
+ authInfo.value = cached
+ return authInfo.value
+ }
+ // 有激活码但无缓存信息,补拉一次状态
+ const info = await fetchAuthStatus()
+ if (info)
+ return info
+ saveAuthInfo({ ...createUnauthorizedInfo(), authStatus: '1' })
+ return authInfo.value
+ }
+
+ // 无激活码:以服务端为准;失败或无法解析 → 未授权,展示提示组件
+ const info = await fetchAuthStatus()
+ if (info)
+ return info
+
+ saveAuthInfo(createUnauthorizedInfo())
+ return authInfo.value
+ }
+ finally {
+ loading.value = false
+ authReady.value = true
+ }
+ }
+
+ /**
+ * 提交授权码激活 → 缓存激活码 → 再查授权状态落库(闭环)
+ */
+ async function activate(code: string) {
+ const trimmed = code.trim()
+ if (!trimmed)
throw new Error('请输入授权码')
const res = await activateDevice({
- activationCode: code,
- deviceId: deviceId.value,
+ activationCode: trimmed,
+ deviceId: getDeviceId(),
})
const ok = res.code === 200 || res.code === 0
if (!ok)
throw new Error(res.msg || '激活失败')
- setAuthStatus('1')
+ setActivationCode(trimmed)
+
+ const info = await fetchAuthStatus()
+ if (!info) {
+ saveAuthInfo({
+ deviceId: res.deviceId || getDeviceId(),
+ deviceName: authInfo.value?.deviceName ?? '',
+ authStatus: '1',
+ startDate: authInfo.value?.startDate ?? '',
+ endDate: res.endDate || authInfo.value?.endDate || '',
+ remainingDays: authInfo.value?.remainingDays ?? 0,
+ })
+ }
+
return res
}
function refreshAuthStatus() {
- authStatus.value = storage.get(AUTH_STATUS_KEY, '0') ?? '0'
+ authInfo.value = readCachedAuthInfo()
+ activationCode.value = storage.get(ACTIVATION_CODE_KEY, '') ?? ''
}
if (options?.watchShow) {
@@ -71,12 +201,19 @@ export function useDeviceAuth(options?: { watchShow?: boolean }) {
}
return {
+ authInfo,
authStatus,
+ activationCode,
isActivated,
+ needAuthPrompt,
+ hasActivationCode,
deviceId,
loading,
- setAuthStatus,
+ authReady,
+ saveAuthInfo,
+ setActivationCode,
fetchAuthStatus,
+ initAuth,
activate,
refreshAuthStatus,
}
diff --git a/src/pages/Ahome/index.vue b/src/pages/Ahome/index.vue
index 1863c2a..4455c7f 100644
--- a/src/pages/Ahome/index.vue
+++ b/src/pages/Ahome/index.vue
@@ -10,10 +10,10 @@ definePage({
},
})
-const { isActivated, fetchAuthStatus } = useDeviceAuth({ watchShow: true })
+const { needAuthPrompt, initAuth } = useDeviceAuth({ watchShow: true })
onMounted(() => {
- fetchAuthStatus()
+ initAuth()
})
@@ -30,7 +30,7 @@ onMounted(() => {
-
+