yeg
2026-09-01 a8fd89f0a85af0c13bbde0ae8ee8fad84ec8e6f7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { formatRevisionScope, formatRevisionTime, getDataRevisionAction, revisionDataTypeLabels, revisionOperationLabels, type DataRevisionAction } from '@/api/dataRevisions'
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
const fieldLabels: Record<string, string> = { voltage: '电压', current: '电流', capacity: '容量', energy: '能量', stepDuration: '工步时间', startVoltage: '起始电压', endVoltage: '终止电压', chargePeakVoltage: '充电峰值电压', dischargeValleyVoltage: '放电谷值电压', chargeCapacity: '充电容量', dischargeCapacity: '放电容量', efficiency: '效率', chargeEnergy: '充电能量', dischargeEnergy: '放电能量' }
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: before[key], after: 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' } : {}) } }) }
onMounted(async () => { try { record.value = await getDataRevisionAction(Number(route.params.id)) } catch (error) { loadError.value = error instanceof Error ? error.message : '数据修订行为加载失败' } finally { loading.value = false } })
</script>
<template><div class="revision-detail-page"><div class="page-tools"><button class="back-button" type="button" @click="goBack"><el-icon><ArrowLeft /></el-icon>返回数据修订记录</button><span>DATA REVISION ACTION</span></div><section v-loading="loading" class="detail-panel"><template v-if="record"><header class="detail-toolbar"><div class="heading-group"><i></i><div><strong>修订行为 #{{ record.actionId }}</strong><span>一次用户提交 · 只读审计记录</span></div></div><span class="operation-tag" :class="record.operationType.toLowerCase()">{{ operationLabels[record.operationType] }}</span></header><section class="summary-section"><div class="summary-grid"><div><span>测试</span><strong>{{ record.testName }} (#{{ record.testId }})</strong></div><div><span>数据类型</span><strong>{{ dataTypeLabels[record.dataType] }}</strong></div><div><span>操作范围</span><strong>{{ formatScope(record.scopeSummary) }}</strong></div><div><span>涉及记录</span><strong>{{ identities.length }} 条</strong></div><div><span>操作人</span><strong>{{ record.operatorName }}</strong></div><div><span>操作时间</span><strong>{{ formatTime(record.createdAtUtc) }}</strong></div></div><div class="reason-line"><span>操作原因</span><p>{{ record.reason }}</p></div><button class="primary-button return-button" type="button" @click="returnToTest">回到对应测试数据列表</button></section><section class="data-section"><div class="section-title"><i></i><strong>记录编号</strong><span>RECORD NUMBERS</span></div><div class="identity-list"><code v-for="identity in identities" :key="identity">{{ identity }}</code><span v-if="!identities.length">暂无记录编号</span></div></section><section class="data-section"><div class="section-title"><i></i><strong>字段变化摘要</strong><span>CHANGE SUMMARY</span></div><div v-if="record.operationType === 'DELETE'" class="deleted-notice"><el-icon><Delete /></el-icon><div><strong>数据已物理删除</strong><p>本页仅保留本次删除行为的范围、身份和原因摘要,不恢复或展示已删除数据。</p></div></div><div v-else-if="changeRows.length" class="change-list"><article v-for="row in changeRows" :key="row.identity"><header>{{ row.identity }}</header><dl v-if="row.changes.length"><div v-for="change in row.changes" :key="change.field"><dt>{{ change.field }}</dt><dd><span>{{ change.before ?? '-' }}</span><el-icon><Right /></el-icon><strong>{{ change.after ?? '-' }}</strong></dd></div></dl><p v-else>本次导入包含该记录,字段值已按提交内容保存。</p></article></div><div v-else class="empty-summary">无字段变化摘要</div></section></template><div v-else-if="!loading" class="not-found"><el-empty :description="loadError || '未找到该数据修订行为'" /><button class="secondary-button" type="button" @click="goBack">返回记录列表</button></div></section></div></template>
<style scoped>.revision-detail-page{width:100%}.page-tools{height:34px;display:flex;align-items:center;justify-content:space-between}.page-tools>span{color:#7f8d99;font-size:10px}.back-button{display:inline-flex;align-items:center;gap:6px;border:0;color:#52606d;background:transparent;cursor:pointer}.detail-panel{min-height:460px;background:#fff;border:1px solid #d8e1e9}.detail-toolbar{height:54px;display:flex;align-items:center;justify-content:space-between;padding:0 16px;background:var(--surface-soft);border-bottom:1px solid var(--line)}.heading-group{display:flex;align-items:center;gap:9px}.heading-group>i,.section-title i{width:3px;height:18px;background:#2b78a3}.heading-group strong,.heading-group span{display:block}.heading-group strong{color:var(--ink);font-size:15px}.heading-group span{margin-top:2px;color:#81909d;font-size:10px}.operation-tag{padding:4px 9px;border:1px solid}.operation-tag.edit{color:#176fcb;background:#edf6ff;border-color:#b8d9fb}.operation-tag.import{color:#23865e;background:#edf9f3;border-color:#b8dfcd}.operation-tag.delete{color:#b34f4f;background:#fff1f1;border-color:#e9bcbc}.summary-section,.data-section{padding:20px 22px}.data-section{border-top:1px solid var(--line)}.summary-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:20px 28px}.summary-grid>div{display:grid;grid-template-columns:72px minmax(0,1fr);gap:8px}.summary-grid span,.reason-line>span{color:#7b8794;font-size:12px}.summary-grid strong{min-width:0;color:#34414d;font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.reason-line{display:grid;grid-template-columns:72px minmax(0,1fr);gap:8px;margin:18px 0;padding-top:16px;border-top:1px dashed #dce5ed}.reason-line p{color:#34414d;font-size:13px}.primary-button,.secondary-button{height:34px;padding:0 13px;border:1px solid transparent;border-radius:3px;cursor:pointer}.primary-button{color:#fff;background:var(--blue)}.return-button{height:30px;padding:0 10px;font-size:12px}.secondary-button{color:#52606d;background:#fff;border-color:var(--line-strong)}.section-title{display:flex;align-items:center;gap:8px;margin-bottom:14px}.section-title i{height:15px}.section-title strong{color:#26333f;font-size:14px}.section-title span{color:#8a98a5;font-size:10px}.identity-list{display:flex;flex-wrap:wrap;gap:6px}.identity-list code{padding:3px 7px;border:1px solid #d8e3ec;background:#f6f8fa;color:#405261;font-size:11px;line-height:18px}.identity-list>span,.empty-summary{color:#8795a3;font-size:12px}.change-list{display:grid;gap:12px}.change-list article{border:1px solid #d8e3ec}.change-list article>header{padding:9px 12px;background:#f6f9fc;color:#405261;font-family:'Cascadia Mono',Consolas,monospace;font-size:12px}.change-list dl{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:1px;background:#e2e9ef}.change-list dl>div{padding:10px 12px;background:#fff}.change-list dt{margin-bottom:6px;color:#71808d;font-size:11px}.change-list dd{display:flex;align-items:center;gap:8px;font-size:12px}.change-list dd span{color:#8795a3;text-decoration:line-through}.change-list dd strong{color:#176fcb}.change-list article>p{padding:12px;color:#52606d;font-size:12px}.deleted-notice{display:flex;align-items:center;gap:14px;padding:18px;border:1px solid #efd0d0;background:#fff7f7;color:#b34f4f}.deleted-notice>.el-icon{font-size:24px}.deleted-notice p{margin-top:5px;color:#785c5c;font-size:12px}.not-found{min-height:420px;display:flex;align-items:center;justify-content:center;flex-direction:column}@media(max-width:900px){.summary-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.change-list dl{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:620px){.summary-section,.data-section{padding:16px 12px}.summary-grid,.change-list dl{grid-template-columns:1fr}.reason-line{grid-template-columns:1fr}}</style>