Compare commits
291 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97d1f08bbe | ||
|
|
7d0053eb71 | ||
|
|
666fb22c49 | ||
|
|
b319a89501 | ||
|
|
8e0b3be438 | ||
|
|
b0d92de738 | ||
|
|
92b3e25989 | ||
|
|
43c75c96a7 | ||
|
|
da0aa0cd0f | ||
|
|
b0c88b9df2 | ||
|
|
7c0ec5844a | ||
|
|
a4a64ef0f9 | ||
|
|
58bb25500e | ||
|
|
3d73c34343 | ||
|
|
f74fedc213 | ||
|
|
8ea49f9609 | ||
|
|
9ee71d29d4 | ||
|
|
039a67c35a | ||
|
|
e17749d47e | ||
|
|
21c859c8f1 | ||
|
|
4fe239c86f | ||
| ab62e56bbb | |||
| 5730b9c5cf | |||
|
|
d4992db198 | ||
|
|
5ccd1709a5 | ||
|
|
b48c1e0d78 | ||
|
|
fcdbbce7a9 | ||
|
|
d08194bfd8 | ||
|
|
55f579ef64 | ||
|
|
783e1c080b | ||
|
|
44cdb3273c | ||
|
|
dbc21cdbfa | ||
|
|
24d83cfd39 | ||
|
|
b213b721bb | ||
|
|
4ae42408c3 | ||
|
|
a9156f0954 | ||
|
|
3e7509cd44 | ||
|
|
24becb82e1 | ||
|
|
6608587edd | ||
|
|
5ad8cdecba | ||
|
|
6b4cca1ef7 | ||
|
|
dea0844829 | ||
|
|
bbd438d23f | ||
|
|
c88128b63b | ||
|
|
95c68942ed | ||
|
|
5db685baca | ||
|
|
fa710efea4 | ||
|
|
d0c3e1d9bd | ||
|
|
589ddd38f3 | ||
|
|
47d1500296 | ||
|
|
4a8f8bff6a | ||
|
|
2b9b87a3db | ||
|
|
b241128105 | ||
|
|
226e3271ee | ||
|
|
1c253fd713 | ||
|
|
ed81d3d398 | ||
|
|
09b54a29ab | ||
|
|
b27615baaf | ||
|
|
c735e7a5bb | ||
|
|
c78f591baf | ||
|
|
cfd8b072dd | ||
|
|
d18e34d2c9 | ||
|
|
53813795db | ||
|
|
8c3098e19a | ||
|
|
780a446aed | ||
|
|
375f01a6ab | ||
|
|
48aab7c1e9 | ||
|
|
d7cfe665e2 | ||
|
|
237c23bb70 | ||
|
|
4cd6302ee0 | ||
|
|
6a75709774 | ||
|
|
629dff1256 | ||
|
|
6d6d03c03c | ||
|
|
6122f53c8e | ||
|
|
5a7eea1052 | ||
|
|
25f3570c18 | ||
|
|
74e015bd12 | ||
|
|
da6a72807b | ||
|
|
bb7ebaea45 | ||
|
|
ae51b590af | ||
|
|
2ec3102eff | ||
|
|
f5f7d259a9 | ||
|
|
4364f88526 | ||
|
|
0f5e21a06c | ||
|
|
ddbaf5651a | ||
|
|
a847419ab5 | ||
|
|
d5fb41cbab | ||
|
|
25e7b754b7 | ||
|
|
a32ca3c849 | ||
|
|
6e979c5dcb | ||
|
|
8b578d4d8b | ||
|
|
52fcdbfe1e | ||
|
|
4559a7b5e2 | ||
|
|
567201563d | ||
|
|
772707ac42 | ||
|
|
4a6db824ba | ||
|
|
8b4c22e959 | ||
|
|
d7f1224df4 | ||
|
|
ac4e0e2077 | ||
|
|
56a6f199c0 | ||
|
|
0abb765b32 | ||
|
|
4f8fdb83d1 | ||
|
|
300b220de2 | ||
|
|
825d2cc46a | ||
|
|
95b602e6d4 | ||
|
|
a7b5bbf0bf | ||
|
|
dfbba11aae | ||
|
|
5cf39e8aa8 | ||
|
|
a19a20ddd8 | ||
|
|
0985cc5d7c | ||
|
|
2be0be681e | ||
|
|
dd9ca8f956 | ||
|
|
5cd60d9a32 | ||
|
|
959ae1dee9 | ||
|
|
d2d1490e9b | ||
|
|
7bcd88c3a7 | ||
|
|
8e3368bd29 | ||
|
|
bc03ba88f0 | ||
|
|
2aee4b281d | ||
|
|
26647222e2 | ||
|
|
a2db45cace | ||
|
|
d761c0449b | ||
|
|
dc6a346fd4 | ||
|
|
e938c6b3d9 | ||
|
|
c9fef2a9d7 | ||
|
|
9319dd06c5 | ||
|
|
7b96ce84fc | ||
|
|
b105ff890c | ||
|
|
61b87304e6 | ||
|
|
83c8dc5f19 | ||
|
|
b1ddf540ca | ||
|
|
0025895696 | ||
|
|
1ec8cce63e | ||
|
|
865d52c135 | ||
|
|
ce8607af36 | ||
|
|
4e8a6300dd | ||
| 919e81da8b | |||
|
|
18cb6dbde8 | ||
| 6d405d16ed | |||
|
|
77d2176812 | ||
| c85eac3888 | |||
|
|
27d2d82fcd | ||
|
|
ecbc3c30c8 | ||
|
|
0a65efd235 | ||
|
|
5cd8fea60c | ||
|
|
3d1b4eb7c6 | ||
| ec1330bdb8 | |||
|
|
e66bcdb293 | ||
|
|
88f1876ef0 | ||
|
|
fdc1fd6fbd | ||
|
|
022d80f30e | ||
| f59f287b63 | |||
| 6e573cc597 | |||
| d2f92ecde4 | |||
|
|
b2a6a1de4e | ||
|
|
f374df79a6 | ||
|
|
154eb9f79c | ||
|
|
d0724cb7f6 | ||
| d6d63523a3 | |||
|
|
83998f88ac | ||
|
|
9c5e54507b | ||
|
|
15689b5284 | ||
|
|
00893d2d1f | ||
|
|
0079f7415e | ||
| 327801d040 | |||
|
|
6e22c01dd8 | ||
|
|
bccb4b1f17 | ||
|
|
1f37cc567c | ||
|
|
f81503091d | ||
|
|
e29f25653e | ||
|
|
9e8e44b886 | ||
|
|
6e10b0c645 | ||
|
|
c8f3b4eddc | ||
|
|
cc848b1ffb | ||
|
|
d2f0382bd9 | ||
|
|
9fecf0ce3f | ||
|
|
290586d0ff | ||
|
|
ddc45af223 | ||
|
|
0e6f123306 | ||
|
|
7b93363b23 | ||
|
|
2fd0dcb8a3 | ||
|
|
86ee05f8af | ||
|
|
38814b9f44 | ||
|
|
fb5d13671d | ||
|
|
310a769092 | ||
|
|
8c63edabdc | ||
|
|
5363625a2f | ||
| efcd6e1cfe | |||
| acc4d0ca67 | |||
| 772f38feca | |||
|
|
1eb141e559 | ||
|
|
16b446bf20 | ||
|
|
bc0de34c15 | ||
|
|
15f2c1ee41 | ||
|
|
81a944062a | ||
|
|
3c19b05f4b | ||
|
|
c1f53cdc69 | ||
|
|
6ef2a6049b | ||
|
|
9989cc98cb | ||
|
|
e192158deb | ||
|
|
2e5d551e5d | ||
|
|
4622eb36d9 | ||
|
|
9c53b7c18e | ||
|
|
7b3805060f | ||
|
|
629600bc00 | ||
|
|
8b144b63fc | ||
|
|
098ab3a41d | ||
|
|
2a53f577aa | ||
|
|
f8b7c224b7 | ||
|
|
f0b3bdd37c | ||
|
|
bb35eb749b | ||
|
|
b3750d6a7f | ||
|
|
33ebb91ab6 | ||
|
|
3e00f2fee7 | ||
|
|
68e9856641 | ||
|
|
51b2e80493 | ||
|
|
cf77572f41 | ||
| fe6524404c | |||
|
|
ccda7218f4 | ||
|
|
ddd0e6a22b | ||
|
|
ed85453de1 | ||
|
|
04939606fc | ||
|
|
2df455b667 | ||
|
|
8fb214d7a7 | ||
|
|
4f2c8a8016 | ||
|
|
c2974dbe0d | ||
|
|
3891e5459a | ||
|
|
9ed47ac5bc | ||
|
|
114fcd140d | ||
|
|
19a30894f8 | ||
|
|
2134fea518 | ||
|
|
82f495ce3b | ||
|
|
f2ecff54a3 | ||
|
|
f56004ac15 | ||
|
|
5303c47248 | ||
| 37731592f7 | |||
|
|
c9e41e0c82 | ||
|
|
4a80a88e6e | ||
|
|
30b219e14d | ||
|
|
ecec9adeea | ||
|
|
1853152138 | ||
| 5619413f37 | |||
|
|
5b736bc475 | ||
|
|
2aa37c3fe3 | ||
|
|
4e0cb72f32 | ||
|
|
ce74ec4db7 | ||
|
|
9938306884 | ||
|
|
ef63fcf807 | ||
|
|
08cffa4256 | ||
|
|
e1a740f8bf | ||
|
|
56d035253b | ||
|
|
e7348107d0 | ||
|
|
ea38b4eb21 | ||
| 67ef976739 | |||
| 60145ec4f7 | |||
|
|
81600ddc69 | ||
|
|
e0941d628b | ||
|
|
760dcbf723 | ||
|
|
b75ecb8c66 | ||
|
|
39f4cfa670 | ||
|
|
862251b83c | ||
|
|
312490ce59 | ||
|
|
d7df999cf2 | ||
|
|
e0b1b5907b | ||
|
|
f06ca03881 | ||
|
|
a9b80ed33c | ||
|
|
834230b26a | ||
|
|
403ddbfb6e | ||
|
|
ffb44ea909 | ||
|
|
b729e4efa1 | ||
|
|
962c286fd5 | ||
|
|
c215f51554 | ||
| 859c85b427 | |||
|
|
5fe637e84f | ||
|
|
5f68071c77 | ||
|
|
1675a5af31 | ||
|
|
ec1d09dbd0 | ||
|
|
eda7b516f6 | ||
|
|
630eb48d28 | ||
|
|
0d2f8a7788 | ||
| 230c68f454 | |||
| 68934060e6 | |||
| 846e7514b7 | |||
| 410cfb0f7a | |||
|
|
c892410b07 | ||
|
|
7caa4a5303 | ||
|
|
c17efbf9f6 | ||
| c46abeecf5 | |||
|
|
0947097932 | ||
|
|
50fc02d4eb | ||
|
|
a122e9e1b3 |
@@ -25,10 +25,10 @@ module.exports = (appInfo) => {
|
|||||||
*/
|
*/
|
||||||
config.windowsOption = {
|
config.windowsOption = {
|
||||||
title: '自动检测平台',
|
title: '自动检测平台',
|
||||||
width: 1600,
|
width: 1920 /1.5,
|
||||||
height: 950,
|
height: 1080 /1.2,
|
||||||
minWidth: 1600,
|
minWidth: 1920 /1.5,
|
||||||
minHeight: 950,
|
minHeight: 1080 /1.2,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
//webSecurity: false,
|
//webSecurity: false,
|
||||||
contextIsolation: false, // false -> 可在渲染进程中使用electron的api,true->需要bridge.js(contextBridge)
|
contextIsolation: false, // false -> 可在渲染进程中使用electron的api,true->需要bridge.js(contextBridge)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ module.exports = (appInfo) => {
|
|||||||
/**
|
/**
|
||||||
* 开发者工具
|
* 开发者工具
|
||||||
*/
|
*/
|
||||||
config.openDevTools = true;
|
config.openDevTools = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 应用程序顶部菜单
|
* 应用程序顶部菜单
|
||||||
|
|||||||
@@ -14,7 +14,10 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "^2.3.1",
|
"@element-plus/icons-vue": "^2.3.1",
|
||||||
|
"@types/event-source-polyfill": "^1.0.5",
|
||||||
|
"@vue-flow/core": "^1.45.0",
|
||||||
"@vueuse/core": "^10.4.1",
|
"@vueuse/core": "^10.4.1",
|
||||||
|
"autofit.js": "^3.2.8",
|
||||||
"axios": "^1.7.3",
|
"axios": "^1.7.3",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
"dayjs": "^1.11.9",
|
"dayjs": "^1.11.9",
|
||||||
@@ -22,10 +25,12 @@
|
|||||||
"echarts": "^5.4.3",
|
"echarts": "^5.4.3",
|
||||||
"echarts-liquidfill": "^3.1.0",
|
"echarts-liquidfill": "^3.1.0",
|
||||||
"element-plus": "^2.7.8",
|
"element-plus": "^2.7.8",
|
||||||
|
"event-source-polyfill": "^1.0.31",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"md5": "^2.3.0",
|
"md5": "^2.3.0",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"mkdirp": "^3.0.1",
|
"mkdirp": "^3.0.1",
|
||||||
|
"node-forge": "^1.3.1",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"pinia": "^2.2.1",
|
"pinia": "^2.2.1",
|
||||||
"pinia-plugin-persistedstate": "^3.2.1",
|
"pinia-plugin-persistedstate": "^3.2.1",
|
||||||
@@ -45,6 +50,7 @@
|
|||||||
"@types/html2canvas": "^1.0.0",
|
"@types/html2canvas": "^1.0.0",
|
||||||
"@types/md5": "^2.3.2",
|
"@types/md5": "^2.3.2",
|
||||||
"@types/node": "^20.14.14",
|
"@types/node": "^20.14.14",
|
||||||
|
"@types/node-forge": "^1.3.11",
|
||||||
"@types/nprogress": "^0.2.0",
|
"@types/nprogress": "^0.2.0",
|
||||||
"@types/qs": "^6.9.8",
|
"@types/qs": "^6.9.8",
|
||||||
"@types/sortablejs": "^1.15.2",
|
"@types/sortablejs": "^1.15.2",
|
||||||
@@ -70,11 +76,11 @@
|
|||||||
"unplugin-auto-import": "^0.18.3",
|
"unplugin-auto-import": "^0.18.3",
|
||||||
"unplugin-vue-components": "^0.27.4",
|
"unplugin-vue-components": "^0.27.4",
|
||||||
"unplugin-vue-setup-extend-plus": "^1.0.0",
|
"unplugin-vue-setup-extend-plus": "^1.0.0",
|
||||||
"vite": "^5.3.1",
|
"vite": "^5.4.19",
|
||||||
"vite-plugin-compression": "^0.5.1",
|
"vite-plugin-compression": "^0.5.1",
|
||||||
"vite-plugin-eslint": "^1.8.1",
|
"vite-plugin-eslint": "^1.8.1",
|
||||||
"vite-plugin-html": "^3.2.0",
|
"vite-plugin-html": "^3.2.0",
|
||||||
"vite-plugin-node-polyfills": "^0.22.0",
|
"vite-plugin-node-polyfills": "^0.24.0",
|
||||||
"vite-plugin-pwa": "^0.16.5",
|
"vite-plugin-pwa": "^0.16.5",
|
||||||
"vite-plugin-svg-icons": "^2.0.1",
|
"vite-plugin-svg-icons": "^2.0.1",
|
||||||
"vue-tsc": "^2.0.21"
|
"vue-tsc": "^2.0.21"
|
||||||
|
|||||||
@@ -1,27 +1,25 @@
|
|||||||
<template>
|
<template>
|
||||||
<!--element-plus语言国际化,全局修改为中文-->
|
<!--element-plus语言国际化,全局修改为中文-->
|
||||||
<el-config-provider
|
<el-config-provider :locale="locale" :size="assemblySize" :button="buttonConfig">
|
||||||
:locale='locale'
|
<router-view />
|
||||||
:size='assemblySize'
|
</el-config-provider>
|
||||||
:button='buttonConfig'
|
|
||||||
>
|
|
||||||
<router-view />
|
|
||||||
</el-config-provider>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang='ts' setup>
|
<script lang="ts" setup>
|
||||||
defineOptions({
|
import autofit from 'autofit.js'
|
||||||
name: 'App',
|
|
||||||
})
|
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { getBrowserLang } from '@/utils'
|
import { getBrowserLang } from '@/utils'
|
||||||
import { useTheme } from '@/hooks/useTheme'
|
import { useTheme } from '@/hooks/useTheme'
|
||||||
import { ElConfigProvider } from 'element-plus'
|
import { ElConfigProvider } from 'element-plus'
|
||||||
import { LanguageType } from './stores/interface'
|
import { type LanguageType } from './stores/interface'
|
||||||
import { useGlobalStore } from '@/stores/modules/global'
|
import { useGlobalStore } from '@/stores/modules/global'
|
||||||
import en from 'element-plus/es/locale/lang/en'
|
import en from 'element-plus/es/locale/lang/en'
|
||||||
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'App'
|
||||||
|
})
|
||||||
|
|
||||||
const globalStore = useGlobalStore()
|
const globalStore = useGlobalStore()
|
||||||
|
|
||||||
// init theme
|
// init theme
|
||||||
@@ -31,16 +29,26 @@ initTheme()
|
|||||||
// init language
|
// init language
|
||||||
const i18n = useI18n()
|
const i18n = useI18n()
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const language = globalStore.language ?? getBrowserLang()
|
const language = globalStore.language ?? getBrowserLang()
|
||||||
i18n.locale.value = language
|
i18n.locale.value = language
|
||||||
globalStore.setGlobalState('language', language as LanguageType)
|
globalStore.setGlobalState('language', language as LanguageType)
|
||||||
|
// 自动适配
|
||||||
|
autofit.init({
|
||||||
|
el: '#app',
|
||||||
|
//dh: 720 * 1,
|
||||||
|
//dw: 1280 * 1.2,
|
||||||
|
dw: 1920 / 1.5,
|
||||||
|
dh: 1080 / 1.2,
|
||||||
|
resize: true,
|
||||||
|
limit: 0.1
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// element language
|
// element language
|
||||||
const locale = computed(() => {
|
const locale = computed(() => {
|
||||||
if (globalStore.language == 'zh') return zhCn
|
if (globalStore.language == 'zh') return zhCn
|
||||||
if (globalStore.language == 'en') return en
|
if (globalStore.language == 'en') return en
|
||||||
return getBrowserLang() == 'zh' ? zhCn : en
|
return getBrowserLang() == 'zh' ? zhCn : en
|
||||||
})
|
})
|
||||||
|
|
||||||
// element assemblySize
|
// element assemblySize
|
||||||
|
|||||||
@@ -1,119 +1,158 @@
|
|||||||
export namespace CheckData {
|
export namespace CheckData {
|
||||||
export interface DataCheck {
|
export interface DataCheck {
|
||||||
scriptName: string,
|
scriptName: string
|
||||||
errorSysId: string,
|
errorSysId: string
|
||||||
dataRule: string,
|
dataRule: string
|
||||||
deviceName: string,
|
deviceName: string
|
||||||
chnNum: string,
|
chnNum: string
|
||||||
|
deviceId: string
|
||||||
|
num?: string | number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PhaseCheckResult {
|
export interface PhaseCheckResult {
|
||||||
// 检测源定值-标准值
|
// 检测源定值-标准值
|
||||||
resultData: number,
|
resultData: number
|
||||||
// 装置原始数据-被检值
|
// 装置原始数据-被检值
|
||||||
data: number,
|
data: number
|
||||||
|
|
||||||
|
|
||||||
|
// 误差值
|
||||||
|
errorData: number
|
||||||
// 第几次谐波
|
// 第几次谐波
|
||||||
num?: number,
|
num?: number
|
||||||
//符合、不符合
|
//符合、不符合
|
||||||
isData?: number,
|
isData?: number
|
||||||
//最大误差值
|
//最大误差值
|
||||||
radius?: string,
|
radius?: string
|
||||||
|
unit?: string
|
||||||
|
}
|
||||||
|
export interface DataItem {
|
||||||
|
num: number
|
||||||
|
isData: number
|
||||||
|
data: number
|
||||||
|
resultData: number
|
||||||
|
radius: string
|
||||||
|
errorData: number
|
||||||
|
unit: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TableRow {
|
||||||
|
isData: number
|
||||||
|
harmNum: number
|
||||||
|
radius: string
|
||||||
|
dataA: DataItem
|
||||||
|
dataB: DataItem
|
||||||
|
dataC: DataItem
|
||||||
|
dataT: DataItem | null
|
||||||
|
unit: string
|
||||||
|
timeDev?: string
|
||||||
|
uaDev?: string | number
|
||||||
|
ubDev?: string | number
|
||||||
|
ucDev?: string | number
|
||||||
|
utDev?: string | number
|
||||||
|
uaStdDev?: string | number
|
||||||
|
ubStdDev?: string | number
|
||||||
|
ucStdDev?: string | number
|
||||||
|
utStdDev?: string | number
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用于定义 查看(设备)通道检测结果响应数据 类型
|
* 用于定义 查看(设备)通道检测结果响应数据 类型
|
||||||
*/
|
*/
|
||||||
export interface ResCheckResult {
|
export interface ResCheckResult {
|
||||||
dataA?: PhaseCheckResult | null,
|
dataA?: PhaseCheckResult | null
|
||||||
dataB?: PhaseCheckResult | null,
|
dataB?: PhaseCheckResult | null
|
||||||
dataC?: PhaseCheckResult | null,
|
dataC?: PhaseCheckResult | null
|
||||||
dataT?: PhaseCheckResult | null,
|
dataT?: PhaseCheckResult | null
|
||||||
|
|
||||||
// 第几次谐波
|
// 第几次谐波
|
||||||
//num: number | null,
|
//num: number | null,
|
||||||
//符合、不符合
|
//符合、不符合
|
||||||
isData?: number,
|
isData?: number
|
||||||
//最大误差值
|
//最大误差值
|
||||||
radius?: string,
|
radius?: string
|
||||||
//单位
|
//单位
|
||||||
unit?: string,
|
unit?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用于定义 查看(设备)通道检测结果表格展示数据 类型
|
* 用于定义 查看(设备)通道检测结果表格展示数据 类型
|
||||||
*/
|
*/
|
||||||
export interface CheckResult {
|
export interface CheckResult {
|
||||||
stdA?: string,
|
stdA?: string
|
||||||
dataA?: string,
|
dataA?: string
|
||||||
errorA?: string,
|
errorA?: string
|
||||||
maxErrorA?: string,
|
maxErrorA?: string
|
||||||
isDataA?: number,
|
isDataA?: number
|
||||||
stdB?: string,
|
unitA?: string
|
||||||
dataB?: string,
|
stdB?: string
|
||||||
errorB?: string,
|
dataB?: string
|
||||||
maxErrorB?: string,
|
errorB?: string
|
||||||
isDataB?: number,
|
maxErrorB?: string
|
||||||
stdC?: string,
|
isDataB?: number
|
||||||
dataC?: string,
|
unitB?: string
|
||||||
errorC?: string,
|
stdC?: string
|
||||||
maxErrorC?: string,
|
dataC?: string
|
||||||
isDataC?: number,
|
errorC?: string
|
||||||
stdT?: string,
|
maxErrorC?: string
|
||||||
dataT?: string,
|
isDataC?: number
|
||||||
errorT?: string,
|
unitC?: string
|
||||||
maxErrorT?: string,
|
stdT?: string
|
||||||
isDataT?: number,
|
dataT?: string
|
||||||
|
errorT?: string
|
||||||
|
maxErrorT?: string
|
||||||
|
isDataT?: number
|
||||||
|
unitT?: string
|
||||||
|
|
||||||
//最大误差值
|
//最大误差值
|
||||||
maxError?: string,
|
maxError?: string
|
||||||
unit?: string,
|
unit?: string
|
||||||
//符合、不符合
|
//符合、不符合
|
||||||
result?: number,
|
result?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用于定义 具体通道的原始数据类型
|
* 用于定义 具体通道的原始数据类型
|
||||||
*/
|
*/
|
||||||
export interface RawDataItem {
|
export interface RawDataItem {
|
||||||
time?: string,
|
time?: string
|
||||||
harmNum?: number | null,
|
harmNum?: number | null
|
||||||
dataA?: string,
|
dataA?: string
|
||||||
dataB?: string,
|
dataB?: string
|
||||||
dataC?: string,
|
dataC?: string
|
||||||
dataT?: string,
|
dataT?: string
|
||||||
unit?: string | null
|
unit?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Device {
|
export interface Device {
|
||||||
deviceId: string; //装置序号Id
|
deviceId: string //装置序号Id
|
||||||
deviceName: string; //设备名称
|
deviceName: string //设备名称
|
||||||
chnNum: number; //设备通道数
|
chnNum: number //设备通道数
|
||||||
|
|
||||||
planId: string; //计划Id
|
planId: string //计划Id
|
||||||
devType: string; //设备类型
|
devType: string //设备类型
|
||||||
devVolt:number; //设备电压
|
devVolt: number //设备电压
|
||||||
devCurr:number; //设备电流
|
devCurr: number //设备电流
|
||||||
factorFlag:number; //是否支持系数校准
|
factorFlag: number //是否支持系数校准
|
||||||
|
checkResult: number //检测结果
|
||||||
|
chnNumList: string[] //连线存储数据
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用来描述检测脚本类型
|
// 用来描述检测脚本类型
|
||||||
export interface ScriptItem {
|
export interface ScriptItem {
|
||||||
id: string,
|
id: string
|
||||||
code: string,
|
code: string
|
||||||
scriptName: string,
|
scriptName: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用来描述 检测数据-左侧树结构
|
// 用来描述 检测数据-左侧树结构
|
||||||
export interface TreeItem {
|
export interface TreeItem {
|
||||||
id:string | null,
|
id: string | null
|
||||||
scriptTypeName: string | null,
|
scriptTypeName: string | null
|
||||||
sourceDesc: string | null,
|
sourceDesc: string | null
|
||||||
harmNum: number | null,
|
harmNum: number | null
|
||||||
index: number | null,
|
index: number | null
|
||||||
fly: number | null,
|
fly: number | null
|
||||||
children: TreeItem[] | null,
|
children: TreeItem[] | null
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用来描述 通道检测结果
|
// 用来描述 通道检测结果
|
||||||
@@ -123,30 +162,32 @@ export namespace CheckData {
|
|||||||
SUCCESS = 1,
|
SUCCESS = 1,
|
||||||
FAIL = 2,
|
FAIL = 2,
|
||||||
TIMEOUT = 3,
|
TIMEOUT = 3,
|
||||||
ERRORDATA = 4
|
ERRORDATA = 4,
|
||||||
|
NOT_PART_IN_ERROR = 5
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeviceCheckResult {
|
export interface DeviceCheckResult {
|
||||||
deviceId: string,
|
deviceId: string
|
||||||
deviceName: string,
|
deviceName: string
|
||||||
|
code?: string
|
||||||
chnResult: ChnCheckResultEnum[] //通道检测结果
|
chnResult: ChnCheckResultEnum[] //通道检测结果
|
||||||
}
|
}
|
||||||
|
|
||||||
//用来描述 某个脚本测试项对所有通道的检测结果
|
//用来描述 某个脚本测试项对所有通道的检测结果
|
||||||
export interface ScriptChnItem {
|
export interface ScriptChnItem {
|
||||||
scriptType: string
|
scriptType: string
|
||||||
scriptName?: string //可以不要该属性,有点多余
|
scriptName?: string //可以不要该属性,有点多余
|
||||||
|
code?: string
|
||||||
// 设备
|
// 设备
|
||||||
devices: Array<DeviceCheckResult>
|
devices: Array<DeviceCheckResult>
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ButtonColorEnum {
|
export enum ButtonColorEnum {
|
||||||
INFO = '#909399',
|
INFO = '#909399',
|
||||||
LOADING = '#003078',
|
LOADING = 'var(--el-color-primary)',
|
||||||
SUCCESS = '#67c23a',
|
SUCCESS = '#91cc75',
|
||||||
WARNING = '#e6a23c',
|
WARNING = '#e6a23c',
|
||||||
DANGER = '#f56c6c',
|
DANGER = '#f56c6c'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -154,25 +195,24 @@ export namespace CheckData {
|
|||||||
*/
|
*/
|
||||||
export interface ButtonResult {
|
export interface ButtonResult {
|
||||||
color: ButtonColorEnum
|
color: ButtonColorEnum
|
||||||
icon: 'Minus' | 'Loading' | 'Close' | 'CircleCheckFilled' | 'Link' | 'WarnTriangleFilled'
|
icon: 'More' | 'Loading' | 'Close' | 'CircleCheckFilled' | 'Link' | 'WarnTriangleFilled' | 'Minus'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用于描述 脚本检测结果展示的按钮类型
|
* 用于描述 脚本检测结果展示的按钮类型
|
||||||
*/
|
*/
|
||||||
export interface ScriptChnViewItem {
|
export interface ScriptChnViewItem {
|
||||||
scriptType: string,
|
scriptType: string
|
||||||
scriptName?: string //脚本项名称,可以不要该属性,有点多余
|
scriptName?: string //脚本项名称,可以不要该属性,有点多余
|
||||||
|
|
||||||
// 设备
|
// 设备
|
||||||
devices: Array<{
|
devices: Array<{
|
||||||
deviceId: string,
|
deviceId: string
|
||||||
deviceName: string,
|
deviceName: string
|
||||||
chnResult: ButtonResult[],
|
chnResult: ButtonResult[]
|
||||||
}>
|
}>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定义检测日志类型
|
* 定义检测日志类型
|
||||||
*/
|
*/
|
||||||
@@ -185,13 +225,16 @@ export namespace CheckData {
|
|||||||
* 定义手动检测时,勾选的测试项
|
* 定义手动检测时,勾选的测试项
|
||||||
*/
|
*/
|
||||||
export interface SelectTestItem {
|
export interface SelectTestItem {
|
||||||
preTest: boolean,
|
preTest: boolean
|
||||||
timeTest: boolean,
|
timeTest: boolean
|
||||||
channelsTest: boolean,
|
channelsTest: boolean
|
||||||
test: boolean
|
test: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//描述比对式检测项描述
|
||||||
|
export interface CompareTestItem {
|
||||||
|
id: string
|
||||||
|
code: string
|
||||||
|
name: string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,22 @@
|
|||||||
import http from "@/api";
|
import { pa } from 'element-plus/es/locale/index.mjs';
|
||||||
import {CheckData} from "@/api/check/interface";
|
import http from '@/api'
|
||||||
|
import {CheckData} from '@/api/check/interface'
|
||||||
|
|
||||||
export const getBigTestItem = (params: {
|
export const getBigTestItem = (params: {
|
||||||
reCheckType:number,
|
reCheckType: number
|
||||||
planId: string,
|
planId: string
|
||||||
devId: string,
|
devIds: string[]
|
||||||
|
patternId: string
|
||||||
}) => {
|
}) => {
|
||||||
return http.post(`/adPlan/getBigTestItem`, params, {loading: false});
|
return http.post(`/adPlan/getBigTestItem`, params, {loading: false})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getScriptList = (params: {
|
||||||
|
devId:string,
|
||||||
|
chnNum:number,
|
||||||
|
num:number
|
||||||
|
}) => {
|
||||||
|
return http.post('/result/getCheckItem', params, {loading: false})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -14,12 +24,12 @@ export const getBigTestItem = (params: {
|
|||||||
* @param params 当为scriptType为null时,表示查询所有脚本类型,否则只查询指定脚本类型。当为chnNum为-1时,表示查询所有通道,否则只查询指定通道。
|
* @param params 当为scriptType为null时,表示查询所有脚本类型,否则只查询指定脚本类型。当为chnNum为-1时,表示查询所有通道,否则只查询指定通道。
|
||||||
*/
|
*/
|
||||||
export const getFormData = (params: {
|
export const getFormData = (params: {
|
||||||
planId: string,
|
planId: string
|
||||||
deviceId: string,
|
deviceId: string
|
||||||
chnNum: string,
|
chnNum: string
|
||||||
scriptType: string | null
|
scriptType: string | null
|
||||||
}) => {
|
}) => {
|
||||||
return http.post("/result/formContent/", params, {loading: false});
|
return http.post('/result/formContent/', params, {loading: false})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -27,13 +37,13 @@ export const getFormData = (params: {
|
|||||||
* @param params
|
* @param params
|
||||||
*/
|
*/
|
||||||
export const getTreeData = (params: {
|
export const getTreeData = (params: {
|
||||||
scriptId?: string,
|
scriptId?: string
|
||||||
devId?: string,
|
devId?: string
|
||||||
devNum?: string,
|
devNum?: string
|
||||||
scriptType?: string | null,
|
scriptType?: string | null
|
||||||
code?: number,
|
code?: string
|
||||||
}) => {
|
}) => {
|
||||||
return http.post<CheckData.TreeItem[]>("/result/treeData/", params, {loading: true});
|
return http.post<CheckData.TreeItem[]>('/result/treeData/', params, {loading: false})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,23 +51,93 @@ export const getTreeData = (params: {
|
|||||||
* @param params
|
* @param params
|
||||||
*/
|
*/
|
||||||
export const getTableData = (params: {
|
export const getTableData = (params: {
|
||||||
scriptType: string | null,
|
scriptType: string | null
|
||||||
scriptId: string,
|
scriptId: string
|
||||||
devId: string,
|
devId: string
|
||||||
devNum: string,
|
devNum: string
|
||||||
code: number,
|
code: string
|
||||||
index: number,
|
index: number
|
||||||
}) => {
|
}) => {
|
||||||
return http.post("/result/resultData/", params, {loading: false});
|
return http.post('/result/resultData/', params, {loading: false})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const exportRawData = (params: {
|
export const exportRawData = (params: {
|
||||||
scriptType: string | null,
|
scriptType: string | null
|
||||||
scriptId: string,
|
scriptId: string
|
||||||
devId: string,
|
devId: string
|
||||||
devNum: string,
|
devNum: string
|
||||||
code: number,
|
code: string
|
||||||
index: number,
|
index: number
|
||||||
}) => {
|
}) => {
|
||||||
return http.download("/result/exportRawData", params, {loading: false});
|
return http.download('/result/exportRawData', params, {loading: false})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重新计算
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const reCalculate = (params: {
|
||||||
|
planId: string
|
||||||
|
scriptId: string
|
||||||
|
errorSysId: string
|
||||||
|
deviceId: string
|
||||||
|
code: string
|
||||||
|
patternId: string
|
||||||
|
|
||||||
|
}) => {
|
||||||
|
return http.post('/result/reCalculate', params, {loading: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取数据获取基本信息
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const getContrastFormContent = (params: {
|
||||||
|
planId: string
|
||||||
|
scriptType: string
|
||||||
|
deviceId: string
|
||||||
|
chnNum: string
|
||||||
|
num: number | null
|
||||||
|
patternId: string
|
||||||
|
}) => {
|
||||||
|
return http.post('/result/getContrastFormContent', params, {loading: false})
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取检测结果
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const getContrastResult = (params: {
|
||||||
|
planId: string
|
||||||
|
scriptType: string
|
||||||
|
deviceId: string
|
||||||
|
chnNum: string | number
|
||||||
|
num: number | string | null
|
||||||
|
waveNum: number | null
|
||||||
|
isWave: boolean
|
||||||
|
patternId: string
|
||||||
|
}) => {
|
||||||
|
return http.post('/result/getContrastResult', params, {loading: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更换误差体系
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const changeErrorSystem = (params: {
|
||||||
|
planId: string
|
||||||
|
scriptId: string
|
||||||
|
errorSysId: string
|
||||||
|
deviceId: string
|
||||||
|
code: string
|
||||||
|
patternId: string
|
||||||
|
}) => {
|
||||||
|
return http.post('/result/changeErrorSystem', params, {loading: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除(误差体系切换时产生的)临时表
|
||||||
|
* @param code
|
||||||
|
*/
|
||||||
|
export const deleteTempTable = (code: string) => {
|
||||||
|
return http.get(`/result/deleteTempTable?code=${code}`, null, {loading: false})
|
||||||
|
}
|
||||||
|
|||||||
@@ -266,172 +266,4 @@ const data = [
|
|||||||
reCheck_Num: 0, //复检次数
|
reCheck_Num: 0, //复检次数
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
// const plan_devicedata = [
|
|
||||||
// {
|
|
||||||
// id: '1', //装置序号ID
|
|
||||||
// name: '模拟装置1', //设备名称
|
|
||||||
// dev_Type: 'PQS882A',//设备类型
|
|
||||||
// dev_Chns: 1, //设备通道数
|
|
||||||
// check_Result: '合格', //检测结果
|
|
||||||
// report_State: '已生成', //报告状态
|
|
||||||
// document_State: '归档', //归档状态
|
|
||||||
// check_State:'检测完成',//检测状态
|
|
||||||
// reCheck_Num: 0, //复检次数
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: '2', //装置序号ID
|
|
||||||
// name: '模拟装置2', //设备名称
|
|
||||||
// dev_Type: 'PQS882B4',//设备类型
|
|
||||||
// dev_Chns: 4, //设备通道数
|
|
||||||
// check_Result: '/', //检测结果
|
|
||||||
// report_State: '未生成', //报告状态
|
|
||||||
// document_State: '未归档', //归档状态
|
|
||||||
// check_State:'未检',//检测状态
|
|
||||||
// reCheck_Num: 0, //复检次数
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: '3', //装置序号ID
|
|
||||||
// name: '模拟装置3', //设备名称
|
|
||||||
// dev_Type: 'PQS882B4',//设备类型
|
|
||||||
// dev_Chns: 4, //设备通道数
|
|
||||||
// check_Result: '/', //检测结果
|
|
||||||
// report_State: '未生成', //报告状态
|
|
||||||
// document_State: '未归档', //归档状态
|
|
||||||
// check_State:'检测中',//检测状态
|
|
||||||
// reCheck_Num: 0, //复检次数
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: '4', //装置序号ID
|
|
||||||
// name: '模拟装置4', //设备名称
|
|
||||||
// dev_Type: 'PQS882B4',//设备类型
|
|
||||||
// dev_Chns: 4, //设备通道数
|
|
||||||
// check_Result: '不合格', //检测结果
|
|
||||||
// report_State: '未生成', //报告状态
|
|
||||||
// document_State: '未归档', //归档状态
|
|
||||||
// check_State:'检测完成',//检测状态
|
|
||||||
// reCheck_Num: 1, //复检次数
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: '5', //装置序号ID
|
|
||||||
// name: '中电测试装置', //设备名称
|
|
||||||
// dev_Type: 'PMC-680M-22-22-00-115ANBC',//设备类型
|
|
||||||
// dev_Chns: 4, //设备通道数
|
|
||||||
// check_Result: '不合格', //检测结果
|
|
||||||
// report_State: '未生成', //报告状态
|
|
||||||
// document_State: '未归档', //归档状态
|
|
||||||
// check_State:'检测完成',//检测状态
|
|
||||||
// reCheck_Num: 1, //复检次数
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: '6', //装置序号ID
|
|
||||||
// name: '易司拓测试装置1', //设备名称
|
|
||||||
// dev_Type: 'E703A',//设备类型
|
|
||||||
// dev_Chns: 1, //设备通道数
|
|
||||||
// check_Result: '不合格', //检测结果
|
|
||||||
// report_State: '已生成', //报告状态
|
|
||||||
// document_State: '未归档', //归档状态
|
|
||||||
// check_State:'检测完成',//检测状态
|
|
||||||
// reCheck_Num: 1, //复检次数
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: '7', //装置序号ID
|
|
||||||
// name: '易司拓测试装置2', //设备名称
|
|
||||||
// dev_Type: 'E703A',//设备类型
|
|
||||||
// dev_Chns: 1, //设备通道数
|
|
||||||
// check_Result: '不合格', //检测结果
|
|
||||||
// report_State: '已生成', //报告状态
|
|
||||||
// document_State: '未归档', //归档状态
|
|
||||||
// check_State:'检测完成',//检测状态
|
|
||||||
// reCheck_Num: 1, //复检次数
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: '8', //装置序号ID
|
|
||||||
// name: '山大电力测试装置1', //设备名称
|
|
||||||
// dev_Type: 'SDL-3002C',//设备类型
|
|
||||||
// dev_Chns: 1, //设备通道数
|
|
||||||
// check_Result: '不合格', //检测结果
|
|
||||||
// report_State: '已生成', //报告状态
|
|
||||||
// document_State: '未归档', //归档状态
|
|
||||||
// check_State:'检测完成',//检测状态
|
|
||||||
// reCheck_Num: 1, //复检次数
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: '9', //装置序号ID
|
|
||||||
// name: '山大电力测试装置2', //设备名称
|
|
||||||
// dev_Type: 'SDL-3002C',//设备类型
|
|
||||||
// dev_Chns: 1, //设备通道数
|
|
||||||
// check_Result: '不合格', //检测结果
|
|
||||||
// report_State: '已生成', //报告状态
|
|
||||||
// document_State: '未归档', //归档状态
|
|
||||||
// check_State:'检测完成',//检测状态
|
|
||||||
// reCheck_Num: 2, //复检次数
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: '10', //装置序号ID
|
|
||||||
// name: '山大电力测试装置2', //设备名称
|
|
||||||
// dev_Type: 'SDL-3002C',//设备类型
|
|
||||||
// dev_Chns: 1, //设备通道数
|
|
||||||
// check_Result: '不合格', //检测结果
|
|
||||||
// report_State: '已生成', //报告状态
|
|
||||||
// document_State: '未归档', //归档状态
|
|
||||||
// check_State:'检测完成',//检测状态
|
|
||||||
// reCheck_Num: 2, //复检次数
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: '11', //装置序号ID
|
|
||||||
// name: '山大电力测试装置2', //设备名称
|
|
||||||
// dev_Type: 'SDL-3002C',//设备类型
|
|
||||||
// dev_Chns: 1, //设备通道数
|
|
||||||
// check_Result: '不合格', //检测结果
|
|
||||||
// report_State: '已生成', //报告状态
|
|
||||||
// document_State: '未归档', //归档状态
|
|
||||||
// check_State:'检测完成',//检测状态
|
|
||||||
// reCheck_Num: 2, //复检次数
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: '12', //装置序号ID
|
|
||||||
// name: '山大电力测试装置2', //设备名称
|
|
||||||
// dev_Type: 'SDL-3002C',//设备类型
|
|
||||||
// dev_Chns: 1, //设备通道数
|
|
||||||
// check_Result: '不合格', //检测结果
|
|
||||||
// report_State: '已生成', //报告状态
|
|
||||||
// document_State: '未归档', //归档状态
|
|
||||||
// check_State:'检测完成',//检测状态
|
|
||||||
// reCheck_Num: 2, //复检次数
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: '13', //装置序号ID
|
|
||||||
// name: '山大电力测试装置2', //设备名称
|
|
||||||
// dev_Type: 'SDL-3002C',//设备类型
|
|
||||||
// dev_Chns: 1, //设备通道数
|
|
||||||
// check_Result: '不合格', //检测结果
|
|
||||||
// report_State: '已生成', //报告状态
|
|
||||||
// document_State: '未归档', //归档状态
|
|
||||||
// check_State:'检测完成',//检测状态
|
|
||||||
// reCheck_Num: 2, //复检次数
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: '14', //装置序号ID
|
|
||||||
// name: '山大电力测试装置3', //设备名称
|
|
||||||
// dev_Type: 'SDL-3002C',//设备类型
|
|
||||||
// dev_Chns: 1, //设备通道数
|
|
||||||
// check_Result: '不合格', //检测结果
|
|
||||||
// report_State: '已生成', //报告状态
|
|
||||||
// document_State: '未归档', //归档状态
|
|
||||||
// check_State:'检测完成',//检测状态
|
|
||||||
// reCheck_Num: 2, //复检次数
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// id: '15', //装置序号ID
|
|
||||||
// name: '山大电力测试装置4', //设备名称
|
|
||||||
// dev_Type: 'SDL-3002C',//设备类型
|
|
||||||
// dev_Chns: 1, //设备通道数
|
|
||||||
// check_Result: '不合格', //检测结果
|
|
||||||
// report_State: '已生成', //报告状态
|
|
||||||
// document_State: '未归档', //归档状态
|
|
||||||
// check_State:'检测完成',//检测状态
|
|
||||||
// reCheck_Num: 2, //复检次数
|
|
||||||
// },
|
|
||||||
// ]
|
|
||||||
|
|
||||||
export default {data,plan_devicedata}
|
export default {data,plan_devicedata}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { Device } from '@/api/device/interface/device'
|
import { pa } from 'element-plus/es/locale/index.mjs';
|
||||||
|
import type {Device} from '@/api/device/interface/device'
|
||||||
import http from '@/api'
|
import http from '@/api'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -7,57 +8,67 @@ import http from '@/api'
|
|||||||
|
|
||||||
//获取被检设备
|
//获取被检设备
|
||||||
export const getPqDevList = (params: Device.ReqPqDevParams) => {
|
export const getPqDevList = (params: Device.ReqPqDevParams) => {
|
||||||
return http.post(`/pqDev/list`, params)
|
return http.post(`/pqDev/list`, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
//添加被检设备
|
//添加被检设备
|
||||||
export const addPqDev = (params: Device.ResPqDev) => {
|
export const addPqDev = (params: Device.ResPqDev) => {
|
||||||
return http.post(`/pqDev/add`, params)
|
return http.post(`/pqDev/add`, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
//编辑被检设备
|
//编辑被检设备
|
||||||
export const updatePqDev = (params: Device.ResPqDev) => {
|
export const updatePqDev = (params: Device.ResPqDev) => {
|
||||||
return http.post(`/pqDev/update`, params)
|
return http.post(`/pqDev/update`, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
//删除被检设备
|
//删除被检设备
|
||||||
export const deletePqDev = (params: string[]) => {
|
export const deletePqDev = (params: string[]) => {
|
||||||
return http.post(`/pqDev/delete`, params)
|
return http.post(`/pqDev/delete`, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
// export const downloadTemplate = () => {
|
//导出被检设备
|
||||||
// return http.download(`/pqDev/downloadTemplate`)
|
export const exportPqDev = (params: Device.ReqPqDevParams) => {
|
||||||
|
return http.download(`/pqDev/export`, params)
|
||||||
|
}
|
||||||
|
// 下载导入文件模板
|
||||||
|
export const downloadTemplate = (params: { pattern: string }) => {
|
||||||
|
return http.download(`/pqDev/downloadTemplate`,params)
|
||||||
|
}
|
||||||
|
|
||||||
|
//导入被检设备
|
||||||
|
export const importPqDev = (params: Device.ReqPqDevParams) => {
|
||||||
|
return http.uploadExcel(`/pqDev/import`, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
//导入比对式被检设备
|
||||||
|
export const importContrastPqDev = (params: Device.ReqPqDevParams) => {
|
||||||
|
return http.upload(`/pqDev/importContrast`, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// //导出灿能二楼设备
|
||||||
|
// export const exportCNDev = (params: Device.ReqPqDevParams) => {
|
||||||
|
// return http.download(`/pqDev/exportCNDev`, params)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 下载灿能二楼设备导入文件模板
|
||||||
|
// export const downloadCNDevTemplate = () => {
|
||||||
|
// return http.download(`/pqDev/downloadCNDevTemplate`)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// //导入灿能二楼设备
|
||||||
|
// export const importCNDev = (params: Device.ReqPqDevParams) => {
|
||||||
|
// return http.uploadExcel(`/pqDev/importCNDev`, params)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// //导入被检设备(比对)
|
export const getPqDevById = (params: Device.ReqPqDevParams) => {
|
||||||
// export const importPqDev=(params: Device.ReqPqDevParams)=>{
|
return http.get(`/pqDev/getById?id=${params.id}`)
|
||||||
// return http.upload(`/pqDev/import`, params)
|
|
||||||
// }
|
|
||||||
// //导入比对式被检设备
|
|
||||||
// export const importContrastPqDev=(params: Device.ReqPqDevParams)=>{
|
|
||||||
// return http.upload(`/pqDev/importContrast`, params)
|
|
||||||
// }
|
|
||||||
|
|
||||||
//导出灿能二楼设备
|
|
||||||
export const exportCNDev = (params: Device.ReqPqDevParams) => {
|
|
||||||
return http.download(`/pqDev/exportCNDev`, params)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 下载灿能二楼设备导入文件模板
|
|
||||||
export const downloadCNDevTemplate = () => {
|
|
||||||
return http.download(`/pqDev/downloadCNDevTemplate`)
|
|
||||||
}
|
|
||||||
|
|
||||||
//导入灿能二楼设备
|
|
||||||
export const importCNDev = (params: Device.ReqPqDevParams) => {
|
|
||||||
return http.uploadExcel(`/pqDev/importCNDev`, params)
|
|
||||||
}
|
|
||||||
//根据设备类型决定(电源、icd、模板、通道数、额定电压、额定电流);
|
//根据设备类型决定(电源、icd、模板、通道数、额定电压、额定电流);
|
||||||
export const getPqDev = () => {
|
export const getPqDev = () => {
|
||||||
return http.get(`/devType/listAll`)
|
return http.get(`/devType/listAll`)
|
||||||
}
|
}
|
||||||
|
|
||||||
//被检设备归档
|
export const getSelectOptions = (params:{ pattern: string }) => {
|
||||||
export const documentedPqDev = (ids: string[]) => {
|
return http.get(`/pqDev/getSelectOptions`, params)
|
||||||
return http.post(`/pqDev/documented`, ids)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,5 +30,7 @@ export const deletePqErrSys = (params: string[]) => {
|
|||||||
return http.post(`/pqErrSys/delete`, params)
|
return http.post(`/pqErrSys/delete`, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//复制误差体系
|
||||||
|
export const copyPqErrSys = (params: ErrorSystem.ErrorSystemList) => {
|
||||||
|
return http.get(`/pqErrSys/copy?id=${params.id}`)
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ export namespace DevType {
|
|||||||
devChns: number; //设备通道数
|
devChns: number; //设备通道数
|
||||||
reportName: string| null;//报告模版名称
|
reportName: string| null;//报告模版名称
|
||||||
state: number;
|
state: number;
|
||||||
|
waveCmd:string| null;//录波指令
|
||||||
createBy?: string| null; //创建用户
|
createBy?: string| null; //创建用户
|
||||||
createTime?: string| null; //创建时间
|
createTime?: string| null; //创建时间
|
||||||
updateBy?: string| null; //更新用户
|
updateBy?: string| null; //更新用户
|
||||||
|
|||||||
@@ -1,91 +1,119 @@
|
|||||||
import type { ReqPage,ResPage } from '@/api/interface'
|
import type { ReqPage, ResPage } from '@/api/interface'
|
||||||
|
import type { Monitor } from './monitor'
|
||||||
|
|
||||||
// 被检设备模块
|
// 被检设备模块
|
||||||
export namespace Device {
|
export namespace Device {
|
||||||
|
/**
|
||||||
/**
|
* 被检设备表格分页查询参数
|
||||||
* 被检设备表格分页查询参数
|
*/
|
||||||
*/
|
export interface ReqPqDevParams extends ReqPage {
|
||||||
export interface ReqPqDevParams extends ReqPage{
|
id: string // 装置序号id 必填
|
||||||
id: string; // 装置序号id 必填
|
name: string //设备名称
|
||||||
name: string; //设备名称
|
devType?: string // 设备名称
|
||||||
devType?: string; // 设备名称
|
createTime?: string //创建时间
|
||||||
createTime?:string; //创建时间
|
pattern: string
|
||||||
pattern:string;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 被检设备表格分页查询参数
|
|
||||||
*/
|
|
||||||
export interface ReqDevReportParams extends ReqPage{
|
|
||||||
planId?:string; // 计划id
|
|
||||||
devId?:string; // 装置id
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 被检设备新增、修改、根据id查询返回的对象
|
* 被检设备表格分页查询参数
|
||||||
*/
|
*/
|
||||||
|
export interface ReqDevReportParams extends ReqPage {
|
||||||
|
planId?: string // 计划id
|
||||||
|
devId?: string // 装置id
|
||||||
|
scriptId?: string // 脚本id
|
||||||
|
planCode?: string
|
||||||
|
devIdList?: string[] // 装置id列表
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 被检设备新增、修改、根据id查询返回的对象
|
||||||
|
*/
|
||||||
export interface ResPqDev {
|
export interface ResPqDev {
|
||||||
id: string; //装置序号ID
|
id: string //装置序号ID
|
||||||
name: string; //设备名称
|
name: string //设备名称
|
||||||
pattern: string; //设备模式 模拟 数字 比对
|
pattern: string //设备模式 模拟 数字 比对
|
||||||
devType: string;//设备类型
|
devType: string //设备类型
|
||||||
devChns: number; //设备通道数
|
manufacturer?: string | null //生产厂家
|
||||||
devVolt: number; //额定电压(V)
|
createDate: string //生产日期
|
||||||
devCurr: number; //额定电流(A)
|
createId: string //出厂编号
|
||||||
manufacturer?: string | null;//生产厂家
|
hardwareVersion: string //固件版本
|
||||||
createDate: string; //生产日期
|
softwareVersion: string //软件版本
|
||||||
createId: string; //出厂编号
|
protocol: string //通讯协议
|
||||||
hardwareVersion: string; //固件版本
|
ip: string //IP地址
|
||||||
softwareVersion: string; //软件版本
|
port: number //端口号
|
||||||
protocol:string; //通讯协议
|
encryptionFlag: number //装置是否为加密版本
|
||||||
ip: string; //IP地址
|
series?: string | null //装置识别码(3ds加密)
|
||||||
port: number; //端口号
|
devKey?: string | null //装置秘钥(3ds加密)
|
||||||
encryptionFlag: number; //装置是否为加密版本
|
sampleId?: string | null //样品编号
|
||||||
series?: string| null; //装置识别码(3ds加密)
|
arrivedDate?: string //送样日期
|
||||||
devKey?: string| null; //装置秘钥(3ds加密)
|
cityName?: string | null //所属地市名称
|
||||||
sampleID?: string| null; //样品编号
|
gdName?: string | null //所属供电公司名称
|
||||||
arrivedDate?: string; //送样日期
|
subName?: string | null //所属电站名称
|
||||||
cityName?: string| null; //所属地市名称
|
reportPath?: string | null //报告路径
|
||||||
gDName?: string| null; //所属供电公司名称
|
planId?: string //检测计划Id
|
||||||
subName?: string| null; //所属电站名称
|
factorFlag?: number //是否支持系数校准(0:不支持,1:支持)
|
||||||
checkState?: number| null; //检测状态
|
preinvestmentPlan: string | null //预投计划
|
||||||
checkResult?: number| null; //检测结果
|
delegate: string | null //委托方
|
||||||
reportState?: number| null; //报告状态
|
inspectChannel?: string[] | string //被检通道
|
||||||
reportPath?: string| null; //报告路径
|
inspectDate?: string | null //定检日期
|
||||||
qRCode?: string| null; //设备关键信息二维码
|
harmSysId?: string | null //谐波系统设备id
|
||||||
reCheckNum: number; //复检次数
|
importFlag?: number //是否为导入设备 0否 1是
|
||||||
planId?:string;//检测计划Id
|
state: number //状态
|
||||||
timeCheckResult?:number;//守时检测结果(0:不符合1:符合)
|
createBy?: string | null //创建用户
|
||||||
factorFlag?:number;//是否支持系数校准(0:不支持,1:支持)
|
createTime?: string | null //创建时间
|
||||||
factorCheckResult?: number;//系数校准结果(0:不合格,1:合格,2:/表示没有做系数校准)
|
updateBy?: string | null //更新用户
|
||||||
state: number; //状态
|
updateTime?: string | null //更新时间
|
||||||
createBy?: string| null; //创建用户
|
|
||||||
createTime?: string| null; //创建时间
|
devChns: number //设备通道数
|
||||||
updateBy?: string| null; //更新用户
|
devVolt: number //额定电压(V)
|
||||||
updateTime?: string| null; //更新时间
|
devCurr: number //额定电流(A)
|
||||||
icdId: string| null;
|
icdId: string | null
|
||||||
power: string| null;//工作电源
|
power: string | null //工作电源
|
||||||
preinvestmentPlan: string| null;
|
|
||||||
|
devId?: number
|
||||||
|
checkState?: number | null //检测状态(0:未检,1:检测中,2:检测完成 3:归档)
|
||||||
|
checkResult?: number | null //检测结果(0:不符合,1:符合,2:未检)
|
||||||
|
reportState?: number | null //报告状态(0:未生成,1:已生成,2:未检)
|
||||||
|
recheckNum: number //复检次数
|
||||||
|
timeCheckResult?: number //守时检测结果(0:不符合1:符合)
|
||||||
|
factorCheckResult?: number //系数校准结果(0:不合格,1:合格,2:未检)
|
||||||
|
realtimeResult?: number //实时数据结论(0:不符合,1:符合,2:未检)
|
||||||
|
statisticsResult?: number //统计数据结论(0:不符合,1:符合,2:未检)
|
||||||
|
recordedResult?: number //录波数据结论(0:不符合,1:符合,2:未检)
|
||||||
|
checkBy?: string | null //检测人
|
||||||
|
checkTime?: string | null //检测时间
|
||||||
|
preDetectTime?: number //预检测耗时
|
||||||
|
coefficientTime?: number //系数校准耗时
|
||||||
|
formalCheckTime?: number //正式检测耗时
|
||||||
|
|
||||||
|
boundPlanName?: string | null
|
||||||
|
assign?: number ////是否分配给检测人员 0否 1是
|
||||||
|
monitorList: Monitor.ResPqMon[]
|
||||||
|
checked: boolean // 是否已选择
|
||||||
|
disabled: boolean // 是否禁用
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SelectOption {
|
||||||
export interface ResDev{
|
label: string
|
||||||
id: string;
|
value: string | number
|
||||||
name: string ,
|
|
||||||
icd:string ,
|
|
||||||
power:string,
|
|
||||||
devVolt:number,
|
|
||||||
devCurr:number,
|
|
||||||
devChns:number,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ResDev {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
icd: string
|
||||||
|
power: string
|
||||||
|
devVolt: number
|
||||||
|
devCurr: number
|
||||||
|
devChns: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ResTH {
|
||||||
/**
|
temperature: number | null //温度
|
||||||
* 被检设备表格查询分页返回的对象;
|
humidity: number | null //湿度
|
||||||
*/
|
}
|
||||||
export interface ResPqDevPage extends ResPage<ResPqDev> {
|
/**
|
||||||
|
* 被检设备表格查询分页返回的对象;
|
||||||
}
|
*/
|
||||||
}
|
export interface ResPqDevPage extends ResPage<ResPqDev> {}
|
||||||
|
}
|
||||||
@@ -33,7 +33,9 @@ export namespace ErrorSystem {
|
|||||||
endFlag?:number;//是否包含结束值
|
endFlag?:number;//是否包含结束值
|
||||||
conditionType?:string;//判断条件值类型(包括值类型,绝对值、相对值)
|
conditionType?:string;//判断条件值类型(包括值类型,绝对值、相对值)
|
||||||
maxErrorValue:number;//误差最大值
|
maxErrorValue:number;//误差最大值
|
||||||
errorValueType:string;//误差值类型(包括值类型,绝对值、相对值1、相对值2)
|
errorValueType:any;//误差值类型(0标称值、1标准值、2值类型)
|
||||||
|
valueType:number;//值类型(1绝对值、2相对值)
|
||||||
|
errorUnit:string;//误差单位
|
||||||
}
|
}
|
||||||
|
|
||||||
//查看详细误差体系
|
//查看详细误差体系
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ export namespace ICD {
|
|||||||
createTime?: string| null; //创建时间
|
createTime?: string| null; //创建时间
|
||||||
updateBy?: string| null; //更新用户
|
updateBy?: string| null; //更新用户
|
||||||
updateTime?: string| null; //更新时间
|
updateTime?: string| null; //更新时间
|
||||||
|
angle: number; // 是否支持电压相角、电流相角指标
|
||||||
|
usePhaseIndex: number; // 角型接线时是否使用相别的指标来进行检测
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,33 +1,36 @@
|
|||||||
import type { ReqPage, ResPage } from '@/api/interface'
|
import type { ReqPage, ResPage } from '@/api/interface'
|
||||||
|
|
||||||
// 被检设备模块
|
// 监测点模块
|
||||||
export namespace Monitor {
|
export namespace Monitor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 电能质量指标字典数据表格分页查询参数
|
* 监测点表格分页查询参数
|
||||||
*/
|
*/
|
||||||
export interface ReqPqMonParams extends ReqPage {
|
export interface ReqPqMonParams extends ReqPage {
|
||||||
id: string; // 装置序号id 必填
|
id: string; // 装置序号id 必填
|
||||||
devType?: string; // 设备名称
|
name?: string; // 设备名称
|
||||||
createTime?: string; //创建时间
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 被检设备新增、修改、根据id查询返回的对象
|
* 监测点新增、修改、根据id查询返回的对象
|
||||||
*/
|
*/
|
||||||
export interface ResPqMon {
|
export interface ResPqMon {
|
||||||
id: string; //监测点ID
|
id: string; //监测点ID
|
||||||
code: string; //默认与谐波系统监测点ID相同
|
|
||||||
devId: string; //所属设备ID
|
devId: string; //所属设备ID
|
||||||
name: string; //所属母线
|
busbar: string;//所属母线
|
||||||
num: number; //监测点序号
|
name: string; //监测点名称
|
||||||
pt: number; //PT变比
|
num: number; //线路号,监测点序号
|
||||||
ct: number; //CT变比
|
pt: string; //PT变比
|
||||||
ptType: string; //接线方式,字典表
|
ct: string; //CT变比
|
||||||
|
connection: string; //接线方式,字典表
|
||||||
|
statInterval: number; //统计间隔
|
||||||
|
harmSysId: string; //默认与谐波系统监测点ID相同
|
||||||
|
checkFlag: number;//是否参与检测0否1是
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 被检设备表格查询分页返回的对象;
|
* 监测点表格查询分页返回的对象;
|
||||||
*/
|
*/
|
||||||
export interface ResPqMonPage extends ResPage<ResPqMon> {
|
export interface ResPqMonPage extends ResPage<ResPqMon> {
|
||||||
|
|
||||||
|
|||||||
39
frontend/src/api/device/interface/report.ts
Normal file
39
frontend/src/api/device/interface/report.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import type { ReqPage, ResPage } from '@/api/interface'
|
||||||
|
import type { UploadFile } from 'element-plus';
|
||||||
|
|
||||||
|
// 报告模版接口
|
||||||
|
export namespace PqReport {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 报告模版表格分页查询参数
|
||||||
|
*/
|
||||||
|
export interface ReqReportParams extends ReqPage {
|
||||||
|
id: string; // 装置序号id 必填
|
||||||
|
name?: string; // 设备名称
|
||||||
|
createTime?: string; //创建时间
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 报告模版新增、修改、根据id查询返回的对象
|
||||||
|
*/
|
||||||
|
export interface ResReport {
|
||||||
|
id: string; //报告模板id
|
||||||
|
name: string;//报告模板名称
|
||||||
|
version:string;//版本号
|
||||||
|
baseFile?:string;//基础模板文件路径
|
||||||
|
detailFile?:string;//检测项模版文件路径
|
||||||
|
description:string;//描述信息
|
||||||
|
state:number;//状态:8-删除 1-正常
|
||||||
|
createBy?: string| null; //创建用户
|
||||||
|
createTime?: string| null; //创建时间
|
||||||
|
updateBy?: string| null; //更新用户
|
||||||
|
updateTime?: string| null; //更新时间
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 报告模版表格查询分页返回的对象;
|
||||||
|
*/
|
||||||
|
export interface ResReportPage extends ResPage<ResReport> {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
47
frontend/src/api/device/interface/standardDevice.ts
Normal file
47
frontend/src/api/device/interface/standardDevice.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import type { ReqPage, ResPage } from '@/api/interface'
|
||||||
|
|
||||||
|
// 标准设备模块
|
||||||
|
export namespace StandardDevice {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标准设备表格分页查询参数
|
||||||
|
*/
|
||||||
|
export interface ReqPqStandardDeviceParams extends ReqPage {
|
||||||
|
id: string; // 装置序号id 必填
|
||||||
|
name: string; //设备名称
|
||||||
|
devType?: string; // 设备名称
|
||||||
|
createTime?: string; //创建时间
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标准设备新增、修改、根据id查询返回的对象
|
||||||
|
*/
|
||||||
|
export interface ResPqStandardDevice {
|
||||||
|
id: string; //装置序号ID
|
||||||
|
name: string; //设备名称
|
||||||
|
devType: string;//设备类型
|
||||||
|
manufacturer?: string | null;//生产厂家
|
||||||
|
protocol: string;//通讯协议
|
||||||
|
ip: string; //IP地址
|
||||||
|
port: number; //端口号
|
||||||
|
inspectChannel:string[] |string;//可检通道数
|
||||||
|
encryptionFlag: number; //装置是否为加密版本
|
||||||
|
series?: string | null; //装置识别码(3ds加密)
|
||||||
|
devKey?: string | null; //装置秘钥(3ds加密)
|
||||||
|
state: number; //状态
|
||||||
|
createBy?: string | null; //创建用户
|
||||||
|
createTime?: string | null; //创建时间
|
||||||
|
updateBy?: string | null; //更新用户
|
||||||
|
updateTime?: string | null; //更新时间
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标准设备表格查询分页返回的对象;
|
||||||
|
*/
|
||||||
|
export interface ResPqStandardDevicePage extends ResPage<ResPqStandardDevice> {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,39 +2,37 @@ import type { ReqPage, ResPage } from '@/api/interface'
|
|||||||
|
|
||||||
// 检测脚本模块
|
// 检测脚本模块
|
||||||
export namespace TestScript {
|
export namespace TestScript {
|
||||||
|
/**
|
||||||
|
* 检测脚本表格分页查询参数
|
||||||
/**
|
*/
|
||||||
* 检测脚本表格分页查询参数
|
export interface ReqTestScriptParams extends ReqPage {
|
||||||
*/
|
id: string // 装置序号id 必填
|
||||||
export interface ReqTestScriptParams extends ReqPage{
|
name: string
|
||||||
id: string; // 装置序号id 必填
|
type: string
|
||||||
name: string;
|
pattern: string
|
||||||
type: string;
|
}
|
||||||
pattern:string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检测脚本接口
|
// 检测脚本接口
|
||||||
export interface ResTestScript {
|
export interface ResTestScript {
|
||||||
id?: string; //检测脚本ID
|
id?: string //检测脚本ID
|
||||||
name: string; //检测脚本名称
|
name: string //检测脚本名称
|
||||||
type: string; //设定0为脚本,1为模板
|
type: string //设定0为脚本,1为模板
|
||||||
pattern: string;//检测脚本模式(字典表Code字段,数字、模拟、比对)
|
pattern: string //检测脚本模式(字典表Code字段,数字、模拟、比对)
|
||||||
valueType?: string;//脚本值类型(字典表Code字段,相对值脚本、绝对值脚本、无)
|
valueType?: string //脚本值类型(字典表Code字段,相对值脚本、绝对值脚本、无)
|
||||||
standardName: string;//参照标准名称
|
standardName: string //参照标准名称
|
||||||
standardTime: string;//标准推行时间
|
standardTime: string //标准推行时间
|
||||||
state?:number;//
|
state?: number //
|
||||||
createBy?: string;
|
createBy?: string
|
||||||
createTime?: string;
|
createTime?: string
|
||||||
updateBy?: string;
|
updateBy?: string
|
||||||
updateTime?: string;
|
updateTime?: string
|
||||||
selectedValue?: string;
|
selectedValue?: string
|
||||||
|
ratedCurr?: number
|
||||||
|
ratedVolt?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检测脚本查询分页返回的对象;
|
* 检测脚本查询分页返回的对象;
|
||||||
*/
|
*/
|
||||||
export interface ResTestScriptPage extends ResPage<ResTestScript> {
|
export interface ResTestScriptPage extends ResPage<ResTestScript> {}
|
||||||
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import type { Monitor } from '@/api/device/interface/monitor'
|
|
||||||
import http from '@/api'
|
import http from '@/api'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -6,23 +5,9 @@ import http from '@/api'
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
//获取监测点
|
//获取监测点
|
||||||
export const getPqMonList = (params: Monitor.ReqPqMonParams) => {
|
export const getPqMonList = (param:any) => {
|
||||||
//return http.post(`/pqMon/list`, params)
|
return http.post(`/pqMonitor/list`, param)
|
||||||
}
|
|
||||||
|
|
||||||
//添加监测点
|
|
||||||
export const addPqMon = (params: Monitor.ResPqMon) => {
|
|
||||||
//return http.post(`/pqMon/add`, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
//编辑监测点
|
|
||||||
export const updatePqMon = (params: Monitor.ResPqMon) => {
|
|
||||||
//return http.post(`/pqMon/update`, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
//删除监测点
|
|
||||||
export const deletePqMon = (params: string[]) => {
|
|
||||||
//return http.post(`/pqMon/delete`, params)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
51
frontend/src/api/device/report/index.ts
Normal file
51
frontend/src/api/device/report/index.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import type {PqReport} from '@/api/device/interface/report'
|
||||||
|
import http from '@/api'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name 报告模版模块
|
||||||
|
*/
|
||||||
|
|
||||||
|
//获取报告模版
|
||||||
|
export const getPqReportList = (params: PqReport.ReqReportParams) => {
|
||||||
|
return http.post(`/report/list`, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
//新增报告模版
|
||||||
|
export const addPqReport = (params: PqReport.ResReport) => {
|
||||||
|
return http.upload(`/report/add`, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
//删除报告模版
|
||||||
|
export const deletePqReport = (params: string[]) => {
|
||||||
|
return http.post(`/report/delete`, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
//查询报告模板详情
|
||||||
|
export const getPqReportById = (params: PqReport.ResReport) => {
|
||||||
|
return http.get(`/report/getById?id=${params.id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
//修改报告模板
|
||||||
|
export const updatePqReport = (params: PqReport.ResReport) => {
|
||||||
|
return http.upload(`/report/update`, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
//查询所有报告模板名称
|
||||||
|
export const getPqReportAllName = () => {
|
||||||
|
return http.get(`/report/listAllName`)
|
||||||
|
}
|
||||||
|
|
||||||
|
//根据名称查询指定报告模板的所有版本
|
||||||
|
export const getPqReportAllVersion = (params:any) => {
|
||||||
|
return http.get(`/report/listAllVersion?name=${params.name}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
//被检设备归档
|
||||||
|
export const documentedPqDev = (ids: string[]) => {
|
||||||
|
return http.post(`/report/documented`, ids)
|
||||||
|
}
|
||||||
|
|
||||||
|
//上传报告到云端
|
||||||
|
export const uploadReportToCloud = (deviceIds: string[]) => {
|
||||||
|
return http.post(`/report/uploadReportToCloud`, deviceIds)
|
||||||
|
}
|
||||||
55
frontend/src/api/device/standardDevice/index.ts
Normal file
55
frontend/src/api/device/standardDevice/index.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import type { StandardDevice } from '@/api/device/interface/standardDevice'
|
||||||
|
import http from '@/api'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name 标准设备管理模块
|
||||||
|
*/
|
||||||
|
|
||||||
|
//获取标准设备
|
||||||
|
export const getPqStandardDevList = (params: StandardDevice.ReqPqStandardDeviceParams) => {
|
||||||
|
return http.post(`/pqStandardDev/list`, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
//根据id查询标准设备
|
||||||
|
export const getPqStandardDevById = (params: StandardDevice.ReqPqStandardDeviceParams) => {
|
||||||
|
return http.get(`/pqStandardDev/getById?id=${params.id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
//添加标准设备
|
||||||
|
export const addPqStandardDev = (params: StandardDevice.ResPqStandardDevice) => {
|
||||||
|
return http.post(`/pqStandardDev/add`, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
//编辑标准设备
|
||||||
|
export const updatePqStandardDev = (params: StandardDevice.ResPqStandardDevice) => {
|
||||||
|
return http.post(`/pqStandardDev/update`, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
//删除标准设备
|
||||||
|
export const deletePqStandardDev = (params: string[]) => {
|
||||||
|
return http.post(`/pqStandardDev/delete`, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
//导出标准设备
|
||||||
|
export const exportPqStandardDev = (params: StandardDevice.ReqPqStandardDeviceParams) => {
|
||||||
|
return http.download(`/pqStandardDev/export`, params)
|
||||||
|
}
|
||||||
|
// 下载导入文件模板
|
||||||
|
export const downloadTemplate = () => {
|
||||||
|
return http.download(`/pqStandardDev/downloadTemplate`)
|
||||||
|
}
|
||||||
|
|
||||||
|
//导入标准设备
|
||||||
|
export const importPqStandardDev = (params: StandardDevice.ReqPqStandardDeviceParams) => {
|
||||||
|
return http.uploadExcel(`/pqStandardDev/import`, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取所有标准设备
|
||||||
|
export const getAllPqStandardDev = () => {
|
||||||
|
return http.get(`/pqStandardDev/getAll`)
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取可以绑定的标准设备
|
||||||
|
export const canBindingList = () => {
|
||||||
|
return http.get(`/pqStandardDev/canBindingList`)
|
||||||
|
}
|
||||||
@@ -1,173 +1,242 @@
|
|||||||
import { ElMessage, ElTreeSelect } from 'element-plus';
|
import { ElMessage } from 'element-plus'
|
||||||
import axios, { AxiosInstance, AxiosError, AxiosRequestConfig, InternalAxiosRequestConfig, AxiosResponse } from 'axios'
|
import axios, {
|
||||||
|
AxiosError,
|
||||||
|
type AxiosInstance,
|
||||||
|
type AxiosRequestConfig,
|
||||||
|
type AxiosResponse,
|
||||||
|
type InternalAxiosRequestConfig
|
||||||
|
} from 'axios'
|
||||||
import { showFullScreenLoading, tryHideFullScreenLoading } from '@/components/Loading/fullScreen'
|
import { showFullScreenLoading, tryHideFullScreenLoading } from '@/components/Loading/fullScreen'
|
||||||
import { LOGIN_URL } from '@/config'
|
import { LOGIN_URL } from '@/config'
|
||||||
import { ElMessage } from 'element-plus'
|
import { type ResultData } from '@/api/interface'
|
||||||
import { ResultData } from '@/api/interface'
|
|
||||||
import { ResultEnum } from '@/enums/httpEnum'
|
import { ResultEnum } from '@/enums/httpEnum'
|
||||||
import { checkStatus } from './helper/checkStatus'
|
import { checkStatus } from './helper/checkStatus'
|
||||||
import { useUserStore } from '@/stores/modules/user'
|
import { useUserStore } from '@/stores/modules/user'
|
||||||
import router from '@/routers'
|
import router from '@/routers'
|
||||||
import {refreshToken} from '@/api/user/login'
|
import { refreshToken } from '@/api/user/login'
|
||||||
|
import { EventSourcePolyfill } from 'event-source-polyfill'
|
||||||
|
|
||||||
export interface CustomAxiosRequestConfig extends InternalAxiosRequestConfig {
|
export interface CustomAxiosRequestConfig extends InternalAxiosRequestConfig {
|
||||||
loading?: boolean;
|
loading?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
// 默认地址请求地址,可在 .env.** 文件中修改
|
// 默认地址请求地址,可在 .env.** 文件中修改
|
||||||
baseURL: import.meta.env.VITE_API_URL as string,
|
baseURL: import.meta.env.VITE_API_URL as string,
|
||||||
// 设置超时时间
|
// 设置超时时间
|
||||||
timeout: ResultEnum.TIMEOUT as number,
|
timeout: ResultEnum.TIMEOUT as number,
|
||||||
// 跨域时候允许携带凭证
|
// 跨域时候允许携带凭证
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
// post请求指定数据类型以及编码
|
// post请求指定数据类型以及编码
|
||||||
headers: { 'Content-Type': 'application/json;charset=utf-8' },
|
headers: { 'Content-Type': 'application/json;charset=utf-8' }
|
||||||
}
|
}
|
||||||
|
|
||||||
class RequestHttp {
|
class RequestHttp {
|
||||||
service: AxiosInstance
|
service: AxiosInstance
|
||||||
|
|
||||||
public constructor(config: AxiosRequestConfig) {
|
public constructor(config: AxiosRequestConfig) {
|
||||||
// 创建实例
|
// 创建实例
|
||||||
this.service = axios.create(config)
|
this.service = axios.create(config)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 请求拦截器
|
* @description 请求拦截器
|
||||||
* 客户端发送请求 -> [请求拦截器] -> 服务器
|
* 客户端发送请求 -> [请求拦截器] -> 服务器
|
||||||
* token校验(JWT) : 接受服务器返回的 token,存储到 vuex/pinia/本地储存当中
|
* token校验(JWT) : 接受服务器返回的 token,存储到 vuex/pinia/本地储存当中
|
||||||
*/
|
*/
|
||||||
this.service.interceptors.request.use(
|
this.service.interceptors.request.use(
|
||||||
(config: CustomAxiosRequestConfig) => {
|
(config: CustomAxiosRequestConfig) => {
|
||||||
isFirst = true
|
isFirst = true
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
// 当前请求不需要显示 loading,在 api 服务中通过指定的第三个参数: { loading: false } 来控制
|
// 当前请求不需要显示 loading,在 api 服务中通过指定的第三个参数: { loading: false } 来控制
|
||||||
config.loading ?? (config.loading = true)
|
config.loading ?? (config.loading = true)
|
||||||
config.loading && showFullScreenLoading()
|
config.loading && showFullScreenLoading()
|
||||||
if (config.headers && typeof config.headers.set === 'function') {
|
if (config.headers && typeof config.headers.set === 'function') {
|
||||||
config.headers.set('Authorization', 'Bearer ' + userStore.accessToken)
|
config.headers.set('Authorization', 'Bearer ' + userStore.accessToken)
|
||||||
config.headers.set('Is-Refresh-Token', userStore.isRefreshToken+"")
|
config.headers.set('Is-Refresh-Token', userStore.isRefreshToken + '')
|
||||||
}
|
}
|
||||||
return config
|
return config
|
||||||
},
|
},
|
||||||
(error: AxiosError) => {
|
(error: AxiosError) => {
|
||||||
return Promise.reject(error)
|
return Promise.reject(error)
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
let isFirst = true
|
|
||||||
/**
|
|
||||||
* @description 响应拦截器
|
|
||||||
* 服务器换返回信息 -> [拦截统一处理] -> 客户端JS获取到信息
|
|
||||||
*/
|
|
||||||
this.service.interceptors.response.use(
|
|
||||||
async (response: AxiosResponse) => {
|
|
||||||
const { data } = response
|
|
||||||
const userStore = useUserStore()
|
|
||||||
tryHideFullScreenLoading()
|
|
||||||
|
|
||||||
if(data.code === ResultEnum.ACCESSTOKEN_EXPIRED){
|
|
||||||
// 用长token去换短token
|
|
||||||
userStore.setAccessToken(userStore.refreshToken)
|
|
||||||
userStore.setIsRefreshToken(true)
|
|
||||||
const result = await refreshToken()
|
|
||||||
if (result) { //获取新token成功的话
|
|
||||||
// 有新的token后,重新请求
|
|
||||||
userStore.setAccessToken(result.data.accessToken)
|
|
||||||
userStore.setRefreshToken(result.data.refreshToken)
|
|
||||||
userStore.setIsRefreshToken(false)
|
|
||||||
response.config.headers.Authorization = `Bearer ${result.data.accessToken}`//重新请求前需要将更新后的新token更换掉之前无效的token,不然会死循环
|
|
||||||
const resp = await this.service.request(response.config)
|
|
||||||
return resp
|
|
||||||
} else {
|
|
||||||
// 刷新失效,跳转登录页
|
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
// 登陆失效
|
|
||||||
if (data.code == ResultEnum.OVERDUE) {
|
let isFirst = true
|
||||||
console.log("登陆失效")
|
/**
|
||||||
userStore.setAccessToken('')
|
* @description 响应拦截器
|
||||||
userStore.setRefreshToken('')
|
* 服务器换返回信息 -> [拦截统一处理] -> 客户端JS获取到信息
|
||||||
userStore.setIsRefreshToken(false)
|
*/
|
||||||
userStore.setUserInfo({ name: '' })
|
this.service.interceptors.response.use(
|
||||||
await router.replace(LOGIN_URL)
|
async (response: AxiosResponse) => {
|
||||||
if(isFirst){//临时处理token失效弹窗多次
|
const { data } = response
|
||||||
ElMessage.error(data.message)
|
const userStore = useUserStore()
|
||||||
isFirst = false
|
tryHideFullScreenLoading()
|
||||||
}
|
|
||||||
return Promise.reject(data)
|
if (data.code === ResultEnum.ACCESSTOKEN_EXPIRED) {
|
||||||
|
// 用长token去换短token
|
||||||
|
userStore.setAccessToken(userStore.refreshToken)
|
||||||
|
userStore.setIsRefreshToken(true)
|
||||||
|
const result = await refreshToken()
|
||||||
|
if (result) {
|
||||||
|
//获取新token成功的话
|
||||||
|
// 有新的token后,重新请求
|
||||||
|
userStore.setAccessToken(result.data.accessToken)
|
||||||
|
userStore.setRefreshToken(result.data.refreshToken)
|
||||||
|
userStore.setIsRefreshToken(false)
|
||||||
|
userStore.setExp(1000 * 60 * 60 * 24 * 30)
|
||||||
|
response.config.headers.Authorization = `Bearer ${result.data.accessToken}` //重新请求前需要将更新后的新token更换掉之前无效的token,不然会死循环
|
||||||
|
const resp = await this.service.request(response.config)
|
||||||
|
return resp
|
||||||
|
} else {
|
||||||
|
// 刷新失效,跳转登录页
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 登陆失效
|
||||||
|
if (data.code === ResultEnum.OVERDUE) {
|
||||||
|
console.log('登陆失效')
|
||||||
|
userStore.setAccessToken('')
|
||||||
|
userStore.setRefreshToken('')
|
||||||
|
userStore.setIsRefreshToken(false)
|
||||||
|
userStore.setUserInfo({ id: '', name: '' })
|
||||||
|
userStore.setExp(0)
|
||||||
|
await router.replace(LOGIN_URL)
|
||||||
|
if (isFirst) {
|
||||||
|
//临时处理token失效弹窗多次
|
||||||
|
ElMessage.error(data.message)
|
||||||
|
isFirst = false
|
||||||
|
}
|
||||||
|
return Promise.reject(data)
|
||||||
|
}
|
||||||
|
// 全局错误信息拦截(防止下载文件的时候返回数据流,没有 code 直接报错)
|
||||||
|
if (data.code && data.code !== ResultEnum.SUCCESS) {
|
||||||
|
if (data.message.includes('&')) {
|
||||||
|
let formattedMessage = data.message.split('&').join('<br>')
|
||||||
|
if (data.message.includes(':')) {
|
||||||
|
formattedMessage = formattedMessage.replace(':', '')
|
||||||
|
}
|
||||||
|
ElMessage.error({ message: formattedMessage, dangerouslyUseHTMLString: true })
|
||||||
|
return Promise.reject(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
ElMessage.error(data.message)
|
||||||
|
return Promise.reject(data)
|
||||||
|
}
|
||||||
|
// 成功请求(在页面上除非特殊情况,否则不用处理失败逻辑)
|
||||||
|
|
||||||
|
if (userStore.exp <= Date.now() && userStore.exp !== 0) {
|
||||||
|
userStore.setAccessToken('')
|
||||||
|
userStore.setRefreshToken('')
|
||||||
|
userStore.setIsRefreshToken(false)
|
||||||
|
userStore.setUserInfo({ id: '', name: '' })
|
||||||
|
userStore.setExp(0)
|
||||||
|
ElMessage.error('登录已过期,请重新登录!')
|
||||||
|
await router.replace(LOGIN_URL)
|
||||||
|
return Promise.reject(data)
|
||||||
|
}
|
||||||
|
// 对于blob类型的响应,返回完整的response对象以保留响应头
|
||||||
|
if (response.config.responseType === 'blob') {
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
async (error: AxiosError) => {
|
||||||
|
const { response } = error
|
||||||
|
tryHideFullScreenLoading()
|
||||||
|
console.log('error', error.message)
|
||||||
|
// 请求超时 && 网络错误单独判断,没有 response
|
||||||
|
if (error.message.indexOf('timeout') !== -1) ElMessage.error('请求超时!请您稍后重试')
|
||||||
|
if (error.message.indexOf('Network Error') !== -1) ElMessage.error('网络错误!请您稍后重试')
|
||||||
|
// 根据服务器响应的错误状态码,做不同的处理
|
||||||
|
if (response) checkStatus(response.status)
|
||||||
|
// 服务器结果都没有返回(可能服务器错误可能客户端断网),断网处理:可以跳转到断网页面
|
||||||
|
if (!window.navigator.onLine) router.replace('/500')
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 常用请求方法封装
|
||||||
|
*/
|
||||||
|
get<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
|
||||||
|
return this.service.get(url, { params, ..._object })
|
||||||
|
}
|
||||||
|
|
||||||
|
post<T>(url: string, params?: object | string, _object = {}): Promise<ResultData<T>> {
|
||||||
|
return this.service.post(url, params, _object)
|
||||||
|
}
|
||||||
|
|
||||||
|
put<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
|
||||||
|
return this.service.put(url, params, _object)
|
||||||
|
}
|
||||||
|
|
||||||
|
delete<T>(url: string, params?: any, _object = {}): Promise<ResultData<T>> {
|
||||||
|
return this.service.delete(url, { params, ..._object })
|
||||||
|
}
|
||||||
|
|
||||||
|
download(url: string, params?: object, _object = {}): Promise<BlobPart> {
|
||||||
|
return this.service.post(url, params, { ..._object, responseType: 'blob' }).then(res => res.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadWithHeaders(url: string, params?: object, _object = {}): Promise<AxiosResponse<Blob>> {
|
||||||
|
return this.service.post(url, params, { ..._object, responseType: 'blob' })
|
||||||
|
}
|
||||||
|
|
||||||
|
upload(url: string, params?: object, _object = {}): Promise<BlobPart> {
|
||||||
|
return this.service.post(url, params, {
|
||||||
|
..._object,
|
||||||
|
headers: { 'Content-Type': 'multipart/form-data' }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 针对excel的上传,默认返回的是blob类型,Excel没问题时返回json特殊处理
|
||||||
|
*/
|
||||||
|
uploadExcel(url: string, params?: object, _object = {}): Promise<BlobPart> {
|
||||||
|
return this.service
|
||||||
|
.post(url, params, {
|
||||||
|
..._object,
|
||||||
|
headers: { 'Content-Type': 'multipart/form-data' },
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
.then(res => res.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加SSE连接方法
|
||||||
|
sse(url: string, params?: any): EventSource {
|
||||||
|
const userStore = useUserStore()
|
||||||
|
// 构造带参数的URL
|
||||||
|
let requestUrl = config.baseURL + url
|
||||||
|
if (params) {
|
||||||
|
const searchParams = new URLSearchParams()
|
||||||
|
for (const key in params) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(params, key)) {
|
||||||
|
searchParams.append(key, String(params[key]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
requestUrl += '?' + searchParams.toString()
|
||||||
}
|
}
|
||||||
// 全局错误信息拦截(防止下载文件的时候返回数据流,没有 code 直接报错)
|
|
||||||
if (data.code && data.code !== ResultEnum.SUCCESS) {
|
|
||||||
if(data.message.includes('&')){
|
|
||||||
const formattedMessage = data.message.split('&').join('<br>');
|
|
||||||
ElMessage.error({ message: formattedMessage, dangerouslyUseHTMLString: true });
|
|
||||||
return Promise.reject(data)
|
|
||||||
}
|
|
||||||
ElMessage.error(data.message)
|
|
||||||
return Promise.reject(data)
|
|
||||||
}
|
|
||||||
// 成功请求(在页面上除非特殊情况,否则不用处理失败逻辑)
|
|
||||||
return data
|
|
||||||
},
|
|
||||||
async (error: AxiosError) => {
|
|
||||||
const { response } = error
|
|
||||||
tryHideFullScreenLoading()
|
|
||||||
console.log('error', error.message)
|
|
||||||
// 请求超时 && 网络错误单独判断,没有 response
|
|
||||||
if (error.message.indexOf('timeout') !== -1) ElMessage.error('请求超时!请您稍后重试')
|
|
||||||
if (error.message.indexOf('Network Error') !== -1) ElMessage.error('网络错误!请您稍后重试')
|
|
||||||
// 根据服务器响应的错误状态码,做不同的处理
|
|
||||||
if (response) checkStatus(response.status)
|
|
||||||
// 服务器结果都没有返回(可能服务器错误可能客户端断网),断网处理:可以跳转到断网页面
|
|
||||||
if (!window.navigator.onLine) router.replace('/500')
|
|
||||||
return Promise.reject(error)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// 创建EventSource连接
|
||||||
* @description 常用请求方法封装
|
const eventSource = new EventSourcePolyfill(requestUrl, {
|
||||||
*/
|
headers: {
|
||||||
get<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
|
Authorization: 'Bearer ' + userStore.accessToken
|
||||||
return this.service.get(url, { params, ..._object })
|
},
|
||||||
}
|
// 增加超时时间到1200秒
|
||||||
|
heartbeatTimeout: 1200000
|
||||||
post<T>(url: string, params?: object | string, _object = {}): Promise<ResultData<T>> {
|
})
|
||||||
return this.service.post(url, params, _object)
|
|
||||||
}
|
|
||||||
|
|
||||||
put<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
|
|
||||||
return this.service.put(url, params, _object)
|
|
||||||
}
|
|
||||||
|
|
||||||
delete<T>(url: string, params?: any, _object = {}): Promise<ResultData<T>> {
|
|
||||||
return this.service.delete(url, { params, ..._object })
|
|
||||||
}
|
|
||||||
|
|
||||||
download(url: string, params?: object, _object = {}): Promise<BlobPart> {
|
|
||||||
return this.service.post(url, params, { ..._object, responseType: 'blob' })
|
|
||||||
}
|
|
||||||
|
|
||||||
upload(url: string, params?: object, _object = {}): Promise<BlobPart> {
|
|
||||||
return this.service.post(url, params, {
|
|
||||||
..._object,
|
|
||||||
headers: { 'Content-Type': 'multipart/form-data' }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 针对excel的上传,默认返回的是blob类型,Excel没问题时返回json特殊处理
|
|
||||||
*/
|
|
||||||
uploadExcel(url: string, params?: object, _object = {}): Promise<BlobPart> {
|
|
||||||
return this.service.post(url, params, {
|
|
||||||
..._object,
|
|
||||||
headers: { 'Content-Type': 'multipart/form-data' },
|
|
||||||
responseType: 'blob',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 设置默认的Authorization头部
|
||||||
|
eventSource.addEventListener('open', function () {
|
||||||
|
console.log('SSE连接已建立')
|
||||||
|
})
|
||||||
|
// 添加错误处理
|
||||||
|
eventSource.addEventListener('error', function (err) {
|
||||||
|
console.error('SSE连接错误:', err)
|
||||||
|
})
|
||||||
|
|
||||||
|
return eventSource
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new RequestHttp(config)
|
export default new RequestHttp(config)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export interface Result {
|
|||||||
* 请求响应参数(包含data)
|
* 请求响应参数(包含data)
|
||||||
*/
|
*/
|
||||||
export interface ResultData<T = any> extends Result {
|
export interface ResultData<T = any> extends Result {
|
||||||
|
map(arg0: (item: any) => { label: any; value: any; }): { label: string; value: string; }[] | { label: string; value: string; }[];
|
||||||
data: T;
|
data: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,23 @@ export namespace Plan {
|
|||||||
createTime?:string; //创建时间
|
createTime?:string; //创建时间
|
||||||
updateBy?:string; //更新用户
|
updateBy?:string; //更新用户
|
||||||
updateTime?:string; //更新时间
|
updateTime?:string; //更新时间
|
||||||
|
|
||||||
|
associateReport:number;//是否关联报告模板 0否 1是
|
||||||
|
reportTemplateName:string;
|
||||||
|
reportTemplateVersion:string;
|
||||||
|
dataRule:string;//数据处理原则
|
||||||
|
|
||||||
|
standardDevIds:string[];
|
||||||
|
standardDevMap:Map<string,number>;//标准设备
|
||||||
|
testItems:string[];//测试项
|
||||||
|
Check_By?:string;//计划检测人
|
||||||
|
progress?: number; // 进度百分比,例如 75
|
||||||
|
children?: ResPlan[];
|
||||||
|
testConfig?: PlanTestConfig;
|
||||||
|
importFlag?: number; // 导入标识,0-否,1-是
|
||||||
|
leader?: string; // 负责人
|
||||||
|
memberIds?: string | string[]; //成员
|
||||||
|
members?: string; //成员字符串
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检测计划 + 分页
|
// 检测计划 + 分页
|
||||||
@@ -30,15 +47,27 @@ export namespace Plan {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export interface ReqPlan extends ResPlan {
|
export interface ReqPlan extends ResPlan {
|
||||||
datasourceIds:string;
|
datasourceIds:string | string[];
|
||||||
sourceIds: string;
|
sourceIds: string | null;
|
||||||
planId:string;
|
planId:string;
|
||||||
scriptName: string ;
|
scriptName: string ;
|
||||||
errorSysName: string;
|
errorSysName: string;
|
||||||
sourceName: string ;
|
sourceName: string ;
|
||||||
|
standardDevNameStr: string;
|
||||||
|
testItemNameStr:string;
|
||||||
devIds: string[];
|
devIds: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PlanTestConfig {
|
||||||
|
planId: string;
|
||||||
|
waveRecord: number;
|
||||||
|
realTime: number;
|
||||||
|
statistics: number;
|
||||||
|
flicker: number;
|
||||||
|
maxTime: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2,54 +2,53 @@ import type { Plan } from './interface'
|
|||||||
import http from '@/api'
|
import http from '@/api'
|
||||||
import type { ErrorSystem } from '../device/interface/error'
|
import type { ErrorSystem } from '../device/interface/error'
|
||||||
import type { Device } from '../device/interface/device'
|
import type { Device } from '../device/interface/device'
|
||||||
import { ReqDevReportParams } from '@/api/device/interface/device'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name 检测计划管理模块
|
* @name 检测计划管理模块
|
||||||
*/
|
*/
|
||||||
// 获取检测计划列表
|
// 获取检测计划列表
|
||||||
export const getPlanList = (params: Plan.ReqPlanParams) => {
|
export const getPlanList = (params: Plan.ReqPlanParams) => {
|
||||||
return http.post(`/adPlan/list`, params)
|
return http.post(`/adPlan/list`, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增检测计划
|
// 新增检测计划
|
||||||
export const addPlan = (params: any) => {
|
export const addPlan = (params: any) => {
|
||||||
return http.post(`/adPlan/add`, params)
|
return http.post(`/adPlan/add`, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 编辑检测计划
|
// 编辑检测计划
|
||||||
export const updatePlan = (params: any) => {
|
export const updatePlan = (params: any) => {
|
||||||
return http.post(`/adPlan/update`, params)
|
return http.post(`/adPlan/update`, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除检测计划
|
// 删除检测计划
|
||||||
export const deletePlan = (params: { id: string[] }) => {
|
export const deletePlan = (params: { id: string[]; pattern: string }) => {
|
||||||
return http.post(`/adPlan/delete`, params)
|
return http.post(`/adPlan/delete?pattern=${params.pattern}`, params.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取指定模式下所有检测源
|
// 获取指定模式下所有检测源
|
||||||
export const getTestSourceList = (params: Plan.ReqPlan) => {
|
export const getTestSourceList = (params: Plan.ReqPlan) => {
|
||||||
return http.get(`/pqSource/getAll?patternId=${params.pattern}`)
|
return http.get(`/pqSource/getAll?patternId=${params.pattern}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取指定模式下所有检测脚本
|
// 获取指定模式下所有检测脚本
|
||||||
export const getPqScriptList = (params: Plan.ReqPlan) => {
|
export const getPqScriptList = (params: Plan.ReqPlan) => {
|
||||||
return http.get(`/pqScript/getAll?patternId=${params.pattern}`)
|
return http.get(`/pqScript/getAll?patternId=${params.pattern}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
//获取所有误差体系
|
//获取所有误差体系
|
||||||
export const getPqErrSysList = () => {
|
export const getPqErrSysList = () => {
|
||||||
return http.get<ErrorSystem.ErrorSystemList>(`/pqErrSys/getAll`)
|
return http.get<ErrorSystem.ErrorSystemList>(`/pqErrSys/getAll`)
|
||||||
}
|
}
|
||||||
|
|
||||||
//获取指定模式下所有未绑定的设备
|
//获取指定模式下所有未绑定的设备
|
||||||
export const getUnboundPqDevList = (params: Plan.ReqPlan) => {
|
export const getUnboundPqDevList = (params: { pattern: string}) => {
|
||||||
return http.get(`/pqDev/listUnbound?pattern=${params.pattern}`)
|
return http.get(`/pqDev/listUnbound?pattern=${params.pattern}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
//根据检测计划id查询出所有已绑定的设备
|
//根据检测计划id查询出所有已绑定的设备
|
||||||
export const getBoundPqDevList = (params: any) => {
|
export const getBoundPqDevList = (params: any) => {
|
||||||
return http.post(`/pqDev/listByPlanId`, params)
|
return http.post(`/adPlan/listByPlanId`, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
//检测计划绑定设备
|
//检测计划绑定设备
|
||||||
@@ -59,34 +58,105 @@ export const getBoundPqDevList = (params: any) => {
|
|||||||
|
|
||||||
// 按照模式查询检测计划(用于首页展示)
|
// 按照模式查询检测计划(用于首页展示)
|
||||||
export const getPlanListByPattern = (params: Plan.ReqPlan) => {
|
export const getPlanListByPattern = (params: Plan.ReqPlan) => {
|
||||||
return http.get(`/adPlan/listByPattern?pattern=${params.pattern}`)
|
return http.get(`/adPlan/listByPattern?pattern=${params.pattern}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导出检测计划
|
// 导出检测计划
|
||||||
export const exportCNPlan = (params: Device.ReqPqDevParams) => {
|
export const exportPlan = (params: Device.ReqPqDevParams) => {
|
||||||
return http.download(`/adPlan/exportCNPlan`, params)
|
return http.download(`/adPlan/export`, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 下载模板
|
// 下载模板
|
||||||
export const downloadCNPlanTemplate = () => {
|
export const downloadTemplate = (params: { patternId: string }) => {
|
||||||
return http.download(`/adPlan/downloadCNPlanTemplate`)
|
return http.download(`/adPlan/downloadTemplate`, params)
|
||||||
}
|
}
|
||||||
// 导入检测计划
|
// 导入检测计划
|
||||||
export const importCNPlan = (params: Device.ReqPqDevParams) => {
|
export const importPlan = (params: Device.ReqPqDevParams) => {
|
||||||
return http.uploadExcel(`/adPlan/importCNPlan`, params)
|
return http.uploadExcel(`/adPlan/import`, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 装置检测报告生成
|
// 装置检测报告生成
|
||||||
export const generateDevReport = (params: Device.ReqDevReportParams) => {
|
export const generateDevReport = (params: Device.ReqDevReportParams) => {
|
||||||
return http.post(`/report/generateReport`, params)
|
return http.post(`/report/generateReport`, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 装置检测报告下载
|
// 装置检测报告下载
|
||||||
export const downloadDevData = (params: Device.ReqDevReportParams) => {
|
export const downloadDevData = (params: Device.ReqDevReportParams) => {
|
||||||
return http.download(`/report/downloadReport`, params)
|
return http.download(`/report/downloadReport`, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 装置检测报告下载(带响应头)
|
||||||
|
export const downloadDevDataWithHeaders = (params: Device.ReqDevReportParams) => {
|
||||||
|
return http.downloadWithHeaders(`/report/downloadReport`, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const staticsAnalyse = (params: { id: string[] }) => {
|
export const staticsAnalyse = (params: { id: string[] }) => {
|
||||||
return http.download('/adPlan/analyse', params)
|
return http.download('/adPlan/analyse', params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//根据计划id分页查询被检设
|
||||||
|
export const getDevListByPlanId = (params: any) => {
|
||||||
|
return http.post(`/adPlan/listDevByPlanId`, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
//修改子计划名称
|
||||||
|
export const updateSubPlanName = (params: Plan.ReqPlan) => {
|
||||||
|
return http.get(`/adPlan/updateSubPlanName?planId=${params.id}&name=${params.name}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
//子计划绑定/解绑标准设备
|
||||||
|
export const subPlanBindStandardDevList = (params: Plan.ReqPlan) => {
|
||||||
|
return http.post(`/adPlan/updateBindStandardDev`, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
//子计划绑定/解绑被检设备
|
||||||
|
export const subPlanBindDev = (params: Plan.ReqPlan) => {
|
||||||
|
return http.post(`/adPlan/updateBindDev`, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
//根据父计划ID获取未被子计划绑定的标准设备
|
||||||
|
export const getUnboundStandardDevList = (params: Plan.ResPlan) => {
|
||||||
|
return http.get(`/adPlan/getUnBoundStandardDev?fatherPlanId=${params.fatherPlanId}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
//根据计划ID获取已绑定的标准设备
|
||||||
|
export const getBoundStandardDevList = (params: Plan.ResPlan) => {
|
||||||
|
return http.get(`/adPlan/getBoundStandardDev?planId=${params.id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
//根据计划ID获取已绑定的所有标准设备
|
||||||
|
export const getBoundStandardDevAllList = (params: { id: string }) => {
|
||||||
|
return http.get(`/adPlan/getBoundStandardDev?planId=${params.id}&all=1`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出子计划
|
||||||
|
export const exportSubPlan = (params: Plan.ResPlan) => {
|
||||||
|
return http.download(`/adPlan/exportSubPlan?planId=${params.id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入子检测计划
|
||||||
|
export const importSubPlan = (params: Plan.ResPlan) => {
|
||||||
|
return http.upload(`/adPlan/importSubPlan`, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出计划检测结果数据
|
||||||
|
export const exportPlanCheckData = (params: any) => {
|
||||||
|
return http.post(
|
||||||
|
`/adPlan/exportPlanCheckData?planId=${params.id}&devIds=${params.devIds}&report=${params.report}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
//根据误差体系id获取测试项
|
||||||
|
export const getPqErrSysTestItemList = (params: {errorSysId : string}) => {
|
||||||
|
return http.get(`/pqErrSys/getTestItems?id=${params.errorSysId}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取计划项目成员
|
||||||
|
export const getMemberList = (params: {id : string}) => {
|
||||||
|
return http.get(`/adPlan/getMemberList?planId=${params.id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入并合并子检测计划检测结果数据
|
||||||
|
export const importAndMergePlanCheckData = (params: Plan.ResPlan) => {
|
||||||
|
return http.upload(`/adPlan/importAndMergePlanCheckData`, params)
|
||||||
|
}
|
||||||
51
frontend/src/api/result/interface/index.ts
Normal file
51
frontend/src/api/result/interface/index.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
|
||||||
|
export interface MonitorResult {
|
||||||
|
/**
|
||||||
|
* 监测点id
|
||||||
|
*/
|
||||||
|
monitorId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监测点序号
|
||||||
|
*/
|
||||||
|
monitorNum: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总检测次数
|
||||||
|
*/
|
||||||
|
totalNum: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合格检测次数
|
||||||
|
*/
|
||||||
|
qualifiedNum: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不合格检测次数
|
||||||
|
*/
|
||||||
|
unQualifiedNum: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 误差体系名称
|
||||||
|
*/
|
||||||
|
errorSysName: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检测结果
|
||||||
|
*/
|
||||||
|
checkResult: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 哪次
|
||||||
|
*/
|
||||||
|
whichTime: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结论来源
|
||||||
|
*/
|
||||||
|
resultOrigin: string;
|
||||||
|
/**
|
||||||
|
* 来源类型
|
||||||
|
*/
|
||||||
|
resultType: string;
|
||||||
|
}
|
||||||
7
frontend/src/api/result/result.ts
Normal file
7
frontend/src/api/result/result.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import http from '@/api'
|
||||||
|
|
||||||
|
export const getMonitorResult = (devId: string) => http.post(`/result/getMonitorResult?devId=${devId}`)
|
||||||
|
export const getMonitorDataSourceResult = (monitorId: string) =>
|
||||||
|
http.get(`/result/getMonitorDataSourceResult?monitorId=${monitorId}`)
|
||||||
|
|
||||||
|
export const updateMonitorResult = (data: any) => http.post('/result/updateMonitorResult', data)
|
||||||
@@ -13,9 +13,9 @@ export const closePreTest = (params) => {
|
|||||||
* 开始正式检测
|
* 开始正式检测
|
||||||
* @param params
|
* @param params
|
||||||
*/
|
*/
|
||||||
export const startTest = (params) => {
|
// export const startTest = (params) => {
|
||||||
return http.post(`/prepare/startTest`, params, {loading: false})
|
// return http.post(`/prepare/startTest`, params, {loading: false})
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 暂停正式检测
|
* 暂停正式检测
|
||||||
@@ -32,3 +32,15 @@ export const pauseTest = () => {
|
|||||||
export const resumeTest = (params) => {
|
export const resumeTest = (params) => {
|
||||||
return http.post(`/prepare/restartTemTest/`, params, {loading: false})
|
return http.post(`/prepare/restartTemTest/`, params, {loading: false})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 比对式通道配对
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
export const contrastTest = (params: any) => {
|
||||||
|
return http.post(`/prepare/startContrastTest`,params)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const exportAlignData= () => {
|
||||||
|
return http.download(`/prepare/exportAlignData`)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
import http from '@/api'
|
import http from '@/api'
|
||||||
import {type Base} from '@/api/system/base/interface'
|
import {type Base} from '@/api/system/base/interface'
|
||||||
|
|
||||||
@@ -12,6 +13,10 @@ export const updateTestConfig = (params: Base.ResTestConfig) => {
|
|||||||
return http.post(`/sysTestConfig/update`, params)
|
return http.post(`/sysTestConfig/update`, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//场景切换
|
||||||
|
export const updateScene = (params: any) => {
|
||||||
|
return http.post(`/sysTestConfig/update`,params)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ export namespace Base {
|
|||||||
id: string; //系统配置表Id
|
id: string; //系统配置表Id
|
||||||
autoGenerate:number;//检测报告是否自动生成0 否;1是
|
autoGenerate:number;//检测报告是否自动生成0 否;1是
|
||||||
maxTime:number;//最大复检次数,默认3次
|
maxTime:number;//最大复检次数,默认3次
|
||||||
dataRule:string;//数据处理原则,关联字典(所有值、部分值、cp95值、平均值、任意值),默认任意值
|
|
||||||
state: number; //状态
|
state: number; //状态
|
||||||
|
scale:number;//小数位
|
||||||
createBy?: string| null; //创建用户
|
createBy?: string| null; //创建用户
|
||||||
createTime?: string| null; //创建时间
|
createTime?: string| null; //创建时间
|
||||||
updateBy?: string| null; //更新用户
|
updateBy?: string| null; //更新用户
|
||||||
|
|||||||
@@ -9,3 +9,8 @@ import http from '@/api'
|
|||||||
export const getAuditLog = (params: AuditLog.ReqAuditLogParams) => {
|
export const getAuditLog = (params: AuditLog.ReqAuditLogParams) => {
|
||||||
return http.post(`/sysLog/list`, params)
|
return http.post(`/sysLog/list`, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const exportCsv = (params: AuditLog.ReqAuditLogParams) => {
|
||||||
|
return http.download(`/sysLog/exportCSV`, params)
|
||||||
|
}
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
import http from '@/api'
|
import http from '@/api'
|
||||||
import {type VersionRegister} from '@/api/system/versionRegister/interface'
|
import { type VersionRegister } from '@/api/system/versionRegister/interface'
|
||||||
|
|
||||||
//获取有效数据配置
|
//获取有效数据配置
|
||||||
export const getRegRes = (params: VersionRegister.ResSys_Reg_Res) => {
|
export const getRegRes = (params: { type: string }) => {
|
||||||
return http.get(`/sysRegRes/getRegResByType?id=${params.type}`)
|
return http.get(`/sysRegRes/getRegResByType?id=${params.type}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//编辑有效数据配置
|
//编辑有效数据配置
|
||||||
export const updateRegRes = (params: VersionRegister.Sys_Reg_Res) => {
|
export const updateRegRes = (params: VersionRegister.Sys_Reg_Res) => {
|
||||||
return http.post(`/sysRegRes/update`, params)
|
return http.post(`/sysRegRes/update`, params)
|
||||||
|
|||||||
@@ -1,21 +1,23 @@
|
|||||||
// 登录模块
|
// 登录模块
|
||||||
import type { ReqPage,ResPage } from '@/api/interface'
|
import type { ReqPage, ResPage } from '@/api/interface'
|
||||||
|
|
||||||
export namespace Login {
|
export namespace Login {
|
||||||
export interface ReqLoginForm {
|
export interface ReqLoginForm {
|
||||||
username: string;
|
username: string
|
||||||
password: string;
|
password: string
|
||||||
}
|
checked: boolean
|
||||||
export interface ResLogin {
|
}
|
||||||
accessToken: string;
|
export interface ResLogin {
|
||||||
refreshToken: string;
|
accessToken: string
|
||||||
userInfo:{
|
refreshToken: string
|
||||||
name: string;
|
userInfo: {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export interface ResAuthButtons {
|
||||||
|
[key: string]: string[]
|
||||||
}
|
}
|
||||||
}
|
|
||||||
export interface ResAuthButtons {
|
|
||||||
[key: string]: string[];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -50,6 +52,8 @@ export namespace User {
|
|||||||
updateTime?: string;//更新时间
|
updateTime?: string;//更新时间
|
||||||
roleIds?: string[]; //
|
roleIds?: string[]; //
|
||||||
roleNames?:string[]; //
|
roleNames?:string[]; //
|
||||||
|
roleCodes?:string[]; //
|
||||||
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用户接口
|
// 用户接口
|
||||||
|
|||||||
@@ -1,40 +1,60 @@
|
|||||||
import type { Login } from '@/api/user/interface/user'
|
import { pa } from 'element-plus/es/locale/index.mjs';
|
||||||
import { ADMIN as rePrefix } from '@/api/system/config/serviceName'
|
import type {Login} from '@/api/user/interface/user'
|
||||||
|
import {ADMIN as rePrefix} from '@/api/system/config/serviceName'
|
||||||
import http from '@/api'
|
import http from '@/api'
|
||||||
import type { Dict } from '@/api/interface'
|
import type {Dict} from '@/api/interface'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name 登录模块
|
* @name 登录模块
|
||||||
*/
|
*/
|
||||||
// 用户登录
|
// 用户登录
|
||||||
export const loginApi = (params: Login.ReqLoginForm) => {
|
export const loginApi = (params: { username: string; password: string}) => {
|
||||||
return http.post<Login.ResLogin>(`${rePrefix}/login`, params, { loading: false })
|
return http.post<Login.ResLogin>(`${rePrefix}/login`, params, {loading: false})
|
||||||
// return http.post<Login.ResLogin>(`/Register1`, params, { loading: false })
|
// return http.post<Login.ResLogin>(`/Register1`, params, { loading: false })
|
||||||
}
|
}
|
||||||
// 获取菜单列表
|
// 获取菜单列表
|
||||||
export const getAuthMenuListApi = () => {
|
export const getAuthMenuListApi = () => {
|
||||||
return http.get<Menu.MenuOptions[]>(`/sysFunction/getMenu`, {}, { loading: false })
|
return http.get<Menu.MenuOptions[]>(`/sysFunction/getMenu`, {}, {loading: false})
|
||||||
// return http.post<Menu.MenuOptions[]>(`/Register2`, {}, { loading: false })
|
// return http.post<Menu.MenuOptions[]>(`/Register2`, {}, { loading: false })
|
||||||
}
|
}
|
||||||
// 获取按钮权限
|
// 获取按钮权限
|
||||||
export const getAuthButtonListApi = () => {
|
export const getAuthButtonListApi = () => {
|
||||||
return http.get<Login.ResAuthButtons>(`/sysFunction/getButton`, {}, { loading: false })
|
return http.get<Login.ResAuthButtons>(`/sysFunction/getButton`, {}, {loading: false})
|
||||||
// return http.post<Login.ResAuthButtons>(`/Register3`, {}, { loading: false })
|
// return http.post<Login.ResAuthButtons>(`/Register3`, {}, { loading: false })
|
||||||
}
|
}
|
||||||
// 用户退出登录
|
// 用户退出登录
|
||||||
export const logoutApi = () => {
|
export const logoutApi = () => {
|
||||||
return http.post(`${rePrefix}/logout`)
|
return http.post(`${rePrefix}/logout`)
|
||||||
}
|
}
|
||||||
|
|
||||||
//获取下拉框列表
|
//获取下拉框列表
|
||||||
export const getDictList = () =>{
|
export const getDictList = () => {
|
||||||
return http.get<Dict>('/dictData/dictDataCache')
|
return http.get<Dict>('/dictData/dictDataCache')
|
||||||
}
|
}
|
||||||
|
|
||||||
//token刷新
|
//token刷新
|
||||||
export const refreshToken = () => {
|
export const refreshToken = () => {
|
||||||
return http.get<Login.ResLogin>(`${rePrefix}/refreshToken`,
|
return http.get<Login.ResLogin>(`${rePrefix}/refreshToken`,
|
||||||
{},
|
{},
|
||||||
{ loading: false }
|
{loading: false}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//获取场景
|
||||||
|
export const getCurrentScene = () => {
|
||||||
|
return http.get('/sysTestConfig/getCurrentScene', {}, {loading: false})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取RSA公钥
|
||||||
|
*/
|
||||||
|
export const getPublicKey = (username: string) => {
|
||||||
|
return http.get(`/admin/getPublicKey?username=${username}`, {}, {loading: false})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取是否在检测中自动生成报告
|
||||||
|
*/
|
||||||
|
export const getAutoGenerate = () => {
|
||||||
|
return http.get('/sysTestConfig/getAutoGenerate', {}, {loading: false})
|
||||||
|
}
|
||||||
|
|||||||
@@ -37,3 +37,7 @@ export const updatePassWord = (params: User.ResPassWordUser) => {
|
|||||||
return http.post(`/sysUser/updatePassword`,params)
|
return http.post(`/sysUser/updatePassword`,params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取所有用户
|
||||||
|
export const getAllUser= () => {
|
||||||
|
return http.get(`/sysUser/getAll`)
|
||||||
|
}
|
||||||
|
|||||||
BIN
frontend/src/assets/images/inspected1.jpg
Normal file
BIN
frontend/src/assets/images/inspected1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 MiB |
BIN
frontend/src/assets/images/inspected2.png
Normal file
BIN
frontend/src/assets/images/inspected2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 54 KiB |
@@ -1,19 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="not-container">
|
<div class="not-container">
|
||||||
<img src="@/assets/images/403.png" class="not-img" alt="403" />
|
<img src="@/assets/images/403.png" class="not-img" alt="403" />
|
||||||
<div class="not-detail">
|
<div class="not-detail">
|
||||||
<h2>403</h2>
|
<h2>403</h2>
|
||||||
<h4>抱歉,您无权访问该页面~🙅♂️🙅♀️</h4>
|
<h4>抱歉,您无权访问该页面~🙅♂️🙅♀️</h4>
|
||||||
<el-button type="primary" @click="router.back"> 返回上一页 </el-button>
|
<el-button type="primary" @click="router.back">返回上一页</el-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="403">
|
<script setup lang="ts" name="403">
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from 'vue-router'
|
||||||
const router = useRouter();
|
|
||||||
|
const router = useRouter()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "./index.scss";
|
@use './index.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="not-container">
|
<div class="not-container">
|
||||||
<img src="@/assets/images/404.png" class="not-img" alt="404" />
|
<img src="@/assets/images/404.png" class="not-img" alt="404" />
|
||||||
<div class="not-detail">
|
<div class="not-detail">
|
||||||
<h2>404</h2>
|
<h2>404</h2>
|
||||||
<h4>抱歉,您访问的页面不存在~🤷♂️🤷♀️</h4>
|
<h4>抱歉,您访问的页面不存在~🤷♂️🤷♀️</h4>
|
||||||
<el-button type="primary" @click="router.back"> 返回上一页 </el-button>
|
<el-button type="primary" @click="router.back">返回上一页</el-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="404">
|
<script setup lang="ts" name="404">
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from 'vue-router'
|
||||||
const router = useRouter();
|
|
||||||
|
const router = useRouter()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "./index.scss";
|
@use './index.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="not-container">
|
<div class="not-container">
|
||||||
<img src="@/assets/images/500.png" class="not-img" alt="500" />
|
<img src="@/assets/images/500.png" class="not-img" alt="500" />
|
||||||
<div class="not-detail">
|
<div class="not-detail">
|
||||||
<h2>500</h2>
|
<h2>500</h2>
|
||||||
<h4>抱歉,您的网络不见了~🤦♂️🤦♀️</h4>
|
<h4>抱歉,您的网络不见了~🤦♂️🤦♀️</h4>
|
||||||
<el-button type="primary" @click="router.back"> 返回上一页 </el-button>
|
<el-button type="primary" @click="router.back">返回上一页</el-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="500">
|
<script setup lang="ts" name="500">
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from 'vue-router'
|
||||||
const router = useRouter();
|
|
||||||
|
const router = useRouter()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "./index.scss";
|
@use './index.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,58 +1,69 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog v-model='dialogVisible' :title='`批量添加${parameter.title}`' :destroy-on-close='true' width='580px'
|
<el-dialog
|
||||||
draggable>
|
v-model="dialogVisible"
|
||||||
<el-form class='drawer-multiColumn-form' label-width='100px'>
|
:title="`批量添加${parameter.title}`"
|
||||||
<el-form-item label='模板下载 :'>
|
:destroy-on-close="true"
|
||||||
<el-button type='primary' :icon='Download' @click='downloadTemp'> 点击下载</el-button>
|
width="580px"
|
||||||
</el-form-item>
|
draggable
|
||||||
<el-form-item label='文件上传 :'>
|
>
|
||||||
<el-upload
|
<el-form class="drawer-multiColumn-form" label-width="100px">
|
||||||
action='#'
|
<el-form-item label="模板下载 :">
|
||||||
class='upload'
|
<el-button type="primary" :icon="Download" @click="downloadTemp">点击下载</el-button>
|
||||||
:drag='true'
|
</el-form-item>
|
||||||
:limit='excelLimit'
|
<el-form-item label="文件上传 :">
|
||||||
:multiple='true'
|
<el-upload
|
||||||
:show-file-list='true'
|
action="#"
|
||||||
:http-request='uploadExcel'
|
class="upload"
|
||||||
:before-upload='beforeExcelUpload'
|
:drag="true"
|
||||||
:on-exceed='handleExceed'
|
:limit="excelLimit"
|
||||||
:accept="parameter.fileType!.join(',')"
|
:multiple="true"
|
||||||
>
|
:show-file-list="true"
|
||||||
<slot name='empty'>
|
:http-request="uploadExcel"
|
||||||
<el-icon class='el-icon--upload'>
|
:before-upload="beforeExcelUpload"
|
||||||
<upload-filled />
|
:on-exceed="handleExceed"
|
||||||
</el-icon>
|
:accept="parameter.fileType!.join(',')"
|
||||||
<div class='el-upload__text'>将文件拖到此处,或<em>点击上传</em></div>
|
>
|
||||||
</slot>
|
<slot name="empty">
|
||||||
<template #tip>
|
<el-icon class="el-icon--upload">
|
||||||
<slot name='tip'>
|
<upload-filled />
|
||||||
<div class='el-upload__tip'>请上传 .xls , .xlsx 标准格式文件,文件最大为 {{ parameter.fileSize }}M</div>
|
</el-icon>
|
||||||
</slot>
|
<div class="el-upload__text">
|
||||||
</template>
|
将文件拖到此处,或
|
||||||
</el-upload>
|
<em>点击上传</em>
|
||||||
</el-form-item>
|
</div>
|
||||||
<el-form-item v-if='parameter.showCover' label='数据覆盖 :'>
|
</slot>
|
||||||
<el-switch v-model='isCover' />
|
<template #tip>
|
||||||
</el-form-item>
|
<slot name="tip">
|
||||||
</el-form>
|
<div class="el-upload__tip">
|
||||||
</el-dialog>
|
请上传 .xls , .xlsx 标准格式文件,文件最大为 {{ parameter.fileSize }}M
|
||||||
|
</div>
|
||||||
|
</slot>
|
||||||
|
</template>
|
||||||
|
</el-upload>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="parameter.showCover" label="数据覆盖 :">
|
||||||
|
<el-switch v-model="isCover" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang='ts' name='ImportExcel'>
|
<script setup lang="ts" name="ImportExcel">
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useDownload } from '@/hooks/useDownload'
|
import { useDownload } from '@/hooks/useDownload'
|
||||||
import { Download } from '@element-plus/icons-vue'
|
import { Download } from '@element-plus/icons-vue'
|
||||||
import { ElNotification, UploadRequestOptions, UploadRawFile, ElMessage } from 'element-plus'
|
import { ElMessage, ElNotification, UploadRawFile, UploadRequestOptions } from 'element-plus'
|
||||||
|
|
||||||
export interface ExcelParameterProps {
|
export interface ExcelParameterProps {
|
||||||
title: string; // 标题
|
title: string // 标题
|
||||||
showCover?: boolean; // 是否显示”数据覆盖“选项
|
showCover?: boolean // 是否显示”数据覆盖“选项
|
||||||
patternId?: string; // 模式ID
|
patternId?: string // 模式ID
|
||||||
fileSize?: number; // 上传文件的大小
|
planId?: string | null //计划ID
|
||||||
fileType?: File.ExcelMimeType[]; // 上传文件的类型
|
fileSize?: number // 上传文件的大小
|
||||||
tempApi?: (params: any) => Promise<any>; // 下载模板的Api
|
fileType?: File.ExcelMimeType[] // 上传文件的类型
|
||||||
importApi?: (params: any) => Promise<any>; // 批量导入的Api
|
tempApi?: (params: any) => Promise<any> // 下载模板的Api
|
||||||
getTableList?: () => void; // 获取表格数据的Api
|
importApi?: (params: any) => Promise<any> // 批量导入的Api
|
||||||
|
getTableList?: () => void // 获取表格数据的Api
|
||||||
}
|
}
|
||||||
|
|
||||||
// 是否覆盖数据
|
// 是否覆盖数据
|
||||||
@@ -63,68 +74,77 @@ const excelLimit = ref(1)
|
|||||||
const dialogVisible = ref(false)
|
const dialogVisible = ref(false)
|
||||||
// 父组件传过来的参数
|
// 父组件传过来的参数
|
||||||
const parameter = ref<ExcelParameterProps>({
|
const parameter = ref<ExcelParameterProps>({
|
||||||
title: '',
|
title: '',
|
||||||
fileSize: 5,
|
fileSize: 5,
|
||||||
fileType: ['application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],
|
fileType: ['application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']
|
||||||
})
|
})
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'result', data: boolean): void
|
||||||
|
}>()
|
||||||
// 接收父组件参数
|
// 接收父组件参数
|
||||||
const acceptParams = (params: ExcelParameterProps) => {
|
const acceptParams = (params: ExcelParameterProps) => {
|
||||||
parameter.value = { ...parameter.value, ...params }
|
parameter.value = { ...parameter.value, ...params }
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Excel 导入模板下载
|
// Excel 导入模板下载
|
||||||
const downloadTemp = () => {
|
const downloadTemp = () => {
|
||||||
if (!parameter.value.tempApi) return
|
if (!parameter.value.tempApi) return
|
||||||
useDownload(parameter.value.tempApi, `${parameter.value.title}模板`, {}, false)
|
useDownload(parameter.value.tempApi, `${parameter.value.title}模板`, { pattern: parameter.value.patternId }, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 文件上传
|
// 文件上传
|
||||||
const uploadExcel = async (param: UploadRequestOptions) => {
|
const uploadExcel = async (param: UploadRequestOptions) => {
|
||||||
let excelFormData = new FormData()
|
let excelFormData = new FormData()
|
||||||
excelFormData.append('file', param.file)
|
excelFormData.append('file', param.file)
|
||||||
if (parameter.value.patternId) {
|
if (parameter.value.patternId) {
|
||||||
excelFormData.append('patternId', parameter.value.patternId)
|
excelFormData.append('patternId', parameter.value.patternId)
|
||||||
}
|
}
|
||||||
isCover.value && excelFormData.append('isCover', isCover.value as unknown as Blob)
|
|
||||||
//await parameter.value.importApi!(excelFormData);
|
excelFormData.append('planId', parameter.value.planId)
|
||||||
await parameter.value.importApi!(excelFormData)
|
|
||||||
.then(res => handleImportResponse(res))
|
isCover.value && excelFormData.append('isCover', isCover.value as unknown as Blob)
|
||||||
parameter.value.getTableList && parameter.value.getTableList()
|
//await parameter.value.importApi!(excelFormData);
|
||||||
dialogVisible.value = false
|
await parameter.value.importApi!(excelFormData).then(res => handleImportResponse(res))
|
||||||
|
|
||||||
|
parameter.value.getTableList && parameter.value.getTableList()
|
||||||
|
dialogVisible.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function handleImportResponse(res: any) {
|
async function handleImportResponse(res: any) {
|
||||||
console.log(res)
|
console.log(res)
|
||||||
|
|
||||||
if (res.type === 'application/json') {
|
if (res.type === 'application/json') {
|
||||||
const fileReader = new FileReader()
|
const fileReader = new FileReader()
|
||||||
fileReader.onloadend = () => {
|
fileReader.onloadend = () => {
|
||||||
try {
|
try {
|
||||||
const jsonData = JSON.parse(fileReader.result)
|
const jsonData = JSON.parse(fileReader.result)
|
||||||
if (jsonData.code === 'A0000') {
|
if (jsonData.code === 'A0000') {
|
||||||
ElMessage.success('导入成功')
|
ElMessage.success('导入成功')
|
||||||
} else {
|
} else {
|
||||||
ElMessage.error(jsonData.message)
|
ElMessageBox.alert(jsonData.message, {
|
||||||
|
title: '导入结果',
|
||||||
|
type: 'error'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
emit('result', jsonData.data)
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
fileReader.readAsText(res)
|
||||||
console.log(err)
|
} else {
|
||||||
}
|
emit('result', false)
|
||||||
|
ElMessage.error('导入失败,请查看下载附件!')
|
||||||
|
let blob = new Blob([res], { type: 'application/vnd.ms-excel' })
|
||||||
|
const url = window.URL.createObjectURL(blob)
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = url
|
||||||
|
link.download = '导入失败数据'
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
link.remove()
|
||||||
}
|
}
|
||||||
fileReader.readAsText(res)
|
|
||||||
} else {
|
|
||||||
ElMessage.error('导入失败,请查看下载附件!')
|
|
||||||
let blob = new Blob([res], { type: 'application/vnd.ms-excel' })
|
|
||||||
const url = window.URL.createObjectURL(blob)
|
|
||||||
const link = document.createElement('a')
|
|
||||||
link.href = url
|
|
||||||
link.download = '导入失败数据'
|
|
||||||
document.body.appendChild(link)
|
|
||||||
link.click()
|
|
||||||
link.remove()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -132,32 +152,32 @@ async function handleImportResponse(res: any) {
|
|||||||
* @param file 上传的文件
|
* @param file 上传的文件
|
||||||
* */
|
* */
|
||||||
const beforeExcelUpload = (file: UploadRawFile) => {
|
const beforeExcelUpload = (file: UploadRawFile) => {
|
||||||
const isExcel = parameter.value.fileType!.includes(file.type as File.ExcelMimeType)
|
const isExcel = parameter.value.fileType!.includes(file.type as File.ExcelMimeType)
|
||||||
const fileSize = file.size / 1024 / 1024 < parameter.value.fileSize!
|
const fileSize = file.size / 1024 / 1024 < parameter.value.fileSize!
|
||||||
if (!isExcel)
|
if (!isExcel)
|
||||||
ElNotification({
|
ElNotification({
|
||||||
title: '温馨提示',
|
title: '温馨提示',
|
||||||
message: '上传文件只能是 xls / xlsx 格式!',
|
message: '上传文件只能是 xls / xlsx 格式!',
|
||||||
type: 'warning',
|
type: 'warning'
|
||||||
})
|
})
|
||||||
if (!fileSize)
|
if (!fileSize)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
ElNotification({
|
ElNotification({
|
||||||
title: '温馨提示',
|
title: '温馨提示',
|
||||||
message: `上传文件大小不能超过 ${parameter.value.fileSize}MB!`,
|
message: `上传文件大小不能超过 ${parameter.value.fileSize}MB!`,
|
||||||
type: 'warning',
|
type: 'warning'
|
||||||
})
|
})
|
||||||
}, 0)
|
}, 0)
|
||||||
return isExcel && fileSize
|
return isExcel && fileSize
|
||||||
}
|
}
|
||||||
|
|
||||||
// 文件数超出提示
|
// 文件数超出提示
|
||||||
const handleExceed = () => {
|
const handleExceed = () => {
|
||||||
ElNotification({
|
ElNotification({
|
||||||
title: '温馨提示',
|
title: '温馨提示',
|
||||||
message: '最多只能上传一个文件!',
|
message: '最多只能上传一个文件!',
|
||||||
type: 'warning',
|
type: 'warning'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上传错误提示
|
// 上传错误提示
|
||||||
@@ -179,9 +199,9 @@ const handleExceed = () => {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
acceptParams,
|
acceptParams
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<style lang='scss' scoped>
|
<style lang="scss" scoped>
|
||||||
@import "./index.scss";
|
@use './index.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
3
frontend/src/components/ImportZip/index.scss
Normal file
3
frontend/src/components/ImportZip/index.scss
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.upload {
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
241
frontend/src/components/ImportZip/index.vue
Normal file
241
frontend/src/components/ImportZip/index.vue
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogVisible"
|
||||||
|
:title="parameter.title"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
width="450px"
|
||||||
|
:close-on-click-modal="!parameter.progressBar"
|
||||||
|
:show-close="!disable"
|
||||||
|
draggable
|
||||||
|
>
|
||||||
|
<el-upload
|
||||||
|
ref="uploadRef"
|
||||||
|
action="#"
|
||||||
|
class="upload"
|
||||||
|
:limit="1"
|
||||||
|
:http-request="uploadZip"
|
||||||
|
accept=".zip"
|
||||||
|
:auto-upload="!parameter.confirmMessage"
|
||||||
|
:on-change="handleChange"
|
||||||
|
:on-remove="handleRemove"
|
||||||
|
:disabled="fileDisabled"
|
||||||
|
>
|
||||||
|
<slot name="empty">
|
||||||
|
<el-button type="primary" :disabled="fileDisabled" icon="Upload">点击上传</el-button>
|
||||||
|
</slot>
|
||||||
|
<template #tip>
|
||||||
|
<slot name="tip">
|
||||||
|
<div class="el-upload__tip">请上传 .zip 标准格式文件</div>
|
||||||
|
</slot>
|
||||||
|
</template>
|
||||||
|
</el-upload>
|
||||||
|
|
||||||
|
<el-text v-if="parameter.progressBar && progressData.status === 'exception'" size="small" type="danger">
|
||||||
|
{{ progressData.message }}
|
||||||
|
</el-text>
|
||||||
|
<el-text v-if="parameter.progressBar && progressData.status === 'success'" size="small" type="success">
|
||||||
|
{{ progressData.message }}
|
||||||
|
</el-text>
|
||||||
|
<el-text v-if="parameter.progressBar && progressData.status === ''" size="small" type="info">
|
||||||
|
{{ progressData.message }}
|
||||||
|
</el-text>
|
||||||
|
<el-progress
|
||||||
|
style="margin-top: 10px; margin-bottom: 10px"
|
||||||
|
v-if="parameter.progressBar"
|
||||||
|
:status="progressData.status"
|
||||||
|
:percentage="progressData.percentage"
|
||||||
|
:stroke-width="10"
|
||||||
|
striped
|
||||||
|
striped-flow
|
||||||
|
></el-progress>
|
||||||
|
|
||||||
|
<template #footer v-if="parameter.confirmMessage">
|
||||||
|
<el-button :disabled="disable" type="primary" @click="uploadSubmit">开始导入</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="ImportZip">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import type { UploadInstance, UploadProps, UploadRequestOptions } from 'element-plus'
|
||||||
|
import http from '@/api'
|
||||||
|
|
||||||
|
export interface ZipParameterProps {
|
||||||
|
title: string // 标题
|
||||||
|
patternId?: string // 模式ID
|
||||||
|
planId?: string // 计划ID
|
||||||
|
importApi?: (params: any) => Promise<any> // 批量导入的Api
|
||||||
|
confirmMessage?: string // 提示信息
|
||||||
|
progressBar?: boolean // 进度条
|
||||||
|
}
|
||||||
|
// dialog状态
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const disable = ref(true)
|
||||||
|
const fileDisabled = ref(false)
|
||||||
|
const uploadRef = ref<UploadInstance>()
|
||||||
|
|
||||||
|
// 父组件传过来的参数
|
||||||
|
const parameter = ref<ZipParameterProps>({
|
||||||
|
title: ''
|
||||||
|
})
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'result', data: boolean): void
|
||||||
|
}>()
|
||||||
|
// 接收父组件参数
|
||||||
|
const acceptParams = (params: ZipParameterProps) => {
|
||||||
|
parameter.value = { ...parameter.value, ...params }
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件上传
|
||||||
|
const uploadZip = (param: UploadRequestOptions) => {
|
||||||
|
let zipFormData = new FormData()
|
||||||
|
zipFormData.append('file', param.file)
|
||||||
|
if (parameter.value.patternId) {
|
||||||
|
zipFormData.append('patternId', parameter.value.patternId)
|
||||||
|
}
|
||||||
|
if (parameter.value.planId) {
|
||||||
|
zipFormData.append('planId', parameter.value.planId)
|
||||||
|
}
|
||||||
|
if (parameter.value.progressBar) {
|
||||||
|
initSSE()
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
parameter.value.importApi!(zipFormData)
|
||||||
|
.then(res => handleImportResponse(res))
|
||||||
|
.catch(err => {
|
||||||
|
fileDisabled.value = false
|
||||||
|
disable.value = false
|
||||||
|
})
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleImportResponse = (res: any) => {
|
||||||
|
if (!parameter.value.progressBar) {
|
||||||
|
if (res.code === 'A0000') {
|
||||||
|
ElMessage.success('导入成功')
|
||||||
|
emit('result', true)
|
||||||
|
dialogVisible.value = false
|
||||||
|
} else {
|
||||||
|
ElMessage.error(res.message)
|
||||||
|
fileDisabled.value = false
|
||||||
|
disable.value = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (res.code !== 'A0000') {
|
||||||
|
ElMessage.error(res.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploadSubmit = () => {
|
||||||
|
if (!uploadRef.value) {
|
||||||
|
return ElMessage.warning('请选择文件!')
|
||||||
|
}
|
||||||
|
progressData.value = {
|
||||||
|
percentage: 0,
|
||||||
|
status: '',
|
||||||
|
message: ''
|
||||||
|
}
|
||||||
|
ElMessageBox.confirm(parameter.value.confirmMessage, '温馨提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
disable.value = true
|
||||||
|
fileDisabled.value = true
|
||||||
|
uploadRef.value?.submit()
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
disable.value = false
|
||||||
|
fileDisabled.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const handleChange: UploadProps['onChange'] = (uploadFile, uploadFiles) => {
|
||||||
|
disable.value = uploadFiles.length === 0
|
||||||
|
progressData.value = {
|
||||||
|
percentage: 0,
|
||||||
|
status: '',
|
||||||
|
message: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
|
||||||
|
disable.value = uploadFiles.length === 0
|
||||||
|
progressData.value = {
|
||||||
|
percentage: 0,
|
||||||
|
status: '',
|
||||||
|
message: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const progressData = ref({
|
||||||
|
percentage: 0,
|
||||||
|
status: '',
|
||||||
|
message: ''
|
||||||
|
})
|
||||||
|
const eventSource = ref<EventSource | null>(null)
|
||||||
|
|
||||||
|
const initSSE = () => {
|
||||||
|
eventSource.value = http.sse('/sse/createSse')
|
||||||
|
|
||||||
|
eventSource.value.onmessage = event => {
|
||||||
|
console.log('收到消息内容是:', event.data)
|
||||||
|
const res = JSON.parse(event.data)
|
||||||
|
progressData.value.percentage = res.data
|
||||||
|
progressData.value.message = res.message
|
||||||
|
if (res.code === 'A0002') {
|
||||||
|
fileDisabled.value = false
|
||||||
|
disable.value = false
|
||||||
|
progressData.value.status = 'exception'
|
||||||
|
ElMessage.error(res.message)
|
||||||
|
}
|
||||||
|
if (progressData.value.percentage === 100) {
|
||||||
|
progressData.value.status = 'success'
|
||||||
|
eventSource.value!.close()
|
||||||
|
ElMessage.success('导入成功')
|
||||||
|
emit('result', true)
|
||||||
|
dialogVisible.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eventSource.value.onerror = error => {
|
||||||
|
console.warn('SSE 连接出错:', error)
|
||||||
|
eventSource.value!.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 添加一个手动关闭EventSource的函数
|
||||||
|
const closeEventSource = () => {
|
||||||
|
if (eventSource.value) {
|
||||||
|
eventSource.value.close()
|
||||||
|
eventSource.value = null
|
||||||
|
console.log('SSE连接已关闭')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 监听 dialogVisible 的变化,确保在对话框关闭时清理资源
|
||||||
|
watch(dialogVisible, newVal => {
|
||||||
|
if (!newVal) {
|
||||||
|
// 延迟执行,确保在组件完全关闭后清理
|
||||||
|
setTimeout(() => {
|
||||||
|
closeEventSource()
|
||||||
|
fileDisabled.value = false
|
||||||
|
disable.value = false
|
||||||
|
progressData.value = {
|
||||||
|
percentage: 0,
|
||||||
|
status: '',
|
||||||
|
message: ''
|
||||||
|
}
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
onUnmounted(() => {
|
||||||
|
closeEventSource()
|
||||||
|
})
|
||||||
|
defineExpose({
|
||||||
|
acceptParams
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@use './index.scss';
|
||||||
|
</style>
|
||||||
@@ -9,5 +9,5 @@
|
|||||||
<script setup lang="ts" name="Loading"></script>
|
<script setup lang="ts" name="Loading"></script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "./index.scss";
|
@use "./index.scss";;
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ import TableColumn from './components/TableColumn.vue'
|
|||||||
import Sortable from 'sortablejs'
|
import Sortable from 'sortablejs'
|
||||||
|
|
||||||
export interface ProTableProps {
|
export interface ProTableProps {
|
||||||
|
|
||||||
columns: ColumnProps[]; // 列配置项 ==> 必传
|
columns: ColumnProps[]; // 列配置项 ==> 必传
|
||||||
data?: any[]; // 静态 table data 数据,若存在则不会使用 requestApi 返回的 data ==> 非必传
|
data?: any[]; // 静态 table data 数据,若存在则不会使用 requestApi 返回的 data ==> 非必传
|
||||||
requestApi?: (params: any) => Promise<any>; // 请求表格数据的 api ==> 非必传
|
requestApi?: (params: any) => Promise<any>; // 请求表格数据的 api ==> 非必传
|
||||||
|
|||||||
@@ -1,49 +1,49 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class='icon-box' >
|
<div class="icon-box">
|
||||||
<el-input
|
<el-input
|
||||||
ref='inputRef'
|
ref="inputRef"
|
||||||
v-model='valueIcon'
|
v-model="valueIcon"
|
||||||
v-bind='$attrs'
|
v-bind="$attrs"
|
||||||
:placeholder='placeholder'
|
:placeholder="placeholder"
|
||||||
:clearable='clearable'
|
:clearable="clearable"
|
||||||
@clear='clearIcon'
|
@clear="clearIcon"
|
||||||
@click='openDialog'
|
@click="openDialog"
|
||||||
>
|
>
|
||||||
<template #append>
|
<template #append>
|
||||||
<el-button :icon='customIcons[iconValue]' />
|
<el-button :icon="customIcons[iconValue]" />
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
<el-dialog v-model='dialogVisible' :title='placeholder' top='5%' width='30%' >
|
<el-dialog v-model="dialogVisible" :title="placeholder" top="5%" width="30%">
|
||||||
<el-input v-model='inputValue' placeholder='搜索图标' size='large' :prefix-icon='Icons.Search' />
|
<el-input v-model="inputValue" placeholder="搜索图标" size="large" :prefix-icon="Icons.Search" />
|
||||||
<el-scrollbar v-if='Object.keys(iconsList).length'>
|
<el-scrollbar v-if="Object.keys(iconsList).length">
|
||||||
<div class='icon-list'>
|
<div class="icon-list">
|
||||||
<div v-for='item in iconsList' :key='item' class='icon-item' @click='selectIcon(item)'>
|
<div v-for="item in iconsList" :key="item" class="icon-item" @click="selectIcon(item)">
|
||||||
<component :is='item'></component>
|
<component :is="item"></component>
|
||||||
<span>{{ item.name }}</span>
|
<span>{{ item.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
<el-empty v-else description='未搜索到您要找的图标~' />
|
<el-empty v-else description="未搜索到您要找的图标~" />
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang='ts' setup name='SelectIcon'>
|
<script lang="ts" setup name="SelectIcon">
|
||||||
import * as Icons from '@element-plus/icons-vue'
|
import * as Icons from '@element-plus/icons-vue'
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
interface SelectIconProps {
|
interface SelectIconProps {
|
||||||
iconValue: string| undefined;
|
iconValue: string | undefined
|
||||||
title?: string;
|
title?: string
|
||||||
clearable?: boolean;
|
clearable?: boolean
|
||||||
placeholder?: string;
|
placeholder?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<SelectIconProps>(), {
|
const props = withDefaults(defineProps<SelectIconProps>(), {
|
||||||
iconValue: '',
|
iconValue: '',
|
||||||
title: '请选择图标',
|
title: '请选择图标',
|
||||||
clearable: true,
|
clearable: true,
|
||||||
placeholder: '请选择图标',
|
placeholder: '请选择图标'
|
||||||
})
|
})
|
||||||
|
|
||||||
// 重新接收一下,防止打包后 clearable 报错
|
// 重新接收一下,防止打包后 clearable 报错
|
||||||
@@ -55,37 +55,36 @@ const openDialog = () => (dialogVisible.value = true)
|
|||||||
|
|
||||||
// 选择图标(触发更新父组件数据)
|
// 选择图标(触发更新父组件数据)
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
'update:iconValue': [value: string];
|
'update:iconValue': [value: string]
|
||||||
}>()
|
}>()
|
||||||
const selectIcon = (item: any) => {
|
const selectIcon = (item: any) => {
|
||||||
dialogVisible.value = false
|
dialogVisible.value = false
|
||||||
valueIcon.value = item.name
|
valueIcon.value = item.name
|
||||||
emit('update:iconValue', item.name)
|
emit('update:iconValue', item.name)
|
||||||
setTimeout(() => inputRef.value.blur(), 0)
|
setTimeout(() => inputRef.value.blur(), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清空图标
|
// 清空图标
|
||||||
const inputRef = ref()
|
const inputRef = ref()
|
||||||
const clearIcon = () => {
|
const clearIcon = () => {
|
||||||
valueIcon.value = ''
|
valueIcon.value = ''
|
||||||
emit('update:iconValue', '')
|
emit('update:iconValue', '')
|
||||||
setTimeout(() => inputRef.value.blur(), 0)
|
setTimeout(() => inputRef.value.blur(), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听搜索框值
|
// 监听搜索框值
|
||||||
const inputValue = ref('')
|
const inputValue = ref('')
|
||||||
const customIcons: { [key: string]: any } = Icons
|
const customIcons: { [key: string]: any } = Icons
|
||||||
const iconsList = computed((): { [key: string]: any } => {
|
const iconsList = computed((): { [key: string]: any } => {
|
||||||
if (!inputValue.value) return Icons
|
if (!inputValue.value) return Icons
|
||||||
let result: { [key: string]: any } = {}
|
let result: { [key: string]: any } = {}
|
||||||
for (const key in customIcons) {
|
for (const key in customIcons) {
|
||||||
if (key.toLowerCase().indexOf(inputValue.value.toLowerCase()) > -1) result[key] = customIcons[key]
|
if (key.toLowerCase().indexOf(inputValue.value.toLowerCase()) > -1) result[key] = customIcons[key]
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang='scss'>
|
<style scoped lang="scss">
|
||||||
@import "./index.scss";
|
@use './index.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,97 +1,84 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class='time-control'>
|
<div class="time-control">
|
||||||
<el-select
|
<el-select class="select" v-model="timeUnit" placeholder="选择时间单位" @change="handleChange">
|
||||||
class='select'
|
<!-- 采用 v-for 动态渲染 -->
|
||||||
v-model='timeUnit'
|
<el-option v-for="unit in timeUnits" :key="unit.value" :label="unit.label" :value="unit.value"></el-option>
|
||||||
placeholder='选择时间单位'
|
</el-select>
|
||||||
@change='handleChange'
|
|
||||||
>
|
|
||||||
<!-- 采用 v-for 动态渲染 -->
|
|
||||||
<el-option
|
|
||||||
v-for='unit in timeUnits'
|
|
||||||
:key='unit.value'
|
|
||||||
:label='unit.label'
|
|
||||||
:value='unit.value'
|
|
||||||
></el-option>
|
|
||||||
</el-select>
|
|
||||||
|
|
||||||
<!-- 禁用时间选择器 -->
|
<!-- 禁用时间选择器 -->
|
||||||
<div class='date-display'>
|
<div class="date-display">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
class='date-picker'
|
class="date-picker"
|
||||||
v-model='startDate'
|
v-model="startDate"
|
||||||
type='date'
|
type="date"
|
||||||
placeholder='起始时间'
|
placeholder="起始时间"
|
||||||
@change='emitDateChange'
|
@change="emitDateChange"
|
||||||
:disabled-date='disableStartDate'
|
:disabled-date="disableStartDate"
|
||||||
:readonly="timeUnit != '自定义'"
|
:readonly="timeUnit != '自定义'"
|
||||||
></el-date-picker>
|
></el-date-picker>
|
||||||
<el-text>~</el-text>
|
<el-text>~</el-text>
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
class='date-picker'
|
class="date-picker"
|
||||||
v-model='endDate'
|
v-model="endDate"
|
||||||
type='date'
|
type="date"
|
||||||
placeholder='结束时间'
|
placeholder="结束时间"
|
||||||
@change='emitDateChange'
|
@change="emitDateChange"
|
||||||
:disabled-date='disableEndDate'
|
:disabled-date="disableEndDate"
|
||||||
:readonly="timeUnit !== '自定义'"
|
:readonly="timeUnit !== '自定义'"
|
||||||
></el-date-picker>
|
></el-date-picker>
|
||||||
|
</div>
|
||||||
|
<div class="date-display" v-if="timeUnit !== '自定义'">
|
||||||
|
<el-button
|
||||||
|
style="width: 10px"
|
||||||
|
class="triangle-button"
|
||||||
|
type="primary"
|
||||||
|
@click="prevPeriod"
|
||||||
|
@change="emitDateChange"
|
||||||
|
>
|
||||||
|
<div class="left_triangle"></div>
|
||||||
|
</el-button>
|
||||||
|
<el-button class="triangle-button" type="primary" @click="goToCurrent">当前</el-button>
|
||||||
|
<el-button
|
||||||
|
style="width: 10px"
|
||||||
|
class="triangle-button"
|
||||||
|
type="primary"
|
||||||
|
@click="nextPeriod"
|
||||||
|
:disabled="isNextDisabled"
|
||||||
|
>
|
||||||
|
<div class="right_triangle"></div>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class='date-display' v-if="timeUnit !== '自定义'">
|
|
||||||
<el-button
|
|
||||||
style='width: 10px;'
|
|
||||||
class='triangle-button'
|
|
||||||
type='primary'
|
|
||||||
@click='prevPeriod'
|
|
||||||
@change='emitDateChange'
|
|
||||||
>
|
|
||||||
<div class='left_triangle'></div>
|
|
||||||
</el-button>
|
|
||||||
<el-button class='triangle-button' type='primary' @click='goToCurrent'>
|
|
||||||
当前
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
style='width: 10px;'
|
|
||||||
class='triangle-button'
|
|
||||||
type='primary'
|
|
||||||
@click='nextPeriod'
|
|
||||||
:disabled='isNextDisabled'
|
|
||||||
>
|
|
||||||
<div class='right_triangle'></div>
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
<script setup lang='ts'>
|
import { onMounted, ref } from 'vue'
|
||||||
import { ref, onMounted, defineProps, defineEmits } from 'vue'
|
|
||||||
|
|
||||||
// 定义时间单位的类型
|
// 定义时间单位的类型
|
||||||
interface TimeUnit {
|
interface TimeUnit {
|
||||||
label: string;
|
label: string
|
||||||
value: string;
|
value: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定义组件的props,包含包括和排除的时间单位
|
// 定义组件的props,包含包括和排除的时间单位
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
include: {
|
include: {
|
||||||
type: Array as () => string[],
|
type: Array as () => string[],
|
||||||
default: () => ['日', '周', '月', '季度', '年', '自定义'],
|
default: () => ['日', '周', '月', '季度', '年', '自定义']
|
||||||
},
|
},
|
||||||
exclude: {
|
exclude: {
|
||||||
type: Array as () => string[],
|
type: Array as () => string[],
|
||||||
default: () => [],
|
default: () => []
|
||||||
},
|
},
|
||||||
default: {
|
default: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '月',
|
default: '月'
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 定义事件
|
// 定义事件
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'update-dates', startDate: string, endDate: string): void;
|
(e: 'update-dates', startDate: string, endDate: string): void
|
||||||
}>()
|
}>()
|
||||||
const timeUnit = ref<string>(props.default) // 默认选择
|
const timeUnit = ref<string>(props.default) // 默认选择
|
||||||
const startDate = ref<Date>(new Date()) // 起始日期
|
const startDate = ref<Date>(new Date()) // 起始日期
|
||||||
@@ -100,219 +87,206 @@ const isNextDisabled = ref<boolean>(false) // 控制下一周期按钮的禁用
|
|||||||
const today = ref<Date>(new Date()) // 当前日期
|
const today = ref<Date>(new Date()) // 当前日期
|
||||||
// 过滤出可用的时间单位
|
// 过滤出可用的时间单位
|
||||||
const timeUnits = ref<TimeUnit[]>(
|
const timeUnits = ref<TimeUnit[]>(
|
||||||
props.include.filter(unit => !props.exclude.includes(unit)).map(unit => ({
|
props.include
|
||||||
label: unit,
|
.filter(unit => !props.exclude.includes(unit))
|
||||||
value: unit,
|
.map(unit => ({
|
||||||
})),
|
label: unit,
|
||||||
|
value: unit
|
||||||
|
}))
|
||||||
)
|
)
|
||||||
|
|
||||||
// 发出日期变化事件
|
// 发出日期变化事件
|
||||||
const emitDateChange = () => {
|
const emitDateChange = () => {
|
||||||
emit('update-dates', formatDate(startDate.value), formatDate(endDate.value))
|
emit('update-dates', formatDate(startDate.value), formatDate(endDate.value))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 在组件挂载时更新日期范围
|
// 在组件挂载时更新日期范围
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
updateDateRange()
|
updateDateRange()
|
||||||
})
|
})
|
||||||
const handleChange = (unit: string) => {
|
const handleChange = (unit: string) => {
|
||||||
// 根据选择的时间单位处理日期变化
|
// 根据选择的时间单位处理日期变化
|
||||||
if (unit !== '自定义') {
|
if (unit !== '自定义') {
|
||||||
updateDateRange()
|
updateDateRange()
|
||||||
} else {
|
} else {
|
||||||
// 自定义选项逻辑
|
// 自定义选项逻辑
|
||||||
startDate.value = new Date(new Date().setDate(new Date().getDate() - 1))
|
startDate.value = new Date(new Date().setDate(new Date().getDate() - 1))
|
||||||
endDate.value = new Date()
|
endDate.value = new Date()
|
||||||
}
|
}
|
||||||
timeUnit.value = unit
|
timeUnit.value = unit
|
||||||
|
|
||||||
// 确保开始时间和结束时间不为空
|
// 确保开始时间和结束时间不为空
|
||||||
if (!startDate.value) {
|
if (!startDate.value) {
|
||||||
startDate.value = new Date()
|
startDate.value = new Date()
|
||||||
}
|
}
|
||||||
if (!endDate.value) {
|
if (!endDate.value) {
|
||||||
endDate.value = new Date()
|
endDate.value = new Date()
|
||||||
}
|
}
|
||||||
|
|
||||||
emitDateChange() // 变化时也发出更新事件
|
emitDateChange() // 变化时也发出更新事件
|
||||||
updateNextButtonStatus()
|
updateNextButtonStatus()
|
||||||
}
|
}
|
||||||
const updateDateRange = () => {
|
const updateDateRange = () => {
|
||||||
|
// 根据选择的时间单位计算起始和结束日期
|
||||||
|
if (timeUnit.value === '日') {
|
||||||
|
startDate.value = today.value
|
||||||
|
endDate.value = today.value
|
||||||
|
} else if (timeUnit.value === '周') {
|
||||||
|
startDate.value = getStartOfWeek(today.value)
|
||||||
|
endDate.value = getEndOfWeek(today.value)
|
||||||
|
} else if (timeUnit.value === '月') {
|
||||||
|
// 获取本月的开始和结束日期
|
||||||
|
startDate.value = new Date(today.value.getFullYear(), today.value.getMonth(), 1)
|
||||||
|
endDate.value = new Date(today.value.getFullYear(), today.value.getMonth() + 1, 0)
|
||||||
|
|
||||||
// 根据选择的时间单位计算起始和结束日期
|
// // 确保结束日期不超过今天
|
||||||
if (timeUnit.value === '日') {
|
// if (endDate.value > today.value) {
|
||||||
startDate.value = today.value
|
// endDate.value = new Date(today.value);
|
||||||
endDate.value = today.value
|
// endDate.value.setHours(23, 59, 59, 999); // 设置结束时间为今天的23:59:59.999
|
||||||
} else if (timeUnit.value === '周') {
|
// }
|
||||||
startDate.value = getStartOfWeek(today.value)
|
} else if (timeUnit.value === '季度') {
|
||||||
endDate.value = getEndOfWeek(today.value)
|
const quarter = Math.floor(today.value.getMonth() / 3)
|
||||||
|
startDate.value = new Date(today.value.getFullYear(), quarter * 3, 1)
|
||||||
} else if (timeUnit.value === '月') {
|
endDate.value = new Date(today.value.getFullYear(), quarter * 3 + 3, 0)
|
||||||
// 获取本月的开始和结束日期
|
|
||||||
startDate.value = new Date(today.value.getFullYear(), today.value.getMonth(), 1);
|
|
||||||
endDate.value = new Date(today.value.getFullYear(), today.value.getMonth() + 1, 0);
|
|
||||||
|
|
||||||
// // 确保结束日期不超过今天
|
|
||||||
// if (endDate.value > today.value) {
|
|
||||||
// endDate.value = new Date(today.value);
|
|
||||||
// endDate.value.setHours(23, 59, 59, 999); // 设置结束时间为今天的23:59:59.999
|
|
||||||
// }
|
|
||||||
|
|
||||||
} else if (timeUnit.value === '季度') {
|
// // 确保结束日期不超过今天
|
||||||
const quarter = Math.floor(today.value.getMonth() / 3);
|
// if (endDate.value > today.value) {
|
||||||
startDate.value = new Date(today.value.getFullYear(), quarter * 3, 1);
|
// endDate.value = new Date(today.value);
|
||||||
endDate.value = new Date(today.value.getFullYear(), quarter * 3 + 3, 0);
|
// endDate.value.setHours(23, 59, 59, 999); // 设置结束时间为今天的23:59:59.999
|
||||||
|
// }
|
||||||
|
} else if (timeUnit.value === '年') {
|
||||||
|
startDate.value = new Date(today.value.getFullYear(), 0, 1)
|
||||||
|
endDate.value = new Date(today.value.getFullYear(), 11, 31)
|
||||||
|
|
||||||
// // 确保结束日期不超过今天
|
// // 确保结束日期不超过今天
|
||||||
// if (endDate.value > today.value) {
|
// if (endDate.value > today.value) {
|
||||||
// endDate.value = new Date(today.value);
|
|
||||||
// endDate.value.setHours(23, 59, 59, 999); // 设置结束时间为今天的23:59:59.999
|
|
||||||
// }
|
|
||||||
|
|
||||||
} else if (timeUnit.value === '年') {
|
// endDate.value = new Date(today.value);
|
||||||
startDate.value = new Date(today.value.getFullYear(), 0, 1);
|
// endDate.value.setHours(23, 59, 59, 999); // 设置结束时间为今天的23:59:59.999
|
||||||
endDate.value = new Date(today.value.getFullYear(), 11, 31);
|
// }
|
||||||
|
}
|
||||||
|
// 确保开始时间和结束时间不为空
|
||||||
|
if (!startDate.value) {
|
||||||
|
startDate.value = new Date()
|
||||||
|
}
|
||||||
|
if (!endDate.value) {
|
||||||
|
endDate.value = new Date()
|
||||||
|
}
|
||||||
|
|
||||||
// // 确保结束日期不超过今天
|
updateNextButtonStatus()
|
||||||
// if (endDate.value > today.value) {
|
|
||||||
|
|
||||||
// endDate.value = new Date(today.value);
|
|
||||||
// endDate.value.setHours(23, 59, 59, 999); // 设置结束时间为今天的23:59:59.999
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
// 确保开始时间和结束时间不为空
|
|
||||||
if (!startDate.value) {
|
|
||||||
startDate.value = new Date()
|
|
||||||
}
|
|
||||||
if (!endDate.value) {
|
|
||||||
endDate.value = new Date()
|
|
||||||
}
|
|
||||||
|
|
||||||
updateNextButtonStatus()
|
|
||||||
}
|
}
|
||||||
const getStartOfWeek = (date: Date) => {
|
const getStartOfWeek = (date: Date) => {
|
||||||
const startOfWeek = new Date(date)
|
const startOfWeek = new Date(date)
|
||||||
const day = startOfWeek.getDay()
|
const day = startOfWeek.getDay()
|
||||||
const diff = day === 0 ? -6 : 1 - day // 星期天的情况
|
const diff = day === 0 ? -6 : 1 - day // 星期天的情况
|
||||||
startOfWeek.setDate(startOfWeek.getDate() + diff)
|
startOfWeek.setDate(startOfWeek.getDate() + diff)
|
||||||
return startOfWeek
|
return startOfWeek
|
||||||
}
|
}
|
||||||
const getEndOfWeek = (date: Date) => {
|
const getEndOfWeek = (date: Date) => {
|
||||||
const endOfWeek = new Date(date)
|
const endOfWeek = new Date(date)
|
||||||
const day = endOfWeek.getDay()
|
const day = endOfWeek.getDay()
|
||||||
const diff = day === 0 ? 0 : 7 - day // 星期天的情况
|
const diff = day === 0 ? 0 : 7 - day // 星期天的情况
|
||||||
endOfWeek.setDate(endOfWeek.getDate() + diff)
|
endOfWeek.setDate(endOfWeek.getDate() + diff)
|
||||||
|
|
||||||
// 获取今天的日期
|
// 获取今天的日期
|
||||||
const today = new Date();
|
const today = new Date()
|
||||||
today.setHours(23, 59, 59, 999); // 设置今天的结束时间(23:59:59.999)
|
today.setHours(23, 59, 59, 999) // 设置今天的结束时间(23:59:59.999)
|
||||||
|
|
||||||
// 返回不超过今天的结束时间
|
// 返回不超过今天的结束时间
|
||||||
//return endOfWeek > today ? today : endOfWeek;
|
//return endOfWeek > today ? today : endOfWeek;
|
||||||
return endOfWeek
|
return endOfWeek
|
||||||
}
|
}
|
||||||
const prevPeriod = () => {
|
const prevPeriod = () => {
|
||||||
const prevStartDate = new Date(startDate.value)
|
const prevStartDate = new Date(startDate.value)
|
||||||
const prevEndDate = new Date(endDate.value)
|
const prevEndDate = new Date(endDate.value)
|
||||||
|
|
||||||
if (timeUnit.value === '日') {
|
if (timeUnit.value === '日') {
|
||||||
prevStartDate.setDate(prevStartDate.getDate() - 1)
|
prevStartDate.setDate(prevStartDate.getDate() - 1)
|
||||||
prevEndDate.setDate(prevEndDate.getDate() - 1)
|
prevEndDate.setDate(prevEndDate.getDate() - 1)
|
||||||
} else if (timeUnit.value === '周') {
|
} else if (timeUnit.value === '周') {
|
||||||
prevStartDate.setDate(prevStartDate.getDate() - 7)
|
prevStartDate.setDate(prevStartDate.getDate() - 7)
|
||||||
prevEndDate.setDate(prevEndDate.getDate() - 7)
|
prevEndDate.setDate(prevEndDate.getDate() - 7)
|
||||||
} else if (timeUnit.value === '月') {
|
} else if (timeUnit.value === '月') {
|
||||||
|
prevStartDate.setMonth(prevStartDate.getMonth() - 1)
|
||||||
|
prevEndDate.setMonth(prevEndDate.getMonth() - 1)
|
||||||
|
} else if (timeUnit.value === '季度') {
|
||||||
|
prevStartDate.setMonth(prevStartDate.getMonth() - 3)
|
||||||
|
prevEndDate.setMonth(prevEndDate.getMonth() - 3)
|
||||||
|
} else if (timeUnit.value === '年') {
|
||||||
|
prevStartDate.setFullYear(prevStartDate.getFullYear() - 1)
|
||||||
|
prevEndDate.setFullYear(prevEndDate.getFullYear() - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
startDate.value = prevStartDate
|
||||||
prevStartDate.setMonth(prevStartDate.getMonth() - 1)
|
endDate.value = prevEndDate
|
||||||
prevEndDate.setMonth(prevEndDate.getMonth() - 1)
|
updateNextButtonStatus()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} else if (timeUnit.value === '季度') {
|
|
||||||
prevStartDate.setMonth(prevStartDate.getMonth() - 3)
|
|
||||||
prevEndDate.setMonth(prevEndDate.getMonth() - 3)
|
|
||||||
} else if (timeUnit.value === '年') {
|
|
||||||
prevStartDate.setFullYear(prevStartDate.getFullYear() - 1)
|
|
||||||
prevEndDate.setFullYear(prevEndDate.getFullYear() - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
startDate.value = prevStartDate
|
|
||||||
endDate.value = prevEndDate
|
|
||||||
updateNextButtonStatus()
|
|
||||||
}
|
}
|
||||||
const goToCurrent = () => {
|
const goToCurrent = () => {
|
||||||
if (timeUnit.value !== '自定义') {
|
if (timeUnit.value !== '自定义') {
|
||||||
updateDateRange() // 更新为当前选择时间单位的时间范围
|
updateDateRange() // 更新为当前选择时间单位的时间范围
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const nextPeriod = () => {
|
const nextPeriod = () => {
|
||||||
const nextStartDate = new Date(startDate.value)
|
const nextStartDate = new Date(startDate.value)
|
||||||
const nextEndDate = new Date(endDate.value)
|
const nextEndDate = new Date(endDate.value)
|
||||||
|
|
||||||
if (timeUnit.value === '日') {
|
if (timeUnit.value === '日') {
|
||||||
nextStartDate.setDate(nextStartDate.getDate() + 1)
|
nextStartDate.setDate(nextStartDate.getDate() + 1)
|
||||||
nextEndDate.setDate(nextEndDate.getDate() + 1)
|
nextEndDate.setDate(nextEndDate.getDate() + 1)
|
||||||
} else if (timeUnit.value === '周') {
|
} else if (timeUnit.value === '周') {
|
||||||
nextStartDate.setDate(nextStartDate.getDate() + 7)
|
nextStartDate.setDate(nextStartDate.getDate() + 7)
|
||||||
nextEndDate.setDate(nextEndDate.getDate() + 7)
|
nextEndDate.setDate(nextEndDate.getDate() + 7)
|
||||||
} else if (timeUnit.value === '月') {
|
} else if (timeUnit.value === '月') {
|
||||||
nextStartDate.setMonth(nextStartDate.getMonth() + 1)
|
nextStartDate.setMonth(nextStartDate.getMonth() + 1)
|
||||||
nextEndDate.setMonth(nextEndDate.getMonth() + 1)
|
nextEndDate.setMonth(nextEndDate.getMonth() + 1)
|
||||||
} else if (timeUnit.value === '季度') {
|
} else if (timeUnit.value === '季度') {
|
||||||
nextStartDate.setMonth(nextStartDate.getMonth() + 3)
|
nextStartDate.setMonth(nextStartDate.getMonth() + 3)
|
||||||
nextEndDate.setMonth(nextStartDate.getMonth() + 3)
|
nextEndDate.setMonth(nextStartDate.getMonth() + 3)
|
||||||
} else if (timeUnit.value === '年') {
|
} else if (timeUnit.value === '年') {
|
||||||
nextStartDate.setFullYear(nextStartDate.getFullYear() + 1)
|
nextStartDate.setFullYear(nextStartDate.getFullYear() + 1)
|
||||||
nextEndDate.setFullYear(nextEndDate.getFullYear() + 1)
|
nextEndDate.setFullYear(nextEndDate.getFullYear() + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
startDate.value = nextStartDate
|
startDate.value = nextStartDate
|
||||||
endDate.value = nextEndDate
|
endDate.value = nextEndDate
|
||||||
updateNextButtonStatus()
|
updateNextButtonStatus()
|
||||||
}
|
}
|
||||||
const updateNextButtonStatus = () => {
|
const updateNextButtonStatus = () => {
|
||||||
|
// 更新下一个按钮的禁用状态
|
||||||
// 更新下一个按钮的禁用状态
|
const maxDate = new Date() // 假设最新日期为今天
|
||||||
const maxDate = new Date() // 假设最新日期为今天
|
// 将 maxDate 设置为当天的开始时间
|
||||||
// 将 maxDate 设置为当天的开始时间
|
maxDate.setHours(0, 0, 0, 0)
|
||||||
maxDate.setHours(0, 0, 0, 0)
|
// 将 endDate 设置为当天的开始时间并进行比较
|
||||||
// 将 endDate 设置为当天的开始时间并进行比较
|
const endDateAdjusted = new Date(endDate.value)
|
||||||
const endDateAdjusted = new Date(endDate.value)
|
endDateAdjusted.setHours(0, 0, 0, 0)
|
||||||
endDateAdjusted.setHours(0, 0, 0, 0)
|
// 仅比较日期部分
|
||||||
// 仅比较日期部分
|
isNextDisabled.value = endDateAdjusted >= maxDate
|
||||||
isNextDisabled.value = endDateAdjusted >= maxDate
|
emitDateChange() // 变化时也发出更新事件
|
||||||
emitDateChange() // 变化时也发出更新事件
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 限制开始日期不能选择超过当前日期
|
// 限制开始日期不能选择超过当前日期
|
||||||
const disableStartDate = (date: Date) => {
|
const disableStartDate = (date: Date) => {
|
||||||
return date > today.value
|
return date > today.value
|
||||||
}
|
}
|
||||||
// 限制结束日期不能超过当前日期且必须大于开始日期
|
// 限制结束日期不能超过当前日期且必须大于开始日期
|
||||||
const disableEndDate = (date: Date) => {
|
const disableEndDate = (date: Date) => {
|
||||||
if (timeUnit.value !== '自定义') return false // 如果不是自定义时间单位,则不限制
|
if (timeUnit.value !== '自定义') return false // 如果不是自定义时间单位,则不限制
|
||||||
const start = new Date(startDate.value)
|
const start = new Date(startDate.value)
|
||||||
return date > today.value || (start && date <= start)
|
return date > today.value || (start && date <= start)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 格式化日期yyyy-mm-dd
|
// 格式化日期yyyy-mm-dd
|
||||||
function formatDate(date: Date | null): string {
|
function formatDate(date: Date | null): string {
|
||||||
if (!date) {
|
if (!date) {
|
||||||
return '';
|
return ''
|
||||||
}
|
}
|
||||||
const year = date.getFullYear();
|
const year = date.getFullYear()
|
||||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||||
const day = String(date.getDate()).padStart(2, '0');
|
const day = String(date.getDate()).padStart(2, '0')
|
||||||
return `${year}-${month}-${day}`;
|
return `${year}-${month}-${day}`
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang='scss'>
|
<style scoped lang="scss">
|
||||||
@import "./index.scss";
|
@use './index.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onBeforeUnmount, onMounted, ref, defineExpose, watch, nextTick } from 'vue'
|
import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||||
// import echarts from './echarts'
|
// import echarts from './echarts'
|
||||||
import * as echarts from 'echarts' // 全引入
|
import * as echarts from 'echarts' // 全引入
|
||||||
// import 'echarts/lib/component/dataZoom'
|
// import 'echarts/lib/component/dataZoom'
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export const HOME_URL: string = "/home/index";
|
|||||||
export const LOGIN_URL: string = "/login";
|
export const LOGIN_URL: string = "/login";
|
||||||
|
|
||||||
// 默认主题颜色
|
// 默认主题颜色
|
||||||
export const DEFAULT_PRIMARY: string = "#003078";
|
export const DEFAULT_PRIMARY: string = "#526ADE";
|
||||||
|
|
||||||
// 路由白名单地址(本地存在的路由 staticRouter.ts 中)
|
// 路由白名单地址(本地存在的路由 staticRouter.ts 中)
|
||||||
export const ROUTER_WHITE_LIST: string[] = ["/500"];
|
export const ROUTER_WHITE_LIST: string[] = ["/500"];
|
||||||
@@ -19,3 +19,5 @@ export const AMAP_MAP_KEY: string = "";
|
|||||||
|
|
||||||
// 百度地图 key
|
// 百度地图 key
|
||||||
export const BAIDU_MAP_KEY: string = "";
|
export const BAIDU_MAP_KEY: string = "";
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,21 +6,24 @@ import { useAuthStore } from '@/stores/modules/auth'
|
|||||||
import type { Directive, DirectiveBinding } from 'vue'
|
import type { Directive, DirectiveBinding } from 'vue'
|
||||||
|
|
||||||
const auth: Directive = {
|
const auth: Directive = {
|
||||||
mounted(el: HTMLElement, binding: DirectiveBinding) {
|
mounted(el: HTMLElement, binding: DirectiveBinding) {
|
||||||
const { value } = binding
|
//console.log('binding',binding)
|
||||||
const authStore = useAuthStore()
|
const { value, modifiers } = binding
|
||||||
const currentPageRoles = authStore.authButtonListGet[authStore.routeName] ?? []
|
let currentPageRoles = []
|
||||||
// console.log('1234',authStore.routeName)
|
const authStore = useAuthStore()
|
||||||
// console.log('123',currentPageRoles)
|
if (modifiers && Object.keys(modifiers).length) {
|
||||||
if (value instanceof Array && value.length) {
|
currentPageRoles = authStore.authButtonListGet[Object.keys(modifiers)[0]] ?? []
|
||||||
//console.log('123456',value)
|
} else {
|
||||||
const hasPermission = value.every(item => currentPageRoles.includes(item))
|
currentPageRoles = authStore.authButtonListGet[authStore.routeName] ?? []
|
||||||
if (!hasPermission) el.remove()
|
}
|
||||||
} else {
|
console.log('currentPageRoles', currentPageRoles)
|
||||||
//console.log('12345',value)
|
if (value instanceof Array && value.length) {
|
||||||
if (!currentPageRoles.includes(value)) el.remove()
|
const hasPermission = value.every(item => currentPageRoles.includes(item))
|
||||||
|
if (!hasPermission) el.remove()
|
||||||
|
} else {
|
||||||
|
if (!currentPageRoles.includes(value)) el.remove()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default auth
|
export default auth
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
export enum ResultEnum {
|
export enum ResultEnum {
|
||||||
SUCCESS = "A0000",
|
SUCCESS = "A0000",
|
||||||
ERROR = 500,
|
ERROR = 500,
|
||||||
ACCESSTOKEN_EXPIRED = 401,
|
ACCESSTOKEN_EXPIRED = "A0024",
|
||||||
OVERDUE = 4001,
|
OVERDUE = "A0025",
|
||||||
TIMEOUT = 30000,
|
TIMEOUT = 30000,
|
||||||
TYPE = "success"
|
TYPE = "success"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { ElNotification } from "element-plus";
|
import { ElNotification } from "element-plus";
|
||||||
|
import type { AxiosResponse } from "axios";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 接收数据流生成 blob,创建链接,下载文件
|
* @description 接收数据流生成 blob,创建链接,下载文件
|
||||||
@@ -8,6 +9,55 @@ import { ElNotification } from "element-plus";
|
|||||||
* @param {Boolean} isNotify 是否有导出消息提示 (默认为 true)
|
* @param {Boolean} isNotify 是否有导出消息提示 (默认为 true)
|
||||||
* @param {String} fileType 导出的文件格式 (默认为.xlsx)
|
* @param {String} fileType 导出的文件格式 (默认为.xlsx)
|
||||||
* */
|
* */
|
||||||
|
/**
|
||||||
|
* 从 Content-Disposition 头解析文件名
|
||||||
|
*/
|
||||||
|
const getFileNameFromContentDisposition = (contentDisposition: string | undefined | null): string | null => {
|
||||||
|
if (!contentDisposition) return null;
|
||||||
|
|
||||||
|
// 优先匹配 filename*=UTF-8'' 格式(RFC 5987)
|
||||||
|
const filenameStarRegex = /filename\*\s*=\s*UTF-8''([^;]+)/i;
|
||||||
|
const starMatches = filenameStarRegex.exec(contentDisposition);
|
||||||
|
|
||||||
|
if (starMatches && starMatches[1]) {
|
||||||
|
try {
|
||||||
|
return decodeURIComponent(starMatches[1]);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('解码 filename* 失败:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 其次匹配 filename="文件名" 或 filename=文件名 格式
|
||||||
|
const filenameRegex = /filename\s*=\s*([^;]+)/i;
|
||||||
|
const matches = filenameRegex.exec(contentDisposition);
|
||||||
|
|
||||||
|
if (matches && matches[1]) {
|
||||||
|
let filename = matches[1].trim();
|
||||||
|
|
||||||
|
// 去除引号
|
||||||
|
filename = filename.replace(/^["']|["']$/g, "");
|
||||||
|
|
||||||
|
// 尝试解码 URL 编码的文件名
|
||||||
|
try {
|
||||||
|
// 检查是否包含 URL 编码字符
|
||||||
|
if (filename.includes('%') || filename.includes('UTF-8')) {
|
||||||
|
// 处理可能的 UTF-8 前缀
|
||||||
|
if (filename.startsWith("UTF-8''") || filename.startsWith("utf-8''")) {
|
||||||
|
filename = filename.substring(7);
|
||||||
|
}
|
||||||
|
filename = decodeURIComponent(filename);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// 如果解码失败,返回原始文件名
|
||||||
|
console.warn('解码文件名失败,使用原始值:', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
export const useDownload = async (
|
export const useDownload = async (
|
||||||
api: (param: any) => Promise<any>,
|
api: (param: any) => Promise<any>,
|
||||||
tempName: string,
|
tempName: string,
|
||||||
@@ -39,6 +89,73 @@ export const useDownload = async (
|
|||||||
document.body.removeChild(exportFile);
|
document.body.removeChild(exportFile);
|
||||||
window.URL.revokeObjectURL(blobUrl);
|
window.URL.revokeObjectURL(blobUrl);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 支持服务器文件名的下载方法,会从 Content-Disposition 头获取文件名
|
||||||
|
* @param {Function} api 导出表格的api方法 (必传)
|
||||||
|
* @param {String} fallbackName 备用文件名,当服务器未提供时使用 (可选)
|
||||||
|
* @param {Object} params 导出的参数 (默认{})
|
||||||
|
* @param {Boolean} isNotify 是否有导出消息提示 (默认为 true)
|
||||||
|
* @param {String} fallbackFileType 备用文件格式,当服务器未提供时使用 (默认为.xlsx)
|
||||||
|
*/
|
||||||
|
export const useDownloadWithServerFileName = async (
|
||||||
|
api: (param: any) => Promise<AxiosResponse<Blob> | any>,
|
||||||
|
fallbackName: string = "",
|
||||||
|
params: any = {},
|
||||||
|
isNotify: boolean = true,
|
||||||
|
fallbackFileType: string = ".xlsx"
|
||||||
|
) => {
|
||||||
|
if (isNotify) {
|
||||||
|
ElNotification({
|
||||||
|
title: "温馨提示",
|
||||||
|
message: "如果数据庞大会导致下载缓慢哦,请您耐心等待!",
|
||||||
|
type: "info",
|
||||||
|
duration: 3000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await api(params);
|
||||||
|
|
||||||
|
// 检查响应是否包含 data 属性(AxiosResponse)还是直接是 Blob
|
||||||
|
const blob = res.data ? new Blob([res.data]) : new Blob([res]);
|
||||||
|
|
||||||
|
// 尝试从响应头获取文件名(如果存在)
|
||||||
|
let serverFileName: string | null = null;
|
||||||
|
if (res.headers) {
|
||||||
|
const headers = res.headers || {};
|
||||||
|
const contentDisposition = headers['content-disposition'] || headers['Content-Disposition'];
|
||||||
|
serverFileName = getFileNameFromContentDisposition(contentDisposition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确定最终使用的文件名
|
||||||
|
let finalFileName: string;
|
||||||
|
if (serverFileName) {
|
||||||
|
finalFileName = serverFileName;
|
||||||
|
} else if (fallbackName) {
|
||||||
|
finalFileName = `${fallbackName}${fallbackFileType}`;
|
||||||
|
} else {
|
||||||
|
finalFileName = `download${fallbackFileType}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 兼容 edge 不支持 createObjectURL 方法
|
||||||
|
if ("msSaveOrOpenBlob" in navigator) {
|
||||||
|
return window.navigator.msSaveOrOpenBlob(blob, finalFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
const blobUrl = window.URL.createObjectURL(blob);
|
||||||
|
const exportFile = document.createElement("a");
|
||||||
|
exportFile.style.display = "none";
|
||||||
|
exportFile.download = finalFileName;
|
||||||
|
exportFile.href = blobUrl;
|
||||||
|
document.body.appendChild(exportFile);
|
||||||
|
exportFile.click();
|
||||||
|
// 去除下载对 url 的影响
|
||||||
|
document.body.removeChild(exportFile);
|
||||||
|
window.URL.revokeObjectURL(blobUrl);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('文件下载失败:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ export const useTable = (
|
|||||||
});
|
});
|
||||||
dataCallBack && (data = dataCallBack(data));
|
dataCallBack && (data = dataCallBack(data));
|
||||||
state.tableData = isPageable ? data.records : data;
|
state.tableData = isPageable ? data.records : data;
|
||||||
|
//console.log(data);
|
||||||
// 解构后台返回的分页数据 (如果有分页更新分页信息)
|
// 解构后台返回的分页数据 (如果有分页更新分页信息)
|
||||||
if (isPageable) {
|
if (isPageable) {
|
||||||
state.resPageable.total = data.total;
|
state.resPageable.total = data.total;
|
||||||
@@ -85,6 +86,7 @@ export const useTable = (
|
|||||||
state.resPageable.size = data.size;
|
state.resPageable.size = data.size;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
//console.log('123');
|
||||||
requestError && requestError(error);
|
requestError && requestError(error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export const useTheme = () => {
|
|||||||
|
|
||||||
// 修改主题颜色
|
// 修改主题颜色
|
||||||
const changePrimary = (val: string | null) => {
|
const changePrimary = (val: string | null) => {
|
||||||
|
|
||||||
if (!val) {
|
if (!val) {
|
||||||
val = DEFAULT_PRIMARY;
|
val = DEFAULT_PRIMARY;
|
||||||
ElMessage({ type: "success", message: `主题颜色已重置为 ${DEFAULT_PRIMARY}` });
|
ElMessage({ type: "success", message: `主题颜色已重置为 ${DEFAULT_PRIMARY}` });
|
||||||
@@ -40,10 +41,13 @@ export const useTheme = () => {
|
|||||||
for (let i = 1; i <= 9; i++) {
|
for (let i = 1; i <= 9; i++) {
|
||||||
const primaryColor = isDark.value ? `${getDarkColor(val, i / 10)}` : `${getLightColor(val, i / 10)}`;
|
const primaryColor = isDark.value ? `${getDarkColor(val, i / 10)}` : `${getLightColor(val, i / 10)}`;
|
||||||
document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, primaryColor);
|
document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, primaryColor);
|
||||||
|
|
||||||
|
// const colorValue = document.documentElement.style.getPropertyValue(`--el-color-primary-light-${i}`).trim();
|
||||||
|
// console.log(`--el-color-primary-light-${i}: ${colorValue}`);
|
||||||
}
|
}
|
||||||
globalStore.setGlobalState("primary", val);
|
globalStore.setGlobalState("primary", val);
|
||||||
};
|
|
||||||
|
}
|
||||||
// 灰色和弱色切换
|
// 灰色和弱色切换
|
||||||
const changeGreyOrWeak = (type: Theme.GreyOrWeakType, value: boolean) => {
|
const changeGreyOrWeak = (type: Theme.GreyOrWeakType, value: boolean) => {
|
||||||
const body = document.body as HTMLElement;
|
const body = document.body as HTMLElement;
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ export default {
|
|||||||
changePassword: "Change Password",
|
changePassword: "Change Password",
|
||||||
changeMode:"Change Mode",
|
changeMode:"Change Mode",
|
||||||
versionRegister:"Version Register",
|
versionRegister:"Version Register",
|
||||||
|
changeTheme: "Change Theme",
|
||||||
|
changeScene: "Change Scene",
|
||||||
logout: "Logout"
|
logout: "Logout"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ export default {
|
|||||||
changePassword: "修改密码",
|
changePassword: "修改密码",
|
||||||
changeMode:"模式切换",
|
changeMode:"模式切换",
|
||||||
versionRegister:"版本注册",
|
versionRegister:"版本注册",
|
||||||
|
changeTheme:"主题切换",
|
||||||
|
changeScene: "场景切换",
|
||||||
logout: "退出登录"
|
logout: "退出登录"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,63 +1,62 @@
|
|||||||
<!-- 经典布局 -->
|
<!-- 经典布局 -->
|
||||||
<template>
|
<template>
|
||||||
<el-container class="layout">
|
<el-container class="layout">
|
||||||
|
<el-header>
|
||||||
<el-header>
|
<div class="header-lf mask-image">
|
||||||
<div class="header-lf mask-image">
|
<div class="logo flx-center">
|
||||||
<div class="logo flx-center">
|
<img class="logo-img" src="@/assets/images/logo.svg" alt="logo" />
|
||||||
<img class="logo-img" src="@/assets/images/logo.svg" alt="logo" />
|
<span class="logo-text">{{ title }}</span>
|
||||||
<span class="logo-text">{{ title }}</span>
|
</div>
|
||||||
</div>
|
<ToolBarLeft />
|
||||||
<ToolBarLeft />
|
</div>
|
||||||
</div>
|
<div class="header-ri">
|
||||||
<div class="header-ri">
|
<ToolBarRight />
|
||||||
<ToolBarRight />
|
</div>
|
||||||
</div>
|
</el-header>
|
||||||
</el-header>
|
<el-container class="classic-content">
|
||||||
<el-container class="classic-content">
|
<el-aside>
|
||||||
<el-aside>
|
<div class="aside-box" :style="{ width: isCollapse ? '65px' : '210px' }">
|
||||||
<div class="aside-box" :style="{ width: isCollapse ? '65px' : '210px' }">
|
<el-scrollbar>
|
||||||
<el-scrollbar>
|
<el-menu
|
||||||
<el-menu
|
:router="false"
|
||||||
:router="false"
|
:default-active="activeMenu"
|
||||||
:default-active="activeMenu"
|
:collapse="isCollapse"
|
||||||
:collapse="isCollapse"
|
:unique-opened="accordion"
|
||||||
:unique-opened="accordion"
|
:collapse-transition="false"
|
||||||
:collapse-transition="false"
|
>
|
||||||
>
|
<SubMenu :menu-list="menuList" />
|
||||||
<SubMenu :menu-list="menuList" />
|
</el-menu>
|
||||||
</el-menu>
|
</el-scrollbar>
|
||||||
</el-scrollbar>
|
</div>
|
||||||
</div>
|
</el-aside>
|
||||||
</el-aside>
|
<el-container class="classic-main">
|
||||||
<el-container class="classic-main">
|
<Main />
|
||||||
<Main />
|
</el-container>
|
||||||
</el-container>
|
</el-container>
|
||||||
</el-container>
|
</el-container>
|
||||||
</el-container>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="layoutClassic">
|
<script setup lang="ts" name="layoutClassic">
|
||||||
import { computed } from "vue";
|
import { computed } from 'vue'
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from 'vue-router'
|
||||||
import { useAuthStore } from "@/stores/modules/auth";
|
import { useAuthStore } from '@/stores/modules/auth'
|
||||||
import { useGlobalStore } from "@/stores/modules/global";
|
import { useGlobalStore } from '@/stores/modules/global'
|
||||||
import Main from "@/layouts/components/Main/index.vue";
|
import Main from '@/layouts/components/Main/index.vue'
|
||||||
import SubMenu from "@/layouts/components/Menu/SubMenu.vue";
|
import SubMenu from '@/layouts/components/Menu/SubMenu.vue'
|
||||||
import ToolBarLeft from "@/layouts/components/Header/ToolBarLeft.vue";
|
import ToolBarLeft from '@/layouts/components/Header/ToolBarLeft.vue'
|
||||||
import ToolBarRight from "@/layouts/components/Header/ToolBarRight.vue";
|
import ToolBarRight from '@/layouts/components/Header/ToolBarRight.vue'
|
||||||
|
|
||||||
const title = import.meta.env.VITE_GLOB_APP_TITLE;
|
const title = import.meta.env.VITE_GLOB_APP_TITLE
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute()
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore()
|
||||||
const globalStore = useGlobalStore();
|
const globalStore = useGlobalStore()
|
||||||
const accordion = computed(() => globalStore.accordion);
|
const accordion = computed(() => globalStore.accordion)
|
||||||
const isCollapse = computed(() => globalStore.isCollapse);
|
const isCollapse = computed(() => globalStore.isCollapse)
|
||||||
const menuList = computed(() => authStore.showMenuListGet);
|
const menuList = computed(() => authStore.showMenuListGet)
|
||||||
const activeMenu = computed(() => (route.meta.activeMenu ? route.meta.activeMenu : route.path) as string);
|
const activeMenu = computed(() => (route.meta.activeMenu ? route.meta.activeMenu : route.path) as string)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "./index.scss";
|
@use './index.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,103 +1,105 @@
|
|||||||
<!-- 分栏布局 -->
|
<!-- 分栏布局 -->
|
||||||
<template>
|
<template>
|
||||||
<el-container class="layout">
|
<el-container class="layout">
|
||||||
<div class="aside-split">
|
<div class="aside-split">
|
||||||
<div class="logo flx-center">
|
<div class="logo flx-center">
|
||||||
<img class="logo-img" src="@/assets/images/logo.svg" alt="logo" />
|
<img class="logo-img" src="@/assets/images/logo.svg" alt="logo" />
|
||||||
</div>
|
</div>
|
||||||
<el-scrollbar>
|
<el-scrollbar>
|
||||||
<div class="split-list">
|
<div class="split-list">
|
||||||
<div
|
<div
|
||||||
v-for="item in menuList"
|
v-for="item in menuList"
|
||||||
:key="item.path"
|
:key="item.path"
|
||||||
class="split-item"
|
class="split-item"
|
||||||
:class="{ 'split-active': splitActive === item.path || `/${splitActive.split('/')[1]}` === item.path }"
|
:class="{
|
||||||
@click="changeSubMenu(item)"
|
'split-active': splitActive === item.path || `/${splitActive.split('/')[1]}` === item.path
|
||||||
>
|
}"
|
||||||
<el-icon>
|
@click="changeSubMenu(item)"
|
||||||
<component :is="item.meta.icon"></component>
|
>
|
||||||
</el-icon>
|
<el-icon>
|
||||||
<span class="title">{{ item.meta.title }}</span>
|
<component :is="item.meta.icon"></component>
|
||||||
</div>
|
</el-icon>
|
||||||
|
<span class="title">{{ item.meta.title }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
<el-aside :class="{ 'not-aside': !subMenuList.length }" :style="{ width: isCollapse ? '65px' : '210px' }">
|
||||||
</div>
|
<div class="logo flx-center">
|
||||||
<el-aside :class="{ 'not-aside': !subMenuList.length }" :style="{ width: isCollapse ? '65px' : '210px' }">
|
<span v-show="subMenuList.length" class="logo-text">{{ isCollapse ? 'G' : title }}</span>
|
||||||
<div class="logo flx-center">
|
</div>
|
||||||
<span v-show="subMenuList.length" class="logo-text">{{ isCollapse ? "G" : title }}</span>
|
<el-scrollbar>
|
||||||
</div>
|
<el-menu
|
||||||
<el-scrollbar>
|
:router="false"
|
||||||
<el-menu
|
:default-active="activeMenu"
|
||||||
:router="false"
|
:collapse="isCollapse"
|
||||||
:default-active="activeMenu"
|
:unique-opened="accordion"
|
||||||
:collapse="isCollapse"
|
:collapse-transition="false"
|
||||||
:unique-opened="accordion"
|
>
|
||||||
:collapse-transition="false"
|
<SubMenu :menu-list="subMenuList" />
|
||||||
>
|
</el-menu>
|
||||||
<SubMenu :menu-list="subMenuList" />
|
</el-scrollbar>
|
||||||
</el-menu>
|
</el-aside>
|
||||||
</el-scrollbar>
|
<el-container>
|
||||||
</el-aside>
|
<el-header>
|
||||||
<el-container>
|
<ToolBarLeft />
|
||||||
<el-header>
|
<ToolBarRight />
|
||||||
<ToolBarLeft />
|
</el-header>
|
||||||
<ToolBarRight />
|
<Main />
|
||||||
</el-header>
|
</el-container>
|
||||||
<Main />
|
|
||||||
</el-container>
|
</el-container>
|
||||||
</el-container>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="layoutColumns">
|
<script setup lang="ts" name="layoutColumns">
|
||||||
import { ref, computed, watch } from "vue";
|
import { computed, ref, watch } from 'vue'
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { useAuthStore } from "@/stores/modules/auth";
|
import { useAuthStore } from '@/stores/modules/auth'
|
||||||
import { useGlobalStore } from "@/stores/modules/global";
|
import { useGlobalStore } from '@/stores/modules/global'
|
||||||
import Main from "@/layouts/components/Main/index.vue";
|
import Main from '@/layouts/components/Main/index.vue'
|
||||||
import ToolBarLeft from "@/layouts/components/Header/ToolBarLeft.vue";
|
import ToolBarLeft from '@/layouts/components/Header/ToolBarLeft.vue'
|
||||||
import ToolBarRight from "@/layouts/components/Header/ToolBarRight.vue";
|
import ToolBarRight from '@/layouts/components/Header/ToolBarRight.vue'
|
||||||
import SubMenu from "@/layouts/components/Menu/SubMenu.vue";
|
import SubMenu from '@/layouts/components/Menu/SubMenu.vue'
|
||||||
|
|
||||||
const title = import.meta.env.VITE_GLOB_APP_TITLE;
|
const title = import.meta.env.VITE_GLOB_APP_TITLE
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute()
|
||||||
const router = useRouter();
|
const router = useRouter()
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore()
|
||||||
const globalStore = useGlobalStore();
|
const globalStore = useGlobalStore()
|
||||||
const accordion = computed(() => globalStore.accordion);
|
const accordion = computed(() => globalStore.accordion)
|
||||||
const isCollapse = computed(() => globalStore.isCollapse);
|
const isCollapse = computed(() => globalStore.isCollapse)
|
||||||
const menuList = computed(() => authStore.showMenuListGet);
|
const menuList = computed(() => authStore.showMenuListGet)
|
||||||
const activeMenu = computed(() => (route.meta.activeMenu ? route.meta.activeMenu : route.path) as string);
|
const activeMenu = computed(() => (route.meta.activeMenu ? route.meta.activeMenu : route.path) as string)
|
||||||
|
|
||||||
const subMenuList = ref<Menu.MenuOptions[]>([]);
|
const subMenuList = ref<Menu.MenuOptions[]>([])
|
||||||
const splitActive = ref("");
|
const splitActive = ref('')
|
||||||
watch(
|
watch(
|
||||||
() => [menuList, route],
|
() => [menuList, route],
|
||||||
() => {
|
() => {
|
||||||
// 当前菜单没有数据直接 return
|
// 当前菜单没有数据直接 return
|
||||||
if (!menuList.value.length) return;
|
if (!menuList.value.length) return
|
||||||
splitActive.value = route.path;
|
splitActive.value = route.path
|
||||||
const menuItem = menuList.value.filter((item: Menu.MenuOptions) => {
|
const menuItem = menuList.value.filter((item: Menu.MenuOptions) => {
|
||||||
return route.path === item.path || `/${route.path.split("/")[1]}` === item.path;
|
return route.path === item.path || `/${route.path.split('/')[1]}` === item.path
|
||||||
});
|
})
|
||||||
if (menuItem[0].children?.length) return (subMenuList.value = menuItem[0].children);
|
if (menuItem[0].children?.length) return (subMenuList.value = menuItem[0].children)
|
||||||
subMenuList.value = [];
|
subMenuList.value = []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
deep: true,
|
deep: true,
|
||||||
immediate: true
|
immediate: true
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
|
|
||||||
// change SubMenu
|
// change SubMenu
|
||||||
const changeSubMenu = (item: Menu.MenuOptions) => {
|
const changeSubMenu = (item: Menu.MenuOptions) => {
|
||||||
splitActive.value = item.path;
|
splitActive.value = item.path
|
||||||
if (item.children?.length) return (subMenuList.value = item.children);
|
if (item.children?.length) return (subMenuList.value = item.children)
|
||||||
subMenuList.value = [];
|
subMenuList.value = []
|
||||||
router.push(item.path);
|
router.push(item.path)
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "./index.scss";
|
@use './index.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -39,7 +39,9 @@
|
|||||||
}
|
}
|
||||||
.el-menu-item:hover {
|
.el-menu-item:hover {
|
||||||
color: #fff; //一级导航划过颜色
|
color: #fff; //一级导航划过颜色
|
||||||
background-color: #5274a5 !important; //一级导航划过背景色
|
//background-color: #5274a5 !important; //一级导航划过背景色
|
||||||
|
background-color:var(--el-color-primary-light-3) !important;
|
||||||
|
|
||||||
}
|
}
|
||||||
.el-sub-menu__hide-arrow {
|
.el-sub-menu__hide-arrow {
|
||||||
width: 65px;
|
width: 65px;
|
||||||
@@ -47,7 +49,9 @@
|
|||||||
}
|
}
|
||||||
.el-menu-item.is-active {
|
.el-menu-item.is-active {
|
||||||
color: #fff !important; //一级导航文字选中颜色
|
color: #fff !important; //一级导航文字选中颜色
|
||||||
background-color: #5274a5 !important; //一级导航选中背景色
|
//background-color: #5274a5 !important; //一级导航选中背景色
|
||||||
|
background-color: var(--el-color-primary-light-3) !important;
|
||||||
|
|
||||||
border-bottom: 0 !important;
|
border-bottom: 0 !important;
|
||||||
}
|
}
|
||||||
.el-sub-menu__title {
|
.el-sub-menu__title {
|
||||||
@@ -67,7 +71,9 @@
|
|||||||
color: #fff !important; //二级导航文字选中颜色
|
color: #fff !important; //二级导航文字选中颜色
|
||||||
// background-color: var(--el-color-primary) !important;
|
// background-color: var(--el-color-primary) !important;
|
||||||
// background-color: #5274a5 !important;//二级导航选中背景色
|
// background-color: #5274a5 !important;//二级导航选中背景色
|
||||||
background-color: #5274a5 !important;
|
|
||||||
|
//background-color: #7588e5 !important;
|
||||||
|
background-color: var(--el-color-primary-light-3) !important;
|
||||||
border-bottom-color: var(--el-color-primary) !important;
|
border-bottom-color: var(--el-color-primary) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,85 +1,78 @@
|
|||||||
<!-- 横向布局 -->
|
<!-- 横向布局 -->
|
||||||
<template>
|
<template>
|
||||||
<el-container class="layout">
|
<el-container class="layout">
|
||||||
<el-header>
|
<el-header>
|
||||||
<div class="logo flx-center">
|
<div class="logo flx-center">
|
||||||
<!-- <img class="logo-img" src="@/assets/images/logo.svg" alt="logo" /> -->
|
<!-- <img class="logo-img" src="@/assets/images/logo.svg" alt="logo" /> -->
|
||||||
<img
|
<img class="logo-img" src="@/assets/images/cn_pms9100_logo.png" alt="logo" />
|
||||||
class="logo-img"
|
<span class="logo-text">{{ title }}</span>
|
||||||
src="@/assets/images/cn_pms9100_logo.png"
|
</div>
|
||||||
alt="logo"
|
<el-menu v-if="showMenuFlag" trigger="click" mode="horizontal" :router="false" :default-active="activeMenu">
|
||||||
/>
|
<!-- 不能直接使用 SubMenu 组件,无法触发 el-menu 隐藏省略功能 -->
|
||||||
<span class="logo-text">{{ title }} </span>
|
<template v-for="subItem in menuList" :key="subItem.path">
|
||||||
</div>
|
<el-sub-menu
|
||||||
<el-menu v-if="showMenuFlag" trigger="click" mode="horizontal" :router="false" :default-active="activeMenu">
|
v-if="subItem.children?.length"
|
||||||
<!-- 不能直接使用 SubMenu 组件,无法触发 el-menu 隐藏省略功能 -->
|
:key="subItem.path"
|
||||||
<template v-for="subItem in menuList" :key="subItem.path">
|
:index="subItem.path + 'el-sub-menu'"
|
||||||
<el-sub-menu
|
>
|
||||||
v-if="subItem.children?.length"
|
<template #title>
|
||||||
:key="subItem.path"
|
<el-icon>
|
||||||
:index="subItem.path + 'el-sub-menu'"
|
<component :is="subItem.meta.icon"></component>
|
||||||
>
|
</el-icon>
|
||||||
<template #title>
|
<span>{{ subItem.meta.title }}</span>
|
||||||
<el-icon>
|
</template>
|
||||||
<component :is="subItem.meta.icon"></component>
|
<SubMenu :menu-list="subItem.children" />
|
||||||
</el-icon>
|
</el-sub-menu>
|
||||||
<span>{{ subItem.meta.title }}</span>
|
<el-menu-item
|
||||||
</template>
|
v-else
|
||||||
<SubMenu :menu-list="subItem.children" />
|
:key="subItem.path + 'el-menu-item'"
|
||||||
</el-sub-menu>
|
:index="subItem.path"
|
||||||
<el-menu-item
|
@click="handleClickMenu(subItem)"
|
||||||
v-else
|
>
|
||||||
:key="subItem.path + 'el-menu-item'"
|
<el-icon>
|
||||||
:index="subItem.path"
|
<component :is="subItem.meta.icon"></component>
|
||||||
@click="handleClickMenu(subItem)"
|
</el-icon>
|
||||||
>
|
<template #title>
|
||||||
<el-icon>
|
<span>{{ subItem.meta.title }}</span>
|
||||||
<component :is="subItem.meta.icon"></component>
|
</template>
|
||||||
</el-icon>
|
</el-menu-item>
|
||||||
<template #title>
|
</template>
|
||||||
<span>{{ subItem.meta.title }}</span>
|
</el-menu>
|
||||||
</template>
|
<ToolBarRight />
|
||||||
</el-menu-item>
|
</el-header>
|
||||||
</template>
|
<Main />
|
||||||
</el-menu>
|
</el-container>
|
||||||
<ToolBarRight />
|
|
||||||
</el-header>
|
|
||||||
<Main />
|
|
||||||
|
|
||||||
</el-container>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="layoutTransverse">
|
<script setup lang="ts" name="layoutTransverse">
|
||||||
import { computed } from "vue";
|
import { computed } from 'vue'
|
||||||
import { useAuthStore } from "@/stores/modules/auth";
|
import { useAuthStore } from '@/stores/modules/auth'
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import Main from "@/layouts/components/Main/index.vue";
|
import Main from '@/layouts/components/Main/index.vue'
|
||||||
import ToolBarRight from "@/layouts/components/Header/ToolBarRight.vue";
|
import ToolBarRight from '@/layouts/components/Header/ToolBarRight.vue'
|
||||||
import SubMenu from "@/layouts/components/Menu/SubMenu.vue";
|
import SubMenu from '@/layouts/components/Menu/SubMenu.vue'
|
||||||
|
|
||||||
const title = import.meta.env.VITE_GLOB_APP_TITLE;
|
const title = import.meta.env.VITE_GLOB_APP_TITLE
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute()
|
||||||
const router = useRouter();
|
const router = useRouter()
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore()
|
||||||
const menuList = computed(() => authStore.showMenuListGet);
|
const menuList = computed(() => authStore.showMenuListGet)
|
||||||
const showMenuFlag=computed(()=>authStore.showMenuFlagGet)
|
const showMenuFlag = computed(() => authStore.showMenuFlagGet)
|
||||||
const activeMenu = computed(
|
const activeMenu = computed(() => (route.meta.activeMenu ? route.meta.activeMenu : route.path) as string)
|
||||||
() => (route.meta.activeMenu ? route.meta.activeMenu : route.path) as string
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleClickMenu = (subItem: Menu.MenuOptions) => {
|
const handleClickMenu = (subItem: Menu.MenuOptions) => {
|
||||||
if (subItem.meta.isLink) return window.open(subItem.meta.isLink, "_blank");
|
if (subItem.meta.isLink) return window.open(subItem.meta.isLink, '_blank')
|
||||||
router.push(subItem.path);
|
router.push(subItem.path)
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "./index.scss";
|
@use './index.scss';
|
||||||
.logo{
|
.logo {
|
||||||
margin-right: 0 !important;
|
margin-right: 0 !important;
|
||||||
}
|
}
|
||||||
.logo-text {
|
.logo-text {
|
||||||
letter-spacing: 2px;
|
letter-spacing: 2px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,56 +1,56 @@
|
|||||||
<!-- 纵向布局 -->
|
<!-- 纵向布局 -->
|
||||||
<template>
|
<template>
|
||||||
<el-container class="layout">
|
<el-container class="layout">
|
||||||
<el-aside>
|
<el-aside>
|
||||||
<div class="aside-box" :style="{ width: isCollapse ? '65px' : '210px' }">
|
<div class="aside-box" :style="{ width: isCollapse ? '65px' : '210px' }">
|
||||||
<div class="logo flx-center">
|
<div class="logo flx-center">
|
||||||
<img class="logo-img" src="@/assets/images/logo.svg" alt="logo" />
|
<img class="logo-img" src="@/assets/images/logo.svg" alt="logo" />
|
||||||
<span v-show="!isCollapse" class="logo-text">{{ title }}</span>
|
<span v-show="!isCollapse" class="logo-text">{{ title }}</span>
|
||||||
</div>
|
</div>
|
||||||
<el-scrollbar>
|
<el-scrollbar>
|
||||||
<el-menu
|
<el-menu
|
||||||
:router="false"
|
:router="false"
|
||||||
:default-active="activeMenu"
|
:default-active="activeMenu"
|
||||||
:collapse="isCollapse"
|
:collapse="isCollapse"
|
||||||
:unique-opened="accordion"
|
:unique-opened="accordion"
|
||||||
:collapse-transition="false"
|
:collapse-transition="false"
|
||||||
>
|
>
|
||||||
<SubMenu :menu-list="menuList" />
|
<SubMenu :menu-list="menuList" />
|
||||||
</el-menu>
|
</el-menu>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
</el-aside>
|
</el-aside>
|
||||||
<el-container>
|
<el-container>
|
||||||
<el-header>
|
<el-header>
|
||||||
<ToolBarLeft />
|
<ToolBarLeft />
|
||||||
<ToolBarRight />
|
<ToolBarRight />
|
||||||
</el-header>
|
</el-header>
|
||||||
<Main />
|
<Main />
|
||||||
|
</el-container>
|
||||||
</el-container>
|
</el-container>
|
||||||
</el-container>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="layoutVertical">
|
<script setup lang="ts" name="layoutVertical">
|
||||||
import { computed } from "vue";
|
import { computed } from 'vue'
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from 'vue-router'
|
||||||
import { useAuthStore } from "@/stores/modules/auth";
|
import { useAuthStore } from '@/stores/modules/auth'
|
||||||
import { useGlobalStore } from "@/stores/modules/global";
|
import { useGlobalStore } from '@/stores/modules/global'
|
||||||
import Main from "@/layouts/components/Main/index.vue";
|
import Main from '@/layouts/components/Main/index.vue'
|
||||||
import ToolBarLeft from "@/layouts/components/Header/ToolBarLeft.vue";
|
import ToolBarLeft from '@/layouts/components/Header/ToolBarLeft.vue'
|
||||||
import ToolBarRight from "@/layouts/components/Header/ToolBarRight.vue";
|
import ToolBarRight from '@/layouts/components/Header/ToolBarRight.vue'
|
||||||
import SubMenu from "@/layouts/components/Menu/SubMenu.vue";
|
import SubMenu from '@/layouts/components/Menu/SubMenu.vue'
|
||||||
|
|
||||||
const title = import.meta.env.VITE_GLOB_APP_TITLE;
|
const title = import.meta.env.VITE_GLOB_APP_TITLE
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute()
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore()
|
||||||
const globalStore = useGlobalStore();
|
const globalStore = useGlobalStore()
|
||||||
const accordion = computed(() => globalStore.accordion);
|
const accordion = computed(() => globalStore.accordion)
|
||||||
const isCollapse = computed(() => globalStore.isCollapse);
|
const isCollapse = computed(() => globalStore.isCollapse)
|
||||||
const menuList = computed(() => authStore.showMenuListGet);
|
const menuList = computed(() => authStore.showMenuListGet)
|
||||||
const activeMenu = computed(() => (route.meta.activeMenu ? route.meta.activeMenu : route.path) as string);
|
const activeMenu = computed(() => (route.meta.activeMenu ? route.meta.activeMenu : route.path) as string)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "./index.scss";
|
@use './index.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,127 +1,104 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="footer flx-align-center pl10">
|
<div class="footer flx-align-center pl10">
|
||||||
<el-dropdown>
|
<el-dropdown>
|
||||||
<!-- <span class="el-dropdown-link">
|
<!-- <span class="el-dropdown-link">
|
||||||
{{ title }}
|
{{ title }}
|
||||||
<el-icon class="el-icon--right">
|
<el-icon class="el-icon--right">
|
||||||
<arrow-down />
|
<arrow-down />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</span> -->
|
</span> -->
|
||||||
<!-- <el-button dictType="primary"> -->
|
<!-- <el-button dictType="primary"> -->
|
||||||
<div class="change_mode">
|
<div class="change_mode">
|
||||||
{{ title }}
|
{{ title }}
|
||||||
<el-icon class="el-icon--right change_mode_down"
|
<el-icon class="el-icon--right change_mode_down"><arrow-down /></el-icon>
|
||||||
><arrow-down
|
<el-icon class="el-icon--right change_mode_up"><arrow-up /></el-icon>
|
||||||
/></el-icon>
|
</div>
|
||||||
<el-icon class="el-icon--right change_mode_up"><arrow-up /></el-icon>
|
<!-- </el-button> -->
|
||||||
</div>
|
<template #dropdown>
|
||||||
<!-- </el-button> -->
|
<el-dropdown-menu>
|
||||||
<template #dropdown>
|
<el-dropdown-item @click="handelOpen('模拟式')">模拟式模块</el-dropdown-item>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-item @click="handelOpen('数字式')">数字式模块</el-dropdown-item>
|
||||||
<el-dropdown-item @click="handelOpen('模拟式')"
|
<el-dropdown-item @click="handelOpen('比对式')">比对式模块</el-dropdown-item>
|
||||||
>模拟式模块</el-dropdown-item
|
</el-dropdown-menu>
|
||||||
>
|
</template>
|
||||||
<el-dropdown-item @click="handelOpen('数字式')"
|
</el-dropdown>
|
||||||
>数字式模块</el-dropdown-item
|
<p style="margin: 0">
|
||||||
>
|
<a href="http://www.shining-electric.com/" target="_blank">2024 © 南京灿能电力自动化股份有限公司</a>
|
||||||
<el-dropdown-item @click="handelOpen('比对式')"
|
</p>
|
||||||
>比对式模块</el-dropdown-item
|
</div>
|
||||||
>
|
|
||||||
</el-dropdown-menu>
|
|
||||||
</template>
|
|
||||||
</el-dropdown>
|
|
||||||
<p style="margin: 0;">
|
|
||||||
<a href="http://www.shining-electric.com/" target="_blank">
|
|
||||||
2024 © 南京灿能电力自动化股份有限公司
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, reactive, computed, onMounted, watch } from "vue";
|
import { computed } from 'vue'
|
||||||
import { useAuthStore } from "@/stores/modules/auth";
|
import { useAuthStore } from '@/stores/modules/auth'
|
||||||
import { useModeStore } from '@/stores/modules/mode'; // 引入模式 store
|
import { useModeStore } from '@/stores/modules/mode' // 引入模式 store
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from 'vue-router'
|
||||||
const title = ref("模拟式模块");
|
|
||||||
const router = useRouter();
|
|
||||||
const authStore = useAuthStore();
|
|
||||||
const modeStore = useModeStore();
|
|
||||||
|
|
||||||
onMounted(() => {
|
const router = useRouter()
|
||||||
switch (modeStore.currentMode) {
|
const authStore = useAuthStore()
|
||||||
case '模拟式':
|
const modeStore = useModeStore()
|
||||||
title.value = '模拟式模块';
|
|
||||||
break;
|
const title = computed(() => {
|
||||||
case '数字式':
|
return modeStore.currentMode === '' ? '模拟式模块' : modeStore.currentMode + '模块'
|
||||||
title.value = '数字式模块';
|
})
|
||||||
break;
|
|
||||||
case '比对式':
|
|
||||||
title.value = '比对式模块';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
title.value = '模拟式模块';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const handelOpen = async (item: string) => {
|
const handelOpen = async (item: string) => {
|
||||||
await authStore.setShowMenu();
|
await authStore.setShowMenu()
|
||||||
modeStore.setCurrentMode(item); // 将模式code存入 store
|
modeStore.setCurrentMode(item) // 将模式code存入 store
|
||||||
|
|
||||||
//if (router.currentRoute.value.path === '/home/index') {
|
|
||||||
// 强制刷新页面
|
|
||||||
window.location.reload();
|
|
||||||
//} else {
|
|
||||||
// router.push({ path: '/home/index' });
|
|
||||||
//}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
//if (router.currentRoute.value.path === '/home/index') {
|
||||||
|
// 强制刷新页面
|
||||||
|
window.location.reload()
|
||||||
|
//} else {
|
||||||
|
// router.push({ path: '/home/index' });
|
||||||
|
//}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "./index.scss";
|
@use './index.scss';
|
||||||
.footer {
|
.footer {
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: var(--el-color-primary);
|
background-color: var(--el-color-primary);
|
||||||
// .el-button:hover {
|
// .el-button:hover {
|
||||||
// background-color: var(--el-color-primary) !important;
|
// background-color: var(--el-color-primary) !important;
|
||||||
// border: none !important;
|
// border: none !important;
|
||||||
// outline: none !important;
|
// outline: none !important;
|
||||||
// }
|
// }
|
||||||
.change_mode {
|
.change_mode {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: auto;
|
width: auto;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
.change_mode_down {
|
.change_mode_down {
|
||||||
display: block;
|
display: block;
|
||||||
|
}
|
||||||
|
.change_mode_up {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.change_mode_up {
|
.change_mode:hover {
|
||||||
display: none;
|
.change_mode_down {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.change_mode_up {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
.el-dropdown {
|
||||||
.change_mode:hover {
|
z-index: 1001;
|
||||||
.change_mode_down {
|
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
.change_mode_up {
|
p {
|
||||||
display: block;
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
text-align: right;
|
||||||
|
line-height: 40px;
|
||||||
|
a {
|
||||||
|
color: #fff;
|
||||||
|
margin-right: 25px; // 增加右边距
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
.el-dropdown {
|
|
||||||
z-index: 1001;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
text-align: center;
|
|
||||||
line-height: 40px;
|
|
||||||
a {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -18,18 +18,43 @@
|
|||||||
</div>
|
</div>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item @click="openDialog('themeRef')">
|
||||||
|
<el-icon><Sunny /></el-icon>{{ t("header.changeTheme") }}
|
||||||
|
</el-dropdown-item>
|
||||||
<el-dropdown-item @click="openDialog('infoRef')">
|
<el-dropdown-item @click="openDialog('infoRef')">
|
||||||
<el-icon><User /></el-icon>{{ $t("header.personalData") }}
|
<el-icon><User /></el-icon>{{ t("header.personalData") }}
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
<el-dropdown-item @click="openDialog('passwordRef')">
|
<el-dropdown-item @click="openDialog('passwordRef')">
|
||||||
<el-icon><Edit /></el-icon>{{ $t("header.changePassword") }}
|
<el-icon><Edit /></el-icon>{{ t("header.changePassword") }}
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
<el-dropdown-item @click="changeMode">
|
<el-dropdown-item @click="changeMode">
|
||||||
<el-icon><Edit /></el-icon>{{ $t("header.changeMode") }}
|
<el-icon><Switch /></el-icon>{{ t("header.changeMode") }}
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
<el-dropdown-item @click="openDialog('versionRegisterRef')">
|
<el-dropdown-item @click="openDialog('versionRegisterRef')">
|
||||||
<el-icon><SetUp /></el-icon>{{ $t("header.versionRegister") }}
|
<el-icon><SetUp /></el-icon>{{ t("header.versionRegister") }}
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
|
<el-dropdown trigger="hover" placement="left-start" v-if="userStore.userInfo.loginName == 'root'">
|
||||||
|
<div class="custom-dropdown-trigger">
|
||||||
|
<el-icon><Tools /></el-icon>
|
||||||
|
<span>{{ t("header.changeScene") }}</span>
|
||||||
|
</div>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item
|
||||||
|
v-for="item in dictStore.getDictData('app_scene')"
|
||||||
|
:key="item.value"
|
||||||
|
:class="{
|
||||||
|
'custom-dropdown-item': true,
|
||||||
|
active: item.value === appSceneStore.currentScene
|
||||||
|
}"
|
||||||
|
@click="changeScene(item.value?? '')"
|
||||||
|
:disabled = "item.value === appSceneStore.currentScene"
|
||||||
|
>
|
||||||
|
{{ item.name }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
@@ -42,6 +67,9 @@
|
|||||||
<PasswordDialog ref="passwordRef"></PasswordDialog>
|
<PasswordDialog ref="passwordRef"></PasswordDialog>
|
||||||
<!-- versionRegisterDialog -->
|
<!-- versionRegisterDialog -->
|
||||||
<VersionDialog ref="versionRegisterRef"></VersionDialog>
|
<VersionDialog ref="versionRegisterRef"></VersionDialog>
|
||||||
|
<!-- ThemeDialog -->
|
||||||
|
<ThemeDialog ref="themeRef"></ThemeDialog>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -50,12 +78,13 @@ import { LOGIN_URL } from "@/config";
|
|||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import { logoutApi } from "@/api/user/login";
|
import { logoutApi } from "@/api/user/login";
|
||||||
import { useUserStore } from "@/stores/modules/user";
|
import { useUserStore } from "@/stores/modules/user";
|
||||||
import { ElMessageBox, ElMessage } from "element-plus";
|
import { ElMessageBox, ElMessage, CHANGE_EVENT } from "element-plus";
|
||||||
import InfoDialog from "./InfoDialog.vue";
|
import InfoDialog from "./InfoDialog.vue";
|
||||||
import PasswordDialog from "./PasswordDialog.vue";
|
import PasswordDialog from "./PasswordDialog.vue";
|
||||||
|
import ThemeDialog from "./ThemeDialog.vue";
|
||||||
import VersionDialog from "@/views/system/versionRegister/index.vue";
|
import VersionDialog from "@/views/system/versionRegister/index.vue";
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
import { Avatar } from "@element-plus/icons-vue";
|
import { ArrowLeft, Avatar, Delete, Document, Sunny, Switch ,Tools} from "@element-plus/icons-vue";
|
||||||
import AssemblySize from "./components/AssemblySize.vue";
|
import AssemblySize from "./components/AssemblySize.vue";
|
||||||
import Language from "./components/Language.vue";
|
import Language from "./components/Language.vue";
|
||||||
import SearchMenu from "./components/SearchMenu.vue";
|
import SearchMenu from "./components/SearchMenu.vue";
|
||||||
@@ -68,10 +97,21 @@ import { useModeStore,useAppSceneStore } from "@/stores/modules/mode";
|
|||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const dictStore = useDictStore();
|
const dictStore = useDictStore();
|
||||||
const username = computed(() => userStore.userInfo.name);
|
const username = computed(() => userStore.userInfo.name);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
const modeStore = useModeStore();
|
const modeStore = useModeStore();
|
||||||
const AppSceneStore = useAppSceneStore();
|
const AppSceneStore = useAppSceneStore();
|
||||||
|
import { useTheme } from "@/hooks/useTheme";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import {updateScene} from '@/api/system/base/index'
|
||||||
|
|
||||||
|
|
||||||
|
const { changePrimary} = useTheme();
|
||||||
|
|
||||||
|
// 初始化 i18n
|
||||||
|
const { t } = useI18n(); // 使用 t 方法替代 $t
|
||||||
|
|
||||||
// 退出登录
|
// 退出登录
|
||||||
const logout = () => {
|
const logout = () => {
|
||||||
ElMessageBox.confirm("您是否确认退出登录?", "温馨提示", {
|
ElMessageBox.confirm("您是否确认退出登录?", "温馨提示", {
|
||||||
@@ -84,7 +124,8 @@ const logout = () => {
|
|||||||
// 2.清除 Token
|
// 2.清除 Token
|
||||||
userStore.setAccessToken("");
|
userStore.setAccessToken("");
|
||||||
userStore.setRefreshToken("");
|
userStore.setRefreshToken("");
|
||||||
userStore.setUserInfo({name: ""});
|
userStore.setExp(0)
|
||||||
|
userStore.setUserInfo({id: "", name: ""});
|
||||||
userStore.setIsRefreshToken(false)
|
userStore.setIsRefreshToken(false)
|
||||||
dictStore.setDictData([]);
|
dictStore.setDictData([]);
|
||||||
modeStore.setCurrentMode('');
|
modeStore.setCurrentMode('');
|
||||||
@@ -101,12 +142,26 @@ const logout = () => {
|
|||||||
const infoRef = ref<InstanceType<typeof InfoDialog> | null>(null);
|
const infoRef = ref<InstanceType<typeof InfoDialog> | null>(null);
|
||||||
const passwordRef = ref<InstanceType<typeof PasswordDialog> | null>(null);
|
const passwordRef = ref<InstanceType<typeof PasswordDialog> | null>(null);
|
||||||
const versionRegisterRef = ref<InstanceType<typeof VersionDialog> | null>(null);
|
const versionRegisterRef = ref<InstanceType<typeof VersionDialog> | null>(null);
|
||||||
|
const themeRef = ref<InstanceType<typeof ThemeDialog> | null>(null);
|
||||||
const openDialog = (ref: string) => {
|
const openDialog = (ref: string) => {
|
||||||
if (ref == "infoRef") infoRef.value?.openDialog();
|
if (ref == "infoRef") infoRef.value?.openDialog();
|
||||||
if (ref == "passwordRef") passwordRef.value?.openDialog();
|
if (ref == "passwordRef") passwordRef.value?.openDialog();
|
||||||
if (ref == "versionRegisterRef") versionRegisterRef.value?.openDialog();
|
if (ref == "versionRegisterRef") versionRegisterRef.value?.openDialog();
|
||||||
|
if (ref == "themeRef") themeRef.value?.openDialog();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const appSceneStore = useAppSceneStore();
|
||||||
|
|
||||||
|
const changeScene = async (value: string) => {
|
||||||
|
appSceneStore.setCurrentMode(value);
|
||||||
|
await updateScene({scene :dictStore.getDictData('app_scene').find(item => item.value == value)?.id});
|
||||||
|
// 强制刷新页面
|
||||||
|
window.location.reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
//模式切换
|
//模式切换
|
||||||
const changeMode = () => {
|
const changeMode = () => {
|
||||||
authStore.changeModel();
|
authStore.changeModel();
|
||||||
@@ -146,4 +201,18 @@ const changeMode = () => {
|
|||||||
// height: 100%;
|
// height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.custom-dropdown-trigger {
|
||||||
|
padding: 8px 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-dropdown-menu__item.custom-dropdown-item.active),
|
||||||
|
:deep(.el-dropdown-menu__item.custom-dropdown-item.active:hover) {
|
||||||
|
background-color: var(--el-color-primary-light-9) !important;
|
||||||
|
color: var(--el-color-primary)
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog v-model="dialogVisible" title="主题切换" width="500px" draggable>
|
||||||
|
<el-divider content-position="center">主题颜色</el-divider>
|
||||||
|
<div style="display: flex; justify-content: center;">
|
||||||
|
<el-color-picker v-model="color" />
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="dialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="sure">确认</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, ref } from "vue";
|
||||||
|
import { useTheme } from "@/hooks/useTheme";
|
||||||
|
import { on } from "events";
|
||||||
|
const color = ref('')
|
||||||
|
const { changePrimary} = useTheme();
|
||||||
|
|
||||||
|
const dialogVisible = ref(false);
|
||||||
|
const openDialog = () => {
|
||||||
|
// 修复:使用可选链和空值合并运算符确保不会出现 null 或 undefined
|
||||||
|
const storedColor = JSON.parse(localStorage.getItem('cn-global') ?? '{}').primary;
|
||||||
|
color.value = storedColor ?? '#526ADE'; // 默认值为 '#526ADE'
|
||||||
|
dialogVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const sure = () => {
|
||||||
|
changePrimary(color.value); // 切换主题
|
||||||
|
dialogVisible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// onMounted(() => {
|
||||||
|
// // 修复:使用可选链和空值合并运算符确保不会出现 null 或 undefined
|
||||||
|
// const storedColor = JSON.parse(localStorage.getItem('cn-global') ?? '{}').primary;
|
||||||
|
// console.log('123',storedColor)
|
||||||
|
// color.value = storedColor ?? '#526ADE'; // 默认值为 '#526ADE'
|
||||||
|
// })
|
||||||
|
|
||||||
|
defineExpose({ openDialog });
|
||||||
|
</script>
|
||||||
|
|
||||||
@@ -1,84 +1,81 @@
|
|||||||
<template>
|
<template>
|
||||||
<Maximize v-show="maximize" />
|
<Maximize v-show="maximize" />
|
||||||
<Tabs v-if="tabs && showMenuFlag" />
|
<Tabs v-if="tabs && showMenuFlag" />
|
||||||
<el-main>
|
<el-main>
|
||||||
<router-view v-slot="{ Component, route }" style="height:100%;">
|
<router-view v-slot="{ Component, route }" style="height: 100%">
|
||||||
<!-- {{ keepAliveName}} -->
|
<!-- {{ keepAliveName}} -->
|
||||||
<!-- <transition name="slide-right" mode="out-in"> -->
|
<!-- <transition name="slide-right" mode="out-in"> -->
|
||||||
<keep-alive :include="tabsMenuList" >
|
<keep-alive :include="tabsMenuList">
|
||||||
<component :is="Component" :key="route.fullPath" />
|
<component :is="Component" :key="route.fullPath" />
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
<!-- </transition> -->
|
<!-- </transition> -->
|
||||||
</router-view>
|
</router-view>
|
||||||
</el-main>
|
</el-main>
|
||||||
<el-footer>
|
<el-footer>
|
||||||
<Footer />
|
<Footer />
|
||||||
</el-footer>
|
</el-footer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onBeforeUnmount, provide, watch, computed } from "vue";
|
import { computed, onBeforeUnmount, provide, ref, watch } from 'vue'
|
||||||
import { storeToRefs } from "pinia";
|
import { storeToRefs } from 'pinia'
|
||||||
import { useDebounceFn } from "@vueuse/core";
|
import { useDebounceFn } from '@vueuse/core'
|
||||||
import { useGlobalStore } from "@/stores/modules/global";
|
import { useGlobalStore } from '@/stores/modules/global'
|
||||||
import { useKeepAliveStore } from "@/stores/modules/keepAlive";
|
import { useKeepAliveStore } from '@/stores/modules/keepAlive'
|
||||||
import Maximize from "./components/Maximize.vue";
|
import Maximize from './components/Maximize.vue'
|
||||||
import Tabs from "@/layouts/components/Tabs/index.vue";
|
import Tabs from '@/layouts/components/Tabs/index.vue'
|
||||||
import Footer from "@/layouts/components/Footer/index.vue";
|
import Footer from '@/layouts/components/Footer/index.vue'
|
||||||
import { useAuthStore } from "@/stores/modules/auth";
|
import { useAuthStore } from '@/stores/modules/auth'
|
||||||
import { useTabsStore } from '@/stores/modules/tabs'
|
import { useTabsStore } from '@/stores/modules/tabs'
|
||||||
|
|
||||||
const tabStore = useTabsStore()
|
const tabStore = useTabsStore()
|
||||||
const globalStore = useGlobalStore();
|
const globalStore = useGlobalStore()
|
||||||
const tabsMenuList = computed(() => tabStore.tabsMenuList.map(item => item.name))
|
const tabsMenuList = computed(() => tabStore.tabsMenuList.map(item => item.name))
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore()
|
||||||
const { maximize, isCollapse, layout, tabs, footer } = storeToRefs(globalStore);
|
const { maximize, isCollapse, layout, tabs, footer } = storeToRefs(globalStore)
|
||||||
const keepAliveStore = useKeepAliveStore();
|
const keepAliveStore = useKeepAliveStore()
|
||||||
const { keepAliveName } = storeToRefs(keepAliveStore);
|
const { keepAliveName } = storeToRefs(keepAliveStore)
|
||||||
// console.log("🚀 ~ keepAliveName:", keepAliveName)
|
|
||||||
//是否显示导航栏
|
//是否显示导航栏
|
||||||
const showMenuFlag = computed(() => authStore.showMenuFlagGet);
|
const showMenuFlag = computed(() => authStore.showMenuFlagGet)
|
||||||
// 注入刷新页面方法
|
// 注入刷新页面方法
|
||||||
const isRouterShow = ref(true);
|
const isRouterShow = ref(true)
|
||||||
const refreshCurrentPage = (val: boolean) => (isRouterShow.value = val);
|
const refreshCurrentPage = (val: boolean) => (isRouterShow.value = val)
|
||||||
provide("refresh", refreshCurrentPage);
|
provide('refresh', refreshCurrentPage)
|
||||||
|
|
||||||
// 监听当前页面是否最大化,动态添加 class
|
// 监听当前页面是否最大化,动态添加 class
|
||||||
watch(
|
watch(
|
||||||
() => maximize.value,
|
() => maximize.value,
|
||||||
() => {
|
() => {
|
||||||
const app = document.getElementById("app") as HTMLElement;
|
const app = document.getElementById('app') as HTMLElement
|
||||||
if (maximize.value) app.classList.add("main-maximize");
|
if (maximize.value) app.classList.add('main-maximize')
|
||||||
else app.classList.remove("main-maximize");
|
else app.classList.remove('main-maximize')
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
);
|
)
|
||||||
|
|
||||||
// 监听布局变化,在 body 上添加相对应的 layout class
|
// 监听布局变化,在 body 上添加相对应的 layout class
|
||||||
watch(
|
watch(
|
||||||
() => layout.value,
|
() => layout.value,
|
||||||
() => {
|
() => {
|
||||||
const body = document.body as HTMLElement;
|
const body = document.body as HTMLElement
|
||||||
body.setAttribute("class", layout.value);
|
body.setAttribute('class', layout.value)
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
);
|
)
|
||||||
|
|
||||||
|
|
||||||
// 监听窗口大小变化,折叠侧边栏
|
// 监听窗口大小变化,折叠侧边栏
|
||||||
const screenWidth = ref(0);
|
const screenWidth = ref(0)
|
||||||
const listeningWindow = useDebounceFn(() => {
|
const listeningWindow = useDebounceFn(() => {
|
||||||
screenWidth.value = document.body.clientWidth;
|
screenWidth.value = document.body.clientWidth
|
||||||
if (!isCollapse.value && screenWidth.value < 1200)
|
if (!isCollapse.value && screenWidth.value < 1200) globalStore.setGlobalState('isCollapse', true)
|
||||||
globalStore.setGlobalState("isCollapse", true);
|
if (isCollapse.value && screenWidth.value > 1200) globalStore.setGlobalState('isCollapse', false)
|
||||||
if (isCollapse.value && screenWidth.value > 1200)
|
}, 100)
|
||||||
globalStore.setGlobalState("isCollapse", false);
|
window.addEventListener('resize', listeningWindow, false)
|
||||||
}, 100);
|
|
||||||
window.addEventListener("resize", listeningWindow, false);
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener("resize", listeningWindow);
|
window.removeEventListener('resize', listeningWindow)
|
||||||
});
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "./index.scss";
|
@use './index.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -20,27 +20,36 @@
|
|||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { onBeforeMount } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
defineProps<{ menuList: Menu.MenuOptions[] }>();
|
defineProps<{ menuList: Menu.MenuOptions[] }>();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const handleClickMenu = (subItem: Menu.MenuOptions) => {
|
const handleClickMenu = (subItem: Menu.MenuOptions) => {
|
||||||
|
//console.log('1456----------------',subItem);
|
||||||
if (subItem.meta.isLink) return window.open(subItem.meta.isLink, "_blank");
|
if (subItem.meta.isLink) return window.open(subItem.meta.isLink, "_blank");
|
||||||
router.push(subItem.path);
|
router.push(subItem.path);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.el-sub-menu .el-sub-menu__title:hover {
|
.el-sub-menu .el-sub-menu__title:hover {
|
||||||
// color: var(--el-menu-hover-text-color) !important;
|
// color: var(--el-menu-hover-text-color) !important;
|
||||||
// background-color: transparent !important;
|
// background-color: transparent !important;
|
||||||
color: #fff !important;//一级导航文字选中颜色
|
color: #fff !important;//一级导航文字选中颜色
|
||||||
background-color: #5274a5 !important; //一级导航选中背景色
|
//background-color: #5274a5 !important; //一级导航选中背景色
|
||||||
|
|
||||||
|
background-color: var(--el-color-primary-light-3) !important;
|
||||||
|
|
||||||
}
|
}
|
||||||
.el-menu--collapse {
|
.el-menu--collapse {
|
||||||
.is-active {
|
.is-active {
|
||||||
.el-sub-menu__title {
|
.el-sub-menu__title {
|
||||||
color: #ffffff !important;
|
color: #ffffff !important;
|
||||||
// background-color: var(--el-color-primary) !important;
|
// background-color: var(--el-color-primary) !important;
|
||||||
background-color: #5274a5 !important;
|
//background-color: #5274a5 !important;
|
||||||
|
background-color: var(--el-color-primary-light-3) !important;
|
||||||
|
|
||||||
border-bottom: 0 !important;
|
border-bottom: 0 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,7 +62,9 @@ const handleClickMenu = (subItem: Menu.MenuOptions) => {
|
|||||||
// color: var(--el-menu-active-color) !important;
|
// color: var(--el-menu-active-color) !important;
|
||||||
// background-color: var(--el-menu-active-bg-color) !important;
|
// background-color: var(--el-menu-active-bg-color) !important;
|
||||||
color: #fff !important;//一级导航文字选中颜色
|
color: #fff !important;//一级导航文字选中颜色
|
||||||
background-color: #5274a5 !important; //一级导航选中背景色
|
//background-color: #5274a5 !important; //一级导航选中背景色
|
||||||
|
background-color: var(--el-color-primary-light-3) !important;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|||||||
@@ -1,81 +1,88 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dropdown trigger="click" :teleported="false">
|
<el-dropdown trigger="click" :teleported="false">
|
||||||
<div class="more-button">
|
<div class="more-button">
|
||||||
<i :class="'iconfont icon-xiala'"></i>
|
<i :class="'iconfont icon-xiala'"></i>
|
||||||
</div>
|
</div>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<el-dropdown-item @click="refresh">
|
<el-dropdown-item @click="refresh">
|
||||||
<el-icon><Refresh /></el-icon>{{ $t("tabs.refresh") }}
|
<el-icon><Refresh /></el-icon>
|
||||||
</el-dropdown-item>
|
{{ $t('tabs.refresh') }}
|
||||||
<el-dropdown-item @click="maximize">
|
</el-dropdown-item>
|
||||||
<el-icon><FullScreen /></el-icon>{{ $t("tabs.maximize") }}
|
<el-dropdown-item @click="maximize">
|
||||||
</el-dropdown-item>
|
<el-icon><FullScreen /></el-icon>
|
||||||
<el-dropdown-item divided @click="closeCurrentTab">
|
{{ $t('tabs.maximize') }}
|
||||||
<el-icon><Remove /></el-icon>{{ $t("tabs.closeCurrent") }}
|
</el-dropdown-item>
|
||||||
</el-dropdown-item>
|
<el-dropdown-item divided @click="closeCurrentTab">
|
||||||
<el-dropdown-item @click="tabStore.closeTabsOnSide(route.fullPath, 'left')">
|
<el-icon><Remove /></el-icon>
|
||||||
<el-icon><DArrowLeft /></el-icon>{{ $t("tabs.closeLeft") }}
|
{{ $t('tabs.closeCurrent') }}
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
<el-dropdown-item @click="tabStore.closeTabsOnSide(route.fullPath, 'right')">
|
<el-dropdown-item @click="tabStore.closeTabsOnSide(route.fullPath, 'left')">
|
||||||
<el-icon><DArrowRight /></el-icon>{{ $t("tabs.closeRight") }}
|
<el-icon><DArrowLeft /></el-icon>
|
||||||
</el-dropdown-item>
|
{{ $t('tabs.closeLeft') }}
|
||||||
<el-dropdown-item divided @click="tabStore.closeMultipleTab(route.fullPath)">
|
</el-dropdown-item>
|
||||||
<el-icon><CircleClose /></el-icon>{{ $t("tabs.closeOther") }}
|
<el-dropdown-item @click="tabStore.closeTabsOnSide(route.fullPath, 'right')">
|
||||||
</el-dropdown-item>
|
<el-icon><DArrowRight /></el-icon>
|
||||||
<el-dropdown-item @click="closeAllTab">
|
{{ $t('tabs.closeRight') }}
|
||||||
<el-icon><FolderDelete /></el-icon>{{ $t("tabs.closeAll") }}
|
</el-dropdown-item>
|
||||||
</el-dropdown-item>
|
<el-dropdown-item divided @click="tabStore.closeMultipleTab(route.fullPath)">
|
||||||
</el-dropdown-menu>
|
<el-icon><CircleClose /></el-icon>
|
||||||
</template>
|
{{ $t('tabs.closeOther') }}
|
||||||
</el-dropdown>
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click="closeAllTab">
|
||||||
|
<el-icon><FolderDelete /></el-icon>
|
||||||
|
{{ $t('tabs.closeAll') }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { inject, nextTick } from "vue";
|
import { inject, nextTick } from 'vue'
|
||||||
import { HOME_URL } from "@/config";
|
import { HOME_URL } from '@/config'
|
||||||
import { useTabsStore } from "@/stores/modules/tabs";
|
import { useTabsStore } from '@/stores/modules/tabs'
|
||||||
import { useGlobalStore } from "@/stores/modules/global";
|
import { useGlobalStore } from '@/stores/modules/global'
|
||||||
import { useKeepAliveStore } from "@/stores/modules/keepAlive";
|
import { useKeepAliveStore } from '@/stores/modules/keepAlive'
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute()
|
||||||
const router = useRouter();
|
const router = useRouter()
|
||||||
const tabStore = useTabsStore();
|
const tabStore = useTabsStore()
|
||||||
const globalStore = useGlobalStore();
|
const globalStore = useGlobalStore()
|
||||||
const keepAliveStore = useKeepAliveStore();
|
const keepAliveStore = useKeepAliveStore()
|
||||||
|
|
||||||
// refresh current page
|
// refresh current page
|
||||||
const refreshCurrentPage: Function = inject("refresh") as Function;
|
const refreshCurrentPage: Function = inject('refresh') as Function
|
||||||
const refresh = () => {
|
const refresh = () => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
keepAliveStore.removeKeepAliveName(route.name as string);
|
keepAliveStore.removeKeepAliveName(route.name as string)
|
||||||
refreshCurrentPage(false);
|
refreshCurrentPage(false)
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
keepAliveStore.addKeepAliveName(route.name as string);
|
keepAliveStore.addKeepAliveName(route.name as string)
|
||||||
refreshCurrentPage(true);
|
refreshCurrentPage(true)
|
||||||
});
|
})
|
||||||
}, 0);
|
}, 0)
|
||||||
};
|
}
|
||||||
|
|
||||||
// maximize current page
|
// maximize current page
|
||||||
const maximize = () => {
|
const maximize = () => {
|
||||||
globalStore.setGlobalState("maximize", true);
|
globalStore.setGlobalState('maximize', true)
|
||||||
};
|
}
|
||||||
|
|
||||||
// Close Current
|
// Close Current
|
||||||
const closeCurrentTab = () => {
|
const closeCurrentTab = () => {
|
||||||
if (route.meta.isAffix) return;
|
if (route.meta.isAffix) return
|
||||||
tabStore.removeTabs(route.fullPath);
|
tabStore.removeTabs(route.fullPath)
|
||||||
};
|
}
|
||||||
|
|
||||||
// Close All
|
// Close All
|
||||||
const closeAllTab = () => {
|
const closeAllTab = () => {
|
||||||
tabStore.closeMultipleTab();
|
tabStore.closeMultipleTab()
|
||||||
router.push(HOME_URL);
|
router.push(HOME_URL)
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "../index.scss";
|
@use '../index.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,30 +1,35 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class='tabs-box'>
|
<div class="tabs-box">
|
||||||
<div class='tabs-menu'>
|
<div class="tabs-menu">
|
||||||
<el-tabs v-model='tabsMenuValue' type='card' @tab-click='tabClick' @tab-remove='tabRemove'>
|
<el-tabs v-model="tabsMenuValue" type="card" @tab-click="tabClick" @tab-remove="tabRemove">
|
||||||
<el-tab-pane v-for='item in tabsMenuList' :key='item.path' :label='item.title' :name='item.path'
|
<el-tab-pane
|
||||||
:closable='item.close'>
|
v-for="item in tabsMenuList"
|
||||||
<template #label>
|
:key="item.path"
|
||||||
<el-icon v-show='item.icon && tabsIcon' class='tabs-icon'>
|
:label="item.title"
|
||||||
<component :is='item.icon'></component>
|
:name="item.path"
|
||||||
</el-icon>
|
:closable="item.close"
|
||||||
{{ item.title }}
|
>
|
||||||
</template>
|
<template #label>
|
||||||
</el-tab-pane>
|
<el-icon v-show="item.icon && tabsIcon" class="tabs-icon">
|
||||||
</el-tabs>
|
<component :is="item.icon"></component>
|
||||||
<MoreButton />
|
</el-icon>
|
||||||
|
{{ item.title }}
|
||||||
|
</template>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
<MoreButton />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang='ts'>
|
<script setup lang="ts">
|
||||||
import Sortable from 'sortablejs'
|
import Sortable from 'sortablejs'
|
||||||
import { ref, computed, watch, onMounted } from 'vue'
|
import { computed, onMounted, ref, watch } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { useGlobalStore } from '@/stores/modules/global'
|
import { useGlobalStore } from '@/stores/modules/global'
|
||||||
import { useTabsStore } from '@/stores/modules/tabs'
|
import { useTabsStore } from '@/stores/modules/tabs'
|
||||||
import { useAuthStore } from '@/stores/modules/auth'
|
import { useAuthStore } from '@/stores/modules/auth'
|
||||||
import { TabsPaneContext, TabPaneName } from 'element-plus'
|
import { TabPaneName, TabsPaneContext } from 'element-plus'
|
||||||
import MoreButton from './components/MoreButton.vue'
|
import MoreButton from './components/MoreButton.vue'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -38,79 +43,77 @@ const tabsMenuList = computed(() => tabStore.tabsMenuList)
|
|||||||
const tabsIcon = computed(() => globalStore.tabsIcon)
|
const tabsIcon = computed(() => globalStore.tabsIcon)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
tabsDrop()
|
tabsDrop()
|
||||||
initTabs()
|
initTabs()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
// 监听路由的变化(防止浏览器后退/前进不变化 tabsMenuValue)
|
// 监听路由的变化(防止浏览器后退/前进不变化 tabsMenuValue)
|
||||||
watch(
|
watch(
|
||||||
() => route.fullPath,
|
() => route.fullPath,
|
||||||
() => {
|
() => {
|
||||||
if (route.meta.isFull) return
|
if (route.meta.isFull) return
|
||||||
if (route.meta.hideTab){
|
if (route.meta.hideTab) {
|
||||||
tabsMenuValue.value = route.meta.parentPath as string
|
tabsMenuValue.value = route.meta.parentPath as string
|
||||||
}else{
|
} else {
|
||||||
tabsMenuValue.value = route.fullPath
|
tabsMenuValue.value = route.fullPath
|
||||||
const tabsParams = {
|
const tabsParams = {
|
||||||
icon: route.meta.icon as string,
|
icon: route.meta.icon as string,
|
||||||
title: route.meta.title as string,
|
title: route.meta.title as string,
|
||||||
path: route.fullPath,
|
path: route.fullPath,
|
||||||
name: route.name as string,
|
name: route.name as string,
|
||||||
close: !route.meta.isAffix,
|
close: !route.meta.isAffix,
|
||||||
isKeepAlive: route.meta.isKeepAlive as boolean,
|
isKeepAlive: route.meta.isKeepAlive as boolean
|
||||||
}
|
}
|
||||||
tabStore.addTabs(tabsParams)
|
tabStore.addTabs(tabsParams)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ immediate: true },
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
// 初始化需要固定的 tabs
|
// 初始化需要固定的 tabs
|
||||||
const initTabs = () => {
|
const initTabs = () => {
|
||||||
authStore.flatMenuListGet.forEach(item => {
|
authStore.flatMenuListGet.forEach(item => {
|
||||||
if (item.meta.isAffix && !item.meta.isHide && !item.meta.isFull) {
|
if (item.meta.isAffix && !item.meta.isHide && !item.meta.isFull) {
|
||||||
const tabsParams = {
|
const tabsParams = {
|
||||||
icon: item.meta.icon,
|
icon: item.meta.icon,
|
||||||
title: item.meta.title,
|
title: item.meta.title,
|
||||||
path: item.path,
|
path: item.path,
|
||||||
name: item.name,
|
name: item.name,
|
||||||
close: !item.meta.isAffix,
|
close: !item.meta.isAffix,
|
||||||
isKeepAlive: item.meta.isKeepAlive,
|
isKeepAlive: item.meta.isKeepAlive,
|
||||||
unshift:true
|
unshift: true
|
||||||
}
|
}
|
||||||
tabStore.addTabs(tabsParams)
|
tabStore.addTabs(tabsParams)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// tabs 拖拽排序
|
// tabs 拖拽排序
|
||||||
const tabsDrop = () => {
|
const tabsDrop = () => {
|
||||||
Sortable.create(document.querySelector('.el-tabs__nav') as HTMLElement, {
|
Sortable.create(document.querySelector('.el-tabs__nav') as HTMLElement, {
|
||||||
draggable: '.el-tabs__item',
|
draggable: '.el-tabs__item',
|
||||||
animation: 300,
|
animation: 300,
|
||||||
onEnd({ newIndex, oldIndex }) {
|
onEnd({ newIndex, oldIndex }) {
|
||||||
const tabsList = [...tabStore.tabsMenuList]
|
const tabsList = [...tabStore.tabsMenuList]
|
||||||
const currRow = tabsList.splice(oldIndex as number, 1)[0]
|
const currRow = tabsList.splice(oldIndex as number, 1)[0]
|
||||||
tabsList.splice(newIndex as number, 0, currRow)
|
tabsList.splice(newIndex as number, 0, currRow)
|
||||||
tabStore.setTabs(tabsList)
|
tabStore.setTabs(tabsList)
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tab Click
|
// Tab Click
|
||||||
const tabClick = (tabItem: TabsPaneContext) => {
|
const tabClick = (tabItem: TabsPaneContext) => {
|
||||||
const fullPath = tabItem.props.name as string
|
const fullPath = tabItem.props.name as string
|
||||||
// console.log("🚀 ~ tabClick ~ fullPath:", tabItem)
|
router.push(fullPath)
|
||||||
router.push(fullPath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove Tab
|
// Remove Tab
|
||||||
const tabRemove = (fullPath: TabPaneName) => {
|
const tabRemove = (fullPath: TabPaneName) => {
|
||||||
tabStore.removeTabs(fullPath as string, fullPath == route.fullPath)
|
tabStore.removeTabs(fullPath as string, fullPath == route.fullPath || '/machine/testScriptAdd' == route.fullPath)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang='scss'>
|
<style scoped lang="scss">
|
||||||
@import "./index.scss";
|
@use './index.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
.layout-light {
|
.layout-light {
|
||||||
background-color: var(--el-color-primary-light-5);
|
background-color: var(--el-color-primary-light-3);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
.layout-content {
|
.layout-content {
|
||||||
|
|||||||
@@ -1,186 +1,198 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-drawer v-model="drawerVisible" title="布局设置" size="290px">
|
<el-drawer v-model="drawerVisible" title="布局设置" size="290px">
|
||||||
<!-- 布局样式 -->
|
<!-- 布局样式 -->
|
||||||
<el-divider class="divider" content-position="center">
|
<el-divider class="divider" content-position="center">
|
||||||
<el-icon><Notification /></el-icon>
|
<el-icon><Notification /></el-icon>
|
||||||
布局样式
|
布局样式
|
||||||
</el-divider>
|
</el-divider>
|
||||||
<div class="layout-box">
|
<div class="layout-box">
|
||||||
<el-tooltip effect="dark" content="纵向" placement="top" :show-after="200">
|
<el-tooltip effect="dark" content="纵向" placement="top" :show-after="200">
|
||||||
<div :class="['layout-item layout-vertical', { 'is-active': layout == 'vertical' }]" @click="setLayout('vertical')">
|
<div
|
||||||
<div class="layout-dark"></div>
|
:class="['layout-item layout-vertical', { 'is-active': layout == 'vertical' }]"
|
||||||
<div class="layout-container">
|
@click="setLayout('vertical')"
|
||||||
<div class="layout-light"></div>
|
>
|
||||||
<div class="layout-content"></div>
|
<div class="layout-dark"></div>
|
||||||
</div>
|
<div class="layout-container">
|
||||||
<el-icon v-if="layout == 'vertical'">
|
<div class="layout-light"></div>
|
||||||
<CircleCheckFilled />
|
<div class="layout-content"></div>
|
||||||
</el-icon>
|
</div>
|
||||||
|
<el-icon v-if="layout == 'vertical'">
|
||||||
|
<CircleCheckFilled />
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip effect="dark" content="经典" placement="top" :show-after="200">
|
||||||
|
<div
|
||||||
|
:class="['layout-item layout-classic', { 'is-active': layout == 'classic' }]"
|
||||||
|
@click="setLayout('classic')"
|
||||||
|
>
|
||||||
|
<div class="layout-dark"></div>
|
||||||
|
<div class="layout-container">
|
||||||
|
<div class="layout-light"></div>
|
||||||
|
<div class="layout-content"></div>
|
||||||
|
</div>
|
||||||
|
<el-icon v-if="layout == 'classic'">
|
||||||
|
<CircleCheckFilled />
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip effect="dark" content="横向" placement="top" :show-after="200">
|
||||||
|
<div
|
||||||
|
:class="['layout-item layout-transverse', { 'is-active': layout == 'transverse' }]"
|
||||||
|
@click="setLayout('transverse')"
|
||||||
|
>
|
||||||
|
<div class="layout-dark"></div>
|
||||||
|
<div class="layout-content"></div>
|
||||||
|
<el-icon v-if="layout == 'transverse'">
|
||||||
|
<CircleCheckFilled />
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip effect="dark" content="分栏" placement="top" :show-after="200">
|
||||||
|
<div
|
||||||
|
:class="['layout-item layout-columns', { 'is-active': layout == 'columns' }]"
|
||||||
|
@click="setLayout('columns')"
|
||||||
|
>
|
||||||
|
<div class="layout-dark"></div>
|
||||||
|
<div class="layout-light"></div>
|
||||||
|
<div class="layout-content"></div>
|
||||||
|
<el-icon v-if="layout == 'columns'">
|
||||||
|
<CircleCheckFilled />
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</el-tooltip>
|
<div class="theme-item">
|
||||||
<el-tooltip effect="dark" content="经典" placement="top" :show-after="200">
|
<span>
|
||||||
<div :class="['layout-item layout-classic', { 'is-active': layout == 'classic' }]" @click="setLayout('classic')">
|
侧边栏反转色
|
||||||
<div class="layout-dark"></div>
|
<el-tooltip effect="dark" content="侧边栏颜色变为深色模式" placement="top">
|
||||||
<div class="layout-container">
|
<el-icon><QuestionFilled /></el-icon>
|
||||||
<div class="layout-light"></div>
|
</el-tooltip>
|
||||||
<div class="layout-content"></div>
|
</span>
|
||||||
</div>
|
<el-switch v-model="asideInverted" @change="setAsideTheme" />
|
||||||
<el-icon v-if="layout == 'classic'">
|
|
||||||
<CircleCheckFilled />
|
|
||||||
</el-icon>
|
|
||||||
</div>
|
</div>
|
||||||
</el-tooltip>
|
<div class="theme-item mb50">
|
||||||
<el-tooltip effect="dark" content="横向" placement="top" :show-after="200">
|
<span>
|
||||||
<div :class="['layout-item layout-transverse', { 'is-active': layout == 'transverse' }]" @click="setLayout('transverse')">
|
头部反转色
|
||||||
<div class="layout-dark"></div>
|
<el-tooltip effect="dark" content="头部颜色变为深色模式" placement="top">
|
||||||
<div class="layout-content"></div>
|
<el-icon><QuestionFilled /></el-icon>
|
||||||
<el-icon v-if="layout == 'transverse'">
|
</el-tooltip>
|
||||||
<CircleCheckFilled />
|
</span>
|
||||||
</el-icon>
|
<el-switch v-model="headerInverted" @change="setHeaderTheme" />
|
||||||
</div>
|
</div>
|
||||||
</el-tooltip>
|
|
||||||
<el-tooltip effect="dark" content="分栏" placement="top" :show-after="200">
|
|
||||||
<div :class="['layout-item layout-columns', { 'is-active': layout == 'columns' }]" @click="setLayout('columns')">
|
|
||||||
<div class="layout-dark"></div>
|
|
||||||
<div class="layout-light"></div>
|
|
||||||
<div class="layout-content"></div>
|
|
||||||
<el-icon v-if="layout == 'columns'">
|
|
||||||
<CircleCheckFilled />
|
|
||||||
</el-icon>
|
|
||||||
</div>
|
|
||||||
</el-tooltip>
|
|
||||||
</div>
|
|
||||||
<div class="theme-item">
|
|
||||||
<span>
|
|
||||||
侧边栏反转色
|
|
||||||
<el-tooltip effect="dark" content="侧边栏颜色变为深色模式" placement="top">
|
|
||||||
<el-icon><QuestionFilled /></el-icon>
|
|
||||||
</el-tooltip>
|
|
||||||
</span>
|
|
||||||
<el-switch v-model="asideInverted" @change="setAsideTheme" />
|
|
||||||
</div>
|
|
||||||
<div class="theme-item mb50">
|
|
||||||
<span>
|
|
||||||
头部反转色
|
|
||||||
<el-tooltip effect="dark" content="头部颜色变为深色模式" placement="top">
|
|
||||||
<el-icon><QuestionFilled /></el-icon>
|
|
||||||
</el-tooltip>
|
|
||||||
</span>
|
|
||||||
<el-switch v-model="headerInverted" @change="setHeaderTheme" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 全局主题 -->
|
<!-- 全局主题 -->
|
||||||
<el-divider class="divider" content-position="center">
|
<el-divider class="divider" content-position="center">
|
||||||
<el-icon><ColdDrink /></el-icon>
|
<el-icon><ColdDrink /></el-icon>
|
||||||
全局主题
|
全局主题
|
||||||
</el-divider>
|
</el-divider>
|
||||||
<div class="theme-item">
|
<div class="theme-item">
|
||||||
<span>主题颜色</span>
|
<span>主题颜色</span>
|
||||||
<el-color-picker v-model="primary" :predefine="colorList" @change="changePrimary" />
|
<el-color-picker v-model="primary" :predefine="colorList" @change="changePrimary" />
|
||||||
</div>
|
</div>
|
||||||
<div class="theme-item">
|
<div class="theme-item">
|
||||||
<span>暗黑模式</span>
|
<span>暗黑模式</span>
|
||||||
<SwitchDark />
|
<SwitchDark />
|
||||||
</div>
|
</div>
|
||||||
<div class="theme-item">
|
<div class="theme-item">
|
||||||
<span>灰色模式</span>
|
<span>灰色模式</span>
|
||||||
<el-switch v-model="isGrey" @change="changeGreyOrWeak('grey', !!$event)" />
|
<el-switch v-model="isGrey" @change="changeGreyOrWeak('grey', !!$event)" />
|
||||||
</div>
|
</div>
|
||||||
<div class="theme-item mb40">
|
<div class="theme-item mb40">
|
||||||
<span>色弱模式</span>
|
<span>色弱模式</span>
|
||||||
<el-switch v-model="isWeak" @change="changeGreyOrWeak('weak', !!$event)" />
|
<el-switch v-model="isWeak" @change="changeGreyOrWeak('weak', !!$event)" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 界面设置 -->
|
<!-- 界面设置 -->
|
||||||
<el-divider class="divider" content-position="center">
|
<el-divider class="divider" content-position="center">
|
||||||
<el-icon><Setting /></el-icon>
|
<el-icon><Setting /></el-icon>
|
||||||
界面设置
|
界面设置
|
||||||
</el-divider>
|
</el-divider>
|
||||||
<div class="theme-item">
|
<div class="theme-item">
|
||||||
<span>菜单折叠</span>
|
<span>菜单折叠</span>
|
||||||
<el-switch v-model="isCollapse" />
|
<el-switch v-model="isCollapse" />
|
||||||
</div>
|
</div>
|
||||||
<div class="theme-item">
|
<div class="theme-item">
|
||||||
<span>菜单手风琴</span>
|
<span>菜单手风琴</span>
|
||||||
<el-switch v-model="accordion" />
|
<el-switch v-model="accordion" />
|
||||||
</div>
|
</div>
|
||||||
<div class="theme-item">
|
<div class="theme-item">
|
||||||
<span>面包屑</span>
|
<span>面包屑</span>
|
||||||
<el-switch v-model="breadcrumb" />
|
<el-switch v-model="breadcrumb" />
|
||||||
</div>
|
</div>
|
||||||
<div class="theme-item">
|
<div class="theme-item">
|
||||||
<span>面包屑图标</span>
|
<span>面包屑图标</span>
|
||||||
<el-switch v-model="breadcrumbIcon" />
|
<el-switch v-model="breadcrumbIcon" />
|
||||||
</div>
|
</div>
|
||||||
<div class="theme-item">
|
<div class="theme-item">
|
||||||
<span>标签栏</span>
|
<span>标签栏</span>
|
||||||
<el-switch v-model="tabs" />
|
<el-switch v-model="tabs" />
|
||||||
</div>
|
</div>
|
||||||
<div class="theme-item">
|
<div class="theme-item">
|
||||||
<span>标签栏图标</span>
|
<span>标签栏图标</span>
|
||||||
<el-switch v-model="tabsIcon" />
|
<el-switch v-model="tabsIcon" />
|
||||||
</div>
|
</div>
|
||||||
<div class="theme-item">
|
<div class="theme-item">
|
||||||
<span>页脚</span>
|
<span>页脚</span>
|
||||||
<el-switch v-model="footer" />
|
<el-switch v-model="footer" />
|
||||||
</div>
|
</div>
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { ref } from 'vue'
|
||||||
import { storeToRefs } from "pinia";
|
import { storeToRefs } from 'pinia'
|
||||||
import { useTheme } from "@/hooks/useTheme";
|
import { useTheme } from '@/hooks/useTheme'
|
||||||
import { useGlobalStore } from "@/stores/modules/global";
|
import { useGlobalStore } from '@/stores/modules/global'
|
||||||
import { LayoutType } from "@/stores/interface";
|
import { LayoutType } from '@/stores/interface'
|
||||||
import { DEFAULT_PRIMARY } from "@/config";
|
import { DEFAULT_PRIMARY } from '@/config'
|
||||||
import mittBus from "@/utils/mittBus";
|
import mittBus from '@/utils/mittBus'
|
||||||
import SwitchDark from "@/components/SwitchDark/index.vue";
|
import SwitchDark from '@/components/SwitchDark/index.vue'
|
||||||
|
|
||||||
const { changePrimary, changeGreyOrWeak, setAsideTheme, setHeaderTheme } = useTheme();
|
const { changePrimary, changeGreyOrWeak, setAsideTheme, setHeaderTheme } = useTheme()
|
||||||
|
|
||||||
const globalStore = useGlobalStore();
|
const globalStore = useGlobalStore()
|
||||||
const {
|
const {
|
||||||
layout,
|
layout,
|
||||||
primary,
|
primary,
|
||||||
isGrey,
|
isGrey,
|
||||||
isWeak,
|
isWeak,
|
||||||
asideInverted,
|
asideInverted,
|
||||||
headerInverted,
|
headerInverted,
|
||||||
isCollapse,
|
isCollapse,
|
||||||
accordion,
|
accordion,
|
||||||
breadcrumb,
|
breadcrumb,
|
||||||
breadcrumbIcon,
|
breadcrumbIcon,
|
||||||
tabs,
|
tabs,
|
||||||
tabsIcon,
|
tabsIcon,
|
||||||
footer
|
footer
|
||||||
} = storeToRefs(globalStore);
|
} = storeToRefs(globalStore)
|
||||||
|
|
||||||
// 预定义主题颜色
|
// 预定义主题颜色
|
||||||
const colorList = [
|
const colorList = [
|
||||||
DEFAULT_PRIMARY,
|
DEFAULT_PRIMARY,
|
||||||
"#daa96e",
|
'#daa96e',
|
||||||
"#0c819f",
|
'#0c819f',
|
||||||
"#409eff",
|
'#409eff',
|
||||||
"#27ae60",
|
'#27ae60',
|
||||||
"#ff5c93",
|
'#ff5c93',
|
||||||
"#e74c3c",
|
'#e74c3c',
|
||||||
"#fd726d",
|
'#fd726d',
|
||||||
"#f39c12",
|
'#f39c12',
|
||||||
"#9b59b6"
|
'#9b59b6'
|
||||||
];
|
]
|
||||||
|
|
||||||
// 设置布局方式
|
// 设置布局方式
|
||||||
const setLayout = (val: LayoutType) => {
|
const setLayout = (val: LayoutType) => {
|
||||||
globalStore.setGlobalState("layout", val);
|
globalStore.setGlobalState('layout', val)
|
||||||
setAsideTheme();
|
setAsideTheme()
|
||||||
};
|
}
|
||||||
|
|
||||||
// 打开主题设置
|
// 打开主题设置
|
||||||
const drawerVisible = ref(false);
|
const drawerVisible = ref(false)
|
||||||
mittBus.on("openThemeDrawer", () => (drawerVisible.value = true));
|
mittBus.on('openThemeDrawer', () => (drawerVisible.value = true))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import "./index.scss";
|
@use './index.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -65,5 +65,6 @@ const setupAll = async () => {
|
|||||||
|
|
||||||
//挂载app
|
//挂载app
|
||||||
setupAll().then(() => {
|
setupAll().then(() => {
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export const initDynamicRouter = async () => {
|
|||||||
});
|
});
|
||||||
userStore.setAccessToken("");
|
userStore.setAccessToken("");
|
||||||
userStore.setRefreshToken("");
|
userStore.setRefreshToken("");
|
||||||
|
userStore.setExp(0)
|
||||||
router.replace(LOGIN_URL);
|
router.replace(LOGIN_URL);
|
||||||
return Promise.reject("No permission");
|
return Promise.reject("No permission");
|
||||||
}
|
}
|
||||||
@@ -52,6 +53,7 @@ export const initDynamicRouter = async () => {
|
|||||||
// 当按钮 || 菜单请求出错时,重定向到登陆页
|
// 当按钮 || 菜单请求出错时,重定向到登陆页
|
||||||
userStore.setAccessToken("");
|
userStore.setAccessToken("");
|
||||||
userStore.setRefreshToken("");
|
userStore.setRefreshToken("");
|
||||||
|
userStore.setExp(0)
|
||||||
router.replace(LOGIN_URL);
|
router.replace(LOGIN_URL);
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,60 +6,60 @@ export type LanguageType = 'zh' | 'en' | null;
|
|||||||
|
|
||||||
/* GlobalState */
|
/* GlobalState */
|
||||||
export interface GlobalState {
|
export interface GlobalState {
|
||||||
layout: LayoutType;
|
layout: LayoutType;
|
||||||
assemblySize: AssemblySizeType;
|
assemblySize: AssemblySizeType;
|
||||||
language: LanguageType;
|
language: LanguageType;
|
||||||
maximize: boolean;
|
maximize: boolean;
|
||||||
primary: string;
|
primary: string;
|
||||||
isDark: boolean;
|
isDark: boolean;
|
||||||
isGrey: boolean;
|
isGrey: boolean;
|
||||||
isWeak: boolean;
|
isWeak: boolean;
|
||||||
asideInverted: boolean;
|
asideInverted: boolean;
|
||||||
headerInverted: boolean;
|
headerInverted: boolean;
|
||||||
isCollapse: boolean;
|
isCollapse: boolean;
|
||||||
accordion: boolean;
|
accordion: boolean;
|
||||||
breadcrumb: boolean;
|
breadcrumb: boolean;
|
||||||
breadcrumbIcon: boolean;
|
breadcrumbIcon: boolean;
|
||||||
tabs: boolean;
|
tabs: boolean;
|
||||||
tabsIcon: boolean;
|
tabsIcon: boolean;
|
||||||
footer: boolean;
|
footer: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* UserState */
|
/* UserState */
|
||||||
export interface UserState {
|
export interface UserState {
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
refreshToken: string;
|
refreshToken: string;
|
||||||
isRefreshToken: boolean;
|
isRefreshToken: boolean;
|
||||||
userInfo: { name: string };
|
userInfo: { id: string, name: string,loginName:string };
|
||||||
}
|
}
|
||||||
|
|
||||||
/* tabsMenuProps */
|
/* tabsMenuProps */
|
||||||
export interface TabsMenuProps {
|
export interface TabsMenuProps {
|
||||||
icon: string;
|
icon: string;
|
||||||
title: string;
|
title: string;
|
||||||
path: string;
|
path: string;
|
||||||
name: string;
|
name: string;
|
||||||
close: boolean;
|
close: boolean;
|
||||||
isKeepAlive: boolean;
|
isKeepAlive: boolean;
|
||||||
unshift?: boolean;
|
unshift?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TabsState */
|
/* TabsState */
|
||||||
export interface TabsState {
|
export interface TabsState {
|
||||||
tabsMenuList: TabsMenuProps[];
|
tabsMenuList: TabsMenuProps[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* AuthState */
|
/* AuthState */
|
||||||
export interface AuthState {
|
export interface AuthState {
|
||||||
routeName: string;
|
routeName: string;
|
||||||
authButtonList: {
|
authButtonList: {
|
||||||
[key: string]: string[];
|
[key: string]: string[];
|
||||||
};
|
};
|
||||||
authMenuList: Menu.MenuOptions[];
|
authMenuList: Menu.MenuOptions[];
|
||||||
showMenuFlag: boolean;
|
showMenuFlag: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* KeepAliveState */
|
/* KeepAliveState */
|
||||||
export interface KeepAliveState {
|
export interface KeepAliveState {
|
||||||
keepAliveName: string[];
|
keepAliveName: string[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ import {
|
|||||||
} from "@/utils";
|
} from "@/utils";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import { AUTH_STORE_KEY } from "@/stores/constant";
|
import { AUTH_STORE_KEY } from "@/stores/constant";
|
||||||
|
import {useModeStore} from '@/stores/modules/mode'
|
||||||
|
|
||||||
|
|
||||||
export const useAuthStore = defineStore({
|
export const useAuthStore = defineStore({
|
||||||
id: AUTH_STORE_KEY,
|
id: AUTH_STORE_KEY,
|
||||||
state: (): AuthState => ({
|
state: (): AuthState => ({
|
||||||
@@ -43,9 +46,16 @@ export const useAuthStore = defineStore({
|
|||||||
},
|
},
|
||||||
// Get AuthMenuList
|
// Get AuthMenuList
|
||||||
async getAuthMenuList() {
|
async getAuthMenuList() {
|
||||||
const { data } = await getAuthMenuListApi();
|
const modeStore = useModeStore()
|
||||||
this.authMenuList = data;
|
|
||||||
|
|
||||||
|
const { data: menuData } = await getAuthMenuListApi();
|
||||||
|
// 根据不同模式过滤菜单
|
||||||
|
const filteredMenu = modeStore.currentMode === '比对式'
|
||||||
|
? filterMenuByExcludedNames(menuData, ['testSource', 'testScript', 'controlSource'])
|
||||||
|
: filterMenuByExcludedNames(menuData, ['standardDevice']);
|
||||||
|
|
||||||
|
this.authMenuList = filteredMenu;
|
||||||
|
|
||||||
},
|
},
|
||||||
// Set RouteName
|
// Set RouteName
|
||||||
async setRouteName(name: string) {
|
async setRouteName(name: string) {
|
||||||
@@ -73,3 +83,21 @@ export const useAuthStore = defineStore({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用菜单过滤函数
|
||||||
|
* @param menuList 菜单列表
|
||||||
|
* @param excludedNames 需要排除的菜单名称数组
|
||||||
|
* @returns 过滤后的菜单列表
|
||||||
|
*/
|
||||||
|
function filterMenuByExcludedNames(menuList: any[], excludedNames: string[]): any[] {
|
||||||
|
return menuList.filter(menu => {
|
||||||
|
// 如果当前项有 children,递归处理子项
|
||||||
|
if (menu.children && menu.children.length > 0) {
|
||||||
|
menu.children = filterMenuByExcludedNames(menu.children, excludedNames);
|
||||||
|
}
|
||||||
|
// 过滤掉在排除列表中的菜单项
|
||||||
|
return !excludedNames.includes(menu.name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,22 +2,22 @@ import {defineStore} from "pinia";
|
|||||||
import {CHECK_STORE_KEY} from "@/stores/constant";
|
import {CHECK_STORE_KEY} from "@/stores/constant";
|
||||||
import type {CheckData} from "@/api/check/interface";
|
import type {CheckData} from "@/api/check/interface";
|
||||||
import type {Plan} from '@/api/plan/interface'
|
import type {Plan} from '@/api/plan/interface'
|
||||||
|
import {useAppSceneStore} from "@/stores/modules/mode";
|
||||||
|
|
||||||
|
export const useCheckStore = defineStore(CHECK_STORE_KEY, {
|
||||||
export const useCheckStore = defineStore("check", {
|
|
||||||
id: CHECK_STORE_KEY,
|
|
||||||
|
|
||||||
state: () => ({
|
state: () => ({
|
||||||
devices: Array<CheckData.Device>(),
|
devices: [] as CheckData.Device[],
|
||||||
plan: Object<Plan.ResPlan>(),
|
plan: {} as Plan.ResPlan,
|
||||||
selectTestItems: Object<CheckData.SelectTestItem>({preTest: true, timeTest: true, channelsTest: false, test: true}),
|
selectTestItems: {preTest: true, timeTest: false, channelsTest: false, test: true} as CheckData.SelectTestItem,
|
||||||
checkType:1, // 0:手动检测 1:自动检测
|
checkType: 1, // 0:手动检测 1:自动检测
|
||||||
reCheckType: 1, // 0:不合格项复检 1:全部复检
|
reCheckType: 1, // 0:不合格项复检 1:全部复检
|
||||||
showDetailType: 0 // 0:数据查询 1:误差体系跟换
|
showDetailType: 0, // 0:数据查询 1:误差体系跟换 2:正式检测
|
||||||
|
temperature: 0,
|
||||||
|
humidity: 0,
|
||||||
|
chnNumList: [],//连线数据
|
||||||
|
nodesConnectable: true,//设置是能可以连线
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getters: {},
|
getters: {},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
addDevices(device: CheckData.Device[]) {
|
addDevices(device: CheckData.Device[]) {
|
||||||
this.devices.push(...device);
|
this.devices.push(...device);
|
||||||
@@ -29,8 +29,13 @@ export const useCheckStore = defineStore("check", {
|
|||||||
this.devices = [];
|
this.devices = [];
|
||||||
},
|
},
|
||||||
initSelectTestItems() {
|
initSelectTestItems() {
|
||||||
|
const appSceneStore = useAppSceneStore()
|
||||||
this.selectTestItems.preTest = true
|
this.selectTestItems.preTest = true
|
||||||
this.selectTestItems.channelsTest = false
|
if (appSceneStore.currentScene === '1') {
|
||||||
|
this.selectTestItems.channelsTest = true
|
||||||
|
} else {
|
||||||
|
this.selectTestItems.channelsTest = false
|
||||||
|
}
|
||||||
this.selectTestItems.test = true
|
this.selectTestItems.test = true
|
||||||
},
|
},
|
||||||
setSelectTestItems(selectTestItems: CheckData.SelectTestItem) {
|
setSelectTestItems(selectTestItems: CheckData.SelectTestItem) {
|
||||||
@@ -44,6 +49,19 @@ export const useCheckStore = defineStore("check", {
|
|||||||
},
|
},
|
||||||
setShowDetailType(showDetailType: number) {
|
setShowDetailType(showDetailType: number) {
|
||||||
this.showDetailType = showDetailType
|
this.showDetailType = showDetailType
|
||||||
}
|
},
|
||||||
|
setTemperature(temperature: number) {
|
||||||
|
this.temperature = temperature
|
||||||
|
},
|
||||||
|
setHumidity(humidity: number) {
|
||||||
|
this.humidity = humidity
|
||||||
|
},
|
||||||
|
setChnNum(chnNumList: string[]) {
|
||||||
|
this.chnNumList = chnNumList
|
||||||
|
},
|
||||||
|
setNodesConnectable(nodesConnectable: boolean) {
|
||||||
|
this.nodesConnectable = nodesConnectable
|
||||||
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import { GlobalState } from "@/stores/interface";
|
import { type GlobalState } from "@/stores/interface";
|
||||||
import { DEFAULT_PRIMARY } from "@/config";
|
import { DEFAULT_PRIMARY} from "@/config";
|
||||||
import piniaPersistConfig from "@/stores/helper/persist";
|
import piniaPersistConfig from "@/stores/helper/persist";
|
||||||
import {GLOBAL_STORE_KEY} from "@/stores/constant";
|
import {GLOBAL_STORE_KEY} from "@/stores/constant";
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ export const useGlobalStore = defineStore({
|
|||||||
// 折叠菜单
|
// 折叠菜单
|
||||||
isCollapse: false,
|
isCollapse: false,
|
||||||
// 菜单手风琴
|
// 菜单手风琴
|
||||||
accordion: true,
|
accordion: false,
|
||||||
// 面包屑导航
|
// 面包屑导航
|
||||||
breadcrumb: true,
|
breadcrumb: true,
|
||||||
// 面包屑导航图标
|
// 面包屑导航图标
|
||||||
@@ -42,6 +42,7 @@ export const useGlobalStore = defineStore({
|
|||||||
tabsIcon: true,
|
tabsIcon: true,
|
||||||
// 页脚
|
// 页脚
|
||||||
footer: false
|
footer: false
|
||||||
|
|
||||||
}),
|
}),
|
||||||
getters: {},
|
getters: {},
|
||||||
actions: {
|
actions: {
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ export const useUserStore = defineStore({
|
|||||||
accessToken: "",
|
accessToken: "",
|
||||||
refreshToken: "",
|
refreshToken: "",
|
||||||
isRefreshToken:false,
|
isRefreshToken:false,
|
||||||
userInfo: { name: "admin" },
|
exp: Number(0),
|
||||||
|
userInfo: {id:"", name: "" ,loginName:""},
|
||||||
}),
|
}),
|
||||||
getters: {},
|
getters: {},
|
||||||
actions: {
|
actions: {
|
||||||
@@ -27,6 +28,9 @@ export const useUserStore = defineStore({
|
|||||||
setUserInfo(userInfo: UserState["userInfo"]) {
|
setUserInfo(userInfo: UserState["userInfo"]) {
|
||||||
this.userInfo = userInfo;
|
this.userInfo = userInfo;
|
||||||
},
|
},
|
||||||
|
setExp(exp: number) {
|
||||||
|
this.exp = exp;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
persist: piniaPersistConfig(USER_STORE_KEY),
|
persist: piniaPersistConfig(USER_STORE_KEY),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -445,7 +445,7 @@
|
|||||||
|
|
||||||
.dialog-small {
|
.dialog-small {
|
||||||
.el-dialog__body {
|
.el-dialog__body {
|
||||||
max-height: 340px;
|
max-height: 350px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -578,6 +578,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.form-four {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
// justify-content: space-between;
|
||||||
|
.el-form-item {
|
||||||
|
display: flex;
|
||||||
|
width: 24%;
|
||||||
|
|
||||||
|
.el-form-item__content {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.el-select,
|
||||||
|
.el-cascader,
|
||||||
|
.el-input__inner,
|
||||||
|
.el-date-editor {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.el-table__cell {
|
.el-table__cell {
|
||||||
border-right: 1px solid #ebeef5 !important;
|
border-right: 1px solid #ebeef5 !important;
|
||||||
|
|||||||
@@ -140,3 +140,9 @@ body,
|
|||||||
:-webkit-any(article, aside, nav, section) h1 {
|
:-webkit-any(article, aside, nav, section) h1 {
|
||||||
font-size: 2em;
|
font-size: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@import 'https://cdn.jsdelivr.net/npm/@vue-flow/core@1.45.0/dist/style.css';
|
||||||
|
@import 'https://cdn.jsdelivr.net/npm/@vue-flow/core@1.45.0/dist/theme-default.css';
|
||||||
|
@import 'https://cdn.jsdelivr.net/npm/@vue-flow/controls@latest/dist/style.css';
|
||||||
|
@import 'https://cdn.jsdelivr.net/npm/@vue-flow/minimap@latest/dist/style.css';
|
||||||
|
@import 'https://cdn.jsdelivr.net/npm/@vue-flow/node-resizer@latest/dist/style.css';
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export function rgbToHex(r: any, g: any, b: any) {
|
|||||||
* @returns {String} 返回处理后的颜色值
|
* @returns {String} 返回处理后的颜色值
|
||||||
*/
|
*/
|
||||||
export function getDarkColor(color: string, level: number) {
|
export function getDarkColor(color: string, level: number) {
|
||||||
|
|
||||||
let reg = /^\#?[0-9A-Fa-f]{6}$/;
|
let reg = /^\#?[0-9A-Fa-f]{6}$/;
|
||||||
if (!reg.test(color)) return ElMessage.warning("输入错误的hex颜色值");
|
if (!reg.test(color)) return ElMessage.warning("输入错误的hex颜色值");
|
||||||
let rgb = hexToRgb(color);
|
let rgb = hexToRgb(color);
|
||||||
@@ -51,6 +52,7 @@ export function getDarkColor(color: string, level: number) {
|
|||||||
* @returns {String} 返回处理后的颜色值
|
* @returns {String} 返回处理后的颜色值
|
||||||
*/
|
*/
|
||||||
export function getLightColor(color: string, level: number) {
|
export function getLightColor(color: string, level: number) {
|
||||||
|
|
||||||
let reg = /^\#?[0-9A-Fa-f]{6}$/;
|
let reg = /^\#?[0-9A-Fa-f]{6}$/;
|
||||||
if (!reg.test(color)) return ElMessage.warning("输入错误的hex颜色值");
|
if (!reg.test(color)) return ElMessage.warning("输入错误的hex颜色值");
|
||||||
let rgb = hexToRgb(color);
|
let rgb = hexToRgb(color);
|
||||||
|
|||||||
234
frontend/src/utils/jwtUtil.ts
Normal file
234
frontend/src/utils/jwtUtil.ts
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
import { useUserStore } from "@/stores/modules/user";
|
||||||
|
|
||||||
|
// JWT Token解析后的载荷接口
|
||||||
|
export interface JwtPayload {
|
||||||
|
userId?: string;
|
||||||
|
loginName?: string;
|
||||||
|
exp?: number;
|
||||||
|
iat?: number;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token信息摘要接口
|
||||||
|
export interface TokenInfo {
|
||||||
|
userId: string | null;
|
||||||
|
loginName: string | null;
|
||||||
|
expiration: string | null;
|
||||||
|
isExpired: boolean;
|
||||||
|
remainingTime: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JWT工具类
|
||||||
|
* 提供JWT token的解析、验证等功能
|
||||||
|
*/
|
||||||
|
export class JwtUtil {
|
||||||
|
/**
|
||||||
|
* Base64URL解码
|
||||||
|
* @param str Base64URL编码的字符串
|
||||||
|
*/
|
||||||
|
private static base64UrlDecode(str: string): string {
|
||||||
|
try {
|
||||||
|
// Base64URL转Base64
|
||||||
|
let base64 = str.replace(/-/g, '+').replace(/_/g, '/');
|
||||||
|
|
||||||
|
// 补齐padding
|
||||||
|
while (base64.length % 4) {
|
||||||
|
base64 += '=';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base64解码
|
||||||
|
const decoded = atob(base64);
|
||||||
|
|
||||||
|
// 处理UTF-8编码
|
||||||
|
return decodeURIComponent(
|
||||||
|
decoded
|
||||||
|
.split('')
|
||||||
|
.map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
|
||||||
|
.join('')
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error('Base64URL解码失败: ' + error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析JWT Token获取载荷信息
|
||||||
|
* @param token JWT token字符串,如果不传则从store中获取
|
||||||
|
*/
|
||||||
|
static parseToken(token?: string): JwtPayload | null {
|
||||||
|
try {
|
||||||
|
let targetToken = token;
|
||||||
|
|
||||||
|
// 如果没有传入token,从store中获取
|
||||||
|
if (!targetToken) {
|
||||||
|
const userStore = useUserStore();
|
||||||
|
targetToken = userStore.accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetToken) {
|
||||||
|
console.warn('Token不存在');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// JWT token由三部分组成,用.分割:header.payload.signature
|
||||||
|
const parts = targetToken.split('.');
|
||||||
|
if (parts.length !== 3) {
|
||||||
|
console.error('无效的JWT token格式');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解码payload部分(第二部分)
|
||||||
|
const payload = parts[1];
|
||||||
|
const decodedPayload = this.base64UrlDecode(payload);
|
||||||
|
const tokenData: JwtPayload = JSON.parse(decodedPayload);
|
||||||
|
|
||||||
|
return tokenData;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('解析JWT Token失败:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定字段的值
|
||||||
|
* @param field 字段名
|
||||||
|
* @param token JWT token字符串,可选
|
||||||
|
*/
|
||||||
|
static getField<T = any>(field: string, token?: string): T | null {
|
||||||
|
const tokenData = this.parseToken(token);
|
||||||
|
return tokenData?.[field] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户ID
|
||||||
|
* @param token JWT token字符串,可选
|
||||||
|
*/
|
||||||
|
static getUserId(token?: string): string | null {
|
||||||
|
return this.getField<string>('userId', token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取登录名
|
||||||
|
* @param token JWT token字符串,可选
|
||||||
|
*/
|
||||||
|
static getLoginName(token?: string): string | null {
|
||||||
|
return this.getField<string>('loginName', token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Token过期时间戳(秒)
|
||||||
|
* @param token JWT token字符串,可选
|
||||||
|
*/
|
||||||
|
static getExpiration(token?: string): number | null {
|
||||||
|
return this.getField<number>('exp', token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Token签发时间戳(秒)
|
||||||
|
* @param token JWT token字符串,可选
|
||||||
|
*/
|
||||||
|
static getIssuedAt(token?: string): number | null {
|
||||||
|
return this.getField<number>('iat', token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查Token是否过期
|
||||||
|
* @param token JWT token字符串,可选
|
||||||
|
*/
|
||||||
|
static isExpired(token?: string): boolean {
|
||||||
|
const exp = this.getExpiration(token);
|
||||||
|
if (!exp) return true;
|
||||||
|
|
||||||
|
// JWT中的exp是秒级时间戳,需要转换为毫秒
|
||||||
|
const expTime = exp * 1000;
|
||||||
|
return Date.now() >= expTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Token剩余有效时间(毫秒)
|
||||||
|
* @param token JWT token字符串,可选
|
||||||
|
*/
|
||||||
|
static getRemainingTime(token?: string): number {
|
||||||
|
const exp = this.getExpiration(token);
|
||||||
|
if (!exp) return 0;
|
||||||
|
|
||||||
|
const expTime = exp * 1000;
|
||||||
|
const remaining = expTime - Date.now();
|
||||||
|
return Math.max(0, remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化剩余时间为可读字符串
|
||||||
|
* @param ms 毫秒数
|
||||||
|
*/
|
||||||
|
static formatRemainingTime(ms: number): string {
|
||||||
|
if (ms <= 0) return '已过期';
|
||||||
|
|
||||||
|
const seconds = Math.floor(ms / 1000);
|
||||||
|
const minutes = Math.floor(seconds / 60);
|
||||||
|
const hours = Math.floor(minutes / 60);
|
||||||
|
const days = Math.floor(hours / 24);
|
||||||
|
|
||||||
|
if (days > 0) return `${days}天 ${hours % 24}小时`;
|
||||||
|
if (hours > 0) return `${hours}小时 ${minutes % 60}分钟`;
|
||||||
|
if (minutes > 0) return `${minutes}分钟 ${seconds % 60}秒`;
|
||||||
|
return `${seconds}秒`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取完整的Token信息摘要
|
||||||
|
* @param token JWT token字符串,可选
|
||||||
|
*/
|
||||||
|
static getTokenInfo(token?: string): TokenInfo {
|
||||||
|
const exp = this.getExpiration(token);
|
||||||
|
const remainingMs = this.getRemainingTime(token);
|
||||||
|
|
||||||
|
return {
|
||||||
|
userId: this.getUserId(token),
|
||||||
|
loginName: this.getLoginName(token),
|
||||||
|
expiration: exp ? new Date(exp * 1000).toLocaleString() : null,
|
||||||
|
isExpired: this.isExpired(token),
|
||||||
|
remainingTime: this.formatRemainingTime(remainingMs)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证Token是否有效(格式正确且未过期)
|
||||||
|
* @param token JWT token字符串,可选
|
||||||
|
*/
|
||||||
|
static isValid(token?: string): boolean {
|
||||||
|
const tokenData = this.parseToken(token);
|
||||||
|
return tokenData !== null && !this.isExpired(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Token的头部信息
|
||||||
|
* @param token JWT token字符串,可选
|
||||||
|
*/
|
||||||
|
static getHeader(token?: string): any | null {
|
||||||
|
try {
|
||||||
|
let targetToken = token;
|
||||||
|
|
||||||
|
if (!targetToken) {
|
||||||
|
const userStore = useUserStore();
|
||||||
|
targetToken = userStore.accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetToken) return null;
|
||||||
|
|
||||||
|
const parts = targetToken.split('.');
|
||||||
|
if (parts.length !== 3) return null;
|
||||||
|
|
||||||
|
const header = parts[0];
|
||||||
|
const decodedHeader = this.base64UrlDecode(header);
|
||||||
|
return JSON.parse(decodedHeader);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('解析JWT Header失败:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出单例方法,方便直接调用
|
||||||
|
export const jwtUtil = JwtUtil;
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import mitt from "mitt";
|
import mitt from "mitt";
|
||||||
|
|
||||||
const mittBus = mitt();
|
const mittBus = mitt();
|
||||||
|
|
||||||
export default mittBus;
|
export default mittBus;
|
||||||
|
|||||||
@@ -1,183 +1,697 @@
|
|||||||
import {ElMessage} from "element-plus";
|
/**
|
||||||
|
* WebSocket客户端服务
|
||||||
|
* 提供WebSocket连接管理、心跳机制、消息处理等功能
|
||||||
|
* 集成JWT token解析,支持自动获取用户登录名
|
||||||
|
*
|
||||||
|
* @author hongawen
|
||||||
|
* @version 2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ElMessage } from "element-plus";
|
||||||
|
import { jwtUtil } from "./jwtUtil";
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 类型定义 (Types & Interfaces)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebSocket消息接口定义(对应后端WebSocketVO结构)
|
||||||
|
*/
|
||||||
|
interface WebSocketMessage<T = any> {
|
||||||
|
type: string; // 消息类型
|
||||||
|
requestId?: string; // 请求ID
|
||||||
|
operateCode?: string; // 操作代码
|
||||||
|
code?: number; // 状态码
|
||||||
|
desc?: string; // 描述信息
|
||||||
|
data?: T; // 泛型数据
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回调函数类型定义
|
||||||
|
*/
|
||||||
|
type CallbackFunction<T = any> = (message: WebSocketMessage<T>) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebSocket配置接口
|
||||||
|
*/
|
||||||
|
interface SocketConfig {
|
||||||
|
url: string; // WebSocket服务器地址
|
||||||
|
heartbeatInterval?: number; // 心跳间隔时间(ms)
|
||||||
|
reconnectDelay?: number; // 重连延迟时间(ms)
|
||||||
|
maxReconnectAttempts?: number; // 最大重连次数
|
||||||
|
timeout?: number; // 超时时间(ms)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接状态枚举
|
||||||
|
*/
|
||||||
|
enum ConnectionStatus {
|
||||||
|
DISCONNECTED = 'disconnected', // 未连接
|
||||||
|
CONNECTING = 'connecting', // 连接中
|
||||||
|
CONNECTED = 'connected', // 已连接
|
||||||
|
RECONNECTING = 'reconnecting', // 重连中
|
||||||
|
ERROR = 'error' // 连接错误
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 常用的WebSocket消息类型定义命名空间
|
||||||
|
*/
|
||||||
|
namespace WebSocketMessageTypes {
|
||||||
|
/**
|
||||||
|
* 预检测相关消息
|
||||||
|
*/
|
||||||
|
export interface PreTestMessage {
|
||||||
|
deviceId?: string;
|
||||||
|
status?: string;
|
||||||
|
progress?: number;
|
||||||
|
errorInfo?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系数校准相关消息
|
||||||
|
*/
|
||||||
|
export interface CoefficientMessage {
|
||||||
|
deviceId: string;
|
||||||
|
channel: number;
|
||||||
|
voltage?: string;
|
||||||
|
current?: string;
|
||||||
|
calibrationResult?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正式检测相关消息
|
||||||
|
*/
|
||||||
|
export interface TestMessage {
|
||||||
|
deviceId: string;
|
||||||
|
testType: string;
|
||||||
|
testResult?: 'success' | 'failed' | 'processing';
|
||||||
|
testData?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用响应消息
|
||||||
|
*/
|
||||||
|
export interface CommonResponse {
|
||||||
|
success: boolean;
|
||||||
|
message?: string;
|
||||||
|
timestamp?: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 导出类型
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
export type {
|
||||||
|
WebSocketMessage,
|
||||||
|
CallbackFunction,
|
||||||
|
SocketConfig,
|
||||||
|
WebSocketMessageTypes
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
ConnectionStatus
|
||||||
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// 主要服务类
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebSocket服务类
|
||||||
|
* 单例模式实现,提供完整的WebSocket连接管理功能
|
||||||
|
*/
|
||||||
export default class SocketService {
|
export default class SocketService {
|
||||||
static instance = null;
|
// ========================================================================
|
||||||
static get Instance() {
|
// 静态属性和方法 (Static)
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单例实例
|
||||||
|
*/
|
||||||
|
private static instance: SocketService | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取单例实例
|
||||||
|
* @returns SocketService实例
|
||||||
|
*/
|
||||||
|
static get Instance(): SocketService {
|
||||||
if (!this.instance) {
|
if (!this.instance) {
|
||||||
this.instance = new SocketService();
|
this.instance = new SocketService();
|
||||||
}
|
}
|
||||||
return this.instance;
|
return this.instance;
|
||||||
}
|
}
|
||||||
// 和服务端连接的socket对象
|
|
||||||
ws = null;
|
|
||||||
// 存储回调函数
|
|
||||||
callBackMapping = {};
|
|
||||||
// 标识是否连接成功
|
|
||||||
connected = false;
|
|
||||||
// 记录重试的次数
|
|
||||||
sendRetryCount = 0;
|
|
||||||
// 重新连接尝试的次数
|
|
||||||
connectRetryCount = 0;
|
|
||||||
work:any;
|
|
||||||
lastActivityTime= 0; // 上次活动时间戳
|
|
||||||
lastResponseHeartTime = Date.now();//最后一次收到心跳回复时间
|
|
||||||
|
|
||||||
reconnectDelay= 5000; // 重新连接延迟,单位毫秒
|
// ========================================================================
|
||||||
|
// 实例属性 (Properties)
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
// 定义连接服务器的方法
|
/**
|
||||||
connect() {
|
* WebSocket连接实例
|
||||||
|
*/
|
||||||
|
private ws: WebSocket | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息回调函数映射表
|
||||||
|
*/
|
||||||
|
private callBackMapping: Record<string, CallbackFunction<any>> = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前连接状态
|
||||||
|
*/
|
||||||
|
private connectionStatus: ConnectionStatus = ConnectionStatus.DISCONNECTED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送消息重试计数器
|
||||||
|
*/
|
||||||
|
private sendRetryCount: number = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接重试计数器
|
||||||
|
*/
|
||||||
|
private connectRetryCount: number = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 心跳Worker实例
|
||||||
|
*/
|
||||||
|
private heartbeatWorker: Worker | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Worker脚本的Blob URL
|
||||||
|
*/
|
||||||
|
private workerBlobUrl: string | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最后一次收到心跳响应的时间戳
|
||||||
|
*/
|
||||||
|
private lastResponseHeartTime: number = Date.now();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebSocket连接配置
|
||||||
|
*/
|
||||||
|
private config: SocketConfig = {
|
||||||
|
// url: 'ws://127.0.0.1:7777/hello',
|
||||||
|
url: 'ws://192.168.1.124:7777/hello',
|
||||||
|
heartbeatInterval: 9000, // 9秒心跳间隔
|
||||||
|
reconnectDelay: 5000, // 5秒重连延迟
|
||||||
|
maxReconnectAttempts: 5, // 最多重连5次
|
||||||
|
timeout: 30000 // 30秒超时
|
||||||
|
};
|
||||||
|
|
||||||
// 连接服务器
|
// ========================================================================
|
||||||
|
// 构造函数 (Constructor)
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 私有构造函数,防止外部直接实例化
|
||||||
|
*/
|
||||||
|
private constructor() {
|
||||||
|
this.initializeProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化属性
|
||||||
|
*/
|
||||||
|
private initializeProperties(): void {
|
||||||
|
this.lastResponseHeartTime = Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// Getter属性 (Computed)
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取连接状态
|
||||||
|
* @returns 是否已连接
|
||||||
|
*/
|
||||||
|
get connected(): boolean {
|
||||||
|
return this.connectionStatus === ConnectionStatus.CONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// 公共方法 (Public Methods)
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置WebSocket连接参数
|
||||||
|
* @param config 部分配置对象
|
||||||
|
*/
|
||||||
|
public configure(config: Partial<SocketConfig>): void {
|
||||||
|
this.config = { ...this.config, ...config };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接WebSocket服务器(同步方式,保持向后兼容)
|
||||||
|
*/
|
||||||
|
public connect(): Promise<void> | void {
|
||||||
|
// 检查浏览器支持
|
||||||
if (!window.WebSocket) {
|
if (!window.WebSocket) {
|
||||||
return console.log('您的浏览器不支持WebSocket');
|
console.log('您的浏览器不支持WebSocket');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// let token = $.cookie('123');
|
// 防止重复连接
|
||||||
// let token = '4E6EF539AAF119D82AC4C2BC84FBA21F';
|
if (this.connectionStatus === ConnectionStatus.CONNECTING || this.connected) {
|
||||||
|
console.warn('WebSocket已连接或正在连接中');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.connectionStatus = ConnectionStatus.CONNECTING;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.ws = new WebSocket(this.buildWebSocketUrl());
|
||||||
|
this.setupEventHandlersLegacy();
|
||||||
|
} catch (error) {
|
||||||
|
this.connectionStatus = ConnectionStatus.ERROR;
|
||||||
|
console.error('WebSocket连接失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步连接WebSocket服务器
|
||||||
|
* @returns Promise<void>
|
||||||
|
*/
|
||||||
|
public connectAsync(): Promise<void> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// 检查浏览器支持
|
||||||
|
if (!window.WebSocket) {
|
||||||
|
const error = '您的浏览器不支持WebSocket';
|
||||||
|
console.error(error);
|
||||||
|
reject(new Error(error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 防止重复连接
|
||||||
|
if (this.connectionStatus === ConnectionStatus.CONNECTING || this.connected) {
|
||||||
|
console.warn('WebSocket已连接或正在连接中');
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.connectionStatus = ConnectionStatus.CONNECTING;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.ws = new WebSocket(this.buildWebSocketUrl());
|
||||||
|
this.setupEventHandlers(resolve, reject);
|
||||||
|
} catch (error) {
|
||||||
|
this.connectionStatus = ConnectionStatus.ERROR;
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册消息回调函数(支持泛型)
|
||||||
|
* @param messageType 消息类型
|
||||||
|
* @param callback 回调函数
|
||||||
|
*/
|
||||||
|
public registerCallBack<T = any>(messageType: string, callback: CallbackFunction<T>): void {
|
||||||
|
if (!messageType || typeof callback !== 'function') {
|
||||||
|
console.error('注册回调函数参数无效');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.callBackMapping[messageType] = callback;
|
||||||
|
console.log(`注册消息处理器: ${messageType}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注销消息回调函数
|
||||||
|
* @param messageType 消息类型
|
||||||
|
*/
|
||||||
|
public unRegisterCallBack(messageType: string): void {
|
||||||
|
if (this.callBackMapping[messageType]) {
|
||||||
|
delete this.callBackMapping[messageType];
|
||||||
|
console.log(`注销消息处理器: ${messageType}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送数据到WebSocket服务器
|
||||||
|
* @param data 要发送的数据
|
||||||
|
* @returns Promise<void>
|
||||||
|
*/
|
||||||
|
public send(data: any): Promise<void> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!this.connected || !this.ws) {
|
||||||
|
// 未连接时的重试机制
|
||||||
|
if (this.sendRetryCount < 3) {
|
||||||
|
this.sendRetryCount++;
|
||||||
|
setTimeout(() => {
|
||||||
|
this.send(data).then(resolve).catch(reject);
|
||||||
|
}, this.sendRetryCount * 500);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
reject(new Error('WebSocket未连接且重试失败'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 重置重试计数
|
||||||
|
this.sendRetryCount = 0;
|
||||||
|
|
||||||
|
// 尝试发送JSON数据,失败则发送原始数据
|
||||||
|
const message = typeof data === 'string' ? data : JSON.stringify(data);
|
||||||
|
this.ws.send(message);
|
||||||
|
|
||||||
|
console.log('发送消息:', message);
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('发送消息失败:', error);
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭WebSocket连接
|
||||||
|
*/
|
||||||
|
public closeWs(): void {
|
||||||
|
console.log('正在关闭WebSocket连接...');
|
||||||
|
|
||||||
|
// 清理心跳
|
||||||
|
this.clearHeartbeat();
|
||||||
|
|
||||||
|
// 关闭连接
|
||||||
|
if (this.ws) {
|
||||||
|
this.ws.close(1000, '主动关闭连接');
|
||||||
|
this.ws = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新状态
|
||||||
|
this.connectionStatus = ConnectionStatus.DISCONNECTED;
|
||||||
|
this.connectRetryCount = 0;
|
||||||
|
this.sendRetryCount = 0;
|
||||||
|
|
||||||
|
console.log('WebSocket连接已关闭');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前连接状态
|
||||||
|
* @returns 连接状态枚举值
|
||||||
|
*/
|
||||||
|
public getConnectionStatus(): ConnectionStatus {
|
||||||
|
return this.connectionStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取连接统计信息
|
||||||
|
* @returns 连接统计对象
|
||||||
|
*/
|
||||||
|
public getConnectionStats(): {
|
||||||
|
status: ConnectionStatus;
|
||||||
|
connectRetryCount: number;
|
||||||
|
lastResponseHeartTime: number;
|
||||||
|
} {
|
||||||
|
return {
|
||||||
|
status: this.connectionStatus,
|
||||||
|
connectRetryCount: this.connectRetryCount,
|
||||||
|
lastResponseHeartTime: this.lastResponseHeartTime
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const url = 'ws://127.0.0.1:7777/hello?name=cdf'
|
// ========================================================================
|
||||||
this.ws = new WebSocket(url);
|
// 私有方法 (Private Methods)
|
||||||
// 连接成功的事件
|
// ========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建完整的WebSocket URL
|
||||||
|
* 自动从JWT token中获取loginName作为name参数
|
||||||
|
* @returns 完整的WebSocket URL
|
||||||
|
*/
|
||||||
|
private buildWebSocketUrl(): string {
|
||||||
|
const { url } = this.config;
|
||||||
|
|
||||||
|
// 直接从JWT token中获取loginName作为name参数
|
||||||
|
const loginName = jwtUtil.getLoginName();
|
||||||
|
|
||||||
|
if (loginName) {
|
||||||
|
const separator = url.includes('?') ? '&' : '?';
|
||||||
|
return `${url}${separator}name=${encodeURIComponent(loginName)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果无法获取loginName,返回原始URL并输出警告
|
||||||
|
console.warn('无法从JWT token中获取loginName,WebSocket连接可能会失败');
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置WebSocket事件处理器(异步版本)
|
||||||
|
* @param resolve Promise resolve回调
|
||||||
|
* @param reject Promise reject回调
|
||||||
|
*/
|
||||||
|
private setupEventHandlers(resolve: () => void, reject: (error: Error) => void): void {
|
||||||
|
if (!this.ws) return;
|
||||||
|
|
||||||
|
// 连接成功事件
|
||||||
|
this.ws.onopen = () => {
|
||||||
|
ElMessage.success("WebSocket连接服务端成功");
|
||||||
|
console.log('WebSocket连接成功');
|
||||||
|
this.connectionStatus = ConnectionStatus.CONNECTED;
|
||||||
|
this.connectRetryCount = 0;
|
||||||
|
this.startHeartbeat();
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 连接关闭事件
|
||||||
|
this.ws.onclose = (event: CloseEvent) => {
|
||||||
|
console.log('WebSocket连接关闭', event.code, event.reason);
|
||||||
|
this.connectionStatus = ConnectionStatus.DISCONNECTED;
|
||||||
|
this.clearHeartbeat();
|
||||||
|
|
||||||
|
// 非正常关闭且未超过最大重连次数,尝试重连
|
||||||
|
if (event.code !== 1000 && this.connectRetryCount < this.config.maxReconnectAttempts!) {
|
||||||
|
this.attemptReconnect();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 连接错误事件
|
||||||
|
this.ws.onerror = (error: Event) => {
|
||||||
|
console.error('WebSocket连接错误:', error);
|
||||||
|
ElMessage.error("WebSocket连接异常");
|
||||||
|
this.connectionStatus = ConnectionStatus.ERROR;
|
||||||
|
reject(new Error('WebSocket连接失败'));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 消息接收事件
|
||||||
|
this.ws.onmessage = (event: MessageEvent) => {
|
||||||
|
this.handleMessage(event);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置WebSocket事件处理器(兼容版本)
|
||||||
|
*/
|
||||||
|
private setupEventHandlersLegacy(): void {
|
||||||
|
if (!this.ws) return;
|
||||||
|
|
||||||
|
// 连接成功事件
|
||||||
this.ws.onopen = () => {
|
this.ws.onopen = () => {
|
||||||
ElMessage.success("webSocket连接服务端成功了");
|
ElMessage.success("webSocket连接服务端成功了");
|
||||||
console.log('连接服务端成功了');
|
console.log('连接服务端成功了');
|
||||||
this.connected = true;
|
this.connectionStatus = ConnectionStatus.CONNECTED;
|
||||||
// 重置重新连接的次数
|
|
||||||
this.connectRetryCount = 0;
|
this.connectRetryCount = 0;
|
||||||
this.updateLastActivityTime();
|
|
||||||
this.startHeartbeat();
|
this.startHeartbeat();
|
||||||
};
|
};
|
||||||
// 1.连接服务端失败
|
|
||||||
// 2.当连接成功之后, 服务器关闭的情况
|
// 连接关闭事件
|
||||||
this.ws.onclose = () => {
|
this.ws.onclose = (event: CloseEvent) => {
|
||||||
console.log('连接webSocket服务端关闭');
|
console.log('连接webSocket服务端关闭');
|
||||||
this.connected = false;
|
this.connectionStatus = ConnectionStatus.DISCONNECTED;
|
||||||
this.connectRetryCount++;
|
|
||||||
this.clearHeartbeat();
|
this.clearHeartbeat();
|
||||||
/* setTimeout(() => {
|
|
||||||
|
// 保持原有的重连逻辑(被注释掉的)
|
||||||
|
// this.connectRetryCount++;
|
||||||
|
/* setTimeout(() => {
|
||||||
this.connect();
|
this.connect();
|
||||||
}, 500 * this.connectRetryCount);*/
|
}, 500 * this.connectRetryCount);*/
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 连接错误事件
|
||||||
this.ws.onerror = () => {
|
this.ws.onerror = () => {
|
||||||
ElMessage.error("webSocket连接异常!");
|
ElMessage.error("webSocket连接异常!");
|
||||||
|
this.connectionStatus = ConnectionStatus.ERROR;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 消息接收事件
|
||||||
|
this.ws.onmessage = (event: MessageEvent) => {
|
||||||
|
this.handleMessage(event);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// 得到服务端发送过来的数据
|
/**
|
||||||
this.ws.onmessage = (event) => {
|
* 处理接收到的消息
|
||||||
// console.log('🚀 ~ SocketService ~ connect ~ event:', event)
|
* 支持心跳响应、JSON消息和普通文本消息
|
||||||
if(event.data == 'over') {
|
* @param event WebSocket消息事件
|
||||||
//心跳消息处理
|
*/
|
||||||
this.lastResponseHeartTime = Date.now();
|
private handleMessage(event: MessageEvent): void {
|
||||||
this.updateLastActivityTime(); // 收到心跳响应时更新活动时间
|
// console.log('Received message:', event.data);
|
||||||
}else {
|
|
||||||
let message: { [key: string]: any };
|
// 心跳响应处理
|
||||||
try {
|
if (event.data === 'over') {
|
||||||
console.log('Received message:',event.data)
|
console.log(`${new Date().toLocaleTimeString()} - 收到心跳响应`);
|
||||||
message = JSON.parse(event.data);
|
this.lastResponseHeartTime = Date.now();
|
||||||
} catch (e) {
|
return;
|
||||||
return console.error("消息解析失败", event.data, e);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* 通过接受服务端发送的type字段来回调函数 */
|
// 检查消息是否为空或无效
|
||||||
|
if (!event.data || event.data.trim() === '') {
|
||||||
|
console.warn('收到空消息,跳过处理');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 业务消息处理
|
||||||
|
try {
|
||||||
|
// 检查是否为JSON格式
|
||||||
|
if (typeof event.data === 'string' && (event.data.startsWith('{') || event.data.startsWith('['))) {
|
||||||
|
const message: WebSocketMessage = JSON.parse(event.data);
|
||||||
if (message?.type && this.callBackMapping[message.type]) {
|
if (message?.type && this.callBackMapping[message.type]) {
|
||||||
this.callBackMapping[message.type](message);
|
this.callBackMapping[message.type](message);
|
||||||
} else {
|
} else {
|
||||||
console.log("抛弃====>")
|
console.warn('未找到对应的消息处理器:', message.type);
|
||||||
console.log(event.data)
|
}
|
||||||
/* 丢弃或继续写你的逻辑 */
|
} else {
|
||||||
|
// 非JSON格式的消息,作为普通文本处理
|
||||||
|
console.log('收到非JSON格式消息:', event.data);
|
||||||
|
// 可以添加文本消息的处理逻辑
|
||||||
|
if (this.callBackMapping['text']) {
|
||||||
|
this.callBackMapping['text']({
|
||||||
|
type: 'text',
|
||||||
|
data: event.data
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
};
|
console.error('消息解析失败:', event.data, error);
|
||||||
}
|
console.error('消息类型:', typeof event.data);
|
||||||
|
console.error('消息长度:', event.data?.length || 0);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
startHeartbeat() {
|
|
||||||
this.lastResponseHeartTime = Date.now();
|
|
||||||
const _this = this
|
|
||||||
const url = window.URL.createObjectURL(new Blob(['(function(e){setInterval(function(){this.postMessage(null)},9000)})()']));
|
|
||||||
this.workerBlobUrl = url; // 存储临时的Blob URL
|
|
||||||
this.work = new Worker(url);
|
|
||||||
this.work.onmessage = function(e){
|
|
||||||
//判断多久没收到心跳响应
|
|
||||||
|
|
||||||
if(_this.lastActivityTime - _this.lastResponseHeartTime > 30000){
|
|
||||||
//说明已经三轮心跳没收到回复了,关闭检测,提示用户。
|
|
||||||
ElMessage.error("业务主体模块发生未知异常,请尝试重新启动!");
|
|
||||||
_this.clearHeartbeat();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_this.sendHeartbeat();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
sendHeartbeat() {
|
|
||||||
console.log(new Date()+"进入心跳消息发送。。。。。。。。。。。。。")
|
|
||||||
this.ws.send('alive');
|
|
||||||
this.updateLastActivityTime(); // 发送心跳后更新活动时间
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
updateLastActivityTime() {
|
|
||||||
this.lastActivityTime = Date.now();
|
|
||||||
}
|
|
||||||
|
|
||||||
clearHeartbeat() {
|
|
||||||
if (this.work) {
|
|
||||||
this.work.terminate();
|
|
||||||
this.work = null;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// 重连机制相关方法
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 尝试重新连接WebSocket
|
||||||
|
*/
|
||||||
|
private attemptReconnect(): void {
|
||||||
|
if (this.connectionStatus === ConnectionStatus.RECONNECTING) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.connectionStatus = ConnectionStatus.RECONNECTING;
|
||||||
|
this.connectRetryCount++;
|
||||||
|
|
||||||
|
const delay = this.config.reconnectDelay! * this.connectRetryCount;
|
||||||
|
|
||||||
|
console.log(`尝试第${this.connectRetryCount}次重连,${delay}ms后开始...`);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
try {
|
||||||
|
const result = this.connect();
|
||||||
|
if (result instanceof Promise) {
|
||||||
|
result.catch((error: any) => {
|
||||||
|
console.error('重连失败:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('重连失败:', error);
|
||||||
|
}
|
||||||
|
}, delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================================================
|
||||||
|
// 心跳机制相关方法
|
||||||
|
// ========================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动心跳机制
|
||||||
|
*/
|
||||||
|
private startHeartbeat(): void {
|
||||||
|
this.lastResponseHeartTime = Date.now();
|
||||||
|
this.createHeartbeatWorker();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建心跳Worker
|
||||||
|
* 使用Worker在独立线程中处理心跳定时器,避免主线程阻塞
|
||||||
|
*/
|
||||||
|
private createHeartbeatWorker(): void {
|
||||||
|
try {
|
||||||
|
// 创建Worker脚本
|
||||||
|
const workerScript = `
|
||||||
|
setInterval(function() {
|
||||||
|
postMessage('heartbeat');
|
||||||
|
}, ${this.config.heartbeatInterval});
|
||||||
|
`;
|
||||||
|
|
||||||
|
this.workerBlobUrl = window.URL.createObjectURL(
|
||||||
|
new Blob([workerScript], { type: 'application/javascript' })
|
||||||
|
);
|
||||||
|
|
||||||
|
this.heartbeatWorker = new Worker(this.workerBlobUrl);
|
||||||
|
|
||||||
|
// 心跳Worker消息处理
|
||||||
|
this.heartbeatWorker.onmessage = (event: MessageEvent) => {
|
||||||
|
this.handleHeartbeatTick();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Worker错误处理
|
||||||
|
this.heartbeatWorker.onerror = (error: ErrorEvent) => {
|
||||||
|
console.error('心跳Worker错误:', error);
|
||||||
|
this.clearHeartbeat();
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('创建心跳Worker失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理心跳定时器事件
|
||||||
|
* 检查连接超时并发送心跳消息
|
||||||
|
*/
|
||||||
|
private handleHeartbeatTick(): void {
|
||||||
|
// 检查是否超时(距离上次收到心跳响应的时间)
|
||||||
|
const timeSinceLastResponse = Date.now() - this.lastResponseHeartTime;
|
||||||
|
|
||||||
|
if (timeSinceLastResponse > this.config.timeout!) {
|
||||||
|
console.error(`WebSocket心跳超时: ${timeSinceLastResponse}ms > ${this.config.timeout}ms`);
|
||||||
|
ElMessage.error("WebSocket连接超时,请检查网络连接!");
|
||||||
|
this.clearHeartbeat();
|
||||||
|
this.closeWs();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sendHeartbeat();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送心跳消息到服务器
|
||||||
|
*/
|
||||||
|
private sendHeartbeat(): void {
|
||||||
|
if (this.connected && this.ws) {
|
||||||
|
console.log(`${new Date().toLocaleTimeString()} - 发送心跳消息`);
|
||||||
|
this.ws.send('alive');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理心跳机制
|
||||||
|
* 终止Worker并清理相关资源
|
||||||
|
*/
|
||||||
|
private clearHeartbeat(): void {
|
||||||
|
if (this.heartbeatWorker) {
|
||||||
|
this.heartbeatWorker.terminate();
|
||||||
|
this.heartbeatWorker = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.workerBlobUrl) {
|
if (this.workerBlobUrl) {
|
||||||
window.URL.revokeObjectURL(this.workerBlobUrl); // 释放临时的Blob URL
|
window.URL.revokeObjectURL(this.workerBlobUrl);
|
||||||
this.workerBlobUrl = null;
|
this.workerBlobUrl = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 回调函数的注册
|
|
||||||
registerCallBack(socketType, callBack) {
|
|
||||||
this.callBackMapping[socketType] = callBack;
|
|
||||||
}
|
|
||||||
// 取消某一个回调函数
|
|
||||||
unRegisterCallBack(socketType) {
|
|
||||||
this.callBackMapping[socketType] = null;
|
|
||||||
}
|
|
||||||
// 发送数据的方法
|
|
||||||
send(data) {
|
|
||||||
// 判断此时此刻有没有连接成功
|
|
||||||
if (this.connected) {
|
|
||||||
this.sendRetryCount = 0;
|
|
||||||
try {
|
|
||||||
this.ws.send(JSON.stringify(data));
|
|
||||||
} catch (e) {
|
|
||||||
this.ws.send(data);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.sendRetryCount++;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.send(data);
|
|
||||||
}, this.sendRetryCount * 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 断开方法
|
|
||||||
closeWs() {
|
|
||||||
if (this.connected) {
|
|
||||||
this.ws.close()
|
|
||||||
}
|
|
||||||
console.log('执行WS关闭命令..');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,220 +1,222 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog :title="dialogTitle" :model-value="dialogVisible" @close="close" v-bind="dialogMiddle">
|
<el-dialog :title="dialogTitle" :model-value="dialogVisible" @close="close" v-bind="dialogMiddle" align-center>
|
||||||
<el-form :model="formContent" ref='dialogFormRef' :rules='rules' class="form-two">
|
<el-form :model="formContent" ref="dialogFormRef" :rules="rules" class="form-two">
|
||||||
<el-form-item label="上级菜单" prop="pid" :label-width="100">
|
<el-form-item label="上级菜单" prop="pid" :label-width="100">
|
||||||
<el-tree-select
|
<el-tree-select
|
||||||
v-model="displayPid"
|
v-model="displayPid"
|
||||||
:data="functionList"
|
:data="functionList"
|
||||||
check-strictly
|
check-strictly
|
||||||
:render-after-expand="false"
|
:render-after-expand="false"
|
||||||
show-checkbox
|
show-checkbox
|
||||||
check-on-click-node
|
check-on-click-node
|
||||||
node-key="id"
|
node-key="id"
|
||||||
:props="defaultProps"
|
:props="defaultProps"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="名称" prop="name" :label-width="100">
|
<el-form-item label="名称" prop="name" :label-width="100">
|
||||||
<el-input v-model="formContent.name" />
|
<el-input v-model="formContent.name" maxlength="32" show-word-limit />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="编码" prop="code" :label-width="100">
|
<el-form-item label="编码" prop="code" :label-width="100">
|
||||||
<el-input v-model="formContent.code" />
|
<el-input v-model="formContent.code" maxlength="32" show-word-limit />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="!formContent.type" label="图标" prop="icon" :label-width="100">
|
<el-form-item v-if="!formContent.type" label="图标" prop="icon" :label-width="100">
|
||||||
<IconSelect
|
<IconSelect
|
||||||
v-model="formContent.icon"
|
v-model="formContent.icon"
|
||||||
:iconValue="formContent.icon"
|
:iconValue="formContent.icon"
|
||||||
@update:icon-value="iconValue => formContent.icon = iconValue"
|
@update:icon-value="iconValue => (formContent.icon = iconValue)"
|
||||||
placeholder="选择一个图标"
|
placeholder="选择一个图标"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="!formContent.type" label="路由地址" prop="path" :label-width="100">
|
<el-form-item v-if="!formContent.type" label="路由地址" prop="path" :label-width="100">
|
||||||
<el-input v-model="formContent.path" />
|
<el-input v-model="formContent.path" maxlength="32" show-word-limit />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="!formContent.type" label="组件地址" prop="component" :label-width="100">
|
<el-form-item v-if="!formContent.type" label="组件地址" prop="component" :label-width="100">
|
||||||
<el-input v-model="formContent.component" />
|
<el-input v-model="formContent.component" maxlength="32" show-word-limit />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="排序" prop="sort" :label-width="100">
|
<el-form-item label="排序" prop="sort" :label-width="100">
|
||||||
<el-input-number v-model="formContent.sort" :min='1' :max='999' />
|
<el-input-number v-model="formContent.sort" :min="1" :max="999" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label='类型' prop='type' :label-width="100">
|
<el-form-item label="类型" prop="type" :label-width="100">
|
||||||
<el-select v-model="formContent.type" clearable placeholder="请选择资源类型">
|
<el-select v-model="formContent.type" clearable placeholder="请选择资源类型">
|
||||||
<el-option label="菜单" :value="0"></el-option>
|
<el-option label="菜单" :value="0"></el-option>
|
||||||
<el-option label="按钮" :value="1"></el-option>
|
<el-option label="按钮" :value="1"></el-option>
|
||||||
<!-- <el-option label="公共资源" :value="2"></el-option>
|
<!-- <el-option label="公共资源" :value="2"></el-option>
|
||||||
<el-option label="服务间调用资源" :value="3"></el-option> -->
|
<el-option label="服务间调用资源" :value="3"></el-option> -->
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="描述" prop="remark" :label-width="100">
|
<el-form-item label="描述" prop="remark" :label-width="100">
|
||||||
<el-input v-model="formContent.remark" :rows="2" type="textarea"/>
|
<el-input v-model="formContent.remark" :rows="2" type="textarea" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="dialog-footer">
|
<div class="dialog-footer">
|
||||||
<el-button @click="close()">取 消</el-button>
|
<el-button @click="close()">取 消</el-button>
|
||||||
<el-button type="primary" @click="save()">确 定</el-button>
|
<el-button type="primary" @click="save()">确 定</el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup name="ResourceDialog">
|
<script lang="ts" setup name="ResourceDialog">
|
||||||
import { defineProps, defineEmits,watch,ref, type Ref, computed } from 'vue';
|
import { computed, type Ref, ref, watch } from 'vue'
|
||||||
import { dialogMiddle } from '@/utils/elementBind'
|
import { dialogMiddle } from '@/utils/elementBind'
|
||||||
import { ElMessage, type FormInstance, type FormItemRule } from 'element-plus'
|
import { ElMessage, type FormInstance, type FormItemRule } from 'element-plus'
|
||||||
import { useDictStore } from '@/stores/modules/dict'
|
import { useDictStore } from '@/stores/modules/dict'
|
||||||
import type { Function } from "@/api/user/interface/function"
|
import type { Function } from '@/api/user/interface/function'
|
||||||
import {addFunction,updateFunction,getFunctionListNoButton} from '@/api/user/function/index'
|
import { addFunction, getFunctionListNoButton, updateFunction } from '@/api/user/function/index'
|
||||||
import IconSelect from '@/components/SelectIcon/index.vue'
|
import IconSelect from '@/components/SelectIcon/index.vue'
|
||||||
const value = ref()
|
|
||||||
// 树形节点配置
|
const value = ref()
|
||||||
const defaultProps = {
|
// 树形节点配置
|
||||||
|
const defaultProps = {
|
||||||
children: 'children',
|
children: 'children',
|
||||||
label: 'name',
|
label: 'name',
|
||||||
value: 'id'
|
value: 'id'
|
||||||
};
|
}
|
||||||
const functionList = ref<Function.ResFunction[]>([])
|
const functionList = ref<Function.ResFunction[]>([])
|
||||||
const dictStore = useDictStore()
|
const dictStore = useDictStore()
|
||||||
// 定义弹出组件元信息
|
// 定义弹出组件元信息
|
||||||
const dialogFormRef = ref()
|
const dialogFormRef = ref()
|
||||||
function useMetaInfo() {
|
function useMetaInfo() {
|
||||||
const dialogVisible = ref(false)
|
const dialogVisible = ref(false)
|
||||||
const titleType = ref('add')
|
const titleType = ref('add')
|
||||||
const formContent = ref<Function.ResFunction>({
|
const formContent = ref<Function.ResFunction>({
|
||||||
id: '',//资源表Id
|
id: '', //资源表Id
|
||||||
pid:'',//节点(0为根节点)
|
pid: '', //节点(0为根节点)
|
||||||
pids:'',//节点上层所有节点
|
pids: '', //节点上层所有节点
|
||||||
name: '',//名称
|
name: '', //名称
|
||||||
code:'',//资源标识
|
code: '', //资源标识
|
||||||
path:'',//路径
|
path: '', //路径
|
||||||
component:'',
|
component: '',
|
||||||
icon:undefined as string | undefined, // 图标
|
icon: undefined as string | undefined, // 图标
|
||||||
sort:100,//排序
|
sort: 100, //排序
|
||||||
type:0,//资源类型0-菜单、1-按钮、2-公共资源、3-服务间调用资源
|
type: 0, //资源类型0-菜单、1-按钮、2-公共资源、3-服务间调用资源
|
||||||
remark: '',//权限资源描述
|
remark: '', //权限资源描述
|
||||||
state:1,//权限资源状态
|
state: 1 //权限资源状态
|
||||||
})
|
})
|
||||||
return { dialogVisible, titleType, formContent }
|
return { dialogVisible, titleType, formContent }
|
||||||
}
|
}
|
||||||
|
|
||||||
const { dialogVisible, titleType, formContent } = useMetaInfo()
|
const { dialogVisible, titleType, formContent } = useMetaInfo()
|
||||||
// 清空formContent
|
// 清空formContent
|
||||||
const resetFormContent = () => {
|
const resetFormContent = () => {
|
||||||
formContent.value = {
|
formContent.value = {
|
||||||
id: '',//资源表Id
|
id: '', //资源表Id
|
||||||
pid:'',//节点(0为根节点)
|
pid: '', //节点(0为根节点)
|
||||||
pids:'',//节点上层所有节点
|
pids: '', //节点上层所有节点
|
||||||
name: '',//名称
|
name: '', //名称
|
||||||
code:'',//资源标识
|
code: '', //资源标识
|
||||||
path:'',//路径
|
path: '', //路径
|
||||||
component:'',
|
component: '',
|
||||||
icon:undefined,//图标
|
icon: undefined, //图标
|
||||||
sort:100,//排序
|
sort: 100, //排序
|
||||||
type:0,//资源类型0-菜单、1-按钮、2-公共资源、3-服务间调用资源
|
type: 0, //资源类型0-菜单、1-按钮、2-公共资源、3-服务间调用资源
|
||||||
remark:'',//权限资源描述
|
remark: '', //权限资源描述
|
||||||
state:1,//权限资源状态
|
state: 1 //权限资源状态
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let dialogTitle = computed(() => {
|
let dialogTitle = computed(() => {
|
||||||
return titleType.value === 'add' ? '新增菜单' : '编辑菜单'
|
return titleType.value === 'add' ? '新增菜单' : '编辑菜单'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 定义规则
|
||||||
|
const formRuleRef = ref<FormInstance>()
|
||||||
|
const rules: Ref<Record<string, Array<FormItemRule>>> = ref({
|
||||||
|
name: [{ required: true, trigger: 'blur', message: '菜单名称必填!' }],
|
||||||
|
code: [{ required: true, trigger: 'blur', message: '编码必填!' }]
|
||||||
|
})
|
||||||
|
|
||||||
// 定义规则
|
watch(
|
||||||
const formRuleRef = ref<FormInstance>()
|
() => formContent.value.type,
|
||||||
const rules : Ref<Record<string, Array<FormItemRule>>> = ref({
|
newVal => {
|
||||||
name :[{required:true,trigger:'blur',message:'菜单名称必填!'}],
|
if (newVal === 1) {
|
||||||
code :[{required:true,trigger:'blur',message:'编码必填!'}]
|
// 选择按钮时,路由地址和组件地址无需校验
|
||||||
})
|
rules.value.path = []
|
||||||
|
rules.value.component = []
|
||||||
|
} else {
|
||||||
|
// 其他情况下,路由地址和组件地址需要校验
|
||||||
|
rules.value.path = [{ required: true, trigger: 'blur', message: '路由地址必填!' }]
|
||||||
|
rules.value.component = [{ required: true, trigger: 'blur', message: '组件地址必填!' }]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
watch(() => formContent.value.type, (newVal) => {
|
// 关闭弹窗
|
||||||
if (newVal === 1) {
|
|
||||||
// 选择按钮时,路由地址和组件地址无需校验
|
|
||||||
rules.value.path = [];
|
|
||||||
rules.value.component = [];
|
|
||||||
} else {
|
|
||||||
// 其他情况下,路由地址和组件地址需要校验
|
|
||||||
rules.value.path = [{ required: true, trigger: 'blur', message: '路由地址必填!' }];
|
|
||||||
rules.value.component = [{ required: true, trigger: 'blur', message: '组件地址必填!' }];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 关闭弹窗
|
|
||||||
const close = () => {
|
const close = () => {
|
||||||
dialogVisible.value = false
|
dialogVisible.value = false
|
||||||
// 清空dialogForm中的值
|
// 清空dialogForm中的值
|
||||||
resetFormContent()
|
resetFormContent()
|
||||||
// 重置表单
|
// 重置表单
|
||||||
dialogFormRef.value?.resetFields()
|
dialogFormRef.value?.resetFields()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 计算属性,用于控制显示的 pid
|
// 计算属性,用于控制显示的 pid
|
||||||
const displayPid = computed({
|
const displayPid = computed({
|
||||||
get: () => {
|
get: () => {
|
||||||
return formContent.value.pid === '0' ? '' : formContent.value.pid;
|
return formContent.value.pid === '0' ? '' : formContent.value.pid
|
||||||
},
|
},
|
||||||
set: (value) => {
|
set: value => {
|
||||||
formContent.value.pid = value;
|
formContent.value.pid = value
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
|
// 保存数据
|
||||||
// 保存数据
|
const save = () => {
|
||||||
const save = () => {
|
|
||||||
try {
|
try {
|
||||||
dialogFormRef.value?.validate(async (valid: boolean) => {
|
dialogFormRef.value?.validate(async (valid: boolean) => {
|
||||||
|
if (formContent.value.pid === undefined || formContent.value.pid === null || formContent.value.pid === '') {
|
||||||
if (formContent.value.pid === undefined || formContent.value.pid === null || formContent.value.pid === '') {
|
formContent.value.pid = '0'
|
||||||
formContent.value.pid = '0';
|
}
|
||||||
}
|
if (
|
||||||
if (formContent.value.pids === undefined || formContent.value.pids === null || formContent.value.pids === '') {
|
formContent.value.pids === undefined ||
|
||||||
formContent.value.pids = '0';
|
formContent.value.pids === null ||
|
||||||
}
|
formContent.value.pids === ''
|
||||||
if (valid) {
|
) {
|
||||||
if (formContent.value.id) {
|
formContent.value.pids = '0'
|
||||||
await updateFunction(formContent.value);
|
}
|
||||||
} else {
|
if (valid) {
|
||||||
await addFunction(formContent.value);
|
if (formContent.value.id) {
|
||||||
}
|
await updateFunction(formContent.value)
|
||||||
ElMessage.success({ message: `${dialogTitle.value}成功!` })
|
} else {
|
||||||
close()
|
await addFunction(formContent.value)
|
||||||
// 刷新表格
|
}
|
||||||
await props.refreshTable!()
|
ElMessage.success({ message: `${dialogTitle.value}成功!` })
|
||||||
|
close()
|
||||||
}
|
// 刷新表格
|
||||||
})
|
await props.refreshTable!()
|
||||||
|
}
|
||||||
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('验证过程中出现错误', err)
|
console.error('验证过程中出现错误', err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 打开弹窗,可能是新增,也可能是编辑
|
// 打开弹窗,可能是新增,也可能是编辑
|
||||||
const open = async (sign: string, data: Function.ResFunction) => {
|
const open = async (sign: string, data: Function.ResFunction) => {
|
||||||
// 重置表单
|
// 重置表单
|
||||||
dialogFormRef.value?.resetFields()
|
dialogFormRef.value?.resetFields()
|
||||||
const response = await getFunctionListNoButton()
|
const response = await getFunctionListNoButton()
|
||||||
functionList.value = response.data as unknown as Function.ResFunction[]
|
functionList.value = response.data as unknown as Function.ResFunction[]
|
||||||
titleType.value = sign
|
titleType.value = sign
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
|
rules.value.path = [{ required: true, trigger: 'blur', message: '路由地址必填!' }]
|
||||||
|
rules.value.component = [{ required: true, trigger: 'blur', message: '组件地址必填!' }]
|
||||||
|
|
||||||
if (formContent.value.pid ==='0') {
|
if (formContent.value.pid === '0') {
|
||||||
formContent.value.pid = '';
|
formContent.value.pid = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.id) {
|
if (data.id) {
|
||||||
formContent.value = { ...data }
|
formContent.value = { ...data }
|
||||||
} else {
|
} else {
|
||||||
resetFormContent()
|
resetFormContent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 对外映射
|
// 对外映射
|
||||||
defineExpose({ open })
|
defineExpose({ open })
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
refreshTable: (() => Promise<void>) | undefined;
|
refreshTable: (() => Promise<void>) | undefined
|
||||||
}>()
|
}>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- 基础信息弹出框 -->
|
<!-- 基础信息弹出框 -->
|
||||||
<el-dialog :model-value="dialogVisible" :title="dialogTitle" v-bind="dialogSmall" @close="close" >
|
<el-dialog :model-value="dialogVisible" :title="dialogTitle" v-bind="dialogSmall" @close="close" align-center>
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
<el-form :model="formContent"
|
<el-form :model="formContent"
|
||||||
@@ -8,11 +8,11 @@
|
|||||||
:rules='rules'
|
:rules='rules'
|
||||||
>
|
>
|
||||||
<el-form-item label="名称" prop='name' :label-width="100" >
|
<el-form-item label="名称" prop='name' :label-width="100" >
|
||||||
<el-input v-model="formContent.name" placeholder="请输入名称" autocomplete="off" :disabled="rootIsEdit"/>
|
<el-input v-model="formContent.name" placeholder="请输入名称" autocomplete="off" :disabled="rootIsEdit" maxlength="32" show-word-limit/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="编码" prop='code' :label-width="100">
|
<el-form-item label="编码" prop='code' :label-width="100">
|
||||||
<el-input v-model="formContent.code" placeholder="请输入编码" autocomplete="off" :disabled="rootIsEdit"/>
|
<el-input v-model="formContent.code" placeholder="请输入编码" autocomplete="off" :disabled="rootIsEdit" maxlength="32" show-word-limit/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label='类型' prop='type' :label-width="100">
|
<el-form-item label='类型' prop='type' :label-width="100">
|
||||||
<el-select v-model="formContent.type" clearable placeholder="请选择类型" :disabled="rootIsEdit">
|
<el-select v-model="formContent.type" clearable placeholder="请选择类型" :disabled="rootIsEdit">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- 基础信息弹出框 -->
|
<!-- 基础信息弹出框 -->
|
||||||
<el-dialog :model-value="dialogVisible" :title="dialogTitle" v-bind="dialogMiddle" @close="close" >
|
<el-dialog :model-value="dialogVisible" :title="dialogTitle" v-bind="dialogMiddle" @close="close" align-center>
|
||||||
<div>
|
<div>
|
||||||
<el-form :model="formContent" ref='dialogFormRef'>
|
<el-form :model="formContent" ref='dialogFormRef'>
|
||||||
<el-tree
|
<el-tree
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<!-- :requestApi="getRoleList" -->
|
<!-- :requestApi="getRoleList" -->
|
||||||
<!-- 表格 header 按钮 -->
|
<!-- 表格 header 按钮 -->
|
||||||
<template #tableHeader='scope'>
|
<template #tableHeader='scope'>
|
||||||
<el-button v-auth.role="'add'" type='primary' :icon='CirclePlus' @click="openDrawer('新增角色')">新增</el-button>
|
<el-button v-auth.role="'add'" type='primary' :icon='CirclePlus' @click="openDrawer('add')">新增</el-button>
|
||||||
<el-button v-auth.role="'delete'" type='danger' :icon='Delete' plain :disabled='!scope.isSelected'
|
<el-button v-auth.role="'delete'" type='danger' :icon='Delete' plain :disabled='!scope.isSelected'
|
||||||
@click='batchDelete(scope.selectedListIds)'>
|
@click='batchDelete(scope.selectedListIds)'>
|
||||||
删除
|
删除
|
||||||
@@ -16,9 +16,9 @@
|
|||||||
</template>
|
</template>
|
||||||
<!-- 表格操作 -->
|
<!-- 表格操作 -->
|
||||||
<template #operation='scope'>
|
<template #operation='scope'>
|
||||||
<el-button v-auth.role="'edit'" type='primary' link :icon='EditPen' @click="openDrawer('编辑角色', scope.row)">编辑</el-button>
|
<el-button v-auth.role="'edit'" type='primary' link :icon='EditPen' @click="openDrawer('edit', scope.row)" :disabled="scope.row.code == 'root'">编辑</el-button>
|
||||||
<el-button v-auth.role="'delete'" v-if="scope.row.type !== 0 && scope.row.type !== 1" type='primary' link :icon='Delete' @click='deleteAccount(scope.row)'>删除</el-button>
|
<el-button v-auth.role="'delete'" type='primary' link :icon='Delete' @click='deleteAccount(scope.row)' :disabled="scope.row.code == 'root'">删除</el-button>
|
||||||
<el-button v-auth.role="'SetPermissions'" type='primary' link :icon='Share' @click="openDrawer('设置权限', scope.row)">设置权限</el-button>
|
<el-button v-auth.role="'SetPermissions'" type='primary' link :icon='Share' @click="openDrawer('设置权限', scope.row)" :disabled="scope.row.code == 'root'">设置权限</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
</ProTable>
|
</ProTable>
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- 基础信息弹出框 -->
|
<!-- 基础信息弹出框 -->
|
||||||
<el-dialog v-model='dialogVisible' :title="dialogTitle" v-bind="dialogSmall" @close="close">
|
<el-dialog v-model='dialogVisible' :title="dialogTitle" v-bind="dialogSmall" @close="close" align-center>
|
||||||
<div>
|
<div>
|
||||||
<el-form :model="formContent"
|
<el-form :model="formContent"
|
||||||
ref='dialogFormRef'
|
ref='dialogFormRef'
|
||||||
:rules='rules'
|
:rules='rules'
|
||||||
>
|
>
|
||||||
<el-form-item label="原密码" prop='oldPassword' :label-width="100">
|
<el-form-item label="原密码" prop='oldPassword' :label-width="100">
|
||||||
<el-input type="oldPassword" v-model="formContent.oldPassword" show-password placeholder="请输入原密码" autocomplete="off" />
|
<el-input type="oldPassword" v-model="formContent.oldPassword" show-password placeholder="请输入原密码" autocomplete="off" maxlength="32" show-word-limit/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="新密码" prop='newPassword' :label-width="100">
|
<el-form-item label="新密码" prop='newPassword' :label-width="100">
|
||||||
<el-input type="newPassword" v-model="formContent.newPassword" show-password placeholder="请输入新密码" autocomplete="off" />
|
<el-input type="newPassword" v-model="formContent.newPassword" show-password placeholder="请输入新密码" autocomplete="off" maxlength="32" show-word-limit/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="确认密码" prop='surePassword' :label-width="100">
|
<el-form-item label="确认密码" prop='surePassword' :label-width="100">
|
||||||
<el-input type="surePassword" v-model="formContent.surePassword" show-password placeholder="请再次输入确认密码" autocomplete="off" />
|
<el-input type="surePassword" v-model="formContent.surePassword" show-password placeholder="请再次输入确认密码" autocomplete="off" maxlength="32" show-word-limit/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- 基础信息弹出框 -->
|
<!-- 基础信息弹出框 -->
|
||||||
<el-dialog v-model='dialogVisible' :title="dialogTitle" v-bind="dialogSmall" @close="close" >
|
<el-dialog v-model='dialogVisible' :title="dialogTitle" v-bind="dialogSmall" @close="close" align-center>
|
||||||
<div>
|
<div>
|
||||||
<el-form :model="formContent"
|
<el-form :model="formContent"
|
||||||
ref='dialogFormRef'
|
ref='dialogFormRef'
|
||||||
:rules='rules'
|
:rules='rules'
|
||||||
>
|
>
|
||||||
<el-form-item label="用户名" prop='name' :label-width="100">
|
<el-form-item label="用户名" prop='name' :label-width="100">
|
||||||
<el-input v-model="formContent.name" placeholder="请输入用户名" autocomplete="off" />
|
<el-input v-model="formContent.name" placeholder="请输入用户名" autocomplete="off" maxlength="32" show-word-limit/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="登录名" prop='loginName' :label-width="100" >
|
<el-form-item label="登录名" prop='loginName' :label-width="100" >
|
||||||
<el-input v-model="formContent.loginName" placeholder="请输入登录名" autocomplete="off" :disabled="LoginNameIsShow"/>
|
<el-input v-model="formContent.loginName" placeholder="请输入登录名" autocomplete="off" :disabled="LoginNameIsShow" maxlength="32" show-word-limit/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="密码" prop='password' :label-width="100" v-if="IsPasswordShow">
|
<el-form-item label="密码" prop='password' :label-width="100" v-if="IsPasswordShow">
|
||||||
<el-input type="password" v-model="formContent.password" show-password placeholder="请输入密码" autocomplete="off" />
|
<el-input type="password" v-model="formContent.password" show-password placeholder="请输入密码" autocomplete="off" maxlength="32" show-word-limit/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label='角色' :label-width='100' prop='roles'>
|
<el-form-item label='角色' :label-width='100' prop='roles'>
|
||||||
<el-select v-model="formContent.roleIds" multiple placeholder="请选择角色">
|
<el-select v-model="formContent.roleIds" multiple placeholder="请选择角色">
|
||||||
|
|||||||
@@ -16,9 +16,9 @@
|
|||||||
</template>
|
</template>
|
||||||
<!-- 表格操作 -->
|
<!-- 表格操作 -->
|
||||||
<template #operation='scope'>
|
<template #operation='scope'>
|
||||||
<el-button v-auth.user="'edit'" type='primary' link :icon='EditPen' @click="openDialog('edit', scope.row)">编辑</el-button>
|
<el-button v-auth.user="'edit'" type='primary' link :icon='EditPen' @click="openDialog('edit', scope.row)" :disabled="scope.row.loginName == 'root'">编辑</el-button>
|
||||||
<el-button v-auth.user="'delete'" type='primary' link :icon='Delete' @click='handleDelete(scope.row)'>删除</el-button>
|
<el-button v-auth.user="'delete'" type='primary' link :icon='Delete' @click='handleDelete(scope.row)' :disabled="scope.row.loginName == 'root'">删除</el-button>
|
||||||
<el-button v-auth.user="'editPassWord'" type='primary' link :icon='Delete' @click='EditPassWord(scope.row)'>修改密码</el-button>
|
<el-button v-auth.user="'editPassWord'" type='primary' link :icon='Delete' @click='EditPassWord(scope.row)' :disabled="scope.row.loginName == 'root'">修改密码</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
</ProTable>
|
</ProTable>
|
||||||
@@ -64,8 +64,8 @@
|
|||||||
// 默认不做操作就直接在 ProTable 组件上绑定 :requestApi="getUserList"
|
// 默认不做操作就直接在 ProTable 组件上绑定 :requestApi="getUserList"
|
||||||
const getTableList = (params: any) => {
|
const getTableList = (params: any) => {
|
||||||
let newParams = JSON.parse(JSON.stringify(params))
|
let newParams = JSON.parse(JSON.stringify(params))
|
||||||
newParams.searchEndTime = endDate.value
|
// newParams.searchEndTime = endDate.value
|
||||||
newParams.searchBeginTime = startDate.value
|
// newParams.searchBeginTime = startDate.value
|
||||||
return getUserList(newParams)
|
return getUserList(newParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,6 +91,7 @@
|
|||||||
render: (scope) => {
|
render: (scope) => {
|
||||||
const roleNames = scope.row.roleNames;
|
const roleNames = scope.row.roleNames;
|
||||||
const roleArray = Array.isArray(roleNames) ? roleNames : [roleNames];
|
const roleArray = Array.isArray(roleNames) ? roleNames : [roleNames];
|
||||||
|
|
||||||
if (roleArray.length > 1) {
|
if (roleArray.length > 1) {
|
||||||
return roleArray.join(', ');
|
return roleArray.join(', ');
|
||||||
}
|
}
|
||||||
@@ -111,19 +112,19 @@
|
|||||||
prop: 'loginTime',
|
prop: 'loginTime',
|
||||||
label: '最后一次登录时间',
|
label: '最后一次登录时间',
|
||||||
minWidth: 180,
|
minWidth: 180,
|
||||||
search: {
|
// search: {
|
||||||
render: () => {
|
// render: () => {
|
||||||
return (
|
// return (
|
||||||
<div class='flx-flex-start'>
|
// <div class='flx-flex-start'>
|
||||||
<TimeControl
|
// <TimeControl
|
||||||
include={['日', '周', '月', '自定义']}
|
// include={['日', '周', '月', '自定义']}
|
||||||
default={'月'}
|
// default={'月'}
|
||||||
onUpdate-dates={handleDateChange}
|
// onUpdate-dates={handleDateChange}
|
||||||
/>
|
// />
|
||||||
</div>
|
// </div>
|
||||||
)
|
// )
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: 'state',
|
prop: 'state',
|
||||||
|
|||||||
88
frontend/src/views/home/components/RemoveableEdge.vue
Normal file
88
frontend/src/views/home/components/RemoveableEdge.vue
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
<script setup>
|
||||||
|
import { BaseEdge, EdgeLabelRenderer, getBezierPath, useVueFlow } from '@vue-flow/core'
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { useCheckStore } from '@/stores/modules/check'
|
||||||
|
|
||||||
|
const checkStore = useCheckStore()
|
||||||
|
const props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
sourceX: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
sourceY: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
targetX: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
targetY: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
sourcePosition: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
targetPosition: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
markerEnd: {
|
||||||
|
type: String,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
type: Object,
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const { removeEdges } = useVueFlow()
|
||||||
|
|
||||||
|
const path = computed(() => getBezierPath(props))
|
||||||
|
const edgeStyle = computed(() => ({
|
||||||
|
...props.style,
|
||||||
|
strokeWidth: 3
|
||||||
|
}))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
inheritAttrs: false
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<BaseEdge :id="id" :style="edgeStyle" :path="path[0]" :marker-end="markerEnd" />
|
||||||
|
<EdgeLabelRenderer>
|
||||||
|
<div
|
||||||
|
:style="{
|
||||||
|
pointerEvents: 'all',
|
||||||
|
position: 'absolute',
|
||||||
|
transform: `translate(-50%, -50%) translate(${path[1]}px,${path[2]}px)`
|
||||||
|
}"
|
||||||
|
class="nodrag nopan"
|
||||||
|
>
|
||||||
|
<el-popconfirm v-if="checkStore.nodesConnectable" title="确定要删除这条连线吗?" @confirm="removeEdges(id)">
|
||||||
|
<template #reference>
|
||||||
|
<el-icon class="edge-icon"><Delete /></el-icon>
|
||||||
|
</template>
|
||||||
|
</el-popconfirm>
|
||||||
|
</div>
|
||||||
|
</EdgeLabelRenderer>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
.edge-icon {
|
||||||
|
background: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
557
frontend/src/views/home/components/channelPairing.vue
Normal file
557
frontend/src/views/home/components/channelPairing.vue
Normal file
@@ -0,0 +1,557 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="flow-container" style="overflow: hidden; position: relative"
|
||||||
|
:style="{ height: vueFlowElement + 'px' }">
|
||||||
|
<!-- <el-button @click="logConnections">打印当前配对</el-button> -->
|
||||||
|
<VueFlow :nodes="nodes" :edges="edges" :connection-radius="30" :nodes-draggable="false" :dragging="false"
|
||||||
|
:zoom-on-scroll="false" :pan-on-drag="false" :disable-zoom-pan-on-connect="true"
|
||||||
|
:prevent-scrolling="true" :fit-view="true" :min-zoom="1" :max-zoom="1"
|
||||||
|
:nodesConnectable="checkStore.nodesConnectable" :elements-selectable="false" auto-connect
|
||||||
|
@connect="handleConnect" @connect-start="handleConnectStart" @connect-end="handleConnectEnd"
|
||||||
|
@pane-ready="onPaneReady" v-on:pane-mouse-move="false"></VueFlow>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 底部操作按钮 -->
|
||||||
|
<!-- <template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button @click="dialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleNext">下一步</el-button>
|
||||||
|
</div>
|
||||||
|
</template> -->
|
||||||
|
<!-- 手动检测-勾选检测项弹窗 -->
|
||||||
|
<!-- <SelectTestItemPopup ref="selectTestItemPopupRef" @openTestDialog="openTestDialog"></SelectTestItemPopup> -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { h, ref, onMounted, PropType } from 'vue'
|
||||||
|
import { VueFlow, useVueFlow } from '@vue-flow/core'
|
||||||
|
import { dialogBig } from '@/utils/elementBind'
|
||||||
|
import { Platform, Flag } from '@element-plus/icons-vue'
|
||||||
|
import { Device } from '@/api/device/interface/device'
|
||||||
|
import { StandardDevice } from '@/api/device/interface/standardDevice'
|
||||||
|
import SelectTestItemPopup from '@/views/home/components/selectTestItemPopup.vue'
|
||||||
|
import { ElMessage, stepProps } from 'element-plus'
|
||||||
|
import CustomEdge from './RemoveableEdge.vue' // 导入自定义连接线组件
|
||||||
|
import { jwtUtil } from '@/utils/jwtUtil'
|
||||||
|
import { useCheckStore } from '@/stores/modules/check'
|
||||||
|
const vueFlowElement = ref(442)
|
||||||
|
const checkStore = useCheckStore()
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const selectTestItemPopupRef = ref<InstanceType<typeof SelectTestItemPopup>>()
|
||||||
|
const testPopup = ref()
|
||||||
|
const dialogTitle = ref('手动检测')
|
||||||
|
const prop = defineProps({
|
||||||
|
devIdList: {
|
||||||
|
type: Array as any,
|
||||||
|
default: []
|
||||||
|
},
|
||||||
|
pqStandardDevList: {
|
||||||
|
type: Array as any,
|
||||||
|
default: []
|
||||||
|
},
|
||||||
|
planIdKey: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
deviceMonitor: {
|
||||||
|
type: Map as PropType<Map<string, any[]>>,
|
||||||
|
default: () => new Map()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 计算对话框高度
|
||||||
|
const dialogHeight = ref(600)
|
||||||
|
|
||||||
|
// 初始化 VueFlow,注册自定义连线类型
|
||||||
|
const { edges, setViewport } = useVueFlow({
|
||||||
|
edgeTypes: {
|
||||||
|
default: CustomEdge
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 初始化时锁定画布位置
|
||||||
|
const onPaneReady = () => {
|
||||||
|
setViewport({ x: 0, y: 0, zoom: 1 })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取公共的label渲染函数
|
||||||
|
const createLabel = (text: string, type: string, Key: number) => {
|
||||||
|
return h(
|
||||||
|
'div',
|
||||||
|
{
|
||||||
|
style: {
|
||||||
|
display: 'flex',
|
||||||
|
// flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
fontSize: '15px',
|
||||||
|
height: '75px',
|
||||||
|
// textAlign: 'center',
|
||||||
|
border: '1px solid #ccc',
|
||||||
|
borderRadius: '8px',
|
||||||
|
padding: '8px',
|
||||||
|
backgroundColor: '#f9f9f9'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
// h(Platform, {
|
||||||
|
// style: {
|
||||||
|
// height: '40px',
|
||||||
|
// marginBottom: '4px',
|
||||||
|
// color: '#526ade'
|
||||||
|
// }
|
||||||
|
// }),
|
||||||
|
h('img', {
|
||||||
|
src:
|
||||||
|
Key == 2
|
||||||
|
? new URL('@/assets/images/inspected1.jpg', import.meta.url).href
|
||||||
|
: new URL('@/assets/images/inspected2.png', import.meta.url).href,
|
||||||
|
// alt: '设备图标',
|
||||||
|
style: {
|
||||||
|
width: '50px',
|
||||||
|
marginRight: '5px'
|
||||||
|
// 保持原有的颜色风格,如果需要可以调整滤镜
|
||||||
|
// filter: 'invert(35%) sepia(65%) saturate(300%) hue-rotate(210deg)'
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
h('div', { style: { textAlign: 'left' } }, ['设备名称:' + text, h('br'), '设备类型:' + type])
|
||||||
|
// h('div', null, '设备名称:' + text),
|
||||||
|
// h('div', null, '设备类型:' + type)
|
||||||
|
]
|
||||||
|
) as any
|
||||||
|
}
|
||||||
|
|
||||||
|
const createLabel3 = (text: string) => {
|
||||||
|
return h(
|
||||||
|
'div',
|
||||||
|
{
|
||||||
|
style: {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
fontSize: '15px',
|
||||||
|
height: '35px',
|
||||||
|
textAlign: 'center',
|
||||||
|
border: '1px solid #ccc',
|
||||||
|
borderRadius: '8px',
|
||||||
|
// padding: '8px',
|
||||||
|
backgroundColor: '#f9f9f9'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
h('div', {
|
||||||
|
style: {
|
||||||
|
width: '8px',
|
||||||
|
marginRight: '4px',
|
||||||
|
color: '#526ade'
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
text
|
||||||
|
]
|
||||||
|
) as any
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleConnectStart = (params: any) => {
|
||||||
|
onPaneReady()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleConnectEnd = (params: any) => {
|
||||||
|
onPaneReady()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleConnect = (params: any) => {
|
||||||
|
console.log('连接信息:', params)
|
||||||
|
const sourceNode = nodes.value.find(node => node.id === params.source)
|
||||||
|
const targetNode = nodes.value.find(node => node.id === params.target)
|
||||||
|
|
||||||
|
// 连接规则验证
|
||||||
|
const isValidConnection = sourceNode?.type === 'input' && targetNode?.type === 'output'
|
||||||
|
|
||||||
|
if (!isValidConnection) {
|
||||||
|
removeEdge(params)
|
||||||
|
ElMessage.warning('只能从被检通道连接到标准通道')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过滤掉当前连接,检查是否还有重复的
|
||||||
|
const existingEdges = edges.value.filter(edge => edge.source === params.source || edge.target === params.target)
|
||||||
|
|
||||||
|
// 如果同源或同目标的连接超过1个,说明有重复
|
||||||
|
if (existingEdges.length > 1) {
|
||||||
|
const duplicateSource = existingEdges.filter(edge => edge.source === params.source).length > 1
|
||||||
|
const duplicateTarget = existingEdges.filter(edge => edge.target === params.target).length > 1
|
||||||
|
|
||||||
|
if (duplicateSource) {
|
||||||
|
removeEdge(params)
|
||||||
|
ElMessage.warning('该被检通道已经连接,不能重复连接')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (duplicateTarget) {
|
||||||
|
removeEdge(params)
|
||||||
|
ElMessage.warning('该标准通道已经连接,不能重复连接')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除不合法连接
|
||||||
|
const removeEdge = (params: any) => {
|
||||||
|
const edgeIndex = edges.value.findIndex(edge => edge.source === params.source && edge.target === params.target)
|
||||||
|
if (edgeIndex !== -1) {
|
||||||
|
edges.value.splice(edgeIndex, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const nodes = ref([])
|
||||||
|
const planId = ref('')
|
||||||
|
const devIds = ref<string[]>()
|
||||||
|
const standardDevIds = ref<string[]>()
|
||||||
|
|
||||||
|
const open = async () => {
|
||||||
|
console.log('开始打开通道配对')
|
||||||
|
edges.value = []
|
||||||
|
devIds.value = prop.devIdList.map(d => d.id)
|
||||||
|
standardDevIds.value = prop.pqStandardDevList.map(d => d.id)
|
||||||
|
planId.value = prop.planIdKey
|
||||||
|
|
||||||
|
nodes.value = createNodes(prop.devIdList, prop.pqStandardDevList, prop.deviceMonitor)
|
||||||
|
dialogVisible.value = true
|
||||||
|
onPaneReady()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
open()
|
||||||
|
})
|
||||||
|
const handleNext = async () => {
|
||||||
|
if (edges.value.length === 0) {
|
||||||
|
ElMessage.warning('请先完成通道配对')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// const sourceKey = edge.source.replace('被检通道-', '').replace('-', '_');
|
||||||
|
let chnNumList: string[] = []
|
||||||
|
await edges.value.forEach(edge => {
|
||||||
|
const match = edge.source.split('-')
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
chnNumList.push(match[2])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const connections = edges.value.reduce(
|
||||||
|
(map, edge) => {
|
||||||
|
// 从source中提取设备ID和通道号: 被检通道-{deviceId}-{channelNum} => {deviceId}-{channelNum}
|
||||||
|
const sourceKey = edge.source.replace('被检通道-', '').replace('-', '_')
|
||||||
|
|
||||||
|
// 从target中提取设备ID和通道号: 标准通道-{deviceId}-{channelNum} => {deviceId}-{channelNum}
|
||||||
|
const targetValue = edge.target.replace('标准通道-', '').replace('-', '_')
|
||||||
|
|
||||||
|
map[sourceKey] = targetValue
|
||||||
|
return map
|
||||||
|
},
|
||||||
|
{} as Record<string, string>
|
||||||
|
)
|
||||||
|
await generateChannelMapping()
|
||||||
|
|
||||||
|
await checkStore.setChnNum(chnNumList)
|
||||||
|
return {
|
||||||
|
title: dialogTitle.value,
|
||||||
|
mapping: channelMapping.value,
|
||||||
|
plan: planId.value,
|
||||||
|
login: jwtUtil.getLoginName(),
|
||||||
|
devIdsArray: devIds.value,
|
||||||
|
standardDevIdsArray: standardDevIds.value,
|
||||||
|
pair: connections
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const openTestDialog = async () => {
|
||||||
|
// 转换连接信息,只保留设备ID和通道号
|
||||||
|
const connections = edges.value.reduce(
|
||||||
|
(map, edge) => {
|
||||||
|
// 从source中提取设备ID和通道号: 被检通道-{deviceId}-{channelNum} => {deviceId}-{channelNum}
|
||||||
|
const sourceKey = edge.source.replace('被检通道-', '').replace('-', '_')
|
||||||
|
|
||||||
|
// 从target中提取设备ID和通道号: 标准通道-{deviceId}-{channelNum} => {deviceId}-{channelNum}
|
||||||
|
const targetValue = edge.target.replace('标准通道-', '').replace('-', '_')
|
||||||
|
|
||||||
|
map[sourceKey] = targetValue
|
||||||
|
return map
|
||||||
|
},
|
||||||
|
{} as Record<string, string>
|
||||||
|
)
|
||||||
|
|
||||||
|
generateChannelMapping()
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
testPopup.value?.open(
|
||||||
|
dialogTitle.value,
|
||||||
|
channelMapping.value,
|
||||||
|
planId.value,
|
||||||
|
jwtUtil.getLoginName(),
|
||||||
|
devIds.value,
|
||||||
|
standardDevIds.value,
|
||||||
|
connections
|
||||||
|
)
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换 edges.value 为 channelMapping 格式
|
||||||
|
const channelMapping = ref<Record<string, Record<string, string>>>({})
|
||||||
|
|
||||||
|
// 生成映射关系的方法
|
||||||
|
const generateChannelMapping = () => {
|
||||||
|
const mapping: Record<string, Record<string, string>> = {}
|
||||||
|
|
||||||
|
edges.value.forEach(edge => {
|
||||||
|
// 解析 source 节点信息(被检通道)
|
||||||
|
const sourceParts = edge.source.split('-')
|
||||||
|
const sourceDeviceId = sourceParts[1]
|
||||||
|
const sourceChannel = sourceParts[2]
|
||||||
|
|
||||||
|
// 解析 target 节点信息(标准通道)
|
||||||
|
const targetParts = edge.target.split('-')
|
||||||
|
const targetDeviceId = targetParts[1]
|
||||||
|
const targetChannel = targetParts[2]
|
||||||
|
|
||||||
|
// 查找对应的节点以获取显示名称
|
||||||
|
const sourceDeviceNode = nodes.value.find(node => node.id === sourceDeviceId)
|
||||||
|
|
||||||
|
const targetDeviceNode = nodes.value.find(node => node.id === targetDeviceId)
|
||||||
|
|
||||||
|
if (sourceDeviceNode && targetDeviceNode) {
|
||||||
|
// 提取设备显示文本
|
||||||
|
const sourceDeviceText = sourceDeviceNode.data.label.children[1].children[0].children
|
||||||
|
const targetDeviceText = targetDeviceNode.data.label.children[1].children[0].children
|
||||||
|
|
||||||
|
// 构造键名 - 现在以标准设备为键
|
||||||
|
const targetKey = `${targetDeviceText}`.replace('设备名称:', '')
|
||||||
|
const sourceValue = `${sourceDeviceText}通道${sourceChannel}`.replace('设备名称:', '')
|
||||||
|
|
||||||
|
// 初始化对象
|
||||||
|
if (!mapping[targetKey]) {
|
||||||
|
mapping[targetKey] = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加映射关系 - 标准设备通道 -> 被检设备信息
|
||||||
|
mapping[targetKey][`通道${targetChannel}`] = sourceValue
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
channelMapping.value = mapping
|
||||||
|
}
|
||||||
|
|
||||||
|
const createNodes = (device: Device.ResPqDev[], standardDev: StandardDevice.ResPqStandardDevice[], deviceMonitor: Map<string, any[]>) => {
|
||||||
|
const channelCounts: Record<string, number> = {}
|
||||||
|
device.forEach(device => {
|
||||||
|
channelCounts[device.id] = device.devChns || 0
|
||||||
|
})
|
||||||
|
|
||||||
|
const inspectionDevices = device.map(d => ({
|
||||||
|
id: d.id,
|
||||||
|
name: d.name,
|
||||||
|
type: 'normal',
|
||||||
|
deviceType: d.devType
|
||||||
|
}))
|
||||||
|
|
||||||
|
const channelCounts2: Record<string, number> = {}
|
||||||
|
standardDev.forEach(dev => {
|
||||||
|
const channelList = dev.inspectChannel ? dev.inspectChannel.split(',') : []
|
||||||
|
channelCounts2[dev.id] = channelList.length
|
||||||
|
})
|
||||||
|
|
||||||
|
const standardDevices = standardDev.map(d => ({
|
||||||
|
id: d.id,
|
||||||
|
name: d.name,
|
||||||
|
type: 'normal',
|
||||||
|
deviceType: d.devType
|
||||||
|
}))
|
||||||
|
|
||||||
|
const newNodes: any[] = []
|
||||||
|
const deviceChannelGroups: { deviceId: string; centerY: number }[] = []
|
||||||
|
const standardChannelGroups: any[] = []
|
||||||
|
|
||||||
|
const deviceWidth = 50
|
||||||
|
const inputChannelX = 350
|
||||||
|
const outputChannelX = 1050
|
||||||
|
const standardWidth = 1170
|
||||||
|
|
||||||
|
// 添加被检通道
|
||||||
|
// let currentYPosition = 50; // 初始Y位置
|
||||||
|
let actualChannelsTotalLength = 0;
|
||||||
|
|
||||||
|
for (const [deviceId, count] of Object.entries(channelCounts)) {
|
||||||
|
// 直接计算当前设备的通道数并累加,无需完整构建数组
|
||||||
|
actualChannelsTotalLength += deviceMonitor.has(deviceId)
|
||||||
|
? (deviceMonitor.get(deviceId) || []).length
|
||||||
|
: count;
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentYPosition = (vueFlowElement.value - 60 * actualChannelsTotalLength) / 2; // 初始Y位置
|
||||||
|
const deviceSpacing = 30; // 设备间的垂直间距
|
||||||
|
|
||||||
|
Object.entries(channelCounts).forEach(([deviceId, count]) => {
|
||||||
|
// 从deviceMonitor中获取实际通道信息
|
||||||
|
let actualChannels = []; // 存储实际的通道号
|
||||||
|
|
||||||
|
// 如果deviceMonitor中有该设备的数据,则使用实际监测点信息
|
||||||
|
if (deviceMonitor.has(deviceId)) {
|
||||||
|
const monitorPoints = deviceMonitor.get(deviceId) || [];
|
||||||
|
// 提取监测点的num值作为通道号
|
||||||
|
actualChannels = monitorPoints.map(point => point.num);
|
||||||
|
//console.log('deviceId', deviceId, '实际通道号:', actualChannels, '监测点:', monitorPoints);
|
||||||
|
} else {
|
||||||
|
// 如果没有monitor数据,默认使用连续的通道号
|
||||||
|
actualChannels = Array.from({ length: count }, (_, i) => i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const yPosition = currentYPosition;
|
||||||
|
// 遍历实际通道号而不是连续的数字
|
||||||
|
actualChannels.forEach((channelNum, index) => {
|
||||||
|
const channelId = `被检通道-${deviceId}-${channelNum}`;
|
||||||
|
newNodes.push({
|
||||||
|
id: channelId,
|
||||||
|
type: 'input',
|
||||||
|
data: { label: createLabel3(`被检通道${channelNum}`) },
|
||||||
|
position: { x: inputChannelX, y: yPosition + index * 50 },
|
||||||
|
sourcePosition: 'right',
|
||||||
|
style: { width: '120px', border: 'none', boxShadow: 'none' }
|
||||||
|
});
|
||||||
|
|
||||||
|
deviceChannelGroups.push({
|
||||||
|
deviceId,
|
||||||
|
centerY: 0
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新currentYPosition,为下一台设备留出空间
|
||||||
|
// 每台设备需要的空间 = 实际通道数 * 50 + 设备间距
|
||||||
|
currentYPosition += actualChannels.length * 50 + deviceSpacing;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加标准通道
|
||||||
|
// let currentYPosition2 = 50; // 初始Y位置
|
||||||
|
let totalCount = 0;
|
||||||
|
// 遍历所有条目并累加 count 值
|
||||||
|
Object.entries(channelCounts2).forEach(([deviceId, count]) => {
|
||||||
|
totalCount += count;
|
||||||
|
});
|
||||||
|
|
||||||
|
let currentYPosition2 = (vueFlowElement.value - 60 * totalCount) / 2; // 初始Y位置; // 初始Y位置
|
||||||
|
const standardDeviceSpacing = 30; // 标准设备间的垂直间距
|
||||||
|
|
||||||
|
Object.entries(channelCounts2).forEach(([deviceId, count]) => {
|
||||||
|
const yPosition2 = currentYPosition2;
|
||||||
|
|
||||||
|
for (let i = 1; i <= count; i++) {
|
||||||
|
const channelId = `标准通道-${deviceId}-${i}`;
|
||||||
|
newNodes.push({
|
||||||
|
id: channelId,
|
||||||
|
type: 'output',
|
||||||
|
data: { label: createLabel3(`标准通道${i}`) },
|
||||||
|
position: { x: outputChannelX, y: yPosition2 + (i - 1) * 50 },
|
||||||
|
targetPosition: 'left',
|
||||||
|
style: { width: '120px', border: 'none', boxShadow: 'none' }
|
||||||
|
});
|
||||||
|
|
||||||
|
standardChannelGroups.push({
|
||||||
|
deviceId,
|
||||||
|
centerY: 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新currentYPosition2,为下一台标准设备留出空间
|
||||||
|
currentYPosition2 += count * 50 + standardDeviceSpacing;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 添加被检设备
|
||||||
|
// let deviceCurrentYPosition = 50; // 与通道计算保持一致的初始位置
|
||||||
|
let deviceCurrentYPosition = (vueFlowElement.value - 60 * actualChannelsTotalLength) / 2; // 与通道计算保持一致的初始位置
|
||||||
|
let lastDeviceId = ''; // 记录上一个处理的设备ID
|
||||||
|
|
||||||
|
deviceChannelGroups.forEach(({ deviceId, centerY }) => {
|
||||||
|
const device = inspectionDevices.find(d => d.id === deviceId)
|
||||||
|
if (device) {
|
||||||
|
// 只有当设备ID变化时才计算新位置
|
||||||
|
if (lastDeviceId !== deviceId) {
|
||||||
|
// 计算该设备对应的实际通道数量
|
||||||
|
let actualChannelCount = channelCounts[deviceId] || 0;
|
||||||
|
|
||||||
|
// 如果deviceMonitor中有该设备的数据,则使用实际监测点数量
|
||||||
|
if (deviceMonitor.has(deviceId)) {
|
||||||
|
const monitorPoints = deviceMonitor.get(deviceId) || [];
|
||||||
|
actualChannelCount = monitorPoints.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算设备高度居中位置 - 基于该设备组的实际位置
|
||||||
|
const deviceCenterY = deviceCurrentYPosition + (actualChannelCount * 50) / 2 - 50
|
||||||
|
newNodes.push({
|
||||||
|
id: device.id,
|
||||||
|
data: { label: createLabel(device.name, device.deviceType, 1) },
|
||||||
|
position: { x: deviceWidth, y: deviceCenterY },
|
||||||
|
class: 'no-handle-node',
|
||||||
|
style: { width: '300px', border: 'none', boxShadow: 'none' }
|
||||||
|
})
|
||||||
|
|
||||||
|
// 更新位置为下一台设备的起始位置
|
||||||
|
deviceCurrentYPosition += actualChannelCount * 50 + 30
|
||||||
|
// 更新上一个设备ID
|
||||||
|
lastDeviceId = deviceId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 添加标准设备
|
||||||
|
// let standardDeviceCurrentYPosition = 50; // 与标准通道计算保持一致的初始位置
|
||||||
|
let standardDeviceCurrentYPosition = (vueFlowElement.value - 60 * totalCount) / 2; // 与标准通道计算保持一致的初始位置
|
||||||
|
let lastStandardDeviceId = ''; // 记录上一个处理的标准设备ID
|
||||||
|
|
||||||
|
standardChannelGroups.forEach(({ deviceId, centerY }) => {
|
||||||
|
const device = standardDevices.find(d => d.id === deviceId)
|
||||||
|
if (device) {
|
||||||
|
// 只有当设备ID变化时才计算新位置
|
||||||
|
if (lastStandardDeviceId !== deviceId) {
|
||||||
|
// 计算该标准设备对应的通道数量
|
||||||
|
const channelCount = channelCounts2[deviceId] || 0
|
||||||
|
// 计算设备高度居中位置 - 基于该设备组的实际位置
|
||||||
|
const deviceCenterY = standardDeviceCurrentYPosition + (channelCount * 50) / 2 - 50
|
||||||
|
newNodes.push({
|
||||||
|
id: device.id,
|
||||||
|
data: { label: createLabel(device.name, device.deviceType, 2) },
|
||||||
|
position: { x: standardWidth, y: deviceCenterY },
|
||||||
|
class: 'no-handle-node',
|
||||||
|
style: { width: '300px', border: 'none', boxShadow: 'none' }
|
||||||
|
})
|
||||||
|
|
||||||
|
// 更新位置为下一台标准设备的起始位置
|
||||||
|
standardDeviceCurrentYPosition += channelCount * 50 + 30
|
||||||
|
// 更新上一个标准设备ID
|
||||||
|
lastStandardDeviceId = deviceId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
//页面高度取决于设备通道
|
||||||
|
// dialogHeight.value = Math.max(yPosition.value, yPosition2.value)
|
||||||
|
|
||||||
|
return newNodes
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ open, handleNext })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.flow-container {
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vue-flow__node.no-handle-node .vue-flow__handle {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,942 +0,0 @@
|
|||||||
<template>
|
|
||||||
<el-dialog v-model='dialogVisible' title="系数校准" v-bind="dialogBig" width="1550px" @close="handleCancel">
|
|
||||||
<div class="test-dialog" >
|
|
||||||
<div class="dialog-content">
|
|
||||||
<div class="right-title">
|
|
||||||
<!-- <div>系数校准表</div> -->
|
|
||||||
<div>{{ outputDsc }}</div>
|
|
||||||
<div>
|
|
||||||
<span style=" font-size: 18px;font-weight: 600;">
|
|
||||||
设备已合格 <span style="color: #67C23A">{{ qualified }}</span> 台/共 <span style="color: green">{{ total }}</span>
|
|
||||||
台
|
|
||||||
</span>
|
|
||||||
<!-- <el-button type="primary" loading
|
|
||||||
v-if="activeIndex > 0 && activeIndex < activeTotalNum">通道系数已校准3台/共3台</el-button>
|
|
||||||
<el-button type="primary" :disabled="true" v-if="activeIndex === activeTotalNum">通道系数已校准3台/共3台</el-button> -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="container">
|
|
||||||
<div class="dialog-left">
|
|
||||||
<el-steps direction="vertical" :active="active" :process-status="currentStepStatus" finish-status="success">
|
|
||||||
<el-step title="开始" />
|
|
||||||
<el-step>
|
|
||||||
<template #title>
|
|
||||||
<span>大电压/电流系数下装</span><br/>
|
|
||||||
<span class="spanStyle">源输出为:</span><br/>
|
|
||||||
<span class="spanStyle" v-if="active > 0">{{big_V_Download}}</span><br/>
|
|
||||||
<span class="spanStyle" v-if="active > 0">{{big_I_Download}}</span>
|
|
||||||
<el-icon v-if="active === 1 " class="loading-box"><el-icon-loading /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-step>
|
|
||||||
<el-step>
|
|
||||||
<template #title>
|
|
||||||
<span>小电压/电流系数下装</span><br/>
|
|
||||||
<span class="spanStyle">源输出为:</span><br/>
|
|
||||||
<span class="spanStyle" v-if="active > 1">{{ small_V_Download }}</span><br/>
|
|
||||||
<span class="spanStyle" v-if="active > 1">{{ small_I_Download }}</span>
|
|
||||||
<el-icon v-if="active === 2" class="loading-box"><el-icon-loading /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-step>
|
|
||||||
<el-step>
|
|
||||||
<template #title>
|
|
||||||
<span>大电压/电流校准</span><br/>
|
|
||||||
<span class="spanStyle">源输出为:</span><br/>
|
|
||||||
<span class="spanStyle" v-if="active > 2">{{ big_V_Adjust }}</span><br/>
|
|
||||||
<span class="spanStyle" v-if="active > 2">{{ big_I_Adjust }}</span>
|
|
||||||
<el-icon v-if="active === 3" class="loading-box"><el-icon-loading /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-step>
|
|
||||||
<el-step>
|
|
||||||
<template #title>
|
|
||||||
<span>小电压/电流校准</span><br/>
|
|
||||||
<span class="spanStyle">源输出为:</span><br/>
|
|
||||||
<span class="spanStyle" v-if="active > 3">{{ small_V_Adjust }}</span><br/>
|
|
||||||
<span class="spanStyle" v-if="active > 3">{{ small_I_Adjust }}</span>
|
|
||||||
<el-icon v-if="active === 4" class="loading-box"><el-icon-loading /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-step>
|
|
||||||
<el-step title="结束"/>
|
|
||||||
</el-steps>
|
|
||||||
</div>
|
|
||||||
<div class="right-content">
|
|
||||||
<el-tabs type="border-card" v-model="editableTabsValue" :active-index="String(activeIndex)" >
|
|
||||||
<el-tab-pane v-for="(device, index) in name" :key="index" :label="device">
|
|
||||||
<template #label>
|
|
||||||
<span class="custom-tabs-label">
|
|
||||||
<span>{{ device }}</span>
|
|
||||||
<el-icon v-if="errorStates[index]" class ="icon-style" >
|
|
||||||
<Failed />
|
|
||||||
</el-icon>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
<channelsTestTable
|
|
||||||
:tableData="getTableDataForChannel(index)"
|
|
||||||
:big_V_loading="big_V_loadingStates"
|
|
||||||
:curV="CurV">
|
|
||||||
</channelsTestTable>
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<template #footer>
|
|
||||||
<div class="dialog-footer">
|
|
||||||
<el-button type="primary" @click="handleSubmit" :disabled="isButtonDisabled">开始系数校准</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
<script lang="tsx" setup name="channelsTest">
|
|
||||||
import { type Device } from '@/api/device/interface/device';
|
|
||||||
import { SuccessFilled, Failed, Message, MessageBox } from '@element-plus/icons-vue'
|
|
||||||
import { type Ref, ref, toRef, watch } from 'vue'
|
|
||||||
import {dialogBig} from '@/utils/elementBind'
|
|
||||||
import { ElMessage, ElMessageBox, TabsPaneContext } from 'element-plus';
|
|
||||||
import { getCoefficientCheck } from '@/api/home/channelsTest/index'
|
|
||||||
import type { ChannelsTest } from '@/api/home/interface/channelsTest';
|
|
||||||
import type { Plan } from '@/api/plan/interface';
|
|
||||||
import { fa } from 'element-plus/es/locale';
|
|
||||||
const activeIndex = ref(0)
|
|
||||||
const activeTotalNum = ref(4)
|
|
||||||
const qualified = ref(0)
|
|
||||||
const outputDsc = ref('电压误差为:±0.1Un%; 电流误差为:±0.5%')
|
|
||||||
const total = ref(0)
|
|
||||||
const dialogVisible = ref(false)
|
|
||||||
const active = ref(0)
|
|
||||||
let timer1: NodeJS.Timeout | null = null; // 声明并初始化 timer1
|
|
||||||
let timer2: NodeJS.Timeout | null = null; // 同样声明并初始化 timer2
|
|
||||||
const name = ref<string[]>([])//系数校准所选设备名字数组
|
|
||||||
const channel = ref<number[]>([])//系数校准所选设备通道数组
|
|
||||||
const devIdArray = ref<string[]>([])//系数校准所选设备ID数组
|
|
||||||
const select_Plan = ref<Plan.ReqPlan>()
|
|
||||||
const planId = ref('')
|
|
||||||
const isButtonDisabled = ref(false);
|
|
||||||
const CurV= ref<number>()//额定电压
|
|
||||||
// 在 setup 函数中
|
|
||||||
const errorStates = ref(new Array(name.value.length).fill(false));
|
|
||||||
//const loadingStates = ref(new Array(name.value.length).fill(false)); // 初始化 loading 状态
|
|
||||||
const big_V_loadingStates = ref(false); // 初始化 大电压大电流下装loading 状态
|
|
||||||
const small_V_loadingStates = ref(false); // 初始化 小电压小电流下装loading 状态
|
|
||||||
const big_V_loadingStates2 = ref(false); // 初始化 大电压大电流校准loading 状态
|
|
||||||
const small_V_loadingStates2 = ref(false); // 初始化 小电压小电流校准loading 状态
|
|
||||||
const editableTabsValue = ref('0')
|
|
||||||
const big_V_Download = ref('')
|
|
||||||
const big_I_Download = ref('')
|
|
||||||
const small_V_Download = ref('')
|
|
||||||
const small_I_Download = ref('')
|
|
||||||
const big_V_Adjust = ref('')
|
|
||||||
const big_I_Adjust = ref('')
|
|
||||||
const small_V_Adjust = ref('')
|
|
||||||
const small_I_Adjust = ref('')
|
|
||||||
const props = defineProps({
|
|
||||||
webMsgSend: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const tableDataMap = new Map<number, Ref<ChannelsTest.CoefficientVO[]>>([]);
|
|
||||||
const currentStepStatus = ref<'error' | 'finish' | 'wait' | 'success' | 'process'>('finish');
|
|
||||||
const webMsgSend = toRef(props, 'webMsgSend');
|
|
||||||
|
|
||||||
watch(webMsgSend,function (newValue,oldValue){
|
|
||||||
if (newValue.code == 10520) {
|
|
||||||
ElMessageBox.alert('报文解析异常!', '初始化失败', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
TableInit();
|
|
||||||
} else if (newValue.code == 10521) {
|
|
||||||
ElMessageBox.alert('程控源參数有误!', '初始化失败', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
TableInit();
|
|
||||||
} else if (newValue.code == 10522) {
|
|
||||||
ElMessageBox.alert('测试项解析有误!', '初始化失败', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
TableInit();
|
|
||||||
} else if (newValue.code == 10523) {
|
|
||||||
ElMessageBox.alert('源连接失败!', '初始化失败', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
TableInit();
|
|
||||||
} else if (newValue.code == 10524) {
|
|
||||||
ElMessageBox.alert('获取源控制权失败!', '初始化失败', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
TableInit();
|
|
||||||
} else if (newValue.code == 10525) {
|
|
||||||
ElMessageBox.alert('重置源失败!', '初始化失败', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
TableInit();
|
|
||||||
} else if (newValue.code == 10527) {
|
|
||||||
ElMessageBox.alert('源未进行初始化!', '初始化失败', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
TableInit();
|
|
||||||
} else if (newValue.code == 10528) {
|
|
||||||
ElMessageBox.alert('目标源有误(该用户已控制其他源,在关闭前无法操作新的源)!', '初始化失败', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
TableInit();
|
|
||||||
} else if (newValue.code == 10529) {
|
|
||||||
ElMessageBox.alert('源状态有误,无法响应报文(例如源处于输出状态,无法响应初始化报文)!', '初始化失败', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
TableInit();
|
|
||||||
} else if (newValue.code == 10550) {
|
|
||||||
ElMessageBox.alert(`${newValue.data}设备连接异常!`, '初始化失败', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
TableInit();
|
|
||||||
} else if (newValue.code == 10551) {
|
|
||||||
ElMessageBox.alert(`${newValue.data}设备触发报告异常!`, '初始化失败', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
TableInit();
|
|
||||||
} else if (newValue.code == 10552) { //todo 10552之后还会发送消息吗?
|
|
||||||
ElMessageBox.alert('存在已经初始化步骤,执行自动关闭,请重新发起检测', '初始化失败', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
TableInit();
|
|
||||||
} else {
|
|
||||||
switch (newValue.requestId){
|
|
||||||
case 'yjc_ytxjy':
|
|
||||||
switch (newValue.operateCode) {
|
|
||||||
case'INIT_GATHER':
|
|
||||||
if(newValue.code ==-1){
|
|
||||||
ElMessageBox.alert('源未知异常', '初始化失败', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
TableInit();
|
|
||||||
}else if (newValue.code == 10523) {
|
|
||||||
ElMessageBox.alert('源连接失败', '源连接失败', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
TableInit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'YJC_xujy':
|
|
||||||
switch (newValue.operateCode) {
|
|
||||||
case 'OPER_GATHER':
|
|
||||||
if (newValue.code == 10552) {
|
|
||||||
ElMessageBox.alert('存在已经初始化步骤,执行自动关闭,请重新发起检测', '初始化失败', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
TableInit();
|
|
||||||
} else if (newValue.code == 10520) {
|
|
||||||
ElMessageBox.alert('解析报文异常,执行自动关闭,请重新发起检测', '解析报文异常', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
TableInit();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'DATA_REQUEST$02':
|
|
||||||
if (newValue.code == 25003) {
|
|
||||||
ElMessageBox.alert('相序校验未通过,执行自动关闭,请重新发起检测', '相序校验未通过', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
TableInit();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'yjc_sbtxjy':
|
|
||||||
switch (newValue.operateCode) {
|
|
||||||
case 'INIT_GATHER$01':
|
|
||||||
if (newValue.code == 10550) {
|
|
||||||
ElMessageBox.alert('设备连接异常', '设备连接异常', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
TableInit();
|
|
||||||
}else if (newValue.code == 10551) {
|
|
||||||
ElMessageBox.alert('设备触发报告异常', '设备触发报告异常', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
TableInit();
|
|
||||||
} else if (newValue.code == 10552) {
|
|
||||||
ElMessageBox.alert('存在已经初始化步骤,执行自动关闭,请重新发起检测', '初始化失败', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
TableInit();
|
|
||||||
} else if (newValue.code == 10520) {
|
|
||||||
ElMessageBox.alert('解析报文异常,执行自动关闭,请重新发起检测', '解析报文异常', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
TableInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'Coefficient_Check':
|
|
||||||
console.log("Coefficient_Checkactive",active.value);
|
|
||||||
switch (newValue.operateCode){
|
|
||||||
case 'big_end'://大电压,电流下装
|
|
||||||
active.value++;
|
|
||||||
big_V_Download.value = 'Ua=Ub=Uc=' + newValue.data.devVolt + 'V';
|
|
||||||
big_I_Download.value = 'Ia=Ib=Ic=' + newValue.data.devCurr + 'A';
|
|
||||||
|
|
||||||
tableLoading('small','系数下装')
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
switch (newValue.operateCode){
|
|
||||||
case 'small_end'://小电压,电流下装
|
|
||||||
active.value++;
|
|
||||||
small_V_Download.value = 'Ua=Ub=Uc=' + newValue.data.devVolt + 'V';
|
|
||||||
small_I_Download.value = 'Ia=Ib=Ic=' + newValue.data.devCurr + 'A';
|
|
||||||
|
|
||||||
tableLoading('big','系数校准')
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
switch (newValue.operateCode){
|
|
||||||
case 'big_comp_end'://大电压,电流校准
|
|
||||||
active.value++;
|
|
||||||
big_V_Adjust.value = 'Ua=Ub=Uc=' + newValue.data.devVolt + 'V';
|
|
||||||
big_I_Adjust.value = 'Ia=Ib=Ic=' + newValue.data.devCurr + 'A';
|
|
||||||
|
|
||||||
tableLoading('small','系数校准')
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
switch (newValue.operateCode){
|
|
||||||
case 'small_comp_end'://小电压,电流校准
|
|
||||||
active.value++;
|
|
||||||
small_V_Adjust.value = 'Ua=Ub=Uc=' + newValue.data.devVolt + 'V';
|
|
||||||
small_I_Adjust.value = 'Ia=Ib=Ic=' + newValue.data.devCurr + 'A';
|
|
||||||
active.value++;
|
|
||||||
|
|
||||||
for (let i = 0; i < name.value.length; i++) {
|
|
||||||
const currentDataRef = tableDataMap.get(i);
|
|
||||||
if (currentDataRef) {
|
|
||||||
const currentData = currentDataRef.value;
|
|
||||||
// 检查当前数据中有无不合格字段
|
|
||||||
const hasError = checkForErrors(currentData);
|
|
||||||
if (hasError) {
|
|
||||||
} else {
|
|
||||||
qualified.value++;
|
|
||||||
}
|
|
||||||
updateErrorState(i, hasError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//editableTabsValue.value = (tabNumber.value).toString();//显示下一个tab
|
|
||||||
isButtonDisabled.value = false; // 恢复按钮
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
switch (newValue.operateCode){
|
|
||||||
case 'DATA_CHNFACTOR$02'://表格
|
|
||||||
// 输出 key 为 0 的数组中的第一条 ChannelsTest.CoefficientVO 对象
|
|
||||||
for (let i = 0; i < name.value.length; i++) {
|
|
||||||
const targetArrayRef = tableDataMap.get(i);
|
|
||||||
if (targetArrayRef) {
|
|
||||||
const targetArray = targetArrayRef.value;
|
|
||||||
if (targetArray.length > 0) {
|
|
||||||
const firstCoefficientVO = targetArray.find(item => item.monitorNum === newValue.data.monitorNum &&
|
|
||||||
item.type === newValue.data.type &&
|
|
||||||
item.desc === newValue.data.desc &&
|
|
||||||
item.devName === newValue.data.devName);
|
|
||||||
if (firstCoefficientVO) { // 检查 firstCoefficientVO 是否存在
|
|
||||||
firstCoefficientVO.aVuData = parseFloat(newValue.data.aVuData).toFixed(4);
|
|
||||||
|
|
||||||
if (!isNaN(parseFloat(newValue.data.aVuXi)) && isFinite(newValue.data.aVuXi)) {
|
|
||||||
firstCoefficientVO.aVuXi = (parseFloat(newValue.data.aVuXi) / 10000).toFixed(4);
|
|
||||||
} else {
|
|
||||||
firstCoefficientVO.aVuXi = newValue.data.aVuXi;
|
|
||||||
}
|
|
||||||
|
|
||||||
firstCoefficientVO.bVuData = parseFloat(newValue.data.bVuData).toFixed(4);
|
|
||||||
|
|
||||||
if (!isNaN(parseFloat(newValue.data.bVuXi)) && isFinite(newValue.data.bVuXi)) {
|
|
||||||
firstCoefficientVO.bVuXi = (parseFloat(newValue.data.bVuXi) / 10000).toFixed(4);
|
|
||||||
} else {
|
|
||||||
firstCoefficientVO.bVuXi = newValue.data.bVuXi;
|
|
||||||
}
|
|
||||||
|
|
||||||
firstCoefficientVO.cVuData = parseFloat(newValue.data.cVuData).toFixed(4);
|
|
||||||
|
|
||||||
if (!isNaN(parseFloat(newValue.data.cVuXi)) && isFinite(newValue.data.cVuXi)) {
|
|
||||||
firstCoefficientVO.cVuXi = (parseFloat(newValue.data.cVuXi) / 10000).toFixed(4);
|
|
||||||
} else {
|
|
||||||
firstCoefficientVO.cVuXi = newValue.data.cVuXi;
|
|
||||||
}
|
|
||||||
|
|
||||||
firstCoefficientVO.aIeData = parseFloat(newValue.data.aIeData).toFixed(4);
|
|
||||||
|
|
||||||
if (!isNaN(parseFloat(newValue.data.aIeXi)) && isFinite(newValue.data.aIeXi)) {
|
|
||||||
firstCoefficientVO.aIeXi = (parseFloat(newValue.data.aIeXi) / 10000).toFixed(4);
|
|
||||||
} else {
|
|
||||||
firstCoefficientVO.aIeXi = newValue.data.aIeXi;
|
|
||||||
}
|
|
||||||
|
|
||||||
firstCoefficientVO.bIeData = parseFloat(newValue.data.bIeData).toFixed(4);
|
|
||||||
|
|
||||||
if (!isNaN(parseFloat(newValue.data.bIeXi)) && isFinite(newValue.data.bIeXi)) {
|
|
||||||
firstCoefficientVO.bIeXi = (parseFloat(newValue.data.bIeXi) / 10000).toFixed(4);
|
|
||||||
} else {
|
|
||||||
firstCoefficientVO.bIeXi = newValue.data.bIeXi;
|
|
||||||
}
|
|
||||||
|
|
||||||
firstCoefficientVO.cIeData = parseFloat(newValue.data.cIeData).toFixed(4);
|
|
||||||
|
|
||||||
if (!isNaN(parseFloat(newValue.data.cIeXi)) && isFinite(newValue.data.cIeXi)) {
|
|
||||||
firstCoefficientVO.cIeXi = (parseFloat(newValue.data.cIeXi) / 10000).toFixed(4);
|
|
||||||
} else {
|
|
||||||
firstCoefficientVO.cIeXi = newValue.data.cIeXi;
|
|
||||||
}
|
|
||||||
|
|
||||||
firstCoefficientVO.aV = newValue.data.aV;
|
|
||||||
firstCoefficientVO.bV = newValue.data.bV;
|
|
||||||
firstCoefficientVO.cV = newValue.data.cV;
|
|
||||||
firstCoefficientVO.aI = newValue.data.aI;
|
|
||||||
firstCoefficientVO.bI = newValue.data.bI;
|
|
||||||
firstCoefficientVO.cI = newValue.data.cI;
|
|
||||||
//console.log(newValue.data.devName + '对象:', firstCoefficientVO);
|
|
||||||
activeIndex.value++;
|
|
||||||
} else {
|
|
||||||
//console.log('未找到匹配的'+ newValue.data.devName+'对象');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//console.log(newValue.data.devName + '数组为空');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//console.log('未找到'+newValue.data.devName+'对应的数组');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'socket_timeout':
|
|
||||||
switch(newValue.operateCode){
|
|
||||||
case 'VOLTAGE':
|
|
||||||
ElMessageBox.alert('连接超时!', '连接超时', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
TableInit();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'connect':
|
|
||||||
switch (newValue.operateCode){
|
|
||||||
case "Source":
|
|
||||||
ElMessageBox.alert('源服务端连接失败', '源服务端连接失败', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
TableInit();
|
|
||||||
break;
|
|
||||||
case "Dev":
|
|
||||||
ElMessageBox.alert('设备服务端连接失败', '设备服务端连接失败', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
type: 'error',
|
|
||||||
})
|
|
||||||
TableInit();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
//出错系数检测初始化
|
|
||||||
const TableInit = () => {
|
|
||||||
console.log("TableInitactive",active.value);
|
|
||||||
isButtonDisabled.value = false; // 恢复按钮
|
|
||||||
for (let i = 0; i < channel.value.length; i++) {
|
|
||||||
const currentTableData = initializeTableData(dataTemplates, i);
|
|
||||||
tableDataMap.set(i,currentTableData)
|
|
||||||
|
|
||||||
// const targetArrayRef = tableDataMap.get(i);
|
|
||||||
// if (targetArrayRef) {
|
|
||||||
// const targetArray = targetArrayRef.value;
|
|
||||||
// if (targetArray.length > 0) {
|
|
||||||
// targetArray.forEach(item => item.loading =false)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
activeIndex.value = 0
|
|
||||||
qualified.value = 0
|
|
||||||
active.value = 0
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//按行图标转动
|
|
||||||
const tableLoading = (type: string, desc: string) => {
|
|
||||||
for (let i = 0; i < channel.value.length; i++) {
|
|
||||||
|
|
||||||
const targetArrayRef = tableDataMap.get(i);
|
|
||||||
if (targetArrayRef) {
|
|
||||||
const targetArray = targetArrayRef.value;
|
|
||||||
if (targetArray.length > 0) {
|
|
||||||
for (let j = 0; j < channel.value[i]; j++) {
|
|
||||||
const firstCoefficientVO = targetArray.find(item => item.monitorNum === (j + 1).toString() &&
|
|
||||||
item.type === type &&
|
|
||||||
item.desc === desc);
|
|
||||||
if (firstCoefficientVO){
|
|
||||||
firstCoefficientVO.loading = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const dataTemplates : ChannelsTest.CoefficientVO[]= [
|
|
||||||
{
|
|
||||||
monitorNum: '1',
|
|
||||||
desc: '系数下装',
|
|
||||||
type:'big',
|
|
||||||
aVuData: '—',
|
|
||||||
aVuXi:'—',
|
|
||||||
bVuData: '—',
|
|
||||||
bVuXi: '—',
|
|
||||||
cVuData: '—',
|
|
||||||
cVuXi: '—',
|
|
||||||
aIeData: '—',
|
|
||||||
aIeXi: '—',
|
|
||||||
bIeData: '—',
|
|
||||||
bIeXi: '—',
|
|
||||||
cIeData: '—',
|
|
||||||
cIeXi: '—',
|
|
||||||
loading:false,
|
|
||||||
devName:'',
|
|
||||||
aV:'—',
|
|
||||||
bV:'—',
|
|
||||||
cV:'—',
|
|
||||||
aI:'—',
|
|
||||||
bI:'—',
|
|
||||||
cI:'—',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
monitorNum: '2',
|
|
||||||
desc: '系数下装',
|
|
||||||
type:'small',
|
|
||||||
aVuData: '—',
|
|
||||||
aVuXi:'—',
|
|
||||||
bVuData: '—',
|
|
||||||
bVuXi: '—',
|
|
||||||
cVuData: '—',
|
|
||||||
cVuXi: '—',
|
|
||||||
aIeData: '—',
|
|
||||||
aIeXi: '—',
|
|
||||||
bIeData: '—',
|
|
||||||
bIeXi: '—',
|
|
||||||
cIeData: '—',
|
|
||||||
cIeXi: '—',
|
|
||||||
loading:false,
|
|
||||||
devName:'',
|
|
||||||
aV:'—',
|
|
||||||
bV:'—',
|
|
||||||
cV:'—',
|
|
||||||
aI:'—',
|
|
||||||
bI:'—',
|
|
||||||
cI:'—',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
monitorNum: '3',
|
|
||||||
desc: '系数校准',
|
|
||||||
type:'big',
|
|
||||||
aVuData: '—',
|
|
||||||
aVuXi:'—',
|
|
||||||
bVuData: '—',
|
|
||||||
bVuXi: '—',
|
|
||||||
cVuData: '—',
|
|
||||||
cVuXi: '—',
|
|
||||||
aIeData: '—',
|
|
||||||
aIeXi: '—',
|
|
||||||
bIeData: '—',
|
|
||||||
bIeXi: '—',
|
|
||||||
cIeData: '—',
|
|
||||||
cIeXi: '—',
|
|
||||||
loading:false,
|
|
||||||
devName:'',
|
|
||||||
aV:'—',
|
|
||||||
bV:'—',
|
|
||||||
cV:'—',
|
|
||||||
aI:'—',
|
|
||||||
bI:'—',
|
|
||||||
cI:'—',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
monitorNum: '4',
|
|
||||||
desc: '系数校准',
|
|
||||||
type:'small',
|
|
||||||
aVuData: '—',
|
|
||||||
aVuXi:'—',
|
|
||||||
bVuData: '—',
|
|
||||||
bVuXi: '—',
|
|
||||||
cVuData: '—',
|
|
||||||
cVuXi: '—',
|
|
||||||
aIeData: '—',
|
|
||||||
aIeXi: '—',
|
|
||||||
bIeData: '—',
|
|
||||||
bIeXi: '—',
|
|
||||||
cIeData: '—',
|
|
||||||
cIeXi: '—',
|
|
||||||
loading:false,
|
|
||||||
devName:'',
|
|
||||||
aV:'—',
|
|
||||||
bV:'—',
|
|
||||||
cV:'—',
|
|
||||||
aI:'—',
|
|
||||||
bI:'—',
|
|
||||||
cI:'—',
|
|
||||||
},
|
|
||||||
|
|
||||||
];
|
|
||||||
|
|
||||||
const dataTemplates2 : ChannelsTest.CoefficientVO[]= [
|
|
||||||
{
|
|
||||||
monitorNum: '1',
|
|
||||||
desc: '系数下装',
|
|
||||||
type:'big',
|
|
||||||
aVuData: '—',
|
|
||||||
aVuXi:'—',
|
|
||||||
bVuData: '—',
|
|
||||||
bVuXi: '—',
|
|
||||||
cVuData: '—',
|
|
||||||
cVuXi: '—',
|
|
||||||
aIeData: '—',
|
|
||||||
aIeXi: '—',
|
|
||||||
bIeData: '—',
|
|
||||||
bIeXi: '—',
|
|
||||||
cIeData: '—',
|
|
||||||
cIeXi: '—',
|
|
||||||
loading:false,
|
|
||||||
devName:''
|
|
||||||
},
|
|
||||||
{
|
|
||||||
monitorNum: '2',
|
|
||||||
desc: '系数下装',
|
|
||||||
type:'small',
|
|
||||||
aVuData: '—',
|
|
||||||
aVuXi:'—',
|
|
||||||
bVuData: '—',
|
|
||||||
bVuXi: '—',
|
|
||||||
cVuData: '—',
|
|
||||||
cVuXi: '—',
|
|
||||||
aIeData: '—',
|
|
||||||
aIeXi: '—',
|
|
||||||
bIeData: '—',
|
|
||||||
bIeXi: '—',
|
|
||||||
cIeData: '—',
|
|
||||||
cIeXi: '—',
|
|
||||||
loading:false,
|
|
||||||
devName:''
|
|
||||||
},
|
|
||||||
{
|
|
||||||
monitorNum: '3',
|
|
||||||
desc: '系数校准',
|
|
||||||
type:'big',
|
|
||||||
aVuData: '—',
|
|
||||||
aVuXi:'不合格',
|
|
||||||
bVuData: '—',
|
|
||||||
bVuXi: '—',
|
|
||||||
cVuData: '—',
|
|
||||||
cVuXi: '—',
|
|
||||||
aIeData: '—',
|
|
||||||
aIeXi: '—',
|
|
||||||
bIeData: '—',
|
|
||||||
bIeXi: '—',
|
|
||||||
cIeData: '—',
|
|
||||||
cIeXi: '—',
|
|
||||||
loading:false,
|
|
||||||
devName:''
|
|
||||||
},
|
|
||||||
{
|
|
||||||
monitorNum: '4',
|
|
||||||
desc: '系数校准',
|
|
||||||
type:'small',
|
|
||||||
aVuData: '—',
|
|
||||||
aVuXi:'—',
|
|
||||||
bVuData: '—',
|
|
||||||
bVuXi: '—',
|
|
||||||
cVuData: '—',
|
|
||||||
cVuXi: '—',
|
|
||||||
aIeData: '—',
|
|
||||||
aIeXi: '—',
|
|
||||||
bIeData: '—',
|
|
||||||
bIeXi: '—',
|
|
||||||
cIeData: '—',
|
|
||||||
cIeXi: '—',
|
|
||||||
loading:false,
|
|
||||||
devName:''
|
|
||||||
},
|
|
||||||
|
|
||||||
];
|
|
||||||
|
|
||||||
// 更新错误状态的方法
|
|
||||||
const updateErrorState = (index: number, hasError: boolean) => {
|
|
||||||
errorStates.value[index] = hasError;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 打开弹窗,可能是新增,也可能是编辑
|
|
||||||
const open = (selection: Device.ResPqDev[],plan:Plan.ReqPlan) => {
|
|
||||||
CurV.value = selection[0]?.devVolt || 57.74;
|
|
||||||
isButtonDisabled.value = false; // 恢复按钮
|
|
||||||
select_Plan.value = plan
|
|
||||||
planId.value = selection[0]?.planId || '';
|
|
||||||
devIdArray.value = selection.map(item => item.id);
|
|
||||||
name.value = selection.map(item => item.name)
|
|
||||||
channel.value = selection.map(item => item.devChns)
|
|
||||||
dialogVisible.value = true;
|
|
||||||
total.value = name.value.length
|
|
||||||
|
|
||||||
|
|
||||||
// 初始化 loadingStates 为 false
|
|
||||||
// loadingStates.value = new Array(selection.length).fill(false);
|
|
||||||
errorStates.value = new Array(selection.length).fill(false);
|
|
||||||
|
|
||||||
for (let i = 0; i < channel.value.length; i++) {
|
|
||||||
const currentTableData = initializeTableData(dataTemplates, i);
|
|
||||||
tableDataMap.set(i,currentTableData)
|
|
||||||
}
|
|
||||||
//console.log('tableDataMap',tableDataMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(e: 'quitClicked'): void;
|
|
||||||
(e: 'submitClicked', callback: (resolve: (value: boolean) => void) => void): void;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const handleCancel=() => {
|
|
||||||
// 清空 name, channel, total
|
|
||||||
name.value = [];
|
|
||||||
channel.value = [];
|
|
||||||
total.value = 0;
|
|
||||||
activeIndex.value = 0
|
|
||||||
qualified.value = 0
|
|
||||||
active.value = 0
|
|
||||||
dialogVisible.value = false
|
|
||||||
editableTabsValue.value = '0'
|
|
||||||
emit('quitClicked'); // 触发事件
|
|
||||||
}
|
|
||||||
|
|
||||||
const getTableDataForChannel = (index: number): any[] => {
|
|
||||||
const data = tableDataMap.get(index);
|
|
||||||
return data ? data.value : [];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
watch(activeIndex, function (newValue, oldValue) {
|
|
||||||
if(activeIndex.value === 1)
|
|
||||||
{
|
|
||||||
outputDsc.value = "电压误差为:±0.1Un%; 电流误差为:±0.5%";
|
|
||||||
// 当前源输出为:Ua=Ub=Uc=57.74V Ia=Ib=Ic=1A"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 示例的 checkForErrors 函数,根据实际需求进行调整
|
|
||||||
const checkForErrors = (data: ChannelsTest.CoefficientVO[]): boolean => {
|
|
||||||
// 这里假设不合格字段的标准是 status 为 '不合格' 或 isValid 为 false
|
|
||||||
return data.some(item =>
|
|
||||||
item.aVuXi === '不合格' ||
|
|
||||||
item.bVuXi === '不合格' ||
|
|
||||||
item.cVuXi === '不合格' ||
|
|
||||||
item.aIeXi === '不合格' ||
|
|
||||||
item.bIeXi === '不合格' ||
|
|
||||||
item.cIeXi === '不合格'
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
|
||||||
// 创建一个 Promise 来等待父组件的回调
|
|
||||||
const response = await new Promise<boolean>((resolve) => {
|
|
||||||
emit('submitClicked', resolve);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
isButtonDisabled.value = true; // 禁用按钮
|
|
||||||
tableLoading('big','系数下装')
|
|
||||||
await getCoefficientCheck({
|
|
||||||
userPageId: "cdf",
|
|
||||||
devIds:devIdArray.value,
|
|
||||||
planId:planId.value,
|
|
||||||
errorSysId: select_Plan.value?.errorSysId,
|
|
||||||
scriptId: select_Plan.value?.scriptId,
|
|
||||||
operateType:'0' // '0'为预检测、‘1‘为正式检测
|
|
||||||
})
|
|
||||||
active.value++;
|
|
||||||
|
|
||||||
// 初始化 loadingStates 为 true
|
|
||||||
// loadingStates.value = new Array(name.value.length).fill(true);
|
|
||||||
|
|
||||||
return;
|
|
||||||
// 初始化 currentTableData
|
|
||||||
let isTimer2Completed = false;
|
|
||||||
|
|
||||||
|
|
||||||
//"80b4b4f52a4c4064a18319525f8ac13c",
|
|
||||||
for (let i = 0; i < channel.value.length; i++) {
|
|
||||||
// 重置状态变量
|
|
||||||
active.value = 0;
|
|
||||||
//activeIndex.value = 0;
|
|
||||||
editableTabsValue.value = i.toString();
|
|
||||||
// 初始化并填充 currentTableData
|
|
||||||
const currentTableData = initializeTableData(dataTemplates2, i);
|
|
||||||
tableDataMap.set(i, currentTableData);
|
|
||||||
//activeIndex.value++;
|
|
||||||
|
|
||||||
// 清除之前的 timer1
|
|
||||||
clearInterval(timer1);
|
|
||||||
// 启动 timer1
|
|
||||||
timer1 = setInterval(() => {
|
|
||||||
active.value++;
|
|
||||||
if (active.value > 5) {
|
|
||||||
clearInterval(timer1);
|
|
||||||
}
|
|
||||||
}, 3000);
|
|
||||||
|
|
||||||
// 清除之前的 timer2
|
|
||||||
clearInterval(timer2);
|
|
||||||
// 启动 timer2
|
|
||||||
timer2 = setInterval(() => {
|
|
||||||
// 初始化并填充 currentTableData
|
|
||||||
const currentTableData = initializeTableData(i > 0 ? dataTemplates2 : dataTemplates2, i);
|
|
||||||
tableDataMap.set(i, currentTableData);
|
|
||||||
activeIndex.value++;
|
|
||||||
|
|
||||||
clearInterval(timer2);
|
|
||||||
const currentDataRef = tableDataMap.get(i);
|
|
||||||
if (currentDataRef) {
|
|
||||||
const currentData = currentDataRef.value;
|
|
||||||
// 检查当前数据中有无不合格字段
|
|
||||||
const hasError = checkForErrors(currentData);
|
|
||||||
if (hasError) {
|
|
||||||
} else {
|
|
||||||
qualified.value++;
|
|
||||||
}
|
|
||||||
updateErrorState(i, hasError);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置标志变量为 true,表示 timer2 已经完成
|
|
||||||
isTimer2Completed = true;
|
|
||||||
}, 3000);
|
|
||||||
|
|
||||||
// 等待 timer2 完成
|
|
||||||
while (!isTimer2Completed) {
|
|
||||||
// 这里可以添加一个短暂的等待,避免死循环
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 100));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重置标志变量
|
|
||||||
isTimer2Completed = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 提取初始化并填充 currentTableData 的函数
|
|
||||||
const initializeTableData = (templates: ChannelsTest.CoefficientVO[], index: number): Ref<ChannelsTest.CoefficientVO[]> => {
|
|
||||||
const currentTableData = ref<ChannelsTest.CoefficientVO[]>([]);
|
|
||||||
for (let j = 0; j < channel.value[index]; j++) {
|
|
||||||
templates.forEach((template) => {
|
|
||||||
// 使用解构赋值排除 id 和 MonitorIdx 属性
|
|
||||||
const { devName,monitorNum: __, ...rest } = template;
|
|
||||||
currentTableData.value.push({
|
|
||||||
monitorNum: (j + 1).toString(),
|
|
||||||
devName: name.value[index],
|
|
||||||
...rest,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return currentTableData;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// 对外映射
|
|
||||||
defineExpose({ open })
|
|
||||||
</script>
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
/* 确保 el-icon-loading 的动画效果没有被覆盖 */
|
|
||||||
.loading-box {
|
|
||||||
animation: rotate 2s linear infinite;
|
|
||||||
font-size: 30px; /* 增大图标的大小 */
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes rotate {
|
|
||||||
from {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-title {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
/* 横向排列 */
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
.custom-tabs-label .el-icon {
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-tabs-label span {
|
|
||||||
vertical-align: middle;
|
|
||||||
margin-left: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-content {
|
|
||||||
height: 510px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-tabs--border-card {
|
|
||||||
height: 470px;
|
|
||||||
}
|
|
||||||
/* .el-icon svg {
|
|
||||||
color: #ff7171;
|
|
||||||
} */
|
|
||||||
|
|
||||||
.icon-style{
|
|
||||||
color: #ff7171;
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-left {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-content {
|
|
||||||
flex: 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spanStyle{
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@@ -1,348 +1,386 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="table-container table-main">
|
<div class="table-container table-main">
|
||||||
<el-table :data="tableData"
|
<el-table
|
||||||
:header-cell-style="{ textAlign: 'center' } "
|
:data="tableData"
|
||||||
:cell-style="{ textAlign: 'center' }"
|
:header-cell-style="{ textAlign: 'center' }"
|
||||||
style="width: 100%"
|
:cell-style="{ textAlign: 'center' }"
|
||||||
max-height="400px"
|
style="width: 100%"
|
||||||
:span-method="objectSpanMethod">
|
max-height="400px"
|
||||||
<el-table-column prop="monitorNum" label="监测点序号" width="80"/>
|
:span-method="objectSpanMethod"
|
||||||
<el-table-column prop="desc" label="描述" width="90"/>
|
>
|
||||||
<el-table-column label="电压通道" >
|
<el-table-column prop="monitorNum" label="监测点序号" width="80" />
|
||||||
<el-table-column prop="Ua" label="A相">
|
<el-table-column prop="desc" label="描述" width="90" />
|
||||||
<el-table-column prop="aVuData" label="被检值(V)" width="100px">
|
<el-table-column label="电压通道">
|
||||||
<template #default="scope">
|
<el-table-column prop="Ua" label="A相">
|
||||||
<el-icon v-if="scope.row.aVuData === '—'&& scope.row.loading" class="loading-box">
|
<el-table-column prop="aVuData" label="被检值(V)" width="100px">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-icon v-if="scope.row.aVuData === '—' && scope.row.loading" class="loading-box">
|
||||||
<el-icon-loading />
|
<el-icon-loading />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
{{ scope.row.aVuData }}
|
{{ scope.row.aVuData }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="aVuXi" label="检测结果" width="90px">
|
<el-table-column prop="aVuXi" label="检测结果" width="90px">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
v-if="scope.row.aVuXi === '不合格'"
|
v-if="scope.row.aVuXi === '不合格'"
|
||||||
class="item"
|
class="item"
|
||||||
effect="dark"
|
effect="dark"
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
误差范围: {{ (-0.001 * props.curV).toFixed(4) + 'V~' + (0.001 * props.curV).toFixed(4)+ 'V'}}<br/>
|
误差范围:
|
||||||
误差值:{{ scope.row.aV + 'V'}}
|
{{
|
||||||
|
(-0.001 * props.curV).toFixed(4) + 'V~' + (0.001 * props.curV).toFixed(4) + 'V'
|
||||||
|
}}
|
||||||
|
<br />
|
||||||
|
误差值:{{ scope.row.aV + 'V' }}
|
||||||
</template>
|
</template>
|
||||||
<el-tag type="danger" class="tooltip-content">
|
<el-tag type="danger" class="tooltip-content">
|
||||||
{{ scope.row.aVuXi }}
|
{{ scope.row.aVuXi }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
v-else-if="scope.row.aVuXi === '合格'"
|
v-else-if="scope.row.aVuXi === '合格'"
|
||||||
class="item"
|
class="item"
|
||||||
effect="dark"
|
effect="dark"
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
误差范围: {{ (-0.001 * props.curV).toFixed(4) + 'V~' + (0.001 * props.curV).toFixed(4)+ 'V'}}<br/>
|
误差范围:
|
||||||
误差值:{{ scope.row.aV + 'V'}}
|
{{
|
||||||
|
(-0.001 * props.curV).toFixed(4) + 'V~' + (0.001 * props.curV).toFixed(4) + 'V'
|
||||||
|
}}
|
||||||
|
<br />
|
||||||
|
误差值:{{ scope.row.aV + 'V' }}
|
||||||
</template>
|
</template>
|
||||||
<el-tag type="success" class="tooltip-content">{{ scope.row.aVuXi }}</el-tag>
|
<el-tag type="success" class="tooltip-content">{{ scope.row.aVuXi }}</el-tag>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<!-- <el-tag type="danger" v-if="scope.row.aVuXi === '不合格'">
|
<!-- <el-tag type="danger" v-if="scope.row.aVuXi === '不合格'">
|
||||||
{{ scope.row.aVuXi }}
|
{{ scope.row.aVuXi }}
|
||||||
</el-tag> -->
|
</el-tag> -->
|
||||||
<el-icon v-else-if="scope.row.aVuXi === '—'&& scope.row.loading" class="loading-box">
|
<el-icon v-else-if="scope.row.aVuXi === '—' && scope.row.loading" class="loading-box">
|
||||||
<el-icon-loading />
|
<el-icon-loading />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
<!-- <span v-else>
|
<!-- <span v-else>
|
||||||
{{ scope.row.aVuXi }}
|
{{ scope.row.aVuXi }}
|
||||||
</span> -->
|
</span> -->
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="Ub" label="B相">
|
<el-table-column prop="Ub" label="B相">
|
||||||
<el-table-column prop="bVuData" label="被检值(V)" width="100px">
|
<el-table-column prop="bVuData" label="被检值(V)" width="100px">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-icon v-if="scope.row.bVuData === '—'&& scope.row.loading" class="loading-box">
|
<el-icon v-if="scope.row.bVuData === '—' && scope.row.loading" class="loading-box">
|
||||||
<el-icon-loading />
|
<el-icon-loading />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
{{ scope.row.bVuData }}
|
{{ scope.row.bVuData }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="bVuXi" label="检测结果" width="90px">
|
<el-table-column prop="bVuXi" label="检测结果" width="90px">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
v-if="scope.row.bVuXi === '不合格'"
|
v-if="scope.row.bVuXi === '不合格'"
|
||||||
class="item"
|
class="item"
|
||||||
effect="dark"
|
effect="dark"
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
误差范围: {{ (-0.001 * props.curV).toFixed(4) + 'V~' + (0.001 * props.curV).toFixed(4)+ 'V'}}<br/>
|
误差范围:
|
||||||
误差值:{{ scope.row.bV + 'V'}}
|
{{
|
||||||
|
(-0.001 * props.curV).toFixed(4) + 'V~' + (0.001 * props.curV).toFixed(4) + 'V'
|
||||||
|
}}
|
||||||
|
<br />
|
||||||
|
误差值:{{ scope.row.bV + 'V' }}
|
||||||
</template>
|
</template>
|
||||||
<el-tag type="danger" class="tooltip-content">
|
<el-tag type="danger" class="tooltip-content">
|
||||||
{{ scope.row.bVuXi }}
|
{{ scope.row.bVuXi }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
v-else-if="scope.row.bVuXi === '合格'"
|
v-else-if="scope.row.bVuXi === '合格'"
|
||||||
class="item"
|
class="item"
|
||||||
effect="dark"
|
effect="dark"
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
误差范围: {{ (-0.001 * props.curV).toFixed(4) + 'V~' + (0.001 * props.curV).toFixed(4)+ 'V'}}<br/>
|
误差范围:
|
||||||
误差值:{{ scope.row.bV + 'V'}}
|
{{
|
||||||
|
(-0.001 * props.curV).toFixed(4) + 'V~' + (0.001 * props.curV).toFixed(4) + 'V'
|
||||||
|
}}
|
||||||
|
<br />
|
||||||
|
误差值:{{ scope.row.bV + 'V' }}
|
||||||
</template>
|
</template>
|
||||||
<el-tag type="success" class="tooltip-content">{{ scope.row.bVuXi }}</el-tag>
|
<el-tag type="success" class="tooltip-content">{{ scope.row.bVuXi }}</el-tag>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-icon v-else-if="scope.row.bVuXi === '—'&& scope.row.loading" class="loading-box">
|
<el-icon v-else-if="scope.row.bVuXi === '—' && scope.row.loading" class="loading-box">
|
||||||
<el-icon-loading />
|
<el-icon-loading />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="Uc" label="C相">
|
<el-table-column prop="Uc" label="C相">
|
||||||
<el-table-column prop="cVuData" label="被检值(V)" width="100px">
|
<el-table-column prop="cVuData" label="被检值(V)" width="100px">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-icon v-if="scope.row.cVuData === '—'&& scope.row.loading" class="loading-box">
|
<el-icon v-if="scope.row.cVuData === '—' && scope.row.loading" class="loading-box">
|
||||||
<el-icon-loading />
|
<el-icon-loading />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
{{ scope.row.cVuData }}
|
{{ scope.row.cVuData }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="cVuXi" label="检测结果" width="90px">
|
<el-table-column prop="cVuXi" label="检测结果" width="90px">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
v-if="scope.row.cVuXi === '不合格'"
|
v-if="scope.row.cVuXi === '不合格'"
|
||||||
class="item"
|
class="item"
|
||||||
effect="dark"
|
effect="dark"
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
误差范围: {{ (-0.001 * props.curV).toFixed(4) + 'V~' + (0.001 * props.curV).toFixed(4)+ 'V'}}<br/>
|
误差范围:
|
||||||
误差值:{{ scope.row.cV + 'V'}}
|
{{
|
||||||
|
(-0.001 * props.curV).toFixed(4) + 'V~' + (0.001 * props.curV).toFixed(4) + 'V'
|
||||||
|
}}
|
||||||
|
<br />
|
||||||
|
误差值:{{ scope.row.cV + 'V' }}
|
||||||
</template>
|
</template>
|
||||||
<el-tag type="danger" class="tooltip-content">
|
<el-tag type="danger" class="tooltip-content">
|
||||||
{{ scope.row.cVuXi }}
|
{{ scope.row.cVuXi }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
v-else-if="scope.row.cVuXi === '合格'"
|
v-else-if="scope.row.cVuXi === '合格'"
|
||||||
class="item"
|
class="item"
|
||||||
effect="dark"
|
effect="dark"
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
误差范围: {{ (-0.001 * props.curV).toFixed(4) + 'V~' + (0.001 * props.curV).toFixed(4) + 'V'}}<br/>
|
误差范围:
|
||||||
误差值:{{ scope.row.cV + 'V'}}
|
{{
|
||||||
|
(-0.001 * props.curV).toFixed(4) + 'V~' + (0.001 * props.curV).toFixed(4) + 'V'
|
||||||
|
}}
|
||||||
|
<br />
|
||||||
|
误差值:{{ scope.row.cV + 'V' }}
|
||||||
</template>
|
</template>
|
||||||
<el-tag type="success" class="tooltip-content">{{ scope.row.cVuXi }}</el-tag>
|
<el-tag type="success" class="tooltip-content">{{ scope.row.cVuXi }}</el-tag>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-icon v-else-if="scope.row.cVuXi === '—'&& scope.row.loading" class="loading-box">
|
<el-icon v-else-if="scope.row.cVuXi === '—' && scope.row.loading" class="loading-box">
|
||||||
<el-icon-loading />
|
<el-icon-loading />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="电流通道" >
|
</el-table-column>
|
||||||
<el-table-column prop="Ia" label="A相">
|
</el-table-column>
|
||||||
<el-table-column prop="aIeData" label="被检值(A)" width="100px">
|
<el-table-column label="电流通道">
|
||||||
<template #default="scope">
|
<el-table-column prop="Ia" label="A相">
|
||||||
<el-icon v-if="scope.row.aIeData === '—'&& scope.row.loading" class="loading-box">
|
<el-table-column prop="aIeData" label="被检值(A)" width="100px">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-icon v-if="scope.row.aIeData === '—' && scope.row.loading" class="loading-box">
|
||||||
<el-icon-loading />
|
<el-icon-loading />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
{{ scope.row.aIeData }}
|
{{ scope.row.aIeData }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="aIeXi" label="检测结果" width="90px">
|
<el-table-column prop="aIeXi" label="检测结果" width="90px">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
v-if="scope.row.aIeXi === '不合格'"
|
v-if="scope.row.aIeXi === '不合格'"
|
||||||
class="item"
|
class="item"
|
||||||
effect="dark"
|
effect="dark"
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
误差范围: {{-0.5 + "%~" +0.5 + "%" }}<br/>
|
误差范围: {{ -0.5 + '%~' + 0.5 + '%' }}
|
||||||
误差值:{{ scope.row.aI + '%'}}
|
<br />
|
||||||
|
误差值:{{ scope.row.aI + '%' }}
|
||||||
</template>
|
</template>
|
||||||
<el-tag type="danger" class="tooltip-content">
|
<el-tag type="danger" class="tooltip-content">
|
||||||
{{ scope.row.aIeXi }}
|
{{ scope.row.aIeXi }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
v-else-if="scope.row.aIeXi === '合格'"
|
v-else-if="scope.row.aIeXi === '合格'"
|
||||||
class="item"
|
class="item"
|
||||||
effect="dark"
|
effect="dark"
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
误差范围: {{ -0.5 + "%~" +0.5 + "%" }}<br/>
|
误差范围: {{ -0.5 + '%~' + 0.5 + '%' }}
|
||||||
误差值:{{ scope.row.aI + '%'}}
|
<br />
|
||||||
|
误差值:{{ scope.row.aI + '%' }}
|
||||||
</template>
|
</template>
|
||||||
<el-tag type="success" class="tooltip-content">{{ scope.row.aIeXi }}</el-tag>
|
<el-tag type="success" class="tooltip-content">{{ scope.row.aIeXi }}</el-tag>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-icon v-else-if="scope.row.aIeXi === '—'&& scope.row.loading" class="loading-box">
|
<el-icon v-else-if="scope.row.aIeXi === '—' && scope.row.loading" class="loading-box">
|
||||||
<el-icon-loading />
|
<el-icon-loading />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="Ib" label="B相">
|
<el-table-column prop="Ib" label="B相">
|
||||||
<el-table-column prop="bIeData" label="被检值(A)" width="100px">
|
<el-table-column prop="bIeData" label="被检值(A)" width="100px">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-icon v-if="scope.row.bIeData === '—'&& scope.row.loading" class="loading-box">
|
<el-icon v-if="scope.row.bIeData === '—' && scope.row.loading" class="loading-box">
|
||||||
<el-icon-loading />
|
<el-icon-loading />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
{{ scope.row.bIeData }}
|
{{ scope.row.bIeData }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="bIeXi" label="检测结果" width="90px">
|
<el-table-column prop="bIeXi" label="检测结果" width="90px">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
v-if="scope.row.bIeXi === '不合格'"
|
v-if="scope.row.bIeXi === '不合格'"
|
||||||
class="item"
|
class="item"
|
||||||
effect="dark"
|
effect="dark"
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
误差范围: {{ -0.5 + "%~" +0.5 + "%" }}<br/>
|
误差范围: {{ -0.5 + '%~' + 0.5 + '%' }}
|
||||||
误差值:{{ scope.row.bI + '%'}}
|
<br />
|
||||||
|
误差值:{{ scope.row.bI + '%' }}
|
||||||
</template>
|
</template>
|
||||||
<el-tag type="danger" class="tooltip-content">
|
<el-tag type="danger" class="tooltip-content">
|
||||||
{{ scope.row.bIeXi }}
|
{{ scope.row.bIeXi }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
v-else-if="scope.row.bIeXi === '合格'"
|
v-else-if="scope.row.bIeXi === '合格'"
|
||||||
class="item"
|
class="item"
|
||||||
effect="dark"
|
effect="dark"
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
误差范围: {{ -0.5 + "%~" +0.5 + "%" }}<br/>
|
误差范围: {{ -0.5 + '%~' + 0.5 + '%' }}
|
||||||
误差值:{{ scope.row.bI + '%'}}
|
<br />
|
||||||
|
误差值:{{ scope.row.bI + '%' }}
|
||||||
</template>
|
</template>
|
||||||
<el-tag type="success" class="tooltip-content">{{ scope.row.bIeXi }}</el-tag>
|
<el-tag type="success" class="tooltip-content">{{ scope.row.bIeXi }}</el-tag>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-icon v-else-if="scope.row.bIeXi === '—'&& scope.row.loading" class="loading-box">
|
<el-icon v-else-if="scope.row.bIeXi === '—' && scope.row.loading" class="loading-box">
|
||||||
<el-icon-loading />
|
<el-icon-loading />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="Ic" label="C相">
|
<el-table-column prop="Ic" label="C相">
|
||||||
<el-table-column prop="cIeData" label="被检值(A)" width="100px">
|
<el-table-column prop="cIeData" label="被检值(A)" width="100px">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-icon v-if="scope.row.cIeData === '—'&& scope.row.loading" class="loading-box">
|
<el-icon v-if="scope.row.cIeData === '—' && scope.row.loading" class="loading-box">
|
||||||
<el-icon-loading />
|
<el-icon-loading />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
<span v-else>
|
<span v-else>
|
||||||
{{ scope.row.cIeData }}
|
{{ scope.row.cIeData }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="cIeXi" label="检测结果" width="90px">
|
<el-table-column prop="cIeXi" label="检测结果" width="90px">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
v-if="scope.row.cIeXi === '不合格'"
|
v-if="scope.row.cIeXi === '不合格'"
|
||||||
class="item"
|
class="item"
|
||||||
effect="dark"
|
effect="dark"
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
误差范围: {{ -0.5 + "%~" +0.5 + "%" }}<br/>
|
误差范围: {{ -0.5 + '%~' + 0.5 + '%' }}
|
||||||
误差值:{{ scope.row.cI + '%'}}
|
<br />
|
||||||
|
误差值:{{ scope.row.cI + '%' }}
|
||||||
</template>
|
</template>
|
||||||
<el-tag type="danger" class="tooltip-content">
|
<el-tag type="danger" class="tooltip-content">
|
||||||
{{ scope.row.cIeXi }}
|
{{ scope.row.cIeXi }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
v-else-if="scope.row.cIeXi === '合格'"
|
v-else-if="scope.row.cIeXi === '合格'"
|
||||||
class="item"
|
class="item"
|
||||||
effect="dark"
|
effect="dark"
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
误差范围: {{ -0.5 + "%~" +0.5 + "%" }}<br/>
|
误差范围: {{ -0.5 + '%~' + 0.5 + '%' }}
|
||||||
误差值:{{ scope.row.cI + '%'}}
|
<br />
|
||||||
|
误差值:{{ scope.row.cI + '%' }}
|
||||||
</template>
|
</template>
|
||||||
<el-tag type="success" class="tooltip-content">{{ scope.row.cIeXi }}</el-tag>
|
<el-tag type="success" class="tooltip-content">{{ scope.row.cIeXi }}</el-tag>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-icon v-else-if="scope.row.cIeXi === '—'&& scope.row.loading" class="loading-box">
|
<el-icon v-else-if="scope.row.cIeXi === '—' && scope.row.loading" class="loading-box">
|
||||||
<el-icon-loading />
|
<el-icon-loading />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table-column>
|
||||||
</div>
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup name="ErrorSystemDialog">
|
<script lang="ts" setup name="ErrorSystemDialog">
|
||||||
import { defineProps, type PropType } from 'vue';
|
import { ElIcon, ElTag } from 'element-plus'
|
||||||
import { ElIcon, ElLoading, ElTag } from 'element-plus';
|
import type { ChannelsTest } from '@/api/home/interface/channelsTest'
|
||||||
import type { ChannelsTest } from '@/api/home/interface/channelsTest';
|
|
||||||
import { number } from 'echarts';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
tableData: ChannelsTest.CoefficientVO[];
|
tableData: ChannelsTest.CoefficientVO[]
|
||||||
big_V_loading: boolean;
|
big_V_loading: boolean
|
||||||
curV: number;
|
curV: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
tableData: () => [],
|
tableData: () => [],
|
||||||
big_V_loading: false,
|
big_V_loading: false,
|
||||||
curV: 0,
|
curV: 0
|
||||||
});
|
})
|
||||||
|
|
||||||
function objectSpanMethod({ row, column, rowIndex, columnIndex }: { row: any, column: any, rowIndex: number, columnIndex: number }) {
|
function objectSpanMethod({
|
||||||
|
row,
|
||||||
|
column,
|
||||||
|
rowIndex,
|
||||||
|
columnIndex
|
||||||
|
}: {
|
||||||
|
row: any
|
||||||
|
column: any
|
||||||
|
rowIndex: number
|
||||||
|
columnIndex: number
|
||||||
|
}) {
|
||||||
if (columnIndex === 0) {
|
if (columnIndex === 0) {
|
||||||
if (rowIndex % 4 === 0) {
|
if (rowIndex % 4 === 0) {
|
||||||
return {
|
return {
|
||||||
rowspan: 4,
|
rowspan: 4,
|
||||||
colspan: 1,
|
colspan: 1
|
||||||
};
|
}
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
rowspan: 0,
|
rowspan: 0,
|
||||||
colspan: 0,
|
colspan: 0
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (columnIndex === 1) {
|
if (columnIndex === 1) {
|
||||||
if (rowIndex % 2 === 0) {
|
if (rowIndex % 2 === 0) {
|
||||||
return {
|
return {
|
||||||
rowspan: 2,
|
rowspan: 2,
|
||||||
colspan: 1,
|
colspan: 1
|
||||||
};
|
}
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
rowspan: 0,
|
rowspan: 0,
|
||||||
colspan: 0,
|
colspan: 0
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
</script>
|
<style scoped>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
.form-grid {
|
.form-grid {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row; /* 横向排列 */
|
flex-direction: row; /* 横向排列 */
|
||||||
@@ -364,40 +402,40 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
margin-bottom: 20px; /* 添加底部边距 */
|
margin-bottom: 20px; /* 添加底部边距 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-table th, .el-table td {
|
.el-table th,
|
||||||
text-align: center; /* 所有单元格文字居中 */
|
.el-table td {
|
||||||
|
text-align: center; /* 所有单元格文字居中 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-container {
|
.table-container {
|
||||||
max-height: 400px; /* 根据需要调整高度 */
|
max-height: 400px; /* 根据需要调整高度 */
|
||||||
overflow-y: auto; /* 允许垂直滚动 */
|
overflow-y: auto; /* 允许垂直滚动 */
|
||||||
overflow-x: hidden; /* 隐藏水平滚动条 */
|
overflow-x: hidden; /* 隐藏水平滚动条 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 确保 el-icon-loading 的动画效果没有被覆盖 */
|
/* 确保 el-icon-loading 的动画效果没有被覆盖 */
|
||||||
.loading-box {
|
.loading-box {
|
||||||
animation: rotate 2s linear infinite;
|
animation: rotate 2s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-margin {
|
.icon-margin {
|
||||||
margin-left: 0px;
|
margin-left: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-align {
|
.icon-align {
|
||||||
vertical-align: 0px;
|
vertical-align: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltip-content {
|
.tooltip-content {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@keyframes rotate {
|
@keyframes rotate {
|
||||||
from {
|
from {
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
}
|
}
|
||||||
to {
|
to {
|
||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
<template>
|
||||||
|
<div class="table-main">
|
||||||
|
<el-table
|
||||||
|
:data="prop.tableData"
|
||||||
|
stripe
|
||||||
|
border
|
||||||
|
:header-cell-style="{ textAlign: 'center' }"
|
||||||
|
:cell-style="{ textAlign: 'center' }"
|
||||||
|
height="368px"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-table-column type="index" label="序号" width="70" fixed="left"/>
|
||||||
|
|
||||||
|
<el-table-column prop="dataA" :label="'被检设备'">
|
||||||
|
<el-table-column prop="timeDev" label="数据时间" width="200"/>
|
||||||
|
<el-table-column prop="uaDev" :label="'A相'+(outerUnit==''?'':'('+outerUnit+')')" v-if="prop.tableData.length==0||prop.tableData[0]?.uaDev != null"/>
|
||||||
|
<el-table-column prop="ubDev" :label="setB+(outerUnit==''?'':'('+outerUnit+')')" v-if="prop.tableData.length==0||prop.tableData[0]?.ubDev != null"/>
|
||||||
|
<el-table-column prop="ucDev" :label="'C相'+(outerUnit==''?'':'('+outerUnit+')')" v-if="prop.tableData.length==0||prop.tableData[0]?.ucDev != null"/>
|
||||||
|
<el-table-column prop="utDev" :label="setT+(outerUnit==''?'':'('+outerUnit+')')" v-if="prop.tableData[0]?.utDev != null"/>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="dataA" :label="'标准设备'">
|
||||||
|
<el-table-column prop="timeStdDev" label="数据时间" width="200"/>
|
||||||
|
<el-table-column prop="uaStdDev" :label="'A相'+(outerUnit==''?'':'('+outerUnit+')')" v-if="prop.tableData.length==0||prop.tableData[0]?.uaStdDev != null"/>
|
||||||
|
<el-table-column prop="ubStdDev" :label="setB+(outerUnit==''?'':'('+outerUnit+')')" v-if="prop.tableData.length==0||prop.tableData[0]?.ubStdDev != null"/>
|
||||||
|
<el-table-column prop="ucStdDev" :label="'C相'+(outerUnit==''?'':'('+outerUnit+')')" v-if="prop.tableData.length==0||prop.tableData[0]?.ucStdDev != null"/>
|
||||||
|
<el-table-column prop="utStdDev" :label="setT+(outerUnit==''?'':'('+outerUnit+')')" v-if="prop.tableData[0]?.utStdDev != null"/>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="tsx" setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { CheckData } from '@/api/check/interface'
|
||||||
|
|
||||||
|
const prop = defineProps({
|
||||||
|
tableData: {
|
||||||
|
type: Array as () => CheckData.TableRow[],
|
||||||
|
default: []
|
||||||
|
},
|
||||||
|
currentCheckItem: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const outerUnit = computed(() => {
|
||||||
|
return prop.tableData.length > 0 ? prop.tableData[0].unit : '';
|
||||||
|
})
|
||||||
|
|
||||||
|
const setB = computed(() => {
|
||||||
|
return prop.currentCheckItem == '三相电流不平衡度'
|
||||||
|
? '负序不平衡度'
|
||||||
|
: prop.currentCheckItem == '三相电压不平衡度'
|
||||||
|
? '负序不平衡度'
|
||||||
|
: 'B相'
|
||||||
|
})
|
||||||
|
const setT = computed(() => {
|
||||||
|
return prop.currentCheckItem == '频率' ? '频率' : 'T相'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
<template>
|
||||||
|
<div class="table-main">
|
||||||
|
<el-table
|
||||||
|
:data="prop.tableData"
|
||||||
|
height="368px"
|
||||||
|
:header-cell-style="{ textAlign: 'center' }"
|
||||||
|
:cell-style="{ textAlign: 'center' }"
|
||||||
|
>
|
||||||
|
<!-- <el-table-column type="index" label="序号" width="70" fixed="left" />-->
|
||||||
|
<el-table-column label="A相" v-if="prop.tableData.length==0|| prop.tableData[0]?.dataA">
|
||||||
|
<el-table-column prop="stdA" :label="'被检值'+(outerUnit==''?'':'('+outerUnit+')')">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.dataA.data }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="dataA" :label="'标准值'+(outerUnit==''?'':'('+outerUnit+')')">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.dataA.resultData }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="isDataA" label="检测结果">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tooltip effect="dark" placement="bottom">
|
||||||
|
<template #content>
|
||||||
|
误差范围:{{ addPercentSigns(row.dataA.radius, row.dataA.unit) }}
|
||||||
|
<br/>
|
||||||
|
误差值:{{ row.dataA.errorData }}{{ row.dataA.unit }}
|
||||||
|
</template>
|
||||||
|
<el-tag type="success" v-if="row.dataA.isData === 1">符合</el-tag>
|
||||||
|
<el-tag type="danger" v-if="row.dataA.isData === 2">不符合</el-tag>
|
||||||
|
<el-tag type="warning" v-if="row.dataA.isData === 4">/</el-tag>
|
||||||
|
<el-tag type="info" v-if="row.dataA.isData === 5">-</el-tag>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="setB" v-if="prop.tableData.length==0|| prop.tableData[0]?.dataB">
|
||||||
|
<el-table-column prop="stdB" :label="'被检值'+(outerUnit==''?'':'('+outerUnit+')')">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.dataB.data }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="dataB" :label="'标准值'+(outerUnit==''?'':'('+outerUnit+')')">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.dataB.resultData }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="isDataB" label="检测结果">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tooltip effect="dark" placement="bottom">
|
||||||
|
<template #content>
|
||||||
|
误差范围:{{ addPercentSigns(row.dataB.radius, row.dataB.unit) }}
|
||||||
|
<br/>
|
||||||
|
误差值:{{ row.dataB.errorData }}{{ row.dataB.unit }}
|
||||||
|
</template>
|
||||||
|
<el-tag type="success" v-if="row.dataB.isData === 1">符合</el-tag>
|
||||||
|
<el-tag type="danger" v-if="row.dataB.isData === 2">不符合</el-tag>
|
||||||
|
<el-tag type="warning" v-if="row.dataB.isData === 4">/</el-tag>
|
||||||
|
<el-tag type="info" v-if="row.dataB.isData === 5">-</el-tag>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="C相" v-if="prop.tableData.length==0|| prop.tableData[0]?.dataC">
|
||||||
|
<el-table-column prop="stdC" :label="'被检值'+(outerUnit==''?'':'('+outerUnit+')')">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.dataC.data }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="dataC" :label="'标准值'+(outerUnit==''?'':'('+outerUnit+')')">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.dataC.resultData }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="isDataC" label="检测结果">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tooltip effect="dark" placement="bottom">
|
||||||
|
<template #content>
|
||||||
|
误差范围:{{ addPercentSigns(row.dataC.radius, row.dataC.unit) }}
|
||||||
|
<br/>
|
||||||
|
误差值:{{ row.dataC.errorData }}{{ row.dataC.unit }}
|
||||||
|
</template>
|
||||||
|
<el-tag type="success" v-if="row.dataC.isData === 1">符合</el-tag>
|
||||||
|
<el-tag type="danger" v-if="row.dataC.isData === 2">不符合</el-tag>
|
||||||
|
<el-tag type="warning" v-if="row.dataC.isData === 4">/</el-tag>
|
||||||
|
<el-tag type="info" v-if="row.dataC.isData === 5">-</el-tag>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="setT" v-if="prop.tableData[0].dataT">
|
||||||
|
<el-table-column prop="stdT" :label="'被检值'+(outerUnit==''?'':'('+outerUnit+')')">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.dataT.data }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="dataT" :label="'标准值'+(outerUnit==''?'':'('+outerUnit+')')">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.dataT.resultData }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="isDataT" label="检测结果">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tooltip effect="dark" placement="bottom">
|
||||||
|
<template #content>
|
||||||
|
误差范围:{{ addPercentSigns(row.dataT.radius, row.dataT.unit) }}
|
||||||
|
<br/>
|
||||||
|
误差值:{{ row.dataT.errorData }}{{ row.dataT.unit }}
|
||||||
|
</template>
|
||||||
|
<el-tag type="success" v-if="row.dataT.isData === 1">符合</el-tag>
|
||||||
|
<el-tag type="danger" v-if="row.dataT.isData === 2">不符合</el-tag>
|
||||||
|
<el-tag type="warning" v-if="row.dataT.isData === 4">/</el-tag>
|
||||||
|
<el-tag type="info" v-if="row.dataT.isData === 5">-</el-tag>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="tsx" setup>
|
||||||
|
import {computed} from 'vue'
|
||||||
|
import {CheckData} from '@/api/check/interface'
|
||||||
|
|
||||||
|
const prop = defineProps({
|
||||||
|
tableData: {
|
||||||
|
type: Array as () => CheckData.TableRow[],
|
||||||
|
default: []
|
||||||
|
},
|
||||||
|
currentCheckItem: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 添加单位
|
||||||
|
const outerUnit = computed(() => {
|
||||||
|
return prop.tableData.length > 0 ? prop.tableData[0].unit : '';
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const addPercentSigns = (text: string, unit: string) => {
|
||||||
|
return text
|
||||||
|
.split('~')
|
||||||
|
.map(part => `${part}${unit}`)
|
||||||
|
.join('~')
|
||||||
|
}
|
||||||
|
const setB = computed(() => {
|
||||||
|
return prop.currentCheckItem == '三相电流不平衡度'
|
||||||
|
? '三相电流不平衡度'
|
||||||
|
: prop.currentCheckItem == '三相电压不平衡度'
|
||||||
|
? '三相电压不平衡度'
|
||||||
|
: 'B相'
|
||||||
|
})
|
||||||
|
|
||||||
|
const setT = computed(() => {
|
||||||
|
return prop.currentCheckItem == '频率' ? '频率' : 'T相'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.form-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row; /* 横向排列 */
|
||||||
|
flex-wrap: wrap; /* 允许换行 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-grid .el-form-item {
|
||||||
|
flex: 1 1 30%; /* 控件宽度 */
|
||||||
|
margin-right: 20px; /* 控件间距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-grid .el-form-item:last-child {
|
||||||
|
margin-right: 0; /* 最后一个控件不需要右边距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
margin-bottom: 10px; /* 调整这里的值以增加或减少间距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tabs {
|
||||||
|
margin-bottom: 20px; /* 添加底部边距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table th,
|
||||||
|
.el-table td {
|
||||||
|
text-align: center; /* 所有单元格文字居中 */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,662 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:append-to-body="appendToBody"
|
||||||
|
class="dialog"
|
||||||
|
title="数据查询"
|
||||||
|
:model-value="visible"
|
||||||
|
@close="close"
|
||||||
|
v-bind="dialogBig"
|
||||||
|
:draggable="false"
|
||||||
|
width="1400px"
|
||||||
|
>
|
||||||
|
<div class="data-check-dialog">
|
||||||
|
<div>
|
||||||
|
<el-form :model="formContent" label-width="auto" class="form-three">
|
||||||
|
<el-form-item label="误差体系">
|
||||||
|
<el-select
|
||||||
|
:disabled="checkStore.showDetailType === 2 || checkStore.showDetailType === 0"
|
||||||
|
v-model="formContent.errorSysId"
|
||||||
|
placeholder="请选择误差体系"
|
||||||
|
autocomplete="off"
|
||||||
|
@change="handleErrorSysChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="option in pqErrorList"
|
||||||
|
:key="option.id"
|
||||||
|
:label="option.name"
|
||||||
|
:value="option.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="数据原则">
|
||||||
|
<el-input v-model="formContent.dataRule" :disabled="true" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="设备名称">
|
||||||
|
<el-input v-model="formContent.deviceName" :disabled="true" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="通道号">
|
||||||
|
<el-select v-model="formContent.chnNum" @change="handleChnNumChange" :disabled="sourceKey == 1">
|
||||||
|
<el-option v-for="item in chnList" :key="item" :label="item" :value="item" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="检测次数">
|
||||||
|
<el-select
|
||||||
|
v-model="formContent.num"
|
||||||
|
clearable
|
||||||
|
@change="handleNumChange"
|
||||||
|
:disabled="sourceKey == 1"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in chnMapList[formContent.chnNum]"
|
||||||
|
:key="item"
|
||||||
|
:label="item"
|
||||||
|
:value="item"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="checkStore.showDetailType === 1">
|
||||||
|
<el-button type="primary" :icon="Postcard">报告生成</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="checkStore.showDetailType === 0">
|
||||||
|
<el-button type="primary" :icon="Histogram" @click="handleReCalculate">重新计算</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<div class="data-check-body">
|
||||||
|
<div class="content-left-tree" v-if="sourceKey == 2">
|
||||||
|
<el-tree
|
||||||
|
style="width: 200px"
|
||||||
|
:data="scriptData"
|
||||||
|
:props="defaultProps"
|
||||||
|
highlight-current
|
||||||
|
node-key="id"
|
||||||
|
ref="treeRef"
|
||||||
|
@node-click="handleNodeClick"
|
||||||
|
></el-tree>
|
||||||
|
</div>
|
||||||
|
<div class="content-right">
|
||||||
|
<div class="content-right-title">
|
||||||
|
<div style="width: 840px">
|
||||||
|
<span class="content-right-title-text">当前检测项目:</span>
|
||||||
|
<!-- 当code为'wave_data'时显示下拉框 -->
|
||||||
|
<el-select
|
||||||
|
v-if="isWaveData"
|
||||||
|
v-model="selectedScriptName"
|
||||||
|
style="width: 200px"
|
||||||
|
@change="handleScriptNameChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in scriptNameOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<!-- 否则显示原来的文本 -->
|
||||||
|
<span v-else style="color: var(--el-color-primary)">{{ rowList.scriptName }}</span>
|
||||||
|
</div>
|
||||||
|
<el-form-item
|
||||||
|
style="
|
||||||
|
margin: 0 auto;
|
||||||
|
margin-bottom: 0px !important;
|
||||||
|
width: 200px;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
"
|
||||||
|
label="录波次数"
|
||||||
|
v-if="isWaveData"
|
||||||
|
>
|
||||||
|
<el-select v-model="waveNumber" @change="handleWaveNumberChange">
|
||||||
|
<el-option v-for="i in waveNumCount" :key="i" :label="i" :value="i" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
style="margin-left: 280px; margin-bottom: 0px !important; width: 300px"
|
||||||
|
label="测试项"
|
||||||
|
>
|
||||||
|
<el-select v-model="currentCheckItem">
|
||||||
|
<el-option
|
||||||
|
v-for="item in tesList"
|
||||||
|
:key="item"
|
||||||
|
:label="item.replace(/\.0$/, '')"
|
||||||
|
:value="item"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="content-right-Tabs">
|
||||||
|
<el-tabs type="border-card" v-model="activeTab">
|
||||||
|
<el-tab-pane label="检测结果" name="resultTab">
|
||||||
|
<CompareDataCheckResultTable
|
||||||
|
:tableData="checkResultData.length == 0 ? [] : currentCheckResultData"
|
||||||
|
:currentCheckItem="currentCheckItem"
|
||||||
|
:currentScriptTypeName="currentScriptTypeName"
|
||||||
|
v-if="activeTab === 'resultTab'"
|
||||||
|
/>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="原始数据" name="rawDataTab">
|
||||||
|
<CompareDataCheckRawDataTable
|
||||||
|
v-if="activeTab === 'rawDataTab'"
|
||||||
|
:tableData="rawTableData.length == 0 ? [] : currentRawTableData"
|
||||||
|
:currentCheckItem="currentCheckItem"
|
||||||
|
:currentScriptTypeName="currentScriptTypeName"
|
||||||
|
/>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { dialogBig } from '@/utils/elementBind'
|
||||||
|
import { computed, reactive, ref } from 'vue'
|
||||||
|
import CompareDataCheckResultTable from './compareDataCheckResultTable.vue'
|
||||||
|
import CompareDataCheckRawDataTable from './compareDataCheckRawDataTable.vue'
|
||||||
|
import { CheckData } from '@/api/check/interface'
|
||||||
|
import { useCheckStore } from '@/stores/modules/check'
|
||||||
|
import { Histogram, Postcard } from '@element-plus/icons-vue'
|
||||||
|
import { getPqErrSysList } from '@/api/plan/plan'
|
||||||
|
import { useModeStore } from '@/stores/modules/mode' // 引入模式 store
|
||||||
|
import { useDictStore } from '@/stores/modules/dict'
|
||||||
|
import {
|
||||||
|
changeErrorSystem,
|
||||||
|
deleteTempTable,
|
||||||
|
getContrastFormContent,
|
||||||
|
getContrastResult,
|
||||||
|
getScriptList,
|
||||||
|
reCalculate
|
||||||
|
} from '@/api/check/test'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { ResultEnum } from '@/enums/httpEnum'
|
||||||
|
|
||||||
|
const { appendToBody = true } = defineProps<{
|
||||||
|
appendToBody: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const checkStore = useCheckStore()
|
||||||
|
const modeStore = useModeStore()
|
||||||
|
const dictStore = useDictStore()
|
||||||
|
const visible = ref(false)
|
||||||
|
const treeRef = ref()
|
||||||
|
|
||||||
|
const pqErrorList = reactive<{ id: string; name: string }[]>([])
|
||||||
|
const activeTab = ref('resultTab')
|
||||||
|
const currentCheckItem = ref<any>()
|
||||||
|
const rowList: any = ref([])
|
||||||
|
let scriptType: string | null = null
|
||||||
|
|
||||||
|
const defaultProps = {
|
||||||
|
children: 'children',
|
||||||
|
label: 'scriptName'
|
||||||
|
}
|
||||||
|
|
||||||
|
const chnMapList: any = ref({})
|
||||||
|
const waveNumCount = ref(0)
|
||||||
|
const waveNumber = ref(1)
|
||||||
|
const selectedScriptName = ref('')
|
||||||
|
const pattern = ref('')
|
||||||
|
// 添加以下内容
|
||||||
|
const isWaveData = ref(false)
|
||||||
|
const scriptNameOptions = ref<{ label: string; value: string }[]>([])
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const formContent = reactive<CheckData.DataCheck>({
|
||||||
|
scriptName: '',
|
||||||
|
errorSysId: '',
|
||||||
|
dataRule: '',
|
||||||
|
deviceName: '',
|
||||||
|
chnNum: '',
|
||||||
|
deviceId: '',
|
||||||
|
num: ''
|
||||||
|
})
|
||||||
|
const sourceKey = ref(1) //1:正式检测进入页面 2:检测数据查询进入
|
||||||
|
|
||||||
|
// 通道下拉列表
|
||||||
|
const chnList: any = ref([])
|
||||||
|
|
||||||
|
// 当前检测项目名称
|
||||||
|
const currentScriptTypeName = ref('')
|
||||||
|
// 检测结果表格数据
|
||||||
|
const checkResultData = ref<CheckData.CheckResult[]>([])
|
||||||
|
// 检测脚本配置数据
|
||||||
|
const scriptData = ref<CheckData.ScriptItem[]>([])
|
||||||
|
//录波对应当前检测项下拉框
|
||||||
|
const selectScript = ref<CheckData.ScriptItem[]>([])
|
||||||
|
// 原始数据表格数据
|
||||||
|
const rawTableData = ref<CheckData.RawDataItem[]>([])
|
||||||
|
const tesList: any = ref([])
|
||||||
|
// 检测结果
|
||||||
|
const currentCheckResultData = computed(() => {
|
||||||
|
const data = checkResultData.value[currentCheckItem.value]
|
||||||
|
return Array.isArray(data) ? data : []
|
||||||
|
})
|
||||||
|
const currentRawTableData = computed(() => {
|
||||||
|
const data = rawTableData.value[currentCheckItem.value]
|
||||||
|
return Array.isArray(data) ? data : []
|
||||||
|
})
|
||||||
|
|
||||||
|
const open = async (row: any, chnNum: string, deviceId: string | null, source: number) => {
|
||||||
|
isWaveData.value = false
|
||||||
|
pattern.value = dictStore.getDictData('Pattern').find(item => item.name === modeStore.currentMode)?.id ?? '' //获取数据字典中对应的id
|
||||||
|
rowList.value = {}
|
||||||
|
formContent.deviceId = deviceId || ''
|
||||||
|
formContent.chnNum = chnNum
|
||||||
|
sourceKey.value = source
|
||||||
|
// 获取基本信息
|
||||||
|
await getBasicInformation(row.scriptType)
|
||||||
|
if (source == 1) {
|
||||||
|
// 正式检测进入页面 - 创建新的对象避免引用共享
|
||||||
|
rowList.value = {
|
||||||
|
scriptName: row.scriptName,
|
||||||
|
scriptType: row.scriptType,
|
||||||
|
// 复制其他需要的属性
|
||||||
|
devices: row.devices ? [...row.devices] : []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 检测数据查询进入---不区分检测数据查询和正式检测
|
||||||
|
await initScriptData()
|
||||||
|
visible.value = true
|
||||||
|
scriptType = null
|
||||||
|
formContent.errorSysId = checkStore.plan.errorSysId
|
||||||
|
|
||||||
|
pqErrorList.length = 0
|
||||||
|
// 获取误差体系
|
||||||
|
let { data: resPqErrorList } = await getPqErrSysList()
|
||||||
|
Object.assign(pqErrorList, resPqErrorList)
|
||||||
|
|
||||||
|
initGetResult()
|
||||||
|
}
|
||||||
|
|
||||||
|
const initGetResult = async () => {
|
||||||
|
// 判断是否为录波数据
|
||||||
|
const isLuoboData =
|
||||||
|
(sourceKey.value == 1 && rowList.value.scriptName == '录波数据') ||
|
||||||
|
(sourceKey.value == 2 && scriptData.value[0]?.code == 'wave_data')
|
||||||
|
if (isLuoboData) {
|
||||||
|
isWaveData.value = true
|
||||||
|
// 设置录波数据相关的选项
|
||||||
|
scriptNameOptions.value = selectScript.value.map(item => ({
|
||||||
|
label: item.scriptName,
|
||||||
|
value: item.scriptName
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 默认选中第一个选项
|
||||||
|
if (scriptNameOptions.value.length > 0) {
|
||||||
|
selectedScriptName.value = scriptNameOptions.value[0].value
|
||||||
|
// 更新rowList以匹配选中的script
|
||||||
|
const selectedItem = selectScript.value.find(item => item.scriptName === selectedScriptName.value)
|
||||||
|
if (selectedItem) {
|
||||||
|
rowList.value.scriptName = selectedScriptName.value
|
||||||
|
rowList.value.scriptType = selectedItem.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await getResults('wave_data')
|
||||||
|
} else {
|
||||||
|
await getResults('')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询大项树
|
||||||
|
const initScriptData = async () => {
|
||||||
|
let response: any = await getScriptList({
|
||||||
|
devId: formContent.deviceId,
|
||||||
|
chnNum: formContent.chnNum,
|
||||||
|
num: formContent.num
|
||||||
|
})
|
||||||
|
|
||||||
|
// 格式化脚本数据
|
||||||
|
let temp = response.data.map((item: any) => {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
scriptName: item.scriptName
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 保存脚本数据
|
||||||
|
scriptData.value = temp
|
||||||
|
|
||||||
|
// 查找code为"录波"的项
|
||||||
|
let luoboItem = response.data.find((item: any) => item.code === 'wave_data')
|
||||||
|
// 如果找到了"录波"项,则使用其subitems,否则使用空数组
|
||||||
|
let temp2 = []
|
||||||
|
if (luoboItem && luoboItem.subItems) {
|
||||||
|
// 格式化脚本数据
|
||||||
|
temp2 = luoboItem.subItems.map((item: any) => {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
scriptName: item.scriptName
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
selectScript.value = temp2
|
||||||
|
|
||||||
|
// 只有在sourceKey == 2时才设置rowList和tree相关属性
|
||||||
|
if (sourceKey.value === 2 && temp.length > 0) {
|
||||||
|
rowList.value.scriptName = temp[0].scriptName
|
||||||
|
rowList.value.scriptType = temp[0].id
|
||||||
|
selectedScriptName.value = temp[0].scriptName
|
||||||
|
setTimeout(() => {
|
||||||
|
treeRef.value?.setCurrentKey(temp[0].id)
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取基本信息
|
||||||
|
const getBasicInformation = async (scriptType: any) => {
|
||||||
|
checkResultData.value = []
|
||||||
|
rawTableData.value = []
|
||||||
|
const scriptType2 = ref('')
|
||||||
|
|
||||||
|
if (sourceKey.value == 1) {
|
||||||
|
scriptType2.value = scriptType
|
||||||
|
} else {
|
||||||
|
scriptType2.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
//确保scriptData已初始化
|
||||||
|
// if (scriptData.value.length === 0) {
|
||||||
|
// await initScriptData()
|
||||||
|
// }
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res: any = await getContrastFormContent({
|
||||||
|
planId: checkStore.plan.id,
|
||||||
|
scriptType: scriptType2.value,
|
||||||
|
deviceId: formContent.deviceId,
|
||||||
|
chnNum: formContent.chnNum,
|
||||||
|
num: formContent.num == '' ? null : parseInt(formContent.num),
|
||||||
|
patternId: pattern.value
|
||||||
|
})
|
||||||
|
|
||||||
|
formContent.dataRule = res.data.dataRule
|
||||||
|
formContent.deviceName = res.data.deviceName
|
||||||
|
formContent.errorSysId = res.data.errorSysId
|
||||||
|
chnMapList.value = res.data.chnMap
|
||||||
|
|
||||||
|
let chnMap: string[] = []
|
||||||
|
for (let key in res.data.chnMap) {
|
||||||
|
chnMap.push(key)
|
||||||
|
}
|
||||||
|
chnList.value = chnMap
|
||||||
|
formContent.chnNum = formContent.chnNum == null ? chnList.value[0] : formContent.chnNum
|
||||||
|
|
||||||
|
// 设置检测次数默认值为chnMap数组的最后一位
|
||||||
|
if (chnMapList.value[formContent.chnNum] && chnMapList.value[formContent.chnNum].length > 0) {
|
||||||
|
// 获取当前通道号对应的检测次数数组,并设置为最后一个值(最大值)
|
||||||
|
const numList = chnMapList.value[formContent.chnNum]
|
||||||
|
formContent.num = numList[numList.length - 1]
|
||||||
|
}
|
||||||
|
waveNumCount.value = res.data.waveNumTotal
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取基本信息失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleChnNumChange = async (value: string) => {
|
||||||
|
formContent.chnNum = value
|
||||||
|
// 更新检测次数为当前通道的最后一条记录
|
||||||
|
updateCheckNumForChn(value)
|
||||||
|
// 执行通用变更处理逻辑
|
||||||
|
await handleCommonChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理检测次数变更
|
||||||
|
const handleNumChange = async (value: string) => {
|
||||||
|
formContent.num = value
|
||||||
|
// 执行通用变更处理逻辑
|
||||||
|
await handleCommonChange()
|
||||||
|
}
|
||||||
|
// 通用的变更处理函数
|
||||||
|
const handleCommonChange = async () => {
|
||||||
|
// 重新初始化脚本数据(更新左侧树)
|
||||||
|
await initScriptData()
|
||||||
|
|
||||||
|
// 触发当前选中节点的点击事件,保持界面状态一致
|
||||||
|
if (sourceKey.value === 2 && scriptData.value.length > 0) {
|
||||||
|
// 查找当前选中的节点
|
||||||
|
const currentNode = scriptData.value.find((item: any) => item.id === rowList.value.scriptType)
|
||||||
|
if (currentNode) {
|
||||||
|
// 如果找到了当前节点,则触发点击事件
|
||||||
|
handleNodeClick(currentNode)
|
||||||
|
} else {
|
||||||
|
// 如果没有找到当前节点,则默认触发第一个节点的点击事件
|
||||||
|
handleNodeClick(scriptData.value[0])
|
||||||
|
}
|
||||||
|
} else if (sourceKey.value === 1 && rowList.value.scriptType) {
|
||||||
|
// 对于正式检测进入的情况,创建一个临时节点对象来触发handleNodeClick
|
||||||
|
const tempNode = {
|
||||||
|
scriptName: rowList.value.scriptName,
|
||||||
|
id: rowList.value.scriptType,
|
||||||
|
code: rowList.value.scriptName === '录波数据' ? 'wave_data' : ''
|
||||||
|
}
|
||||||
|
handleNodeClick(tempNode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新检测次数为指定通道的最后一条记录
|
||||||
|
const updateCheckNumForChn = (chnNum: string) => {
|
||||||
|
if (chnMapList.value[chnNum] && chnMapList.value[chnNum].length > 0) {
|
||||||
|
// 获取当前通道号对应的检测次数数组,并设置为最后一个值(最大值)
|
||||||
|
const numList = chnMapList.value[chnNum]
|
||||||
|
formContent.num = numList[numList.length - 1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 左边树变化
|
||||||
|
const handleNodeClick = (data: any) => {
|
||||||
|
rowList.value.scriptName = data.scriptName
|
||||||
|
rowList.value.scriptType = data.id
|
||||||
|
|
||||||
|
// 判断是否为录波数据
|
||||||
|
if (data.code === 'wave_data') {
|
||||||
|
isWaveData.value = true
|
||||||
|
// 过滤掉"录波"选项,设置下拉框数据
|
||||||
|
scriptNameOptions.value = selectScript.value
|
||||||
|
//.filter(item => item.code !== 'wave_data' && item.code !== 'FREQ')
|
||||||
|
.map(item => ({
|
||||||
|
label: item.scriptName,
|
||||||
|
value: item.scriptName
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 每次选中录波数据时都重置为第一个选项并触发getResults
|
||||||
|
if (scriptNameOptions.value.length > 0) {
|
||||||
|
selectedScriptName.value = scriptNameOptions.value[0].value
|
||||||
|
// 更新rowList并触发getResults
|
||||||
|
rowList.value.scriptName = selectedScriptName.value
|
||||||
|
const selectedItem = selectScript.value.find(item => item.scriptName === selectedScriptName.value)
|
||||||
|
|
||||||
|
if (selectedItem) {
|
||||||
|
rowList.value.scriptType = selectedItem.id
|
||||||
|
getResults('wave_data')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isWaveData.value = false
|
||||||
|
getResults(data.code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 获取结果
|
||||||
|
const getResults = async (code: any) => {
|
||||||
|
checkResultData.value = []
|
||||||
|
rawTableData.value = []
|
||||||
|
|
||||||
|
// 判断是否为录波数据请求
|
||||||
|
const isWaveDataRequest = code === 'wave_data' || isWaveData.value
|
||||||
|
console.log('isWaveDataRequest:', rowList.value.scriptType)
|
||||||
|
getContrastResult({
|
||||||
|
planId: checkStore.plan.id,
|
||||||
|
scriptType: rowList.value.scriptType,
|
||||||
|
deviceId: formContent.deviceId,
|
||||||
|
chnNum: formContent.chnNum,
|
||||||
|
num: formContent.num == '' ? null : formContent.num,
|
||||||
|
waveNum: isWaveDataRequest ? waveNumber.value : null,
|
||||||
|
isWave: isWaveDataRequest,
|
||||||
|
patternId: pattern.value
|
||||||
|
}).then((res: any) => {
|
||||||
|
let list: string[] = []
|
||||||
|
for (let key in res.data.resultMap) {
|
||||||
|
list.push(key)
|
||||||
|
}
|
||||||
|
currentCheckItem.value = list[0]
|
||||||
|
tesList.value = list
|
||||||
|
checkResultData.value = res.data.resultMap
|
||||||
|
rawTableData.value = res.data.rawDataMap
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加处理scriptName变化的方法
|
||||||
|
const handleScriptNameChange = (value: string) => {
|
||||||
|
selectedScriptName.value = value
|
||||||
|
rowList.value.scriptName = value
|
||||||
|
// 查找选中项的scriptType
|
||||||
|
const selectedItem = selectScript.value.find(item => item.scriptName === value)
|
||||||
|
if (selectedItem) {
|
||||||
|
rowList.value.scriptType = selectedItem.id
|
||||||
|
getResults('wave_data')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加处理waveNumber变化的方法
|
||||||
|
const handleWaveNumberChange = (value: number) => {
|
||||||
|
waveNumber.value = value
|
||||||
|
// 当录波次数改变时,重新获取结果
|
||||||
|
getResults('wave_data')
|
||||||
|
}
|
||||||
|
|
||||||
|
const close = async () => {
|
||||||
|
visible.value = false
|
||||||
|
formContent.num = ''
|
||||||
|
// 可以在这里添加其他清理逻辑
|
||||||
|
if (checkStore.showDetailType === 1) {
|
||||||
|
await deleteTempTable(checkStore.plan.code + '')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleErrorSysChange = async () => {
|
||||||
|
changeErrorSystem({
|
||||||
|
planId: checkStore.plan.id,
|
||||||
|
scriptId: '',
|
||||||
|
errorSysId: formContent.errorSysId,
|
||||||
|
deviceId: formContent.deviceId,
|
||||||
|
code: checkStore.plan.code + '',
|
||||||
|
patternId: dictStore.getDictData('Pattern').find(item => item.name === modeStore.currentMode)?.id ?? ''
|
||||||
|
}).then(res => {
|
||||||
|
if (res.code === ResultEnum.SUCCESS) {
|
||||||
|
ElMessage.success('切换误差体系成功')
|
||||||
|
handleChnNumChange(formContent.chnNum)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReCalculate = async () => {
|
||||||
|
reCalculate({
|
||||||
|
planId: checkStore.plan.id,
|
||||||
|
scriptId: '',
|
||||||
|
errorSysId: formContent.errorSysId,
|
||||||
|
deviceId: formContent.deviceId,
|
||||||
|
code: checkStore.plan.code + '',
|
||||||
|
patternId: dictStore.getDictData('Pattern').find(item => item.name === modeStore.currentMode)?.id ?? ''
|
||||||
|
}).then(res => {
|
||||||
|
if (res.code === ResultEnum.SUCCESS) {
|
||||||
|
ElMessage.success('重新计算成功!')
|
||||||
|
handleChnNumChange(formContent.chnNum)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
open
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.dialog {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.data-check-dialog {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.data-check-head {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-check-body {
|
||||||
|
height: 500px;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
.content-left-tree {
|
||||||
|
width: 18%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
max-height: 495px;
|
||||||
|
|
||||||
|
padding: 10px 0.5% 0px 0.5%;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: auto;
|
||||||
|
margin-right: 10px;
|
||||||
|
.content-tree {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin-top: 10px;
|
||||||
|
|
||||||
|
.custom-tree-node {
|
||||||
|
overflow-x: hidden !important;
|
||||||
|
white-space: nowrap !important;
|
||||||
|
text-overflow: ellipsis !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-right {
|
||||||
|
width: 82%;
|
||||||
|
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.content-right-title {
|
||||||
|
display: flex;
|
||||||
|
padding: 10px 0;
|
||||||
|
margin-top: 0px;
|
||||||
|
line-height: 1.5;
|
||||||
|
|
||||||
|
.content-right-title-text {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-right-Tabs {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
display: flex;
|
||||||
|
.el-tabs {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.content-left {
|
||||||
|
height: 100%;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
padding: 10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
height: 410px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.el-tabs--border-card > .el-tabs__content) {
|
||||||
|
height: 367px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
601
frontend/src/views/home/components/comparePreTest.vue
Normal file
601
frontend/src/views/home/components/comparePreTest.vue
Normal file
@@ -0,0 +1,601 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-tabs type="border-card">
|
||||||
|
<el-tab-pane label="预检测项目">
|
||||||
|
<div class="form-grid">
|
||||||
|
<el-checkbox v-for="(item, index) in detectionOptions" v-model="item.selected" :key="index"
|
||||||
|
:label="item.name" disabled></el-checkbox>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
|
||||||
|
<div class="test-dialog">
|
||||||
|
<div class="dialog-left">
|
||||||
|
<el-steps direction="vertical" :active="activeIndex" :process-status="currentStepStatus"
|
||||||
|
finish-status="success">
|
||||||
|
<el-step :status="step1" title="设备通讯校验"/>
|
||||||
|
<el-step :status="step2" title="模型一致性校验"/>
|
||||||
|
<el-step :status="step3" title="实时数据对齐验证" v-if="!props.onlyWave"/>
|
||||||
|
<el-step :status="step4" title="相序校验"/>
|
||||||
|
<!-- <el-step :status="step6" title="遥控录波功能验证"/> -->
|
||||||
|
<el-step :status="step5" :title="ts === 'error'? '检测失败':ts === 'process'? '检测中':ts === 'success'? '检测成功':'待检测'"/>
|
||||||
|
</el-steps>
|
||||||
|
</div>
|
||||||
|
<div class="dialog-right">
|
||||||
|
<el-collapse v-model="collapseActiveName" accordion>
|
||||||
|
<el-collapse-item title="设备通讯校验" name="1">
|
||||||
|
<div class="div-log">
|
||||||
|
<p v-for="(item, index) in step1InitLog" :key="index"
|
||||||
|
:style="{ color: item.type === 'error' ? '#F56C6C' : 'var(--el-text-color-regular)' }">
|
||||||
|
{{ item.log }} <br/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</el-collapse-item>
|
||||||
|
<el-collapse-item title="模型一致性校验" name="2">
|
||||||
|
<div class="div-log">
|
||||||
|
<p v-for="(item, index) in step2InitLog" :key="index"
|
||||||
|
:style="{ color: item.type === 'error' ? '#F56C6C' : 'var(--el-text-color-regular)' }">
|
||||||
|
{{ item.log }} <br/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</el-collapse-item>
|
||||||
|
<el-collapse-item name="3" v-if="!props.onlyWave">
|
||||||
|
<template #title>
|
||||||
|
实时数据对齐验证
|
||||||
|
<el-icon class="title-icon" @click="openDialog" v-if="isShowDialog"><InfoFilled/></el-icon>
|
||||||
|
</template>
|
||||||
|
<div class="div-log">
|
||||||
|
<p v-for="(item, index) in step3InitLog" :key="index"
|
||||||
|
:style="{ color: item.type === 'error' ? '#F56C6C' : 'var(--el-text-color-regular)' }">
|
||||||
|
{{ item.log }}
|
||||||
|
<br/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</el-collapse-item>
|
||||||
|
<el-collapse-item title="相序校验" name="4">
|
||||||
|
<div class="div-log">
|
||||||
|
<p v-for="(item, index) in step4InitLog" :key="index"
|
||||||
|
:style="{ color: item.type === 'error' ? '#F56C6C' : 'var(--el-text-color-regular)' }">
|
||||||
|
{{ item.log }} <br/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</el-collapse-item>
|
||||||
|
<!-- <el-collapse-item title="遥控录波功能验证" name="3">
|
||||||
|
<div class="div-log">
|
||||||
|
<p v-for="(item, index) in step3InitLog" :key="index"
|
||||||
|
:style="{ color: item.type === 'error' ? '#F56C6C' : 'var(--el-text-color-regular)' }">
|
||||||
|
{{ item.log.split('&&')[0] }}
|
||||||
|
<br v-if="item.log.includes('&&')"/>
|
||||||
|
{{ item.log.split('&&')[1] }}
|
||||||
|
|
||||||
|
<br v-if="item.log.includes('&&')"/>
|
||||||
|
{{ item.log.split('&&')[2] }}
|
||||||
|
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</el-collapse-item> -->
|
||||||
|
</el-collapse>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<RealTimeData ref="realTimeDataRef" />
|
||||||
|
</template>
|
||||||
|
<script lang="tsx" setup name="preTest">
|
||||||
|
import { ElMessage, ElMessageBox, StepProps } from 'element-plus'
|
||||||
|
import { computed, PropType, ref, toRef, watch } from 'vue'
|
||||||
|
import RealTimeData from './realTimeDataAlign.vue'
|
||||||
|
|
||||||
|
const realTimeDataRef = ref()
|
||||||
|
|
||||||
|
|
||||||
|
const step1InitLog = ref([
|
||||||
|
{
|
||||||
|
type: 'info',
|
||||||
|
log: '暂无数据,等待检测开始',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const step2InitLog = ref([
|
||||||
|
{
|
||||||
|
type: 'info',
|
||||||
|
log: '暂无数据,等待检测开始',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const step3InitLog = ref([
|
||||||
|
{
|
||||||
|
type: 'info',
|
||||||
|
log: '暂无数据,等待检测开始',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const step4InitLog = ref([
|
||||||
|
{
|
||||||
|
type: 'info',
|
||||||
|
log: '暂无数据,等待检测开始',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const isShowDialog = ref(false)
|
||||||
|
const collapseActiveName = ref('1')
|
||||||
|
const activeIndex = ref(0)
|
||||||
|
const activeTotalNum = computed(() => {
|
||||||
|
let count = 4; // 基础步骤数:设备通讯校验、模型一致性校验、相序校验、最终状态
|
||||||
|
if (props.onlyWave) {
|
||||||
|
count++; // 添加实时数据对齐验证步骤
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
});
|
||||||
|
const step1 = ref<StepProps['status']>('wait')
|
||||||
|
const step2 = ref<StepProps['status']>('wait')
|
||||||
|
const step3 = ref<StepProps['status']>('wait')
|
||||||
|
const step4 = ref<StepProps['status']>('wait')
|
||||||
|
const step5 = ref<StepProps['status']>('wait')
|
||||||
|
|
||||||
|
|
||||||
|
//定义与预检测配置数组
|
||||||
|
const detectionOptions = ref([
|
||||||
|
{
|
||||||
|
id: 0,
|
||||||
|
name: "设备通讯校验",
|
||||||
|
selected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: "模型一致性校验",
|
||||||
|
selected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: "实时数据对齐验证",
|
||||||
|
selected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: "相序校验",
|
||||||
|
selected: true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const currentStepStatus = ref<'error' | 'finish' | 'wait' | 'success' | 'process'>('finish');
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
testStatus: {
|
||||||
|
type: String,
|
||||||
|
default: 'wait'
|
||||||
|
},
|
||||||
|
webMsgSend: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
mapping: {
|
||||||
|
type: Object as PropType<Record<string, Record<string, string>>>,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
onlyWave: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const testStatus = toRef(props, 'testStatus');
|
||||||
|
const webMsgSend = toRef(props, 'webMsgSend');
|
||||||
|
const ts = ref('');
|
||||||
|
|
||||||
|
|
||||||
|
// 在 script setup 中定义接口
|
||||||
|
interface ChannelData {
|
||||||
|
devNum: string;
|
||||||
|
standardDevInfo: string;
|
||||||
|
dataList: {
|
||||||
|
timeDev: string | null;
|
||||||
|
uaDev: number | null;
|
||||||
|
ubDev: number | null;
|
||||||
|
ucDev: number | null;
|
||||||
|
timeStdDev: string | null;
|
||||||
|
uaStdDev: number | null;
|
||||||
|
ubStdDev: number | null;
|
||||||
|
ucStdDev: number | null;
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DeviceData {
|
||||||
|
devName: string;
|
||||||
|
channelDataList: ChannelData[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改 testDataStructure 的类型声明
|
||||||
|
const testDataStructure = ref<Record<string, DeviceData>>({});
|
||||||
|
|
||||||
|
watch(webMsgSend, function (newValue, oldValue) {
|
||||||
|
if(testStatus.value == 'success' || testStatus.value == 'error'){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (testStatus.value !== 'waiting') {
|
||||||
|
if(newValue.code == 25004){
|
||||||
|
ElMessage.error('接收数据超时!')
|
||||||
|
ts.value = 'error'
|
||||||
|
step5.value = 'error'
|
||||||
|
}else if(newValue.code == 10550){
|
||||||
|
ElMessage.error('设备连接异常!')
|
||||||
|
ts.value = 'error'
|
||||||
|
step5.value = 'error'
|
||||||
|
}
|
||||||
|
switch (newValue.requestId) {
|
||||||
|
case 'yjc_sbtxjy':
|
||||||
|
if (newValue.code == 10200) {
|
||||||
|
step1InitLog.value.push({
|
||||||
|
type: 'info',
|
||||||
|
log: newValue.data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (newValue.code == 10201) {
|
||||||
|
step1.value = 'process'
|
||||||
|
step1InitLog.value = [{
|
||||||
|
type: 'wait',
|
||||||
|
log: '正在进行设备通讯校验.....',
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
if (newValue.code == 10551) {
|
||||||
|
step1InitLog.value.push({
|
||||||
|
type: 'error',
|
||||||
|
log: newValue.data + '设备触发报告异常!',
|
||||||
|
})
|
||||||
|
step1.value = 'error'
|
||||||
|
ts.value = 'error'
|
||||||
|
step5.value = 'error'
|
||||||
|
}
|
||||||
|
if (newValue.code == 10552) {
|
||||||
|
step1InitLog.value = [{
|
||||||
|
type: 'error',
|
||||||
|
log: '存在已经初始化步骤,执行自动关闭,请重新发起检测!',
|
||||||
|
}];
|
||||||
|
step1.value = 'error'
|
||||||
|
ts.value = 'error'
|
||||||
|
step5.value = 'error'
|
||||||
|
}
|
||||||
|
if (newValue.code == 25001) {
|
||||||
|
step1InitLog.value.push({
|
||||||
|
type: 'info',
|
||||||
|
log: newValue.data,
|
||||||
|
})
|
||||||
|
activeIndex.value = 1
|
||||||
|
step1.value = 'success'
|
||||||
|
step2.value = 'process'
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'record_wave_step1':
|
||||||
|
if (newValue.code == 25002) { //某一路录波校验失败
|
||||||
|
step1InitLog.value.push({
|
||||||
|
type: 'error',
|
||||||
|
log: newValue.data,
|
||||||
|
})
|
||||||
|
} else if (newValue.code == 25003) { //最终失败
|
||||||
|
step1.value = 'error'
|
||||||
|
ts.value = 'error'
|
||||||
|
step5.value = 'error'
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'yjc_mxyzxjy':
|
||||||
|
if (newValue.code == 10200) { //单个监测点成功
|
||||||
|
step2InitLog.value.push({
|
||||||
|
type: 'info',
|
||||||
|
log: newValue.data + '模型一致性检验成功!',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (newValue.code == 10201) {
|
||||||
|
step2.value = 'process'
|
||||||
|
step2InitLog.value = [{
|
||||||
|
type: 'wait',
|
||||||
|
log: '正在进行模型一致性校验.....',
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
if (newValue.code == 25002) { //单个监测点失败
|
||||||
|
step2InitLog.value.push({
|
||||||
|
type: 'error',
|
||||||
|
log: newValue.data + '模型一致性检验失败!',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (newValue.code == 25001) { //最终成功
|
||||||
|
step2.value = 'success'
|
||||||
|
step3.value = 'process'
|
||||||
|
activeIndex.value = 2
|
||||||
|
}
|
||||||
|
if (newValue.code == 25003) { //最终失败
|
||||||
|
step2.value = 'error'
|
||||||
|
ts.value = 'error'
|
||||||
|
step5.value = 'error'
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'yjc_align':
|
||||||
|
if (newValue.code == 10200) { //单个监测点成功
|
||||||
|
step3InitLog.value.push({
|
||||||
|
type: 'info',
|
||||||
|
log: newValue.data + '数据对齐检验成功!',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (newValue.code == 10201) {
|
||||||
|
step3.value = 'process'
|
||||||
|
step3InitLog.value = [{
|
||||||
|
type: 'wait',
|
||||||
|
log: '正在进行数据对齐检验.....',
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
if (newValue.code == 25002) { //单个监测点失败
|
||||||
|
|
||||||
|
step3InitLog.value.push({
|
||||||
|
type: 'error',
|
||||||
|
log: newValue.data + '数据对齐检验失败!',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (newValue.code == 25001 && newValue.data) { //最终成功
|
||||||
|
isShowDialog.value = true
|
||||||
|
step3.value = 'success'
|
||||||
|
step4.value = 'process'
|
||||||
|
activeIndex.value = 3
|
||||||
|
testDataStructure.value = newValue.data
|
||||||
|
}
|
||||||
|
if (newValue.code == 25003) { //最终失败
|
||||||
|
|
||||||
|
isShowDialog.value = true
|
||||||
|
step3.value = 'error'
|
||||||
|
ts.value = 'error'
|
||||||
|
step5.value = 'error'
|
||||||
|
testDataStructure.value = newValue.data
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'YJC_xujy':
|
||||||
|
if (newValue.code == 10200) {
|
||||||
|
step4InitLog.value.push({
|
||||||
|
type: 'info',
|
||||||
|
log: newValue.data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (newValue.code == 10201) {
|
||||||
|
step4.value = 'process'
|
||||||
|
step4InitLog.value = [{
|
||||||
|
type: 'wait',
|
||||||
|
log: '正在进行相序性检.....',
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
if (newValue.code == 25002) {
|
||||||
|
step4InitLog.value.push({
|
||||||
|
type: 'error',
|
||||||
|
log: newValue.data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (newValue.code == 25003) {
|
||||||
|
step4InitLog.value.push({
|
||||||
|
type: 'error',
|
||||||
|
log: newValue.data,
|
||||||
|
})
|
||||||
|
step4.value = 'error'
|
||||||
|
ts.value = 'error'
|
||||||
|
step5.value = 'error'
|
||||||
|
}
|
||||||
|
if (newValue.code == 25001) {
|
||||||
|
step4.value = 'success'
|
||||||
|
step5.value = 'success'
|
||||||
|
ts.value = 'success'
|
||||||
|
activeIndex.value = 4
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'quit':
|
||||||
|
break;
|
||||||
|
case 'connect':
|
||||||
|
switch (newValue.operateCode) {
|
||||||
|
case "Contrast_Dev":
|
||||||
|
step1.value = 'error'
|
||||||
|
step1InitLog.value = [{
|
||||||
|
type: 'error',
|
||||||
|
log: '设备服务端连接失败!',
|
||||||
|
}];
|
||||||
|
ts.value = 'error'
|
||||||
|
step5.value = 'error'
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'unknown_operate':
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'error_flow_end':
|
||||||
|
ElMessageBox.alert(`当前流程存在异常结束!`, '检测失败', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
type: 'error',
|
||||||
|
})
|
||||||
|
ts.value = 'error'
|
||||||
|
step5.value = 'error'
|
||||||
|
break;
|
||||||
|
case 'socket_timeout':
|
||||||
|
ElMessageBox.alert(`设备连接异常,请检查设备连接情况!`, '检测失败', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
type: 'error',
|
||||||
|
})
|
||||||
|
ts.value = 'error'
|
||||||
|
step5.value = 'error'
|
||||||
|
break;
|
||||||
|
case 'server_error':
|
||||||
|
ElMessageBox.alert('服务端主动关闭连接!', '初始化失败', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
type: 'error',
|
||||||
|
})
|
||||||
|
ts.value = 'error'
|
||||||
|
step5.value = 'error'
|
||||||
|
break;
|
||||||
|
case 'device_error':
|
||||||
|
ElMessageBox.alert('设备主动关闭连接!', '初始化失败', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
type: 'error',
|
||||||
|
})
|
||||||
|
ts.value = 'error'
|
||||||
|
step5.value = 'error'
|
||||||
|
break;
|
||||||
|
case 'yjc_xyjy' :
|
||||||
|
switch (newValue.operateCode) {
|
||||||
|
case 'INIT_GATHER$03':
|
||||||
|
if (newValue.code == 10552) {
|
||||||
|
ElMessageBox.alert('重复的初始化操作!', '检测失败', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
type: 'error',
|
||||||
|
})
|
||||||
|
ts.value = 'error'
|
||||||
|
step5.value = 'error'
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// watch(activeIndex, function (newValue, oldValue) {
|
||||||
|
// if (newValue <= activeTotalNum.value - 2) {
|
||||||
|
// collapseActiveName.value = (newValue + 1).toString()
|
||||||
|
// } else {
|
||||||
|
// collapseActiveName.value = newValue.toString()
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
|
||||||
|
watch(activeIndex, function (newValue, oldValue) {
|
||||||
|
if(props.onlyWave === true)
|
||||||
|
{
|
||||||
|
if (Number(collapseActiveName.value) < activeTotalNum.value - 2) {
|
||||||
|
if(newValue == 2){
|
||||||
|
collapseActiveName.value = '4'
|
||||||
|
}else{
|
||||||
|
collapseActiveName.value = (newValue + 1).toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
if (Number(collapseActiveName.value) < activeTotalNum.value) {
|
||||||
|
collapseActiveName.value = (newValue + 1).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
//监听goods_sn的变化
|
||||||
|
watch(testStatus, function (newValue, oldValue) {
|
||||||
|
ts.value = props.testStatus;
|
||||||
|
if (ts.value === 'start') {
|
||||||
|
ts.value = 'process'
|
||||||
|
} else if (ts.value === 'waiting') {
|
||||||
|
activeIndex.value = 0
|
||||||
|
step1InitLog.value = [
|
||||||
|
{
|
||||||
|
type: 'info',
|
||||||
|
log: '暂无数据,等待检测开始',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
step1.value = 'finish'
|
||||||
|
step2.value = 'wait'
|
||||||
|
step3.value = 'wait'
|
||||||
|
step4.value = 'wait'
|
||||||
|
step5.value = 'wait'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:testStatus']);
|
||||||
|
//监听sn
|
||||||
|
watch(ts, function (newValue, oldValue) {
|
||||||
|
//修改父组件
|
||||||
|
emit('update:testStatus', ts.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 定义一个初始化参数的方法
|
||||||
|
function initializeParameters() {
|
||||||
|
activeIndex.value = 0
|
||||||
|
step1.value = 'wait'
|
||||||
|
step2.value = 'wait'
|
||||||
|
step3.value = 'wait'
|
||||||
|
step4.value = 'wait'
|
||||||
|
step5.value = 'wait'
|
||||||
|
|
||||||
|
step1InitLog.value = [
|
||||||
|
{
|
||||||
|
type: 'info',
|
||||||
|
log: '暂无数据,等待检测开始',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
step2InitLog.value = [
|
||||||
|
{
|
||||||
|
type: 'info',
|
||||||
|
log: '暂无数据,等待检测开始',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
step3InitLog.value = [
|
||||||
|
{
|
||||||
|
type: 'info',
|
||||||
|
log: '暂无数据,等待检测开始',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
step4InitLog.value = [
|
||||||
|
{
|
||||||
|
type: 'info',
|
||||||
|
log: '暂无数据,等待检测开始',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
// 清空实时数据对齐验证的数据
|
||||||
|
|
||||||
|
testDataStructure.value = {}
|
||||||
|
isShowDialog.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const openDialog = () => {
|
||||||
|
|
||||||
|
realTimeDataRef.value.open(props.mapping,testDataStructure.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
initializeParameters,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.title-icon {
|
||||||
|
margin-left: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 16px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test-dialog {
|
||||||
|
height: 350px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
/* 横向排列 */
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-left {
|
||||||
|
width: 15%;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-right {
|
||||||
|
margin-left: 20px;
|
||||||
|
width: 80%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-right :deep(.el-collapse-item__header) {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.div-log {
|
||||||
|
height: 145px;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-left: 10px;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 5px 0;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
917
frontend/src/views/home/components/compareTest.vue
Normal file
917
frontend/src/views/home/components/compareTest.vue
Normal file
@@ -0,0 +1,917 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="dialog" v-bind="dialogBig">
|
||||||
|
<div class="dialog-title">
|
||||||
|
<div class="timeView">
|
||||||
|
<el-icon style="margin: 0px 5px">
|
||||||
|
<Clock/>
|
||||||
|
</el-icon>
|
||||||
|
<span>检测用时:{{ timeView }}</span>
|
||||||
|
</div>
|
||||||
|
<el-progress style="width: 82%; margin-right: 3%" :percentage="percentage" :color="customColors"/>
|
||||||
|
<el-button style="width: 10%" type="text" :icon="InfoFilled" @click="showTestLog">检测项进度</el-button>
|
||||||
|
</div>
|
||||||
|
<div class="dialog-content">
|
||||||
|
<el-table
|
||||||
|
:data="checkResultView"
|
||||||
|
row-key="scriptType"
|
||||||
|
height="450px"
|
||||||
|
:header-cell-style="{ background: 'var(--el-color-primary)', color: '#eee', textAlign: 'center' }"
|
||||||
|
style="width: 100%"
|
||||||
|
border
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
fixed
|
||||||
|
prop="scriptName"
|
||||||
|
label="检测项目"
|
||||||
|
width="150px"
|
||||||
|
align="center"
|
||||||
|
></el-table-column>
|
||||||
|
|
||||||
|
<template v-if="chnSum <= MAX_CHN_SUM">
|
||||||
|
<el-table-column
|
||||||
|
v-for="(item, index1) in deviceList"
|
||||||
|
:key="item.deviceId"
|
||||||
|
:label="item.deviceName"
|
||||||
|
:min-width="110"
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
|
<el-table-column
|
||||||
|
v-for="(chnItem, index2) in checkStore.chnNumList"
|
||||||
|
:key="`${item.deviceId}${chnItem}`"
|
||||||
|
:label="'通道' + chnItem"
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tooltip
|
||||||
|
:content="
|
||||||
|
row.devices[index1].chnResult[index2].icon === 'More'
|
||||||
|
? '暂无数据'
|
||||||
|
: row.devices[index1].chnResult[index2].icon === 'CircleCheckFilled'
|
||||||
|
? '符合'
|
||||||
|
: row.devices[index1].chnResult[index2].icon === 'Close'
|
||||||
|
? '不符合'
|
||||||
|
: row.devices[index1].chnResult[index2].icon ===
|
||||||
|
'WarnTriangleFilled'
|
||||||
|
? '数据异常'
|
||||||
|
: row.devices[index1].chnResult[index2].icon === 'Loading'
|
||||||
|
? '检测中'
|
||||||
|
: '连接中断'
|
||||||
|
"
|
||||||
|
placement="right"
|
||||||
|
>
|
||||||
|
<el-button
|
||||||
|
:disabled="
|
||||||
|
row.devices[index1].chnResult[index2].color ===
|
||||||
|
CheckData.ButtonColorEnum.INFO ||
|
||||||
|
row.devices[index1].chnResult[index2].color ===
|
||||||
|
CheckData.ButtonColorEnum.LOADING
|
||||||
|
"
|
||||||
|
:color="row.devices[index1].chnResult[index2].color"
|
||||||
|
size="small"
|
||||||
|
@click="handleClick(row, chnItem, row.scriptType)"
|
||||||
|
style="align-self: center"
|
||||||
|
>
|
||||||
|
<el-icon
|
||||||
|
v-if="row.devices[index1].chnResult[index2].icon === 'Loading'"
|
||||||
|
class="loading-box"
|
||||||
|
style="color: #fff"
|
||||||
|
>
|
||||||
|
<component :is="Loading"/>
|
||||||
|
</el-icon>
|
||||||
|
<el-icon v-else style="color: #fff">
|
||||||
|
<component :is="row.devices[index1].chnResult[index2].icon"/>
|
||||||
|
</el-icon>
|
||||||
|
</el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="drawer-container">
|
||||||
|
<el-drawer v-model="drawer" title="检测项进度" direction="btt" :size="'38%'">
|
||||||
|
<div ref="scrollContainerRef" style="height: 100%; overflow-y: auto">
|
||||||
|
<p
|
||||||
|
v-for="(item, index) in testLogList"
|
||||||
|
:key="index"
|
||||||
|
:style="{
|
||||||
|
color:
|
||||||
|
item.type === 'error'
|
||||||
|
? '#F56C6C'
|
||||||
|
: item.type === 'warning'
|
||||||
|
? '#e6a23c'
|
||||||
|
: 'var(--el-text-color-regular)'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ item.log }}
|
||||||
|
<br/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</el-drawer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<CompareDataCheckSingleChannelSingleTestPopup
|
||||||
|
ref="dataCheckSingleChannelSingleTestPopupRef"
|
||||||
|
:append-to-body="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="tsx" setup name="test">
|
||||||
|
import {InfoFilled, Loading} from '@element-plus/icons-vue'
|
||||||
|
import CompareDataCheckSingleChannelSingleTestPopup from './compareDataCheckSingleChannelSingleTestPopup.vue'
|
||||||
|
import {computed, ComputedRef, nextTick, onBeforeMount, onMounted, reactive, ref, toRef, watch} from 'vue'
|
||||||
|
import {dialogBig} from '@/utils/elementBind'
|
||||||
|
import {CheckData} from '@/api/check/interface'
|
||||||
|
import {useCheckStore} from '@/stores/modules/check'
|
||||||
|
import {ElMessage, ElMessageBox} from 'element-plus'
|
||||||
|
import {getBigTestItem} from '@/api/check/test'
|
||||||
|
import {getAutoGenerate} from '@/api/user/login'
|
||||||
|
import {useModeStore} from '@/stores/modules/mode' // 引入模式 store
|
||||||
|
import {useDictStore} from '@/stores/modules/dict'
|
||||||
|
import { ca } from 'element-plus/es/locale'
|
||||||
|
|
||||||
|
const checkStore = useCheckStore()
|
||||||
|
const modeStore = useModeStore()
|
||||||
|
const dictStore = useDictStore()
|
||||||
|
// 最大通道数
|
||||||
|
const MAX_CHN_SUM = 12
|
||||||
|
|
||||||
|
// 总测试项数
|
||||||
|
let checkTotal = 0
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
testStatus: {
|
||||||
|
type: String,
|
||||||
|
default: 'waiting'
|
||||||
|
},
|
||||||
|
stepsActive: {
|
||||||
|
type: Number
|
||||||
|
},
|
||||||
|
webMsgSend: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits([
|
||||||
|
'update:testStatus',
|
||||||
|
'update:webMsgSend',
|
||||||
|
'sendPause',
|
||||||
|
'sendResume',
|
||||||
|
'sendReCheck',
|
||||||
|
'closeWebSocket'
|
||||||
|
])
|
||||||
|
|
||||||
|
// 用来保存测试项进度抽屉是否打开
|
||||||
|
const drawer = ref(false)
|
||||||
|
// 进度条颜色
|
||||||
|
const customColors = [{color: '#91cc75', percentage: 100}]
|
||||||
|
// 检测脚本数据
|
||||||
|
let scriptData: CheckData.ScriptItem[] = []
|
||||||
|
// 用来保存被检设备
|
||||||
|
const deviceList = reactive<CheckData.Device[]>([])
|
||||||
|
// 当前进行的测试项索引
|
||||||
|
let activeIndex = 0
|
||||||
|
// 百分比
|
||||||
|
const percentage = ref(0)
|
||||||
|
// 时间计数器
|
||||||
|
let timer: any = null
|
||||||
|
const timeCount = ref(0)
|
||||||
|
const timeView = ref('00:00:00')
|
||||||
|
//测试项开始检测时间(或继续检测时间)
|
||||||
|
const startData = ref(new Date())
|
||||||
|
//测试项检测结束时间(或暂停时的时间)
|
||||||
|
const endData = ref(new Date())
|
||||||
|
const timeDifference = ref(0)
|
||||||
|
// 真正的检测结果(详细到通道)
|
||||||
|
const checkResult = reactive<CheckData.ScriptChnItem[]>([])
|
||||||
|
// 用来存放检测出现失败的测试项id。只要有一个通道检测不合格,则该检测项的id会被加入该数组。
|
||||||
|
let errorCheckItem: Array<{ scriptType: string; type: CheckData.ChnCheckResultEnum }> = []
|
||||||
|
// 用来存放检测日志
|
||||||
|
const testLogList = reactive<CheckData.LogItem[]>([{type: 'info', log: '暂无数据,等待检测开始'}])
|
||||||
|
// 添加一个响应式变量来跟踪是否需要显示录波项目
|
||||||
|
const showWaveItem = ref(false)
|
||||||
|
const testStatus = toRef(props, 'testStatus')
|
||||||
|
const webMsgSend = toRef(props, 'webMsgSend')
|
||||||
|
|
||||||
|
const scrollContainerRef = ref()
|
||||||
|
const dataCheckSingleChannelSingleTestPopupRef =
|
||||||
|
ref<InstanceType<typeof CompareDataCheckSingleChannelSingleTestPopup>>()
|
||||||
|
|
||||||
|
// 总通道数
|
||||||
|
const chnSum = computed(() => {
|
||||||
|
let sum = 0
|
||||||
|
deviceList.forEach(item => {
|
||||||
|
sum += item.chnNum
|
||||||
|
})
|
||||||
|
return sum
|
||||||
|
})
|
||||||
|
|
||||||
|
// 用来展示的检测结果
|
||||||
|
const checkResultView: ComputedRef<CheckData.ScriptChnViewItem[]> = computed(() => {
|
||||||
|
let result: CheckData.ScriptChnViewItem[] = checkResult.map(item => {
|
||||||
|
let temp: CheckData.ScriptChnViewItem = {
|
||||||
|
scriptType: item.scriptType,
|
||||||
|
scriptName: item.scriptName,
|
||||||
|
devices: []
|
||||||
|
}
|
||||||
|
item.devices.forEach(device => {
|
||||||
|
let tempChnBtnResult: CheckData.ButtonResult[] = []
|
||||||
|
|
||||||
|
if (chnSum.value <= MAX_CHN_SUM) {
|
||||||
|
for (let j = 0; j < device.chnResult.length; j++) {
|
||||||
|
switch (device.chnResult[j]) {
|
||||||
|
case CheckData.ChnCheckResultEnum.UNKNOWN:
|
||||||
|
tempChnBtnResult.push({color: CheckData.ButtonColorEnum.INFO, icon: 'More'})
|
||||||
|
break
|
||||||
|
case CheckData.ChnCheckResultEnum.LOADING:
|
||||||
|
tempChnBtnResult.push({color: CheckData.ButtonColorEnum.LOADING, icon: 'Loading'})
|
||||||
|
break
|
||||||
|
case CheckData.ChnCheckResultEnum.SUCCESS:
|
||||||
|
tempChnBtnResult.push({
|
||||||
|
color: CheckData.ButtonColorEnum.SUCCESS,
|
||||||
|
icon: 'CircleCheckFilled'
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case CheckData.ChnCheckResultEnum.FAIL:
|
||||||
|
tempChnBtnResult.push({color: CheckData.ButtonColorEnum.DANGER, icon: 'Close'})
|
||||||
|
break
|
||||||
|
case CheckData.ChnCheckResultEnum.TIMEOUT:
|
||||||
|
tempChnBtnResult.push({color: CheckData.ButtonColorEnum.WARNING, icon: 'Link'})
|
||||||
|
break
|
||||||
|
case CheckData.ChnCheckResultEnum.ERRORDATA:
|
||||||
|
tempChnBtnResult.push({
|
||||||
|
color: CheckData.ButtonColorEnum.WARNING,
|
||||||
|
icon: 'WarnTriangleFilled'
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case CheckData.ChnCheckResultEnum.NOT_PART_IN_ERROR:
|
||||||
|
tempChnBtnResult.push({color: CheckData.ButtonColorEnum.INFO, icon: 'Minus'})
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
temp.devices.push({
|
||||||
|
deviceId: device.deviceId,
|
||||||
|
deviceName: device.deviceName,
|
||||||
|
chnResult: tempChnBtnResult
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return temp
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(testStatus, function (newValue, oldValue) {
|
||||||
|
if (newValue == 'start' || newValue == 'waiting') {
|
||||||
|
if (!checkStore.selectTestItems.preTest && !checkStore.selectTestItems.channelsTest) {
|
||||||
|
ElMessage.success('初始化开始!')
|
||||||
|
emit('update:testStatus', 'test_init')
|
||||||
|
if (checkStore.selectTestItems.test && checkStore.selectTestItems.preTest) {
|
||||||
|
testLogList.push({type: 'info', log: `${new Date().toLocaleString()}:初始化开始!`})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
emit('update:testStatus', 'process')
|
||||||
|
}
|
||||||
|
// 开始计时
|
||||||
|
startTimeCount()
|
||||||
|
showTestLog()
|
||||||
|
|
||||||
|
//startTimer() // todo 可移除
|
||||||
|
startData.value = new Date()
|
||||||
|
timeDifference.value = 0
|
||||||
|
} else if (newValue == 'error') {
|
||||||
|
stopTimeCount()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
webMsgSend,
|
||||||
|
function (newValue, oldValue) {
|
||||||
|
switch (newValue.requestId) {
|
||||||
|
case 'record_wave_step1':
|
||||||
|
switch (newValue.code) {
|
||||||
|
case 10200:
|
||||||
|
setLogList('info', newValue.data)
|
||||||
|
break
|
||||||
|
case 25002:
|
||||||
|
setLogList('error', newValue.data)
|
||||||
|
break
|
||||||
|
case 25003:
|
||||||
|
ElMessageBox.alert('录波对齐失败!', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
type: 'error'
|
||||||
|
})
|
||||||
|
stopTimeCount()
|
||||||
|
break
|
||||||
|
case 25001: {
|
||||||
|
// 当录波校验完成时,更新录波项目的按钮状态
|
||||||
|
setLogList('info', '录波校验完成!')
|
||||||
|
// 解析返回的数据
|
||||||
|
const waveData = JSON.parse(newValue.data)
|
||||||
|
// 找到录波项目并更新其状态
|
||||||
|
const waveResultItem = checkResult.find(item => item.scriptType === 'wave_data')
|
||||||
|
// if (waveResultItem) {
|
||||||
|
// // 将录波项目状态设置为SUCCESS
|
||||||
|
// waveResultItem.devices.forEach(device => {
|
||||||
|
// device.chnResult.fill(CheckData.ChnCheckResultEnum.SUCCESS)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
if (waveResultItem) {
|
||||||
|
// 根据返回的chnResult更新各个设备的通道状态
|
||||||
|
waveResultItem.devices.forEach(device => {
|
||||||
|
const deviceData = waveData.find((d: any) => d.deviceId === device.deviceId)
|
||||||
|
if (deviceData) {
|
||||||
|
// 根据实际返回的chnResult更新状态
|
||||||
|
deviceData.chnResult.forEach((result: number, index: number) => {
|
||||||
|
// 创建数字到枚举的映射
|
||||||
|
const resultMap: { [key: number]: CheckData.ChnCheckResultEnum } = {
|
||||||
|
1: CheckData.ChnCheckResultEnum.SUCCESS,
|
||||||
|
2: CheckData.ChnCheckResultEnum.FAIL,
|
||||||
|
3: CheckData.ChnCheckResultEnum.TIMEOUT,
|
||||||
|
4: CheckData.ChnCheckResultEnum.ERRORDATA,
|
||||||
|
5: CheckData.ChnCheckResultEnum.NOT_PART_IN_ERROR
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用映射关系设置状态,如果没有对应的映射则设为UNKNOWN
|
||||||
|
device.chnResult[index] = resultMap[result] || CheckData.ChnCheckResultEnum.UNKNOWN
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 触发响应式更新
|
||||||
|
checkResult.splice(0, 0)
|
||||||
|
stopTimeCount()
|
||||||
|
updatePercentage()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'connect':
|
||||||
|
switch (newValue.operateCode) {
|
||||||
|
case 'Contrast_Dev':
|
||||||
|
setLogList('error', '设备服务端连接失败!')
|
||||||
|
stopTimeCount()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'unknown_operate':
|
||||||
|
break
|
||||||
|
case 'error_flow_end':
|
||||||
|
ElMessageBox.alert(`当前流程存在异常结束!`, '检测失败', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
type: 'error'
|
||||||
|
})
|
||||||
|
setLogList('error', '当前流程存在异常结束!')
|
||||||
|
stopTimeCount()
|
||||||
|
break
|
||||||
|
case 'socket_timeout':
|
||||||
|
ElMessageBox.alert(`设备连接异常,请检查设备连接情况!`, '检测失败', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
type: 'error'
|
||||||
|
})
|
||||||
|
setLogList('error', '设备连接异常,请检查设备连接情况!')
|
||||||
|
stopTimeCount()
|
||||||
|
break
|
||||||
|
case 'server_error':
|
||||||
|
ElMessageBox.alert('服务端主动关闭连接!', '初始化失败', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
type: 'error'
|
||||||
|
})
|
||||||
|
setLogList('error', '服务端主动关闭连接!')
|
||||||
|
stopTimeCount()
|
||||||
|
break
|
||||||
|
case 'device_error':
|
||||||
|
ElMessageBox.alert('设备主动关闭连接!', '初始化失败', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
type: 'error'
|
||||||
|
})
|
||||||
|
setLogList('error', '设备主动关闭连接!')
|
||||||
|
stopTimeCount()
|
||||||
|
break
|
||||||
|
case 'yjc_xyjy' :
|
||||||
|
if (newValue.code == 10552) {
|
||||||
|
ElMessageBox.alert('重复的初始化操作!', '检测失败', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
type: 'error',
|
||||||
|
})
|
||||||
|
setLogList('error', '重复的初始化操作!')
|
||||||
|
stopTimeCount()
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'yjc_sbtxjy' :
|
||||||
|
ElMessageBox.alert('重复的初始化操作!', '检测失败', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
type: 'error',
|
||||||
|
})
|
||||||
|
setLogList('error', '重复的初始化操作!')
|
||||||
|
stopTimeCount()
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (checkStore.selectTestItems.preTest == false && newValue.requestId != 'formal_real') {
|
||||||
|
if (testLogList[0].log == '正在检测,请稍等...' || testLogList[0].log == '暂无数据,等待检测开始') {
|
||||||
|
testLogList.shift()
|
||||||
|
setLogList('info', '初始化开始!')
|
||||||
|
}
|
||||||
|
let str =
|
||||||
|
newValue.requestId == 'yjc_sbtxjy'
|
||||||
|
? '设备通讯校验'
|
||||||
|
: newValue.requestId == 'yjc_mxyzxjy'
|
||||||
|
? '模型一致性检验'
|
||||||
|
: newValue.requestId == 'yjc_align'
|
||||||
|
? '数据对齐检验'
|
||||||
|
: newValue.requestId == 'YJC_xujy'
|
||||||
|
? '相序校验'
|
||||||
|
: ''
|
||||||
|
// 预检测处理
|
||||||
|
switch (newValue.code) {
|
||||||
|
case 25001:
|
||||||
|
// 成功
|
||||||
|
if (newValue.data != undefined) return
|
||||||
|
setLogList('info', str + '成功!')
|
||||||
|
if (newValue.requestId == 'YJC_xujy') setLogList('info', '初始化成功!')
|
||||||
|
break
|
||||||
|
case 25003:
|
||||||
|
// 失败
|
||||||
|
if (newValue.data != undefined) return
|
||||||
|
setLogList('error', str + '失败!')
|
||||||
|
emit('update:testStatus', 'error')
|
||||||
|
stopTimeCount()
|
||||||
|
if (newValue.requestId == 'YJC_xujy') setLogList('info', '初始化失败!')
|
||||||
|
break
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 预监测 正式检测
|
||||||
|
else if (newValue.requestId == 'formal_real') {
|
||||||
|
if (testLogList[0].log == '正在检测,请稍等...' || testLogList[0].log == '暂无数据,等待检测开始') {
|
||||||
|
testLogList.shift()
|
||||||
|
}
|
||||||
|
switch (newValue.code) {
|
||||||
|
case 25001:
|
||||||
|
case 25006:
|
||||||
|
case 25005:
|
||||||
|
case 25007:// 添加闪变处理
|
||||||
|
{
|
||||||
|
let result: CheckData.ScriptChnItem[] = []
|
||||||
|
|
||||||
|
let message = JSON.parse(newValue.data)
|
||||||
|
|
||||||
|
|
||||||
|
// 当收到 25005/25006 消息时,录波项目开始loading
|
||||||
|
|
||||||
|
if (newValue.code == 25005 || newValue.code == 25006) {
|
||||||
|
// 设置录波项目为LOADING状态
|
||||||
|
const waveResultItem = checkResult.find(item => item.code === 'wave_data')
|
||||||
|
|
||||||
|
if (waveResultItem) {
|
||||||
|
waveResultItem.devices.forEach(device => {
|
||||||
|
device.chnResult.fill(CheckData.ChnCheckResultEnum.LOADING)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newValue.code == 25007) {
|
||||||
|
// 设置闪变项目为LOADING状态
|
||||||
|
const flickerResultItem = checkResult.find(item => item.code === 'flicker_data')
|
||||||
|
|
||||||
|
if (flickerResultItem) {
|
||||||
|
flickerResultItem.devices.forEach(device => {
|
||||||
|
device.chnResult.fill(CheckData.ChnCheckResultEnum.LOADING)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptData.forEach(item => {
|
||||||
|
// 处理当前节点的数据
|
||||||
|
const temp: CheckData.ScriptChnItem = {
|
||||||
|
scriptType: item.id,
|
||||||
|
scriptName: item.scriptName,
|
||||||
|
devices: []
|
||||||
|
}
|
||||||
|
|
||||||
|
// 特殊处理录波项目 - 如果是25005消息且当前项目是录波项目,则使用已设置的状态
|
||||||
|
if ((newValue.code == 25005 || newValue.code == 25006) && item.code === 'wave_data') {
|
||||||
|
|
||||||
|
const existingWaveItem = checkResult.find(checkItem => checkItem.scriptType === 'wave_data')
|
||||||
|
if (existingWaveItem) {
|
||||||
|
temp.devices = [...existingWaveItem.devices] // 保留已设置的devices
|
||||||
|
}
|
||||||
|
} // 特殊处理闪变项目 - 如果是25007消息且当前项目是闪变项目,则使用已设置的状态
|
||||||
|
else if (newValue.code == 25007 && item.code === 'PST') {
|
||||||
|
const existingFlickerItem = checkResult.find(checkItem => checkItem.scriptType === 'PST')
|
||||||
|
if (existingFlickerItem) {
|
||||||
|
temp.devices = [...existingFlickerItem.devices] // 保留已设置的devices
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// 找到message中所有scriptName与当前item.code匹配的项
|
||||||
|
const matchedDevices = message
|
||||||
|
.filter((msg: any) => msg.scriptName === item.code)
|
||||||
|
.map((msg: any) => ({
|
||||||
|
deviceId: msg.deviceId,
|
||||||
|
deviceName: msg.deviceName,
|
||||||
|
chnResult: msg.chnResult
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 添加匹配到的设备
|
||||||
|
temp.devices.push(...matchedDevices)
|
||||||
|
|
||||||
|
// 对于未匹配到的设备,也要添加占位符(特别是录波项目)
|
||||||
|
if (item.code === 'wave_data' || item.code === 'PST') {
|
||||||
|
deviceList.forEach(device => {
|
||||||
|
const isDeviceExist = matchedDevices.some((matchedDevice: any) => matchedDevice.deviceId === device.deviceId)
|
||||||
|
if (!isDeviceExist) {
|
||||||
|
// 对于录波项目或未匹配到的设备,添加默认状态
|
||||||
|
temp.devices.push({
|
||||||
|
deviceId: device.deviceId,
|
||||||
|
deviceName: device.deviceName,
|
||||||
|
chnResult: new Array(checkStore.chnNumList.length).fill(
|
||||||
|
item.code === 'wave_data' ?
|
||||||
|
CheckData.ChnCheckResultEnum.UNKNOWN :
|
||||||
|
CheckData.ChnCheckResultEnum.UNKNOWN
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.push(temp)
|
||||||
|
})
|
||||||
|
|
||||||
|
Object.assign(checkResult, result)
|
||||||
|
if (newValue.code == 25001) {
|
||||||
|
setLogList('info', '检测完成!')
|
||||||
|
stopTimeCount()
|
||||||
|
updatePercentage()
|
||||||
|
}
|
||||||
|
if(newValue.code == 25005){
|
||||||
|
setLogList("error", '实时数据校验失败!开始录波校验...')
|
||||||
|
}
|
||||||
|
if(newValue.code == 25006){
|
||||||
|
setLogList("error", '统计数据校验失败!开始录波校验...')
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case 25003:
|
||||||
|
setLogList('error', '检测失败!')
|
||||||
|
stopTimeCount()
|
||||||
|
updatePercentage()
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
if (newValue.code != 10201) {
|
||||||
|
setLogList(newValue.code == 10200 ? 'info' : 'error', newValue.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{deep: true}
|
||||||
|
)
|
||||||
|
const setLogList = (state: 'error' | 'info' | 'warning', text: string) => {
|
||||||
|
testLogList.push({
|
||||||
|
type: state,
|
||||||
|
log: `${new Date().toLocaleString()}:` + text
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新进度条
|
||||||
|
const updatePercentage = async () => {
|
||||||
|
if (testLogList.length - 1 < checkStore.chnNumList.length) {
|
||||||
|
percentage.value = Math.trunc(((testLogList.length - 1) / checkStore.chnNumList.length) * 100)
|
||||||
|
} else {
|
||||||
|
percentage.value = 100
|
||||||
|
emit('update:testStatus', 'success')
|
||||||
|
|
||||||
|
let {data: autoGenerate} = await getAutoGenerate()
|
||||||
|
if (autoGenerate == 1) {
|
||||||
|
//调用自动生成报告接口
|
||||||
|
let devIdList = checkStore.devices.map(item => {
|
||||||
|
return item.deviceId
|
||||||
|
})
|
||||||
|
|
||||||
|
// await generateDevReport({
|
||||||
|
// planId: checkStore.plan.id,
|
||||||
|
// devIdList: devIdList,
|
||||||
|
// scriptId: checkStore.plan.scriptId,
|
||||||
|
// planCode: checkStore.plan.code + ''
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
stopTimeCount()
|
||||||
|
ElMessageBox.alert(
|
||||||
|
'检测全部结束,你可以停留在此页面查看检测结果,或返回首页进行复检、报告生成和归档等操作',
|
||||||
|
'检测完成',
|
||||||
|
{
|
||||||
|
confirmButtonText: '确定'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// 关闭WebSocket连接
|
||||||
|
emit('closeWebSocket')
|
||||||
|
//clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ========== 时间计数器管理函数 ==========
|
||||||
|
// 开始计时
|
||||||
|
const startTimeCount = () => {
|
||||||
|
if (!timer) {
|
||||||
|
timer = setInterval(() => {
|
||||||
|
timeCount.value = timeCount.value + 1
|
||||||
|
timeView.value = secondToTime(timeCount.value)
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
nextTick(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
initCheckResult(CheckData.ChnCheckResultEnum.LOADING)
|
||||||
|
}, 500)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止计时
|
||||||
|
const stopTimeCount = () => {
|
||||||
|
if (timer) {
|
||||||
|
clearInterval(timer)
|
||||||
|
timer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将秒数转换为 HH:MM:SS 格式
|
||||||
|
const secondToTime = (second: number) => {
|
||||||
|
let h: string | number = Math.floor(second / 3600) // 小时
|
||||||
|
let m: string | number = Math.floor((second - h * 3600) / 60) // 分钟
|
||||||
|
let s: string | number = Math.floor(second % 60) // 秒
|
||||||
|
|
||||||
|
// 补齐前导零
|
||||||
|
h = h < 10 ? '0' + h : h
|
||||||
|
m = m < 10 ? '0' + m : m
|
||||||
|
s = s < 10 ? '0' + s : s
|
||||||
|
return h + ':' + m + ':' + s
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount(async () => {
|
||||||
|
})
|
||||||
|
|
||||||
|
const showTestLog = () => {
|
||||||
|
drawer.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化检测脚本数据
|
||||||
|
const initScriptData = async () => {
|
||||||
|
scriptData = []
|
||||||
|
const pattern = dictStore.getDictData('Pattern').find(item => item.name === modeStore.currentMode)?.id ?? ''
|
||||||
|
let response: any = await getBigTestItem({
|
||||||
|
reCheckType: checkStore.reCheckType,
|
||||||
|
planId: checkStore.plan.id,
|
||||||
|
devIds: checkStore.devices.map(item => item.deviceId),
|
||||||
|
patternId: pattern
|
||||||
|
})
|
||||||
|
// 格式化脚本数据,初始化时排除录波项目
|
||||||
|
let temp = response.data
|
||||||
|
.map((item: any) => {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
scriptName: item.scriptName
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 保存脚本数据并设置总数
|
||||||
|
scriptData.push(...temp)
|
||||||
|
checkTotal = scriptData.length
|
||||||
|
console.log('shul',checkTotal)
|
||||||
|
}
|
||||||
|
// 初始化设备列表
|
||||||
|
const initDeviceList = () => {
|
||||||
|
Object.assign(deviceList, checkStore.devices)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化检测结果 (详细到通道)
|
||||||
|
const initCheckResult = (defaultValue: CheckData.ChnCheckResultEnum) => {
|
||||||
|
let result: CheckData.ScriptChnItem[] = []
|
||||||
|
|
||||||
|
scriptData.forEach(item => {
|
||||||
|
let temp: CheckData.ScriptChnItem = {
|
||||||
|
scriptType: item.id,
|
||||||
|
code: item.code,
|
||||||
|
scriptName: item.scriptName,
|
||||||
|
devices: []
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < deviceList?.length; i++) {
|
||||||
|
let tempChnResult: CheckData.ChnCheckResultEnum[] = []
|
||||||
|
for (let j = 0; j < checkStore.chnNumList.length; j++) {
|
||||||
|
// 录波项目初始化为UNKNOWN状态,其他项目使用传入的默认值
|
||||||
|
if ((item.code === 'wave_data' || item.code === 'PST')&& checkTotal > 1) {
|
||||||
|
tempChnResult.push(CheckData.ChnCheckResultEnum.UNKNOWN)
|
||||||
|
} else {
|
||||||
|
tempChnResult.push(defaultValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
temp.devices.push({
|
||||||
|
deviceId: deviceList[i].deviceId,
|
||||||
|
deviceName: deviceList[i].deviceName,
|
||||||
|
chnResult: tempChnResult
|
||||||
|
})
|
||||||
|
}
|
||||||
|
result.push(temp)
|
||||||
|
})
|
||||||
|
|
||||||
|
Object.assign(checkResult, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
const scrollToBottom = () => {
|
||||||
|
if (scrollContainerRef.value) {
|
||||||
|
scrollContainerRef.value.scrollTop = scrollContainerRef.value.scrollHeight + 70
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
testLogList,
|
||||||
|
() => {
|
||||||
|
scrollToBottom()
|
||||||
|
},
|
||||||
|
{deep: true}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 点击查看(设备)通道检测详情。参数1:设备信息,参数2:通道号,-1:代表查看全部通道
|
||||||
|
const handleClick = (item: any, chnNum: number, scriptType: string) => {
|
||||||
|
let checkResultItem = checkResult.find(obj => obj.scriptType === scriptType)
|
||||||
|
|
||||||
|
let flag = -1
|
||||||
|
if (checkResultItem) {
|
||||||
|
let device = checkResultItem.devices.find(obj => obj.deviceId === item.deviceId)
|
||||||
|
if (device) {
|
||||||
|
let chnResult = device.chnResult
|
||||||
|
if (chnNum === -1) {
|
||||||
|
if (chnResult.findIndex(obj => obj === CheckData.ChnCheckResultEnum.TIMEOUT) !== -1) {
|
||||||
|
flag = 0
|
||||||
|
}
|
||||||
|
if (chnResult.findIndex(obj => obj === CheckData.ChnCheckResultEnum.ERRORDATA) !== -1) {
|
||||||
|
flag = 1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (chnResult[chnNum - 1] === CheckData.ChnCheckResultEnum.TIMEOUT) {
|
||||||
|
flag = 0
|
||||||
|
}
|
||||||
|
if (chnResult[chnNum - 1] === CheckData.ChnCheckResultEnum.ERRORDATA) {
|
||||||
|
flag = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flag === 0) {
|
||||||
|
ElMessageBox.alert('连接超时,请检查设备通讯是否正常', '连接超时', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (flag === -1 || flag === 1) {
|
||||||
|
checkStore.setShowDetailType(2)
|
||||||
|
|
||||||
|
dataCheckSingleChannelSingleTestPopupRef.value?.open(item, chnNum + '', item.devices[0].deviceId, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePause = () => {
|
||||||
|
//emit('sendPause')
|
||||||
|
testLogList.push({
|
||||||
|
type: 'error',
|
||||||
|
log: `${new Date().toLocaleString()}:当前测试小项正在执行中,将在该小项执行结束后暂停...`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const initializeParameters = async () => {
|
||||||
|
await initScriptData()
|
||||||
|
initDeviceList()
|
||||||
|
initCheckResult(CheckData.ChnCheckResultEnum.UNKNOWN)
|
||||||
|
percentage.value = 0
|
||||||
|
timeCount.value = 0
|
||||||
|
timeView.value = '00:00:00'
|
||||||
|
testLogList.splice(0, testLogList.length, {
|
||||||
|
type: 'info',
|
||||||
|
log: checkStore.selectTestItems.preTest ? '正在检测,请稍等...' : '暂无数据,等待检测开始'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
onMounted(() => {
|
||||||
|
|
||||||
|
if (!checkStore.selectTestItems.preTest) {
|
||||||
|
// 判断是否预检测
|
||||||
|
initializeParameters()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
initializeParameters,
|
||||||
|
handlePause,
|
||||||
|
showTestLog,
|
||||||
|
startTimeCount
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
:deep(.el-table .header-row) {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table .warning-row) {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table .success-row {
|
||||||
|
--el-table-tr-bg-color: var(--el-color-success-light-9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow-y: hidden;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-title {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
.timeView {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: #91cc75;
|
||||||
|
width: 28%;
|
||||||
|
margin-right: 0px;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 26px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-content {
|
||||||
|
max-height: 450px;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-collapse-item__header) {
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-log {
|
||||||
|
height: 50px;
|
||||||
|
overflow-y: hidden;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 5px 0;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-container {
|
||||||
|
:deep(header.el-drawer__header) {
|
||||||
|
color: #fff !important;
|
||||||
|
background-color: var(--el-color-primary) !important;
|
||||||
|
|
||||||
|
.el-drawer__close-btn svg:hover {
|
||||||
|
color: #ccc !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-drawer__title {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-box {
|
||||||
|
animation: loading 1.5s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes loading {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
:deep(.el-button--small) {
|
||||||
|
height: 20px !important;
|
||||||
|
width: 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table--default td) {
|
||||||
|
padding: 5px 0 !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
686
frontend/src/views/home/components/compareTestPopup.vue
Normal file
686
frontend/src/views/home/components/compareTestPopup.vue
Normal file
@@ -0,0 +1,686 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:title="dialogTitle"
|
||||||
|
width="1550px"
|
||||||
|
:model-value="dialogVisible"
|
||||||
|
:before-close="beforeClose"
|
||||||
|
@close="handleClose"
|
||||||
|
height="1000px"
|
||||||
|
draggable
|
||||||
|
>
|
||||||
|
<div class="steps-container">
|
||||||
|
<el-steps
|
||||||
|
v-if="showSteps"
|
||||||
|
class="test-head-steps"
|
||||||
|
simple
|
||||||
|
:active="stepsActiveIndex"
|
||||||
|
process-status="finish"
|
||||||
|
finish-status="success"
|
||||||
|
>
|
||||||
|
<el-step
|
||||||
|
title="通道配对"
|
||||||
|
:icon="stepsActive > 0 || stepsActiveIndex > stepsTotalNum - 1 ? SuccessFilled : Switch"
|
||||||
|
@click="handleStepClick(0)"
|
||||||
|
/>
|
||||||
|
<el-step
|
||||||
|
v-if="preTestSelected"
|
||||||
|
title="预检测"
|
||||||
|
:icon="stepsActive > 1 || stepsActiveIndex > stepsTotalNum - 1 ? SuccessFilled : Edit"
|
||||||
|
@click="handleStepClick(1)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-step
|
||||||
|
v-if="testSelected"
|
||||||
|
title="正式检测"
|
||||||
|
:icon="stepsActive > 2 || stepsActiveIndex > stepsTotalNum - 1 ? SuccessFilled : Coin"
|
||||||
|
@click="handleStepClick(2)"
|
||||||
|
/>
|
||||||
|
<el-step title="检测完成" :icon="stepsActiveIndex > stepsTotalNum - 1 ? SuccessFilled : Key" />
|
||||||
|
</el-steps>
|
||||||
|
</div>
|
||||||
|
<keep-alive>
|
||||||
|
<ChannelPairing
|
||||||
|
v-if="stepsActiveView == 0"
|
||||||
|
ref="channelPairingRef"
|
||||||
|
:devIdList="prop.devIdList"
|
||||||
|
:pqStandardDevList="prop.pqStandardDevList"
|
||||||
|
:planIdKey="prop.planIdKey"
|
||||||
|
:deviceMonitor="deviceMonitor2"
|
||||||
|
/>
|
||||||
|
</keep-alive>
|
||||||
|
<keep-alive>
|
||||||
|
<ComparePreTest
|
||||||
|
v-if="preTestSelected && stepsActiveView == 1"
|
||||||
|
ref="preTestRef"
|
||||||
|
v-model:testStatus="preTestStatus"
|
||||||
|
:webMsgSend="webMsgSend"
|
||||||
|
:mapping="channelMapping"
|
||||||
|
:onlyWave="onlyWave"
|
||||||
|
/>
|
||||||
|
</keep-alive>
|
||||||
|
<keep-alive>
|
||||||
|
<CompareTest
|
||||||
|
v-if="testSelected && stepsActiveView == 2"
|
||||||
|
ref="testRef"
|
||||||
|
:webMsgSend="webMsgSend"
|
||||||
|
v-model:testStatus="TestStatus"
|
||||||
|
:stepsActive="stepsActive"
|
||||||
|
/>
|
||||||
|
</keep-alive>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<div>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:icon="VideoPlay"
|
||||||
|
v-if="ActiveStatue === 'waiting' && !(testSelected && stepsActiveIndex == 2)"
|
||||||
|
@click="handleSubmitFast"
|
||||||
|
>
|
||||||
|
开始检测
|
||||||
|
</el-button>
|
||||||
|
<!-- <el-button type="primary" v-if="TestStatus === 'test_init'" disabled>
|
||||||
|
<el-icon class="loading-box" style="color: #fff; margin-right: 8px">
|
||||||
|
<component :is="Refresh" />
|
||||||
|
</el-icon>
|
||||||
|
初始化中
|
||||||
|
</el-button> -->
|
||||||
|
<el-button type="primary" v-if="TestStatus == 'process'" :icon="VideoPause" @click="handlePause()">
|
||||||
|
停止检测
|
||||||
|
</el-button>
|
||||||
|
<el-button type="warning" v-if="TestStatus === 'paused_ing'" disabled>
|
||||||
|
<el-icon class="loading-box" style="color: #fff; margin-right: 8px">
|
||||||
|
<component :is="Refresh" />
|
||||||
|
</el-icon>
|
||||||
|
暂停中
|
||||||
|
</el-button>
|
||||||
|
<el-button type="warning" v-if="TestStatus == 'paused'" :icon="VideoPlay" @click="sendResume">
|
||||||
|
继续检测
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="warning"
|
||||||
|
:icon="VideoPlay"
|
||||||
|
v-if="
|
||||||
|
nextStepText !== '下一步' &&
|
||||||
|
(ActiveStatue === 'success' ||
|
||||||
|
ActiveStatue === 'error' ||
|
||||||
|
ActiveStatue === 'test_init_fail' ||
|
||||||
|
ActiveStatue === 'connect_timeout' ||
|
||||||
|
ActiveStatue === 'pause_timeout' ||
|
||||||
|
ActiveStatue === 'waiting')
|
||||||
|
"
|
||||||
|
@click="handleSubmitAgain"
|
||||||
|
>
|
||||||
|
重新检测
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
:type="ActiveStatue === 'success' ? 'primary' : 'danger'"
|
||||||
|
:icon="Right"
|
||||||
|
v-if="
|
||||||
|
nextStepText !== '下一步' &&
|
||||||
|
(ActiveStatue === 'success' ||
|
||||||
|
ActiveStatue === 'error' ||
|
||||||
|
ActiveStatue === 'test_init_fail' ||
|
||||||
|
ActiveStatue === 'connect_timeout' ||
|
||||||
|
ActiveStatue === 'pause_timeout')
|
||||||
|
"
|
||||||
|
@click="nextStep"
|
||||||
|
>
|
||||||
|
{{ nextStepText }}
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" @click="handleQuit" v-else>退出检测</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="tsx" setup name="testPopup">
|
||||||
|
import { nextTick, reactive, ref, watch } from 'vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { Coin, Edit, Key, Refresh, Right, SuccessFilled, Switch, VideoPause, VideoPlay } from '@element-plus/icons-vue'
|
||||||
|
import ComparePreTest from './comparePreTest.vue'
|
||||||
|
import ChannelPairing from './channelPairing.vue'
|
||||||
|
import CompareTest from './compareTest.vue'
|
||||||
|
import socketClient from '@/utils/webSocketClient'
|
||||||
|
import { useCheckStore } from '@/stores/modules/check'
|
||||||
|
import { contrastTest, pauseTest, resumeTest, startPreTest } from '@/api/socket/socket'
|
||||||
|
import { useUserStore } from '@/stores/modules/user'
|
||||||
|
import { JwtUtil } from '@/utils/jwtUtil'
|
||||||
|
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const checkStore = useCheckStore()
|
||||||
|
const nextStepText = ref('下一步')
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const channelPairingRef = ref()
|
||||||
|
const showSteps = ref(false)
|
||||||
|
|
||||||
|
const stepsTotalNum = ref(-1) //步骤总数
|
||||||
|
const stepsActiveIndex = ref(0) //当前正在执行的步骤索引
|
||||||
|
const stepsActiveView = ref(1) //当前正在执行的步骤在(预处理、守时校验、系数校准、正式检测)中的排序,仅用于页面显示
|
||||||
|
const stepsActive = ref(-1) //当前正在执行的步骤在(预处理、守时校验、系数校准、正式检测)中的排序,实际记录步骤的状态,用于切换步骤
|
||||||
|
const ActiveStatue = ref('waiting') //当前步骤状态
|
||||||
|
const preTestStatus = ref('waiting') //预检测执行状态
|
||||||
|
const TestStatus = ref('waiting') //正式检测执行状态
|
||||||
|
const webMsgSend = ref() //webSocket推送的数据
|
||||||
|
|
||||||
|
const dialogTitle = ref('')
|
||||||
|
const showComponent = ref(true)
|
||||||
|
const preTestRef = ref<InstanceType<typeof ComparePreTest> | null>(null)
|
||||||
|
const testRef: any = ref(null)
|
||||||
|
|
||||||
|
const prop = defineProps({
|
||||||
|
devIdList: {
|
||||||
|
type: Array as any,
|
||||||
|
default: []
|
||||||
|
},
|
||||||
|
pqStandardDevList: {
|
||||||
|
type: Array as any,
|
||||||
|
default: []
|
||||||
|
},
|
||||||
|
planIdKey: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const dataSocket = reactive({
|
||||||
|
socketServe: socketClient.Instance
|
||||||
|
})
|
||||||
|
|
||||||
|
// 勾选的检测内容
|
||||||
|
const preTestSelected = ref(true)
|
||||||
|
const testSelected = ref(false)
|
||||||
|
|
||||||
|
const initOperate = () => {
|
||||||
|
ActiveStatue.value = 'waiting'
|
||||||
|
preTestStatus.value = 'waiting'
|
||||||
|
|
||||||
|
TestStatus.value = 'waiting'
|
||||||
|
|
||||||
|
stepsActiveIndex.value = 0
|
||||||
|
showComponent.value = true
|
||||||
|
// 初始化勾选的检测内容
|
||||||
|
preTestSelected.value = checkStore.selectTestItems.preTest
|
||||||
|
testSelected.value = checkStore.selectTestItems.test
|
||||||
|
|
||||||
|
let count = 0
|
||||||
|
for (let key in checkStore.selectTestItems) {
|
||||||
|
if (checkStore.selectTestItems[key]) {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stepsTotalNum.value = count + 1
|
||||||
|
|
||||||
|
if (preTestSelected.value) {
|
||||||
|
stepsActiveView.value = 0
|
||||||
|
stepsActive.value = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (testSelected.value) {
|
||||||
|
stepsActiveView.value = 0
|
||||||
|
stepsActive.value = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const channelMapping = ref<Record<string, Record<string, string>>>({})
|
||||||
|
const planId = ref('')
|
||||||
|
const loginName = ref('')
|
||||||
|
const devIds = ref<[]>()
|
||||||
|
const standardDevIds = ref<[]>()
|
||||||
|
const pairs = ref<any>()
|
||||||
|
const testAgain = ref(false) //重新检测按钮是否显示
|
||||||
|
const checkNumber = ref(0) //检测次数
|
||||||
|
const deviceMonitor2 = ref<Map<string, any[]>>()
|
||||||
|
const onlyWave = ref(false) //计划数据源是否只有录波
|
||||||
|
const open = async (
|
||||||
|
title: string,
|
||||||
|
mapping: any,
|
||||||
|
plan: string,
|
||||||
|
login: string,
|
||||||
|
devIdsArray: [],
|
||||||
|
standardDevIdsArray: [],
|
||||||
|
pair: any,
|
||||||
|
deviceMonitor: Map<string, any[]>,
|
||||||
|
planIsOnlyWave: boolean
|
||||||
|
) => {
|
||||||
|
if (checkStore.selectTestItems.preTest && !checkStore.selectTestItems.test) {
|
||||||
|
testAgain.value = true
|
||||||
|
}
|
||||||
|
deviceMonitor2.value = deviceMonitor
|
||||||
|
onlyWave.value = planIsOnlyWave
|
||||||
|
checkStore.setNodesConnectable(true)
|
||||||
|
dialogTitle.value = title
|
||||||
|
channelMapping.value = mapping
|
||||||
|
planId.value = plan
|
||||||
|
loginName.value = login
|
||||||
|
devIds.value = devIdsArray
|
||||||
|
standardDevIds.value = standardDevIdsArray
|
||||||
|
pairs.value = pair
|
||||||
|
showSteps.value = true
|
||||||
|
initOperate()
|
||||||
|
dialogTitle.value = title
|
||||||
|
dialogVisible.value = true
|
||||||
|
|
||||||
|
// 等待组件渲染完成
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
if (preTestRef.value) {
|
||||||
|
preTestRef.value.initializeParameters()
|
||||||
|
}
|
||||||
|
|
||||||
|
//开始创建webSocket客户端
|
||||||
|
socketClient.Instance.connect()
|
||||||
|
dataSocket.socketServe = socketClient.Instance
|
||||||
|
dataSocket.socketServe.registerCallBack('aaa', (res: { code: number }) => {
|
||||||
|
// console.log('Received message:', res)
|
||||||
|
// 处理来自服务器的消息
|
||||||
|
if (res.code === 20000) {
|
||||||
|
//ElMessage.error(message.message)
|
||||||
|
// loading.close()
|
||||||
|
} else {
|
||||||
|
webMsgSend.value = res
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//预检测-重新检测
|
||||||
|
const handleSubmitAgain = async () => {
|
||||||
|
if (checkStore.selectTestItems.preTest) {
|
||||||
|
stepsActiveIndex.value = 1
|
||||||
|
stepsActiveView.value = 1
|
||||||
|
stepsActive.value = 1
|
||||||
|
} else {
|
||||||
|
stepsActiveIndex.value = 1
|
||||||
|
stepsActiveView.value = 2
|
||||||
|
stepsActive.value = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
let count = 0
|
||||||
|
for (let key in checkStore.selectTestItems) {
|
||||||
|
if (checkStore.selectTestItems[key]) {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stepsTotalNum.value = count + 1
|
||||||
|
|
||||||
|
// 通知子组件清空并重新初始化
|
||||||
|
if (preTestRef.value) {
|
||||||
|
preTestRef.value.initializeParameters()
|
||||||
|
}
|
||||||
|
testRef.value && testRef.value.initializeParameters()
|
||||||
|
|
||||||
|
// 重置状态
|
||||||
|
nextStepText.value = '下一步'
|
||||||
|
ActiveStatue.value = 'waiting'
|
||||||
|
preTestStatus.value = 'waiting'
|
||||||
|
TestStatus.value = 'waiting'
|
||||||
|
|
||||||
|
await contrastTest({
|
||||||
|
planId: planId.value,
|
||||||
|
loginName: loginName.value,
|
||||||
|
devIds: devIds.value,
|
||||||
|
standardDevIds: standardDevIds.value,
|
||||||
|
pairs: pairs.value,
|
||||||
|
testItemList: [checkStore.selectTestItems.preTest, false, checkStore.selectTestItems.test],
|
||||||
|
|
||||||
|
userId: userStore.userInfo.id
|
||||||
|
})
|
||||||
|
|
||||||
|
preTestStatus.value = 'start'
|
||||||
|
}
|
||||||
|
|
||||||
|
//开始检测
|
||||||
|
const handleSubmitFast = async () => {
|
||||||
|
if (channelPairingRef.value) {
|
||||||
|
const res = await channelPairingRef.value.handleNext()
|
||||||
|
|
||||||
|
if (!res) return
|
||||||
|
dialogTitle.value = res.title
|
||||||
|
channelMapping.value = res.mapping
|
||||||
|
planId.value = res.plan
|
||||||
|
loginName.value = res.login
|
||||||
|
devIds.value = res.devIdsArray
|
||||||
|
standardDevIds.value = res.standardDevIdsArray
|
||||||
|
pairs.value = res.pair
|
||||||
|
checkStore.setNodesConnectable(false)
|
||||||
|
// nodesConnectable.value = false
|
||||||
|
}
|
||||||
|
if (!dataSocket.socketServe.connected) {
|
||||||
|
ElMessage.error('webSocket连接中断!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (checkStore.selectTestItems.preTest) {
|
||||||
|
stepsActiveIndex.value = 1
|
||||||
|
stepsActiveView.value = 1
|
||||||
|
stepsActive.value = 1
|
||||||
|
} else {
|
||||||
|
stepsActiveIndex.value = 1
|
||||||
|
stepsActiveView.value = 2
|
||||||
|
stepsActive.value = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (stepsActive.value) {
|
||||||
|
case 1:
|
||||||
|
if (preTestStatus.value == 'waiting') {
|
||||||
|
if (checkStore.selectTestItems.preTest) {
|
||||||
|
await contrastTest({
|
||||||
|
planId: planId.value,
|
||||||
|
loginName: loginName.value,
|
||||||
|
devIds: devIds.value,
|
||||||
|
standardDevIds: standardDevIds.value,
|
||||||
|
pairs: pairs.value,
|
||||||
|
testItemList: [checkStore.selectTestItems.preTest, false, checkStore.selectTestItems.test],
|
||||||
|
|
||||||
|
userId: userStore.userInfo.id
|
||||||
|
})
|
||||||
|
preTestStatus.value = 'start'
|
||||||
|
if (checkStore.selectTestItems.test) {
|
||||||
|
console.log(111111)
|
||||||
|
|
||||||
|
testRef.value.initializeParameters()
|
||||||
|
testRef.value.showTestLog()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
if (TestStatus.value == 'waiting') {
|
||||||
|
// 如果没有预检测,直接进行正式检测需要先初始化
|
||||||
|
if (!checkStore.selectTestItems.preTest && checkStore.selectTestItems.test) {
|
||||||
|
await contrastTest({
|
||||||
|
planId: planId.value,
|
||||||
|
loginName: loginName.value,
|
||||||
|
devIds: devIds.value,
|
||||||
|
standardDevIds: standardDevIds.value,
|
||||||
|
pairs: pairs.value,
|
||||||
|
testItemList: [checkStore.selectTestItems.preTest, false, checkStore.selectTestItems.test],
|
||||||
|
|
||||||
|
userId: userStore.userInfo.id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
TestStatus.value = 'start'
|
||||||
|
} else if (TestStatus.value == 'paused') {
|
||||||
|
// 发送继续指令
|
||||||
|
//sendResume()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'quitClicked'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
watch(preTestStatus, function (newValue, oldValue) {
|
||||||
|
console.log('预检测状态', newValue, oldValue)
|
||||||
|
ActiveStatue.value = newValue
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(TestStatus, function (newValue, oldValue) {
|
||||||
|
console.log('正式检测状态', newValue, oldValue)
|
||||||
|
|
||||||
|
ActiveStatue.value = newValue
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(stepsActiveIndex, function (newValue, oldValue) {
|
||||||
|
if (checkStore.selectTestItems.test && checkStore.selectTestItems.preTest && newValue == 2) {
|
||||||
|
setTimeout(() => {
|
||||||
|
testRef.value.initializeParameters()
|
||||||
|
testRef.value.showTestLog()
|
||||||
|
testRef.value.startTimeCount()
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
console.log('步骤索引', newValue, oldValue)
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(ActiveStatue, function (newValue, oldValue) {
|
||||||
|
console.log('当前步骤状态-----', newValue)
|
||||||
|
console.log('stepsActiveIndex-----', stepsActiveIndex.value)
|
||||||
|
console.log('stepsTotalNum----', stepsTotalNum.value)
|
||||||
|
if (newValue === 'error') {
|
||||||
|
stepsActiveIndex.value = stepsTotalNum.value + 1
|
||||||
|
nextStepText.value = '检测失败'
|
||||||
|
}
|
||||||
|
if (newValue === 'success' && stepsActiveIndex.value === stepsTotalNum.value - 1) {
|
||||||
|
stepsActiveIndex.value += 2
|
||||||
|
nextStepText.value = '检测完成'
|
||||||
|
}
|
||||||
|
if (newValue === 'test_init_fail') {
|
||||||
|
stepsActiveIndex.value += 2
|
||||||
|
nextStepText.value = '初始化失败'
|
||||||
|
}
|
||||||
|
if (newValue === 'connect_timeout') {
|
||||||
|
stepsActiveIndex.value += 2
|
||||||
|
nextStepText.value = '连接超时'
|
||||||
|
}
|
||||||
|
if (newValue === 'pause_timeout') {
|
||||||
|
stepsActiveIndex.value += 2
|
||||||
|
// nextStepText.value = '结束测试'
|
||||||
|
nextStepText.value = '暂停超时'
|
||||||
|
}
|
||||||
|
if (newValue === 'success' && stepsActiveIndex.value < stepsTotalNum.value - 1) {
|
||||||
|
nextStep() // 实现自动点击,进入下一个测试内容
|
||||||
|
//handleSubmitFast()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleQuit = () => {
|
||||||
|
console.log('handleQuit', ActiveStatue.value)
|
||||||
|
if (
|
||||||
|
ActiveStatue.value !== 'success' &&
|
||||||
|
ActiveStatue.value !== 'waiting' &&
|
||||||
|
ActiveStatue.value !== 'paused' &&
|
||||||
|
ActiveStatue.value !== 'test_init_fail' &&
|
||||||
|
ActiveStatue.value !== 'connect_timeout' &&
|
||||||
|
ActiveStatue.value !== 'pause_timeout'
|
||||||
|
) {
|
||||||
|
beforeClose(() => {})
|
||||||
|
} else {
|
||||||
|
handleClose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePause = () => {
|
||||||
|
sendPause()
|
||||||
|
testRef.value?.handlePause()
|
||||||
|
}
|
||||||
|
const sendPause = () => {
|
||||||
|
console.log('发起暂停请求')
|
||||||
|
|
||||||
|
TestStatus.value = 'paused_ing'
|
||||||
|
pauseTest()
|
||||||
|
}
|
||||||
|
const sendResume = () => {
|
||||||
|
console.log('发起继续检测请求')
|
||||||
|
|
||||||
|
resumeTest({
|
||||||
|
userPageId: JwtUtil.getLoginName(),
|
||||||
|
devIds: checkStore.devices.map(item => item.deviceId),
|
||||||
|
planId: checkStore.plan.id,
|
||||||
|
reCheckType: '2', // 0:'系数校验','1'为预检测、‘2‘为正式检测、'8'为不合格项复检
|
||||||
|
userId: userStore.userInfo.id,
|
||||||
|
temperature: checkStore.temperature,
|
||||||
|
humidity: checkStore.humidity
|
||||||
|
})
|
||||||
|
Object.assign(webMsgSend.value, {
|
||||||
|
requestId: 'Resume_Success'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const sendReCheck = () => {
|
||||||
|
console.log('发送重新检测指令')
|
||||||
|
startPreTest({
|
||||||
|
userPageId: JwtUtil.getLoginName(),
|
||||||
|
devIds: checkStore.devices.map(item => item.deviceId),
|
||||||
|
planId: checkStore.plan.id,
|
||||||
|
reCheckType: '2', // 0:'系数校验','1'为预检测、‘2‘为正式检测、'8'为不合格项复检
|
||||||
|
userId: userStore.userInfo.id,
|
||||||
|
temperature: checkStore.temperature,
|
||||||
|
humidity: checkStore.humidity,
|
||||||
|
testItemList: [
|
||||||
|
checkStore.selectTestItems.preTest,
|
||||||
|
checkStore.selectTestItems.channelsTest,
|
||||||
|
checkStore.selectTestItems.test
|
||||||
|
]
|
||||||
|
}).then(res => {
|
||||||
|
console.log(res)
|
||||||
|
if (res.code === 'A001014') {
|
||||||
|
ElMessageBox.alert('装置配置异常', '初始化失败', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
type: 'error'
|
||||||
|
})
|
||||||
|
TestStatus.value = 'test_init_fail'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
TestStatus.value = 'start'
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeWebSocket = () => {
|
||||||
|
dataSocket.socketServe.closeWs()
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextStep = () => {
|
||||||
|
if (
|
||||||
|
stepsActiveIndex.value == stepsTotalNum.value + 1 ||
|
||||||
|
ActiveStatue.value === 'error' ||
|
||||||
|
ActiveStatue.value === 'test_init_fail' ||
|
||||||
|
ActiveStatue.value === 'connect_timeout' ||
|
||||||
|
ActiveStatue.value === 'pause_timeout'
|
||||||
|
) {
|
||||||
|
handleClose()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (ActiveStatue.value != 'error') {
|
||||||
|
ActiveStatue.value = 'waiting'
|
||||||
|
let tempStep = stepsActiveIndex.value
|
||||||
|
let idx = -1
|
||||||
|
stepsActiveIndex.value++
|
||||||
|
for (let selectTestItemsKey in checkStore.selectTestItems) {
|
||||||
|
if (tempStep == 0 && checkStore.selectTestItems[selectTestItemsKey]) {
|
||||||
|
console.log('selectTestItemsKey1')
|
||||||
|
stepsActiveView.value = idx
|
||||||
|
stepsActive.value = idx
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (checkStore.selectTestItems[selectTestItemsKey] && tempStep != 0) {
|
||||||
|
console.log('selectTestItemsKey2')
|
||||||
|
tempStep--
|
||||||
|
}
|
||||||
|
console.log('selectTestItemsKey3', idx)
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleStepClick = (step: number) => {
|
||||||
|
if (step > stepsActive.value) {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
stepsActiveView.value = step
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearData() {
|
||||||
|
stepsTotalNum.value = -1
|
||||||
|
stepsActiveIndex.value = 1
|
||||||
|
stepsActiveView.value = -1
|
||||||
|
preTestStatus.value = 'waiting'
|
||||||
|
TestStatus.value = 'waiting'
|
||||||
|
ActiveStatue.value = 'waiting'
|
||||||
|
nextStepText.value = '下一步'
|
||||||
|
}
|
||||||
|
|
||||||
|
const beforeClose = (done: () => void) => {
|
||||||
|
if (stepsActiveIndex.value < stepsTotalNum.value && ActiveStatue.value != 'error') {
|
||||||
|
ElMessageBox.confirm('检测未完成,是否退出当前检测流程?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
handleClose()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
handleClose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
showSteps.value = false
|
||||||
|
dataSocket.socketServe.closeWs()
|
||||||
|
dialogVisible.value = false
|
||||||
|
clearData()
|
||||||
|
showComponent.value = false
|
||||||
|
|
||||||
|
emit('quitClicked') // 触发事件
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对外映射
|
||||||
|
defineExpose({ open })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.test-head-steps {
|
||||||
|
:deep(.el-step) {
|
||||||
|
.el-step__head.is-success {
|
||||||
|
color: #91cc75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-step__title.is-success {
|
||||||
|
color: #91cc75;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.dialog-big .el-dialog__body) {
|
||||||
|
max-height: 840px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.steps-container :deep(.test-head-steps) {
|
||||||
|
height: 80px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.steps-container :deep(.el-step__title) {
|
||||||
|
font-size: 20px !important;
|
||||||
|
/* 设置标题字体大小 */
|
||||||
|
vertical-align: baseline !important;
|
||||||
|
display: inline-block;
|
||||||
|
/* 确保文字和图标在同一行 */
|
||||||
|
line-height: 1;
|
||||||
|
/* 调整行高以确保底部对齐 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.steps-container :deep(.el-step__icon-inner) {
|
||||||
|
font-size: 18px !important;
|
||||||
|
vertical-align: baseline !important;
|
||||||
|
display: inline-block;
|
||||||
|
/* 确保文字和图标在同一行 */
|
||||||
|
line-height: 1;
|
||||||
|
/* 调整行高以确保底部对齐 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.steps-container :deep(.el-step__icon) {
|
||||||
|
font-size: 18px !important;
|
||||||
|
vertical-align: baseline !important;
|
||||||
|
display: inline-block;
|
||||||
|
/* 确保文字和图标在同一行 */
|
||||||
|
line-height: 1;
|
||||||
|
/* 调整行高以确保底部对齐 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-box {
|
||||||
|
animation: loading 1.5s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes loading {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user