92 lines
2.6 KiB
Vue
92 lines
2.6 KiB
Vue
<script setup>
|
|
/** 共用折线图容器:父级负责 option/数据,组件补充主题、可见尺寸和卸载清理。 */
|
|
import { computed, onMounted, onBeforeUnmount, ref } from "vue";
|
|
import { use } from "echarts/core";
|
|
import { CanvasRenderer } from "echarts/renderers";
|
|
import { LineChart } from "echarts/charts";
|
|
import {
|
|
GridComponent,
|
|
TooltipComponent,
|
|
LegendComponent,
|
|
} from "echarts/components";
|
|
import VChart from "vue-echarts";
|
|
import { t as useTheme } from "~/lib/useTheme.js";
|
|
import "~/assets/styles/ChartBox.CYP_mhat.css";
|
|
|
|
use([
|
|
// 仅注册实际使用的折线图、Canvas 和基础组件;新增图表类型时须同步显式注册。
|
|
CanvasRenderer,
|
|
LineChart,
|
|
GridComponent,
|
|
TooltipComponent,
|
|
LegendComponent,
|
|
]);
|
|
const props = defineProps({
|
|
option: { type: Object, required: true },
|
|
height: { default: 140 },
|
|
loading: Boolean,
|
|
});
|
|
const theme = useTheme();
|
|
const container = ref(null);
|
|
const visible = ref(false);
|
|
let observer;
|
|
onMounted(() => {
|
|
// 折叠面板初始尺寸可能为零;等容器可测量后创建图表,避免 ECharts 零宽初始化。
|
|
observer = new ResizeObserver(([entry]) => {
|
|
if (entry?.contentRect.width > 0 && entry.contentRect.height > 0)
|
|
visible.value = true;
|
|
});
|
|
if (container.value) observer.observe(container.value);
|
|
});
|
|
onBeforeUnmount(() => observer?.disconnect());
|
|
const themedOption = computed(() => {
|
|
// 在不修改父级 option 的前提下合成坐标轴主题,兼容单个轴与多轴数组。
|
|
const dark = theme.isDark.value;
|
|
const color = dark ? "#a1a1aa" : "#94a3b8";
|
|
const grid = dark ? "rgba(255,255,255,.08)" : "rgba(0,0,0,.06)";
|
|
const axis = {
|
|
axisLine: { lineStyle: { color: grid } },
|
|
axisLabel: { color, fontSize: 11 },
|
|
};
|
|
const apply = (value, y = false) => ({
|
|
...axis,
|
|
...value,
|
|
...(y ? { splitLine: { lineStyle: { color: grid } } } : {}),
|
|
});
|
|
return {
|
|
textStyle: { color },
|
|
...props.option,
|
|
xAxis: Array.isArray(props.option.xAxis)
|
|
? props.option.xAxis.map((value) => apply(value))
|
|
: apply(props.option.xAxis),
|
|
yAxis: Array.isArray(props.option.yAxis)
|
|
? props.option.yAxis.map((value) => apply(value, true))
|
|
: apply(props.option.yAxis, true),
|
|
};
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div ref="container" class="chart-box" :style="{ height: `${height}px` }">
|
|
<VChart
|
|
v-if="visible"
|
|
:option="themedOption"
|
|
:loading="loading"
|
|
autoresize
|
|
class="chart"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.chart-box {
|
|
width: 100%;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
}
|
|
.chart {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
</style>
|