import type { FormationField, FormationItem, FormationSettings } from '@/api/formationReports'
|
|
export interface FormationResult { value: number | null; reason: string }
|
export const formationInputKeys = ['Z', 'H', 'F', 'A', 'B']
|
export const formationCalculationKeys = ['FN', 'C', 'chargePerGram', 'chargePerGramTotal', 'capacity1v', 'capacity1vf', 'dischargeInflection', 'dischargeInflectionTotal']
|
export function formationCellKey(item: FormationItem, cycle: number, key: string) {
|
return `${item.source.selection.testId}|${item.source.selection.testBatteryId}|${cycle}|${key}`
|
}
|
/** 报表人工值优先,即使是空字符串也不能回退电池组;基础字段始终按来源列隔离。 */
|
export function formationInputValue(item: FormationItem, field: FormationField, values: Record<string, string>, cycle = 0) {
|
const key = formationCellKey(item, cycle, field.key)
|
if (Object.hasOwn(values, key)) return values[key]!
|
if (field.kind !== 'battery') return ''
|
return item.businessFields.find(value => field.fieldDefinitionId != null
|
? value.fieldDefinitionId === field.fieldDefinitionId : value.fieldName === field.key)?.fieldValue ?? ''
|
}
|
const result = (value: number | null, reason = ''): FormationResult => value != null && !Number.isFinite(value)
|
? { value: null, reason: '计算结果超出有效数值范围' } : { value, reason }
|
function number(value: string, label: string) {
|
return /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/.test(value.trim()) ? result(Number(value)) : result(null, `${label}为空或不是数字`)
|
}
|
function multiply(...values: FormationResult[]): FormationResult {
|
const missing = values.find(value => value.value == null)
|
return missing ?? result(values.reduce((product, value) => product * value.value!, 1))
|
}
|
function divide(value: FormationResult, by: FormationResult, label: string): FormationResult {
|
if (value.value == null) return value
|
if (by.value == null) return by
|
return by.value === 0 ? result(null, `${label}不能为0`) : result(value.value / by.value)
|
}
|
/** 页面预览与编辑按已确认公式联动,保存时由后端用相同快照重新计算并冻结。 */
|
export function formationCalculatedValue(item: FormationItem, cycle: number, key: string, settings: Pick<FormationSettings, 'fields' | 'values'>): FormationResult {
|
const read = (name: string) => {
|
const field = settings.fields.find(field => field.scope === 'detail' && field.key === name)
|
?? {key:name,scope:'detail',kind:'battery',displayName:name,unit:''} as FormationField
|
return number(formationInputValue(item, field, settings.values), name)
|
}
|
const h = read('H'), z = read('Z'), a = read('A'), f = read('F'), b = read('B')
|
const c = multiply(h, z, a, f, b)
|
if (key === 'FN') return divide(divide(f, h, 'H'), z, 'Z')
|
if (key === 'C') return c
|
if (!item.cycleNos.includes(cycle)) return result(null, '无该循环')
|
const manual = number(settings.values[formationCellKey(item, cycle, 'manual')] ?? '', '数字')
|
if (manual.value != null && manual.value < 0) { manual.value = null; manual.reason = '数字不能为负数' }
|
if (key === 'chargePerGram') return divide(divide(multiply(manual, c), h, 'H'), z, 'Z')
|
if (key === 'chargePerGramTotal') return divide(multiply(manual, c), f, 'F')
|
const last = [...(item.chargeSteps?.[cycle] ?? [])].filter(step => step.direction === 'DISCHARGE').sort((a,b)=>a.stepNo-b.stepNo).at(-1)
|
const capacity = result(last?.capacity == null ? null : Number(last.capacity) * 1000, '最后一个放电工步容量缺失')
|
if (key === 'capacity1v') return capacity
|
if (key === 'capacity1vf') return divide(capacity, f, 'F')
|
// 仅这两个指标是用户指定的演示占位,页面和保存版使用固定值。
|
if (key === 'dischargeInflection') return result(3.934, '演示占位')
|
if (key === 'dischargeInflectionTotal') return result(3.982, '演示占位')
|
return result(null, '指标未定义')
|
}
|
export function formationNumberText(value: number | null | undefined) {
|
return value == null || !Number.isFinite(Number(value)) ? '—' : String(Number(Number(value).toPrecision(12)))
|
}
|