yeg
2026-09-07 63edfe987ecbbe7442ddb1314bd0f7beada49edf
完成拼接展示打印注释整理并记录验收关闭
3个文件已修改
7 ■■■■■ 已修改文件
docs/pages/report-splice.md 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/spliceReports.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/spliceSavedReport.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/pages/report-splice.md
@@ -3,7 +3,7 @@
- 列表:`/report-splice`;新增:`/report-splice/new`;编辑:`/report-splice/:id/edit`。
- 查看使用 `?mode=view`,指定历史版本使用 `?mode=view&version=N`。
- 正式数据来自 `/system/splice-report`;模板来自所属类型的 `/templates` 接口,不使用本地种子业务记录。
- 对应数据分析 Ticket11–13;本轮实现及自动验证完成后仍须用户重新验收,不能沿用已撤回的 Ticket11 关闭结论。
- 对应数据分析 Ticket11–13;2026-09-07 用户重新验收通过,验收后注释整理及复验完成,均已关闭;此前撤回的 Ticket11 关闭记录仅作历史追溯。
## 来源与统计
src/api/spliceReports.ts
@@ -26,12 +26,14 @@
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
@@ -39,6 +41,7 @@
    field.fieldDefinitionId ? field.fieldDefinitionId === value.fieldDefinitionId : field.name === value.fieldName)
  return field.category === 'battery' ? business?.fieldValue ?? '' : ''
}
/** 人工统计绑定测试电池和原循环身份,来源重排不改变归属。 */
export function spliceStatisticKey(segment: SpliceSegment, cycle: number, metric: string) {
  return `${segment.source.selection.testId}|${segment.source.selection.testBatteryId}|${cycle}|${metric}`
}
src/utils/spliceSavedReport.ts
@@ -57,6 +57,7 @@
      const group = batteries.slice(batteryStart, batteryStart + 4)
      const segments = record.segments.filter(segment => group.includes(segment.source.batteryNo) && segment.originalCycleNos.some(no => range.includes(segment.offset + no)))
      const boundaries = sources.flatMap(testId => {
        // 各电池可能缺少段首循环,来源线取本图参与电池的最早循环,跨图时限定在图内。
        const starts = segments.filter(segment => segment.source.selection.testId === testId)
          .map(segment => segment.offset + segment.originalCycleNos[0]!)
        return starts.length ? [{ name: label(testId), xAxis: Math.max(range[0]!, Math.min(...starts)) }] : []
@@ -71,6 +72,7 @@
            xAxis: { type: 'value', min: range[0]! - 0.5, max: range.at(-1)! + 0.5, minInterval: 1, name: '拼接循环号', nameLocation: 'middle', nameGap: 28, axisLabel: { hideOverlap: true, formatter: (value: number) => Number.isInteger(value) ? String(value) : '' } },
            yAxis: { type: 'value', scale: true, name: metric.unit },
            series: group.map((battery, index) => ({ name: battery, type: 'line', connectNulls: false, showSymbol: true, symbolSize: 5, itemStyle: { color: color(battery) },
              // 人工调整仅作用于表格,曲线始终保留冻结的计算原值。
              data: segments.filter(segment => segment.source.batteryNo === battery).flatMap(segment => segment.originalCycleNos.filter(no => range.includes(segment.offset + no)).map(no => [segment.offset + no, numeric(segment.statistics[String(no)]?.[metric.key])])),
              markLine: index === 0 ? { silent: true, symbol: 'none', lineStyle: { color: '#82909b', type: 'dashed' }, label: { formatter: '{b}', position: 'end' }, data: boundaries } : undefined,
            })),