payment-statistics/src/views/components/chartsComponents/AskSectionChart.vue

355 lines
9.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div ref="chartDom" class="w-full h-full"></div>
</template>
<script setup lang="ts">
import { convertNumber } from "@/utils/convertNumber";
import { usePlatformType } from "@/utils/device";
import * as echarts from "echarts";
interface DataItem {
tag: string;
total: number;
}
const props = defineProps({
chartData:{
type:Array<DataItem>,
default: () => []
},
shorthand:{
type:Boolean,
default: false
},
});
const renderItem = (params: any, api: { coord: (arg0: any[]) => any[]; value: (arg0: number) => any; style: () => any }) => {
// 柱子索引值
const { seriesIndex } = params;
// 基础坐标
const basicsCoord = api.coord([api.value(seriesIndex), api.value(1)]);
// 顶部基础 y 轴
const topBasicsYAxis = basicsCoord[1];
// 基础 x 轴
const basicsXAxis = basicsCoord[0];
// 底部 y 轴
const bottomYAxis = api.coord([api.value(seriesIndex), 0])[1];
return {
type: "group",
children: [
{
type: "leftShape",
shape: {
topBasicsYAxis,
basicsXAxis,
bottomYAxis,
},
style: {
fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "rgba(0, 147, 221, 1)" },
{ offset: 1, color: "rgba(0, 88, 255, 0.20)" },
]), // 覆盖基础样式
},
},
{
type: "rightShape",
shape: {
topBasicsYAxis,
basicsXAxis,
bottomYAxis,
},
style: {
fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "rgba(0, 147, 221, 1)" },
{ offset: 1, color: "rgba(0, 88, 255, 0.20)" },
]), // 覆盖基础样式
},
},
{
type: "topShape",
shape: {
topBasicsYAxis,
basicsXAxis,
bottomYAxis,
},
style: {
fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "rgba(143, 231, 255, 1)" },
{ offset: 1, color: "rgba(0, 132, 255, 1)" },
]), // 覆盖基础样式
},
},
{
type: "middleShape",
shape: {
topBasicsYAxis,
basicsXAxis,
bottomYAxis,
},
style: {
fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "rgba(143, 231, 255, 1)" },
{ offset: 1, color: "rgba(0, 132, 255, 0)" },
]), // 覆盖基础样式
},
},
],
};
};
const chartDom = ref(null);
let myChart: echarts.ECharts | null = null;
const initChart = () => {
if (!chartDom.value) return;
myChart = echarts.init(chartDom.value);
const {isMobile} = usePlatformType()
// 侧面宽度
const WIDTH = isMobile ? 6 : 10;
// 斜角高度
const OBLIQUE_ANGLE_HEIGHT = isMobile ? 2 : 3.5;
const leftShape = echarts.graphic.extendShape({
buildPath(ctx, shape) {
const { topBasicsYAxis, bottomYAxis, basicsXAxis } = shape;
const p1 = [basicsXAxis - WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT];
const p2 = [basicsXAxis - WIDTH, bottomYAxis];
const p3 = [basicsXAxis, bottomYAxis];
const p4 = [basicsXAxis, topBasicsYAxis];
ctx.moveTo(p1[0], p1[1]);
ctx.lineTo(p2[0], p2[1]);
ctx.lineTo(p3[0], p3[1]);
ctx.lineTo(p4[0], p4[1]);
},
});
const rightShape = echarts.graphic.extendShape({
buildPath(ctx, shape) {
const { topBasicsYAxis, bottomYAxis, basicsXAxis } = shape;
const p1 = [basicsXAxis, topBasicsYAxis];
const p2 = [basicsXAxis, bottomYAxis];
const p3 = [basicsXAxis + WIDTH, bottomYAxis];
const p4 = [basicsXAxis + WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT];
ctx.moveTo(p1[0], p1[1]);
ctx.lineTo(p2[0], p2[1]);
ctx.lineTo(p3[0], p3[1]);
ctx.lineTo(p4[0], p4[1]);
},
});
const topShape = echarts.graphic.extendShape({
buildPath(ctx, shape) {
const { topBasicsYAxis, basicsXAxis } = shape;
const p1 = [basicsXAxis, topBasicsYAxis];
const p2 = [basicsXAxis + WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT];
const p3 = [basicsXAxis, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT * 2];
const p4 = [basicsXAxis - WIDTH, topBasicsYAxis - OBLIQUE_ANGLE_HEIGHT];
ctx.moveTo(p1[0], p1[1]);
ctx.lineTo(p2[0], p2[1]);
ctx.lineTo(p3[0], p3[1]);
ctx.lineTo(p4[0], p4[1]);
},
});
const middleShape = echarts.graphic.extendShape({
buildPath(ctx, shape) {
const { topBasicsYAxis, basicsXAxis, bottomYAxis } = shape;
const p1 = [basicsXAxis - 0.5, topBasicsYAxis];
const p2 = [basicsXAxis - 0.5, bottomYAxis];
const p3 = [basicsXAxis + 0.5, bottomYAxis];
const p4 = [basicsXAxis + 0.5, topBasicsYAxis];
ctx.moveTo(p1[0], p1[1]);
ctx.lineTo(p2[0], p2[1]);
ctx.lineTo(p3[0], p3[1]);
ctx.lineTo(p4[0], p4[1]);
},
});
echarts.graphic.registerShape("leftShape", leftShape);
echarts.graphic.registerShape("rightShape", rightShape);
echarts.graphic.registerShape("topShape", topShape);
echarts.graphic.registerShape("middleShape", middleShape);
// 初始化时设置空数据后续通过updateChartData更新
const options = {
grid: {
top: props.shorthand ? 30:40,
left: props.shorthand ? 40 : 60,
right: props.shorthand ?20 : 30,
bottom: props.shorthand ?30:45,
},
xAxis: {
type: "category",
data: [],
axisLabel: {
formatter: (value: any) => {
if(props.shorthand){
return value.slice(0,2)
}else{
if (value.length <= 4) return value;
const arr = [];
for (let i = 0; i < value.length; i += 4) {
arr.push(value.slice(i, i + 4));
}
return arr.join('\n');
}
},
color: "#C0EEFF",
fontSize: props.shorthand ? 10 : 14,
interval: 0,
},
nameTextStyle: {
color: "#243174",
},
axisLine: {
lineStyle: {
color: "#243174",
},
},
},
yAxis: {
name: "人数",
nameLocation: "end",
nameTextStyle: {
color: "#C0EEFF",
fontSize: props.shorthand ? 10 : 14,
padding: [0, 5, 0, 0],
align: "right",
},
type: "value",
min: 0,
max: 100,
interval: 20,
axisLabel: {
color: "#C0EEFF",
fontSize: props.shorthand ? 10 : 14,
formatter: (value: any) => {
if(props.shorthand){
return convertNumber(value)
}else{
return value
}
},
},
axisLine: {
show: true,
lineStyle: {
color: "#243174",
},
},
splitLine: {
lineStyle: {
type: "dashed",
color: "#308EFF",
width: 1,
opacity: 0.2,
},
},
},
series: [
{
type: "custom",
renderItem: renderItem,
color: "blue",
data: [],
},
{
type: "bar",
label: {
show: true,
position: "top",
fontSize: props.shorthand ? 10 : 12,
color: "rgba(192, 238, 255, 1)",
},
tooltip: {
show: false,
},
itemStyle: {
color: "transparent",
},
data: [],
},
],
};
myChart.setOption(options);
// 如果有数据,立即更新
if (props.chartData && props.chartData.length > 0) {
updateChartData();
}
window.addEventListener("resize", handleResize);
};
// 仅更新图表数据,不重新创建图表
const updateChartData = () => {
if (!myChart || !props.chartData) return;
const chartData = props.chartData || [];
// 处理图表数据
const tags = chartData.map(item => item.tag);
const values = chartData.map(item => item.total);
// 计算y轴最大值和间隔
const maxValue = values.length > 0 ? Math.max(...values) : 0;
const yAxisMax = maxValue > 0 ? Math.ceil(maxValue / 100) * 100 : 100;
const yAxisInterval = Math.ceil(yAxisMax / 5 / 100) * 100 || 20;
myChart.setOption({
xAxis: {
data: tags,
},
yAxis: {
min: 0,
max: yAxisMax,
interval: yAxisInterval,
},
series: [
{
data: values,
},
{
data: values,
},
],
});
};
const handleResize = () => {
myChart?.resize();
};
// 监听数据变化,只更新数据不重新创建图表
watch(
() => props.chartData,
() => {
if (myChart) {
updateChartData();
}
},
{ deep: true,immediate:true }
);
onMounted(() => {
if(!myChart){
initChart();
}
})
onUnmounted(() => {
window.removeEventListener("resize", handleResize);
myChart?.dispose();
});
</script>
<style lang="scss"></style>