1305 lines
45 KiB
Vue
1305 lines
45 KiB
Vue
<!-- 检测测试主界面组件 -->
|
||
<template>
|
||
<div>
|
||
<!-- 主检测界面 -->
|
||
<div class='dialog' v-bind='dialogBig'>
|
||
<!-- 标题栏:显示时间、进度条、日志按钮 -->
|
||
<div class='dialog-title'>
|
||
<!-- 检测用时显示 -->
|
||
<div class='timeView'>
|
||
<el-icon style='margin: 0px 5px;'>
|
||
<Clock />
|
||
</el-icon>
|
||
<span>检测用时:{{ timeView }}</span>
|
||
</div>
|
||
<!-- 检测进度条 -->
|
||
<el-progress
|
||
style='width: 82%; margin-right: 3%;'
|
||
:percentage='percentage'
|
||
:color='customColors' />
|
||
<!-- 检测项进度按钮 -->
|
||
<el-button style='width: 10%' type='text' :icon='InfoFilled' @click='showTestLog'>检测项进度</el-button>
|
||
</div>
|
||
|
||
<!-- 检测结果表格内容区 -->
|
||
<div class='dialog-content'>
|
||
<!-- 检测结果表格 -->
|
||
<el-table :data='checkResultView' row-key='scriptType' height='450px'
|
||
:header-cell-style="{ background: 'var(--el-color-primary)', color: '#eee', textAlign: 'center' } "
|
||
style='width: 100%'
|
||
border>
|
||
<!-- 固定列:检测项目名称 -->
|
||
<el-table-column fixed prop='scriptName' label='检测项目' width='150px' align='center'>
|
||
</el-table-column>
|
||
|
||
<!-- 动态列:当通道数不超过最大限制时显示详细通道 -->
|
||
<template v-if='chnSum<=MAX_CHN_SUM'>
|
||
<!-- 遍历设备列表 -->
|
||
<el-table-column v-for='(item,index1) in deviceList' :key='item.deviceId' :label='item.deviceName'
|
||
:min-width='110' align='center'>
|
||
<!-- 遍历每个设备的通道 -->
|
||
<el-table-column v-for='(chnItem,index2) in item.chnNum' :key='`${item.deviceId}${chnItem}`'
|
||
:label="'通道'+chnItem" align='center'>
|
||
<template #default='{row}'>
|
||
<!-- 通道状态提示 -->
|
||
<el-tooltip
|
||
:content="row.devices[index1].chnResult[index2].icon==='More'?'暂无数据'
|
||
:row.devices[index1].chnResult[index2].icon==='CircleCheckFilled'?'符合'
|
||
:row.devices[index1].chnResult[index2].icon==='Close'?'不符合'
|
||
:row.devices[index1].chnResult[index2].icon==='WarnTriangleFilled'?'数据异常'
|
||
:row.devices[index1].chnResult[index2].icon==='Loading'?'检测中':'连接中断'"
|
||
placement='right'>
|
||
<!-- 通道状态按钮 -->
|
||
<el-button
|
||
:disabled='row.devices[index1].chnResult[index2].color===CheckData.ButtonColorEnum.INFO || row.devices[index1].chnResult[index2].color===CheckData.ButtonColorEnum.LOADING'
|
||
:color='row.devices[index1].chnResult[index2].color'
|
||
size='small'
|
||
@click='handleClick(item,chnItem,row.scriptType)'
|
||
style='align-self: center;'
|
||
>
|
||
<!-- 加载状态图标 -->
|
||
<el-icon v-if="row.devices[index1].chnResult[index2].icon==='Loading'" class='loading-box'
|
||
style='color: #fff'>
|
||
<component :is='Loading' />
|
||
</el-icon>
|
||
<!-- 其他状态图标 -->
|
||
<el-icon v-else style='color: #fff'>
|
||
<component :is='row.devices[index1].chnResult[index2].icon' />
|
||
</el-icon>
|
||
</el-button>
|
||
|
||
</el-tooltip>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table-column>
|
||
</template>
|
||
|
||
|
||
</el-table>
|
||
</div>
|
||
|
||
</div>
|
||
<!-- 检测日志抽屉容器 -->
|
||
<div class='drawer-container'>
|
||
<!-- 从下往上弹出的检测日志抽屉 -->
|
||
<el-drawer v-model='drawer' title='检测项进度' direction='btt' :size="'38%'">
|
||
<!-- 日志内容滚动容器 -->
|
||
<div ref='scrollContainerRef' style='height: 100%; overflow-y: auto;'>
|
||
<!-- 日志列表,根据类型显示不同颜色 -->
|
||
<p v-for='(item, index) in testLogList'
|
||
:key='index'
|
||
:style="{color:item.type==='error'?'#F56C6C':item.type==='warning'?'#e6a23c':'var(--el-text-color-regular)'}">
|
||
{{ item.log }}<br />
|
||
</p>
|
||
</div>
|
||
</el-drawer>
|
||
</div>
|
||
|
||
<!-- 单通道单测试项详情弹窗 -->
|
||
<dataCheckSingleChannelSingleTestPopup ref='dataCheckSingleChannelSingleTestPopupRef' />
|
||
</div>
|
||
</template>
|
||
<script lang='tsx' setup name='test'>
|
||
// Element Plus 图标导入
|
||
import { InfoFilled, Loading } from '@element-plus/icons-vue'
|
||
// 单通道单测试项详情弹窗组件
|
||
import dataCheckSingleChannelSingleTestPopup from './dataCheckSingleChannelSingleTestPopup.vue'
|
||
// Vue 3 Composition API
|
||
import { computed, reactive, ref, toRef, watch } from 'vue'
|
||
// 对话框大小绑定工具
|
||
import { dialogBig } from '@/utils/elementBind'
|
||
// 检测数据类型定义
|
||
import { CheckData } from '@/api/check/interface'
|
||
// 检测相关状态管理
|
||
import { useCheckStore } from '@/stores/modules/check'
|
||
// Element Plus 消息组件
|
||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||
// 检测相关API
|
||
import { getBigTestItem } from '@/api/check/test'
|
||
import { getAutoGenerate } from '@/api/user/login'
|
||
import { generateDevReport } from '@/api/plan/plan'
|
||
|
||
|
||
// 获取检测状态管理实例
|
||
const checkStore = useCheckStore()
|
||
|
||
// 最大通道数限制,超过此数量将使用汇总显示
|
||
const MAX_CHN_SUM = 12
|
||
|
||
// 总测试项数
|
||
let checkTotal = 0
|
||
|
||
// 组件属性定义
|
||
const props = defineProps({
|
||
// 测试状态:waiting/start/process/paused/success等
|
||
testStatus: {
|
||
type: String,
|
||
default: 'waiting',
|
||
},
|
||
// 当前步骤索引
|
||
stepsActive: {
|
||
type: Number,
|
||
},
|
||
// WebSocket消息数据
|
||
webMsgSend: {
|
||
type: Object,
|
||
default: () => ({}),
|
||
},
|
||
})
|
||
|
||
// 定义向父组件发送的事件
|
||
const emit = defineEmits(['update:testStatus', 'update:webMsgSend', 'sendPause', 'sendResume', 'closeWebSocket'])
|
||
|
||
// ========== 界面状态相关 ==========
|
||
// 测试项进度抽屉是否打开
|
||
const drawer = ref(false)
|
||
// 进度条颜色配置
|
||
const customColors = [{ color: '#91cc75', percentage: 100 }]
|
||
|
||
// ========== 检测数据相关 ==========
|
||
// 检测脚本配置数据
|
||
let scriptData: CheckData.ScriptItem[] = []
|
||
// 被检测的设备列表
|
||
const deviceList = reactive<CheckData.Device[]>([])
|
||
// 当前正在执行的测试项索引
|
||
let activeIndex = 0
|
||
// 检测进度百分比
|
||
const percentage = ref(0)
|
||
|
||
// ========== 时间计算相关 ==========
|
||
// 定时器实例
|
||
let timer: any = null
|
||
// 总计时秒数
|
||
const timeCount = ref(0)
|
||
// 格式化的时间显示(HH:MM:SS)
|
||
const timeView = ref('00:00:00')
|
||
// 测试项开始检测时间(或继续检测时间)
|
||
const startData = ref(new Date())
|
||
// 测试项检测结束时间(或暂停时的时间)
|
||
const endData = ref(new Date())
|
||
// 累计时间差(毫秒)
|
||
const timeDifference = ref(0)
|
||
|
||
// ========== 检测结果相关 ==========
|
||
// 详细检测结果数据(详细到每个设备的每个通道)
|
||
const checkResult = reactive<CheckData.ScriptChnItem[]>([])
|
||
// 存储检测失败的测试项信息(只要有一个通道不合格,该测试项就被记录)
|
||
let errorCheckItem: Array<{ scriptType: string, type: CheckData.ChnCheckResultEnum }> = []
|
||
// 检测日志列表
|
||
const testLogList = reactive<CheckData.LogItem[]>([{ type: 'info', log: '暂无数据,等待检测开始' }])
|
||
|
||
// ========== 响应式引用 ==========
|
||
// 将props转为ref,便于watch监听
|
||
const testStatus = toRef(props, 'testStatus')
|
||
const webMsgSend = toRef(props, 'webMsgSend')
|
||
|
||
// ========== DOM引用 ==========
|
||
// 日志滚动容器引用
|
||
const scrollContainerRef = ref()
|
||
// 单通道单测试项详情弹窗组件引用
|
||
const dataCheckSingleChannelSingleTestPopupRef = ref<InstanceType<typeof dataCheckSingleChannelSingleTestPopup>>()
|
||
|
||
// ========== 计算属性 ==========
|
||
// 计算所有设备的总通道数
|
||
const chnSum = computed(() => {
|
||
let sum = 0
|
||
deviceList.forEach((item) => {
|
||
sum += item.chnNum
|
||
})
|
||
return sum
|
||
})
|
||
|
||
// 将原始检测结果转换为表格展示所需的视图数据
|
||
const checkResultView: ComputedRef<CheckData.ScriptChnViewItem[]> = computed(() => {
|
||
|
||
let result: CheckData.ScriptChnViewItem[] = checkResult.map(item => {
|
||
let temp: CheckData.ScriptChnViewItem = {
|
||
scriptType: item.scriptType,
|
||
scriptName: item.scriptName,
|
||
devices: [],
|
||
}
|
||
|
||
// 遍历每个设备,生成对应的按钮显示数据
|
||
item.devices.forEach(device => {
|
||
let tempChnBtnResult: CheckData.ButtonResult[] = []
|
||
|
||
// 只有在总通道数不超过最大限制时,才显示详细通道
|
||
if (chnSum.value <= MAX_CHN_SUM) {
|
||
// 为每个通道生成对应的按钮配置(颜色+图标)
|
||
for (let j = 0; j < device.chnResult.length; j++) {
|
||
switch (device.chnResult[j]) {
|
||
case CheckData.ChnCheckResultEnum.UNKNOWN:
|
||
tempChnBtnResult.push({ color: CheckData.ButtonColorEnum.INFO, icon: 'More' })
|
||
break
|
||
case CheckData.ChnCheckResultEnum.LOADING:
|
||
tempChnBtnResult.push({ color: CheckData.ButtonColorEnum.LOADING, icon: 'Loading' })
|
||
break
|
||
case CheckData.ChnCheckResultEnum.SUCCESS:
|
||
tempChnBtnResult.push({ color: CheckData.ButtonColorEnum.SUCCESS, icon: 'CircleCheckFilled' })
|
||
break
|
||
case CheckData.ChnCheckResultEnum.FAIL:
|
||
tempChnBtnResult.push({ color: CheckData.ButtonColorEnum.DANGER, icon: 'Close' })
|
||
break
|
||
case CheckData.ChnCheckResultEnum.TIMEOUT:
|
||
tempChnBtnResult.push({ color: CheckData.ButtonColorEnum.WARNING, icon: 'Link' })
|
||
break
|
||
case CheckData.ChnCheckResultEnum.ERRORDATA:
|
||
tempChnBtnResult.push({ color: CheckData.ButtonColorEnum.WARNING, icon: 'WarnTriangleFilled' })
|
||
break
|
||
case CheckData.ChnCheckResultEnum.NOT_PART_IN_ERROR:
|
||
tempChnBtnResult.push({ color: CheckData.ButtonColorEnum.INFO, icon: 'Minus' })
|
||
break
|
||
default:
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
temp.devices.push({
|
||
deviceId: device.deviceId,
|
||
deviceName: device.deviceName,
|
||
chnResult: tempChnBtnResult,
|
||
})
|
||
})
|
||
return temp
|
||
})
|
||
|
||
return result
|
||
})
|
||
|
||
// ========== 监听器 ==========
|
||
// 监听测试状态变化
|
||
watch(testStatus, function(newValue, oldValue) {
|
||
// 开始测试
|
||
if (newValue == 'start') {
|
||
// 根据测试项配置决定是否需要初始化
|
||
if (!checkStore.selectTestItems.preTest && !checkStore.selectTestItems.channelsTest) {
|
||
ElMessage.success('初始化开始!')
|
||
emit('update:testStatus', 'test_init')
|
||
testLogList.push({ type: 'info', log: `${new Date().toLocaleString()}:初始化开始!` })
|
||
} else {
|
||
// 直接进入检测流程
|
||
emit('update:testStatus', 'process')
|
||
}
|
||
// 开始计时
|
||
startTimeCount()
|
||
// 显示测试日志抽屉
|
||
showTestLog()
|
||
// 重置时间记录
|
||
startData.value = new Date()
|
||
timeDifference.value = 0
|
||
}
|
||
|
||
// 需要停止计时的状态
|
||
if (newValue == 'recheck' || newValue == 'error' || newValue == 'test_init_fail' || newValue == 'connect_timeout' || newValue == 'pause_timeout' || oldValue == 'error_flow_end') {
|
||
stopTimeCount()
|
||
}
|
||
})
|
||
|
||
// 防重复处理计数器
|
||
let count = 0
|
||
|
||
// 监听WebSocket消息变化,处理各种检测状态和错误
|
||
watch(webMsgSend, function(newValue, oldValue) {
|
||
console.log('webMsgSend', newValue)
|
||
// 只在非等待状态下处理消息
|
||
if (testStatus.value !== 'waiting') {
|
||
// 步骤4:正式检测阶段的消息处理
|
||
if (props.stepsActive === 4) {
|
||
// ========== 初始化错误码处理 ==========
|
||
if (newValue.code == 10520) {
|
||
ElMessageBox.alert('报文解析异常!', '初始化失败', {
|
||
confirmButtonText: '确定',
|
||
type: 'error',
|
||
})
|
||
testLogList.push({ type: 'error', log: `${new Date().toLocaleString()}:报文解析异常!` })
|
||
emit('update:testStatus', 'test_init_fail')
|
||
} else if (newValue.code == 10521) {
|
||
ElMessageBox.alert('程控源參数有误!', '初始化失败', {
|
||
confirmButtonText: '确定',
|
||
type: 'error',
|
||
})
|
||
testLogList.push({ type: 'error', log: `${new Date().toLocaleString()}:程控源參数有误!` })
|
||
emit('update:testStatus', 'test_init_fail')
|
||
} else if (newValue.code == 10522) {
|
||
ElMessageBox.alert('测试项解析有误!', '初始化失败', {
|
||
confirmButtonText: '确定',
|
||
type: 'error',
|
||
})
|
||
testLogList.push({ type: 'error', log: `${new Date().toLocaleString()}:测试项解析有误!` })
|
||
emit('update:testStatus', 'test_init_fail')
|
||
} else if (newValue.code == 10523) {
|
||
if (count === 0) {
|
||
ElMessageBox.alert('源连接失败!', '初始化失败', {
|
||
confirmButtonText: '确定',
|
||
type: 'error',
|
||
})
|
||
testLogList.push({ type: 'error', log: `${new Date().toLocaleString()}:源连接失败!` })
|
||
emit('update:testStatus', 'test_init_fail')
|
||
count++
|
||
}
|
||
} else if (newValue.code == 10524) {
|
||
ElMessageBox.alert('获取源控制权失败!', '初始化失败', {
|
||
confirmButtonText: '确定',
|
||
type: 'error',
|
||
})
|
||
testLogList.push({ type: 'error', log: `${new Date().toLocaleString()}:获取源控制权失败!` })
|
||
emit('update:testStatus', 'test_init_fail')
|
||
} else if (newValue.code == 10525) {
|
||
ElMessageBox.alert('重置源失败!', '初始化失败', {
|
||
confirmButtonText: '确定',
|
||
type: 'error',
|
||
})
|
||
testLogList.push({ type: 'error', log: `${new Date().toLocaleString()}:重置源失败!` })
|
||
emit('update:testStatus', 'test_init_fail')
|
||
} else if (newValue.code == 10527) {
|
||
ElMessageBox.alert('源未进行初始化!', '初始化失败', {
|
||
confirmButtonText: '确定',
|
||
type: 'error',
|
||
})
|
||
testLogList.push({ type: 'error', log: `${new Date().toLocaleString()}:源未进行初始化!` })
|
||
emit('update:testStatus', 'test_init_fail')
|
||
} else if (newValue.code == 10528) {
|
||
ElMessageBox.alert('目标源有误(该用户已控制其他源,在关闭前无法操作新的源)!', '初始化失败', {
|
||
confirmButtonText: '确定',
|
||
type: 'error',
|
||
})
|
||
testLogList.push({
|
||
type: 'error',
|
||
log: `${new Date().toLocaleString()}:目标源有误(该用户已控制其他源,在关闭前无法操作新的源)!`,
|
||
})
|
||
emit('update:testStatus', 'test_init_fail')
|
||
} else if (newValue.code == 10529) {
|
||
ElMessageBox.alert('源状态有误,无法响应报文(例如源处于输出状态,无法响应初始化报文)!', '初始化失败', {
|
||
confirmButtonText: '确定',
|
||
type: 'error',
|
||
})
|
||
testLogList.push({
|
||
type: 'error',
|
||
log: `${new Date().toLocaleString()}:源状态有误,无法响应报文(例如源处于输出状态,无法响应初始化报文)!`,
|
||
})
|
||
emit('update:testStatus', 'test_init_fail')
|
||
} else if (newValue.code == 10550) {
|
||
testLogList.push({ type: 'error', log: `${new Date().toLocaleString()}:${newValue.data}设备连接异常!` })
|
||
emit('update:testStatus', 'test_init_fail')
|
||
} else if (newValue.code == 10551) {
|
||
ElMessageBox.alert(`${newValue.data}设备触发报告异常!`, '初始化失败', {
|
||
confirmButtonText: '确定',
|
||
type: 'error',
|
||
})
|
||
testLogList.push({ type: 'error', log: `${new Date().toLocaleString()}:${newValue.data}设备触发报告异常!` })
|
||
emit('update:testStatus', 'test_init_fail')
|
||
} else if (newValue.code == 10552) {
|
||
ElMessageBox.alert('存在已经初始化步骤,执行自动关闭,请重新发起检测', '初始化失败', {
|
||
confirmButtonText: '确定',
|
||
type: 'error',
|
||
})
|
||
testLogList.push({
|
||
type: 'error',
|
||
log: `${new Date().toLocaleString()}:存在已经初始化步骤,执行自动关闭,请重新发起检测!`,
|
||
})
|
||
emit('update:testStatus', 'test_init_fail')
|
||
} else {
|
||
// ========== 根据请求ID处理不同类型的消息 ==========
|
||
switch (newValue.requestId) {
|
||
// 服务端错误
|
||
case 'server_error':
|
||
if (newValue.operateCode === 'server_error' && count === 0) {
|
||
ElMessageBox.alert('服务端主动关闭连接!', '初始化失败', {
|
||
confirmButtonText: '确定',
|
||
type: 'error',
|
||
})
|
||
testLogList.push({ type: 'error', log: `${new Date().toLocaleString()}:服务端主动关闭连接!` })
|
||
emit('update:testStatus', 'test_init_fail')
|
||
count++
|
||
}
|
||
break
|
||
// 设备端错误
|
||
case 'device_error':
|
||
if (newValue.operateCode === 'device_error' && count === 0) {
|
||
ElMessageBox.alert('设备端主动关闭连接!', '初始化失败', {
|
||
confirmButtonText: '确定',
|
||
type: 'error',
|
||
})
|
||
testLogList.push({ type: 'error', log: `${new Date().toLocaleString()}:设备端主动关闭连接!` })
|
||
emit('update:testStatus', 'test_init_fail')
|
||
count++
|
||
}
|
||
break
|
||
// 正式测试相关消息
|
||
case 'formal_real':
|
||
switch (newValue.operateCode) {
|
||
case 'stop_timeout':
|
||
ElMessageBox.alert(`暂停时间已过10分钟,本次检测已失效,请重新发起检测!`, '暂停时间过长', {
|
||
confirmButtonText: '确定',
|
||
type: 'error',
|
||
})
|
||
emit('update:testStatus', 'pause_timeout')
|
||
break
|
||
}
|
||
break
|
||
case 'error_flow_end':
|
||
ElMessageBox.alert(`设备连接异常,请检查设备连接情况!`, '初始化失败', {
|
||
confirmButtonText: '确定',
|
||
type: 'error',
|
||
})
|
||
emit('update:testStatus', 'test_init_fail')
|
||
break
|
||
case 'socket_timeout':
|
||
ElMessageBox.alert('连接超时!', '连接超时', {
|
||
confirmButtonText: '确定',
|
||
type: 'error',
|
||
})
|
||
emit('update:testStatus', 'connect_timeout')
|
||
break
|
||
case 'connect':
|
||
switch (newValue.operateCode) {
|
||
case 'Source':
|
||
ElMessageBox.alert('源通讯失败,请检查源连接情况!', '初始化失败', {
|
||
confirmButtonText: '确定',
|
||
type: 'error',
|
||
})
|
||
testLogList.push({ type: 'error', log: `${new Date().toLocaleString()}:源通讯失败!` })
|
||
break
|
||
case 'Dev':
|
||
ElMessageBox.alert('设备通讯失败,请检查设备连接情况!', '初始化失败', {
|
||
confirmButtonText: '确定',
|
||
type: 'error',
|
||
})
|
||
testLogList.push({ type: 'error', log: `${new Date().toLocaleString()}:设备通讯失败!` })
|
||
break
|
||
}
|
||
emit('update:testStatus', 'test_init_fail')
|
||
break
|
||
// 源通信校验阶段
|
||
case 'yjc_ytxjy':
|
||
switch (newValue.operateCode) {
|
||
case 'INIT_GATHER':
|
||
if (newValue.code == 10200) {
|
||
testLogList.push({ type: 'info', log: `${new Date().toLocaleString()}:源初始化成功!` })
|
||
percentage.value = 1
|
||
}
|
||
if (newValue.code == -1) {
|
||
ElMessageBox.alert('源未知异常!', '初始化失败', {
|
||
confirmButtonText: '确定',
|
||
type: 'error',
|
||
})
|
||
testLogList.push({ type: 'error', log: `${new Date().toLocaleString()}:源未知异常!` })
|
||
emit('update:testStatus', 'test_init_fail')
|
||
}
|
||
break
|
||
}
|
||
break
|
||
// 设备通信校验阶段
|
||
case 'yjc_sbtxjy':
|
||
switch (newValue.operateCode) {
|
||
case 'INIT_GATHER$01':
|
||
if (newValue.code == 25001) {
|
||
testLogList.push({ type: 'info', log: `${new Date().toLocaleString()}:设备通讯校验成功!` })
|
||
percentage.value = 2
|
||
}
|
||
break
|
||
}
|
||
break
|
||
// 协议校验阶段
|
||
case 'yjc_xyjy':
|
||
switch (newValue.operateCode) {
|
||
case 'VERIFY_MAPPING$01':
|
||
if (newValue.code == 25001) {
|
||
ElMessage.success('初始化成功!')
|
||
testLogList.push({ type: 'info', log: `${new Date().toLocaleString()}:协议校验成功!` })
|
||
timeDifference.value = +new Date().getTime() - startData.value.getTime()
|
||
testLogList.push({ type: 'info', log: `${new Date().toLocaleString()}:初始化成功!` })
|
||
percentage.value = 3
|
||
// 初始化完成,准备开始正式检测
|
||
activeIndex = getNextActiveIndex() + 2
|
||
emit('update:testStatus', 'process')
|
||
} else if (newValue.code == 25002) {
|
||
let data = JSON.parse(newValue.data)
|
||
ElMessageBox.alert(`脚本与icd校验失败!icd名称:${data['icdType']} -> 校验项:${data['dataType']}`, '初始化失败', {
|
||
confirmButtonText: '确定',
|
||
type: 'error',
|
||
})
|
||
testLogList.push({
|
||
type: 'error',
|
||
log: `${new Date().toLocaleString()}:脚本与icd校验失败!icd名称:${data['icdType']} -> 校验项:${data['dataType']}`,
|
||
})
|
||
emit('update:testStatus', 'test_init_fail')
|
||
} else if (newValue.code == 10500) {
|
||
ElMessageBox.alert(`装置中未找到该icd!`, '初始化失败', {
|
||
confirmButtonText: '确定',
|
||
type: 'error',
|
||
})
|
||
testLogList.push({
|
||
type: 'error',
|
||
log: `${new Date().toLocaleString()}:装置中未找到该icd!`,
|
||
})
|
||
emit('update:testStatus', 'test_init_fail')
|
||
}
|
||
break
|
||
}
|
||
break
|
||
// 暂停测试成功
|
||
case 'preStopTest':
|
||
if (newValue.operateCode == 'stop') {
|
||
ElMessage.success('暂停成功')
|
||
emit('update:testStatus', 'paused')
|
||
pauseSuccessCallback()
|
||
}
|
||
break
|
||
// 继续测试成功
|
||
case 'Resume_Success':
|
||
ElMessage.success('开始继续检测')
|
||
emit('update:testStatus', 'process')
|
||
handleResumeTest()
|
||
break
|
||
// ========== 各类检测项的开始和结束消息处理 ==========
|
||
// 频率检测
|
||
case 'FREQ_Start':
|
||
handleStartItem('FREQ', newValue.desc)
|
||
break
|
||
case 'FREQ_End':
|
||
handleEndItem('FREQ', newValue.desc, newValue.data)
|
||
break
|
||
case 'V_Start':
|
||
handleStartItem('V', newValue.desc)
|
||
break
|
||
case 'V_End':
|
||
handleEndItem('V', newValue.desc, newValue.data)
|
||
break
|
||
case 'HV_Start':
|
||
handleStartItem('HV', newValue.desc)
|
||
break
|
||
case 'HV_End':
|
||
handleEndItem('HV', newValue.desc, newValue.data)
|
||
break
|
||
case 'HI_Start':
|
||
handleStartItem('HI', newValue.desc)
|
||
break
|
||
case 'HI_End':
|
||
handleEndItem('HI', newValue.desc, newValue.data)
|
||
break
|
||
case 'HP_Start':
|
||
handleStartItem('HP', newValue.desc)
|
||
break
|
||
case 'HP_End':
|
||
handleEndItem('HP', newValue.desc, newValue.data)
|
||
break
|
||
case 'HSV_Start':
|
||
handleStartItem('HSV', newValue.desc)
|
||
break
|
||
case 'HSV_End':
|
||
handleEndItem('HSV', newValue.desc, newValue.data)
|
||
break
|
||
case 'HSI_Start':
|
||
handleStartItem('HSI', newValue.desc)
|
||
break
|
||
case 'HSI_End':
|
||
handleEndItem('HSI', newValue.desc, newValue.data)
|
||
break
|
||
case 'VOLTAGE_Start':
|
||
handleStartItem('VOLTAGE', newValue.desc)
|
||
break
|
||
case 'VOLTAGE_End':
|
||
handleEndItem('VOLTAGE', newValue.desc, newValue.data)
|
||
break
|
||
case 'I_Start':
|
||
handleStartItem('I', newValue.desc)
|
||
break
|
||
case 'I_End':
|
||
handleEndItem('I', newValue.desc, newValue.data)
|
||
break
|
||
case 'IMBV_Start':
|
||
handleStartItem('IMBV', newValue.desc)
|
||
break
|
||
case 'IMBV_End':
|
||
handleEndItem('IMBV', newValue.desc, newValue.data)
|
||
break
|
||
case 'IMBA_Start':
|
||
handleStartItem('IMBA', newValue.desc)
|
||
break
|
||
case 'IMBA_End':
|
||
handleEndItem('IMBA', newValue.desc, newValue.data)
|
||
break
|
||
case 'F_Start':
|
||
handleStartItem('F', newValue.desc)
|
||
break
|
||
case 'F_End':
|
||
handleEndItem('F', newValue.desc, newValue.data)
|
||
break
|
||
// 检测结束
|
||
case 'Quit':
|
||
console.log('检测结束')
|
||
break
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}, { deep: true })
|
||
|
||
// ========== 检测项处理函数 ==========
|
||
// 处理检测项开始消息
|
||
const handleStartItem = (code: string, desc: string | undefined) => {
|
||
if (desc === undefined) {
|
||
// 大测试项开始
|
||
activeIndex = getActiveIndex(code)
|
||
updateCheckResultView(code, true) // 更新界面为加载状态
|
||
updateLog(true) // 记录开始日志
|
||
} else {
|
||
// 子测试项开始
|
||
testLogList.push({ type: 'info', log: `${new Date().toLocaleString()}:${desc}准确度检测:开始` })
|
||
}
|
||
}
|
||
|
||
// 处理检测项结束消息
|
||
const handleEndItem = (code: string, desc: string | undefined, devices: CheckData.DeviceCheckResult[] = []) => {
|
||
if (desc === undefined) {
|
||
// 大测试项结束
|
||
updatePercentage() // 更新进度
|
||
updateCheckResultView(code, false, devices) // 更新检测结果
|
||
updateLog(false) // 记录结束日志
|
||
if (testStatus.value != 'paused') {
|
||
// 如果未暂停,继续下一个测试项
|
||
activeIndex = getNextActiveIndex(code)
|
||
}
|
||
} else {
|
||
// 子测试项结束,根据结果记录不同类型的日志
|
||
let result = getResult(devices)
|
||
if (result === 1) {
|
||
testLogList.push({ type: 'info', log: `${new Date().toLocaleString()}:${desc}检测结束:符合` })
|
||
}
|
||
if (result === 2) {
|
||
testLogList.push({ type: 'error', log: `${new Date().toLocaleString()}:${desc}检测结束:不符合` })
|
||
}
|
||
if (result === 4) {
|
||
testLogList.push({ type: 'warning', log: `${new Date().toLocaleString()}:${desc}检测结束:数据异常` })
|
||
}
|
||
}
|
||
}
|
||
|
||
// 更新检测进度
|
||
const updatePercentage = async () => {
|
||
if (activeIndex < checkTotal) {
|
||
// 计算并更新进度百分比
|
||
percentage.value = Math.trunc(activeIndex / checkTotal * 100)
|
||
} else {
|
||
// 所有检测项完成
|
||
percentage.value = 100
|
||
emit('update:testStatus', 'success')
|
||
|
||
// 检查是否需要自动生成报告
|
||
let { data: autoGenerate } = await getAutoGenerate()
|
||
if (autoGenerate == 1) {
|
||
// 自动生成报告
|
||
let devIdList = checkStore.devices.map(item => {
|
||
return item.deviceId
|
||
})
|
||
|
||
await generateDevReport({
|
||
'planId': checkStore.plan.id,
|
||
'devIdList': devIdList,
|
||
'scriptId': checkStore.plan.scriptId,
|
||
'planCode': checkStore.plan.code + '',
|
||
'pageNum': 1,
|
||
'pageSize': 999,
|
||
})
|
||
}
|
||
// 提示检测完成
|
||
ElMessageBox.alert('检测全部结束,你可以停留在此页面查看检测结果,或返回首页进行复检、报告生成和归档等操作', '检测完成', {
|
||
confirmButtonText: '确定',
|
||
})
|
||
// 关闭WebSocket连接
|
||
emit('closeWebSocket')
|
||
}
|
||
}
|
||
|
||
// ========== 生命周期钩子 ==========
|
||
// 组件挂载前初始化数据
|
||
onBeforeMount(async () => {
|
||
await initScriptData() // 获取检测脚本配置
|
||
initDeviceList() // 初始化设备列表
|
||
initCheckResult() // 初始化检测结果数据结构
|
||
})
|
||
|
||
// ========== 界面交互函数 ==========
|
||
// 显示测试日志抽屉
|
||
const showTestLog = () => {
|
||
drawer.value = true
|
||
}
|
||
|
||
// ========== 数据初始化函数 ==========
|
||
// 从服务器获取检测脚本配置数据
|
||
const initScriptData = async () => {
|
||
// 根据复检类型、计划ID、设备ID列表获取测试项
|
||
let response: any = await getBigTestItem({
|
||
reCheckType: checkStore.reCheckType,
|
||
planId: checkStore.plan.id,
|
||
devIds: checkStore.devices.map(item => item.deviceId),
|
||
})
|
||
|
||
// 格式化脚本数据
|
||
let temp = response.data.map((item: any) => {
|
||
return {
|
||
...item,
|
||
scriptName: item.scriptName,
|
||
}
|
||
})
|
||
|
||
// 保存脚本数据并设置总数
|
||
scriptData.push(...temp)
|
||
checkTotal = scriptData.length
|
||
}
|
||
|
||
// 从store中获取设备列表数据
|
||
const initDeviceList = () => {
|
||
Object.assign(deviceList, checkStore.devices)
|
||
}
|
||
|
||
// 初始化检测结果数据结构(每个测试项的每个设备的每个通道)
|
||
const initCheckResult = () => {
|
||
let result: CheckData.ScriptChnItem[] = []
|
||
|
||
// 为每个检测脚本创建结果数据结构
|
||
scriptData.forEach(item => {
|
||
let temp: CheckData.ScriptChnItem = {
|
||
scriptType: item.id,
|
||
scriptName: item.scriptName,
|
||
devices: [],
|
||
}
|
||
|
||
// 为每个设备创建通道结果
|
||
for (let i = 0; i < deviceList?.length; i++) {
|
||
let tempChnResult: CheckData.ChnCheckResultEnum[] = []
|
||
// 初始化所有通道状态为UNKNOWN
|
||
for (let j = 0; j < deviceList[i].chnNum; j++) {
|
||
tempChnResult.push(CheckData.ChnCheckResultEnum.UNKNOWN)
|
||
}
|
||
temp.devices.push({
|
||
deviceId: deviceList[i].deviceId,
|
||
deviceName: deviceList[i].deviceName,
|
||
chnResult: tempChnResult,
|
||
})
|
||
}
|
||
result.push(temp)
|
||
})
|
||
Object.assign(checkResult, result)
|
||
}
|
||
|
||
// ========== 数据更新函数 ==========
|
||
// 更新指定测试项的检测结果
|
||
const updateCheckResult = (data: CheckData.ScriptChnItem) => {
|
||
const { scriptType } = { ...data }
|
||
|
||
// 找到对应的测试项并更新其结果
|
||
checkResult.forEach(item => {
|
||
if (item.scriptType == scriptType) {
|
||
// 更新每个设备的通道结果
|
||
for (let i = 0; i < item.devices.length; i++) {
|
||
let targetDevice = data.devices.find(dev => dev.deviceId === item.devices[i].deviceId)
|
||
if (targetDevice !== undefined) {
|
||
// 深拷贝结果数据
|
||
item.devices[i].chnResult = [...targetDevice.chnResult]
|
||
}
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 滚动日志到底部
|
||
const scrollToBottom = () => {
|
||
if (scrollContainerRef.value) {
|
||
scrollContainerRef.value.scrollTop = scrollContainerRef.value.scrollHeight + 70
|
||
}
|
||
}
|
||
|
||
// ========== 工具函数 ==========
|
||
// 生成随机整数(0到max-1)
|
||
function getRandomInt(max: number): number {
|
||
return Math.floor(Math.random() * max)
|
||
}
|
||
|
||
|
||
// 将毫秒时间差转换为可读的时间格式
|
||
function getTimeDifference(timeDifference: number): string {
|
||
// 时间单位常量
|
||
const millisecondsPerDay = 1000 * 60 * 60 * 24
|
||
const millisecondsPerHour = 1000 * 60 * 60
|
||
const millisecondsPerMinute = 1000 * 60
|
||
const millisecondsPerSecond = 1000
|
||
|
||
// 计算各时间单位的值
|
||
const days = Math.floor(timeDifference / millisecondsPerDay)
|
||
const hours = Math.floor((timeDifference % millisecondsPerDay) / millisecondsPerHour)
|
||
const minutes = Math.floor((timeDifference % millisecondsPerHour) / millisecondsPerMinute)
|
||
const seconds = Math.floor((timeDifference % millisecondsPerMinute) / millisecondsPerSecond)
|
||
|
||
// 根据时间长度返回合适的格式
|
||
if (days > 0) {
|
||
return `: ${days} 天, ${hours} 小时, ${minutes} 分钟, ${seconds} 秒`
|
||
} else if (hours > 0) {
|
||
return `: ${hours} 小时, ${minutes} 分钟, ${seconds} 秒`
|
||
} else {
|
||
return `: ${minutes} 分钟, ${seconds} 秒`
|
||
}
|
||
}
|
||
|
||
// 监听日志列表变化,自动滚动到底部
|
||
watch(testLogList, () => {
|
||
scrollToBottom()
|
||
}, { deep: true })
|
||
|
||
// ========== 日志记录函数 ==========
|
||
// 更新检测日志(记录检测项的开始和结束)
|
||
const updateLog = (isStart: boolean) => {
|
||
const currentTime = ref(new Date().toLocaleString())
|
||
let timeDifferenceItem = 0
|
||
|
||
// 确保在有效的测试项范围内
|
||
if (activeIndex <= checkTotal) {
|
||
if (isStart) {
|
||
// 记录测试项开始
|
||
startData.value = new Date()
|
||
testLogList.push({
|
||
type: 'info',
|
||
log: currentTime.value + ` :${scriptData[activeIndex - 1].scriptName}准确度检测:开始`,
|
||
})
|
||
} else {
|
||
// 记录测试项结束
|
||
endData.value = new Date()
|
||
timeDifferenceItem = endData.value.getTime() - startData.value.getTime()
|
||
timeDifference.value += timeDifferenceItem
|
||
|
||
// 根据检测结果记录不同类型的日志
|
||
let errorItem = getErrorCheckItem(scriptData[activeIndex - 1].id)
|
||
switch (errorItem?.type) {
|
||
case CheckData.ChnCheckResultEnum.SUCCESS:
|
||
testLogList.push({
|
||
type: 'info',
|
||
log: currentTime.value + ` :${scriptData[activeIndex - 1].scriptName}准确度检测结束:符合,用时` + getTimeDifference(timeDifferenceItem),
|
||
})
|
||
break
|
||
case CheckData.ChnCheckResultEnum.FAIL:
|
||
testLogList.push({
|
||
type: 'error',
|
||
log: currentTime.value + ` :${scriptData[activeIndex - 1].scriptName}准确度检测结束:不符合,用时` + getTimeDifference(timeDifferenceItem),
|
||
})
|
||
break
|
||
case CheckData.ChnCheckResultEnum.TIMEOUT:
|
||
testLogList.push({
|
||
type: 'warning',
|
||
log: currentTime.value + ` :${scriptData[activeIndex - 1].scriptName}准确度检测结束:连接超时,用时` + getTimeDifference(timeDifferenceItem),
|
||
})
|
||
break
|
||
case CheckData.ChnCheckResultEnum.ERRORDATA:
|
||
testLogList.push({
|
||
type: 'warning',
|
||
log: currentTime.value + ` :${scriptData[activeIndex - 1].scriptName}准确度检测结束:数据异常,用时` + getTimeDifference(timeDifferenceItem),
|
||
})
|
||
break
|
||
}
|
||
|
||
// 更新总时间显示
|
||
timeView.value = secondToTime(timeDifference.value / 1000)
|
||
// 如果是最后一个测试项,记录总结日志
|
||
if (activeIndex === checkTotal) {
|
||
testLogList.push({
|
||
type: 'info',
|
||
log: currentTime.value + ' :检测结束,总用时' + getTimeDifference(timeDifference.value),
|
||
})
|
||
stopTimeCount()
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// ========== 错误处理函数 ==========
|
||
// 设置测试项错误状态(分析所有通道结果,确定整体状态)
|
||
const setErrorCheckItem = (scriptType: string, devices: CheckData.DeviceCheckResult[]) => {
|
||
let type = 1 // 默认为成功
|
||
let tempChnResult: CheckData.ChnCheckResultEnum[] = []
|
||
|
||
// 收集所有设备所有通道的结果
|
||
for (let i = 0; i < devices.length; i++) {
|
||
tempChnResult.push(...devices[i].chnResult)
|
||
}
|
||
|
||
// 按优先级确定整体状态(数据异常 > 失败 > 超时)
|
||
if (tempChnResult.some(item => item === CheckData.ChnCheckResultEnum.ERRORDATA)) {
|
||
type = CheckData.ChnCheckResultEnum.ERRORDATA
|
||
}
|
||
if (tempChnResult.some(item => item === CheckData.ChnCheckResultEnum.FAIL)) {
|
||
type = CheckData.ChnCheckResultEnum.FAIL
|
||
}
|
||
if (tempChnResult.some(item => item === CheckData.ChnCheckResultEnum.TIMEOUT)) {
|
||
type = CheckData.ChnCheckResultEnum.TIMEOUT
|
||
// 处理超时情况:记录日志并弹出提示
|
||
for (let i = 0; i < devices.length; i++) {
|
||
if (devices[i].chnResult.some(item => item === CheckData.ChnCheckResultEnum.TIMEOUT)) {
|
||
testLogList.push({
|
||
type: 'warning',
|
||
log: `${new Date().toLocaleString()} :${devices[i].deviceName}连接超时`,
|
||
})
|
||
ElMessageBox.alert('连接超时!', '连接超时', {
|
||
confirmButtonText: '确定',
|
||
type: 'error',
|
||
})
|
||
emit('update:testStatus', 'connect_timeout')
|
||
}
|
||
}
|
||
}
|
||
|
||
// 记录测试项的错误状态
|
||
errorCheckItem.push({ scriptType, type: type })
|
||
}
|
||
|
||
// 获取指定测试项的错误状态
|
||
// 判断该检测项是否全部合格(所有设备所有通道所有子检测项目全部合格为合格,否则为不合格)
|
||
function getErrorCheckItem(scriptType: string) {
|
||
let results = errorCheckItem.filter((item) => item.scriptType === scriptType)
|
||
if (results.length > 0) {
|
||
return results[0]
|
||
} else {
|
||
return null
|
||
}
|
||
}
|
||
|
||
// 更新检测结果视图
|
||
const updateCheckResultView = (scriptCode: string, isStart: boolean, devices: CheckData.DeviceCheckResult[] = []) => {
|
||
// 根据代码找到对应的测试项ID
|
||
let scriptType = scriptData.filter(item => item.code === scriptCode)[0]?.id
|
||
let temp = null
|
||
|
||
if (isStart) {
|
||
// 开始时设置为加载状态
|
||
temp = getLoadingResult(scriptType)
|
||
} else {
|
||
// 结束时设置实际结果并记录错误状态
|
||
setErrorCheckItem(scriptType, devices)
|
||
temp = {
|
||
scriptType,
|
||
devices,
|
||
}
|
||
}
|
||
updateCheckResult(temp)
|
||
}
|
||
|
||
// 生成加载状态的结果数据(所有通道显示为正在检测)
|
||
const getLoadingResult = (scriptType: string) => {
|
||
let devices = []
|
||
|
||
// 为每个设备的每个通道设置LOADING状态
|
||
devices = deviceList.map(item => {
|
||
let tempChnResult: CheckData.ChnCheckResultEnum[] = []
|
||
for (let i = 0; i < item.chnNum; i++) {
|
||
tempChnResult.push(CheckData.ChnCheckResultEnum.LOADING)
|
||
}
|
||
|
||
return {
|
||
deviceId: item.deviceId,
|
||
deviceName: item.deviceName,
|
||
chnResult: tempChnResult,
|
||
}
|
||
})
|
||
|
||
let tempScriptChnItem: CheckData.ScriptChnItem = {
|
||
scriptType,
|
||
devices,
|
||
}
|
||
|
||
return tempScriptChnItem
|
||
}
|
||
|
||
// 分析设备检测结果,返回整体状态
|
||
const getResult = (devices: CheckData.DeviceCheckResult[] = []) => {
|
||
let type = 1 // 默认成功
|
||
let tempChnResult: CheckData.ChnCheckResultEnum[] = []
|
||
|
||
// 收集所有设备的所有通道结果
|
||
for (let i = 0; i < devices.length; i++) {
|
||
tempChnResult.push(...devices[i].chnResult)
|
||
}
|
||
|
||
// 按优先级确定结果类型(数据异常 > 失败)
|
||
if (tempChnResult.some(item => item === CheckData.ChnCheckResultEnum.ERRORDATA)) {
|
||
type = CheckData.ChnCheckResultEnum.ERRORDATA
|
||
}
|
||
if (tempChnResult.some(item => item === CheckData.ChnCheckResultEnum.FAIL)) {
|
||
type = CheckData.ChnCheckResultEnum.FAIL
|
||
}
|
||
return type
|
||
}
|
||
|
||
// ========== 事件处理函数 ==========
|
||
// 点击通道按钮,查看检测详情
|
||
// 参数:设备信息,通道号(-1代表查看全部通道),测试项类型
|
||
const handleClick = (item: any, chnNum: number, scriptType: string) => {
|
||
// 查找对应的检测结果
|
||
let checkResultItem = checkResult.find(obj => obj.scriptType === scriptType)
|
||
let flag = -1 // -1:正常, 0:超时, 1:数据异常
|
||
|
||
if (checkResultItem) {
|
||
let device = checkResultItem.devices.find(obj => obj.deviceId === item.deviceId)
|
||
if (device) {
|
||
let chnResult = device.chnResult
|
||
if (chnNum === -1) {
|
||
// 查看全部通道的状态
|
||
if (chnResult.findIndex(obj => obj === CheckData.ChnCheckResultEnum.TIMEOUT) !== -1) {
|
||
flag = 0
|
||
}
|
||
if (chnResult.findIndex(obj => obj === CheckData.ChnCheckResultEnum.ERRORDATA) !== -1) {
|
||
flag = 1
|
||
}
|
||
} else {
|
||
// 查看特定通道的状态
|
||
if (chnResult[chnNum - 1] === CheckData.ChnCheckResultEnum.TIMEOUT) {
|
||
flag = 0
|
||
}
|
||
if (chnResult[chnNum - 1] === CheckData.ChnCheckResultEnum.ERRORDATA) {
|
||
flag = 1
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 根据状态进行不同处理
|
||
if (flag === 0) {
|
||
// 超时状态:显示提示信息
|
||
ElMessageBox.alert('连接超时,请检查设备通讯是否正常', '连接超时', {
|
||
confirmButtonText: '确定',
|
||
type: 'warning',
|
||
})
|
||
}
|
||
if (flag === -1 || flag === 1) {
|
||
// 正常或数据异常:打开详情弹窗
|
||
checkStore.setShowDetailType(2)
|
||
dataCheckSingleChannelSingleTestPopupRef.value?.open(item.deviceId, chnNum + '', scriptType)
|
||
}
|
||
}
|
||
|
||
// ========== 暂停/继续相关函数 ==========
|
||
// 处理暂停请求
|
||
const handlePause = () => {
|
||
testLogList.push({
|
||
type: 'error',
|
||
log: `${new Date().toLocaleString()}:当前测试小项正在执行中,将在该小项执行结束后暂停...`,
|
||
})
|
||
}
|
||
|
||
// 暂停成功回调
|
||
const pauseSuccessCallback = () => {
|
||
// 记录暂停时的时间
|
||
endData.value = new Date()
|
||
let diffTime = endData.value.getTime() - startData.value.getTime()
|
||
timeDifference.value += diffTime
|
||
|
||
// 记录暂停日志
|
||
testLogList.push({
|
||
type: 'info',
|
||
log: `${new Date().toLocaleString()}:暂停检测`,
|
||
})
|
||
stopTimeCount()
|
||
console.log('暂停中')
|
||
}
|
||
|
||
|
||
// 处理继续检测
|
||
const handleResumeTest = () => {
|
||
// 重新开始计时
|
||
startData.value = new Date()
|
||
testLogList.push({ type: 'info', log: `${new Date().toLocaleString()}:开始重新检测!` })
|
||
resumeTimeCount()
|
||
console.log('开始继续检测')
|
||
}
|
||
|
||
// ========== 测试项索引管理函数 ==========
|
||
// 根据测试项代码获取对应的索引号(从1开始)
|
||
const getActiveIndex = (code: string): number => {
|
||
for (let i = 0; i < scriptData.length; i++) {
|
||
if (scriptData[i].code === code) {
|
||
return i + 1
|
||
}
|
||
}
|
||
return -1
|
||
}
|
||
|
||
// 根据当前测试项代码获取下一个测试项的索引号
|
||
const getNextActiveIndex = (code: string = ''): number => {
|
||
if (code === '') {
|
||
return -1
|
||
}
|
||
for (let i = 0; i < scriptData.length; i++) {
|
||
if (scriptData[i].code === code) {
|
||
return i + 2 // 下一个测试项索引
|
||
}
|
||
}
|
||
return -1
|
||
}
|
||
// ========== 时间计数器管理函数 ==========
|
||
// 开始计时
|
||
const startTimeCount = () => {
|
||
if (!timer) {
|
||
timer = setInterval(() => {
|
||
timeCount.value = timeCount.value + 1
|
||
timeView.value = secondToTime(timeCount.value)
|
||
}, 1000)
|
||
}
|
||
}
|
||
|
||
// 停止计时
|
||
const stopTimeCount = () => {
|
||
if (timer) {
|
||
clearInterval(timer)
|
||
timer = null
|
||
}
|
||
}
|
||
|
||
|
||
// 恢复计时(用于暂停后继续)
|
||
const resumeTimeCount = () => {
|
||
timer = setInterval(() => {
|
||
timeCount.value = timeCount.value + 1
|
||
timeView.value = secondToTime(timeCount.value)
|
||
}, 1000)
|
||
}
|
||
|
||
|
||
// 将秒数转换为 HH:MM:SS 格式
|
||
const secondToTime = (second: number) => {
|
||
let h: string | number = Math.floor(second / 3600) // 小时
|
||
let m: string | number = Math.floor((second - h * 3600) / 60) // 分钟
|
||
let s: string | number = Math.floor(second % 60) // 秒
|
||
|
||
// 补齐前导零
|
||
h = h < 10 ? '0' + h : h
|
||
m = m < 10 ? '0' + m : m
|
||
s = s < 10 ? '0' + s : s
|
||
return h + ':' + m + ':' + s
|
||
}
|
||
|
||
// 组件卸载前清理定时器
|
||
onBeforeUnmount(() => {
|
||
if (timer) {
|
||
clearInterval(timer)
|
||
timer = null
|
||
}
|
||
})
|
||
|
||
// 暴露给父组件的方法
|
||
defineExpose({
|
||
handlePause, // 暂停方法
|
||
})
|
||
</script>
|
||
|
||
<!-- ========== 样式定义 ========== -->
|
||
<style scoped lang='scss'>
|
||
|
||
/* 表格样式 */
|
||
:deep(.el-table .header-row) {
|
||
background-color: #f5f7fa;
|
||
}
|
||
|
||
:deep(.el-table .warning-row) {
|
||
color: red;
|
||
}
|
||
|
||
.el-table .success-row {
|
||
--el-table-tr-bg-color: var(--el-color-success-light-9);
|
||
}
|
||
|
||
/* 主对话框容器 */
|
||
.dialog {
|
||
display: flex;
|
||
flex-direction: column;
|
||
overflow-y: hidden;
|
||
overflow-x: hidden;
|
||
}
|
||
|
||
/* 对话框标题栏 */
|
||
.dialog-title {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-right: 10px;
|
||
margin-bottom: 10px;
|
||
|
||
/* 时间显示区域 */
|
||
.timeView {
|
||
display: flex;
|
||
align-items: center;
|
||
color: #91cc75;
|
||
width: 28%;
|
||
margin-right: 0px;
|
||
text-align: left;
|
||
font-size: 26px;
|
||
font-weight: bold;
|
||
}
|
||
}
|
||
|
||
/* 对话框内容区 */
|
||
.dialog-content {
|
||
max-height: 450px;
|
||
overflow-y: hidden;
|
||
}
|
||
|
||
/* 折叠面板头部样式 */
|
||
:deep(.el-collapse-item__header) {
|
||
height: 30px;
|
||
}
|
||
|
||
/* 日志显示区域 */
|
||
.dialog-log {
|
||
height: 50px;
|
||
overflow-y: hidden;
|
||
|
||
p {
|
||
margin: 5px 0;
|
||
font-size: 14px;
|
||
}
|
||
}
|
||
|
||
/* 抽屉容器样式 */
|
||
.drawer-container {
|
||
:deep(header.el-drawer__header) {
|
||
color: #fff !important;
|
||
background-color: var(--el-color-primary) !important;
|
||
|
||
.el-drawer__close-btn svg:hover {
|
||
color: #ccc !important;
|
||
}
|
||
|
||
.el-drawer__title {
|
||
color: #fff !important;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 加载动画样式 */
|
||
.loading-box {
|
||
animation: loading 1.5s linear infinite;
|
||
}
|
||
|
||
@keyframes loading {
|
||
from {
|
||
transform: rotate(0deg);
|
||
}
|
||
to {
|
||
transform: rotate(360deg);
|
||
}
|
||
}
|
||
|
||
</style>
|
||
|
||
<style lang='scss' scoped>
|
||
/* 表格按钮样式 */
|
||
:deep(.el-button--small) {
|
||
height: 20px !important;
|
||
width: 20px !important;
|
||
}
|
||
|
||
/* 表格单元格内边距 */
|
||
:deep(.el-table--default td ) {
|
||
padding: 5px 0 !important;
|
||
}
|
||
|
||
</style>
|