Compare commits
216 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 |
@@ -25,10 +25,10 @@ module.exports = (appInfo) => {
|
||||
*/
|
||||
config.windowsOption = {
|
||||
title: '自动检测平台',
|
||||
width: 1600,
|
||||
height: 950,
|
||||
minWidth: 1600,
|
||||
minHeight: 950,
|
||||
width: 1920 /1.5,
|
||||
height: 1080 /1.2,
|
||||
minWidth: 1920 /1.5,
|
||||
minHeight: 1080 /1.2,
|
||||
webPreferences: {
|
||||
//webSecurity: false,
|
||||
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": {
|
||||
"@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",
|
||||
"autofit.js": "^3.2.8",
|
||||
"axios": "^1.7.3",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "^1.11.9",
|
||||
@@ -22,6 +25,7 @@
|
||||
"echarts": "^5.4.3",
|
||||
"echarts-liquidfill": "^3.1.0",
|
||||
"element-plus": "^2.7.8",
|
||||
"event-source-polyfill": "^1.0.31",
|
||||
"html2canvas": "^1.4.1",
|
||||
"md5": "^2.3.0",
|
||||
"mitt": "^3.0.1",
|
||||
@@ -72,11 +76,11 @@
|
||||
"unplugin-auto-import": "^0.18.3",
|
||||
"unplugin-vue-components": "^0.27.4",
|
||||
"unplugin-vue-setup-extend-plus": "^1.0.0",
|
||||
"vite": "^5.3.1",
|
||||
"vite": "^5.4.19",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-eslint": "^1.8.1",
|
||||
"vite-plugin-html": "^3.2.0",
|
||||
"vite-plugin-node-polyfills": "^0.23.0",
|
||||
"vite-plugin-node-polyfills": "^0.24.0",
|
||||
"vite-plugin-pwa": "^0.16.5",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vue-tsc": "^2.0.21"
|
||||
|
||||
@@ -1,27 +1,25 @@
|
||||
<template>
|
||||
<!--element-plus语言国际化,全局修改为中文-->
|
||||
<el-config-provider
|
||||
:locale='locale'
|
||||
:size='assemblySize'
|
||||
:button='buttonConfig'
|
||||
>
|
||||
<el-config-provider :locale="locale" :size="assemblySize" :button="buttonConfig">
|
||||
<router-view />
|
||||
</el-config-provider>
|
||||
</template>
|
||||
|
||||
<script lang='ts' setup>
|
||||
defineOptions({
|
||||
name: 'App',
|
||||
})
|
||||
<script lang="ts" setup>
|
||||
import autofit from 'autofit.js'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { getBrowserLang } from '@/utils'
|
||||
import { useTheme } from '@/hooks/useTheme'
|
||||
import { ElConfigProvider } from 'element-plus'
|
||||
import { LanguageType } from './stores/interface'
|
||||
import { type LanguageType } from './stores/interface'
|
||||
import { useGlobalStore } from '@/stores/modules/global'
|
||||
import en from 'element-plus/es/locale/lang/en'
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
||||
|
||||
defineOptions({
|
||||
name: 'App'
|
||||
})
|
||||
|
||||
const globalStore = useGlobalStore()
|
||||
|
||||
// init theme
|
||||
@@ -34,6 +32,16 @@ onMounted(() => {
|
||||
const language = globalStore.language ?? getBrowserLang()
|
||||
i18n.locale.value = language
|
||||
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
|
||||
|
||||
@@ -1,125 +1,158 @@
|
||||
export namespace CheckData {
|
||||
export interface DataCheck {
|
||||
scriptName: string,
|
||||
errorSysId: string,
|
||||
dataRule: string,
|
||||
deviceName: string,
|
||||
chnNum: string,
|
||||
scriptName: string
|
||||
errorSysId: string
|
||||
dataRule: string
|
||||
deviceName: string
|
||||
chnNum: string
|
||||
deviceId: string
|
||||
num?: string | number
|
||||
}
|
||||
|
||||
export interface PhaseCheckResult {
|
||||
// 检测源定值-标准值
|
||||
resultData: number,
|
||||
resultData: number
|
||||
// 装置原始数据-被检值
|
||||
data: number,
|
||||
data: number
|
||||
|
||||
// 误差值
|
||||
errorData: number,
|
||||
errorData: number
|
||||
// 第几次谐波
|
||||
num?: number,
|
||||
num?: number
|
||||
//符合、不符合
|
||||
isData?: number,
|
||||
isData?: number
|
||||
//最大误差值
|
||||
radius?: string,
|
||||
unit?: 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 {
|
||||
dataA?: PhaseCheckResult | null,
|
||||
dataB?: PhaseCheckResult | null,
|
||||
dataC?: PhaseCheckResult | null,
|
||||
dataT?: PhaseCheckResult | null,
|
||||
dataA?: PhaseCheckResult | null
|
||||
dataB?: PhaseCheckResult | null
|
||||
dataC?: PhaseCheckResult | null
|
||||
dataT?: PhaseCheckResult | null
|
||||
|
||||
// 第几次谐波
|
||||
//num: number | null,
|
||||
//符合、不符合
|
||||
isData?: number,
|
||||
isData?: number
|
||||
//最大误差值
|
||||
radius?: string,
|
||||
radius?: string
|
||||
//单位
|
||||
unit?: string,
|
||||
unit?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于定义 查看(设备)通道检测结果表格展示数据 类型
|
||||
*/
|
||||
export interface CheckResult {
|
||||
stdA?: string,
|
||||
dataA?: string,
|
||||
errorA?: string,
|
||||
maxErrorA?: string,
|
||||
isDataA?: number,
|
||||
unitA?: string,
|
||||
stdB?: string,
|
||||
dataB?: string,
|
||||
errorB?: string,
|
||||
maxErrorB?: string,
|
||||
isDataB?: number,
|
||||
unitB?: string,
|
||||
stdC?: string,
|
||||
dataC?: string,
|
||||
errorC?: string,
|
||||
maxErrorC?: string,
|
||||
isDataC?: number,
|
||||
unitC?: string,
|
||||
stdT?: string,
|
||||
dataT?: string,
|
||||
errorT?: string,
|
||||
maxErrorT?: string,
|
||||
isDataT?: number,
|
||||
unitT?: string,
|
||||
stdA?: string
|
||||
dataA?: string
|
||||
errorA?: string
|
||||
maxErrorA?: string
|
||||
isDataA?: number
|
||||
unitA?: string
|
||||
stdB?: string
|
||||
dataB?: string
|
||||
errorB?: string
|
||||
maxErrorB?: string
|
||||
isDataB?: number
|
||||
unitB?: string
|
||||
stdC?: string
|
||||
dataC?: string
|
||||
errorC?: string
|
||||
maxErrorC?: string
|
||||
isDataC?: number
|
||||
unitC?: string
|
||||
stdT?: string
|
||||
dataT?: string
|
||||
errorT?: string
|
||||
maxErrorT?: string
|
||||
isDataT?: number
|
||||
unitT?: string
|
||||
|
||||
//最大误差值
|
||||
maxError?: string,
|
||||
unit?: string,
|
||||
maxError?: string
|
||||
unit?: string
|
||||
//符合、不符合
|
||||
result?: number,
|
||||
result?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于定义 具体通道的原始数据类型
|
||||
*/
|
||||
export interface RawDataItem {
|
||||
time?: string,
|
||||
harmNum?: number | null,
|
||||
dataA?: string,
|
||||
dataB?: string,
|
||||
dataC?: string,
|
||||
dataT?: string,
|
||||
time?: string
|
||||
harmNum?: number | null
|
||||
dataA?: string
|
||||
dataB?: string
|
||||
dataC?: string
|
||||
dataT?: string
|
||||
unit?: string | null
|
||||
}
|
||||
|
||||
export interface Device {
|
||||
deviceId: string; //装置序号Id
|
||||
deviceName: string; //设备名称
|
||||
chnNum: number; //设备通道数
|
||||
deviceId: string //装置序号Id
|
||||
deviceName: string //设备名称
|
||||
chnNum: number //设备通道数
|
||||
|
||||
planId: string; //计划Id
|
||||
devType: string; //设备类型
|
||||
devVolt: number; //设备电压
|
||||
devCurr: number; //设备电流
|
||||
factorFlag: number; //是否支持系数校准
|
||||
planId: string //计划Id
|
||||
devType: string //设备类型
|
||||
devVolt: number //设备电压
|
||||
devCurr: number //设备电流
|
||||
factorFlag: number //是否支持系数校准
|
||||
checkResult: number //检测结果
|
||||
chnNumList: string[] //连线存储数据
|
||||
}
|
||||
|
||||
// 用来描述检测脚本类型
|
||||
export interface ScriptItem {
|
||||
id: string,
|
||||
code: string,
|
||||
scriptName: string,
|
||||
id: string
|
||||
code: string
|
||||
scriptName: string
|
||||
}
|
||||
|
||||
// 用来描述 检测数据-左侧树结构
|
||||
export interface TreeItem {
|
||||
id: string | null,
|
||||
scriptTypeName: string | null,
|
||||
sourceDesc: string | null,
|
||||
harmNum: number | null,
|
||||
index: number | null,
|
||||
fly: number | null,
|
||||
children: TreeItem[] | null,
|
||||
id: string | null
|
||||
scriptTypeName: string | null
|
||||
sourceDesc: string | null
|
||||
harmNum: number | null
|
||||
index: number | null
|
||||
fly: number | null
|
||||
children: TreeItem[] | null
|
||||
}
|
||||
|
||||
// 用来描述 通道检测结果
|
||||
@@ -134,8 +167,9 @@ export namespace CheckData {
|
||||
}
|
||||
|
||||
export interface DeviceCheckResult {
|
||||
deviceId: string,
|
||||
deviceName: string,
|
||||
deviceId: string
|
||||
deviceName: string
|
||||
code?: string
|
||||
chnResult: ChnCheckResultEnum[] //通道检测结果
|
||||
}
|
||||
|
||||
@@ -143,7 +177,7 @@ export namespace CheckData {
|
||||
export interface ScriptChnItem {
|
||||
scriptType: string
|
||||
scriptName?: string //可以不要该属性,有点多余
|
||||
|
||||
code?: string
|
||||
// 设备
|
||||
devices: Array<DeviceCheckResult>
|
||||
}
|
||||
@@ -153,7 +187,7 @@ export namespace CheckData {
|
||||
LOADING = 'var(--el-color-primary)',
|
||||
SUCCESS = '#91cc75',
|
||||
WARNING = '#e6a23c',
|
||||
DANGER = '#f56c6c',
|
||||
DANGER = '#f56c6c'
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,18 +202,17 @@ export namespace CheckData {
|
||||
* 用于描述 脚本检测结果展示的按钮类型
|
||||
*/
|
||||
export interface ScriptChnViewItem {
|
||||
scriptType: string,
|
||||
scriptType: string
|
||||
scriptName?: string //脚本项名称,可以不要该属性,有点多余
|
||||
|
||||
// 设备
|
||||
devices: Array<{
|
||||
deviceId: string,
|
||||
deviceName: string,
|
||||
chnResult: ButtonResult[],
|
||||
deviceId: string
|
||||
deviceName: string
|
||||
chnResult: ButtonResult[]
|
||||
}>
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 定义检测日志类型
|
||||
*/
|
||||
@@ -192,13 +225,16 @@ export namespace CheckData {
|
||||
* 定义手动检测时,勾选的测试项
|
||||
*/
|
||||
export interface SelectTestItem {
|
||||
preTest: boolean,
|
||||
timeTest: boolean,
|
||||
channelsTest: boolean,
|
||||
preTest: boolean
|
||||
timeTest: boolean
|
||||
channelsTest: boolean
|
||||
test: boolean
|
||||
}
|
||||
|
||||
//描述比对式检测项描述
|
||||
export interface CompareTestItem {
|
||||
id: string
|
||||
code: string
|
||||
name: string
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
import http from "@/api";
|
||||
import {CheckData} from "@/api/check/interface";
|
||||
import { pa } from 'element-plus/es/locale/index.mjs';
|
||||
import http from '@/api'
|
||||
import {CheckData} from '@/api/check/interface'
|
||||
|
||||
export const getBigTestItem = (params: {
|
||||
reCheckType: number,
|
||||
planId: string,
|
||||
devId: string,
|
||||
reCheckType: number
|
||||
planId: 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时,表示查询所有通道,否则只查询指定通道。
|
||||
*/
|
||||
export const getFormData = (params: {
|
||||
planId: string,
|
||||
deviceId: string,
|
||||
chnNum: string,
|
||||
planId: string
|
||||
deviceId: string
|
||||
chnNum: string
|
||||
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
|
||||
*/
|
||||
export const getTreeData = (params: {
|
||||
scriptId?: string,
|
||||
devId?: string,
|
||||
devNum?: string,
|
||||
scriptType?: string | null,
|
||||
code?: string,
|
||||
scriptId?: string
|
||||
devId?: string
|
||||
devNum?: string
|
||||
scriptType?: string | null
|
||||
code?: string
|
||||
}) => {
|
||||
return http.post<CheckData.TreeItem[]>("/result/treeData/", params, {loading: false});
|
||||
return http.post<CheckData.TreeItem[]>('/result/treeData/', params, {loading: false})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,25 +51,25 @@ export const getTreeData = (params: {
|
||||
* @param params
|
||||
*/
|
||||
export const getTableData = (params: {
|
||||
scriptType: string | null,
|
||||
scriptId: string,
|
||||
devId: string,
|
||||
devNum: string,
|
||||
code: string,
|
||||
index: number,
|
||||
scriptType: string | null
|
||||
scriptId: string
|
||||
devId: string
|
||||
devNum: string
|
||||
code: string
|
||||
index: number
|
||||
}) => {
|
||||
return http.post("/result/resultData/", params, {loading: false});
|
||||
return http.post('/result/resultData/', params, {loading: false})
|
||||
}
|
||||
|
||||
export const exportRawData = (params: {
|
||||
scriptType: string | null,
|
||||
scriptId: string,
|
||||
devId: string,
|
||||
devNum: string,
|
||||
code: string,
|
||||
index: number,
|
||||
scriptType: string | null
|
||||
scriptId: string
|
||||
devId: string
|
||||
devNum: string
|
||||
code: string
|
||||
index: number
|
||||
}) => {
|
||||
return http.download("/result/exportRawData", params, {loading: false});
|
||||
return http.download('/result/exportRawData', params, {loading: false})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,13 +77,46 @@ export const exportRawData = (params: {
|
||||
* @param params
|
||||
*/
|
||||
export const reCalculate = (params: {
|
||||
planId: string,
|
||||
scriptId: string,
|
||||
errorSysId: string,
|
||||
deviceId: string,
|
||||
planId: string
|
||||
scriptId: string
|
||||
errorSysId: string
|
||||
deviceId: string
|
||||
code: string
|
||||
patternId: string
|
||||
|
||||
}) => {
|
||||
return http.post("/result/reCalculate", params, {loading: true});
|
||||
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})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,13 +124,14 @@ export const reCalculate = (params: {
|
||||
* @param params
|
||||
*/
|
||||
export const changeErrorSystem = (params: {
|
||||
planId: string,
|
||||
scriptId: string,
|
||||
errorSysId: string,
|
||||
deviceId: string,
|
||||
planId: string
|
||||
scriptId: string
|
||||
errorSysId: string
|
||||
deviceId: string
|
||||
code: string
|
||||
patternId: string
|
||||
}) => {
|
||||
return http.post("/result/changeErrorSystem", params, {loading: true});
|
||||
return http.post('/result/changeErrorSystem', params, {loading: true})
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -266,172 +266,4 @@ const data = [
|
||||
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}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { pa } from 'element-plus/es/locale/index.mjs';
|
||||
import type {Device} from '@/api/device/interface/device'
|
||||
import http from '@/api'
|
||||
|
||||
@@ -30,8 +31,8 @@ export const exportPqDev = (params: Device.ReqPqDevParams) => {
|
||||
return http.download(`/pqDev/export`, params)
|
||||
}
|
||||
// 下载导入文件模板
|
||||
export const downloadTemplate = () => {
|
||||
return http.download(`/pqDev/downloadTemplate`)
|
||||
export const downloadTemplate = (params: { pattern: string }) => {
|
||||
return http.download(`/pqDev/downloadTemplate`,params)
|
||||
}
|
||||
|
||||
//导入被检设备
|
||||
@@ -59,13 +60,15 @@ export const importContrastPqDev = (params: Device.ReqPqDevParams) => {
|
||||
// return http.uploadExcel(`/pqDev/importCNDev`, params)
|
||||
// }
|
||||
|
||||
export const getPqDevById = (params: Device.ReqPqDevParams) => {
|
||||
return http.get(`/pqDev/getById?id=${params.id}`)
|
||||
}
|
||||
|
||||
//根据设备类型决定(电源、icd、模板、通道数、额定电压、额定电流);
|
||||
export const getPqDev = () => {
|
||||
return http.get(`/devType/listAll`)
|
||||
}
|
||||
|
||||
//被检设备归档
|
||||
export const documentedPqDev = (ids: string[]) => {
|
||||
return http.post(`/pqDev/documented`, ids)
|
||||
export const getSelectOptions = (params:{ pattern: string }) => {
|
||||
return http.get(`/pqDev/getSelectOptions`, params)
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ export namespace DevType {
|
||||
devChns: number; //设备通道数
|
||||
reportName: string| null;//报告模版名称
|
||||
state: number;
|
||||
waveCmd:string| null;//录波指令
|
||||
createBy?: string| null; //创建用户
|
||||
createTime?: string| null; //创建时间
|
||||
updateBy?: string| null; //更新用户
|
||||
|
||||
@@ -1,93 +1,119 @@
|
||||
import type { ReqPage, ResPage } from '@/api/interface'
|
||||
import type { Monitor } from './monitor'
|
||||
|
||||
// 被检设备模块
|
||||
export namespace Device {
|
||||
|
||||
/**
|
||||
* 被检设备表格分页查询参数
|
||||
*/
|
||||
export interface ReqPqDevParams extends ReqPage {
|
||||
id: string; // 装置序号id 必填
|
||||
name: string; //设备名称
|
||||
devType?: string; // 设备名称
|
||||
createTime?: string; //创建时间
|
||||
pattern: string;
|
||||
id: string // 装置序号id 必填
|
||||
name: string //设备名称
|
||||
devType?: string // 设备名称
|
||||
createTime?: string //创建时间
|
||||
pattern: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 被检设备表格分页查询参数
|
||||
*/
|
||||
export interface ReqDevReportParams extends ReqPage {
|
||||
planId?: string; // 计划id
|
||||
devId?: string; // 装置id
|
||||
scriptId?: string; // 脚本id
|
||||
planCode?: string;
|
||||
devIdList?: string[]; // 装置id列表
|
||||
planId?: string // 计划id
|
||||
devId?: string // 装置id
|
||||
scriptId?: string // 脚本id
|
||||
planCode?: string
|
||||
devIdList?: string[] // 装置id列表
|
||||
}
|
||||
|
||||
/**
|
||||
* 被检设备新增、修改、根据id查询返回的对象
|
||||
*/
|
||||
export interface ResPqDev {
|
||||
id: string; //装置序号ID
|
||||
name: string; //设备名称
|
||||
pattern: string; //设备模式 模拟 数字 比对
|
||||
devType: string;//设备类型
|
||||
devChns: number; //设备通道数
|
||||
devVolt: number; //额定电压(V)
|
||||
devCurr: number; //额定电流(A)
|
||||
manufacturer?: string | null;//生产厂家
|
||||
createDate: string; //生产日期
|
||||
createId: string; //出厂编号
|
||||
hardwareVersion: string; //固件版本
|
||||
softwareVersion: string; //软件版本
|
||||
protocol: string; //通讯协议
|
||||
ip: string; //IP地址
|
||||
port: number; //端口号
|
||||
encryptionFlag: number; //装置是否为加密版本
|
||||
series?: string | null; //装置识别码(3ds加密)
|
||||
devKey?: string | null; //装置秘钥(3ds加密)
|
||||
sampleId?: string | null; //样品编号
|
||||
arrivedDate?: string; //送样日期
|
||||
cityName?: string | null; //所属地市名称
|
||||
gDName?: string | null; //所属供电公司名称
|
||||
subName?: string | null; //所属电站名称
|
||||
checkState?: number | null; //检测状态
|
||||
checkResult?: number | null; //检测结果
|
||||
reportState?: number | null; //报告状态
|
||||
reportPath?: string | null; //报告路径
|
||||
qRCode?: string | null; //设备关键信息二维码
|
||||
reCheckNum: number; //复检次数
|
||||
planId?: string;//检测计划Id
|
||||
timeCheckResult?: number;//守时检测结果(0:不符合1:符合)
|
||||
factorFlag?: number;//是否支持系数校准(0:不支持,1:支持)
|
||||
factorCheckResult?: number;//系数校准结果(0:不合格,1:合格,2:/表示没有做系数校准)
|
||||
state: number; //状态
|
||||
createBy?: string | null; //创建用户
|
||||
createTime?: string | null; //创建时间
|
||||
updateBy?: string | null; //更新用户
|
||||
updateTime?: string | null; //更新时间
|
||||
icdId: string | null;
|
||||
power: string | null;//工作电源
|
||||
preinvestmentPlan: string | null;
|
||||
id: string //装置序号ID
|
||||
name: string //设备名称
|
||||
pattern: string //设备模式 模拟 数字 比对
|
||||
devType: string //设备类型
|
||||
manufacturer?: string | null //生产厂家
|
||||
createDate: string //生产日期
|
||||
createId: string //出厂编号
|
||||
hardwareVersion: string //固件版本
|
||||
softwareVersion: string //软件版本
|
||||
protocol: string //通讯协议
|
||||
ip: string //IP地址
|
||||
port: number //端口号
|
||||
encryptionFlag: number //装置是否为加密版本
|
||||
series?: string | null //装置识别码(3ds加密)
|
||||
devKey?: string | null //装置秘钥(3ds加密)
|
||||
sampleId?: string | null //样品编号
|
||||
arrivedDate?: string //送样日期
|
||||
cityName?: string | null //所属地市名称
|
||||
gdName?: string | null //所属供电公司名称
|
||||
subName?: string | null //所属电站名称
|
||||
reportPath?: string | null //报告路径
|
||||
planId?: string //检测计划Id
|
||||
factorFlag?: number //是否支持系数校准(0:不支持,1:支持)
|
||||
preinvestmentPlan: string | null //预投计划
|
||||
delegate: string | null //委托方
|
||||
inspectChannel?: string[] | string //被检通道
|
||||
inspectDate?: string | null //定检日期
|
||||
harmSysId?: string | null //谐波系统设备id
|
||||
importFlag?: number //是否为导入设备 0否 1是
|
||||
state: number //状态
|
||||
createBy?: string | null //创建用户
|
||||
createTime?: string | null //创建时间
|
||||
updateBy?: string | null //更新用户
|
||||
updateTime?: string | null //更新时间
|
||||
|
||||
devChns: number //设备通道数
|
||||
devVolt: number //额定电压(V)
|
||||
devCurr: number //额定电流(A)
|
||||
icdId: string | null
|
||||
power: 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 {
|
||||
label: string
|
||||
value: string | number
|
||||
}
|
||||
|
||||
export interface ResDev {
|
||||
id: string;
|
||||
name: string,
|
||||
icd: string,
|
||||
power: string,
|
||||
devVolt: number,
|
||||
devCurr: number,
|
||||
devChns: number,
|
||||
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> {}
|
||||
}
|
||||
@@ -24,6 +24,8 @@ export namespace ICD {
|
||||
createTime?: string| null; //创建时间
|
||||
updateBy?: string| null; //更新用户
|
||||
updateTime?: string| null; //更新时间
|
||||
angle: number; // 是否支持电压相角、电流相角指标
|
||||
usePhaseIndex: number; // 角型接线时是否使用相别的指标来进行检测
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,33 +1,36 @@
|
||||
import type { ReqPage, ResPage } from '@/api/interface'
|
||||
|
||||
// 被检设备模块
|
||||
// 监测点模块
|
||||
export namespace Monitor {
|
||||
|
||||
/**
|
||||
* 电能质量指标字典数据表格分页查询参数
|
||||
* 监测点表格分页查询参数
|
||||
*/
|
||||
export interface ReqPqMonParams extends ReqPage {
|
||||
id: string; // 装置序号id 必填
|
||||
devType?: string; // 设备名称
|
||||
createTime?: string; //创建时间
|
||||
name?: string; // 设备名称
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 被检设备新增、修改、根据id查询返回的对象
|
||||
* 监测点新增、修改、根据id查询返回的对象
|
||||
*/
|
||||
export interface ResPqMon {
|
||||
id: string; //监测点ID
|
||||
code: string; //默认与谐波系统监测点ID相同
|
||||
devId: string; //所属设备ID
|
||||
name: string; //所属母线
|
||||
num: number; //监测点序号
|
||||
pt: number; //PT变比
|
||||
ct: number; //CT变比
|
||||
ptType: string; //接线方式,字典表
|
||||
busbar: string;//所属母线
|
||||
name: string; //监测点名称
|
||||
num: number; //线路号,监测点序号
|
||||
pt: string; //PT变比
|
||||
ct: string; //CT变比
|
||||
connection: string; //接线方式,字典表
|
||||
statInterval: number; //统计间隔
|
||||
harmSysId: string; //默认与谐波系统监测点ID相同
|
||||
checkFlag: number;//是否参与检测0否1是
|
||||
}
|
||||
|
||||
/**
|
||||
* 被检设备表格查询分页返回的对象;
|
||||
* 监测点表格查询分页返回的对象;
|
||||
*/
|
||||
export interface ResPqMonPage extends ResPage<ResPqMon> {
|
||||
|
||||
|
||||
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> {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { Monitor } from '@/api/device/interface/monitor'
|
||||
import http from '@/api'
|
||||
|
||||
/**
|
||||
@@ -6,23 +5,9 @@ import http from '@/api'
|
||||
*/
|
||||
|
||||
//获取监测点
|
||||
export const getPqMonList = (params: Monitor.ReqPqMonParams) => {
|
||||
//return http.post(`/pqMon/list`, params)
|
||||
}
|
||||
|
||||
//添加监测点
|
||||
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)
|
||||
export const getPqMonList = (param:any) => {
|
||||
return http.post(`/pqMonitor/list`, param)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -40,3 +40,12 @@ 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,17 +1,23 @@
|
||||
import { ElMessage, ElTreeSelect } from 'element-plus';
|
||||
import axios, { AxiosInstance, AxiosError, AxiosRequestConfig, InternalAxiosRequestConfig, AxiosResponse } from 'axios'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import axios, {
|
||||
AxiosError,
|
||||
type AxiosInstance,
|
||||
type AxiosRequestConfig,
|
||||
type AxiosResponse,
|
||||
type InternalAxiosRequestConfig
|
||||
} from 'axios'
|
||||
import { showFullScreenLoading, tryHideFullScreenLoading } from '@/components/Loading/fullScreen'
|
||||
import { LOGIN_URL } from '@/config'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { ResultData } from '@/api/interface'
|
||||
import { type ResultData } from '@/api/interface'
|
||||
import { ResultEnum } from '@/enums/httpEnum'
|
||||
import { checkStatus } from './helper/checkStatus'
|
||||
import { useUserStore } from '@/stores/modules/user'
|
||||
import router from '@/routers'
|
||||
import { refreshToken } from '@/api/user/login'
|
||||
import { EventSourcePolyfill } from 'event-source-polyfill'
|
||||
|
||||
export interface CustomAxiosRequestConfig extends InternalAxiosRequestConfig {
|
||||
loading?: boolean;
|
||||
loading?: boolean
|
||||
}
|
||||
|
||||
const config = {
|
||||
@@ -22,7 +28,7 @@ const config = {
|
||||
// 跨域时候允许携带凭证
|
||||
withCredentials: true,
|
||||
// post请求指定数据类型以及编码
|
||||
headers: { 'Content-Type': 'application/json;charset=utf-8' },
|
||||
headers: { 'Content-Type': 'application/json;charset=utf-8' }
|
||||
}
|
||||
|
||||
class RequestHttp {
|
||||
@@ -46,13 +52,13 @@ class RequestHttp {
|
||||
config.loading && showFullScreenLoading()
|
||||
if (config.headers && typeof config.headers.set === 'function') {
|
||||
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
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
return Promise.reject(error)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
let isFirst = true
|
||||
@@ -71,7 +77,8 @@ class RequestHttp {
|
||||
userStore.setAccessToken(userStore.refreshToken)
|
||||
userStore.setIsRefreshToken(true)
|
||||
const result = await refreshToken()
|
||||
if (result) { //获取新token成功的话
|
||||
if (result) {
|
||||
//获取新token成功的话
|
||||
// 有新的token后,重新请求
|
||||
userStore.setAccessToken(result.data.accessToken)
|
||||
userStore.setRefreshToken(result.data.refreshToken)
|
||||
@@ -86,14 +93,15 @@ class RequestHttp {
|
||||
}
|
||||
// 登陆失效
|
||||
if (data.code === ResultEnum.OVERDUE) {
|
||||
console.log("登陆失效")
|
||||
console.log('登陆失效')
|
||||
userStore.setAccessToken('')
|
||||
userStore.setRefreshToken('')
|
||||
userStore.setIsRefreshToken(false)
|
||||
userStore.setUserInfo({ id: '', name: '' })
|
||||
userStore.setExp(0)
|
||||
await router.replace(LOGIN_URL)
|
||||
if(isFirst){//临时处理token失效弹窗多次
|
||||
if (isFirst) {
|
||||
//临时处理token失效弹窗多次
|
||||
ElMessage.error(data.message)
|
||||
isFirst = false
|
||||
}
|
||||
@@ -102,11 +110,11 @@ class RequestHttp {
|
||||
// 全局错误信息拦截(防止下载文件的时候返回数据流,没有 code 直接报错)
|
||||
if (data.code && data.code !== ResultEnum.SUCCESS) {
|
||||
if (data.message.includes('&')) {
|
||||
let formattedMessage = data.message.split('&').join('<br>');
|
||||
let formattedMessage = data.message.split('&').join('<br>')
|
||||
if (data.message.includes(':')) {
|
||||
formattedMessage = formattedMessage.replace(':', '')
|
||||
}
|
||||
ElMessage.error({ message: formattedMessage, dangerouslyUseHTMLString: true });
|
||||
ElMessage.error({ message: formattedMessage, dangerouslyUseHTMLString: true })
|
||||
return Promise.reject(data)
|
||||
}
|
||||
|
||||
@@ -125,6 +133,10 @@ class RequestHttp {
|
||||
await router.replace(LOGIN_URL)
|
||||
return Promise.reject(data)
|
||||
}
|
||||
// 对于blob类型的响应,返回完整的response对象以保留响应头
|
||||
if (response.config.responseType === 'blob') {
|
||||
return response
|
||||
}
|
||||
return data
|
||||
},
|
||||
async (error: AxiosError) => {
|
||||
@@ -139,7 +151,7 @@ class RequestHttp {
|
||||
// 服务器结果都没有返回(可能服务器错误可能客户端断网),断网处理:可以跳转到断网页面
|
||||
if (!window.navigator.onLine) router.replace('/500')
|
||||
return Promise.reject(error)
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -163,6 +175,10 @@ class RequestHttp {
|
||||
}
|
||||
|
||||
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' })
|
||||
}
|
||||
|
||||
@@ -177,14 +193,50 @@ class RequestHttp {
|
||||
* 针对excel的上传,默认返回的是blob类型,Excel没问题时返回json特殊处理
|
||||
*/
|
||||
uploadExcel(url: string, params?: object, _object = {}): Promise<BlobPart> {
|
||||
return this.service.post(url, params, {
|
||||
return this.service
|
||||
.post(url, params, {
|
||||
..._object,
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
responseType: 'blob',
|
||||
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()
|
||||
}
|
||||
|
||||
// 创建EventSource连接
|
||||
const eventSource = new EventSourcePolyfill(requestUrl, {
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + userStore.accessToken
|
||||
},
|
||||
// 增加超时时间到1200秒
|
||||
heartbeatTimeout: 1200000
|
||||
})
|
||||
|
||||
// 设置默认的Authorization头部
|
||||
eventSource.addEventListener('open', function () {
|
||||
console.log('SSE连接已建立')
|
||||
})
|
||||
// 添加错误处理
|
||||
eventSource.addEventListener('error', function (err) {
|
||||
console.error('SSE连接错误:', err)
|
||||
})
|
||||
|
||||
return eventSource
|
||||
}
|
||||
}
|
||||
|
||||
export default new RequestHttp(config)
|
||||
|
||||
@@ -15,6 +15,7 @@ export interface Result {
|
||||
* 请求响应参数(包含data)
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,20 @@ export namespace Plan {
|
||||
|
||||
associateReport:number;//是否关联报告模板 0否 1是
|
||||
reportTemplateName:string;
|
||||
reportTemplateVersion: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; //成员字符串
|
||||
}
|
||||
|
||||
// 检测计划 + 分页
|
||||
@@ -34,15 +47,27 @@ export namespace Plan {
|
||||
}
|
||||
|
||||
|
||||
|
||||
export interface ReqPlan extends ResPlan {
|
||||
datasourceIds:string;
|
||||
sourceIds: string;
|
||||
datasourceIds:string | string[];
|
||||
sourceIds: string | null;
|
||||
planId:string;
|
||||
scriptName: string ;
|
||||
errorSysName: string;
|
||||
sourceName: string ;
|
||||
standardDevNameStr: string;
|
||||
testItemNameStr:string;
|
||||
devIds: string[];
|
||||
}
|
||||
|
||||
export interface PlanTestConfig {
|
||||
planId: string;
|
||||
waveRecord: number;
|
||||
realTime: number;
|
||||
statistics: number;
|
||||
flicker: number;
|
||||
maxTime: number;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -2,7 +2,6 @@ import type { Plan } from './interface'
|
||||
import http from '@/api'
|
||||
import type { ErrorSystem } from '../device/interface/error'
|
||||
import type { Device } from '../device/interface/device'
|
||||
import { ReqDevReportParams } from '@/api/device/interface/device'
|
||||
|
||||
/**
|
||||
* @name 检测计划管理模块
|
||||
@@ -23,8 +22,8 @@ export const updatePlan = (params: any) => {
|
||||
}
|
||||
|
||||
// 删除检测计划
|
||||
export const deletePlan = (params: { id: string[] }) => {
|
||||
return http.post(`/adPlan/delete`, params)
|
||||
export const deletePlan = (params: { id: string[]; pattern: string }) => {
|
||||
return http.post(`/adPlan/delete?pattern=${params.pattern}`, params.id)
|
||||
}
|
||||
|
||||
// 获取指定模式下所有检测源
|
||||
@@ -43,13 +42,13 @@ export const getPqErrSysList = () => {
|
||||
}
|
||||
|
||||
//获取指定模式下所有未绑定的设备
|
||||
export const getUnboundPqDevList = (params: Plan.ReqPlan) => {
|
||||
export const getUnboundPqDevList = (params: { pattern: string}) => {
|
||||
return http.get(`/pqDev/listUnbound?pattern=${params.pattern}`)
|
||||
}
|
||||
|
||||
//根据检测计划id查询出所有已绑定的设备
|
||||
export const getBoundPqDevList = (params: any) => {
|
||||
return http.post(`/pqDev/listByPlanId`, params)
|
||||
return http.post(`/adPlan/listByPlanId`, params)
|
||||
}
|
||||
|
||||
//检测计划绑定设备
|
||||
@@ -86,7 +85,78 @@ export const downloadDevData = (params: Device.ReqDevReportParams) => {
|
||||
return http.download(`/report/downloadReport`, params)
|
||||
}
|
||||
|
||||
// 装置检测报告下载(带响应头)
|
||||
export const downloadDevDataWithHeaders = (params: Device.ReqDevReportParams) => {
|
||||
return http.downloadWithHeaders(`/report/downloadReport`, params)
|
||||
}
|
||||
|
||||
export const staticsAnalyse = (params: { id: string[] }) => {
|
||||
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)
|
||||
@@ -32,3 +32,15 @@ export const pauseTest = () => {
|
||||
export const resumeTest = (params) => {
|
||||
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 {type Base} from '@/api/system/base/interface'
|
||||
|
||||
@@ -12,6 +13,10 @@ export const updateTestConfig = (params: Base.ResTestConfig) => {
|
||||
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
|
||||
autoGenerate:number;//检测报告是否自动生成0 否;1是
|
||||
maxTime:number;//最大复检次数,默认3次
|
||||
dataRule:string;//数据处理原则,关联字典(所有值、部分值、cp95值、平均值、任意值),默认任意值
|
||||
state: number; //状态
|
||||
scale:number;//小数位
|
||||
createBy?: string| null; //创建用户
|
||||
createTime?: string| null; //创建时间
|
||||
updateBy?: string| null; //更新用户
|
||||
|
||||
@@ -2,11 +2,10 @@ import http from '@/api'
|
||||
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}`)
|
||||
}
|
||||
|
||||
|
||||
//编辑有效数据配置
|
||||
export const updateRegRes = (params: VersionRegister.Sys_Reg_Res) => {
|
||||
return http.post(`/sysRegRes/update`, params)
|
||||
|
||||
@@ -3,20 +3,20 @@ import type { ReqPage,ResPage } from '@/api/interface'
|
||||
|
||||
export namespace Login {
|
||||
export interface ReqLoginForm {
|
||||
username: string;
|
||||
password: string;
|
||||
checked: boolean;
|
||||
username: string
|
||||
password: string
|
||||
checked: boolean
|
||||
}
|
||||
export interface ResLogin {
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
accessToken: string
|
||||
refreshToken: string
|
||||
userInfo: {
|
||||
id: string;
|
||||
name: string;
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
}
|
||||
export interface ResAuthButtons {
|
||||
[key: string]: string[];
|
||||
[key: string]: string[]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,8 @@ export namespace User {
|
||||
updateTime?: string;//更新时间
|
||||
roleIds?: string[]; //
|
||||
roleNames?:string[]; //
|
||||
roleCodes?:string[]; //
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
// 用户接口
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { pa } from 'element-plus/es/locale/index.mjs';
|
||||
import type {Login} from '@/api/user/interface/user'
|
||||
import {ADMIN as rePrefix} from '@/api/system/config/serviceName'
|
||||
import http from '@/api'
|
||||
|
||||
@@ -37,3 +37,7 @@ export const updatePassWord = (params: User.ResPassWordUser) => {
|
||||
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 |
@@ -10,10 +10,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="403">
|
||||
import { useRouter } from "vue-router";
|
||||
const router = useRouter();
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "./index.scss";
|
||||
@use './index.scss';
|
||||
</style>
|
||||
|
||||
@@ -10,10 +10,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="404">
|
||||
import { useRouter } from "vue-router";
|
||||
const router = useRouter();
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "./index.scss";
|
||||
@use './index.scss';
|
||||
</style>
|
||||
|
||||
@@ -10,10 +10,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="500">
|
||||
import { useRouter } from "vue-router";
|
||||
const router = useRouter();
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "./index.scss";
|
||||
@use './index.scss';
|
||||
</style>
|
||||
|
||||
@@ -1,58 +1,69 @@
|
||||
<template>
|
||||
<el-dialog v-model='dialogVisible' :title='`批量添加${parameter.title}`' :destroy-on-close='true' width='580px'
|
||||
draggable>
|
||||
<el-form class='drawer-multiColumn-form' label-width='100px'>
|
||||
<el-form-item label='模板下载 :'>
|
||||
<el-button type='primary' :icon='Download' @click='downloadTemp'> 点击下载</el-button>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="`批量添加${parameter.title}`"
|
||||
:destroy-on-close="true"
|
||||
width="580px"
|
||||
draggable
|
||||
>
|
||||
<el-form class="drawer-multiColumn-form" label-width="100px">
|
||||
<el-form-item label="模板下载 :">
|
||||
<el-button type="primary" :icon="Download" @click="downloadTemp">点击下载</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item label='文件上传 :'>
|
||||
<el-form-item label="文件上传 :">
|
||||
<el-upload
|
||||
action='#'
|
||||
class='upload'
|
||||
:drag='true'
|
||||
:limit='excelLimit'
|
||||
:multiple='true'
|
||||
:show-file-list='true'
|
||||
:http-request='uploadExcel'
|
||||
:before-upload='beforeExcelUpload'
|
||||
:on-exceed='handleExceed'
|
||||
action="#"
|
||||
class="upload"
|
||||
:drag="true"
|
||||
:limit="excelLimit"
|
||||
:multiple="true"
|
||||
:show-file-list="true"
|
||||
:http-request="uploadExcel"
|
||||
:before-upload="beforeExcelUpload"
|
||||
:on-exceed="handleExceed"
|
||||
:accept="parameter.fileType!.join(',')"
|
||||
>
|
||||
<slot name='empty'>
|
||||
<el-icon class='el-icon--upload'>
|
||||
<slot name="empty">
|
||||
<el-icon class="el-icon--upload">
|
||||
<upload-filled />
|
||||
</el-icon>
|
||||
<div class='el-upload__text'>将文件拖到此处,或<em>点击上传</em></div>
|
||||
<div class="el-upload__text">
|
||||
将文件拖到此处,或
|
||||
<em>点击上传</em>
|
||||
</div>
|
||||
</slot>
|
||||
<template #tip>
|
||||
<slot name='tip'>
|
||||
<div class='el-upload__tip'>请上传 .xls , .xlsx 标准格式文件,文件最大为 {{ parameter.fileSize }}M</div>
|
||||
<slot name="tip">
|
||||
<div class="el-upload__tip">
|
||||
请上传 .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 v-if="parameter.showCover" label="数据覆盖 :">
|
||||
<el-switch v-model="isCover" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts' name='ImportExcel'>
|
||||
<script setup lang="ts" name="ImportExcel">
|
||||
import { ref } from 'vue'
|
||||
import { useDownload } from '@/hooks/useDownload'
|
||||
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 {
|
||||
title: string; // 标题
|
||||
showCover?: boolean; // 是否显示”数据覆盖“选项
|
||||
patternId?: string; // 模式ID
|
||||
fileSize?: number; // 上传文件的大小
|
||||
fileType?: File.ExcelMimeType[]; // 上传文件的类型
|
||||
tempApi?: (params: any) => Promise<any>; // 下载模板的Api
|
||||
importApi?: (params: any) => Promise<any>; // 批量导入的Api
|
||||
getTableList?: () => void; // 获取表格数据的Api
|
||||
title: string // 标题
|
||||
showCover?: boolean // 是否显示”数据覆盖“选项
|
||||
patternId?: string // 模式ID
|
||||
planId?: string | null //计划ID
|
||||
fileSize?: number // 上传文件的大小
|
||||
fileType?: File.ExcelMimeType[] // 上传文件的类型
|
||||
tempApi?: (params: any) => Promise<any> // 下载模板的Api
|
||||
importApi?: (params: any) => Promise<any> // 批量导入的Api
|
||||
getTableList?: () => void // 获取表格数据的Api
|
||||
}
|
||||
|
||||
// 是否覆盖数据
|
||||
@@ -65,9 +76,11 @@ const dialogVisible = ref(false)
|
||||
const parameter = ref<ExcelParameterProps>({
|
||||
title: '',
|
||||
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) => {
|
||||
parameter.value = { ...parameter.value, ...params }
|
||||
@@ -77,7 +90,7 @@ const acceptParams = (params: ExcelParameterProps) => {
|
||||
// Excel 导入模板下载
|
||||
const downloadTemp = () => {
|
||||
if (!parameter.value.tempApi) return
|
||||
useDownload(parameter.value.tempApi, `${parameter.value.title}模板`, {'patternId':parameter.value.patternId}, false)
|
||||
useDownload(parameter.value.tempApi, `${parameter.value.title}模板`, { pattern: parameter.value.patternId }, false)
|
||||
}
|
||||
|
||||
// 文件上传
|
||||
@@ -87,15 +100,17 @@ const uploadExcel = async (param: UploadRequestOptions) => {
|
||||
if (parameter.value.patternId) {
|
||||
excelFormData.append('patternId', parameter.value.patternId)
|
||||
}
|
||||
|
||||
excelFormData.append('planId', parameter.value.planId)
|
||||
|
||||
isCover.value && excelFormData.append('isCover', isCover.value as unknown as Blob)
|
||||
//await parameter.value.importApi!(excelFormData);
|
||||
await parameter.value.importApi!(excelFormData)
|
||||
.then(res => handleImportResponse(res))
|
||||
await parameter.value.importApi!(excelFormData).then(res => handleImportResponse(res))
|
||||
|
||||
parameter.value.getTableList && parameter.value.getTableList()
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
|
||||
async function handleImportResponse(res: any) {
|
||||
console.log(res)
|
||||
|
||||
@@ -107,14 +122,19 @@ async function handleImportResponse(res: any) {
|
||||
if (jsonData.code === 'A0000') {
|
||||
ElMessage.success('导入成功')
|
||||
} else {
|
||||
ElMessage.error(jsonData.message)
|
||||
ElMessageBox.alert(jsonData.message, {
|
||||
title: '导入结果',
|
||||
type: 'error'
|
||||
})
|
||||
}
|
||||
emit('result', jsonData.data)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
fileReader.readAsText(res)
|
||||
} else {
|
||||
emit('result', false)
|
||||
ElMessage.error('导入失败,请查看下载附件!')
|
||||
let blob = new Blob([res], { type: 'application/vnd.ms-excel' })
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
@@ -138,14 +158,14 @@ const beforeExcelUpload = (file: UploadRawFile) => {
|
||||
ElNotification({
|
||||
title: '温馨提示',
|
||||
message: '上传文件只能是 xls / xlsx 格式!',
|
||||
type: 'warning',
|
||||
type: 'warning'
|
||||
})
|
||||
if (!fileSize)
|
||||
setTimeout(() => {
|
||||
ElNotification({
|
||||
title: '温馨提示',
|
||||
message: `上传文件大小不能超过 ${parameter.value.fileSize}MB!`,
|
||||
type: 'warning',
|
||||
type: 'warning'
|
||||
})
|
||||
}, 0)
|
||||
return isExcel && fileSize
|
||||
@@ -156,7 +176,7 @@ const handleExceed = () => {
|
||||
ElNotification({
|
||||
title: '温馨提示',
|
||||
message: '最多只能上传一个文件!',
|
||||
type: 'warning',
|
||||
type: 'warning'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -179,9 +199,9 @@ const handleExceed = () => {
|
||||
// }
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
acceptParams
|
||||
})
|
||||
</script>
|
||||
<style lang='scss' scoped>
|
||||
@import "./index.scss";
|
||||
<style lang="scss" scoped>
|
||||
@use './index.scss';
|
||||
</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>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "./index.scss";
|
||||
@use "./index.scss";;
|
||||
</style>
|
||||
|
||||
@@ -119,6 +119,7 @@ import TableColumn from './components/TableColumn.vue'
|
||||
import Sortable from 'sortablejs'
|
||||
|
||||
export interface ProTableProps {
|
||||
|
||||
columns: ColumnProps[]; // 列配置项 ==> 必传
|
||||
data?: any[]; // 静态 table data 数据,若存在则不会使用 requestApi 返回的 data ==> 非必传
|
||||
requestApi?: (params: any) => Promise<any>; // 请求表格数据的 api ==> 非必传
|
||||
|
||||
@@ -1,49 +1,49 @@
|
||||
<template>
|
||||
<div class='icon-box' >
|
||||
<div class="icon-box">
|
||||
<el-input
|
||||
ref='inputRef'
|
||||
v-model='valueIcon'
|
||||
v-bind='$attrs'
|
||||
:placeholder='placeholder'
|
||||
:clearable='clearable'
|
||||
@clear='clearIcon'
|
||||
@click='openDialog'
|
||||
ref="inputRef"
|
||||
v-model="valueIcon"
|
||||
v-bind="$attrs"
|
||||
:placeholder="placeholder"
|
||||
:clearable="clearable"
|
||||
@clear="clearIcon"
|
||||
@click="openDialog"
|
||||
>
|
||||
<template #append>
|
||||
<el-button :icon='customIcons[iconValue]' />
|
||||
<el-button :icon="customIcons[iconValue]" />
|
||||
</template>
|
||||
</el-input>
|
||||
<el-dialog v-model='dialogVisible' :title='placeholder' top='5%' width='30%' >
|
||||
<el-input v-model='inputValue' placeholder='搜索图标' size='large' :prefix-icon='Icons.Search' />
|
||||
<el-scrollbar v-if='Object.keys(iconsList).length'>
|
||||
<div class='icon-list'>
|
||||
<div v-for='item in iconsList' :key='item' class='icon-item' @click='selectIcon(item)'>
|
||||
<component :is='item'></component>
|
||||
<el-dialog v-model="dialogVisible" :title="placeholder" top="5%" width="30%">
|
||||
<el-input v-model="inputValue" placeholder="搜索图标" size="large" :prefix-icon="Icons.Search" />
|
||||
<el-scrollbar v-if="Object.keys(iconsList).length">
|
||||
<div class="icon-list">
|
||||
<div v-for="item in iconsList" :key="item" class="icon-item" @click="selectIcon(item)">
|
||||
<component :is="item"></component>
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<el-empty v-else description='未搜索到您要找的图标~' />
|
||||
<el-empty v-else description="未搜索到您要找的图标~" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang='ts' setup name='SelectIcon'>
|
||||
<script lang="ts" setup name="SelectIcon">
|
||||
import * as Icons from '@element-plus/icons-vue'
|
||||
import { computed, ref } from 'vue';
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
interface SelectIconProps {
|
||||
iconValue: string| undefined;
|
||||
title?: string;
|
||||
clearable?: boolean;
|
||||
placeholder?: string;
|
||||
iconValue: string | undefined
|
||||
title?: string
|
||||
clearable?: boolean
|
||||
placeholder?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<SelectIconProps>(), {
|
||||
iconValue: '',
|
||||
title: '请选择图标',
|
||||
clearable: true,
|
||||
placeholder: '请选择图标',
|
||||
placeholder: '请选择图标'
|
||||
})
|
||||
|
||||
// 重新接收一下,防止打包后 clearable 报错
|
||||
@@ -55,7 +55,7 @@ const openDialog = () => (dialogVisible.value = true)
|
||||
|
||||
// 选择图标(触发更新父组件数据)
|
||||
const emit = defineEmits<{
|
||||
'update:iconValue': [value: string];
|
||||
'update:iconValue': [value: string]
|
||||
}>()
|
||||
const selectIcon = (item: any) => {
|
||||
dialogVisible.value = false
|
||||
@@ -83,9 +83,8 @@ const iconsList = computed((): { [key: string]: any } => {
|
||||
}
|
||||
return result
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang='scss'>
|
||||
@import "./index.scss";
|
||||
<style scoped lang="scss">
|
||||
@use './index.scss';
|
||||
</style>
|
||||
|
||||
@@ -1,97 +1,84 @@
|
||||
<template>
|
||||
<div class='time-control'>
|
||||
<el-select
|
||||
class='select'
|
||||
v-model='timeUnit'
|
||||
placeholder='选择时间单位'
|
||||
@change='handleChange'
|
||||
>
|
||||
<div class="time-control">
|
||||
<el-select class="select" v-model="timeUnit" placeholder="选择时间单位" @change="handleChange">
|
||||
<!-- 采用 v-for 动态渲染 -->
|
||||
<el-option
|
||||
v-for='unit in timeUnits'
|
||||
:key='unit.value'
|
||||
:label='unit.label'
|
||||
:value='unit.value'
|
||||
></el-option>
|
||||
<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
|
||||
class='date-picker'
|
||||
v-model='startDate'
|
||||
type='date'
|
||||
placeholder='起始时间'
|
||||
@change='emitDateChange'
|
||||
:disabled-date='disableStartDate'
|
||||
class="date-picker"
|
||||
v-model="startDate"
|
||||
type="date"
|
||||
placeholder="起始时间"
|
||||
@change="emitDateChange"
|
||||
:disabled-date="disableStartDate"
|
||||
:readonly="timeUnit != '自定义'"
|
||||
></el-date-picker>
|
||||
<el-text>~</el-text>
|
||||
<el-date-picker
|
||||
class='date-picker'
|
||||
v-model='endDate'
|
||||
type='date'
|
||||
placeholder='结束时间'
|
||||
@change='emitDateChange'
|
||||
:disabled-date='disableEndDate'
|
||||
class="date-picker"
|
||||
v-model="endDate"
|
||||
type="date"
|
||||
placeholder="结束时间"
|
||||
@change="emitDateChange"
|
||||
:disabled-date="disableEndDate"
|
||||
:readonly="timeUnit !== '自定义'"
|
||||
></el-date-picker>
|
||||
</div>
|
||||
<div class='date-display' v-if="timeUnit !== '自定义'">
|
||||
<div class="date-display" v-if="timeUnit !== '自定义'">
|
||||
<el-button
|
||||
style='width: 10px;'
|
||||
class='triangle-button'
|
||||
type='primary'
|
||||
@click='prevPeriod'
|
||||
@change='emitDateChange'
|
||||
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'>
|
||||
当前
|
||||
<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'
|
||||
style="width: 10px"
|
||||
class="triangle-button"
|
||||
type="primary"
|
||||
@click="nextPeriod"
|
||||
:disabled="isNextDisabled"
|
||||
>
|
||||
<div class='right_triangle'></div>
|
||||
<div class="right_triangle"></div>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup lang='ts'>
|
||||
import { ref, onMounted, defineProps, defineEmits } from 'vue'
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue'
|
||||
|
||||
// 定义时间单位的类型
|
||||
interface TimeUnit {
|
||||
label: string;
|
||||
value: string;
|
||||
label: string
|
||||
value: string
|
||||
}
|
||||
|
||||
// 定义组件的props,包含包括和排除的时间单位
|
||||
const props = defineProps({
|
||||
include: {
|
||||
type: Array as () => string[],
|
||||
default: () => ['日', '周', '月', '季度', '年', '自定义'],
|
||||
default: () => ['日', '周', '月', '季度', '年', '自定义']
|
||||
},
|
||||
exclude: {
|
||||
type: Array as () => string[],
|
||||
default: () => [],
|
||||
default: () => []
|
||||
},
|
||||
default: {
|
||||
type: String,
|
||||
default: '月',
|
||||
},
|
||||
default: '月'
|
||||
}
|
||||
})
|
||||
|
||||
// 定义事件
|
||||
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 startDate = ref<Date>(new Date()) // 起始日期
|
||||
@@ -100,16 +87,17 @@ const isNextDisabled = ref<boolean>(false) // 控制下一周期按钮的禁用
|
||||
const today = ref<Date>(new Date()) // 当前日期
|
||||
// 过滤出可用的时间单位
|
||||
const timeUnits = ref<TimeUnit[]>(
|
||||
props.include.filter(unit => !props.exclude.includes(unit)).map(unit => ({
|
||||
props.include
|
||||
.filter(unit => !props.exclude.includes(unit))
|
||||
.map(unit => ({
|
||||
label: unit,
|
||||
value: unit,
|
||||
})),
|
||||
value: unit
|
||||
}))
|
||||
)
|
||||
|
||||
// 发出日期变化事件
|
||||
const emitDateChange = () => {
|
||||
emit('update-dates', formatDate(startDate.value), formatDate(endDate.value))
|
||||
|
||||
}
|
||||
|
||||
// 在组件挂载时更新日期范围
|
||||
@@ -139,7 +127,6 @@ const handleChange = (unit: string) => {
|
||||
updateNextButtonStatus()
|
||||
}
|
||||
const updateDateRange = () => {
|
||||
|
||||
// 根据选择的时间单位计算起始和结束日期
|
||||
if (timeUnit.value === '日') {
|
||||
startDate.value = today.value
|
||||
@@ -147,32 +134,29 @@ const updateDateRange = () => {
|
||||
} 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);
|
||||
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);
|
||||
startDate.value = new Date(today.value.getFullYear(), quarter * 3, 1);
|
||||
endDate.value = new Date(today.value.getFullYear(), quarter * 3 + 3, 0);
|
||||
const quarter = Math.floor(today.value.getMonth() / 3)
|
||||
startDate.value = new Date(today.value.getFullYear(), quarter * 3, 1)
|
||||
endDate.value = new Date(today.value.getFullYear(), quarter * 3 + 3, 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 === '年') {
|
||||
startDate.value = new Date(today.value.getFullYear(), 0, 1);
|
||||
endDate.value = new Date(today.value.getFullYear(), 11, 31);
|
||||
startDate.value = new Date(today.value.getFullYear(), 0, 1)
|
||||
endDate.value = new Date(today.value.getFullYear(), 11, 31)
|
||||
|
||||
// // 确保结束日期不超过今天
|
||||
// if (endDate.value > today.value) {
|
||||
@@ -205,8 +189,8 @@ const getEndOfWeek = (date: Date) => {
|
||||
endOfWeek.setDate(endOfWeek.getDate() + diff)
|
||||
|
||||
// 获取今天的日期
|
||||
const today = new Date();
|
||||
today.setHours(23, 59, 59, 999); // 设置今天的结束时间(23:59:59.999)
|
||||
const today = new Date()
|
||||
today.setHours(23, 59, 59, 999) // 设置今天的结束时间(23:59:59.999)
|
||||
|
||||
// 返回不超过今天的结束时间
|
||||
//return endOfWeek > today ? today : endOfWeek;
|
||||
@@ -223,13 +207,8 @@ const prevPeriod = () => {
|
||||
prevStartDate.setDate(prevStartDate.getDate() - 7)
|
||||
prevEndDate.setDate(prevEndDate.getDate() - 7)
|
||||
} 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)
|
||||
@@ -273,7 +252,6 @@ const nextPeriod = () => {
|
||||
updateNextButtonStatus()
|
||||
}
|
||||
const updateNextButtonStatus = () => {
|
||||
|
||||
// 更新下一个按钮的禁用状态
|
||||
const maxDate = new Date() // 假设最新日期为今天
|
||||
// 将 maxDate 设置为当天的开始时间
|
||||
@@ -286,7 +264,6 @@ const updateNextButtonStatus = () => {
|
||||
emitDateChange() // 变化时也发出更新事件
|
||||
}
|
||||
|
||||
|
||||
// 限制开始日期不能选择超过当前日期
|
||||
const disableStartDate = (date: Date) => {
|
||||
return date > today.value
|
||||
@@ -298,21 +275,18 @@ const disableEndDate = (date: Date) => {
|
||||
return date > today.value || (start && date <= start)
|
||||
}
|
||||
|
||||
|
||||
// 格式化日期yyyy-mm-dd
|
||||
function formatDate(date: Date | null): string {
|
||||
if (!date) {
|
||||
return '';
|
||||
return ''
|
||||
}
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
return `${year}-${month}-${day}`;
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
return `${year}-${month}-${day}`
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang='scss'>
|
||||
@import "./index.scss";
|
||||
<style scoped lang="scss">
|
||||
@use './index.scss';
|
||||
</style>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</template>
|
||||
|
||||
<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 * as echarts from 'echarts' // 全引入
|
||||
// import 'echarts/lib/component/dataZoom'
|
||||
|
||||
@@ -7,20 +7,23 @@ import type { Directive, DirectiveBinding } from 'vue'
|
||||
|
||||
const auth: Directive = {
|
||||
mounted(el: HTMLElement, binding: DirectiveBinding) {
|
||||
const { value } = binding
|
||||
//console.log('binding',binding)
|
||||
const { value, modifiers } = binding
|
||||
let currentPageRoles = []
|
||||
const authStore = useAuthStore()
|
||||
const currentPageRoles = authStore.authButtonListGet[authStore.routeName] ?? []
|
||||
// console.log('1234',authStore.routeName)
|
||||
// console.log('123',currentPageRoles)
|
||||
if (modifiers && Object.keys(modifiers).length) {
|
||||
currentPageRoles = authStore.authButtonListGet[Object.keys(modifiers)[0]] ?? []
|
||||
} else {
|
||||
currentPageRoles = authStore.authButtonListGet[authStore.routeName] ?? []
|
||||
}
|
||||
console.log('currentPageRoles', currentPageRoles)
|
||||
if (value instanceof Array && value.length) {
|
||||
//console.log('123456',value)
|
||||
const hasPermission = value.every(item => currentPageRoles.includes(item))
|
||||
if (!hasPermission) el.remove()
|
||||
} else {
|
||||
//console.log('12345',value)
|
||||
if (!currentPageRoles.includes(value)) el.remove()
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default auth
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ElNotification } from "element-plus";
|
||||
import type { AxiosResponse } from "axios";
|
||||
|
||||
/**
|
||||
* @description 接收数据流生成 blob,创建链接,下载文件
|
||||
@@ -8,6 +9,55 @@ import { ElNotification } from "element-plus";
|
||||
* @param {Boolean} isNotify 是否有导出消息提示 (默认为 true)
|
||||
* @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 (
|
||||
api: (param: any) => Promise<any>,
|
||||
tempName: string,
|
||||
@@ -42,3 +92,70 @@ export const useDownload = async (
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @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));
|
||||
state.tableData = isPageable ? data.records : data;
|
||||
//console.log(data);
|
||||
// 解构后台返回的分页数据 (如果有分页更新分页信息)
|
||||
if (isPageable) {
|
||||
state.resPageable.total = data.total;
|
||||
@@ -85,6 +86,7 @@ export const useTable = (
|
||||
state.resPageable.size = data.size;
|
||||
}
|
||||
} catch (error) {
|
||||
//console.log('123');
|
||||
requestError && requestError(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -27,6 +27,7 @@ export default {
|
||||
changeMode:"Change Mode",
|
||||
versionRegister:"Version Register",
|
||||
changeTheme: "Change Theme",
|
||||
changeScene: "Change Scene",
|
||||
logout: "Logout"
|
||||
}
|
||||
};
|
||||
|
||||
@@ -27,6 +27,7 @@ export default {
|
||||
changeMode:"模式切换",
|
||||
versionRegister:"版本注册",
|
||||
changeTheme:"主题切换",
|
||||
changeScene: "场景切换",
|
||||
logout: "退出登录"
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<!-- 经典布局 -->
|
||||
<template>
|
||||
<el-container class="layout">
|
||||
|
||||
<el-header>
|
||||
<div class="header-lf mask-image">
|
||||
<div class="logo flx-center">
|
||||
@@ -38,26 +37,26 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="layoutClassic">
|
||||
import { computed } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useAuthStore } from "@/stores/modules/auth";
|
||||
import { useGlobalStore } from "@/stores/modules/global";
|
||||
import Main from "@/layouts/components/Main/index.vue";
|
||||
import SubMenu from "@/layouts/components/Menu/SubMenu.vue";
|
||||
import ToolBarLeft from "@/layouts/components/Header/ToolBarLeft.vue";
|
||||
import ToolBarRight from "@/layouts/components/Header/ToolBarRight.vue";
|
||||
import { computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useAuthStore } from '@/stores/modules/auth'
|
||||
import { useGlobalStore } from '@/stores/modules/global'
|
||||
import Main from '@/layouts/components/Main/index.vue'
|
||||
import SubMenu from '@/layouts/components/Menu/SubMenu.vue'
|
||||
import ToolBarLeft from '@/layouts/components/Header/ToolBarLeft.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 authStore = useAuthStore();
|
||||
const globalStore = useGlobalStore();
|
||||
const accordion = computed(() => globalStore.accordion);
|
||||
const isCollapse = computed(() => globalStore.isCollapse);
|
||||
const menuList = computed(() => authStore.showMenuListGet);
|
||||
const activeMenu = computed(() => (route.meta.activeMenu ? route.meta.activeMenu : route.path) as string);
|
||||
const route = useRoute()
|
||||
const authStore = useAuthStore()
|
||||
const globalStore = useGlobalStore()
|
||||
const accordion = computed(() => globalStore.accordion)
|
||||
const isCollapse = computed(() => globalStore.isCollapse)
|
||||
const menuList = computed(() => authStore.showMenuListGet)
|
||||
const activeMenu = computed(() => (route.meta.activeMenu ? route.meta.activeMenu : route.path) as string)
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "./index.scss";
|
||||
@use './index.scss';
|
||||
</style>
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
v-for="item in menuList"
|
||||
:key="item.path"
|
||||
class="split-item"
|
||||
:class="{ 'split-active': splitActive === item.path || `/${splitActive.split('/')[1]}` === item.path }"
|
||||
:class="{
|
||||
'split-active': splitActive === item.path || `/${splitActive.split('/')[1]}` === item.path
|
||||
}"
|
||||
@click="changeSubMenu(item)"
|
||||
>
|
||||
<el-icon>
|
||||
@@ -24,7 +26,7 @@
|
||||
</div>
|
||||
<el-aside :class="{ 'not-aside': !subMenuList.length }" :style="{ width: isCollapse ? '65px' : '210px' }">
|
||||
<div class="logo flx-center">
|
||||
<span v-show="subMenuList.length" class="logo-text">{{ isCollapse ? "G" : title }}</span>
|
||||
<span v-show="subMenuList.length" class="logo-text">{{ isCollapse ? 'G' : title }}</span>
|
||||
</div>
|
||||
<el-scrollbar>
|
||||
<el-menu
|
||||
@@ -49,55 +51,55 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="layoutColumns">
|
||||
import { ref, computed, watch } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { useAuthStore } from "@/stores/modules/auth";
|
||||
import { useGlobalStore } from "@/stores/modules/global";
|
||||
import Main from "@/layouts/components/Main/index.vue";
|
||||
import ToolBarLeft from "@/layouts/components/Header/ToolBarLeft.vue";
|
||||
import ToolBarRight from "@/layouts/components/Header/ToolBarRight.vue";
|
||||
import SubMenu from "@/layouts/components/Menu/SubMenu.vue";
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useAuthStore } from '@/stores/modules/auth'
|
||||
import { useGlobalStore } from '@/stores/modules/global'
|
||||
import Main from '@/layouts/components/Main/index.vue'
|
||||
import ToolBarLeft from '@/layouts/components/Header/ToolBarLeft.vue'
|
||||
import ToolBarRight from '@/layouts/components/Header/ToolBarRight.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 router = useRouter();
|
||||
const authStore = useAuthStore();
|
||||
const globalStore = useGlobalStore();
|
||||
const accordion = computed(() => globalStore.accordion);
|
||||
const isCollapse = computed(() => globalStore.isCollapse);
|
||||
const menuList = computed(() => authStore.showMenuListGet);
|
||||
const activeMenu = computed(() => (route.meta.activeMenu ? route.meta.activeMenu : route.path) as string);
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const authStore = useAuthStore()
|
||||
const globalStore = useGlobalStore()
|
||||
const accordion = computed(() => globalStore.accordion)
|
||||
const isCollapse = computed(() => globalStore.isCollapse)
|
||||
const menuList = computed(() => authStore.showMenuListGet)
|
||||
const activeMenu = computed(() => (route.meta.activeMenu ? route.meta.activeMenu : route.path) as string)
|
||||
|
||||
const subMenuList = ref<Menu.MenuOptions[]>([]);
|
||||
const splitActive = ref("");
|
||||
const subMenuList = ref<Menu.MenuOptions[]>([])
|
||||
const splitActive = ref('')
|
||||
watch(
|
||||
() => [menuList, route],
|
||||
() => {
|
||||
// 当前菜单没有数据直接 return
|
||||
if (!menuList.value.length) return;
|
||||
splitActive.value = route.path;
|
||||
if (!menuList.value.length) return
|
||||
splitActive.value = route.path
|
||||
const menuItem = menuList.value.filter((item: Menu.MenuOptions) => {
|
||||
return route.path === item.path || `/${route.path.split("/")[1]}` === item.path;
|
||||
});
|
||||
if (menuItem[0].children?.length) return (subMenuList.value = menuItem[0].children);
|
||||
subMenuList.value = [];
|
||||
return route.path === item.path || `/${route.path.split('/')[1]}` === item.path
|
||||
})
|
||||
if (menuItem[0].children?.length) return (subMenuList.value = menuItem[0].children)
|
||||
subMenuList.value = []
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
)
|
||||
|
||||
// change SubMenu
|
||||
const changeSubMenu = (item: Menu.MenuOptions) => {
|
||||
splitActive.value = item.path;
|
||||
if (item.children?.length) return (subMenuList.value = item.children);
|
||||
subMenuList.value = [];
|
||||
router.push(item.path);
|
||||
};
|
||||
splitActive.value = item.path
|
||||
if (item.children?.length) return (subMenuList.value = item.children)
|
||||
subMenuList.value = []
|
||||
router.push(item.path)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "./index.scss";
|
||||
@use './index.scss';
|
||||
</style>
|
||||
|
||||
@@ -4,11 +4,7 @@
|
||||
<el-header>
|
||||
<div class="logo flx-center">
|
||||
<!-- <img class="logo-img" src="@/assets/images/logo.svg" alt="logo" /> -->
|
||||
<img
|
||||
class="logo-img"
|
||||
src="@/assets/images/cn_pms9100_logo.png"
|
||||
alt="logo"
|
||||
/>
|
||||
<img class="logo-img" src="@/assets/images/cn_pms9100_logo.png" alt="logo" />
|
||||
<span class="logo-text">{{ title }}</span>
|
||||
</div>
|
||||
<el-menu v-if="showMenuFlag" trigger="click" mode="horizontal" :router="false" :default-active="activeMenu">
|
||||
@@ -45,37 +41,34 @@
|
||||
<ToolBarRight />
|
||||
</el-header>
|
||||
<Main />
|
||||
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="layoutTransverse">
|
||||
import { computed } from "vue";
|
||||
import { useAuthStore } from "@/stores/modules/auth";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import Main from "@/layouts/components/Main/index.vue";
|
||||
import ToolBarRight from "@/layouts/components/Header/ToolBarRight.vue";
|
||||
import SubMenu from "@/layouts/components/Menu/SubMenu.vue";
|
||||
import { computed } from 'vue'
|
||||
import { useAuthStore } from '@/stores/modules/auth'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import Main from '@/layouts/components/Main/index.vue'
|
||||
import ToolBarRight from '@/layouts/components/Header/ToolBarRight.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 router = useRouter();
|
||||
const authStore = useAuthStore();
|
||||
const menuList = computed(() => authStore.showMenuListGet);
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const authStore = useAuthStore()
|
||||
const menuList = computed(() => authStore.showMenuListGet)
|
||||
const showMenuFlag = computed(() => authStore.showMenuFlagGet)
|
||||
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 handleClickMenu = (subItem: Menu.MenuOptions) => {
|
||||
if (subItem.meta.isLink) return window.open(subItem.meta.isLink, "_blank");
|
||||
router.push(subItem.path);
|
||||
};
|
||||
if (subItem.meta.isLink) return window.open(subItem.meta.isLink, '_blank')
|
||||
router.push(subItem.path)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "./index.scss";
|
||||
@use './index.scss';
|
||||
.logo {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
@@ -31,26 +31,26 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="layoutVertical">
|
||||
import { computed } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useAuthStore } from "@/stores/modules/auth";
|
||||
import { useGlobalStore } from "@/stores/modules/global";
|
||||
import Main from "@/layouts/components/Main/index.vue";
|
||||
import ToolBarLeft from "@/layouts/components/Header/ToolBarLeft.vue";
|
||||
import ToolBarRight from "@/layouts/components/Header/ToolBarRight.vue";
|
||||
import SubMenu from "@/layouts/components/Menu/SubMenu.vue";
|
||||
import { computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useAuthStore } from '@/stores/modules/auth'
|
||||
import { useGlobalStore } from '@/stores/modules/global'
|
||||
import Main from '@/layouts/components/Main/index.vue'
|
||||
import ToolBarLeft from '@/layouts/components/Header/ToolBarLeft.vue'
|
||||
import ToolBarRight from '@/layouts/components/Header/ToolBarRight.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 authStore = useAuthStore();
|
||||
const globalStore = useGlobalStore();
|
||||
const accordion = computed(() => globalStore.accordion);
|
||||
const isCollapse = computed(() => globalStore.isCollapse);
|
||||
const menuList = computed(() => authStore.showMenuListGet);
|
||||
const activeMenu = computed(() => (route.meta.activeMenu ? route.meta.activeMenu : route.path) as string);
|
||||
const route = useRoute()
|
||||
const authStore = useAuthStore()
|
||||
const globalStore = useGlobalStore()
|
||||
const accordion = computed(() => globalStore.accordion)
|
||||
const isCollapse = computed(() => globalStore.isCollapse)
|
||||
const menuList = computed(() => authStore.showMenuListGet)
|
||||
const activeMenu = computed(() => (route.meta.activeMenu ? route.meta.activeMenu : route.path) as string)
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "./index.scss";
|
||||
@use './index.scss';
|
||||
</style>
|
||||
|
||||
@@ -10,75 +10,51 @@
|
||||
<!-- <el-button dictType="primary"> -->
|
||||
<div class="change_mode">
|
||||
{{ title }}
|
||||
<el-icon class="el-icon--right change_mode_down"
|
||||
><arrow-down
|
||||
/></el-icon>
|
||||
<el-icon class="el-icon--right change_mode_down"><arrow-down /></el-icon>
|
||||
<el-icon class="el-icon--right change_mode_up"><arrow-up /></el-icon>
|
||||
</div>
|
||||
<!-- </el-button> -->
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="handelOpen('模拟式')"
|
||||
>模拟式模块</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item @click="handelOpen('数字式')"
|
||||
>数字式模块</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item @click="handelOpen('比对式')"
|
||||
>比对式模块</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item @click="handelOpen('模拟式')">模拟式模块</el-dropdown-item>
|
||||
<el-dropdown-item @click="handelOpen('数字式')">数字式模块</el-dropdown-item>
|
||||
<el-dropdown-item @click="handelOpen('比对式')">比对式模块</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<p style="margin: 0;" >
|
||||
<a href="http://www.shining-electric.com/" target="_blank">
|
||||
2024 © 南京灿能电力自动化股份有限公司
|
||||
</a>
|
||||
<p style="margin: 0">
|
||||
<a href="http://www.shining-electric.com/" target="_blank">2024 © 南京灿能电力自动化股份有限公司</a>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, computed, onMounted, watch } from "vue";
|
||||
import { useAuthStore } from "@/stores/modules/auth";
|
||||
import { useModeStore } from '@/stores/modules/mode'; // 引入模式 store
|
||||
import { useRouter } from "vue-router";
|
||||
const title = ref("模拟式模块");
|
||||
const router = useRouter();
|
||||
const authStore = useAuthStore();
|
||||
const modeStore = useModeStore();
|
||||
import { computed } from 'vue'
|
||||
import { useAuthStore } from '@/stores/modules/auth'
|
||||
import { useModeStore } from '@/stores/modules/mode' // 引入模式 store
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
onMounted(() => {
|
||||
switch (modeStore.currentMode) {
|
||||
case '模拟式':
|
||||
title.value = '模拟式模块';
|
||||
break;
|
||||
case '数字式':
|
||||
title.value = '数字式模块';
|
||||
break;
|
||||
case '比对式':
|
||||
title.value = '比对式模块';
|
||||
break;
|
||||
default:
|
||||
title.value = '模拟式模块';
|
||||
}
|
||||
});
|
||||
const router = useRouter()
|
||||
const authStore = useAuthStore()
|
||||
const modeStore = useModeStore()
|
||||
|
||||
const title = computed(() => {
|
||||
return modeStore.currentMode === '' ? '模拟式模块' : modeStore.currentMode + '模块'
|
||||
})
|
||||
|
||||
const handelOpen = async (item: string) => {
|
||||
await authStore.setShowMenu();
|
||||
modeStore.setCurrentMode(item); // 将模式code存入 store
|
||||
await authStore.setShowMenu()
|
||||
modeStore.setCurrentMode(item) // 将模式code存入 store
|
||||
|
||||
//if (router.currentRoute.value.path === '/home/index') {
|
||||
// 强制刷新页面
|
||||
window.location.reload();
|
||||
window.location.reload()
|
||||
//} else {
|
||||
// router.push({ path: '/home/index' });
|
||||
//}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
@import "./index.scss";
|
||||
@use './index.scss';
|
||||
.footer {
|
||||
position: relative;
|
||||
background-color: var(--el-color-primary);
|
||||
|
||||
@@ -33,6 +33,28 @@
|
||||
<el-dropdown-item @click="openDialog('versionRegisterRef')">
|
||||
<el-icon><SetUp /></el-icon>{{ t("header.versionRegister") }}
|
||||
</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>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
@@ -62,7 +84,7 @@ import PasswordDialog from "./PasswordDialog.vue";
|
||||
import ThemeDialog from "./ThemeDialog.vue";
|
||||
import VersionDialog from "@/views/system/versionRegister/index.vue";
|
||||
import { computed } from "vue";
|
||||
import { Avatar, Delete, Document, Sunny, Switch } 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 Language from "./components/Language.vue";
|
||||
import SearchMenu from "./components/SearchMenu.vue";
|
||||
@@ -75,12 +97,16 @@ import { useModeStore,useAppSceneStore } from "@/stores/modules/mode";
|
||||
const userStore = useUserStore();
|
||||
const dictStore = useDictStore();
|
||||
const username = computed(() => userStore.userInfo.name);
|
||||
|
||||
const router = useRouter();
|
||||
const authStore = useAuthStore();
|
||||
const modeStore = useModeStore();
|
||||
const AppSceneStore = useAppSceneStore();
|
||||
import { useTheme } from "@/hooks/useTheme";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import {updateScene} from '@/api/system/base/index'
|
||||
|
||||
|
||||
const { changePrimary} = useTheme();
|
||||
|
||||
// 初始化 i18n
|
||||
@@ -126,7 +152,14 @@ const openDialog = (ref: string) => {
|
||||
};
|
||||
|
||||
|
||||
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();
|
||||
};
|
||||
|
||||
|
||||
//模式切换
|
||||
@@ -168,4 +201,18 @@ const changeMode = () => {
|
||||
// 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>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<Maximize v-show="maximize" />
|
||||
<Tabs v-if="tabs && showMenuFlag" />
|
||||
<el-main>
|
||||
<router-view v-slot="{ Component, route }" style="height:100%;">
|
||||
<router-view v-slot="{ Component, route }" style="height: 100%">
|
||||
<!-- {{ keepAliveName}} -->
|
||||
<!-- <transition name="slide-right" mode="out-in"> -->
|
||||
<keep-alive :include="tabsMenuList">
|
||||
@@ -17,68 +17,65 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onBeforeUnmount, provide, watch, computed } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useDebounceFn } from "@vueuse/core";
|
||||
import { useGlobalStore } from "@/stores/modules/global";
|
||||
import { useKeepAliveStore } from "@/stores/modules/keepAlive";
|
||||
import Maximize from "./components/Maximize.vue";
|
||||
import Tabs from "@/layouts/components/Tabs/index.vue";
|
||||
import Footer from "@/layouts/components/Footer/index.vue";
|
||||
import { useAuthStore } from "@/stores/modules/auth";
|
||||
import { computed, onBeforeUnmount, provide, ref, watch } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useDebounceFn } from '@vueuse/core'
|
||||
import { useGlobalStore } from '@/stores/modules/global'
|
||||
import { useKeepAliveStore } from '@/stores/modules/keepAlive'
|
||||
import Maximize from './components/Maximize.vue'
|
||||
import Tabs from '@/layouts/components/Tabs/index.vue'
|
||||
import Footer from '@/layouts/components/Footer/index.vue'
|
||||
import { useAuthStore } from '@/stores/modules/auth'
|
||||
import { useTabsStore } from '@/stores/modules/tabs'
|
||||
|
||||
const tabStore = useTabsStore()
|
||||
const globalStore = useGlobalStore();
|
||||
const globalStore = useGlobalStore()
|
||||
const tabsMenuList = computed(() => tabStore.tabsMenuList.map(item => item.name))
|
||||
const authStore = useAuthStore();
|
||||
const { maximize, isCollapse, layout, tabs, footer } = storeToRefs(globalStore);
|
||||
const keepAliveStore = useKeepAliveStore();
|
||||
const { keepAliveName } = storeToRefs(keepAliveStore);
|
||||
// console.log("🚀 ~ keepAliveName:", keepAliveName)
|
||||
const authStore = useAuthStore()
|
||||
const { maximize, isCollapse, layout, tabs, footer } = storeToRefs(globalStore)
|
||||
const keepAliveStore = useKeepAliveStore()
|
||||
const { keepAliveName } = storeToRefs(keepAliveStore)
|
||||
//是否显示导航栏
|
||||
const showMenuFlag = computed(() => authStore.showMenuFlagGet);
|
||||
const showMenuFlag = computed(() => authStore.showMenuFlagGet)
|
||||
// 注入刷新页面方法
|
||||
const isRouterShow = ref(true);
|
||||
const refreshCurrentPage = (val: boolean) => (isRouterShow.value = val);
|
||||
provide("refresh", refreshCurrentPage);
|
||||
const isRouterShow = ref(true)
|
||||
const refreshCurrentPage = (val: boolean) => (isRouterShow.value = val)
|
||||
provide('refresh', refreshCurrentPage)
|
||||
|
||||
// 监听当前页面是否最大化,动态添加 class
|
||||
watch(
|
||||
() => maximize.value,
|
||||
() => {
|
||||
const app = document.getElementById("app") as HTMLElement;
|
||||
if (maximize.value) app.classList.add("main-maximize");
|
||||
else app.classList.remove("main-maximize");
|
||||
const app = document.getElementById('app') as HTMLElement
|
||||
if (maximize.value) app.classList.add('main-maximize')
|
||||
else app.classList.remove('main-maximize')
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
)
|
||||
|
||||
// 监听布局变化,在 body 上添加相对应的 layout class
|
||||
watch(
|
||||
() => layout.value,
|
||||
() => {
|
||||
const body = document.body as HTMLElement;
|
||||
body.setAttribute("class", layout.value);
|
||||
const body = document.body as HTMLElement
|
||||
body.setAttribute('class', layout.value)
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
)
|
||||
|
||||
// 监听窗口大小变化,折叠侧边栏
|
||||
const screenWidth = ref(0);
|
||||
const screenWidth = ref(0)
|
||||
const listeningWindow = useDebounceFn(() => {
|
||||
screenWidth.value = document.body.clientWidth;
|
||||
if (!isCollapse.value && screenWidth.value < 1200)
|
||||
globalStore.setGlobalState("isCollapse", true);
|
||||
if (isCollapse.value && screenWidth.value > 1200)
|
||||
globalStore.setGlobalState("isCollapse", false);
|
||||
}, 100);
|
||||
window.addEventListener("resize", listeningWindow, false);
|
||||
screenWidth.value = document.body.clientWidth
|
||||
if (!isCollapse.value && screenWidth.value < 1200) globalStore.setGlobalState('isCollapse', true)
|
||||
if (isCollapse.value && screenWidth.value > 1200) globalStore.setGlobalState('isCollapse', false)
|
||||
}, 100)
|
||||
window.addEventListener('resize', listeningWindow, false)
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener("resize", listeningWindow);
|
||||
});
|
||||
window.removeEventListener('resize', listeningWindow)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "./index.scss";
|
||||
@use './index.scss';
|
||||
</style>
|
||||
|
||||
@@ -20,13 +20,17 @@
|
||||
</template>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onBeforeMount } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
defineProps<{ menuList: Menu.MenuOptions[] }>();
|
||||
const router = useRouter();
|
||||
const handleClickMenu = (subItem: Menu.MenuOptions) => {
|
||||
//console.log('1456----------------',subItem);
|
||||
if (subItem.meta.isLink) return window.open(subItem.meta.isLink, "_blank");
|
||||
router.push(subItem.path);
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.el-sub-menu .el-sub-menu__title:hover {
|
||||
|
||||
@@ -6,25 +6,32 @@
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="refresh">
|
||||
<el-icon><Refresh /></el-icon>{{ $t("tabs.refresh") }}
|
||||
<el-icon><Refresh /></el-icon>
|
||||
{{ $t('tabs.refresh') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click="maximize">
|
||||
<el-icon><FullScreen /></el-icon>{{ $t("tabs.maximize") }}
|
||||
<el-icon><FullScreen /></el-icon>
|
||||
{{ $t('tabs.maximize') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item divided @click="closeCurrentTab">
|
||||
<el-icon><Remove /></el-icon>{{ $t("tabs.closeCurrent") }}
|
||||
<el-icon><Remove /></el-icon>
|
||||
{{ $t('tabs.closeCurrent') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click="tabStore.closeTabsOnSide(route.fullPath, 'left')">
|
||||
<el-icon><DArrowLeft /></el-icon>{{ $t("tabs.closeLeft") }}
|
||||
<el-icon><DArrowLeft /></el-icon>
|
||||
{{ $t('tabs.closeLeft') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click="tabStore.closeTabsOnSide(route.fullPath, 'right')">
|
||||
<el-icon><DArrowRight /></el-icon>{{ $t("tabs.closeRight") }}
|
||||
<el-icon><DArrowRight /></el-icon>
|
||||
{{ $t('tabs.closeRight') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item divided @click="tabStore.closeMultipleTab(route.fullPath)">
|
||||
<el-icon><CircleClose /></el-icon>{{ $t("tabs.closeOther") }}
|
||||
<el-icon><CircleClose /></el-icon>
|
||||
{{ $t('tabs.closeOther') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click="closeAllTab">
|
||||
<el-icon><FolderDelete /></el-icon>{{ $t("tabs.closeAll") }}
|
||||
<el-icon><FolderDelete /></el-icon>
|
||||
{{ $t('tabs.closeAll') }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
@@ -32,50 +39,50 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { inject, nextTick } from "vue";
|
||||
import { HOME_URL } from "@/config";
|
||||
import { useTabsStore } from "@/stores/modules/tabs";
|
||||
import { useGlobalStore } from "@/stores/modules/global";
|
||||
import { useKeepAliveStore } from "@/stores/modules/keepAlive";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { inject, nextTick } from 'vue'
|
||||
import { HOME_URL } from '@/config'
|
||||
import { useTabsStore } from '@/stores/modules/tabs'
|
||||
import { useGlobalStore } from '@/stores/modules/global'
|
||||
import { useKeepAliveStore } from '@/stores/modules/keepAlive'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const tabStore = useTabsStore();
|
||||
const globalStore = useGlobalStore();
|
||||
const keepAliveStore = useKeepAliveStore();
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const tabStore = useTabsStore()
|
||||
const globalStore = useGlobalStore()
|
||||
const keepAliveStore = useKeepAliveStore()
|
||||
|
||||
// refresh current page
|
||||
const refreshCurrentPage: Function = inject("refresh") as Function;
|
||||
const refreshCurrentPage: Function = inject('refresh') as Function
|
||||
const refresh = () => {
|
||||
setTimeout(() => {
|
||||
keepAliveStore.removeKeepAliveName(route.name as string);
|
||||
refreshCurrentPage(false);
|
||||
keepAliveStore.removeKeepAliveName(route.name as string)
|
||||
refreshCurrentPage(false)
|
||||
nextTick(() => {
|
||||
keepAliveStore.addKeepAliveName(route.name as string);
|
||||
refreshCurrentPage(true);
|
||||
});
|
||||
}, 0);
|
||||
};
|
||||
keepAliveStore.addKeepAliveName(route.name as string)
|
||||
refreshCurrentPage(true)
|
||||
})
|
||||
}, 0)
|
||||
}
|
||||
|
||||
// maximize current page
|
||||
const maximize = () => {
|
||||
globalStore.setGlobalState("maximize", true);
|
||||
};
|
||||
globalStore.setGlobalState('maximize', true)
|
||||
}
|
||||
|
||||
// Close Current
|
||||
const closeCurrentTab = () => {
|
||||
if (route.meta.isAffix) return;
|
||||
tabStore.removeTabs(route.fullPath);
|
||||
};
|
||||
if (route.meta.isAffix) return
|
||||
tabStore.removeTabs(route.fullPath)
|
||||
}
|
||||
|
||||
// Close All
|
||||
const closeAllTab = () => {
|
||||
tabStore.closeMultipleTab();
|
||||
router.push(HOME_URL);
|
||||
};
|
||||
tabStore.closeMultipleTab()
|
||||
router.push(HOME_URL)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../index.scss";
|
||||
@use '../index.scss';
|
||||
</style>
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
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 { useGlobalStore } from '@/stores/modules/global'
|
||||
import { useTabsStore } from '@/stores/modules/tabs'
|
||||
import { useAuthStore } from '@/stores/modules/auth'
|
||||
import { TabsPaneContext, TabPaneName } from 'element-plus'
|
||||
import { TabPaneName, TabsPaneContext } from 'element-plus'
|
||||
import MoreButton from './components/MoreButton.vue'
|
||||
|
||||
const route = useRoute()
|
||||
@@ -105,18 +105,15 @@ const tabsDrop = () => {
|
||||
// Tab Click
|
||||
const tabClick = (tabItem: TabsPaneContext) => {
|
||||
const fullPath = tabItem.props.name as string
|
||||
// console.log("🚀 ~ tabClick ~ fullPath:", tabItem)
|
||||
router.push(fullPath)
|
||||
}
|
||||
|
||||
// Remove Tab
|
||||
const tabRemove = (fullPath: TabPaneName) => {
|
||||
|
||||
|
||||
tabStore.removeTabs(fullPath as string, fullPath == route.fullPath || '/machine/testScriptAdd' == route.fullPath)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './index.scss';
|
||||
@use './index.scss';
|
||||
</style>
|
||||
|
||||
@@ -7,7 +7,10 @@
|
||||
</el-divider>
|
||||
<div class="layout-box">
|
||||
<el-tooltip effect="dark" content="纵向" placement="top" :show-after="200">
|
||||
<div :class="['layout-item layout-vertical', { 'is-active': layout == 'vertical' }]" @click="setLayout('vertical')">
|
||||
<div
|
||||
:class="['layout-item layout-vertical', { 'is-active': layout == 'vertical' }]"
|
||||
@click="setLayout('vertical')"
|
||||
>
|
||||
<div class="layout-dark"></div>
|
||||
<div class="layout-container">
|
||||
<div class="layout-light"></div>
|
||||
@@ -19,7 +22,10 @@
|
||||
</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-item layout-classic', { 'is-active': layout == 'classic' }]"
|
||||
@click="setLayout('classic')"
|
||||
>
|
||||
<div class="layout-dark"></div>
|
||||
<div class="layout-container">
|
||||
<div class="layout-light"></div>
|
||||
@@ -31,7 +37,10 @@
|
||||
</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-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'">
|
||||
@@ -40,7 +49,10 @@
|
||||
</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-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>
|
||||
@@ -128,18 +140,18 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useTheme } from "@/hooks/useTheme";
|
||||
import { useGlobalStore } from "@/stores/modules/global";
|
||||
import { LayoutType } from "@/stores/interface";
|
||||
import { DEFAULT_PRIMARY } from "@/config";
|
||||
import mittBus from "@/utils/mittBus";
|
||||
import SwitchDark from "@/components/SwitchDark/index.vue";
|
||||
import { ref } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useTheme } from '@/hooks/useTheme'
|
||||
import { useGlobalStore } from '@/stores/modules/global'
|
||||
import { LayoutType } from '@/stores/interface'
|
||||
import { DEFAULT_PRIMARY } from '@/config'
|
||||
import mittBus from '@/utils/mittBus'
|
||||
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 {
|
||||
layout,
|
||||
primary,
|
||||
@@ -154,33 +166,33 @@ const {
|
||||
tabs,
|
||||
tabsIcon,
|
||||
footer
|
||||
} = storeToRefs(globalStore);
|
||||
} = storeToRefs(globalStore)
|
||||
|
||||
// 预定义主题颜色
|
||||
const colorList = [
|
||||
DEFAULT_PRIMARY,
|
||||
"#daa96e",
|
||||
"#0c819f",
|
||||
"#409eff",
|
||||
"#27ae60",
|
||||
"#ff5c93",
|
||||
"#e74c3c",
|
||||
"#fd726d",
|
||||
"#f39c12",
|
||||
"#9b59b6"
|
||||
];
|
||||
'#daa96e',
|
||||
'#0c819f',
|
||||
'#409eff',
|
||||
'#27ae60',
|
||||
'#ff5c93',
|
||||
'#e74c3c',
|
||||
'#fd726d',
|
||||
'#f39c12',
|
||||
'#9b59b6'
|
||||
]
|
||||
|
||||
// 设置布局方式
|
||||
const setLayout = (val: LayoutType) => {
|
||||
globalStore.setGlobalState("layout", val);
|
||||
setAsideTheme();
|
||||
};
|
||||
globalStore.setGlobalState('layout', val)
|
||||
setAsideTheme()
|
||||
}
|
||||
|
||||
// 打开主题设置
|
||||
const drawerVisible = ref(false);
|
||||
mittBus.on("openThemeDrawer", () => (drawerVisible.value = true));
|
||||
const drawerVisible = ref(false)
|
||||
mittBus.on('openThemeDrawer', () => (drawerVisible.value = true))
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "./index.scss";
|
||||
@use './index.scss';
|
||||
</style>
|
||||
|
||||
@@ -30,7 +30,7 @@ export interface UserState {
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
isRefreshToken: boolean;
|
||||
userInfo: { id: string, name: string };
|
||||
userInfo: { id: string, name: string,loginName:string };
|
||||
}
|
||||
|
||||
/* tabsMenuProps */
|
||||
|
||||
@@ -8,6 +8,9 @@ import {
|
||||
} from "@/utils";
|
||||
import { useRouter } from "vue-router";
|
||||
import { AUTH_STORE_KEY } from "@/stores/constant";
|
||||
import {useModeStore} from '@/stores/modules/mode'
|
||||
|
||||
|
||||
export const useAuthStore = defineStore({
|
||||
id: AUTH_STORE_KEY,
|
||||
state: (): AuthState => ({
|
||||
@@ -43,8 +46,15 @@ export const useAuthStore = defineStore({
|
||||
},
|
||||
// Get AuthMenuList
|
||||
async getAuthMenuList() {
|
||||
const { data } = await getAuthMenuListApi();
|
||||
this.authMenuList = data;
|
||||
const modeStore = useModeStore()
|
||||
|
||||
const { data: menuData } = await getAuthMenuListApi();
|
||||
// 根据不同模式过滤菜单
|
||||
const filteredMenu = modeStore.currentMode === '比对式'
|
||||
? filterMenuByExcludedNames(menuData, ['testSource', 'testScript', 'controlSource'])
|
||||
: filterMenuByExcludedNames(menuData, ['standardDevice']);
|
||||
|
||||
this.authMenuList = filteredMenu;
|
||||
|
||||
},
|
||||
// Set RouteName
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,21 +4,20 @@ import type {CheckData} from "@/api/check/interface";
|
||||
import type {Plan} from '@/api/plan/interface'
|
||||
import {useAppSceneStore} from "@/stores/modules/mode";
|
||||
|
||||
const AppSceneStore = useAppSceneStore()
|
||||
export const useCheckStore = defineStore("check", {
|
||||
id: CHECK_STORE_KEY,
|
||||
|
||||
export const useCheckStore = defineStore(CHECK_STORE_KEY, {
|
||||
state: () => ({
|
||||
devices: Array<CheckData.Device>(),
|
||||
plan: Object<Plan.ResPlan>(),
|
||||
selectTestItems: Object<CheckData.SelectTestItem>({preTest: true, timeTest: true, channelsTest: false, test: true}),
|
||||
devices: [] as CheckData.Device[],
|
||||
plan: {} as Plan.ResPlan,
|
||||
selectTestItems: {preTest: true, timeTest: false, channelsTest: false, test: true} as CheckData.SelectTestItem,
|
||||
checkType: 1, // 0:手动检测 1:自动检测
|
||||
reCheckType: 1, // 0:不合格项复检 1:全部复检
|
||||
showDetailType: 0 // 0:数据查询 1:误差体系跟换 2:正式检测
|
||||
showDetailType: 0, // 0:数据查询 1:误差体系跟换 2:正式检测
|
||||
temperature: 0,
|
||||
humidity: 0,
|
||||
chnNumList: [],//连线数据
|
||||
nodesConnectable: true,//设置是能可以连线
|
||||
}),
|
||||
|
||||
getters: {},
|
||||
|
||||
actions: {
|
||||
addDevices(device: CheckData.Device[]) {
|
||||
this.devices.push(...device);
|
||||
@@ -30,11 +29,12 @@ export const useCheckStore = defineStore("check", {
|
||||
this.devices = [];
|
||||
},
|
||||
initSelectTestItems() {
|
||||
const appSceneStore = useAppSceneStore()
|
||||
this.selectTestItems.preTest = true
|
||||
if (AppSceneStore.currentScene === '1') {
|
||||
if (appSceneStore.currentScene === '1') {
|
||||
this.selectTestItems.channelsTest = true
|
||||
} else {
|
||||
this.selectTestItems.timeTest = true
|
||||
this.selectTestItems.channelsTest = false
|
||||
}
|
||||
this.selectTestItems.test = true
|
||||
},
|
||||
@@ -49,6 +49,19 @@ export const useCheckStore = defineStore("check", {
|
||||
},
|
||||
setShowDetailType(showDetailType: number) {
|
||||
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,5 +1,5 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { GlobalState } from "@/stores/interface";
|
||||
import { type GlobalState } from "@/stores/interface";
|
||||
import { DEFAULT_PRIMARY} from "@/config";
|
||||
import piniaPersistConfig from "@/stores/helper/persist";
|
||||
import {GLOBAL_STORE_KEY} from "@/stores/constant";
|
||||
@@ -49,7 +49,6 @@ export const useGlobalStore = defineStore({
|
||||
// Set GlobalState
|
||||
setGlobalState(...args: ObjToKeyValArray<GlobalState>) {
|
||||
this.$patch({ [args[0]]: args[1] });
|
||||
console.log(DEFAULT_PRIMARY);
|
||||
}
|
||||
},
|
||||
persist: piniaPersistConfig(GLOBAL_STORE_KEY)
|
||||
|
||||
@@ -10,7 +10,7 @@ export const useUserStore = defineStore({
|
||||
refreshToken: "",
|
||||
isRefreshToken:false,
|
||||
exp: Number(0),
|
||||
userInfo: {id:"", name: "admin" },
|
||||
userInfo: {id:"", name: "" ,loginName:""},
|
||||
}),
|
||||
getters: {},
|
||||
actions: {
|
||||
|
||||
@@ -140,3 +140,9 @@ body,
|
||||
:-webkit-any(article, aside, nav, section) h1 {
|
||||
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';
|
||||
|
||||
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";
|
||||
|
||||
const mittBus = mitt();
|
||||
|
||||
export default mittBus;
|
||||
|
||||
@@ -1,185 +1,697 @@
|
||||
/**
|
||||
* 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 {
|
||||
static instance = null;
|
||||
static get Instance() {
|
||||
// ========================================================================
|
||||
// 静态属性和方法 (Static)
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* 单例实例
|
||||
*/
|
||||
private static instance: SocketService | null = null;
|
||||
|
||||
/**
|
||||
* 获取单例实例
|
||||
* @returns SocketService实例
|
||||
*/
|
||||
static get Instance(): SocketService {
|
||||
if (!this.instance) {
|
||||
this.instance = new SocketService();
|
||||
}
|
||||
return this.instance;
|
||||
}
|
||||
// 和服务端连接的socket对象
|
||||
ws = null;
|
||||
// 存储回调函数
|
||||
callBackMapping = {};
|
||||
// 标识是否连接成功
|
||||
connected = false;
|
||||
// 记录重试的次数
|
||||
sendRetryCount = 0;
|
||||
// 重新连接尝试的次数
|
||||
connectRetryCount = 0;
|
||||
work:any;
|
||||
workerBlobUrl:any;
|
||||
lastActivityTime= 0; // 上次活动时间戳
|
||||
lastResponseHeartTime = Date.now();//最后一次收到心跳回复时间
|
||||
|
||||
reconnectDelay= 5000; // 重新连接延迟,单位毫秒
|
||||
// ========================================================================
|
||||
// 实例属性 (Properties)
|
||||
// ========================================================================
|
||||
|
||||
// 定义连接服务器的方法
|
||||
connect() {
|
||||
/**
|
||||
* WebSocket连接实例
|
||||
*/
|
||||
private ws: WebSocket | null = null;
|
||||
|
||||
// 连接服务器
|
||||
if (!window.WebSocket) {
|
||||
return console.log('您的浏览器不支持WebSocket');
|
||||
/**
|
||||
* 消息回调函数映射表
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
// let token = $.cookie('123');
|
||||
// let token = '4E6EF539AAF119D82AC4C2BC84FBA21F';
|
||||
/**
|
||||
* 初始化属性
|
||||
*/
|
||||
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) {
|
||||
console.log('您的浏览器不支持WebSocket');
|
||||
return;
|
||||
}
|
||||
|
||||
// 防止重复连接
|
||||
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 = () => {
|
||||
ElMessage.success("webSocket连接服务端成功了");
|
||||
console.log('连接服务端成功了');
|
||||
this.connected = true;
|
||||
// 重置重新连接的次数
|
||||
this.connectionStatus = ConnectionStatus.CONNECTED;
|
||||
this.connectRetryCount = 0;
|
||||
this.updateLastActivityTime();
|
||||
this.startHeartbeat();
|
||||
};
|
||||
// 1.连接服务端失败
|
||||
// 2.当连接成功之后, 服务器关闭的情况
|
||||
this.ws.onclose = () => {
|
||||
|
||||
// 连接关闭事件
|
||||
this.ws.onclose = (event: CloseEvent) => {
|
||||
console.log('连接webSocket服务端关闭');
|
||||
this.connected = false;
|
||||
this.connectRetryCount++;
|
||||
this.connectionStatus = ConnectionStatus.DISCONNECTED;
|
||||
this.clearHeartbeat();
|
||||
|
||||
// 保持原有的重连逻辑(被注释掉的)
|
||||
// this.connectRetryCount++;
|
||||
/* setTimeout(() => {
|
||||
this.connect();
|
||||
}, 500 * this.connectRetryCount);*/
|
||||
|
||||
|
||||
};
|
||||
|
||||
// 连接错误事件
|
||||
this.ws.onerror = () => {
|
||||
ElMessage.error("webSocket连接异常!");
|
||||
|
||||
|
||||
this.connectionStatus = ConnectionStatus.ERROR;
|
||||
};
|
||||
|
||||
|
||||
// 得到服务端发送过来的数据
|
||||
this.ws.onmessage = (event) => {
|
||||
// console.log('🚀 ~ SocketService ~ connect ~ event:', event)
|
||||
if(event.data == 'over') {
|
||||
//心跳消息处理
|
||||
this.lastResponseHeartTime = Date.now();
|
||||
this.updateLastActivityTime(); // 收到心跳响应时更新活动时间
|
||||
}else {
|
||||
let message: { [key: string]: any };
|
||||
try {
|
||||
console.log('Received message:',event.data)
|
||||
message = JSON.parse(event.data);
|
||||
} catch (e) {
|
||||
return console.error("消息解析失败", event.data, e);
|
||||
// 消息接收事件
|
||||
this.ws.onmessage = (event: MessageEvent) => {
|
||||
this.handleMessage(event);
|
||||
};
|
||||
}
|
||||
|
||||
/* 通过接受服务端发送的type字段来回调函数 */
|
||||
/**
|
||||
* 处理接收到的消息
|
||||
* 支持心跳响应、JSON消息和普通文本消息
|
||||
* @param event WebSocket消息事件
|
||||
*/
|
||||
private handleMessage(event: MessageEvent): void {
|
||||
// console.log('Received message:', event.data);
|
||||
|
||||
// 心跳响应处理
|
||||
if (event.data === 'over') {
|
||||
console.log(`${new Date().toLocaleTimeString()} - 收到心跳响应`);
|
||||
this.lastResponseHeartTime = Date.now();
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查消息是否为空或无效
|
||||
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]) {
|
||||
this.callBackMapping[message.type](message);
|
||||
} else {
|
||||
console.log("抛弃====>")
|
||||
console.log(event.data)
|
||||
/* 丢弃或继续写你的逻辑 */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
startHeartbeat() {
|
||||
this.lastResponseHeartTime = Date.now();
|
||||
const _this = this
|
||||
_this.workerBlobUrl = window.URL.createObjectURL(new Blob(['(function(e){setInterval(function(){this.postMessage(null)},9000)})()']));
|
||||
|
||||
this.work = new Worker(_this.workerBlobUrl);
|
||||
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() {
|
||||
const _this = this
|
||||
if (_this.work) {
|
||||
_this.work.terminate();
|
||||
_this.work = null;
|
||||
}
|
||||
if (_this.workerBlobUrl) {
|
||||
window.URL.revokeObjectURL(_this.workerBlobUrl); // 释放临时的Blob URL
|
||||
_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);
|
||||
console.warn('未找到对应的消息处理器:', message.type);
|
||||
}
|
||||
} else {
|
||||
this.sendRetryCount++;
|
||||
setTimeout(() => {
|
||||
this.send(data);
|
||||
}, this.sendRetryCount * 500);
|
||||
// 非JSON格式的消息,作为普通文本处理
|
||||
console.log('收到非JSON格式消息:', event.data);
|
||||
// 可以添加文本消息的处理逻辑
|
||||
if (this.callBackMapping['text']) {
|
||||
this.callBackMapping['text']({
|
||||
type: 'text',
|
||||
data: event.data
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 断开方法
|
||||
closeWs() {
|
||||
if (this.connected) {
|
||||
this.ws.close()
|
||||
}
|
||||
console.log('执行WS关闭命令..');
|
||||
} catch (error) {
|
||||
console.error('消息解析失败:', event.data, error);
|
||||
console.error('消息类型:', typeof event.data);
|
||||
console.error('消息长度:', event.data?.length || 0);
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// 重连机制相关方法
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* 尝试重新连接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) {
|
||||
window.URL.revokeObjectURL(this.workerBlobUrl);
|
||||
this.workerBlobUrl = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<el-dialog :title="dialogTitle" :model-value="dialogVisible" @close="close" v-bind="dialogMiddle">
|
||||
<el-form :model="formContent" ref='dialogFormRef' :rules='rules' class="form-two">
|
||||
<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-item label="上级菜单" prop="pid" :label-width="100">
|
||||
<el-tree-select
|
||||
v-model="displayPid"
|
||||
@@ -14,29 +14,29 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
<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 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 v-if="!formContent.type" label="图标" prop="icon" :label-width="100">
|
||||
<IconSelect
|
||||
v-model="formContent.icon"
|
||||
:iconValue="formContent.icon"
|
||||
@update:icon-value="iconValue => formContent.icon = iconValue"
|
||||
@update:icon-value="iconValue => (formContent.icon = iconValue)"
|
||||
placeholder="选择一个图标"
|
||||
/>
|
||||
</el-form-item>
|
||||
<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 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 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 label='类型' prop='type' :label-width="100">
|
||||
<el-form-item label="类型" prop="type" :label-width="100">
|
||||
<el-select v-model="formContent.type" clearable placeholder="请选择资源类型">
|
||||
<el-option label="菜单" :value="0"></el-option>
|
||||
<el-option label="按钮" :value="1"></el-option>
|
||||
@@ -58,20 +58,21 @@
|
||||
</template>
|
||||
|
||||
<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 { ElMessage, type FormInstance, type FormItemRule } from 'element-plus'
|
||||
import { useDictStore } from '@/stores/modules/dict'
|
||||
import type { Function } from "@/api/user/interface/function"
|
||||
import {addFunction,updateFunction,getFunctionListNoButton} from '@/api/user/function/index'
|
||||
import type { Function } from '@/api/user/interface/function'
|
||||
import { addFunction, getFunctionListNoButton, updateFunction } from '@/api/user/function/index'
|
||||
import IconSelect from '@/components/SelectIcon/index.vue'
|
||||
|
||||
const value = ref()
|
||||
// 树形节点配置
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
value: 'id'
|
||||
};
|
||||
}
|
||||
const functionList = ref<Function.ResFunction[]>([])
|
||||
const dictStore = useDictStore()
|
||||
// 定义弹出组件元信息
|
||||
@@ -91,7 +92,7 @@
|
||||
sort: 100, //排序
|
||||
type: 0, //资源类型0-菜单、1-按钮、2-公共资源、3-服务间调用资源
|
||||
remark: '', //权限资源描述
|
||||
state:1,//权限资源状态
|
||||
state: 1 //权限资源状态
|
||||
})
|
||||
return { dialogVisible, titleType, formContent }
|
||||
}
|
||||
@@ -111,7 +112,7 @@ const resetFormContent = () => {
|
||||
sort: 100, //排序
|
||||
type: 0, //资源类型0-菜单、1-按钮、2-公共资源、3-服务间调用资源
|
||||
remark: '', //权限资源描述
|
||||
state:1,//权限资源状态
|
||||
state: 1 //权限资源状态
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,8 +120,6 @@ const resetFormContent = () => {
|
||||
return titleType.value === 'add' ? '新增菜单' : '编辑菜单'
|
||||
})
|
||||
|
||||
|
||||
|
||||
// 定义规则
|
||||
const formRuleRef = ref<FormInstance>()
|
||||
const rules: Ref<Record<string, Array<FormItemRule>>> = ref({
|
||||
@@ -128,17 +127,20 @@ const resetFormContent = () => {
|
||||
code: [{ required: true, trigger: 'blur', message: '编码必填!' }]
|
||||
})
|
||||
|
||||
watch(() => formContent.value.type, (newVal) => {
|
||||
watch(
|
||||
() => formContent.value.type,
|
||||
newVal => {
|
||||
if (newVal === 1) {
|
||||
// 选择按钮时,路由地址和组件地址无需校验
|
||||
rules.value.path = [];
|
||||
rules.value.component = [];
|
||||
rules.value.path = []
|
||||
rules.value.component = []
|
||||
} else {
|
||||
// 其他情况下,路由地址和组件地址需要校验
|
||||
rules.value.path = [{ required: true, trigger: 'blur', message: '路由地址必填!' }];
|
||||
rules.value.component = [{ required: true, trigger: 'blur', message: '组件地址必填!' }];
|
||||
rules.value.path = [{ required: true, trigger: 'blur', message: '路由地址必填!' }]
|
||||
rules.value.component = [{ required: true, trigger: 'blur', message: '组件地址必填!' }]
|
||||
}
|
||||
});
|
||||
}
|
||||
)
|
||||
|
||||
// 关闭弹窗
|
||||
const close = () => {
|
||||
@@ -149,40 +151,40 @@ const close = () => {
|
||||
dialogFormRef.value?.resetFields()
|
||||
}
|
||||
|
||||
|
||||
// 计算属性,用于控制显示的 pid
|
||||
const displayPid = computed({
|
||||
get: () => {
|
||||
return formContent.value.pid === '0' ? '' : formContent.value.pid;
|
||||
return formContent.value.pid === '0' ? '' : formContent.value.pid
|
||||
},
|
||||
set: (value) => {
|
||||
formContent.value.pid = value;
|
||||
set: value => {
|
||||
formContent.value.pid = value
|
||||
}
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
// 保存数据
|
||||
const save = () => {
|
||||
try {
|
||||
dialogFormRef.value?.validate(async (valid: boolean) => {
|
||||
|
||||
if (formContent.value.pid === undefined || formContent.value.pid === null || formContent.value.pid === '') {
|
||||
formContent.value.pid = '0';
|
||||
formContent.value.pid = '0'
|
||||
}
|
||||
if (formContent.value.pids === undefined || formContent.value.pids === null || formContent.value.pids === '') {
|
||||
formContent.value.pids = '0';
|
||||
if (
|
||||
formContent.value.pids === undefined ||
|
||||
formContent.value.pids === null ||
|
||||
formContent.value.pids === ''
|
||||
) {
|
||||
formContent.value.pids = '0'
|
||||
}
|
||||
if (valid) {
|
||||
if (formContent.value.id) {
|
||||
await updateFunction(formContent.value);
|
||||
await updateFunction(formContent.value)
|
||||
} else {
|
||||
await addFunction(formContent.value);
|
||||
await addFunction(formContent.value)
|
||||
}
|
||||
ElMessage.success({ message: `${dialogTitle.value}成功!` })
|
||||
close()
|
||||
// 刷新表格
|
||||
await props.refreshTable!()
|
||||
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
@@ -190,7 +192,6 @@ const displayPid = computed({
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 打开弹窗,可能是新增,也可能是编辑
|
||||
const open = async (sign: string, data: Function.ResFunction) => {
|
||||
// 重置表单
|
||||
@@ -199,11 +200,11 @@ const displayPid = computed({
|
||||
functionList.value = response.data as unknown as Function.ResFunction[]
|
||||
titleType.value = sign
|
||||
dialogVisible.value = true
|
||||
rules.value.path = [{ required: true, trigger: 'blur', message: '路由地址必填!' }];
|
||||
rules.value.component = [{ required: true, trigger: 'blur', message: '组件地址必填!' }];
|
||||
rules.value.path = [{ required: true, trigger: 'blur', message: '路由地址必填!' }]
|
||||
rules.value.component = [{ required: true, trigger: 'blur', message: '组件地址必填!' }]
|
||||
|
||||
if (formContent.value.pid === '0') {
|
||||
formContent.value.pid = '';
|
||||
formContent.value.pid = ''
|
||||
}
|
||||
|
||||
if (data.id) {
|
||||
@@ -216,7 +217,6 @@ const displayPid = computed({
|
||||
// 对外映射
|
||||
defineExpose({ open })
|
||||
const props = defineProps<{
|
||||
refreshTable: (() => Promise<void>) | undefined;
|
||||
refreshTable: (() => Promise<void>) | undefined
|
||||
}>()
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<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>
|
||||
|
||||
<el-form :model="formContent"
|
||||
@@ -8,11 +8,11 @@
|
||||
:rules='rules'
|
||||
>
|
||||
<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 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 label='类型' prop='type' :label-width="100">
|
||||
<el-select v-model="formContent.type" clearable placeholder="请选择类型" :disabled="rootIsEdit">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<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>
|
||||
<el-form :model="formContent" ref='dialogFormRef'>
|
||||
<el-tree
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
</template>
|
||||
<!-- 表格操作 -->
|
||||
<template #operation='scope'>
|
||||
<el-button v-auth.role="'edit'" type='primary' link :icon='EditPen' @click="openDrawer('edit', scope.row)">编辑</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="'SetPermissions'" type='primary' link :icon='Share' @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'" 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)" :disabled="scope.row.code == 'root'">设置权限</el-button>
|
||||
</template>
|
||||
|
||||
</ProTable>
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
<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>
|
||||
<el-form :model="formContent"
|
||||
ref='dialogFormRef'
|
||||
:rules='rules'
|
||||
>
|
||||
<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 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 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>
|
||||
</div>
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
<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>
|
||||
<el-form :model="formContent"
|
||||
ref='dialogFormRef'
|
||||
:rules='rules'
|
||||
>
|
||||
<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 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 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 label='角色' :label-width='100' prop='roles'>
|
||||
<el-select v-model="formContent.roleIds" multiple placeholder="请选择角色">
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
</template>
|
||||
<!-- 表格操作 -->
|
||||
<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="'delete'" type='primary' link :icon='Delete' @click='handleDelete(scope.row)'>删除</el-button>
|
||||
<el-button v-auth.user="'editPassWord'" type='primary' link :icon='Delete' @click='EditPassWord(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)' :disabled="scope.row.loginName == 'root'">删除</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>
|
||||
|
||||
</ProTable>
|
||||
@@ -64,8 +64,8 @@
|
||||
// 默认不做操作就直接在 ProTable 组件上绑定 :requestApi="getUserList"
|
||||
const getTableList = (params: any) => {
|
||||
let newParams = JSON.parse(JSON.stringify(params))
|
||||
newParams.searchEndTime = endDate.value
|
||||
newParams.searchBeginTime = startDate.value
|
||||
// newParams.searchEndTime = endDate.value
|
||||
// newParams.searchBeginTime = startDate.value
|
||||
return getUserList(newParams)
|
||||
}
|
||||
|
||||
@@ -91,6 +91,7 @@
|
||||
render: (scope) => {
|
||||
const roleNames = scope.row.roleNames;
|
||||
const roleArray = Array.isArray(roleNames) ? roleNames : [roleNames];
|
||||
|
||||
if (roleArray.length > 1) {
|
||||
return roleArray.join(', ');
|
||||
}
|
||||
@@ -111,19 +112,19 @@
|
||||
prop: 'loginTime',
|
||||
label: '最后一次登录时间',
|
||||
minWidth: 180,
|
||||
search: {
|
||||
render: () => {
|
||||
return (
|
||||
<div class='flx-flex-start'>
|
||||
<TimeControl
|
||||
include={['日', '周', '月', '自定义']}
|
||||
default={'月'}
|
||||
onUpdate-dates={handleDateChange}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
},
|
||||
// search: {
|
||||
// render: () => {
|
||||
// return (
|
||||
// <div class='flx-flex-start'>
|
||||
// <TimeControl
|
||||
// include={['日', '周', '月', '自定义']}
|
||||
// default={'月'}
|
||||
// onUpdate-dates={handleDateChange}
|
||||
// />
|
||||
// </div>
|
||||
// )
|
||||
// },
|
||||
// },
|
||||
},
|
||||
{
|
||||
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,977 +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: #91cc75">{{ 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 {Failed} from '@element-plus/icons-vue'
|
||||
import {type Ref, ref, toRef, watch} from 'vue'
|
||||
import {dialogBig} from '@/utils/elementBind'
|
||||
import {ElMessageBox} 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 {useUserStore} from "@/stores/modules/user";
|
||||
|
||||
|
||||
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 userStore = useUserStore()
|
||||
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 'yjc_xyjy':
|
||||
switch (newValue.operateCode) {
|
||||
case 'VERIFY_MAPPING$01':
|
||||
if (newValue.code == 10200) {
|
||||
let data = JSON.parse(newValue.data)
|
||||
ElMessageBox.alert(`脚本与icd检验失败! icd名称:${data['icdType']} -> 校验项:${data['dataType']}`, '初始化失败', {
|
||||
confirmButtonText: '确定',
|
||||
type: 'error',
|
||||
})
|
||||
TableInit();
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'Coefficient_Check':
|
||||
console.log("Coefficient_Checkactive", active.value);
|
||||
switch (newValue.operateCode) {
|
||||
case 'big_start'://大电压,电流下装
|
||||
big_V_Download.value = 'Ua=Ub=Uc=' + newValue.data.devVolt + 'V';
|
||||
big_I_Download.value = 'Ia=Ib=Ic=' + newValue.data.devCurr + 'A';
|
||||
break;
|
||||
case 'big_end'://大电压,电流下装
|
||||
active.value++;
|
||||
tableLoading('small', '系数下装')
|
||||
break;
|
||||
}
|
||||
switch (newValue.operateCode) {
|
||||
case 'small_start'://小电压,电流下装
|
||||
small_V_Download.value = 'Ua=Ub=Uc=' + newValue.data.devVolt + 'V';
|
||||
small_I_Download.value = 'Ia=Ib=Ic=' + newValue.data.devCurr + 'A';
|
||||
break;
|
||||
case 'small_end'://小电压,电流下装
|
||||
active.value++;
|
||||
tableLoading('big', '系数校准')
|
||||
break;
|
||||
}
|
||||
switch (newValue.operateCode) {
|
||||
case 'big_comp_start'://大电压,电流校准
|
||||
big_V_Adjust.value = 'Ua=Ub=Uc=' + newValue.data.devVolt + 'V';
|
||||
big_I_Adjust.value = 'Ia=Ib=Ic=' + newValue.data.devCurr + 'A';
|
||||
break;
|
||||
case 'big_comp_end'://大电压,电流校准
|
||||
active.value++;
|
||||
tableLoading('small', '系数校准')
|
||||
break;
|
||||
}
|
||||
switch (newValue.operateCode) {
|
||||
case 'small_comp_start'://小电压,电流校准
|
||||
small_V_Adjust.value = 'Ua=Ub=Uc=' + newValue.data.devVolt + 'V';
|
||||
small_I_Adjust.value = 'Ia=Ib=Ic=' + newValue.data.devCurr + 'A';
|
||||
break;
|
||||
case 'small_comp_end'://小电压,电流校准
|
||||
active.value++;
|
||||
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 = () => {
|
||||
|
||||
//dataSocket.socketServe.closeWs()
|
||||
// 清空 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‘为正式检测
|
||||
userId: userStore.userInfo.id
|
||||
})
|
||||
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,11 +1,13 @@
|
||||
<template>
|
||||
<div class="table-container table-main">
|
||||
<el-table :data="tableData"
|
||||
<el-table
|
||||
:data="tableData"
|
||||
:header-cell-style="{ textAlign: 'center' }"
|
||||
:cell-style="{ textAlign: 'center' }"
|
||||
style="width: 100%"
|
||||
max-height="400px"
|
||||
:span-method="objectSpanMethod">
|
||||
:span-method="objectSpanMethod"
|
||||
>
|
||||
<el-table-column prop="monitorNum" label="监测点序号" width="80" />
|
||||
<el-table-column prop="desc" label="描述" width="90" />
|
||||
<el-table-column label="电压通道">
|
||||
@@ -29,7 +31,11 @@
|
||||
placement="bottom-start"
|
||||
>
|
||||
<template #content>
|
||||
误差范围: {{ (-0.001 * props.curV).toFixed(4) + 'V~' + (0.001 * props.curV).toFixed(4)+ 'V'}}<br/>
|
||||
误差范围:
|
||||
{{
|
||||
(-0.001 * props.curV).toFixed(4) + 'V~' + (0.001 * props.curV).toFixed(4) + 'V'
|
||||
}}
|
||||
<br />
|
||||
误差值:{{ scope.row.aV + 'V' }}
|
||||
</template>
|
||||
<el-tag type="danger" class="tooltip-content">
|
||||
@@ -43,7 +49,11 @@
|
||||
placement="bottom-start"
|
||||
>
|
||||
<template #content>
|
||||
误差范围: {{ (-0.001 * props.curV).toFixed(4) + 'V~' + (0.001 * props.curV).toFixed(4)+ 'V'}}<br/>
|
||||
误差范围:
|
||||
{{
|
||||
(-0.001 * props.curV).toFixed(4) + 'V~' + (0.001 * props.curV).toFixed(4) + 'V'
|
||||
}}
|
||||
<br />
|
||||
误差值:{{ scope.row.aV + 'V' }}
|
||||
</template>
|
||||
<el-tag type="success" class="tooltip-content">{{ scope.row.aVuXi }}</el-tag>
|
||||
@@ -80,7 +90,11 @@
|
||||
placement="bottom-start"
|
||||
>
|
||||
<template #content>
|
||||
误差范围: {{ (-0.001 * props.curV).toFixed(4) + 'V~' + (0.001 * props.curV).toFixed(4)+ 'V'}}<br/>
|
||||
误差范围:
|
||||
{{
|
||||
(-0.001 * props.curV).toFixed(4) + 'V~' + (0.001 * props.curV).toFixed(4) + 'V'
|
||||
}}
|
||||
<br />
|
||||
误差值:{{ scope.row.bV + 'V' }}
|
||||
</template>
|
||||
<el-tag type="danger" class="tooltip-content">
|
||||
@@ -94,7 +108,11 @@
|
||||
placement="bottom-start"
|
||||
>
|
||||
<template #content>
|
||||
误差范围: {{ (-0.001 * props.curV).toFixed(4) + 'V~' + (0.001 * props.curV).toFixed(4)+ 'V'}}<br/>
|
||||
误差范围:
|
||||
{{
|
||||
(-0.001 * props.curV).toFixed(4) + 'V~' + (0.001 * props.curV).toFixed(4) + 'V'
|
||||
}}
|
||||
<br />
|
||||
误差值:{{ scope.row.bV + 'V' }}
|
||||
</template>
|
||||
<el-tag type="success" class="tooltip-content">{{ scope.row.bVuXi }}</el-tag>
|
||||
@@ -125,7 +143,11 @@
|
||||
placement="bottom-start"
|
||||
>
|
||||
<template #content>
|
||||
误差范围: {{ (-0.001 * props.curV).toFixed(4) + 'V~' + (0.001 * props.curV).toFixed(4)+ 'V'}}<br/>
|
||||
误差范围:
|
||||
{{
|
||||
(-0.001 * props.curV).toFixed(4) + 'V~' + (0.001 * props.curV).toFixed(4) + 'V'
|
||||
}}
|
||||
<br />
|
||||
误差值:{{ scope.row.cV + 'V' }}
|
||||
</template>
|
||||
<el-tag type="danger" class="tooltip-content">
|
||||
@@ -139,7 +161,11 @@
|
||||
placement="bottom-start"
|
||||
>
|
||||
<template #content>
|
||||
误差范围: {{ (-0.001 * props.curV).toFixed(4) + 'V~' + (0.001 * props.curV).toFixed(4) + 'V'}}<br/>
|
||||
误差范围:
|
||||
{{
|
||||
(-0.001 * props.curV).toFixed(4) + 'V~' + (0.001 * props.curV).toFixed(4) + 'V'
|
||||
}}
|
||||
<br />
|
||||
误差值:{{ scope.row.cV + 'V' }}
|
||||
</template>
|
||||
<el-tag type="success" class="tooltip-content">{{ scope.row.cVuXi }}</el-tag>
|
||||
@@ -172,7 +198,8 @@
|
||||
placement="bottom-start"
|
||||
>
|
||||
<template #content>
|
||||
误差范围: {{-0.5 + "%~" +0.5 + "%" }}<br/>
|
||||
误差范围: {{ -0.5 + '%~' + 0.5 + '%' }}
|
||||
<br />
|
||||
误差值:{{ scope.row.aI + '%' }}
|
||||
</template>
|
||||
<el-tag type="danger" class="tooltip-content">
|
||||
@@ -186,7 +213,8 @@
|
||||
placement="bottom-start"
|
||||
>
|
||||
<template #content>
|
||||
误差范围: {{ -0.5 + "%~" +0.5 + "%" }}<br/>
|
||||
误差范围: {{ -0.5 + '%~' + 0.5 + '%' }}
|
||||
<br />
|
||||
误差值:{{ scope.row.aI + '%' }}
|
||||
</template>
|
||||
<el-tag type="success" class="tooltip-content">{{ scope.row.aIeXi }}</el-tag>
|
||||
@@ -217,7 +245,8 @@
|
||||
placement="bottom-start"
|
||||
>
|
||||
<template #content>
|
||||
误差范围: {{ -0.5 + "%~" +0.5 + "%" }}<br/>
|
||||
误差范围: {{ -0.5 + '%~' + 0.5 + '%' }}
|
||||
<br />
|
||||
误差值:{{ scope.row.bI + '%' }}
|
||||
</template>
|
||||
<el-tag type="danger" class="tooltip-content">
|
||||
@@ -231,7 +260,8 @@
|
||||
placement="bottom-start"
|
||||
>
|
||||
<template #content>
|
||||
误差范围: {{ -0.5 + "%~" +0.5 + "%" }}<br/>
|
||||
误差范围: {{ -0.5 + '%~' + 0.5 + '%' }}
|
||||
<br />
|
||||
误差值:{{ scope.row.bI + '%' }}
|
||||
</template>
|
||||
<el-tag type="success" class="tooltip-content">{{ scope.row.bIeXi }}</el-tag>
|
||||
@@ -262,7 +292,8 @@
|
||||
placement="bottom-start"
|
||||
>
|
||||
<template #content>
|
||||
误差范围: {{ -0.5 + "%~" +0.5 + "%" }}<br/>
|
||||
误差范围: {{ -0.5 + '%~' + 0.5 + '%' }}
|
||||
<br />
|
||||
误差值:{{ scope.row.cI + '%' }}
|
||||
</template>
|
||||
<el-tag type="danger" class="tooltip-content">
|
||||
@@ -276,7 +307,8 @@
|
||||
placement="bottom-start"
|
||||
>
|
||||
<template #content>
|
||||
误差范围: {{ -0.5 + "%~" +0.5 + "%" }}<br/>
|
||||
误差范围: {{ -0.5 + '%~' + 0.5 + '%' }}
|
||||
<br />
|
||||
误差值:{{ scope.row.cI + '%' }}
|
||||
</template>
|
||||
<el-tag type="success" class="tooltip-content">{{ scope.row.cIeXi }}</el-tag>
|
||||
@@ -293,56 +325,62 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="ErrorSystemDialog">
|
||||
import { defineProps, type PropType } from 'vue';
|
||||
import { ElIcon, ElLoading, ElTag } from 'element-plus';
|
||||
import type { ChannelsTest } from '@/api/home/interface/channelsTest';
|
||||
import { number } from 'echarts';
|
||||
import { ElIcon, ElTag } from 'element-plus'
|
||||
import type { ChannelsTest } from '@/api/home/interface/channelsTest'
|
||||
|
||||
interface Props {
|
||||
tableData: ChannelsTest.CoefficientVO[];
|
||||
big_V_loading: boolean;
|
||||
curV: number;
|
||||
tableData: ChannelsTest.CoefficientVO[]
|
||||
big_V_loading: boolean
|
||||
curV: number
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
tableData: () => [],
|
||||
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 (rowIndex % 4 === 0) {
|
||||
return {
|
||||
rowspan: 4,
|
||||
colspan: 1,
|
||||
};
|
||||
colspan: 1
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
rowspan: 0,
|
||||
colspan: 0,
|
||||
};
|
||||
colspan: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
if (columnIndex === 1) {
|
||||
if (rowIndex % 2 === 0) {
|
||||
return {
|
||||
rowspan: 2,
|
||||
colspan: 1,
|
||||
};
|
||||
colspan: 1
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
rowspan: 0,
|
||||
colspan: 0,
|
||||
};
|
||||
colspan: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.form-grid {
|
||||
display: flex;
|
||||
flex-direction: row; /* 横向排列 */
|
||||
@@ -364,7 +402,8 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
margin-bottom: 20px; /* 添加底部边距 */
|
||||
}
|
||||
|
||||
.el-table th, .el-table td {
|
||||
.el-table th,
|
||||
.el-table td {
|
||||
text-align: center; /* 所有单元格文字居中 */
|
||||
}
|
||||
|
||||
@@ -391,7 +430,6 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
@keyframes rotate {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
|
||||
@@ -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
@@ -6,17 +6,11 @@
|
||||
style="width: 100%;">
|
||||
<el-table-column type="index" label="序号" width="70" fixed="left"/>
|
||||
<el-table-column prop="time" label="数据时间"/>
|
||||
<template v-if="!isThreePhase && phaseT === 0">
|
||||
<el-table-column prop="dataA" :label="'A相'+(unit==''?'':'('+unit+')')"/>
|
||||
<el-table-column prop="dataB" :label="'B相'+(unit==''?'':'('+unit+')')"/>
|
||||
<el-table-column prop="dataC" :label="'C相'+(unit==''?'':'('+unit+')')"/>
|
||||
</template>
|
||||
<template v-if="!isThreePhase && phaseT === 1">
|
||||
<el-table-column prop="dataT" :label="tableHeader+(unit==''?'':'('+unit+')')"/>
|
||||
</template>
|
||||
<template v-if="isThreePhase">
|
||||
<el-table-column prop="dataB" :label="'负序不平衡度'+(unit==''?'':'('+unit+')')"/>
|
||||
</template>
|
||||
<el-table-column v-if="!isThreePhase && phaseA==1" prop="dataA" :label="'A相'+(unit==''?'':'('+unit+')')"/>
|
||||
<el-table-column v-if="!isThreePhase && phaseB==1" prop="dataB" :label="'B相'+(unit==''?'':'('+unit+')')"/>
|
||||
<el-table-column v-if="!isThreePhase && phaseC==1" prop="dataC" :label="'C相'+(unit==''?'':'('+unit+')')"/>
|
||||
<el-table-column v-if="!isThreePhase && phaseT === 1" prop="dataT" :label="tableHeader+(unit==''?'':'('+unit+')')"/>
|
||||
<el-table-column v-if="isThreePhase" prop="dataB" :label="'负序不平衡度'+(unit==''?'':'('+unit+')')"/>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
@@ -37,6 +31,16 @@ const unit = computed(() => {
|
||||
return tableData.length > 0 ? tableData[0].unit : '';
|
||||
})
|
||||
|
||||
const phaseA = computed(() => {
|
||||
return tableData[0].dataA == '/' ? 0 : 1
|
||||
})
|
||||
const phaseB = computed(() => {
|
||||
return tableData[0].dataB == '/' ? 0 : 1
|
||||
})
|
||||
const phaseC = computed(() => {
|
||||
return tableData[0].dataC == '/' ? 0 : 1
|
||||
})
|
||||
|
||||
const phaseT = computed(() => {
|
||||
return tableData[0].dataT == '/' ? 0 : 1
|
||||
})
|
||||
|
||||
@@ -1,22 +1,26 @@
|
||||
<template>
|
||||
|
||||
<div class="table-main">
|
||||
<el-table v-if="tableData.length > 0" :data="tableData" height="357px" :header-cell-style="{ textAlign: 'center' } "
|
||||
:cell-style="{ textAlign: 'center' }">
|
||||
<el-table
|
||||
v-if="tableData.length > 0"
|
||||
:data="tableData"
|
||||
height="357px"
|
||||
:header-cell-style="{ textAlign: 'center' }"
|
||||
:cell-style="{ textAlign: 'center' }"
|
||||
>
|
||||
<!-- <el-table-column prop="chnNum" label="通道号" width="80">-->
|
||||
<!-- <template #default="{row}">-->
|
||||
<!-- {{ '通道' + row.chnNum }}-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<template v-if="phaseT === 0">
|
||||
<el-table-column label="A相">
|
||||
<el-table-column prop="stdA" width="130" :label="'标准值'+(outerUnit==''?'':'('+outerUnit+')')"/>
|
||||
<el-table-column prop="dataA" width="130" :label="'被检值'+(outerUnit==''?'':'('+outerUnit+')')"/>
|
||||
<el-table-column label="A相" v-if="phaseA === 1">
|
||||
<el-table-column prop="stdA" :label="'标准值' + (outerUnit == '' ? '' : '(' + outerUnit + ')')" />
|
||||
<el-table-column prop="dataA" :label="'被检值' + (outerUnit == '' ? '' : '(' + outerUnit + ')')" />
|
||||
<el-table-column prop="isDataA" label="检测结果">
|
||||
<template #default="scope">
|
||||
<el-tooltip effect="dark" placement="bottom">
|
||||
<template #content>
|
||||
误差范围:{{ scope.row.maxErrorA }} <br/>
|
||||
误差范围:{{ scope.row.maxErrorA }}
|
||||
<br />
|
||||
误差值:{{ scope.row.errorA }} {{ scope.row.errorA !== '/' ? innerUnitA : '' }}
|
||||
</template>
|
||||
<el-tag type="success" v-if="scope.row.isDataA === 1">符合</el-tag>
|
||||
@@ -27,14 +31,15 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
<el-table-column label="B相">
|
||||
<el-table-column prop="stdB" width="130" :label="'标准值'+(outerUnit==''?'':'('+outerUnit+')')"/>
|
||||
<el-table-column prop="dataB" width="130" :label="'被检值'+(outerUnit==''?'':'('+outerUnit+')')"/>
|
||||
<el-table-column label="B相" v-if="phaseB === 1">
|
||||
<el-table-column prop="stdB" :label="'标准值' + (outerUnit == '' ? '' : '(' + outerUnit + ')')" />
|
||||
<el-table-column prop="dataB" :label="'被检值' + (outerUnit == '' ? '' : '(' + outerUnit + ')')" />
|
||||
<el-table-column prop="isDataB" label="检测结果">
|
||||
<template #default="scope">
|
||||
<el-tooltip effect="dark" placement="bottom">
|
||||
<template #content>
|
||||
误差范围:{{ scope.row.maxErrorB }}<br/>
|
||||
误差范围:{{ scope.row.maxErrorB }}
|
||||
<br />
|
||||
误差值:{{ scope.row.errorB }} {{ scope.row.errorB !== '/' ? innerUnitB : '' }}
|
||||
</template>
|
||||
<el-tag type="success" v-if="scope.row.isDataB === 1">符合</el-tag>
|
||||
@@ -45,14 +50,15 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
<el-table-column label="C相">
|
||||
<el-table-column prop="stdC" width="130" :label="'标准值'+(outerUnit==''?'':'('+outerUnit+')')"/>
|
||||
<el-table-column prop="dataC" width="130" :label="'被检值'+(outerUnit==''?'':'('+outerUnit+')')"/>
|
||||
<el-table-column label="C相" v-if="phaseC === 1">
|
||||
<el-table-column prop="stdC" :label="'标准值' + (outerUnit == '' ? '' : '(' + outerUnit + ')')" />
|
||||
<el-table-column prop="dataC" :label="'被检值' + (outerUnit == '' ? '' : '(' + outerUnit + ')')" />
|
||||
<el-table-column prop="isDataC" label="检测结果">
|
||||
<template #default="scope">
|
||||
<el-tooltip effect="dark" placement="bottom">
|
||||
<template #content>
|
||||
误差范围: {{ scope.row.maxErrorC }}<br/>
|
||||
误差范围: {{ scope.row.maxErrorC }}
|
||||
<br />
|
||||
误差值:{{ scope.row.errorC }} {{ scope.row.errorC !== '/' ? innerUnitC : '' }}
|
||||
</template>
|
||||
<el-tag type="success" v-if="scope.row.isDataC === 1">符合</el-tag>
|
||||
@@ -63,17 +69,16 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
</template>
|
||||
|
||||
<template v-if="phaseT === 1">
|
||||
<el-table-column :label="tableHeader">
|
||||
<el-table-column :label="tableHeader" v-if="phaseT === 1">
|
||||
<el-table-column prop="stdT" :label="'标准值' + (outerUnit == '' ? '' : '(' + outerUnit + ')')" />
|
||||
<el-table-column prop="dataT" :label="'被检值' + (outerUnit == '' ? '' : '(' + outerUnit + ')')" />
|
||||
<el-table-column prop="isDataT" label="检测结果">
|
||||
<template #default="scope">
|
||||
<el-tooltip effect="dark" placement="bottom">
|
||||
<template #content>
|
||||
误差范围: {{ scope.row.maxErrorT }}<br/>
|
||||
误差范围: {{ scope.row.maxErrorT }}
|
||||
<br />
|
||||
误差值:{{ scope.row.errorT }} {{ scope.row.errorT !== '/' ? innerUnitT : '' }}
|
||||
</template>
|
||||
<el-tag type="success" v-if="scope.row.isDataT === 1">符合</el-tag>
|
||||
@@ -84,56 +89,54 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column prop="maxError" label="误差范围"/>-->
|
||||
<!-- <el-table-column prop="result" label="检测结果">-->
|
||||
<!-- <template #default="scope">-->
|
||||
<!-- <span v-if="scope.row.result === 1">符合</span>-->
|
||||
<!-- <el-tag type="danger" v-if="scope.row.result === 2">不符合</el-tag>-->
|
||||
<!-- <span v-if="scope.row.result === 4">/</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
</template>
|
||||
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script lang="tsx" setup>
|
||||
import {defineProps} from 'vue';
|
||||
import {CheckData} from "@/api/check/interface";
|
||||
import { computed } from 'vue'
|
||||
import { CheckData } from '@/api/check/interface'
|
||||
|
||||
const { tableData, currentScriptTypeName } = defineProps<{
|
||||
tableData: CheckData.CheckResult[],
|
||||
tableData: CheckData.CheckResult[]
|
||||
currentScriptTypeName: string
|
||||
}>();
|
||||
|
||||
}>()
|
||||
|
||||
const outerUnit = computed(() => {
|
||||
return tableData.length > 0 ? tableData[0].unit : '';
|
||||
return tableData.length > 0 ? tableData[0].unit : ''
|
||||
})
|
||||
|
||||
const innerUnitA = computed(() => {
|
||||
return tableData.length > 0 ? tableData[0].unitA : '';
|
||||
return tableData.length > 0 ? tableData[0].unitA : ''
|
||||
})
|
||||
const innerUnitB = computed(() => {
|
||||
return tableData.length > 0 ? tableData[0].unitB : '';
|
||||
return tableData.length > 0 ? tableData[0].unitB : ''
|
||||
})
|
||||
const innerUnitC = computed(() => {
|
||||
return tableData.length > 0 ? tableData[0].unitC : '';
|
||||
return tableData.length > 0 ? tableData[0].unitC : ''
|
||||
})
|
||||
const innerUnitT = computed(() => {
|
||||
return tableData.length > 0 ? tableData[0].unitT : '';
|
||||
return tableData.length > 0 ? tableData[0].unitT : ''
|
||||
})
|
||||
|
||||
const phaseA = computed(() => {
|
||||
return tableData.length <= 0 || tableData[0].dataA == null || tableData[0].dataA == '/' ? 0 : 1
|
||||
})
|
||||
const phaseB = computed(() => {
|
||||
return tableData.length <= 0 || tableData[0].dataB == null || tableData[0].dataB == '/' ? 0 : 1
|
||||
})
|
||||
const phaseC = computed(() => {
|
||||
return tableData.length <= 0 || tableData[0].dataC == null || tableData[0].dataC == '/' ? 0 : 1
|
||||
})
|
||||
|
||||
const phaseT = computed(() => {
|
||||
return tableData[0].dataT == null || tableData[0].dataT == undefined ? 0 : 1
|
||||
return tableData.length <= 0 || tableData[0].dataT == null || tableData[0].dataT == '/' ? 0 : 1
|
||||
})
|
||||
|
||||
const tableHeader = computed(() => {
|
||||
if (phaseT.value === 1) {
|
||||
let index = currentScriptTypeName.indexOf('=');
|
||||
return currentScriptTypeName.substring(0, index);
|
||||
let index = currentScriptTypeName.indexOf('=')
|
||||
return currentScriptTypeName.substring(0, index)
|
||||
}
|
||||
return currentScriptTypeName
|
||||
})
|
||||
@@ -146,11 +149,9 @@ const tableHeader = computed(() => {
|
||||
// }
|
||||
// return result;
|
||||
// })
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.form-grid {
|
||||
display: flex;
|
||||
flex-direction: row; /* 横向排列 */
|
||||
@@ -176,8 +177,8 @@ const tableHeader = computed(() => {
|
||||
margin-bottom: 20px; /* 添加底部边距 */
|
||||
}
|
||||
|
||||
.el-table th, .el-table td {
|
||||
.el-table th,
|
||||
.el-table td {
|
||||
text-align: center; /* 所有单元格文字居中 */
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,31 +1,50 @@
|
||||
<template>
|
||||
<el-dialog :append-to-body="appendToBody" class="dialog" title="数据查询" :model-value='visible' @close="close"
|
||||
v-bind="dialogBig" :draggable="false" width="1400px">
|
||||
<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 class="data-check-head">
|
||||
<el-form :model='formContent' label-width="auto" class="form-three ">
|
||||
<el-form :model="formContent" label-width="auto" class="form-three">
|
||||
<el-form-item label="检测脚本">
|
||||
<el-input v-model='formContent.scriptName' :disabled="true"/>
|
||||
<el-input v-model="formContent.scriptName" :disabled="true" />
|
||||
</el-form-item>
|
||||
<el-form-item label="误差体系">
|
||||
<el-select :disabled="checkStore.showDetailType===2 || checkStore.showDetailType===0" v-model="formContent.errorSysId" placeholder="请选择误差体系" autocomplete="off"
|
||||
@change="handleErrorSysChange">
|
||||
<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"
|
||||
v-for="option in pqErrorList"
|
||||
:key="option.id"
|
||||
:label="option.name"
|
||||
:value="option.id"/>
|
||||
:value="option.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="数据原则">
|
||||
<el-input v-model='formContent.dataRule' :disabled="true"/>
|
||||
<el-input v-model="formContent.dataRule" :disabled="true" />
|
||||
</el-form-item>
|
||||
<el-form-item label="设备名称">
|
||||
<el-input v-model='formContent.deviceName' :disabled="true"/>
|
||||
<el-input v-model="formContent.deviceName" :disabled="true" />
|
||||
</el-form-item>
|
||||
<el-form-item label='通道号'>
|
||||
<el-form-item label="通道号">
|
||||
<el-select v-model="formContent.chnNum">
|
||||
<el-option v-for="item in chnList" :key="item.value" :label="item.label" :value="item.value"/>
|
||||
<el-option
|
||||
v-for="item in chnList"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="checkStore.showDetailType === 1">
|
||||
@@ -39,24 +58,31 @@
|
||||
|
||||
<div class="data-check-body">
|
||||
<div class="content-left-tree">
|
||||
<div style="width: 99%;">
|
||||
<el-input
|
||||
placeholder='请输入测试项名称'
|
||||
clearable
|
||||
v-model='searchValue'
|
||||
></el-input>
|
||||
<div style="width: 99%">
|
||||
<el-input placeholder="请输入测试项名称" clearable v-model="searchValue"></el-input>
|
||||
</div>
|
||||
<div class="content-tree">
|
||||
<el-tree :filter-node-method='filterNode' :highlight-current="true" :default-expanded-keys="defaultExpandedKeys"
|
||||
<el-tree
|
||||
:filter-node-method="filterNode"
|
||||
:highlight-current="true"
|
||||
:default-expanded-keys="defaultExpandedKeys"
|
||||
node-key="index"
|
||||
:data="treeDataAll"
|
||||
:props="defaultProps"
|
||||
@node-click="handleNodeClick" class="custom-tree" ref="treeRef">
|
||||
@node-click="handleNodeClick"
|
||||
class="custom-tree"
|
||||
ref="treeRef"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<el-tooltip effect="dark" :content="data.scriptTypeName" placement="right">
|
||||
<span class="custom-tree-node" :style="data.fly===2? 'color: #F56C6C;':data.fly===4? 'color: #e6a23c;':''">{{
|
||||
data.scriptTypeName
|
||||
}}</span>
|
||||
<span
|
||||
class="custom-tree-node"
|
||||
:style="
|
||||
data.fly === 2 ? 'color: #F56C6C;' : data.fly === 4 ? 'color: #e6a23c;' : ''
|
||||
"
|
||||
>
|
||||
{{ data.scriptTypeName }}
|
||||
</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-tree>
|
||||
@@ -65,18 +91,23 @@
|
||||
|
||||
<div class="content-right">
|
||||
<div class="content-right-title">
|
||||
<div style="width: 840px;">
|
||||
<span class="content-right-title-text">当前检测项目:
|
||||
<el-popover trigger="hover" :content="currentDesc" :width="popoverWidth" placement="right">
|
||||
<div style="width: 840px">
|
||||
<span class="content-right-title-text">
|
||||
当前检测项目:
|
||||
<el-popover trigger="hover" :content="currentDesc" width="500px" placement="right">
|
||||
<template #reference>
|
||||
<el-button type="text" style="font-size: 14px;">
|
||||
<el-button type="text" style="font-size: 14px">
|
||||
{{ currentScriptTypeName }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
</span>
|
||||
</div>
|
||||
<el-form-item style="margin-left: 0px;margin-bottom:0px !important;width: 280px;" v-if="checkListLevel.length" label='测试项'>
|
||||
<el-form-item
|
||||
style="margin-left: 0px; margin-bottom: 0px !important; width: 280px"
|
||||
v-if="checkListLevel.length"
|
||||
label="测试项"
|
||||
>
|
||||
<el-cascader
|
||||
v-model="currentCheckItem"
|
||||
:options="checkListLevel"
|
||||
@@ -88,10 +119,17 @@
|
||||
<div class="content-right-Tabs">
|
||||
<el-tabs type="border-card" v-model="activeTab">
|
||||
<el-tab-pane label="检测结果" name="resultTab">
|
||||
<DataCheckResultTable :tableData="checkResultData" :currentScriptTypeName="currentScriptTypeName"/>
|
||||
<DataCheckResultTable
|
||||
:tableData="checkResultData"
|
||||
:currentScriptTypeName="currentScriptTypeName"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="原始数据" name="rawDataTab">
|
||||
<DataCheckRawDataTable :tableData="rawTableData" :currentScriptTypeName="currentScriptTypeName" @exportRawDataHandler="exportRawDataHandler"/>
|
||||
<DataCheckRawDataTable
|
||||
:tableData="rawTableData"
|
||||
:currentScriptTypeName="currentScriptTypeName"
|
||||
@exportRawDataHandler="exportRawDataHandler"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
@@ -99,45 +137,54 @@
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
</template>
|
||||
<script setup lang='ts'>
|
||||
<script setup lang="ts">
|
||||
import { dialogBig } from '@/utils/elementBind'
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import DataCheckResultTable from './dataCheckResultTable.vue'
|
||||
import DataCheckRawDataTable from './dataCheckRawDataTable.vue'
|
||||
import {CheckData} from "@/api/check/interface";
|
||||
import {useDictStore} from "@/stores/modules/dict";
|
||||
import {useCheckStore} from "@/stores/modules/check";
|
||||
import {changeErrorSystem, deleteTempTable, exportRawData, getFormData, getTableData, getTreeData, reCalculate} from "@/api/check/test";
|
||||
import { CheckData } from '@/api/check/interface'
|
||||
import { useCheckStore } from '@/stores/modules/check'
|
||||
import {
|
||||
changeErrorSystem,
|
||||
deleteTempTable,
|
||||
exportRawData,
|
||||
getFormData,
|
||||
getTableData,
|
||||
getTreeData,
|
||||
reCalculate
|
||||
} from '@/api/check/test'
|
||||
import { generateDevReport, getPqErrSysList } from '@/api/plan/plan'
|
||||
import {useDownload} from "@/hooks/useDownload";
|
||||
import {Histogram, Postcard} from "@element-plus/icons-vue";
|
||||
import {ResultEnum} from "@/enums/httpEnum";
|
||||
import {ElMessage} from "element-plus";
|
||||
|
||||
const {appendToBody} = withDefaults(defineProps<{
|
||||
appendToBody: boolean
|
||||
}>(), {appendToBody: true})
|
||||
|
||||
const defaultProps = {
|
||||
label: "scriptTypeName",
|
||||
children: "children",
|
||||
};
|
||||
import { useDownload } from '@/hooks/useDownload'
|
||||
import { Histogram, Postcard } from '@element-plus/icons-vue'
|
||||
import { ResultEnum } from '@/enums/httpEnum'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useDictStore } from '@/stores/modules/dict'
|
||||
import { useModeStore } from '@/stores/modules/mode'
|
||||
|
||||
const dictStore = useDictStore()
|
||||
const modeStore = useModeStore()
|
||||
|
||||
const { appendToBody = true } = defineProps<{
|
||||
appendToBody: boolean
|
||||
}>()
|
||||
const defaultProps = {
|
||||
label: 'scriptTypeName',
|
||||
children: 'children'
|
||||
}
|
||||
|
||||
const checkStore = useCheckStore()
|
||||
|
||||
const visible = ref(false)
|
||||
const treeRef = ref()
|
||||
const searchValue = ref<string>('')
|
||||
const pqErrorList = reactive<{ id: string; name: string; }[]>([])
|
||||
watch(searchValue, (val) => {
|
||||
const pqErrorList = reactive<{ id: string; name: string }[]>([])
|
||||
watch(searchValue, val => {
|
||||
treeRef.value!.filter(val)
|
||||
})
|
||||
|
||||
// 格式化数字
|
||||
const fixed = 4;
|
||||
const fixed = 4
|
||||
|
||||
// 表单数据
|
||||
const formContent = reactive<CheckData.DataCheck>({
|
||||
@@ -145,7 +192,7 @@ const formContent = reactive<CheckData.DataCheck>({
|
||||
errorSysId: '',
|
||||
dataRule: '',
|
||||
deviceName: '',
|
||||
chnNum: '',
|
||||
chnNum: ''
|
||||
})
|
||||
// 原始误差体系id
|
||||
let originErrorSysId: string = ''
|
||||
@@ -157,7 +204,7 @@ const currentCheckItem = ref<any>()
|
||||
// 检测项列表
|
||||
// const checkList = reactive<{ value: string, label: string }[]>([])
|
||||
// 带有层级的检测项列表
|
||||
const checkListLevel = reactive<{ value: string, label: string, children: { value: string, label: string }[] }[]>([])
|
||||
const checkListLevel = reactive<{ value: string; label: string; children: { value: string; label: string }[] }[]>([])
|
||||
|
||||
let deviceId: string = ''
|
||||
let originScriptType: string | null = null
|
||||
@@ -166,7 +213,6 @@ let scriptType: string | null = null
|
||||
// 通道下拉列表
|
||||
let chnList: any[] = []
|
||||
|
||||
|
||||
// 左侧树数据
|
||||
const treeDataAll = reactive<CheckData.TreeItem[]>([])
|
||||
|
||||
@@ -176,12 +222,15 @@ const checkIndex = ref<string>('')
|
||||
// 当前检测项目名称
|
||||
const currentScriptTypeName = ref('')
|
||||
// 当前检测项目描述
|
||||
const currentDesc = ref('');
|
||||
const currentDesc = ref('')
|
||||
// 右侧Tab选中项
|
||||
const activeTab = ref<string>('resultTab')
|
||||
|
||||
//存放相应的表格数据
|
||||
const resTableData = reactive<{ resultData: Map<string, any>, rawData: Map<string, any> }>({resultData: new Map(), rawData: new Map()})
|
||||
const resTableData = reactive<{ resultData: Map<string, any>; rawData: Map<string, any> }>({
|
||||
resultData: new Map(),
|
||||
rawData: new Map()
|
||||
})
|
||||
|
||||
// 检测结果表格数据
|
||||
const checkResultData = reactive<CheckData.CheckResult[]>([])
|
||||
@@ -190,7 +239,6 @@ const popoverWidth: ComputedRef<string> = computed(() => {
|
||||
return `${1140 - (currentScriptTypeName.value.length + 7 + 3) * 14 - (checkListLevel.length ? 280 : 0)}px`
|
||||
})
|
||||
|
||||
|
||||
// 原始数据表格数据
|
||||
const rawTableData = reactive<CheckData.RawDataItem[]>([])
|
||||
|
||||
@@ -210,27 +258,28 @@ const handleNodeClick = async (data: any) => {
|
||||
currentDesc.value = data.sourceDesc
|
||||
scriptType = data.scriptType ?? scriptType
|
||||
|
||||
console.log("点击左侧树节点触发事件handleNodeClick", checkIndex.value)
|
||||
console.log('点击左侧树节点触发事件handleNodeClick', checkIndex.value)
|
||||
if (checkIndex.value !== '') {
|
||||
await updateTableData()
|
||||
activeTab.value = 'resultTab'
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const handleErrorSysChange = async () => {
|
||||
console.log("切换误差体系", formContent.errorSysId);
|
||||
console.log('切换误差体系', formContent.errorSysId)
|
||||
changeErrorSystem({
|
||||
planId: checkStore.plan.id,
|
||||
scriptId: checkStore.plan.scriptId,
|
||||
errorSysId: formContent.errorSysId,
|
||||
deviceId: deviceId,
|
||||
code: checkStore.plan.code + '',
|
||||
}).then((res) => {
|
||||
patternId: dictStore.getDictData('Pattern').find(item => item.name === modeStore.currentMode)?.id ?? ''
|
||||
}).then(res => {
|
||||
if (res.code === ResultEnum.SUCCESS) {
|
||||
ElMessage.success('切换误差体系成功')
|
||||
if (originErrorSysId != formContent.errorSysId) {
|
||||
planCode = checkStore.plan.code + "_temp"
|
||||
planCode = checkStore.plan.code + '_temp'
|
||||
} else {
|
||||
planCode = checkStore.plan.code + ''
|
||||
}
|
||||
@@ -243,16 +292,18 @@ const handleErrorSysChange = async () => {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
watch(() => formContent.chnNum, async (newVal, oldVal) => {
|
||||
watch(
|
||||
() => formContent.chnNum,
|
||||
async (newVal, oldVal) => {
|
||||
// console.log("通道号", newVal);
|
||||
if (newVal != '') {
|
||||
handleChnNumChange()
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
const handleChnNumChange = async () => {
|
||||
console.log("通道号", formContent.chnNum);
|
||||
console.log('通道号', formContent.chnNum)
|
||||
// 发起请求,查询该测试项的检测结果
|
||||
const { data: resTreeDataTemp }: { data: CheckData.TreeItem[] } = await getTreeData({
|
||||
scriptId: checkStore.plan.scriptId,
|
||||
@@ -273,14 +324,13 @@ const handleChnNumChange = async () => {
|
||||
activeTab.value = 'resultTab'
|
||||
}
|
||||
|
||||
|
||||
watch(currentCheckItem, (newVal, oldVal) => {
|
||||
if (newVal !== '') {
|
||||
let key = newVal[0]
|
||||
if (newVal.length == 2) {
|
||||
key += "_" + newVal[1]
|
||||
key += '_' + newVal[1]
|
||||
}
|
||||
console.log("当前检测项", key);
|
||||
console.log('当前检测项', key)
|
||||
doCurrentCheckItemUpdate(key)
|
||||
} else {
|
||||
activeTab.value = 'resultTab'
|
||||
@@ -297,7 +347,7 @@ const defaultOperate = () => {
|
||||
checkIndex.value = node.index
|
||||
defaultExpandedKeys = [node.index]
|
||||
|
||||
treeRef.value?.setCurrentKey(node.index);
|
||||
treeRef.value?.setCurrentKey(node.index)
|
||||
scriptType = node.scriptType ?? scriptType
|
||||
} else {
|
||||
currentScriptTypeName.value = ''
|
||||
@@ -308,9 +358,8 @@ const defaultOperate = () => {
|
||||
}
|
||||
|
||||
const updateTableData = async () => {
|
||||
console.log("左侧树被选中的叶子节点checkIndex", checkIndex.value);
|
||||
console.log('左侧树被选中的叶子节点checkIndex', checkIndex.value)
|
||||
if (checkIndex.value !== '') {
|
||||
|
||||
checkListLevel.length = 0
|
||||
checkResultData.length = 0
|
||||
rawTableData.length = 0
|
||||
@@ -324,7 +373,7 @@ const updateTableData = async () => {
|
||||
devId: deviceId,
|
||||
devNum: formContent.chnNum + '',
|
||||
code: planCode,
|
||||
index: parseInt(checkIndex.value),
|
||||
index: parseInt(checkIndex.value)
|
||||
})
|
||||
|
||||
let keys1 = Object.keys(data.resultData)
|
||||
@@ -335,18 +384,36 @@ const updateTableData = async () => {
|
||||
if ((key.includes('谐波') || key.includes('简谐波')) && key !== '谐波有功功率') {
|
||||
for (let item of data.resultData[key]) {
|
||||
let num = formatHarmNum(item.harmNum + '')
|
||||
label = item.isData === 1 ? `${num}` : item.isData === 4 ? `${num}(/)` : item.isData === 5 ? `${num}(-)` : `${num}(不符合)`
|
||||
label =
|
||||
item.isData === 1
|
||||
? `${num}`
|
||||
: item.isData === 4
|
||||
? `${num}(/)`
|
||||
: item.isData === 5
|
||||
? `${num}(-)`
|
||||
: `${num}(不符合)`
|
||||
children.push({ label: label, value: num })
|
||||
resultData.set(key + "_" + num, item)
|
||||
resultData.set(key + '_' + num, item)
|
||||
}
|
||||
checkListLevel.push({ label: key, value: key, children: children })
|
||||
} else {
|
||||
label = data.resultData[key][0].isData === 1 ? `${key}` : data.resultData[key][0].isData === 4 ? `${key}(/)` : data.resultData[key][0].isData === 5 ? `${key}(-)` : `${key}(不符合)`
|
||||
label =
|
||||
data.resultData[key][0].isData === 1
|
||||
? `${key}`
|
||||
: data.resultData[key][0].isData === 4
|
||||
? `${key}(/)`
|
||||
: data.resultData[key][0].isData === 5
|
||||
? `${key}(-)`
|
||||
: `${key}(不符合)`
|
||||
resultData.set(key, data.resultData[key][0])
|
||||
checkListLevel.push({ label: label, value: key, children: [] })
|
||||
let temp = checkListLevel.find(item => item.label.includes('电压幅值'))
|
||||
if (temp) {
|
||||
checkListLevel.splice(checkListLevel.indexOf(temp), 1)
|
||||
checkListLevel.unshift(temp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let keys2 = Object.keys(data.rawData)
|
||||
let rawData = new Map()
|
||||
@@ -354,9 +421,9 @@ const updateTableData = async () => {
|
||||
if ((key.includes('谐波') || key.includes('简谐波')) && key !== '谐波有功功率') {
|
||||
for (let item of data.rawData[key]) {
|
||||
let num = formatHarmNum(item.harmNum + '')
|
||||
let value = rawData.get(key + "_" + num)
|
||||
let value = rawData.get(key + '_' + num)
|
||||
if (!value) {
|
||||
rawData.set(key + "_" + num, [item])
|
||||
rawData.set(key + '_' + num, [item])
|
||||
} else {
|
||||
value.push(item)
|
||||
}
|
||||
@@ -366,7 +433,6 @@ const updateTableData = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
resTableData.resultData = resultData
|
||||
resTableData.rawData = rawData
|
||||
|
||||
@@ -379,8 +445,6 @@ const updateTableData = async () => {
|
||||
currentCheckItem.value = [checkListLevel[0].value + '']
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
checkResultData.length = 0
|
||||
rawTableData.length = 0
|
||||
@@ -415,18 +479,15 @@ const open = async (_deviceId: string, chnNum: string, _scriptType: string | nul
|
||||
scriptType
|
||||
})
|
||||
|
||||
chnList = resFormContent.chnList.map((item: { value: string, label: string }) => ({
|
||||
chnList = resFormContent.chnList.map((item: { value: string; label: string }) => ({
|
||||
value: item.value,
|
||||
label: item.value
|
||||
//label: item.label == '1' ? `${item.value}` : item.label == '2' ? `${item.value}(不符合)` : `${item.value}`
|
||||
}))
|
||||
|
||||
let dataRuleName = dictStore.getDictData('Data_Rule').find(item => item.id == resFormContent.dataRule)?.name
|
||||
|
||||
Object.assign(formContent, {
|
||||
...resFormContent,
|
||||
dataRule: dataRuleName,
|
||||
chnNum: chnList.length > 0 ? chnList[0].value : '',
|
||||
chnNum: chnList.length > 0 ? chnList[0].value : ''
|
||||
})
|
||||
originErrorSysId = formContent.errorSysId
|
||||
|
||||
@@ -434,11 +495,16 @@ const open = async (_deviceId: string, chnNum: string, _scriptType: string | nul
|
||||
let { data: resPqErrorList } = await getPqErrSysList()
|
||||
Object.assign(pqErrorList, resPqErrorList)
|
||||
|
||||
visible.value = true;
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
const handleGenerateReport = async () => {
|
||||
await generateDevReport({'planId': checkStore.plan.id, 'devIdList': [deviceId],'scriptId':checkStore.plan.scriptId,'planCode':planCode})
|
||||
await generateDevReport({
|
||||
planId: checkStore.plan.id,
|
||||
devIdList: [deviceId],
|
||||
scriptId: checkStore.plan.scriptId,
|
||||
planCode: planCode
|
||||
})
|
||||
ElMessage.success({ message: `报告生成成功!` })
|
||||
}
|
||||
|
||||
@@ -448,8 +514,9 @@ const handleReCalculate = async () => {
|
||||
scriptId: checkStore.plan.scriptId,
|
||||
errorSysId: formContent.errorSysId,
|
||||
deviceId: deviceId,
|
||||
code: checkStore.plan.code + ''
|
||||
}).then((res) => {
|
||||
code: checkStore.plan.code + '',
|
||||
patternId: dictStore.getDictData('Pattern').find(item => item.name === modeStore.currentMode)?.id ?? ''
|
||||
}).then(res => {
|
||||
ElMessage.success('重新计算成功!')
|
||||
// if (originErrorSysId != formContent.errorSysId) {
|
||||
// planCode = checkStore.plan.code + "_temp"
|
||||
@@ -471,7 +538,7 @@ const close = async () => {
|
||||
errorSysName: '',
|
||||
dataRule: '',
|
||||
deviceName: '',
|
||||
chnNum: '',
|
||||
chnNum: ''
|
||||
})
|
||||
treeDataAll.length = 0
|
||||
// harmNumList.length = 0
|
||||
@@ -488,12 +555,12 @@ const close = async () => {
|
||||
pqErrorList.length = 0
|
||||
planCode = ''
|
||||
|
||||
visible.value = false;
|
||||
visible.value = false
|
||||
|
||||
if (checkStore.showDetailType === 1) {
|
||||
await deleteTempTable(checkStore.plan.code + '')
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const setCheckResultData = (data: CheckData.ResCheckResult | null) => {
|
||||
let result: CheckData.CheckResult[] = []
|
||||
@@ -530,7 +597,7 @@ const setCheckResultData = (data: CheckData.ResCheckResult | null) => {
|
||||
|
||||
maxError: data.radius,
|
||||
unit: data.unit,
|
||||
result: data.isData,
|
||||
result: data.isData
|
||||
})
|
||||
}
|
||||
|
||||
@@ -547,22 +614,28 @@ const setCheckResultData = (data: CheckData.ResCheckResult | null) => {
|
||||
|
||||
maxError: data.radius,
|
||||
unit: data.unit,
|
||||
result: data.isData,
|
||||
result: data.isData
|
||||
})
|
||||
}
|
||||
Object.assign(checkResultData, result)
|
||||
console.log("检测结果", checkResultData);
|
||||
console.log('检测结果', checkResultData)
|
||||
}
|
||||
|
||||
const exportRawDataHandler = () => {
|
||||
useDownload(exportRawData, '原始数据', {
|
||||
useDownload(
|
||||
exportRawData,
|
||||
'原始数据',
|
||||
{
|
||||
scriptType,
|
||||
scriptId: checkStore.plan.scriptId,
|
||||
devId: deviceId,
|
||||
devNum: formContent.chnNum + '',
|
||||
code: checkStore.plan.code + '',
|
||||
index: parseInt(checkIndex.value),
|
||||
}, false, '.xlsx')
|
||||
index: parseInt(checkIndex.value)
|
||||
},
|
||||
false,
|
||||
'.xlsx'
|
||||
)
|
||||
}
|
||||
|
||||
const formatHarmNum = (num: string) => {
|
||||
@@ -588,7 +661,7 @@ const setRawData = (data: CheckData.RawDataItem[]) => {
|
||||
})
|
||||
rawTableData.length = 0
|
||||
Object.assign(rawTableData, data)
|
||||
console.log("原始数据", rawTableData)
|
||||
console.log('原始数据', rawTableData)
|
||||
}
|
||||
const dataToShow = (num: number): string => {
|
||||
if (num == null || num == undefined) {
|
||||
@@ -597,14 +670,13 @@ const dataToShow = (num: number): string => {
|
||||
return num + ''
|
||||
}
|
||||
|
||||
|
||||
const numberToFixed = (num: number): string => {
|
||||
if (num == null || num == undefined) {
|
||||
return '/'
|
||||
} else {
|
||||
let result = num.toFixed(fixed)
|
||||
if (result === "-0.0000") {
|
||||
return result.replace(/-/g, "")
|
||||
if (result === '-0.0000') {
|
||||
return result.replace(/-/g, '')
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -619,8 +691,8 @@ const stringToFixed = (str: string): string => {
|
||||
return '/'
|
||||
} else {
|
||||
let result = num.toFixed(fixed)
|
||||
if (result === "-0.0000") {
|
||||
return result.replace(/-/g, "")
|
||||
if (result === '-0.0000') {
|
||||
return result.replace(/-/g, '')
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -636,30 +708,30 @@ const getError = (num1: number, num2: number): string => {
|
||||
|
||||
const toMaxErrorStr = (oldMaxErroe: any, unit: any) => {
|
||||
let result = oldMaxErroe ?? '/'
|
||||
let idx = result.indexOf('~');
|
||||
let idx = result.indexOf('~')
|
||||
if (idx > 0) {
|
||||
let left = result.substring(0, idx)
|
||||
let right = result.substring(idx, result.length)
|
||||
result = left + unit + right + unit;
|
||||
result = left + unit + right + unit
|
||||
}
|
||||
return result;
|
||||
return result
|
||||
}
|
||||
|
||||
const findFirstLeafNode = (node: any): any => {
|
||||
if (!node.children || node.children.length === 0) {
|
||||
return node;
|
||||
return node
|
||||
}
|
||||
|
||||
return findFirstLeafNode(node.children[0]);
|
||||
return findFirstLeafNode(node.children[0])
|
||||
}
|
||||
const getDefaultNode = (data: any[]) => {
|
||||
if (!data || data.length === 0) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
|
||||
const firstElement = data[0];
|
||||
const firstElement = data[0]
|
||||
|
||||
return findFirstLeafNode(firstElement);
|
||||
return findFirstLeafNode(firstElement)
|
||||
}
|
||||
|
||||
const toAngleLast = () => {
|
||||
@@ -694,7 +766,7 @@ const filter = (treeData: any[], fly: number) => {
|
||||
filter(node.children, fly)
|
||||
// 检查 children 是否被全部移除
|
||||
if (node.children.length === 0) {
|
||||
treeData.splice(i, 1);
|
||||
treeData.splice(i, 1)
|
||||
}
|
||||
} else {
|
||||
treeData.splice(i, 1)
|
||||
@@ -711,34 +783,33 @@ const updateTreeFly = (treeData: any[], fly: number) => {
|
||||
// 如果当前节点是叶子节点且 fly 字段等于 targetFly
|
||||
if (!node.children || node.children.length === 0) {
|
||||
if (node.fly === targetFly) {
|
||||
node.fly = targetFly; // 确保叶子节点的 fly 字段被设置为 targetFly
|
||||
return true; // 返回 true 表示找到并更新了
|
||||
node.fly = targetFly // 确保叶子节点的 fly 字段被设置为 targetFly
|
||||
return true // 返回 true 表示找到并更新了
|
||||
}
|
||||
return false; // 否则返回 false
|
||||
return false // 否则返回 false
|
||||
}
|
||||
|
||||
// 递归更新子节点
|
||||
let updated = false;
|
||||
let updated = false
|
||||
for (let child of node.children) {
|
||||
if (recursiveUpdate(child, targetFly)) {
|
||||
updated = true;
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有子节点被更新了,则更新当前节点的 fly 字段
|
||||
if (updated) {
|
||||
node.fly = targetFly;
|
||||
node.fly = targetFly
|
||||
}
|
||||
return updated;
|
||||
return updated
|
||||
}
|
||||
|
||||
for (let i = 0; i < treeData.length; i++) {
|
||||
// 调用递归函数从根节点开始更新
|
||||
recursiveUpdate(treeData[i], fly);
|
||||
recursiveUpdate(treeData[i], fly)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
defineExpose({
|
||||
open
|
||||
})
|
||||
@@ -751,7 +822,6 @@ defineExpose({
|
||||
overflow-y: hidden;
|
||||
overflow-x: hidden;
|
||||
|
||||
|
||||
.data-check-dialog {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
516
frontend/src/views/home/components/deviceConnectionPopup.vue
Normal file
516
frontend/src/views/home/components/deviceConnectionPopup.vue
Normal file
@@ -0,0 +1,516 @@
|
||||
<template>
|
||||
<!-- <el-dialog title="设备通道配对" v-model="dialogVisible" v-bind="dialogBig">
|
||||
<div
|
||||
class="flow-container"
|
||||
style="overflow: hidden; position: relative"
|
||||
:style="{ height: dialogHeight + 'px' }"
|
||||
>
|
||||
<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"
|
||||
: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>
|
||||
</el-dialog> -->
|
||||
<!-- devIdList.value
|
||||
pqStandardDevList.value
|
||||
planIdKey.value -->
|
||||
<!-- 手动检测-勾选检测项弹窗 -->
|
||||
<SelectTestItemPopup ref="selectTestItemPopupRef" @openTestDialog="openTestDialog"></SelectTestItemPopup>
|
||||
<CompareTestPopup
|
||||
ref="testPopup"
|
||||
:key="compareTestKey"
|
||||
v-if="CompareTestVisible"
|
||||
:devIdList="devIdList"
|
||||
:pqStandardDevList="pqStandardDevList"
|
||||
:planIdKey="planIdKey"
|
||||
></CompareTestPopup>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { h, ref } 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 CompareTestPopup from './compareTestPopup.vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import CustomEdge from './RemoveableEdge.vue' // 导入自定义连接线组件
|
||||
import { jwtUtil } from '@/utils/jwtUtil'
|
||||
import { useCheckStore } from '@/stores/modules/check'
|
||||
import { Plan } from '@/api/plan/interface'
|
||||
import { fa } from 'element-plus/es/locale'
|
||||
|
||||
const checkStore = useCheckStore()
|
||||
const dialogVisible = ref(false)
|
||||
const selectTestItemPopupRef = ref<InstanceType<typeof SelectTestItemPopup>>()
|
||||
const testPopup = ref()
|
||||
const dialogTitle = ref('手动检测')
|
||||
const compareTestKey = ref(0)
|
||||
// 计算对话框高度
|
||||
const dialogHeight = ref(600)
|
||||
const CompareTestVisible = ref(false)
|
||||
// 初始化 VueFlow,注册自定义连线类型
|
||||
const { edges, setViewport } = useVueFlow({
|
||||
edgeTypes: {
|
||||
default: CustomEdge
|
||||
}
|
||||
})
|
||||
|
||||
// 初始化时锁定画布位置
|
||||
const onPaneReady = () => {
|
||||
setViewport({ x: 0, y: 0, zoom: 1 })
|
||||
}
|
||||
|
||||
// 提取公共的label渲染函数
|
||||
const createLabel = (text: string, type: string) => {
|
||||
return h(
|
||||
'div',
|
||||
{
|
||||
style: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
fontSize: '15px',
|
||||
textAlign: 'center',
|
||||
border: '1px solid #ccc',
|
||||
borderRadius: '8px',
|
||||
padding: '8px',
|
||||
backgroundColor: '#f9f9f9'
|
||||
}
|
||||
},
|
||||
[
|
||||
h(Platform, {
|
||||
style: {
|
||||
width: '20px',
|
||||
marginBottom: '4px',
|
||||
color: '#526ade'
|
||||
}
|
||||
}),
|
||||
h('div', null, '设备名称:' + text),
|
||||
h('div', null, '设备类型:' + type)
|
||||
]
|
||||
) as any
|
||||
}
|
||||
|
||||
const createLabel3 = (text: string) => {
|
||||
return h(
|
||||
'div',
|
||||
{
|
||||
style: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
fontSize: '15px',
|
||||
textAlign: 'center',
|
||||
border: '1px solid #ccc',
|
||||
borderRadius: '8px',
|
||||
padding: '8px',
|
||||
backgroundColor: '#f9f9f9'
|
||||
}
|
||||
},
|
||||
[
|
||||
h(Flag, {
|
||||
style: {
|
||||
width: '20px',
|
||||
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 devIdList = ref<Device.ResPqDev[]>([])
|
||||
const pqStandardDevList = ref<StandardDevice.ResPqStandardDevice[]>([])
|
||||
const planIdKey = ref<string>('')
|
||||
const deviceMonitor = ref<Map<string, any[]>>();
|
||||
const planIsOnlyWave = ref(false)
|
||||
const open = async (
|
||||
|
||||
device: Device.ResPqDev[],
|
||||
standardDev: StandardDevice.ResPqStandardDevice[],
|
||||
fatherPlanId: string,
|
||||
DeviceMonitoringMap: Map<string, any[]>,
|
||||
checkType: string,
|
||||
isOnlyWave:boolean
|
||||
) => {
|
||||
planIsOnlyWave.value = isOnlyWave
|
||||
CompareTestVisible.value = false
|
||||
devIdList.value = device
|
||||
pqStandardDevList.value = standardDev
|
||||
planIdKey.value = fatherPlanId
|
||||
deviceMonitor.value = DeviceMonitoringMap
|
||||
if(checkType == "一键检测"){
|
||||
openTestDialog()
|
||||
}else{
|
||||
selectTestItemPopupRef.value?.open()
|
||||
}
|
||||
|
||||
// edges.value = []
|
||||
|
||||
// devIds.value = device.map(d => d.id)
|
||||
// standardDevIds.value = standardDev.map(d => d.id)
|
||||
// planId.value = fatherPlanId
|
||||
// nodes.value = createNodes(device, standardDev)
|
||||
// edges.value = []
|
||||
|
||||
// devIds.value = device.map(d => d.id)
|
||||
// standardDevIds.value = standardDev.map(d => d.id)
|
||||
// planId.value = fatherPlanId
|
||||
// nodes.value = createNodes(device, standardDev)
|
||||
// dialogVisible.value = true
|
||||
// onPaneReady()
|
||||
}
|
||||
|
||||
const handleNext = async () => {
|
||||
if (edges.value.length === 0) {
|
||||
ElMessage.warning('请先完成通道配对')
|
||||
return
|
||||
}
|
||||
// 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])
|
||||
}
|
||||
})
|
||||
|
||||
await checkStore.setChnNum(chnNumList)
|
||||
CompareTestVisible.value = false
|
||||
dialogVisible.value = false
|
||||
|
||||
selectTestItemPopupRef.value?.open()
|
||||
}
|
||||
|
||||
const openTestDialog = async () => {
|
||||
compareTestKey.value++ // 每次调用时更新key
|
||||
CompareTestVisible.value = true
|
||||
// 转换连接信息,只保留设备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,
|
||||
deviceMonitor.value ,
|
||||
planIsOnlyWave.value
|
||||
)
|
||||
}, 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
|
||||
const targetDeviceText = targetDeviceNode.data.label.children[1].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[]) => {
|
||||
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 = 0
|
||||
const inputChannelX = 200
|
||||
const outputChannelX = 800
|
||||
const standardWidth = 950
|
||||
|
||||
const yPosition = ref(25)
|
||||
const yPosition2 = ref(25)
|
||||
|
||||
// 添加被检通道
|
||||
Object.entries(channelCounts).forEach(([deviceId, count]) => {
|
||||
for (let i = 1; i <= count; i++) {
|
||||
const channelId = `被检通道-${deviceId}-${i}`
|
||||
newNodes.push({
|
||||
id: channelId,
|
||||
type: 'input',
|
||||
data: { label: createLabel3(`被检通道${i}`) },
|
||||
position: { x: inputChannelX, y: yPosition.value },
|
||||
sourcePosition: 'right',
|
||||
style: { width: '150px', border: 'none', boxShadow: 'none' }
|
||||
})
|
||||
|
||||
// 计算设备节点Y坐标(居中显示)
|
||||
if (i == 1 && count == 1) {
|
||||
deviceChannelGroups.push({
|
||||
deviceId,
|
||||
centerY: yPosition.value - 25
|
||||
})
|
||||
} else if (i == 2 && count == 2) {
|
||||
deviceChannelGroups.push({
|
||||
deviceId,
|
||||
centerY: yPosition.value - 50
|
||||
})
|
||||
} else if (i == 3 && count == 3) {
|
||||
deviceChannelGroups.push({
|
||||
deviceId,
|
||||
centerY: yPosition.value - 75
|
||||
})
|
||||
} else if (i == 4 && count == 4) {
|
||||
deviceChannelGroups.push({
|
||||
deviceId,
|
||||
centerY: yPosition.value - 100
|
||||
})
|
||||
}
|
||||
|
||||
yPosition.value += 50
|
||||
}
|
||||
yPosition.value += 50
|
||||
})
|
||||
|
||||
// 添加标准通道
|
||||
Object.entries(channelCounts2).forEach(([deviceId, count]) => {
|
||||
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.value },
|
||||
targetPosition: 'left',
|
||||
style: { width: '150px', border: 'none', boxShadow: 'none' }
|
||||
})
|
||||
|
||||
// 计算设备节点Y坐标(居中显示)
|
||||
if (i == 1 && count == 1) {
|
||||
standardChannelGroups.push({
|
||||
deviceId,
|
||||
centerY: yPosition2.value - 25
|
||||
})
|
||||
} else if (i == 2 && count == 2) {
|
||||
standardChannelGroups.push({
|
||||
deviceId,
|
||||
centerY: yPosition2.value - 50
|
||||
})
|
||||
} else if (i == 3 && count == 3) {
|
||||
standardChannelGroups.push({
|
||||
deviceId,
|
||||
centerY: yPosition2.value - 100
|
||||
})
|
||||
} else if (i == 4 && count == 4) {
|
||||
standardChannelGroups.push({
|
||||
deviceId,
|
||||
centerY: yPosition2.value - 100
|
||||
})
|
||||
}
|
||||
|
||||
yPosition2.value += 50
|
||||
}
|
||||
yPosition2.value += 50
|
||||
})
|
||||
|
||||
// 添加被检设备
|
||||
deviceChannelGroups.forEach(({ deviceId, centerY }) => {
|
||||
const device = inspectionDevices.find(d => d.id === deviceId)
|
||||
if (device) {
|
||||
newNodes.push({
|
||||
id: device.id,
|
||||
data: { label: createLabel(device.name, device.deviceType) },
|
||||
position: { x: deviceWidth, y: centerY },
|
||||
class: 'no-handle-node',
|
||||
style: { width: '200px', border: 'none', boxShadow: 'none' }
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 添加标准设备
|
||||
standardChannelGroups.forEach(({ deviceId, centerY }) => {
|
||||
const device = standardDevices.find(d => d.id === deviceId)
|
||||
if (device) {
|
||||
newNodes.push({
|
||||
id: device.id,
|
||||
data: { label: createLabel(device.name, device.deviceType) },
|
||||
position: { x: standardWidth, y: centerY },
|
||||
class: 'no-handle-node',
|
||||
style: { width: '200px', border: 'none', boxShadow: 'none' }
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
//页面高度取决于设备通道
|
||||
dialogHeight.value = Math.max(yPosition.value, yPosition2.value)
|
||||
|
||||
return newNodes
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.flow-container {
|
||||
width: 100%;
|
||||
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.vue-flow__node.no-handle-node .vue-flow__handle {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
@@ -2,16 +2,12 @@
|
||||
<div class="dialog" v-bind="dialogBig">
|
||||
<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: #91cc75">{{ 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">
|
||||
@@ -90,6 +86,21 @@
|
||||
</template>
|
||||
|
||||
<script lang="tsx" setup name="FactorTest">
|
||||
/**
|
||||
* 系数校准组件
|
||||
*
|
||||
* 功能概述:
|
||||
* 1. 对电力设备进行系数校准,确保测量精度
|
||||
* 2. 包含4个校准步骤:大/小电压电流系数的下装和校准
|
||||
* 3. 支持多设备并行校准,实时显示校准数据和结果
|
||||
* 4. 校准完成后自动统计合格设备数量并通知父组件
|
||||
*
|
||||
* 校准流程:
|
||||
* 第1步:大电压/电流系数下装 - 向设备下发大幅值的系数参数
|
||||
* 第2步:小电压/电流系数下装 - 向设备下发小幅值的系数参数
|
||||
* 第3步:大电压/电流校准 - 使用大幅值进行校准测试
|
||||
* 第4步:小电压/电流校准 - 使用小幅值进行校准测试
|
||||
*/
|
||||
import {type Device} from '@/api/device/interface/device';
|
||||
import {Failed} from '@element-plus/icons-vue'
|
||||
import {onBeforeMount, type Ref, ref, toRef, watch} from 'vue'
|
||||
@@ -101,182 +112,235 @@ import type {Plan} from '@/api/plan/interface';
|
||||
import {useCheckStore} from "@/stores/modules/check";
|
||||
import {useUserStore} from "@/stores/modules/user";
|
||||
|
||||
// ==================== 状态管理 ====================
|
||||
const checkStore = useCheckStore()
|
||||
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 userStore = useUserStore()
|
||||
|
||||
// ==================== 界面控制变量 ====================
|
||||
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) // 当前激活的校准步骤(0-5)
|
||||
const editableTabsValue = ref('0') // 当前选中的标签页
|
||||
|
||||
// ==================== 定时器管理 ====================
|
||||
let timer1: NodeJS.Timeout | null = null // 定时器1
|
||||
let timer2: NodeJS.Timeout | null = null // 定时器2
|
||||
|
||||
// ==================== 设备信息管理 ====================
|
||||
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 select_Plan = ref<Plan.ReqPlan>() // 选中的检测计划
|
||||
const planId = ref('') // 计划ID
|
||||
const CurV = ref<number>() // 额定电压值
|
||||
|
||||
// ==================== 状态控制变量 ====================
|
||||
const isButtonDisabled = ref(false) // 按钮禁用状态
|
||||
const errorStates = ref(new Array(name.value.length).fill(false)) // 各设备错误状态数组
|
||||
|
||||
// ==================== 加载状态管理 ====================
|
||||
const big_V_loadingStates = ref(false) // 大电压大电流下装加载状态
|
||||
const small_V_loadingStates = ref(false) // 小电压小电流下装加载状态
|
||||
const big_V_loadingStates2 = ref(false) // 大电压大电流校准加载状态
|
||||
const small_V_loadingStates2 = ref(false) // 小电压小电流校准加载状态
|
||||
|
||||
// ==================== 源输出参数显示 ====================
|
||||
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('') // 小电压校准时的电流输出显示
|
||||
// ==================== 组件Props定义 ====================
|
||||
const props = defineProps({
|
||||
testStatus: {
|
||||
type: String,
|
||||
default: 'wait'
|
||||
default: 'wait' // 从父组件接收的测试状态
|
||||
},
|
||||
webMsgSend: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
default: () => ({}) // 从父组件接收的WebSocket消息
|
||||
},
|
||||
})
|
||||
const userStore = useUserStore()
|
||||
|
||||
// ==================== 响应式Props引用 ====================
|
||||
const testStatus = toRef(props, 'testStatus');
|
||||
const tableDataMap = new Map<number, Ref<ChannelsTest.CoefficientVO[]>>([]);
|
||||
const currentStepStatus = ref<'error' | 'finish' | 'wait' | 'success' | 'process'>('finish');
|
||||
const webMsgSend = toRef(props, 'webMsgSend');
|
||||
|
||||
// ==================== 数据管理 ====================
|
||||
const tableDataMap = new Map<number, Ref<ChannelsTest.CoefficientVO[]>>([]); // 存储每个设备的表格数据映射
|
||||
const currentStepStatus = ref<'error' | 'finish' | 'wait' | 'success' | 'process'>('finish'); // 当前步骤状态
|
||||
|
||||
// ==================== 组件生命周期 ====================
|
||||
onBeforeMount(() => {
|
||||
// 初始化
|
||||
initData()
|
||||
initData() // 组件挂载前初始化数据
|
||||
})
|
||||
|
||||
/**
|
||||
* 初始化组件数据
|
||||
* 从store中获取设备信息,初始化表格数据和状态变量
|
||||
*/
|
||||
const initData = () => {
|
||||
// 获取设备基础信息
|
||||
CurV.value = checkStore.devices[0]?.devVolt || 57.74; // 获取额定电压,默认57.74V
|
||||
isButtonDisabled.value = false; // 恢复按钮可用状态
|
||||
select_Plan.value = checkStore.plan // 获取检测计划
|
||||
planId.value = checkStore.devices[0]?.planId || ''; // 获取计划ID
|
||||
|
||||
CurV.value = checkStore.devices[0]?.devVolt || 57.74;
|
||||
isButtonDisabled.value = false; // 恢复按钮
|
||||
select_Plan.value = checkStore.plan
|
||||
planId.value = checkStore.devices[0]?.planId || '';
|
||||
devIdArray.value = checkStore.devices.map(item => item.deviceId);
|
||||
name.value = checkStore.devices.map(item => item.deviceName)
|
||||
channel.value = checkStore.devices.map(item => item.chnNum)
|
||||
dialogVisible.value = true;
|
||||
total.value = name.value.length
|
||||
// 提取设备信息数组
|
||||
devIdArray.value = checkStore.devices.map(item => item.deviceId); // 设备ID数组
|
||||
name.value = checkStore.devices.map(item => item.deviceName); // 设备名称数组
|
||||
channel.value = checkStore.devices.map(item => item.chnNum); // 设备通道数量数组
|
||||
|
||||
// 设置界面状态
|
||||
dialogVisible.value = true; // 显示弹窗
|
||||
total.value = name.value.length; // 设置设备总数
|
||||
|
||||
// 初始化 loadingStates 为 false
|
||||
// loadingStates.value = new Array(selection.length).fill(false);
|
||||
// 初始化错误状态数组,所有设备初始状态为无错误
|
||||
errorStates.value = new Array(checkStore.devices.length).fill(false);
|
||||
|
||||
// 为每个设备初始化表格数据
|
||||
for (let i = 0; i < channel.value.length; i++) {
|
||||
const currentTableData = initializeTableData(dataTemplates, i);
|
||||
tableDataMap.set(i, currentTableData)
|
||||
tableDataMap.set(i, currentTableData); // 将表格数据存储到Map中,以设备索引为key
|
||||
}
|
||||
//console.log('tableDataMap',tableDataMap);
|
||||
}
|
||||
|
||||
|
||||
// ==================== 状态监听器 ====================
|
||||
/**
|
||||
* 监听父组件传入的测试状态变化
|
||||
* 当父组件通知开始测试时,启动系数校准流程
|
||||
*/
|
||||
watch(testStatus, function (newValue, oldValue) {
|
||||
if (newValue === 'start') {
|
||||
// 开始系数校准操作
|
||||
// 父组件通知开始系数校准操作
|
||||
handleSubmit()
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 监听WebSocket消息,处理系数校准过程中的各种响应
|
||||
* 主要处理:
|
||||
* 1. 错误代码的统一处理
|
||||
* 2. 系数校准流程的各个步骤响应
|
||||
* 3. 表格数据的实时更新
|
||||
*/
|
||||
watch(webMsgSend, function (newValue, oldValue) {
|
||||
console.log('webMsgSend---code', newValue.code)
|
||||
console.log('webMsgSend---requestId', newValue.requestId)
|
||||
|
||||
// 只有在非等待状态下才处理WebSocket消息
|
||||
if (testStatus.value !== 'waiting') {
|
||||
// ==================== 通用错误代码处理 ====================
|
||||
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之后还会发送消息吗?
|
||||
} else if (newValue.code == 10552) {
|
||||
// 存在已经初始化步骤,执行自动关闭
|
||||
ElMessageBox.alert('存在已经初始化步骤,执行自动关闭,请重新发起检测', '初始化失败', {
|
||||
confirmButtonText: '确定',
|
||||
type: 'error',
|
||||
})
|
||||
TableInit();
|
||||
} else {
|
||||
// ==================== 特定业务消息处理 ====================
|
||||
console.log('显示东西code', newValue.code)
|
||||
console.log('显示东西requestId', newValue.requestId)
|
||||
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',
|
||||
@@ -285,16 +349,19 @@ watch(webMsgSend, function (newValue, oldValue) {
|
||||
}
|
||||
}
|
||||
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',
|
||||
@@ -304,6 +371,7 @@ watch(webMsgSend, function (newValue, oldValue) {
|
||||
break;
|
||||
case 'DATA_REQUEST$02':
|
||||
if (newValue.code == 25003) {
|
||||
// 相序校验未通过
|
||||
ElMessageBox.alert('相序校验未通过,执行自动关闭,请重新发起检测', '相序校验未通过', {
|
||||
confirmButtonText: '确定',
|
||||
type: 'error',
|
||||
@@ -313,42 +381,48 @@ watch(webMsgSend, function (newValue, oldValue) {
|
||||
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 'yjc_xyjy':
|
||||
switch (newValue.operateCode) {
|
||||
case 'VERIFY_MAPPING$01':
|
||||
if (newValue.code == 10200) {
|
||||
if (newValue.code == 25002) {
|
||||
// 脚本与ICD校验失败
|
||||
let data = JSON.parse(newValue.data)
|
||||
ElMessageBox.alert(`脚本与icd校验失败!icd名称:${data['icdType']} -> 校验项:${data['dataType']}`, '初始化失败', {
|
||||
confirmButtonText: '确定',
|
||||
@@ -356,139 +430,173 @@ watch(webMsgSend, function (newValue, oldValue) {
|
||||
})
|
||||
TableInit();
|
||||
}
|
||||
if (newValue.code == 10500) {
|
||||
// 装置中未找到该ICD
|
||||
ElMessageBox.alert(`装置中未找到该icd!`, '初始化失败', {
|
||||
confirmButtonText: '确定',
|
||||
type: 'error',
|
||||
})
|
||||
TableInit();
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
// ★★★ 处理系数校准核心业务消息 ★★★
|
||||
case 'Coefficient_Check':
|
||||
console.log("Coefficient_Checkactive", active.value);
|
||||
|
||||
// ==================== 第1阶段:大电压/电流系数下装 ====================
|
||||
switch (newValue.operateCode) {
|
||||
case 'big_start'://大电压,电流下装
|
||||
case 'big_start':
|
||||
// 大电压/电流系数下装开始,显示源输出参数
|
||||
big_V_Download.value = 'Ua=Ub=Uc=' + newValue.data.devVolt + 'V';
|
||||
big_I_Download.value = 'Ia=Ib=Ic=' + newValue.data.devCurr + 'A';
|
||||
break;
|
||||
case 'big_end'://大电压,电流下装
|
||||
active.value++;
|
||||
tableLoading('small', '系数下装')
|
||||
case 'big_end':
|
||||
// 大电压/电流系数下装完成,进入下一阶段
|
||||
active.value++; // 步骤进度+1
|
||||
tableLoading('small', '系数下装') // 开始小电压/电流下装的加载动画
|
||||
break;
|
||||
}
|
||||
|
||||
// ==================== 第2阶段:小电压/电流系数下装 ====================
|
||||
switch (newValue.operateCode) {
|
||||
case 'small_start'://小电压,电流下装
|
||||
case 'small_start':
|
||||
// 小电压/电流系数下装开始,显示源输出参数
|
||||
small_V_Download.value = 'Ua=Ub=Uc=' + newValue.data.devVolt + 'V';
|
||||
small_I_Download.value = 'Ia=Ib=Ic=' + newValue.data.devCurr + 'A';
|
||||
break;
|
||||
case 'small_end'://小电压,电流下装
|
||||
active.value++;
|
||||
tableLoading('big', '系数校准')
|
||||
case 'small_end':
|
||||
// 小电压/电流系数下装完成,进入校准阶段
|
||||
active.value++; // 步骤进度+1
|
||||
tableLoading('big', '系数校准') // 开始大电压/电流校准的加载动画
|
||||
break;
|
||||
}
|
||||
|
||||
// ==================== 第3阶段:大电压/电流系数校准 ====================
|
||||
switch (newValue.operateCode) {
|
||||
case 'big_comp_start'://大电压,电流校准
|
||||
case 'big_comp_start':
|
||||
// 大电压/电流系数校准开始,显示源输出参数
|
||||
big_V_Adjust.value = 'Ua=Ub=Uc=' + newValue.data.devVolt + 'V';
|
||||
big_I_Adjust.value = 'Ia=Ib=Ic=' + newValue.data.devCurr + 'A';
|
||||
break;
|
||||
case 'big_comp_end'://大电压,电流校准
|
||||
active.value++;
|
||||
tableLoading('small', '系数校准')
|
||||
case 'big_comp_end':
|
||||
// 大电压/电流系数校准完成,进入最后阶段
|
||||
active.value++; // 步骤进度+1
|
||||
tableLoading('small', '系数校准') // 开始小电压/电流校准的加载动画
|
||||
break;
|
||||
}
|
||||
|
||||
// ==================== 第4阶段:小电压/电流系数校准(最终阶段)====================
|
||||
switch (newValue.operateCode) {
|
||||
case 'small_comp_start'://小电压,电流校准
|
||||
case 'small_comp_start':
|
||||
// 小电压/电流系数校准开始,显示源输出参数
|
||||
small_V_Adjust.value = 'Ua=Ub=Uc=' + newValue.data.devVolt + 'V';
|
||||
small_I_Adjust.value = 'Ia=Ib=Ic=' + newValue.data.devCurr + 'A';
|
||||
break;
|
||||
case 'small_comp_end'://小电压,电流校准
|
||||
active.value++;
|
||||
active.value++;
|
||||
case 'small_comp_end':
|
||||
// ★★★ 小电压/电流系数校准完成 - 整个系数校准流程结束 ★★★
|
||||
active.value++; // 步骤进度+1
|
||||
active.value++; // 再+1,跳转到完成状态
|
||||
|
||||
// 统计所有设备的校准结果
|
||||
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++;
|
||||
if (!hasError) {
|
||||
qualified.value++; // 合格设备计数+1
|
||||
}
|
||||
updateErrorState(i, hasError);
|
||||
updateErrorState(i, hasError); // 更新设备错误状态显示
|
||||
}
|
||||
}
|
||||
|
||||
//editableTabsValue.value = (tabNumber.value).toString();//显示下一个tab
|
||||
isButtonDisabled.value = false; // 恢复按钮
|
||||
isButtonDisabled.value = false; // 恢复按钮可用状态
|
||||
|
||||
// ★★★ 通知父组件系数校准成功完成,触发自动步骤切换 ★★★
|
||||
emit('update:testStatus', 'success')
|
||||
break;
|
||||
}
|
||||
// ==================== 表格数据实时更新 ====================
|
||||
switch (newValue.operateCode) {
|
||||
case 'DATA_CHNFACTOR$02'://表格
|
||||
// 输出 key 为 0 的数组中的第一条 ChannelsTest.CoefficientVO 对象
|
||||
case 'DATA_CHNFACTOR$02':
|
||||
// 接收并更新表格中的系数校准数据
|
||||
console.log('表格', name.value)
|
||||
|
||||
// 遍历所有设备,找到匹配的表格项并更新数据
|
||||
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 &&
|
||||
// 根据监测点号、类型、描述和设备名称找到对应的表格行
|
||||
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 (firstCoefficientVO) {
|
||||
// 更新A相电压数据和误差
|
||||
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.aVuXi = newValue.data.aVuXi; // 可能是"不合格"等文本
|
||||
}
|
||||
|
||||
// 更新B相电压数据和误差
|
||||
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.bVuXi = newValue.data.bVuXi; // 可能是"不合格"等文本
|
||||
}
|
||||
|
||||
// 更新C相电压数据和误差
|
||||
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.cVuXi = newValue.data.cVuXi; // 可能是"不合格"等文本
|
||||
}
|
||||
|
||||
// 更新A相电流数据和误差
|
||||
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.aIeXi = newValue.data.aIeXi; // 可能是"不合格"等文本
|
||||
}
|
||||
|
||||
// 更新B相电流数据和误差
|
||||
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.bIeXi = newValue.data.bIeXi; // 可能是"不合格"等文本
|
||||
}
|
||||
|
||||
// 更新C相电流数据和误差
|
||||
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.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++;
|
||||
activeIndex.value++; // 更新活跃索引
|
||||
} else {
|
||||
console.log('未找到匹配的' + newValue.data.devName + '对象');
|
||||
}
|
||||
@@ -502,9 +610,11 @@ watch(webMsgSend, function (newValue, oldValue) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
// 处理Socket连接超时
|
||||
case 'socket_timeout':
|
||||
switch (newValue.operateCode) {
|
||||
case 'VOLTAGE':
|
||||
// 电压连接超时
|
||||
ElMessageBox.alert('连接超时!', '连接超时', {
|
||||
confirmButtonText: '确定',
|
||||
type: 'error',
|
||||
@@ -513,9 +623,11 @@ watch(webMsgSend, function (newValue, oldValue) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
// 处理连接失败
|
||||
case 'connect':
|
||||
switch (newValue.operateCode) {
|
||||
case "Source":
|
||||
// 源服务端连接失败
|
||||
ElMessageBox.alert('源服务端连接失败', '源服务端连接失败', {
|
||||
confirmButtonText: '确定',
|
||||
type: 'error',
|
||||
@@ -523,6 +635,7 @@ watch(webMsgSend, function (newValue, oldValue) {
|
||||
TableInit();
|
||||
break;
|
||||
case "Dev":
|
||||
// 设备服务端连接失败
|
||||
ElMessageBox.alert('设备服务端连接失败', '设备服务端连接失败', {
|
||||
confirmButtonText: '确定',
|
||||
type: 'error',
|
||||
@@ -530,43 +643,47 @@ watch(webMsgSend, function (newValue, oldValue) {
|
||||
TableInit();
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
//出错系数检测初始化
|
||||
// ==================== 错误处理函数 ====================
|
||||
/**
|
||||
* 系数校准出错时的初始化处理
|
||||
* 通知父组件检测失败,重置相关状态
|
||||
*/
|
||||
const TableInit = () => {
|
||||
console.log("出错系数检测",active.value);
|
||||
emit('update:testStatus', 'test_init_fail')
|
||||
// isButtonDisabled.value = false; // 恢复按钮
|
||||
// for (let i = 0; i < channel.value.length; i++) {
|
||||
// const currentTableData = initializeTableData(dataTemplates, i);
|
||||
// tableDataMap.set(i, currentTableData)
|
||||
// }
|
||||
// activeIndex.value = 0
|
||||
// qualified.value = 0
|
||||
// active.value = 0
|
||||
|
||||
// 通知父组件系数校准失败
|
||||
emit('update:testStatus', 'error')
|
||||
}
|
||||
|
||||
//按行图标转动
|
||||
// ==================== 界面动画控制 ====================
|
||||
/**
|
||||
* 控制表格中特定类型和描述的行显示加载动画
|
||||
* @param type 类型:'big' | 'small'
|
||||
* @param desc 描述:'系数下装' | '系数校准'
|
||||
*/
|
||||
const tableLoading = (type: string, desc: string) => {
|
||||
console.log('转动',channel.value)
|
||||
for (let i = 0; i < channel.value.length; i++) {
|
||||
|
||||
// 遍历所有设备
|
||||
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() &&
|
||||
// 找到对应类型和描述的表格行
|
||||
const firstCoefficientVO = targetArray.find(item =>
|
||||
item.monitorNum === (j + 1).toString() &&
|
||||
item.type === type &&
|
||||
item.desc === desc);
|
||||
if (firstCoefficientVO) {
|
||||
firstCoefficientVO.loading = true;
|
||||
firstCoefficientVO.loading = true; // 开启加载动画
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -576,11 +693,47 @@ const tableLoading = (type: string, desc: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 表格数据模板 ====================
|
||||
/**
|
||||
* 系数校准表格的数据模板
|
||||
* 每个设备的每个通道都会根据这4个模板生成对应的表格行
|
||||
*
|
||||
* 模板说明:
|
||||
* 1. 监测点1 - 大幅值系数下装
|
||||
* 2. 监测点2 - 小幅值系数下装
|
||||
* 3. 监测点3 - 大幅值系数校准
|
||||
* 4. 监测点4 - 小幅值系数校准
|
||||
*/
|
||||
const dataTemplates: ChannelsTest.CoefficientVO[] = [
|
||||
{
|
||||
monitorNum: '1',
|
||||
desc: '系数下装',
|
||||
type: 'big',
|
||||
monitorNum: '1', // 监测点编号
|
||||
desc: '系数下装', // 操作描述
|
||||
type: 'big', // 幅值类型:大幅值
|
||||
aVuData: '—', // A相电压测量值
|
||||
aVuXi: '—', // A相电压误差
|
||||
bVuData: '—', // B相电压测量值
|
||||
bVuXi: '—', // B相电压误差
|
||||
cVuData: '—', // C相电压测量值
|
||||
cVuXi: '—', // C相电压误差
|
||||
aIeData: '—', // A相电流测量值
|
||||
aIeXi: '—', // A相电流误差
|
||||
bIeData: '—', // B相电流测量值
|
||||
bIeXi: '—', // B相电流误差
|
||||
cIeData: '—', // C相电流测量值
|
||||
cIeXi: '—', // C相电流误差
|
||||
loading: false, // 加载状态
|
||||
devName: '', // 设备名称(动态填充)
|
||||
aV: '—', // A相电压原始值
|
||||
bV: '—', // B相电压原始值
|
||||
cV: '—', // C相电压原始值
|
||||
aI: '—', // A相电流原始值
|
||||
bI: '—', // B相电流原始值
|
||||
cI: '—', // C相电流原始值
|
||||
},
|
||||
{
|
||||
monitorNum: '2', // 监测点编号
|
||||
desc: '系数下装', // 操作描述
|
||||
type: 'small', // 幅值类型:小幅值
|
||||
aVuData: '—',
|
||||
aVuXi: '—',
|
||||
bVuData: '—',
|
||||
@@ -603,9 +756,9 @@ const dataTemplates: ChannelsTest.CoefficientVO[] = [
|
||||
cI: '—',
|
||||
},
|
||||
{
|
||||
monitorNum: '2',
|
||||
desc: '系数下装',
|
||||
type: 'small',
|
||||
monitorNum: '3', // 监测点编号
|
||||
desc: '系数校准', // 操作描述
|
||||
type: 'big', // 幅值类型:大幅值
|
||||
aVuData: '—',
|
||||
aVuXi: '—',
|
||||
bVuData: '—',
|
||||
@@ -628,9 +781,9 @@ const dataTemplates: ChannelsTest.CoefficientVO[] = [
|
||||
cI: '—',
|
||||
},
|
||||
{
|
||||
monitorNum: '3',
|
||||
desc: '系数校准',
|
||||
type: 'big',
|
||||
monitorNum: '4', // 监测点编号
|
||||
desc: '系数校准', // 操作描述
|
||||
type: 'small', // 幅值类型:小幅值
|
||||
aVuData: '—',
|
||||
aVuXi: '—',
|
||||
bVuData: '—',
|
||||
@@ -652,64 +805,51 @@ const dataTemplates: ChannelsTest.CoefficientVO[] = [
|
||||
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: '—',
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
|
||||
// 更新错误状态的方法
|
||||
// ==================== 工具函数 ====================
|
||||
/**
|
||||
* 更新设备错误状态显示
|
||||
* @param index 设备索引
|
||||
* @param hasError 是否有错误
|
||||
*/
|
||||
const updateErrorState = (index: number, hasError: boolean) => {
|
||||
errorStates.value[index] = hasError;
|
||||
};
|
||||
|
||||
|
||||
// const emit = defineEmits<{
|
||||
|
||||
// (e: 'submitClicked', callback: (resolve: (value: boolean) => void) => void): void;
|
||||
// }>();
|
||||
|
||||
const emit = defineEmits(['update:testStatus']);
|
||||
|
||||
/**
|
||||
* 获取指定设备的表格数据
|
||||
* @param index 设备索引
|
||||
* @returns 返回对应设备的表格数据数组
|
||||
*/
|
||||
const getTableDataForChannel = (index: number): any[] => {
|
||||
const data = tableDataMap.get(index);
|
||||
return data ? data.value : [];
|
||||
}
|
||||
|
||||
// ==================== 父子组件通信 ====================
|
||||
const emit = defineEmits(['update:testStatus']);
|
||||
|
||||
|
||||
// ==================== 界面状态监听 ====================
|
||||
/**
|
||||
* 监听活跃索引变化,更新界面描述信息
|
||||
*/
|
||||
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 函数,根据实际需求进行调整
|
||||
// ==================== 业务逻辑函数 ====================
|
||||
/**
|
||||
* 检查设备校准数据是否存在错误
|
||||
* @param data 设备的校准数据数组
|
||||
* @returns 返回是否存在不合格项
|
||||
*/
|
||||
const checkForErrors = (data: ChannelsTest.CoefficientVO[]): boolean => {
|
||||
// 这里假设不合格字段的标准是 status 为 '不合格' 或 isValid 为 false
|
||||
// 检查各相电压和电流的误差值是否为"不合格"
|
||||
return data.some(item =>
|
||||
item.aVuXi === '不合格' ||
|
||||
item.bVuXi === '不合格' ||
|
||||
@@ -720,43 +860,35 @@ const checkForErrors = (data: ChannelsTest.CoefficientVO[]): boolean => {
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 处理系数校准开始事件
|
||||
* 由父组件通过状态监听触发
|
||||
*/
|
||||
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‘为正式检测
|
||||
userId:userStore.userInfo.id
|
||||
})
|
||||
active.value++;
|
||||
|
||||
isButtonDisabled.value = true; // 禁用按钮,防止重复提交
|
||||
tableLoading('big', '系数下装') // 开启大幅值系数下装的加载动画
|
||||
active.value++; // 步骤进度+1,进入第一个校准阶段
|
||||
console.log('开始检测active.value', active.value)
|
||||
};
|
||||
|
||||
// 提取初始化并填充 currentTableData 的函数
|
||||
/**
|
||||
* 为指定设备初始化表格数据
|
||||
* @param templates 数据模板数组
|
||||
* @param index 设备索引
|
||||
* @returns 返回初始化后的表格数据引用
|
||||
*/
|
||||
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,
|
||||
monitorNum: (j + 1).toString(), // 监测点号从1开始
|
||||
devName: name.value[index], // 设置设备名称
|
||||
...rest, // 其他模板属性
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -806,9 +938,6 @@ const initializeTableData = (templates: ChannelsTest.CoefficientVO[], index: num
|
||||
height: 470px;
|
||||
}
|
||||
|
||||
/* .el-icon svg {
|
||||
color: #ff7171;
|
||||
} */
|
||||
|
||||
.icon-style {
|
||||
color: #ff7171;
|
||||
|
||||
@@ -1,202 +0,0 @@
|
||||
<template>
|
||||
<!-- 基础信息弹出框 -->
|
||||
<el-dialog :model-value="visible" title="被检监测点匹配" v-bind="dialogSmall" @close="handleCancel" width="500" draggable>
|
||||
<div>
|
||||
<el-form :model="formData" ref='formRuleRef' :rules='rules' change-event="selectChange">
|
||||
<el-form-item label="监测点名称:" :label-width="100">
|
||||
<el-tree-select v-model="value1" :data="sourcesList" style="width: 240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="监测点名称:" :label-width="100">
|
||||
<el-tree-select v-model="value2" :data="sourcesList" style="width: 240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="监测点名称:" :label-width="100">
|
||||
<el-tree-select v-model="value3" :data="sourcesList" style="width: 240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="监测点名称:" :label-width="100">
|
||||
<el-tree-select v-model="value4" :data="sourcesList" style="width: 240px" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="handleCancel">取 消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import{ElMessage, FormInstance,FormItemRule}from'element-plus'
|
||||
import { defineProps, defineEmits, reactive,watch,ref, Ref } from 'vue';
|
||||
import { dialogSmall} from '@/utils/elementBind'
|
||||
import {dictPattern,dictTestState,dictReportState,dictResult,testPlanDataList,sourceDataList,deviceDataList,testSoureDataList,testScriptDataList,testErrSystDataList,planData,testFatherPlanList} from '@/api/plan/planData'
|
||||
|
||||
const selectChange = (val: any) => {
|
||||
console.log(val)
|
||||
}
|
||||
const value1 = ref()
|
||||
const value2 = ref()
|
||||
const value3 = ref()
|
||||
const value4 = ref()
|
||||
|
||||
const sourcesList = [
|
||||
{
|
||||
value: '1',
|
||||
label: '高精度设备-PQV520-1',
|
||||
disabled: false,
|
||||
children: [
|
||||
{
|
||||
value: '1-1',
|
||||
label: '监测点1',
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
value: '1-2',
|
||||
label: '监测点2',
|
||||
disabled: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: '2',
|
||||
label: '高精度设备-PQV520-2',
|
||||
disabled: false,
|
||||
children: [
|
||||
{
|
||||
value: '2-1',
|
||||
label: '监测点1',
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
value: '2-2',
|
||||
label: '监测点2',
|
||||
disabled: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: '3',
|
||||
label: '高精度设备-PQV520-3',
|
||||
disabled: false,
|
||||
children: [
|
||||
{
|
||||
value: '3-1',
|
||||
label: '监测点1',
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
value: '3-2',
|
||||
label: '监测点2',
|
||||
disabled: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: '4',
|
||||
label: '高精度设备-PQV520-4',
|
||||
disabled: false,
|
||||
children: [
|
||||
{
|
||||
value: '4-1',
|
||||
label: '监测点1',
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
value: '4-2',
|
||||
label: '监测点2',
|
||||
disabled: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
}>();
|
||||
|
||||
const fatherPlanList = [
|
||||
{ label: '/', value: 'type0' },
|
||||
{ label: '检测计划1', value: 'type1' },
|
||||
{ label: '检测计划2', value: 'type2' },
|
||||
{ label: '检测计划3', value: 'type3' },
|
||||
{ label: '检测计划4', value: 'type4' },
|
||||
];
|
||||
|
||||
const sourceList = [
|
||||
{ label: '分钟统计数据最大值', value: 'type0' },
|
||||
{ label: '分钟统计数据最大值', value: 'type1' },
|
||||
{ label: '分钟统计数据CP95值', value: 'type2' },
|
||||
];
|
||||
|
||||
const scriptList = [
|
||||
{ label: '/', value: 'type0' },
|
||||
{ label: '国网入网检测脚本(单影响量-模拟式)', value: 'type1' },
|
||||
{ label: '国网入网检测脚本(Q/GDW 10650.4 - 2021) 数字式', value: 'type1' },
|
||||
];
|
||||
|
||||
const errorList = [
|
||||
{ label: 'Q/GDW 1650.2- 2016', value: 'type0' },
|
||||
{ label: 'Q/GDW 10650.2 - 2021', value: 'type1' },
|
||||
];
|
||||
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void;
|
||||
(e: 'submit', data: any): void;
|
||||
}>();
|
||||
|
||||
// 定义规则
|
||||
const formRuleRef = ref<FormInstance>()
|
||||
const rules: Ref<Record<string, Array<FormItemRule>>> = ref({
|
||||
name: [{ required: true, message: '检测计划名称必填!', trigger: 'blur' }],
|
||||
source_Id: [{ required: true, message: '检测源必选!', trigger: 'blur' }],
|
||||
dataSource_Id: [{ required: true, message: '数据源必选!', trigger: 'blur' }],
|
||||
script_Id: [{ required: true, message: '检测脚本必选!', trigger: 'blur' }],
|
||||
error_Sys_Id: [{ required: true, message: '误差体系必选!', trigger: 'blur' }],
|
||||
// name: [{ required: true, message: '检测计划名称必填!', trigger: 'blur' }],
|
||||
// father_Plan_Id: [{ required: true, message: '参照标准名称必填!', trigger: 'change' }],
|
||||
|
||||
});
|
||||
|
||||
const handleCancel = () => {
|
||||
//重置表单内容
|
||||
//取消表单校验状态
|
||||
formRuleRef.value && formRuleRef.value.resetFields()
|
||||
emit('update:visible', false); // 关闭对话框
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
|
||||
try {
|
||||
formRuleRef.value?.validate((valid: boolean) => {
|
||||
if (valid)
|
||||
{
|
||||
// 将表单数据转为json,发送到后端
|
||||
let confirmFormData = JSON.parse(JSON.stringify(props.formData));
|
||||
//console.log(confirmFormData)
|
||||
emit('submit', props.formData); // 提交表单数据
|
||||
emit('update:visible', false); // 提交后关闭对话框
|
||||
}
|
||||
else
|
||||
{
|
||||
ElMessage.error('请填选必填项!')
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('验证过程中发生错误', error)
|
||||
}
|
||||
};
|
||||
|
||||
// 当 props.visible 改变时,更新 formData
|
||||
watch(() => props.visible, (newVal) => {
|
||||
if (!newVal) {
|
||||
// 这里可以重置表单数据,如果需要的话
|
||||
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -11,8 +11,7 @@
|
||||
|
||||
<div class="test-dialog">
|
||||
<div class="dialog-left">
|
||||
<el-steps direction="vertical" :active="activeIndex" :process-status="currentStepStatus"
|
||||
finish-status="success">
|
||||
<el-steps direction="vertical" :active="activeIndex" finish-status="success">
|
||||
<el-step :status="step1" title="源通讯校验"/>
|
||||
<el-step :status="step2" title="设备通讯校验"/>
|
||||
<el-step :status="step3" title="协议校验"/>
|
||||
@@ -67,9 +66,19 @@
|
||||
|
||||
</template>
|
||||
<script lang="tsx" setup name="preTest">
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
import {defineExpose, toRef} from 'vue';
|
||||
/**
|
||||
* 预检测组件
|
||||
* 负责电力设备检测中的预检测阶段,包含4个步骤:
|
||||
* 1. 源通讯校验 - 验证源端通讯是否正常
|
||||
* 2. 设备通讯校验 - 检查设备IP、端口、识别码、密钥
|
||||
* 3. 协议校验 - 进行ICD报告触发测试
|
||||
* 4. 相序校验 - 判断装置接线是否正确
|
||||
*/
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ref, toRef, watch } from 'vue'
|
||||
|
||||
// ==================== 日志数据存储 ====================
|
||||
// 各步骤的日志数据,用于在右侧折叠面板中显示实时日志
|
||||
const step1InitLog = ref([
|
||||
{
|
||||
type: 'info',
|
||||
@@ -77,13 +86,6 @@ const step1InitLog = ref([
|
||||
},
|
||||
])
|
||||
|
||||
const step1Log = ref([
|
||||
{
|
||||
type: 'info',
|
||||
log: '源通讯校验成功',
|
||||
},
|
||||
])
|
||||
|
||||
const step2InitLog = ref([
|
||||
{
|
||||
type: 'info',
|
||||
@@ -91,25 +93,6 @@ const step2InitLog = ref([
|
||||
},
|
||||
])
|
||||
|
||||
const step2Log = ref([
|
||||
{
|
||||
type: 'info',
|
||||
log: '被检设备:240001通讯校验成功',
|
||||
},
|
||||
{
|
||||
type: 'info',
|
||||
log: '被检设备:240002通讯校验成功',
|
||||
},
|
||||
{
|
||||
type: 'info',
|
||||
log: '被检设备:240003通讯校验成功',
|
||||
},
|
||||
{
|
||||
type: 'info',
|
||||
log: '被检设备:240004通讯校验成功',
|
||||
},
|
||||
])
|
||||
|
||||
const step3InitLog = ref([
|
||||
{
|
||||
type: 'info',
|
||||
@@ -117,26 +100,6 @@ const step3InitLog = ref([
|
||||
},
|
||||
])
|
||||
|
||||
const step3Log = ref([
|
||||
{
|
||||
type: 'info',
|
||||
log: '被检设备:240001协议校验成功',
|
||||
},
|
||||
{
|
||||
type: 'info',
|
||||
log: '被检设备:240002协议校验成功',
|
||||
},
|
||||
{
|
||||
type: 'info',
|
||||
log: '被检设备:240003协议校验成功',
|
||||
},
|
||||
{
|
||||
type: 'info',
|
||||
log: '被检设备:240004协议校验成功',
|
||||
},
|
||||
])
|
||||
|
||||
|
||||
const step4InitLog = ref([
|
||||
{
|
||||
type: 'info',
|
||||
@@ -144,35 +107,21 @@ const step4InitLog = ref([
|
||||
},
|
||||
])
|
||||
|
||||
const step4Log = ref([
|
||||
{
|
||||
type: 'info',
|
||||
log: '被检设备:240001相序校验成功',
|
||||
},
|
||||
{
|
||||
type: 'info',
|
||||
log: '被检设备:240002相序校验成功',
|
||||
},
|
||||
{
|
||||
type: 'info',
|
||||
log: '被检设备:240003相序校验成功',
|
||||
},
|
||||
{
|
||||
type: 'info',
|
||||
log: '被检设备:240004相序校验成功',
|
||||
},
|
||||
])
|
||||
// ==================== 界面状态控制 ====================
|
||||
const collapseActiveName = ref('1') // 当前展开的折叠面板
|
||||
const activeIndex = ref(0) // 当前激活的步骤索引(用于步骤条高亮)
|
||||
const activeTotalNum = ref(5) // 总步骤数
|
||||
|
||||
const collapseActiveName = ref('1')
|
||||
const activeIndex = ref(0)
|
||||
const activeTotalNum = ref(5)
|
||||
const step1 = ref('wait')
|
||||
const step2 = ref('wait')
|
||||
const step3 = ref('wait')
|
||||
const step4 = ref('wait')
|
||||
const step5 = ref('wait')
|
||||
// ==================== 步骤状态管理 ====================
|
||||
// 各步骤的执行状态:wait/process/success/error
|
||||
const step1 = ref('wait') // 源通讯校验状态
|
||||
const step2 = ref('wait') // 设备通讯校验状态
|
||||
const step3 = ref('wait') // 协议校验状态
|
||||
const step4 = ref('wait') // 相序校验状态
|
||||
const step5 = ref('wait') // 整体检测结果状态
|
||||
|
||||
//定义与预检测配置数组
|
||||
// ==================== 预检测项目配置 ====================
|
||||
// 定义预检测包含的检测项目(顶部tabs显示用,只读)
|
||||
const detectionOptions = ref([
|
||||
{
|
||||
id: 0,
|
||||
@@ -193,45 +142,67 @@ const detectionOptions = ref([
|
||||
id: 3,
|
||||
name: "相序校验",//判断装置的接线是否正确
|
||||
selected: true,
|
||||
},
|
||||
// {
|
||||
// id: 4,
|
||||
// name: "守时校验",//判断装置24小时内的守时误差是否小于1s
|
||||
// selected: true,
|
||||
// },
|
||||
// {
|
||||
// id: 5,
|
||||
// name: "通道系数校准",//通过私有协议与装置进行通讯,校准三相电压电流的通道系数
|
||||
// selected: true,
|
||||
// },
|
||||
// {
|
||||
// id: 6,
|
||||
// name: "实时数据比对",
|
||||
// },
|
||||
// {
|
||||
// id: 7,
|
||||
// name: "录波数据比对",
|
||||
// },
|
||||
}
|
||||
]);
|
||||
|
||||
const currentStepStatus = ref<'error' | 'finish' | 'wait' | 'success' | 'process'>('finish');
|
||||
|
||||
// ==================== 组件Props定义 ====================
|
||||
const props = defineProps({
|
||||
testStatus: {
|
||||
type: String,
|
||||
default: 'wait'
|
||||
default: 'wait' // 从父组件接收的测试状态
|
||||
},
|
||||
webMsgSend: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
default: () => ({}) // 从父组件接收的WebSocket消息
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// ==================== 响应式Props引用 ====================
|
||||
const testStatus = toRef(props, 'testStatus');
|
||||
const webMsgSend = toRef(props, 'webMsgSend');
|
||||
const ts = ref('');
|
||||
const ts = ref(''); // 内部测试状态,用于向父组件同步
|
||||
|
||||
// ==================== 错误处理函数 ====================
|
||||
/**
|
||||
* 处理致命错误 - 会终止整个检测流程
|
||||
* @param stepRef 当前步骤状态引用
|
||||
* @param logRef 当前步骤日志引用
|
||||
* @param message 错误消息
|
||||
*/
|
||||
function handleFatalError(stepRef: any, logRef: any, message: string) {
|
||||
stepRef.value = 'error';
|
||||
ts.value = 'error';
|
||||
step5.value = 'error';
|
||||
logRef.value.push({
|
||||
type: 'error',
|
||||
log: message
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理警告错误 - 只标记当前步骤失败,检测可继续
|
||||
* @param stepRef 当前步骤状态引用
|
||||
* @param logRef 当前步骤日志引用
|
||||
* @param message 错误消息
|
||||
*/
|
||||
function handleWarningError(stepRef: any, logRef: any, message: string) {
|
||||
stepRef.value = 'error';
|
||||
logRef.value.push({
|
||||
type: 'error',
|
||||
log: message
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== WebSocket消息处理 ====================
|
||||
/**
|
||||
* 监听WebSocket消息,根据不同的requestId和operateCode处理各种检测状态
|
||||
* 主要消息类型:
|
||||
* - yjc_ytxjy: 源通讯校验
|
||||
* - yjc_sbtxjy: 设备通讯校验
|
||||
* - yjc_xyjy: 协议校验
|
||||
* - YJC_xujy: 相序校验
|
||||
*/
|
||||
watch(webMsgSend, function (newValue, oldValue) {
|
||||
if (testStatus.value !== 'waiting') {
|
||||
switch (newValue.requestId) {
|
||||
@@ -254,33 +225,16 @@ watch(webMsgSend, function (newValue, oldValue) {
|
||||
}];
|
||||
} else if (newValue.code == 10552) {
|
||||
ElMessage.error(newValue.code)
|
||||
step1.value = 'error'
|
||||
ts.value = 'error'
|
||||
step5.value = 'error'
|
||||
handleFatalError(step1, step1InitLog, '重复的初始化操作!')
|
||||
} else if (newValue.code == 10523) {
|
||||
step1.value = 'error'
|
||||
ts.value = 'error'
|
||||
step5.value = 'error'
|
||||
step1InitLog.value = [{
|
||||
type: 'error',
|
||||
log: '源连接失败!',
|
||||
}];
|
||||
handleFatalError(step1, step1InitLog, '源连接失败!')
|
||||
} else if (newValue.code == -1) {
|
||||
step1.value = 'error'
|
||||
ts.value = 'error'
|
||||
step5.value = 'error'
|
||||
step1InitLog.value = [{
|
||||
type: 'error',
|
||||
log: '源未知异常!',
|
||||
}];
|
||||
handleFatalError(step1, step1InitLog, '源未知异常!')
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
break;
|
||||
case 'yjc_sbtxjy':
|
||||
|
||||
switch (newValue.operateCode) {
|
||||
case 'INIT_GATHER$01':
|
||||
if (newValue.code == 10200) {
|
||||
@@ -296,30 +250,11 @@ watch(webMsgSend, function (newValue, oldValue) {
|
||||
log: '正在进行设备通讯校验.....',
|
||||
}];
|
||||
} else if (newValue.code == 10550) {
|
||||
step2InitLog.value.push({
|
||||
type: 'error',
|
||||
log: newValue.data + '设备连接异常!',
|
||||
})
|
||||
step2.value = 'error'
|
||||
// ts.value = 'error'
|
||||
// step5.value = 'error'
|
||||
handleWarningError(step2, step2InitLog, newValue.data + '设备连接异常!')
|
||||
} else if (newValue.code == 10551) {
|
||||
step2InitLog.value.push({
|
||||
type: 'error',
|
||||
log: newValue.data + '设备触发报告异常!',
|
||||
})
|
||||
step2.value = 'error'
|
||||
ts.value = 'error'
|
||||
step5.value = 'error'
|
||||
handleFatalError(step2, step2InitLog, newValue.data + '设备触发报告异常!')
|
||||
} else if (newValue.code == 10552) {
|
||||
//ElMessage.error("存在已经初始化步骤,已经自动关闭,请重新发起检测!")
|
||||
step2InitLog.value = [{
|
||||
type: 'wait',
|
||||
log: '存在已经初始化步骤,执行自动关闭,请重新发起检测!',
|
||||
}];
|
||||
step2.value = 'error'
|
||||
ts.value = 'error'
|
||||
step5.value = 'error'
|
||||
handleFatalError(step2, step2InitLog, '存在已经初始化步骤,执行自动关闭,请重新发起检测!')
|
||||
} else if (newValue.code == 25001) {
|
||||
activeIndex.value = 2
|
||||
step2.value = 'success'
|
||||
@@ -327,7 +262,6 @@ watch(webMsgSend, function (newValue, oldValue) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'yjc_xyjy':
|
||||
switch (newValue.operateCode) {
|
||||
@@ -345,30 +279,11 @@ watch(webMsgSend, function (newValue, oldValue) {
|
||||
log: '正在进行通讯协议校验.....',
|
||||
}];
|
||||
} else if (newValue.code == 10550) {
|
||||
step3InitLog.value.push({
|
||||
type: 'error',
|
||||
log: newValue.data + '设备连接异常!',
|
||||
})
|
||||
step3.value = 'error'
|
||||
// ts.value = 'error'
|
||||
// step5.value = 'error'
|
||||
handleWarningError(step3, step3InitLog, newValue.data + '设备连接异常!')
|
||||
} else if (newValue.code == 10551) {
|
||||
step3InitLog.value.push({
|
||||
type: 'error',
|
||||
log: newValue.data + '设备触发报告异常!',
|
||||
})
|
||||
step3.value = 'error'
|
||||
ts.value = 'error'
|
||||
step5.value = 'error'
|
||||
handleFatalError(step3, step3InitLog, newValue.data + '设备触发报告异常!')
|
||||
} else if (newValue.code == 10552) {
|
||||
step3.value = 'error'
|
||||
//ElMessage.error("存在已经初始化步骤,已经自动关闭,请重新发起检测!")
|
||||
step3InitLog.value = [{
|
||||
type: 'wait',
|
||||
log: '存在已经初始化步骤,执行自动关闭,请重新发起检测!',
|
||||
}];
|
||||
ts.value = 'error'
|
||||
step5.value = 'error'
|
||||
handleFatalError(step3, step3InitLog, '存在已经初始化步骤,执行自动关闭,请重新发起检测!')
|
||||
}
|
||||
break;
|
||||
case 'INIT_GATHER$02':
|
||||
@@ -385,30 +300,11 @@ watch(webMsgSend, function (newValue, oldValue) {
|
||||
log: '正在进行通讯协议校验.....',
|
||||
}];
|
||||
} else if (newValue.code == 10550) {
|
||||
step3InitLog.value.push({
|
||||
type: 'error',
|
||||
log: newValue.data + '设备连接异常!',
|
||||
})
|
||||
step3.value = 'error'
|
||||
// ts.value = 'error'
|
||||
// step5.value = 'error'
|
||||
handleWarningError(step3, step3InitLog, newValue.data + '设备连接异常!')
|
||||
} else if (newValue.code == 10551) {
|
||||
step3InitLog.value.push({
|
||||
type: 'error',
|
||||
log: newValue.data + '设备触发报告异常!',
|
||||
})
|
||||
step3.value = 'error'
|
||||
ts.value = 'error'
|
||||
step5.value = 'error'
|
||||
handleFatalError(step3, step3InitLog, newValue.data + '设备触发报告异常!')
|
||||
} else if (newValue.code == 10552) {
|
||||
step3.value = 'error'
|
||||
//ElMessage.error("存在已经初始化步骤,已经自动关闭,请重新发起检测!")
|
||||
step3InitLog.value = [{
|
||||
type: 'wait',
|
||||
log: '存在已经初始化步骤,执行自动关闭,请重新发起检测!',
|
||||
}];
|
||||
ts.value = 'error'
|
||||
step5.value = 'error'
|
||||
handleFatalError(step3, step3InitLog, '存在已经初始化步骤,执行自动关闭,请重新发起检测!')
|
||||
}
|
||||
break;
|
||||
case 'INIT_GATHER$03':
|
||||
@@ -421,30 +317,11 @@ watch(webMsgSend, function (newValue, oldValue) {
|
||||
} else if (newValue.code == 10201) {
|
||||
step3.value = 'process'
|
||||
} else if (newValue.code == 10550) {
|
||||
step3InitLog.value.push({
|
||||
type: 'error',
|
||||
log: newValue.data + '设备连接异常!',
|
||||
})
|
||||
step3.value = 'error'
|
||||
// ts.value = 'error'
|
||||
// step5.value = 'error'
|
||||
handleWarningError(step3, step3InitLog, newValue.data + '设备连接异常!')
|
||||
} else if (newValue.code == 10551) {
|
||||
step3InitLog.value.push({
|
||||
type: 'error',
|
||||
log: newValue.data + '设备触发报告异常!',
|
||||
})
|
||||
step3.value = 'error'
|
||||
ts.value = 'error'
|
||||
step5.value = 'error'
|
||||
handleFatalError(step3, step3InitLog, newValue.data + '设备触发报告异常!')
|
||||
} else if (newValue.code == 10552) {
|
||||
//ElMessage.error("当前步骤已经初始化,执行自动关闭,请重新发起检测!")
|
||||
step3.value = 'error'
|
||||
step3InitLog.value = [{
|
||||
type: 'wait',
|
||||
log: '存在已经初始化步骤,执行自动关闭,请重新发起检测!',
|
||||
}];
|
||||
ts.value = 'error'
|
||||
step5.value = 'error'
|
||||
handleFatalError(step3, step3InitLog, '存在已经初始化步骤,执行自动关闭,请重新发起检测!')
|
||||
}
|
||||
break;
|
||||
case 'VERIFY_MAPPING$01':
|
||||
@@ -452,15 +329,11 @@ watch(webMsgSend, function (newValue, oldValue) {
|
||||
activeIndex.value = 3
|
||||
step3.value = 'success'
|
||||
step4.value = 'process'
|
||||
} else if (newValue.code == 10200) {
|
||||
} else if (newValue.code == 25002) {
|
||||
let data = JSON.parse(newValue.data)
|
||||
step3InitLog.value.push({
|
||||
type: 'error',
|
||||
log: `脚本与icd检验失败! icd名称:${data['icdType']} -> 校验项:${data['dataType']}`,
|
||||
})
|
||||
step3.value = 'error'
|
||||
ts.value = 'error'
|
||||
step5.value = 'error'
|
||||
handleFatalError(step3, step3InitLog, `脚本与icd检验失败! icd名称:${data['icdType']} -> 校验项:${data['dataType']}`)
|
||||
} else if (newValue.code == 10500) {
|
||||
handleFatalError(step3, step3InitLog, '装置中未找到该icd!')
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -476,53 +349,36 @@ watch(webMsgSend, function (newValue, oldValue) {
|
||||
|
||||
} else if (newValue.code == 10201) {
|
||||
step4.value = 'process'
|
||||
step4InitLog.value = [{
|
||||
step4InitLog.value.push({
|
||||
type: 'wait',
|
||||
log: '源参数下发中.....',
|
||||
}];
|
||||
});
|
||||
} else if (newValue.code == 10552) {
|
||||
ElMessage.error("存在已经初始化步骤,已经自动关闭,请重新发起检测!")
|
||||
step4.value = 'error'
|
||||
ts.value = 'error'
|
||||
step5.value = 'error'
|
||||
handleFatalError(step4, step4InitLog, '存在已经初始化步骤,执行自动关闭,请重新发起检测!')
|
||||
} else if (newValue.code == 10520) {
|
||||
step4.value = 'error'
|
||||
step4InitLog.value.push({
|
||||
type: 'error',
|
||||
log: '解析报文异常',
|
||||
})
|
||||
ts.value = 'error'
|
||||
step5.value = 'error'
|
||||
handleFatalError(step4, step4InitLog, '解析报文异常')
|
||||
}
|
||||
break;
|
||||
|
||||
case 'DATA_REQUEST$02':
|
||||
if (newValue.code == 10200) {
|
||||
let type = 'info'
|
||||
if (newValue.data.includes('不合格')) {
|
||||
type = 'error'
|
||||
}
|
||||
|
||||
newValue.data.split('<br/>')
|
||||
step4InitLog.value.push({
|
||||
type: type,
|
||||
log: newValue.data,
|
||||
})
|
||||
|
||||
} else if (newValue.code == 10201) {
|
||||
step4.value = 'process'
|
||||
step4InitLog.value = [{
|
||||
step4InitLog.value.push({
|
||||
type: 'wait',
|
||||
log: '获取数据相序校验数据!',
|
||||
}];
|
||||
});
|
||||
} else if (newValue.code == 25003) {
|
||||
step4.value = 'error'
|
||||
step4InitLog.value.push({
|
||||
type: 'error',
|
||||
log: '相序校验未通过!',
|
||||
})
|
||||
ts.value = 'error'
|
||||
step5.value = 'error'
|
||||
handleFatalError(step4, step4InitLog, '相序校验未通过!')
|
||||
} else if (newValue.code == 25001) {
|
||||
step4.value = 'success'
|
||||
step5.value = 'success'
|
||||
@@ -536,47 +392,85 @@ watch(webMsgSend, function (newValue, oldValue) {
|
||||
console.log("@@@@", ts.value)
|
||||
break
|
||||
}
|
||||
|
||||
break;
|
||||
case 'quit':
|
||||
break;
|
||||
case 'connect':
|
||||
switch (newValue.operateCode) {
|
||||
case "Source":
|
||||
step1.value = 'error'
|
||||
step1InitLog.value = [{
|
||||
type: 'error',
|
||||
log: '源服务端连接失败!',
|
||||
}];
|
||||
ts.value = 'error'
|
||||
step5.value = 'error'
|
||||
handleFatalError(step1, step1InitLog, '源服务端连接失败!')
|
||||
break;
|
||||
case "Dev":
|
||||
step2.value = 'error'
|
||||
step2InitLog.value = [{
|
||||
type: 'error',
|
||||
log: '设备服务端连接失败!',
|
||||
}];
|
||||
ts.value = 'error'
|
||||
step5.value = 'error'
|
||||
handleFatalError(step2, step2InitLog, '设备服务端连接失败!')
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'unknown_operate':
|
||||
|
||||
break;
|
||||
case 'error_flow_end':
|
||||
ElMessageBox.alert(`当前流程存在异常结束!`, '检测失败', {
|
||||
confirmButtonText: '确定',
|
||||
type: 'error',
|
||||
})
|
||||
// 根据当前步骤选择对应的日志记录
|
||||
const currentStepLog = activeIndex.value === 0 ? step1InitLog :
|
||||
activeIndex.value === 1 ? step2InitLog :
|
||||
activeIndex.value === 2 ? step3InitLog : step4InitLog
|
||||
const currentStep = activeIndex.value === 0 ? step1 :
|
||||
activeIndex.value === 1 ? step2 :
|
||||
activeIndex.value === 2 ? step3 : step4
|
||||
handleFatalError(currentStep, currentStepLog, '设备连接异常,检测终止!')
|
||||
break;
|
||||
case 'socket_timeout':
|
||||
ElMessageBox.alert(`设备连接异常,请检查设备连接情况!`, '检测失败', {
|
||||
confirmButtonText: '确定',
|
||||
type: 'error',
|
||||
})
|
||||
ts.value = 'error'
|
||||
step5.value = 'error'
|
||||
// 根据当前步骤选择对应的日志记录
|
||||
const timeoutStepLog = activeIndex.value === 0 ? step1InitLog :
|
||||
activeIndex.value === 1 ? step2InitLog :
|
||||
activeIndex.value === 2 ? step3InitLog : step4InitLog
|
||||
const timeoutStep = activeIndex.value === 0 ? step1 :
|
||||
activeIndex.value === 1 ? step2 :
|
||||
activeIndex.value === 2 ? step3 : step4
|
||||
handleFatalError(timeoutStep, timeoutStepLog, 'Socket连接超时,检测终止!')
|
||||
break;
|
||||
case 'server_error':
|
||||
ElMessageBox.alert('服务端主动关闭连接!', '初始化失败', {
|
||||
confirmButtonText: '确定',
|
||||
type: 'error',
|
||||
})
|
||||
// 根据当前步骤选择对应的日志记录
|
||||
const serverStepLog = activeIndex.value === 0 ? step1InitLog :
|
||||
activeIndex.value === 1 ? step2InitLog :
|
||||
activeIndex.value === 2 ? step3InitLog : step4InitLog
|
||||
const serverStep = activeIndex.value === 0 ? step1 :
|
||||
activeIndex.value === 1 ? step2 :
|
||||
activeIndex.value === 2 ? step3 : step4
|
||||
handleFatalError(serverStep, serverStepLog, '服务端主动关闭连接!')
|
||||
break;
|
||||
case 'device_error':
|
||||
ElMessageBox.alert('设备主动关闭连接!', '初始化失败', {
|
||||
confirmButtonText: '确定',
|
||||
type: 'error',
|
||||
})
|
||||
// 根据当前步骤选择对应的日志记录
|
||||
const deviceStepLog = activeIndex.value === 0 ? step1InitLog :
|
||||
activeIndex.value === 1 ? step2InitLog :
|
||||
activeIndex.value === 2 ? step3InitLog : step4InitLog
|
||||
const deviceStep = activeIndex.value === 0 ? step1 :
|
||||
activeIndex.value === 1 ? step2 :
|
||||
activeIndex.value === 2 ? step3 : step4
|
||||
handleFatalError(deviceStep, deviceStepLog, '设备主动关闭连接!')
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// ==================== 界面联动控制 ====================
|
||||
/**
|
||||
* 监听当前激活步骤,自动展开对应的日志折叠面板
|
||||
*/
|
||||
watch(activeIndex, function (newValue, oldValue) {
|
||||
if (newValue <= activeTotalNum.value - 2) {
|
||||
collapseActiveName.value = (newValue + 1).toString()
|
||||
@@ -585,7 +479,10 @@ watch(activeIndex, function (newValue, oldValue) {
|
||||
}
|
||||
})
|
||||
|
||||
//监听goods_sn的变化
|
||||
/**
|
||||
* 监听父组件传入的测试状态变化
|
||||
* 处理测试开始和重置逻辑
|
||||
*/
|
||||
watch(testStatus, function (newValue, oldValue) {
|
||||
ts.value = props.testStatus;
|
||||
if (ts.value === 'start') {
|
||||
@@ -606,15 +503,23 @@ watch(testStatus, function (newValue, oldValue) {
|
||||
}
|
||||
})
|
||||
|
||||
// ==================== 父子组件通信 ====================
|
||||
const emit = defineEmits(['update:testStatus']);
|
||||
//监听sn
|
||||
|
||||
/**
|
||||
* 监听内部测试状态变化,同步给父组件
|
||||
* 实现双向数据绑定
|
||||
*/
|
||||
watch(ts, function (newValue, oldValue) {
|
||||
//修改父组件
|
||||
emit('update:testStatus', ts.value)
|
||||
})
|
||||
|
||||
|
||||
// 定义一个初始化参数的方法
|
||||
// ==================== 对外暴露方法 ====================
|
||||
/**
|
||||
* 初始化参数方法
|
||||
* 由父组件调用,用于重置所有步骤状态和日志
|
||||
*/
|
||||
function initializeParameters() {
|
||||
activeIndex.value = 0
|
||||
step1.value = 'process'
|
||||
@@ -651,6 +556,7 @@ function initializeParameters() {
|
||||
}
|
||||
|
||||
|
||||
// 暴露方法给父组件使用
|
||||
defineExpose({
|
||||
initializeParameters,
|
||||
});
|
||||
@@ -663,9 +569,6 @@ defineExpose({
|
||||
flex-direction: row;
|
||||
/* 横向排列 */
|
||||
margin-top: 20px;
|
||||
/* .dialog-left{
|
||||
margin-right: 20px;
|
||||
} */
|
||||
}
|
||||
|
||||
.dialog-left {
|
||||
@@ -673,19 +576,6 @@ defineExpose({
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
/* .dialog-left :deep(.test-head-steps){
|
||||
height: 80px;
|
||||
/* margin-bottom: 10px;
|
||||
}
|
||||
*/
|
||||
|
||||
/* .dialog-left :deep(.el-step__title) {
|
||||
font-size: 18px !important; /* 设置标题字体大小
|
||||
} */
|
||||
|
||||
/* .dialog-left :deep(.el-step__icon-inner) {
|
||||
font-size: 24px !important;
|
||||
} */
|
||||
|
||||
.dialog-right {
|
||||
margin-left: 20px;
|
||||
|
||||
292
frontend/src/views/home/components/realTimeDataAlign.vue
Normal file
292
frontend/src/views/home/components/realTimeDataAlign.vue
Normal file
@@ -0,0 +1,292 @@
|
||||
<template>
|
||||
<el-dialog title="实时数据详情" v-model='dialogVisible' @close="handleClose" v-bind="dialogBig">
|
||||
<el-tabs v-model="activeTab" type="card">
|
||||
<el-tab-pane
|
||||
v-for="(device, deviceName, index) in testDataStructure"
|
||||
:key="deviceName"
|
||||
:name="`channel${index + 1}`">
|
||||
<template #label>
|
||||
<span>
|
||||
{{ deviceName }}
|
||||
<el-icon v-if="tabStatus[deviceName]" style="color: red; margin-left: 5px;">
|
||||
<CircleClose />
|
||||
</el-icon>
|
||||
<el-icon v-else style="color: green; margin-left: 5px;">
|
||||
<CircleCheck />
|
||||
</el-icon>
|
||||
</span>
|
||||
</template>
|
||||
<div class="table-toolbar">
|
||||
<el-form-item label="标准设备通道号" prop="createId">
|
||||
<el-select
|
||||
v-model="selectedChannels[deviceName]"
|
||||
placeholder="选择通道"
|
||||
style="width: 150px;"
|
||||
@change="() => handleDutChannelChange(deviceName)">
|
||||
<el-option
|
||||
v-for="channel in device.channelDataList"
|
||||
:key="channel.stdDevNum"
|
||||
:label="`通道${channel.stdDevNum}`"
|
||||
:value="`通道${channel.stdDevNum}`">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<span style="margin-left: 20px; font-size: 14px; color: var(--el-color-primary);">
|
||||
标准设备:{{ deviceName }}-{{ selectedChannels[deviceName] }} ---> 被检设备:{{ formatDutChannelLabel(getMappedDutChannel(deviceName, selectedChannels[deviceName])) }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-button type="primary" @click="exportData">导出数据</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
:data="tableDataMap[deviceName]"
|
||||
:header-cell-style="{ textAlign: 'center',backgroundColor: 'var(--el-color-primary)',color: '#fff' } "
|
||||
:cell-style="{ textAlign: 'center' }"
|
||||
style="width: 100%"
|
||||
:style="{ height: '400px',maxHeight: '400px',overflow:'hidden'}">
|
||||
|
||||
<el-table-column :label="`${deviceName}-${selectedChannels[deviceName] || '通道1'}`">
|
||||
<el-table-column prop="timeStdDev" label="数据时标" width="200"/>
|
||||
<el-table-column prop="uaStdDev" label="A相(V)"/>
|
||||
<el-table-column prop="ubStdDev" label="B相(V)"/>
|
||||
<el-table-column prop="ucStdDev" label="C相(V)"/>
|
||||
</el-table-column>
|
||||
<el-table-column :label="formatDutChannelLabel(getMappedDutChannel(deviceName, selectedChannels[deviceName]))">
|
||||
<el-table-column prop="timeDev" label="数据时标" width="200"/>
|
||||
<el-table-column prop="uaDev" label="A相(V)"/>
|
||||
<el-table-column prop="ubDev" label="B相(V)"/>
|
||||
<el-table-column prop="ucDev" label="C相(V)"/>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang='tsx' name='realTimeDataAlign'>
|
||||
import { dialogBig } from "@/utils/elementBind";
|
||||
import { PropType, ref, nextTick } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { CircleCheck, CircleClose } from '@element-plus/icons-vue';
|
||||
import {exportAlignData} from "@/api/socket/socket";
|
||||
import {useDownload} from "@/hooks/useDownload";
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
const activeTab = ref('channel1');
|
||||
|
||||
// 在 script setup 中定义接口
|
||||
interface ChannelData {
|
||||
stdDevNum: string;
|
||||
devInfo: 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 {
|
||||
stdDevName: string;
|
||||
channelDataList: ChannelData[];
|
||||
}
|
||||
|
||||
// 修改 testDataStructure 的类型声明
|
||||
const testDataStructure = ref<Record<string, DeviceData>>({});
|
||||
|
||||
// 每个设备选中的通道
|
||||
const selectedChannels = ref<Record<string, string>>({});
|
||||
|
||||
// 通道映射关系:标准设备通道 -> 被检设备通道
|
||||
const channelMapping = ref<Record<string, Record<string, string>>>({});
|
||||
|
||||
// 每个设备的表格数据
|
||||
const tableDataMap = ref<Record<string, any[]>>({});
|
||||
|
||||
// 每个tab的状态(true表示有不完整数据,false表示数据完整)
|
||||
const tabStatus = ref<Record<string, boolean>>({});
|
||||
|
||||
// 检查设备数据是否有不完整的行(包含null的行)
|
||||
const hasIncompleteData = (deviceName: string) => {
|
||||
const tableData = tableDataMap.value[deviceName];
|
||||
if (!tableData || tableData.length === 0) return false;
|
||||
|
||||
// 检查每一行是否有缺失数据(包含null的字段)
|
||||
return tableData.some(row => {
|
||||
return row.uaDev === '/' || row.ubDev === '/' || row.ucDev === '/' ||
|
||||
row.uaStdDev === '/' || row.ubStdDev === '/' || row.ucStdDev === '/';
|
||||
});
|
||||
};
|
||||
|
||||
// 获取映射的被检设备通道
|
||||
const getMappedDutChannel = (deviceName: string, stdChannel: string) => {
|
||||
|
||||
// 添加安全检查
|
||||
if (!channelMapping.value[deviceName]) return '';
|
||||
return channelMapping.value[deviceName][stdChannel] || '';
|
||||
};
|
||||
|
||||
// 格式化被检设备通道标签,将设备名称和通道号用"-"连接
|
||||
const formatDutChannelLabel = (dutChannel: string) => {
|
||||
// 如果是"被检设备X通道Y"格式,则转换为"被检设备X-通道Y"
|
||||
if (!dutChannel) return '未映射';
|
||||
return dutChannel.replace(/(.+)(通道\d+)/, '$1-$2');
|
||||
};
|
||||
|
||||
// 处理标准设备通道切换
|
||||
const handleDutChannelChange = (deviceName: string) => {
|
||||
// 更新指定设备的表格数据,但不改变tab图标状态
|
||||
updateTableData(deviceName);
|
||||
};
|
||||
|
||||
// 根据 testDataStructure 生成表格数据
|
||||
const generateTableData = (deviceName: string, stdChannel: string) => {
|
||||
const deviceData = testDataStructure.value[deviceName];
|
||||
if (!deviceData) return [];
|
||||
|
||||
// 根据实际通道编号查找对应的数据
|
||||
const channelData = deviceData.channelDataList.find(channel =>
|
||||
`通道${channel.stdDevNum}` === stdChannel
|
||||
);
|
||||
|
||||
if (!channelData) return [];
|
||||
|
||||
// 生成表格数据
|
||||
return channelData.dataList.map(dataItem => {
|
||||
return {
|
||||
timeDev: dataItem.timeDev !== null ? dataItem.timeDev : '/',
|
||||
uaDev: dataItem.uaDev !== null ? dataItem.uaDev : '/',
|
||||
ubDev: dataItem.ubDev !== null ? dataItem.ubDev : '/',
|
||||
ucDev: dataItem.ucDev !== null ? dataItem.ucDev : '/',
|
||||
timeStdDev: dataItem.timeStdDev !== null ? dataItem.timeStdDev : '/',
|
||||
uaStdDev: dataItem.uaStdDev !== null ? dataItem.uaStdDev : '/',
|
||||
ubStdDev: dataItem.ubStdDev !== null ? dataItem.ubStdDev : '/',
|
||||
ucStdDev: dataItem.ucStdDev !== null ? dataItem.ucStdDev : '/'
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// 更新指定设备的表格数据
|
||||
const updateTableData = (deviceName: string) => {
|
||||
const selectedChannel = selectedChannels.value[deviceName];
|
||||
if (selectedChannel) {
|
||||
const tableData = generateTableData(deviceName, selectedChannel);
|
||||
tableDataMap.value[deviceName] = tableData;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化所有设备的数据和状态
|
||||
const initAllTableData = () => {
|
||||
Object.keys(testDataStructure.value).forEach(deviceName => {
|
||||
// 确保设备有数据
|
||||
if (!testDataStructure.value[deviceName] ||
|
||||
!testDataStructure.value[deviceName].channelDataList ||
|
||||
testDataStructure.value[deviceName].channelDataList.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 默认选择第一个可用通道
|
||||
const firstChannel = testDataStructure.value[deviceName].channelDataList[0];
|
||||
selectedChannels.value[deviceName] = `通道${firstChannel.stdDevNum}`;
|
||||
|
||||
// 生成表格数据
|
||||
updateTableData(deviceName);
|
||||
|
||||
// 初始化tab状态(只在初始化时设置一次)
|
||||
if (tabStatus.value[deviceName] === undefined) {
|
||||
tabStatus.value[deviceName] = hasIncompleteData(deviceName);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const open = async (mapping : Record<string, Record<string, string>>,data : any) => {
|
||||
let parsedData = data;
|
||||
|
||||
// 如果 data 是字符串,先解析为对象
|
||||
if (typeof data === 'string') {
|
||||
try {
|
||||
parsedData = JSON.parse(data);
|
||||
} catch (error) {
|
||||
console.error('数据解析失败:', error);
|
||||
ElMessage.error('数据格式错误');
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 转换数据格式以匹配组件期望的格式
|
||||
const convertedData: Record<string, DeviceData> = {};
|
||||
|
||||
// 假设传入的数据是一个数组,需要转换为以设备名为键的对象
|
||||
if (Array.isArray(parsedData)) {
|
||||
|
||||
parsedData.forEach((deviceItem: any) => {
|
||||
const deviceName = deviceItem.stdDevName;
|
||||
convertedData[deviceName] = {
|
||||
stdDevName: deviceName,
|
||||
channelDataList: deviceItem.channelDataList?.map((channel: any) => ({
|
||||
stdDevNum: channel.stdDevNum,
|
||||
devInfo: channel.devInfo,
|
||||
dataList: channel.dataList?.map((dataItem: any) => ({
|
||||
timeDev: dataItem.timeDev,
|
||||
uaDev: dataItem.uaDev,
|
||||
ubDev: dataItem.ubDev,
|
||||
ucDev: dataItem.ucDev,
|
||||
timeStdDev: dataItem.timeStdDev,
|
||||
uaStdDev: dataItem.uaStdDev,
|
||||
ubStdDev: dataItem.ubStdDev,
|
||||
ucStdDev: dataItem.ucStdDev
|
||||
})) || []
|
||||
})) || []
|
||||
};
|
||||
});
|
||||
} else if (parsedData && typeof parsedData === 'object') {
|
||||
|
||||
// 如果已经是期望的格式,直接使用
|
||||
Object.assign(convertedData, parsedData);
|
||||
}
|
||||
|
||||
testDataStructure.value = convertedData;
|
||||
|
||||
channelMapping.value = mapping;
|
||||
dialogVisible.value = true;
|
||||
// 使用 nextTick 确保 DOM 更新后再初始化数据
|
||||
await nextTick();
|
||||
// 初始化数据和状态
|
||||
initAllTableData();
|
||||
|
||||
// 设置默认激活的 tab
|
||||
activeTab.value = 'channel1';
|
||||
};
|
||||
|
||||
// 导出数据
|
||||
const exportData =async () => {
|
||||
useDownload(exportAlignData, '原始数据', null, false, '.xlsx')
|
||||
ElMessage.success('数据导出成功');
|
||||
// 这里可以添加实际的数据导出逻辑
|
||||
};
|
||||
|
||||
// 关闭弹窗
|
||||
const handleClose = () => {
|
||||
dialogVisible.value = false;
|
||||
// 清空数据
|
||||
testDataStructure.value = {};
|
||||
tableDataMap.value = {};
|
||||
selectedChannels.value = {};
|
||||
tabStatus.value = {};
|
||||
};
|
||||
|
||||
defineExpose({ open });
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.table-toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
:deep(.el-dialog__body) {
|
||||
padding: 10px 20px 20px 20px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,154 +0,0 @@
|
||||
<template>
|
||||
<el-dialog title="报告生成" :model-value='visible' @close="handleCancel" width="832px" draggable>
|
||||
<div class="report-dialog">
|
||||
<div class="report-title form-two">
|
||||
|
||||
<el-form-item label="检测脚本" label-width="100px">
|
||||
<el-input v-model='testScriptName' :disabled="true"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="误差体系" label-width="100px">
|
||||
<el-input v-model='errorSysName' :disabled="true"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="数据原则" label-width="100px">
|
||||
<el-input v-model='dataRule' :disabled="true"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="报告模板" label-width="100px">
|
||||
<el-input v-model='reportTemplate' :disabled="true"/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="report-content">
|
||||
<div>
|
||||
<el-tabs type="border-card">
|
||||
<el-tab-pane label="报告生成进度">
|
||||
<div class="form-grid">
|
||||
<div class="tabs-title ">
|
||||
<el-button type="primary" :icon="Download" >报告下载</el-button>
|
||||
<span style=" font-size: 18px;font-weight: 600;">
|
||||
已生成 <span style="color: #91cc75">2</span> 台/共 <span style="color: green">3</span> 台
|
||||
</span>
|
||||
|
||||
</div>
|
||||
<div class="table-main">
|
||||
<el-table :data="reportData" :header-cell-style="{ textAlign: 'center' } " :cell-style="{ textAlign: 'center' }" style="width: 100%" border class="custom-table">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column prop="id" width="70" label="序号" />
|
||||
<el-table-column prop="deviceName" width="150" label="设备名称" />
|
||||
<el-table-column label="生成进度">
|
||||
<template #default="scope">
|
||||
<el-progress :color="customColors" :percentage="scope.row.processValue" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="action" label="操作" width="100">
|
||||
<template #default="scope">
|
||||
<el-button type='primary' link :icon='Download' :disabled="scope.row.processValue < 100">下载</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
</template>
|
||||
<script setup lang='ts'>
|
||||
|
||||
import IPAddress from '@/components/IpAddress/index.vue'
|
||||
import { dialogBig } from '@/utils/elementBind'
|
||||
import { type Device } from '@/api/device/interface/device'
|
||||
import { ElMessage, type FormItemRule } from 'element-plus'
|
||||
import { addPqDev, updatePqDev } from '@/api/device/device'
|
||||
import { computed, reactive, type Ref, ref } from 'vue'
|
||||
import { useDictStore } from '@/stores/modules/dict'
|
||||
import { CirclePlus, Delete, Download,View } from '@element-plus/icons-vue'
|
||||
|
||||
|
||||
|
||||
const reportTemplate = ref('国网检测模板V1.0');
|
||||
const testScriptName = ref('Q/GDW 10650.4-2021 模拟式');
|
||||
const errorSysName = ref('Q/GDW 10650.2-2021');
|
||||
const dataRule = ref('所有值');
|
||||
const scriptSwitch = ref(true);
|
||||
const currentScriptDsc = ref('电压准确度检测:频率:42.5Hz Ua=46.192V 0° Ub=46.192V -120° Uc=46.192V 120° Ia=1A 0° Ib=1A -120° Ic=1A 120°');
|
||||
|
||||
const reportData = ref([
|
||||
{ id: '1', deviceName: '240001', processValue: '100' , action:'查看' },
|
||||
{ id: '2', deviceName: '240002', processValue: '100' , action:'查看' },
|
||||
{ id: '3', deviceName: '240003', processValue: '10', action:'查看' },
|
||||
])
|
||||
const customColors = [
|
||||
{ color: "red", percentage: 0 },
|
||||
{ color: "red", percentage: 10 },
|
||||
{ color: "red", percentage: 20 },
|
||||
{ color: "red", percentage: 30 }, //红
|
||||
{ color: "red", percentage: 40 },
|
||||
{ color: "#e6a23c", percentage: 50 },
|
||||
{ color: "#e6a23c", percentage: 60 },
|
||||
{ color: "#e6a23c", percentage: 70 }, //黄
|
||||
{ color: "#e6a23c", percentage: 80 }, //1989fa
|
||||
{ color: "#e6a23c", percentage: 90 }, //1989fa
|
||||
{ color: "#5cb87a", percentage: 100 }, //绿
|
||||
];
|
||||
const handleNodeClick = (data) => {
|
||||
console.log(data);
|
||||
};
|
||||
const MonIsShow = ref(false)
|
||||
const DevIsShow = ref(false)
|
||||
const IsPasswordShow = ref(false)
|
||||
const dictStore = useDictStore()
|
||||
// 定义弹出组件元信息
|
||||
const dialogFormRef = ref()
|
||||
const disabledDate = (time: Date) => {
|
||||
return time.getTime() > Date.now()
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void;
|
||||
(e: 'submit', data: any): void;
|
||||
}>();
|
||||
|
||||
const handleCancel = () => {
|
||||
emit('update:visible', false); // 关闭对话框
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
|
||||
.report-dialog{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.report-title{
|
||||
margin-left: 15px;
|
||||
|
||||
/* display: flex; */
|
||||
/* flex-direction: row;
|
||||
|
||||
margin-top: 10px; */
|
||||
}
|
||||
|
||||
.report-content{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.tabs-title{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.el-tabs__content{
|
||||
padding-top: 5px !important;
|
||||
}
|
||||
</style>
|
||||
267
frontend/src/views/home/components/reportResultPopup.vue
Normal file
267
frontend/src/views/home/components/reportResultPopup.vue
Normal file
@@ -0,0 +1,267 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="报告生成"
|
||||
destroy-on-close
|
||||
width="750"
|
||||
draggable
|
||||
:close-on-click-modal="false"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-tabs v-if="dialogVisible" v-model="activeName" @tab-click="handleTabClick">
|
||||
<el-tab-pane
|
||||
v-for="(result, index) in resultData"
|
||||
:key="result.monitorId"
|
||||
:label="`测量回路${result.monitorNum}`"
|
||||
:name="index"
|
||||
>
|
||||
<el-row :gutter="20" style="margin-top: 10px">
|
||||
<el-col :span="8">
|
||||
<div
|
||||
style="
|
||||
background-color: #f9fafb;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
"
|
||||
>
|
||||
<el-text style="margin-right: 10px">总测试次数:</el-text>
|
||||
<el-text size="large" type="primary" tag="b">
|
||||
{{ result.totalNum }}
|
||||
</el-text>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div
|
||||
style="
|
||||
background-color: #f9fafb;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
"
|
||||
>
|
||||
<el-text style="margin-right: 10px">符合次数:</el-text>
|
||||
<el-text size="large" type="success" tag="b">{{ result.qualifiedNum }}</el-text>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div
|
||||
style="
|
||||
background-color: #f9fafb;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
"
|
||||
>
|
||||
<el-text style="margin-right: 10px">不符合次数:</el-text>
|
||||
<el-text size="large" type="danger" tag="b">{{ result.unQualifiedNum }}</el-text>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-descriptions :column="1" style="margin-top: 20px">
|
||||
<el-descriptions-item label-align="right">
|
||||
<template #label>
|
||||
<el-text type="info">测试标准:</el-text>
|
||||
</template>
|
||||
<el-text>{{ result.errorSysName }}</el-text>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label-align="right">
|
||||
<template #label>
|
||||
<el-text type="info">检测结论:</el-text>
|
||||
</template>
|
||||
<el-tag disable-transitions v-if="result.checkResult === 1" type="success">符合</el-tag>
|
||||
<el-tag disable-transitions v-else-if="result.checkResult === 2" type="danger">不符合</el-tag>
|
||||
<el-tag disable-transitions v-else-if="result.checkResult === 4" type="danger">无法比较</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label-align="right">
|
||||
<template #label>
|
||||
<el-text type="info">结论来源:</el-text>
|
||||
</template>
|
||||
<el-text>第{{ result.whichTime }}次检测的{{ result.resultOrigin }}</el-text>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<template #footer>
|
||||
<el-button type="primary" size="small" @click="handleChooseClick">重新选择</el-button>
|
||||
<el-button type="primary" @click="handleConfirmGenerate">确认生成</el-button>
|
||||
</template>
|
||||
<!-- 选择检测数据源弹框-->
|
||||
<el-dialog
|
||||
v-model="dialogSourceVisible"
|
||||
append-to-body
|
||||
title="选择检测数据源"
|
||||
destroy-on-close
|
||||
width="400"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-form ref="formRef" :rules="rules" :model="submitSourceData" label-width="120px" label-position="top">
|
||||
<el-form-item label="选择次数:" prop="whichTime">
|
||||
<el-select v-model="submitSourceData.whichTime" placeholder="请选择次数" @change="handleTimeChange">
|
||||
<el-option
|
||||
v-for="time in whichTimeData"
|
||||
:key="time"
|
||||
:label="`第${time}次`"
|
||||
:value="time"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="数据源和检测结论:" prop="resultType">
|
||||
<el-select
|
||||
v-model="submitSourceData.resultType"
|
||||
placeholder="请选择数据源和检测结论"
|
||||
clearable
|
||||
@change="handleSourceChange"
|
||||
>
|
||||
<template #label="{ label }">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between">
|
||||
<el-text>{{ label }}</el-text>
|
||||
<el-tag disable-transitions v-if="submitSourceData.checkResult === 1" type="success">
|
||||
符合
|
||||
</el-tag>
|
||||
<el-tag disable-transitions v-if="submitSourceData.checkResult === 2" type="danger">
|
||||
不符合
|
||||
</el-tag>
|
||||
<el-tag disable-transitions v-if="submitSourceData.checkResult === 4" type="info">
|
||||
无法比较
|
||||
</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
<el-option
|
||||
v-for="item in sourceData"
|
||||
:key="item.dataSourceCode"
|
||||
:label="item.dataSourceName"
|
||||
:value="item.dataSourceCode"
|
||||
>
|
||||
<div style="display: flex; align-items: center; justify-content: space-between">
|
||||
<el-text>{{ item.dataSourceName }}</el-text>
|
||||
<el-tag v-if="item.checkResult === 1" type="success">符合</el-tag>
|
||||
<el-tag v-if="item.checkResult === 2" type="danger">不符合</el-tag>
|
||||
</div>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogSourceVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSureChoose">确认</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts" name="reportPopup">
|
||||
import { getMonitorDataSourceResult, getMonitorResult, updateMonitorResult } from '@/api/result/result'
|
||||
import { type MonitorResult } from '@/api/result/interface'
|
||||
import { generateDevReport } from '@/api/plan/plan'
|
||||
import { useCheckStore } from '@/stores/modules/check'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const dialogSourceVisible = ref(false)
|
||||
const devData = ref<any>()
|
||||
const activeName = ref<number>(0)
|
||||
const checkStore = useCheckStore()
|
||||
|
||||
// 定义 emit 事件
|
||||
const emit = defineEmits<{
|
||||
(e: 'reportGenerated'): void
|
||||
}>()
|
||||
const resultData = ref<MonitorResult[]>([])
|
||||
const resultSourceData = ref<any>({})
|
||||
const whichTimeData = ref<any>([])
|
||||
const sourceData = ref<any>([])
|
||||
const formRef = ref()
|
||||
const submitSourceData = reactive({
|
||||
monitorId: '',
|
||||
whichTime: '',
|
||||
resultType: '',
|
||||
checkResult: -1
|
||||
})
|
||||
|
||||
const rules = {
|
||||
whichTime: [{ required: true, message: '请选择次数', trigger: 'change' }],
|
||||
resultType: [{ required: true, message: '请选择数据源和检测结论', trigger: 'change' }]
|
||||
}
|
||||
const handleClose = () => {
|
||||
activeName.value = 0
|
||||
}
|
||||
const open = (data: any) => {
|
||||
devData.value = data
|
||||
getResultData()
|
||||
}
|
||||
const getResultData = async () => {
|
||||
const res = await getMonitorResult(devData.value.id)
|
||||
if (res.data && Array.isArray(res.data)) {
|
||||
resultData.value = res.data
|
||||
}
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const handleTabClick = (tab: any) => {
|
||||
activeName.value = tab.name
|
||||
}
|
||||
const handleChooseClick = async () => {
|
||||
const currentResult = resultData.value[activeName.value]
|
||||
if (currentResult) {
|
||||
submitSourceData.monitorId = currentResult.monitorId
|
||||
submitSourceData.whichTime = currentResult.whichTime
|
||||
submitSourceData.resultType = currentResult.resultType
|
||||
submitSourceData.checkResult = currentResult.checkResult
|
||||
const res = await getMonitorDataSourceResult(currentResult.monitorId)
|
||||
if (res.data) {
|
||||
resultSourceData.value = res.data
|
||||
// 选择第几次
|
||||
whichTimeData.value = Object.keys(resultSourceData.value)
|
||||
sourceData.value = resultSourceData.value[currentResult.whichTime]
|
||||
}
|
||||
}
|
||||
dialogSourceVisible.value = true
|
||||
}
|
||||
const handleTimeChange = (value: any) => {
|
||||
sourceData.value = resultSourceData.value[value]
|
||||
submitSourceData.resultType = ''
|
||||
submitSourceData.checkResult = -1
|
||||
}
|
||||
const handleSourceChange = (value: any) => {
|
||||
submitSourceData.checkResult = resultSourceData.value[submitSourceData.whichTime].find(
|
||||
(item: any) => item.dataSourceCode === value
|
||||
).checkResult
|
||||
}
|
||||
const handleSureChoose = () => {
|
||||
formRef.value.validate().then(async () => {
|
||||
await updateMonitorResult(submitSourceData)
|
||||
await getResultData()
|
||||
dialogSourceVisible.value = false
|
||||
})
|
||||
}
|
||||
|
||||
// 处理确认生成报告
|
||||
const handleConfirmGenerate = async () => {
|
||||
try {
|
||||
await generateDevReport({
|
||||
planId: checkStore.plan.id,
|
||||
devIdList: [devData.value.id],
|
||||
scriptId: checkStore.plan.scriptId,
|
||||
planCode: checkStore.plan.code + '',
|
||||
pageNum: 1,
|
||||
pageSize: 999
|
||||
})
|
||||
ElMessage.success({ message: `报告生成成功!` })
|
||||
dialogVisible.value = false
|
||||
emit('reportGenerated') // 触发事件通知父组件
|
||||
} catch (error) {
|
||||
ElMessage.error('报告生成失败')
|
||||
console.error('报告生成错误:', error)
|
||||
}
|
||||
}
|
||||
defineExpose({
|
||||
open
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="scss"></style>
|
||||
@@ -4,18 +4,27 @@
|
||||
<div class="result-title">
|
||||
<el-row>
|
||||
<el-form-item label="检测脚本">
|
||||
<el-input v-model='testScriptName' :disabled="true"/>
|
||||
<el-input v-model="testScriptName" :disabled="true" />
|
||||
</el-form-item>
|
||||
<el-form-item label="误差体系">
|
||||
<el-input v-model='errorSysName' :disabled="true"/>
|
||||
<el-input v-model="errorSysName" :disabled="true" />
|
||||
</el-form-item>
|
||||
<el-form-item label="数据原则">
|
||||
<el-input v-model='dataRule' :disabled="true"/>
|
||||
<el-input v-model="dataRule" :disabled="true" />
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
</div>
|
||||
<div class="result-content">
|
||||
<el-table :data="resultData" stripe max-height="350" :header-cell-style="{ textAlign: 'center' } " :cell-style="{ textAlign: 'center' }" style="width: 100%" border v-on:cell-click="handleClick">
|
||||
<el-table
|
||||
:data="resultData"
|
||||
stripe
|
||||
max-height="350"
|
||||
:header-cell-style="{ textAlign: 'center' }"
|
||||
:cell-style="{ textAlign: 'center' }"
|
||||
style="width: 100%"
|
||||
border
|
||||
v-on:cell-click="handleClick"
|
||||
>
|
||||
<el-table-column prop="deviceName" label="被检设备" />
|
||||
<el-table-column prop="result_1" label="通道1" />
|
||||
<el-table-column prop="result_2" label="通道2" />
|
||||
@@ -23,9 +32,7 @@
|
||||
<el-table-column prop="result_4" label="通道4" />
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="result-footer">
|
||||
你可以停留在本页查看数据,或返回首页进行复检、报告生成和归档
|
||||
</div>
|
||||
<div class="result-footer">你可以停留在本页查看数据,或返回首页进行复检、报告生成和归档</div>
|
||||
</div>
|
||||
<DataCheckPopup
|
||||
:visible="DataCheckDialogVisible"
|
||||
@@ -35,66 +42,57 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="testPopup">
|
||||
import{ElMessage, ElSelectV2, FormInstance,FormItemRule}from'element-plus'
|
||||
import { defineProps, defineEmits, reactive,watch,ref, Ref } from 'vue';
|
||||
import { dialogBig,dialogMiddle} from '@/utils/elementBind'
|
||||
import { ref } from 'vue'
|
||||
import { dialogBig } from '@/utils/elementBind'
|
||||
//import IndicatorTypeDialog from "@/views/machine/errorSystem/components/IndicatorTypeDialog.vue"; // 导入子组件
|
||||
import {CirclePlus, Delete, EditPen,FolderOpened,CopyDocument,Edit, Picture, UploadFilled, SuccessFilled,VideoPlay,Right,Refresh,Close} from '@element-plus/icons-vue'
|
||||
import { useDictStore } from '@/stores/modules/dict'
|
||||
import preTest from './preTest.vue'
|
||||
import timeTest from './timeTest.vue'
|
||||
import channelsTest from './channelsTest.vue'
|
||||
import DataCheckPopup from './dataCheckPopup.vue';
|
||||
import { log } from 'console';
|
||||
import DataCheckPopup from './dataCheckPopup.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
}>();
|
||||
visible: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', value: boolean): void;
|
||||
(e: 'submit', data: any): void;
|
||||
}>();
|
||||
(e: 'update:visible', value: boolean): void
|
||||
(e: 'submit', data: any): void
|
||||
}>()
|
||||
|
||||
const testScriptName = ref('Q/GDW 10650.4-2021 模拟式');
|
||||
const errorSysName = ref('Q/GDW 10650.2-2021');
|
||||
const dataRule = ref('所有值');
|
||||
const DataCheckDialogVisible = ref(false);
|
||||
const testScriptName = ref('Q/GDW 10650.4-2021 模拟式')
|
||||
const errorSysName = ref('Q/GDW 10650.2-2021')
|
||||
const dataRule = ref('所有值')
|
||||
const DataCheckDialogVisible = ref(false)
|
||||
|
||||
const resultData = ref([
|
||||
{
|
||||
deviceName: "被检设备1",
|
||||
result_1: "合格",
|
||||
result_2: "合格",
|
||||
result_3: "合格",
|
||||
result_4: "合格",
|
||||
deviceName: '被检设备1',
|
||||
result_1: '合格',
|
||||
result_2: '合格',
|
||||
result_3: '合格',
|
||||
result_4: '合格'
|
||||
},
|
||||
{
|
||||
deviceName: "被检设备2",
|
||||
result_1: "合格",
|
||||
result_2: "合格",
|
||||
result_3: "—",
|
||||
result_4: "—",
|
||||
deviceName: '被检设备2',
|
||||
result_1: '合格',
|
||||
result_2: '合格',
|
||||
result_3: '—',
|
||||
result_4: '—'
|
||||
},
|
||||
{
|
||||
deviceName: "被检设备3",
|
||||
result_1: "不合格",
|
||||
result_2: "合格",
|
||||
result_3: "—",
|
||||
result_4: "—",
|
||||
},
|
||||
]);
|
||||
deviceName: '被检设备3',
|
||||
result_1: '不合格',
|
||||
result_2: '合格',
|
||||
result_3: '—',
|
||||
result_4: '—'
|
||||
}
|
||||
])
|
||||
|
||||
const handleClick = (row: any) => {
|
||||
console.log(111)
|
||||
DataCheckDialogVisible.value = true;
|
||||
};
|
||||
DataCheckDialogVisible.value = true
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
emit('update:visible', false); // 关闭对话框
|
||||
};
|
||||
|
||||
|
||||
emit('update:visible', false) // 关闭对话框
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<el-form-item v-if="checkStore.plan.timeCheck===1" prop="timeTest" :label-width="100">
|
||||
<el-checkbox v-model="formContent.timeTest" label="守时检测"/>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="AppSceneStore.currentScene === '1'" prop="channelsTest" :label-width="100">
|
||||
<el-form-item v-if="channelsTestShow" prop="channelsTest" :label-width="100">
|
||||
<el-checkbox v-model="formContent.channelsTest" label="系数校准"/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="test" :label-width="100">
|
||||
@@ -28,33 +28,45 @@
|
||||
|
||||
<script setup lang='tsx' name='selectTestItemPopup'>
|
||||
import {dialogSmall} from "@/utils/elementBind";
|
||||
import {ref} from "vue";
|
||||
import {reactive, ref} from "vue";
|
||||
import {useCheckStore} from "@/stores/modules/check";
|
||||
import type {CheckData} from "@/api/check/interface";
|
||||
import {ElMessageBox} from "element-plus";
|
||||
import {useAppSceneStore} from "@/stores/modules/mode";
|
||||
import {useAppSceneStore,useModeStore} from "@/stores/modules/mode";
|
||||
|
||||
|
||||
const AppSceneStore = useAppSceneStore()
|
||||
const emit = defineEmits(['openTestDialog'])
|
||||
const checkStore = useCheckStore();
|
||||
|
||||
const modeStore = useModeStore()
|
||||
const dialogFormRef = ref()
|
||||
|
||||
const channelsTestShow = ref(false)
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const formContent = reactive<CheckData.SelectTestItem>({preTest: true, timeTest: false, channelsTest: false, test: false})
|
||||
|
||||
const open = async () => {
|
||||
|
||||
resetFormContent()
|
||||
checkStore.setSelectTestItems(formContent)
|
||||
dialogVisible.value = true
|
||||
if(modeStore.currentMode === '比对式'){
|
||||
channelsTestShow.value = false
|
||||
}else{
|
||||
if(AppSceneStore.currentScene === '1'){
|
||||
channelsTestShow.value = true
|
||||
}else{
|
||||
channelsTestShow.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 清空表单内容
|
||||
const resetFormContent = () => {
|
||||
Object.assign(formContent, {preTest: true, channelsTest: false, timeTest: false, test: false})
|
||||
let hasResult = checkStore.devices.some((device) => device.checkResult === 0)
|
||||
Object.assign(formContent, {preTest: !hasResult, channelsTest: false, timeTest: false, test: hasResult})
|
||||
}
|
||||
|
||||
const handleStart = () => {
|
||||
const handleStart = async () => {
|
||||
let count = 0
|
||||
for (let key in formContent) {
|
||||
if (formContent[key]) {
|
||||
@@ -88,8 +100,12 @@ const handleStart = () => {
|
||||
}
|
||||
checkStore.setCheckType(0)
|
||||
checkStore.setSelectTestItems({...formContent})
|
||||
|
||||
|
||||
|
||||
handleClose()
|
||||
emit('openTestDialog')
|
||||
|
||||
emit('openTestDialog',checkStore.selectTestItems.test)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
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