315 lines
8.3 KiB
Vue
315 lines
8.3 KiB
Vue
<template>
|
|
<div>
|
|
<!-- 元器件选择菜单 (Drawer) -->
|
|
<div class="drawer drawer-end z-50">
|
|
<input
|
|
id="component-drawer"
|
|
type="checkbox"
|
|
class="drawer-toggle"
|
|
v-model="showComponentsMenu"
|
|
/>
|
|
<div class="drawer-side">
|
|
<label
|
|
for="component-drawer"
|
|
aria-label="close sidebar"
|
|
class="drawer-overlay !bg-opacity-50"
|
|
></label>
|
|
<div
|
|
class="menu p-0 w-[460px] min-h-full bg-base-100 shadow-xl flex flex-col"
|
|
>
|
|
<!-- 菜单头部 -->
|
|
<div
|
|
class="p-6 border-b bg-base-200 border-base-300 flex justify-between items-center"
|
|
>
|
|
<h3 class="text-xl font-bold text-primary flex items-center gap-2">
|
|
<Plus :size="20" class="text-primary" />
|
|
添加元器件
|
|
</h3>
|
|
<button
|
|
class="btn btn-ghost btn-sm btn-circle"
|
|
@click="closeMenu"
|
|
>
|
|
<X :size="20" />
|
|
</button>
|
|
</div>
|
|
|
|
<!-- 导航栏 -->
|
|
<div class="tabs tabs-boxed bg-base-200 mx-6 mt-4 rounded-box">
|
|
<a
|
|
class="tab"
|
|
:class="{ 'tab-active': activeTab === 'components' }"
|
|
@click="activeTab = 'components'"
|
|
>元器件</a
|
|
>
|
|
<a
|
|
class="tab"
|
|
:class="{ 'tab-active': activeTab === 'templates' }"
|
|
@click="activeTab = 'templates'"
|
|
>模板</a
|
|
>
|
|
<a
|
|
class="tab"
|
|
:class="{ 'tab-active': activeTab === 'virtual' }"
|
|
@click="activeTab = 'virtual'"
|
|
>虚拟外设</a
|
|
>
|
|
</div>
|
|
|
|
<!-- 搜索框 -->
|
|
<div class="px-6 py-4 w-full">
|
|
<label class="input w-full">
|
|
<Search :size="16" class="h-[1em] opacity-50" />
|
|
<input
|
|
type="text"
|
|
placeholder="搜索..."
|
|
class="grow"
|
|
v-model="searchQuery"
|
|
/>
|
|
</label>
|
|
</div>
|
|
|
|
<!-- 统一的项目列表 -->
|
|
<ItemList
|
|
v-if="activeTab === 'components'"
|
|
:items="availableComponents"
|
|
:search-query="searchQuery"
|
|
:component-modules="componentModules"
|
|
:no-results-message="'没有找到匹配的元器件'"
|
|
item-type="component"
|
|
@item-click="addComponent"
|
|
@clear-search="searchQuery = ''"
|
|
/>
|
|
|
|
<ItemList
|
|
v-if="activeTab === 'templates'"
|
|
:items="availableTemplates"
|
|
:search-query="searchQuery"
|
|
:component-modules="componentModules"
|
|
:no-results-message="'没有找到匹配的模板'"
|
|
item-type="template"
|
|
@item-click="addTemplate"
|
|
@clear-search="searchQuery = ''"
|
|
/>
|
|
|
|
<ItemList
|
|
v-if="activeTab === 'virtual'"
|
|
:items="availableVirtualDevices"
|
|
:search-query="searchQuery"
|
|
:component-modules="componentModules"
|
|
:no-results-message="'没有找到匹配的虚拟外设'"
|
|
item-type="virtual"
|
|
@item-click="addComponent"
|
|
@clear-search="searchQuery = ''"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, computed, shallowRef, onMounted } from "vue";
|
|
import { Plus, X, Search } from "lucide-vue-next";
|
|
import ItemList from "./ItemList.vue";
|
|
import { useAlertStore } from "@/components/Alert";
|
|
import {
|
|
availableComponents,
|
|
availableVirtualDevices,
|
|
availableTemplates,
|
|
getAllComponentTypes,
|
|
type ComponentConfig,
|
|
type VirtualDeviceConfig,
|
|
type TemplateConfig,
|
|
useComponentManager, // 导入 componentManager
|
|
} from "./index.ts";
|
|
|
|
// Props 定义
|
|
interface Props {
|
|
open: boolean;
|
|
}
|
|
|
|
const props = defineProps<Props>();
|
|
|
|
// 定义组件发出的事件(保留部分必要的事件)
|
|
const emit = defineEmits([
|
|
"close",
|
|
"update:open",
|
|
]);
|
|
|
|
// 使用 componentManager
|
|
const componentManager = useComponentManager();
|
|
|
|
// 使用 Alert 系统
|
|
const alert = useAlertStore();
|
|
|
|
// 当前激活的选项卡
|
|
const activeTab = ref("components");
|
|
|
|
// --- 搜索功能 ---
|
|
const searchQuery = ref("");
|
|
|
|
// 显示/隐藏组件菜单
|
|
const showComponentsMenu = computed({
|
|
get: () => props.open,
|
|
set: (value) => emit("update:open", value),
|
|
});
|
|
|
|
// 组件模块缓存
|
|
const componentModules = shallowRef<Record<string, any>>({});
|
|
|
|
// 动态加载组件定义
|
|
async function loadComponentModule(type: string) {
|
|
if (!componentModules.value[type]) {
|
|
try {
|
|
// 假设组件都在 src/components/equipments/ 目录下,且文件名与 type 相同
|
|
const module = await import(`@/components/equipments/${type}.vue`);
|
|
|
|
// 将模块添加到缓存中
|
|
componentModules.value = {
|
|
...componentModules.value,
|
|
[type]: module,
|
|
};
|
|
|
|
console.log(`Loaded module for ${type}:`, module);
|
|
} catch (error) {
|
|
console.error(`Failed to load component module ${type}:`, error);
|
|
return null;
|
|
}
|
|
}
|
|
return componentModules.value[type];
|
|
}
|
|
|
|
// 预加载组件模块
|
|
async function preloadComponentModules() {
|
|
const allTypes = getAllComponentTypes();
|
|
|
|
for (const type of allTypes) {
|
|
try {
|
|
await loadComponentModule(type);
|
|
} catch (error) {
|
|
console.error(`Failed to preload component ${type}:`, error);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 关闭菜单
|
|
function closeMenu() {
|
|
emit("update:open", false);
|
|
emit("close");
|
|
}
|
|
|
|
// 添加新元器件 - 使用 componentManager
|
|
async function addComponent(
|
|
componentTemplate: ComponentConfig | VirtualDeviceConfig,
|
|
) {
|
|
if (!componentManager) {
|
|
console.error("ComponentManager not available");
|
|
return;
|
|
}
|
|
|
|
// 先加载组件模块
|
|
const moduleRef = await loadComponentModule(componentTemplate.type);
|
|
let defaultProps: Record<string, any> = {};
|
|
|
|
// 尝试直接调用组件导出的getDefaultProps方法
|
|
if (moduleRef) {
|
|
if (typeof moduleRef.getDefaultProps === "function") {
|
|
defaultProps = moduleRef.getDefaultProps();
|
|
console.log(
|
|
`Got default props from ${componentTemplate.type}:`,
|
|
defaultProps,
|
|
);
|
|
} else {
|
|
// 回退到配置文件
|
|
console.log(`No getDefaultProps found for ${componentTemplate.type}`);
|
|
}
|
|
} else {
|
|
console.log(`Failed to load module for ${componentTemplate.type}`);
|
|
}
|
|
|
|
try {
|
|
// 使用 componentManager 添加组件
|
|
await componentManager.addComponent({
|
|
type: componentTemplate.type,
|
|
name: componentTemplate.name,
|
|
props: defaultProps,
|
|
});
|
|
|
|
// 显示成功消息
|
|
alert?.success(`成功添加元器件: ${componentTemplate.name}`);
|
|
|
|
// 关闭菜单
|
|
closeMenu();
|
|
} catch (error) {
|
|
console.error("添加元器件失败:", error);
|
|
alert?.error("添加元器件失败,请检查控制台错误信息");
|
|
}
|
|
}
|
|
|
|
// 添加模板 - 使用 componentManager
|
|
async function addTemplate(template: TemplateConfig) {
|
|
if (!componentManager) {
|
|
console.error("ComponentManager not available");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// 加载模板JSON文件
|
|
const response = await fetch(template.path);
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to load template: ${response.statusText}`);
|
|
}
|
|
|
|
const templateData = await response.json();
|
|
console.log("加载模板:", templateData);
|
|
|
|
// 使用 componentManager 添加模板
|
|
const result = await componentManager.addTemplate({
|
|
id: template.id,
|
|
name: template.name,
|
|
template: templateData,
|
|
});
|
|
|
|
if (result) {
|
|
// 使用 Alert 显示结果消息
|
|
if (result.success) {
|
|
alert?.success(result.message);
|
|
} else {
|
|
alert?.error(result.message);
|
|
}
|
|
}
|
|
|
|
// 关闭菜单
|
|
closeMenu();
|
|
} catch (error) {
|
|
console.error("加载模板出错:", error);
|
|
alert?.error("无法加载模板文件,请检查控制台错误信息");
|
|
}
|
|
}
|
|
|
|
// 生命周期钩子
|
|
onMounted(() => {
|
|
// 预加载组件模块
|
|
preloadComponentModules();
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* 动画效果 */
|
|
.animate-slideUp {
|
|
animation: slideUp 0.3s ease-out forwards;
|
|
}
|
|
|
|
@keyframes slideUp {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
}
|
|
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
</style>
|