fix: Component awlays reset

This commit is contained in:
2025-05-17 17:32:16 +08:00
parent c7907b4253
commit 7aff4f3f02
7 changed files with 48 additions and 161 deletions

View File

@@ -112,7 +112,15 @@
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, onUnmounted, computed, watch } from "vue";
import {
ref,
reactive,
onMounted,
onUnmounted,
computed,
watch,
provide,
} from "vue";
import WireComponent from "./equipments/Wire.vue";
// 导入 diagram 管理器
@@ -122,10 +130,7 @@ import {
updatePartPosition,
updatePartAttribute,
deletePart,
addConnection,
deleteConnection,
findConnectionsByPart,
moveGroupComponents,
parseConnectionPin,
connectionArrayToWireItem,
validateDiagramData,
@@ -137,7 +142,8 @@ import type {
ConnectionArray,
WireItem,
} from "./diagramManager";
import { toString } from "lodash";
import { CanvasCurrentSelectedComponentID } from "./InjectKeys";
// 右键菜单处理函数
function handleContextMenu(e: MouseEvent) {
@@ -174,6 +180,9 @@ const hoveredComponent = ref<string | null>(null);
const draggingComponentId = ref<string | null>(null);
const componentDragOffset = reactive({ x: 0, y: 0 });
// Provide and Inject
provide(CanvasCurrentSelectedComponentID, selectedComponentId);
// Diagram 数据
const diagramData = ref<DiagramData>({
version: 1,
@@ -244,7 +253,7 @@ const wireItems = computed<WireItem[]>(() => {
console.log(`线路${index} - 计算后的起点位置:`, startPos);
}
} catch (error) {
console.error(`获取引脚位置出错:`, error);
// console.error(`获取引脚位置出错:`, error);
}
}
}
@@ -490,7 +499,7 @@ function startComponentDrag(e: MouseEvent, component: DiagramPart) {
// 阻止事件冒泡
e.stopPropagation();
console.debug(`Start Drag Component: ${component.type}:${component.id}`)
console.debug(`Start Drag Component: ${component.type}:${component.id}`);
// 设置拖拽状态
draggingComponentId.value = component.id;

View File

@@ -0,0 +1,4 @@
import type { InjectionKey, Ref } from "vue";
export const CanvasCurrentSelectedComponentID = Symbol() as InjectionKey<Ref<string | null>>

View File

@@ -44,11 +44,13 @@
</CollapsibleSection>
<CollapsibleSection title="组件功能" v-model:isExpanded="componentCapsExpanded" status="default" class="mt-4">
<div v-if="componentData && componentData.type">
<component v-if="capabilityComponent" :is="capabilityComponent" v-bind="componentData.attrs" />
<div v-else class="text-gray-400">该组件没有提供特殊功能</div>
<div id="ComponentCapabilities" ref="ComponentCapabilities"></div>
<div v-if="!(componentData && componentData.type)" class="text-gray-400">
选择元件以查看其功能
</div>
<div v-else-if="!componentCaps?.hasChildNodes()" class="text-gray-400">
该组件没有提供特殊功能
</div>
<div v-else class="text-gray-400">选择元件以查看其功能</div>
</CollapsibleSection>
<!-- 未来可以在这里添加更多的分区 -->
@@ -71,9 +73,12 @@ import { type PropertyConfig } from "@/components/equipments/componentConfig"; /
import CollapsibleSection from "./CollapsibleSection.vue"; // 可折叠区域组件
import PropertyEditor from "./PropertyEditor.vue"; // 属性编辑器组件
import DDSPropertyEditor from "./equipments/DDSPropertyEditor.vue"; // DDS专用属性编辑器组件
import { ref, computed, watch, shallowRef, markRaw, h, createApp } from "vue"; // Vue核心API
import { isNull, isUndefined } from "lodash";
import type { JSX } from "vue/jsx-runtime";
import {
ref,
computed,
watch,
useTemplateRef,
} from "vue"; // Vue核心API
// 引脚接口定义
interface Pin {
@@ -95,6 +100,8 @@ const pinsSectionExpanded = ref(false); // 引脚配置区域默认折叠
const componentCapsExpanded = ref(true); // 组件功能区域默认展开
const wireSectionExpanded = ref(false); // 连线管理区域默认折叠
const componentCaps = useTemplateRef("ComponentCapabilities");
// DDS组件特殊属性的本地状态
const ddsProperties = ref({
frequency: 1000, // 频率默认1000Hz
@@ -207,129 +214,6 @@ function updateDDSProperties(newProperties: any) {
);
}
}
// 存储当前选中组件的能力组件
const capabilityComponent = shallowRef<JSX.Element>();
// 获取组件实例上暴露的方法
async function getExposedCapabilities(componentType: string) {
try {
// 动态导入组件
const module = await import(`./equipments/${componentType}.vue`);
const Component = module.default;
// 创建一个临时div作为挂载点
const tempDiv = document.createElement("div");
// 创建临时应用实例并挂载组件
let exposedMethods: any = null;
const app = createApp({
render() {
return h(Component, {
ref: (el: any) => {
if (el) {
// 获取组件实例暴露的方法
exposedMethods = el;
}
},
});
},
});
// 挂载应用
const vm = app.mount(tempDiv);
// 确保实例已创建并获取到暴露的方法
await new Promise((resolve) => setTimeout(resolve, 0));
// 检查是否有getCapabilities方法
if (
exposedMethods &&
typeof exposedMethods.getCapabilities === "function"
) {
// 获取能力组件定义
const CapabilityComponent = exposedMethods.getCapabilities();
// 卸载应用清理DOM
app.unmount();
tempDiv.remove();
return CapabilityComponent;
}
// 卸载应用清理DOM
app.unmount();
tempDiv.remove();
return null;
} catch (error) {
console.error(`获取${componentType}能力页面失败:`, error);
return null;
}
}
// 监听选中组件变化,动态加载能力组件
watch(
() => props.componentData,
async (newComponentData) => {
if (newComponentData && newComponentData.type) {
try {
// 首先尝试从实例中获取暴露的方法
let capsComponent = null;
if (!isUndefined( newComponentData.capsPage ) && !isNull(newComponentData.capsPage)) {
capsComponent = newComponentData.capsPage;
capabilityComponent.value = markRaw(capsComponent);
}
else
capsComponent = await getExposedCapabilities(newComponentData.type);
if (capsComponent) {
capabilityComponent.value = markRaw(capsComponent);
newComponentData.capsPage = capsComponent;
console.log(`已从实例加载${newComponentData.type}组件的能力页面`);
return;
}
// 如果实例方法获取失败,回退到模块导出方法
const module = await import(
`./equipments/${newComponentData.type}.vue`
);
if (
(module.default &&
typeof module.default.getCapabilities === "function") ||
typeof module.getCapabilities === "function"
) {
const getCapsFn =
typeof module.getCapabilities === "function"
? module.getCapabilities
: module.default.getCapabilities;
const moduleCapComponent = getCapsFn();
if (moduleCapComponent) {
capabilityComponent.value = markRaw(moduleCapComponent);
console.log(`已从模块加载${newComponentData.type}组件的能力页面`);
return;
}
}
capabilityComponent.value = null;
console.log(`组件${newComponentData.type}没有提供getCapabilities方法`);
} catch (error) {
console.error(`加载组件${newComponentData.type}能力页面失败:`, error);
capabilityComponent.value = null;
}
} else {
capabilityComponent.value = null;
}
},
{ immediate: true },
);
// 修改hasComponentCaps计算属性
const hasComponentCaps = computed(() => {
return capabilityComponent.value !== null;
});
</script>
<style scoped>

View File

@@ -1,5 +1,3 @@
import type { JSX } from "vue/jsx-runtime";
// 定义 diagram.json 的类型结构
export interface DiagramData {
version: number;
@@ -17,7 +15,6 @@ export interface DiagramPart {
x: number;
y: number;
attrs: Record<string, any>;
capsPage?: JSX.Element;
rotate: number;
group: string;
positionlock: boolean;

View File

@@ -1,5 +1,5 @@
<template>
<div class="motherboard-container" :style="{
<div class="motherboard-container" v-bind="$attrs" :style="{
width: width + 'px',
height: height + 'px',
position: 'relative',
@@ -9,21 +9,31 @@
<image href="../equipments/svg/motherboard.svg" width="100%" height="100%" preserveAspectRatio="xMidYMid meet" />
</svg>
</div>
<Teleport to="#ComponentCapabilities" v-if="selectecComponentID === props.componentId">
<MotherBoardCaps :jtagAddr="eqps.boardAddr" :jtagPort="eqps.boardPort.toString()" />
</Teleport>
</template>
<script setup lang="tsx">
import MotherBoardCaps from "./MotherBoardCaps.vue";
import { useEquipments } from "@/stores/equipments";
import { ref, computed, defineComponent, watchEffect } from "vue";
import { ref, computed, watchEffect, inject } from "vue";
import { CanvasCurrentSelectedComponentID } from "../InjectKeys";
// 主板特有属性
export interface MotherBoardProps {
size?: number;
boardAddr?: string;
boardPort?: string;
componentId?: string;
}
const emit = defineEmits<{
(e: "pinClick", pinId: string): void;
}>();
const props = withDefaults(defineProps<MotherBoardProps>(), getDefaultProps());
const selectecComponentID = inject(CanvasCurrentSelectedComponentID, ref(null));
// 计算实际宽高
const width = computed(() => 800 * props.size);
@@ -34,9 +44,6 @@ const eqps = useEquipments();
const bitstreamFile = ref<File | null>();
watchEffect(() => {
console.trace(
`board监听改动: ${props.size} ${props.boardAddr}:${props.boardPort}`,
);
eqps.setAddr(props.boardAddr);
eqps.setPort(props.boardPort);
});
@@ -49,20 +56,6 @@ defineExpose({
}),
// 主板没有引脚但为了接口一致性提供一个空的getPinPosition方法
getPinPosition: () => null,
getCapabilities: () => {
// 返回组件定义而不是直接返回JSX
return defineComponent({
name: "MotherBoardCaps",
setup() {
return () => (
<MotherBoardCaps
jtagAddr={eqps.boardAddr}
jtagPort={eqps.boardPort.toString()}
/>
);
},
});
},
});
</script>
@@ -73,6 +66,7 @@ export function getDefaultProps(): MotherBoardProps {
size: 1,
boardAddr: "127.0.0.1",
boardPort: "1234",
componentId: "DefaultMotherBoardID",
};
}
</script>

View File

@@ -183,4 +183,4 @@ export function generatePropsFromAttrs(attrs: Record<string, any>): PropertyConf
export function getPropValue(component: DiagramPart, propName: string): any {
if (!component) return undefined;
return (component as any)[propName];
}
}