更改授权文件,增加装机管理页面

This commit is contained in:
徐星 2026-04-02 18:07:43 +08:00
parent 0d9e6c27c2
commit 71095c294c
33 changed files with 958 additions and 0 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

31
dist/assets/Dashboard--eri3aJi.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/assets/DeviceDetail-CiWFBze4.js vendored Normal file

File diff suppressed because one or more lines are too long

6
dist/assets/DeviceList-NCBTEyEZ.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
import{U as b}from"./upload-Cl9qkJl5.js";import{d as y,a as l,b as s,f as d,u as g,j as i,F as r,r as c,s as _,o as a,t as o,h as m,w as v,e as f}from"./index-C5t0nGiQ.js";const k={class:"p-6"},h={class:"mb-6"},D={class:"flex items-center justify-between mb-2"},F={class:"flex items-center gap-3"},C={class:"px-4 py-2 rounded text-white flex items-center gap-2",style:{"background-color":"#4a7c59"}},B={class:"bg-white rounded-lg",style:{"box-shadow":"0 1px 2px rgba(0,0,0,0.05)"}},S={class:"divide-y",style:{"border-color":"#F0F0F0"}},A={class:"flex items-center gap-4 mb-2"},z={class:"text-lg font-semibold"},M={class:"text-sm",style:{color:"rgba(0,0,0,0.45)"}},N={class:"text-sm",style:{color:"#52C41A"}},T={class:"text-sm mb-1",style:{color:"rgba(0,0,0,0.65)"}},V={class:"text-sm mb-1",style:{color:"rgba(0,0,0,0.65)"}},L={class:"text-sm mb-3",style:{color:"rgba(0,0,0,0.65)"}},j={key:0,class:"mb-3"},R={key:1,class:"mb-4"},U={class:"text-sm list-disc pl-5",style:{color:"rgba(0,0,0,0.65)"}},E={class:"flex items-center gap-6 pt-2"},G={key:1,class:"flex items-center gap-6 pt-3"},H=["onClick"],J=y({__name:"FirmwareLibrary",setup(O){const p=_([{version:"v2.3.0",releaseDate:"2024-02-20",status:"已发布",fileSize:"12.5MB",downloads:1234,md5:"a1b2c3d4e5f6...",sha256:"...",model:"GD30",hardwareRange:"A1-A3",upgradeType:"可选",signed:!0,notes:["修复OTA升级流程中的断点续传Bug","优化设备心跳上报频率","新增配置文件远程下发功能","增强安全性,升级前强制验证固件签名"],expanded:!0},{version:"v2.2.0",releaseDate:"2024-01-15",status:"已发布",fileSize:"12.0MB",downloads:2456,expanded:!1},{version:"v2.1.0",releaseDate:"2023-12-01",status:"已发布",fileSize:"11.5MB",downloads:3587,expanded:!1}]);return($,e)=>{const u=f("router-link");return a(),l("div",k,[s("div",h,[s("div",D,[e[2]||(e[2]=s("h2",{class:"text-2xl font-semibold"},"固件库",-1)),s("div",F,[s("button",C,[d(g(b),{size:16}),e[0]||(e[0]=i(" 上传固件 ",-1))]),e[1]||(e[1]=s("button",{class:"px-4 py-2 rounded flex items-center gap-2",style:{border:"1px solid #D9D9D9",color:"rgba(0,0,0,0.85)"}}," 发布固件 ",-1))])])]),s("div",B,[e[10]||(e[10]=s("div",{class:"p-6 border-b",style:{"border-color":"#F0F0F0"}},[s("h3",{class:"text-base font-medium"},"固件版本库")],-1)),s("div",S,[(a(!0),l(r,null,c(p.value,t=>(a(),l("div",{key:t.version,class:"p-6"},[s("div",A,[s("span",z,"版本: "+o(t.version),1),s("span",M,"发布日期: "+o(t.releaseDate),1),s("span",N,o(t.status),1)]),s("div",T," 文件大小: "+o(t.fileSize)+" 下载次数: "+o(t.downloads.toLocaleString())+"次 ",1),t.expanded&&t.md5?(a(),l(r,{key:0},[s("div",V," MD5: "+o(t.md5)+"SHA256: "+o(t.sha256),1),s("div",L," 适用型号: "+o(t.model)+" 硬件版本范围: "+o(t.hardwareRange)+" 升级类型: "+o(t.upgradeType),1),t.signed?(a(),l("div",j,[...e[3]||(e[3]=[s("span",{class:"text-sm",style:{color:"#52C41A"}},"数字签名: 已签名",-1)])])):m("",!0),t.notes&&t.notes.length?(a(),l("div",R,[e[4]||(e[4]=s("div",{class:"text-sm font-medium mb-1",style:{color:"rgba(0,0,0,0.85)"}},"发布说明:",-1)),s("ul",U,[(a(!0),l(r,null,c(t.notes,(n,x)=>(a(),l("li",{key:x},o(n),1))),128))])])):m("",!0),s("div",E,[e[6]||(e[6]=s("button",{class:"text-sm",style:{color:"#4a7c59"}},"下载",-1)),e[7]||(e[7]=s("button",{class:"text-sm",style:{color:"#4a7c59"}},"编辑",-1)),e[8]||(e[8]=s("button",{class:"text-sm",style:{color:"#4a7c59"}},"撤回发布",-1)),d(u,{to:"/firmware-upgrade",class:"text-sm",style:{color:"#4a7c59"}},{default:v(()=>[...e[5]||(e[5]=[i("查看升级任务",-1)])]),_:1})])],64)):(a(),l("div",G,[e[9]||(e[9]=s("button",{class:"text-sm",style:{color:"#4a7c59"}},"下载",-1)),s("button",{class:"text-sm",style:{color:"#4a7c59"},onClick:n=>t.expanded=!0},"查看详情",8,H)]))]))),128))])])])}}});export{J as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
import{d as o,a,b as e,t as l,o as r}from"./index-C5t0nGiQ.js";const n={class:"p-6"},d={class:"text-2xl font-semibold mb-4"},c=o({__name:"PlaceholderPage",props:{title:{}},setup(s){return(i,t)=>(r(),a("div",n,[e("h2",d,l(s.title),1),t[0]||(t[0]=e("div",{class:"bg-white p-6 rounded-lg",style:{"box-shadow":"0 1px 2px rgba(0,0,0,0.05)"}},[e("p",{style:{color:"rgba(0,0,0,0.45)"}},"此页面正在开发中...")],-1))]))}});export{c as default};

File diff suppressed because one or more lines are too long

1
dist/assets/RepairOrders-Cub_3lJ7.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/assets/RepairStats-DDRWP6tS.js vendored Normal file
View File

@ -0,0 +1 @@
import{D as x}from"./download-hBwFRgUo.js";import{d as u,a as l,b as e,f as m,u as g,j as v,F as n,r as d,o as r,n as c,t as o,s as y}from"./index-C5t0nGiQ.js";const f={class:"p-6"},h={class:"mb-6 flex items-center justify-between"},_={class:"px-4 py-2 rounded text-white flex items-center gap-2 text-sm",style:{"background-color":"#4a7c59"}},w={class:"bg-white p-4 rounded-lg mb-6",style:{"box-shadow":"0 1px 2px rgba(0,0,0,0.05)"}},k={class:"flex items-center gap-4"},D={class:"flex items-center gap-1"},C=["onClick"],F={class:"mb-6"},A={class:"grid grid-cols-4 gap-4"},j={class:"text-sm",style:{color:"rgba(0,0,0,0.45)"}},B={class:"grid grid-cols-2 gap-6 mb-6"},N={class:"bg-white p-6 rounded-lg",style:{"box-shadow":"0 1px 2px rgba(0,0,0,0.05)"}},S={class:"space-y-4"},T={class:"text-sm w-16 flex-shrink-0",style:{color:"rgba(0,0,0,0.65)"}},V={class:"flex-1 h-5 rounded",style:{"background-color":"#F5F5F5",overflow:"hidden"}},z={class:"text-sm w-14 text-right",style:{color:"rgba(0,0,0,0.85)"}},G=u({__name:"RepairStats",setup(E){const a=y("本月"),i=["本月","上月","本季度","本年度","自定义"],b=[{value:"156",label:"总工单数",color:"rgba(0,0,0,0.85)"},{value:"8",label:"处理中",color:"#4a7c59"},{value:"145",label:"已完成",color:"#52C41A"},{value:"3",label:"待处理",color:"#FF4D4F"}],p=[{name:"板卡故障",percent:65.5,color:"#4a7c59"},{name:"固件异常",percent:22.3,color:"#52C41A"},{name:"通信故障",percent:6.3,color:"#FAAD14"},{name:"其他",percent:5.9,color:"#8C8C8C"}];return(L,s)=>(r(),l("div",f,[e("div",h,[s[1]||(s[1]=e("h2",{class:"text-2xl font-semibold"},"维修统计",-1)),e("button",_,[m(g(x),{size:16}),s[0]||(s[0]=v(" 导出报告 ",-1))])]),e("div",w,[e("div",k,[s[2]||(s[2]=e("span",{class:"text-sm",style:{color:"rgba(0,0,0,0.65)"}},"时间范围:",-1)),s[3]||(s[3]=e("select",{class:"px-3 py-1.5 border rounded text-sm",style:{"border-color":"#D9D9D9","background-color":"#fff"}},[e("option",null,"本月")],-1)),e("div",D,[(r(),l(n,null,d(i,t=>e("button",{key:t,class:"px-4 py-1.5 rounded text-sm transition-colors",style:c({backgroundColor:a.value===t?"#4a7c59":"transparent",color:a.value===t?"#fff":"rgba(0,0,0,0.65)"}),onClick:R=>a.value=t},o(t),13,C)),64))])])]),e("div",F,[s[4]||(s[4]=e("h3",{class:"text-base font-medium mb-3",style:{color:"rgba(0,0,0,0.85)"}},"维修统计",-1)),e("div",A,[(r(),l(n,null,d(b,t=>e("div",{key:t.label,class:"bg-white p-5 rounded-lg",style:{"box-shadow":"0 1px 2px rgba(0,0,0,0.05)"}},[e("div",{class:"text-3xl font-semibold mb-1",style:c({color:t.color})},o(t.value),5),e("div",j,o(t.label),1)])),64))])]),e("div",B,[e("div",N,[s[5]||(s[5]=e("h3",{class:"text-base font-semibold mb-5"},"故障类型分布",-1)),e("div",S,[(r(),l(n,null,d(p,t=>e("div",{key:t.name,class:"flex items-center gap-3"},[e("span",T,o(t.name),1),e("div",V,[e("div",{class:"h-full rounded",style:c({width:t.percent+"%",backgroundColor:t.color})},null,4)]),e("span",z,o(t.percent)+"%",1)])),64))])]),s[6]||(s[6]=e("div",{class:"bg-white p-6 rounded-lg",style:{"box-shadow":"0 1px 2px rgba(0,0,0,0.05)"}},[e("h3",{class:"text-base font-semibold mb-5"},"维修趋势"),e("div",{class:"flex items-center justify-center h-40",style:{color:"rgba(0,0,0,0.25)"}},[e("div",{class:"text-center text-sm"},[e("div",null,"📊 维修趋势图表"),e("div",null,"1月-6月趋势")])])],-1))]),s[7]||(s[7]=e("div",{class:"bg-white p-4 rounded-lg flex items-center gap-3",style:{"box-shadow":"0 1px 2px rgba(0,0,0,0.05)"}},[e("button",{class:"px-4 py-2 rounded text-white text-sm",style:{"background-color":"#4a7c59"}},"导出报表"),e("button",{class:"px-4 py-2 rounded text-sm",style:{border:"1px solid #D9D9D9",color:"rgba(0,0,0,0.85)"}},"打印")],-1))]))}});export{G as default};

File diff suppressed because one or more lines are too long

6
dist/assets/arrow-left-De7UcLOo.js vendored Normal file
View File

@ -0,0 +1,6 @@
import{c as e}from"./index-C5t0nGiQ.js";/**
* @license lucide-vue-next v0.487.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const t=e("arrow-left",[["path",{d:"m12 19-7-7 7-7",key:"1l729n"}],["path",{d:"M19 12H5",key:"x3x0zl"}]]);export{t as A};

6
dist/assets/clock-CMNFmLxx.js vendored Normal file
View File

@ -0,0 +1,6 @@
import{c}from"./index-C5t0nGiQ.js";/**
* @license lucide-vue-next v0.487.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const o=c("clock",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["polyline",{points:"12 6 12 12 16 14",key:"68esgv"}]]);export{o as C};

6
dist/assets/download-hBwFRgUo.js vendored Normal file
View File

@ -0,0 +1,6 @@
import{c as o}from"./index-C5t0nGiQ.js";/**
* @license lucide-vue-next v0.487.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const a=o("download",[["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["polyline",{points:"7 10 12 15 17 10",key:"2ggqvy"}],["line",{x1:"12",x2:"12",y1:"15",y2:"3",key:"1vk2je"}]]);export{a as D};

91
dist/assets/index-C5t0nGiQ.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/assets/index-CRkCzBUD.css vendored Normal file

File diff suppressed because one or more lines are too long

6
dist/assets/info-DbTQmklK.js vendored Normal file
View File

@ -0,0 +1,6 @@
import{c}from"./index-C5t0nGiQ.js";/**
* @license lucide-vue-next v0.487.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const o=c("info",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 16v-4",key:"1dtifu"}],["path",{d:"M12 8h.01",key:"e9boi3"}]]);export{o as I};

6
dist/assets/plus-BfKzmzQH.js vendored Normal file
View File

@ -0,0 +1,6 @@
import{c as e}from"./index-C5t0nGiQ.js";/**
* @license lucide-vue-next v0.487.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const a=e("plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]);export{a as P};

6
dist/assets/search-BFqPyJ5C.js vendored Normal file
View File

@ -0,0 +1,6 @@
import{c}from"./index-C5t0nGiQ.js";/**
* @license lucide-vue-next v0.487.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const r=c("search",[["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}],["path",{d:"m21 21-4.3-4.3",key:"1qie3q"}]]);export{r as S};

6
dist/assets/server-hSQBXLWa.js vendored Normal file
View File

@ -0,0 +1,6 @@
import{c as e}from"./index-C5t0nGiQ.js";/**
* @license lucide-vue-next v0.487.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const y=e("server",[["rect",{width:"20",height:"8",x:"2",y:"2",rx:"2",ry:"2",key:"ngkwjq"}],["rect",{width:"20",height:"8",x:"2",y:"14",rx:"2",ry:"2",key:"iecqi9"}],["line",{x1:"6",x2:"6.01",y1:"6",y2:"6",key:"16zg32"}],["line",{x1:"6",x2:"6.01",y1:"18",y2:"18",key:"nzw8ys"}]]);export{y as S};

11
dist/assets/triangle-alert-DaaA5cmC.js vendored Normal file
View File

@ -0,0 +1,11 @@
import{c as e}from"./index-C5t0nGiQ.js";/**
* @license lucide-vue-next v0.487.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const a=e("circle-check",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]]);/**
* @license lucide-vue-next v0.487.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const r=e("triangle-alert",[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",key:"wmoenq"}],["path",{d:"M12 9v4",key:"juzpu7"}],["path",{d:"M12 17h.01",key:"p32p05"}]]);export{a as C,r as T};

6
dist/assets/upload-Cl9qkJl5.js vendored Normal file
View File

@ -0,0 +1,6 @@
import{c as o}from"./index-C5t0nGiQ.js";/**
* @license lucide-vue-next v0.487.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const a=o("upload",[["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["polyline",{points:"17 8 12 3 7 8",key:"t8dd8p"}],["line",{x1:"12",x2:"12",y1:"3",y2:"15",key:"widbto"}]]);export{a as U};

6
dist/assets/x-Cy9cTPf9.js vendored Normal file
View File

@ -0,0 +1,6 @@
import{c as e}from"./index-C5t0nGiQ.js";/**
* @license lucide-vue-next v0.487.0 - ISC
*
* This source code is licensed under the ISC license.
* See the LICENSE file in the root directory of this source tree.
*/const c=e("x",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]);export{c as X};

136
docs/flowchart.md Normal file
View File

@ -0,0 +1,136 @@
# 地空业务支撑平台 —— 生产管理子系统 流程图
```mermaid
flowchart TD
%% ===== 入口 =====
HOME["首页 Dashboard\n设备统计 · 待处理任务 · 快捷导航"]
%% ===== 阶段零:型号管理(核心枢纽)=====
subgraph S0["型号管理(核心枢纽)"]
MODEL["设备型号管理\n型号列表 · 在产/停产"]
MODEL -->|绑定| AUTH_FILE["授权文件\n按型号绑定"]
MODEL -->|绑定| CFG_FILE["配置文件\n按型号绑定"]
MODEL -->|绑定| FW_FILE["固件文件\n主机/主协板/发射板/采集板"]
MODEL -->|管理| CHECKLIST["装配Checklist模板\n按型号配置检查项"]
end
%% ===== 阶段一:固件与校准 =====
subgraph S1["阶段一:固件与校准"]
FW_LIB["固件库\n主机固件 · 主协板固件\n发射板固件 · 采集板固件"]
FW_LIB -->|上传zip| FW_UPLOAD["上传固件弹窗\n选择类型 · 版本号 · zip文件"]
FW_LIB -->|烧录采集板| CALIB["采集板校准记录\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子设备列表 · 维修历史"]
end
%% ===== 阶段三:授权出厂 =====
subgraph S3["阶段三:授权出厂"]
LIC_MGR["授权管理\n授权文件列表 · 按型号管理"]
LIC_MGR -->|选择授权项生成| LIC_DRAWER["授权项抽屉\n选择型号 · 勾选功能模块\n设置有效期 · 生成授权文件"]
end
%% ===== 阶段四APP使用非平台=====
subgraph S4["阶段四APP使用"]
APP["APP端操作"]
APP -->|读取UID/SN匹配| CALIB
APP -->|按型号下载| AUTH_FILE
APP -->|按型号下载| CFG_FILE
APP -->|按型号下载| FW_FILE
end
%% ===== 阶段五:配置管理 =====
subgraph S5["阶段五:配置管理"]
CFG_MGR["配置文件管理\n配置列表 · 筛选"]
CFG_MGR -->|新建配置抽屉| CFG_NEW["新建配置抽屉\n选择型号 · 发射参数\n采集参数 · 网络参数"]
CFG_MGR -->|编辑详情| PARAM_CFG["参数配置\n发射参数 · 采集参数\n保护参数 · 网络参数"]
end
%% ===== 阶段六:维修运维 =====
subgraph S6["阶段六:维修运维"]
REPAIR["维修工单列表\n筛选 · 状态 · 优先级"]
REPAIR -->|新建工单抽屉| NEW_ORDER["新建工单抽屉\n设备信息 · 故障信息 · 工单信息"]
REPAIR -->|处理抽屉| PROCESS["处理工单抽屉\n更换板卡 · 授权处理 · 报废申请"]
REPAIR -->|详情抽屉| ORDER_DETAIL["工单详情抽屉\n工单/设备/故障信息\n处理记录 · 板卡更换 · 授权处理"]
REPAIR -->|维修统计| REPAIR_STATS["维修统计\n故障分布 · 维修趋势"]
end
%% ===== 阶段七:报废回收 =====
subgraph S7["阶段七:报废回收"]
SCRAP["报废管理\n报废设备列表 · 审批流程"]
SCRAP -->|关联工单| ORDER_DETAIL
SCRAP -->|回收入库| DEV_REG
end
%% ===== 首页导航 =====
HOME -->|待校准设备| CALIB
HOME -->|待处理工单| REPAIR
HOME -->|授权即将到期| LIC_MGR
HOME -->|固件待升级| FW_LIB
HOME -->|设备总数| DEV_LIST
HOME --> MODEL
%% ===== 型号管理跨模块关联 =====
MODEL -->|授权按钮| LIC_MGR
MODEL -->|配置按钮| CFG_MGR
MODEL -->|固件按钮| FW_LIB
MODEL -->|Checklist模板| DEV_REG
%% ===== 跨阶段数据流 =====
DEV_REG -->|型号必须匹配| MODEL
DEV_DETAIL -->|查看校准| CALIB
DEV_DETAIL -->|查看维修历史| REPAIR
PROCESS -->|申请报废| SCRAP
ORDER_DETAIL -->|更换采集板需重新校准| CALIB
FW_LIB -->|关联型号固件| MODEL
%% ===== 支撑模块 =====
subgraph SUPPORT["支撑模块Header菜单"]
DATA_STATS["数据统计"]
OPS_RPT["运营报告"]
USER_MGR["用户管理"]
ROLE_MGR["角色权限"]
SYS_LOG["操作日志"]
SYS_SET["系统设置"]
end
%% 样式
style S0 fill:#F0F2F5,stroke:#8C8C8C,stroke-width:2px
style S1 fill:#F9F0FF,stroke:#722ED1,stroke-width:2px
style S2 fill:#eef5f0,stroke:#4a7c59,stroke-width:2px
style S3 fill:#E6FFFB,stroke:#13C2C2,stroke-width:2px
style S4 fill:#F6FFED,stroke:#52C41A,stroke-width:2px,stroke-dasharray: 5 5
style S5 fill:#FFF7E6,stroke:#FA8C16,stroke-width:2px
style S6 fill:#FFF1F0,stroke:#FF4D4F,stroke-width:2px
style S7 fill:#FAFAFA,stroke:#D9D9D9,stroke-width:2px
style SUPPORT fill:#FAFAFA,stroke:#D9D9D9,stroke-width:1px
style HOME fill:#4a7c59,color:#FFFFFF,stroke:#4a7c59
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菜单 | 页面(占位) |

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

View File

@ -0,0 +1,478 @@
<script setup lang="ts">
import { Plus, ArrowLeft } from 'lucide-vue-next'
import { useRouter } from 'vue-router'
import { ref } from 'vue'
const router = useRouter()
const showDrawer = ref(false)
const showProcessDrawer = ref(false)
const showDetailDrawer = ref(false)
const detailOrderId = ref('')
const detailRegenAuth = ref(true)
const detailPushFw = ref(false)
const openDetail = (id: string) => {
detailOrderId.value = id
detailRegenAuth.value = true
detailPushFw.value = false
showDetailDrawer.value = true
}
const processOrderId = ref('')
const processAction = ref('')
const processNote = ref('')
const replaceBoardOld = ref('')
const replaceBoardNew = ref('')
const regenAuth = ref(true)
const pushFw = ref(false)
const scrapReason = ref('')
const openProcess = (id: string) => {
processOrderId.value = id
processAction.value = ''
processNote.value = ''
replaceBoardOld.value = ''
replaceBoardNew.value = ''
regenAuth.value = true
pushFw.value = false
scrapReason.value = ''
showProcessDrawer.value = true
}
const deviceSN = ref('GD30-2024-000056')
const faultType = ref('板卡故障')
const faultDesc = ref('')
const faultPhenomenon = ref('')
const priority = ref('低')
const assignee = ref('李工')
const estimatedDate = ref('2024-02-28')
const note = ref('')
interface RepairOrder {
id: string
sn: string
fault: string
status: '处理中' | '已处理' | '待处理'
priority: '高' | '中' | '低'
}
const orders: RepairOrder[] = [
{ id: 'WO-2024-0001', sn: 'GD30-xxxx', fault: '板卡故障', status: '处理中', priority: '高' },
{ id: 'WO-2024-0002', sn: 'GD30-xxxx', fault: '固件异常', status: '已处理', priority: '中' },
{ id: 'WO-2024-0003', sn: 'GD30-xxxx', fault: '通信异常', status: '待处理', priority: '低' },
]
const statusStyle = (s: RepairOrder['status']) => {
if (s === '处理中') return { backgroundColor: '#eef5f0', color: '#4a7c59', border: '1px solid #a3c4ad' }
if (s === '已处理') return { backgroundColor: '#F6FFED', color: '#52C41A', border: '1px solid #B7EB8F' }
return { backgroundColor: '#FAFAFA', color: 'rgba(0,0,0,0.65)', border: '1px solid #D9D9D9' }
}
const statusIcon = (s: RepairOrder['status']) => {
if (s === '处理中') return '\u{1F527}'
if (s === '已处理') return '\u2705'
return '\u23F3'
}
const priorityStyle = (p: RepairOrder['priority']) => {
if (p === '高') return { backgroundColor: '#FFF1F0', color: '#FF4D4F', border: '1px solid #FFCCC7' }
if (p === '中') return { backgroundColor: '#FFF7E6', color: '#FA8C16', border: '1px solid #FFD591' }
return { backgroundColor: '#F0F5FF', color: '#597EF7', border: '1px solid #ADC6FF' }
}
</script>
<template>
<div class="p-6">
<!-- Page 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 text-white flex items-center gap-2" style="background-color: #4a7c59" @click="showDrawer = true">
<Plus :size="16" /> 新建工单
</button>
<button class="px-4 py-2 rounded flex items-center gap-2" style="border: 1px solid #D9D9D9; color: rgba(0,0,0,0.85)" @click="router.push('/repair/stats')">
维修统计
</button>
</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)">
<h3 class="text-base font-medium mb-4">筛选条件</h3>
<div class="grid grid-cols-6 gap-4 mb-4">
<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>全部</option><option>处理中</option><option>已处理</option><option>待处理</option>
</select>
</div>
<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>全部</option><option></option><option></option><option></option>
</select>
</div>
<div class="col-span-2">
<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>全部</option><option>王工程师</option><option>李工程师</option><option>张工程师</option>
</select>
</div>
</div>
<div class="grid grid-cols-6 gap-4 items-end">
<div>
<label class="block text-sm mb-1" style="color: rgba(0,0,0,0.65)">创建日期:</label>
<input type="date" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9" placeholder="开始日期" />
</div>
<div class="flex items-center justify-center pt-5" style="color: rgba(0,0,0,0.45)">~</div>
<div>
<label class="block text-sm mb-1 opacity-0">.</label>
<input type="date" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9" placeholder="结束日期" />
</div>
<div>
<label class="block text-sm mb-1" style="color: rgba(0,0,0,0.65)">SN:</label>
<input type="text" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9" placeholder="搜索" />
</div>
<div>
<button class="w-full px-4 py-2 rounded text-white text-sm" style="background-color: #4a7c59">搜索</button>
</div>
<div>
<button class="w-full px-4 py-2 rounded text-sm" style="border: 1px solid #D9D9D9; color: rgba(0,0,0,0.65)">重置</button>
</div>
</div>
</div>
<!-- Order List -->
<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" style="border-color: #F0F0F0">
<span class="text-base font-semibold">维修工单列表</span>
<span class="text-sm ml-2" style="color: rgba(0,0,0,0.45)"> 156 个工单</span>
</div>
<div class="divide-y" style="border-color: #F0F0F0">
<div v-for="order in orders" :key="order.id" class="px-6 py-5">
<!-- Order info row -->
<div class="flex items-center gap-4 mb-3">
<span class="font-semibold">{{ order.id }}</span>
<span class="text-sm" style="color: rgba(0,0,0,0.65)">{{ order.sn }}</span>
<span class="text-sm" style="color: rgba(0,0,0,0.65)">{{ order.fault }}</span>
<span class="px-2 py-0.5 rounded text-xs inline-flex items-center gap-1" :style="statusStyle(order.status)">
{{ statusIcon(order.status) }}{{ order.status }}
</span>
<span class="px-2 py-0.5 rounded text-xs" :style="priorityStyle(order.priority)">
{{ order.priority }}
</span>
</div>
<!-- Actions row -->
<div class="flex items-center gap-1 text-sm">
<span style="color: rgba(0,0,0,0.45)">操作:</span>
<button style="color: #4a7c59" class="px-2" @click="openDetail(order.id)">详情</button>
<template v-if="order.status === '处理中'">
<button style="color: #4a7c59" class="px-2" @click="openProcess(order.id)">处理</button>
</template>
<template v-else-if="order.status === '已处理'">
</template>
<template v-else>
<button style="color: #4a7c59" class="px-2" @click="openProcess(order.id)">处理</button>
</template>
</div>
</div>
</div>
</div>
<!-- Pagination -->
<div class="bg-white p-4 rounded-lg flex items-center justify-between" style="box-shadow: 0 1px 2px rgba(0,0,0,0.05)">
<div class="text-sm" style="color: rgba(0,0,0,0.65)">显示 1-10 / 156 </div>
<div class="flex items-center gap-2">
<button class="px-3 py-1 rounded border text-sm" style="border-color: #D9D9D9; color: rgba(0,0,0,0.45)" disabled>&lt; 上一页</button>
<button class="px-3 py-1 rounded text-sm" style="background-color: #4a7c59; color: #fff">1</button>
<button class="px-3 py-1 rounded border text-sm" style="border-color: #D9D9D9; color: rgba(0,0,0,0.85)">2</button>
<button class="px-3 py-1 rounded border text-sm" style="border-color: #D9D9D9; color: rgba(0,0,0,0.85)">3</button>
<span style="color: rgba(0,0,0,0.45)">...</span>
<button class="px-3 py-1 rounded border text-sm" style="border-color: #D9D9D9; color: rgba(0,0,0,0.85)">16</button>
<button class="px-3 py-1 rounded border text-sm" style="border-color: #D9D9D9; color: rgba(0,0,0,0.85)">下一页 &gt;</button>
</div>
</div>
<!-- New Repair Order Drawer -->
<div v-if="showDrawer" class="fixed inset-0 z-50 flex justify-end" style="background-color: rgba(0,0,0,0.45)" @click.self="showDrawer = false">
<div class="bg-white w-[520px] h-full flex flex-col" style="box-shadow: -4px 0 12px rgba(0,0,0,0.1)">
<!-- Header -->
<div class="flex items-center gap-3 p-5 border-b" style="border-color: #F0F0F0">
<button @click="showDrawer = 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>
<!-- Body -->
<div class="flex-1 overflow-y-auto p-6 space-y-6">
<!-- 设备信息 -->
<div class="p-5 rounded-lg" style="background-color: #FAFAFA; border: 1px solid #F0F0F0">
<h4 class="text-base font-semibold mb-4">设备信息</h4>
<div class="space-y-3">
<div class="flex items-center gap-4">
<div class="flex-1">
<label class="block text-sm mb-1" style="color: rgba(0,0,0,0.65)">设备SN:</label>
<select v-model="deviceSN" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
<option>GD30-2024-000056</option>
<option>GD30-2024-000078</option>
<option>GT20-2024-000012</option>
</select>
</div>
<button class="text-sm mt-5" style="color: #4a7c59">查找设备</button>
</div>
<div class="text-sm space-y-1" style="color: rgba(0,0,0,0.65)">
<div>设备型号GD30-Supreme</div>
<div>当前固件v2.2.0</div>
<div>硬件版本A1</div>
<div>客户XX地质勘探公司</div>
</div>
</div>
</div>
<!-- 故障信息 -->
<div class="p-5 rounded-lg" style="background-color: #FAFAFA; border: 1px solid #F0F0F0">
<h4 class="text-base font-semibold mb-4">故障信息</h4>
<div class="space-y-4">
<div>
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.65)">故障类型:</label>
<div class="space-y-1.5">
<label v-for="ft in ['板卡故障', '固件异常', '通信故障', '其他问题']" :key="ft" class="flex items-center gap-2 cursor-pointer text-sm">
<input type="radio" v-model="faultType" :value="ft" style="accent-color: #4a7c59" />
<span>{{ ft }}</span>
</label>
</div>
</div>
<div>
<label class="block text-sm mb-1" style="color: rgba(0,0,0,0.65)">故障描述:</label>
<textarea v-model="faultDesc" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; min-height: 70px; resize: vertical" placeholder="请输入故障描述..."></textarea>
</div>
<div>
<label class="block text-sm mb-1" style="color: rgba(0,0,0,0.65)">故障现象:</label>
<textarea v-model="faultPhenomenon" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; min-height: 70px; resize: vertical" placeholder="请输入故障现象..."></textarea>
</div>
</div>
</div>
<!-- 工单信息 -->
<div class="p-5 rounded-lg" style="background-color: #FAFAFA; border: 1px solid #F0F0F0">
<h4 class="text-base font-semibold mb-4">工单信息</h4>
<div class="space-y-4">
<div>
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.65)">优先级</label>
<div class="space-y-1.5">
<label v-for="p in ['低', '中', '高']" :key="p" class="flex items-center gap-2 cursor-pointer text-sm">
<input type="radio" v-model="priority" :value="p" style="accent-color: #4a7c59" />
<span :style="{ color: p === '高' ? '#FF4D4F' : p === '中' ? '#FA8C16' : '#597EF7' }">{{ p }}</span>
</label>
</div>
</div>
<div>
<label class="block text-sm mb-1" style="color: rgba(0,0,0,0.65)">维修人员:</label>
<select v-model="assignee" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
<option>李工</option><option>王工</option><option>张工</option>
</select>
</div>
<div>
<label class="block text-sm mb-1" style="color: rgba(0,0,0,0.65)">预计修复时间:</label>
<input type="date" v-model="estimatedDate" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9" />
</div>
<div>
<label class="block text-sm mb-1" style="color: rgba(0,0,0,0.65)">备注:</label>
<textarea v-model="note" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; min-height: 60px; resize: vertical" placeholder="请输入备注信息..."></textarea>
</div>
</div>
</div>
</div>
<!-- Footer -->
<div class="flex items-center justify-center gap-4 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="showDrawer = false">取消</button>
<button class="px-6 py-2 rounded text-white text-sm" style="background-color: #4a7c59" @click="showDrawer = false">创建工单</button>
</div>
</div>
</div>
<!-- Process Order Drawer -->
<div v-if="showProcessDrawer" class="fixed inset-0 z-50 flex justify-end" style="background-color: rgba(0,0,0,0.45)" @click.self="showProcessDrawer = false">
<div class="bg-white w-[520px] 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="showProcessDrawer = 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">处理工单 {{ processOrderId }}</h3>
</div>
<div class="flex-1 overflow-y-auto p-6 space-y-6">
<!-- 处理操作 -->
<div class="p-5 rounded-lg" style="background-color: #FAFAFA; border: 1px solid #F0F0F0">
<h4 class="text-base font-semibold mb-4">处理操作</h4>
<div class="space-y-2">
<label v-for="a in ['更换板卡', '固件修复', '参数重置', '其他处理']" :key="a" class="flex items-center gap-2 cursor-pointer text-sm">
<input type="radio" v-model="processAction" :value="a" style="accent-color: #4a7c59" />
<span>{{ a }}</span>
</label>
</div>
</div>
<!-- 板卡更换条件显示 -->
<div v-if="processAction === '更换板卡'" class="p-5 rounded-lg" style="background-color: #FAFAFA; border: 1px solid #F0F0F0">
<h4 class="text-base font-semibold mb-4">板卡更换</h4>
<div class="space-y-3">
<div>
<label class="block text-sm mb-1" style="color: rgba(0,0,0,0.65)">原板卡SN:</label>
<input v-model="replaceBoardOld" type="text" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9" placeholder="如 AC-2024-0001" />
</div>
<div>
<label class="block text-sm mb-1" style="color: rgba(0,0,0,0.65)">新板卡SN:</label>
<input v-model="replaceBoardNew" type="text" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9" placeholder="如 AC-2024-0003" />
</div>
</div>
</div>
<!-- 授权处理 -->
<div class="p-5 rounded-lg" style="background-color: #FAFAFA; border: 1px solid #F0F0F0">
<h4 class="text-base font-semibold mb-4">授权处理</h4>
<div class="space-y-2">
<label class="flex items-center gap-2 cursor-pointer text-sm">
<input type="checkbox" v-model="regenAuth" class="w-4 h-4" style="accent-color: #4a7c59" />
<span>重新生成授权文件</span>
</label>
<label class="flex items-center gap-2 cursor-pointer text-sm">
<input type="checkbox" v-model="pushFw" class="w-4 h-4" style="accent-color: #4a7c59" />
<span>推送适配固件</span>
</label>
</div>
</div>
<!-- 处理备注 -->
<div class="p-5 rounded-lg" style="background-color: #FAFAFA; border: 1px solid #F0F0F0">
<h4 class="text-base font-semibold mb-4">处理备注</h4>
<textarea v-model="processNote" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; min-height: 80px; resize: vertical" placeholder="请输入处理说明..."></textarea>
</div>
<!-- 报废处理 -->
<div class="p-5 rounded-lg" style="background-color: #FFF1F0; border: 1px solid #FFCCC7">
<h4 class="text-base font-semibold mb-2" style="color: #CF1322">报废处理</h4>
<p class="text-xs mb-3" style="color: #FF4D4F">如设备无法修复可申请报废报废操作不可逆请谨慎操作</p>
<div class="mb-3">
<label class="block text-sm mb-1" style="color: rgba(0,0,0,0.65)">报废原因:</label>
<select v-model="scrapReason" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
<option value="">请选择报废原因</option>
<option>主板损坏无法修复</option>
<option>多个核心部件损坏</option>
<option>维修成本超过设备价值</option>
<option>设备老化严重</option>
</select>
</div>
<button
class="px-4 py-2 rounded text-sm"
:style="{ border: '1px solid #FF4D4F', color: scrapReason ? '#FF4D4F' : '#D9D9D9' }"
:disabled="!scrapReason"
@click="showProcessDrawer = false; router.push('/scrap')"
>申请报废</button>
</div>
</div>
<div class="flex items-center justify-center gap-4 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="showProcessDrawer = false">取消</button>
<button class="px-6 py-2 rounded text-white text-sm" style="background-color: #4a7c59" @click="showProcessDrawer = false">提交处理</button>
</div>
</div>
</div>
<!-- Detail Drawer -->
<div v-if="showDetailDrawer" class="fixed inset-0 z-50 flex justify-end" style="background-color: rgba(0,0,0,0.45)" @click.self="showDetailDrawer = false">
<div class="bg-white w-[540px] 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="showDetailDrawer = 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>
<span class="text-sm" style="color: rgba(0,0,0,0.45)">{{ detailOrderId }}</span>
</div>
<div class="flex-1 overflow-y-auto p-6 space-y-5">
<!-- 工单信息 -->
<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="space-y-2 text-sm" style="color: rgba(0,0,0,0.65)">
<div>工单号{{ detailOrderId }}</div>
<div>状态<span class="px-2 py-0.5 rounded text-xs" style="background-color: #eef5f0; color: #4a7c59; border: 1px solid #a3c4ad">🔧 处理中</span></div>
<div>优先级<span style="color: #FF4D4F"></span></div>
<div>创建时间2024-02-25 14:30:00</div>
<div>维修人员李工</div>
<div>预计修复2024-02-28</div>
</div>
</div>
<!-- 设备信息 -->
<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="space-y-2 text-sm" style="color: rgba(0,0,0,0.65)">
<div>设备SNGD30-2024-000056</div>
<div>设备型号GD30-Supreme</div>
<div>当前固件v2.2.0</div>
<div>硬件版本A1</div>
<div>客户XX地质勘探公司</div>
</div>
</div>
<!-- 故障信息 -->
<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="space-y-2 text-sm" style="color: rgba(0,0,0,0.65)">
<div>故障类型板卡故障</div>
<div>故障描述采集板AC-2024-0001无法正常工作引起数据采集异常</div>
<div>故障现象启动后采集板指示灯不亮无法获取测量数据</div>
</div>
</div>
<!-- 处理记录 -->
<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="space-y-2 text-sm" style="color: rgba(0,0,0,0.65)">
<div>2024-02-25 14:35:00 李工 接单初步诊断</div>
<div>2024-02-25 15:00:00 李工 确认板卡故障准备更换</div>
<div>2024-02-26 09:30:00 李工 更换板卡 AC-2024-0001AC-2024-0003</div>
<div>2024-02-26 10:00:00 李工 板卡更换完成设备恢复正常</div>
</div>
<button class="text-sm mt-2" style="color: #4a7c59">添加记录</button>
</div>
<!-- 板卡更换记录 -->
<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="space-y-2 text-sm" style="color: rgba(0,0,0,0.65)">
<div>原板卡AC-2024-0001 硬件版本: A1 状态: <span style="color: #FA8C16">已换下</span></div>
<div>新板卡AC-2024-0003 硬件版本: A1 状态: <span class="px-1.5 py-0.5 rounded text-xs" style="background-color: #F6FFED; color: #52C41A; border: 1px solid #B7EB8F"> 正常</span></div>
<div>更换时间2024-02-26 09:30:00</div>
<div>更换人员李工</div>
</div>
</div>
<!-- 授权处理 -->
<div class="p-5 rounded-lg" style="background-color: #FAFAFA; border: 1px solid #F0F0F0">
<h4 class="text-base font-semibold mb-3">授权处理</h4>
<p class="text-sm mb-3" style="color: rgba(0,0,0,0.65)">板卡更换后需要重新生成授权文件</p>
<div class="space-y-2">
<label class="flex items-center gap-2 cursor-pointer text-sm">
<input type="checkbox" v-model="detailRegenAuth" class="w-4 h-4" style="accent-color: #4a7c59" />
重新生成授权文件
</label>
<label class="flex items-center gap-2 cursor-pointer text-sm" style="color: rgba(0,0,0,0.65)">
<input type="checkbox" v-model="detailPushFw" class="w-4 h-4" style="accent-color: #4a7c59" />
推送适配固件
</label>
</div>
<button class="mt-3 px-4 py-2 rounded text-white text-sm" style="background-color: #4a7c59">处理授权</button>
</div>
</div>
<div class="flex items-center justify-center gap-4 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="showDetailDrawer = false">取消</button>
<button class="px-6 py-2 rounded text-white text-sm" style="background-color: #4a7c59" @click="showDetailDrawer = false">关闭工单</button>
</div>
</div>
</div>
</div>
</template>

View File

@ -0,0 +1,95 @@
<script setup lang="ts">
import { Download } from 'lucide-vue-next'
import { ref } from 'vue'
const activeTab = ref('本月')
const tabs = ['本月', '上月', '本季度', '本年度', '自定义']
const stats = [
{ value: '156', label: '总工单数', color: 'rgba(0,0,0,0.85)' },
{ value: '8', label: '处理中', color: '#4a7c59' },
{ value: '145', label: '已完成', color: '#52C41A' },
{ value: '3', label: '待处理', color: '#FF4D4F' },
]
const faultTypes = [
{ name: '板卡故障', percent: 65.5, color: '#4a7c59' },
{ name: '固件异常', percent: 22.3, color: '#52C41A' },
{ name: '通信故障', percent: 6.3, color: '#FAAD14' },
{ name: '其他', percent: 5.9, color: '#8C8C8C' },
]
</script>
<template>
<div class="p-6">
<!-- Page Header -->
<div class="mb-6 flex items-center justify-between">
<h2 class="text-2xl font-semibold">维修统计</h2>
<button class="px-4 py-2 rounded text-white flex items-center gap-2 text-sm" style="background-color: #4a7c59">
<Download :size="16" /> 导出报告
</button>
</div>
<!-- Time Filter -->
<div class="bg-white p-4 rounded-lg mb-6" style="box-shadow: 0 1px 2px rgba(0,0,0,0.05)">
<div class="flex items-center gap-4">
<span class="text-sm" style="color: rgba(0,0,0,0.65)">时间范围:</span>
<select class="px-3 py-1.5 border rounded text-sm" style="border-color: #D9D9D9; background-color: #fff">
<option>本月</option>
</select>
<div class="flex items-center gap-1">
<button
v-for="tab in tabs" :key="tab"
class="px-4 py-1.5 rounded text-sm transition-colors"
:style="{
backgroundColor: activeTab === tab ? '#4a7c59' : 'transparent',
color: activeTab === tab ? '#fff' : 'rgba(0,0,0,0.65)',
}"
@click="activeTab = tab"
>{{ tab }}</button>
</div>
</div>
</div>
<!-- Stats Summary -->
<div class="mb-6">
<h3 class="text-base font-medium mb-3" style="color: rgba(0,0,0,0.85)">维修统计</h3>
<div class="grid grid-cols-4 gap-4">
<div v-for="s in stats" :key="s.label" class="bg-white p-5 rounded-lg" style="box-shadow: 0 1px 2px rgba(0,0,0,0.05)">
<div class="text-3xl font-semibold mb-1" :style="{ color: s.color }">{{ s.value }}</div>
<div class="text-sm" style="color: rgba(0,0,0,0.45)">{{ s.label }}</div>
</div>
</div>
</div>
<!-- Charts Row -->
<div class="grid grid-cols-2 gap-6 mb-6">
<!-- Fault Distribution -->
<div class="bg-white p-6 rounded-lg" style="box-shadow: 0 1px 2px rgba(0,0,0,0.05)">
<h3 class="text-base font-semibold mb-5">故障类型分布</h3>
<div class="space-y-4">
<div v-for="ft in faultTypes" :key="ft.name" class="flex items-center gap-3">
<span class="text-sm w-16 flex-shrink-0" style="color: rgba(0,0,0,0.65)">{{ ft.name }}</span>
<div class="flex-1 h-5 rounded" style="background-color: #F5F5F5; overflow: hidden">
<div class="h-full rounded" :style="{ width: ft.percent + '%', backgroundColor: ft.color }"></div>
</div>
<span class="text-sm w-14 text-right" style="color: rgba(0,0,0,0.85)">{{ ft.percent }}%</span>
</div>
</div>
</div>
<!-- Trend Placeholder -->
<div class="bg-white p-6 rounded-lg" style="box-shadow: 0 1px 2px rgba(0,0,0,0.05)">
<h3 class="text-base font-semibold mb-5">维修趋势</h3>
<div class="flex items-center justify-center h-40" style="color: rgba(0,0,0,0.25)">
<div class="text-center text-sm">
<div>📊 维修趋势图表</div>
<div>1-6月趋势</div>
</div>
</div>
</div>
</div>
</div>
</template>