import { FONT, PALETTE, categoryAxis, footnote, titleStyle, watermarks, } from '../theme.js'; const REGION_ORDER = ['中国', '海外', '亚太', '美洲', '欧洲及其他']; const CHANNEL_ORDER = ['线下渠道', '线上渠道', '批发及其他']; function orderedValues(values, order) { return [...values].sort((a, b) => { const aIndex = order.indexOf(a); const bIndex = order.indexOf(b); return (aIndex === -1 ? order.length : aIndex) - (bIndex === -1 ? order.length : bIndex) || a.localeCompare(b, 'zh-CN'); }); } function orderedPeriods(rows) { return [...new Set(rows.map((row) => row.period))].sort((a, b) => { const [yearA, halfA] = String(a).match(/(\d{4})H(\d)/).slice(1).map(Number); const [yearB, halfB] = String(b).match(/(\d{4})H(\d)/).slice(1).map(Number); return yearA - yearB || halfA - halfB; }); } /** 泡泡玛特区域 × 渠道同比增速热力图。 */ export function regionChannelYoyTimeseriesChart(rows, { width, height }) { const periods = orderedPeriods(rows); const regions = orderedValues(new Set(rows.map((row) => row.region)), REGION_ORDER); const channels = orderedValues(new Set(rows.map((row) => row.channel)), CHANNEL_ORDER); const dimensions = regions.flatMap((region) => channels.map((channel) => ({ region, channel }))); const periodIndex = new Map(periods.map((period, index) => [period, index])); const dimensionIndex = new Map(dimensions.map(({ region, channel }, index) => [`${region}_${channel}`, index])); const data = rows.map((row) => [ periodIndex.get(row.period), dimensionIndex.get(`${row.region}_${row.channel}`), row.yoy, ]); return { backgroundColor: '#fff', title: { ...titleStyle, text: '泡泡玛特区域渠道同比增速趋势', subtext: '颜色越深代表同比变化幅度越大;空白表示当期未单列披露', }, tooltip: { position: 'top', textStyle: { fontFamily: FONT }, formatter: (params) => { const [periodIndexValue, dimensionIndexValue, value] = params.value; const { region, channel } = dimensions[dimensionIndexValue]; const sign = value >= 0 ? '+' : ''; return `${periods[periodIndexValue]}
${region} · ${channel}
同比:${sign}${value.toFixed(1)}%`; }, }, grid: { left: 178, right: 42, top: 112, // 底部依次预留:横轴标签 → visualMap 控制器 → 数据来源。 bottom: 155, }, xAxis: { ...categoryAxis, data: periods, axisLabel: { ...categoryAxis.axisLabel, formatter: (value) => String(value).replace(/^20/, ''), }, }, yAxis: { ...categoryAxis, type: 'category', inverse: true, data: dimensions.map(({ region, channel }) => `${region} · ${channel}`), axisLabel: { ...categoryAxis.axisLabel, fontSize: 12, margin: 12, }, }, visualMap: { min: -100, max: 200, calculable: true, orient: 'horizontal', left: 'center', bottom: 64, text: ['较高增长', '较低增长'], textStyle: { color: PALETTE.grey, fontFamily: FONT, fontSize: 12 }, // 红色系代表较高增长;两端均采用浅色,保证数值标签清晰。 inRange: { color: ['#9cc4d0', '#f3eee6', PALETTE.coral] }, }, series: [{ name: '同比增速', type: 'heatmap', data, label: { show: true, fontFamily: FONT, fontSize: 11, color: PALETTE.ink, formatter: (params) => { const value = params.value[2]; return `${value >= 0 ? '+' : ''}${value.toFixed(0)}%`; }, }, emphasis: { itemStyle: { borderColor: PALETTE.ink, borderWidth: 1 }, }, }], graphic: [ ...watermarks(width, height), footnote('数据来源:泡泡玛特财报;早期海外为合并口径 | @思考的Joey'), ], }; }