feat: enhance DiagramCanvas and Pin components with wire creation and tooltip functionality

- Added wire creation logic in DiagramCanvas.vue with mouse tracking and event handling.
- Implemented tooltip display for pins in Pin.vue with detailed information on hover.
- Updated ProjectView.vue to handle wire creation and deletion events.
- Refactored Wire.vue to support dynamic path rendering based on routing mode.
This commit is contained in:
alivender
2025-04-26 21:53:33 +08:00
parent b6839af5d2
commit b3a5342d6b
4 changed files with 896 additions and 33 deletions

View File

@@ -5,15 +5,18 @@
:height="height"
:viewBox="'0 0 ' + viewBoxWidth + ' ' + viewBoxHeight"
class="pin-component"
:data-pin-id="props.label"
>
<g :transform="`translate(${viewBoxWidth/2}, ${viewBoxHeight/2})`">
<g v-if="props.appearance === 'None'">
<g transform="translate(-12.5, -12.5)" class="interactive">
<circle
<g :transform="`translate(${viewBoxWidth/2}, ${viewBoxHeight/2})`"> <g v-if="props.appearance === 'None'">
<g transform="translate(-12.5, -12.5)" class="interactive"> <circle
style="fill:#909090"
cx="12.5"
cy="12.5"
r="3.75" />
r="3.75"
@mouseenter="showPinTooltip"
@mouseleave="hidePinTooltip"
@click.stop="handlePinClick"
:data-pin-id="`${props.label}-${props.constraint}`" />
</g>
</g>
<g v-else-if="props.appearance === 'Dip'">
@@ -25,31 +28,52 @@
height="25"
x="0"
y="0"
rx="2.5" />
<circle
rx="2.5" /> <circle
style="fill:#ecececc5;fill-opacity:0.772973"
cx="12.5"
cy="12.5"
r="3.75" />
r="3.75"
@mouseenter="showPinTooltip"
@mouseleave="hidePinTooltip"
@click.stop="handlePinClick"
:data-pin-id="`${props.label}-${props.constraint}`" />
<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'">
</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>
</g>
</g>
<!-- SMT样式的针脚 --> <circle
fill="#ecececc5"
cx="10"
cy="0"
r="3.75"
class="interactive"
@mouseenter="showPinTooltip"
@mouseleave="hidePinTooltip"
@click.stop="handlePinClick"
:data-pin-id="`${props.label}-${props.constraint}`" />
</g> </g>
</svg>
<!-- 提示框 - 在SVG外部 -->
<div v-if="showTooltip" class="pin-tooltip" :style="{
position: 'absolute',
top: tooltipPosition.top + 'px',
left: tooltipPosition.left + 'px',
transform: 'translate(-50%, -100%)',
marginTop: '-5px'
}">
{{ tooltipText }}
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { ref, computed, reactive } from 'vue';
interface Props {
size?: number;
@@ -70,11 +94,92 @@ const props = withDefaults(defineProps<Props>(), {
});
const emit = defineEmits([
'value-change'
'value-change',
'pin-click' // 新增Pin点击事件
]);
// 内部状态
const analogValue = ref(0);
const showTooltip = ref(false);
const tooltipText = ref('');
const tooltipPosition = reactive({
top: 0,
left: 0
});
// 显示针脚提示
function showPinTooltip(event: MouseEvent) {
showTooltip.value = true;
const target = event.target as SVGElement;
const rect = target.getBoundingClientRect();
// 更新提示位置
tooltipPosition.top = rect.top;
tooltipPosition.left = rect.left + rect.width / 2;
// 更新提示文本
tooltipText.value = generateTooltipText();
}
// 隐藏针脚提示
function hidePinTooltip() {
showTooltip.value = false;
}
// 生成提示文本
function generateTooltipText() {
const parts = [];
parts.push(`标签: ${props.label}`);
if (props.constraint) {
parts.push(`约束: ${props.constraint}`);
} else {
parts.push('约束: 未定义');
}
parts.push(`方向: ${getDirectionText()}`);
parts.push(`类型: ${props.type === 'digital' ? '数字' : '模拟'}`);
return parts.join(' | ');
}
// 获取方向文本
function getDirectionText() {
switch (props.direction) {
case 'input': return '输入';
case 'output': return '输出';
case 'inout': return '双向';
default: return '未知';
}
}
// 处理针脚点击
function handlePinClick(event: MouseEvent) {
// 获取针脚在SVG中的位置
const target = event.target as SVGElement;
const rect = target.getBoundingClientRect();
const pinCenter = {
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
},
// 获取原始事件
originalEvent: event
});
}
const width = computed(() => props.appearance === 'None' ? 40 * props.size : 30 * props.size);
const height = computed(() => {
@@ -112,7 +217,46 @@ defineExpose({
direction: props.direction,
type: props.type,
appearance: props.appearance
})
}), // 添加获取针脚位置的方法
getPinPosition: (pinLabel: string) => {
// 在 Pin 组件中,只有一个针脚,所以直接检查标签是否匹配
if (pinLabel !== props.label) return null;
// 使用自身作为一个唯一标识符确保针脚位置是基于实际的DOM位置
// 这样可以避免document.querySelector获取到非预期的元素
const pinElement = document.querySelector(`[data-pin-id="${props.label}-${props.constraint}"]`) as SVGElement;
if (pinElement) {
// 获取针脚元素的位置
const rect = pinElement.getBoundingClientRect();
return {
x: rect.left + rect.width / 2,
y: rect.top + rect.height / 2
};
}
// 如果找不到特定元素,使用计算出的位置
// 这种情况下我们需要找到父SVG元素位置
const svgElement = document.querySelector(`.pin-component[data-pin-id="${props.label}"]`) as SVGElement;
if (!svgElement) {
console.error(`找不到针脚 ${props.label} 的SVG元素`);
return null;
}
const svgRect = svgElement.getBoundingClientRect();
// 根据针脚类型和方向计算相对位置
let pinX = svgRect.left + svgRect.width / 2;
let pinY = svgRect.top + svgRect.height / 2;
// 添加一个小的随机偏移,确保不同针脚返回不同位置
// 仅在测试时启用,生产环境应使用更精确的算法
const randomOffset = 0.1;
pinX += Math.random() * randomOffset;
pinY += Math.random() * randomOffset;
return { x: pinX, y: pinY };
}
});
</script>
@@ -120,6 +264,7 @@ defineExpose({
.pin-component {
display: block;
user-select: none;
position: relative;
}
.interactive {
cursor: pointer;
@@ -128,4 +273,15 @@ defineExpose({
.interactive:hover {
filter: brightness(1.2);
}
.pin-tooltip {
background-color: rgba(0, 0, 0, 0.8);
color: white;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
z-index: 1000;
pointer-events: none;
white-space: nowrap;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
</style>