yeg
2026-09-07 5352180db89ff65c481995334a1fdc5c9fe1d3b8
展示化成充电工步终止电压
2个文件已修改
33 ■■■■■ 已修改文件
src/api/formationReports.ts 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/FormationReportForm.vue 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/formationReports.ts
@@ -3,15 +3,17 @@
export interface FormationSelection { testId: number; testBatteryId: number }
export interface FormationCandidate { selection: FormationSelection; name: string; type: string; groupId: number; batteryGroup: string; batteryNo: string }
export interface FormationField { key: string; scope: 'detail' | 'cycle'; kind: 'battery' | 'metric' | 'manual'; displayName: string; unit: string; fieldDefinitionId?: number | null }
export interface FormationChargeStep { stepNo: number; name: string; stepType: string; endVoltage: number | null }
export interface FormationItem {
  source: FormationCandidate
  cycleNos: number[]
  cycles: Array<Record<string, unknown>>
  metrics: Record<string, Record<string, number | null>>
  businessFields: Array<{ fieldDefinitionId: number; fieldName: string; fieldValue: string | null }>
  chargeSteps: Record<string, FormationChargeStep[]>
}
export interface FormationMatrix { items: FormationItem[]; cycleNos: number[] }
export interface FormationSettings { fields: FormationField[]; values: Record<string, string> }
export interface FormationSettings { fields: FormationField[]; values: Record<string, string>; chargeStepSelections: Record<string, number> }
export interface FormationSummary { id: number; name: string; creator: string; createdAt: number; testName?: string; batteryGroup?: string }
export interface FormationVersion { versionNo: number; operatorName: string; createdAt: number; changeSummary: string }
export interface SavedFormationReport extends FormationSummary, FormationMatrix {
src/views/FormationReportForm.vue
@@ -36,6 +36,7 @@
const cycleNos = ref<number[]>([])
const fields = ref<FormationField[]>(structuredClone(defaultFormationFields))
const values = ref<Record<string, string>>({})
const chargeStepSelections = ref<Record<string, number>>({})
const detailFields = computed(() => fields.value.filter(field => field.scope === 'detail'))
const cycleFields = computed(() => fields.value.filter(field => field.scope === 'cycle'))
const reportColumns = computed(() => [...new Set(items.value.map(item => item.source.selection.testId))].map(id => ({
@@ -51,11 +52,18 @@
function setValue(item: FormationItem, cycle: number, field: FormationField, value: string) {
  values.value[formationValueKey(item, cycle, field)] = value
}
function formContent() { return JSON.stringify({ name: reportName.value, description: description.value, fields: fields.value, values: values.value }) }
function chargeChoices(item: FormationItem, cycle: number) { return item.chargeSteps?.[String(cycle)] || [] }
function chargeChoice(item: FormationItem, cycle: number) {
  const choices = chargeChoices(item, cycle)
  const reliable = choices.filter(step => ['cc', 'charge', 'charging'].includes((step.stepType || '').toLowerCase()))
  return reliable.length === 1 ? reliable[0] : choices.find(step => step.stepNo === chargeStepSelections.value[`${item.source.selection.testId}|${item.source.selection.testBatteryId}|${cycle}`])
}
function chargeKey(item: FormationItem, cycle: number) { return `${item.source.selection.testId}|${item.source.selection.testBatteryId}|${cycle}` }
function formContent() { return JSON.stringify({ name: reportName.value, description: description.value, fields: fields.value, values: values.value, chargeStepSelections: chargeStepSelections.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
  fields.value = record.settings.fields; values.value = record.settings.values; chargeStepSelections.value = record.settings.chargeStepSelections || {}
  changeSummary.value = ''; savedContent.value = formContent()
}
function pruneValues() {
@@ -81,7 +89,7 @@
  saving.value = true
  try {
    pruneValues()
    const content = { name: reportName.value.trim(), description: description.value, settings: { fields: fields.value, values: values.value } }
  const content = { name: reportName.value.trim(), description: description.value, settings: { fields: fields.value, values: values.value, chargeStepSelections: chargeStepSelections.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) })
@@ -130,6 +138,12 @@
    if (sequence !== previewSequence) return
    items.value = result.items
    cycleNos.value = result.cycleNos
    for (const item of items.value) for (const cycle of item.cycleNos) {
      const key = `${item.source.selection.testId}|${item.source.selection.testBatteryId}|${cycle}`
      const choices = item.chargeSteps?.[String(cycle)] || []
      const reliable = choices.filter(step => ['cc', 'charge', 'charging'].includes((step.stepType || '').toLowerCase()))
      if (reliable.length === 1) chargeStepSelections.value[key] = reliable[0].stepNo
    }
    fields.value = fields.value.map(field => field.kind === 'battery' && field.fieldDefinitionId == null
      ? { ...field, fieldDefinitionId: items.value.flatMap(item => item.businessFields).find(value => value.fieldName === field.key)?.fieldDefinitionId ?? null } : field)
    pruneValues()
@@ -142,14 +156,14 @@
async function load() {
  loading.value = true; loadError.value = ''
  previewSequence++; previewLoading.value = false; previewError.value = ''
  saved.value = null; items.value = []; cycleNos.value = []; values.value = {}; selectedKeys.value = []
  saved.value = null; items.value = []; cycleNos.value = []; values.value = {}; chargeStepSelections.value = {}; selectedKeys.value = []
  try {
    const permissions = (await getUserInfo()).permissions || []
    const has = (action: string) => permissions.includes('*:*:*') || permissions.includes(`system:formation-report:${action}`)
    canCreate.value = has('add'); canEdit.value = has('edit'); canQuery.value = has('query')
    if (!editingId.value) {
      if (!canCreate.value) throw new Error('无新增化成报表权限')
      reportName.value = ''; description.value = ''; fields.value = structuredClone(defaultFormationFields)
      reportName.value = ''; description.value = ''; fields.value = structuredClone(defaultFormationFields); chargeStepSelections.value = {}
      candidates.value = await getFormationSources()
    } else {
      const editing = route.query.mode === 'edit' && !route.query.version
@@ -196,7 +210,10 @@
            <div class="cycle-table-wrap"><table class="cycle-table cycle-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 cycleFields" :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)"><template v-if="item.cycleNos.includes(cycle)"><el-select v-if="chargeChoices(item, cycle).length && !chargeChoice(item, cycle)" :model-value="chargeStepSelections[chargeKey(item, cycle)]" :disabled="readonly" placeholder="选择工步" size="small" @update:model-value="chargeStepSelections[chargeKey(item, cycle)] = $event"><el-option v-for="step in chargeChoices(item, cycle)" :key="step.stepNo" :label="`${step.stepNo} ${step.name || ''}`" :value="step.stepNo" /></el-select><span v-else>{{ chargeChoice(item, cycle)?.stepNo ?? '未识别' }}</span></template><span v-else class="missing-cycle">无该循环</span></td></tr>
                <tr><td>充电工步名称</td><td v-for="item in reportItems" :key="selectionKey(item.source)"><span v-if="!item.cycleNos.includes(cycle)" class="missing-cycle">无该循环</span><span v-else>{{ chargeChoice(item, cycle)?.name || '未选择' }}</span></td></tr>
                <tr><td>终止电压 (V)</td><td v-for="item in reportItems" :key="selectionKey(item.source)"><span v-if="!item.cycleNos.includes(cycle)" class="missing-cycle">无该循环</span><span v-else>{{ chargeChoice(item, cycle)?.endVoltage ?? '缺失' }}</span></td></tr>
                <tr v-for="field in cycleFields" :key="field.key"><td>{{ formationFieldLabel(field) }}</td><td v-for="item in reportItems" :key="selectionKey(item.source)">
                <span v-if="!item.cycleNos.includes(cycle)" class="missing-cycle">无该循环</span>
                <el-input v-else :model-value="inputValue(item, cycle, field)" @update:model-value="setValue(item, cycle, field, $event)" :disabled="readonly" :aria-label="`${item.source.name} ${item.source.batteryNo} 循环${cycle} ${field.displayName}`" :placeholder="field.kind === 'manual' ? '人工填写' : '指标缺失'" size="small" />
              </td></tr></tbody>