feat: remake most of forntend
This commit is contained in:
@@ -1,26 +1,79 @@
|
||||
<template>
|
||||
<div class="bg-base-200 min-h-screen">
|
||||
<template> <div class="bg-base-200 min-h-screen">
|
||||
<main class="hero min-h-screen bg-base-200">
|
||||
<div class="hero-content flex-col lg:flex-row-reverse">
|
||||
<img src="https://placehold.co/600x400" class="max-w-sm rounded-lg shadow-2xl" />
|
||||
<div>
|
||||
<h1 class="text-5xl font-bold">Welcome to FPGA Web Lab!</h1>
|
||||
<p class="py-6">
|
||||
Prototype and simulate electronic circuits in your browser.
|
||||
<div class="hero-content flex-col lg:flex-row-reverse gap-8 lg:gap-12 py-10 px-4">
|
||||
<!-- 图片容器 -->
|
||||
<div class="image-container relative w-full max-w-sm hover:scale-105 hover:-rotate-1 transition-transform duration-500 ease-in-out">
|
||||
<img src="https://placehold.co/600x400" class="w-full rounded-2xl shadow-2xl border-4 border-base-300 transition-shadow duration-300 hover:shadow-primary" />
|
||||
<!-- 这里使用relative定位,限制覆盖层只在图片容器内 -->
|
||||
<div class="absolute inset-0 bg-primary opacity-10 rounded-2xl pointer-events-none"></div>
|
||||
</div>
|
||||
<!-- 内容容器 -->
|
||||
<div class="content-container max-w-md lg:max-w-2xl transform transition-all duration-500 ease-in-out">
|
||||
<h1 class="text-4xl md:text-5xl font-bold mb-3 relative group">
|
||||
<span class="relative z-10 bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
|
||||
Welcome to
|
||||
</span>
|
||||
<span class="text-base-content">FPGA Web Lab!</span>
|
||||
<span class="absolute bottom-0 left-0 w-0 h-1 bg-primary transition-all duration-500 ease-in-out group-hover:w-3/4"></span>
|
||||
</h1>
|
||||
|
||||
<p class="py-6 text-lg opacity-80 leading-relaxed">
|
||||
Prototype and simulate electronic circuits in your browser with our modern, intuitive interface. Create, test, and share your FPGA designs seamlessly.
|
||||
</p>
|
||||
<button class="btn btn-primary">Get Started</button>
|
||||
<div class="flex flex-wrap gap-4 actions-container">
|
||||
<router-link to="/project" class="btn btn-primary text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path>
|
||||
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path>
|
||||
</svg>
|
||||
进入工程界面
|
||||
</router-link>
|
||||
|
||||
<router-link to="/login" class="btn btn-secondary text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
|
||||
<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
|
||||
</svg>
|
||||
登录
|
||||
</router-link>
|
||||
|
||||
<router-link to="/user" class="btn btn-accent text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
||||
<circle cx="12" cy="7" r="4"></circle>
|
||||
</svg>
|
||||
用户中心
|
||||
</router-link>
|
||||
<router-link to="/test" class="btn btn-info text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path>
|
||||
<polyline points="17 21 17 13 7 13 7 21"></polyline>
|
||||
<polyline points="7 3 7 8 15 8"></polyline>
|
||||
</svg>
|
||||
测试功能
|
||||
</router-link>
|
||||
|
||||
<router-link to="/test/jtag" class="btn btn-warning text-base-100 shadow-lg transform transition-all duration-300 hover:scale-105 hover:shadow-xl hover:-translate-y-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect>
|
||||
<line x1="8" y1="21" x2="16" y2="21"></line>
|
||||
<line x1="12" y1="17" x2="12" y2="21"></line>
|
||||
</svg>
|
||||
JTAG测试
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="mt-8 p-4 bg-base-300 rounded-lg shadow-inner opacity-80 transition-all duration-300 hover:opacity-100 hover:shadow-md">
|
||||
<p class="text-sm">
|
||||
<span class="font-semibold text-primary">提示:</span> 您可以在工程界面中创建、编辑和测试您的FPGA项目,使用我们简洁直观的界面轻松进行硬件设计。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<div class="fixed bottom-10 right-10 btn btn-circle">
|
||||
<ThemeControlButton class=""></ThemeControlButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import ThemeControlButton from "@/components/ThemeControlButton.vue";
|
||||
import "@/router";
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
<template>
|
||||
<div class="h-screen w-screen flex justify-center">
|
||||
<div class="h-full w-32"></div>
|
||||
<div class="h-screen flex flex-col overflow-hidden">
|
||||
<!-- 主要内容 -->
|
||||
<div class="flex-1 flex justify-center">
|
||||
<div class="h-full w-32"></div>
|
||||
|
||||
<div class="h-full w-[70%] shadow-2xl flex">
|
||||
<button class="btn btn-primary h-10 w-30">获取ID Code</button>
|
||||
<div class="h-full w-[70%] shadow-2xl flex items-start p-4">
|
||||
<button class="btn btn-primary h-10 w-30">获取ID Code</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<template>
|
||||
<main>
|
||||
<div class="flex items-center justify-center min-h-screen">
|
||||
<LoginCard />
|
||||
<div class="relative w-full max-w-md">
|
||||
<LoginCard />
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
@@ -1,127 +1,258 @@
|
||||
<template>
|
||||
<div class="h-screen w-screen flex flex-col">
|
||||
<!-- 顶部工具栏 -->
|
||||
<div class="flex items-center p-2 border-b border-base-300 bg-base-100">
|
||||
<h2 class="text-xl font-bold mr-auto">FPGA 工程界面</h2>
|
||||
<button class="btn btn-circle btn-primary" @click="openComponentsMenu">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="12" y1="5" x2="12" y2="19"></line>
|
||||
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-1 overflow-hidden">
|
||||
<div class="h-screen flex flex-col overflow-hidden">
|
||||
<div class="flex flex-1 overflow-hidden relative">
|
||||
<!-- 左侧图形化区域 -->
|
||||
<DiagramCanvas
|
||||
ref="diagramCanvas"
|
||||
:initialComponents="components"
|
||||
@component-selected="handleComponentSelected"
|
||||
@component-moved="handleComponentMoved"
|
||||
/>
|
||||
|
||||
<!-- 右侧编辑区域 -->
|
||||
<div class="w-[40%] bg-base-100 border-l flex flex-col p-4">
|
||||
<h3 class="text-lg font-bold mb-4">属性编辑器</h3>
|
||||
<div v-if="!selectedComponent" class="text-gray-400">选择元器件以编辑属性</div>
|
||||
<div v-else>
|
||||
<div class="mb-2">编辑元器件: {{ getComponentName(selectedComponent) }}</div>
|
||||
<!-- 这里可以添加元器件的属性编辑表单 -->
|
||||
</div>
|
||||
<div class="relative bg-base-200 overflow-hidden" :style="{ width: leftPanelWidth + '%' }"> <DiagramCanvas
|
||||
ref="diagramCanvas" :components="components"
|
||||
:componentModules="componentModules"
|
||||
@component-selected="handleComponentSelected"
|
||||
@component-moved="handleComponentMoved"
|
||||
@update-component-prop="updateComponentProp"
|
||||
@component-delete="handleComponentDelete"
|
||||
/>
|
||||
<!-- 添加元器件按钮 -->
|
||||
<button class="btn btn-circle btn-primary absolute top-8 right-8 shadow-lg z-10" @click="openComponentsMenu">
|
||||
<!-- SVG icon -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="12" y1="5" x2="12" y2="19"></line>
|
||||
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 元器件选择菜单 -->
|
||||
<div v-if="showComponentsMenu" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50" @click.self="showComponentsMenu = false">
|
||||
<div class="bg-base-100 p-4 rounded-lg shadow-xl max-w-3xl max-h-[80vh] overflow-auto">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-xl font-bold">选择元器件</h3>
|
||||
<button class="btn btn-ghost btn-sm" @click="showComponentsMenu = false">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 md:grid-cols-3 gap-4">
|
||||
<div v-for="(component, index) in availableComponents" :key="index"
|
||||
class="border p-4 rounded-lg flex flex-col items-center hover:bg-base-200 cursor-pointer"
|
||||
@click="addComponent(component)">
|
||||
<div class="flex-1 flex items-center justify-center p-2">
|
||||
<component :is="component.type" style="transform: scale(0.5);"></component>
|
||||
</div>
|
||||
<div class="mt-2 text-center">{{ component.name }}</div>
|
||||
<!-- 拖拽分割线 -->
|
||||
<div
|
||||
class="resizer cursor-col-resize bg-base-300 hover:bg-primary hover:opacity-70 active:bg-primary active:opacity-90 transition-colors"
|
||||
@mousedown="startResize"
|
||||
></div>
|
||||
|
||||
<!-- 右侧编辑区域 -->
|
||||
<div class="bg-base-100 flex flex-col p-4 overflow-auto" :style="{ width: (100 - leftPanelWidth) + '%' }">
|
||||
<h3 class="text-lg font-bold mb-4">属性编辑器</h3>
|
||||
<div v-if="!selectedComponentData" class="text-gray-400">选择元器件以编辑属性</div>
|
||||
<div v-else>
|
||||
<div class="mb-4 pb-4 border-b border-base-300">
|
||||
<h4 class="font-semibold text-lg mb-1">{{ selectedComponentData.name }}</h4>
|
||||
<p class="text-xs text-gray-500">ID: {{ selectedComponentData.id }}</p>
|
||||
<p class="text-xs text-gray-500">类型: {{ selectedComponentData.type }}</p>
|
||||
</div>
|
||||
|
||||
<!-- 动态属性表单 -->
|
||||
<div v-if="selectedComponentConfig && selectedComponentConfig.props" class="space-y-4">
|
||||
<div v-for="prop in selectedComponentConfig.props" :key="prop.name" class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">{{ prop.label || prop.name }}</span>
|
||||
</label>
|
||||
<!-- 根据 prop 类型选择输入控件 -->
|
||||
<input
|
||||
v-if="prop.type === 'string'"
|
||||
type="text"
|
||||
:placeholder="prop.label || prop.name"
|
||||
class="input input-bordered input-sm w-full"
|
||||
:value="selectedComponentData.props[prop.name]"
|
||||
@input="updateComponentProp(selectedComponentData.id, prop.name, ($event.target as HTMLInputElement).value)"
|
||||
/>
|
||||
<input
|
||||
v-else-if="prop.type === 'number'"
|
||||
type="number"
|
||||
:placeholder="prop.label || prop.name"
|
||||
class="input input-bordered input-sm w-full"
|
||||
:value="selectedComponentData.props[prop.name]"
|
||||
@input="updateComponentProp(selectedComponentData.id, prop.name, parseFloat(($event.target as HTMLInputElement).value) || prop.default)"
|
||||
/> <!-- 可以为 boolean 添加 checkbox,为 color 添加 color picker 等 -->
|
||||
<div v-else-if="prop.type === 'boolean'" class="flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox checkbox-sm mr-2"
|
||||
:checked="selectedComponentData.props[prop.name]"
|
||||
@change="updateComponentProp(selectedComponentData.id, prop.name, ($event.target as HTMLInputElement).checked)"
|
||||
/>
|
||||
<span>{{ prop.label || prop.name }}</span>
|
||||
</div> <!-- 下拉选择框 -->
|
||||
<select
|
||||
v-else-if="prop.type === 'select' && prop.options"
|
||||
class="select select-bordered select-sm w-full"
|
||||
:value="selectedComponentData.props[prop.name]"
|
||||
@change="(event) => {
|
||||
const selectElement = event.target as HTMLSelectElement;
|
||||
const value = selectElement.value;
|
||||
console.log('选择的值:', value, '类型:', typeof value);
|
||||
updateComponentProp(selectedComponentData.id, prop.name, value);
|
||||
}"
|
||||
>
|
||||
<option v-for="option in prop.options" :key="option.value" :value="option.value">
|
||||
{{ option.label }}
|
||||
</option>
|
||||
</select>
|
||||
|
||||
<p v-else class="text-xs text-warning">不支持的属性类型: {{ prop.type }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="selectedComponentData && !selectedComponentConfig" class="text-gray-500 text-sm">
|
||||
正在加载组件配置...
|
||||
</div>
|
||||
<div v-else-if="selectedComponentData && selectedComponentConfig && (!selectedComponentConfig.props || selectedComponentConfig.props.length === 0)" class="text-gray-500 text-sm">
|
||||
此组件没有可配置的属性。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> </div>
|
||||
|
||||
<!-- 元器件选择组件 -->
|
||||
<ComponentSelector
|
||||
:open="showComponentsMenu"
|
||||
@update:open="showComponentsMenu = $event"
|
||||
@add-component="handleAddComponent"
|
||||
@close="showComponentsMenu = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 引入wokwi-elements和组件
|
||||
import "@wokwi/elements";
|
||||
import { ref, reactive } from 'vue';
|
||||
// import "@wokwi/elements"; // 不再需要全局引入 wokwi
|
||||
import { ref, reactive, computed, onMounted, onUnmounted, defineAsyncComponent, shallowRef } from 'vue'; // 引入 defineAsyncComponent 和 shallowRef
|
||||
import DiagramCanvas from '@/components/DiagramCanvas.vue';
|
||||
import ComponentSelector from '@/components/ComponentSelector.vue';
|
||||
import { getComponentConfig } from '@/components/equipments/componentConfig';
|
||||
import type { ComponentConfig } from '@/components/equipments/componentConfig';
|
||||
|
||||
// 元器件管理
|
||||
// --- 元器件管理 ---
|
||||
const showComponentsMenu = ref(false);
|
||||
interface ComponentItem {
|
||||
id: string;
|
||||
type: string;
|
||||
type: string; // 现在是组件的文件名或标识符,例如 'MechanicalButton'
|
||||
name: string;
|
||||
x: number;
|
||||
y: number;
|
||||
props?: Record<string, any>; // 添加 props 字段来存储组件实例的属性
|
||||
}
|
||||
const components = ref<ComponentItem[]>([]);
|
||||
const selectedComponent = ref<string | null>(null);
|
||||
const selectedComponentData = ref<ComponentItem | null>(null);
|
||||
const selectedComponentId = ref<string | null>(null); // 重命名为 selectedComponentId
|
||||
const selectedComponentData = computed(() => { // 改为计算属性
|
||||
return components.value.find(c => c.id === selectedComponentId.value) || null;
|
||||
});
|
||||
const diagramCanvas = ref(null);
|
||||
|
||||
// 可用元器件列表
|
||||
const availableComponents = [
|
||||
{ type: 'wokwi-led', name: 'LED灯' },
|
||||
{ type: 'wokwi-resistor', name: '电阻' },
|
||||
{ type: 'wokwi-pushbutton', name: '按钮' },
|
||||
{ type: 'wokwi-7segment', name: '7段数码管' },
|
||||
{ type: 'wokwi-arduino-uno', name: 'Arduino Uno' },
|
||||
{ type: 'wokwi-servo', name: '舵机' },
|
||||
{ type: 'wokwi-lcd1602', name: 'LCD显示屏' },
|
||||
{ type: 'wokwi-dht22', name: '温湿度传感器' },
|
||||
{ type: 'wokwi-buzzer', name: '蜂鸣器' }
|
||||
];
|
||||
// 存储动态导入的组件模块
|
||||
interface ComponentModule {
|
||||
default: any;
|
||||
config?: {
|
||||
props?: Array<{
|
||||
name: string;
|
||||
type: string;
|
||||
label?: string;
|
||||
default: any;
|
||||
}>;
|
||||
};
|
||||
}
|
||||
|
||||
// 打开元器件选择菜单
|
||||
const componentModules = shallowRef<Record<string, ComponentModule>>({});
|
||||
const selectedComponentConfig = shallowRef<ComponentModule['config'] | null>(null); // 存储选中组件的配置
|
||||
|
||||
// 动态加载组件定义
|
||||
async function loadComponentModule(type: string) {
|
||||
if (!componentModules.value[type]) {
|
||||
try {
|
||||
// 假设组件都在 src/components/equipments/ 目录下,且文件名与 type 相同
|
||||
const module = await import(`../components/equipments/${type}.vue`);
|
||||
|
||||
// 使用 markRaw 包装模块,避免不必要的响应式处理
|
||||
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];
|
||||
}
|
||||
|
||||
// --- 分割面板 ---
|
||||
const leftPanelWidth = ref(60);
|
||||
const isResizing = ref(false);
|
||||
|
||||
// 分割面板拖拽相关函数
|
||||
function startResize(e: MouseEvent) {
|
||||
isResizing.value = true;
|
||||
document.addEventListener('mousemove', onResize);
|
||||
document.addEventListener('mouseup', stopResize);
|
||||
e.preventDefault(); // 防止文本选择
|
||||
}
|
||||
|
||||
function onResize(e: MouseEvent) {
|
||||
if (!isResizing.value) return;
|
||||
|
||||
// 获取容器宽度和鼠标位置
|
||||
const container = document.querySelector('.flex-1.overflow-hidden') as HTMLElement;
|
||||
if (!container) return;
|
||||
|
||||
const containerWidth = container.clientWidth;
|
||||
const mouseX = e.clientX;
|
||||
|
||||
// 计算左侧面板应占的百分比
|
||||
let newWidth = (mouseX / containerWidth) * 100;
|
||||
|
||||
// 限制最小宽度和最大宽度
|
||||
newWidth = Math.max(20, Math.min(newWidth, 80));
|
||||
|
||||
// 更新宽度
|
||||
leftPanelWidth.value = newWidth;
|
||||
}
|
||||
|
||||
function stopResize() {
|
||||
isResizing.value = false;
|
||||
document.removeEventListener('mousemove', onResize);
|
||||
document.removeEventListener('mouseup', stopResize);
|
||||
}
|
||||
|
||||
// --- 元器件操作 ---
|
||||
function openComponentsMenu() {
|
||||
showComponentsMenu.value = true;
|
||||
}
|
||||
|
||||
// 添加新元器件
|
||||
function addComponent(componentTemplate) {
|
||||
const newComponent = {
|
||||
// 处理 ComponentSelector 组件添加元器件事件
|
||||
async function handleAddComponent(componentData: { type: string; name: string; props: Record<string, any> }) {
|
||||
// 加载组件模块以便后续使用
|
||||
await loadComponentModule(componentData.type);
|
||||
|
||||
const newComponent: ComponentItem = {
|
||||
id: `component-${Date.now()}`,
|
||||
type: componentTemplate.type,
|
||||
name: componentTemplate.name,
|
||||
x: 100,
|
||||
y: 100
|
||||
type: componentData.type,
|
||||
name: componentData.name,
|
||||
x: 100, // 或者计算画布中心位置
|
||||
y: 100,
|
||||
props: componentData.props, // 使用从 ComponentSelector 传递的默认属性
|
||||
};
|
||||
|
||||
|
||||
components.value.push(newComponent);
|
||||
// 由于我们使用的是响应式数据绑定,不需要再次调用 diagramCanvas 的 addComponent
|
||||
// DiagramCanvas 组件通过 :initialComponents="components" 已经接收到更新
|
||||
|
||||
showComponentsMenu.value = false;
|
||||
}
|
||||
|
||||
// 处理组件选中事件
|
||||
function handleComponentSelected(component) {
|
||||
selectedComponent.value = component ? component.id : null;
|
||||
selectedComponentData.value = component;
|
||||
async function handleComponentSelected(componentData: ComponentItem | null) {
|
||||
selectedComponentId.value = componentData ? componentData.id : null;
|
||||
selectedComponentConfig.value = null; // 重置配置
|
||||
|
||||
if (componentData) {
|
||||
// 从配置文件中获取组件配置
|
||||
const config = getComponentConfig(componentData.type);
|
||||
if (config) {
|
||||
selectedComponentConfig.value = config;
|
||||
console.log(`Config for ${componentData.type}:`, config);
|
||||
} else {
|
||||
console.warn(`No config found for component type ${componentData.type}`);
|
||||
}
|
||||
|
||||
// 同时加载组件模块以备用
|
||||
await loadComponentModule(componentData.type);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理组件移动事件
|
||||
function handleComponentMoved(moveData) {
|
||||
function handleComponentMoved(moveData: { id: string; x: number; y: number }) {
|
||||
const component = components.value.find(c => c.id === moveData.id);
|
||||
if (component) {
|
||||
component.x = moveData.x;
|
||||
@@ -129,9 +260,95 @@ function handleComponentMoved(moveData) {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取组件名称
|
||||
function getComponentName(componentId) {
|
||||
const component = components.value.find(c => c.id === componentId);
|
||||
return component ? component.name : '';
|
||||
// 处理组件删除事件
|
||||
function handleComponentDelete(componentId: string) {
|
||||
// 查找要删除的组件索引
|
||||
const index = components.value.findIndex(c => c.id === componentId);
|
||||
if (index !== -1) {
|
||||
// 从数组中移除该组件
|
||||
components.value.splice(index, 1);
|
||||
// 如果删除的是当前选中的组件,清除选中状态
|
||||
if (selectedComponentId.value === componentId) {
|
||||
selectedComponentId.value = null;
|
||||
selectedComponentConfig.value = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新组件属性的方法,处理字符串类型的初始值特殊格式
|
||||
function updateComponentProp(componentId: string | { id: string; propName: string; value: any }, propName?: string, value?: any) {
|
||||
// 处理来自 DiagramCanvas 的事件
|
||||
if (typeof componentId === 'object') {
|
||||
const { id, propName: name, value: val } = componentId;
|
||||
componentId = id;
|
||||
propName = name;
|
||||
value = val;
|
||||
}
|
||||
const component = components.value.find(c => c.id === componentId);
|
||||
if (component && propName !== undefined) {
|
||||
if (!component.props) {
|
||||
component.props = {};
|
||||
}
|
||||
|
||||
// 检查值是否为对象,如果是对象并有value属性,则使用该属性值
|
||||
if (value !== null && typeof value === 'object' && 'value' in value) {
|
||||
value = value.value;
|
||||
}
|
||||
|
||||
// 直接更新属性值
|
||||
component.props[propName] = value;
|
||||
|
||||
console.log(`Updated ${componentId} prop ${propName} to:`, value, typeof value);
|
||||
}
|
||||
}
|
||||
|
||||
// --- 生命周期钩子 ---
|
||||
onMounted(() => {
|
||||
// 无需在这里预加载组件,ComponentSelector 组件会处理这部分逻辑
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('mousemove', onResize);
|
||||
document.removeEventListener('mouseup', stopResize);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
/* 样式保持不变 */
|
||||
@import "../assets/main.css";
|
||||
|
||||
/* 分割线样式 */
|
||||
.resizer {
|
||||
width: 6px;
|
||||
height: 100%;
|
||||
background-color: var(--b3);
|
||||
cursor: col-resize;
|
||||
transition: background-color 0.3s;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.resizer:hover, .resizer:active {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
/* 调整大小时应用全局样式 */
|
||||
:global(body.resizing) {
|
||||
cursor: col-resize;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.animate-slideRight {
|
||||
animation: slideRight 0.3s ease-out forwards;
|
||||
}
|
||||
|
||||
@keyframes slideRight {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="w-screen h-screen">
|
||||
<div class="h-screen overflow-hidden">
|
||||
<Switch width="1400" height="360" />
|
||||
<MechanicalButton width="1400" height="360" />
|
||||
<PopButton></PopButton>
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import UploadCard from "@/components/UploadCard.vue";
|
||||
import Sidebar from "../components/Sidebar.vue";
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Reference in New Issue
Block a user