SocialNetworks_duan/src/views/GroupEvolution/component/groupChart.vue
2025-07-28 16:02:19 +08:00

271 lines
6.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="groupChart-component">
<img :src="title" alt="" class="title" v-if="title" />
<div class="chart-name" v-if="chartName">
<img class="name-icon" src="@/assets/images/groupEvolution/chart-name-icon.png" alt="">
<span>{{ chartName }}</span>
</div>
<div class="container" :id="chartId"></div>
<div class="slider-container">
<div
class="sliding-block"
:style="{ left: `${sliderLeft}px` }"
@mousedown="onMouseDown"
></div>
</div>
</div>
</template>
<script setup>
import { defineProps, onMounted, onBeforeUnmount, ref, computed } from "vue";
import * as echarts from "echarts";
let chartInstance = null;
const props = defineProps({
title: {
type: String,
default: ""
},
chartData: {
type: Object,
default: ""
},
chartName: {
type: String,
default: ""
}
});
// 生成唯一ID避免多个实例冲突
const chartId = computed(() => `group-chart-${Math.random().toString(36).substring(2, 9)}`);
const sliderLeft = ref(25); // 滑块初始left
const sliderWidth = 24; // 滑块宽度
const sliderContainerWidth = ref(0); // 容器宽度
let isDragging = false;
let startX = 0;
let startLeft = 0;
const onMouseDown = (e) => {
isDragging = true;
startX = e.clientX;
startLeft = sliderLeft.value;
document.addEventListener("mousemove", onMouseMove);
document.addEventListener("mouseup", onMouseUp);
};
const onMouseMove = (e) => {
if (!isDragging) return;
const deltaX = e.clientX - startX;
let newLeft = startLeft + deltaX;
newLeft = Math.max(0, Math.min(newLeft, sliderContainerWidth.value - sliderWidth));
sliderLeft.value = newLeft;
// 计算当前滑块对应的x轴索引
const percent = newLeft / (sliderContainerWidth.value - sliderWidth);
const xIndex = Math.round(percent * (props.chartData.xAxisData.length - 1));
// 调用ECharts显示tooltip
if (chartInstance) {
chartInstance.dispatchAction({
type: "showTip",
seriesIndex: 0, // 只显示第一个系列的tooltip
dataIndex: xIndex
});
}
};
const onMouseUp = () => {
isDragging = false;
document.removeEventListener("mousemove", onMouseMove);
document.removeEventListener("mouseup", onMouseUp);
};
const initChart = () => {
// 使用唯一ID初始化图表
chartInstance = echarts.init(document.getElementById(chartId.value));
const legendData = props.chartData.seriesList.map((item) => ({
name: item.name,
icon: "circle"
}));
const option = {
tooltip: {
trigger: "axis"
},
legend: {
data: legendData,
right: 10, // 距离右侧 10px
top: 13,
textStyle: {
color: "#fff"
}
},
tooltip: {
trigger: "axis",
backgroundColor: "rgba(0,0,0,0)", // 透明背景
borderColor: "rgba(0,0,0,0)", // 透明边框
borderWidth: 0,
extraCssText: "box-shadow:none;padding:0;",
formatter: function (params) {
const color = {
头部自媒体: "#33b6fb",
官方媒体: "#00d6da",
普通自媒体: "#fddc33"
};
const listHtml = params
.map(
(item) => `
<div style="
display:flex;
align-items:center;
justify-content:space-between;
width:100%;
height:30px;
border-radius:4px;
background:rgba(204,250,255,0.2);
margin-bottom:6px;
padding:0 8px;
color:#fff;
font-size:12px;
">
<p style="width:10px;height:10px;border-radius:50%;background-color:${color[item.seriesName]}"></p>
<span>${item.seriesName}</span>
<span style="
color: #fff;
text-align: right;
font-family: MiSans;
font-size: 14px;
font-style: normal;
font-weight: 500;
line-height: normal;
">${item.value}</span>
</div>
`
)
.join("");
return `
<div style="
width:150px;
border-radius:6px;
background:linear-gradient(304deg, rgba(28,103,175,0.3) -6.04%, rgba(2,95,137,0.3) 85.2%);
backdrop-filter:blur(4px);
padding:10px;
">
${listHtml}
</div>
`;
}
},
grid: {
left: "3%",
right: "4%",
bottom: "3%",
top: "30%",
containLabel: true
},
xAxis: {
type: "category",
boundaryGap: false,
data: props.chartData.xAxisData,
axisLabel: {
textStyle: {
color: "#94C1EC"
}
}
},
yAxis: {
name: "数量",
max: props.chartData.yAxisRange.max,
min: props.chartData.yAxisRange.min,
interval: props.chartData.yAxisRange.interval,
nameTextStyle: {
color: "#B6D6F7",
fontSize: 13,
align: "left",
padding: [0, 0, 5, -28] // 负值让文字更靠左
},
axisLabel: {
textStyle: {
color: "#94C1EC"
}
},
type: "value",
splitLine: {
show: true, // 是否显示分割线
lineStyle: {
color: ["rgba(57, 69, 106, 0.86)"], // 分割线颜色,可以使用数组实现交替颜色
width: 1, // 分割线宽度
type: "dotted" // 分割线类型
}
}
},
series: props.chartData.seriesList
};
chartInstance.setOption(option);
};
onMounted(() => {
initChart();
// 获取容器宽度
sliderContainerWidth.value = document.querySelector(".slider-container").offsetWidth;
});
onBeforeUnmount(() => {
// 销毁图表实例,避免内存泄漏
if (chartInstance) {
chartInstance.dispose();
chartInstance = null;
}
document.removeEventListener("mousemove", onMouseMove);
document.removeEventListener("mouseup", onMouseUp);
});
</script>
<style scoped lang="less">
.groupChart-component {
width: 100%;
height: 100%;
padding: 10px 10px;
.title {
margin-top: -18px;
margin-left: -12px;
}
.chart-name {
margin-left: -8px;
display: flex;
align-items: center;
justify-content: flex-start;
color: #fff;
font-size: 16px;
font-family: PingFang SC;
font-weight: 400;
}
.container {
top: -15px;
width: 100%;
height: 180px;
}
.slider-container {
width: 100%;
height: 4px;
border-radius: 20px;
background: linear-gradient(270deg, rgba(0, 82, 125, 0.48) 0%, rgba(0, 200, 255, 0.23) 100%);
backdrop-filter: blur(3px);
margin-top: -10px;
position: relative;
.sliding-block {
width: 24px;
height: 8px;
border-radius: 2px;
background: linear-gradient(270deg, #00527d 0%, #00c8ff 100%);
backdrop-filter: blur(3px);
position: absolute;
top: -2px;
left: 25px;
cursor: pointer;
}
}
}
</style>