From cfd4511378aefa04d4e06058cee628b6edd8e9c1 Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期二, 08 九月 2026 23:07:09 +0800
Subject: [PATCH] 统一三类报表测试详情的字段与交互
---
src/views/SpliceReportForm.vue | 77 +++++---------------------------------
1 files changed, 11 insertions(+), 66 deletions(-)
diff --git a/src/views/SpliceReportForm.vue b/src/views/SpliceReportForm.vue
index 60d8cbd..7036d86 100644
--- a/src/views/SpliceReportForm.vue
+++ b/src/views/SpliceReportForm.vue
@@ -4,11 +4,12 @@
import { useRoute, useRouter } from 'vue-router'
import { getUserInfo } from '@/api/auth'
import { getSpliceSources, previewSpliceReport, createSpliceReport, editSpliceReport, refreshSpliceSource,
- getSpliceReport, getSpliceVersion, listSpliceVersions, spliceFields, spliceFieldValue, spliceStatisticKey, spliceSourceKey, spliceSourceValue, spliceExperiment, spliceSourceFields, spliceDetailValue,
- type SpliceCandidate, type SpliceMatrix, type SpliceField, type SpliceSettings, type SpliceSegment, type SavedSpliceReport } from '@/api/spliceReports'
+ getSpliceReport, getSpliceVersion, listSpliceVersions, spliceStatisticKey, spliceSourceKey, spliceReportDetailColumn,
+ type SpliceCandidate, type SpliceMatrix, type SpliceSettings, type SavedSpliceReport } from '@/api/spliceReports'
import { openSpliceSavedReportPrint } from '@/utils/spliceSavedReport'
import SpliceSavedReport from '@/components/SpliceSavedReport.vue'
-import SpliceFieldControls from '@/components/SpliceFieldControls.vue'
+import ReportTestDetail from '@/components/ReportTestDetail.vue'
+import { snapshotReportDetails } from '@/utils/reportTestDetails'
const route = useRoute()
const router = useRouter()
@@ -31,10 +32,7 @@
const sources = ref<SpliceCandidate[]>([])
const selectedTests = ref<SpliceCandidate[]>([])
const samples = ref<string[]>([])
-const fields = ref<SpliceField[]>([])
-const detailFieldValues = ref<Record<string, Record<string, string>>>({})
const statisticValues = ref<Record<string, string>>({})
-const sourceExperimentNames = ref<Record<string,string>>({})
const sourceDetailOverrides = ref<SpliceSettings['sourceDetailOverrides']>({})
const hiddenSeries = ref(false)
const statisticMetrics = [{ key: 'finalVoltage1', title: '最终电压1', unit: 'V' }, { key: 'energyEfficiency', title: '能量效率', unit: '%' }] as const
@@ -49,7 +47,6 @@
const selectedCandidateId = ref<string | null>(null)
const candidateGroupId = ref<number | null>(null)
const candidateQuery = ref('')
-const batteryGroup = computed(() => [...new Set(selectedTests.value.map(item => item.group))].join('、'))
const candidateTests = computed(() => sources.value.filter(source =>
(!candidateGroupId.value || source.groupId === candidateGroupId.value) &&
(source.name.includes(candidateQuery.value) || String(source.selection.testId).includes(candidateQuery.value) || source.batteryNo.includes(candidateQuery.value))))
@@ -58,18 +55,11 @@
const matrix = computed(() => savedReport.value || draftMatrix.value)
const segmentMode = computed(() => !savedReport.value || savedReport.value.spliceMode === 'battery-segments')
const detailColumns = computed(() => matrix.value?.segments || [])
-const sourceFields = computed(() => spliceSourceFields(detailColumns.value, sourceDetailOverrides.value))
-const independentDetails = computed(() => segmentMode.value && sourceDetailOverrides.value !== undefined)
+const reportDetailColumns = computed(() => detailColumns.value.map(spliceReportDetailColumn))
const statisticSettings = computed<SpliceSettings>(() => ({
- ...(sourceDetailOverrides.value === undefined ? {} : { sourceDetailOverrides: sourceDetailOverrides.value }),
- sourceExperimentNames: sourceExperimentNames.value,
+ sourceDetailOverrides: snapshotReportDetails(reportDetailColumns.value, sourceDetailOverrides.value),
hiddenSeries: hiddenSeries.value,
- fieldStructure: fields.value,
- batteryFields: fields.value.filter(field => field.category === 'battery').map(field => field.name),
- specialFields: fields.value.filter(field => field.category === 'special').map(field => field.name),
- detailFieldValues: Object.fromEntries(fields.value.map(field => [field.name, Object.fromEntries((segmentMode.value ? selectedTests.value.map(spliceSourceKey) : samples.value)
- .filter(battery => detailFieldValues.value[field.name]?.[battery] !== undefined)
- .map(battery => [battery, detailFieldValues.value[field.name]![battery]!]))])),
+ fieldStructure: [], batteryFields: [], specialFields: [], detailFieldValues: {},
statisticValues: statisticValues.value,
metrics: statisticMetrics.filter(metric => statisticVisible[metric.key]).map(metric => metric.key), seriesColors: seriesColors.value,
}))
@@ -84,10 +74,7 @@
function applySaved(saved: SavedSpliceReport) {
savedReport.value = saved
reportName.value = saved.name; experimentName.value = saved.description || ''
- fields.value = spliceFields(saved.settings).map(field => ({ ...field }))
- detailFieldValues.value = structuredClone(saved.settings.detailFieldValues || {})
statisticValues.value = { ...saved.settings.statisticValues }
- sourceExperimentNames.value = { ...saved.settings.sourceExperimentNames }
sourceDetailOverrides.value = saved.settings.sourceDetailOverrides === undefined ? undefined : structuredClone(saved.settings.sourceDetailOverrides)
hiddenSeries.value = saved.settings.hiddenSeries === true
seriesColors.value = [...(saved.settings.seriesColors || ['#3978f6', '#109854', '#d25b45', '#7754a8'])]
@@ -112,8 +99,8 @@
} else {
if (!canCreate.value) throw new Error('无新增拼接报表权限')
selectedTests.value = []; samples.value = []; reportName.value = ''; experimentName.value = ''
- fields.value = []; detailFieldValues.value = {}; statisticValues.value = {}; reordered = false
- sourceExperimentNames.value = {}; hiddenSeries.value = false
+ statisticValues.value = {}; reordered = false
+ hiddenSeries.value = false
sourceDetailOverrides.value = {}
sources.value = await getSpliceSources()
savedContent.value = formContent()
@@ -165,33 +152,6 @@
const item = selectedTests.value[index]
if (!item) return
selectedTests.value.splice(index, 1); selectedTests.value.splice(index + direction, 0, item); reordered = true
-}
-function fieldValue(field: SpliceField, battery: string) { return spliceFieldValue(matrix.value?.segments || [], statisticSettings.value, field, battery) }
-function setFieldValue(field: SpliceField, battery: string, value: string) {
- detailFieldValues.value[field.name] ||= {}
- detailFieldValues.value[field.name][battery] = value
-}
-function setDetailValue(segment: SpliceSegment, value: string, field?: typeof sourceFields.value[number]) {
- sourceDetailOverrides.value ||= {}
- const key = spliceSourceKey(segment.source)
- const current = sourceDetailOverrides.value[key] || {}
- sourceDetailOverrides.value[key] = field ? { ...current, fieldValues: { ...current.fieldValues, [field.key]: value } } : { ...current, experimentName: value }
-}
-function applyFields(selected: SpliceField[]) {
- // 模板不携值;同名但身份或单位不同的字段不能继承原模板人工值。来源详情覆盖完全独立。
- for (const field of selected) {
- const previous = fields.value.find(item => item.name === field.name)
- if (!previous || (previous.fieldDefinitionId ?? null) !== (field.fieldDefinitionId ?? null) || previous.category !== field.category || previous.unit !== field.unit) delete detailFieldValues.value[field.name]
- }
- fields.value = selected
-}
-async function addSpecialField() {
- try {
- const { value } = await ElMessageBox.prompt('请输入字段名称', '添加项', { confirmButtonText: '添加', cancelButtonText: '取消', inputValidator: value => Boolean(value?.trim()) || '请输入字段名称' })
- const name = value.trim()
- if (fields.value.some(field => field.name === name)) { ElMessage.warning('该字段已经存在'); return }
- fields.value.push({ name, displayName: name, category: 'special', unit: '' })
- } catch (error) { if (error !== 'cancel' && error !== 'close') ElMessage.error('添加字段失败') }
}
function updateSeriesColor(index: number, color: string | null) { if (color) seriesColors.value[index] = color }
async function printReport() {
@@ -257,23 +217,8 @@
</div>
</section>
- <section class="detail-panel">
- <header class="panel-heading"><div><strong>测试详情</strong></div></header>
- <div class="detail-meta"><label><span>报表说明</span><el-input v-model="experimentName" /></label><div><span>电池组</span><strong>{{ batteryGroup }}</strong></div></div>
- <div v-if="independentDetails" class="detail-table-wrap"><table class="detail-table source-detail-table" :style="{ minWidth: `${Math.max(760, detailColumns.length * 190 + 170)}px` }"><thead><tr><th>来源详情字段</th><th v-for="segment in detailColumns" :key="spliceSourceKey(segment.source)">{{ segment.source.name }}<br>{{ segment.source.group }} · {{ segment.source.batteryNo }}</th></tr></thead><tbody>
- <tr><td>实验</td><td v-for="segment in detailColumns" :key="spliceSourceKey(segment.source)"><el-input :model-value="spliceDetailValue(segment, sourceDetailOverrides)" :aria-label="`${segment.source.name} ${segment.source.batteryNo} 实验名称`" placeholder="缺失" @update:model-value="setDetailValue(segment, $event)" /></td></tr>
- <tr v-for="field in sourceFields" :key="field.key"><td>{{ field.name }}({{ field.category === 'battery' ? '电池字段' : '特殊字段' }})</td><td v-for="segment in detailColumns" :key="spliceSourceKey(segment.source)"><el-input :model-value="spliceDetailValue(segment, sourceDetailOverrides, field)" :aria-label="`${segment.source.name} ${segment.source.batteryNo} ${field.category === 'battery' ? '电池字段' : '特殊字段'} ${field.name}`" size="small" placeholder="缺失" @update:model-value="setDetailValue(segment, $event, field)" /></td></tr>
- </tbody></table></div>
- <div v-if="segmentMode" class="detail-table-wrap"><table class="detail-table report-field-table" :style="{ minWidth: `${Math.max(760, detailColumns.length * 190 + 170)}px` }"><thead><tr><th>{{ independentDetails ? '报表字段' : '字段' }}</th><th v-for="segment in detailColumns" :key="spliceSourceKey(segment.source)">{{ segment.source.name }}<br>{{ segment.source.group }} · {{ segment.source.batteryNo }}</th></tr></thead><tbody>
- <tr v-if="!independentDetails"><td>实验</td><td v-for="segment in detailColumns" :key="spliceSourceKey(segment.source)"><el-input :model-value="spliceExperiment(segment, statisticSettings)" placeholder="缺失" @update:model-value="sourceExperimentNames[spliceSourceKey(segment.source)] = $event" /></td></tr>
- <tr v-for="field in fields" :key="field.name"><td>{{ field.displayName }}{{ field.unit ? ` (${field.unit})` : '' }}</td><td v-for="segment in detailColumns" :key="spliceSourceKey(segment.source)"><el-input :model-value="spliceSourceValue(segment, statisticSettings, field)" :aria-label="`${segment.source.name} ${segment.source.batteryNo} 报表字段 ${field.displayName}`" size="small" placeholder="缺失" @update:model-value="setFieldValue(field, spliceSourceKey(segment.source), $event)" /></td></tr>
- </tbody></table></div>
- <div v-else class="detail-table-wrap"><table class="detail-table" :style="{ minWidth: `${Math.max(760, samples.length * 190 + 170)}px` }"><thead><tr><th>字段</th><th v-for="sample in samples" :key="sample">{{ sample }}</th></tr></thead><tbody>
- <tr v-for="field in fields" :key="field.name"><td>{{ field.displayName }}{{ field.unit ? ` (${field.unit})` : '' }}</td><td v-for="sample in samples" :key="sample"><el-input :model-value="fieldValue(field, sample)" size="small" placeholder="缺失" @update:model-value="setFieldValue(field, sample, $event)" /></td></tr>
- </tbody></table></div>
- <div class="detail-actions"><button class="primary-button" type="button" @click="addSpecialField"><el-icon><Plus /></el-icon>添加项</button></div>
- <SpliceFieldControls :fields="fields" :segments="matrix?.segments || []" :can-create="canCreate" :can-edit="canEdit" @apply="applyFields" />
- </section>
+ <ReportTestDetail v-model="sourceDetailOverrides" :columns="reportDetailColumns" :disabled="saving || previewing || !!previewError" />
+ <div class="detail-meta"><label><span>报表说明</span><el-input v-model="experimentName" /></label></div>
<section v-if="statisticPreview" class="chart-panel">
<div class="metric-switches"><label v-for="metric in statisticMetrics" :key="metric.key"><span>{{ metric.title }}</span><el-switch v-model="statisticVisible[metric.key]" /></label></div>
--
Gitblit v1.7.1