feat: frontend add boundary scan

This commit is contained in:
2025-05-19 13:30:06 +08:00
parent 2a3ef1ea7d
commit 5042bf8ce5
12 changed files with 611 additions and 538 deletions

View File

@@ -1,106 +1,75 @@
<template>
<div class="seven-segment-display" :style="{ width: width + 'px', height: height + 'px', position: 'relative' }"> <svg
xmlns="http://www.w3.org/2000/svg"
:width="width"
:height="height"
viewBox="0 0 120 220"
class="display"
>
<div class="seven-segment-display" :style="{
width: width + 'px',
height: height + 'px',
position: 'relative',
}">
<svg xmlns="http://www.w3.org/2000/svg" :width="width" :height="height" viewBox="0 0 120 220" class="display">
<!-- 数码管基座 -->
<rect width="120" height="180" x="0" y="0" fill="#222" rx="10" ry="10" />
<rect width="110" height="170" x="5" y="5" fill="#333" rx="5" ry="5" />
<!-- 7 + 小数点每个段由多边形表示重新设计点位置使其更接近实际数码管 -->
<!-- 7 + 小数点每个段由多边形表示重新设计点位置使其更接近实际数码管 -->
<!-- a段 (顶部横线) -->
<polygon
:points="'30,20 90,20 98,28 82,36 38,36 22,28'"
:fill="isSegmentActive('a') ? segmentColor : inactiveColor"
:style="{ opacity: isSegmentActive('a') ? 1 : 0.15 }"
class="segment"
/>
<polygon :points="'30,20 90,20 98,28 82,36 38,36 22,28'"
:fill="isSegmentActive('a') ? segmentColor : inactiveColor"
:style="{ opacity: isSegmentActive('a') ? 1 : 0.15 }" class="segment" />
<!-- b段 (右上竖线) -->
<polygon
:points="'100,30 108,38 108,82 100,90 92,82 92,38'"
:fill="isSegmentActive('b') ? segmentColor : inactiveColor"
:style="{ opacity: isSegmentActive('b') ? 1 : 0.15 }"
class="segment"
/>
<polygon :points="'100,30 108,38 108,82 100,90 92,82 92,38'"
:fill="isSegmentActive('b') ? segmentColor : inactiveColor"
:style="{ opacity: isSegmentActive('b') ? 1 : 0.15 }" class="segment" />
<!-- c段 (右下竖线) -->
<polygon
:points="'100,90 108,98 108,142 100,150 92,142 92,98'"
:fill="isSegmentActive('c') ? segmentColor : inactiveColor"
:style="{ opacity: isSegmentActive('c') ? 1 : 0.15 }"
class="segment"
/>
<polygon :points="'100,90 108,98 108,142 100,150 92,142 92,98'"
:fill="isSegmentActive('c') ? segmentColor : inactiveColor"
:style="{ opacity: isSegmentActive('c') ? 1 : 0.15 }" class="segment" />
<!-- d段 (底部横线) -->
<polygon
:points="'30,160 90,160 98,152 82,144 38,144 22,152'"
:fill="isSegmentActive('d') ? segmentColor : inactiveColor"
:style="{ opacity: isSegmentActive('d') ? 1 : 0.15 }"
class="segment"
/>
<polygon :points="'30,160 90,160 98,152 82,144 38,144 22,152'"
:fill="isSegmentActive('d') ? segmentColor : inactiveColor"
:style="{ opacity: isSegmentActive('d') ? 1 : 0.15 }" class="segment" />
<!-- e段 (左下竖线) -->
<polygon
:points="'20,90 28,98 28,142 20,150 12,142 12,98'"
:fill="isSegmentActive('e') ? segmentColor : inactiveColor"
:style="{ opacity: isSegmentActive('e') ? 1 : 0.15 }"
class="segment"
/>
<polygon :points="'20,90 28,98 28,142 20,150 12,142 12,98'"
:fill="isSegmentActive('e') ? segmentColor : inactiveColor"
:style="{ opacity: isSegmentActive('e') ? 1 : 0.15 }" class="segment" />
<!-- f段 (左上竖线) -->
<polygon
:points="'20,30 28,38 28,82 20,90 12,82 12,38'"
:fill="isSegmentActive('f') ? segmentColor : inactiveColor"
:style="{ opacity: isSegmentActive('f') ? 1 : 0.15 }"
class="segment"
/>
<polygon :points="'20,30 28,38 28,82 20,90 12,82 12,38'"
:fill="isSegmentActive('f') ? segmentColor : inactiveColor"
:style="{ opacity: isSegmentActive('f') ? 1 : 0.15 }" class="segment" />
<!-- g段 (中间横线) -->
<polygon
:points="'30,90 38,82 82,82 90,90 82,98 38,98'"
:fill="isSegmentActive('g') ? segmentColor : inactiveColor"
:style="{ opacity: isSegmentActive('g') ? 1 : 0.15 }"
class="segment"
/> <!-- dp段 (小数点) -->
<circle
cx="108"
cy="154"
r="6"
:fill="isSegmentActive('dp') ? segmentColor : inactiveColor"
:style="{ opacity: isSegmentActive('dp') ? 1 : 0.15 }"
class="segment"
/>
<polygon :points="'30,90 38,82 82,82 90,90 82,98 38,98'"
:fill="isSegmentActive('g') ? segmentColor : inactiveColor"
:style="{ opacity: isSegmentActive('g') ? 1 : 0.15 }" class="segment" />
<!-- dp段 (小数点) -->
<circle cx="108" cy="154" r="6" :fill="isSegmentActive('dp') ? segmentColor : inactiveColor"
:style="{ opacity: isSegmentActive('dp') ? 1 : 0.15 }" class="segment" />
</svg>
<!-- 引脚 -->
<div v-for="pin in pins" :key="pin.pinId"
:style="{
position: 'absolute',
left: `${pin.x * props.size}px`,
top: `${pin.y * props.size}px`,
transform: 'translate(-50%, -50%)'
}"
:data-pin-wrapper="`${pin.pinId}`"
:data-pin-x="`${pin.x * props.size}`"
:data-pin-y="`${pin.y * props.size}`">
<Pin
:ref="el => { if(el) pinRefs[pin.pinId] = el }"
:label="pin.pinId"
:constraint="pin.constraint"
:pinId="pin.pinId"
@pin-click="$emit('pin-click', $event)"
/>
<div v-for="pin in pins" :key="pin.pinId" :style="{
position: 'absolute',
left: `${pin.x * props.size}px`,
top: `${pin.y * props.size}px`,
transform: 'translate(-50%, -50%)',
}" :data-pin-wrapper="`${pin.pinId}`" :data-pin-x="`${pin.x * props.size}`"
:data-pin-y="`${pin.y * props.size}`">
<Pin :ref="(el) => {
if (el) pinRefs[pin.pinId] = el;
}
" :label="pin.pinId" :constraint="pin.constraint" :pinId="pin.pinId" @pin-click="$emit('pin-click', $event)" />
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, watch, onMounted, onUnmounted } from 'vue';
import { getConstraintState, onConstraintStateChange } from '../../stores/constraints';
import Pin from './Pin.vue';
import { ref, computed, watch, onMounted, onUnmounted } from "vue";
import { useConstraintsStore } from "../../stores/constraints";
import Pin from "./Pin.vue";
const { getConstraintState, onConstraintStateChange } = useConstraintsStore();
// 存储Pin引用
const pinRefs = ref<Record<string, any>>({});
@@ -115,41 +84,41 @@ interface SevenSegmentDisplayProps {
x: number;
y: number;
}[];
cathodeType?: 'common' | 'anode'; // 共阴极或共阳极
cathodeType?: "common" | "anode"; // 共阴极或共阳极
}
const props = withDefaults(defineProps<SevenSegmentDisplayProps>(), {
size: 1,
color: 'red',
cathodeType: 'common', // 默认为共阴极
color: "red",
cathodeType: "common", // 默认为共阴极
pins: () => [
{ pinId: 'a', constraint: '', x: 10 , y: 170 }, // a段
{ pinId: 'b', constraint: '', x: 25-1 , y: 170 }, // b段
{ pinId: 'c', constraint: '', x: 40-2 , y: 170 }, // c段
{ pinId: 'd', constraint: '', x: 55-3 , y: 170 }, // d段
{ pinId: 'e', constraint: '', x: 70-4 , y: 170 }, // e段
{ pinId: 'f', constraint: '', x: 85-5 , y: 170 }, // f段
{ pinId: 'g', constraint: '', x: 100-6, y: 170 }, // g段
{ pinId: 'dp', constraint: '', x: 115-7, y: 170 }, // 小数点
{ pinId: 'COM', constraint: '', x: 60 , y: 10 } // 公共端,稍微低一点
]
{ pinId: "a", constraint: "", x: 10, y: 170 }, // a段
{ pinId: "b", constraint: "", x: 25 - 1, y: 170 }, // b段
{ pinId: "c", constraint: "", x: 40 - 2, y: 170 }, // c段
{ pinId: "d", constraint: "", x: 55 - 3, y: 170 }, // d段
{ pinId: "e", constraint: "", x: 70 - 4, y: 170 }, // e段
{ pinId: "f", constraint: "", x: 85 - 5, y: 170 }, // f段
{ pinId: "g", constraint: "", x: 100 - 6, y: 170 }, // g段
{ pinId: "dp", constraint: "", x: 115 - 7, y: 170 }, // 小数点
{ pinId: "COM", constraint: "", x: 60, y: 10 }, // 公共端,稍微低一点
],
});
const width = computed(() => 120 * props.size);
const height = computed(() => 220 * props.size);
// 计算段颜色和非激活状态颜色
const segmentColor = computed(() => props.color || 'red');
const inactiveColor = computed(() => '#FFFFFF');
const segmentColor = computed(() => props.color || "red");
const inactiveColor = computed(() => "#FFFFFF");
// 监听props变化
watch(
() => props,
(newProps) => {
console.log('SevenSegmentDisplay props changed:', newProps);
console.log("SevenSegmentDisplay props changed:", newProps);
updateSegmentStates();
},
{ deep: true }
{ deep: true },
);
// 段引脚状态
@@ -165,29 +134,34 @@ const segmentStates = ref({
});
// 判断段是否激活
function isSegmentActive(segment: 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'dp'): boolean {
function isSegmentActive(
segment: "a" | "b" | "c" | "d" | "e" | "f" | "g" | "dp",
): boolean {
return segmentStates.value[segment];
}
// 更新引脚状态的函数
function updateSegmentStates() {
for (const pin of props.pins) {
if (['a', 'b', 'c', 'd', 'e', 'f', 'g', 'dp'].includes(pin.pinId)) {
if (["a", "b", "c", "d", "e", "f", "g", "dp"].includes(pin.pinId)) {
// 如果constraint为空则默认为未激活状态
if (!pin.constraint) {
segmentStates.value[pin.pinId as keyof typeof segmentStates.value] = false;
segmentStates.value[pin.pinId as keyof typeof segmentStates.value] =
false;
continue;
}
const pinState = getConstraintState(pin.constraint);
// 根据阴极/阳极类型反转逻辑
if (props.cathodeType === 'common') {
if (props.cathodeType === "common") {
// 共阴极: 高电平激活段
segmentStates.value[pin.pinId as keyof typeof segmentStates.value] = pinState === 'high';
segmentStates.value[pin.pinId as keyof typeof segmentStates.value] =
pinState === "high";
} else {
// 共阳极: 低电平激活段
segmentStates.value[pin.pinId as keyof typeof segmentStates.value] = pinState === 'low';
segmentStates.value[pin.pinId as keyof typeof segmentStates.value] =
pinState === "low";
}
}
}
@@ -195,8 +169,11 @@ function updateSegmentStates() {
// 监听约束状态变化
function onConstraintChange(constraint: string, level: string) {
const affectedPin = props.pins.find(pin => pin.constraint === constraint);
if (affectedPin && ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'dp'].includes(affectedPin.pinId)) {
const affectedPin = props.pins.find((pin) => pin.constraint === constraint);
if (
affectedPin &&
["a", "b", "c", "d", "e", "f", "g", "dp"].includes(affectedPin.pinId)
) {
updateSegmentStates();
}
}
@@ -213,7 +190,7 @@ onUnmounted(() => {
// 暴露属性和方法
defineExpose({
updateSegmentStates
updateSegmentStates,
});
</script>
@@ -224,7 +201,9 @@ defineExpose({
}
.segment {
transition: opacity 0.2s, fill 0.2s;
transition:
opacity 0.2s,
fill 0.2s;
}
/* 数码管发光效果 */
@@ -238,19 +217,19 @@ defineExpose({
export function getDefaultProps() {
return {
size: 1,
color: 'red',
cathodeType: 'common',
color: "red",
cathodeType: "common",
pins: [
{ pinId: 'a', constraint: '', x: 10 , y: 170 },
{ pinId: 'b', constraint: '', x: 25-1 , y: 170 },
{ pinId: 'c', constraint: '', x: 40-2 , y: 170 },
{ pinId: 'd', constraint: '', x: 55-3 , y: 170 },
{ pinId: 'e', constraint: '', x: 70-4 , y: 170 },
{ pinId: 'f', constraint: '', x: 85-5 , y: 170 },
{ pinId: 'g', constraint: '', x: 100-6, y: 170 },
{ pinId: 'dp', constraint: '', x: 115-7, y: 170 },
{ pinId: 'COM', constraint: '', x: 60 , y: 10 }
]
{ pinId: "a", constraint: "", x: 10, y: 170 },
{ pinId: "b", constraint: "", x: 25 - 1, y: 170 },
{ pinId: "c", constraint: "", x: 40 - 2, y: 170 },
{ pinId: "d", constraint: "", x: 55 - 3, y: 170 },
{ pinId: "e", constraint: "", x: 70 - 4, y: 170 },
{ pinId: "f", constraint: "", x: 85 - 5, y: 170 },
{ pinId: "g", constraint: "", x: 100 - 6, y: 170 },
{ pinId: "dp", constraint: "", x: 115 - 7, y: 170 },
{ pinId: "COM", constraint: "", x: 60, y: 10 },
],
};
}
</script>