From f0cf0d494bf195b593aa6d3c60fb6a988ed2eb52 Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期三, 09 九月 2026 11:06:11 +0800
Subject: [PATCH] 将工步列表两项电压统一为文件中值
---
tests/test-console-data-import.test.mjs | 14 ++++++-
src/api/tests.ts | 4 +-
src/utils/consoleDataImport.ts | 5 +-
src/views/DataRevisionDetail.vue | 2
tests/test-ticket23-console-step-regression.test.mjs | 2
src/components/ConsoleDataListPanel.vue | 28 +++++++-------
tests/test-console-data-import-browser.mjs | 21 ++++++++++
7 files changed, 53 insertions(+), 23 deletions(-)
diff --git a/src/api/tests.ts b/src/api/tests.ts
index 7c36894..fcb8daa 100644
--- a/src/api/tests.ts
+++ b/src/api/tests.ts
@@ -382,8 +382,8 @@
stepDuration: number | string | null
startVoltage: number | string | null
endVoltage: number | string | null
- chargePeakVoltage: number | string | null
- dischargeValleyVoltage: number | string | null
+ chargeMedianVoltage: number | string | null
+ dischargeMedianVoltage: number | string | null
}
export interface PersistedTestCycle extends PersistedTestRecord {
diff --git a/src/components/ConsoleDataListPanel.vue b/src/components/ConsoleDataListPanel.vue
index d32600e..461e9d8 100644
--- a/src/components/ConsoleDataListPanel.vue
+++ b/src/components/ConsoleDataListPanel.vue
@@ -40,8 +40,8 @@
stepDuration: string
startVoltage: string
endVoltage: string
- chargePeakVoltage: string
- dischargeValleyVoltage: string
+ chargeMedianVoltage: string
+ dischargeMedianVoltage: string
voltage: string
current: string
capacity: string
@@ -102,8 +102,8 @@
{ label: '能量(mWh)', value: 'energy' },
{ label: '起始电压(V)', value: 'startVoltage' },
{ label: '终止电压(V)', value: 'endVoltage' },
- { label: '充电峰值电压(V)', value: 'chargePeakVoltage' },
- { label: '放电谷值电压(V)', value: 'dischargeValleyVoltage' },
+ { label: '充电中值电压(V)', value: 'chargeMedianVoltage' },
+ { label: '放电中值电压(V)', value: 'dischargeMedianVoltage' },
]
: [
{ label: '充电容量(mAh)', value: 'chargeCapacity' },
@@ -114,7 +114,7 @@
],
)
function emptyRow(base: Partial<DataRow>): DataRow {
- 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 }
+ 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: '-', chargeMedianVoltage: '-', dischargeMedianVoltage: '-', 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)
@@ -136,7 +136,7 @@
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: 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) })
+ 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), chargeMedianVoltage: record.chargeMedianVoltage == null ? '-' : Number(record.chargeMedianVoltage).toFixed(4), dischargeMedianVoltage: record.dischargeMedianVoltage == null ? '-' : Number(record.dischargeMedianVoltage).toFixed(4) })
}
function mapCycle(record: PersistedTestCycle): DataRow {
const startedAt = formatTime(record.recordedAt)
@@ -344,8 +344,8 @@
ElMessage.warning('没有可下载的数据')
return
}
- 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 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.chargeMedianVoltage, row.dischargeMedianVoltage]) : 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)
const anchor = document.createElement('a')
@@ -457,8 +457,8 @@
<th>能量(mWh)</th>
<th>起始电压(V)</th>
<th>终止电压(V)</th>
- <th>充电峰值(V)</th>
- <th>放电谷值(V)</th>
+ <th>充电中值电压(V)</th>
+ <th>放电中值电压(V)</th>
<th class="action-column">操作</th>
</tr>
<tr v-else>
@@ -549,12 +549,12 @@
<span v-else>{{ row.endVoltage }}</span>
</td>
<td>
- <el-input v-if="editing && draftRows[row.id]" v-model="draftRows[row.id].chargePeakVoltage" size="small" />
- <span v-else>{{ row.chargePeakVoltage }}</span>
+ <el-input v-if="editing && draftRows[row.id]" v-model="draftRows[row.id].chargeMedianVoltage" size="small" />
+ <span v-else>{{ row.chargeMedianVoltage }}</span>
</td>
<td>
- <el-input v-if="editing && draftRows[row.id]" v-model="draftRows[row.id].dischargeValleyVoltage" size="small" />
- <span v-else>{{ row.dischargeValleyVoltage }}</span>
+ <el-input v-if="editing && draftRows[row.id]" v-model="draftRows[row.id].dischargeMedianVoltage" size="small" />
+ <span v-else>{{ row.dischargeMedianVoltage }}</span>
</td>
<td class="action-column">
<div class="row-actions"><button type="button" @click="openRowEditor(row)">编辑</button><button class="delete-action" type="button" @click="deleteSelected([row.id])">删除</button></div>
diff --git a/src/utils/consoleDataImport.ts b/src/utils/consoleDataImport.ts
index e99ed38..dc77e33 100644
--- a/src/utils/consoleDataImport.ts
+++ b/src/utils/consoleDataImport.ts
@@ -3,7 +3,7 @@
export const consoleImportColumns = {
REALTIME: { recordNo: '记录编号', batteryNo: '电池号', recordedAt: '设备时间', cycleNo: '循环序号', stepNo: '工步序号', stepName: '工步名称', sequenceId: '记录序号', voltage: '电压(V)', current: '电流(mA)', capacity: '容量(mAh)', energy: '能量(mWh)', temperature: '温度(℃)', positivePotential: '正电位(V)', negativePotential: '负电位(V)' },
- STEP: { recordNo: '记录编号', batteryNo: '电池号', recordedAt: '设备时间', cycleNo: '循环序号', stepNo: '工步序号', stepName: '工步', stepDuration: '工步时间', capacity: '容量(mAh)', energy: '能量(mWh)', startVoltage: '起始电压(V)', endVoltage: '终止电压(V)', chargePeakVoltage: '充电峰值电压(V)', dischargeValleyVoltage: '放电谷值电压(V)' },
+ STEP: { recordNo: '记录编号', batteryNo: '电池号', recordedAt: '设备时间', cycleNo: '循环序号', stepNo: '工步序号', stepName: '工步', stepDuration: '工步时间', capacity: '容量(mAh)', energy: '能量(mWh)', startVoltage: '起始电压(V)', endVoltage: '终止电压(V)', chargeMedianVoltage: '充电中值电压(V)', dischargeMedianVoltage: '放电中值电压(V)' },
CYCLE: { recordNo: '记录编号', batteryNo: '电池号', recordedAt: '设备时间', cycleNo: '循环序号', chargeCapacity: '充电容量(mAh)', dischargeCapacity: '放电容量(mAh)', efficiency: '充放电效率(%)', chargeEnergy: '充电能量(mWh)', dischargeEnergy: '放电能量(mWh)' },
} satisfies Record<ConsoleDataType, Record<string, string>>
@@ -100,7 +100,8 @@
}
const columns: Record<string, string> = consoleImportColumns[type]
const missing = Object.entries(columns).filter(([field, header]) => {
- if (party && ['recordNo', 'batteryNo', 'temperature', 'positivePotential', 'negativePotential', 'chargePeakVoltage', 'dischargeValleyVoltage'].includes(field)) return false
+ // 中值只读取对应列;缺列可为空,不借用旧峰值或谷值。
+ if (party && ['recordNo', 'batteryNo', 'temperature', 'positivePotential', 'negativePotential', 'chargeMedianVoltage', 'dischargeMedianVoltage'].includes(field)) return false
if (party && type !== 'REALTIME' && field === 'recordedAt') return false
return !headers.includes(headerFor(field, header))
}).map(([field, header]) => headerFor(field, header))
diff --git a/src/views/DataRevisionDetail.vue b/src/views/DataRevisionDetail.vue
index e3d1b3b..941ab95 100644
--- a/src/views/DataRevisionDetail.vue
+++ b/src/views/DataRevisionDetail.vue
@@ -6,7 +6,7 @@
const route = useRoute(); const router = useRouter(); const record = ref<DataRevisionAction | null>(null); const loading = ref(true); const loadError = ref('')
const dataTypeLabels = revisionDataTypeLabels; const operationLabels = revisionOperationLabels
// 后端修订摘要保留 A/Ah/Wh,仅展示时换算,不改写已保存的修订事实。
-const fieldLabels: Record<string, string> = { voltage: '电压(V)', current: '电流(mA)', capacity: '容量(mAh)', energy: '能量(mWh)', stepDuration: '工步时间(s)', startVoltage: '起始电压(V)', endVoltage: '终止电压(V)', chargePeakVoltage: '充电峰值电压(V)', dischargeValleyVoltage: '放电谷值电压(V)', chargeCapacity: '充电容量(mAh)', dischargeCapacity: '放电容量(mAh)', efficiency: '效率(%)', chargeEnergy: '充电能量(mWh)', dischargeEnergy: '放电能量(mWh)' }
+const fieldLabels: Record<string, string> = { voltage: '电压(V)', current: '电流(mA)', capacity: '容量(mAh)', energy: '能量(mWh)', stepDuration: '工步时间(s)', startVoltage: '起始电压(V)', endVoltage: '终止电压(V)', chargeMedianVoltage: '充电中值电压(V)', dischargeMedianVoltage: '放电中值电压(V)', chargePeakVoltage: '充电峰值电压(V)', dischargeValleyVoltage: '放电谷值电压(V)', chargeCapacity: '充电容量(mAh)', dischargeCapacity: '放电容量(mAh)', efficiency: '效率(%)', chargeEnergy: '充电能量(mWh)', dischargeEnergy: '放电能量(mWh)' }
function parseArray(value: string | null) { if (!value) return [] as Array<Record<string, unknown> | string>; try { const parsed = JSON.parse(value); return Array.isArray(parsed) ? parsed : [] } catch { return [] as Array<Record<string, unknown> | string> } }
const identities = computed(() => parseArray(record.value?.identitySummary || null).map(item => String(item))); const changeRows = computed(() => parseArray(record.value?.changesSummary || null).map((item, index) => { if (!item || typeof item !== 'object') return { identity: identities.value[index] || '-', changes: [] as Array<{ field: string; before: unknown; after: unknown }> }; const row = item as Record<string, unknown>; const before = row.before && typeof row.before === 'object' ? row.before as Record<string, unknown> : {}; const after = row.after && typeof row.after === 'object' ? row.after as Record<string, unknown> : row; const changes = Object.keys(after).filter(key => before[key] !== after[key] && fieldLabels[key]).map(key => ({ field: fieldLabels[key], before: formatTestDisplayValue(key, before[key]), after: formatTestDisplayValue(key, after[key]) })); return { identity: String(row.identity || row.recordNo || identities.value[index] || '-'), changes } }))
const formatTime = formatRevisionTime; const formatScope = formatRevisionScope; function goBack() { router.push('/data-revision') }; function returnToTest() { if (record.value) router.push({ path: `/test-list/${record.value.testId}/console`, query: { tab: 'data-list', dataType: record.value.dataType, ...(record.value.testReceptionStatus === 'ENDED' ? { state: 'ended' } : {}) } }) }
diff --git a/tests/test-console-data-import-browser.mjs b/tests/test-console-data-import-browser.mjs
index 5301ecd..cec62f7 100644
--- a/tests/test-console-data-import-browser.mjs
+++ b/tests/test-console-data-import-browser.mjs
@@ -18,7 +18,7 @@
function standardFile(type, invalid = false) {
const fields = consoleImportColumns[type]
- const row = { recordNo: '17', batteryNo: 'BAT-1', recordedAt: '2023-05-08 11:42:11', cycleNo: 1, stepNo: 1, stepName: '恒流放电', sequenceId: 17, capacity: 8.5122, temperature: 25, positivePotential: 1.5, negativePotential: -0.1 }
+ const row = { recordNo: '17', batteryNo: 'BAT-1', recordedAt: '2023-05-08 11:42:11', cycleNo: 1, stepNo: 1, stepName: '恒流放电', sequenceId: 17, capacity: 8.5122, temperature: 25, positivePotential: 1.5, negativePotential: -0.1, chargeMedianVoltage: 0, dischargeMedianVoltage: 1.1709 }
const rows = [Object.values(fields), Object.keys(fields).map(field => row[field] ?? '')]
if (invalid) {
while (rows.length < 19) rows.push([])
@@ -81,6 +81,12 @@
source[XLSX.utils.encode_cell({ r: row + 1, c: column + 10 })]?.v ?? null))
}
}
+ }
+ if (process.env.PARTY_EXCEL) {
+ const imported = JSON.parse(calls.find(call => call.dataType === 'STEP').changesSummary)
+ const source = XLSX.utils.sheet_to_json(XLSX.readFile(process.env.PARTY_EXCEL).Sheets.step)
+ assert.deepEqual(imported.map(row => [row.chargeMedianVoltage, row.dischargeMedianVoltage]),
+ source.map(row => [row['充电中值电压(V)'], row['放电中值电压(V)']]))
}
await page.goto(`${origin}/test-list/1/console/data/import?dataType=REALTIME`)
await page.locator('input[type=file]').setInputFiles(standardFile('REALTIME', true))
@@ -163,6 +169,19 @@
await expect(page.locator('.table-wrap tbody')).toContainText('BAT-1')
await page.reload()
await expect(page.locator('.table-wrap tbody')).toContainText('BAT-1')
+ if (type === 'STEP') {
+ await expect(page.locator('.table-wrap thead')).toContainText('充电中值电压(V)')
+ await expect(page.locator('.table-wrap thead')).toContainText('放电中值电压(V)')
+ await expect(page.locator('.table-wrap tbody')).toContainText('0.0000')
+ await expect(page.locator('.table-wrap tbody')).toContainText('1.1709')
+ const csvEvent = page.waitForEvent('download')
+ await page.getByRole('button', { name: '下载', exact: true }).click()
+ const csv = await readFile(await (await csvEvent).path(), 'utf8')
+ assert.match(csv, /充电中值电压\(V\)/)
+ assert.match(csv, /放电中值电压\(V\)/)
+ assert.match(csv, /"0\.0000","1\.1709"/)
+ assert.doesNotMatch(csv, /峰值|谷值/)
+ }
}
assert.deepEqual(errors, [])
console.log('Ticket03/04 浏览器:独立模板、一键模板、三 Sheet 预览、错误下载和阻断、单次提交原因与批次、子页刷新及窄屏通过;导入未请求实时监控')
diff --git a/tests/test-console-data-import.test.mjs b/tests/test-console-data-import.test.mjs
index 3316b70..778e5f5 100644
--- a/tests/test-console-data-import.test.mjs
+++ b/tests/test-console-data-import.test.mjs
@@ -106,8 +106,18 @@
assert.equal(step.data.recordNo, 'step:1:1')
assert.equal(step.data.recordedAt, 1683521083000)
assert.equal(step.data.stepDuration, 3752)
- assert.equal(step.data.chargePeakVoltage, null)
- assert.equal(step.data.dischargeValleyVoltage, null)
+ assert.equal(step.data.chargeMedianVoltage, 0)
+ assert.equal(step.data.dischargeMedianVoltage, 1.1709)
+ // 中值空格和缺列不变成零,也不回退读取同一文件的峰谷值。
+ XLSX.utils.sheet_add_aoa(workbook.Sheets.step, [['', '', '充电峰值电压(V)', '放电谷值电压(V)'], ['', '', 1.8, 0.8]], { origin: 'I1' })
+ const absent = parseConsoleImport(workbook, 'STEP', ['BAT-1'])[0]
+ assert.deepEqual(absent.errors, [])
+ assert.equal(absent.data.chargeMedianVoltage, null)
+ assert.equal(absent.data.dischargeMedianVoltage, null)
+ XLSX.utils.sheet_add_aoa(workbook.Sheets.step, [['充电中值电压(V)', '放电中值电压(V)'], ['', '不是数字']], { origin: 'I1' })
+ const invalid = parseConsoleImport(workbook, 'STEP', ['BAT-1'])[0]
+ assert.equal(invalid.data.chargeMedianVoltage, null)
+ assert.match(invalid.errors.join(), /放电中值电压.*必须是数字或空值/)
const cycle = parseConsoleImport(workbook, 'CYCLE', ['BAT-1'])[0]
assert.deepEqual(cycle.errors, [])
assert.equal(cycle.data.recordNo, 'cycle:1')
diff --git a/tests/test-ticket23-console-step-regression.test.mjs b/tests/test-ticket23-console-step-regression.test.mjs
index 46d0965..037b7f7 100644
--- a/tests/test-ticket23-console-step-regression.test.mjs
+++ b/tests/test-ticket23-console-step-regression.test.mjs
@@ -23,7 +23,7 @@
assert.match(view, /const physical = physicalSteps\.find\(item => item\.stepNo === row\.stepNo && item\.stepName === row\.stepName\) \|\| physicalSteps\.find\(item => item\.stepNo === row\.stepNo\)/)
assert.match(view, /displayRows\.push\(\{ no: mapping\?\.logicalNo \|\| row\.stepNo/)
assert.match(dataList, /起始电压\(V\)/)
-assert.match(dataList, /放电谷值\(V\)/)
+assert.match(dataList, /放电中值电压\(V\)/)
assert.match(dataList, /<th>工步<\/th>/)
assert.doesNotMatch(dataList, /<th>逻辑工步<\/th>/)
assert.doesNotMatch(dataList, /<th>实际工步<\/th>/)
--
Gitblit v1.7.1