| src/components/ConsoleDataListPanel.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/TestConsole.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| tests/test-ticket11-browser.mjs | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/components/ConsoleDataListPanel.vue
@@ -361,7 +361,7 @@ ) </script> <template> <section class="console-data-panel"> <section class="console-data-panel" :class="activeTab"> <header class="section-heading"> <div><i></i><strong>数据列表</strong><span>TEST DATA</span></div> <nav class="data-tabs" aria-label="数据列表子页"> @@ -429,7 +429,7 @@ <th>记录序号</th> <th>设备时间</th> <th>服务器接收时间</th> <th>来源</th> <th class="source-column">来源</th> <th>温度(℃)</th> <th>正电位(V)</th> <th>负电位(V)</th> @@ -487,7 +487,7 @@ <td>{{ row.sequenceId }}</td> <td>{{ row.recordedAt }}</td> <td>{{ row.serverReceivedAt }}</td> <td :title="row.sourceIdentity">{{ row.sourceType === 'COLLECTOR' ? `采集器 ${row.collectorCode}` : row.sourceType === 'SNAPSHOT' ? '检测柜 + 采集器' : '检测柜' }}<small>{{ row.sourceIdentity }}</small><small v-if="row.sourceType === 'SNAPSHOT'">采集器 {{ row.collectorCode }} · {{ row.collectorDeviceTime }}</small><small v-if="row.sourceType === 'COLLECTOR'">{{ row.channelCode }}</small><small v-if="row.extensionMeasurements !== '-'">{{ row.extensionMeasurements }}</small></td> <td class="source-column" :title="row.sourceIdentity">{{ row.sourceType === 'COLLECTOR' ? `采集器 ${row.collectorCode}` : row.sourceType === 'SNAPSHOT' ? '检测柜 + 采集器' : '检测柜' }}<small>{{ row.sourceIdentity }}</small><small v-if="row.sourceType === 'SNAPSHOT'">采集器 {{ row.collectorCode }} · {{ row.collectorDeviceTime }}</small><small v-if="row.sourceType === 'COLLECTOR'">{{ row.channelCode }}</small><small v-if="row.extensionMeasurements !== '-'">{{ row.extensionMeasurements }}</small></td> <td><el-input v-if="editing && draftRows[row.id]" v-model="draftRows[row.id].temperature" size="small" /><span v-else>{{ row.temperature }}</span></td> <td><el-input v-if="editing && draftRows[row.id]" v-model="draftRows[row.id].positivePotential" size="small" /><span v-else>{{ row.positivePotential }}</span></td> <td><el-input v-if="editing && draftRows[row.id]" v-model="draftRows[row.id].negativePotential" size="small" /><span v-else>{{ row.negativePotential }}</span></td> @@ -848,6 +848,22 @@ font-family: 'Cascadia Mono', Consolas, monospace; font-size: 11px; } .source-column { width: 280px; white-space: normal; overflow-wrap: anywhere; } .console-data-panel.realtime table { min-width: 2100px; } .console-data-panel.realtime th:nth-child(8), .console-data-panel.realtime th:nth-child(9) { width: 185px; } .source-column small { display: block; line-height: 1.6; } .empty-cell { height: 150px; color: #8795a3; @@ -869,6 +885,20 @@ --el-pagination-hover-color: var(--blue); } @media (max-width: 900px) { .section-heading { height: auto; min-height: 90px; flex-wrap: wrap; gap: 8px; padding-top: 10px; padding-bottom: 10px; } .data-tabs { position: static; order: 3; flex-basis: 100%; transform: none; } .filter-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } src/views/TestConsole.vue
@@ -107,6 +107,7 @@ if (disposed || reconnectTimer !== null) return reconnectTimer = window.setTimeout(async () => { reconnectTimer = null if (!backendDetail.value) { await loadConsole(); return } try { applyLiveState(await getTestLiveState(testId.value)); if (!disposed) connectLiveStream() } catch (error) { handleLiveError(error instanceof Error ? error : new Error('实时状态加载失败')) } }, 2000) @@ -142,7 +143,7 @@ scheduleLiveReconnect() } onMounted(async () => { async function loadConsole() { try { const detail = await getTestDetail(testId.value) if (disposed) return @@ -174,9 +175,10 @@ if (activeTab.value === 'step-chart') await loadRealtimeRecords() if (activeTab.value === 'project-chart') await loadProjectChartData() } catch (error) { ElMessage.error(error instanceof Error ? error.message : '测试状态加载失败') handleLiveError(error instanceof Error ? error : new Error('测试状态加载失败')) } }) } onMounted(loadConsole) onBeforeUnmount(() => { disposed = true; closeLiveStream?.(); if (reconnectTimer !== null) window.clearTimeout(reconnectTimer) }) async function loadRealtimeRecords() { tests/test-ticket11-browser.mjs
@@ -19,12 +19,16 @@ const live = { ...detail, updatedAt: time, sources: [], exceptions: [], testPlan: { currentCycleNo: 2, currentStepNo: 3, plannedCycleCount: 5, rows: [{ rowType: 'STEP', stepNo: 3, cyclePosition: 1, stepName: '设备计划放电', durationSeconds: 60, voltageV: 3.2, executionStatus: '进行中' }] } } let liveReads = 0 let unavailable = false let detailUnavailable = false let forbidden = false const requests = [] await context.addInitScript(() => localStorage.setItem('battery_detection_auth_token', 'ticket11-token')) await page.route('http://ticket11.test/**', async route => { const url = new URL(route.request().url()) if (url.pathname.startsWith('/dev-api/')) { requests.push(url.pathname) if (detailUnavailable && url.pathname.endsWith('/system/test/42')) return route.fulfill({ status: 503, json: { code: 503, msg: '测试配置暂时不可用' } }) if (forbidden && /\/live(?:-state)?$/.test(url.pathname)) return route.fulfill({ status: 403, json: { code: 403, msg: '实时状态权限不足' } }) if (unavailable && /\/live(?:-state)?$/.test(url.pathname)) return route.fulfill({ status: 503, json: { code: 503, msg: '后端暂时不可用' } }) let result = detail if (url.pathname.endsWith('/live-state')) { liveReads++; result = live } @@ -57,6 +61,8 @@ assert.match(await page.locator('body').innerText(), /HUMIDITY.*41.*%/) assert.match(await page.locator('body').innerText(), /S-04/) assert.match(await page.locator('body').innerText(), /adapter-a:1:2:3:4/) await mkdir(new URL('../test-results/ticket11/', import.meta.url), { recursive: true }) await page.screenshot({ path: new URL('../test-results/ticket11/source-desktop.png', import.meta.url).pathname.replace(/^\/(\w:)/, '$1'), fullPage: true }) await panel.getByRole('button', { name: '编辑', exact: true }).click() const dialog = page.getByRole('dialog') await dialog.locator('label').filter({ hasText: '电压(V)' }).locator('input').fill('4.1256') @@ -105,6 +111,7 @@ await original.waitFor({ state: 'detached' }) await page.reload() await page.getByText('IMPORT-REALTIME', { exact: true }).waitFor() await expect(panel.getByRole('button', { name: '实时', exact: true })).toHaveAttribute('aria-current', 'page') assert.equal(await page.getByText('RT-1', { exact: true }).count(), 0) await page.getByRole('button', { name: '工步', exact: true }).click() await page.getByText('STEP-1', { exact: true }).waitFor() @@ -135,6 +142,14 @@ await page.reload() await expect(page.locator('.reception-state')).toHaveText('暂停接收') await expect(page.locator('.metric-list .voltage')).toHaveText('-') forbidden = true await expect(page.locator('.live-error')).toContainText('权限不足', { timeout: 10000 }) forbidden = false detailUnavailable = true await page.reload() await expect(page.locator('.live-error')).toContainText('测试配置暂时不可用') detailUnavailable = false await expect(page.locator('.reception-state')).toHaveText('暂停接收', { timeout: 10000 }) assert.ok(requests.every(url => !url.includes('simulator') && !url.includes('overrides'))) assert.deepEqual(errors, []) await mkdir(new URL('../test-results/ticket11/', import.meta.url), { recursive: true })