feat: 增加不同地区各渠道收入占比图
This commit is contained in:
+61
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user