| | |
| | | import type { ReportMetricKey } from '@/utils/reportMeasurementChart' |
| | | import { openFormationSavedReportPrint } from '@/utils/formationSavedReportPrint' |
| | | import FormationFieldControls from '@/components/FormationFieldControls.vue' |
| | | import { formationSourceKey, formationSourceFields, formationDetailValue } from '@/utils/formationSourceDetails' |
| | | import FormationStepCurves from '@/components/FormationStepCurves.vue' |
| | | import { formationChargeSteps, formationCycleKey } from '@/utils/formationStepChart' |
| | | import type { FormationSettings, FormationStepSelection } from '@/api/formationReports' |
| | |
| | | const cycleNos = ref<number[]>([]) |
| | | const fields = ref<FormationField[]>(structuredClone(defaultFormationFields)) |
| | | const values = ref<Record<string, string>>({}) |
| | | const sourceDetailOverrides = ref<NonNullable<FormationSettings['sourceDetailOverrides']>>({}) |
| | | const sourceFields = computed(() => formationSourceFields(reportItems.value.map(item => item.source), sourceDetailOverrides.value)) |
| | | function setDetail(source: FormationCandidate, value: string, field?: typeof sourceFields.value[number]) { |
| | | const key = formationSourceKey(source) |
| | | const current = sourceDetailOverrides.value[key] ?? {} |
| | | sourceDetailOverrides.value[key] = field ? { ...current, fieldValues: { ...current.fieldValues, [field.key]: value } } : { ...current, experimentName: value } |
| | | } |
| | | const chargeStepSelections = ref<Record<string, number>>({}) |
| | | const metrics = ref<ReportMetricKey[]>(formationCurveMetrics.map(metric => metric.key)) |
| | | const seriesColors = ref<Record<string, string>>({}) |
| | |
| | | const curveStepSelections = ref<Record<string, FormationStepSelection>>({}) |
| | | const hiddenSeriesKeys = ref<string[]>([]) |
| | | const actualSteps = computed(() => !saved.value || saved.value.curveMode === 'actual-steps') |
| | | const stepSettings = computed<FormationSettings>(() => ({ fields: fields.value, values: values.value, chargeStepSelections: chargeStepSelections.value, metrics: metrics.value, seriesColors: seriesColors.value, manualChargeSteps: manualChargeSteps.value, curveStepSelections: curveStepSelections.value, hiddenSeriesKeys: hiddenSeriesKeys.value, activeCycle: activeCycle.value })) |
| | | const stepSettings = computed<FormationSettings>(() => ({ fields: fields.value, values: values.value, sourceDetailOverrides: sourceDetailOverrides.value, chargeStepSelections: chargeStepSelections.value, metrics: metrics.value, seriesColors: seriesColors.value, manualChargeSteps: manualChargeSteps.value, curveStepSelections: curveStepSelections.value, hiddenSeriesKeys: hiddenSeriesKeys.value, activeCycle: activeCycle.value })) |
| | | function changeStepSettings(settings: Partial<FormationSettings>) { |
| | | if (settings.curveStepSelections) curveStepSelections.value = settings.curveStepSelections |
| | | if (settings.hiddenSeriesKeys) hiddenSeriesKeys.value = settings.hiddenSeriesKeys |
| | |
| | | } |
| | | function chargeKey(item: FormationItem, cycle: number) { return `${item.source.selection.testId}|${item.source.selection.testBatteryId}|${cycle}` } |
| | | function updateSeriesColor(item: FormationItem, color: string | null) { if (color) seriesColors.value[formationSeriesKey(item)] = color } |
| | | function formContent() { return JSON.stringify({ name: reportName.value, description: description.value, settings: actualSteps.value ? stepSettings.value : { fields: fields.value, values: values.value, chargeStepSelections: chargeStepSelections.value, metrics: metrics.value, seriesColors: seriesColors.value } }) } |
| | | function formContent() { return JSON.stringify({ name: reportName.value, description: description.value, settings: actualSteps.value ? stepSettings.value : { fields: fields.value, values: values.value, sourceDetailOverrides: sourceDetailOverrides.value, chargeStepSelections: chargeStepSelections.value, metrics: metrics.value, seriesColors: seriesColors.value } }) } |
| | | function restore(record: SavedFormationReport) { |
| | | saved.value = record; reportName.value = record.name; description.value = record.description || '' |
| | | items.value = record.items; cycleNos.value = record.cycleNos |
| | | fields.value = record.settings.fields; values.value = record.settings.values; chargeStepSelections.value = record.settings.chargeStepSelections || {} |
| | | sourceDetailOverrides.value = record.settings.sourceDetailOverrides ?? {} |
| | | metrics.value = record.settings.metrics ?? formationCurveMetrics.map(metric => metric.key) |
| | | seriesColors.value = record.settings.seriesColors ?? {} |
| | | manualChargeSteps.value = record.settings.manualChargeSteps ?? {}; curveStepSelections.value = record.settings.curveStepSelections ?? {} |
| | |
| | | (field.scope === 'detail' ? [0] : item.cycleNos).map(cycle => formationValueKey(item, cycle, field))))) |
| | | values.value = Object.fromEntries(Object.entries(values.value).filter(([key]) => allowed.has(key))) |
| | | const sourceKeys = new Set(items.value.flatMap(item => [formationSeriesKey(item), ...Object.entries(item.stepCurves ?? {}).flatMap(([cycle, steps]) => Object.keys(steps).map(step => `${formationSeriesKey(item)}|${cycle}|${step}`))])) |
| | | sourceDetailOverrides.value = Object.fromEntries(Object.entries(sourceDetailOverrides.value).filter(([key]) => sourceKeys.has(key))) |
| | | hiddenSeriesKeys.value = hiddenSeriesKeys.value.filter(key => sourceKeys.has(key)) |
| | | const cycleKeys = new Set(items.value.flatMap(item => item.cycleNos.map(cycle => formationCycleKey(item, cycle)))) |
| | | manualChargeSteps.value = Object.fromEntries(Object.entries(manualChargeSteps.value).filter(([key]) => cycleKeys.has(key))) |
| | |
| | | saving.value = true |
| | | try { |
| | | pruneValues() |
| | | const content = { name: reportName.value.trim(), description: description.value, settings: actualSteps.value ? stepSettings.value : { fields: fields.value, values: values.value, chargeStepSelections: chargeStepSelections.value, metrics: metrics.value, seriesColors: seriesColors.value } } |
| | | const content = { name: reportName.value.trim(), description: description.value, settings: actualSteps.value ? stepSettings.value : { fields: fields.value, values: values.value, sourceDetailOverrides: sourceDetailOverrides.value, chargeStepSelections: chargeStepSelections.value, metrics: metrics.value, seriesColors: seriesColors.value } } |
| | | const result = editingId.value && saved.value |
| | | ? await editFormationReport(editingId.value, { ...content, baseVersionNo: saved.value.versionNo, changeSummary: changeSummary.value.trim() }) |
| | | : await createFormationReport({ ...content, selections: items.value.map(item => item.source.selection) }) |
| | |
| | | loading.value = true; loadError.value = '' |
| | | previewSequence++; previewLoading.value = false; previewError.value = '' |
| | | saved.value = null; items.value = []; cycleNos.value = []; values.value = {}; chargeStepSelections.value = {}; selectedKeys.value = [] |
| | | sourceDetailOverrides.value = {} |
| | | try { |
| | | const permissions = (await getUserInfo()).permissions || [] |
| | | const has = (action: string) => permissions.includes('*:*:*') || permissions.includes(`system:formation-report:${action}`) |
| | |
| | | <div class="report-data-table-wrap"><table class="report-data-table" :style="tableStyle"> |
| | | <thead><tr><th>测试</th><th v-for="column in reportColumns" :key="column.id" :colspan="column.items.length">{{ column.name }}</th></tr> |
| | | <tr><th>电池组 / 电池号</th><th v-for="item in reportItems" :key="selectionKey(item.source)">{{ item.source.batteryGroup }} / {{ item.source.batteryNo }}</th></tr></thead> |
| | | <tbody><tr v-for="field in detailFields" :key="field.key"><td>{{ formationFieldLabel(field) }}</td><td v-for="item in reportItems" :key="selectionKey(item.source)"> |
| | | <tbody> |
| | | <tr><td>实验名称</td><td v-for="item in reportItems" :key="selectionKey(item.source)"><el-input :model-value="formationDetailValue(item.source, sourceDetailOverrides)" :disabled="readonly" :aria-label="`${item.source.name} ${item.source.batteryNo} 实验名称`" @update:model-value="setDetail(item.source, $event)" /></td></tr> |
| | | <tr v-for="field in sourceFields" :key="`console:${field.key}`"><td>{{ field.name }}({{ field.category === 'battery' ? '电池字段' : '特殊字段' }})</td><td v-for="item in reportItems" :key="selectionKey(item.source)"><el-input :model-value="formationDetailValue(item.source, sourceDetailOverrides, field)" :disabled="readonly" :aria-label="`${item.source.name} ${item.source.batteryNo} 详情 ${field.name}`" @update:model-value="setDetail(item.source, $event, field)" /></td></tr> |
| | | <tr v-for="field in detailFields" :key="field.key"><td>{{ formationFieldLabel(field) }}</td><td v-for="item in reportItems" :key="selectionKey(item.source)"> |
| | | <el-input :model-value="inputValue(item, 0, field)" @update:model-value="setValue(item, 0, field, $event)" :disabled="readonly" :aria-label="`${item.source.name} ${item.source.batteryNo} ${field.displayName}`" :placeholder="field.kind === 'manual' ? '人工填写' : '指标缺失'" size="small" /> |
| | | </td></tr></tbody> |
| | | </table></div> |