更新新的流程图
This commit is contained in:
parent
7f5d01a4d3
commit
0d9e6c27c2
|
|
@ -8,6 +8,7 @@
|
|||
"name": "device-management-platform",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"element-plus": "^2.13.6",
|
||||
"lucide-vue-next": "^0.487.0",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.0"
|
||||
|
|
@ -65,6 +66,24 @@
|
|||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ctrl/tinycolor": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-4.2.0.tgz",
|
||||
"integrity": "sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@element-plus/icons-vue": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.2.tgz",
|
||||
"integrity": "sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"vue": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.12",
|
||||
"resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
|
||||
|
|
@ -490,6 +509,31 @@
|
|||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "1.7.5",
|
||||
"resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.7.5.tgz",
|
||||
"integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/utils": "^0.2.11"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/dom": {
|
||||
"version": "1.7.6",
|
||||
"resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.7.6.tgz",
|
||||
"integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/core": "^1.7.5",
|
||||
"@floating-ui/utils": "^0.2.11"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/utils": {
|
||||
"version": "0.2.11",
|
||||
"resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.11.tgz",
|
||||
"integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@isaacs/fs-minipass": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
|
||||
|
|
@ -552,6 +596,17 @@
|
|||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@popperjs/core": {
|
||||
"name": "@sxzz/popperjs-es",
|
||||
"version": "2.11.8",
|
||||
"resolved": "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.8.tgz",
|
||||
"integrity": "sha512-wOwESXvvED3S8xBmcPWHs2dUuzrE4XiZeFu7e1hROIJkm02a49N120pmOXxY33sBb6hArItm5W5tcg1cBtV+HQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.60.1",
|
||||
"resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz",
|
||||
|
|
@ -1172,6 +1227,27 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.17.24",
|
||||
"resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.24.tgz",
|
||||
"integrity": "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/lodash-es": {
|
||||
"version": "4.17.12",
|
||||
"resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz",
|
||||
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/lodash": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/web-bluetooth": {
|
||||
"version": "0.0.20",
|
||||
"resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
|
||||
"integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vitejs/plugin-vue": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz",
|
||||
|
|
@ -1292,6 +1368,48 @@
|
|||
"integrity": "sha512-nBxuiuS9Lj5bPkPbWogPUnjxxWpkRniX7e5UBQDWl6Fsf4roq9wwV+cR7ezQ4zXswNvPIlsdj1slcLB7XCsRAw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@vueuse/core": {
|
||||
"version": "12.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-12.0.0.tgz",
|
||||
"integrity": "sha512-C12RukhXiJCbx4MGhjmd/gH52TjJsc3G0E0kQj/kb19H3Nt6n1CA4DRWuTdWWcaFRdlTe0npWDS942mvacvNBw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/web-bluetooth": "^0.0.20",
|
||||
"@vueuse/metadata": "12.0.0",
|
||||
"@vueuse/shared": "12.0.0",
|
||||
"vue": "^3.5.13"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/metadata": {
|
||||
"version": "12.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-12.0.0.tgz",
|
||||
"integrity": "sha512-Yzimd1D3sjxTDOlF05HekU5aSGdKjxhuhRFHA7gDWLn57PRbBIh+SF5NmjhJ0WRgF3my7T8LBucyxdFJjIfRJQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@vueuse/shared": {
|
||||
"version": "12.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-12.0.0.tgz",
|
||||
"integrity": "sha512-3i6qtcq2PIio5i/vVYidkkcgvmTjCqrf26u+Fd4LhnbBmIT6FN8y6q/GJERp8lfcB9zVEfjdV0Br0443qZuJpw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"vue": "^3.5.13"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/async-validator": {
|
||||
"version": "4.2.5",
|
||||
"resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz",
|
||||
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/chownr": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/chownr/-/chownr-3.0.0.tgz",
|
||||
|
|
@ -1308,6 +1426,12 @@
|
|||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.20",
|
||||
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.20.tgz",
|
||||
"integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||
|
|
@ -1318,6 +1442,32 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/element-plus": {
|
||||
"version": "2.13.6",
|
||||
"resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.13.6.tgz",
|
||||
"integrity": "sha512-XHgwXr8Fjz6i+6BaqFhAbae/dJbG7bBAAlHrY3pWL7dpj+JcqcOyKYt4Oy5KP86FQwS1k4uIZDjCx2FyUR5lDg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ctrl/tinycolor": "^4.2.0",
|
||||
"@element-plus/icons-vue": "^2.3.2",
|
||||
"@floating-ui/dom": "^1.0.1",
|
||||
"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
|
||||
"@types/lodash": "^4.17.20",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@vueuse/core": "12.0.0",
|
||||
"async-validator": "^4.2.5",
|
||||
"dayjs": "^1.11.19",
|
||||
"lodash": "^4.17.23",
|
||||
"lodash-es": "^4.17.23",
|
||||
"lodash-unified": "^1.0.3",
|
||||
"memoize-one": "^6.0.0",
|
||||
"normalize-wheel-es": "^1.2.0",
|
||||
"vue-component-type-helpers": "^3.2.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
"version": "5.20.1",
|
||||
"resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz",
|
||||
|
|
@ -1698,6 +1848,29 @@
|
|||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.18.1",
|
||||
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.18.1.tgz",
|
||||
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
"version": "4.18.1",
|
||||
"resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.18.1.tgz",
|
||||
"integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash-unified": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/lodash-unified/-/lodash-unified-1.0.3.tgz",
|
||||
"integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/lodash-es": "*",
|
||||
"lodash": "*",
|
||||
"lodash-es": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/lucide-vue-next": {
|
||||
"version": "0.487.0",
|
||||
"resolved": "https://registry.npmmirror.com/lucide-vue-next/-/lucide-vue-next-0.487.0.tgz",
|
||||
|
|
@ -1716,6 +1889,12 @@
|
|||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||
}
|
||||
},
|
||||
"node_modules/memoize-one": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz",
|
||||
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/minipass": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.3.tgz",
|
||||
|
|
@ -1757,6 +1936,12 @@
|
|||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-wheel-es": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz",
|
||||
"integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
|
||||
|
|
@ -2023,6 +2208,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vue-component-type-helpers": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmmirror.com/vue-component-type-helpers/-/vue-component-type-helpers-3.2.6.tgz",
|
||||
"integrity": "sha512-O02tnvIfOQVmnvoWwuSydwRoHjZVt8UEBR+2p4rT35p8GAy5VTlWP8o5qXfJR/GWCN0nVZoYWsVUvx2jwgdBmQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vue-router": {
|
||||
"version": "4.6.4",
|
||||
"resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.6.4.tgz",
|
||||
|
|
|
|||
|
|
@ -9,13 +9,14 @@
|
|||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"element-plus": "^2.13.6",
|
||||
"lucide-vue-next": "^0.487.0",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.0",
|
||||
"lucide-vue-next": "^0.487.0"
|
||||
"vue-router": "^4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.2.3",
|
||||
"@tailwindcss/vite": "4.1.12",
|
||||
"@vitejs/plugin-vue": "^5.2.3",
|
||||
"tailwindcss": "4.1.12",
|
||||
"vite": "6.3.5"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { useRoute } from 'vue-router'
|
||||
import { Monitor, Settings2, Key, Cpu, FileCode, Gauge, Wrench } from 'lucide-vue-next'
|
||||
import { Monitor, Settings2, Key, Cpu, FileCode, Gauge, Wrench, Recycle } from 'lucide-vue-next'
|
||||
|
||||
const route = useRoute()
|
||||
const isActive = (path: string) =>
|
||||
|
|
@ -57,7 +57,7 @@ const menuGroups = [
|
|||
]},
|
||||
{ title: '维修', items: [
|
||||
{ path: '/repair', label: '维修工单', icon: Wrench },
|
||||
{ path: '/scrap', label: '报废回收', icon: Wrench },
|
||||
{ path: '/scrap', label: '报废回收', icon: Recycle },
|
||||
]},
|
||||
]
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -93,10 +93,6 @@ const getStatusStyle = (status: CalibrationRecord['status']) => {
|
|||
<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: #4a7c59">8</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>
|
||||
|
|
@ -185,7 +181,7 @@ const getStatusStyle = (status: CalibrationRecord['status']) => {
|
|||
<td class="px-6 py-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<button class="text-sm" style="color: #4a7c59">详情</button>
|
||||
<button class="text-sm" style="color: #4a7c59">下载报告</button>
|
||||
<button class="text-sm" style="color: #4a7c59">校准文件</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
|||
|
|
@ -170,7 +170,6 @@ const getStatusStyle = (status: ConfigFile['status']) => {
|
|||
<div class="flex items-center gap-3">
|
||||
<button class="text-sm" style="color: #4a7c59">详情</button>
|
||||
<button class="text-sm" style="color: #4a7c59">编辑</button>
|
||||
<button class="text-sm" style="color: #4a7c59">下发</button>
|
||||
<button class="text-sm" style="color: #4a7c59">下载</button>
|
||||
<button class="text-sm" style="color: #FF4D4F">删除</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ import { TrendingUp, TrendingDown, Server, Wifi, CheckCircle, PackageCheck, Wren
|
|||
const metrics = [
|
||||
{ label: '设备总数', value: '5,234', trend: 'up' as const, trendValue: '+5.2%', color: '#4a7c59', icon: Server, link: '/devices' },
|
||||
{ label: '装配中', value: '4,856', trend: 'up' as const, trendValue: '+2.8%', color: '#52C41A', icon: Wifi, link: '/devices' },
|
||||
{ label: '已激活', value: '4,912', trend: 'up' as const, trendValue: '+1.5%', color: '#4a7c59', icon: CheckCircle, link: '/activation' },
|
||||
{ label: '已激活', value: '4,912', trend: 'up' as const, trendValue: '+1.5%', color: '#4a7c59', icon: CheckCircle, link: '/devices' },
|
||||
{ label: '有新版本', value: '156', color: '#722ED1', icon: PackageCheck, link: '/firmware' },
|
||||
{ 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' },
|
||||
|
|
@ -100,10 +100,10 @@ const maxStatusValue = computed(() => Math.max(...deviceStatusData.map((d) => d.
|
|||
|
||||
const taskGroups = [
|
||||
{
|
||||
title: '待校准设备', count: 23, link: '/calibration',
|
||||
title: '校准即将到期', count: 23, link: '/calibration',
|
||||
tasks: [
|
||||
{ deviceSN: 'AC20240308005', description: '采集板校准即将到期', time: '3天后到期', link: '/calibration' },
|
||||
{ deviceSN: 'AC20240308006', description: '新采集板待校准', time: '今天', link: '/calibration' },
|
||||
{ deviceSN: 'AC20240308006', description: '采集板校准即将到期', time: '今天到期', link: '/calibration' },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -112,7 +112,15 @@
|
|||
|
||||
<!-- 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 style="display: flex; justify-content: space-between;">
|
||||
<h3 class="text-lg font-semibold mb-6">装配记录</h3>
|
||||
<button>
|
||||
<span style="color: #52C41A;">
|
||||
查看
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -170,6 +170,25 @@ const boardVersionData = [
|
|||
status: 'active',
|
||||
},
|
||||
]
|
||||
|
||||
// Checklist template drawer state
|
||||
const showChecklistDrawer = ref(false)
|
||||
const newTplModel = ref('GD30')
|
||||
const newTplItems = ref<{ text: string }[]>([{ text: '' }])
|
||||
|
||||
const openChecklistDrawer = () => {
|
||||
newTplModel.value = 'GD30'
|
||||
newTplItems.value = [{ text: '' }]
|
||||
showChecklistDrawer.value = true
|
||||
}
|
||||
|
||||
const addTplItem = () => {
|
||||
newTplItems.value.push({ text: '' })
|
||||
}
|
||||
|
||||
const removeTplItem = (index: number) => {
|
||||
newTplItems.value.splice(index, 1)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -291,7 +310,17 @@ const boardVersionData = [
|
|||
<!-- Assembly Checklist -->
|
||||
<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">装配Checklist模板</h3>
|
||||
<button
|
||||
class="px-4 py-2 rounded text-white flex items-center gap-2"
|
||||
:style="{ backgroundColor: '#4a7c59' }"
|
||||
@click="openChecklistDrawer"
|
||||
>
|
||||
<Plus :size="16" />
|
||||
新增模版
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tabs -->
|
||||
|
|
@ -318,7 +347,6 @@ const boardVersionData = [
|
|||
<tr>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)', width: '50px' }">序号</th>
|
||||
<th class="px-3 py-3 text-left text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)' }">项目名称</th>
|
||||
<th class="px-3 py-3 text-center text-sm font-medium" :style="{ color: 'rgba(0, 0, 0, 0.85)', width: '70px' }">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
@ -352,33 +380,9 @@ const boardVersionData = [
|
|||
{{ item.text }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-3 py-3 text-center">
|
||||
<button
|
||||
@click="deleteChecklistItem(activeTab, item.id)"
|
||||
class="text-gray-400 hover:text-red-500 transition-colors"
|
||||
>
|
||||
<Trash2 :size="15" />
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="mt-4 flex items-center justify-between">
|
||||
<button
|
||||
@click="addChecklistItem(activeTab)"
|
||||
class="px-4 py-2 text-sm flex items-center gap-1 rounded transition-colors"
|
||||
:style="{ color: '#4a7c59', border: '1px dashed #4a7c59' }"
|
||||
>
|
||||
<Plus :size="14" />
|
||||
添加检查项
|
||||
</button>
|
||||
<button
|
||||
class="px-6 py-2 text-sm rounded text-white"
|
||||
:style="{ backgroundColor: '#4a7c59' }"
|
||||
>
|
||||
确认保存
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -539,5 +543,78 @@ const boardVersionData = [
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Checklist Template Drawer -->
|
||||
<div v-if="showChecklistDrawer" class="fixed inset-0 z-50 flex justify-end" style="background-color: rgba(0,0,0,0.45)" @click.self="showChecklistDrawer = false">
|
||||
<div class="bg-white w-[480px] h-full flex flex-col" style="box-shadow: -4px 0 12px rgba(0,0,0,0.1)">
|
||||
<!-- Drawer Header -->
|
||||
<div class="flex items-center justify-between p-5 border-b" style="border-color: #F0F0F0">
|
||||
<h3 class="text-lg font-semibold">新增Checklist模版</h3>
|
||||
<button @click="showChecklistDrawer = false" class="p-1 rounded hover:bg-gray-100" style="color: rgba(0,0,0,0.45)">
|
||||
<X :size="18" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Drawer Body -->
|
||||
<div class="flex-1 overflow-y-auto p-6">
|
||||
<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>
|
||||
<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>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm mb-2" style="color: rgba(0,0,0,0.85)">检查项列表</label>
|
||||
<div class="space-y-3">
|
||||
<div
|
||||
v-for="(item, index) in newTplItems"
|
||||
:key="index"
|
||||
class="flex items-center gap-2"
|
||||
>
|
||||
<span class="text-sm w-6 text-center flex-shrink-0" style="color: rgba(0,0,0,0.45)">{{ index + 1 }}</span>
|
||||
<input
|
||||
v-model="item.text"
|
||||
type="text"
|
||||
class="flex-1 px-3 py-2 border rounded text-sm"
|
||||
style="border-color: #D9D9D9"
|
||||
placeholder="请输入检查项名称"
|
||||
/>
|
||||
<button
|
||||
@click="removeTplItem(index)"
|
||||
class="p-1 rounded hover:bg-red-50 text-gray-400 hover:text-red-500 transition-colors flex-shrink-0"
|
||||
:disabled="newTplItems.length <= 1"
|
||||
:style="{ opacity: newTplItems.length <= 1 ? 0.3 : 1 }"
|
||||
>
|
||||
<Trash2 :size="15" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
@click="addTplItem"
|
||||
class="mt-3 px-4 py-2 text-sm flex items-center gap-1 rounded transition-colors w-full justify-center"
|
||||
:style="{ color: '#4a7c59', border: '1px dashed #4a7c59' }"
|
||||
>
|
||||
<Plus :size="14" />
|
||||
添加检查项
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Drawer Footer -->
|
||||
<div class="flex items-center justify-end gap-3 p-5 border-t" style="border-color: #F0F0F0">
|
||||
<button class="px-4 py-2 rounded text-sm" style="border: 1px solid #D9D9D9; color: rgba(0,0,0,0.85)" @click="showChecklistDrawer = false">取消</button>
|
||||
<button
|
||||
class="px-4 py-2 rounded text-white text-sm"
|
||||
:style="{ backgroundColor: '#4a7c59' }"
|
||||
@click="showChecklistDrawer = false"
|
||||
>确认生成</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -65,7 +65,7 @@ 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: 'RX20240308002', spec: 'GD30-RX-V1.8', calibration: '已校准', quantity: 2 },
|
||||
{ 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 },
|
||||
|
|
@ -132,12 +132,6 @@ const toggleChecklistItem = (id: number) => {
|
|||
placeholder="请输入主板SN号"
|
||||
value="MB20240308001"
|
||||
/>
|
||||
<span
|
||||
class="px-2 py-1 rounded text-xs whitespace-nowrap"
|
||||
:style="{ backgroundColor: '#F6FFED', color: '#52C41A', border: '1px solid #B7EB8F' }"
|
||||
>
|
||||
已绑定
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
<script setup lang="ts">
|
||||
import { Upload } from 'lucide-vue-next'
|
||||
import { ref } from 'vue'
|
||||
import { ref, reactive } from 'vue'
|
||||
import { ElDialog, ElForm, ElFormItem, ElInput, ElSelect, ElOption, ElCheckbox, ElMessage, ElUpload, ElButton } from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
|
||||
// 固件列表接口 - 新增 firmwareType 字段
|
||||
interface Firmware {
|
||||
version: string
|
||||
releaseDate: string
|
||||
|
|
@ -16,6 +19,18 @@ interface Firmware {
|
|||
signed?: boolean
|
||||
notes?: string[]
|
||||
expanded: boolean
|
||||
firmwareType: '主协板' | '采集板' | '发射板' | '主机固件' | '计算单元固件' // 新增固件类型
|
||||
}
|
||||
|
||||
// 上传表单接口 - 新增 firmwareType 字段
|
||||
interface FirmwareUploadForm {
|
||||
version: string
|
||||
hardwareRange: string
|
||||
upgradeType: '可选' | '强制'
|
||||
signed: boolean
|
||||
notes: string
|
||||
file: File | null
|
||||
firmwareType: '主协板' | '采集板' | '发射板' | '主机固件' | '计算单元固件' // 新增固件类型
|
||||
}
|
||||
|
||||
const firmwares = ref<Firmware[]>([
|
||||
|
|
@ -38,6 +53,7 @@ const firmwares = ref<Firmware[]>([
|
|||
'增强安全性,升级前强制验证固件签名',
|
||||
],
|
||||
expanded: true,
|
||||
firmwareType: '主机固件' // 为原有数据补充固件类型
|
||||
},
|
||||
{
|
||||
version: 'v2.2.0',
|
||||
|
|
@ -46,6 +62,7 @@ const firmwares = ref<Firmware[]>([
|
|||
fileSize: '12.0MB',
|
||||
downloads: 2456,
|
||||
expanded: false,
|
||||
firmwareType: '主协板' // 为原有数据补充固件类型
|
||||
},
|
||||
{
|
||||
version: 'v2.1.0',
|
||||
|
|
@ -54,8 +71,102 @@ const firmwares = ref<Firmware[]>([
|
|||
fileSize: '11.5MB',
|
||||
downloads: 3587,
|
||||
expanded: false,
|
||||
firmwareType: '采集板' // 为原有数据补充固件类型
|
||||
},
|
||||
])
|
||||
|
||||
// ============== 上传弹窗相关 ==============
|
||||
const uploadDialogVisible = ref(false)
|
||||
// 升级类型选项
|
||||
const upgradeTypes = ref<('可选' | '强制')[]>(['可选', '强制'])
|
||||
// 新增:固件类型选项(5种)
|
||||
const firmwareTypes = ref<('主协板' | '采集板' | '发射板' | '主机固件' | '计算单元固件')[]>(
|
||||
['主协板', '采集板', '发射板', '主机固件', '计算单元固件']
|
||||
)
|
||||
|
||||
// 初始化上传表单 - 新增 firmwareType 默认值
|
||||
const uploadForm = ref<FirmwareUploadForm>({
|
||||
version: '',
|
||||
hardwareRange: '',
|
||||
upgradeType: '可选',
|
||||
signed: true,
|
||||
notes: '',
|
||||
file: null,
|
||||
firmwareType: '主机固件' // 固件类型默认值
|
||||
})
|
||||
|
||||
// 表单校验规则 - 新增 firmwareType 必选校验
|
||||
const uploadFormRules = reactive({
|
||||
version: [{ required: true, message: '请输入固件版本', trigger: 'blur' }],
|
||||
hardwareRange: [{ required: true, message: '请输入硬件版本范围', trigger: 'blur' }],
|
||||
upgradeType: [{ required: true, message: '请选择升级类型', trigger: 'change' }],
|
||||
firmwareType: [{ required: true, message: '请选择固件类型', trigger: 'change' }], // 新增
|
||||
file: [{ required: true, message: '请上传固件ZIP包', trigger: 'change' }]
|
||||
})
|
||||
|
||||
// 文件上传前校验(仅允许ZIP)
|
||||
const beforeUpload = (file: File) => {
|
||||
const isZip = file.type === 'application/zip' || file.name.endsWith('.zip')
|
||||
if (!isZip) {
|
||||
ElMessage.error('只能上传 ZIP 格式的固件包!')
|
||||
return false
|
||||
}
|
||||
uploadForm.value.file = file
|
||||
return false // 关闭自动上传,手动提交
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
const resetUploadForm = () => {
|
||||
uploadForm.value = {
|
||||
version: '',
|
||||
hardwareRange: '',
|
||||
upgradeType: '可选',
|
||||
signed: true,
|
||||
notes: '',
|
||||
file: null,
|
||||
firmwareType: '主机固件' // 重置时恢复默认值
|
||||
}
|
||||
}
|
||||
|
||||
// 表单ref(用于校验)
|
||||
const uploadFormRef = ref<any>(null)
|
||||
|
||||
// 提交上传固件
|
||||
const handleUploadSubmit = () => {
|
||||
if (!uploadFormRef.value) return
|
||||
|
||||
uploadFormRef.value.validate((valid: boolean) => {
|
||||
if (!valid) return
|
||||
|
||||
// 组装新固件数据 - 新增 firmwareType 字段
|
||||
const newFirmware: Firmware = {
|
||||
version: uploadForm.value.version,
|
||||
releaseDate: new Date().toISOString().split('T')[0],
|
||||
status: '草稿',
|
||||
fileSize: formatFileSize(uploadForm.value.file!.size),
|
||||
downloads: 0,
|
||||
model: 'GD30',
|
||||
hardwareRange: uploadForm.value.hardwareRange,
|
||||
upgradeType: uploadForm.value.upgradeType,
|
||||
signed: uploadForm.value.signed,
|
||||
notes: uploadForm.value.notes.split('\n').filter(item => item.trim()),
|
||||
expanded: false,
|
||||
firmwareType: uploadForm.value.firmwareType // 新增固件类型赋值
|
||||
}
|
||||
|
||||
// 添加到列表
|
||||
firmwares.value.unshift(newFirmware)
|
||||
ElMessage.success('固件上传成功!')
|
||||
uploadDialogVisible.value = false
|
||||
resetUploadForm()
|
||||
})
|
||||
}
|
||||
|
||||
// 文件大小格式化
|
||||
const formatFileSize = (bytes: number): string => {
|
||||
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + 'KB'
|
||||
return (bytes / (1024 * 1024)).toFixed(1) + 'MB'
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -63,15 +174,12 @@ const firmwares = ref<Firmware[]>([
|
|||
<!-- Page Header -->
|
||||
<div class="mb-6">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h2 class="text-2xl font-semibold">固件库</h2>
|
||||
<h2 class="text-2xl font-semibold">GD-30 Supreme固件库</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">
|
||||
<button class="px-4 py-2 rounded text-white flex items-center gap-2" style="background-color: #4a7c59" @click="uploadDialogVisible = true">
|
||||
<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)">
|
||||
发布固件
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -84,11 +192,15 @@ const firmwares = ref<Firmware[]>([
|
|||
|
||||
<div class="divide-y" style="border-color: #F0F0F0">
|
||||
<div v-for="fw in firmwares" :key="fw.version" class="p-6">
|
||||
<!-- Header row -->
|
||||
<div class="flex items-center gap-4 mb-2">
|
||||
<!-- Header row - 新增固件类型展示 -->
|
||||
<div class="flex items-center gap-4 mb-2 flex-wrap">
|
||||
<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 -->
|
||||
|
|
@ -123,7 +235,6 @@ const firmwares = ref<Firmware[]>([
|
|||
<button class="text-sm" style="color: #4a7c59">下载</button>
|
||||
<button class="text-sm" style="color: #4a7c59">编辑</button>
|
||||
<button class="text-sm" style="color: #4a7c59">撤回发布</button>
|
||||
<router-link to="/firmware-upgrade" class="text-sm" style="color: #4a7c59">查看升级任务</router-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -137,5 +248,70 @@ const firmwares = ref<Firmware[]>([
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ============== 上传固件弹窗 ============== -->
|
||||
<ElDialog v-model="uploadDialogVisible" title="上传固件" width="550px" append-to-body>
|
||||
<ElForm :model="uploadForm" :rules="uploadFormRules" ref="uploadFormRef" label-width="110px" class="mt-4">
|
||||
<!-- 固件版本 -->
|
||||
<ElFormItem label="固件版本" prop="version">
|
||||
<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" />
|
||||
</ElFormItem>
|
||||
|
||||
<!-- 升级类型 -->
|
||||
<ElFormItem label="升级类型" prop="upgradeType">
|
||||
<ElSelect v-model="uploadForm.upgradeType" placeholder="请选择升级类型">
|
||||
<ElOption v-for="type in upgradeTypes" :key="type" :label="type" :value="type" />
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
|
||||
<!-- 数字签名 -->
|
||||
<ElFormItem label="数字签名">
|
||||
<ElCheckbox v-model="uploadForm.signed">已签名</ElCheckbox>
|
||||
</ElFormItem>
|
||||
|
||||
<!-- 固件上传 -->
|
||||
<ElFormItem label="固件包" prop="file">
|
||||
<ElUpload
|
||||
:before-upload="beforeUpload"
|
||||
:file-list="[]"
|
||||
accept=".zip"
|
||||
drag
|
||||
>
|
||||
<div class="el-upload__text">点击或拖拽 ZIP 文件到此处</div>
|
||||
</ElUpload>
|
||||
</ElFormItem>
|
||||
|
||||
<!-- 发布说明 -->
|
||||
<ElFormItem label="发布说明">
|
||||
<el-input
|
||||
v-model="uploadForm.notes"
|
||||
style="width: 100%"
|
||||
:rows="4"
|
||||
type="textarea"
|
||||
placeholder="每行一条发布说明,自动换行分割"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
|
||||
<!-- 弹窗底部按钮 -->
|
||||
<template #footer>
|
||||
<div class="flex justify-end gap-2">
|
||||
<ElButton @click="uploadDialogVisible = false; resetUploadForm()">取消</ElButton>
|
||||
<ElButton type="primary" style="background-color: #4a7c59; border-color: #4a7c59" @click="handleUploadSubmit">确认上传</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -1,164 +1,143 @@
|
|||
<script setup lang="ts">
|
||||
import { Info, Download, Upload, Link as LinkIcon } from 'lucide-vue-next'
|
||||
import { ref } from 'vue'
|
||||
import { Info, Download, Link as LinkIcon, X, ArrowLeft, Check, AlertTriangle } from 'lucide-vue-next'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
interface ScrapDevice {
|
||||
sn: string
|
||||
model: string
|
||||
scrapDate: string
|
||||
status: '待审批' | '已审批' | '已回收'
|
||||
sourceOrder: string
|
||||
reason: string
|
||||
sn: string; model: string; scrapDate: string; reason: string; sourceOrder: string
|
||||
status: '待审批' | '审批中' | '已审批' | '已驳回' | '回收中' | '已回收'
|
||||
applicant: string; approver: string; residualValue: number
|
||||
recyclableParts: string[]
|
||||
}
|
||||
|
||||
const scrapDevices: ScrapDevice[] = [
|
||||
{
|
||||
sn: 'GD30-2023-001234',
|
||||
model: 'GD-30 Supreme',
|
||||
scrapDate: '2024-03-01',
|
||||
status: '已回收',
|
||||
sourceOrder: 'WO-2024-0001',
|
||||
reason: '主板损坏无法修复',
|
||||
},
|
||||
{
|
||||
sn: 'GT20-2023-000567',
|
||||
model: 'GT-20 瞬变电磁仪',
|
||||
scrapDate: '2024-03-05',
|
||||
status: '已审批',
|
||||
sourceOrder: 'WO-2024-0025',
|
||||
reason: '多个核心部件损坏',
|
||||
},
|
||||
{
|
||||
sn: 'GM10-2023-000890',
|
||||
model: 'GM10 大地电磁仪',
|
||||
scrapDate: '2024-03-08',
|
||||
status: '待审批',
|
||||
sourceOrder: 'WO-2024-0048',
|
||||
reason: '维修成本超过设备价值',
|
||||
},
|
||||
const scrapDevices = ref<ScrapDevice[]>([
|
||||
{ sn: 'GD30-2023-001234', model: 'GD-30 Supreme', scrapDate: '2024-03-01', status: '已回收', sourceOrder: 'WO-2024-0001', reason: '主板损坏无法修复', applicant: '李工', approver: '张主管', residualValue: 500, recyclableParts: ['采集板 AC20240308002', '测控板 CT20240308003'] },
|
||||
{ sn: 'GT20-2023-000567', model: 'GT-20', scrapDate: '2024-03-05', status: '已审批', sourceOrder: 'WO-2024-0025', reason: '多个核心部件损坏', applicant: '王工', approver: '张主管', residualValue: 300, recyclableParts: ['电源模块 PS20240308004'] },
|
||||
{ sn: 'GD30-2023-000890', model: 'GD-30 Supreme', scrapDate: '2024-03-08', status: '待审批', sourceOrder: 'WO-2024-0048', reason: '维修成本超过设备价值', applicant: '李工', approver: '', residualValue: 800, recyclableParts: ['采集板 AC20240309001', '发射板 TX20240309002', '升压板 BO20240309003'] },
|
||||
{ sn: 'GD30-2023-001100', model: 'GD-30 Supreme', scrapDate: '2024-03-10', status: '审批中', sourceOrder: 'WO-2024-0052', reason: '设备老化严重', applicant: '张工', approver: '', residualValue: 200, recyclableParts: [] },
|
||||
{ sn: 'GT20-2023-001200', model: 'GT-20', scrapDate: '2024-03-12', status: '已驳回', sourceOrder: 'WO-2024-0055', reason: '通信模块故障', applicant: '王工', approver: '张主管', residualValue: 0, recyclableParts: [] },
|
||||
])
|
||||
|
||||
// Drawers
|
||||
const showDetailDrawer = ref(false)
|
||||
const showApprovalDrawer = ref(false)
|
||||
const showRecycleDrawer = ref(false)
|
||||
const currentDevice = ref<ScrapDevice | null>(null)
|
||||
const approvalComment = ref('')
|
||||
const recycleNote = ref('')
|
||||
const recycleChecked = ref<Set<string>>(new Set())
|
||||
|
||||
const openDetail = (d: ScrapDevice) => { currentDevice.value = d; showDetailDrawer.value = true }
|
||||
const openApproval = (d: ScrapDevice) => { currentDevice.value = d; approvalComment.value = ''; showApprovalDrawer.value = true }
|
||||
const openRecycle = (d: ScrapDevice) => {
|
||||
currentDevice.value = d; recycleNote.value = ''
|
||||
recycleChecked.value = new Set()
|
||||
showRecycleDrawer.value = true
|
||||
}
|
||||
|
||||
const getStatusStyle = (s: ScrapDevice['status']) => {
|
||||
const map: Record<string, any> = {
|
||||
'待审批': { backgroundColor: '#FFFBE6', color: '#FAAD14', border: '1px solid #FFE58F' },
|
||||
'审批中': { backgroundColor: '#eef5f0', color: '#4a7c59', border: '1px solid #a3c4ad' },
|
||||
'已审批': { backgroundColor: '#F6FFED', color: '#52C41A', border: '1px solid #B7EB8F' },
|
||||
'已驳回': { backgroundColor: '#FFF1F0', color: '#FF4D4F', border: '1px solid #FFCCC7' },
|
||||
'回收中': { backgroundColor: '#eef5f0', color: '#4a7c59', border: '1px solid #a3c4ad' },
|
||||
'已回收': { backgroundColor: '#F6FFED', color: '#52C41A', border: '1px solid #B7EB8F' },
|
||||
}
|
||||
return map[s]
|
||||
}
|
||||
|
||||
const statCounts = {
|
||||
total: scrapDevices.value.length,
|
||||
pending: scrapDevices.value.filter(d => d.status === '待审批' || d.status === '审批中').length,
|
||||
approved: scrapDevices.value.filter(d => d.status === '已审批').length,
|
||||
recycled: scrapDevices.value.filter(d => d.status === '已回收').length,
|
||||
}
|
||||
|
||||
const flowSteps = [
|
||||
{ label: '申请报废', desc: '维修工单发起' },
|
||||
{ label: '主管审批', desc: '审核报废原因' },
|
||||
{ label: '物料检测', desc: '检测可回收物料' },
|
||||
{ label: '回收入库', desc: '物料回收登记' },
|
||||
{ label: '报废完成', desc: '设备注销' },
|
||||
]
|
||||
|
||||
const getStatusStyle = (status: ScrapDevice['status']) => {
|
||||
switch (status) {
|
||||
case '已回收':
|
||||
return {
|
||||
backgroundColor: '#F6FFED',
|
||||
color: '#52C41A',
|
||||
border: '1px solid #B7EB8F',
|
||||
}
|
||||
case '已审批':
|
||||
return {
|
||||
backgroundColor: '#eef5f0',
|
||||
color: '#4a7c59',
|
||||
border: '1px solid #a3c4ad',
|
||||
}
|
||||
case '待审批':
|
||||
return {
|
||||
backgroundColor: '#FFFBE6',
|
||||
color: '#FAAD14',
|
||||
border: '1px solid #FFE58F',
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-6">
|
||||
<!-- Page Header -->
|
||||
<!-- 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 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>
|
||||
<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">报废流程说明</div>
|
||||
<div class="text-sm">
|
||||
报废单由维修工单中申请报废创建,关联来源维修工单。报废设备需经过审批流程,审批通过后进行物料回收和入库。
|
||||
<!-- Approval Flow -->
|
||||
<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-semibold mb-4">报废审批流程</h3>
|
||||
<div class="flex items-center justify-between">
|
||||
<div v-for="(step, i) in flowSteps" :key="i" class="flex items-center gap-3 flex-1">
|
||||
<div class="text-center flex-1">
|
||||
<div class="w-10 h-10 rounded-full flex items-center justify-center mx-auto mb-2" :style="{ backgroundColor: '#eef5f0', border: '2px solid #4a7c59', color: '#4a7c59', fontWeight: 600, fontSize: '14px' }">{{ i + 1 }}</div>
|
||||
<div class="text-sm font-medium">{{ step.label }}</div>
|
||||
<div class="text-xs" style="color: rgba(0,0,0,0.45)">{{ step.desc }}</div>
|
||||
</div>
|
||||
<div v-if="i < flowSteps.length - 1" style="color: #D9D9D9; font-size: 18px; flex-shrink: 0">→</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stat Cards -->
|
||||
<!-- Stats -->
|
||||
<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: rgba(0, 0, 0, 0.85)">156</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">{{ statCounts.total }}</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">12</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">{{ statCounts.pending }}</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: #4a7c59">8</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: #4a7c59">{{ statCounts.approved }}</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: #52C41A">136</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: #52C41A">{{ statCounts.recycled }}</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)">
|
||||
<!-- Filter -->
|
||||
<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号搜索"
|
||||
/>
|
||||
<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>
|
||||
<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><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"
|
||||
/>
|
||||
<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>
|
||||
<button class="w-full px-4 py-2 rounded text-white" style="background-color: #4a7c59">查询</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Scrap Device List -->
|
||||
<div class="bg-white rounded-lg mb-6" style="box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05)">
|
||||
<!-- 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" style="border-color: #F0F0F0">
|
||||
<h3 class="text-lg font-semibold">报废设备列表</h3>
|
||||
</div>
|
||||
|
|
@ -166,42 +145,33 @@ const getStatusStyle = (status: ScrapDevice['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)">设备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>
|
||||
<th class="px-6 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)">设备SN</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>
|
||||
<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="(device, index) in scrapDevices"
|
||||
:key="index"
|
||||
class="border-b"
|
||||
style="border-color: #F0F0F0"
|
||||
>
|
||||
<td class="px-6 py-4">{{ device.sn }}</td>
|
||||
<td class="px-6 py-4" style="color: rgba(0, 0, 0, 0.65)">{{ device.model }}</td>
|
||||
<td class="px-6 py-4" style="color: rgba(0, 0, 0, 0.65)">{{ device.scrapDate }}</td>
|
||||
<td class="px-6 py-4" style="color: rgba(0, 0, 0, 0.65)">{{ device.reason }}</td>
|
||||
<td class="px-6 py-4">
|
||||
<span class="px-2 py-1 rounded text-xs" :style="getStatusStyle(device.status)">
|
||||
{{ device.status }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<router-link :to="'/repair/' + device.sourceOrder" class="text-sm flex items-center gap-1" style="color: #4a7c59">
|
||||
<LinkIcon :size="14" />
|
||||
{{ device.sourceOrder }}
|
||||
<tr v-for="(d, i) in scrapDevices" :key="i" class="border-b" style="border-color: #F0F0F0">
|
||||
<td class="px-5 py-4 text-sm">{{ d.sn }}</td>
|
||||
<td class="px-5 py-4 text-sm" style="color: rgba(0,0,0,0.65)">{{ d.model }}</td>
|
||||
<td class="px-5 py-4 text-sm" style="color: rgba(0,0,0,0.65)">{{ d.reason }}</td>
|
||||
<td class="px-5 py-4 text-sm" style="color: rgba(0,0,0,0.65)">{{ d.applicant }}</td>
|
||||
<td class="px-5 py-4"><span class="px-2 py-0.5 rounded text-xs" :style="getStatusStyle(d.status)">{{ d.status }}</span></td>
|
||||
<td class="px-5 py-4">
|
||||
<router-link :to="'/repair/' + d.sourceOrder" class="text-sm flex items-center gap-1" style="color: #4a7c59">
|
||||
<LinkIcon :size="14" /> {{ d.sourceOrder }}
|
||||
</router-link>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<button class="text-sm" style="color: #4a7c59">查看详情</button>
|
||||
<button class="text-sm" style="color: #4a7c59">物料检测</button>
|
||||
<button v-if="device.status === '已审批'" class="text-sm" style="color: #52C41A" @click="router.push('/registration')">回收入库</button>
|
||||
<td class="px-5 py-4">
|
||||
<div class="flex items-center gap-3 text-sm">
|
||||
<button style="color: #4a7c59" @click="openDetail(d)">详情</button>
|
||||
<button v-if="d.status === '待审批' || d.status === '审批中'" style="color: #4a7c59" @click="openApproval(d)">审批</button>
|
||||
<button v-if="d.status === '已审批'" style="color: #52C41A" @click="openRecycle(d)">回收入库</button>
|
||||
<button v-if="d.status === '已驳回'" style="color: #FA8C16">重新申请</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
@ -210,24 +180,151 @@ const getStatusStyle = (status: ScrapDevice['status']) => {
|
|||
</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"
|
||||
style="border-color: #D9D9D9; color: rgba(0, 0, 0, 0.45)"
|
||||
disabled
|
||||
>
|
||||
上一页
|
||||
</button>
|
||||
<button class="px-3 py-1 rounded" style="background-color: #4a7c59; color: #fff">1</button>
|
||||
<button class="px-3 py-1 rounded border" style="border-color: #D9D9D9; color: rgba(0, 0, 0, 0.85)">2</button>
|
||||
<button class="px-3 py-1 rounded border" style="border-color: #D9D9D9; color: rgba(0, 0, 0, 0.85)">3</button>
|
||||
<button class="px-3 py-1 rounded border" style="border-color: #D9D9D9; color: rgba(0, 0, 0, 0.85)">下一页</button>
|
||||
<!-- Detail Drawer -->
|
||||
<div v-if="showDetailDrawer && currentDevice" 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-[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="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="px-2 py-0.5 rounded text-xs" :style="getStatusStyle(currentDevice.status)">{{ currentDevice.status }}</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>设备SN:{{ currentDevice.sn }}</div>
|
||||
<div>设备型号:{{ currentDevice.model }}</div>
|
||||
<div>报废日期:{{ currentDevice.scrapDate }}</div>
|
||||
<div>报废原因:{{ currentDevice.reason }}</div>
|
||||
<div>残值评估:{{ currentDevice.residualValue }} 元</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>申请人:{{ currentDevice.applicant }}</div>
|
||||
<div>审批人:{{ currentDevice.approver || '待分配' }}</div>
|
||||
<div>来源工单:{{ currentDevice.sourceOrder }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="currentDevice.recyclableParts.length" class="p-5 rounded-lg" style="background-color: #FAFAFA; border: 1px solid #F0F0F0">
|
||||
<h4 class="text-base font-semibold mb-3">可回收物料</h4>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<span v-for="p in currentDevice.recyclableParts" :key="p" class="px-3 py-1 rounded text-xs" style="background-color: #eef5f0; color: #4a7c59; border: 1px solid #a3c4ad">{{ p }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Approval Timeline -->
|
||||
<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-3">
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="w-6 h-6 rounded-full flex items-center justify-center flex-shrink-0" style="background-color: #F6FFED; border: 1px solid #B7EB8F"><Check :size="12" style="color: #52C41A" /></div>
|
||||
<div class="text-sm" style="color: rgba(0,0,0,0.65)">
|
||||
<div>{{ currentDevice.applicant }} 提交报废申请</div>
|
||||
<div class="text-xs" style="color: rgba(0,0,0,0.35)">{{ currentDevice.scrapDate }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="currentDevice.status !== '待审批'" class="flex items-start gap-3">
|
||||
<div class="w-6 h-6 rounded-full flex items-center justify-center flex-shrink-0" :style="currentDevice.status === '已驳回' ? { backgroundColor: '#FFF1F0', border: '1px solid #FFCCC7' } : { backgroundColor: '#F6FFED', border: '1px solid #B7EB8F' }">
|
||||
<X v-if="currentDevice.status === '已驳回'" :size="12" style="color: #FF4D4F" />
|
||||
<Check v-else :size="12" style="color: #52C41A" />
|
||||
</div>
|
||||
<div class="text-sm" style="color: rgba(0,0,0,0.65)">
|
||||
<div>{{ currentDevice.approver || '审批中' }} {{ currentDevice.status === '已驳回' ? '驳回申请' : '审批通过' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="currentDevice.status === '已回收'" class="flex items-start gap-3">
|
||||
<div class="w-6 h-6 rounded-full flex items-center justify-center flex-shrink-0" style="background-color: #F6FFED; border: 1px solid #B7EB8F"><Check :size="12" style="color: #52C41A" /></div>
|
||||
<div class="text-sm" style="color: rgba(0,0,0,0.65)">物料回收完成,设备已注销</div>
|
||||
</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="showDetailDrawer = false">关闭</button>
|
||||
<button v-if="currentDevice.status === '待审批' || currentDevice.status === '审批中'" class="px-6 py-2 rounded text-white text-sm" style="background-color: #4a7c59" @click="showDetailDrawer = false; openApproval(currentDevice)">去审批</button>
|
||||
<button v-if="currentDevice.status === '已审批'" class="px-6 py-2 rounded text-white text-sm" style="background-color: #52C41A" @click="showDetailDrawer = false; openRecycle(currentDevice)">回收入库</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Approval Drawer -->
|
||||
<div v-if="showApprovalDrawer && currentDevice" class="fixed inset-0 z-50 flex justify-end" style="background-color: rgba(0,0,0,0.45)" @click.self="showApprovalDrawer = 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 gap-3 p-5 border-b" style="border-color: #F0F0F0">
|
||||
<button @click="showApprovalDrawer = 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">
|
||||
<!-- Warning -->
|
||||
<div class="p-4 rounded-lg flex items-start gap-3" style="background-color: #FFF1F0; border: 1px solid #FFCCC7">
|
||||
<AlertTriangle :size="18" style="color: #FF4D4F; flex-shrink: 0; margin-top: 2px" />
|
||||
<div class="text-sm" style="color: #CF1322">报废操作不可逆,审批通过后设备将进入回收流程,请仔细核实信息。</div>
|
||||
</div>
|
||||
<!-- Device summary -->
|
||||
<div class="p-5 rounded-lg" style="background-color: #FAFAFA; border: 1px solid #F0F0F0">
|
||||
<h4 class="text-sm font-semibold mb-3">报废设备</h4>
|
||||
<div class="space-y-1 text-sm" style="color: rgba(0,0,0,0.65)">
|
||||
<div>{{ currentDevice.sn }} · {{ currentDevice.model }}</div>
|
||||
<div>报废原因:{{ currentDevice.reason }}</div>
|
||||
<div>残值评估:{{ currentDevice.residualValue }} 元</div>
|
||||
<div>申请人:{{ currentDevice.applicant }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Recyclable parts -->
|
||||
<div v-if="currentDevice.recyclableParts.length" class="p-5 rounded-lg" style="background-color: #FAFAFA; border: 1px solid #F0F0F0">
|
||||
<h4 class="text-sm font-semibold mb-3">可回收物料({{ currentDevice.recyclableParts.length }}件)</h4>
|
||||
<div class="space-y-1 text-sm" style="color: rgba(0,0,0,0.65)">
|
||||
<div v-for="p in currentDevice.recyclableParts" :key="p">{{ p }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Comment -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">审批意见</label>
|
||||
<textarea v-model="approvalComment" class="w-full px-3 py-2 border rounded text-sm" style="border-color: #D9D9D9; min-height: 80px; resize: vertical" placeholder="请输入审批意见..."></textarea>
|
||||
</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="showApprovalDrawer = false">取消</button>
|
||||
<button class="px-6 py-2 rounded text-sm" style="border: 1px solid #FF4D4F; color: #FF4D4F" @click="showApprovalDrawer = false">驳回</button>
|
||||
<button class="px-6 py-2 rounded text-white text-sm" style="background-color: #4a7c59" @click="showApprovalDrawer = false">审批通过</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recycle Drawer -->
|
||||
<div v-if="showRecycleDrawer && currentDevice" class="fixed inset-0 z-50 flex justify-end" style="background-color: rgba(0,0,0,0.45)" @click.self="showRecycleDrawer = 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 gap-3 p-5 border-b" style="border-color: #F0F0F0">
|
||||
<button @click="showRecycleDrawer = 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">
|
||||
<div class="p-5 rounded-lg" style="background-color: #FAFAFA; border: 1px solid #F0F0F0">
|
||||
<h4 class="text-sm font-semibold mb-2">报废设备</h4>
|
||||
<div class="text-sm" style="color: rgba(0,0,0,0.65)">{{ currentDevice.sn }} · {{ currentDevice.model }}</div>
|
||||
</div>
|
||||
<!-- Recyclable parts checklist -->
|
||||
<div>
|
||||
<h4 class="text-sm font-semibold mb-3">物料检测与回收</h4>
|
||||
<div v-if="currentDevice.recyclableParts.length === 0" class="text-sm" style="color: rgba(0,0,0,0.45)">无可回收物料</div>
|
||||
<div v-else class="space-y-2">
|
||||
<label v-for="p in currentDevice.recyclableParts" :key="p" class="flex items-center gap-3 p-3 rounded cursor-pointer text-sm" :style="{ backgroundColor: recycleChecked.has(p) ? '#F6FFED' : '#FAFAFA', border: recycleChecked.has(p) ? '1px solid #B7EB8F' : '1px solid #F0F0F0' }">
|
||||
<input type="checkbox" :checked="recycleChecked.has(p)" @change="recycleChecked.has(p) ? recycleChecked.delete(p) : recycleChecked.add(p)" class="w-4 h-4" style="accent-color: #4a7c59" />
|
||||
<span>{{ p }}</span>
|
||||
<span v-if="recycleChecked.has(p)" class="ml-auto text-xs" style="color: #52C41A">已检测</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2">回收备注</label>
|
||||
<textarea v-model="recycleNote" 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 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="showRecycleDrawer = false">取消</button>
|
||||
<button class="px-6 py-2 rounded text-white text-sm" style="background-color: #52C41A" @click="showRecycleDrawer = false; router.push('/registration')">确认回收入库</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -21,8 +21,6 @@ export const router = createRouter({
|
|||
{ path: 'repair/:orderId', component: () => import('./pages/RepairOrderDetail.vue') },
|
||||
{ path: 'scrap', component: () => import('./pages/ScrapManagement.vue') },
|
||||
{ path: 'firmware', component: () => import('./pages/FirmwareLibrary.vue') },
|
||||
{ path: 'firmware-upgrade', component: () => import('./pages/PlaceholderPage.vue'), props: { title: '升级任务' } },
|
||||
{ path: 'calibration-reports', component: () => import('./pages/PlaceholderPage.vue'), props: { title: '校准报告' } },
|
||||
{ path: 'config-files', component: () => import('./pages/ConfigFileManagement.vue') },
|
||||
{ path: 'config-files/:configId', component: () => import('./pages/ParameterConfiguration.vue') },
|
||||
{ path: 'reports', component: () => import('./pages/PlaceholderPage.vue'), props: { title: '数据报表' } },
|
||||
|
|
|
|||
Loading…
Reference in New Issue