diff --git a/software/h5-uniapp/package.json b/software/h5-uniapp/package.json index b128ecf..c279bff 100644 --- a/software/h5-uniapp/package.json +++ b/software/h5-uniapp/package.json @@ -3,7 +3,8 @@ "version": "1.0.0", "private": true, "scripts": { - "build:mp-weixin": "uni build --platform mp-weixin" + "build:mp-weixin": "uni build --platform mp-weixin", + "typecheck": "vue-tsc --noEmit -p tsconfig.json" }, "dependencies": { "@dcloudio/uni-app": "3.0.0-4060620250520001", @@ -21,6 +22,7 @@ "@dcloudio/uni-stacktracey": "3.0.0-4060620250520001", "@dcloudio/vite-plugin-uni": "3.0.0-4060620250520001", "typescript": "^5.4.5", - "vite": "^5.2.8" + "vite": "^5.2.8", + "vue-tsc": "^2.2.12" } } diff --git a/software/h5-uniapp/src/api/index.ts b/software/h5-uniapp/src/api/index.ts index 16bdf33..e95145e 100644 --- a/software/h5-uniapp/src/api/index.ts +++ b/software/h5-uniapp/src/api/index.ts @@ -116,7 +116,21 @@ export interface CabinetItem { id: number abstract_id: string name?: string + /** + * **配置态**:`1` = 启用(默认)/ `2` = 禁用(后端据此拦截开合闸、开门、充电)。 + * + * ⛔ **不表示在线** —— 全仓没有任何写入方会把它写成 `0`(离线)。 + * 展示"在线/离线"一律用 [`CabinetItem.is_online`]。 + */ status: number + /** + * **实时在线**(`GET /api/h5/cabinets` 返回,值来自 Redis `device:{imei}.online`) + * + * `true` 在线 / `false` 明确离线 / `null`、缺失 = 无数据(键或字段不存在)。 + * 口径与 PC 设备列表 `is_online` **同源**(后端同一纯函数)。 + * ⚠️ 与 `status`(启用/禁用配置态)严格分离,不得互相推断。 + */ + is_online?: boolean | null project_id?: number rssi?: number imei?: string @@ -164,6 +178,16 @@ export interface CompartmentInfo { export interface CabinBoard { id: number board_id: number + /** + * ⛔ **不要用它推断"仓控板在线"** —— `cabin_boards.status` 在**整个后端没有任何写入方** + * (全仓只有建表默认值 0,`INSERT INTO cabin_boards (cabinet_id, board_id)` 没写这一列; + * 生产库实测取值分布只有 `0`)。协议里也**没有"仓控板在线"的概念**: + * 与平台保持 TCP 长连接的是**柜控板**(按 IMEI 标识),仓控板挂在柜内 485 总线上, + * 没有独立上行通路 ⇒ 这个状态**推不出来**。 + * 该列保留仅为兼容后端响应形状;**界面不得据此显示"在线/离线"** + * (此前小程序展示的板级徽标恒定显示"离线",属谎报,已删除)。 + * 柜级在线状态请看 `CabinetDetail.is_online`(Redis 实时值)。 + */ status: number compartments: CompartmentInfo[] } @@ -174,7 +198,19 @@ export interface CabinetDetail { name?: string imei?: string iccid?: string + /** + * **配置态**:`1` = 启用 / `2` = 禁用(后端据此拦截指令链路)。 + * + * ⛔ **不表示在线**;展示"在线/离线"请用 [`CabinetDetail.is_online`]。 + */ status: number + /** + * **实时在线**(`GET /api/cabinets/{id}` 返回,值来自 Redis `device:{imei}.online`) + * + * `true` 在线 / `false` 明确离线 / `null`、缺失 = 无数据。 + * 判据与设备列表 `is_online` **同源**(后端同一纯函数)。 + */ + is_online?: boolean | null rssi?: number /** 直流停电(协议 §5.4);true=停电 / false=正常 / null=无数据 */ pow_fail_dc?: boolean | null @@ -214,6 +250,15 @@ export interface CompartmentDetail { batt_status?: string | null /** 电源状态(协议 §3.8,2 位 hex);与电池状态相互独立,null/缺失 = 展示 "-" */ pow_status?: string | null + /** + * **仓体温度**(协议附 A,K×100) + * + * 后端 `GET /api/h5/compartments/{id}` 会返回该字段 + * (`routes/h5.rs` 的 `COMPARTMENT_REALTIME_FIELDS` 第 6 项 `"temp"`,有单测守门), + * 展示走 `utils/deviceStatus.ts::formatCelsius`。 + * 此前类型里漏了它 ⇒ 页面 `data.temp` 报 TS2339(类型契约与后端不一致,已补)。 + */ + temp?: number | null } export interface ChargeRecord { diff --git a/software/h5-uniapp/src/env.d.ts b/software/h5-uniapp/src/env.d.ts index f1f8dc3..ee75c71 100644 --- a/software/h5-uniapp/src/env.d.ts +++ b/software/h5-uniapp/src/env.d.ts @@ -5,3 +5,31 @@ declare module '*.vue' { const component: DefineComponent<{}, {}, any> export default component } + +/** + * 微信小程序宿主全局 `wx`。 + * + * **为什么需要手工声明**:`@dcloudio/types` 只声明跨端的 `uni`,**不声明宿主特有的 `wx`**; + * 而自定义导航栏的安全区计算必须用宿主 API(胶囊按钮位置没有等价的跨端取法), + * 所以 6 个页面里直接用了 `wx.*`。不声明时 `vue-tsc` 会报 12 处 + * `TS2304: Cannot find name 'wx'` —— 噪声会把**真正的类型错误淹掉** + * (与「打印 ❌ 却 exit 0」是同一类问题:判据不再指向真实缺陷)。 + * + * ⚠️ **刻意只声明实际用到的两个 API**,不写 `[k: string]: any`: + * 后者会让 `wx.anything()` 全部通过,等于把这一层检查关掉。需要新宿主 API 时, + * 按同样方式在下面补一条(签名照官方文档抄)。 + * + * 运行期说明:`wx` 只存在于 `mp-weixin` 产物中,而本工程只构建该目标(`build:mp-weixin`)。 + */ +declare const wx: { + /** 取宿主系统信息(本工程只消费 `statusBarHeight`) */ + getSystemInfoSync(): { + /** 状态栏高度(px) */ + statusBarHeight?: number + } + /** 取右上角胶囊按钮布局,用于按"胶囊高度 + 上下间距"反推自定义导航栏高度 */ + getMenuButtonBoundingClientRect(): { + top: number + height: number + } +} diff --git a/software/h5-uniapp/src/pages/device-detail/index.vue b/software/h5-uniapp/src/pages/device-detail/index.vue index 689ca0b..9ef64aa 100644 --- a/software/h5-uniapp/src/pages/device-detail/index.vue +++ b/software/h5-uniapp/src/pages/device-detail/index.vue @@ -15,7 +15,13 @@ {{ data.abstract_id }} - {{ data.status === 1 ? '在线' : '离线' }} + + {{ data.status === 2 ? '禁用' : (data.is_online === true ? '在线' : '离线') }} {{ data.rssi != null ? data.rssi : '—' }} @@ -71,7 +77,16 @@ {{ data.imei }}-{{ board.board_id }} - {{ board.status === 1 ? '在线' : '离线' }} + @@ -247,7 +262,9 @@ function openLocation() { .status-badge { display: inline-flex; align-items: center; gap: 4px; padding: 3px 8px; font-size: 11px; font-weight: 500; border-radius: 999px; } .status-badge.online { background: #f0fdf4; color: #22c55e; } .status-badge.offline { background: #f1f5f9; color: #94a3b8; } -.status-badge.online::before, .status-badge.offline::before { content: ''; width: 5px; height: 5px; border-radius: 50%; background: currentColor; } +/* 配置态「禁用」(`cabinets.status === 2`),与"离线"区分 */ +.status-badge.disabled { background: #fff7ed; color: #f97316; } +.status-badge.online::before, .status-badge.offline::before, .status-badge.disabled::before { content: ''; width: 5px; height: 5px; border-radius: 50%; background: currentColor; } .signal { display: flex; align-items: flex-end; gap: 1.5px; height: 14px; } .signal-bar { width: 2.5px; border-radius: 1px; } .signal-bar.l1 { height: 4px; } .signal-bar.l2 { height: 7px; } .signal-bar.l3 { height: 10px; } .signal-bar.l4 { height: 13px; } .signal-bar.l5 { height: 14px; } @@ -287,8 +304,8 @@ function openLocation() { .board-section { margin: 12px 16px; background: #fff; border-radius: 12px; padding: 14px; box-shadow: 0 1px 2px rgba(0,0,0,0.04); } .board-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; } .board-title { font-size: 14px; font-weight: 600; color: #111827; font-variant-numeric: tabular-nums; } -.board-status { font-size: 11px; color: #94a3b8; } -.board-status.online { color: #22c55e; } +/* `.board-status` 样式已随"板级在线徽标"一并删除:该状态无数据来源(见模板注释), + 留着只会诱使后来者再加回那个恒定显示"离线"的徽标。 */ .comp-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; } .comp { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-radius: 8px; border: 1.5px solid transparent; } .comp:active { transform: scale(0.97); } diff --git a/software/h5-uniapp/src/pages/home/index.vue b/software/h5-uniapp/src/pages/home/index.vue index d6c737f..ed7b1cb 100644 --- a/software/h5-uniapp/src/pages/home/index.vue +++ b/software/h5-uniapp/src/pages/home/index.vue @@ -40,7 +40,16 @@ {{ cab.abstract_id }} - {{ cab.status === 1 ? '在线' : '离线' }} + + {{ cab.status === 2 ? '禁用' : (cab.is_online === true ? '在线' : '离线') }} {{ formatCelsius(cab.temp) }} @@ -267,7 +276,9 @@ function showScanInput() { .status-badge { display: inline-flex; align-items: center; gap: 4px; padding: 3px 8px; font-size: 11px; font-weight: 500; border-radius: 999px; } .status-badge.online { background: #f0fdf4; color: #22c55e; } .status-badge.offline { background: #f1f5f9; color: #94a3b8; } -.status-badge.online::before, .status-badge.offline::before { content: ''; width: 5px; height: 5px; border-radius: 50%; background: currentColor; } +/* 配置态「禁用」(`cabinets.status === 2`):与"离线"区分开的橙色徽标 */ +.status-badge.disabled { background: #fff7ed; color: #f97316; } +.status-badge.online::before, .status-badge.offline::before, .status-badge.disabled::before { content: ''; width: 5px; height: 5px; border-radius: 50%; background: currentColor; } .temp-badge { font-size: 12px; color: #475569; font-variant-numeric: tabular-nums; flex-shrink: 0; } .header-right { display: flex; align-items: center; gap: 8px; } .signal-inline { display: flex; align-items: center; gap: 3px; } diff --git a/software/h5-uniapp/src/pages/me/index.vue b/software/h5-uniapp/src/pages/me/index.vue index 912a040..fe65152 100644 --- a/software/h5-uniapp/src/pages/me/index.vue +++ b/software/h5-uniapp/src/pages/me/index.vue @@ -27,7 +27,7 @@