Files
pqs-9100_client/frontend/src/views/machine/freqConverter/components/freqConverterTestPopup.vue
2026-06-08 08:45:05 +08:00

409 lines
10 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
:model-value="dialogVisible"
:title="dialogTitle"
:width="dialogWidth"
:style="dialogStyle"
:close-on-click-modal="dialogBig.closeOnClickModal"
:draggable="dialogBig.draggable"
:class="dialogBig.class"
:show-close="true"
:close-on-press-escape="false"
:before-close="handleBeforeClose"
@closed="handleClosed"
destroy-on-close
align-center
>
<div class="freq-converter-test-popup">
<el-steps :active="currentStep - 1" finish-status="success" simple>
<el-step title="准备" />
<el-step title="检测" />
</el-steps>
<FreqConverterDetectChannelPairing
v-if="dialogVisible && currentStep === 1"
ref="channelPairingRef"
:freq-converter="currentFreqConverter"
/>
<FreqConverterDipChart
v-else-if="dialogVisible && currentStep === 2"
:selected-mapping="selectedMapping"
:web-msg-send="webSocketMessage"
:result-data="historyResultData"
:auto-draw-curve="false"
/>
</div>
<template #footer>
<div class="dialog-footer">
<el-button
v-if="!startDetectSuccess"
type="primary"
:disabled="currentStep !== 1 || startLoading"
:loading="startLoading"
@click="handleStart"
>
开始检测
</el-button>
<el-button type="danger" plain @click="handleExit">退出检测</el-button>
</div>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import {ElMessage, ElMessageBox} from 'element-plus'
import {computed, onBeforeUnmount, onMounted, ref} from 'vue'
import {dialogBig} from '@/utils/elementBind'
import {type FreqConverter} from '@/api/device/interface/freqConverter'
import {getFreqConverterResult, startFreqConverterDetect, stopFreqConverterDetect} from '@/api/device/freqConverter'
import {JwtUtil} from '@/utils/jwtUtil'
import FreqConverterDetectChannelPairing from '@/views/machine/freqConverter/components/freqConverterDetectChannelPairing.vue'
import FreqConverterDipChart from '@/views/machine/freqConverter/components/freqConverterDipChart.vue'
import socketClient from '@/utils/webSocketClient'
const SOCKET_CALLBACK_KEY = 'aaa'
const props = defineProps<{
refreshTable?: (() => Promise<void>) | (() => void) | undefined;
}>()
const dialogVisible = ref(false)
const currentStep = ref(1)
const currentFreqConverter = ref<FreqConverter.ResFreqConverter | null>(null)
const channelPairingRef = ref<InstanceType<typeof FreqConverterDetectChannelPairing>>()
const webSocketMessage = ref<any>(null)
const historyResultData = ref<any>(null)
const socketServe = ref<typeof socketClient.Instance | null>(null)
const selectedMapping = ref<Record<string, any> | null>(null)
const startLoading = ref(false)
const startDetectSuccess = ref(false)
const hasSocketError = ref(false)
const viewportWidth = ref(typeof window === 'undefined' ? 1280 : window.innerWidth)
const dialogTitle = computed(() => '变频器检测')
const dialogWidth = computed(() => (viewportWidth.value < 820 ? 'calc(100vw - 24px)' : dialogBig.width))
const dialogStyle = computed(() => ({
maxWidth: dialogBig.maxWidth,
minWidth: viewportWidth.value < 820 ? '320px' : dialogBig.minWidth
}))
const SOCKET_ERROR_MESSAGE_MAP: Record<number, string> = {
10550: '设备连接异常',
10551: '设备触发报告异常',
10552: '重复的初始化操作',
10553: '通讯模块通讯异常',
10554: '报文解析异常',
10556: '不存在上线的设备',
400:'请求格式或者参数错误',
404:'未知错误',
// 408:'超时',
// 409:'业务执行不符合预期',
500:'未知错误'
}
const normalizeFormalRealPayload = (payload: any) => {
if (!payload || typeof payload !== 'object') {
return payload
}
if (typeof payload.data !== 'string') {
return payload
}
try {
return {
...payload,
data: JSON.parse(payload.data)
}
} catch (error) {
console.error('formal_real 数据解析失败:', error, payload.data)
return payload
}
}
const extractResultArray = (payload: any) => {
if (Array.isArray(payload)) {
return payload
}
if (Array.isArray(payload?.data)) {
return payload.data
}
if (Array.isArray(payload?.data?.records)) {
return payload.data.records
}
if (Array.isArray(payload?.records)) {
return payload.records
}
if (Array.isArray(payload?.list)) {
return payload.list
}
return [] as any[]
}
const handleSocketMessage = (payload: any) => {
const requestId = `${payload?.requestId ?? ''}`
const normalizedRequestId = requestId.trim().toLowerCase()
const code = Number(payload?.code)
if (requestId === 'yjc_sbtxjy' && code !== 10200 || code in SOCKET_ERROR_MESSAGE_MAP) {
hasSocketError.value = true
ElMessage.error(SOCKET_ERROR_MESSAGE_MAP[code] || `检测异常,错误码:${code}`)
return
}
if (normalizedRequestId.startsWith('formal_real')) {
webSocketMessage.value = normalizeFormalRealPayload(payload)
}
}
const updateViewportWidth = () => {
viewportWidth.value = window.innerWidth
}
const connectWebSocket = () => {
try {
if (socketServe.value) {
socketServe.value.unRegisterCallBack?.(SOCKET_CALLBACK_KEY)
if (socketServe.value.connected) {
socketServe.value.closeWs()
}
}
socketClient.Instance.connect()
socketServe.value = socketClient.Instance
socketServe.value.registerCallBack(SOCKET_CALLBACK_KEY, handleSocketMessage)
} catch (error) {
console.error('WebSocket连接处理失败:', error)
ElMessage.error('WebSocket连接建立失败请重试')
}
}
const closeWebSocket = () => {
try {
if (socketServe.value) {
socketServe.value.unRegisterCallBack?.(SOCKET_CALLBACK_KEY)
if (socketServe.value.connected) {
socketServe.value.closeWs()
}
socketServe.value = null
}
} catch (error) {
console.error('WebSocket关闭失败:', error)
socketServe.value = null
}
}
const resetState = () => {
currentStep.value = 1
webSocketMessage.value = null
historyResultData.value = null
currentFreqConverter.value = null
selectedMapping.value = null
startLoading.value = false
startDetectSuccess.value = false
hasSocketError.value = false
}
const closeDialog = () => {
dialogVisible.value = false
closeWebSocket()
resetState()
}
const handleClosed = async () => {
await props.refreshTable?.()
}
const stopDetect = async () => {
if (!startDetectSuccess.value) {
return
}
try {
await stopFreqConverterDetect({
userId: JwtUtil.getLoginName()
})
} catch (error) {
console.error('停止变频器检测失败:', error)
}
}
const confirmExit = async () => {
await ElMessageBox.confirm(
'检测未完成,是否退出当前检测流程?',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
)
}
const isFreqConverterDetected = () => {
return Number(currentFreqConverter.value?.testStatus) === 1
}
const confirmResetLastDetectData = async () => {
if (!isFreqConverterDetected()) {
return true
}
try {
await ElMessageBox.confirm(
'是否覆盖上次检测数据',
'提示',
{
confirmButtonText: '是',
cancelButtonText: '否',
distinguishCancelAndClose: true,
type: 'warning'
}
)
return true
} catch (action) {
if (action === 'cancel') {
return false
}
return null
}
}
const handleBeforeClose = async (done: () => void) => {
if (hasSocketError.value) {
await stopDetect()
closeDialog()
done()
return
}
try {
await confirmExit()
await stopDetect()
closeDialog()
done()
} catch {
// 用户取消关闭
}
}
const handleStart = async () => {
if (currentStep.value === 1) {
const mapping = channelPairingRef.value?.getChannelMapping()
if (!mapping || !channelPairingRef.value?.hasValidConnection()) {
ElMessage.warning('请先选择设备通道并完成连线')
return
}
const reset = await confirmResetLastDetectData()
if (reset === null) {
return
}
startLoading.value = true
try {
if (reset === false) {
const historyResult = await getFreqConverterResult({
converterId: mapping.freqConverterId
})
historyResultData.value = extractResultArray(historyResult?.data ?? historyResult)
} else {
historyResultData.value = null
}
const res = await startFreqConverterDetect({
converterId: mapping.freqConverterId,
monitorId: `${mapping.deviceId}_${mapping.deviceChannel}`,
userId: JwtUtil.getLoginName(),
reset
})
if (res.code === 'A0000') {
selectedMapping.value = mapping
currentStep.value = 2
startDetectSuccess.value = true
return
}
} catch (error) {
console.error('开始变频器检测失败:', error)
ElMessage.error('开始检测失败')
} finally {
startLoading.value = false
}
return
}
}
const handleExit = async () => {
if (!hasSocketError.value) {
try {
await confirmExit()
} catch {
return
}
}
await stopDetect()
closeDialog()
}
const open = (row: FreqConverter.ResFreqConverter) => {
resetState()
currentFreqConverter.value = {...row}
dialogVisible.value = true
connectWebSocket()
}
onMounted(() => {
window.addEventListener('resize', updateViewportWidth)
})
onBeforeUnmount(() => {
window.removeEventListener('resize', updateViewportWidth)
closeWebSocket()
})
defineExpose({open, channelPairingRef})
</script>
<style scoped>
.freq-converter-test-popup {
display: flex;
flex-direction: column;
gap: 10px;
}
.section-card {
border: 1px solid var(--el-border-color-light);
}
.section-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
}
.section-tip {
color: var(--el-text-color-secondary);
font-size: 12px;
}
:deep(.el-dialog__body) {
padding-top: 10px;
padding-bottom: 10px;
overflow: hidden;
}
</style>