<template>
<div ref="chartRef"></div>
</template>
<script>
import * as echarts from "echarts";
export default {
props: {
showLargeBtn: {
type: Boolean,
default: true
},
colorType: {
type: [String, Number],
default: 1,
},
loopShowTip: {
type: Boolean,
default: false,
},
showLegend: {
type: Boolean,
default: true,
},
showValue: {
type: Boolean,
default: false,
},
showValueTotal: {
type: Boolean,
default: false,
},
showTop: {
type: Boolean,
default: false,
},
},
data() {
return {
myChart: null,
timer: null,
data: { xAxisData: [], seriesData: [] },
xAxisData: [],
};
},
mounted() {
this.initChart();
window.addEventListener("resize", this.resize);
},
beforeDestroy() {
window.removeEventListener("resize", this.resize);
this.$refs.chartRef.onmouseout = null;
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
if (this.myChart && this.myChart != null) {
this.myChart.dispose();
}
},
methods: {
initChart() {
const vm = this;
if (vm.myChart && vm.myChart != null) {
vm.myChart.dispose();
vm.myChart = null;
}
if (vm.data.xAxisData == false) {
vm.$refs.chartRef.innerHTML = `<div class="el-empty"><div class="el-empty__image"><img src="${require("@/assets/empty.png")}"/></div></div>`;
return;
}
// 绘制左侧面
const CubeLeft = echarts.graphic.extendShape({
buildPath: function (ctx, shape) {
// 会canvas的应该都能看得懂,shape是从custom传入的
const xAxisPoint = shape.xAxisPoint;
const c0 = [shape.x, shape.y];
const c1 = [shape.x - 10, shape.y - 2];
const c2 = [xAxisPoint[0] - 10, xAxisPoint[1] - 2];
const c3 = [xAxisPoint[0], xAxisPoint[1] + 2];
ctx
.moveTo(c0[0], c0[1])
.lineTo(c1[0], c1[1])
.lineTo(c2[0], c2[1])
.lineTo(c3[0], c3[1])
.closePath();
},
});
// 绘制右侧面
const CubeRight = echarts.graphic.extendShape({
buildPath: function (ctx, shape) {
const xAxisPoint = shape.xAxisPoint;
const c1 = [shape.x, shape.y];
const c2 = [xAxisPoint[0], xAxisPoint[1] + 2];
const c3 = [xAxisPoint[0] + 10, xAxisPoint[1] - 2];
const c4 = [shape.x + 10, shape.y - 2];
ctx
.moveTo(c1[0], c1[1])
.lineTo(c2[0], c2[1])
.lineTo(c3[0], c3[1])
.lineTo(c4[0], c4[1])
.closePath();
},
});
// 绘制顶面
const CubeTop = echarts.graphic.extendShape({
buildPath: function (ctx, shape) {
const c1 = [shape.x, shape.y + 2];
const c2 = [shape.x + 10, shape.y - 2];
const c3 = [shape.x, shape.y - 6];
const c4 = [shape.x - 10, shape.y - 2];
ctx
.moveTo(c1[0], c1[1])
.lineTo(c2[0], c2[1])
.lineTo(c3[0], c3[1])
.lineTo(c4[0], c4[1])
.closePath();
},
});
// 绘制文字
const CubeText = echarts.graphic.extendShape({
buildPath: function (ctx, shape) {
const c1 = [shape.x, shape.y + 2];
const c2 = [shape.x + 10, shape.y - 2];
const c3 = [shape.x, shape.y - 6];
const c4 = [shape.x - 10, shape.y - 2];
ctx
.moveTo(c1[0], c1[1])
.lineTo(c2[0], c2[1])
.lineTo(c3[0], c3[1])
.lineTo(c4[0], c4[1])
.closePath();
},
});
// 注册三个面图形
echarts.graphic.registerShape("CubeLeft", CubeLeft);
echarts.graphic.registerShape("CubeRight", CubeRight);
echarts.graphic.registerShape("CubeTop", CubeTop);
const itemColor1 = ["#21CBC5", "#486DEF"];
const itemColor2 = ["#FDAD4D", "#A9D942"];
const barColor1 = [
{
left: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
{
offset: 0,
color: 'rgba(106,238,197,1)',
},
{
offset: 0.8,
color: "rgba(36,183,137,1)",
},
]),
right: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
{
offset: 0,
color: "rgba(106,238,197,0.8)",
},
{
offset: 0.8,
color: "rgba(36,183,137,0.8)",
},
]),
top: '#55ddb3',
},
{
left: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
{
offset: 0,
color: 'rgba(131,185,255,1)',
},
{
offset: 0.8,
color: "rgba(0,111,255,1)",
},
]),
right: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
{
offset: 0,
color: "rgba(131,185,255,0.8)",
},
{
offset: 0.8,
color: "rgba(0,111,255,0.8)",
},
]),
top: '#69AAFF',
}
]
const barColor2 = [
{
left: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
{
offset: 0,
color: 'rgba(155,176,250,1)',
},
{
offset: 0.8,
color: "rgba(71,136,255,1)",
},
]),
right: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
{
offset: 0,
color: "rgba(155,176,250,0.8)",
},
{
offset: 0.8,
color: "rgba(71,136,255,0.8)",
},
]),
top: '#79a2ed',
},
{ // background: linear-gradient(0deg, #FFBF80 0%, #F48D27 100%);
left: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
{
offset: 0,
color: 'rgba(255,191,128,1)',
},
{
offset: 0.8,
color: "rgba(244,141,39,1)",
},
]),
right: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
{
offset: 0,
color: "rgba(255,191,128,0.8)",
},
{
offset: 0.8,
color: "rgba(244,141,39,0.8)",
},
]),
top: '#FFD6AE',
}
]
const barColor = vm.colorType == 1 ? barColor1 : barColor2
const itemColor = vm.colorType == 1 ? itemColor1 : itemColor2
this.xAxisData = [];
const legendData = vm.data.seriesData.map((item,index) => {
return {
name: item.name,
itemStyle: {
color: itemColor[index]
}
}
});
vm.xAxisData = vm.data.xAxisData;
const seriesData = [];
let barWidth = 24;
switch(vm.colorType) {
case 1:
barWidth = 24;
break;
}
const itemTotalData = vm.data.seriesData[0].data.map((item0, index0) => item0 + vm.data.seriesData[1].data[index0]).map((item) => {
return item == 0 ? null : item;
})
const itemHeightData = [] // [[第1个系列第1列的高度, 第1个系列第2列的高度...], [第2个系列第1列的高度, 第2个系列第2列的高度...]]
vm.data.seriesData.forEach((item, index) => {
const itemHeightArr = []
item.data.forEach((item1, index1) => {
if (index == 0) {
itemHeightArr.push(item1)
} else {
itemHeightArr.push(itemHeightData[index - 1][index1] + item1)
}
})
itemHeightData.push(itemHeightArr)
})
vm.data.seriesData.forEach((item, index) => {
item.data = item.data.map((item) => {
return item == 0 ? null : item;
})
const seriesItem = {
name: item.name,
type: "custom",
renderItem: (params, api) => {
var color = barColor[params.seriesIndex];
const curValueTotal = itemHeightData[params.seriesIndex][params.dataIndex]
// const location = api.coord([api.value(0), api.value(1)]); // 不叠加的时候
const location = api.coord([api.value(0), curValueTotal]); // 叠加的时候
let str = ''
if (vm.showValueTotal || vm.showTop || vm.showValue) {
if (vm.showTop) {
/* if (params.dataIndex == 0) {
str += '{top1|Top.1}' + '\n'
} else if (params.dataIndex == 1) {
str += '{top2|Top.2}' + '\n'
} else if (params.dataIndex == 2) {
str += '{top3|Top.3}' + '\n'
} */
if (params.dataIndex < 3) {
str += `{top${params.dataIndex +1}|Top.${params.dataIndex + 1 }}` + '\n'
}
}
if (vm.showValueTotal) {
str += '{text|' + itemTotalData[params.dataIndex] + '}'
}
if (vm.showValue) {
let itemTotal = 0
let itemStr = ''
const seriesLen = seriesData.length
for(let i=seriesLen-1; i>=0; i--) {
itemTotal += seriesData[i].data[params.dataIndex]
itemStr += `{name${i}|` + seriesData[i].data[params.dataIndex] + `}`
if (i > 0) {
itemStr += '\n'
}
}
if (itemTotal > 0) {
str += itemStr
}
}
}
return {
type: "group",
children: [
{
type: "CubeLeft",
shape: {
api,
xValue: api.value(0),
yValue: api.value(1),
x: location[0],
y: location[1],
// xAxisPoint: api.coord([api.value(0), 0]), // 不叠加的时候
xAxisPoint: api.coord([api.value(0), curValueTotal - api.value(1)]), // 叠加的时候
},
style: {
fill: (color = barColor[params.seriesIndex].left),
},
},
{
type: "CubeRight",
shape: {
api,
xValue: api.value(0),
yValue: api.value(1),
x: location[0],
y: location[1],
// xAxisPoint: api.coord([api.value(0), 0]), // 不叠加的时候
xAxisPoint: api.coord([api.value(0), curValueTotal - api.value(1)]), // 叠加的时候
},
style: {
fill: (color = barColor[params.seriesIndex].right),
},
},
{
type: "CubeTop",
shape: {
api,
xValue: api.value(0),
yValue: api.value(1),
x: location[0],
y: location[1],
// xAxisPoint: api.coord([api.value(0), 0]), // 不叠加的时候
xAxisPoint: api.coord([api.value(0), curValueTotal - api.value(1)]), // 叠加的时候
},
style: {
fill: (color = barColor[params.seriesIndex].top),
},
},
{
type: 'text',
ignore: params.seriesIndex != vm.data.seriesData.length - 1,
style: {
text: str,
x: location[0],
y: location[1] - 8 - 12 * str.split('\n').length - (str.includes('Top') ? 5 : 0),
textAlign: 'center',
rich: {
top1: {
fill: '#F76060',
fontWeight: 'bold',
padding: [0, 0, 5, 0]
},
top2: {
fill: '#809DBE',
fontWeight: 'bold',
padding: [0, 0, 5, 0]
},
top3: {
fill: '#FC9B3C',
fontWeight: 'bold',
padding: [0, 0, 5, 0]
},
text: {
},
name0: {
fill: itemColor[0]
},
name1: {
fill: itemColor[1]
},
name2: {
fill: itemColor[2]
},
name3: {
color: itemColor[3]
}
}
},
}
],
};
},
stack: "group",
data: item.data,
}
if (item.avg !== undefined) {
seriesItem.markLine = {
symbol: 'none',
label: {
show: false
// color: 'rgba(30, 144, 245, 1)',
// formatter: function (params) {
// return item.name.replace('数量','平均值') + ':' + lwTavg
// }
},
lineStyle: {
// color: 'rgba(30, 144, 245, 1)',
shadowColor: '#ffffff', // 阴影颜色
shadowBlur: 3, // 阴影的模糊大小
shadowOffsetX: 0, // 阴影的水平偏移
shadowOffsetY: 0, // 阴影的垂直偏移
},
data: [{ name: item.name.replace('数量','平均值'), yAxis: item.avg, type: 'average' }]
}
}
seriesData.push(seriesItem);
});
vm.myChart = echarts.init(vm.$refs.chartRef);
let dataZoomTnterval = 11
let option = {
color: itemColor,
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow",
},
borderWidth: 0,
confine: true,
},
legend: {
top: "5px",
right: 0,
icon: "circle",
itemWidth: 12,
itemHeight: 12,
show: vm.showLegend,
data: legendData,
},
grid: {
top: vm.showLegend
? legendData.length > 4
? "70px"
: "50px"
: "35px",
left: "0",
right: "20px",
bottom: vm.xAxisData.length > dataZoomTnterval + 1 ? "20px" : '0',
containLabel: true,
},
xAxis: {
type: "category",
show: true,
triggerEvent: true, // 只有加上这个,才能绑定上
axisTick: {
show: false,
},
splitLine: {
show: false,
},
axisLine: {
show: false,
},
axisLabel: {
interval: 0,
fontSize: 12,
color: "#999999",
overflow: "truncate",
ellipsis: "...",
width: 80,
// formatter: res => {
// const len = res.length
// return len > 5 ? res.slice(0, 5) + '...' : res
// },
// margin: 15
},
boundaryGap: true,
data: vm.xAxisData,
},
yAxis: {
type: "value",
show: true,
splitLine: {
show: true,
lineStyle: {
type: "dashed",
color: "#ECECEC",
},
},
axisTick: {
show: false,
alignWithLabel: false,
inside: false,
},
axisLine: {
show: false,
},
axisLabel: {
fontSize: 12,
color: "#999999",
},
max: function (value) {
if (value.max < 10) {
return 10;
}
// return Math.round(value.max / 50) * 50 + 50
},
},
dataZoom: [
{
type: "slider",
height: 0, // dataZoom 组件的高度,同样只对第一个 x 轴有效
left: 30, // 组件距离左侧的距离
right: 15, // 组件距离右侧的距离
bottom: 10, // 组件距离底部的距离
borderColor: "transparent",
startValue: vm.xAxisData[0],
endValue: vm.xAxisData[dataZoomTnterval],
textStyle: {
color: "transparent",
},
moveHandleIcon: "none",
moveHandleStyle: {
backgroundColor: "rgba(66, 104, 239, 0.1)",
borderColor: "rgba(66, 104, 239, 1)",
borderCap: "round",
},
show: vm.xAxisData.length > dataZoomTnterval + 1,
},
],
series: seriesData,
};
vm.myChart.setOption(option);
if (vm.timer) {
clearInterval(vm.timer);
}
let currentIndex = -1;
let dataZoomEndValue = dataZoomTnterval;
// 自动轮询展示提示框
function switchTooltip(myChart) {
const dataLen = vm.xAxisData.length;
if (dataLen > 0) {
currentIndex = (currentIndex + 1) % dataLen; // 取余 循环展示
if (
currentIndex > dataZoomEndValue ||
currentIndex < dataZoomEndValue - dataZoomTnterval
) {
myChart.dispatchAction({
type: "dataZoom",
// 开始位置的数值
startValue:
currentIndex < dataZoomTnterval
? 0
: currentIndex - dataZoomTnterval,
endValue:
currentIndex < dataZoomTnterval
? dataZoomTnterval
: currentIndex,
});
}
myChart.dispatchAction({
type: "showTip",
seriesIndex: 0,
dataIndex: currentIndex,
});
}
}
function startTooltipLoop(myChart) {
clearInterval(vm.timer);
if (vm.loopShowTip) {
vm.timer = setInterval(() => switchTooltip(myChart), 2000);
}
}
// 开始轮询
startTooltipLoop(vm.myChart);
// 监听dataZoom的change事件
vm.myChart.on("datazoom", function (event) {
if (event.endValue) {
dataZoomEndValue = event.endValue;
}
});
// 鼠标离开, 开始轮询
/* vm.$refs.chartRef.addEventListener('mouseout', () => {
startTooltipLoop(vm.myChart)
}) */
vm.$refs.chartRef.onmouseout = () => {
startTooltipLoop(vm.myChart);
};
vm.myChart.on("mouseover", (params) => {
if (vm.timer) {
clearInterval(vm.timer);
vm.timer = null;
}
currentIndex = params.dataIndex;
// currentIndex = -1
});
// 点击事件
if (vm.showLargeBtn) {
vm.myChart.on('click', (params) => {
this.$emit('changeTableShow', params.name)
})
}
},
setData(data) {
this.data = data;
this.initChart();
},
resize() {
if (this.myChart) {
this.myChart.resize();
}
},
},
};
</script>