257 lines
7.8 KiB
Vue
257 lines
7.8 KiB
Vue
<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"
|
||
>
|
||
<!-- 数码管基座 -->
|
||
<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段 + 小数点,每个段由多边形表示,重新设计点位置使其更接近实际数码管 -->
|
||
<!-- 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"
|
||
/>
|
||
|
||
<!-- 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"
|
||
/>
|
||
|
||
<!-- 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"
|
||
/>
|
||
|
||
<!-- 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"
|
||
/>
|
||
|
||
<!-- 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"
|
||
/>
|
||
|
||
<!-- 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"
|
||
/>
|
||
|
||
<!-- 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"
|
||
/>
|
||
</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>
|
||
</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';
|
||
|
||
// 存储Pin引用
|
||
const pinRefs = ref<Record<string, any>>({});
|
||
|
||
// 数码管属性
|
||
interface SevenSegmentDisplayProps {
|
||
size?: number;
|
||
color?: string;
|
||
pins?: {
|
||
pinId: string;
|
||
constraint: string;
|
||
x: number;
|
||
y: number;
|
||
}[];
|
||
cathodeType?: 'common' | 'anode'; // 共阴极或共阳极
|
||
}
|
||
|
||
const props = withDefaults(defineProps<SevenSegmentDisplayProps>(), {
|
||
size: 1,
|
||
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 } // 公共端,稍微低一点
|
||
]
|
||
});
|
||
|
||
const width = computed(() => 120 * props.size);
|
||
const height = computed(() => 220 * props.size);
|
||
|
||
// 计算段颜色和非激活状态颜色
|
||
const segmentColor = computed(() => props.color || 'red');
|
||
const inactiveColor = computed(() => '#FFFFFF');
|
||
|
||
// 监听props变化
|
||
watch(
|
||
() => props,
|
||
(newProps) => {
|
||
console.log('SevenSegmentDisplay props changed:', newProps);
|
||
updateSegmentStates();
|
||
},
|
||
{ deep: true }
|
||
);
|
||
|
||
// 段引脚状态
|
||
const segmentStates = ref({
|
||
a: false,
|
||
b: false,
|
||
c: false,
|
||
d: false,
|
||
e: false,
|
||
f: false,
|
||
g: false,
|
||
dp: false,
|
||
});
|
||
|
||
// 判断段是否激活
|
||
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)) {
|
||
// 如果constraint为空,则默认为未激活状态
|
||
if (!pin.constraint) {
|
||
segmentStates.value[pin.pinId as keyof typeof segmentStates.value] = false;
|
||
continue;
|
||
}
|
||
|
||
const pinState = getConstraintState(pin.constraint);
|
||
|
||
// 根据阴极/阳极类型反转逻辑
|
||
if (props.cathodeType === 'common') {
|
||
// 共阴极: 高电平激活段
|
||
segmentStates.value[pin.pinId as keyof typeof segmentStates.value] = pinState === 'high';
|
||
} else {
|
||
// 共阳极: 低电平激活段
|
||
segmentStates.value[pin.pinId as keyof typeof segmentStates.value] = pinState === 'low';
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 监听约束状态变化
|
||
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)) {
|
||
updateSegmentStates();
|
||
}
|
||
}
|
||
|
||
// 生命周期钩子
|
||
onMounted(() => {
|
||
updateSegmentStates();
|
||
onConstraintStateChange(onConstraintChange);
|
||
});
|
||
|
||
onUnmounted(() => {
|
||
// 清理约束状态监听
|
||
});
|
||
|
||
// 暴露属性和方法
|
||
defineExpose({
|
||
updateSegmentStates
|
||
});
|
||
</script>
|
||
|
||
<style scoped>
|
||
.seven-segment-display {
|
||
display: inline-block;
|
||
position: relative;
|
||
}
|
||
|
||
.segment {
|
||
transition: opacity 0.2s, fill 0.2s;
|
||
}
|
||
|
||
/* 数码管发光效果 */
|
||
.segment[style*="opacity: 1"] {
|
||
filter: drop-shadow(0 0 4px v-bind(segmentColor)) drop-shadow(0 0 2px v-bind(segmentColor));
|
||
}
|
||
</style>
|
||
|
||
<!-- 导出默认属性函数供外部使用 -->
|
||
<script lang="ts">
|
||
export function getDefaultProps() {
|
||
return {
|
||
size: 1,
|
||
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 }
|
||
]
|
||
};
|
||
}
|
||
</script>
|