fix: Component awlays reset
This commit is contained in:
parent
c7907b4253
commit
7aff4f3f02
|
@ -112,7 +112,15 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<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";
|
import WireComponent from "./equipments/Wire.vue";
|
||||||
|
|
||||||
// 导入 diagram 管理器
|
// 导入 diagram 管理器
|
||||||
|
@ -122,10 +130,7 @@ import {
|
||||||
updatePartPosition,
|
updatePartPosition,
|
||||||
updatePartAttribute,
|
updatePartAttribute,
|
||||||
deletePart,
|
deletePart,
|
||||||
addConnection,
|
|
||||||
deleteConnection,
|
deleteConnection,
|
||||||
findConnectionsByPart,
|
|
||||||
moveGroupComponents,
|
|
||||||
parseConnectionPin,
|
parseConnectionPin,
|
||||||
connectionArrayToWireItem,
|
connectionArrayToWireItem,
|
||||||
validateDiagramData,
|
validateDiagramData,
|
||||||
|
@ -137,7 +142,8 @@ import type {
|
||||||
ConnectionArray,
|
ConnectionArray,
|
||||||
WireItem,
|
WireItem,
|
||||||
} from "./diagramManager";
|
} from "./diagramManager";
|
||||||
import { toString } from "lodash";
|
|
||||||
|
import { CanvasCurrentSelectedComponentID } from "./InjectKeys";
|
||||||
|
|
||||||
// 右键菜单处理函数
|
// 右键菜单处理函数
|
||||||
function handleContextMenu(e: MouseEvent) {
|
function handleContextMenu(e: MouseEvent) {
|
||||||
|
@ -174,6 +180,9 @@ const hoveredComponent = ref<string | null>(null);
|
||||||
const draggingComponentId = ref<string | null>(null);
|
const draggingComponentId = ref<string | null>(null);
|
||||||
const componentDragOffset = reactive({ x: 0, y: 0 });
|
const componentDragOffset = reactive({ x: 0, y: 0 });
|
||||||
|
|
||||||
|
// Provide and Inject
|
||||||
|
provide(CanvasCurrentSelectedComponentID, selectedComponentId);
|
||||||
|
|
||||||
// Diagram 数据
|
// Diagram 数据
|
||||||
const diagramData = ref<DiagramData>({
|
const diagramData = ref<DiagramData>({
|
||||||
version: 1,
|
version: 1,
|
||||||
|
@ -244,7 +253,7 @@ const wireItems = computed<WireItem[]>(() => {
|
||||||
console.log(`线路${index} - 计算后的起点位置:`, startPos);
|
console.log(`线路${index} - 计算后的起点位置:`, startPos);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`获取引脚位置出错:`, error);
|
// console.error(`获取引脚位置出错:`, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -490,7 +499,7 @@ function startComponentDrag(e: MouseEvent, component: DiagramPart) {
|
||||||
// 阻止事件冒泡
|
// 阻止事件冒泡
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
console.debug(`Start Drag Component: ${component.type}:${component.id}`)
|
console.debug(`Start Drag Component: ${component.type}:${component.id}`);
|
||||||
|
|
||||||
// 设置拖拽状态
|
// 设置拖拽状态
|
||||||
draggingComponentId.value = component.id;
|
draggingComponentId.value = component.id;
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
import type { InjectionKey, Ref } from "vue";
|
||||||
|
|
||||||
|
export const CanvasCurrentSelectedComponentID = Symbol() as InjectionKey<Ref<string | null>>
|
||||||
|
|
|
@ -44,11 +44,13 @@
|
||||||
</CollapsibleSection>
|
</CollapsibleSection>
|
||||||
|
|
||||||
<CollapsibleSection title="组件功能" v-model:isExpanded="componentCapsExpanded" status="default" class="mt-4">
|
<CollapsibleSection title="组件功能" v-model:isExpanded="componentCapsExpanded" status="default" class="mt-4">
|
||||||
<div v-if="componentData && componentData.type">
|
<div id="ComponentCapabilities" ref="ComponentCapabilities"></div>
|
||||||
<component v-if="capabilityComponent" :is="capabilityComponent" v-bind="componentData.attrs" />
|
<div v-if="!(componentData && componentData.type)" class="text-gray-400">
|
||||||
<div v-else class="text-gray-400">该组件没有提供特殊功能</div>
|
选择元件以查看其功能
|
||||||
|
</div>
|
||||||
|
<div v-else-if="!componentCaps?.hasChildNodes()" class="text-gray-400">
|
||||||
|
该组件没有提供特殊功能
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="text-gray-400">选择元件以查看其功能</div>
|
|
||||||
</CollapsibleSection>
|
</CollapsibleSection>
|
||||||
|
|
||||||
<!-- 未来可以在这里添加更多的分区 -->
|
<!-- 未来可以在这里添加更多的分区 -->
|
||||||
|
@ -71,9 +73,12 @@ import { type PropertyConfig } from "@/components/equipments/componentConfig"; /
|
||||||
import CollapsibleSection from "./CollapsibleSection.vue"; // 可折叠区域组件
|
import CollapsibleSection from "./CollapsibleSection.vue"; // 可折叠区域组件
|
||||||
import PropertyEditor from "./PropertyEditor.vue"; // 属性编辑器组件
|
import PropertyEditor from "./PropertyEditor.vue"; // 属性编辑器组件
|
||||||
import DDSPropertyEditor from "./equipments/DDSPropertyEditor.vue"; // DDS专用属性编辑器组件
|
import DDSPropertyEditor from "./equipments/DDSPropertyEditor.vue"; // DDS专用属性编辑器组件
|
||||||
import { ref, computed, watch, shallowRef, markRaw, h, createApp } from "vue"; // Vue核心API
|
import {
|
||||||
import { isNull, isUndefined } from "lodash";
|
ref,
|
||||||
import type { JSX } from "vue/jsx-runtime";
|
computed,
|
||||||
|
watch,
|
||||||
|
useTemplateRef,
|
||||||
|
} from "vue"; // Vue核心API
|
||||||
|
|
||||||
// 引脚接口定义
|
// 引脚接口定义
|
||||||
interface Pin {
|
interface Pin {
|
||||||
|
@ -95,6 +100,8 @@ const pinsSectionExpanded = ref(false); // 引脚配置区域默认折叠
|
||||||
const componentCapsExpanded = ref(true); // 组件功能区域默认展开
|
const componentCapsExpanded = ref(true); // 组件功能区域默认展开
|
||||||
const wireSectionExpanded = ref(false); // 连线管理区域默认折叠
|
const wireSectionExpanded = ref(false); // 连线管理区域默认折叠
|
||||||
|
|
||||||
|
const componentCaps = useTemplateRef("ComponentCapabilities");
|
||||||
|
|
||||||
// DDS组件特殊属性的本地状态
|
// DDS组件特殊属性的本地状态
|
||||||
const ddsProperties = ref({
|
const ddsProperties = ref({
|
||||||
frequency: 1000, // 频率,默认1000Hz
|
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>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import type { JSX } from "vue/jsx-runtime";
|
|
||||||
|
|
||||||
// 定义 diagram.json 的类型结构
|
// 定义 diagram.json 的类型结构
|
||||||
export interface DiagramData {
|
export interface DiagramData {
|
||||||
version: number;
|
version: number;
|
||||||
|
@ -17,7 +15,6 @@ export interface DiagramPart {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
attrs: Record<string, any>;
|
attrs: Record<string, any>;
|
||||||
capsPage?: JSX.Element;
|
|
||||||
rotate: number;
|
rotate: number;
|
||||||
group: string;
|
group: string;
|
||||||
positionlock: boolean;
|
positionlock: boolean;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="motherboard-container" :style="{
|
<div class="motherboard-container" v-bind="$attrs" :style="{
|
||||||
width: width + 'px',
|
width: width + 'px',
|
||||||
height: height + 'px',
|
height: height + 'px',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
@ -9,21 +9,31 @@
|
||||||
<image href="../equipments/svg/motherboard.svg" width="100%" height="100%" preserveAspectRatio="xMidYMid meet" />
|
<image href="../equipments/svg/motherboard.svg" width="100%" height="100%" preserveAspectRatio="xMidYMid meet" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
<Teleport to="#ComponentCapabilities" v-if="selectecComponentID === props.componentId">
|
||||||
|
<MotherBoardCaps :jtagAddr="eqps.boardAddr" :jtagPort="eqps.boardPort.toString()" />
|
||||||
|
</Teleport>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import MotherBoardCaps from "./MotherBoardCaps.vue";
|
import MotherBoardCaps from "./MotherBoardCaps.vue";
|
||||||
import { useEquipments } from "@/stores/equipments";
|
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 {
|
export interface MotherBoardProps {
|
||||||
size?: number;
|
size?: number;
|
||||||
boardAddr?: string;
|
boardAddr?: string;
|
||||||
boardPort?: string;
|
boardPort?: string;
|
||||||
|
componentId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: "pinClick", pinId: string): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
const props = withDefaults(defineProps<MotherBoardProps>(), getDefaultProps());
|
const props = withDefaults(defineProps<MotherBoardProps>(), getDefaultProps());
|
||||||
|
const selectecComponentID = inject(CanvasCurrentSelectedComponentID, ref(null));
|
||||||
|
|
||||||
// 计算实际宽高
|
// 计算实际宽高
|
||||||
const width = computed(() => 800 * props.size);
|
const width = computed(() => 800 * props.size);
|
||||||
|
@ -34,9 +44,6 @@ const eqps = useEquipments();
|
||||||
const bitstreamFile = ref<File | null>();
|
const bitstreamFile = ref<File | null>();
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
console.trace(
|
|
||||||
`board监听改动: ${props.size} ${props.boardAddr}:${props.boardPort}`,
|
|
||||||
);
|
|
||||||
eqps.setAddr(props.boardAddr);
|
eqps.setAddr(props.boardAddr);
|
||||||
eqps.setPort(props.boardPort);
|
eqps.setPort(props.boardPort);
|
||||||
});
|
});
|
||||||
|
@ -49,20 +56,6 @@ defineExpose({
|
||||||
}),
|
}),
|
||||||
// 主板没有引脚,但为了接口一致性,提供一个空的getPinPosition方法
|
// 主板没有引脚,但为了接口一致性,提供一个空的getPinPosition方法
|
||||||
getPinPosition: () => null,
|
getPinPosition: () => null,
|
||||||
getCapabilities: () => {
|
|
||||||
// 返回组件定义而不是直接返回JSX
|
|
||||||
return defineComponent({
|
|
||||||
name: "MotherBoardCaps",
|
|
||||||
setup() {
|
|
||||||
return () => (
|
|
||||||
<MotherBoardCaps
|
|
||||||
jtagAddr={eqps.boardAddr}
|
|
||||||
jtagPort={eqps.boardPort.toString()}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -73,6 +66,7 @@ export function getDefaultProps(): MotherBoardProps {
|
||||||
size: 1,
|
size: 1,
|
||||||
boardAddr: "127.0.0.1",
|
boardAddr: "127.0.0.1",
|
||||||
boardPort: "1234",
|
boardPort: "1234",
|
||||||
|
componentId: "DefaultMotherBoardID",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -217,7 +217,6 @@ async function handleAddComponent(componentData: {
|
||||||
x: Math.round(position.x + offsetX),
|
x: Math.round(position.x + offsetX),
|
||||||
y: Math.round(position.y + offsetY),
|
y: Math.round(position.y + offsetY),
|
||||||
attrs: componentData.props,
|
attrs: componentData.props,
|
||||||
capsPage: capsPage, // 设置组件的能力页面
|
|
||||||
rotate: 0,
|
rotate: 0,
|
||||||
group: "",
|
group: "",
|
||||||
positionlock: false,
|
positionlock: false,
|
||||||
|
|
Loading…
Reference in New Issue