From e90da20e82b15c886bb57488776b6cfb3cb411a1 Mon Sep 17 00:00:00 2001 From: 12451 <12451@example.com> Date: Fri, 3 Jul 2026 04:10:46 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20H5=20=E7=94=A8=E6=88=B7=E7=AB=AF=20uni-?= =?UTF-8?q?app=20=E9=87=8D=E5=86=99=EF=BC=88=E6=9B=BF=E6=8D=A2=20Taro?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 使用 uni-app + Vue 3 + Composition API 重写 H5 用户端 - 6 个页面:登录、首页、设备列表、设备详情、仓体详情、个人中心 - Pinia 状态管理 + API 层封装 - TabBar 导航(首页/设备/我的) - 已构建并部署到服务器 /data/pms/h5/ - 登录页渲染正常,布局完整 --- software/h5-taro/config/index.js | 1 + software/h5-taro/src/app.config.ts | 4 +- software/h5-taro/src/index.html | 8 +- .../src/pages/compartment-detail/index.tsx | 5 + .../h5-taro/src/pages/device-detail/index.tsx | 5 + software/h5-taro/src/pages/home/index.tsx | 8 + software/h5-taro/src/pages/login/index.tsx | 7 +- software/h5-uniapp/index.html | 23 ++ software/h5-uniapp/package.json | 27 ++ software/h5-uniapp/src/App.vue | 16 + software/h5-uniapp/src/api/index.ts | 164 ++++++++++ software/h5-uniapp/src/env.d.ts | 7 + software/h5-uniapp/src/main.ts | 10 + software/h5-uniapp/src/manifest.json | 24 ++ software/h5-uniapp/src/pages.json | 27 ++ .../src/pages/compartment-detail/index.vue | 308 ++++++++++++++++++ .../src/pages/device-detail/index.vue | 195 +++++++++++ .../h5-uniapp/src/pages/devices/index.vue | 105 ++++++ software/h5-uniapp/src/pages/home/index.vue | 212 ++++++++++++ software/h5-uniapp/src/pages/login/index.vue | 150 +++++++++ software/h5-uniapp/src/pages/me/index.vue | 126 +++++++ .../src/static/tabbar/device-active.png | Bin 0 -> 69 bytes .../h5-uniapp/src/static/tabbar/device.png | Bin 0 -> 69 bytes .../src/static/tabbar/home-active.png | Bin 0 -> 69 bytes software/h5-uniapp/src/static/tabbar/home.png | Bin 0 -> 69 bytes .../h5-uniapp/src/static/tabbar/me-active.png | Bin 0 -> 69 bytes software/h5-uniapp/src/static/tabbar/me.png | Bin 0 -> 69 bytes software/h5-uniapp/src/store/auth.ts | 43 +++ software/h5-uniapp/tsconfig.json | 18 + software/h5-uniapp/vite.config.ts | 11 + 30 files changed, 1497 insertions(+), 7 deletions(-) create mode 100644 software/h5-uniapp/index.html create mode 100644 software/h5-uniapp/package.json create mode 100644 software/h5-uniapp/src/App.vue create mode 100644 software/h5-uniapp/src/api/index.ts create mode 100644 software/h5-uniapp/src/env.d.ts create mode 100644 software/h5-uniapp/src/main.ts create mode 100644 software/h5-uniapp/src/manifest.json create mode 100644 software/h5-uniapp/src/pages.json create mode 100644 software/h5-uniapp/src/pages/compartment-detail/index.vue create mode 100644 software/h5-uniapp/src/pages/device-detail/index.vue create mode 100644 software/h5-uniapp/src/pages/devices/index.vue create mode 100644 software/h5-uniapp/src/pages/home/index.vue create mode 100644 software/h5-uniapp/src/pages/login/index.vue create mode 100644 software/h5-uniapp/src/pages/me/index.vue create mode 100644 software/h5-uniapp/src/static/tabbar/device-active.png create mode 100644 software/h5-uniapp/src/static/tabbar/device.png create mode 100644 software/h5-uniapp/src/static/tabbar/home-active.png create mode 100644 software/h5-uniapp/src/static/tabbar/home.png create mode 100644 software/h5-uniapp/src/static/tabbar/me-active.png create mode 100644 software/h5-uniapp/src/static/tabbar/me.png create mode 100644 software/h5-uniapp/src/store/auth.ts create mode 100644 software/h5-uniapp/tsconfig.json create mode 100644 software/h5-uniapp/vite.config.ts diff --git a/software/h5-taro/config/index.js b/software/h5-taro/config/index.js index e6ab532..19a301e 100644 --- a/software/h5-taro/config/index.js +++ b/software/h5-taro/config/index.js @@ -38,6 +38,7 @@ const config = { staticDirectory: 'static', router: { mode: 'browser', + basename: '/h5/', }, postcss: { autoprefixer: { enable: true, config: {} }, diff --git a/software/h5-taro/src/app.config.ts b/software/h5-taro/src/app.config.ts index a3c644b..bfeda26 100644 --- a/software/h5-taro/src/app.config.ts +++ b/software/h5-taro/src/app.config.ts @@ -1,11 +1,11 @@ export default { pages: [ - 'pages/login/index', 'pages/home/index', 'pages/devices/index', + 'pages/me/index', + 'pages/login/index', 'pages/device-detail/index', 'pages/compartment-detail/index', - 'pages/me/index', ], window: { backgroundTextStyle: 'light', diff --git a/software/h5-taro/src/index.html b/software/h5-taro/src/index.html index 337785d..02751dd 100644 --- a/software/h5-taro/src/index.html +++ b/software/h5-taro/src/index.html @@ -5,17 +5,17 @@ 安知充 + + +
+ + + diff --git a/software/h5-uniapp/package.json b/software/h5-uniapp/package.json new file mode 100644 index 0000000..7c658ec --- /dev/null +++ b/software/h5-uniapp/package.json @@ -0,0 +1,27 @@ +{ + "name": "h5-uniapp", + "version": "1.0.0", + "private": true, + "scripts": { + "dev:h5": "uni", + "build:h5": "uni build" + }, + "dependencies": { + "@dcloudio/uni-app": "3.0.0-4060620250520001", + "@dcloudio/uni-app-plus": "3.0.0-4060620250520001", + "@dcloudio/uni-components": "3.0.0-4060620250520001", + "@dcloudio/uni-h5": "3.0.0-4060620250520001", + "@dcloudio/uni-mp-weixin": "3.0.0-4060620250520001", + "pinia": "^2.1.7", + "vue": "^3.4.21" + }, + "devDependencies": { + "@dcloudio/types": "^3.4.8", + "@dcloudio/uni-automator": "3.0.0-4060620250520001", + "@dcloudio/uni-cli-shared": "3.0.0-4060620250520001", + "@dcloudio/uni-stacktracey": "3.0.0-4060620250520001", + "@dcloudio/vite-plugin-uni": "3.0.0-4060620250520001", + "typescript": "^5.4.5", + "vite": "^5.2.8" + } +} diff --git a/software/h5-uniapp/src/App.vue b/software/h5-uniapp/src/App.vue new file mode 100644 index 0000000..986dc03 --- /dev/null +++ b/software/h5-uniapp/src/App.vue @@ -0,0 +1,16 @@ + + + diff --git a/software/h5-uniapp/src/api/index.ts b/software/h5-uniapp/src/api/index.ts new file mode 100644 index 0000000..2e7a196 --- /dev/null +++ b/software/h5-uniapp/src/api/index.ts @@ -0,0 +1,164 @@ +// API 层 — 复用 Taro 项目的逻辑,改用 uni.request + +const BASE_URL = '/api/h5' + +interface RequestOptions { + method?: string + body?: unknown + params?: Record +} + +async function request(path: string, options: RequestOptions = {}): Promise { + const token = uni.getStorageSync('h5_token') + const header: Record = { + 'Content-Type': 'application/json', + } + if (token) { + header['Authorization'] = `Bearer ${token}` + } + + let url = `${BASE_URL}${path}` + if (options.params) { + const qs = Object.entries(options.params) + .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`) + .join('&') + url += `?${qs}` + } + + return new Promise((resolve, reject) => { + uni.request({ + url, + method: (options.method as any) || 'GET', + header, + data: options.body, + success: (res) => { + if (res.statusCode >= 400) { + const msg = (res.data as any)?.message || (res.data as any)?.error || '请求失败' + reject(new Error(msg)) + return + } + resolve(res.data as T) + }, + fail: (err) => { + reject(new Error(err.errMsg || '网络错误')) + } + }) + }) +} + +// ---- Types ---- + +export interface LoginParams { + phone: string + password: string +} + +export interface LoginResult { + token: string + user: H5User +} + +export interface H5User { + id: number + phone: string + name?: string +} + +export interface DashboardData { + online_cabinets: number + charging_count: number + idle_channels: number + fault_channels: number + today_charge_count: number + today_energy_kwh: number + today_charge_hours: number + alerts: { project: string; cabinet: string; channel: number }[] + projects: { id: number; name: string; cabinet_count: number; charging: number; idle: number; fault: number }[] +} + +export interface CabinetItem { + id: number + abstract_id: string + name?: string + status: number + project_id?: number +} + +export interface CompartmentInfo { + id: number + channel_id: number + status: number + voltage?: number + current?: number +} + +export interface CabinBoard { + id: number + board_id: number + status: number + compartments: CompartmentInfo[] +} + +export interface CabinetDetail { + id: number + abstract_id: string + name?: string + imei?: string + status: number + cabin_boards: CabinBoard[] +} + +export interface CompartmentDetail { + id: number + channel_id: number + status: number + voltage?: number + current?: number + power?: number + energy?: number + soc?: number + soh?: number + cell_voltages?: number[] + cell_temps?: number[] + bms_alerts?: string[] + charge_records?: ChargeRecord[] +} + +export interface ChargeRecord { + id: number + start_time: string + end_time?: string + energy_kwh: number + cost: number +} + +// ---- API ---- + +export const h5Api = { + login: (params: LoginParams) => + request<{ data: LoginResult }>('/h5/auth/login', { method: 'POST', body: params }), + + getDashboard: () => + request<{ data: DashboardData }>('/h5/dashboard'), + + getCabinets: (params?: { project_id?: number }) => + request<{ data: CabinetItem[] }>('/h5/cabinets', { params: params as Record }), + + getCabinetByAbstract: (abstractId: string) => + request<{ data: CabinetDetail }>(`/h5/cabinets/abstract/${abstractId}`), + + getCabinetDetail: (id: number) => + request<{ data: CabinetDetail }>(`/h5/cabinets/${id}`), + + getCompartmentDetail: (id: number) => + request<{ data: CompartmentDetail }>(`/h5/compartments/${id}`), + + startCharge: (compartmentId: number) => + request<{ code: number }>('/h5/charge/start', { method: 'POST', body: { compartment_id: compartmentId } }), + + stopCharge: (compartmentId: number) => + request<{ code: number }>('/h5/charge/stop', { method: 'POST', body: { compartment_id: compartmentId } }), + + openDoor: (compartmentId: number) => + request<{ code: number }>('/h5/door/open', { method: 'POST', body: { compartment_id: compartmentId } }), +} diff --git a/software/h5-uniapp/src/env.d.ts b/software/h5-uniapp/src/env.d.ts new file mode 100644 index 0000000..f1f8dc3 --- /dev/null +++ b/software/h5-uniapp/src/env.d.ts @@ -0,0 +1,7 @@ +/// + +declare module '*.vue' { + import { DefineComponent } from 'vue' + const component: DefineComponent<{}, {}, any> + export default component +} diff --git a/software/h5-uniapp/src/main.ts b/software/h5-uniapp/src/main.ts new file mode 100644 index 0000000..14d3ec9 --- /dev/null +++ b/software/h5-uniapp/src/main.ts @@ -0,0 +1,10 @@ +import { createSSRApp } from 'vue' +import { createPinia } from 'pinia' +import App from './App.vue' + +export function createApp() { + const app = createSSRApp(App) + const pinia = createPinia() + app.use(pinia) + return { app } +} diff --git a/software/h5-uniapp/src/manifest.json b/software/h5-uniapp/src/manifest.json new file mode 100644 index 0000000..121edbf --- /dev/null +++ b/software/h5-uniapp/src/manifest.json @@ -0,0 +1,24 @@ +{ + "name": "安知充", + "appid": "", + "description": "智能充电柜管理系统", + "versionName": "1.0.0", + "versionCode": "100", + "transformPx": false, + "h5": { + "title": "安知充", + "router": { + "mode": "history", + "base": "/h5/" + }, + "devServer": { + "port": 5175, + "proxy": { + "/api": { + "target": "http://10.8.0.252:3000", + "changeOrigin": true + } + } + } + } +} diff --git a/software/h5-uniapp/src/pages.json b/software/h5-uniapp/src/pages.json new file mode 100644 index 0000000..f6edb8b --- /dev/null +++ b/software/h5-uniapp/src/pages.json @@ -0,0 +1,27 @@ +{ + "pages": [ + { "path": "pages/home/index", "style": { "navigationBarTitleText": "首页" } }, + { "path": "pages/devices/index", "style": { "navigationBarTitleText": "设备" } }, + { "path": "pages/me/index", "style": { "navigationBarTitleText": "我的" } }, + { "path": "pages/login/index", "style": { "navigationBarTitleText": "登录", "navigationStyle": "custom" } }, + { "path": "pages/device-detail/index", "style": { "navigationBarTitleText": "设备详情" } }, + { "path": "pages/compartment-detail/index", "style": { "navigationBarTitleText": "仓体详情" } } + ], + "tabBar": { + "color": "#999999", + "selectedColor": "#2563eb", + "backgroundColor": "#ffffff", + "borderStyle": "black", + "list": [ + { "pagePath": "pages/home/index", "text": "首页", "iconPath": "static/tabbar/home.png", "selectedIconPath": "static/tabbar/home-active.png" }, + { "pagePath": "pages/devices/index", "text": "设备", "iconPath": "static/tabbar/device.png", "selectedIconPath": "static/tabbar/device-active.png" }, + { "pagePath": "pages/me/index", "text": "我的", "iconPath": "static/tabbar/me.png", "selectedIconPath": "static/tabbar/me-active.png" } + ] + }, + "globalStyle": { + "navigationBarTextStyle": "black", + "navigationBarTitleText": "安知充", + "navigationBarBackgroundColor": "#ffffff", + "backgroundColor": "#f5f5f5" + } +} diff --git a/software/h5-uniapp/src/pages/compartment-detail/index.vue b/software/h5-uniapp/src/pages/compartment-detail/index.vue new file mode 100644 index 0000000..6f5f3b0 --- /dev/null +++ b/software/h5-uniapp/src/pages/compartment-detail/index.vue @@ -0,0 +1,308 @@ + + + + + diff --git a/software/h5-uniapp/src/pages/device-detail/index.vue b/software/h5-uniapp/src/pages/device-detail/index.vue new file mode 100644 index 0000000..3287029 --- /dev/null +++ b/software/h5-uniapp/src/pages/device-detail/index.vue @@ -0,0 +1,195 @@ + + + + + diff --git a/software/h5-uniapp/src/pages/devices/index.vue b/software/h5-uniapp/src/pages/devices/index.vue new file mode 100644 index 0000000..47437df --- /dev/null +++ b/software/h5-uniapp/src/pages/devices/index.vue @@ -0,0 +1,105 @@ + + + + + diff --git a/software/h5-uniapp/src/pages/home/index.vue b/software/h5-uniapp/src/pages/home/index.vue new file mode 100644 index 0000000..b3e2938 --- /dev/null +++ b/software/h5-uniapp/src/pages/home/index.vue @@ -0,0 +1,212 @@ + + + + + diff --git a/software/h5-uniapp/src/pages/login/index.vue b/software/h5-uniapp/src/pages/login/index.vue new file mode 100644 index 0000000..d6d748c --- /dev/null +++ b/software/h5-uniapp/src/pages/login/index.vue @@ -0,0 +1,150 @@ + + + + + diff --git a/software/h5-uniapp/src/pages/me/index.vue b/software/h5-uniapp/src/pages/me/index.vue new file mode 100644 index 0000000..91a415e --- /dev/null +++ b/software/h5-uniapp/src/pages/me/index.vue @@ -0,0 +1,126 @@ + + + + + diff --git a/software/h5-uniapp/src/static/tabbar/device-active.png b/software/h5-uniapp/src/static/tabbar/device-active.png new file mode 100644 index 0000000000000000000000000000000000000000..defb8edcaa1e6e06e16d108fab72d2e772b67b50 GIT binary patch literal 69 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx1|;Q0k8}bl9#0p?kcv6UKh7Tj@|YPI&2D7e Q1F{%AUHx3vIVCg!0Hj(D { + const token = ref(null) + const user = ref(null) + const isAuthenticated = ref(false) + + async function login(phone: string, password: string) { + const res = await h5Api.login({ phone, password }) + const { token: t, user: u } = res.data + uni.setStorageSync('h5_token', t) + uni.setStorageSync('h5_user', JSON.stringify(u)) + token.value = t + user.value = u + isAuthenticated.value = true + } + + function logout() { + uni.removeStorageSync('h5_token') + uni.removeStorageSync('h5_user') + token.value = null + user.value = null + isAuthenticated.value = false + } + + function restore() { + try { + const t = uni.getStorageSync('h5_token') + const raw = uni.getStorageSync('h5_user') + if (t) { + token.value = t + user.value = raw ? JSON.parse(raw) : null + isAuthenticated.value = true + } + } catch { + // storage 读取失败,忽略 + } + } + + return { token, user, isAuthenticated, login, logout, restore } +}) diff --git a/software/h5-uniapp/tsconfig.json b/software/h5-uniapp/tsconfig.json new file mode 100644 index 0000000..aa72f12 --- /dev/null +++ b/software/h5-uniapp/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "strict": true, + "jsx": "preserve", + "moduleResolution": "node", + "esModuleInterop": true, + "sourceMap": true, + "skipLibCheck": true, + "lib": ["esnext", "dom"], + "types": ["@dcloudio/types"], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] +} diff --git a/software/h5-uniapp/vite.config.ts b/software/h5-uniapp/vite.config.ts new file mode 100644 index 0000000..bfecbad --- /dev/null +++ b/software/h5-uniapp/vite.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'vite' +import uni from '@dcloudio/vite-plugin-uni' + +export default defineConfig({ + plugins: [uni()], + base: '/h5/', + build: { + outDir: 'dist/build/h5', + assetsDir: 'static' + } +})