211 lines
4.7 KiB
Vue
211 lines
4.7 KiB
Vue
<template>
|
||
<div class="w-full h-150">
|
||
<v-chart
|
||
v-if="data"
|
||
class="w-full h-full"
|
||
:option="option"
|
||
autoresize
|
||
:update-options="updateOptions"
|
||
/>
|
||
<div
|
||
v-else
|
||
class="w-full h-full flex items-center justify-center text-gray-500"
|
||
>
|
||
暂无数据
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { computed, shallowRef } from "vue";
|
||
import VChart from "vue-echarts";
|
||
import type { LogicDataType } from "./index";
|
||
|
||
// Echarts
|
||
import { use } from "echarts/core";
|
||
import { LineChart } from "echarts/charts";
|
||
import {
|
||
TooltipComponent,
|
||
GridComponent,
|
||
DataZoomComponent,
|
||
AxisPointerComponent,
|
||
ToolboxComponent,
|
||
} from "echarts/components";
|
||
import { CanvasRenderer } from "echarts/renderers";
|
||
import type { ComposeOption } from "echarts/core";
|
||
import type { LineSeriesOption } from "echarts/charts";
|
||
import type {
|
||
AxisPointerComponentOption,
|
||
TooltipComponentOption,
|
||
GridComponentOption,
|
||
DataZoomComponentOption,
|
||
} from "echarts/components";
|
||
import type {
|
||
ToolboxComponentOption,
|
||
XAXisOption,
|
||
YAXisOption,
|
||
} from "echarts/types/dist/shared";
|
||
|
||
use([
|
||
TooltipComponent,
|
||
ToolboxComponent,
|
||
GridComponent,
|
||
AxisPointerComponent,
|
||
DataZoomComponent,
|
||
LineChart,
|
||
CanvasRenderer,
|
||
]);
|
||
|
||
type EChartsOption = ComposeOption<
|
||
| AxisPointerComponentOption
|
||
| TooltipComponentOption
|
||
| ToolboxComponentOption
|
||
| GridComponentOption
|
||
| DataZoomComponentOption
|
||
| LineSeriesOption
|
||
>;
|
||
|
||
// Define props
|
||
interface Props {
|
||
data?: LogicDataType;
|
||
}
|
||
|
||
const props = defineProps<Props>();
|
||
|
||
// 添加更新选项来减少重绘
|
||
const updateOptions = shallowRef({
|
||
notMerge: false,
|
||
lazyUpdate: true,
|
||
silent: false
|
||
});
|
||
|
||
const option = computed((): EChartsOption => {
|
||
if (!props.data) return {};
|
||
|
||
const channelCount = props.data.y.length;
|
||
const channelSpacing = 2; // 每个通道之间的间距
|
||
|
||
// 使用单个网格
|
||
const grids: GridComponentOption[] = [
|
||
{
|
||
left: "5%",
|
||
right: "5%",
|
||
top: "5%",
|
||
bottom: "15%",
|
||
},
|
||
];
|
||
|
||
// 单个X轴
|
||
const xAxis: XAXisOption[] = [
|
||
{
|
||
type: "category",
|
||
boundaryGap: false,
|
||
data: props.data!.x.map((x) => x.toFixed(3)),
|
||
axisLabel: {
|
||
formatter: (value: string) => `${value}${props.data!.xUnit}`,
|
||
},
|
||
},
|
||
];
|
||
|
||
// 单个Y轴,范围根据通道数量调整
|
||
const yAxis: YAXisOption[] = [
|
||
{
|
||
type: "value",
|
||
min: -0.5,
|
||
max: channelCount * channelSpacing - 0.5,
|
||
interval: channelSpacing,
|
||
axisLabel: {
|
||
formatter: (value: number) => {
|
||
const channelIndex = Math.round(value / channelSpacing);
|
||
return channelIndex < channelCount
|
||
? props.data!.channelNames[channelIndex]
|
||
: "";
|
||
},
|
||
},
|
||
splitLine: { show: false },
|
||
},
|
||
];
|
||
|
||
// 创建系列数据,每个通道有不同的Y偏移
|
||
const series: LineSeriesOption[] = props.data.y.map((channelData, index) => ({
|
||
name: props.data!.channelNames[index],
|
||
type: "line",
|
||
data: channelData.map((value) => value + index * channelSpacing + 0.2),
|
||
step: "end",
|
||
lineStyle: {
|
||
width: 2,
|
||
},
|
||
areaStyle: {
|
||
opacity: 0.3,
|
||
origin: index * channelSpacing,
|
||
},
|
||
symbol: "none",
|
||
// 优化性能配置
|
||
sampling: "lttb",
|
||
// large: true,
|
||
// largeThreshold: 2000,
|
||
// progressive: 2000,
|
||
// 减少动画以避免闪烁
|
||
animation: false,
|
||
}));
|
||
|
||
return {
|
||
// 全局动画配置
|
||
animation: false,
|
||
tooltip: {
|
||
trigger: "axis",
|
||
axisPointer: {
|
||
type: "line",
|
||
label: {
|
||
backgroundColor: "#6a7985",
|
||
},
|
||
// 减少axisPointer的动画
|
||
animation: false,
|
||
},
|
||
formatter: (params: any) => {
|
||
if (Array.isArray(params) && params.length > 0) {
|
||
const timeValue = props.data!.x[params[0].dataIndex];
|
||
const dataIndex = params[0].dataIndex;
|
||
|
||
let tooltip = `Time: ${timeValue.toFixed(3)}${props.data!.xUnit}<br/>`;
|
||
|
||
// 显示所有通道在当前时间点的原始数值(0或1)
|
||
props.data!.channelNames.forEach((channelName, index) => {
|
||
const originalValue = props.data!.y[index][dataIndex];
|
||
tooltip += `${channelName}: ${originalValue}<br/>`;
|
||
});
|
||
|
||
return tooltip;
|
||
}
|
||
return "";
|
||
},
|
||
// 优化tooltip性能
|
||
hideDelay: 100,
|
||
},
|
||
toolbox: {
|
||
feature: {
|
||
restore: {},
|
||
},
|
||
},
|
||
grid: grids,
|
||
xAxis: xAxis,
|
||
yAxis: yAxis,
|
||
dataZoom: [
|
||
{
|
||
show: true,
|
||
realtime: true,
|
||
start: 0,
|
||
end: 100,
|
||
},
|
||
{
|
||
type: "inside",
|
||
realtime: true,
|
||
start: 0,
|
||
end: 100,
|
||
},
|
||
],
|
||
series: series,
|
||
};
|
||
});
|
||
</script>
|