Files
pqs-9100_client/frontend/src/views/home/components/compareTestPopup.vue
2025-11-13 08:42:05 +08:00

703 lines
22 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
:title="dialogTitle"
width="90vw"
:style="{ maxWidth: '1550px', minWidth: '800px' }"
: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"
:dialogWidth="computedDialogWidth"
/>
</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, onMounted, onUnmounted, 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 windowWidth = ref(window.innerWidth)
const computedDialogWidth = ref(0)
// 监听窗口大小变化
const handleResize = () => {
windowWidth.value = window.innerWidth
// 计算对话框实际宽度 (90vw但不超过1550px不小于800px)
computedDialogWidth.value = Math.min(Math.max(window.innerWidth * 0.9, 800), 1550)
}
onMounted(() => {
window.addEventListener('resize', handleResize)
handleResize() // 初始化
})
onUnmounted(() => {
window.removeEventListener('resize', handleResize)
})
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()
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
// 等待组件渲染完成
await nextTick()
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) {
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) {
ActiveStatue.value = newValue
})
watch(TestStatus, function (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)
}
})
watch(ActiveStatue, function (newValue, oldValue) {
if (newValue === 'error') {
stepsActiveIndex.value += 0
nextStepText.value = '检测失败'
}
if (newValue === 'success' && stepsActiveIndex.value === stepsTotalNum.value - 1) {
stepsActiveIndex.value += 2
nextStepText.value = '检测完成'
}
if (newValue === 'test_init_fail') {
stepsActiveIndex.value += 0
nextStepText.value = '初始化失败'
}
if (newValue === 'connect_timeout') {
stepsActiveIndex.value += 0
nextStepText.value = '连接超时'
}
if (newValue === 'pause_timeout') {
stepsActiveIndex.value += 0
// nextStepText.value = '结束测试'
nextStepText.value = '暂停超时'
}
if (newValue === 'success' && stepsActiveIndex.value < stepsTotalNum.value - 1) {
nextStep() // 实现自动点击,进入下一个测试内容
//handleSubmitFast()
}
})
const handleQuit = () => {
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 = () => {
TestStatus.value = 'paused_ing'
pauseTest()
}
const sendResume = () => {
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 = () => {
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 => {
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]) {
stepsActiveView.value = idx
stepsActive.value = idx
return
}
if (checkStore.selectTestItems[selectTestItemsKey] && tempStep != 0) {
tempStep--
}
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>