2026-04-17 09:15:58 +08:00
|
|
|
|
<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"
|
|
|
|
|
|
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"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</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: '报文解析异常',
|
2026-04-22 19:32:28 +08:00
|
|
|
|
10556: '不存在上线的设备',
|
|
|
|
|
|
|
|
|
|
|
|
400:'请求格式或者参数错误',
|
|
|
|
|
|
404:'未知错误',
|
|
|
|
|
|
// 408:'超时',
|
|
|
|
|
|
// 409:'业务执行不符合预期',
|
|
|
|
|
|
500:'未知错误'
|
2026-04-17 09:15:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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 handleSocketMessage = (payload: any) => {
|
|
|
|
|
|
const requestId = `${payload?.requestId ?? ''}`
|
|
|
|
|
|
const normalizedRequestId = requestId.trim().toLowerCase()
|
|
|
|
|
|
const code = Number(payload?.code)
|
|
|
|
|
|
|
2026-04-22 19:32:28 +08:00
|
|
|
|
if (requestId === 'yjc_sbtxjy' && code !== 10200 || code in SOCKET_ERROR_MESSAGE_MAP) {
|
2026-04-17 09:15:58 +08:00
|
|
|
|
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 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()
|
|
|
|
|
|
await props.refreshTable?.()
|
|
|
|
|
|
done()
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
await confirmExit()
|
|
|
|
|
|
await stopDetect()
|
|
|
|
|
|
closeDialog()
|
|
|
|
|
|
await props.refreshTable?.()
|
|
|
|
|
|
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 = 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()
|
|
|
|
|
|
await props.refreshTable?.()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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>
|