更新维修统计进入页面

This commit is contained in:
徐星 2026-04-02 10:35:15 +08:00
parent cce893db83
commit 7f5d01a4d3
8 changed files with 111 additions and 303 deletions

View File

@ -48,7 +48,6 @@ const menuGroups = [
]},
{ title: '授权', items: [
{ path: '/licenses', label: '授权管理', icon: Key },
{ path: '/activation', label: '激活管理', icon: Key },
]},
{ title: '配置', items: [
{ path: '/config-files', label: '配置管理', icon: FileCode },
@ -58,7 +57,6 @@ const menuGroups = [
]},
{ title: '维修', items: [
{ path: '/repair', label: '维修工单', icon: Wrench },
{ path: '/repair/stats', label: '维修统计', icon: Wrench },
{ path: '/scrap', label: '报废回收', icon: Wrench },
]},
]

View File

@ -1,247 +0,0 @@
<script setup lang="ts">
import { Info } from 'lucide-vue-next'
interface ActivationRecord {
sn: string
model: string
activationDate: string
status: '已激活' | '激活失败' | '待激活'
operator: string
}
const activationRecords: ActivationRecord[] = [
{
sn: 'GD30-2025-000001',
model: 'GD30 高密度电法仪',
activationDate: '2025-02-10 14:30',
status: '已激活',
operator: '王工程师',
},
{
sn: 'GT20-2025-000045',
model: 'GT20 瞬变电磁仪',
activationDate: '2025-02-09 10:15',
status: '已激活',
operator: '李工程师',
},
{
sn: 'GM10-2025-000023',
model: 'GM10 大地电磁仪',
activationDate: '2025-02-08 16:20',
status: '待激活',
operator: '张工程师',
},
]
const getStatusStyle = (status: ActivationRecord['status']) => {
switch (status) {
case '已激活':
return {
backgroundColor: '#F6FFED',
color: '#52C41A',
border: '1px solid #B7EB8F',
}
case '激活失败':
return {
backgroundColor: '#FFF1F0',
color: '#FF4D4F',
border: '1px solid #FFCCC7',
}
case '待激活':
return {
backgroundColor: '#FFFBE6',
color: '#FAAD14',
border: '1px solid #FFE58F',
}
}
}
</script>
<template>
<div class="p-6">
<!-- Page Header -->
<div class="mb-6">
<h2 class="text-2xl font-semibold mb-1">激活管理</h2>
<p class="text-sm" 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="font-medium mb-1">APP激活流程说明</div>
<div class="text-sm">
APP读取设备UID 与后台SN匹配 下载授权文件和配置 生成设备配置
激活设备激活成功后设备每次开机会自动检测授权文件更新
</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">
<div>
<label class="block text-sm mb-2" style="color: rgba(0, 0, 0, 0.65)">设备SN号</label>
<input
type="text"
class="w-full px-3 py-2 border rounded"
style="border-color: #D9D9D9"
placeholder="输入设备SN号搜索"
/>
</div>
<div>
<label class="block text-sm mb-2" style="color: rgba(0, 0, 0, 0.65)">激活状态</label>
<select
class="w-full px-3 py-2 border rounded"
style="border-color: #D9D9D9; background-color: #fff"
>
<option>全部</option>
<option>已激活</option>
<option>待激活</option>
<option>激活失败</option>
</select>
</div>
<div>
<label class="block text-sm mb-2" style="color: rgba(0, 0, 0, 0.65)">激活日期</label>
<input
type="date"
class="w-full px-3 py-2 border rounded"
style="border-color: #D9D9D9"
/>
</div>
<div class="flex items-end">
<button class="w-full px-4 py-2 rounded text-white" style="background-color: #4a7c59">
查询
</button>
</div>
</div>
</div>
<!-- Activation Records 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">
<h3 class="text-lg font-semibold">激活记录</h3>
</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)">设备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>
</tr>
</thead>
<tbody>
<tr
v-for="(record, index) in activationRecords"
:key="index"
class="border-b"
style="border-color: #F0F0F0"
>
<td class="px-6 py-4">{{ record.sn }}</td>
<td class="px-6 py-4" style="color: rgba(0, 0, 0, 0.65)">{{ record.model }}</td>
<td class="px-6 py-4" style="color: rgba(0, 0, 0, 0.65)">{{ record.activationDate }}</td>
<td class="px-6 py-4" style="color: rgba(0, 0, 0, 0.65)">{{ record.operator }}</td>
<td class="px-6 py-4">
<span class="px-2 py-1 rounded text-xs" :style="getStatusStyle(record.status)">
{{ record.status }}
</span>
</td>
<td class="px-6 py-4">
<div class="flex items-center gap-3">
<button class="text-sm" style="color: #4a7c59">详情</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Activation Process Card -->
<div class="bg-white p-6 rounded-lg mb-4" style="box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05)">
<h3 class="text-lg font-semibold mb-6">APP激活流程 &amp; 开机授权检测</h3>
<div class="flex items-center justify-between gap-6">
<div
class="flex-1 p-6 rounded-lg text-center"
style="background-color: #eef5f0; border: 2px solid #4a7c59"
>
<div
class="w-12 h-12 rounded-full flex items-center justify-center mx-auto mb-3"
style="background-color: #4a7c59; color: #fff; font-size: 20px; font-weight: bold"
></div>
<div class="font-medium mb-2" style="color: #4a7c59">读取UID</div>
<div class="text-sm" style="color: #2d5a3d">APP读取设备UID</div>
</div>
<div style="color: #D9D9D9; font-size: 24px"></div>
<div
class="flex-1 p-6 rounded-lg text-center"
style="background-color: #F9F0FF; border: 2px solid #722ED1"
>
<div
class="w-12 h-12 rounded-full flex items-center justify-center mx-auto mb-3"
style="background-color: #722ED1; color: #fff; font-size: 20px; font-weight: bold"
></div>
<div class="font-medium mb-2" style="color: #722ED1">SN匹配</div>
<div class="text-sm" style="color: #531DAB">后台SN匹配验证</div>
</div>
<div style="color: #D9D9D9; font-size: 24px"></div>
<div
class="flex-1 p-6 rounded-lg text-center"
style="background-color: #F6FFED; border: 2px solid #52C41A"
>
<div
class="w-12 h-12 rounded-full flex items-center justify-center mx-auto mb-3"
style="background-color: #52C41A; color: #fff; font-size: 20px; font-weight: bold"
></div>
<div class="font-medium mb-2" style="color: #52C41A">下载文件</div>
<div class="text-sm" style="color: #389E0D">下载授权和配置</div>
</div>
<div style="color: #D9D9D9; font-size: 24px"></div>
<div
class="flex-1 p-6 rounded-lg text-center"
style="background-color: #FFFBE6; border: 2px solid #FAAD14"
>
<div
class="w-12 h-12 rounded-full flex items-center justify-center mx-auto mb-3"
style="background-color: #FAAD14; color: #fff; font-size: 20px; font-weight: bold"
></div>
<div class="font-medium mb-2" style="color: #FAAD14">生成配置</div>
<div class="text-sm" style="color: #D46B08">生成设备配置文件</div>
</div>
<div style="color: #D9D9D9; font-size: 24px"></div>
<div
class="flex-1 p-6 rounded-lg text-center"
style="background-color: #E6FFFB; border: 2px solid #13C2C2"
>
<div
class="w-12 h-12 rounded-full flex items-center justify-center mx-auto mb-3"
style="background-color: #13C2C2; color: #fff; font-size: 20px; font-weight: bold"
></div>
<div class="font-medium mb-2" style="color: #13C2C2">激活设备</div>
<div class="text-sm" style="color: #006D75">完成激活流程</div>
</div>
</div>
</div>
<!-- Boot Auth Check Banner -->
<div
class="p-4 rounded-lg flex items-start gap-3"
style="background-color: #FFFBE6; border: 1px solid #FFE58F"
>
<Info :size="20" style="color: #FAAD14; flex-shrink: 0; margin-top: 2px" />
<div style="color: #D46B08">
<div class="font-medium">开机授权检测机制</div>
<div class="text-sm mt-1">
设备每次开机 APP自动连接 检测授权文件是否有更新 有更新则自动下载
</div>
</div>
</div>
</div>
</template>

View File

@ -87,7 +87,7 @@ const metrics = [
{ label: '维修中', value: '23', trend: 'down' as const, trendValue: '-12.3%', color: '#FF4D4F', icon: Wrench, link: '/repair' },
{ label: '报废', value: '56', color: '#FA8C16', icon: Target, link: '/scrap' },
{ label: '授权即将到期', value: '45', color: '#FAAD14', icon: Clock, link: '/licenses' },
{ label: '升级', value: '8', color: '#13C2C2', icon: Upload, link: '/firmware' },
{ label: '升级', value: '8', color: '#13C2C2', icon: Upload, link: '/firmware' },
]
const deviceStatusData = [
@ -116,8 +116,8 @@ const taskGroups = [
{
title: '固件升级通知', count: 8, link: '/firmware',
tasks: [
{ deviceSN: 'SN2024030710', description: '固件版本v2.3.5可用', time: '1天前', link: '/firmware' },
{ deviceSN: 'SN2024030711', description: '固件版本v2.3.5可用', time: '1天前', link: '/firmware' },
{ deviceSN: 'GD-30 Supreme', description: '固件版本v2.3.5可用', time: '1天前', link: '/firmware' },
{ deviceSN: 'GD-10 Supreme', description: '固件版本v2.3.5可用', time: '1天前', link: '/firmware' },
],
},
{

View File

@ -68,28 +68,6 @@
</div>
</div>
<!-- Hardware Topology 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-lg font-semibold mb-6">硬件拓扑</h3>
<div class="flex items-center justify-center gap-6">
<template v-for="(board, i) in boards" :key="board.name">
<div class="flex items-center gap-6">
<div class="text-center">
<div
class="w-28 h-28 rounded-lg flex flex-col items-center justify-center mb-3"
:style="{ backgroundColor: board.bg, border: '2px solid ' + board.color }"
>
<div class="text-base font-semibold" :style="{ color: board.color }">{{ board.name }}</div>
<div class="text-xs mt-2" :style="{ color: board.color }">{{ board.version }}</div>
</div>
<div class="text-xs" style="color: rgba(0, 0, 0, 0.45)">{{ board.sn }}</div>
</div>
<div v-if="Number(i) < boards.length - 1" style="color: #D9D9D9; font-size: 24px"></div>
</div>
</template>
</div>
</div>
<!-- License Info 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-lg font-semibold mb-6">授权信息</h3>
@ -100,12 +78,7 @@
</div>
<div>
<div class="text-sm mb-2" style="color: rgba(0, 0, 0, 0.45)">授权状态</div>
<span
class="inline-block px-2 py-1 rounded text-xs"
style="background-color: #F6FFED; color: #52C41A; border: 1px solid #B7EB8F"
>
已激活
</span>
<span class="inline-block px-2 py-1 rounded text-xs" style="background-color: #F6FFED; color: #52C41A; border: 1px solid #B7EB8F">已激活</span>
</div>
<div>
<div class="text-sm mb-2" style="color: rgba(0, 0, 0, 0.45)">授权类型</div>
@ -123,15 +96,92 @@
<div class="text-sm mb-2" style="color: rgba(0, 0, 0, 0.45)">剩余天数</div>
<div style="color: #52C41A">317</div>
</div>
<div>
<div class="text-sm mb-2" style="color: rgba(0, 0, 0, 0.45)">授权功能模块</div>
<div>1D(SP/VES/IP) / 2D(SP/ERT/IP) / 3D(SP/ERT/IP)</div>
</div>
<div>
<div class="text-sm mb-2" style="color: rgba(0, 0, 0, 0.45)">授权文件</div>
<!-- 授权模块列表 -->
<div class="mt-6 pt-4" style="border-top: 1px solid #F0F0F0">
<div class="text-sm font-medium mb-3">授权功能模块</div>
<div class="flex flex-wrap gap-2">
<span v-for="m in authModules" :key="m" class="px-3 py-1 rounded text-xs" style="background-color: #eef5f0; color: #4a7c59; border: 1px solid #a3c4ad">{{ m }}</span>
</div>
</div>
<div class="mt-4 flex items-center gap-4">
<div class="text-sm" style="color: rgba(0,0,0,0.45)">授权文件</div>
<button class="text-sm" style="color: #4a7c59">auth_gd30_v2.3.lic</button>
</div>
</div>
<!-- Assembly Record 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-lg font-semibold mb-6">装配记录</h3>
<div class="grid grid-cols-3 gap-x-12 gap-y-4 mb-6">
<div>
<div class="text-sm mb-1" style="color: rgba(0,0,0,0.45)">装配工单</div>
<div class="text-sm">ASM-2025-000001</div>
</div>
<div>
<div class="text-sm mb-1" style="color: rgba(0,0,0,0.45)">装配人员</div>
<div class="text-sm">张工程师</div>
</div>
<div>
<div class="text-sm mb-1" style="color: rgba(0,0,0,0.45)">装配日期</div>
<div class="text-sm">2025-01-15</div>
</div>
<div>
<div class="text-sm mb-1" style="color: rgba(0,0,0,0.45)">测试人员</div>
<div class="text-sm">李工程师</div>
</div>
<div>
<div class="text-sm mb-1" style="color: rgba(0,0,0,0.45)">测试结果</div>
<span class="px-2 py-0.5 rounded text-xs" style="background-color: #F6FFED; color: #52C41A; border: 1px solid #B7EB8F">通过</span>
</div>
<div>
<div class="text-sm mb-1" style="color: rgba(0,0,0,0.45)">Checklist完成</div>
<div class="text-sm">22/22 </div>
</div>
</div>
<!-- Checklist摘要 -->
<div class="p-4 rounded-lg" style="background-color: #FAFAFA; border: 1px solid #F0F0F0">
<div class="text-sm font-medium mb-2">装配Checklist摘要</div>
<div class="grid grid-cols-2 gap-2">
<div v-for="item in checklistSummary" :key="item.name" class="flex items-center gap-2 text-sm">
<span style="color: #52C41A"></span>
<span style="color: rgba(0,0,0,0.65)">{{ item.name }}</span>
</div>
</div>
</div>
</div>
<!-- Sub-Device List 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-lg font-semibold mb-6">子设备列表</h3>
<div class="overflow-x-auto">
<table class="w-full">
<thead style="background-color: #FAFAFA">
<tr>
<th class="px-4 py-3 text-left text-sm font-medium" style="color: rgba(0,0,0,0.85)">板卡类型</th>
<th class="px-4 py-3 text-left text-sm font-medium" style="color: rgba(0,0,0,0.85)">SN号</th>
<th class="px-4 py-3 text-left text-sm font-medium" style="color: rgba(0,0,0,0.85)">硬件版本</th>
<th class="px-4 py-3 text-left text-sm font-medium" style="color: rgba(0,0,0,0.85)">固件版本</th>
<th class="px-4 py-3 text-left text-sm font-medium" style="color: rgba(0,0,0,0.85)">校准状态</th>
<th class="px-4 py-3 text-left text-sm font-medium" style="color: rgba(0,0,0,0.85)">状态</th>
</tr>
</thead>
<tbody>
<tr v-for="sub in subDevices" :key="sub.sn" class="border-b" style="border-color: #F0F0F0">
<td class="px-4 py-3 text-sm">{{ sub.type }}</td>
<td class="px-4 py-3 text-sm" style="color: #4a7c59">{{ sub.sn }}</td>
<td class="px-4 py-3 text-sm" style="color: rgba(0,0,0,0.65)">{{ sub.hwVersion }}</td>
<td class="px-4 py-3 text-sm" style="color: rgba(0,0,0,0.65)">{{ sub.fwVersion }}</td>
<td class="px-4 py-3">
<span class="px-2 py-0.5 rounded text-xs" :style="sub.calibration === '已校准' ? { backgroundColor: '#F6FFED', color: '#52C41A', border: '1px solid #B7EB8F' } : { backgroundColor: '#FAFAFA', color: 'rgba(0,0,0,0.45)', border: '1px solid #D9D9D9' }">{{ sub.calibration }}</span>
</td>
<td class="px-4 py-3">
<span class="px-2 py-0.5 rounded text-xs" style="background-color: #F6FFED; color: #52C41A; border: 1px solid #B7EB8F">{{ sub.status }}</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Firmware Info Card -->
@ -220,4 +270,20 @@ const maintenanceHistory = [
{ date: '2024-02-15', type: '主板更换', operator: '李工程师', description: '更换主控板MB20231215001 → 新MB20240215001' },
{ date: '2024-01-20', type: '常规保养', operator: '张工程师', description: '清洁设备,检查线路连接,测试功能正常' },
]
const authModules = ['1D SP', '2D SP', '3D SP', '1D VES', '2D ERT', '3D ERT', '1D IP', '2D IP', '3D IP']
const checklistSummary = [
{ name: '主板SN扫码绑定主机' }, { name: '采集板SN录入' }, { name: '发射板SN录入' },
{ name: '内置升压模块检查' }, { name: 'GPS/北斗检测' }, { name: '电池安装与容量检测' },
{ name: '系统启动正常' }, { name: '采集APP连接' }, { name: 'IP66防护与密封' }, { name: '出厂装箱核对' },
]
const subDevices = [
{ type: '主板', sn: 'MB20240308001', hwVersion: 'A1', fwVersion: 'v2.3.5', calibration: '无需校准', status: '正常' },
{ type: '采集板', sn: 'RX20240308002', hwVersion: 'A1', fwVersion: 'v1.8.2', calibration: '已校准', status: '正常' },
{ type: '发射板', sn: 'TX20240308003', hwVersion: 'A1', fwVersion: 'v1.5.0', calibration: '无需校准', status: '正常' },
{ type: '测控板', sn: 'MC20240308004', hwVersion: 'A1', fwVersion: 'v1.5.1', calibration: '无需校准', status: '正常' },
{ type: '升压板', sn: 'BO20240308005', hwVersion: 'A1', fwVersion: 'v1.0.0', calibration: '无需校准', status: '正常' },
]
</script>

View File

@ -123,7 +123,6 @@
</div>
<div class="flex items-center gap-3 pt-4 border-t" style="border-color: #F0F0F0">
<button class="text-sm" style="color: #4a7c59" @click="router.push('/devices/' + device.sn)">详情</button>
<button class="text-sm" style="color: #4a7c59">BOM</button>
<template v-if="device.status === '已激活'">
<span style="color: #D9D9D9">|</span>
<button class="text-sm flex items-center gap-1" style="color: #FF4D4F">

View File

@ -144,10 +144,10 @@ const toggleChecklistItem = (id: number) => {
<label class="block text-sm mb-2" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">
装机测试状态
</label>
<div class="flex items-center gap-2 px-3 py-2 border rounded" :style="{ borderColor: '#D9D9D9', backgroundColor: '#FAFAFA' }">
<div class="w-full px-3 py-2 border rounded" :style="{ borderColor: '#D9D9D9', backgroundColor: '#FAFAFA' }">
<select>
<option><span :style="{ color: '#52C41A' }">测试通过</span></option>
<option><span :style="{ color: '#c41a1aff' }">测试不通过</span></option>
<option>测试通过</option>
<option>测试不通过</option>
</select>
</div>
</div>
@ -414,12 +414,12 @@ const toggleChecklistItem = (id: number) => {
<!-- Note -->
<div>
<div class="text-sm font-medium mb-2">备注</div>
<div class="text-sm font-medium mb-2">装配记录信息</div>
<textarea
v-model="photoNote"
class="w-full px-3 py-2 border rounded text-sm"
style="border-color: #D9D9D9; min-height: 80px; resize: vertical"
placeholder="输入备注信息(可选)"
placeholder="输入装配记录信息..."
></textarea>
</div>
</div>

View File

@ -16,7 +16,7 @@ interface ScrapDevice {
const scrapDevices: ScrapDevice[] = [
{
sn: 'GD30-2023-001234',
model: 'GD30 地质探测仪',
model: 'GD-30 Supreme',
scrapDate: '2024-03-01',
status: '已回收',
sourceOrder: 'WO-2024-0001',
@ -24,15 +24,15 @@ const scrapDevices: ScrapDevice[] = [
},
{
sn: 'GT20-2023-000567',
model: 'GT20 物探仪',
model: 'GT-20 瞬变电磁仪',
scrapDate: '2024-03-05',
status: '已审批',
sourceOrder: 'WO-2024-0025',
reason: '多个核心部件损坏',
},
{
sn: 'GTXD-2023-000890',
model: 'GTXD 探测仪',
sn: 'GM10-2023-000890',
model: 'GM10 大地电磁仪',
scrapDate: '2024-03-08',
status: '待审批',
sourceOrder: 'WO-2024-0048',
@ -71,13 +71,6 @@ const getStatusStyle = (status: ScrapDevice['status']) => {
<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)"
>
<Upload :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)"

View File

@ -15,7 +15,6 @@ export const router = createRouter({
{ path: 'registration', component: () => import('./pages/DeviceRegistration.vue') },
{ path: 'licenses', component: () => import('./pages/LicenseManagement.vue') },
{ path: 'licenses/generate', component: () => import('./pages/LicenseGenerate.vue') },
{ path: 'activation', component: () => import('./pages/ActivationManagement.vue') },
{ path: 'calibration', component: () => import('./pages/CalibrationRecords.vue') },
{ path: 'repair', component: () => import('./pages/RepairOrders.vue') },
{ path: 'repair/stats', component: () => import('./pages/RepairStats.vue') },