import { request, type ApiResponse } from './request'
|
export interface SpliceSelection { testId: number; testBatteryId: number }
|
export interface SpliceCandidate { selection: SpliceSelection; name: string; type: string; groupId: number; group: string; batteryNo: string; startedAt: number }
|
export interface SpliceField { name: string; category: 'battery' | 'special'; displayName: string; unit: string; fieldDefinitionId?: number | null }
|
export interface SpliceBusinessField { fieldDefinitionId: number; fieldName: string; fieldValue: string | null }
|
export interface SpliceSegment { source: SpliceCandidate; offset: number; originalCycleNos: number[]; statistics: Record<string, Record<string, number | null>>; businessFields?: SpliceBusinessField[]; consoleDetail?: { experimentName?: string; batteryFields: string[]; specialFields: string[]; fieldValues: Record<string, string> } }
|
export interface SpliceMatrix { segments: SpliceSegment[]; cycleNos: number[] }
|
export interface SpliceSettings { sourceExperimentNames?: Record<string,string>; hiddenSeries?: boolean; fieldStructure?: SpliceField[]; statisticValues?: Record<string, string>; batteryFields?: string[]; specialFields?: string[]; detailFieldValues?: Record<string, Record<string, string>>; metrics?: string[]; seriesColors?: string[] }
|
export interface SpliceSummary { id: number; name: string; creator: string; createdAt: number }
|
export interface SavedSpliceReport extends SpliceSummary { spliceMode?: 'battery-segments'; description?: string; selections: SpliceSelection[]; segments: SpliceSegment[]; cycleNos: number[]; settings: SpliceSettings; versionNo: number; operatorName: string; versionCreatedAt: number; changeSummary: string }
|
const root='/system/splice-report'
|
async function data<T>(path:string, method='GET', body?:unknown){return (await request<ApiResponse & {data:T}>(`${root}${path}`,{method,...(body===undefined?{}:{body:JSON.stringify(body)})})).data}
|
export function listSpliceReports(){return request<ApiResponse & {rows:SpliceSummary[]}>(`${root}/list`).then(r=>r.rows)}
|
export function getSpliceSources(){return data<SpliceCandidate[]>('/sources')}
|
export function previewSpliceReport(s:SpliceSelection[]){return data<SpliceMatrix>('/preview','POST',s)}
|
export function createSpliceReport(body:Record<string, unknown>){return data<SavedSpliceReport>('','POST',body)}
|
export function getSpliceReport(id:number, editing=false){return data<SavedSpliceReport>(`/${id}${editing?'/editing':''}`)}
|
export function getSpliceVersion(id:number,versionNo:number){return data<SavedSpliceReport>(`/${id}/versions/${versionNo}`)}
|
export function getSplicePrintVersion(id:number,versionNo:number){return data<SavedSpliceReport>(`/${id}/versions/${versionNo}/print`)}
|
export function editSpliceReport(id:number,body:Record<string, unknown>){return data<SavedSpliceReport>(`/${id}/versions`,'POST',body)}
|
export function deleteSpliceReport(id:number){return data<void>(`/${id}`,'DELETE')}
|
export function listSpliceVersions(id:number){return data<Array<{versionNo:number;operatorName:string;createdAt:number;changeSummary:string}>>(`/${id}/versions`)}
|
export function refreshSpliceSource(id:number,baseVersionNo:number,changeSummary:string){return data<SavedSpliceReport>(`/${id}/refresh-source`,'POST',{baseVersionNo,changeSummary})}
|
export interface SpliceTemplate { id: number; name: string; type: string; fields: SpliceField[]; creator: string; updatedAt: number }
|
export function listSpliceTemplates(){return data<SpliceTemplate[]>('/templates')}
|
export function createSpliceTemplate(name:string,fields:SpliceField[]){return data<number>('/templates','POST',{name,fields})}
|
export function updateSpliceTemplate(id:number,name:string,fields:SpliceField[]){return data<void>(`/templates/${id}`,'PUT',{name,fields})}
|
|
/** 优先读取冻结字段结构;早期版本仅有字段名时保留原展示。 */
|
export function spliceFields(settings: SpliceSettings): SpliceField[] {
|
return settings.fieldStructure ?? [
|
...(settings.batteryFields ?? []).map(name => ({ name, category: 'battery' as const, displayName: name, unit: '' })),
|
...(settings.specialFields ?? []).map(name => ({ name, category: 'special' as const, displayName: name, unit: '' })),
|
]
|
}
|
/** 人工填写优先;未填写时取首个来源的冻结共享字段,空字符串仍视为人工值。 */
|
export function spliceFieldValue(segments: SpliceSegment[], settings: SpliceSettings, field: SpliceField, battery: string) {
|
const manual = settings.detailFieldValues?.[field.name]?.[battery]
|
if (manual !== undefined) return manual
|
const business = segments.find(segment => segment.source.batteryNo === battery)?.businessFields?.find(value =>
|
field.fieldDefinitionId ? field.fieldDefinitionId === value.fieldDefinitionId : field.name === value.fieldName)
|
return field.category === 'battery' ? business?.fieldValue ?? '' : ''
|
}
|
/** 新版列身份包含测试与测试电池;空字符串不回退为组字段或其他列。 */
|
export function spliceSourceKey(source: SpliceCandidate) { return `${source.selection.testId}|${source.selection.testBatteryId}` }
|
export function spliceSourceValue(segment: SpliceSegment, settings: SpliceSettings, field: SpliceField) {
|
const manual = settings.detailFieldValues?.[field.name]?.[spliceSourceKey(segment.source)]
|
if (manual !== undefined) return manual
|
// 控制台详情只有名称,没有共享字段身份;模板字段一旦带 ID,必须按 ID 取组值,不能被同名详情冒认。
|
if (field.fieldDefinitionId) {
|
return segment.businessFields?.find(value => value.fieldDefinitionId === field.fieldDefinitionId)?.fieldValue ?? ''
|
}
|
const detail = segment.consoleDetail?.fieldValues[field.name]
|
if (detail !== undefined) return detail
|
if (segment.consoleDetail?.batteryFields.includes(field.name) || segment.consoleDetail?.specialFields.includes(field.name)) return ''
|
return ''
|
}
|
export function spliceExperiment(segment: SpliceSegment, settings: SpliceSettings) {
|
return settings.sourceExperimentNames?.[spliceSourceKey(segment.source)] ?? segment.consoleDetail?.experimentName ?? ''
|
}
|
/** 人工统计绑定测试电池和原循环身份,来源重排不改变归属。 */
|
export function spliceStatisticKey(segment: SpliceSegment, cycle: number, metric: string) {
|
return `${segment.source.selection.testId}|${segment.source.selection.testBatteryId}|${cycle}|${metric}`
|
}
|