feat: Enhance equipment components with pin functionality and constraint management
- Added pin support to MechanicalButton, enabling pin-click events and componentId handling. - Updated Pin component to manage constraint states and colors dynamically. - Integrated SMT_LED with pin functionality, allowing LED state to respond to constraints. - Enhanced Wire component to reflect constraint colors and manage wire states based on pin connections. - Introduced wireManager for managing wire states and constraints. - Implemented a constraints store for managing and notifying constraint state changes across components. - Updated component configuration to remove appearance options and clarify constraint descriptions. - Improved ProjectView to handle optional chaining for props and ensure robust data handling. - Initialized constraint communication in main application entry point.
This commit is contained in:
@@ -76,13 +76,15 @@
|
||||
pointerEvents: 'auto'
|
||||
}">
|
||||
<Pin
|
||||
ref="pinRef"
|
||||
direction="output"
|
||||
type="digital"
|
||||
appearance="None"
|
||||
:label="props.label"
|
||||
:constraint="props.constraint"
|
||||
:size="0.8"
|
||||
:componentId="props.componentId"
|
||||
@value-change="handlePinValueChange"
|
||||
@pin-click="handlePinClick"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -91,15 +93,18 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, computed } from 'vue';
|
||||
import Pin from './Pin.vue';
|
||||
import { notifyConstraintChange } from '../../stores/constraints';
|
||||
|
||||
const pinRef = ref<any>(null);
|
||||
|
||||
// 从Pin组件继承属性
|
||||
interface PinProps {
|
||||
label?: string;
|
||||
constraint?: string;
|
||||
componentId?: string; // 添加componentId属性
|
||||
// 这些属性被预设为固定值,但仍然包含在类型中以便完整继承
|
||||
direction?: 'input' | 'output' | 'inout';
|
||||
type?: 'digital' | 'analog';
|
||||
appearance?: 'None' | 'Dip' | 'SMT';
|
||||
}
|
||||
|
||||
// 按钮特有属性
|
||||
@@ -118,10 +123,10 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
buttonText: '',
|
||||
label: 'BTN',
|
||||
constraint: '',
|
||||
componentId: 'button-default', // 添加默认componentId
|
||||
// 这些值会被覆盖,但需要默认值以满足类型要求
|
||||
direction: 'output',
|
||||
type: 'digital',
|
||||
appearance: 'Dip'
|
||||
type: 'digital'
|
||||
});
|
||||
|
||||
// 计算实际宽高
|
||||
@@ -142,7 +147,8 @@ const emit = defineEmits([
|
||||
'press',
|
||||
'release',
|
||||
'click',
|
||||
'value-change'
|
||||
'value-change',
|
||||
'pin-click'
|
||||
]);
|
||||
|
||||
// 内部状态
|
||||
@@ -155,6 +161,11 @@ function handlePinValueChange(value: any) {
|
||||
emit('value-change', value);
|
||||
}
|
||||
|
||||
// 处理Pin点击事件
|
||||
function handlePinClick(info: any) {
|
||||
emit('pin-click', info);
|
||||
}
|
||||
|
||||
// --- 按键状态逻辑 ---
|
||||
function toggleButtonState(isPressed: boolean) {
|
||||
isKeyPressed.value = isPressed;
|
||||
@@ -163,9 +174,17 @@ function toggleButtonState(isPressed: boolean) {
|
||||
// 发出事件通知父组件
|
||||
if (isPressed) {
|
||||
emit('press');
|
||||
// 如果有约束,通知约束状态变化为高电平
|
||||
if (props.constraint) {
|
||||
notifyConstraintChange(props.constraint, 'high');
|
||||
}
|
||||
} else {
|
||||
emit('release');
|
||||
emit('click');
|
||||
// 如果有约束,通知约束状态变化为低电平
|
||||
if (props.constraint) {
|
||||
notifyConstraintChange(props.constraint, 'low');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,11 +215,18 @@ defineExpose({
|
||||
// 继承自Pin的属性
|
||||
label: props.label,
|
||||
constraint: props.constraint,
|
||||
componentId: props.componentId, // 添加componentId
|
||||
// 固定的Pin属性
|
||||
direction: 'output',
|
||||
type: 'digital',
|
||||
appearance: 'None'
|
||||
})
|
||||
type: 'digital'
|
||||
}),
|
||||
// 代理 getPinPosition 到内部 Pin
|
||||
getPinPosition: (pinLabel: string) => {
|
||||
if (pinRef.value && pinRef.value.getPinPosition) {
|
||||
return pinRef.value.getPinPosition(pinLabel);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,71 +1,35 @@
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
:width="width"
|
||||
:height="height"
|
||||
:width="width"
|
||||
:height="height"
|
||||
:viewBox="'0 0 ' + viewBoxWidth + ' ' + viewBoxHeight"
|
||||
class="pin-component"
|
||||
:data-component-id="componentId"
|
||||
:data-component-id="props.componentId"
|
||||
:data-pin-label="props.label"
|
||||
> <g :transform="`translate(${viewBoxWidth/2}, ${viewBoxHeight/2})`">
|
||||
<g v-if="props.appearance === 'None'">
|
||||
>
|
||||
<g :transform="`translate(${viewBoxWidth/2}, ${viewBoxHeight/2})`">
|
||||
<g>
|
||||
<g transform="translate(-12.5, -12.5)">
|
||||
<circle
|
||||
style="fill:#909090"
|
||||
:style="{ fill: pinColor }"
|
||||
cx="12.5"
|
||||
cy="12.5"
|
||||
r="3.75"
|
||||
class="interactive"
|
||||
@click.stop="handlePinClick"
|
||||
:data-pin-element="`${props.componentId}-${props.label}`" />
|
||||
</g>
|
||||
</g>
|
||||
<g v-else-if="props.appearance === 'Dip'">
|
||||
<!-- 使用inkscape创建的SVG替代原有Dip样式 -->
|
||||
<g transform="translate(-12.5, -12.5)">
|
||||
<rect
|
||||
:style="`fill:${props.type === 'analog' ? '#2a6099' : '#000000'};fill-opacity:0.772973`"
|
||||
width="25"
|
||||
height="25"
|
||||
x="0"
|
||||
y="0"
|
||||
rx="2.5" />
|
||||
<circle
|
||||
style="fill:#ecececc5;fill-opacity:0.772973"
|
||||
cx="12.5"
|
||||
cy="12.5"
|
||||
r="3.75"
|
||||
class="interactive"
|
||||
@click.stop="handlePinClick"
|
||||
:data-pin-element="`${props.componentId}-${props.label}`" />
|
||||
<text
|
||||
style="font-size:6.85px;text-align:start;fill:#ffffff;fill-opacity:0.772973"
|
||||
x="7.3"
|
||||
y="7"
|
||||
xml:space="preserve">{{ props.label }}</text>
|
||||
</g>
|
||||
</g>
|
||||
<g v-else-if="props.appearance === 'SMT'">
|
||||
<rect x="-20" y="-10" width="40" height="20" fill="#aaa" rx="2" ry="2" />
|
||||
<rect x="-18" y="-8" width="36" height="16" :fill="getColorByType" rx="1" ry="1" />
|
||||
<rect x="-16" y="-6" width="26" height="12" :fill="getColorByType" rx="1" ry="1" />
|
||||
<text text-anchor="middle" dominant-baseline="middle" font-size="8" fill="white" x="-3">{{ props.label }}</text>
|
||||
<circle
|
||||
fill="#ecececc5"
|
||||
cx="10"
|
||||
cy="0"
|
||||
r="3.75"
|
||||
class="interactive"
|
||||
@click.stop="handlePinClick"
|
||||
:data-pin-element="`${props.componentId}-${props.label}`"
|
||||
/>
|
||||
:data-pin-element="`${props.componentId}`" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, reactive } from 'vue';
|
||||
import { ref, computed, reactive, watch, onMounted, onUnmounted } from 'vue';
|
||||
import { getConstraintColor, getConstraintState, onConstraintStateChange, notifyConstraintChange } from '../../stores/constraints';
|
||||
|
||||
// 生成唯一ID
|
||||
const uniqueId = `pin-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
||||
|
||||
interface Props {
|
||||
size?: number;
|
||||
@@ -73,7 +37,6 @@ interface Props {
|
||||
constraint?: string;
|
||||
direction?: 'input' | 'output' | 'inout';
|
||||
type?: 'digital' | 'analog';
|
||||
appearance?: 'None' | 'Dip' | 'SMT';
|
||||
componentId?: string; // 添加组件ID属性,用于唯一标识
|
||||
}
|
||||
|
||||
@@ -83,7 +46,6 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
constraint: '',
|
||||
direction: 'input',
|
||||
type: 'digital',
|
||||
appearance: 'Dip',
|
||||
componentId: 'pin-default' // 默认ID
|
||||
});
|
||||
|
||||
@@ -104,42 +66,64 @@ function handlePinClick(event: MouseEvent) {
|
||||
x: rect.left + rect.width / 2,
|
||||
y: rect.top + rect.height / 2
|
||||
};
|
||||
|
||||
console.log(`针脚 ${props.label} 被点击,位置:`, pinCenter);
|
||||
|
||||
// 发送针脚点击事件给父组件
|
||||
emit('pin-click', {
|
||||
label: props.label,
|
||||
constraint: props.constraint,
|
||||
type: props.type,
|
||||
direction: props.direction,
|
||||
// 获取针脚在页面上的位置
|
||||
position: {
|
||||
x: pinCenter.x,
|
||||
y: pinCenter.y
|
||||
},
|
||||
// 获取原始事件
|
||||
position: pinCenter,
|
||||
originalEvent: event
|
||||
});
|
||||
}
|
||||
|
||||
const width = computed(() => props.appearance === 'None' ? 40 * props.size : 30 * props.size);
|
||||
const height = computed(() => {
|
||||
if (props.appearance === 'None') return 20 * props.size;
|
||||
if (props.appearance === 'Dip') return 30 * props.size; // 调整Dip样式高度
|
||||
return 60 * props.size;
|
||||
});
|
||||
const viewBoxWidth = computed(() => props.appearance === 'None' ? 40 : 30);
|
||||
const viewBoxHeight = computed(() => {
|
||||
if (props.appearance === 'None') return 20;
|
||||
if (props.appearance === 'Dip') return 30; // 调整Dip样式视图高度
|
||||
return 60;
|
||||
});
|
||||
const width = computed(() => 40 * props.size);
|
||||
const height = computed(() => 20 * props.size);
|
||||
const viewBoxWidth = computed(() => 40);
|
||||
const viewBoxHeight = computed(() => 20);
|
||||
|
||||
const getColorByType = computed(() => {
|
||||
return props.type === 'analog' ? '#2a6099' : '#444';
|
||||
});
|
||||
|
||||
// 根据约束电平状态计算引脚颜色
|
||||
const pinColor = computed(() => {
|
||||
return getConstraintColor(props.constraint) || getColorByType.value;
|
||||
});
|
||||
|
||||
// 监听约束状态变化
|
||||
let unsubscribe: (() => void) | null = null;
|
||||
|
||||
onMounted(() => {
|
||||
// 监听约束状态变化
|
||||
if (props.constraint) {
|
||||
unsubscribe = onConstraintStateChange((constraint, level) => {
|
||||
if (constraint === props.constraint) {
|
||||
emit('value-change', { constraint, level });
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (unsubscribe) {
|
||||
unsubscribe();
|
||||
}
|
||||
});
|
||||
|
||||
watch(() => props.constraint, (newConstraint, oldConstraint) => {
|
||||
if (unsubscribe) {
|
||||
unsubscribe();
|
||||
unsubscribe = null;
|
||||
}
|
||||
if (newConstraint) {
|
||||
unsubscribe = onConstraintStateChange((constraint, level) => {
|
||||
if (constraint === newConstraint) {
|
||||
emit('value-change', { constraint, level });
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function updateAnalogValue(value: number) {
|
||||
if (props.type !== 'analog') return;
|
||||
analogValue.value = Math.max(0, Math.min(1, value));
|
||||
@@ -158,38 +142,39 @@ defineExpose({
|
||||
constraint: props.constraint,
|
||||
direction: props.direction,
|
||||
type: props.type,
|
||||
appearance: props.appearance
|
||||
}), // 添加获取针脚位置的方法
|
||||
getPinPosition: (pinLabel: string) => {
|
||||
// 在 Pin 组件中,只有一个针脚,所以直接检查标签是否匹配
|
||||
if (pinLabel !== props.label) return null;
|
||||
|
||||
// 使用组件ID和针脚标签的组合作为唯一标识符
|
||||
const selector = `[data-pin-element="${props.componentId}-${props.label}"]`;
|
||||
|
||||
const pinElement = document.querySelector(selector) as SVGElement;
|
||||
if (pinElement) {
|
||||
// 获取针脚元素的位置
|
||||
const rect = pinElement.getBoundingClientRect();
|
||||
componentId: props.componentId
|
||||
}),
|
||||
getPinPosition: (componentId: string) => {
|
||||
if (componentId !== props.componentId) return null;
|
||||
console.log('getPinPosition', componentId, props.componentId);
|
||||
const uniqueSelector = `[data-pin-element="${props.componentId}"]`;
|
||||
console.log('uniqueSelector', uniqueSelector);
|
||||
const pinElements = document.querySelectorAll(uniqueSelector);
|
||||
console.log('pinElements', pinElements);
|
||||
if (pinElements.length === 0) return null;
|
||||
if (pinElements.length === 1) {
|
||||
const rect = pinElements[0].getBoundingClientRect();
|
||||
return {
|
||||
x: rect.left + rect.width / 2,
|
||||
y: rect.top + rect.height / 2
|
||||
};
|
||||
}
|
||||
|
||||
// 如果找不到特定元素,使用SVG元素位置
|
||||
const svgSelector = `svg.pin-component[data-component-id="${props.componentId}"][data-pin-label="${props.label}"]`;
|
||||
|
||||
const svgElement = document.querySelector(svgSelector) as SVGElement;
|
||||
if (!svgElement) return null;
|
||||
|
||||
const svgRect = svgElement.getBoundingClientRect();
|
||||
|
||||
// 根据针脚类型和方向计算相对位置
|
||||
let pinX = svgRect.left + svgRect.width / 2;
|
||||
let pinY = svgRect.top + svgRect.height / 2;
|
||||
|
||||
return { x: pinX, y: pinY };
|
||||
for (const pinElement of pinElements) {
|
||||
let parentSvg = pinElement.closest('svg.pin-component');
|
||||
if (!parentSvg) continue;
|
||||
if (parentSvg.getAttribute('data-component-id') === props.componentId) {
|
||||
const rect = pinElement.getBoundingClientRect();
|
||||
return {
|
||||
x: rect.left + rect.width / 2,
|
||||
y: rect.top + rect.height / 2
|
||||
};
|
||||
}
|
||||
}
|
||||
const rect = pinElements[0].getBoundingClientRect();
|
||||
return {
|
||||
x: rect.left + rect.width / 2,
|
||||
y: rect.top + rect.height / 2
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -14,16 +14,15 @@
|
||||
<rect width="90" height="50" x="5" y="5" fill="#222" rx="3" ry="3" />
|
||||
|
||||
<!-- LED 发光部分 -->
|
||||
<rect
|
||||
<rect
|
||||
width="70"
|
||||
height="30"
|
||||
x="15"
|
||||
y="15"
|
||||
:fill="ledColor"
|
||||
:style="{ opacity: isOn ? brightness/100 : 0.2 }"
|
||||
:style="{ opacity: isOn ? 1 : 0.2 }"
|
||||
rx="15"
|
||||
ry="15"
|
||||
@click="toggleLed"
|
||||
class="interactive"
|
||||
/>
|
||||
|
||||
@@ -35,46 +34,64 @@
|
||||
x="12"
|
||||
y="12"
|
||||
:fill="ledColor"
|
||||
:style="{ opacity: brightness/100 * 0.3 }"
|
||||
:style="{ opacity: 0.3 }"
|
||||
rx="18"
|
||||
ry="18"
|
||||
filter="blur(5px)"
|
||||
class="glow"
|
||||
/>
|
||||
</svg>
|
||||
<!-- 新增:数字输入引脚Pin,放在LED左侧居中 -->
|
||||
<div style="position:absolute;left:-18px;top:50%;transform:translateY(-50%);">
|
||||
<Pin
|
||||
ref="pinRef"
|
||||
v-bind="props"
|
||||
@pin-click="$emit('pin-click', $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import { ref, computed, watch, onMounted, onUnmounted } from 'vue';
|
||||
import { getConstraintState, onConstraintStateChange } from '../../stores/constraints';
|
||||
import Pin from './Pin.vue';
|
||||
|
||||
// LED特有属性
|
||||
interface Props {
|
||||
size?: number;
|
||||
color?: string;
|
||||
initialOn?: boolean;
|
||||
brightness?: number;
|
||||
// --- 关键:暴露getPinPosition,代理到内部Pin ---
|
||||
const pinRef = ref<any>(null);
|
||||
|
||||
// 从Pin组件继承属性
|
||||
interface PinProps {
|
||||
label?: string;
|
||||
constraint?: string;
|
||||
componentId?: string; // 添加componentId属性
|
||||
// 这些属性被预设为固定值,但仍然包含在类型中以便完整继承
|
||||
direction?: 'input' | 'output' | 'inout';
|
||||
type?: 'digital' | 'analog';
|
||||
}
|
||||
|
||||
// 组件属性定义
|
||||
// LED特有属性
|
||||
interface LEDProps {
|
||||
size?: number;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
// 组合两个接口
|
||||
interface Props extends PinProps, LEDProps {}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
size: 1,
|
||||
color: 'red',
|
||||
initialOn: false,
|
||||
brightness: 80, // 亮度默认为80%
|
||||
constraint: ''
|
||||
constraint: '',
|
||||
label: 'LED',
|
||||
componentId: ''
|
||||
});
|
||||
|
||||
// 计算实际宽高
|
||||
const width = computed(() => 100 * props.size);
|
||||
const height = computed(() => 60 * props.size);
|
||||
|
||||
// 内部状态
|
||||
const isOn = ref(props.initialOn);
|
||||
const brightness = ref(props.brightness);
|
||||
const isOn = ref(false);
|
||||
|
||||
// LED 颜色映射表
|
||||
const colorMap: Record<string, string> = {
|
||||
'red': '#ff3333',
|
||||
'green': '#33ff33',
|
||||
@@ -85,70 +102,64 @@ const colorMap: Record<string, string> = {
|
||||
'purple': '#9933ff'
|
||||
};
|
||||
|
||||
// 计算实际LED颜色
|
||||
const ledColor = computed(() => {
|
||||
return colorMap[props.color.toLowerCase()] || props.color;
|
||||
});
|
||||
|
||||
// 定义组件发出的事件
|
||||
const emit = defineEmits([
|
||||
'toggle',
|
||||
'brightness-change',
|
||||
'value-change'
|
||||
]);
|
||||
// 监听约束状态变化
|
||||
let unsubscribe: (() => void) | null = null;
|
||||
|
||||
// 手动切换LED状态
|
||||
function toggleLed() {
|
||||
isOn.value = !isOn.value;
|
||||
emit('toggle', isOn.value);
|
||||
emit('value-change', {
|
||||
isOn: isOn.value,
|
||||
brightness: brightness.value
|
||||
});
|
||||
}
|
||||
|
||||
// 设置亮度
|
||||
function setBrightness(value: number) {
|
||||
// 限制亮度值在0-100范围内
|
||||
brightness.value = Math.max(0, Math.min(100, value));
|
||||
emit('brightness-change', brightness.value);
|
||||
emit('value-change', {
|
||||
isOn: isOn.value,
|
||||
brightness: brightness.value
|
||||
});
|
||||
}
|
||||
|
||||
// 手动设置LED开关状态
|
||||
function setLedState(on: boolean) {
|
||||
isOn.value = on;
|
||||
emit('toggle', isOn.value);
|
||||
emit('value-change', {
|
||||
isOn: isOn.value,
|
||||
brightness: brightness.value
|
||||
});
|
||||
}
|
||||
|
||||
// 监听props变化
|
||||
watch(() => props.brightness, (newVal) => {
|
||||
brightness.value = newVal;
|
||||
onMounted(() => {
|
||||
if (props.constraint) {
|
||||
unsubscribe = onConstraintStateChange((constraint, level) => {
|
||||
if (constraint === props.constraint) {
|
||||
isOn.value = (level === 'high');
|
||||
}
|
||||
});
|
||||
// 初始化LED状态
|
||||
const currentState = getConstraintState(props.constraint);
|
||||
isOn.value = (currentState === 'high');
|
||||
}
|
||||
});
|
||||
|
||||
watch(() => props.initialOn, (newVal) => {
|
||||
isOn.value = newVal;
|
||||
onUnmounted(() => {
|
||||
if (unsubscribe) {
|
||||
unsubscribe();
|
||||
}
|
||||
});
|
||||
|
||||
watch(() => props.constraint, (newConstraint) => {
|
||||
if (unsubscribe) {
|
||||
unsubscribe();
|
||||
unsubscribe = null;
|
||||
}
|
||||
if (newConstraint) {
|
||||
unsubscribe = onConstraintStateChange((constraint, level) => {
|
||||
if (constraint === newConstraint) {
|
||||
isOn.value = (level === 'high');
|
||||
}
|
||||
});
|
||||
// 初始化LED状态
|
||||
const currentState = getConstraintState(newConstraint);
|
||||
isOn.value = (currentState === 'high');
|
||||
}
|
||||
});
|
||||
|
||||
// 向外暴露方法
|
||||
defineExpose({
|
||||
toggleLed,
|
||||
setBrightness,
|
||||
setLedState,
|
||||
getInfo: () => ({
|
||||
// LED特有属性
|
||||
color: props.color,
|
||||
isOn: isOn.value,
|
||||
brightness: brightness.value,
|
||||
constraint: props.constraint
|
||||
})
|
||||
constraint: props.constraint,
|
||||
componentId: props.componentId,
|
||||
direction: 'input',
|
||||
type: 'digital'
|
||||
}),
|
||||
getPinPosition: (componentId: string) => {
|
||||
if (pinRef.value && pinRef.value.getPinPosition) {
|
||||
return pinRef.value.getPinPosition(componentId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, defineEmits, reactive } from 'vue';
|
||||
import { computed, defineEmits, reactive, watch, onMounted, onUnmounted } from 'vue';
|
||||
import { getConstraintColor, getConstraintState, onConstraintStateChange } from '../../stores/constraints';
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
@@ -53,7 +54,17 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
constraint: ''
|
||||
});
|
||||
|
||||
const computedStroke = computed(() => props.isActive ? '#ff9800' : props.strokeColor);
|
||||
// 响应约束状态变化的颜色
|
||||
const constraintColor = computed(() => {
|
||||
if (!props.constraint) return props.strokeColor;
|
||||
return getConstraintColor(props.constraint);
|
||||
});
|
||||
|
||||
// 计算实际使用的颜色:isActive优先,其次是constraint电平颜色,最后是默认色
|
||||
const computedStroke = computed(() => {
|
||||
if (props.isActive) return '#ff9800';
|
||||
return constraintColor.value || props.strokeColor;
|
||||
});
|
||||
|
||||
const emit = defineEmits(['click', 'update:active', 'update:position']);
|
||||
|
||||
@@ -95,6 +106,44 @@ function calculateOrthogonalPath(startX: number, startY: number, endX: number, e
|
||||
return `M ${startX} ${startY} L ${startX} ${middleY} L ${endX} ${middleY} L ${endX} ${endY}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 监听约束状态变化
|
||||
let unsubscribe: (() => void) | null = null;
|
||||
|
||||
onMounted(() => {
|
||||
// 监听约束状态变化
|
||||
if (props.constraint) {
|
||||
unsubscribe = onConstraintStateChange((constraint, level) => {
|
||||
if (constraint === props.constraint) {
|
||||
// 约束状态变化,触发重新渲染
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
// 清理监听
|
||||
if (unsubscribe) {
|
||||
unsubscribe();
|
||||
}
|
||||
});
|
||||
|
||||
// 监听约束属性变化
|
||||
watch(() => props.constraint, (newConstraint, oldConstraint) => {
|
||||
if (unsubscribe) {
|
||||
unsubscribe();
|
||||
unsubscribe = null;
|
||||
}
|
||||
|
||||
if (newConstraint) {
|
||||
unsubscribe = onConstraintStateChange((constraint, level) => {
|
||||
if (constraint === newConstraint) {
|
||||
// 约束状态变化,触发重新渲染
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 暴露方法,用于获取这条连线的信息
|
||||
defineExpose({ id: props.id,
|
||||
getInfo: () => ({
|
||||
|
||||
@@ -154,18 +154,6 @@ const componentConfigs: Record<string, ComponentConfig> = {
|
||||
{ value: 'analog', label: 'analog' }
|
||||
],
|
||||
description: '引脚的模数特性,数字或模拟'
|
||||
},
|
||||
{
|
||||
name: 'appearance',
|
||||
type: 'select',
|
||||
label: '引脚样式',
|
||||
default: 'Dip',
|
||||
options: [
|
||||
{ value: 'None', label: 'None' },
|
||||
{ value: 'Dip', label: 'Dip' },
|
||||
{ value: 'SMT', label: 'SMT' }
|
||||
],
|
||||
description: '引脚的外观样式,不影响功能'
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -313,9 +301,9 @@ const componentConfigs: Record<string, ComponentConfig> = {
|
||||
{
|
||||
name: 'constraint',
|
||||
type: 'string',
|
||||
label: '连接约束',
|
||||
label: '引脚约束',
|
||||
default: '',
|
||||
description: '相同约束字符串的组件将被视为有电气连接'
|
||||
description: '相同约束字符串的引脚将被视为有电气连接'
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -341,7 +329,7 @@ const componentConfigs: Record<string, ComponentConfig> = {
|
||||
default: '#4a5568',
|
||||
description: '线条颜色,使用CSS颜色值'
|
||||
},
|
||||
{
|
||||
{
|
||||
name: 'strokeWidth',
|
||||
type: 'number',
|
||||
label: '线条宽度',
|
||||
|
||||
Reference in New Issue
Block a user