由于官方的demo无法实现跨轴展示效果,故进行了下面的调整,并且实现了各项数据的统一计算,只需传入正负值的datas数据,即可自动区分各项的数据。由于传入的数字可能是小数,所以引入了math.js,防止数据相加时出现精度丢失的问题。我这里的math.js是经过实际需要进行了重新封装的,详情请参考https://www.npmjs.com/package/mathjs
import math from 'utils/math.js';
/*
* 收入支出合计瀑布柱形图
* @param {string} title 标题
* @param {array} labels: X轴labels
* @param {array} datas: 数据,整数为收入,负数为支出
* @param {number} rotate: x轴文本倾斜角度
* @param {number} maxNum: x轴文本最大显示字符数,超出显示省略号
*/
function chartOption(title, labels, datas, rotate = 45, maxNum = 4) {
const dataSum = []; // 透明项的data数据
const dataIn1 = []; // 收入上半段的data数据 (跨象限时)
const dataIn2 = []; // 收入下半段的data数据 (跨象限时)
const dataOut1 = []; // 支出上半段的data数据 (跨象限时)
const dataOut2 = []; // 支出下半段的data数据 (跨象限时)
const dataTotal = []; // 最终金额合计的data数据
const setSumIndex = []; // 有跨象限时值的坐标
const len = datas.length;
// 组装数据
datas.forEach((item, index) => {
if (index === 0) {
dataSum.push(0);
} else if (index < len - 1) {
let num = 0;
for(let i = 0; i < index; i++) {
num = math.add(num, datas[i]);
}
if ((item >= 0 && num >= 0) || (item < 0 && num <= 0)) {
dataSum.push(num);
} else {
num = math.add(num, item);
dataSum.push(num);
}
} else {
dataSum.push(0);
}
if (index === len - 1) {
dataIn1.push('-');
dataIn2.push('-');
dataOut1.push('-');
dataOut2.push('-');
dataTotal.push(item);
} else if (item >= 0) {
// 当前起始位置
let beginNum = 0;
for(let i = 0; i < index; i++) {
beginNum = math.add(beginNum, datas[i]);
}
if (index === 0 || beginNum >= 0) {
dataIn1.push(item);
dataIn2.push('-');
} else if (beginNum < 0) {
if (Math.abs(beginNum) === item) {
dataIn1.push(item);
dataIn2.push('-');
} else if (Math.abs(beginNum) > item) {
dataIn1.push(item * -1);
dataIn2.push('-');
} else {
dataIn1.push(math.add(item, beginNum));
dataIn2.push(beginNum);
setSumIndex.push(index);
}
}
dataOut1.push('-');
dataOut2.push('-');
dataTotal.push('-');
} else if (item < 0) {
dataIn1.push('-');
dataIn2.push('-');
// 当前起始位置
let beginNum = 0;
for(let i = 0; i < index; i++) {
beginNum = math.add(beginNum, datas[i]);
}
if (index === 0 || beginNum <= 0) {
dataOut1.push(item);
dataOut2.push('-');
} else if (beginNum > 0) {
if (beginNum >= Math.abs(item)) {
dataOut1.push(item * -1);
dataOut2.push('-');
} else {
dataOut1.push(math.add(item, beginNum));
dataOut2.push(beginNum);
setSumIndex.push(index);
}
}
dataTotal.push('-');
}
});
// 跨分区的为0
setSumIndex.forEach(item => {
dataSum[item] = 0;
})
const option = {
title: {
text: title,
subtext: '人民币'
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
formatter: (params) => {
let tar = null;
let val = 0;
const len = params.length;
for (let i = 1; i < len; i++) {
if (params[i].value !== '-') {
if (tar === null) {
tar = params[i];
}
if (tar.seriesName === '合计') {
val = math.add(val, params[i].value);
} else {
val = math.add(val, Math.abs(params[i].value));
}
}
}
// 支出显示负数
if (tar.seriesName === '支出') {
val = math.multi(val, -1);
}
return tar && tar.name + '<br/>' + tar.seriesName + ' : ' + val;
}
},
legend: {
data: ['收入', '支出', '合计'],
x:'right',
y:'top'
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
axisLine: {
onZero: false
},
axisLabel : {//坐标轴刻度标签的相关设置。
interval:0,
rotate: rotate, // 倾斜45度
formatter(value) {
if (value.length > maxNum) {
return `${value.slice(0, maxNum)}...`;
}
return value;
}
},
data: labels // x轴显示的文本
},
yAxis: {
type: 'value',
axisLine: {
show: false
}
},
series: [
{
name: 'Placeholder',
type: 'bar',
stack: 'Total',
silent: true,
itemStyle: {
borderColor: 'transparent',
color: 'transparent'
},
emphasis: {
itemStyle: {
borderColor: 'transparent',
color: 'transparent'
}
},
data: dataSum
}, {
name: '收入',
type: 'bar',
stack: 'Total',
label: {
show: true,
position: 'top',
formatter: (params) => {
const dataIndex = params.dataIndex;
if (dataIn1[dataIndex] !== '-') {
const d1 = Math.abs(dataIn1[dataIndex]);
const d2 = Math.abs(dataIn2[dataIndex] === '-' ? 0 : dataIn2[dataIndex]);
return math.add(d1, d2);
} else {
return '';
}
}
},
data: dataIn1
}, {
name: '收入',
type: 'bar',
stack: 'Total',
label: {
show: false,
position: 'top'
},
data: dataIn2
}, {
name: '支出',
type: 'bar',
stack: 'Total',
label: {
show: true,
position: 'bottom',
formatter: (params) => {
const dataIndex = params.dataIndex;
if (dataOut1[dataIndex] !== '-') {
const d1 = Math.abs(dataOut1[dataIndex]);
const d2 = Math.abs(dataOut2[dataIndex] === '-' ? 0 : dataOut2[dataIndex]);
return math.multi(math.add(d1, d2), -1);
} else {
return '';
}
}
},
data: dataOut1
}, {
name: '支出',
type: 'bar',
stack: 'Total',
label: {
show: false,
position: 'bottom'
},
data: dataOut2
}, {
name: '合计',
type: 'bar',
stack: 'Total',
label: {
show: true,
position: 'top'
},
data: dataTotal
}
]
};
return option;
}
实现效果图如下: