From bbde4c4a6643ee3d1f9a7784bd68b0b0bfb07a2d Mon Sep 17 00:00:00 2001 From: Ironsp <1522278303@qq.com> Date: Tue, 8 Sep 2026 16:23:17 +0800 Subject: [PATCH] =?UTF-8?q?feat=20=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auto-imports.d.ts | 17 +++ src/components.d.ts | 1 + src/components/home/deviceFooter.vue | 183 ++++++++++++++++++++++++ src/main.ts | 2 + src/pages/Ahome/index.vue | 3 + src/store/index.js | 30 ---- src/store/index.ts | 5 + src/store/modules/home.ts | 53 +++++++ src/uni-pages.d.ts | 1 - src/utils/device.ts | 203 +++++++++++++++++++++++++++ src/utils/sqlite.ts | 16 +-- 11 files changed, 475 insertions(+), 39 deletions(-) create mode 100644 src/components/home/deviceFooter.vue delete mode 100644 src/store/index.js create mode 100644 src/store/index.ts create mode 100644 src/store/modules/home.ts create mode 100644 src/utils/device.ts diff --git a/src/auto-imports.d.ts b/src/auto-imports.d.ts index 3875e62..b268c29 100644 --- a/src/auto-imports.d.ts +++ b/src/auto-imports.d.ts @@ -32,12 +32,18 @@ declare global { const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch'] const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] const defineComponent: typeof import('vue')['defineComponent'] + const device: typeof import('./utils/device')['default'] const eagerComputed: typeof import('@vueuse/core')['eagerComputed'] const effectScope: typeof import('vue')['effectScope'] const executeSql: typeof import('./utils/sqlite')['executeSql'] const extendRef: typeof import('@vueuse/core')['extendRef'] const getCurrentInstance: typeof import('vue')['getCurrentInstance'] const getCurrentScope: typeof import('vue')['getCurrentScope'] + const getDeviceInfoFromStorage: typeof import('./utils/device')['getDeviceInfoFromStorage'] + const getDeviceTypeName: typeof import('./utils/device')['getDeviceTypeName'] + const getOrInitDeviceInfo: typeof import('./utils/device')['getOrInitDeviceInfo'] + const getOsTypeName: typeof import('./utils/device')['getOsTypeName'] + const getSystemInfo: typeof import('./utils/device')['getSystemInfo'] const h: typeof import('vue')['h'] const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch'] const initTables: typeof import('./utils/sqlite')['initTables'] @@ -114,6 +120,7 @@ declare global { const resolveComponent: typeof import('vue')['resolveComponent'] const resolveRef: typeof import('@vueuse/core')['resolveRef'] const resolveUnref: typeof import('@vueuse/core')['resolveUnref'] + const saveDeviceInfo: typeof import('./utils/device')['saveDeviceInfo'] const selectSql: typeof import('./utils/sqlite')['selectSql'] const shallowReactive: typeof import('vue')['shallowReactive'] const shallowReadonly: typeof import('vue')['shallowReadonly'] @@ -328,6 +335,9 @@ declare global { // @ts-ignore export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' import('vue') + // @ts-ignore + export type { SystemInfo } from './utils/device' + import('./utils/device') } // for vue template auto import @@ -361,12 +371,18 @@ declare module 'vue' { readonly debouncedWatch: UnwrapRef readonly defineAsyncComponent: UnwrapRef readonly defineComponent: UnwrapRef + readonly device: UnwrapRef readonly eagerComputed: UnwrapRef readonly effectScope: UnwrapRef readonly executeSql: UnwrapRef readonly extendRef: UnwrapRef readonly getCurrentInstance: UnwrapRef readonly getCurrentScope: UnwrapRef + readonly getDeviceInfoFromStorage: UnwrapRef + readonly getDeviceTypeName: UnwrapRef + readonly getOrInitDeviceInfo: UnwrapRef + readonly getOsTypeName: UnwrapRef + readonly getSystemInfo: UnwrapRef readonly h: UnwrapRef readonly ignorableWatch: UnwrapRef readonly initTables: UnwrapRef @@ -443,6 +459,7 @@ declare module 'vue' { readonly resolveComponent: UnwrapRef readonly resolveRef: UnwrapRef readonly resolveUnref: UnwrapRef + readonly saveDeviceInfo: UnwrapRef readonly selectSql: UnwrapRef readonly shallowReactive: UnwrapRef readonly shallowReadonly: UnwrapRef diff --git a/src/components.d.ts b/src/components.d.ts index 18b953c..e6b4f3f 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -13,6 +13,7 @@ declare module 'vue' { DeviceInfo: typeof import('./components/deviceInfo.vue')['default'] FullscreenNav: typeof import('./components/FullscreenNav.vue')['default'] HomeConstruction: typeof import('./components/home/construction.vue')['default'] + HomeDeviceFooter: typeof import('./components/home/deviceFooter.vue')['default'] HomeHomeNav: typeof import('./components/home/homeNav.vue')['default'] HomeList: typeof import('./components/home/list.vue')['default'] HomeOverview: typeof import('./components/home/overview.vue')['default'] diff --git a/src/components/home/deviceFooter.vue b/src/components/home/deviceFooter.vue new file mode 100644 index 0000000..a7ce2a0 --- /dev/null +++ b/src/components/home/deviceFooter.vue @@ -0,0 +1,183 @@ + + + + + diff --git a/src/main.ts b/src/main.ts index c97747e..8cce6d6 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,9 +1,11 @@ import { createSSRApp } from 'vue' import App from './App.vue' +import store from './store' import 'uno.css' export function createApp() { const app = createSSRApp(App) + app.use(store) return { app, } diff --git a/src/pages/Ahome/index.vue b/src/pages/Ahome/index.vue index 6084ad0..3e07553 100644 --- a/src/pages/Ahome/index.vue +++ b/src/pages/Ahome/index.vue @@ -24,6 +24,9 @@ definePage({ + + + diff --git a/src/store/index.js b/src/store/index.js deleted file mode 100644 index 24e7f62..0000000 --- a/src/store/index.js +++ /dev/null @@ -1,30 +0,0 @@ -import { defineStore } from 'pinia' -// import { computed, ref } from 'vue' -// import { getDevicetList } from '@/api/equipmentList/index' - -export const useDeviceStore = defineStore('devices', () => { - // --- State (状态) --- - // 相当于 Vuex 中的 state,用 ref 或 reactive 定义 - // const deviceList = ref(); - - // --- Getters (计算属性) --- - // 相当于 Vuex 中的 getters,直接用 computed 定义 - - // --- Actions (动作) --- - // 相当于 Vuex 中的 actions/mutations,直接写同步或异步函数 - // async function getdeviceList(query?: any) { - // try { - // const response = await getDevicetList(query); - // console.log('获取设备列表:', response); - // deviceList.value = response.rows; - // } catch (error) { - // console.error('获取设备列表失败', error); - // } - // } - - // ⚠️ 核心点:必须把需要暴露出去的变量和方法 return 出去! - return { - // deviceList, - // getdeviceList - } -}) diff --git a/src/store/index.ts b/src/store/index.ts new file mode 100644 index 0000000..94e2432 --- /dev/null +++ b/src/store/index.ts @@ -0,0 +1,5 @@ +import { createPinia } from 'pinia' + +const store = createPinia() + +export default store diff --git a/src/store/modules/home.ts b/src/store/modules/home.ts new file mode 100644 index 0000000..ffa8232 --- /dev/null +++ b/src/store/modules/home.ts @@ -0,0 +1,53 @@ +import type { SystemInfo } from '@/utils/device' +import { defineStore } from 'pinia' +import { computed, ref } from 'vue' +import { getDeviceTypeName, getOrInitDeviceInfo, getOsTypeName } from '@/utils/device' + +export const useDeviceStore = defineStore('device', () => { + // --- State --- + const currentDevice = ref(null) + + // --- Getters --- + const deviceTypeName = computed(() => { + return currentDevice.value ? getDeviceTypeName(currentDevice.value.deviceType) : '未知' + }) + + const osTypeName = computed(() => { + return currentDevice.value ? getOsTypeName(currentDevice.value.osType) : '未知' + }) + + const isPhone = computed(() => currentDevice.value?.deviceType === 'phone') + const isTablet = computed(() => currentDevice.value?.deviceType === 'tablet') + const isPC = computed(() => currentDevice.value?.deviceType === 'pc') + + // --- Actions --- + /** + * 初始化设备信息 + * 从本地存储读取或重新获取当前设备信息,并写入 state + */ + function initDevice() { + currentDevice.value = getOrInitDeviceInfo() + console.log('初始化设备信息11111111111111', currentDevice.value) + } + + function getDevice() { + if (!currentDevice.value) { + initDevice() + } + return currentDevice.value + } + + return { + // State + currentDevice, + // Getters + deviceTypeName, + osTypeName, + isPhone, + isTablet, + isPC, + // Actions + initDevice, + getDevice, + } +}) diff --git a/src/uni-pages.d.ts b/src/uni-pages.d.ts index 266b6cd..da6a0e6 100644 --- a/src/uni-pages.d.ts +++ b/src/uni-pages.d.ts @@ -6,7 +6,6 @@ type _LocationUrl = "/pages/Ahome/index" | "/pages/bluetooth/index" | - "/pages/demo/sqlite" | "/pages/heatmap/index" | "/pages/message/index" | "/pages/project/index" | diff --git a/src/utils/device.ts b/src/utils/device.ts new file mode 100644 index 0000000..f36b40a --- /dev/null +++ b/src/utils/device.ts @@ -0,0 +1,203 @@ +/** + * 设备信息工具类 + * 获取当前运行设备的信息(手机、平板、电脑) + */ + +import storage from './storage' + +export interface SystemInfo { + brand: string + model: string + system: string + platform: string + screenWidth: number + screenHeight: number + windowWidth: number + windowHeight: number + statusBarHeight?: number + deviceType: 'phone' | 'tablet' | 'pc' + osType: 'android' | 'ios' | 'windows' | 'mac' | 'linux' | 'unknown' + deviceId: string +} + +const STORAGE_KEY = 'device_info' +const DEVICE_ID_KEY = 'device_unique_id' + +/** + * 获取设备类型 + */ +function getDeviceType(info: UniApp.GetSystemInfoResult): SystemInfo['deviceType'] { + const platform = info.platform?.toLowerCase() || '' + + if (platform === 'windows' || platform === 'mac' || platform === 'linux') { + return 'pc' + } + + const screenWidth = info.screenWidth || 0 + const screenHeight = info.screenHeight || 0 + const minSize = Math.min(screenWidth, screenHeight) + + if (minSize >= 600) { + return 'tablet' + } + + return 'phone' +} + +/** + * 获取操作系统类型 + */ +function getOsType(info: UniApp.GetSystemInfoResult): SystemInfo['osType'] { + const system = info.system?.toLowerCase() || '' + const platform = info.platform?.toLowerCase() || '' + + if (system.includes('android') || platform === 'android') { + return 'android' + } + if (system.includes('ios') || platform === 'ios') { + return 'ios' + } + if (platform === 'windows') { + return 'windows' + } + if (platform === 'mac') { + return 'mac' + } + if (platform === 'linux') { + return 'linux' + } + return 'unknown' +} + +/** + * 生成 UUID v4 + */ +function generateUUID(): string { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { + const r = (Math.random() * 16) | 0 + const v = c === 'x' ? r : (r & 0x3) | 0x8 + return v.toString(16) + }) +} + +/** + * 获取设备唯一标识 + */ +function getDeviceId(): string { + // 从存储获取 + const storedId = storage.get(DEVICE_ID_KEY) + if (storedId) { + console.log('设备ID (已存储):', storedId) + return storedId + } + + // 尝试使用系统 deviceId + try { + const deviceInfo = uni.getDeviceInfo() + if (deviceInfo.deviceId) { + storage.set(DEVICE_ID_KEY, deviceInfo.deviceId) + console.log('设备ID (系统):', deviceInfo.deviceId) + return deviceInfo.deviceId + } + } + catch { + // 忽略 + } + + // 生成新的 UUID + const newId = generateUUID() + storage.set(DEVICE_ID_KEY, newId) + console.log('设备ID (新生成):', newId) + return newId +} + +/** + * 获取完整系统信息 + */ +export function getSystemInfo(): SystemInfo { + const info = uni.getSystemInfoSync() + + return { + brand: info.brand || 'unknown', + model: info.model || 'unknown', + system: info.system || 'unknown', + platform: info.platform || 'unknown', + screenWidth: info.screenWidth || 0, + screenHeight: info.screenHeight || 0, + windowWidth: info.windowWidth || 0, + windowHeight: info.windowHeight || 0, + statusBarHeight: info.statusBarHeight, + deviceType: getDeviceType(info), + osType: getOsType(info), + deviceId: getDeviceId(), + } +} + +/** + * 获取设备类型中文名 + */ +export function getDeviceTypeName(type: SystemInfo['deviceType']): string { + const typeMap: Record = { + phone: '手机', + tablet: '平板', + pc: '电脑', + } + return typeMap[type] || '未知' +} + +/** + * 获取操作系统中文名 + */ +export function getOsTypeName(type: SystemInfo['osType']): string { + const typeMap: Record = { + android: 'Android', + ios: 'iOS', + windows: 'Windows', + mac: 'macOS', + linux: 'Linux', + unknown: '未知', + } + return typeMap[type] || '未知' +} + +/** + * 保存设备信息到本地存储 + */ +export function saveDeviceInfo(info: SystemInfo): void { + storage.set(STORAGE_KEY, info) +} + +/** + * 从本地存储获取设备信息 + */ +export function getDeviceInfoFromStorage(): SystemInfo | null { + return storage.get(STORAGE_KEY) || null +} + +/** + * 获取设备信息(优先从存储读取,没有则获取并保存) + */ +export function getOrInitDeviceInfo(): SystemInfo { + let deviceInfo = getDeviceInfoFromStorage() + + if (!deviceInfo) { + deviceInfo = getSystemInfo() + saveDeviceInfo(deviceInfo) + } + else if (!deviceInfo.deviceId) { + // 旧数据没有 deviceId,补充生成 + deviceInfo.deviceId = getDeviceId() + saveDeviceInfo(deviceInfo) + } + + return deviceInfo +} + +export default { + getSystemInfo, + getDeviceTypeName, + getOsTypeName, + saveDeviceInfo, + getDeviceInfoFromStorage, + getOrInitDeviceInfo, +} diff --git a/src/utils/sqlite.ts b/src/utils/sqlite.ts index e3a9e91..fc1b478 100644 --- a/src/utils/sqlite.ts +++ b/src/utils/sqlite.ts @@ -399,11 +399,11 @@ class DB { */ export async function initTables() { const sqls = [ - // 设备信息表 - `CREATE TABLE IF NOT EXISTS device ( + // 项目信息表 + `CREATE TABLE IF NOT EXISTS project ( id INTEGER PRIMARY KEY AUTOINCREMENT, - device_name TEXT NOT NULL, - device_type TEXT, + project_name TEXT NOT NULL, + project_type TEXT, status TEXT DEFAULT 'offline', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP @@ -411,7 +411,7 @@ export async function initTables() { // 作业记录表 `CREATE TABLE IF NOT EXISTS work_record ( id INTEGER PRIMARY KEY AUTOINCREMENT, - device_id INTEGER, + project_id INTEGER, start_time DATETIME, end_time DATETIME, speed REAL, @@ -421,7 +421,7 @@ export async function initTables() { gps_lat REAL, gps_lng REAL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (device_id) REFERENCES device(id) + FOREIGN KEY (project_id) REFERENCES project(id) )`, // RTK 轨迹数据表 `CREATE TABLE IF NOT EXISTS rtk_track ( @@ -439,13 +439,13 @@ export async function initTables() { // 告警记录表 `CREATE TABLE IF NOT EXISTS warning_record ( id INTEGER PRIMARY KEY AUTOINCREMENT, - device_id INTEGER, + project_id INTEGER, warning_type TEXT, warning_level TEXT, message TEXT, is_read INTEGER DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (device_id) REFERENCES device(id) + FOREIGN KEY (project_id) REFERENCES project(id) )`, ]