feat 授权激活
This commit is contained in:
2
src/components.d.ts
vendored
2
src/components.d.ts
vendored
@@ -16,6 +16,7 @@ declare module 'vue' {
|
|||||||
HeatmapLayerPanel: typeof import('./components/heatmap/layerPanel.vue')['default']
|
HeatmapLayerPanel: typeof import('./components/heatmap/layerPanel.vue')['default']
|
||||||
HeatmapLegend: typeof import('./components/heatmap/legend.vue')['default']
|
HeatmapLegend: typeof import('./components/heatmap/legend.vue')['default']
|
||||||
HeatmapPointDetail: typeof import('./components/heatmap/pointDetail.vue')['default']
|
HeatmapPointDetail: typeof import('./components/heatmap/pointDetail.vue')['default']
|
||||||
|
HomeActivateNotice: typeof import('./components/home/activateNotice.vue')['default']
|
||||||
HomeConstruction: typeof import('./components/home/construction.vue')['default']
|
HomeConstruction: typeof import('./components/home/construction.vue')['default']
|
||||||
HomeHomeNav: typeof import('./components/home/homeNav.vue')['default']
|
HomeHomeNav: typeof import('./components/home/homeNav.vue')['default']
|
||||||
HomeList: typeof import('./components/home/list.vue')['default']
|
HomeList: typeof import('./components/home/list.vue')['default']
|
||||||
@@ -39,6 +40,7 @@ declare module 'vue' {
|
|||||||
SettingsSensorConfig: typeof import('./components/settings/sensorConfig.vue')['default']
|
SettingsSensorConfig: typeof import('./components/settings/sensorConfig.vue')['default']
|
||||||
SettingsWifiNetwork: typeof import('./components/settings/wifiNetwork.vue')['default']
|
SettingsWifiNetwork: typeof import('./components/settings/wifiNetwork.vue')['default']
|
||||||
SettingsWifiSettings: typeof import('./components/settings/wifiSettings.vue')['default']
|
SettingsWifiSettings: typeof import('./components/settings/wifiSettings.vue')['default']
|
||||||
|
Unauthorized: typeof import('./components/unauthorized.vue')['default']
|
||||||
WarningFilter: typeof import('./components/warning/filter.vue')['default']
|
WarningFilter: typeof import('./components/warning/filter.vue')['default']
|
||||||
WarningItem: typeof import('./components/warning/item.vue')['default']
|
WarningItem: typeof import('./components/warning/item.vue')['default']
|
||||||
WorkDashboard: typeof import('./components/work/dashboard.vue')['default']
|
WorkDashboard: typeof import('./components/work/dashboard.vue')['default']
|
||||||
|
|||||||
155
src/components/home/activateNotice.vue
Normal file
155
src/components/home/activateNotice.vue
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useDialog, useToast } from '@wot-ui/ui'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { useDeviceAuth } from '@/composables/useDeviceAuth'
|
||||||
|
|
||||||
|
const dialog = useDialog('activate-dialog')
|
||||||
|
const toast = useToast('activate-toast')
|
||||||
|
const { deviceId, activate } = useDeviceAuth()
|
||||||
|
|
||||||
|
const activationCode = ref('')
|
||||||
|
|
||||||
|
function goActivate() {
|
||||||
|
activationCode.value = ''
|
||||||
|
dialog
|
||||||
|
.show({
|
||||||
|
title: '设备授权激活',
|
||||||
|
showClose: true,
|
||||||
|
showCancelButton: false,
|
||||||
|
confirmButtonText: '激活',
|
||||||
|
closeOnClickModal: true,
|
||||||
|
beforeConfirm: async () => {
|
||||||
|
const code = activationCode.value.trim()
|
||||||
|
if (!code) {
|
||||||
|
toast.show('请输入授权码')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await activate(code)
|
||||||
|
toast.success('激活成功')
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
catch (error: unknown) {
|
||||||
|
// Axios 错误已由 request 拦截器提示,此处只提示业务/校验错误
|
||||||
|
const isAxios = typeof error === 'object' && error !== null && 'config' in error
|
||||||
|
if (!isAxios && error instanceof Error)
|
||||||
|
toast.show(error.message)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="card">
|
||||||
|
<text class="card-title">
|
||||||
|
设备未授权激活
|
||||||
|
</text>
|
||||||
|
<view class="notice-box">
|
||||||
|
<text class="notice-title">
|
||||||
|
检测到 1 台新设备待激活
|
||||||
|
</text>
|
||||||
|
<text class="notice-desc">
|
||||||
|
请输入设备ID完成授权激活后,方可获取该设备ID获取压实数据及位置信息
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
<view class="card-footer">
|
||||||
|
<wd-button type="primary" size="large" round @tap="goActivate">
|
||||||
|
立即激活
|
||||||
|
</wd-button>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<wd-dialog selector="activate-dialog">
|
||||||
|
<view class="activate-body">
|
||||||
|
<text class="device-label">
|
||||||
|
设备ID
|
||||||
|
</text>
|
||||||
|
<view class="device-id-box">
|
||||||
|
<text class="device-id-text">
|
||||||
|
{{ deviceId }}
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
<wd-input
|
||||||
|
v-model="activationCode"
|
||||||
|
prefix-icon="lock"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</wd-dialog>
|
||||||
|
<wd-toast selector="activate-toast" />
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.card {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
display: block;
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333333;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-box {
|
||||||
|
background-color: #E8F3FF;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-title {
|
||||||
|
display: block;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2B6DE5;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-desc {
|
||||||
|
display: block;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #5B8DEF;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activate-body {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333333;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-id-box {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-id-text {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333333;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useCurrentProject } from '@/composables/useCurrentProject'
|
import { useCurrentProject } from '@/composables/useCurrentProject'
|
||||||
|
import { useDeviceAuth } from '@/composables/useDeviceAuth'
|
||||||
|
|
||||||
const { hasCurrentProject, currentProject } = useCurrentProject()
|
const { hasCurrentProject, currentProject } = useCurrentProject()
|
||||||
|
const { isActivated } = useDeviceAuth()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -12,7 +14,8 @@ const { hasCurrentProject, currentProject } = useCurrentProject()
|
|||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="card-body">
|
<view class="card-body">
|
||||||
<view class="construction-info">
|
<unauthorized v-if="!isActivated" />
|
||||||
|
<view v-else class="construction-info">
|
||||||
<view class="icon-placeholder" />
|
<view class="icon-placeholder" />
|
||||||
<view class="info">
|
<view class="info">
|
||||||
<text class="segment">
|
<text class="segment">
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted } from 'vue'
|
import { computed, onMounted } from 'vue'
|
||||||
import { useCurrentProject } from '@/composables/useCurrentProject'
|
import { useCurrentProject } from '@/composables/useCurrentProject'
|
||||||
|
import { useDeviceAuth } from '@/composables/useDeviceAuth'
|
||||||
import { calcUncompactedArea } from '@/service/project'
|
import { calcUncompactedArea } from '@/service/project'
|
||||||
import { useDbStore } from '@/store/modules/db'
|
import { useDbStore } from '@/store/modules/db'
|
||||||
|
|
||||||
const db = useDbStore()
|
const db = useDbStore()
|
||||||
const { hasCurrentProject, currentProjectId } = useCurrentProject()
|
const { hasCurrentProject, currentProjectId } = useCurrentProject()
|
||||||
|
const { isActivated } = useDeviceAuth()
|
||||||
|
|
||||||
const currentProject = computed(() => {
|
const currentProject = computed(() => {
|
||||||
if (!hasCurrentProject.value || currentProjectId.value == null)
|
if (!hasCurrentProject.value || currentProjectId.value == null)
|
||||||
@@ -92,12 +94,13 @@ onMounted(async () => {
|
|||||||
<text class="card-title">
|
<text class="card-title">
|
||||||
项目作业进度
|
项目作业进度
|
||||||
</text>
|
</text>
|
||||||
<text class="card-sub">
|
<text v-if="isActivated" class="card-sub">
|
||||||
{{ currentProject?.stake_range || currentProject?.project_name || '暂无施工段' }}
|
{{ currentProject?.stake_range || currentProject?.project_name || '暂无施工段' }}
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="card-body">
|
<view class="card-body">
|
||||||
<view class="progress-content">
|
<unauthorized v-if="!isActivated" />
|
||||||
|
<view v-else class="progress-content">
|
||||||
<view class="progress-circle">
|
<view class="progress-circle">
|
||||||
<wd-circle :model-value="progressPercent ?? 0" :size="120">
|
<wd-circle :model-value="progressPercent ?? 0" :size="120">
|
||||||
<view class="progress-circle-text">
|
<view class="progress-circle-text">
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Project } from '@/service/project'
|
import type { Project } from '@/service/project'
|
||||||
import { computed, onMounted } from 'vue'
|
import { computed, onMounted } from 'vue'
|
||||||
|
import { useDeviceAuth } from '@/composables/useDeviceAuth'
|
||||||
import { useDbStore } from '@/store/modules/db'
|
import { useDbStore } from '@/store/modules/db'
|
||||||
|
|
||||||
const db = useDbStore()
|
const db = useDbStore()
|
||||||
|
const { isActivated } = useDeviceAuth()
|
||||||
|
|
||||||
/** 首页展示最近 2 条,按 id 倒序 */
|
/** 首页展示最近 2 条,按 id 倒序 */
|
||||||
const list = computed(() => (db.project || []).slice().sort((a, b) => (b.id ?? 0) - (a.id ?? 0)).slice(0, 2))
|
const list = computed(() => (db.project || []).slice().sort((a, b) => (b.id ?? 0) - (a.id ?? 0)).slice(0, 2))
|
||||||
@@ -34,20 +36,23 @@ function goToDetail(item: Project) {
|
|||||||
<text class="card-title">
|
<text class="card-title">
|
||||||
项目列表
|
项目列表
|
||||||
</text>
|
</text>
|
||||||
<wd-button variant="text" @tap="goToProjectList">
|
<wd-button v-if="isActivated" variant="text" @tap="goToProjectList">
|
||||||
查看全部
|
查看全部
|
||||||
</wd-button>
|
</wd-button>
|
||||||
</view>
|
</view>
|
||||||
<view class="card-body">
|
<view class="card-body">
|
||||||
<view v-if="!list.length" class="empty-tip">
|
<unauthorized v-if="!isActivated" />
|
||||||
|
<view v-else-if="!list.length" class="empty-tip">
|
||||||
暂无项目
|
暂无项目
|
||||||
</view>
|
</view>
|
||||||
<home-list
|
<template v-else>
|
||||||
v-for="item in list"
|
<home-list
|
||||||
:key="item.id"
|
v-for="item in list"
|
||||||
:item="item"
|
:key="item.id"
|
||||||
@tap="goToDetail(item)"
|
:item="item"
|
||||||
/>
|
@tap="goToDetail(item)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useCurrentProject } from '@/composables/useCurrentProject'
|
import { useCurrentProject } from '@/composables/useCurrentProject'
|
||||||
|
import { useDeviceAuth } from '@/composables/useDeviceAuth'
|
||||||
import { useRtkLive } from '@/composables/useRtkLive'
|
import { useRtkLive } from '@/composables/useRtkLive'
|
||||||
|
|
||||||
const { hasCurrentProject } = useCurrentProject()
|
const { hasCurrentProject } = useCurrentProject()
|
||||||
|
const { isActivated } = useDeviceAuth()
|
||||||
const { speedText, tempText } = useRtkLive()
|
const { speedText, tempText } = useRtkLive()
|
||||||
|
|
||||||
function goToWork() {
|
function goToWork() {
|
||||||
@@ -20,7 +22,8 @@ function goToWork() {
|
|||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="card-body">
|
<view class="card-body">
|
||||||
<view v-if="!hasCurrentProject" class="empty-tip">
|
<unauthorized v-if="!isActivated" />
|
||||||
|
<view v-else-if="!hasCurrentProject" class="empty-tip">
|
||||||
--
|
--
|
||||||
</view>
|
</view>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
|||||||
@@ -2,10 +2,12 @@
|
|||||||
import type { WarningRecord } from '@/service/warningRecord'
|
import type { WarningRecord } from '@/service/warningRecord'
|
||||||
import { computed, onMounted } from 'vue'
|
import { computed, onMounted } from 'vue'
|
||||||
import { useCurrentProject } from '@/composables/useCurrentProject'
|
import { useCurrentProject } from '@/composables/useCurrentProject'
|
||||||
|
import { useDeviceAuth } from '@/composables/useDeviceAuth'
|
||||||
import { useDbStore } from '@/store/modules/db'
|
import { useDbStore } from '@/store/modules/db'
|
||||||
|
|
||||||
const db = useDbStore()
|
const db = useDbStore()
|
||||||
const { hasCurrentProject, currentProjectId } = useCurrentProject()
|
const { hasCurrentProject, currentProjectId } = useCurrentProject()
|
||||||
|
const { isActivated } = useDeviceAuth()
|
||||||
|
|
||||||
/** 首页只展示当前项目最近几条 */
|
/** 首页只展示当前项目最近几条 */
|
||||||
const list = computed(() => {
|
const list = computed(() => {
|
||||||
@@ -43,23 +45,26 @@ function goToDetail(item: WarningRecord) {
|
|||||||
<text class="card-title">
|
<text class="card-title">
|
||||||
异常警告
|
异常警告
|
||||||
</text>
|
</text>
|
||||||
<wd-button variant="text" @tap="goToWarningList">
|
<wd-button v-if="isActivated" variant="text" @tap="goToWarningList">
|
||||||
查看全部
|
查看全部
|
||||||
</wd-button>
|
</wd-button>
|
||||||
</view>
|
</view>
|
||||||
<view class="card-body">
|
<view class="card-body">
|
||||||
<view v-if="!hasCurrentProject" class="empty-tip">
|
<unauthorized v-if="!isActivated" />
|
||||||
|
<view v-else-if="!hasCurrentProject" class="empty-tip">
|
||||||
--
|
--
|
||||||
</view>
|
</view>
|
||||||
<view v-else-if="!list.length" class="empty-tip">
|
<view v-else-if="!list.length" class="empty-tip">
|
||||||
暂无异常警告
|
暂无异常警告
|
||||||
</view>
|
</view>
|
||||||
<home-warning_list
|
<template v-else>
|
||||||
v-for="item in list"
|
<home-warning_list
|
||||||
:key="item.id"
|
v-for="item in list"
|
||||||
:item="item"
|
:key="item.id"
|
||||||
@tap="goToDetail(item)"
|
:item="item"
|
||||||
/>
|
@tap="goToDetail(item)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
113
src/components/unauthorized.vue
Normal file
113
src/components/unauthorized.vue
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
withDefaults(defineProps<{
|
||||||
|
title?: string
|
||||||
|
tipLine1?: string
|
||||||
|
tipLine2?: string
|
||||||
|
}>(), {
|
||||||
|
title: '设备未授权激活',
|
||||||
|
tipLine1: '请点击右上角设置图标',
|
||||||
|
tipLine2: '复制设备 ID 获取授权码完成激活',
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="unauthorized">
|
||||||
|
<view class="icon-wrap">
|
||||||
|
<view class="lock">
|
||||||
|
<view class="lock-shackle" />
|
||||||
|
<view class="lock-body">
|
||||||
|
<view class="lock-dot" />
|
||||||
|
<view class="lock-slot" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<text class="title">
|
||||||
|
{{ title }}
|
||||||
|
</text>
|
||||||
|
<text class="tip">
|
||||||
|
{{ tipLine1 }}
|
||||||
|
</text>
|
||||||
|
<text class="tip">
|
||||||
|
{{ tipLine2 }}
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.unauthorized {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 32px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-wrap {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #FFECEC;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lock {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lock-shackle {
|
||||||
|
width: 22px;
|
||||||
|
height: 16px;
|
||||||
|
border: 3px solid #F04848;
|
||||||
|
border-bottom: none;
|
||||||
|
border-radius: 11px 11px 0 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lock-body {
|
||||||
|
width: 32px;
|
||||||
|
height: 24px;
|
||||||
|
border: 3px solid #F04848;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lock-dot {
|
||||||
|
width: 5px;
|
||||||
|
height: 5px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #F04848;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lock-slot {
|
||||||
|
width: 2px;
|
||||||
|
height: 5px;
|
||||||
|
background-color: #F04848;
|
||||||
|
border-radius: 1px;
|
||||||
|
margin-top: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333333;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999999;
|
||||||
|
line-height: 22px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
83
src/composables/useDeviceAuth.ts
Normal file
83
src/composables/useDeviceAuth.ts
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import type { AuthStatus } from '@/api/types'
|
||||||
|
import { onShow } from '@dcloudio/uni-app'
|
||||||
|
import { computed, ref } from 'vue'
|
||||||
|
import { activateDevice, getDeviceStatus } from '@/api/device'
|
||||||
|
import { getDeviceId } from '@/utils/device'
|
||||||
|
import storage from '@/utils/storage'
|
||||||
|
|
||||||
|
const AUTH_STATUS_KEY = 'device_auth_status'
|
||||||
|
|
||||||
|
/** 模块级共享,保证各页面读到同一授权状态 */
|
||||||
|
const authStatus = ref<AuthStatus>(storage.get<AuthStatus>(AUTH_STATUS_KEY, '0') ?? '0')
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备授权状态:仅 authStatus === '1' 视为已激活
|
||||||
|
* 未激活时首页相关模块保留标题,内容展示未授权提示
|
||||||
|
*
|
||||||
|
* 注意:onShow 仅在页面级调用时注册,避免子组件重复监听
|
||||||
|
*/
|
||||||
|
export function useDeviceAuth(options?: { watchShow?: boolean }) {
|
||||||
|
const isActivated = computed(() => authStatus.value === '1')
|
||||||
|
const deviceId = computed(() => getDeviceId())
|
||||||
|
|
||||||
|
function setAuthStatus(status: AuthStatus) {
|
||||||
|
authStatus.value = status
|
||||||
|
storage.set(AUTH_STATUS_KEY, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
// 接口失败时沿用本地缓存状态
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 提交授权码激活设备,成功后更新本地状态 */
|
||||||
|
async function activate(activationCode: string) {
|
||||||
|
const code = activationCode.trim()
|
||||||
|
if (!code)
|
||||||
|
throw new Error('请输入授权码')
|
||||||
|
|
||||||
|
const res = await activateDevice({
|
||||||
|
activationCode: code,
|
||||||
|
deviceId: deviceId.value,
|
||||||
|
})
|
||||||
|
const ok = res.code === 200 || res.code === 0
|
||||||
|
if (!ok)
|
||||||
|
throw new Error(res.msg || '激活失败')
|
||||||
|
|
||||||
|
setAuthStatus('1')
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshAuthStatus() {
|
||||||
|
authStatus.value = storage.get<AuthStatus>(AUTH_STATUS_KEY, '0') ?? '0'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options?.watchShow) {
|
||||||
|
onShow(() => {
|
||||||
|
refreshAuthStatus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
authStatus,
|
||||||
|
isActivated,
|
||||||
|
deviceId,
|
||||||
|
loading,
|
||||||
|
setAuthStatus,
|
||||||
|
fetchAuthStatus,
|
||||||
|
activate,
|
||||||
|
refreshAuthStatus,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
"navigationBarTitleText": "Road-Roller"
|
"navigationBarTitleText": "Road-Roller"
|
||||||
},
|
},
|
||||||
"pages": [
|
"pages": [
|
||||||
// GENERATED BY UNI-PAGES, PLATFORM: H5 || APP
|
// GENERATED BY UNI-PAGES, PLATFORM: APP || H5
|
||||||
{
|
{
|
||||||
"path": "pages/Ahome/index",
|
"path": "pages/Ahome/index",
|
||||||
"type": "home",
|
"type": "home",
|
||||||
@@ -121,7 +121,7 @@
|
|||||||
"color": "@tabFontColor",
|
"color": "@tabFontColor",
|
||||||
"selectedColor": "@tabSelectedColor",
|
"selectedColor": "@tabSelectedColor",
|
||||||
"list": [
|
"list": [
|
||||||
// GENERATED BY UNI-PAGES, PLATFORM: H5 || APP
|
// GENERATED BY UNI-PAGES, PLATFORM: APP || H5
|
||||||
{
|
{
|
||||||
"pagePath": "pages/Ahome/index",
|
"pagePath": "pages/Ahome/index",
|
||||||
"text": "首页",
|
"text": "首页",
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { onMounted } from 'vue'
|
||||||
|
import { useDeviceAuth } from '@/composables/useDeviceAuth'
|
||||||
|
|
||||||
definePage({
|
definePage({
|
||||||
type: 'home',
|
type: 'home',
|
||||||
layout: false,
|
layout: false,
|
||||||
@@ -6,6 +9,12 @@ definePage({
|
|||||||
navigationStyle: 'custom',
|
navigationStyle: 'custom',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const { isActivated, fetchAuthStatus } = useDeviceAuth({ watchShow: true })
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchAuthStatus()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -21,6 +30,7 @@ definePage({
|
|||||||
<home-projectList />
|
<home-projectList />
|
||||||
</view>
|
</view>
|
||||||
<view class="column-right">
|
<view class="column-right">
|
||||||
|
<home-activateNotice v-if="!isActivated" />
|
||||||
<home-progress />
|
<home-progress />
|
||||||
<home-warning />
|
<home-warning />
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ function generateUUID(): string {
|
|||||||
/**
|
/**
|
||||||
* 获取设备唯一标识
|
* 获取设备唯一标识
|
||||||
*/
|
*/
|
||||||
function getDeviceId(): string {
|
export function getDeviceId(): string {
|
||||||
// 从存储获取
|
// 从存储获取
|
||||||
const storedId = storage.get<string>(DEVICE_ID_KEY)
|
const storedId = storage.get<string>(DEVICE_ID_KEY)
|
||||||
if (storedId) {
|
if (storedId) {
|
||||||
@@ -195,6 +195,7 @@ export function getOrInitDeviceInfo(): SystemInfo {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
getSystemInfo,
|
getSystemInfo,
|
||||||
|
getDeviceId,
|
||||||
getDeviceTypeName,
|
getDeviceTypeName,
|
||||||
getOsTypeName,
|
getOsTypeName,
|
||||||
saveDeviceInfo,
|
saveDeviceInfo,
|
||||||
|
|||||||
Reference in New Issue
Block a user