Files
pqs-9100_client/frontend/src/views/home/components/dataCheckSingleChannelSingleTestPopup.vue
2025-10-15 08:49:11 +08:00

915 lines
30 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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 class="data-check-head">
<el-form :model="formContent" label-width="auto" class="form-three">
<el-form-item label="检测脚本">
<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-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">
<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">
<el-button type="primary" :icon="Postcard" @click="handleGenerateReport">报告生成</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">
<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"
node-key="index"
:data="treeDataAll"
:props="defaultProps"
@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>
</el-tooltip>
</template>
</el-tree>
</div>
</div>
<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="500px" placement="right">
<template #reference>
<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-cascader
v-model="currentCheckItem"
:options="checkListLevel"
:props="{ expandTrigger: 'click' as const }"
placement="bottom-end"
/>
</el-form-item>
</div>
<div class="content-right-Tabs">
<el-tabs type="border-card" v-model="activeTab">
<el-tab-pane label="检测结果" name="resultTab">
<DataCheckResultTable
:tableData="checkResultData"
:currentScriptTypeName="currentScriptTypeName"
/>
</el-tab-pane>
<el-tab-pane label="原始数据" name="rawDataTab">
<DataCheckRawDataTable
:tableData="rawTableData"
:currentScriptTypeName="currentScriptTypeName"
@exportRawDataHandler="exportRawDataHandler"
/>
</el-tab-pane>
</el-tabs>
</div>
</div>
</div>
</div>
</el-dialog>
</template>
<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 { 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'
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 => {
treeRef.value!.filter(val)
})
// 格式化数字
const fixed = 4
// 表单数据
const formContent = reactive<CheckData.DataCheck>({
scriptName: '',
errorSysId: '',
dataRule: '',
deviceName: '',
chnNum: ''
})
// 原始误差体系id
let originErrorSysId: string = ''
let planCode: string = ''
// 谐波次数列表
// const harmNumList = reactive<{ value: string, label: string }[]>([])
// 当前选中的检测项
const currentCheckItem = ref<any>()
// 检测项列表
// const checkList = reactive<{ value: string, label: string }[]>([])
// 带有层级的检测项列表
const checkListLevel = reactive<{ value: string; label: string; children: { value: string; label: string }[] }[]>([])
let deviceId: string = ''
let originScriptType: string | null = null
let scriptType: string | null = null
// 通道下拉列表
let chnList: any[] = []
// 左侧树数据
const treeDataAll = reactive<CheckData.TreeItem[]>([])
// 左侧树被选中的叶子节点id
const checkIndex = ref<string>('')
// 当前检测项目名称
const currentScriptTypeName = 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 checkResultData = reactive<CheckData.CheckResult[]>([])
const popoverWidth: ComputedRef<string> = computed(() => {
return `${1140 - (currentScriptTypeName.value.length + 7 + 3) * 14 - (checkListLevel.length ? 280 : 0)}px`
})
// 原始数据表格数据
const rawTableData = reactive<CheckData.RawDataItem[]>([])
// 左侧树默认展开节点id
let defaultExpandedKeys: string[] = []
const filterNode = (value: string, data: any) => {
if (!value) return true
return data.scriptTypeName.includes(value)
}
// 点击左侧树节点触发事件
const handleNodeClick = async (data: any) => {
if (!data.children) {
checkIndex.value = data.index
currentScriptTypeName.value = data.scriptTypeName
currentDesc.value = data.sourceDesc
scriptType = data.scriptType ?? scriptType
if (checkIndex.value !== '') {
await updateTableData()
activeTab.value = 'resultTab'
}
}
}
const handleErrorSysChange = async () => {
changeErrorSystem({
planId: checkStore.plan.id,
scriptId: checkStore.plan.scriptId,
errorSysId: formContent.errorSysId,
deviceId: 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('切换误差体系成功')
if (originErrorSysId != formContent.errorSysId) {
planCode = checkStore.plan.code + '_temp'
} else {
planCode = checkStore.plan.code + ''
}
if (formContent.chnNum != chnList[0].value) {
formContent.chnNum = chnList[0].value
} else {
handleChnNumChange()
}
}
})
}
watch(
() => formContent.chnNum,
async (newVal, oldVal) => {
// console.log("通道号", newVal);
if (newVal != '') {
handleChnNumChange()
}
}
)
const handleChnNumChange = async () => {
// 发起请求,查询该测试项的检测结果
const { data: resTreeDataTemp }: { data: CheckData.TreeItem[] } = await getTreeData({
scriptId: checkStore.plan.scriptId,
devId: deviceId,
devNum: formContent.chnNum + '',
scriptType: originScriptType,
code: planCode
})
updateTreeFly(resTreeDataTemp, 4)
updateTreeFly(resTreeDataTemp, 2)
treeDataAll.length = 0
Object.assign(treeDataAll, resTreeDataTemp)
defaultOperate()
await updateTableData()
activeTab.value = 'resultTab'
}
watch(currentCheckItem, (newVal, oldVal) => {
if (newVal !== '') {
let key = newVal[0]
if (newVal.length == 2) {
key += '_' + newVal[1]
}
doCurrentCheckItemUpdate(key)
} else {
activeTab.value = 'resultTab'
}
})
// 默认操作
const defaultOperate = () => {
let node = getDefaultNode(treeDataAll)
if (node) {
currentScriptTypeName.value = node.scriptTypeName
currentDesc.value = node.sourceDesc
checkIndex.value = node.index
defaultExpandedKeys = [node.index]
treeRef.value?.setCurrentKey(node.index)
scriptType = node.scriptType ?? scriptType
} else {
currentScriptTypeName.value = ''
currentDesc.value = ''
checkIndex.value = ''
defaultExpandedKeys = []
}
}
const updateTableData = async () => {
console.log('左侧树被选中的叶子节点checkIndex', checkIndex.value)
if (checkIndex.value !== '') {
checkListLevel.length = 0
checkResultData.length = 0
rawTableData.length = 0
currentCheckItem.value = ''
// console.log("更新表格数据");
// 发起请求,查询该测试项的检测结果
const { data } = await getTableData({
scriptType,
scriptId: checkStore.plan.scriptId,
devId: deviceId,
devNum: formContent.chnNum + '',
code: planCode,
index: parseInt(checkIndex.value)
})
let keys1 = Object.keys(data.resultData)
let resultData = new Map()
for (let key of keys1) {
let children = []
let label = key
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}(不符合)`
children.push({ label: label, value: num })
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}(不符合)`
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()
for (let key of keys2) {
if ((key.includes('谐波') || key.includes('简谐波')) && key !== '谐波有功功率') {
for (let item of data.rawData[key]) {
let num = formatHarmNum(item.harmNum + '')
let value = rawData.get(key + '_' + num)
if (!value) {
rawData.set(key + '_' + num, [item])
} else {
value.push(item)
}
}
} else {
rawData.set(key, data.rawData[key])
}
}
resTableData.resultData = resultData
resTableData.rawData = rawData
toAngleLast()
if (checkListLevel.length !== 0) {
if (checkListLevel[0].children.length !== 0) {
currentCheckItem.value = [checkListLevel[0].value + '', checkListLevel[0].children[0].value + '']
} else {
currentCheckItem.value = [checkListLevel[0].value + '']
}
}
} else {
checkResultData.length = 0
rawTableData.length = 0
}
}
const doCurrentCheckItemUpdate = (newVal: string) => {
let resCheckResult: CheckData.ResCheckResult = resTableData.resultData.get(newVal)
setCheckResultData(resCheckResult)
let tempRawData = resTableData.rawData.get(newVal)
if (tempRawData) {
setRawData(tempRawData)
} else {
setRawData([])
}
activeTab.value = 'resultTab'
}
const open = async (_deviceId: string, chnNum: string, _scriptType: string | null) => {
planCode = checkStore.plan.code + ''
deviceId = _deviceId
originScriptType = _scriptType
scriptType = _scriptType
// 发起后端请求,查询详细信息 当chnNum为-1时查询所有通道号
const { data: resFormContent }: { data: any } = await getFormData({
planId: checkStore.plan.id,
deviceId,
chnNum,
scriptType
})
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}`
}))
Object.assign(formContent, {
...resFormContent,
chnNum: chnList.length > 0 ? chnList[0].value : ''
})
originErrorSysId = formContent.errorSysId
pqErrorList.length = 0
let { data: resPqErrorList } = await getPqErrSysList()
Object.assign(pqErrorList, resPqErrorList)
visible.value = true
}
const handleGenerateReport = async () => {
await generateDevReport({
planId: checkStore.plan.id,
devIdList: [deviceId],
scriptId: checkStore.plan.scriptId,
planCode: planCode
})
ElMessage.success({ message: `报告生成成功!` })
}
const handleReCalculate = async () => {
reCalculate({
planId: checkStore.plan.id,
scriptId: checkStore.plan.scriptId,
errorSysId: formContent.errorSysId,
deviceId: deviceId,
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"
// } else {
// planCode = checkStore.plan.code + ''
// }
if (formContent.chnNum != chnList[0].value) {
formContent.chnNum = chnList[0].value
} else {
handleChnNumChange()
}
})
}
const close = async () => {
//数据清空
Object.assign(formContent, {
scriptName: '',
errorSysName: '',
dataRule: '',
deviceName: '',
chnNum: ''
})
treeDataAll.length = 0
// harmNumList.length = 0
// currentHarmNum.value = '-1'
currentCheckItem.value = ''
checkResultData.length = 0
rawTableData.length = 0
//checkList.length = 0
defaultExpandedKeys = []
checkIndex.value = ''
activeTab.value = 'resultTab'
currentScriptTypeName.value = ''
currentDesc.value = ''
pqErrorList.length = 0
planCode = ''
visible.value = false
if (checkStore.showDetailType === 1) {
await deleteTempTable(checkStore.plan.code + '')
}
}
const setCheckResultData = (data: CheckData.ResCheckResult | null) => {
let result: CheckData.CheckResult[] = []
if (data == null || data == undefined) {
Object.assign(checkResultData, [])
return
}
if (data.dataA != null && data.dataB != null && data.dataC != null) {
result.push({
// stdA: numberToFixed(data.dataA.resultData),
// dataA: numberToFixed(data.dataA.data),
stdA: dataToShow(data.dataA.resultData),
dataA: dataToShow(data.dataA.data),
errorA: data.dataA.errorData == null ? '/' : data.dataA.errorData + '',
maxErrorA: toMaxErrorStr(data.dataA.radius, data.dataA.unit),
unitA: data.dataA.unit,
isDataA: data.dataA.isData,
// stdB: numberToFixed(data.dataB.resultData),
// dataB: numberToFixed(data.dataB.data),
stdB: dataToShow(data.dataB.resultData),
dataB: dataToShow(data.dataB.data),
errorB: data.dataB.errorData == null ? '/' : data.dataB.errorData + '',
maxErrorB: toMaxErrorStr(data.dataB.radius, data.dataB.unit),
isDataB: data.dataB.isData,
unitB: data.dataB.unit,
// stdC: numberToFixed(data.dataC.resultData),
// dataC: numberToFixed(data.dataC.data),
stdC: dataToShow(data.dataC.resultData),
dataC: dataToShow(data.dataC.data),
errorC: data.dataC.errorData == null ? '/' : data.dataC.errorData + '',
maxErrorC: toMaxErrorStr(data.dataC.radius, data.dataC.unit),
isDataC: data.dataC.isData,
unitC: data.dataC.unit,
maxError: data.radius,
unit: data.unit,
result: data.isData
})
}
if (data.dataT != null && data.dataA == null && data.dataB == null && data.dataC == null) {
result.push({
// stdT: numberToFixed(data.dataT.resultData),
// dataT: numberToFixed(data.dataT.data),
stdT: dataToShow(data.dataT.resultData),
dataT: dataToShow(data.dataT.data),
errorT: data.dataT.errorData == null ? '/' : data.dataT.errorData + '',
maxErrorT: toMaxErrorStr(data.dataT.radius, data.dataT.unit),
isDataT: data.dataT?.isData,
unitT: data.dataT.unit,
maxError: data.radius,
unit: data.unit,
result: data.isData
})
}
Object.assign(checkResultData, result)
}
const exportRawDataHandler = () => {
useDownload(
exportRawData,
'原始数据',
{
scriptType,
scriptId: checkStore.plan.scriptId,
devId: deviceId,
devNum: formContent.chnNum + '',
code: checkStore.plan.code + '',
index: parseInt(checkIndex.value)
},
false,
'.xlsx'
)
}
const formatHarmNum = (num: string) => {
// debugger
if (num.includes('.5')) {
return num
} else {
num = num.replace('.0', '')
return num
}
}
const setRawData = (data: CheckData.RawDataItem[]) => {
data.forEach((item: CheckData.RawDataItem) => {
// item.dataA = stringToFixed(item.dataA)
// item.dataB = stringToFixed(item.dataB)
// item.dataC = stringToFixed(item.dataC)
// item.dataT = stringToFixed(item.dataT)
item.dataA = dataToShow(item.dataA)
item.dataB = dataToShow(item.dataB)
item.dataC = dataToShow(item.dataC)
item.dataT = dataToShow(item.dataT)
})
rawTableData.length = 0
Object.assign(rawTableData, data)
}
const dataToShow = (num: number): string => {
if (num == null || num == undefined) {
return '/'
}
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, '')
}
return result
}
}
const stringToFixed = (str: string): string => {
if (str == null || str == undefined) {
return '/'
} else {
let num = Number(str)
if (isNaN(num)) {
return '/'
} else {
let result = num.toFixed(fixed)
if (result === '-0.0000') {
return result.replace(/-/g, '')
}
return result
}
}
}
const getError = (num1: number, num2: number): string => {
if (num1 == null || num1 == undefined || num2 == null || num2 == undefined) {
return '/'
}
return Math.abs(Number(numberToFixed(num1)) - Number(numberToFixed(num2))).toFixed(fixed)
}
const toMaxErrorStr = (oldMaxErroe: any, unit: any) => {
let result = oldMaxErroe ?? '/'
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
}
return result
}
const findFirstLeafNode = (node: any): any => {
if (!node.children || node.children.length === 0) {
return node
}
return findFirstLeafNode(node.children[0])
}
const getDefaultNode = (data: any[]) => {
if (!data || data.length === 0) {
return null
}
const firstElement = data[0]
return findFirstLeafNode(firstElement)
}
const toAngleLast = () => {
let angleIndex = -1
for (let i = 0; i < checkListLevel.length; i++) {
if (checkListLevel[i].value.toString().includes('相角')) {
angleIndex = i
break
}
}
if (angleIndex != -1) {
let temp = checkListLevel[angleIndex]
checkListLevel.splice(angleIndex, 1)
checkListLevel.push(temp)
}
}
// fly: 1合格 2不合格 4数据错误
const filterTree = (treeData: any[], fly: number): any[] => {
if (!treeData || treeData.length === 0) {
return []
}
filter(treeData, fly)
return treeData
}
const filter = (treeData: any[], fly: number) => {
for (let i = treeData.length - 1; i >= 0; i--) {
let node = treeData[i]
if (node.fly !== fly) {
if (node.children && node.children.length > 0) {
filter(node.children, fly)
// 检查 children 是否被全部移除
if (node.children.length === 0) {
treeData.splice(i, 1)
}
} else {
treeData.splice(i, 1)
}
}
}
}
const updateTreeFly = (treeData: any[], fly: number) => {
// 递归函数
function recursiveUpdate(node: any, targetFly: number) {
//if (!node) return false; // 如果节点不存在,返回 false
// 如果当前节点是叶子节点且 fly 字段等于 targetFly
if (!node.children || node.children.length === 0) {
if (node.fly === targetFly) {
node.fly = targetFly // 确保叶子节点的 fly 字段被设置为 targetFly
return true // 返回 true 表示找到并更新了
}
return false // 否则返回 false
}
// 递归更新子节点
let updated = false
for (let child of node.children) {
if (recursiveUpdate(child, targetFly)) {
updated = true
}
}
// 如果有子节点被更新了,则更新当前节点的 fly 字段
if (updated) {
node.fly = targetFly
}
return updated
}
for (let i = 0; i < treeData.length; i++) {
// 调用递归函数从根节点开始更新
recursiveUpdate(treeData[i], fly)
}
}
defineExpose({
open
})
</script>
<style lang="scss" scoped>
.dialog {
display: flex;
flex-direction: column;
overflow-y: hidden;
overflow-x: hidden;
.data-check-dialog {
display: flex;
flex-direction: column;
overflow-y: hidden;
.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;
.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%;
margin-left: 10px;
flex: 1;
.content-right-title {
display: flex;
padding: 10px 0;
margin-top: 10px;
line-height: 1.5;
.content-right-title-text {
font-size: 14px;
font-weight: bold;
}
}
//.el-divider--horizontal {
// //margin-top: 0px;
// margin-bottom: 5px;
// height: 20px;
//}
//.content-right-title-desc {
// font-size: 16px;
// height: 48px;
// max-height: 48px;
// overflow-y: auto;
//}
}
.content-right-Tabs {
box-sizing: border-box;
margin-top: 10px;
margin-bottom: 10px;
max-height: 400px;
}
}
}
}
</style>
<!--<style lang="scss">-->
<!--.el-popover.popover-class {-->
<!-- .el-popover__title {-->
<!-- color: #fff;-->
<!-- background-color: var(--el-color-primary) !important;-->
<!-- }-->
<!--}-->
<!--</style>-->