yeg
2026-09-07 4780b1c1627e1588a5c09fd36c402c003ca01ca7
接入对比报表字段模板

通过正式 API 加载、保存和应用字段模板;模板只包含字段结构,不携带来源、测量或人工填写值。
2个文件已修改
31 ■■■■■ 已修改文件
src/api/compareReports.ts 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/ReportCompareForm.vue 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/compareReports.ts
@@ -68,6 +68,13 @@
  units: Record<string, string>
  displayUnits: Record<string, string>
}
export interface CompareFieldTemplate { id: number; name: string; type: string; fields: Array<{ key?: string; name: string; unit?: string; category?: string }>; creator: string; updatedAt: number }
export async function listCompareFieldTemplates() {
  return (await request<ApiResponse & { data: CompareFieldTemplate[] }>('/system/compare-report/templates')).data
}
export async function createCompareFieldTemplate(name: string, fields: CompareFieldTemplate['fields']) {
  return (await request<ApiResponse & { id: number }>('/system/compare-report/templates', { method: 'POST', body: JSON.stringify({ name, fields }) })).id
}
/** 按报表新增权限读取检测完成测试中的可选来源。 */
export async function getCompareSources() {
src/views/ReportCompareForm.vue
@@ -12,6 +12,7 @@
import type { CompareMetricKey, CompareTestSelection } from '@/stores/compareReports'
import { compareSelectionKey, createCompareReport, editCompareReport, getCompareReport, getCompareSources, previewCompareReport, refreshCompareSource,
  type CompareCandidate, type CompareItem, type CompareSelection, type SavedCompareReport } from '@/api/compareReports'
import { listCompareFieldTemplates, createCompareFieldTemplate, type CompareFieldTemplate } from '@/api/compareReports'
import { compareMetricDefinitions as metricDefinitions, openCompareReportPrint } from '@/utils/compareReportPrint'
import { compareChartOption } from '@/utils/compareReportChart'
@@ -76,11 +77,14 @@
const selectedBatteryTemplateFieldIds = ref<number[]>([])
const specialTemplateSelections = reactive<Record<string, string[]>>({})
const persistedTemplates = ref<CompareFieldTemplate[]>([])
const templateLoading = ref(false)
const fieldTemplates = computed<FieldTemplate[]>(() => [
  { key: 'cycle', name: '循环寿命模板', fields: ['容量保持率', '能量效率', '循环次数', '内阻变化'] },
  { key: 'thermal', name: '热特性模板', fields: ['最高温度', '温差', '温升速率'] },
  ...Object.entries(specialTemplateSelections).map(([name, fields]) => ({ key: `saved:${name}`, name, fields })),
  ...persistedTemplates.value.map(item => ({ key: `remote:${item.id}`, name: item.name, fields: item.fields.filter(field => field.category !== 'battery').map(field => field.name) })),
])
const metricVisible = reactive<Record<CompareMetricKey, boolean>>({
@@ -127,6 +131,13 @@
  } finally {
    candidatesLoading.value = false
  }
}
async function loadFieldTemplates() {
  templateLoading.value = true
  try { persistedTemplates.value = await listCompareFieldTemplates() }
  catch (error) { ElMessage.error(error instanceof Error ? error.message : '字段模板加载失败') }
  finally { templateLoading.value = false }
}
function detailKey(item: SelectedTest) {
@@ -219,6 +230,7 @@
      canCreate.value = Boolean(info.permissions?.some(item => item === '*:*:*' || item === 'system:compare-report:add'))
      if (!canCreate.value) throw new Error('无新增对比报表权限')
      await loadCandidates()
      await loadFieldTemplates()
    }
  } catch (error) { if (!cancelled) loadError.value = error instanceof Error ? error.message : '报表加载失败' }
  finally { if (!cancelled) reportLoading.value = false }
@@ -236,6 +248,14 @@
    detailFields.value.forEach(ensureDetailFieldValues)
  } catch (error) { previewError.value = error instanceof Error ? error.message : '曲线预览失败' }
  finally { previewLoading.value = false }
}
async function saveCurrentFieldTemplate() {
  const result = await ElMessageBox.prompt('请输入模板名称', '保存对比字段模板', { inputValidator: value => value.trim() ? true : '请输入模板名称', confirmButtonText: '保存', cancelButtonText: '取消' })
  const name = result.value.trim()
  const fields = [...batteryFields.value.map(name => ({ name, category: 'battery', unit: '' })), ...specialFields.value.map(name => ({ name, category: 'special', unit: '' }))]
  try { await createCompareFieldTemplate(name, fields); await loadFieldTemplates(); ElMessage.success('字段模板已保存') }
  catch (error) { ElMessage.error(error instanceof Error ? error.message : '模板保存失败') }
}
function openAddTest() {
@@ -355,6 +375,8 @@
  } else {
    const nextFields = selectedTemplate.value.fields
    specialFields.value = nextFields.filter(field => !batteryFields.value.includes(field))
    const remote = persistedTemplates.value.find(item => `remote:${item.id}` === selectedTemplateKey.value)
    if (remote) batteryFields.value = remote.fields.filter(field => field.category === 'battery').map(field => field.name)
    detailFields.value.forEach(ensureDetailFieldValues)
  }
  templateDialogVisible.value = false
@@ -534,7 +556,7 @@
        <div v-else class="template-empty">暂无字段,请先到电池组字段管理中添加字段</div>
      </div>
      <el-radio-group v-else v-model="selectedTemplateKey" class="template-options"><el-radio v-for="item in fieldTemplates" :key="item.key" :value="item.key" border>{{ item.name }}({{ item.fields.join('、') }})</el-radio></el-radio-group>
      <template #footer><div v-if="templateMode === 'battery'" class="battery-template-actions"><button class="primary-button" type="button" @click="applyFieldTemplate"><el-icon><Check /></el-icon>确认</button></div><div v-else><button class="secondary-button" type="button" @click="templateDialogVisible = false">取消</button><button class="primary-button" type="button" @click="applyFieldTemplate">应用字段</button></div></template>
      <template #footer><div v-if="templateMode === 'battery'" class="battery-template-actions"><button class="secondary-button" type="button" @click="saveCurrentFieldTemplate" :disabled="templateLoading">保存为模板</button><button class="primary-button" type="button" @click="applyFieldTemplate"><el-icon><Check /></el-icon>确认</button></div><div v-else><button class="secondary-button" type="button" @click="saveCurrentFieldTemplate" :disabled="templateLoading">保存为模板</button><button class="secondary-button" type="button" @click="templateDialogVisible = false">取消</button><button class="primary-button" type="button" @click="applyFieldTemplate">应用字段</button></div></template>
    </el-dialog>
  </div>
</template>