From 7835baa4f22d5ff39b95f5cb05aefc9d3ceb67ae Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期六, 05 九月 2026 11:45:35 +0800
Subject: [PATCH] 对齐测试控制台数据与实时状态
---
src/components/ConsoleDataListPanel.vue | 49 ++++++++++++++++++-------------------------------
1 files changed, 18 insertions(+), 31 deletions(-)
diff --git a/src/components/ConsoleDataListPanel.vue b/src/components/ConsoleDataListPanel.vue
index fb23b3e..2e1d887 100644
--- a/src/components/ConsoleDataListPanel.vue
+++ b/src/components/ConsoleDataListPanel.vue
@@ -5,7 +5,8 @@
import * as XLSX from 'xlsx'
import { Download, EditPen, Refresh, Search, UploadFilled, Clock, Check } from '@element-plus/icons-vue'
import { getPersistedTestCycles, getPersistedTestRecords, getPersistedTestSteps, type PersistedTestCycle, type PersistedTestRecord, type PersistedTestStep } from '@/api/tests'
-import { submitConsoleDataAction, type ConsoleDataType } from '@/api/consoleData'
+import { consoleImportColumns, submitConsoleDataAction, type ConsoleDataType } from '@/api/consoleData'
+import { formatBusinessTime as formatTime } from '@/utils/businessTime'
type DataTab = 'realtime' | 'step' | 'cycle'
interface ConsoleCell {
@@ -48,6 +49,8 @@
positivePotential: string
negativePotential: string
extensionMeasurements: string
+ sourceIdentity: string
+ collectorDeviceTime: string
chargeCapacity: string
dischargeCapacity: string
efficiency: string
@@ -109,19 +112,11 @@
{ label: '放电能量(Wh)', value: 'dischargeEnergy' },
],
)
-function formatTime(value?: string | null) {
- return value
- ? value
- .replace('T', ' ')
- .replace('Z', '')
- .replace(/\.\d{3,6}$/, '')
- : '-'
-}
function emptyRow(base: Partial<DataRow>): DataRow {
- return { id: '', sourceType: 'CABINET', metricCode: '-', metricValue: '-', unit: '-', collectorCode: '-', channelCode: '-', recordNo: '', batteryNo: '', cycleNo: 0, stepNo: 0, stepName: '', actualStepLabel: '-', sequenceId: 0, recordedAt: '', absoluteTime: '', serverReceivedAt: '', stepDuration: '-', startVoltage: '-', endVoltage: '-', chargePeakVoltage: '-', dischargeValleyVoltage: '-', voltage: '-', current: '-', capacity: '-', energy: '-', temperature: '-', positivePotential: '-', negativePotential: '-', extensionMeasurements: '-', chargeCapacity: '-', dischargeCapacity: '-', efficiency: '-', chargeEnergy: '-', dischargeEnergy: '-', ...base }
+ return { sourceIdentity: '-', collectorDeviceTime: '-', id: '', sourceType: 'CABINET', metricCode: '-', metricValue: '-', unit: '-', collectorCode: '-', channelCode: '-', recordNo: '', batteryNo: '', cycleNo: 0, stepNo: 0, stepName: '', actualStepLabel: '-', sequenceId: 0, recordedAt: '', absoluteTime: '', serverReceivedAt: '', stepDuration: '-', startVoltage: '-', endVoltage: '-', chargePeakVoltage: '-', dischargeValleyVoltage: '-', voltage: '-', current: '-', capacity: '-', energy: '-', temperature: '-', positivePotential: '-', negativePotential: '-', extensionMeasurements: '-', chargeCapacity: '-', dischargeCapacity: '-', efficiency: '-', chargeEnergy: '-', dischargeEnergy: '-', ...base }
}
function mapRecord(record: PersistedTestRecord): DataRow {
- const deviceTime = formatTime(record.deviceTime || record.recordedAt)
+ const deviceTime = formatTime(record.deviceTime ?? record.recordedAt)
const identity = record.stableIdentity || [record.adapterId, record.devtype, record.deviceId, record.subdevid, record.channelId, record.sequenceId].join(':')
const collector = record.sourceType === 'COLLECTOR'
const snapshot = record.sourceType === 'SNAPSHOT'
@@ -132,10 +127,10 @@
if (snapshot && record.extensionMeasurementsJson) {
try {
const parsed = typeof record.extensionMeasurementsJson === 'string' ? JSON.parse(record.extensionMeasurementsJson) : record.extensionMeasurementsJson
- extensionMeasurements = Object.entries(parsed as Record<string, any>).map(([code, value]) => `${code}=${value?.value ?? value?.metricValue ?? '-'}${value?.unit ? ` ${value.unit}` : ''}`).join('; ') || '-'
+ extensionMeasurements = Object.entries(parsed as Record<string, { value?: unknown; metricValue?: unknown; unit?: string; channelCode?: string }>).map(([code, value]) => `${code}${value?.channelCode ? ` (${value.channelCode})` : ''}=${value?.value ?? value?.metricValue ?? '-'}${value?.unit ? ` ${value.unit}` : ''}`).join('; ') || '-'
} catch { extensionMeasurements = String(record.extensionMeasurementsJson) }
}
- return emptyRow({ id: identity, sourceType: record.sourceType || 'CABINET', metricCode: record.metricCode || '-', metricValue: extensionValue, unit: record.unit || '-', collectorCode: record.collectorCode || '-', channelCode: record.channelCode || '-', recordNo: record.recordNo, batteryNo: record.batteryNo || '-', cycleNo: Number(record.cycleNo || 0), stepNo: Number(record.stepNo || 0), stepName: collector ? '-' : (record.stepName || '-'), sequenceId: Number(record.sequenceId || record.cabinetSequenceId || 0), recordedAt: deviceTime, absoluteTime: deviceTime, serverReceivedAt: formatTime(record.serverReceivedAt), voltage: collector ? '-' : (record.voltage == null ? '-' : Number(record.voltage).toFixed(4)), current: collector ? '-' : (record.current == null ? '-' : Number(record.current).toFixed(4)), capacity: collector ? '-' : (record.capacity == null ? '-' : Number(record.capacity).toFixed(4)), energy: collector ? '-' : (record.energy == null ? '-' : Number(record.energy).toFixed(4)), temperature: snapshot ? (record.temperature == null ? '-' : Number(record.temperature).toFixed(4)) : extension('TEMPERATURE'), positivePotential: snapshot ? (record.positivePotential == null ? '-' : Number(record.positivePotential).toFixed(4)) : extension('POSITIVE_POTENTIAL'), negativePotential: snapshot ? (record.negativePotential == null ? '-' : Number(record.negativePotential).toFixed(4)) : extension('NEGATIVE_POTENTIAL'), extensionMeasurements })
+ return emptyRow({ sourceIdentity: record.sourceIdentity || '-', collectorDeviceTime: formatTime(record.collectorDeviceTime), id: identity, sourceType: record.sourceType || 'CABINET', metricCode: record.metricCode || '-', metricValue: extensionValue, unit: record.unit || '-', collectorCode: record.collectorCode || String(record.collectorId ?? '-'), channelCode: record.channelCode || '-', recordNo: record.recordNo, batteryNo: record.batteryNo || '-', cycleNo: Number(record.cycleNo || 0), stepNo: Number(record.stepNo || 0), stepName: collector ? '-' : (record.stepName || '-'), sequenceId: Number(record.sequenceId || record.cabinetSequenceId || 0), recordedAt: deviceTime, absoluteTime: deviceTime, serverReceivedAt: formatTime(record.serverReceivedAt), voltage: collector ? '-' : (record.voltage == null ? '-' : Number(record.voltage).toFixed(4)), current: collector ? '-' : (record.current == null ? '-' : Number(record.current).toFixed(4)), capacity: collector ? '-' : (record.capacity == null ? '-' : Number(record.capacity).toFixed(4)), energy: collector ? '-' : (record.energy == null ? '-' : Number(record.energy).toFixed(4)), temperature: snapshot ? (record.temperature == null ? '-' : Number(record.temperature).toFixed(4)) : extension('TEMPERATURE'), positivePotential: snapshot ? (record.positivePotential == null ? '-' : Number(record.positivePotential).toFixed(4)) : extension('POSITIVE_POTENTIAL'), negativePotential: snapshot ? (record.negativePotential == null ? '-' : Number(record.negativePotential).toFixed(4)) : extension('NEGATIVE_POTENTIAL'), extensionMeasurements })
}
function mapStep(record: PersistedTestStep): DataRow {
const startedAt = formatTime(record.recordedAt)
@@ -151,22 +146,19 @@
loading.value = true
loadError.value = ''
try {
- if (props.receptionStatus === 'NOT_STARTED') {
- rows.value = []
- stepRows.value = []
- cycleRows.value = []
- return
- }
const [records, steps, cycles] = await Promise.all([getPersistedTestRecords(props.testId), getPersistedTestSteps(props.testId), getPersistedTestCycles(props.testId)])
// 后端查询接口已经返回当前有效数据,前端不再读取覆盖表或拼接伪造行。
rows.value = records.map(mapRecord).sort((a, b) => b.absoluteTime.localeCompare(a.absoluteTime) || b.sequenceId - a.sequenceId)
stepRows.value = steps.map(mapStep)
cycleRows.value = cycles.map(mapCycle)
+ currentPage.value = Math.min(currentPage.value, Math.max(1, Math.ceil(filteredRows.value.length / pageSize.value)))
+ return true
} catch (error) {
rows.value = []
stepRows.value = []
cycleRows.value = []
loadError.value = error instanceof Error ? error.message : '测试数据加载失败'
+ return false
} finally {
loading.value = false
}
@@ -221,14 +213,8 @@
function goToRevisions() {
router.push({ path: '/data-revision', query: { testId: String(props.testId), dataType: dataType.value } })
}
-function templateHeaders() {
- return activeTab.value === 'cycle' ? ['记录编号', '电池号', '循环序号', '充放电容量(Ah)', '效率(%)', '能量(Wh)'] : activeTab.value === 'step' ? ['记录编号', '电池号', '循环序号', '工步序号', '工步', '工步时间', '容量(Ah)', '能量(Wh)', '起止电压(V)', '充电峰值电压(V)', '放电谷值电压(V)'] : ['记录编号', '电池号', '设备时间', '循环序号', '工步序号', '工步名称', '记录序号', '服务器接收时间', '电压(V)', '电流(A)', '容量(Ah)', '能量(Wh)']
-}
-function realtimeTemplateHeaders() {
- return [...templateHeaders(), '温度(℃)', '正电位(V)', '负电位(V)']
-}
function downloadTemplate() {
- const worksheet = XLSX.utils.aoa_to_sheet([activeTab.value === 'realtime' ? realtimeTemplateHeaders() : templateHeaders()])
+ const worksheet = XLSX.utils.aoa_to_sheet([Object.values(consoleImportColumns[dataType.value])])
const workbook = XLSX.utils.book_new()
XLSX.utils.book_append_sheet(workbook, worksheet, `${activeTab.value}模板`)
XLSX.writeFile(workbook, `测试${activeTab.value}数据导入模板.xlsx`)
@@ -329,9 +315,7 @@
}
try {
await submitConsoleDataAction(props.testId, { dataType: dataType.value, operationType: 'DELETE', scopeSummary: `物理删除${ids.length}条${activeTab.value}数据`, identitySummary: JSON.stringify(physicalIds), changesSummary: null, reason: result.value.trim() })
- if (activeTab.value === 'realtime') rows.value = rows.value.filter(row => !ids.includes(row.id))
- else if (activeTab.value === 'step') stepRows.value = stepRows.value.filter(row => !physicalIds.includes(row.id))
- else cycleRows.value = cycleRows.value.filter(row => !ids.includes(row.id))
+ await loadRecords()
selectedIds.value = new Set()
ElMessage.success(`已从当前有效视图移除 ${ids.length} 条数据`)
} catch (error) {
@@ -342,6 +326,8 @@
if (activeTab.value === tab) return
if (editing.value) cancelBatchEdit()
activeTab.value = tab
+ selectedIds.value = new Set()
+ void router.replace({ query: { ...route.query, tab: 'data-list', dataType: dataType.value } })
currentPage.value = 1
// 切换数据子页时主动同步一次,除此之外不进行后台轮询。
void loadRecords()
@@ -349,7 +335,8 @@
function escapeCsv(value: unknown) {
return `"${String(value ?? '').replaceAll('"', '""')}"`
}
-function downloadCsv() {
+async function downloadCsv() {
+ if (!await loadRecords()) return
if (!filteredRows.value.length) {
ElMessage.warning('没有可下载的数据')
return
@@ -500,7 +487,7 @@
<td>{{ row.sequenceId }}</td>
<td>{{ row.recordedAt }}</td>
<td>{{ row.serverReceivedAt }}</td>
- <td>{{ row.sourceType === 'COLLECTOR' ? `采集器 ${row.collectorCode}` : row.sourceType === 'SNAPSHOT' ? '检测柜 + 采集器' : '检测柜' }}<small v-if="row.sourceType === 'COLLECTOR'">{{ row.channelCode }}</small></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><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>
--
Gitblit v1.7.1