You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
321 lines
8.7 KiB
Vue
321 lines
8.7 KiB
Vue
<script setup lang="ts">
|
|
import { onMounted, onUnmounted, ref, watch, nextTick } from "vue";
|
|
import * as echarts from "echarts";
|
|
|
|
// 定义组件接收的props
|
|
const props = defineProps({
|
|
xData: {
|
|
type: Array as PropType<Array<string>>,
|
|
default: () => [],
|
|
},
|
|
legendArr: {
|
|
type: Array as PropType<Array<string>>,
|
|
default: () => [],
|
|
},
|
|
datas: {
|
|
type: Array as PropType<Array<Array<number>>>,
|
|
default: () => [],
|
|
},
|
|
colorArr: {
|
|
type: Array as PropType<Array<Array<string>>>,
|
|
default: () => [],
|
|
},
|
|
});
|
|
const chartContainer = ref<HTMLDivElement | null>(null);
|
|
let chartInstance: echarts.ECharts | null = null;
|
|
|
|
// 自定义3D形状
|
|
const CubeLeft = echarts.graphic.extendShape({
|
|
shape: { x: 0, y: 0 },
|
|
buildPath: (ctx, shape) => {
|
|
const { xAxisPoint } = shape;
|
|
const [c0, c1, c2, c3] = [
|
|
[shape.x, shape.y],
|
|
[shape.x - 12, shape.y - 6],
|
|
[xAxisPoint[0] - 12, xAxisPoint[1] - 6],
|
|
[xAxisPoint[0], xAxisPoint[1]],
|
|
];
|
|
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({
|
|
shape: { x: 0, y: 0 },
|
|
buildPath: (ctx, shape) => {
|
|
const { xAxisPoint } = shape;
|
|
const [c1, c2, c3, c4] = [
|
|
[shape.x, shape.y],
|
|
[xAxisPoint[0], xAxisPoint[1]],
|
|
[xAxisPoint[0] + 12, xAxisPoint[1] - 6],
|
|
[shape.x + 12, shape.y - 6],
|
|
];
|
|
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({
|
|
shape: { x: 0, y: 0 },
|
|
buildPath: (ctx, shape) => {
|
|
const [c1, c2, c3, c4] = [
|
|
[shape.x, shape.y],
|
|
[shape.x + 12, shape.y - 6],
|
|
[shape.x, shape.y - 12],
|
|
[shape.x - 12, shape.y - 6],
|
|
];
|
|
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 initChart = () => {
|
|
if (!chartContainer.value) return;
|
|
|
|
chartInstance = echarts.init(chartContainer.value);
|
|
setTimeout(() => {
|
|
chartInstance?.resize();
|
|
updateChart();
|
|
}, 500);
|
|
};
|
|
|
|
// 更新图表配置
|
|
const updateChart = () => {
|
|
if (!chartInstance) return;
|
|
const series = props.datas
|
|
.map((item, index) => [
|
|
{
|
|
type: "custom",
|
|
name: props.legendArr[index],
|
|
renderItem: (params, api) => ({
|
|
type: "group",
|
|
x: (index - props.datas.length / 2) * 30 + 15,
|
|
children: [
|
|
{
|
|
type: "CubeLeft",
|
|
shape: {
|
|
api,
|
|
xValue: api.value(0),
|
|
yValue: api.value(1),
|
|
x: api.coord([api.value(0), api.value(1)])[0],
|
|
y: api.coord([api.value(0), api.value(1)])[1],
|
|
xAxisPoint: api.coord([api.value(0), 0]),
|
|
},
|
|
style: {
|
|
fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
{
|
|
offset: 0,
|
|
color: props.colorArr[index % props.colorArr.length][1],
|
|
},
|
|
{
|
|
offset: 1,
|
|
color: props.colorArr[index % props.colorArr.length][0],
|
|
},
|
|
]),
|
|
},
|
|
},
|
|
{
|
|
type: "CubeRight",
|
|
shape: {
|
|
api,
|
|
xValue: api.value(0),
|
|
yValue: api.value(1),
|
|
x: api.coord([api.value(0), api.value(1)])[0],
|
|
y: api.coord([api.value(0), api.value(1)])[1],
|
|
xAxisPoint: api.coord([api.value(0), 0]),
|
|
},
|
|
style: {
|
|
fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
{
|
|
offset: 0,
|
|
color: props.colorArr[index % props.colorArr.length][1],
|
|
},
|
|
{
|
|
offset: 1,
|
|
color: props.colorArr[index % props.colorArr.length][0],
|
|
},
|
|
]),
|
|
},
|
|
},
|
|
{
|
|
type: "CubeTop",
|
|
shape: {
|
|
api,
|
|
xValue: api.value(0),
|
|
yValue: api.value(1),
|
|
x: api.coord([api.value(0), api.value(1)])[0],
|
|
y: api.coord([api.value(0), api.value(1)])[1],
|
|
xAxisPoint: api.coord([api.value(0), 0]),
|
|
},
|
|
style: {
|
|
fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
{
|
|
offset: 0,
|
|
color: props.colorArr[index % props.colorArr.length][1],
|
|
},
|
|
{
|
|
offset: 1,
|
|
color: props.colorArr[index % props.colorArr.length][1],
|
|
},
|
|
]),
|
|
},
|
|
},
|
|
],
|
|
}),
|
|
data: item,
|
|
},
|
|
{
|
|
type: "bar",
|
|
name: props.legendArr[index],
|
|
barWidth: 25,
|
|
// label: {
|
|
// normal: {
|
|
// show: true,
|
|
// position: "top",
|
|
// fontSize: 16,
|
|
// color: "#fff",
|
|
// offset: [0, -10],
|
|
// },
|
|
// },
|
|
itemStyle: { color: "transparent" },
|
|
data: item,
|
|
yAxisIndex: 0, // 使用左y轴
|
|
},
|
|
{
|
|
type: "bar",
|
|
name: props.legendArr[index],
|
|
barWidth: 25,
|
|
itemStyle: { color: "transparent" },
|
|
data: item,
|
|
yAxisIndex: 1, // 使用左y轴
|
|
},
|
|
])
|
|
.flat();
|
|
|
|
chartInstance.setOption({
|
|
tooltip: {
|
|
trigger: "axis",
|
|
borderWidth: 0,
|
|
backgroundColor: "rgba(8,36,68,.9)",
|
|
color: "#fff",
|
|
textStyle: { color: "#fff" },
|
|
formatter: (params) => {
|
|
let str = params[0].name + "</br>";
|
|
params.forEach((item, index) => {
|
|
if (item.seriesType === "custom") {
|
|
str += `
|
|
<div style='display:flex;justify-content:space-between;align-items:center'>
|
|
<div style='margin-right:20px;'>
|
|
<span style="display:inline-block;width:10px;height:10px;border-radius:5px;background-color:${
|
|
props.colorArr[index % props.colorArr.length][0]
|
|
}"></span>
|
|
${item.seriesName}
|
|
</div>
|
|
<span> ${item.value ? item.value : "-"}</span>
|
|
</div>`;
|
|
}
|
|
});
|
|
return str;
|
|
},
|
|
},
|
|
grid: {
|
|
left: "5%",
|
|
right: "5%",
|
|
top: "5%",
|
|
bottom: "10%",
|
|
containLabel: true,
|
|
},
|
|
legend: {
|
|
left: "center",
|
|
top: "90%",
|
|
itemWidth: 12, // 图例项宽度
|
|
itemHeight: 8,
|
|
textStyle: { color: "#fff", fontSize: 12 },
|
|
data: props.legendArr.map((name, index) => ({
|
|
name,
|
|
textStyle: { color: "#fff", fontSize: 12 },
|
|
itemStyle: { color: props.colorArr[index % props.colorArr.length][1] },
|
|
})),
|
|
},
|
|
xAxis: {
|
|
type: "category",
|
|
data: props.xData,
|
|
axisLine: { lineStyle: { color: "rgba(239, 247, 253, .1)" } },
|
|
axisLabel: { fontSize: 12, color: "#fff", margin: 12 },
|
|
},
|
|
yAxis: [
|
|
{
|
|
// name: "kWh",
|
|
// nameTextStyle: { color: "#fff", fontSize: 12 },
|
|
splitLine: {
|
|
lineStyle: { type: "dashed", color: "rgba(80,112,242,0.3)" },
|
|
},
|
|
axisLabel: { textStyle: { color: "#8C8C8C" }, fontSize: 12 },
|
|
// axisLine: { lineStyle: { color: "#8C8C8C" } },
|
|
// scale: true, // 同步刻度
|
|
},
|
|
{
|
|
// name: "kWh",
|
|
// nameTextStyle: { color: "#fff", fontSize: 12 },
|
|
splitLine: { lineStyle: { color: "transparent" } },
|
|
axisLabel: { textStyle: { color: "#8C8C8C" }, fontSize: 12 },
|
|
// axisLine: { lineStyle: { color: "#8C8C8C" } },
|
|
position: "right",
|
|
// scale: true, // 同步刻度
|
|
},
|
|
],
|
|
series,
|
|
});
|
|
};
|
|
const handleResize = () => {
|
|
chartInstance?.resize();
|
|
};
|
|
// 生命周期管理
|
|
onMounted(async () => {
|
|
await nextTick();
|
|
initChart();
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
if (chartInstance) {
|
|
chartInstance.dispose();
|
|
chartInstance = null;
|
|
chartInstance?.resize();
|
|
}
|
|
});
|
|
|
|
// 响应式更新
|
|
watch(
|
|
() => props,
|
|
async () => {
|
|
await nextTick();
|
|
updateChart();
|
|
// delay(600).then(() => resize());
|
|
},
|
|
{
|
|
deep: true,
|
|
immediate: true,
|
|
}
|
|
);
|
|
</script>
|
|
<template>
|
|
<div ref="chartContainer" style="width: 100%; height: 100%" />
|
|
</template>
|