feat: upload and download bitstream from the component of project view

This commit is contained in:
2025-05-13 18:14:57 +08:00
parent eae67d04d4
commit eea03f5bc8
15 changed files with 4243 additions and 1365 deletions

View File

@@ -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>