SourceTermAnalysisSystem_vue/src/pages/largeScreen1/dataAnalysis.vue

225 lines
5.7 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.

<script setup lang="ts">
import type { ECharts, EChartsOption } from 'echarts';
import * as echarts from 'echarts';
// ---------- 接收父组件传递的数据 ----------
interface TaskSeriesData {
name: string;
data: number[];
}
interface ChartData {
xList: string[];
seriesList: TaskSeriesData[];
}
const props = defineProps({
chartData: {
type: Object as () => ChartData,
required: true,
default: () => ({ xList: [], seriesList: [] })
}
});
// 定义Tab类型
type TabType = 'day' | 'month';
const activeTab = ref<TabType>('day');
// 图表相关引用
const chartRef = ref<HTMLDivElement | null>(null);
let myChart: ECharts | null = null;
let resizeHandler: () => void;
// ---------- 渐变颜色配置(适配多任务) ----------
const colorMap = [
{ line: '#52c4fa', area: ['rgba(82, 196, 250, 0.6)', 'rgba(82, 196, 250, 0.1)'] },
{ line: '#99c741', area: ['rgba(153, 199, 65, 0.6)', 'rgba(153, 199, 65, 0.1)'] },
{ line: '#f56c6c', area: ['rgba(245, 108, 108, 0.6)', 'rgba(245, 108, 108, 0.1)'] },
{ line: '#e6a23c', area: ['rgba(230, 162, 60, 0.6)', 'rgba(230, 162, 60, 0.1)'] },
{ line: '#67c23a', area: ['rgba(103, 194, 58, 0.6)', 'rgba(103, 194, 58, 0.1)'] },
];
// ---------- 初始化图表 ----------
function initChart() {
if (!chartRef.value || !props.chartData.xList.length) return;
// 销毁原有实例
if (myChart) {
myChart.dispose();
}
// 创建新实例
myChart = echarts.init(chartRef.value);
// 构建图表配置
const option: EChartsOption = {
animation: false, // 全局关闭所有动画
tooltip: {
trigger: 'axis',
axisPointer: { type: 'line', lineStyle: { color: '#ccc' } },
textStyle: { color: '#333' },
backgroundColor: 'rgba(255,255,255,0.9)',
padding: 10,
formatter: (params: any) => {
let res = `<div style="font-weight:bold;">${params[0].name}</div>`;
params.forEach((item: any) => {
res += `<div>${item.seriesName}: ${item.value}</div>`;
});
return res;
},
},
legend: {
top: 20,
left: 'center',
textStyle: { color: '#ade6ee' },
data: props.chartData.seriesList.map(item => item.name), // 动态渲染任务名
icon: 'circle',
itemWidth: 10,
itemHeight: 10,
},
grid: {
left: '0%',
right: '0%',
bottom: '0%',
top: '15%',
containLabel: true,
},
xAxis: {
type: 'category',
boundaryGap: false,
data: props.chartData.xList, // 父组件传递的x轴日期
axisLine: { lineStyle: { color: '#416273' } },
axisLabel: {
color: '#ade6ee',
fontSize: 12,
formatter(value, index) {
const dataLen = props.chartData.xList.length;
if (index === dataLen - 1) {
return `${value}\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0`;
}
return value;
},
},
splitLine: { show: false },
axisTick: { show: false },
},
yAxis: {
type: 'value',
// 动态计算Y轴最大值向上取整到千位
max: () => {
const allValues = props.chartData.seriesList.flatMap(item => item.data);
const maxVal = Math.max(...allValues);
return maxVal > 0 ? Math.ceil(maxVal / 1000) * 1000 : 1000;
},
axisLabel: { color: '#ade6ee', fontSize: 12 },
splitLine: { lineStyle: { color: '#77B5D533', type: 'solid' } },
show: true,
axisLine: { show: true, lineStyle: { color: '#416273' } },
},
series: props.chartData.seriesList.map((item, index) => ({
name: item.name,
type: 'line',
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: colorMap[index % colorMap.length].area[0] },
{ offset: 1, color: colorMap[index % colorMap.length].area[1] },
],
},
},
lineStyle: { color: colorMap[index % colorMap.length].line },
data: item.data, // 父组件传递的任务数据
symbol: 'none',
})),
};
myChart.setOption(option);
// 窗口大小监听
resizeHandler = () => {
myChart?.resize();
};
window.addEventListener('resize', resizeHandler);
}
// ---------- 监听数据变化,重新渲染图表 ----------
watch([() => props.chartData, () => activeTab], () => {
initChart();
}, { deep: true });
// ---------- 生命周期 ----------
onMounted(() => {
initChart();
});
onUnmounted(() => {
if (myChart) {
myChart.dispose();
myChart = null;
}
if (resizeHandler) {
window.removeEventListener('resize', resizeHandler);
}
});
// ---------- 暴露activeTab给父组件 ----------
defineExpose({
activeTab
});
</script>
<template>
<div class="task-area-chart-container" style="position: relative;">
<!-- 右上角Tab切换 -->
<div class="tab-bar">
<span :class="{ active: activeTab === 'day' }" @click="activeTab = 'day'"></span>
<span :class="{ active: activeTab === 'month' }" @click="activeTab = 'month'"></span>
</div>
<!-- 图表容器 -->
<div ref="chartRef" class="chart" />
</div>
</template>
<style scoped>
/* 保留原有样式,省略 */
.task-area-chart-container {
width: 100%;
height: 100%;
}
.chart {
width: 100%;
height: 100%;
}
.tab-bar {
position: absolute;
top: 15px;
right: 0;
display: flex;
color: #fff;
font-size: 14px;
z-index: 10;
background: rgb(7, 39, 43);
border: 1px solid #0b8c82;
/* user-select: none; */
}
.tab-bar span {
width: 50px;
height: 30px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
padding: 2px 8px;
transition: background-color 0.2s;
}
.tab-bar span.active {
background-color: #1397a3;
}
</style>