yeg
2026-09-06 6163832178a3f36df323374c283a694fea334691
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import type { CompareItem } from '@/api/compareReports'
import type { PersistedTestRecord } from '@/api/tests'
import type { CompareMetricKey } from '@/stores/compareReports'
import { toTestDisplayValue } from './testDataUnits'
 
/** 完整所选范围参与图例身份,避免同名电池的不同循环或工步混淆。 */
export function compareSeriesName(item: CompareItem) {
  const source = item.source
  return `${source.name} · ${source.batteryNo} · 循环${source.selection.cycleNo} · ${source.process}`
}
 
/** 页面和打印共用测量、时间及单位口径;不连接缺失值,也不限制曲线条数。 */
export function compareChartOption(metric: { key: CompareMetricKey; unit: string }, items: CompareItem[], colors: string[]) {
  const field: Record<CompareMetricKey, keyof PersistedTestRecord> = { voltage: 'voltage', current: 'current', positiveVoltage: 'positivePotential', negativeVoltage: 'negativePotential', temperature: 'temperature' }
  const series = items.map(item => item.records.flatMap(row => {
    // 柜与采集器分别使用自己的设备时间,共享已冻结的比对项零点。
    const time = metric.key === 'voltage' || metric.key === 'current' ? row.deviceTime : row.collectorDeviceTime
    if (time == null || item.zeroTime == null) return []
    const value = row[field[metric.key]]
    return [[(Number(time) - item.zeroTime) / 1000, value == null ? null : toTestDisplayValue(metric.key, Number(value))] as [number, number | null]]
  }).sort((left, right) => left[0] - right[0]))
  const values = series.flat().map(point => point[1]).filter((value): value is number => value !== null)
  const dataMin = Math.min(0, ...values)
  const dataMax = Math.max(0, ...values)
  const range = Math.max(dataMax - dataMin, 1)
  const axisMax = Math.max(metric.unit === '℃' ? Math.ceil(dataMax / 10) * 10 : Math.ceil(dataMax * 10) / 10, 1)
  const yAxis = (position: 'left' | 'right') => ({
    type: 'value', name: metric.unit, position, min: dataMin - range * 0.05, max: axisMax,
    nameTextStyle: { color: '#8795a3', fontSize: 10 },
    axisLine: { show: true, lineStyle: { color: '#cbd8e4' } },
    axisTick: { show: true, lineStyle: { color: '#cbd8e4' } },
    splitLine: { show: position === 'left', lineStyle: { color: '#e8eef4' } },
    axisLabel: { color: '#8795a3', fontSize: 10 },
  })
  return {
    animation: false, color: colors, grid: { top: 40, right: 46, bottom: 42, left: 46 },
    tooltip: { trigger: 'axis' }, legend: { show: false },
    xAxis: { type: 'value', min: 0, name: '相对时间 / s', nameLocation: 'middle', nameGap: 28,
      axisLine: { lineStyle: { color: '#cbd8e4' } }, axisLabel: { color: '#8795a3', fontSize: 10 },
      nameTextStyle: { color: '#26333f', fontSize: 11, fontWeight: 600 } },
    yAxis: [yAxis('left'), yAxis('right')],
    series: items.map((item, index) => ({ name: compareSeriesName(item), type: 'line', smooth: false,
      connectNulls: false, showSymbol: true, symbolSize: 5, lineStyle: { width: 2 }, data: series[index] || [] })),
  }
}