yeg
2026-09-03 39ba15c8f5aaf1a905c2eac942eb86eadc95bb21
优化停止测试反馈并清理完成态告警
2个文件已修改
29 ■■■■■ 已修改文件
src/views/TestConsole.vue 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tests/test-console-alarm-and-status.contract.test.mjs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/TestConsole.vue
@@ -22,6 +22,7 @@
const testStatusLabel = ref('待测试')
let closeLiveStream: (() => void) | null = null
let reconnectTimer: number | null = null
let stopPending = false
const runtimeAttach = ref(false)
const receptionState = ref<ReceptionState>('未开始接收')
const realtimeRecords = ref<PersistedTestRecord[]>([])
@@ -89,6 +90,7 @@
}
function applyLiveState(next: TestLiveState) {
  if (stopPending && next.receptionStatus !== 'ENDED') return
  liveState.value = next
  liveConnection.value = next.sources.some(source => Boolean(source.latestSnapshot?.observedAt)
    || Boolean(source.latestSnapshot?.measurements?.length)) ? '已连接' : '空数据'
@@ -124,7 +126,7 @@
    }
    else if (event === 'test-status' && data && typeof data === 'object' && 'testStatus' in data && liveState.value)
      applyLiveState({ ...liveState.value, testStatus: (data as { testStatus: TestLiveState['testStatus'] }).testStatus })
    else if (event === 'exception' && Array.isArray(data) && liveState.value) {
    else if (event === 'exception' && Array.isArray(data) && liveState.value && !stopPending) {
      const current = (data as TestLiveState['exceptions']).filter(item => !item.status || item.status === 'OPEN')
      liveState.value = { ...liveState.value, exceptions: current }
      // 来源异常和测试预警由下方告警面板展示,不能把正常暂停/完成误报成连接错误。
@@ -258,7 +260,8 @@
// 实时告警只展示后端当前仍开放的异常,异常事实由后端状态和消息说明。
const currentExceptions = computed(() => (liveState.value?.exceptions || [])
  .filter(item => item.sourceType !== 'COLLECTOR' || !item.status || item.status === 'OPEN'))
  .filter(item => receptionState.value === '接收中'
    && (item.sourceType !== 'COLLECTOR' || !item.status || item.status === 'OPEN')))
function openStepSettings() { stepDraft.value = stepSettings.value.map(item => ({ ...item })); selectedPlanSteps.value = []; stepEditing.value = true }
function togglePlanStep(position: number, checked: string | number | boolean) { const next = new Set(selectedPlanSteps.value); checked ? next.add(position) : next.delete(position); selectedPlanSteps.value = [...next].sort((a, b) => a - b) }
@@ -458,11 +461,27 @@
  if (state.value === '检测完成') return
  try {
    await ElMessageBox.confirm('停止测试后将不可恢复,系统会停止请求本测试的数据,但不会向设备发送停止命令,是否继续?', '确认停止测试', { confirmButtonText: '停止测试', cancelButtonText: '取消', type: 'warning', confirmButtonClass: 'danger-confirm' })
    backendDetail.value = await endTestReception(testId.value)
    const previousState = state.value
    const previousReceptionState = receptionState.value
    const previousLiveState = liveState.value
    // 停止是不可恢复动作,先更新本地视图,避免等待后端配置重载和采样事务提交。
    stopPending = true
    state.value = '检测完成'
    receptionState.value = '已结束接收'
    liveError.value = ''
    testStatusLabel.value = ({ WAITING: '待测试', RUNNING: '检测中', PAUSED: '暂停', RUNNING_ABNORMAL: '检测中', COMPLETION_PENDING: '检测完成', COMPLETED: '检测完成', TERMINATED: '检测完成' } as Record<string, string>)[backendDetail.value.testStatus || 'COMPLETED'] || '检测完成'
    testStatusLabel.value = '检测完成'
    if (liveState.value)
      liveState.value = { ...liveState.value, testStatus: 'COMPLETED', receptionStatus: 'ENDED', exceptions: [] }
    try {
      backendDetail.value = await endTestReception(testId.value)
      stopPending = false
    } catch (error) {
      stopPending = false
      state.value = previousState
      receptionState.value = previousReceptionState
      liveState.value = previousLiveState
      throw error
    }
    ElMessage.success('测试已停止,系统不再请求数据')
  } catch (error) {
    if (error === 'cancel' || error === 'close') return
tests/test-console-alarm-and-status.contract.test.mjs
@@ -5,6 +5,8 @@
assert.match(view, /function connectionStatusLabel\(status\?: string\)/)
assert.match(view, /status === 'ONLINE' \? '在线' : '离线'/)
assert.match(view, /receptionState\.value !== '接收中'/)
assert.match(view, /liveState\.value = \{ \.\.\.liveState\.value, testStatus: 'COMPLETED', receptionStatus: 'ENDED', exceptions: \[\] \}/)
assert.doesNotMatch(view, /STOPPED: '结束'/)
assert.doesNotMatch(view, /FINISHED: '结束'/)
assert.match(view, /function exceptionTitle\(item: Record<string, unknown>\)/)