修正控制台修订入口与删除语义
数据列表展示设备和服务器时间,旧编辑链接复用控制台,导入校验读取后端测试电池,删除改为当前有效视图移除。
| | |
| | | energy: number | string | null |
| | | deviceId: number |
| | | channelId: number |
| | | adapterId?: number |
| | | adapterCode?: string |
| | | devtype?: number |
| | | subdevid?: number |
| | | } |
| | | |
| | | export interface PersistedTestStep extends PersistedTestRecord { |
| | |
| | | } |
| | | function mapRecord(record: PersistedTestRecord): DataRow { |
| | | const deviceTime = formatTime(record.deviceTime || record.recordedAt) |
| | | return emptyRow({ id: `${record.deviceId}:${record.channelId}:${record.sequenceId}`, recordNo: record.recordNo, batteryNo: record.batteryNo || '-', cycleNo: Number(record.cycleNo), stepNo: Number(record.stepNo), stepName: record.stepName || '-', sequenceId: Number(record.sequenceId), recordedAt: deviceTime, absoluteTime: deviceTime, serverReceivedAt: formatTime(record.serverReceivedAt), voltage: record.voltage == null ? '-' : Number(record.voltage).toFixed(4), current: record.current == null ? '-' : Number(record.current).toFixed(4), capacity: record.capacity == null ? '-' : Number(record.capacity).toFixed(4), energy: record.energy == null ? '-' : Number(record.energy).toFixed(4) }) |
| | | const identity = [record.adapterId, record.devtype, record.deviceId, record.subdevid, record.channelId, record.sequenceId].join(':') |
| | | return emptyRow({ id: identity, recordNo: record.recordNo, batteryNo: record.batteryNo || '-', cycleNo: Number(record.cycleNo), stepNo: Number(record.stepNo), stepName: record.stepName || '-', sequenceId: Number(record.sequenceId), recordedAt: deviceTime, absoluteTime: deviceTime, serverReceivedAt: formatTime(record.serverReceivedAt), voltage: record.voltage == null ? '-' : Number(record.voltage).toFixed(4), current: record.current == null ? '-' : Number(record.current).toFixed(4), capacity: record.capacity == null ? '-' : Number(record.capacity).toFixed(4), energy: record.energy == null ? '-' : Number(record.energy).toFixed(4) }) |
| | | } |
| | | function mapStep(record: PersistedTestStep): DataRow { |
| | | const startedAt = formatTime(record.recordedAt) |
| | | return emptyRow({ id: String(record.id), 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), 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) }) |
| | | const identity = `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) }) |
| | | } |
| | | function mapCycle(record: PersistedTestCycle): DataRow { |
| | | const startedAt = formatTime(record.recordedAt) |
| | | return emptyRow({ id: String(record.id), recordNo: record.recordNo, batteryNo: record.batteryNo, cycleNo: Number(record.cycleNo), sequenceId: Number(record.sequenceId || 0), recordedAt: startedAt, absoluteTime: formatTime(record.absoluteTime || record.recordedAt), 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) }) |
| | | const identity = `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) }) |
| | | } |
| | | async function loadRecords() { |
| | | loading.value = true |
| | |
| | | const physicalIds = ids |
| | | let result: { value: string } |
| | | try { |
| | | result = await ElMessageBox.prompt(`确认物理删除 ${ids.length} 条${activeTab.value}数据?删除后不可恢复。`, '删除确认', { inputPlaceholder: '请输入删除原因', inputValidator: value => (value.trim() ? true : '删除原因不能为空') }) |
| | | result = await ElMessageBox.prompt(`确认从当前有效视图移除 ${ids.length} 条${activeTab.value}数据?原始设备数据仍会保留。`, '移除数据', { inputPlaceholder: '请输入移除原因', inputValidator: value => (value.trim() ? true : '移除原因不能为空') }) |
| | | } catch { |
| | | return |
| | | } |
| | | 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(), overrides: physicalIds.map(stableIdentity => ({ stableIdentity, fieldValues: null, deleted: true })) }) |
| | | await submitConsoleDataAction(props.testId, { dataType: dataType.value, operationType: 'DELETE', scopeSummary: `从当前有效视图移除${ids.length}条${activeTab.value}数据`, identitySummary: JSON.stringify(physicalIds), changesSummary: null, reason: result.value.trim(), overrides: physicalIds.map(stableIdentity => ({ stableIdentity, fieldValues: null, deleted: true })) }) |
| | | 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)) |
| | | selectedIds.value = new Set() |
| | | ElMessage.success(`已物理删除 ${ids.length} 条数据`) |
| | | ElMessage.success(`已从当前有效视图移除 ${ids.length} 条数据`) |
| | | } catch (error) { |
| | | ElMessage.error(error instanceof Error ? error.message : '数据删除失败') |
| | | } |
| | |
| | | <th>电池</th> |
| | | <th>循环号</th> |
| | | <th>工步</th> |
| | | <th>设备时间</th> |
| | | <th>服务器接收时间</th> |
| | | <th>工步时间</th> |
| | | <th>容量(Ah)</th> |
| | | <th>能量(Wh)</th> |
| | |
| | | <th>记录编号</th> |
| | | <th>电池</th> |
| | | <th>循环号</th> |
| | | <th>设备时间</th> |
| | | <th>服务器接收时间</th> |
| | | <th>充电容量(Ah)</th> |
| | | <th>放电容量(Ah)</th> |
| | | <th>充放电效率(%)</th> |
| | |
| | | </td> |
| | | <td>{{ row.cycleNo }}</td> |
| | | <td>{{ row.stepNo }} · {{ row.stepName }}</td> |
| | | <td>{{ row.recordedAt }}</td> |
| | | <td>{{ row.serverReceivedAt }}</td> |
| | | <td> |
| | | <el-input v-if="editing && draftRows[row.id]" v-model="draftRows[row.id].stepDuration" size="small" /> |
| | | <span v-else>{{ row.stepDuration }}</span> |
| | |
| | | <strong>{{ row.batteryNo }}</strong> |
| | | </td> |
| | | <td>{{ row.cycleNo }}</td> |
| | | <td>{{ row.recordedAt }}</td> |
| | | <td>{{ row.serverReceivedAt }}</td> |
| | | <td> |
| | | <el-input v-if="editing && draftRows[row.id]" v-model="draftRows[row.id].chargeCapacity" size="small" /> |
| | | <span v-else>{{ row.chargeCapacity }}</span> |
| | |
| | | </tr></template |
| | | > |
| | | <tr v-if="!loading && !pagedRows.length"> |
| | | <td :colspan="activeTab === 'realtime' ? 14 : activeTab === 'step' ? 13 : 10" class="empty-cell"> |
| | | <td :colspan="activeTab === 'realtime' ? 14 : activeTab === 'step' ? 15 : 12" class="empty-cell"> |
| | | {{ loadError || '暂无检测柜记录' }} |
| | | </td> |
| | | </tr> |
| | |
| | | { |
| | | path: 'test-list/:id/console/data/:recordId/edit', |
| | | name: 'test-console-data-edit', |
| | | component: () => import('@/views/ConsoleDataEdit.vue'), |
| | | // 兼容旧链接,复用控制台后端修订入口,避免回退到浏览器演示状态。 |
| | | component: () => import('@/views/TestConsole.vue'), |
| | | meta: { title: '修改测试数据' }, |
| | | }, |
| | | { |
| | |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { useRoute, useRouter } from 'vue-router' |
| | | import * as XLSX from 'xlsx' |
| | | import { consoleDataRowsByTest, type ConsoleDataRow } from '@/stores/consoleTestData' |
| | | import type { ConsoleDataRow } from '@/stores/consoleTestData' |
| | | import { submitConsoleDataAction, type ConsoleDataType } from '@/api/consoleData' |
| | | import { getTestDetail } from '@/api/tests' |
| | | |
| | |
| | | const numericHeaders = requiredHeaders.filter(header => !['记录编号', '电池号', '记录时间', '工步', '工步名称', '工步时间'].includes(header)) |
| | | const validRows = computed(() => validatedRows.value.filter(row => row.errors.length === 0)) |
| | | const failedRows = computed(() => validatedRows.value.filter(row => row.errors.length > 0)) |
| | | const knownBatteries = computed(() => new Set((consoleDataRowsByTest[testId] || []).map(row => row.batteryNo))) |
| | | const knownBatteries = ref<Set<string>>(new Set()) |
| | | |
| | | function backToDataList() { |
| | | router.push({ path: `/test-list/${testId}/console`, query: { tab: 'data-list', state: canImport.value ? 'ended' : 'running' } }) |
| | |
| | | onMounted(async () => { |
| | | try { |
| | | const detail = await getTestDetail(testId) |
| | | knownBatteries.value = new Set((detail.batteries || []).map(item => item.batteryNo)) |
| | | canImport.value = detail.receptionStatus === 'ENDED' && detail.testStatus === 'COMPLETED' |
| | | } catch { |
| | | canImport.value = false |