| | |
| | | import { getPersistedTestCycles, getPersistedTestRecords, getPersistedTestSteps, type PersistedTestCycle, type PersistedTestRecord, type PersistedTestStep } from '@/api/tests' |
| | | import { consoleImportColumns, submitConsoleDataAction, type ConsoleDataType } from '@/api/consoleData' |
| | | import { formatBusinessTime as formatTime } from '@/utils/businessTime' |
| | | import { formatTestDisplayValue, toTestApiValue } from '@/utils/testDataUnits' |
| | | |
| | | type DataTab = 'realtime' | 'step' | 'cycle' |
| | | interface ConsoleCell { |
| | |
| | | activeTab.value === 'realtime' |
| | | ? [ |
| | | { label: '电压(V)', value: 'voltage' }, |
| | | { label: '电流(A)', value: 'current' }, |
| | | { label: '容量(Ah)', value: 'capacity' }, |
| | | { label: '能量(Wh)', value: 'energy' }, |
| | | { label: '电流(mA)', value: 'current' }, |
| | | { label: '容量(mAh)', value: 'capacity' }, |
| | | { label: '能量(mWh)', value: 'energy' }, |
| | | { label: '温度(℃)', value: 'temperature' }, |
| | | { label: '正电位(V)', value: 'positivePotential' }, |
| | | { label: '负电位(V)', value: 'negativePotential' }, |
| | |
| | | : activeTab.value === 'step' |
| | | ? [ |
| | | { label: '工步时间', value: 'stepDuration' }, |
| | | { label: '容量(Ah)', value: 'capacity' }, |
| | | { label: '能量(Wh)', value: 'energy' }, |
| | | { label: '容量(mAh)', value: 'capacity' }, |
| | | { label: '能量(mWh)', value: 'energy' }, |
| | | { label: '起始电压(V)', value: 'startVoltage' }, |
| | | { label: '终止电压(V)', value: 'endVoltage' }, |
| | | { label: '充电峰值电压(V)', value: 'chargePeakVoltage' }, |
| | | { label: '放电谷值电压(V)', value: 'dischargeValleyVoltage' }, |
| | | ] |
| | | : [ |
| | | { label: '充电容量(Ah)', value: 'chargeCapacity' }, |
| | | { label: '放电容量(Ah)', value: 'dischargeCapacity' }, |
| | | { label: '充电容量(mAh)', value: 'chargeCapacity' }, |
| | | { label: '放电容量(mAh)', value: 'dischargeCapacity' }, |
| | | { label: '效率(%)', value: 'efficiency' }, |
| | | { label: '充电能量(Wh)', value: 'chargeEnergy' }, |
| | | { label: '放电能量(Wh)', value: 'dischargeEnergy' }, |
| | | { label: '充电能量(mWh)', value: 'chargeEnergy' }, |
| | | { label: '放电能量(mWh)', value: 'dischargeEnergy' }, |
| | | ], |
| | | ) |
| | | function emptyRow(base: Partial<DataRow>): DataRow { |
| | |
| | | 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({ 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 }) |
| | | 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 ? '-' : formatTestDisplayValue('current', record.current), capacity: collector ? '-' : formatTestDisplayValue('capacity', record.capacity), energy: collector ? '-' : formatTestDisplayValue('energy', record.energy), 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) |
| | | const identity = record.stableIdentity || `step:${[record.adapterId, record.devtype, record.deviceId, record.subdevid, record.channelId, record.cycleNo, record.stepNo].join(':')}` |
| | | return emptyRow({ id: identity, recordNo: record.recordNo, batteryNo: record.batteryNo, cycleNo: Number(record.cycleNo), stepNo: Number(record.stepNo), stepName: record.stepName || '-', actualStepLabel: record.actualStepLabel || `${record.stepNo} · ${record.stepName || '-'}`, sequenceId: Number(record.sequenceId || 0), recordedAt: startedAt, absoluteTime: formatTime(record.absoluteTime || record.recordedAt), serverReceivedAt: formatTime(record.serverReceivedAt), stepDuration: record.stepDuration == null ? '-' : String(record.stepDuration), capacity: record.capacity == null ? '-' : Number(record.capacity).toFixed(4), energy: record.energy == null ? '-' : Number(record.energy).toFixed(4), startVoltage: record.startVoltage == null ? '-' : Number(record.startVoltage).toFixed(4), endVoltage: record.endVoltage == null ? '-' : Number(record.endVoltage).toFixed(4), chargePeakVoltage: record.chargePeakVoltage == null ? '-' : Number(record.chargePeakVoltage).toFixed(4), dischargeValleyVoltage: record.dischargeValleyVoltage == null ? '-' : Number(record.dischargeValleyVoltage).toFixed(4) }) |
| | | return emptyRow({ id: identity, recordNo: record.recordNo, batteryNo: record.batteryNo, cycleNo: Number(record.cycleNo), stepNo: Number(record.stepNo), stepName: record.stepName || '-', actualStepLabel: record.actualStepLabel || `${record.stepNo} · ${record.stepName || '-'}`, sequenceId: Number(record.sequenceId || 0), recordedAt: startedAt, absoluteTime: formatTime(record.absoluteTime || record.recordedAt), serverReceivedAt: formatTime(record.serverReceivedAt), stepDuration: record.stepDuration == null ? '-' : String(record.stepDuration), capacity: formatTestDisplayValue('capacity', record.capacity), energy: formatTestDisplayValue('energy', record.energy), startVoltage: record.startVoltage == null ? '-' : Number(record.startVoltage).toFixed(4), endVoltage: record.endVoltage == null ? '-' : Number(record.endVoltage).toFixed(4), chargePeakVoltage: record.chargePeakVoltage == null ? '-' : Number(record.chargePeakVoltage).toFixed(4), dischargeValleyVoltage: record.dischargeValleyVoltage == null ? '-' : Number(record.dischargeValleyVoltage).toFixed(4) }) |
| | | } |
| | | function mapCycle(record: PersistedTestCycle): DataRow { |
| | | const startedAt = formatTime(record.recordedAt) |
| | | const identity = record.stableIdentity || `cycle:${[record.adapterId, record.devtype, record.deviceId, record.subdevid, record.channelId, record.cycleNo].join(':')}` |
| | | return emptyRow({ id: identity, recordNo: record.recordNo, batteryNo: record.batteryNo, cycleNo: Number(record.cycleNo), sequenceId: Number(record.sequenceId || 0), recordedAt: startedAt, absoluteTime: formatTime(record.absoluteTime || record.recordedAt), serverReceivedAt: formatTime(record.serverReceivedAt), chargeCapacity: record.chargeCapacity == null ? '-' : Number(record.chargeCapacity).toFixed(4), dischargeCapacity: record.dischargeCapacity == null ? '-' : Number(record.dischargeCapacity).toFixed(4), efficiency: record.efficiency == null ? '-' : Number(record.efficiency).toFixed(2), chargeEnergy: record.chargeEnergy == null ? '-' : Number(record.chargeEnergy).toFixed(4), dischargeEnergy: record.dischargeEnergy == null ? '-' : Number(record.dischargeEnergy).toFixed(4) }) |
| | | return emptyRow({ id: identity, recordNo: record.recordNo, batteryNo: record.batteryNo, cycleNo: Number(record.cycleNo), sequenceId: Number(record.sequenceId || 0), recordedAt: startedAt, absoluteTime: formatTime(record.absoluteTime || record.recordedAt), serverReceivedAt: formatTime(record.serverReceivedAt), chargeCapacity: formatTestDisplayValue('chargeCapacity', record.chargeCapacity), dischargeCapacity: formatTestDisplayValue('dischargeCapacity', record.dischargeCapacity), efficiency: record.efficiency == null ? '-' : Number(record.efficiency).toFixed(2), chargeEnergy: formatTestDisplayValue('chargeEnergy', record.chargeEnergy), dischargeEnergy: formatTestDisplayValue('dischargeEnergy', record.dischargeEnergy) }) |
| | | } |
| | | async function loadRecords() { |
| | | loading.value = true |
| | |
| | | const values = Object.fromEntries(fields |
| | | .filter(item => row[item.value as keyof DataRow] !== before[item.value as keyof DataRow]) |
| | | .map(item => [item.value, row[item.value as keyof DataRow]])) |
| | | const after = { ...before, ...values } |
| | | return { identity: id, before, after, override: { stableIdentity: id, fieldValues: JSON.stringify(values), deleted: false } } |
| | | // 编辑框使用毫单位;提交字段和摘要都回到 API 单位,正式修订前值仍由后端读取。 |
| | | const apiValues = Object.fromEntries(Object.entries(values).map(([field, value]) => [field, toTestApiValue(field, String(value))])) |
| | | const apiBefore = { ...before, ...Object.fromEntries(fields.map(item => [item.value, toTestApiValue(item.value, String(before[item.value as keyof DataRow]))])) } |
| | | return { identity: id, before: apiBefore, after: { ...apiBefore, ...apiValues }, override: { stableIdentity: id, fieldValues: JSON.stringify(apiValues), deleted: false } } |
| | | }) |
| | | }) |
| | | await submitConsoleDataAction(props.testId, { dataType: dataType.value, operationType: 'EDIT', scopeSummary: `按格修改${changed.length}条${activeTab.value}数据`, identitySummary: JSON.stringify(editItems.map(item => item.identity)), changesSummary: JSON.stringify(editItems.map(item => ({ identity: item.identity, before: item.before, after: item.after }))), reason: editForm.remark.trim(), overrides: editItems.map(item => item.override) }) |
| | |
| | | ElMessage.warning('没有可下载的数据') |
| | | return |
| | | } |
| | | const headers = activeTab.value === 'cycle' ? ['记录编号', '电池', '循环号', '充电容量(Ah)', '放电容量(Ah)', '充放电效率(%)', '充电能量(Wh)', '放电能量(Wh)'] : activeTab.value === 'step' ? ['记录编号', '电池', '循环号', '工步序号', '工步', '工步时间', '容量(Ah)', '能量(Wh)', '起始电压(V)', '终止电压(V)', '充电峰值(V)', '放电谷值(V)'] : ['记录编号', '电池', '循环号', '实际工步号', '实际工步名称', '记录序号', '设备时间', '服务器接收时间', '电压(V)', '电流(A)', '容量(Ah)', '能量(Wh)', '温度(℃)', '正电位(V)', '负电位(V)'] |
| | | const headers = activeTab.value === 'cycle' ? ['记录编号', '电池', '循环号', '充电容量(mAh)', '放电容量(mAh)', '充放电效率(%)', '充电能量(mWh)', '放电能量(mWh)'] : activeTab.value === 'step' ? ['记录编号', '电池', '循环号', '工步序号', '工步', '工步时间', '容量(mAh)', '能量(mWh)', '起始电压(V)', '终止电压(V)', '充电峰值(V)', '放电谷值(V)'] : ['记录编号', '电池', '循环号', '实际工步号', '实际工步名称', '记录序号', '设备时间', '服务器接收时间', '电压(V)', '电流(mA)', '容量(mAh)', '能量(mWh)', '温度(℃)', '正电位(V)', '负电位(V)'] |
| | | const records = activeTab.value === 'cycle' ? filteredRows.value.map(row => [row.recordNo, row.batteryNo, row.cycleNo, row.chargeCapacity, row.dischargeCapacity, row.efficiency, row.chargeEnergy, row.dischargeEnergy]) : activeTab.value === 'step' ? filteredRows.value.map(row => [row.recordNo, row.batteryNo, row.cycleNo, row.stepNo, row.stepName, row.stepDuration, row.capacity, row.energy, row.startVoltage, row.endVoltage, row.chargePeakVoltage, row.dischargeValleyVoltage]) : filteredRows.value.map(row => [row.recordNo, row.batteryNo, row.cycleNo, row.stepNo, row.stepName, row.sequenceId, row.recordedAt, row.serverReceivedAt, row.voltage, row.current, row.capacity, row.energy, row.temperature, row.positivePotential, row.negativePotential]) |
| | | const blob = new Blob([`\uFEFF${[headers, ...records].map(item => item.map(escapeCsv).join(',')).join('\n')}`], { type: 'text/csv;charset=utf-8' }) |
| | | const url = URL.createObjectURL(blob) |
| | |
| | | <th>正电位(V)</th> |
| | | <th>负电位(V)</th> |
| | | <th>电压(V)</th> |
| | | <th>电流(A)</th> |
| | | <th>容量(Ah)</th> |
| | | <th>能量(Wh)</th> |
| | | <th>电流(mA)</th> |
| | | <th>容量(mAh)</th> |
| | | <th>能量(mWh)</th> |
| | | <th class="action-column">操作</th> |
| | | </tr> |
| | | <tr v-else-if="activeTab === 'step'"> |
| | |
| | | <th>设备时间</th> |
| | | <th>服务器接收时间</th> |
| | | <th>工步时间</th> |
| | | <th>容量(Ah)</th> |
| | | <th>能量(Wh)</th> |
| | | <th>容量(mAh)</th> |
| | | <th>能量(mWh)</th> |
| | | <th>起始电压(V)</th> |
| | | <th>终止电压(V)</th> |
| | | <th>充电峰值(V)</th> |
| | |
| | | <th>循环号</th> |
| | | <th>设备时间</th> |
| | | <th>服务器接收时间</th> |
| | | <th>充电容量(Ah)</th> |
| | | <th>放电容量(Ah)</th> |
| | | <th>充电容量(mAh)</th> |
| | | <th>放电容量(mAh)</th> |
| | | <th>充放电效率(%)</th> |
| | | <th>充电能量(Wh)</th> |
| | | <th>放电能量(Wh)</th> |
| | | <th>充电能量(mWh)</th> |
| | | <th>放电能量(mWh)</th> |
| | | <th class="action-column">操作</th> |
| | | </tr> |
| | | </thead> |
| | |
| | | } |
| | | .data-toolbar > div { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | max-width: 100%; |
| | | gap: 8px; |
| | | } |
| | | .data-toolbar button { |
| | | white-space: nowrap; |
| | | } |
| | | .load-error { |
| | | color: #b34f4f; |
| | | } |