更新新增板卡的抽屉页面

This commit is contained in:
徐星 2026-04-08 11:27:41 +08:00
parent 71095c294c
commit 269064f448
13 changed files with 1365 additions and 486 deletions

View File

@ -4,37 +4,38 @@
flowchart TD
%% ===== 入口 =====
HOME["首页 Dashboard\n设备统计 · 待处理任务 · 快捷导航"]
HOME["首页 Dashboard\n设备统计(8项指标) · 设备状态分布\n待处理任务(4组) · 快捷导航"]
%% ===== 阶段零:型号管理(核心枢纽)=====
subgraph S0["型号管理(核心枢纽)"]
MODEL["设备型号管理\n型号列表 · 在产/停产"]
MODEL -->|绑定| AUTH_FILE["授权文件\n按型号绑定"]
MODEL["设备型号管理\n型号列表 · 在产/停产\n新增型号抽屉"]
MODEL -->|绑定| AUTH_FILE["授权文件\n按型号绑定授权项"]
MODEL -->|绑定| CFG_FILE["配置文件\n按型号绑定"]
MODEL -->|绑定| FW_FILE["固件文件\n主机/主协板/发射板/采集板"]
MODEL -->|管理| CHECKLIST["装配Checklist模板\n按型号配置检查项"]
MODEL -->|管理| CHECKLIST["装配Checklist模板\nTab切换型号 · 可编辑\n新增模版抽屉"]
MODEL -->|管理| BOARD_MGR["板卡型号管理\n主协板/采集板/发射板/升压板\nTab筛选 · 详情抽屉"]
end
%% ===== 阶段一:固件与校准 =====
subgraph S1["阶段一:固件与校准"]
FW_LIB["固件库\n主机固件 · 主协板固件\n发射板固件 · 采集板固件"]
FW_LIB -->|上传zip| FW_UPLOAD["上传固件弹窗\n选择类型 · 版本号 · zip文件"]
FW_LIB -->|烧录采集板| CALIB["采集板校准记录\n校准数据管理"]
FW_LIB -->|上传zip| FW_UPLOAD["上传固件弹窗(Element Plus)\n版本号 · 硬件版本范围\n固件类型(5种) · 升级类型\n数字签名 · 发布说明"]
CALIB["采集板校准管理\n校准记录列表 · 导入\n合格/不合格/待校准"]
end
%% ===== 阶段二:生产装配 =====
subgraph S2["阶段二:生产装配"]
DEV_REG["设备登记\n装机信息 · BOM清单 · Checklist"]
DEV_REG -->|导入BOM| IMPORT_DIALOG["Excel导入弹窗\n下载模板 · 上传文件"]
DEV_REG -->|拍照上传| PHOTO_DIALOG["照片上传弹窗\n多张照片 · 备注"]
DEV_REG -->|登记完成| DEV_LIST["设备列表\n筛选 · 搜索 · 状态"]
DEV_LIST -->|点击详情| DEV_DETAIL["设备详情\n基本信息 \n授权信息 · 装配记录\n子设备列表 · 维修历史"]
DEV_REG["设备登记\n装机信息表单 · 型号匹配提示\nBOM清单 · 装配Checklist"]
DEV_REG -->|导入BOM| IMPORT_DIALOG["Excel导入弹窗\n下载模板 · 上传文件\n支持xlsx/xls/csv"]
DEV_REG -->|拍照上传| PHOTO_DIALOG["照片上传弹窗\n多张照片 · 装配记录信息"]
DEV_REG -->|登记完成| DEV_LIST["设备列表\n5项筛选 · 卡片式列表\n导出 · 分页"]
DEV_LIST -->|点击详情| DEV_DETAIL["设备详情页\n基本信息 · 授权信息(含模块列表)\n装配记录(含Checklist摘要)\n子设备列表(5种板卡)\n固件信息 · 维修历史(时间线)"]
end
%% ===== 阶段三:授权出厂 =====
subgraph S3["阶段三:授权出厂"]
LIC_MGR["授权管理\n授权文件列表 · 按型号管理"]
LIC_MGR -->|选择授权项生成| LIC_DRAWER["授权项抽屉\n选择型号 · 勾选功能模块\n设置有效期 · 生成授权文件"]
LIC_MGR["授权管理\n授权文件列表 · 按型号筛选\n已发布/草稿/已停用"]
LIC_MGR -->|选择授权项| LIC_DRAWER["授权项抽屉(640px)\n选择型号(自动预选) · 有效期\n11项功能模块勾选\n全选/清空 · 保存"]
end
%% ===== 阶段四APP使用非平台=====
@ -48,57 +49,66 @@ end
%% ===== 阶段五:配置管理 =====
subgraph S5["阶段五:配置管理"]
CFG_MGR["配置文件管理\n配置列表 · 筛选"]
CFG_MGR -->|新建配置抽屉| CFG_NEW["新建配置抽屉\n选择型号 · 发射参数\n采集参数 · 网络参数"]
CFG_MGR -->|编辑详情| PARAM_CFG["参数配置\n发射参数 · 采集参数\n保护参数 · 网络参数"]
CFG_MGR["配置文件管理\n配置列表 · 筛选(型号/版本/关键字)\n生效/已停用"]
CFG_MGR -->|新建配置抽屉| CFG_NEW["新建配置抽屉(520px)\n基本信息 · 发射参数\n采集参数 · 网络参数"]
CFG_MGR -->|编辑详情| PARAM_CFG["参数配置页\n发射参数(电压/电流/脉宽/波形/占空比)\n采集参数(量程/采样率/通道数)\n保护参数(过压/过流/短路/高温)\n网络参数(WiFi前缀)"]
end
%% ===== 阶段六:维修运维 =====
subgraph S6["阶段六:维修运维"]
REPAIR["维修工单列表\n筛选 · 状态 · 优先级"]
REPAIR -->|新建工单抽屉| NEW_ORDER["新建工单抽屉\n设备信息 · 故障信息 · 工单信息"]
REPAIR -->|处理抽屉| PROCESS["处理工单抽屉\n更换板卡 · 授权处理 · 报废申请"]
REPAIR -->|详情抽屉| ORDER_DETAIL["工单详情抽屉\n工单/设备/故障信息\n处理记录 · 板卡更换 · 授权处理"]
REPAIR -->|维修统计| REPAIR_STATS["维修统计\n故障分布 · 维修趋势"]
REPAIR["维修工单列表\n6项筛选 · 卡片式列表\n状态(处理中/已处理/待处理)\n优先级(高/中/低)"]
REPAIR -->|新建工单抽屉| NEW_ORDER["新建工单抽屉(520px)\n设备信息(SN选择+设备查找)\n故障信息(类型/描述/现象)\n工单信息(优先级/人员/时间/备注)"]
REPAIR -->|处理抽屉| PROCESS["处理工单抽屉(520px)\n处理操作(更换板卡/固件修复/参数重置/其他)\n板卡更换(原SN→新SN)\n授权处理(重新生成/推送固件)\n报废处理(选择原因→申请报废)"]
REPAIR -->|详情抽屉| ORDER_DRAWER["工单详情抽屉(540px)\n工单/设备/故障信息\n处理记录 · 板卡更换记录\n授权处理 · 关闭工单"]
REPAIR -->|独立详情页| ORDER_PAGE["工单详情页(/repair/:orderId)\n工单/设备/故障信息\n处理记录 · 板卡更换记录\n授权处理\n关闭工单 · 申请报废"]
REPAIR -->|维修统计| REPAIR_STATS["维修统计页\n时间范围筛选(5个Tab)\n4项统计指标\n故障类型分布(条形图)\n维修趋势(折线图)"]
end
%% ===== 阶段七:报废回收 =====
subgraph S7["阶段七:报废回收"]
SCRAP["报废管理\n报废设备列表 · 审批流程"]
SCRAP -->|关联工单| ORDER_DETAIL
SCRAP -->|回收入库| DEV_REG
SCRAP["报废管理\n审批流程(5步)\n4项统计 · 筛选\n报废设备列表"]
SCRAP -->|详情抽屉| SCRAP_DETAIL["报废详情抽屉(520px)\n设备信息 · 残值评估\n审批信息 · 可回收物料\n审批记录时间线"]
SCRAP -->|审批抽屉| SCRAP_APPROVE["报废审批抽屉(480px)\n⚠不可逆警告\n设备摘要 · 可回收物料\n审批意见\n驳回/审批通过"]
SCRAP -->|回收入库抽屉| SCRAP_RECYCLE["物料回收入库抽屉(480px)\n物料检测勾选\n回收备注\n确认回收入库"]
end
%% ===== 首页导航 =====
HOME -->|待校准设备| CALIB
HOME -->|待处理工单| REPAIR
%% ===== 首页导航(8项指标跳转) =====
HOME -->|设备总数/装配中/已激活| DEV_LIST
HOME -->|有新版本/可升级| FW_LIB
HOME -->|维修中| REPAIR
HOME -->|报废| SCRAP
HOME -->|授权即将到期| LIC_MGR
HOME -->|固件待升级| FW_LIB
HOME -->|设备总数| DEV_LIST
HOME -->|校准即将到期| CALIB
HOME --> MODEL
%% ===== 型号管理跨模块关联 =====
MODEL -->|授权按钮| LIC_MGR
MODEL -->|配置按钮| CFG_MGR
MODEL -->|固件按钮| FW_LIB
MODEL -->|Checklist模板| DEV_REG
%% ===== 板卡管理关联 =====
BOARD_MGR -->|固件按钮| FW_LIB
BOARD_MGR -->|详情→服役记录| DEV_DETAIL
BOARD_MGR -->|详情→维修记录| REPAIR
%% ===== 跨阶段数据流 =====
DEV_REG -->|型号必须匹配| MODEL
DEV_DETAIL -->|查看校准| CALIB
DEV_DETAIL -->|查看维修历史| REPAIR
PROCESS -->|申请报废| SCRAP
ORDER_DETAIL -->|更换采集板需重新校准| CALIB
FW_LIB -->|关联型号固件| MODEL
ORDER_PAGE -->|申请报废| SCRAP
ORDER_DRAWER -->|更换采集板需重新校准| CALIB
SCRAP -->|关联来源工单| ORDER_PAGE
SCRAP_RECYCLE -->|回收入库| DEV_REG
%% ===== 支撑模块 =====
subgraph SUPPORT["支撑模块Header菜单"]
DATA_STATS["数据统计"]
OPS_RPT["运营报告"]
USER_MGR["用户管理"]
ROLE_MGR["角色权限"]
SYS_LOG["操作日志"]
SYS_SET["系统设置"]
DATA_STATS["数据报表(占位)"]
OPS_RPT["运营报告(占位)"]
USER_MGR["用户管理(占位)"]
ROLE_MGR["角色权限(占位)"]
SYS_LOG["操作日志(占位)"]
SYS_SET["系统设置(占位)"]
end
%% 样式
@ -117,20 +127,23 @@ style APP fill:#F6FFED,stroke:#52C41A,stroke-dasharray: 5 5
## 页面清单
| 模块 | 页面 | 路由 | 交互方式 |
|------|------|------|----------|
| 首页 | Dashboard | `/` | 卡片点击跳转 |
| 设备 | 设备列表 | `/devices` | 筛选/详情跳转 |
| 设备 | 设备详情 | `/devices/:id` | 页面 |
| 设备 | 设备登记 | `/registration` | 弹窗(导入/拍照) |
| 设备 | 型号管理 | `/models` | 抽屉(新增型号/Checklist模板) |
| 授权 | 授权管理 | `/licenses` | 抽屉(选择授权项生成) |
| 固件 | 固件库 | `/firmware` | 弹窗(上传固件) Tab分类 |
| 配置 | 配置管理 | `/config-files` | 抽屉(新建配置) |
| 配置 | 参数配置 | `/config-files/:id` | 页面 |
| 校准 | 校准记录 | `/calibration` | 页面 |
| 维修 | 维修工单 | `/repair` | 抽屉(新建/处理/详情) |
| 维修 | 维修统计 | `/repair/stats` | 页面 |
| 维修 | 工单详情 | `/repair/:orderId` | 页面 |
| 报废 | 报废管理 | `/scrap` | 页面 |
| 系统 | 用户/角色/日志/设置 | Header菜单 | 页面(占位) |
| 模块 | 页面 | 路由 | 交互方式 | 组件文件 |
|------|------|------|----------|----------|
| 首页 | Dashboard | `/` | 卡片点击跳转 | `Dashboard.vue` |
| 设备 | 设备列表 | `/devices` | 5项筛选 · 卡片式列表 | `DeviceList.vue` |
| 设备 | 设备详情 | `/devices/:id` | 独立页面(6个信息卡片) | `DeviceDetail.vue` |
| 设备 | 设备登记 | `/registration` | 弹窗(Excel导入/拍照上传) | `DeviceRegistration.vue` |
| 设备 | 设备型号管理 | `/models` | 抽屉(新增型号/新增Checklist模板) | `DeviceModelManagement.vue` |
| 设备 | 板卡型号管理 | `/boards` | Tab筛选 · 抽屉(板卡详情) | `BoardManagement.vue` |
| 授权 | 授权管理 | `/licenses` | 抽屉(选择授权项 640px) | `LicenseManagement.vue` |
| 固件 | 固件库 | `/firmware` | 弹窗(上传固件 Element Plus Dialog) | `FirmwareLibrary.vue` |
| 配置 | 配置文件管理 | `/config-files` | 抽屉(新建配置 520px) | `ConfigFileManagement.vue` |
| 配置 | 参数配置 | `/config-files/:configId` | 独立页面(新建/编辑两种模式) | `ParameterConfiguration.vue` |
| 校准 | 校准管理 | `/calibration` | 表格列表 · 导入 | `CalibrationRecords.vue` |
| 维修 | 维修工单 | `/repair` | 抽屉(新建520px/处理520px/详情540px) | `RepairOrders.vue` |
| 维修 | 维修统计 | `/repair/stats` | 独立页面(统计+图表) | `RepairStats.vue` |
| 维修 | 工单详情 | `/repair/:orderId` | 独立页面(含底部操作栏) | `RepairOrderDetail.vue` |
| 报废 | 报废管理 | `/scrap` | 抽屉(详情520px/审批480px/回收480px) | `ScrapManagement.vue` |
| 系统 | 数据报表 | `/reports` | 占位页面 | `PlaceholderPage.vue` |
| 系统 | 操作日志 | `/logs` | 占位页面 | `PlaceholderPage.vue` |
| 系统 | 系统设置 | `/settings` | 占位页面 | `PlaceholderPage.vue` |

959
docs/产品原型文档.md Normal file
View File

@ -0,0 +1,959 @@
# 地空业务支撑平台 —— 生产管理子系统 产品原型文档
> 版本v1.0
> 更新日期2026-04-08
> 技术栈Vue 3 + Vue Router + Element Plus + TailwindCSS + Lucide Icons
> 主色调:`#4a7c59`(绿色系)
---
## 一、系统概述
本系统为"地空业务支撑平台"的生产管理子系统,面向地球物理仪器(高密度电法仪等)的全生命周期管理,涵盖:
- 设备型号管理(核心枢纽)
- 板卡型号管理
- 设备登记与装配
- 授权文件管理
- 配置文件管理
- 固件库管理
- 采集板校准
- 维修工单
- 报废回收
### 1.1 全局布局
| 区域 | 说明 |
|------|------|
| 左侧边栏200px | Logo + 分组导航菜单,固定不滚动 |
| 顶部 Header56px | 系统标题 + 数据统计/运营报告入口 + 用户菜单(用户管理/角色权限/操作日志/系统设置/退出) |
| 主内容区 | 背景色 `#F0F2F5`,内容区可滚动 |
### 1.2 导航菜单结构
| 分组 | 菜单项 | 路由 | 图标 |
|------|--------|------|------|
| — | 首页 | `/` | — |
| 设备 | 设备列表 | `/devices` | Monitor |
| 设备 | 设备型号管理 | `/models` | Settings2 |
| 设备 | 板卡型号管理 | `/boards` | Cpu |
| 授权 | 授权管理 | `/licenses` | Key |
| 配置 | 配置管理 | `/config-files` | FileCode |
| 校准 | 校准记录 | `/calibration` | Gauge |
| 维修 | 维修工单 | `/repair` | Wrench |
| 维修 | 报废回收 | `/scrap` | Recycle |
---
## 二、页面详细说明
---
### 2.1 首页 Dashboard
**路由**`/`
#### 功能描述
系统首页,展示全局数据概览和待处理任务快捷入口。
#### 统计卡片8个可点击跳转
| 指标 | 示例值 | 趋势 | 跳转 |
|------|--------|------|------|
| 设备总数 | 5,234 | +5.2% | `/devices` |
| 装配中 | 4,856 | +2.8% | `/devices` |
| 已激活 | 4,912 | +1.5% | `/devices` |
| 有新版本 | 156 | — | `/firmware` |
| 维修中 | 23 | -12.3% | `/repair` |
| 报废 | 56 | — | `/scrap` |
| 授权即将到期 | 45 | — | `/licenses` |
| 可升级 | 8 | — | `/firmware` |
#### 设备状态分布(横向条形图)
| 状态 | 颜色 |
|------|------|
| 已装配 | `#52C41A` |
| 已出厂 | `#FF4D4F` |
| 已激活 | `#FAAD14` |
| 报废 | `#8C8C8C` |
#### 待处理任务2列布局每组显示2条+查看全部链接)
| 任务组 | 跳转 |
|--------|------|
| 校准即将到期 | `/calibration` |
| 维修工单 | `/repair` |
| 固件升级通知 | `/firmware` |
| 授权即将到期 | `/licenses` |
---
### 2.2 设备型号管理
**路由**`/models`
#### 功能描述
管理设备型号GD-30/GD-20/GD-10 Supreme是平台核心枢纽每个型号关联授权项、配置文件。
#### 页面区块
**1. 信息提示横幅**
- 绿色背景提示:型号管理是平台核心枢纽
**2. 型号列表(表格)**
| 列名 | 说明 |
|------|------|
| 型号名称 | 如 GD-30 Supreme |
| 编码 | 如 GD30-2024 |
| 状态 | 在产(绿色标签)/ 停产(黄色标签) |
| 操作 | 编辑 / 授权(跳转`/licenses`/ 配置(跳转`/config-files` |
- 右上角按钮:「新增设备型号」
**3. 装配 Checklist 模板**
- Tab 切换不同型号GD30 / GD20 / GD10
- 表格列:序号(可拖拽排序)、项目名称(点击可编辑)
- GD30 示例有 22 项检查项主板SN扫码绑定、采集板SN录入、GPS/北斗检测、电池安装、IP66防护等
- 右上角按钮:「新增模版」
#### 抽屉新增设备型号480px宽
| 字段 | 类型 | 必填 |
|------|------|------|
| 型号名称 | 文本输入 | 是 |
| 型号编码 | 文本输入 | 是 |
| 状态 | 单选(在产/停产) | — |
#### 抽屉:新增 Checklist 模版480px宽
| 字段 | 类型 | 必填 |
|------|------|------|
| 设备型号 | 下拉选择 | 是 |
| 检查项列表 | 动态列表(可增删) | — |
---
### 2.3 板卡型号管理
**路由**`/boards`
#### 功能描述
管理板卡型号信息,包括主协板、采集板、发射板、升压板。
#### 页面区块
**1. Tab 筛选**:全部 / 主协板 / 采集板 / 发射板 / 升压板
**2. 板卡列表(表格)**
| 列名 | 说明 |
|------|------|
| 类型 | 主协板/采集板/发射板/升压板 |
| 型号 | 如 MB-V2.3、RX-V2.2 |
| 最新固件版本 | 如 v2.3.5 |
| 操作 | 详情 / 固件(跳转`/firmware` |
- 右上角按钮:「导出」「新增板卡」
#### 抽屉板卡详情560px宽
包含以下信息区块:
- 基本信息:板卡类型、型号、当前固件、生产日期
- 固件升级记录:时间线(日期、版本变更、升级方式、操作人)
- 校准历史:日期、结果、到期日、校准人
- 服役记录关联设备SN可点击跳转、服役时间段、状态在役/已拆卸)
- 维修记录:关联工单号(可点击跳转)、日期、操作、操作人
---
### 2.4 设备列表
**路由**`/devices`
#### 功能描述
展示所有设备,支持筛选和搜索。
#### 筛选条件
| 筛选项 | 类型 |
|--------|------|
| 设备型号 | 下拉(全部/GD30/GT20/GM10 |
| 设备状态 | 下拉(全部/已激活/已出厂/装配中) |
| 生产日期 | 日期选择器 |
| 产品型号 | 下拉 |
| 搜索 | 按钮 |
#### 设备卡片列表(非表格,卡片式)
每张卡片展示:
| 字段 | 说明 |
|------|------|
| 设备SN号 | 如 GD30-20240308-001 |
| 型号 | 如 GD30 高密度电法仪 |
| 状态 | 已激活(绿)/ 已出厂(橙)/ 装配中(绿底) |
| 主机版本 | 如 v2.3.5 |
| 生产日期 | 如 2024-03-08 14:30 |
卡片底部操作:
- 详情(跳转 `/devices/:sn`
- 下线(仅已激活设备显示,红色)
- 右上角按钮:「导出」「登记设备」(跳转`/registration`
- 分页:显示 X-Y / 共 N 台
---
### 2.5 设备详情
**路由**`/devices/:id`
#### 功能描述
展示单台设备的完整信息,包含基本信息、授权、装配记录、子设备、固件、维修历史。
#### 页面区块
**1. 页面头部**
- 返回按钮 + 设备SN号 + 状态标签
**2. 基本信息卡片**
| 字段 | 示例 |
|------|------|
| 设备型号 | GD30 高密度电法仪 |
| 主机SN号 | GD30-2025-000001 |
| 设备状态 | 已激活 |
| 生产日期 | 2025-01-15 |
| 出厂日期 | 2025-02-01 |
| 激活日期 | 2025-02-10 |
| 装配人员 | 张工程师 |
| 测试人员 | 李工程师 |
| 客户名称 | 北京地质研究院 |
**3. 授权信息卡片**
| 字段 | 示例 |
|------|------|
| 授权ID | LIC-2025-0001 |
| 授权状态 | 已激活 |
| 授权类型 | 正式授权 |
| 生效日期 | 2025-02-10 |
| 到期日期 | 2026-02-10 |
| 剩余天数 | 317天 |
- 授权功能模块标签列表1D SP, 2D SP, 3D SP, 1D VES, 2D ERT, 3D ERT, 1D IP, 2D IP, 3D IP
- 授权文件下载链接
**4. 装配记录卡片**
| 字段 | 示例 |
|------|------|
| 装配工单 | ASM-2025-000001 |
| 装配人员 | 张工程师 |
| 装配日期 | 2025-01-15 |
| 测试人员 | 李工程师 |
| 测试结果 | 通过 |
| Checklist完成 | 22/22 项 |
- Checklist 摘要2列网格勾选图标 + 项目名称)
**5. 子设备列表(表格)**
| 列名 | 说明 |
|------|------|
| 板卡类型 | 主板/采集板/发射板/测控板/升压板 |
| SN号 | 绿色可点击 |
| 硬件版本 | 如 A1 |
| 固件版本 | 如 v2.3.5 |
| 校准状态 | 已校准/无需校准 |
| 状态 | 正常 |
**6. 固件信息卡片**
| 字段 | 示例 |
|------|------|
| 当前固件版本 | v2.3.5 |
| 最后更新时间 | 2024-03-01 10:30 |
| 更新方式 | 远程OTA |
| 配置文件版本 | v1.2.0 |
| 配置同步时间 | 2024-03-01 10:35 |
**7. 维修历史(时间线)**
- 每条记录:日期、类型(固件升级/主板更换/常规保养)、操作人、描述
- 右上角「查看全部」链接跳转 `/repair`
---
### 2.6 设备登记
**路由**`/registration`
#### 功能描述
登记新设备信息包含装机信息填写、BOM清单管理、装配Checklist执行。
#### 页面区块
**1. 装机信息卡片(表单)**
| 字段 | 类型 | 必填 | 示例 |
|------|------|------|------|
| 设备型号 | 下拉选择GD-30/GD-20/GD-10 Supreme | 是 | GD-30 Supreme |
| 主机SN号 | 文本输入 | 是 | GD30-20240308-001 |
| 主板SN号 | 文本输入 | 是 | MB20240308001 |
| 装机测试状态 | 下拉(测试通过/测试不通过) | — | 测试通过 |
| 生产日期 | 日期选择器 | — | 2024-03-08 |
| 登记人 | 文本输入(只读) | — | 张工 |
**2. 型号匹配提示横幅**
- 成功(绿色):显示匹配到的授权文件、配置文件、固件版本
- 警告(黄色):未匹配到型号关联信息,提示先在型号管理中配置
**3. 装机清单 BOM表格**
| 列名 | 说明 |
|------|------|
| 物料编码 | 如 MB-2024-001 |
| 物料名称 | 主协板/采集板/测控板/发射板/升压板/外壳机箱 |
| SN号 | 板卡SN号 |
| 型号 | 如 MB-V2.3 |
| 校准状态 | 已校准/无需校准 |
| 数量 | 数字 |
| 操作 | 编辑 / 删除 |
- 右上角按钮「导入」打开Excel导入弹窗
**4. 装配 Checklist列表**
- 进度显示:完成 X/Y
- 每项包含:勾选框、序号圆圈、项目名称、版本校验提示(红色警告)、拍照上传按钮
- 已完成项:绿色背景 + 已上传照片数量
- 未完成项:灰色背景 + 拍照上传按钮
**5. 底部操作栏sticky**
- 取消 / 更新 / 提交
#### 弹窗导入BOM清单
| 区域 | 说明 |
|------|------|
| 第一步 | 下载导入模板BOM导入模板.xlsx |
| 第二步 | 上传Excel文件支持 .xlsx/.xls/.csv拖拽或点击上传 |
| 提示 | 黄色提示确保格式与模板一致物料编码和SN号为必填 |
#### 弹窗:上传照片
| 区域 | 说明 |
|------|------|
| 照片网格 | 已上传照片缩略图(可删除)+ 添加照片按钮 |
| 装配记录信息 | 文本域,输入装配记录 |
| 操作 | 取消 / 确认上传(显示照片数量) |
---
### 2.7 授权管理
**路由**`/licenses`
#### 功能描述
管理设备授权文件,按设备型号管理授权模块配置。
#### 页面区块
**1. 信息提示横幅(青色)**
- 说明授权文件按设备型号管理设备在APP激活时自动下载对应型号的授权文件
**2. 筛选条件**
| 筛选项 | 类型 |
|--------|------|
| 设备型号 | 下拉(全部/GD-10/GD-20/GD-30 |
| 状态 | 下拉(全部/已发布/草稿/已停用) |
| 查询 | 按钮 |
**3. 授权列表(表格)**
| 列名 | 说明 |
|------|------|
| 适配型号 | 如 GD-10 Supreme |
| 授权模块 | 如 1D SP, 2D SP, 1D VES... |
| 有效期 | 永久 / 1年 / 2年 |
| 创建日期 | 日期 |
| 状态 | 已发布(绿)/ 草稿(黄)/ 已停用(灰) |
| 操作 | 详情 / 下载 / 发布(草稿时)/ 停用(已发布时) |
- 右上角按钮:「导出」「选择授权项」
- 分页
#### 抽屉选择授权项640px宽
| 字段 | 类型 | 说明 |
|------|------|------|
| 设备型号 | 下拉选择 | 必填,切换型号自动预选对应授权项 |
| 授权有效期 | 下拉(永久/1年/2年/自定义) | — |
| 到期日期 | 日期选择器 | 仅"自定义"时显示 |
**功能授权项列表(表格+勾选)**
| 授权项名称 | GD-10 | GD-20 | GD-30 |
|------------|-------|-------|-------|
| 1D SP | ✓ | ✓ | ✓ |
| 2D SP | ✓ | ✓ | ✓ |
| 3D SP | ✗ | ✓ | ✓ |
| 1D VES | ✓ | ✓ | ✓ |
| 2D ERT | ✓ | ✓ | ✓ |
| 3D ERT | ✗ | ✓ | ✓ |
| 1D IP | ✓ | ✓ | ✓ |
| 2D IP | ✓ | ✓ | ✓ |
| 3D IP | ✗ | ✓ | ✓ |
| 跨孔Cross-Hole | ✗ | ✓ | ✓ |
| 水上Marine | ✗ | ✓ | ✓ |
- 顶部显示:已选 X / Y 项
- 操作:全选 / 清空
- 底部:取消 / 保存
---
### 2.8 配置文件管理
**路由**`/config-files`
#### 功能描述
管理设备型号配置文件,包含发射参数、采集参数、网络参数等。
#### 页面区块
**1. 信息提示横幅(绿色)**
- 说明配置文件按设备型号绑定适配
**2. 筛选条件**
| 筛选项 | 类型 |
|--------|------|
| 适配型号 | 下拉 |
| 配置版本 | 下拉 |
| 关键字 | 文本输入 |
| 查询 | 按钮 |
**3. 配置文件列表(表格)**
| 列名 | 说明 |
|------|------|
| 配置文件 | 如 CFG-GD30-v1.2.0(绿色) |
| 适配型号 | 如 GD-30 Supreme |
| 版本 | 如 v1.2.0 |
| 创建时间 | 日期时间 |
| 状态 | 生效(绿)/ 已停用(灰) |
| 操作 | 详情 / 编辑 / 下载 / 删除 |
- 右上角按钮:「新建配置」
- 分页
#### 抽屉新建配置文件520px宽
分为以下区块:
**基本信息**
| 字段 | 类型 | 必填 |
|------|------|------|
| 适配型号 | 下拉选择 (GD-10 Supreme/GD-20 Supreme/GD-30 Supreme) | 是 |
| 配置版本 | 文本输入 | 是 |
**发射参数**
| 字段 | 类型 |
|------|------|
| 最大发射电压 | 下拉500V/800V/1000V/1200V/1500V |
| 最大发射电流 | 下拉2A/5A/8A/10A/15A |
| 占空比 | 下拉 50%50%、100%|
| 脉宽宽度 | 下拉0.25s/0.5s/1s/2s/4s/8s/16s/32s/64s /(0.25s/0.5s/1s/2s/4s/8s)|
| 迭代次数 | 下拉(1~256) |
**采集参数**
| 字段 | 类型 |
|------|------|
| 支持通道数 | 数字输入(1/6/12) |
| 采样率 | 下拉(50Hz/60Hz)/(50Hz/60Hz/100Hz/1000Hz) |
| 电压测量量程 | 下拉(±2.5V)/±2.5V/±80V /±80V/±600V |
| 全波形采集 | 下拉(支持/不支持) |
**网络参数**
| 字段 | 类型 |
|------|------|
| WiFi SSID 前缀 | 文本输入 |
---
### 2.9 参数配置(配置详情)
**路由**`/config-files/:configId`
#### 功能描述
配置设备型号的详细参数,支持新建和编辑两种模式。
#### 页面区块
**1. 型号信息卡片**
- 新建模式:适配型号(下拉)、配置文件版本(输入)、状态(待生成)
- 编辑模式:适配型号(只读)、版本(只读)、最后更新时间
**2. 发射参数卡片**
| 字段 | 类型 |
|------|------|
| 最大发射电压 | 下拉500V~1500V |
| 最大发射电流 | 下拉2A~10A |
| 发射脉宽 | 多选标签0.25s~64s共9项 |
| 发射波形 | 多选标签0+0-、+0-0、+- |
| 支持全波形测量 | 复选框 |
| 占空比支持 | 复选框50%/100% |
**3. 采集参数卡片**
| 字段 | 类型 |
|------|------|
| 电压测量范围 | 多选标签±2.5V/±80V/±600V |
| 采样率 | 多选标签50Hz/60Hz/100Hz/1000Hz |
| 支持通道数 | 数字输入 |
| 迭代次数范围 | 范围输入(最小~最大) |
**4. 保护参数卡片**
| 字段 | 类型 |
|------|------|
| 过压保护 | 复选框 + 阈值输入V |
| 过流保护 | 复选框 + 阈值输入A |
| 短路保护 | 复选框 |
| 高温保护 | 复选框 + 阈值输入°C |
**5. 网络参数卡片**
| 字段 | 类型 |
|------|------|
| WiFi SSID | 文本输入 |
**6. 底部操作栏sticky**
- 新建模式:取消 / 预览配置 / 确认生成配置文件
- 编辑模式:取消 / 重置默认 / 预览配置 / 下发配置
---
### 2.10 固件库
**路由**`/firmware`
#### 功能描述
管理固件版本,支持上传、查看详情、下载。
#### 页面区块
**固件版本列表(卡片式)**
每个版本卡片展示:
| 字段 | 说明 |
|------|------|
| 版本号 | 如 v2.3.0 |
| 发布日期 | 日期 |
| 状态 | 已发布 / 草稿 |
| 文件大小 | 如 12.5MB |
| 下载次数 | 如 1,234次 |
展开详情后额外显示:
| 字段 | 说明 |
|------|------|
| MD5 / SHA256 | 校验值 |
| 升级类型 | 可选 / 强制 |
| 数字签名 | 已签名 |
| 发布说明 | 列表形式 |
操作按钮:下载 / 编辑 / 撤回发布 / 查看详情
- 右上角按钮:「上传固件」
#### 弹窗上传固件Element Plus Dialog550px宽
| 字段 | 类型 | 必填 |
|------|------|------|
| 固件版本 | 文本输入 | 是 |
| 硬件版本范围 | 文本输入 | 是 |
| 升级类型 | 下拉(可选/强制) | 是 |
| 固件类型 | 下拉(主协板/采集板/发射板/主机固件/计算单元固件) | 是 |
| 数字签名 | 复选框 | — |
| 固件包 | ZIP文件上传拖拽 | 是 |
| 发布说明 | 多行文本(每行一条) | — |
---
### 2.11 校准管理
**路由**`/calibration`
#### 功能描述
管理采集板校准数据。仅针对采集板,其他板卡无需校准。
#### 页面区块
**1. 信息提示横幅(紫色)**
- 说明校准仅针对采集板
**2. 筛选条件**
| 筛选项 | 类型 |
|--------|------|
| 采集板SN号 | 文本输入 |
| 校准状态 | 下拉(全部/合格/不合格/待校准) |
| 校准人员 | 下拉 |
| 查询 | 按钮 |
**3. 校准记录列表(表格)**
| 列名 | 说明 |
|------|------|
| 采集板SN号 | 如 RX20240308001 |
| 校准日期 | 日期 |
| 到期日期 | 日期 |
| 校准人员 | 如 王工程师 |
| 状态 | 合格(绿)/ 不合格(红)/ 待校准(黄) |
| 操作 | 详情 / 校准文件 |
- 右上角按钮:「导入」
- 分页
---
### 2.12 维修工单
**路由**`/repair`
#### 功能描述
管理维修工单的全生命周期,包括新建、处理、查看详情。
#### 页面区块
**1. 筛选条件**
| 筛选项 | 类型 |
|--------|------|
| 状态 | 下拉(全部/处理中/已处理/待处理) |
| 优先级 | 下拉(全部/高/中/低) |
| 维修人员 | 下拉 |
| 创建日期 | 日期范围(开始~结束) |
| SN | 文本输入 |
| 搜索/重置 | 按钮 |
**2. 维修工单列表(卡片式)**
每条工单展示:
| 字段 | 说明 |
|------|------|
| 工单号 | 如 WO-2024-0001 |
| 设备SN | 如 GD30-xxxx |
| 故障类型 | 板卡故障/固件异常/通信异常 |
| 状态 | 处理中(绿底)/ 已处理(绿标签)/ 待处理灰标签带emoji图标 |
| 优先级 | 高(红)/ 中(橙)/ 低(蓝) |
操作按钮:详情 / 处理(处理中和待处理时显示)
- 右上角按钮:「新建工单」「维修统计」(跳转`/repair/stats`
- 分页
#### 抽屉新建维修工单520px宽
分为三个区块:
**设备信息**
| 字段 | 类型 |
|------|------|
| 设备SN | 下拉选择 + 查找设备按钮 |
| 设备信息展示 | 只读(型号、固件、硬件版本、客户) |
**故障信息**
| 字段 | 类型 |
|------|------|
| 故障类型 | 单选(板卡故障/固件异常/通信故障/其他问题) |
| 故障描述 | 多行文本 |
| 故障现象 | 多行文本 |
**工单信息**
| 字段 | 类型 |
|------|------|
| 优先级 | 单选(低/中/高,带颜色) |
| 维修人员 | 下拉选择 |
| 预计修复时间 | 日期选择器 |
| 备注 | 多行文本 |
底部:取消 / 创建工单
#### 抽屉处理工单520px宽
分为以下区块:
**处理操作**
- 单选:更换板卡 / 固件修复 / 参数重置 / 其他处理
**板卡更换(条件显示,选择"更换板卡"时)**
| 字段 | 类型 |
|------|------|
| 原板卡SN | 文本输入 |
| 新板卡SN | 文本输入 |
**授权处理**
| 字段 | 类型 |
|------|------|
| 重新生成授权文件 | 复选框(默认勾选) |
| 推送适配固件 | 复选框 |
**处理备注**
- 多行文本
**报废处理(红色警告区域)**
- 警告文字:报废操作不可逆
- 报废原因:下拉选择(主板损坏无法修复/多个核心部件损坏/维修成本超过设备价值/设备老化严重)
- 申请报废按钮(跳转`/scrap`
底部:取消 / 提交处理
#### 抽屉工单详情540px宽
包含以下信息区块:
- 工单信息:工单号、状态、优先级、创建时间、维修人员、预计修复
- 设备信息设备SN、型号、固件、硬件版本、客户
- 故障信息:故障类型、描述、现象
- 处理记录:时间线列表 + 添加记录按钮
- 板卡更换记录:原板卡/新板卡信息、更换时间、更换人员
- 授权处理:复选框(重新生成授权文件/推送适配固件)+ 处理授权按钮
底部:取消 / 关闭工单
---
### 2.13 维修工单详情(独立页面)
**路由**`/repair/:orderId`
#### 功能描述
维修工单的独立详情页面,与抽屉详情内容一致但以独立页面形式展示。
#### 页面区块(白色卡片堆叠)
1. 工单信息卡片
2. 设备信息卡片
3. 故障信息卡片
4. 处理记录卡片(时间线 + 添加记录按钮)
5. 板卡更换记录卡片
6. 授权处理卡片(复选框 + 处理授权按钮)
**底部操作栏sticky**
- 取消 / 关闭工单 / 申请报废(红色,跳转`/scrap`
---
### 2.14 维修统计
**路由**`/repair/stats`
#### 功能描述
展示维修数据统计和趋势分析。
#### 页面区块
**1. 时间范围筛选**
- 下拉选择 + Tab 切换(本月/上月/本季度/本年度/自定义)
**2. 统计概览4列卡片**
| 指标 | 颜色 |
|------|------|
| 总工单数 | 黑色 |
| 处理中 | 绿色 `#4a7c59` |
| 已完成 | 绿色 `#52C41A` |
| 待处理 | 红色 `#FF4D4F` |
**3. 图表区域2列**
| 图表 | 说明 |
|------|------|
| 故障类型分布 | 横向进度条板卡故障65.5%、固件异常22.3%、通信故障6.3%、其他5.9% |
| 维修趋势 | 占位图表1月-6月趋势 |
- 右上角按钮:「导出报告」
---
### 2.15 报废管理
**路由**`/scrap`
#### 功能描述
管理报废设备审批与物料回收,包含完整的审批流程。
#### 页面区块
**1. 报废审批流程(步骤条)**
| 步骤 | 描述 |
|------|------|
| 1. 申请报废 | 维修工单发起 |
| 2. 主管审批 | 审核报废原因 |
| 3. 物料检测 | 检测可回收物料 |
| 4. 回收入库 | 物料回收登记 |
| 5. 报废完成 | 设备注销 |
**2. 统计概览4列卡片**
| 指标 | 颜色 |
|------|------|
| 报废总数 | 黑色 |
| 待审批 | 黄色 |
| 已审批待回收 | 绿色 |
| 已回收 | 绿色 |
**3. 筛选条件**
| 筛选项 | 类型 |
|--------|------|
| 设备SN号 | 文本输入 |
| 状态 | 下拉(全部/待审批/审批中/已审批/已驳回/回收中/已回收) |
| 报废日期 | 日期选择器 |
| 查询 | 按钮 |
**4. 报废设备列表(表格)**
| 列名 | 说明 |
|------|------|
| 设备SN | 如 GD30-2023-001234 |
| 型号 | 如 GD-30 Supreme |
| 报废原因 | 如 主板损坏无法修复 |
| 申请人 | 如 李工 |
| 状态 | 待审批(黄)/审批中(绿底)/已审批(绿)/已驳回(红)/回收中/已回收 |
| 来源工单 | 可点击跳转到维修工单详情 |
| 操作 | 详情 / 审批(待审批时)/ 回收入库(已审批时)/ 重新申请(已驳回时) |
- 右上角按钮:「导出」
#### 抽屉报废详情520px宽
包含以下信息区块:
- 设备信息SN、型号、报废日期、报废原因、残值评估
- 审批信息:申请人、审批人、来源工单
- 可回收物料:标签列表(如 采集板 AC20240308002、测控板 CT20240308003
- 审批记录:时间线(提交申请 → 审批通过/驳回 → 物料回收完成)
底部:关闭 / 去审批(待审批时)/ 回收入库(已审批时)
#### 抽屉报废审批480px宽
- 红色警告横幅:报废操作不可逆
- 报废设备摘要SN、型号、报废原因、残值评估、申请人
- 可回收物料列表
- 审批意见:多行文本
底部:取消 / 驳回(红色)/ 审批通过
#### 抽屉物料回收入库480px宽
- 报废设备摘要
- 物料检测与回收:勾选列表(每项可勾选标记"已检测",绿色高亮)
- 回收备注:多行文本
底部:取消 / 确认回收入库(绿色,跳转`/registration`
---
## 三、占位页面
以下页面目前为占位状态,显示"此页面正在开发中..."
| 页面 | 路由 | 入口 |
|------|------|------|
| 数据报表 | `/reports` | Header 图标 |
| 操作日志 | `/logs` | Header 用户菜单 |
| 系统设置 | `/settings` | Header 用户菜单 |
| 用户管理 | `/users` | Header 用户菜单 |
| 角色权限 | `/roles` | Header 用户菜单 |
---
## 四、通用交互规范
### 4.1 状态标签颜色规范
| 状态类型 | 背景色 | 文字色 | 边框色 |
|----------|--------|--------|--------|
| 成功/已激活/已发布/合格/已审批/已回收 | `#F6FFED` | `#52C41A` | `#B7EB8F` |
| 进行中/在产/处理中 | `#eef5f0` | `#4a7c59` | `#a3c4ad` |
| 警告/草稿/待审批/待校准 | `#FFFBE6` | `#FAAD14` | `#FFE58F` |
| 错误/已驳回/高优先级 | `#FFF1F0` | `#FF4D4F` | `#FFCCC7` |
| 已出厂/中优先级 | `#FFF7E6` | `#FA8C16` | `#FFD591` |
| 低优先级 | `#F0F5FF` | `#597EF7` | `#ADC6FF` |
| 停用/灰色 | `#FAFAFA` | `rgba(0,0,0,0.45)` | `#D9D9D9` |
### 4.2 交互模式
| 模式 | 宽度 | 使用场景 |
|------|------|----------|
| 抽屉Drawer | 480px~640px | 新建/编辑/详情(从右侧滑入,背景遮罩) |
| 弹窗Dialog | 520px~560px | 导入文件/上传照片 |
| 独立页面 | 全宽 | 设备详情/参数配置/维修工单详情/维修统计 |
### 4.3 表格/列表规范
- 表头背景:`#FAFAFA`
- 行分隔线:`#F0F0F0`
- 分页组件:左侧显示"显示 X-Y / 共 N 条",右侧页码按钮
- 操作按钮:绿色文字链接 `#4a7c59`,危险操作红色 `#FF4D4F`
### 4.4 表单规范
- 必填标记:红色星号 `*`
- 输入框边框:`#D9D9D9`
- 主按钮:`#4a7c59` 绿色背景白色文字
- 次按钮:`#D9D9D9` 边框灰色文字
- 危险按钮:`#FF4D4F` 边框红色文字
- 禁用状态:`#D9D9D9` 背景
---
## 五、页面路由总览
| 路由 | 页面 | 交互方式 | 文件 |
|------|------|----------|------|
| `/` | 首页 Dashboard | 卡片点击跳转 | `Dashboard.vue` |
| `/models` | 设备型号管理 | 抽屉(新增型号/Checklist模板 | `DeviceModelManagement.vue` |
| `/boards` | 板卡型号管理 | Tab筛选 + 抽屉(详情) | `BoardManagement.vue` |
| `/devices` | 设备列表 | 筛选 + 卡片列表 | `DeviceList.vue` |
| `/devices/:id` | 设备详情 | 独立页面 | `DeviceDetail.vue` |
| `/registration` | 设备登记 | 弹窗(导入/拍照) | `DeviceRegistration.vue` |
| `/licenses` | 授权管理 | 抽屉(选择授权项) | `LicenseManagement.vue` |
| `/firmware` | 固件库 | 弹窗(上传固件) | `FirmwareLibrary.vue` |
| `/config-files` | 配置文件管理 | 抽屉(新建配置) | `ConfigFileManagement.vue` |
| `/config-files/:configId` | 参数配置 | 独立页面 | `ParameterConfiguration.vue` |
| `/calibration` | 校准管理 | 表格列表 | `CalibrationRecords.vue` |
| `/repair` | 维修工单 | 抽屉(新建/处理/详情) | `RepairOrders.vue` |
| `/repair/stats` | 维修统计 | 独立页面 | `RepairStats.vue` |
| `/repair/:orderId` | 工单详情 | 独立页面 | `RepairOrderDetail.vue` |
| `/scrap` | 报废管理 | 抽屉(详情/审批/回收) | `ScrapManagement.vue` |
---
## 六、跨模块数据流关系
```
设备型号管理 ──→ 授权管理(按设备型号绑定授权项)
设备型号管理 ──→ 配置管理(按设备型号绑定配置文件)
板卡型号管理 ──→ 固件库(按板卡型号关联固件)
设备型号管理 ──→ 设备登记Checklist模板按型号配置
设备登记 ──→ 设备列表(登记完成后出现在列表中)
设备列表 ──→ 设备详情(点击查看)
设备详情 ──→ 校准记录(查看校准信息)
设备详情 ──→ 维修工单(查看维修历史)
维修工单 ──→ 报废管理(申请报废)
维修工单 ──→ 校准记录(更换采集板需重新校准)
报废管理 ──→ 维修工单(关联来源工单)
报废管理 ──→ 设备登记(回收入库后物料重新登记)
```

View File

@ -44,7 +44,8 @@ const isActive = (path: string) =>
const menuGroups = [
{ title: '设备', items: [
{ path: '/devices', label: '设备列表', icon: Monitor },
{ path: '/models', label: '型号管理', icon: Settings2 },
{ path: '/models', label: '设备型号管理', icon: Settings2 },
{ path: '/boards', label: '板卡型号管理', icon: Cpu },
]},
{ title: '授权', items: [
{ path: '/licenses', label: '授权管理', icon: Key },

View File

@ -0,0 +1,265 @@
<script setup lang="ts">
import { ref } from 'vue'
import {ArrowLeft, X} from 'lucide-vue-next'
import { useRouter } from 'vue-router'
const router = useRouter()
const goToFirmware = () => {
router.push('/firmware')
}
const activeType = ref('全部')
const boardTypes = ['全部', '主协板', '采集板', '发射板', '升压板']
interface Board {type: string; hwVersion: string; fwVersion: string
productionDate: string
}
const allBoards = ref<Board[]>([
{ type: '主协板', hwVersion: 'MB-V2.3', fwVersion: 'v2.3.5', productionDate: '2024-01-10' },
{ type: '主协板', hwVersion: 'MB-V2.2', fwVersion: 'v2.3.5', productionDate: '2024-02-15' },
{ type: '主协板', hwVersion: 'MB-V2.1', fwVersion: 'v1.5.0', productionDate: '2024-01-12' },
{ type: '采集板', hwVersion: 'RX-V2.2', fwVersion: 'v1.8.2', productionDate: '2024-01-15' },
{ type: '采集板', hwVersion: 'RX-V2.3', fwVersion: 'v1.8.0', productionDate: '2024-03-09' },
{ type: '发射板', hwVersion: 'TX-V2.1', fwVersion: 'v1.5.0', productionDate: '2024-01-18' },
{ type: '发射板', hwVersion: 'TX-V2.2', fwVersion: 'v1.4.2', productionDate: '2023-06-01' },
{ type: '升压板', hwVersion: 'BO-V2.1', fwVersion: 'v1.0.0', productionDate: '2024-01-20' },
{ type: '采集板', hwVersion: 'RX-V2.3', fwVersion: 'v1.7.0',productionDate: '2023-08-10' },
])
const filteredBoards = ref(allBoards.value)
const filterBoards = () => {
filteredBoards.value = activeType.value === '全部'
? allBoards.value
: allBoards.value.filter(b => b.type === activeType.value)
}
filterBoards()
const switchType = (t: string) => { activeType.value = t; filterBoards() }
// Detail drawer
const showDetail = ref(false)
const currentBoard = ref<Board | null>(null)
const openDetail = (b: Board) => { currentBoard.value = b; showDetail.value = true }
// Add board drawer
const showAddDrawer = ref(false)
const addForm = ref({
type: '主协板' as string,
hwVersion: '',
fwVersion: '',
productionDate: '',
})
const openAddDrawer = () => {
addForm.value = { type: '主协板', hwVersion: '', fwVersion: '', productionDate: '' }
showAddDrawer.value = true
}
const submitAddBoard = () => {
const { type, hwVersion, fwVersion, productionDate } = addForm.value
if (!hwVersion || !fwVersion || !productionDate) return
allBoards.value.push({ type, hwVersion, fwVersion, productionDate })
filterBoards()
showAddDrawer.value = false
}
const upgradeHistory = [
{ date: '2024-03-01', from: 'v2.3.0', to: 'v2.3.5', operator: '王工程师', method: '远程OTA' },
{ date: '2024-01-20', from: 'v2.2.0', to: 'v2.3.0', operator: '李工程师', method: '本地烧录' },
]
const calibHistory = [
{ date: '2024-03-05', result: '合格', calibrator: '张工程师', expiry: '2025-03-05' },
{ date: '2023-03-10', result: '合格', calibrator: '王工程师', expiry: '2024-03-10' },
]
const serviceHistory = [
{ deviceSn: 'GD30-2025-000001', from: '2025-01-15', to: '至今', role: '在役' },
{ deviceSn: 'GD30-2024-000890', from: '2024-03-08', to: '2024-12-31', role: '已拆卸' },
]
const repairHistory = [
{ orderId: 'WO-2024-0001', date: '2024-02-26', action: '更换(旧板换下)', operator: '李工' },
]
</script>
<template>
<div class="p-6">
<!-- Header -->
<div class="mb-6">
<div class="flex items-center justify-between mb-2">
<h2 class="text-2xl font-semibold">板卡型号管理</h2>
<div class="flex items-center gap-3">
<button
class="px-4 py-2 rounded flex items-center gap-2"
style="border: 1px solid #D9D9D9; color: rgba(0, 0, 0, 0.85)"
>
<Download :size="16" />
导出
</button>
<button
class="px-4 py-2 rounded text-white flex items-center gap-2"
style="background-color: #4a7c59"
@click="openAddDrawer"
>
<Plus :size="16" />
新增板卡
</button>
</div>
</div>
</div>
<!-- Board Type Tabs + Table -->
<div class="bg-white rounded-lg mb-6" style="box-shadow: 0 1px 2px rgba(0,0,0,0.05)">
<div class="flex border-b" style="border-color: #F0F0F0">
<button v-for="t in boardTypes" :key="t" @click="switchType(t)"
class="px-5 py-3 text-sm font-medium transition-colors"
:style="{ color: activeType === t ? '#4a7c59' : 'rgba(0,0,0,0.65)', borderBottom: activeType === t ? '2px solid #4a7c59' : '2px solid transparent', marginBottom: '-1px' }">
{{ t }}
</button>
</div>
<div class="overflow-x-auto">
<table class="w-full">
<thead style="background-color: #FAFAFA">
<tr>
<th class="px-5 py-3 text-left text-sm font-medium" style="color: rgba(0,0,0,0.85)">类型</th>
<th class="px-5 py-3 text-left text-sm font-medium" style="color: rgba(0,0,0,0.85)">型号</th>
<th class="px-5 py-3 text-left text-sm font-medium" style="color: rgba(0,0,0,0.85)">最新固件版本</th>
<th class="px-5 py-3 text-left text-sm font-medium" style="color: rgba(0,0,0,0.85)">操作</th>
</tr>
</thead>
<tbody>
<tr v-for="b in filteredBoards" class="border-b" style="border-color: #F0F0F0">
<td class="px-5 py-4 text-sm" style="color: rgba(0,0,0,0.65)">{{ b.type }}</td>
<td class="px-5 py-4 text-sm">{{ b.hwVersion }}</td>
<td class="px-5 py-4 text-sm" style="color: rgba(0,0,0,0.65)">{{ b.fwVersion }}</td>
<td class="px-5 py-4">
<div class="flex items-center gap-3">
<button class="text-sm" style="color: #4a7c59" @click="openDetail(b)">详情</button>
<button class="text-sm" style="color: #4a7c59" @click="goToFirmware">固件</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Detail Drawer -->
<div v-if="showDetail && currentBoard" class="fixed inset-0 z-50 flex justify-end" style="background-color: rgba(0,0,0,0.45)" @click.self="showDetail = false">
<div class="bg-white w-[560px] h-full flex flex-col" style="box-shadow: -4px 0 12px rgba(0,0,0,0.1)">
<div class="flex items-center gap-3 p-5 border-b" style="border-color: #F0F0F0">
<button @click="showDetail = false" class="p-1 rounded hover:bg-gray-100" style="color: rgba(0,0,0,0.65)"><ArrowLeft :size="18" /></button>
<h3 class="text-lg font-semibold">板卡详情</h3>
</div>
<div class="flex-1 overflow-y-auto p-6 space-y-5">
<!-- Basic Info -->
<div class="p-5 rounded-lg" style="background-color: #FAFAFA; border: 1px solid #F0F0F0">
<h4 class="text-base font-semibold mb-3">基本信息</h4>
<div class="grid grid-cols-2 gap-3 text-sm" style="color: rgba(0,0,0,0.65)">
<div>板卡类型{{ currentBoard.type }}</div>
<div>型号{{ currentBoard.hwVersion }}</div>
<div>当前固件{{ currentBoard.fwVersion }}</div>
<div>生产日期{{ currentBoard.productionDate }}</div>
</div>
<!-- Upgrade History -->
<div class="p-5 rounded-lg" style="background-color: #FAFAFA; border: 1px solid #F0F0F0">
<h4 class="text-base font-semibold mb-3">固件升级记录</h4>
<div v-if="upgradeHistory.length === 0" class="text-sm" style="color: rgba(0,0,0,0.25)">暂无升级记录</div>
<div v-else class="space-y-2">
<div v-for="(u, i) in upgradeHistory" :key="i" class="flex items-center gap-3 text-sm" style="color: rgba(0,0,0,0.65)">
<span style="color: rgba(0,0,0,0.35)">{{ u.date }}</span>
<span>{{ u.from }} {{ u.to }}</span>
<span class="px-1.5 py-0.5 rounded text-xs" style="background-color: #eef5f0; color: #4a7c59">{{ u.method }}</span>
<span style="color: rgba(0,0,0,0.35)">{{ u.operator }}</span>
</div>
</div>
</div>
<!-- Calibration History -->
<div class="p-5 rounded-lg" style="background-color: #FAFAFA; border: 1px solid #F0F0F0">
<h4 class="text-base font-semibold mb-3">校准历史</h4>
<div v-if="calibHistory.length === 0" class="text-sm" style="color: rgba(0,0,0,0.25)">暂无校准记录</div>
<div v-else class="space-y-2">
<div v-for="(c, i) in calibHistory" :key="i" class="flex items-center gap-3 text-sm" style="color: rgba(0,0,0,0.65)">
<span style="color: rgba(0,0,0,0.35)">{{ c.date }}</span>
<span class="px-1.5 py-0.5 rounded text-xs" style="background-color: #F6FFED; color: #52C41A; border: 1px solid #B7EB8F">{{ c.result }}</span>
<span>到期{{ c.expiry }}</span>
<span style="color: rgba(0,0,0,0.35)">{{ c.calibrator }}</span>
</div>
</div>
</div>
<!-- Service History -->
<div class="p-5 rounded-lg" style="background-color: #FAFAFA; border: 1px solid #F0F0F0">
<h4 class="text-base font-semibold mb-3">服役记录</h4>
<div v-if="serviceHistory.length === 0" class="text-sm" style="color: rgba(0,0,0,0.25)">暂无服役记录</div>
<div v-else class="space-y-2">
<div v-for="(s, i) in serviceHistory" :key="i" class="flex items-center gap-3 text-sm" style="color: rgba(0,0,0,0.65)">
<router-link :to="'/devices/' + s.deviceSn" style="color: #4a7c59">{{ s.deviceSn }}</router-link>
<span>{{ s.from }} ~ {{ s.to }}</span>
<span class="px-1.5 py-0.5 rounded text-xs" :style="s.role === '在役' ? { backgroundColor: '#F6FFED', color: '#52C41A', border: '1px solid #B7EB8F' } : { backgroundColor: '#FAFAFA', color: 'rgba(0,0,0,0.45)', border: '1px solid #D9D9D9' }">{{ s.role }}</span>
</div>
</div>
</div>
<!-- Repair History -->
<div class="p-5 rounded-lg" style="background-color: #FAFAFA; border: 1px solid #F0F0F0">
<h4 class="text-base font-semibold mb-3">维修记录</h4>
<div v-if="repairHistory.length === 0" class="text-sm" style="color: rgba(0,0,0,0.25)">暂无维修记录</div>
<div v-else class="space-y-2">
<div v-for="(r, i) in repairHistory" :key="i" class="flex items-center gap-3 text-sm" style="color: rgba(0,0,0,0.65)">
<span style="color: rgba(0,0,0,0.35)">{{ r.date }}</span>
<router-link :to="'/repair/' + r.orderId" style="color: #4a7c59">{{ r.orderId }}</router-link>
<span>{{ r.action }}</span>
<span style="color: rgba(0,0,0,0.35)">{{ r.operator }}</span>
</div>
</div>
</div>
</div>
<div class="flex items-center justify-center gap-3 p-5 border-t" style="border-color: #F0F0F0">
<button class="px-6 py-2 rounded text-sm" style="border: 1px solid #D9D9D9; color: rgba(0,0,0,0.85)" @click="showDetail = false">关闭</button>
</div>
</div>
</div>
</div>
<!-- Add Board Drawer -->
<div v-if="showAddDrawer" class="fixed inset-0 z-50 flex justify-end" style="background-color: rgba(0,0,0,0.45)" @click.self="showAddDrawer = false">
<div class="bg-white w-[480px] h-full flex flex-col" style="box-shadow: -4px 0 12px rgba(0,0,0,0.1)">
<div class="flex items-center justify-between p-5 border-b" style="border-color: #F0F0F0">
<h3 class="text-lg font-semibold">新增板卡</h3>
<button @click="showAddDrawer = false" class="p-1 rounded hover:bg-gray-100" style="color: rgba(0,0,0,0.45)"><X :size="18" /></button>
</div>
<div class="flex-1 overflow-y-auto p-6 space-y-5">
<!-- 板卡类型 -->
<div>
<label class="block text-sm font-medium mb-1.5" style="color: rgba(0,0,0,0.85)"><span class="text-red-500 mr-0.5">*</span>板卡类型</label>
<select v-model="addForm.type" class="w-full px-3 py-2 rounded text-sm" style="border: 1px solid #D9D9D9; color: rgba(0,0,0,0.85); outline: none">
<option v-for="t in boardTypes.filter(x => x !== '全部')" :key="t" :value="t">{{ t }}</option>
</select>
</div>
<!-- 型号 -->
<div>
<label class="block text-sm font-medium mb-1.5" style="color: rgba(0,0,0,0.85)"><span class="text-red-500 mr-0.5">*</span>板卡型号</label>
<input v-model="addForm.hwVersion" type="text" placeholder="如 MB-V2.3" class="w-full px-3 py-2 rounded text-sm" style="border: 1px solid #D9D9D9; outline: none" />
</div>
<!-- 固件版本 -->
<div>
<label class="block text-sm font-medium mb-1.5" style="color: rgba(0,0,0,0.85)"><span class="text-red-500 mr-0.5">*</span>固件版本</label>
<input v-model="addForm.fwVersion" type="text" placeholder="如 v1.0.0" class="w-full px-3 py-2 rounded text-sm" style="border: 1px solid #D9D9D9; outline: none" />
</div>
<!-- 生产日期 -->
<div>
<label class="block text-sm font-medium mb-1.5" style="color: rgba(0,0,0,0.85)"><span class="text-red-500 mr-0.5">*</span>生产日期</label>
<input v-model="addForm.productionDate" type="date" class="w-full px-3 py-2 rounded text-sm" style="border: 1px solid #D9D9D9; outline: none" />
</div>
</div>
<div class="flex items-center justify-end gap-3 p-5 border-t" style="border-color: #F0F0F0">
<button class="px-6 py-2 rounded text-sm" style="border: 1px solid #D9D9D9; color: rgba(0,0,0,0.85)" @click="showAddDrawer = false">取消</button>
<button class="px-6 py-2 rounded text-sm text-white" style="background-color: #4a7c59" @click="submitAddBoard">确认新增</button>
</div>
</div>
</div>
</div>
</template>

View File

@ -1,9 +1,8 @@
<script setup lang="ts">
import { Info } from 'lucide-vue-next'
import { Download, Info } from 'lucide-vue-next'
interface CalibrationRecord {
boardSn: string
deviceSn: string
calibrationDate: string
calibrator: string
expiryDate: string
@ -13,7 +12,6 @@ interface CalibrationRecord {
const calibrationRecords: CalibrationRecord[] = [
{
boardSn: 'RX20240308001',
deviceSn: 'GD30-2025-000001',
calibrationDate: '2024-03-01',
calibrator: '王工程师',
expiryDate: '2025-03-01',
@ -21,7 +19,6 @@ const calibrationRecords: CalibrationRecord[] = [
},
{
boardSn: 'RX20240308002',
deviceSn: 'GD20-2025-000045',
calibrationDate: '2024-03-05',
calibrator: '李工程师',
expiryDate: '2025-03-05',
@ -29,7 +26,6 @@ const calibrationRecords: CalibrationRecord[] = [
},
{
boardSn: 'RX20240308003',
deviceSn: 'GD10-2025-000023',
calibrationDate: '2024-03-08',
calibrator: '张工程师',
expiryDate: '2025-03-08',
@ -65,7 +61,16 @@ const getStatusStyle = (status: CalibrationRecord['status']) => {
<div class="p-6">
<!-- Page Header -->
<div class="mb-6">
<h2 class="text-2xl font-semibold mb-1">校准管理</h2>
<div class="flex items-center justify-between mb-2">
<h2 class="text-2xl font-semibold">校准管理</h2>
<div class="flex items-center gap-3">
<button class="px-4 py-2 rounded flex items-center gap-2" style="border: 1px solid #D9D9D9; color: rgba(0,0,0,0.85)">
<Download :size="16" /> 导入
</button>
</div>
</div>
<p class="text-sm" style="color: rgba(0, 0, 0, 0.45)">管理采集板校准数据</p>
</div>
@ -83,22 +88,6 @@ const getStatusStyle = (status: CalibrationRecord['status']) => {
</div>
</div>
<!-- Stat Cards -->
<div class="grid grid-cols-4 gap-6 mb-6">
<div class="bg-white p-6 rounded-lg" style="box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05)">
<div class="text-sm mb-2" style="color: rgba(0, 0, 0, 0.45)">采集板校准总数</div>
<div class="text-3xl font-semibold" style="color: #4a7c59">1,245</div>
</div>
<div class="bg-white p-6 rounded-lg" style="box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05)">
<div class="text-sm mb-2" style="color: rgba(0, 0, 0, 0.45)">待校准采集板</div>
<div class="text-3xl font-semibold" style="color: #FAAD14">23</div>
</div>
<div class="bg-white p-6 rounded-lg" style="box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05)">
<div class="text-sm mb-2" style="color: rgba(0, 0, 0, 0.45)">校准即将到期</div>
<div class="text-3xl font-semibold" style="color: #FF4D4F">15</div>
</div>
</div>
<!-- Filter Card -->
<div class="bg-white p-6 rounded-lg mb-6" style="box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05)">
<div class="grid grid-cols-4 gap-4">
@ -153,7 +142,6 @@ const getStatusStyle = (status: CalibrationRecord['status']) => {
<thead style="background-color: #FAFAFA">
<tr>
<th class="px-6 py-3 text-left text-sm font-medium" style="color: rgba(0, 0, 0, 0.85)">采集板SN号</th>
<th class="px-6 py-3 text-left text-sm font-medium" style="color: rgba(0, 0, 0, 0.85)">所属设备</th>
<th class="px-6 py-3 text-left text-sm font-medium" style="color: rgba(0, 0, 0, 0.85)">校准日期</th>
<th class="px-6 py-3 text-left text-sm font-medium" style="color: rgba(0, 0, 0, 0.85)">到期日期</th>
<th class="px-6 py-3 text-left text-sm font-medium" style="color: rgba(0, 0, 0, 0.85)">校准人员</th>
@ -169,7 +157,6 @@ const getStatusStyle = (status: CalibrationRecord['status']) => {
style="border-color: #F0F0F0"
>
<td class="px-6 py-4">{{ record.boardSn }}</td>
<td class="px-6 py-4" style="color: rgba(0, 0, 0, 0.65)">{{ record.deviceSn }}</td>
<td class="px-6 py-4" style="color: rgba(0, 0, 0, 0.65)">{{ record.calibrationDate }}</td>
<td class="px-6 py-4" style="color: rgba(0, 0, 0, 0.65)">{{ record.expiryDate }}</td>
<td class="px-6 py-4" style="color: rgba(0, 0, 0, 0.65)">{{ record.calibrator }}</td>

View File

@ -19,14 +19,14 @@ interface ConfigFile {
const configFiles: ConfigFile[] = [
{
configId: 'CFG-GD30-v1.2.0',
model: 'GD30 高密度电法仪',
model: 'GD-30 Supreme',
version: 'v1.2.0',
createdTime: '2024-03-01 10:30',
status: '生效',
},
{
configId: 'CFG-GT20-v1.5.3',
model: 'GT20 瞬变电磁仪',
configId: 'CFG-GD20-v1.5.3',
model: 'GD-20 Supreme',
version: 'v1.5.3',
createdTime: '2024-02-28 14:20',
status: '生效',
@ -95,9 +95,9 @@ const getStatusStyle = (status: ConfigFile['status']) => {
style="border-color: #D9D9D9; background-color: #fff"
>
<option>全部型号</option>
<option>GD30 高密度电法仪</option>
<option>GT20 瞬变电磁仪</option>
<option>GM10 大地电磁仪</option>
<option>GD-30 Supreme</option>
<option>GD-20 Supreme</option>
<option>GD-10 Supreme</option>
</select>
</div>
<div>
@ -118,7 +118,7 @@ const getStatusStyle = (status: ConfigFile['status']) => {
type="text"
class="w-full px-3 py-2 border rounded"
style="border-color: #D9D9D9"
placeholder="搜索配置ID或描述"
placeholder="搜索配置文件或描述"
/>
</div>
<div class="flex items-end">
@ -142,7 +142,7 @@ const getStatusStyle = (status: ConfigFile['status']) => {
<table class="w-full">
<thead style="background-color: #FAFAFA">
<tr>
<th class="px-6 py-3 text-left text-sm font-medium" style="color: rgba(0, 0, 0, 0.85)">配置ID</th>
<th class="px-6 py-3 text-left text-sm font-medium" style="color: rgba(0, 0, 0, 0.85)">配置文件</th>
<th class="px-6 py-3 text-left text-sm font-medium" style="color: rgba(0, 0, 0, 0.85)">适配型号</th>
<th class="px-6 py-3 text-left text-sm font-medium" style="color: rgba(0, 0, 0, 0.85)">版本</th>
<th class="px-6 py-3 text-left text-sm font-medium" style="color: rgba(0, 0, 0, 0.85)">创建时间</th>
@ -218,9 +218,9 @@ const getStatusStyle = (status: ConfigFile['status']) => {
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">适配型号 <span style="color: #FF4D4F">*</span></label>
<select v-model="newModel" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
<option value="">请选择型号</option>
<option>GD30 高密度电法仪</option>
<option>GT20 瞬变电磁仪</option>
<option>GM10 大地电磁仪</option>
<option>GD-30 Supreme</option>
<option>GD-20 Supreme</option>
<option>GD-10 Supreme</option>
</select>
</div>
<div>
@ -278,7 +278,7 @@ const getStatusStyle = (status: ConfigFile['status']) => {
<div>
<label class="block text-sm mb-1" style="color: rgba(0,0,0,0.65)">采样率</label>
<select class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
<option>50Hz60Hz100Hz1000Hz</option><option>50Hz60Hz100Hz1000Hz</option><option>50Hz60Hz</option>
<option>50Hz60Hz100Hz1000Hz</option><option>50Hz60Hz</option>
</select>
</div>
<div>
@ -301,12 +301,8 @@ const getStatusStyle = (status: ConfigFile['status']) => {
<h4 class="text-base font-semibold mb-4">网络参数</h4>
<div class="space-y-4">
<div>
<label class="block text-sm mb-1" style="color: rgba(0,0,0,0.65)">WiFi SSID</label>
<input type="text" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9" placeholder="如 GD30_Device" />
</div>
<div>
<label class="block text-sm mb-1" style="color: rgba(0,0,0,0.65)">默认IP地址</label>
<input type="text" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9" placeholder="192.168.1.100" />
<label class="block text-sm mb-1" style="color: rgba(0,0,0,0.65)">WiFi SSID 前缀</label>
<input type="text" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9" placeholder="如 GD30_" />
</div>
</div>
</div>

View File

@ -112,8 +112,8 @@
</span>
</div>
<div>
<div class="text-xs mb-1" style="color: rgba(0, 0, 0, 0.45)">固件版本</div>
<div>{{ device.firmwareVersion }}</div>
<div class="text-xs mb-1" style="color: rgba(0, 0, 0, 0.45)">主机版本</div>
<div>{{ device.version }}</div>
</div>
<div>
<div class="text-xs mb-1" style="color: rgba(0, 0, 0, 0.45)">生产日期</div>
@ -172,7 +172,7 @@ interface Device {
sn: string
model: string
status: '已激活' | '已出厂' | '装配中'
firmwareVersion: string
version: string
registrationDate: string
}
@ -185,21 +185,21 @@ const devices: Device[] = [
sn: 'GD30-20240308-001',
model: 'GD30 高密度电法仪',
status: '已激活',
firmwareVersion: 'v2.3.5',
version: 'v2.3.5',
registrationDate: '2024-03-08 14:30',
},
{
sn: 'GT20-20240307-045',
model: 'GT20 瞬变电磁仪',
status: '已出厂',
firmwareVersion: 'v1.8.2',
version: 'v1.8.2',
registrationDate: '2024-03-07 10:15',
},
{
sn: 'GD30-20240308-002',
model: 'GD30 高密度电法仪',
status: '装配中',
firmwareVersion: 'v2.3.4',
version: 'v2.3.4',
registrationDate: '2024-03-08 16:20',
},
]

View File

@ -16,9 +16,6 @@ const showDrawer = ref(false)
const drawerForm = ref({
name: '',
code: '',
authFile: '',
configFile: '',
firmwareVersion: '',
status: '在产' as '在产' | '停产',
})
@ -34,41 +31,22 @@ interface ChecklistData {
[key: string]: ChecklistItem[]
}
const activeTab = ref('GD30')
const stats = [
{ label: '型号总数', value: '12', icon: Server, color: '#4a7c59' },
{ label: '在产型号', value: '8', icon: CheckCircle2, color: '#52C41A' },
{ label: '停产型号', value: '3', icon: AlertTriangle, color: '#FAAD14' },
{ label: '关联设备总数', value: '5,234', icon: Layers, color: '#722ED1' },
]
const activeTab = ref('GD-30 Supreme')
const modelData = [
{
name: 'GD30 高密度电法仪',
name: 'GD-30 Supreme',
code: 'GD30-2024',
authFile: 'auth_gd30_v2.3.lic',
configFile: 'config_gd30_v1.5.json',
firmwareVersion: 'v2.3.5',
deviceCount: 2456,
status: '在产' as const,
},
{
name: 'GT20 瞬变电磁仪',
code: 'GT20-2023',
authFile: 'auth_gt20_v1.8.lic',
configFile: 'config_gt20_v1.2.json',
firmwareVersion: 'v1.8.2',
deviceCount: 1823,
name: 'GD-20 Supreme',
code: 'GD20-2023',
status: '在产' as const,
},
{
name: 'GM10 大地电磁仪',
code: 'GM10-2022',
authFile: 'auth_gm10_v1.5.lic',
configFile: 'config_gm10_v1.0.json',
firmwareVersion: 'v1.5.1',
deviceCount: 955,
name: 'GD-10 Supreme',
code: 'GD10-2022',
status: '停产' as const,
},
]
@ -98,14 +76,14 @@ const checklistData = ref<ChecklistData>({
{ id: 21, text: '过流/过压/短路保护', type: 'bool', required: true, note: '' },
{ id: 22, text: '出厂装箱核对', type: 'bool', required: true, note: '' },
],
GT20: [
GD20: [
{ id: 1, text: '主板SN扫码绑定主机', type: 'text', required: true, note: '唯一SN' },
{ id: 2, text: '采集板SN录入', type: 'text', required: true, note: '' },
{ id: 3, text: 'GPS/北斗检测', type: 'bool', required: true, note: '授时正常' },
{ id: 4, text: '系统启动正常', type: 'bool', required: true, note: '' },
{ id: 5, text: '整体功能测试', type: 'bool', required: true, note: '' },
],
GM10: [
GD10: [
{ id: 1, text: '主板SN扫码绑定主机', type: 'text', required: true, note: '唯一SN' },
{ id: 2, text: '传感器模块连接', type: 'bool', required: true, note: '' },
{ id: 3, text: '接口板安装', type: 'bool', required: true, note: '' },
@ -173,11 +151,11 @@ const boardVersionData = [
// Checklist template drawer state
const showChecklistDrawer = ref(false)
const newTplModel = ref('GD30')
const newTplModel = ref('GD-30 Supreme')
const newTplItems = ref<{ text: string }[]>([{ text: '' }])
const openChecklistDrawer = () => {
newTplModel.value = 'GD30'
newTplModel.value = 'GD-30 Supreme'
newTplItems.value = [{ text: '' }]
showChecklistDrawer.value = true
}
@ -206,30 +184,7 @@ const removeTplItem = (index: number) => {
>
<Info :size="20" :style="{ color: '#4a7c59', flexShrink: 0, marginTop: '2px' }" />
<div :style="{ color: '#2d5a3d' }">
型号管理是平台核心枢纽每个型号关联授权文件配置文件和固件版本
</div>
</div>
<!-- Stat Cards -->
<div class="grid grid-cols-4 gap-6 mb-6">
<div
v-for="(stat, index) in stats"
:key="index"
class="bg-white p-6 rounded-lg"
:style="{ boxShadow: '0 1px 2px rgba(0, 0, 0, 0.05)' }"
>
<div class="flex items-center justify-between">
<div>
<div class="text-sm mb-2" :style="{ color: 'rgba(0, 0, 0, 0.65)' }">{{ stat.label }}</div>
<div class="text-3xl font-semibold">{{ stat.value }}</div>
</div>
<div
class="w-12 h-12 rounded-lg flex items-center justify-center"
:style="{ backgroundColor: `${stat.color}15` }"
>
<component :is="stat.icon" :size="24" :style="{ color: stat.color }" />
</div>
</div>
型号管理是平台核心枢纽每个型号关联授权项配置文件
</div>
</div>
@ -244,7 +199,7 @@ const removeTplItem = (index: number) => {
@click="showDrawer = true"
>
<Plus :size="16" />
新增型号
新增设备型号
</button>
</div>
</div>
@ -254,10 +209,6 @@ const removeTplItem = (index: number) => {
<tr>
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">型号名称</th>
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">编码</th>
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">授权文件</th>
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">配置文件</th>
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">固件版本</th>
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">设备数</th>
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">状态</th>
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">操作</th>
</tr>
@ -271,16 +222,6 @@ const removeTplItem = (index: number) => {
>
<td class="px-6 py-4">{{ model.name }}</td>
<td class="px-6 py-4" :style="{ color: 'rgba(0, 0, 0, 0.65)' }">{{ model.code }}</td>
<td class="px-6 py-4">
<router-link to="/licenses" class="text-sm" style="color: #4a7c59">{{ model.authFile }}</router-link>
</td>
<td class="px-6 py-4">
<router-link to="/config-files" class="text-sm" style="color: #4a7c59">{{ model.configFile }}</router-link>
</td>
<td class="px-6 py-4">
<router-link to="/firmware" class="text-sm" style="color: #4a7c59">{{ model.firmwareVersion }}</router-link>
</td>
<td class="px-6 py-4">{{ model.deviceCount.toLocaleString() }}</td>
<td class="px-6 py-4">
<span
class="px-2 py-1 rounded text-xs"
@ -298,7 +239,6 @@ const removeTplItem = (index: number) => {
<button class="text-sm" :style="{ color: '#4a7c59' }">编辑</button>
<router-link to="/licenses" class="text-sm" style="color: #4a7c59">授权</router-link>
<router-link to="/config-files" class="text-sm" style="color: #4a7c59">配置</router-link>
<router-link to="/firmware" class="text-sm" style="color: #4a7c59">固件</router-link>
</div>
</td>
</tr>
@ -403,99 +343,14 @@ const removeTplItem = (index: number) => {
<div class="space-y-5">
<div>
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">型号名称 <span style="color: #FF4D4F">*</span></label>
<input v-model="drawerForm.name" type="text" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9" placeholder="如GD30 高密度电法仪" />
<input v-model="drawerForm.name" type="text" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9" placeholder="如GD-30 Supreme" />
</div>
<div>
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">型号编码 <span style="color: #FF4D4F">*</span></label>
<select v-model="drawerForm.code" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
<option value="">请选择编码</option>
<option>GD10-2025</option>
<option>GD20-2025</option>
<option>GD30-2025</option>
<option>GT20-2025</option>
<option>GM10-2025</option>
</select>
<input class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
</input>
</div>
<div>
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">绑定授权文件</label>
<select v-model="drawerForm.authFile" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
<option value="">请选择授权文件</option>
<option>auth_gd10_v1.0.lic</option>
<option>auth_gd20_v2.0.lic</option>
<option>auth_gd30_v2.3.lic</option>
<option>auth_gt20_v1.8.lic</option>
<option>auth_gm10_v1.5.lic</option>
</select>
<div class="text-xs mt-1" style="color: rgba(0,0,0,0.45)">授权文件按型号绑定设备激活时自动下载</div>
</div>
<div>
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">绑定配置文件</label>
<select v-model="drawerForm.configFile" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
<option value="">请选择配置文件</option>
<option>config_gd10_v1.0.json</option>
<option>config_gd20_v1.2.json</option>
<option>config_gd30_v1.5.json</option>
<option>config_gt20_v1.2.json</option>
<option>config_gm10_v1.0.json</option>
</select>
<div class="text-xs mt-1" style="color: rgba(0,0,0,0.45)">配置文件包含发射参数采集参数网络参数等</div>
</div>
<div>
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">绑定主机固件版本</label>
<select v-model="drawerForm.firmwareVersion" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
<option value="">请选择主机固件版本</option>
<option>v2.3.5</option>
<option>v2.3.0</option>
<option>v2.2.0</option>
<option>v2.1.0</option>
<option>v1.8.2</option>
<option>v1.5.1</option>
</select>
</div>
<div>
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">绑定主协板固件版本</label>
<select v-model="drawerForm.firmwareVersion" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
<option value="">请选择主协板固件版本</option>
<option>v2.3.5</option>
<option>v2.3.0</option>
<option>v2.2.0</option>
<option>v2.1.0</option>
<option>v1.8.2</option>
<option>v1.5.1</option>
</select>
</div>
<div>
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">绑定发射板固件版本</label>
<select v-model="drawerForm.firmwareVersion" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
<option value="">请选择发射板固件版本</option>
<option>v2.3.5</option>
<option>v2.3.0</option>
<option>v2.2.0</option>
<option>v2.1.0</option>
<option>v1.8.2</option>
<option>v1.5.1</option>
</select>
</div>
<div>
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">绑定采集板固件版本</label>
<select v-model="drawerForm.firmwareVersion" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
<option value="">请选择采集板固件版本</option>
<option>v2.3.5</option>
<option>v2.3.0</option>
<option>v2.2.0</option>
<option>v2.1.0</option>
<option>v1.8.2</option>
<option>v1.5.1</option>
</select>
</div>
<div>
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">状态</label>
<div class="flex items-center gap-4">
@ -509,25 +364,6 @@ const removeTplItem = (index: number) => {
</label>
</div>
</div>
<!-- Preview -->
<div class="p-4 rounded-lg" style="background-color: #FAFAFA; border: 1px solid #F0F0F0">
<div class="text-sm font-medium mb-3">绑定预览</div>
<div class="space-y-2 text-sm" style="color: rgba(0,0,0,0.65)">
<div class="flex justify-between">
<span>授权文件</span>
<span :style="{ color: drawerForm.authFile ? '#52C41A' : '#FF4D4F' }">{{ drawerForm.authFile || '未绑定' }}</span>
</div>
<div class="flex justify-between">
<span>配置文件</span>
<span :style="{ color: drawerForm.configFile ? '#52C41A' : '#FF4D4F' }">{{ drawerForm.configFile || '未绑定' }}</span>
</div>
<div class="flex justify-between">
<span>固件版本</span>
<span :style="{ color: drawerForm.firmwareVersion ? '#52C41A' : '#FF4D4F' }">{{ drawerForm.firmwareVersion || '未绑定' }}</span>
</div>
</div>
</div>
</div>
</div>
@ -561,9 +397,9 @@ const removeTplItem = (index: number) => {
<div>
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">设备型号 <span style="color: #FF4D4F">*</span></label>
<select v-model="newTplModel" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
<option value="GD30">GD30</option>
<option value="GT20">GT20</option>
<option value="GM10">GM10</option>
<option value="GD-30 Supreme">GD-30 Supreme</option>
<option value="GT20">GD-20 Supreme</option>
<option value="GM10">GD-10 Supreme</option>
</select>
</div>

View File

@ -64,11 +64,11 @@ const checklistItems = ref([
])
const bomData = [
{ code: 'MB-2024-001', name: '主板', sn: 'MB20240308001', spec: 'GD30-MB-V2.3', calibration: '无需校准', quantity: 1 },
{ code: 'RX-2024-002', name: '采集板', sn: 'RX20240308001、RX20240308002', spec: 'GD30-RX-V1.8', calibration: '已校准', quantity: 2 },
{ code: 'MC-2024-003', name: '测控板', sn: 'MC20240308003', spec: 'GD30-MC-V1.5', calibration: '无需校准', quantity: 1 },
{ code: 'TX-2024-003', name: '发射板', sn: 'TX20240308003', spec: 'GD30-TX-V1.5', calibration: '无需校准', quantity: 1 },
{ code: 'BO-2024-004', name: '升压板', sn: 'BO20240308004', spec: 'BP600', calibration: '无需校准', quantity: 1 },
{ code: 'MB-2024-001', name: '主板', sn: 'MB20240308001', spec: 'MB-V2.3', calibration: '无需校准', quantity: 1 },
{ code: 'RX-2024-002', name: '采集板', sn: 'RX20240308001、RX20240308002', spec: 'RX-V1.8', calibration: '已校准', quantity: 2 },
{ code: 'MC-2024-003', name: '测控板', sn: 'MC20240308003', spec: 'MC-V1.5', calibration: '无需校准', quantity: 1 },
{ code: 'TX-2024-003', name: '发射板', sn: 'TX20240308003', spec: 'TX-V1.5', calibration: '无需校准', quantity: 1 },
{ code: 'BO-2024-004', name: '升压板', sn: 'BO20240308004', spec: 'BO-V1.4', calibration: '无需校准', quantity: 1 },
{ code: 'CS-2024-005', name: '外壳机箱', sn: '-', spec: 'AL6061-T6', calibration: '无需校准', quantity: 1 },
]
@ -103,9 +103,9 @@ const toggleChecklistItem = (id: number) => {
class="w-full px-3 py-2 border rounded"
:style="{ borderColor: '#D9D9D9', backgroundColor: '#fff' }"
>
<option value="GD30">GD30 高密度电法仪</option>
<option value="GT20">GT20 瞬变电磁仪</option>
<option value="GTXD">GM10 大地电磁仪</option>
<option value="GD30">GD-30 Supreme</option>
<option value="GT20">GD-20 Supreme</option>
<option value="GTXD">GD-10 Supreme</option>
</select>
</div>
<div>
@ -202,7 +202,7 @@ const toggleChecklistItem = (id: number) => {
<div class="bg-white rounded-lg mb-6" :style="{ boxShadow: '0 1px 2px rgba(0, 0, 0, 0.05)' }">
<div class="p-6 border-b" :style="{ borderColor: '#F0F0F0' }">
<div class="flex items-center justify-between">
<h3 class="text-lg font-semibold">设备BOM清单</h3>
<h3 class="text-lg font-semibold">装机清单</h3>
<button
class="px-4 py-2 rounded flex items-center gap-2"
:style="{ border: '1px solid #D9D9D9', color: 'rgba(0, 0, 0, 0.85)' }"
@ -220,7 +220,7 @@ const toggleChecklistItem = (id: number) => {
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">物料编码</th>
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">物料名称</th>
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">SN号</th>
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">规格型号</th>
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">型号</th>
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">校准状态</th>
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">数量</th>
<th class="px-6 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">操作</th>

View File

@ -174,7 +174,7 @@ const formatFileSize = (bytes: number): string => {
<!-- Page Header -->
<div class="mb-6">
<div class="flex items-center justify-between mb-2">
<h2 class="text-2xl font-semibold">GD-30 Supreme固件库</h2>
<h2 class="text-2xl font-semibold">固件库</h2>
<div class="flex items-center gap-3">
<button class="px-4 py-2 rounded text-white flex items-center gap-2" style="background-color: #4a7c59" @click="uploadDialogVisible = true">
<Upload :size="16" />
@ -197,10 +197,6 @@ const formatFileSize = (bytes: number): string => {
<span class="text-lg font-semibold">版本: {{ fw.version }}</span>
<span class="text-sm" style="color: rgba(0,0,0,0.45)">发布日期: {{ fw.releaseDate }}</span>
<span class="text-sm" style="color: #52C41A">{{ fw.status }}</span>
<!-- 新增固件类型标签 -->
<span class="text-sm px-2 py-1 rounded" style="background-color: #f0f9ff; color: #4a7c59">
类型: {{ fw.firmwareType }}
</span>
</div>
<!-- Basic info -->
@ -214,7 +210,7 @@ const formatFileSize = (bytes: number): string => {
MD5: {{ fw.md5 }}&emsp;SHA256: {{ fw.sha256 }}
</div>
<div class="text-sm mb-3" style="color: rgba(0,0,0,0.65)">
适用型号: {{ fw.model }}&emsp;硬件版本范围: {{ fw.hardwareRange }}&emsp;升级类型: {{ fw.upgradeType }}
升级类型: {{ fw.upgradeType }}
</div>
<!-- Signature -->
@ -257,13 +253,6 @@ const formatFileSize = (bytes: number): string => {
<ElInput v-model="uploadForm.version" placeholder="例如v2.4.0" />
</ElFormItem>
<!-- 新增固件类型选择 -->
<ElFormItem label="固件类型" prop="firmwareType">
<ElSelect v-model="uploadForm.firmwareType" placeholder="请选择固件类型">
<ElOption v-for="type in firmwareTypes" :key="type" :label="type" :value="type" />
</ElSelect>
</ElFormItem>
<!-- 硬件版本范围 -->
<ElFormItem label="硬件版本范围" prop="hardwareRange">
<ElInput v-model="uploadForm.hardwareRange" placeholder="例如A1-A3" />

View File

@ -1,165 +0,0 @@
<script setup lang="ts">
import { ArrowLeft, Info } from 'lucide-vue-next'
import { useRouter } from 'vue-router'
import { ref, computed } from 'vue'
const router = useRouter()
const selectedModel = ref('GD-10 Supreme')
const expiryType = ref('永久')
const expiryDate = ref('')
interface AuthItem {
name: string
}
const authItems = ref<AuthItem[]>([
{ name: '1D SP' },
{ name: '2D SP' },
{ name: '3D SP' },
{ name: '1D VES' },
{ name: '2D ERT' },
{ name: '3D ERT' },
{ name: '1D IP'},
{ name: '2D IP' },
{ name: '3D IP'},
{ name: '跨孔Cross-Hole' },
{ name: '水上Marine'},
])
const availableItems = computed(() => {
return authItems.value.map(item => {
let available = false
return { ...item, available }
})
})
const checkedItems = ref(new Set<string>())
const toggleItem = (name: string) => {
if (checkedItems.value.has(name)) checkedItems.value.delete(name)
else checkedItems.value.add(name)
}
const selectAll = () => {
availableItems.value.forEach(item => {
if (item.available) checkedItems.value.add(item.name)
})
}
const clearAll = () => {
checkedItems.value.clear()
}
const selectedCount = computed(() => {
return availableItems.value.filter(i => i.available && checkedItems.value.has(i.name)).length
})
const totalAvailable = computed(() => {
return availableItems.value.filter(i => i.available).length
})
</script>
<template>
<div class="p-6">
<!-- Page Header -->
<div class="mb-6">
<div class="flex items-center gap-4 mb-2">
<button @click="router.go(-1)" class="p-2 rounded hover:bg-gray-100 transition-colors" style="color: rgba(0,0,0,0.65)">
<ArrowLeft :size="20" />
</button>
<h2 class="text-2xl font-semibold">选择授权项生成</h2>
</div>
<p class="text-sm ml-12" style="color: rgba(0,0,0,0.45)">选择设备型号和授权功能模块生成授权文件</p>
</div>
<!-- Info Banner -->
<div class="mb-6 p-4 rounded-lg flex items-start gap-3" style="background-color: #eef5f0; border: 1px solid #a3c4ad">
<Info :size="20" style="color: #4a7c59; flex-shrink: 0; margin-top: 2px" />
<div style="color: #2d5a3d">
<div class="text-sm">
不同设备型号支持不同的授权功能模块选择型号后下方表格会显示该型号可用的授权项生成的授权文件将在设备APP激活时自动下载到对应设备
</div>
</div>
</div>
<!-- Model & Expiry Selection -->
<div class="bg-white p-6 rounded-lg mb-6" style="box-shadow: 0 1px 2px rgba(0,0,0,0.05)">
<h3 class="text-lg font-semibold mb-6">基本信息</h3>
<div class="grid grid-cols-3 gap-6">
<div>
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">设备型号 <span style="color: #FF4D4F">*</span></label>
<select v-model="selectedModel" class="w-full px-3 py-2 border rounded" style="border-color: #D9D9D9; background-color: #fff" @change="clearAll()">
<option>GD-10 Supreme</option>
<option>GD-20 Supreme</option>
<option>GD-30 Supreme</option>
</select>
</div>
<div>
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">授权有效期</label>
<select v-model="expiryType" class="w-full px-3 py-2 border rounded" style="border-color: #D9D9D9; background-color: #fff">
<option>永久</option>
<option>1</option>
<option>2</option>
<option>自定义</option>
</select>
</div>
<div v-if="expiryType === '自定义'">
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">到期日期</label>
<input type="date" v-model="expiryDate" class="w-full px-3 py-2 border rounded" style="border-color: #D9D9D9" />
</div>
</div>
</div>
<!-- Authorization Items Table -->
<div class="bg-white rounded-lg mb-6" style="box-shadow: 0 1px 2px rgba(0,0,0,0.05)">
<div class="p-6 border-b flex items-center justify-between" style="border-color: #F0F0F0">
<div class="flex items-center gap-3">
<h3 class="text-lg font-semibold">功能授权项选择</h3>
<span class="text-sm" style="color: rgba(0,0,0,0.45)">已选 {{ selectedCount }} / {{ totalAvailable }} </span>
</div>
<div class="flex items-center gap-3">
<button class="text-sm" style="color: #4a7c59" @click="selectAll">全选可用项</button>
<button class="text-sm" style="color: rgba(0,0,0,0.45)" @click="clearAll">清空</button>
</div>
</div>
<div class="overflow-x-auto">
<table class="w-full">
<thead style="background-color: #FAFAFA">
<tr>
<th class="px-6 py-3 text-left text-sm font-medium" style="color: rgba(0,0,0,0.85); width: 150px">选择</th>
<th class="px-6 py-3 text-left text-sm font-medium" style="color: rgba(0,0,0,0.85)">授权项名称</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in availableItems" :key="index" class="border-b" style="border-color: #F0F0F0">
<td class="px-6 py-3">
<input
type="checkbox"
class="w-4 h-4"
style="accent-color: #4a7c59"
:checked="checkedItems.has(item.name)"
:disabled="!item.available"
@change="toggleItem(item.name)"
/>
</td>
<td class="px-6 py-3 text-sm">{{ item.name }}</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Action Bar -->
<div class="flex items-center justify-end gap-3 p-4 bg-white rounded-lg sticky bottom-0" style="box-shadow: 0 -2px 8px rgba(0,0,0,0.05)">
<button class="px-6 py-2 rounded" style="border: 1px solid #D9D9D9; color: rgba(0,0,0,0.85)" @click="router.go(-1)">取消</button>
<button
class="px-6 py-2 rounded text-white"
:style="{ backgroundColor: selectedCount > 0 ? '#52C41A' : '#D9D9D9' }"
:disabled="selectedCount === 0"
@click="router.push('/licenses')"
>
生成授权文件{{ selectedCount }}
</button>
</div>
</div>
</template>

View File

@ -60,11 +60,11 @@ const onModelChange = () => {
})
}
interface License { licenseId: string; model: string; status: '已发布' | '草稿' | '已停用'; modules: string; expiry: string; createdDate: string }
interface License {model: string; status: '已发布' | '草稿' | '已停用'; modules: string; expiry: string; createdDate: string }
const licenses: License[] = [
{ licenseId: 'LIC-GD10-Supreme-v1.0', model: 'GD-10 Supreme', status: '已发布', modules: '1D SP, 2D SP, 1D VES, 2D ERT, 1D IP, 2D IP', expiry: '永久', createdDate: '2025-02-01' },
{ licenseId: 'LIC-GD20-Supreme-v1.0', model: 'GD-20 Supreme', status: '已发布', modules: '全部模块', expiry: '永久', createdDate: '2025-02-05' },
{ licenseId: 'LIC-GD30-Supreme-v1.0', model: 'GD-30 Supreme', status: '草稿', modules: '全部模块(不含水上)', expiry: '1年', createdDate: '2025-02-08' },
{model: 'GD-10 Supreme', status: '已发布', modules: '1D SP, 2D SP, 1D VES, 2D ERT, 1D IP, 2D IP', expiry: '永久', createdDate: '2025-02-01' },
{model: 'GD-20 Supreme', status: '已发布', modules: '全部模块', expiry: '永久', createdDate: '2025-02-05' },
{model: 'GD-30 Supreme', status: '草稿', modules: '全部模块(不含水上)', expiry: '1年', createdDate: '2025-02-08' },
]
const getStatusStyle = (s: License['status']) => {
if (s === '已发布') return { backgroundColor: '#F6FFED', color: '#52C41A', border: '1px solid #B7EB8F' }
@ -84,7 +84,7 @@ const getStatusStyle = (s: License['status']) => {
<Download :size="16" /> 导出
</button>
<button class="px-4 py-2 rounded text-white flex items-center gap-2" style="background-color: #4a7c59" @click="openDrawer">
<Plus :size="16" /> 选择授权项生成
<Plus :size="16" /> 选择授权项
</button>
</div>
</div>
@ -96,7 +96,7 @@ const getStatusStyle = (s: License['status']) => {
<Info :size="20" style="color: #13C2C2; flex-shrink: 0; margin-top: 2px" />
<div style="color: #006D75">
<div class="font-medium mb-1">授权说明</div>
<div class="text-sm">授权文件按设备型号管理每个型号对应一套授权模块配置点击"选择授权项生成"可按型号勾选功能模块并生成授权文件设备在APP激活时自动下载对应型号的授权文件</div>
<div class="text-sm">授权文件按设备型号管理每个型号对应一套授权模块配置点击"选择授权项"可按型号勾选功能模块设备在APP激活时自动下载对应型号的授权文件</div>
</div>
</div>
@ -127,7 +127,6 @@ const getStatusStyle = (s: License['status']) => {
<table class="w-full">
<thead style="background-color: #FAFAFA">
<tr>
<th class="px-6 py-3 text-left text-sm font-medium" style="color: rgba(0,0,0,0.85)">授权文件ID</th>
<th class="px-6 py-3 text-left text-sm font-medium" style="color: rgba(0,0,0,0.85)">适配型号</th>
<th class="px-6 py-3 text-left text-sm font-medium" style="color: rgba(0,0,0,0.85)">授权模块</th>
<th class="px-6 py-3 text-left text-sm font-medium" style="color: rgba(0,0,0,0.85)">有效期</th>
@ -138,7 +137,6 @@ const getStatusStyle = (s: License['status']) => {
</thead>
<tbody>
<tr v-for="(lic, i) in licenses" :key="i" class="border-b" style="border-color: #F0F0F0">
<td class="px-6 py-4" style="color: #4a7c59">{{ lic.licenseId }}</td>
<td class="px-6 py-4">{{ lic.model }}</td>
<td class="px-6 py-4 text-sm" style="color: rgba(0,0,0,0.65); max-width: 200px">{{ lic.modules }}</td>
<td class="px-6 py-4" style="color: rgba(0,0,0,0.65)">{{ lic.expiry }}</td>
@ -175,7 +173,7 @@ const getStatusStyle = (s: License['status']) => {
<div class="bg-white w-[640px] h-full flex flex-col" style="box-shadow: -4px 0 12px rgba(0,0,0,0.1)">
<!-- Header -->
<div class="flex items-center justify-between p-5 border-b" style="border-color: #F0F0F0">
<h3 class="text-lg font-semibold">选择授权项生成</h3>
<h3 class="text-lg font-semibold">选择授权项</h3>
<button @click="showDrawer = false" class="p-1 rounded hover:bg-gray-100" style="color: rgba(0,0,0,0.45)">
<X :size="18" />
</button>
@ -243,7 +241,7 @@ const getStatusStyle = (s: License['status']) => {
:style="{ backgroundColor: selectedCount > 0 ? '#52C41A' : '#D9D9D9' }"
:disabled="selectedCount === 0"
@click="showDrawer = false"
>生成授权文件{{ selectedCount }}</button>
>保存</button>
</div>
</div>
</div>

View File

@ -13,8 +13,8 @@ export const router = createRouter({
{ path: 'devices', component: () => import('./pages/DeviceList.vue') },
{ path: 'devices/:id', component: () => import('./pages/DeviceDetail.vue') },
{ path: 'registration', component: () => import('./pages/DeviceRegistration.vue') },
{ path: 'boards', component: () => import('./pages/BoardManagement.vue') },
{ path: 'licenses', component: () => import('./pages/LicenseManagement.vue') },
{ path: 'licenses/generate', component: () => import('./pages/LicenseGenerate.vue') },
{ path: 'calibration', component: () => import('./pages/CalibrationRecords.vue') },
{ path: 'repair', component: () => import('./pages/RepairOrders.vue') },
{ path: 'repair/stats', component: () => import('./pages/RepairStats.vue') },