feat(admin): apply shared blue-white self-operated workbench style

This commit is contained in:
2026-10-20 19:16:47 +08:00
parent 9bfce44988
commit 464b72cff9
38 changed files with 1851 additions and 340 deletions
@@ -1,4 +1,6 @@
import { describe, expect, it } from 'vitest'
import { describe, expect, it, vi } from 'vitest'
import type { BarSeriesOption } from 'echarts'
import { adminWorkbenchChartTheme } from '@/styles/adminWorkbenchChartTheme'
import { turnoverChart, turnoverNumber, volumeText, windowText, turnoverReasonsText, volumeNote } from './turnoverPresentation'
import type { TurnoverCharts, TurnoverWindowMetric } from './turnoverTypes'
describe('turnover honest precision and charts', () => {
@@ -35,4 +37,51 @@ describe('turnover honest precision and charts', () => {
expect(JSON.stringify(option.series)).toContain('"data":[10,null]')
expect(windowText(partial)).toBe('已采集 150,覆盖不足')
})
it.each(['owners', 'volume', 'daily'] as const)('applies the workbench theme to %s without adding artificial bar lengths', kind => {
const charts: TurnoverCharts = { ownerPhysicalTop: [], unknownOwnerCount: 0, ownerVolumeTop: [], unknownVolumeOwnerCount: 0, averageDaily: [] }
const option = turnoverChart(charts, kind)
expect(option.textStyle).toMatchObject({ color: adminWorkbenchChartTheme.colors.text, fontFamily: adminWorkbenchChartTheme.fontFamily })
expect(option.tooltip).toMatchObject({ backgroundColor: adminWorkbenchChartTheme.colors.surface, borderColor: adminWorkbenchChartTheme.colors.border, renderMode: 'richText' })
expect(kind === 'daily' ? option.yAxis : option.xAxis).toMatchObject({ type: 'value', min: 0, axisLabel: adminWorkbenchChartTheme.axisLabel, splitLine: adminWorkbenchChartTheme.splitLine })
const series = (option.series as BarSeriesOption[])[0]!
expect(series.itemStyle).toMatchObject({ color: adminWorkbenchChartTheme.colors.primary, borderRadius: kind === 'daily' ? [6, 6, 0, 0] : [0, 6, 6, 0] })
expect(series.data).toEqual([])
expect(series).not.toHaveProperty('showBackground')
expect(series).not.toHaveProperty('barMinHeight')
})
it('preserves owner ordering, volume precision, zero and unknown without changing click indexes', () => {
const volume = { value: null, knownSubtotal: null, missingDimensionQuantity: null, missingDimensionSkuCount: 0, unknownQuantitySkuCount: 0, declaredDimensionQuantity: 0, reasonCodes: [] }
const values = ['12345.678901', '0', null, '0.000001']
const charts: TurnoverCharts = {
ownerPhysicalTop: values.map((physicalQuantity, index) => ({ customerCode: `OWNER-${index}`, customerName: index === 0 ? '货主名称' : null, physicalQuantity })),
ownerVolumeTop: values.map((value, index) => ({ customerCode: `OWNER-${index}`, customerName: index === 0 ? '货主名称' : null, inventoryVolumeM3: { ...volume, value } })),
unknownOwnerCount: 1, unknownVolumeOwnerCount: 1, averageDaily: [],
}
for (const kind of ['owners', 'volume'] as const) {
const option = turnoverChart(charts, kind)
expect(option.yAxis).toMatchObject({ inverse: true, data: ['货主名称', 'OWNER-1', 'OWNER-2', 'OWNER-3'] })
expect((option.series as BarSeriesOption[])[0]!.data).toEqual([12345.678901, 0, null, 0.000001])
}
const option = turnoverChart(charts, 'volume')
expect(option.grid).toMatchObject({ right: 110 })
const label = (option.series as BarSeriesOption[])[0]!.label as { formatter: (params: { value: number | null }) => string }
const tooltip = option.tooltip as { valueFormatter: (value: number | null) => string }
expect(label.formatter({ value: 12345.678901 })).toBe('12,345.68 m³')
expect(label.formatter({ value: 0.000001 })).toBe('<0.01 m³')
expect(tooltip.valueFormatter(12345.678901)).toBe('12,345.68 m³')
expect(tooltip.valueFormatter(null)).toBe('— m³')
})
it('continues to honor reduced-motion preference', () => {
const charts: TurnoverCharts = { ownerPhysicalTop: [], unknownOwnerCount: 0, ownerVolumeTop: [], unknownVolumeOwnerCount: 0, averageDaily: [] }
const matchMedia = vi.fn(() => ({ matches: true }))
vi.stubGlobal('window', { matchMedia })
try {
expect(turnoverChart(charts, 'owners').animation).toBe(false)
expect(matchMedia).toHaveBeenCalledWith('(prefers-reduced-motion: reduce)')
matchMedia.mockReturnValue({ matches: false })
expect(turnoverChart(charts, 'owners').animation).toBe(true)
} finally {
vi.unstubAllGlobals()
}
})
})