feat: 完成逻辑分析仪前端设计
This commit is contained in:
@@ -1,17 +1,43 @@
|
||||
<template>
|
||||
<div class="w-full h-150">
|
||||
<v-chart
|
||||
v-if="data"
|
||||
class="w-full h-full"
|
||||
:option="option"
|
||||
autoresize
|
||||
<div
|
||||
class="w-full"
|
||||
:class="{
|
||||
'h-48': !analyzer.logicData.value,
|
||||
'h-150': analyzer.logicData.value,
|
||||
}"
|
||||
>
|
||||
<v-chart
|
||||
v-if="analyzer.logicData.value"
|
||||
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"
|
||||
class="w-full h-full flex flex-col gap-6 items-center justify-center"
|
||||
>
|
||||
暂无数据
|
||||
<div class="text-center">
|
||||
<h3 class="text-xl font-semibold text-slate-600 mb-2">
|
||||
暂无逻辑分析数据
|
||||
</h3>
|
||||
<p class="text-sm text-slate-500">点击下方按钮生成测试数据用于观察</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="group relative px-8 py-3 bg-gradient-to-r from-blue-500 to-purple-600 hover:from-blue-600 hover:to-purple-700 text-white font-medium rounded-lg shadow-lg hover:shadow-xl transform hover:scale-105 transition-all duration-200 ease-in-out focus:outline-none focus:ring-4 focus:ring-blue-300 active:scale-95"
|
||||
@click="handleGenerateTestData"
|
||||
>
|
||||
<span class="flex items-center gap-2">
|
||||
<RefreshCcw
|
||||
class="w-5 h-5 group-hover:rotate-180 transition-transform duration-300"
|
||||
/>
|
||||
生成测试数据
|
||||
</span>
|
||||
<div
|
||||
class="absolute inset-0 bg-white opacity-0 group-hover:opacity-20 rounded-lg transition-opacity duration-200"
|
||||
></div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -19,7 +45,8 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, shallowRef } from "vue";
|
||||
import VChart from "vue-echarts";
|
||||
import type { LogicDataType } from "./index";
|
||||
import { generateTestLogicData } from "./index";
|
||||
import { RefreshCcw } from "lucide-vue-next";
|
||||
|
||||
// Echarts
|
||||
import { use } from "echarts/core";
|
||||
@@ -45,6 +72,9 @@ import type {
|
||||
XAXisOption,
|
||||
YAXisOption,
|
||||
} from "echarts/types/dist/shared";
|
||||
import { useLogicAnalyzerState } from "./LogicAnalyzerManager";
|
||||
import { useRequiredInjection } from "@/utils/Common";
|
||||
import { isUndefined } from "lodash";
|
||||
|
||||
use([
|
||||
TooltipComponent,
|
||||
@@ -65,24 +95,19 @@ type EChartsOption = ComposeOption<
|
||||
| LineSeriesOption
|
||||
>;
|
||||
|
||||
// Define props
|
||||
interface Props {
|
||||
data?: LogicDataType;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const analyzer = useRequiredInjection(useLogicAnalyzerState);
|
||||
|
||||
// 添加更新选项来减少重绘
|
||||
const updateOptions = shallowRef({
|
||||
notMerge: false,
|
||||
lazyUpdate: true,
|
||||
silent: false
|
||||
silent: false,
|
||||
});
|
||||
|
||||
const option = computed((): EChartsOption => {
|
||||
if (!props.data) return {};
|
||||
if (isUndefined(analyzer.logicData.value)) return {};
|
||||
|
||||
const channelCount = props.data.y.length;
|
||||
const channelCount = analyzer.logicData.value.y.length;
|
||||
const channelSpacing = 2; // 每个通道之间的间距
|
||||
|
||||
// 使用单个网格
|
||||
@@ -100,9 +125,12 @@ const option = computed((): EChartsOption => {
|
||||
{
|
||||
type: "category",
|
||||
boundaryGap: false,
|
||||
data: props.data!.x.map((x) => x.toFixed(3)),
|
||||
data: analyzer.logicData.value.x.map((x) => x.toFixed(3)),
|
||||
axisLabel: {
|
||||
formatter: (value: string) => `${value}${props.data!.xUnit}`,
|
||||
formatter: (value: string) =>
|
||||
analyzer.logicData.value
|
||||
? `${value}${analyzer.logicData.value.xUnit}`
|
||||
: `${value}`,
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -117,8 +145,8 @@ const option = computed((): EChartsOption => {
|
||||
axisLabel: {
|
||||
formatter: (value: number) => {
|
||||
const channelIndex = Math.round(value / channelSpacing);
|
||||
return channelIndex < channelCount
|
||||
? props.data!.channelNames[channelIndex]
|
||||
return channelIndex < channelCount && analyzer.logicData.value
|
||||
? analyzer.logicData.value.channelNames[channelIndex]
|
||||
: "";
|
||||
},
|
||||
},
|
||||
@@ -127,27 +155,33 @@ const option = computed((): EChartsOption => {
|
||||
];
|
||||
|
||||
// 创建系列数据,每个通道有不同的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,
|
||||
}));
|
||||
const series: LineSeriesOption[] = analyzer.logicData.value.y.map(
|
||||
(channelData: number[], index: number) => ({
|
||||
name:
|
||||
analyzer.logicData.value?.channelNames?.[index] ??
|
||||
`Channel ${index + 1}`,
|
||||
type: "line",
|
||||
data: channelData.map(
|
||||
(value: number) => 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 {
|
||||
// 全局动画配置
|
||||
@@ -164,16 +198,21 @@ const option = computed((): EChartsOption => {
|
||||
},
|
||||
formatter: (params: any) => {
|
||||
if (Array.isArray(params) && params.length > 0) {
|
||||
const timeValue = props.data!.x[params[0].dataIndex];
|
||||
const timeValue = analyzer.logicData.value!.x[params[0].dataIndex];
|
||||
const dataIndex = params[0].dataIndex;
|
||||
|
||||
let tooltip = `Time: ${timeValue.toFixed(3)}${props.data!.xUnit}<br/>`;
|
||||
let tooltip = `Time: ${timeValue.toFixed(3)}${analyzer.logicData.value!.xUnit}<br/>`;
|
||||
|
||||
// 显示所有通道在当前时间点的原始数值(0或1)
|
||||
props.data!.channelNames.forEach((channelName, index) => {
|
||||
const originalValue = props.data!.y[index][dataIndex];
|
||||
tooltip += `${channelName}: ${originalValue}<br/>`;
|
||||
});
|
||||
if (analyzer.logicData.value) {
|
||||
analyzer.logicData.value.channelNames.forEach(
|
||||
(channelName: string, index: number) => {
|
||||
const originalValue =
|
||||
analyzer.logicData.value!.y[index][dataIndex];
|
||||
tooltip += `${channelName}: ${originalValue}<br/>`;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return tooltip;
|
||||
}
|
||||
@@ -207,4 +246,8 @@ const option = computed((): EChartsOption => {
|
||||
series: series,
|
||||
};
|
||||
});
|
||||
|
||||
function handleGenerateTestData() {
|
||||
analyzer.logicData.value = generateTestLogicData();
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user