fix: Component awlays reset
This commit is contained in:
		@@ -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;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								src/components/InjectKeys.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/components/InjectKeys.ts
									
									
									
									
									
										Normal file
									
								
							@@ -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 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>
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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>
 | 
			
		||||
 
 | 
			
		||||
@@ -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];
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -217,7 +217,6 @@ async function handleAddComponent(componentData: {
 | 
			
		||||
    x: Math.round(position.x + offsetX),
 | 
			
		||||
    y: Math.round(position.y + offsetY),
 | 
			
		||||
    attrs: componentData.props,
 | 
			
		||||
    capsPage: capsPage, // 设置组件的能力页面
 | 
			
		||||
    rotate: 0,
 | 
			
		||||
    group: "",
 | 
			
		||||
    positionlock: false,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user