Files
sgxt_web/src/views/home/echarts/barHatEcharts.vue
2025-09-20 19:55:19 +08:00

368 lines
10 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 style="height:100%;width:100%" :id="echartsId"></div>
</template>
<script setup>
import * as echarts from "echarts";
import { nextTick, onMounted, onUnmounted, watch, defineProps, ref } from "vue";
const props = defineProps({
echartsId: {
type: String,
default: 'barHatId'
},
data: {
type: Object,
default: {
xDate: ['巴宜区', '工布江达县', '波密县', '朗县', '墨脱县', '察隅县', '米林县'],
list: [
{ name: "总数", value: [10,20,30,40,50,60,70], color:['rgba(0,244,255,1)','rgba(0,77,167,1)'], hatColor:'#087df9'},
{ name: "已处置", value: [10,20,30,40,50,60,70], color:['rgba(24, 232, 229, 1)','rgba(3, 110, 83, 1)'], hatColor:'#00FFFF'},
],
}
},
// 新增:是否启用自动循环展示提示框
autoTooltip: {
type: Boolean,
default: false
},
// 新增:提示框循环间隔时间(毫秒)
tooltipInterval: {
type: Number,
default: 2000
},
// 新增:鼠标悬停时是否暂停循环
pauseOnHover: {
type: Boolean,
default: true
}
});
// 响应式变量
const myChart = ref(null);
const tooltipTimer = ref(null);
const currentTooltipIndex = ref(0);
const isPaused = ref(false);
watch(() => props.data, val => {
nextTick(() => { handleDate() })
}, { immediate: true, deep: true })
// 启动自动循环展示提示框
function startAutoTooltip() {
// 详细的条件检查和日志
if (!props.autoTooltip) {
return;
}
if (!props.data) {
// console.warn('自动提示框未启动 - 缺少数据');
return;
}
if (!props.data.xDate || props.data.xDate.length === 0) {
// console.warn('自动提示框未启动 - xDate数据为空');
return;
}
if (!props.data.list || props.data.list.length === 0) {
// console.warn('自动提示框未启动 - list数据为空');
return;
}
if (!myChart.value) {
// console.warn('自动提示框未启动 - 图表实例不存在');
return;
}
// 清除之前的定时器
stopAutoTooltip();
const dataLength = props.data.xDate.length;
currentTooltipIndex.value = 0;
// console.log(`开始自动提示框循环 - 数据长度: ${dataLength}, 间隔: ${props.tooltipInterval}ms`);
// console.log('数据预览:', {
// xDate: props.data.xDate.slice(0, 3),
// listCount: props.data.list.length,
// firstSeriesName: props.data.list[0]?.name,
// firstSeriesValueCount: props.data.list[0]?.value?.length
// });
tooltipTimer.value = setInterval(() => {
try {
// 检查图表实例是否仍然存在
if (!myChart.value) {
// console.error('图表实例丢失,停止自动提示框');
stopAutoTooltip();
return;
}
// 检查是否暂停
if (isPaused.value) {
// console.log('提示框循环已暂停');
return;
}
// 先隐藏当前提示框
myChart.value.dispatchAction({
type: 'hideTip'
});
// 延迟一点时间再显示新的提示框,确保动画效果
setTimeout(() => {
if (myChart.value && !isPaused.value) {
try {
// 获取当前数据点信息
const currentData = props.data.xDate[currentTooltipIndex.value];
const currentValues = props.data.list.map(series => series.value[currentTooltipIndex.value]);
// 验证数据有效性
if (currentData === undefined) {
// console.error(`数据索引 ${currentTooltipIndex.value} 超出范围`);
stopAutoTooltip();
return;
}
// 显示当前索引的提示框 - 使用第一个系列
myChart.value.dispatchAction({
type: 'showTip',
seriesIndex: 0,
dataIndex: currentTooltipIndex.value
});
// console.log(`✓ 显示提示框 [${currentTooltipIndex.value}/${dataLength-1}] - ${currentData}:`, currentValues);
} catch (error) {
// console.error('显示提示框时出错:', error);
// 如果出错,尝试停止循环避免持续错误
stopAutoTooltip();
}
}
}, 100);
// 更新索引,循环展示
currentTooltipIndex.value = (currentTooltipIndex.value + 1) % dataLength;
} catch (error) {
// console.error('自动提示框循环出错:', error);
stopAutoTooltip();
}
}, props.tooltipInterval);
// console.log(`✓ 自动提示框已启动 - 间隔: ${props.tooltipInterval}ms, 数据长度: ${dataLength}`);
}
// 停止自动循环展示提示框
function stopAutoTooltip() {
if (tooltipTimer.value) {
clearInterval(tooltipTimer.value);
tooltipTimer.value = null;
// console.log('自动提示框已停止');
}
}
// 暂停自动循环
function pauseAutoTooltip() {
isPaused.value = true;
// console.log('自动提示框已暂停');
}
// 恢复自动循环
function resumeAutoTooltip() {
isPaused.value = false;
// console.log('自动提示框已恢复');
}
// 处理数据
function handleDate() {
let xDate = props.data.xDate;
let legend = props.data.list.map(v => { return { name: v.name } })
let series = props.data.list.map((item, i) => {
let obj = {
name: item.name,
type: "bar",
data: item.value,
barWidth: "10px",
itemStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: item.color ? item.color[0] : "rgba(0,244,255,1)" },
{ offset: 1, color: item.color ? item.color[1] : "rgba(0,77,167,1)" }], false),
}
},
markPoint: {
symbol: 'path://M62 62h900v900h-900v-900z', // 使用 SVG path 绘制扁圆形状
symbolSize: [11, 4], // 设置扁圆的宽和高
itemStyle: { color: item.hatColor || '#087df9' },// 圆盘颜色
data: item.value.map((obj, index) => ({
xAxis: index, // 对应柱子的横坐标
yAxis: obj + 0 // 柱子的值加上一些偏移量
}))
},
}
return obj
})
lineChartFn(xDate, legend, series)
}
function lineChartFn(xDate, legend, series) {
myChart.value = echarts.init(document.getElementById(props.echartsId));
var option = {
legend: {
type: "plain",
show: true,
right: 0,
textStyle: { color: "#ddd" },
data: legend
},
tooltip: {
trigger: "item",
backgroundColor: 'rgba(0, 0, 0, 0.9)',
borderColor: '#00d4ff',
borderWidth: 2,
borderRadius: 8,
textStyle: {
color: '#fff',
fontSize: 13,
fontWeight: 'normal'
},
formatter: function(params) {
// 获取当前数据点的所有系列信息
const dataIndex = params.dataIndex;
const categoryName = params.name;
let result = `<div style="margin-bottom: 8px; font-weight: bold; color: #00d4ff; font-size: 14px; border-bottom: 1px solid #00d4ff; padding-bottom: 4px;">${categoryName}</div>`;
// 遍历所有系列,显示该数据点的所有信息
if (props.data && props.data.list) {
props.data.list.forEach((seriesData, index) => {
const value = seriesData.value[dataIndex];
const color = seriesData.color ? seriesData.color[0] : '#00d4ff';
result += `<div style="margin: 4px 0; display: flex; align-items: center;">
<span style="display: inline-block; width: 12px; height: 12px; background: ${color}; border-radius: 50%; margin-right: 8px; border: 1px solid rgba(255,255,255,0.3);"></span>
<span style="color: #fff; margin-right: 8px; min-width: 60px;">${seriesData.name}:</span>
<span style="color: #00d4ff; font-weight: bold; font-size: 14px;">${value}</span>
</div>`;
});
}
return result;
},
// 确保提示框能够正确显示
confine: true,
// 添加动画效果
transitionDuration: 0.2,
// 设置提示框位置
position: function (point, params, dom, rect, size) {
// 计算提示框位置,避免超出边界
let x = point[0] + 15;
let y = point[1] - 10;
// 如果右侧空间不够,显示在左侧
if (x + size.contentSize[0] > size.viewSize[0]) {
x = point[0] - size.contentSize[0] - 15;
}
// 如果上方空间不够,显示在下方
if (y < 0) {
y = point[1] + 20;
}
return [x, y];
}
},
grid: {
top: "25%",
right: "2%",
left: "6%",
bottom: "22%"
},
xAxis: [
{
type: "category",
data: xDate,
axisLine: {
lineStyle: {
color: "rgba(255,255,255,0.12)"
}
},
axisLabel: {
margin: 10,
color: "#e2e9ff",
textStyle: {
fontSize: 14
}
}
}
],
yAxis: [
{
// name: '单位:万元',
axisLabel: {
formatter: "{value}",
color: "#e2e9ff"
},
axisLine: {
show: false,
lineStyle: {
color: "rgba(255,255,255,1)"
}
},
splitLine: {
lineStyle: {
color: "rgba(255,255,255,0.12)"
}
}
}
],
series: series
};
option && myChart.value.setOption(option);
// 添加鼠标事件监听
if (props.pauseOnHover) {
myChart.value.on('mouseover', () => {
pauseAutoTooltip();
});
myChart.value.on('mouseout', () => {
resumeAutoTooltip();
});
}
// 启动自动循环展示
if (props.autoTooltip) {
setTimeout(() => {
startAutoTooltip();
}, 1000); // 延迟1秒启动确保图表完全渲染
}
window.onresize = function () {
if (myChart.value) {
myChart.value.resize();
}
};
}
onMounted(() => {
lineChartFn();
});
// 组件卸载时清理定时器
onUnmounted(() => {
stopAutoTooltip();
if (myChart.value) {
myChart.value.dispose();
myChart.value = null;
}
});
</script>
<style lang="scss" scoped>
.circlecz {
height: 100%;
background: rgba(0,29,75,0.6);
border-radius: 0 0 4px 4px;
}
</style>