feat: upload and download bitstream from the component of project view
This commit is contained in:
@@ -1,68 +1,60 @@
|
||||
<template> <div class="property-panel">
|
||||
<CollapsibleSection
|
||||
title="基本属性"
|
||||
v-model:isExpanded="propertySectionExpanded"
|
||||
status="default"
|
||||
>
|
||||
<PropertyEditor
|
||||
:componentData="componentData"
|
||||
:componentConfig="componentConfig"
|
||||
@updateProp="(componentId, propName, value) => $emit('updateProp', componentId, propName, value)"
|
||||
@updateDirectProp="(componentId, propName, value) => $emit('updateDirectProp', componentId, propName, value)"
|
||||
/>
|
||||
<template>
|
||||
<div class="property-panel">
|
||||
<CollapsibleSection title="基本属性" v-model:isExpanded="propertySectionExpanded" status="default">
|
||||
<PropertyEditor :componentData="componentData" :componentConfig="componentConfig" @updateProp="
|
||||
(componentId, propName, value) =>
|
||||
$emit('updateProp', componentId, propName, value)
|
||||
" @updateDirectProp="
|
||||
(componentId, propName, value) =>
|
||||
$emit('updateDirectProp', componentId, propName, value)
|
||||
" />
|
||||
</CollapsibleSection>
|
||||
|
||||
|
||||
<!-- 信号发生器(DDS)特殊属性编辑器 -->
|
||||
<div v-if="isDDSComponent">
|
||||
<DDSPropertyEditor
|
||||
v-model="ddsProperties"
|
||||
@update:modelValue="updateDDSProperties"
|
||||
/>
|
||||
<DDSPropertyEditor v-model="ddsProperties" @update:modelValue="updateDDSProperties" />
|
||||
</div>
|
||||
|
||||
<!-- 如果选中的组件有pins属性,则显示引脚配置区域 -->
|
||||
<CollapsibleSection
|
||||
v-if="hasPinsProperty"
|
||||
title="引脚配置"
|
||||
v-model:isExpanded="pinsSectionExpanded"
|
||||
status="default"
|
||||
>
|
||||
<CollapsibleSection v-if="hasPinsProperty" title="引脚配置" v-model:isExpanded="pinsSectionExpanded" status="default">
|
||||
<div class="space-y-4 p-2">
|
||||
<!-- 显示现有的pins -->
|
||||
<div v-for="(pin, index) in componentPins" :key="index" class="pin-item p-2 border rounded-md bg-base-200">
|
||||
<div class="font-medium mb-2">引脚 #{{ index + 1 }}</div>
|
||||
|
||||
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text text-xs">ID</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
v-model="componentPins[index].pinId"
|
||||
class="input input-bordered input-sm w-full"
|
||||
placeholder="引脚ID"
|
||||
@change="updatePins"
|
||||
/>
|
||||
<input type="text" v-model="componentPins[index].pinId" class="input input-bordered input-sm w-full"
|
||||
placeholder="引脚ID" @change="updatePins" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text text-xs">约束条件</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
v-model="componentPins[index].constraint"
|
||||
class="input input-bordered input-sm w-full"
|
||||
placeholder="约束条件"
|
||||
@change="updatePins"
|
||||
/>
|
||||
<input type="text" v-model="componentPins[index].constraint" class="input input-bordered input-sm w-full"
|
||||
placeholder="约束条件" @change="updatePins" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
|
||||
<CollapsibleSection title="组件功能" v-model:isExpanded="componentCapsExpanded" status="default" class="mt-4">
|
||||
<div v-if="componentData && componentData.type">
|
||||
<component v-if="capabilityComponent" :is="capabilityComponent" />
|
||||
<div v-else class="text-gray-400">
|
||||
该组件没有提供特殊功能
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-gray-400">
|
||||
选择元件以查看其功能
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
|
||||
<!-- 未来可以在这里添加更多的分区 -->
|
||||
<!-- 例如:
|
||||
<CollapsibleSection
|
||||
@@ -77,120 +69,258 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { type DiagramPart } from '@/components/diagramManager';
|
||||
import { type PropertyConfig } from '@/components/equipments/componentConfig';
|
||||
import CollapsibleSection from './CollapsibleSection.vue';
|
||||
import PropertyEditor from './PropertyEditor.vue';
|
||||
import DDSPropertyEditor from './equipments/DDSPropertyEditor.vue';
|
||||
import { ref, computed, watch } from 'vue';
|
||||
// 导入所需的类型和组件
|
||||
import { type DiagramPart } from "@/components/diagramManager"; // 图表部件类型定义
|
||||
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";
|
||||
|
||||
// 定义Pin接口
|
||||
// 引脚接口定义
|
||||
interface Pin {
|
||||
pinId: string;
|
||||
constraint: string;
|
||||
x: number;
|
||||
y: number;
|
||||
pinId: string; // 引脚ID
|
||||
constraint: string; // 引脚约束条件
|
||||
x: number; // 引脚X坐标位置
|
||||
y: number; // 引脚Y坐标位置
|
||||
}
|
||||
|
||||
// 定义属性
|
||||
// 定义组件的输入属性
|
||||
const props = defineProps<{
|
||||
componentData: DiagramPart | null;
|
||||
componentConfig: { props: PropertyConfig[] } | null;
|
||||
componentData: DiagramPart | null; // 当前选中的组件数据
|
||||
componentConfig: { props: PropertyConfig[] } | null; // 组件的配置信息
|
||||
}>();
|
||||
|
||||
// 控制各个分区的展开状态
|
||||
const propertySectionExpanded = ref(true);
|
||||
const pinsSectionExpanded = ref(false);
|
||||
const wireSectionExpanded = ref(false);
|
||||
// 控制各个属性分区的展开状态
|
||||
const propertySectionExpanded = ref(true); // 基本属性区域默认展开
|
||||
const pinsSectionExpanded = ref(false); // 引脚配置区域默认折叠
|
||||
const componentCapsExpanded = ref(true); // 组件功能区域默认展开
|
||||
const wireSectionExpanded = ref(false); // 连线管理区域默认折叠
|
||||
|
||||
// DDS特殊属性
|
||||
// DDS组件特殊属性的本地状态
|
||||
const ddsProperties = ref({
|
||||
frequency: 1000,
|
||||
phase: 0,
|
||||
waveform: 'sine',
|
||||
customWaveformPoints: []
|
||||
frequency: 1000, // 频率,默认1000Hz
|
||||
phase: 0, // 相位,默认0度
|
||||
waveform: "sine", // 波形类型,默认正弦波
|
||||
customWaveformPoints: [], // 自定义波形点数据,默认空数组
|
||||
});
|
||||
|
||||
// 本地维护一个pins数组副本
|
||||
// 本地维护一个pins数组副本,用于编辑操作
|
||||
const componentPins = ref<Pin[]>([]);
|
||||
|
||||
// 监听组件变化,更新本地pins数据
|
||||
watch(() => props.componentData?.attrs?.pins, (newPins) => {
|
||||
if (newPins) {
|
||||
componentPins.value = JSON.parse(JSON.stringify(newPins));
|
||||
} else {
|
||||
componentPins.value = [];
|
||||
}
|
||||
}, { deep: true, immediate: true });
|
||||
// 监听组件pins属性变化,更新本地pins数据
|
||||
watch(
|
||||
() => props.componentData?.attrs?.pins,
|
||||
(newPins) => {
|
||||
if (newPins) {
|
||||
// 深拷贝以避免直接修改原始数据
|
||||
componentPins.value = JSON.parse(JSON.stringify(newPins));
|
||||
} else {
|
||||
componentPins.value = [];
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }, // 深度监听并立即执行
|
||||
);
|
||||
|
||||
// 监听DDS组件数据变化,更新特殊属性
|
||||
watch(() => props.componentData?.attrs, (newAttrs) => {
|
||||
if (newAttrs && isDDSComponent.value) {
|
||||
ddsProperties.value = {
|
||||
frequency: newAttrs.frequency || 1000,
|
||||
phase: newAttrs.phase || 0,
|
||||
waveform: newAttrs.waveform || 'sine',
|
||||
customWaveformPoints: newAttrs.customWaveformPoints || []
|
||||
};
|
||||
}
|
||||
}, { deep: true, immediate: true });
|
||||
watch(
|
||||
() => props.componentData?.attrs,
|
||||
(newAttrs) => {
|
||||
if (newAttrs && isDDSComponent.value) {
|
||||
// 从组件属性中提取DDS特有属性
|
||||
ddsProperties.value = {
|
||||
frequency: newAttrs.frequency || 1000,
|
||||
phase: newAttrs.phase || 0,
|
||||
waveform: newAttrs.waveform || "sine",
|
||||
customWaveformPoints: newAttrs.customWaveformPoints || [],
|
||||
};
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }, // 深度监听并立即执行
|
||||
);
|
||||
|
||||
// 计算属性:检查组件是否有pins属性
|
||||
const hasPinsProperty = computed(() => {
|
||||
if (!props.componentData || !props.componentData.attrs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查配置中是否有pins属性
|
||||
|
||||
// 方法1:检查配置中是否有pins属性
|
||||
if (props.componentConfig && props.componentConfig.props) {
|
||||
return props.componentConfig.props.some(prop => prop.name === 'pins' && prop.isArrayType);
|
||||
return props.componentConfig.props.some(
|
||||
(prop) => prop.name === "pins" && prop.isArrayType,
|
||||
);
|
||||
}
|
||||
|
||||
// 或直接检查attrs中是否有pins属性
|
||||
return 'pins' in props.componentData.attrs;
|
||||
|
||||
// 方法2:直接检查attrs中是否有pins属性
|
||||
return "pins" in props.componentData.attrs;
|
||||
});
|
||||
|
||||
// 计算属性:检查组件是否为DDS组件
|
||||
const isDDSComponent = computed(() => {
|
||||
return props.componentData?.type === 'DDS';
|
||||
return props.componentData?.type === "DDS";
|
||||
});
|
||||
|
||||
// 定义事件
|
||||
// 定义向父组件发送的事件
|
||||
const emit = defineEmits<{
|
||||
(e: 'updateProp', componentId: string, propName: string, value: any): void;
|
||||
(e: 'updateDirectProp', componentId: string, propName: string, value: any): void;
|
||||
// 更新嵌套属性(如数组或对象中的属性)
|
||||
(e: "updateProp", componentId: string, propName: string, value: any): void;
|
||||
// 更新直接属性
|
||||
(
|
||||
e: "updateDirectProp",
|
||||
componentId: string,
|
||||
propName: string,
|
||||
value: any,
|
||||
): void;
|
||||
}>();
|
||||
|
||||
// 更新pins属性
|
||||
// 更新pins属性的函数
|
||||
function updatePins() {
|
||||
if (props.componentData && props.componentData.id) {
|
||||
emit('updateProp', props.componentData.id, 'pins', componentPins.value);
|
||||
// 将编辑后的pins数据发送给父组件
|
||||
emit("updateProp", props.componentData.id, "pins", componentPins.value);
|
||||
}
|
||||
}
|
||||
|
||||
// 监听DDS组件数据变化,更新特殊属性
|
||||
watch(() => props.componentData?.attrs, (newAttrs) => {
|
||||
if (newAttrs && isDDSComponent.value) {
|
||||
ddsProperties.value = {
|
||||
frequency: newAttrs.frequency || 1000,
|
||||
phase: newAttrs.phase || 0,
|
||||
waveform: newAttrs.waveform || 'sine',
|
||||
customWaveformPoints: newAttrs.customWaveformPoints || []
|
||||
};
|
||||
}
|
||||
}, { deep: true, immediate: true });
|
||||
|
||||
// 更新DDS属性
|
||||
// 更新DDS特殊属性的函数
|
||||
function updateDDSProperties(newProperties: any) {
|
||||
// 更新本地状态
|
||||
ddsProperties.value = newProperties;
|
||||
if (props.componentData && props.componentData.id) {
|
||||
// 将各个属性单独更新,而不是作为一个整体
|
||||
emit('updateProp', props.componentData.id, 'frequency', newProperties.frequency);
|
||||
emit('updateProp', props.componentData.id, 'phase', newProperties.phase);
|
||||
emit('updateProp', props.componentData.id, 'waveform', newProperties.waveform);
|
||||
emit('updateProp', props.componentData.id, 'customWaveformPoints', newProperties.customWaveformPoints);
|
||||
// 将各个属性单独更新,而不是作为一个整体更新
|
||||
emit(
|
||||
"updateProp",
|
||||
props.componentData.id,
|
||||
"frequency",
|
||||
newProperties.frequency,
|
||||
);
|
||||
emit("updateProp", props.componentData.id, "phase", newProperties.phase);
|
||||
emit(
|
||||
"updateProp",
|
||||
props.componentData.id,
|
||||
"waveform",
|
||||
newProperties.waveform,
|
||||
);
|
||||
emit(
|
||||
"updateProp",
|
||||
props.componentData.id,
|
||||
"customWaveformPoints",
|
||||
newProperties.customWaveformPoints,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 存储当前选中组件的能力组件
|
||||
const capabilityComponent = shallowRef(null);
|
||||
|
||||
// 获取组件实例上暴露的方法
|
||||
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') {
|
||||
// 获取能力页面JSX
|
||||
const capsComponent = exposedMethods.getCapabilities();
|
||||
|
||||
// 卸载应用,清理DOM
|
||||
app.unmount();
|
||||
tempDiv.remove();
|
||||
|
||||
return capsComponent;
|
||||
}
|
||||
|
||||
// 卸载应用,清理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 {
|
||||
// 首先尝试从实例中获取暴露的方法
|
||||
const capsComponent = await getExposedCapabilities(newComponentData.type);
|
||||
|
||||
if (capsComponent) {
|
||||
capabilityComponent.value = markRaw(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>
|
||||
|
||||
Reference in New Issue
Block a user