feat: 增加不同地区各渠道收入占比图

This commit is contained in:
R524809
2026-09-08 16:53:44 +08:00
parent 44c6437b64
commit bab02e1efd
25 changed files with 888 additions and 29 deletions
+61
View File
@@ -291,6 +291,67 @@ export function barSeries({ name, data, color, barWidth, formatter, ...rest }) {
};
}
/**
* 归一化堆叠占比图的矮段标签外置偏移。
* 段高不足(< minInsidePx 像素)放不下段内标签时,标签标到柱体右侧;
* 同一根柱里有多个矮段时,柱侧标签自下而上按 gap 像素扇形排开避免重叠
* (先例:ppmt_channel_revenue 的 fanOutsideLabels、ppmt_region_share 的柱侧标签)。
* 返回 { 行序号: { 系列序号: dy } },dy 为 ECharts label.offset 的纵向偏移。
*/
export function fanTinyLabelOffsets(rows, seriesKeys, pxPerUnit, { minInsidePx = 15, gap = 13 } = {}) {
return rows.map((row) => {
const centers = [];
let cum = 0;
seriesKeys.forEach((key) => {
const value = Number.isFinite(row[key]) ? row[key] : 0;
centers.push((cum + value / 2) * pxPerUnit);
cum += value;
});
const tinyIdx = seriesKeys
.map((_, index) => index)
.filter((index) => Number.isFinite(row[seriesKeys[index]])
&& row[seriesKeys[index]] > 0
&& row[seriesKeys[index]] * pxPerUnit < minInsidePx);
if (tinyIdx.length === 0) return {};
const ys = tinyIdx.map((index) => centers[index]);
for (let k = 1; k < ys.length; k += 1) ys[k] = Math.max(ys[k], ys[k - 1] + gap);
if (tinyIdx.length === seriesKeys.length) {
// 整根柱都是矮段:扇形整体对中到柱身中部,避免标签飘离柱顶
const shift = cum * pxPerUnit / 2 - (ys[0] + ys[ys.length - 1]) / 2;
for (let k = 0; k < ys.length; k += 1) ys[k] += shift;
if (ys[0] < 2) {
for (let k = 0; k < ys.length; k += 1) ys[k] += 2 - ys[0];
}
}
const offsets = {};
tinyIdx.forEach((index, k) => { offsets[index] = centers[index] - ys[k]; });
return offsets;
});
}
/**
* 归一化堆叠占比图的逐点标签:段高够时段内显示(用系列的段内配色),
* 矮段外置到柱体右侧(深字白描边),保证每个分类的占比数值都可见。
* 必须配合 fanTinyLabelOffsets 计算的 dy 使用;value 为空或 0 时不渲染该段。
*/
export function stackedSharePoint(value, { pxPerUnit, labelColor, dy = 0, minInsidePx = 15 }) {
if (!Number.isFinite(value) || value === 0) return null;
if (value * pxPerUnit >= minInsidePx) {
return { value, label: { position: 'inside', color: labelColor } };
}
return {
value,
label: {
position: 'right',
color: PALETTE.ink,
distance: 3,
offset: [0, dy],
textBorderColor: '#fff',
textBorderWidth: 2,
},
};
}
/** 底部注释 */
export function footnote(text) {
return {