feat: Refactor code structure for improved readability and maintainability

This commit is contained in:
alivender
2025-05-09 19:28:43 +08:00
parent d4b34bd6d4
commit 6a786c1519
17 changed files with 2913 additions and 583 deletions

View File

@@ -1,5 +1,5 @@
<template>
<div class="flex-1 min-w-[60%] bg-base-200 relative overflow-hidden diagram-container" ref="canvasContainer"
<div class="flex-1 h-full w-full bg-base-200 relative overflow-hidden diagram-container" ref="canvasContainer"
@mousedown="handleCanvasMouseDown"
@mousedown.middle.prevent="startMiddleDrag"
@wheel.prevent="onZoom"
@@ -38,9 +38,8 @@
<div
ref="canvas"
class="diagram-canvas"
:style="{ transform: `translate(${position.x}px, ${position.y}px) scale(${scale})` }">
<!-- 渲染连线 -->
<svg class="wires-layer" width="4000" height="4000">
:style="{ transform: `translate(${position.x}px, ${position.y}px) scale(${scale})` }"> <!-- 渲染连线 -->
<svg class="wires-layer" width="10000" height="10000">
<!-- 已完成的连线 -->
<WireComponent
v-for="(wire, index) in wireItems"
@@ -77,33 +76,25 @@
<!-- 渲染画布上的组件 -->
<div v-for="component in diagramParts" :key="component.id"
class="component-wrapper"
:class="{
class="component-wrapper" :class="{
'component-hover': hoveredComponent === component.id,
'component-selected': selectedComponentId === component.id,
'component-disabled': component.isOn,
'component-hidden': component.hide
}"
:style="{
'component-disabled': !component.isOn,
'component-hidepins': component.hidepins
}" :style="{
top: component.y + 'px',
left: component.x + 'px',
zIndex: selectedComponentId === component.id ? 999 : 1,
zIndex: component.index ?? 0,
transform: component.rotate ? `rotate(${component.rotate}deg)` : 'none',
opacity: component.isOn ? 0.6 : 1,
display: component.hide ? 'none' : 'block'
opacity: component.isOn ? 1 : 0.6,
display: 'block'
}"
@mousedown.left.stop="startComponentDrag($event, component)"
@mouseover="hoveredComponent = component.id"
@mouseleave="hoveredComponent = null"> <!-- 动态渲染组件 -->
<component :is="getComponentDefinition(component.type)"
v-if="props.componentModules[component.type]"
v-bind="prepareComponentProps(component.attrs || {}, component.id)" @update:bindKey="(value: string) => updateComponentProp(component.id, 'bindKey', value)"
@pin-click="(pinInfo: any) => handlePinClick(component.id, pinInfo, pinInfo.originalEvent)"
:ref="(el: any) => {
if (el && componentRefs.value) {
componentRefs.value[component.id] = el;
}
}"
v-bind="prepareComponentProps(component.attrs || {}, component.id)" @update:bindKey="(value: string) => updateComponentProp(component.id, 'bindKey', value)" @pin-click="(pinInfo: any) => handlePinClick(component.id, pinInfo, pinInfo.originalEvent)" :ref="(el: any) => setComponentRef(component.id, el)"
/>
<!-- Fallback if component module not loaded yet -->
@@ -191,9 +182,18 @@ const diagramData = ref<DiagramData>({
// 组件引用跟踪
const componentRefs = ref<Record<string, any>>({});
// 计算属性:从 diagramData 中提取组件列表
// 计算属性:从 diagramData 中提取组件列表并按index属性排序
const diagramParts = computed<DiagramPart[]>(() => {
return diagramData.value.parts;
// 克隆原始数组以避免直接修改原始数据
const parts = [...diagramData.value.parts];
// 按照index属性进行排序index值大的排在后面显示在上层
// 如果没有定义index则默认为0
return parts.sort((a, b) => {
const indexA = a.index ?? 0;
const indexB = b.index ?? 0;
return indexA - indexB;
});
});
// 计算属性:转换连接为 WireItem 列表以供渲染
@@ -219,14 +219,20 @@ const wireItems = computed<WireItem[]>(() => {
// 如果找到组件,设置连线端点位置
if (startComp) {
startPos.x = startComp.x;
startPos.y = startComp.y; // 尝试获取引脚精确位置(如果有实现)
startPos.y = startComp.y;
// 尝试获取引脚精确位置(如果有实现)
const startCompRef = componentRefs.value?.[startCompId];
if (startCompRef && typeof startCompRef.getPinPosition === 'function') {
try {
const pinPos = startCompRef.getPinPosition(startPinId);
console.log(`线路${index} - 起点引脚位置(来自${startCompId}):`, pinPos);
if (pinPos) {
startPos.x = pinPos.x;
startPos.y = pinPos.y;
// 正确合并组件位置与引脚相对位置
startPos.x = startComp.x + pinPos.x;
startPos.y = startComp.y + pinPos.y;
console.log(`线路${index} - 计算后的起点位置:`, startPos);
}
} catch (error) {
console.error(`获取引脚位置出错:`, error);
@@ -243,9 +249,13 @@ const wireItems = computed<WireItem[]>(() => {
if (endCompRef && typeof endCompRef.getPinPosition === 'function') {
try {
const pinPos = endCompRef.getPinPosition(endPinId);
console.log(`线路${index} - 终点引脚位置(来自${endCompId}):`, pinPos);
if (pinPos) {
endPos.x = pinPos.x;
endPos.y = pinPos.y;
// 正确合并组件位置与引脚相对位置
endPos.x = endComp.x + pinPos.x;
endPos.y = endComp.y + pinPos.y;
console.log(`线路${index} - 计算后的终点位置:`, endPos);
}
} catch (error) {
console.error(`获取引脚位置出错:`, error);
@@ -282,7 +292,7 @@ const fileInput = ref<HTMLInputElement | null>(null);
// --- 缩放功能 ---
const MIN_SCALE = 0.2;
const MAX_SCALE = 3.0;
const MAX_SCALE = 10.0;
function onZoom(e: WheelEvent) {
e.preventDefault();
@@ -341,6 +351,22 @@ function prepareComponentProps(attrs: Record<string, any>, componentId?: string)
return result;
}
// 设置组件引用
function setComponentRef(componentId: string, el: any) {
if (componentRefs.value) {
if (el) {
componentRefs.value[componentId] = el;
} else {
delete componentRefs.value[componentId];
}
}
}
// 重置组件引用缓存
function resetComponentRefs() {
componentRefs.value = {};
}
// 加载组件模块
async function loadComponentModule(type: string) {
if (!props.componentModules[type]) {
@@ -567,39 +593,74 @@ function handlePinClick(componentId: string, pinInfo: any, event: MouseEvent) {
// 获取引脚ID
const pinId = pinInfo.label;
console.log('----引脚点击详情开始----');
console.log('组件ID:', componentId);
console.log('引脚ID:', pinId);
console.log('引脚原始信息:', pinInfo);
console.log('鼠标位置:', mousePosition);
if (!isCreatingWire.value) {
// 开始创建连线
const containerRect = canvasContainer.value.getBoundingClientRect();
// 获取引脚位置 - 优先使用pinInfo中传递的position
// 获取初始位置信息
let pinPosition = pinInfo.position;
console.log(pinInfo);
console.log('引脚位置:', pinPosition);
console.log('Pin信息:', pinInfo);
console.log('Pin初始位置:', pinPosition);
console.log('组件ID:', componentId);
console.log('引脚ID:', pinId);
console.log('组件引用:', componentRefs);
console.log('组件引用:', componentRefs.value[componentId]);
// 从组件引用中获取组件实例
const component = componentRefs.value[componentId];
if (component && typeof component.getPinPosition === 'function') {
console.log('组件引用:', component);
// 查找组件部件对象以获取组件位置
const componentPart = diagramParts.value.find(p => p.id === componentId);
if (!componentPart) {
console.error('找不到组件部件对象:', componentId);
}
// 重新设置引脚位置(初始化)
pinPosition = { x: 0, y: 0 };
// 如果组件实例存在且有 getPinPosition 方法
if (component && typeof component.getPinPosition === 'function' && componentPart) {
try {
console.log('从组件获取引脚位置:', componentId, pinId);
pinPosition += component.getPinPosition(pinId);
console.log('尝试从组件获取引脚位置');
console.log('组件部件位置:', componentPart.x, componentPart.y);
const pinRelativePos = component.getPinPosition(pinId);
console.log('组件返回的引脚相对位置:', pinRelativePos);
if (pinRelativePos) {
// 计算引脚的绝对位置 = 组件位置 + 引脚相对位置
pinPosition = {
x: componentPart.x + pinRelativePos.x,
y: componentPart.y + pinRelativePos.y
};
console.log('计算的引脚绝对位置:', pinPosition);
} else {
// 如果没有找到引脚位置,使用组件位置
pinPosition = {
x: componentPart.x,
y: componentPart.y
};
console.log('使用组件位置:', pinPosition);
}
} catch (error) {
console.error(`获取引脚位置出错:`, error);
}
}
// 如果还是没有position使用默认位置
if (!pinPosition) {
console.warn('无法获取引脚位置,使用默认值');
pinPosition = { x: 0, y: 0 };
} else if (componentPart) {
// 如果组件不存在或没有 getPinPosition 方法,使用组件的位置
pinPosition = {
x: componentPart.x,
y: componentPart.y
};
console.log('使用组件位置:', pinPosition);
}
console.log('最终的引脚位置:', pinPosition);
console.log('最终使用的引脚位置:', pinPosition);
// 计算引脚在画布坐标系中的位置
const pinCanvasX = (pinPosition.x - containerRect.left - position.x) / scale.value;
const pinCanvasY = (pinPosition.y - containerRect.top - position.y) / scale.value;
setWireCreationStart(pinCanvasX, pinCanvasY, componentId, pinId, pinInfo.constraint);
// 使用最终的引脚位置作为连线起点
setWireCreationStart(pinPosition.x, pinPosition.y, componentId, pinId, pinInfo.constraint);
document.addEventListener('mousemove', onCreatingWireMouseMove);
} else {
// 完成连线创建
@@ -609,6 +670,48 @@ function handlePinClick(componentId: string, pinInfo: any, event: MouseEvent) {
return;
}
// 获取终点引脚位置
let endPosition = { x: 0, y: 0 };
const componentPart = diagramParts.value.find(p => p.id === componentId);
const endComponent = componentRefs.value[componentId];
console.log('终点组件部件:', componentPart);
console.log('终点组件引用:', endComponent);
// 如果找到组件,设置终点位置
if (componentPart) {
endPosition.x = componentPart.x;
endPosition.y = componentPart.y;
// 如果组件实现了getPinPosition方法使用它
if (endComponent && typeof endComponent.getPinPosition === 'function') {
try {
const pinPos = endComponent.getPinPosition(pinId);
console.log('终点组件返回的引脚位置:', pinPos);
if (pinPos) {
// 正确合并组件位置与引脚相对位置
endPosition = {
x: componentPart.x + pinPos.x,
y: componentPart.y + pinPos.x
};
console.log('终点引脚位置(来自组件方法):', endPosition);
}
} catch (error) {
console.error(`获取终点引脚位置出错:`, error);
}
} else {
// 对于没有提供引脚精确位置的组件,使用组件位置
console.log('终点组件没有提供引脚位置方法,使用组件位置');
}
} else {
console.error('找不到终点组件部件对象:', componentId);
}
console.log('最终使用的终点位置:', endPosition);
console.log('线路从', creatingWireStartInfo, '到', { componentId, pinId });
console.log('----引脚点击详情结束----');
// 创建新的连线
const newConnection: ConnectionArray = [
`${creatingWireStartInfo.componentId}:${creatingWireStartInfo.pinId}`,
@@ -822,6 +925,9 @@ function showToast(message: string, type: 'success' | 'error' | 'info' = 'info',
// --- 生命周期钩子 ---
onMounted(async () => {
// 重置组件引用
resetComponentRefs();
// 加载图表数据
try {
diagramData.value = await loadDiagramData();
@@ -843,12 +949,11 @@ onMounted(async () => {
} catch (error) {
console.error('加载图表数据失败:', error);
}
// 初始化中心位置
// 初始化中心位置
if (canvasContainer.value) {
// 修改为将画布中心点放在容器中心点
position.x = canvasContainer.value.clientWidth / 2 - 2000; // 画布宽度的一半
position.y = canvasContainer.value.clientHeight / 2 - 2000; // 画布高度的一半
position.x = canvasContainer.value.clientWidth / 2 - 5000; // 画布宽度的一半
position.y = canvasContainer.value.clientHeight / 2 - 5000; // 画布高度的一半
}
// 添加键盘事件监听器
@@ -858,7 +963,7 @@ onMounted(async () => {
// 处理键盘事件
function handleKeyDown(e: KeyboardEvent) {
// 如果当前有选中的组件并且按下了Delete键
if (selectedComponentId.value && (e.key === 'Delete' || e.key === 'Backspace')) {
if (selectedComponentId.value && (e.key === 'Delete')) {
// 触发删除组件事件
deleteComponent(selectedComponentId.value);
}
@@ -961,6 +1066,11 @@ defineExpose({
watch(diagramData, (newData) => {
saveDiagramData(newData);
}, { deep: true });
// 当组件模块加载完成后,确保组件引用正确建立
watch(() => props.componentModules, () => {
// 这里不需要特别处理Vue 会自动通过 setComponentRef 函数更新引用
}, { deep: true });
</script>
<style scoped>
@@ -969,23 +1079,20 @@ watch(diagramData, (newData) => {
width: 100%;
height: 100%;
overflow: hidden;
/* background-image:
linear-gradient(to right, rgba(100, 100, 100, 0.1) 1px, transparent 1px),
linear-gradient(to bottom, rgba(100, 100, 100, 0.1) 1px, transparent 1px),
linear-gradient(to right, rgba(80, 80, 80, 0.2) 100px, transparent 100px),
linear-gradient(to bottom, rgba(80, 80, 80, 0.2) 100px, transparent 100px); */
background-size: 20px 20px, 20px 20px, 100px 100px, 100px 100px;
background-position: 0 0;
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
display: flex;
flex-direction: column;
}
.diagram-canvas {
position: relative;
width: 4000px;
height: 4000px;
width: 10000px;
height: 10000px;
transform-origin: 0 0;
user-select: none;
-webkit-user-select: none;
@@ -1002,6 +1109,7 @@ watch(diagramData, (newData) => {
height: 100%;
pointer-events: auto; /* 修复:允许线被点击 */
z-index: 50;
overflow: visible; /* 确保超出SVG范围的内容也能显示 */
}
.wires-layer path {
@@ -1035,7 +1143,6 @@ watch(diagramData, (newData) => {
outline: 3px dashed;
outline-color: #e74c3c #f39c12 #3498db #2ecc71;
outline-offset: 3px;
z-index: 999 !important; /* 使用更高的z-index确保始终在顶层 */
}
/* 禁用状态 */
@@ -1044,6 +1151,11 @@ watch(diagramData, (newData) => {
filter: grayscale(70%);
}
/* 隐藏引脚状态 */
.component-hidepins :deep([data-pin-wrapper]) {
display: none;
}
/* 为黑暗模式设置不同的网格线颜色 */
/* :root[data-theme="dark"] .diagram-container {
background-image: