409 lines
10 KiB
Vue
409 lines
10 KiB
Vue
<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>
|