feat: Refactor code structure for improved readability and maintainability
This commit is contained in:
parent
d4b34bd6d4
commit
6a786c1519
|
@ -0,0 +1,197 @@
|
|||
<script setup lang="ts">
|
||||
// 定义属性接口
|
||||
interface Props {
|
||||
title: string;
|
||||
isExpanded?: boolean;
|
||||
status?: 'default' | 'success' | 'error';
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
isExpanded: false,
|
||||
status: 'default'
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:isExpanded', value: boolean): void
|
||||
}>();
|
||||
|
||||
// 切换展开/收起状态
|
||||
const toggleExpand = () => {
|
||||
emit('update:isExpanded', !props.isExpanded);
|
||||
};
|
||||
|
||||
// 动画处理函数
|
||||
const enter = (element: Element, done: () => void) => {
|
||||
if (element instanceof HTMLElement) {
|
||||
const height = element.scrollHeight;
|
||||
element.style.height = '0px';
|
||||
// 触发重绘
|
||||
element.offsetHeight;
|
||||
element.style.height = height + 'px';
|
||||
|
||||
element.addEventListener('transitionend', () => {
|
||||
done();
|
||||
}, { once: true });
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
};
|
||||
|
||||
const afterEnter = (element: Element) => {
|
||||
if (element instanceof HTMLElement) {
|
||||
element.style.height = 'auto';
|
||||
}
|
||||
};
|
||||
|
||||
const leave = (element: Element, done: () => void) => {
|
||||
if (element instanceof HTMLElement) {
|
||||
const height = element.scrollHeight;
|
||||
element.style.height = height + 'px';
|
||||
// 触发重绘
|
||||
element.offsetHeight;
|
||||
element.style.height = '0px';
|
||||
|
||||
element.addEventListener('transitionend', () => {
|
||||
done();
|
||||
}, { once: true });
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="section" :class="[`status-${status}`]">
|
||||
<div class="section-header" @click="toggleExpand">
|
||||
<h2>{{ title }}</h2>
|
||||
<span class="expand-icon" :class="{ 'is-expanded': isExpanded }">▶</span>
|
||||
</div>
|
||||
<transition
|
||||
name="collapse"
|
||||
@enter="enter"
|
||||
@after-enter="afterEnter"
|
||||
@leave="leave"
|
||||
>
|
||||
<div v-show="isExpanded" class="section-content">
|
||||
<div class="section-inner">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.section {
|
||||
margin-bottom: var(--spacing-md, 0.75rem);
|
||||
border: 1px solid hsl(var(--b3));
|
||||
border-radius: var(--radius-md, 0.375rem);
|
||||
overflow: hidden;
|
||||
background-color: hsl(var(--b1));
|
||||
box-shadow: var(--shadow-sm, 0 1px 2px 0 rgba(0, 0, 0, 0.05));
|
||||
transition: all var(--transition-normal, 0.3s);
|
||||
}
|
||||
|
||||
/* 默认状态 */
|
||||
.section.status-default {
|
||||
border-color: hsl(var(--b3));
|
||||
}
|
||||
|
||||
/* 成功状态 */
|
||||
.section.status-success {
|
||||
animation: borderPulseSuccess 2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
border-color: hsl(var(--su));
|
||||
box-shadow: 0px 0px 3px hsl(var(--su));
|
||||
}
|
||||
|
||||
/* 失败状态 */
|
||||
.section.status-error {
|
||||
animation: borderPulseError 2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
border-color: hsl(var(--er));
|
||||
box-shadow: 0px 0px 3px hsl(var(--er));
|
||||
}
|
||||
|
||||
/* 信息状态 */
|
||||
.section.status-info {
|
||||
animation: borderPulseInfo 2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
border-color: hsl(var(--in));
|
||||
box-shadow: 0px 0px 3px hsl(var(--in));
|
||||
}
|
||||
|
||||
@keyframes borderPulseSuccess {
|
||||
0% { border-color: hsl(var(--b3)); box-shadow: 0px 0px 0px transparent;}
|
||||
50% { border-color: hsl(var(--su)); box-shadow: 0px 0px 5px hsl(var(--su));}
|
||||
100% { border-color: hsl(var(--su)); box-shadow: 0px 0px 3px hsl(var(--su));}
|
||||
}
|
||||
|
||||
@keyframes borderPulseError {
|
||||
0% { border-color: hsl(var(--b3)); box-shadow: 0px 0px 0px transparent;}
|
||||
50% { border-color: hsl(var(--er)); box-shadow: 0px 0px 5px hsl(var(--er));}
|
||||
100% { border-color: hsl(var(--er)); box-shadow: 0px 0px 3px hsl(var(--er));}
|
||||
}
|
||||
|
||||
@keyframes borderPulseInfo {
|
||||
0% { border-color: hsl(var(--b3)); box-shadow: 0px 0px 0px transparent;}
|
||||
50% { border-color: hsl(var(--in)); box-shadow: 0px 0px 5px hsl(var(--in));}
|
||||
100% { border-color: hsl(var(--in)); box-shadow: 0px 0px 3px hsl(var(--in));}
|
||||
}
|
||||
|
||||
.section-header {
|
||||
padding: var(--spacing-sm, 0.5rem) var(--spacing-md, 0.75rem);
|
||||
background-color: hsl(var(--b1));
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
border-bottom: 1px solid hsl(var(--b3));
|
||||
transition: all var(--transition-normal, 0.3s);
|
||||
}
|
||||
|
||||
.section-header:hover {
|
||||
background-color: hsl(var(--b2));
|
||||
}
|
||||
|
||||
.section-header h2 {
|
||||
margin: 0;
|
||||
font-size: 1.1em;
|
||||
font-weight: 500;
|
||||
color: hsl(var(--p));
|
||||
transition: color var(--transition-normal, 0.3s);
|
||||
}
|
||||
|
||||
.expand-icon {
|
||||
font-size: 16px;
|
||||
color: hsl(var(--bc));
|
||||
transition: all var(--transition-normal, 0.3s);
|
||||
}
|
||||
|
||||
.expand-icon.is-expanded {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.section-content {
|
||||
overflow: hidden;
|
||||
transition: all var(--transition-normal, 0.3s);
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.section-inner {
|
||||
padding: var(--spacing-md, 0.75rem);
|
||||
color: hsl(var(--bc));
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.section-inner {
|
||||
padding: var(--spacing-sm, 0.5rem);
|
||||
}
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
overflow: visible; /* 允许内容溢出 */
|
||||
}
|
||||
|
||||
.card-body {
|
||||
overflow: visible; /* 允许内容溢出 */
|
||||
}
|
||||
</style>
|
|
@ -5,8 +5,7 @@
|
|||
<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="menu p-0 w-[460px] min-h-full bg-base-100 shadow-xl flex flex-col"> <!-- 菜单头部 -->
|
||||
<div class="p-6 border-b border-base-300 flex justify-between items-center">
|
||||
<h3 class="text-xl font-bold text-primary flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-primary">
|
||||
|
@ -24,13 +23,20 @@
|
|||
</label>
|
||||
</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 border-b border-base-300">
|
||||
<div class="join w-full">
|
||||
<div class="join-item flex-1 relative">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="搜索元器件..."
|
||||
placeholder="搜索..."
|
||||
class="input input-bordered input-sm w-full pl-10"
|
||||
v-model="searchQuery"
|
||||
@keyup.enter="searchComponents"
|
||||
|
@ -44,8 +50,8 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 元器件列表 -->
|
||||
<div class="px-6 py-4 overflow-auto flex-1">
|
||||
<!-- 元器件列表 (组件选项卡) -->
|
||||
<div v-if="activeTab === 'components'" class="px-6 py-4 overflow-auto flex-1">
|
||||
<div v-if="filteredComponents.length > 0" class="grid grid-cols-2 gap-4">
|
||||
<div v-for="(component, index) in filteredComponents" :key="index"
|
||||
class="card bg-base-200 hover:bg-base-300 transition-all duration-300 hover:shadow-md cursor-pointer"
|
||||
|
@ -81,6 +87,47 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 模板列表 (模板选项卡) -->
|
||||
<div v-if="activeTab === 'templates'" class="px-6 py-4 overflow-auto flex-1">
|
||||
<div v-if="filteredTemplates.length > 0" class="grid grid-cols-2 gap-4">
|
||||
<div v-for="(template, index) in filteredTemplates" :key="index"
|
||||
class="card bg-base-200 hover:bg-base-300 transition-all duration-300 hover:shadow-md cursor-pointer"
|
||||
@click="addTemplate(template)">
|
||||
<div class="card-body p-3 items-center text-center">
|
||||
<div class="bg-base-100 rounded-lg w-full h-[90px] flex items-center justify-center overflow-hidden p-2">
|
||||
<img :src="template.thumbnailUrl || '/placeholder-template.png'" alt="Template thumbnail" class="max-h-full max-w-full object-contain" />
|
||||
</div>
|
||||
<h3 class="card-title text-sm mt-2">{{ template.name }}</h3>
|
||||
<p class="text-xs opacity-70">{{ template.description || '模板' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 无搜索结果 -->
|
||||
<div v-else class="py-16 text-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="mx-auto text-base-300 mb-3">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
<line x1="8" y1="11" x2="14" y2="11"></line>
|
||||
</svg>
|
||||
<p class="text-base-content opacity-70">没有找到匹配的模板</p>
|
||||
<button class="btn btn-sm btn-ghost mt-3" @click="searchQuery = ''">
|
||||
清除搜索
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 虚拟外设列表 (虚拟外设选项卡) -->
|
||||
<div v-if="activeTab === 'virtual'" class="px-6 py-4 overflow-auto flex-1">
|
||||
<div class="py-16 text-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="mx-auto text-base-300 mb-3">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<line x1="12" y1="8" x2="12" y2="16"></line>
|
||||
<line x1="8" y1="12" x2="16" y2="12"></line>
|
||||
</svg>
|
||||
<p class="text-base-content opacity-70">虚拟外设功能即将推出</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部操作区 -->
|
||||
<div class="p-4 border-t border-base-300 bg-base-200 flex justify-between">
|
||||
<label for="component-drawer" class="btn btn-sm btn-ghost" @click="closeMenu">
|
||||
|
@ -110,7 +157,10 @@ interface Props {
|
|||
const props = defineProps<Props>();
|
||||
|
||||
// 定义组件发出的事件
|
||||
const emit = defineEmits(['close', 'add-component', 'update:open']);
|
||||
const emit = defineEmits(['close', 'add-component', 'add-template', 'update:open']);
|
||||
|
||||
// 当前激活的选项卡
|
||||
const activeTab = ref('components');
|
||||
|
||||
// --- 搜索功能 ---
|
||||
const searchQuery = ref('');
|
||||
|
@ -121,15 +171,28 @@ const availableComponents = [
|
|||
{ type: 'Switch', name: '开关' },
|
||||
{ type: 'Pin', name: '引脚' },
|
||||
{ type: 'SMT_LED', name: '贴片LED' },
|
||||
{ type: 'SevenSegmentDisplay', name: '数码管' },
|
||||
{ type: 'HDMI', name: 'HDMI接口' },
|
||||
{ type: 'DDR', name: 'DDR内存' },
|
||||
{ type: 'ETH', name: '以太网接口' },
|
||||
{ type: 'SD', name: 'SD卡插槽' },
|
||||
{ type: 'SFP', name: 'SFP光纤模块' },
|
||||
{ type: 'SMA', name: 'SMA连接器' },
|
||||
{ type: 'MotherBoard', name: '主板' }
|
||||
{ type: 'MotherBoard', name: '主板' },
|
||||
{ type: 'PG2L100H_FBG676', name: 'PG2L100H FBG676芯片' }
|
||||
];
|
||||
|
||||
// --- 可用模板列表 ---
|
||||
const availableTemplates = ref([
|
||||
{
|
||||
name: 'PG2L100H 基础开发板',
|
||||
id: 'PG2L100H_Pango100pro',
|
||||
description: '包含主板和两个LED的基本设置',
|
||||
path: '/src/components/equipments/templates/PG2L100H_Pango100pro.json',
|
||||
thumbnailUrl: '/src/components/equipments/svg/motherboard.svg'
|
||||
}
|
||||
]);
|
||||
|
||||
// 显示/隐藏组件菜单
|
||||
const showComponentsMenu = computed({
|
||||
get: () => props.open,
|
||||
|
@ -180,6 +243,7 @@ function getPreviewSize(componentType: string): number {
|
|||
'Switch': 0.35, // 开关较大,需要更小尺寸
|
||||
'Pin': 0.8, // 引脚较小,可以大一些
|
||||
'SMT_LED': 0.7, // LED可以保持适中
|
||||
'SevenSegmentDisplay': 0.4, // 数码管较大,需要较小尺寸
|
||||
'HDMI': 0.5, // HDMI接口较大
|
||||
'DDR': 0.5, // DDR内存较大
|
||||
'ETH': 0.5, // 以太网接口较大
|
||||
|
@ -235,9 +299,36 @@ async function addComponent(componentTemplate: { type: string; name: string }) {
|
|||
closeMenu();
|
||||
}
|
||||
|
||||
// 添加模板
|
||||
async function addTemplate(template: any) {
|
||||
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);
|
||||
|
||||
// 发出事件,将模板数据传递给父组件
|
||||
emit('add-template', {
|
||||
id: template.id,
|
||||
name: template.name,
|
||||
template: templateData
|
||||
});
|
||||
|
||||
// 关闭菜单
|
||||
closeMenu();
|
||||
} catch (error) {
|
||||
console.error('加载模板出错:', error);
|
||||
alert('无法加载模板文件,请检查控制台错误信息');
|
||||
}
|
||||
}
|
||||
|
||||
// 过滤后的元器件列表 (用于菜单)
|
||||
const filteredComponents = computed(() => {
|
||||
if (!searchQuery.value) {
|
||||
if (!searchQuery.value || activeTab.value !== 'components') {
|
||||
return availableComponents;
|
||||
}
|
||||
const query = searchQuery.value.toLowerCase();
|
||||
|
@ -247,6 +338,18 @@ const filteredComponents = computed(() => {
|
|||
);
|
||||
});
|
||||
|
||||
// 过滤后的模板列表 (用于菜单)
|
||||
const filteredTemplates = computed(() => {
|
||||
if (!searchQuery.value || activeTab.value !== 'templates') {
|
||||
return availableTemplates.value;
|
||||
}
|
||||
const query = searchQuery.value.toLowerCase();
|
||||
return availableTemplates.value.filter(template =>
|
||||
template.name.toLowerCase().includes(query) ||
|
||||
(template.description && template.description.toLowerCase().includes(query))
|
||||
);
|
||||
});
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
// 预加载组件模块
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="flex-1 min-w-[60%] bg-base-200 relative overflow-hidden diagram-container" ref="canvasContainer"
|
||||
<div class="flex-1 h-full w-full bg-base-200 relative overflow-hidden diagram-container" ref="canvasContainer"
|
||||
@mousedown="handleCanvasMouseDown"
|
||||
@mousedown.middle.prevent="startMiddleDrag"
|
||||
@wheel.prevent="onZoom"
|
||||
|
@ -38,9 +38,8 @@
|
|||
<div
|
||||
ref="canvas"
|
||||
class="diagram-canvas"
|
||||
:style="{ transform: `translate(${position.x}px, ${position.y}px) scale(${scale})` }">
|
||||
<!-- 渲染连线 -->
|
||||
<svg class="wires-layer" width="4000" height="4000">
|
||||
:style="{ transform: `translate(${position.x}px, ${position.y}px) scale(${scale})` }"> <!-- 渲染连线 -->
|
||||
<svg class="wires-layer" width="10000" height="10000">
|
||||
<!-- 已完成的连线 -->
|
||||
<WireComponent
|
||||
v-for="(wire, index) in wireItems"
|
||||
|
@ -77,33 +76,25 @@
|
|||
|
||||
<!-- 渲染画布上的组件 -->
|
||||
<div v-for="component in diagramParts" :key="component.id"
|
||||
class="component-wrapper"
|
||||
:class="{
|
||||
class="component-wrapper" :class="{
|
||||
'component-hover': hoveredComponent === component.id,
|
||||
'component-selected': selectedComponentId === component.id,
|
||||
'component-disabled': component.isOn,
|
||||
'component-hidden': component.hide
|
||||
}"
|
||||
:style="{
|
||||
'component-disabled': !component.isOn,
|
||||
'component-hidepins': component.hidepins
|
||||
}" :style="{
|
||||
top: component.y + 'px',
|
||||
left: component.x + 'px',
|
||||
zIndex: selectedComponentId === component.id ? 999 : 1,
|
||||
zIndex: component.index ?? 0,
|
||||
transform: component.rotate ? `rotate(${component.rotate}deg)` : 'none',
|
||||
opacity: component.isOn ? 0.6 : 1,
|
||||
display: component.hide ? 'none' : 'block'
|
||||
opacity: component.isOn ? 1 : 0.6,
|
||||
display: 'block'
|
||||
}"
|
||||
@mousedown.left.stop="startComponentDrag($event, component)"
|
||||
@mouseover="hoveredComponent = component.id"
|
||||
@mouseleave="hoveredComponent = null"> <!-- 动态渲染组件 -->
|
||||
<component :is="getComponentDefinition(component.type)"
|
||||
v-if="props.componentModules[component.type]"
|
||||
v-bind="prepareComponentProps(component.attrs || {}, component.id)" @update:bindKey="(value: string) => updateComponentProp(component.id, 'bindKey', value)"
|
||||
@pin-click="(pinInfo: any) => handlePinClick(component.id, pinInfo, pinInfo.originalEvent)"
|
||||
:ref="(el: any) => {
|
||||
if (el && componentRefs.value) {
|
||||
componentRefs.value[component.id] = el;
|
||||
}
|
||||
}"
|
||||
v-bind="prepareComponentProps(component.attrs || {}, component.id)" @update:bindKey="(value: string) => updateComponentProp(component.id, 'bindKey', value)" @pin-click="(pinInfo: any) => handlePinClick(component.id, pinInfo, pinInfo.originalEvent)" :ref="(el: any) => setComponentRef(component.id, el)"
|
||||
/>
|
||||
|
||||
<!-- Fallback if component module not loaded yet -->
|
||||
|
@ -191,9 +182,18 @@ const diagramData = ref<DiagramData>({
|
|||
// 组件引用跟踪
|
||||
const componentRefs = ref<Record<string, any>>({});
|
||||
|
||||
// 计算属性:从 diagramData 中提取组件列表
|
||||
// 计算属性:从 diagramData 中提取组件列表,并按index属性排序
|
||||
const diagramParts = computed<DiagramPart[]>(() => {
|
||||
return diagramData.value.parts;
|
||||
// 克隆原始数组以避免直接修改原始数据
|
||||
const parts = [...diagramData.value.parts];
|
||||
|
||||
// 按照index属性进行排序,index值大的排在后面(显示在上层)
|
||||
// 如果没有定义index则默认为0
|
||||
return parts.sort((a, b) => {
|
||||
const indexA = a.index ?? 0;
|
||||
const indexB = b.index ?? 0;
|
||||
return indexA - indexB;
|
||||
});
|
||||
});
|
||||
|
||||
// 计算属性:转换连接为 WireItem 列表以供渲染
|
||||
|
@ -219,14 +219,20 @@ const wireItems = computed<WireItem[]>(() => {
|
|||
// 如果找到组件,设置连线端点位置
|
||||
if (startComp) {
|
||||
startPos.x = startComp.x;
|
||||
startPos.y = startComp.y; // 尝试获取引脚精确位置(如果有实现)
|
||||
startPos.y = startComp.y;
|
||||
|
||||
// 尝试获取引脚精确位置(如果有实现)
|
||||
const startCompRef = componentRefs.value?.[startCompId];
|
||||
if (startCompRef && typeof startCompRef.getPinPosition === 'function') {
|
||||
try {
|
||||
const pinPos = startCompRef.getPinPosition(startPinId);
|
||||
console.log(`线路${index} - 起点引脚位置(来自${startCompId}):`, pinPos);
|
||||
|
||||
if (pinPos) {
|
||||
startPos.x = pinPos.x;
|
||||
startPos.y = pinPos.y;
|
||||
// 正确合并组件位置与引脚相对位置
|
||||
startPos.x = startComp.x + pinPos.x;
|
||||
startPos.y = startComp.y + pinPos.y;
|
||||
console.log(`线路${index} - 计算后的起点位置:`, startPos);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`获取引脚位置出错:`, error);
|
||||
|
@ -243,9 +249,13 @@ const wireItems = computed<WireItem[]>(() => {
|
|||
if (endCompRef && typeof endCompRef.getPinPosition === 'function') {
|
||||
try {
|
||||
const pinPos = endCompRef.getPinPosition(endPinId);
|
||||
console.log(`线路${index} - 终点引脚位置(来自${endCompId}):`, pinPos);
|
||||
|
||||
if (pinPos) {
|
||||
endPos.x = pinPos.x;
|
||||
endPos.y = pinPos.y;
|
||||
// 正确合并组件位置与引脚相对位置
|
||||
endPos.x = endComp.x + pinPos.x;
|
||||
endPos.y = endComp.y + pinPos.y;
|
||||
console.log(`线路${index} - 计算后的终点位置:`, endPos);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`获取引脚位置出错:`, error);
|
||||
|
@ -282,7 +292,7 @@ const fileInput = ref<HTMLInputElement | null>(null);
|
|||
|
||||
// --- 缩放功能 ---
|
||||
const MIN_SCALE = 0.2;
|
||||
const MAX_SCALE = 3.0;
|
||||
const MAX_SCALE = 10.0;
|
||||
|
||||
function onZoom(e: WheelEvent) {
|
||||
e.preventDefault();
|
||||
|
@ -341,6 +351,22 @@ function prepareComponentProps(attrs: Record<string, any>, componentId?: string)
|
|||
return result;
|
||||
}
|
||||
|
||||
// 设置组件引用
|
||||
function setComponentRef(componentId: string, el: any) {
|
||||
if (componentRefs.value) {
|
||||
if (el) {
|
||||
componentRefs.value[componentId] = el;
|
||||
} else {
|
||||
delete componentRefs.value[componentId];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 重置组件引用缓存
|
||||
function resetComponentRefs() {
|
||||
componentRefs.value = {};
|
||||
}
|
||||
|
||||
// 加载组件模块
|
||||
async function loadComponentModule(type: string) {
|
||||
if (!props.componentModules[type]) {
|
||||
|
@ -567,39 +593,74 @@ function handlePinClick(componentId: string, pinInfo: any, event: MouseEvent) {
|
|||
// 获取引脚ID
|
||||
const pinId = pinInfo.label;
|
||||
|
||||
console.log('----引脚点击详情开始----');
|
||||
console.log('组件ID:', componentId);
|
||||
console.log('引脚ID:', pinId);
|
||||
console.log('引脚原始信息:', pinInfo);
|
||||
console.log('鼠标位置:', mousePosition);
|
||||
|
||||
if (!isCreatingWire.value) {
|
||||
// 开始创建连线
|
||||
const containerRect = canvasContainer.value.getBoundingClientRect();
|
||||
// 获取引脚位置 - 优先使用pinInfo中传递的position
|
||||
// 获取初始位置信息
|
||||
let pinPosition = pinInfo.position;
|
||||
console.log(pinInfo);
|
||||
console.log('引脚位置:', pinPosition);
|
||||
console.log('Pin信息:', pinInfo);
|
||||
console.log('Pin初始位置:', pinPosition);
|
||||
console.log('组件ID:', componentId);
|
||||
console.log('引脚ID:', pinId);
|
||||
console.log('组件引用:', componentRefs);
|
||||
console.log('组件引用:', componentRefs.value[componentId]);
|
||||
|
||||
// 从组件引用中获取组件实例
|
||||
const component = componentRefs.value[componentId];
|
||||
if (component && typeof component.getPinPosition === 'function') {
|
||||
console.log('组件引用:', component);
|
||||
|
||||
// 查找组件部件对象以获取组件位置
|
||||
const componentPart = diagramParts.value.find(p => p.id === componentId);
|
||||
if (!componentPart) {
|
||||
console.error('找不到组件部件对象:', componentId);
|
||||
}
|
||||
|
||||
// 重新设置引脚位置(初始化)
|
||||
pinPosition = { x: 0, y: 0 };
|
||||
|
||||
// 如果组件实例存在且有 getPinPosition 方法
|
||||
if (component && typeof component.getPinPosition === 'function' && componentPart) {
|
||||
try {
|
||||
console.log('从组件获取引脚位置:', componentId, pinId);
|
||||
pinPosition += component.getPinPosition(pinId);
|
||||
console.log('尝试从组件获取引脚位置');
|
||||
console.log('组件部件位置:', componentPart.x, componentPart.y);
|
||||
const pinRelativePos = component.getPinPosition(pinId);
|
||||
console.log('组件返回的引脚相对位置:', pinRelativePos);
|
||||
|
||||
if (pinRelativePos) {
|
||||
// 计算引脚的绝对位置 = 组件位置 + 引脚相对位置
|
||||
pinPosition = {
|
||||
x: componentPart.x + pinRelativePos.x,
|
||||
y: componentPart.y + pinRelativePos.y
|
||||
};
|
||||
console.log('计算的引脚绝对位置:', pinPosition);
|
||||
} else {
|
||||
// 如果没有找到引脚位置,使用组件位置
|
||||
pinPosition = {
|
||||
x: componentPart.x,
|
||||
y: componentPart.y
|
||||
};
|
||||
console.log('使用组件位置:', pinPosition);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`获取引脚位置出错:`, error);
|
||||
}
|
||||
}
|
||||
// 如果还是没有position,使用默认位置
|
||||
if (!pinPosition) {
|
||||
console.warn('无法获取引脚位置,使用默认值');
|
||||
pinPosition = { x: 0, y: 0 };
|
||||
} else if (componentPart) {
|
||||
// 如果组件不存在或没有 getPinPosition 方法,使用组件的位置
|
||||
pinPosition = {
|
||||
x: componentPart.x,
|
||||
y: componentPart.y
|
||||
};
|
||||
console.log('使用组件位置:', pinPosition);
|
||||
}
|
||||
|
||||
console.log('最终的引脚位置:', pinPosition);
|
||||
console.log('最终使用的引脚位置:', pinPosition);
|
||||
|
||||
// 计算引脚在画布坐标系中的位置
|
||||
const pinCanvasX = (pinPosition.x - containerRect.left - position.x) / scale.value;
|
||||
const pinCanvasY = (pinPosition.y - containerRect.top - position.y) / scale.value;
|
||||
|
||||
setWireCreationStart(pinCanvasX, pinCanvasY, componentId, pinId, pinInfo.constraint);
|
||||
// 使用最终的引脚位置作为连线起点
|
||||
setWireCreationStart(pinPosition.x, pinPosition.y, componentId, pinId, pinInfo.constraint);
|
||||
document.addEventListener('mousemove', onCreatingWireMouseMove);
|
||||
} else {
|
||||
// 完成连线创建
|
||||
|
@ -609,6 +670,48 @@ function handlePinClick(componentId: string, pinInfo: any, event: MouseEvent) {
|
|||
return;
|
||||
}
|
||||
|
||||
// 获取终点引脚位置
|
||||
let endPosition = { x: 0, y: 0 };
|
||||
const componentPart = diagramParts.value.find(p => p.id === componentId);
|
||||
const endComponent = componentRefs.value[componentId];
|
||||
|
||||
console.log('终点组件部件:', componentPart);
|
||||
console.log('终点组件引用:', endComponent);
|
||||
|
||||
// 如果找到组件,设置终点位置
|
||||
if (componentPart) {
|
||||
endPosition.x = componentPart.x;
|
||||
endPosition.y = componentPart.y;
|
||||
|
||||
// 如果组件实现了getPinPosition方法,使用它
|
||||
if (endComponent && typeof endComponent.getPinPosition === 'function') {
|
||||
try {
|
||||
const pinPos = endComponent.getPinPosition(pinId);
|
||||
console.log('终点组件返回的引脚位置:', pinPos);
|
||||
|
||||
if (pinPos) {
|
||||
// 正确合并组件位置与引脚相对位置
|
||||
endPosition = {
|
||||
x: componentPart.x + pinPos.x,
|
||||
y: componentPart.y + pinPos.x
|
||||
};
|
||||
console.log('终点引脚位置(来自组件方法):', endPosition);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`获取终点引脚位置出错:`, error);
|
||||
}
|
||||
} else {
|
||||
// 对于没有提供引脚精确位置的组件,使用组件位置
|
||||
console.log('终点组件没有提供引脚位置方法,使用组件位置');
|
||||
}
|
||||
} else {
|
||||
console.error('找不到终点组件部件对象:', componentId);
|
||||
}
|
||||
|
||||
console.log('最终使用的终点位置:', endPosition);
|
||||
console.log('线路从', creatingWireStartInfo, '到', { componentId, pinId });
|
||||
console.log('----引脚点击详情结束----');
|
||||
|
||||
// 创建新的连线
|
||||
const newConnection: ConnectionArray = [
|
||||
`${creatingWireStartInfo.componentId}:${creatingWireStartInfo.pinId}`,
|
||||
|
@ -822,6 +925,9 @@ function showToast(message: string, type: 'success' | 'error' | 'info' = 'info',
|
|||
|
||||
// --- 生命周期钩子 ---
|
||||
onMounted(async () => {
|
||||
// 重置组件引用
|
||||
resetComponentRefs();
|
||||
|
||||
// 加载图表数据
|
||||
try {
|
||||
diagramData.value = await loadDiagramData();
|
||||
|
@ -843,12 +949,11 @@ onMounted(async () => {
|
|||
} catch (error) {
|
||||
console.error('加载图表数据失败:', error);
|
||||
}
|
||||
|
||||
// 初始化中心位置
|
||||
// 初始化中心位置
|
||||
if (canvasContainer.value) {
|
||||
// 修改为将画布中心点放在容器中心点
|
||||
position.x = canvasContainer.value.clientWidth / 2 - 2000; // 画布宽度的一半
|
||||
position.y = canvasContainer.value.clientHeight / 2 - 2000; // 画布高度的一半
|
||||
position.x = canvasContainer.value.clientWidth / 2 - 5000; // 画布宽度的一半
|
||||
position.y = canvasContainer.value.clientHeight / 2 - 5000; // 画布高度的一半
|
||||
}
|
||||
|
||||
// 添加键盘事件监听器
|
||||
|
@ -858,7 +963,7 @@ onMounted(async () => {
|
|||
// 处理键盘事件
|
||||
function handleKeyDown(e: KeyboardEvent) {
|
||||
// 如果当前有选中的组件,并且按下了Delete键
|
||||
if (selectedComponentId.value && (e.key === 'Delete' || e.key === 'Backspace')) {
|
||||
if (selectedComponentId.value && (e.key === 'Delete')) {
|
||||
// 触发删除组件事件
|
||||
deleteComponent(selectedComponentId.value);
|
||||
}
|
||||
|
@ -961,6 +1066,11 @@ defineExpose({
|
|||
watch(diagramData, (newData) => {
|
||||
saveDiagramData(newData);
|
||||
}, { deep: true });
|
||||
|
||||
// 当组件模块加载完成后,确保组件引用正确建立
|
||||
watch(() => props.componentModules, () => {
|
||||
// 这里不需要特别处理,Vue 会自动通过 setComponentRef 函数更新引用
|
||||
}, { deep: true });
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@ -969,23 +1079,20 @@ watch(diagramData, (newData) => {
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
/* background-image:
|
||||
linear-gradient(to right, rgba(100, 100, 100, 0.1) 1px, transparent 1px),
|
||||
linear-gradient(to bottom, rgba(100, 100, 100, 0.1) 1px, transparent 1px),
|
||||
linear-gradient(to right, rgba(80, 80, 80, 0.2) 100px, transparent 100px),
|
||||
linear-gradient(to bottom, rgba(80, 80, 80, 0.2) 100px, transparent 100px); */
|
||||
background-size: 20px 20px, 20px 20px, 100px 100px, 100px 100px;
|
||||
background-position: 0 0;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.diagram-canvas {
|
||||
position: relative;
|
||||
width: 4000px;
|
||||
height: 4000px;
|
||||
width: 10000px;
|
||||
height: 10000px;
|
||||
transform-origin: 0 0;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
|
@ -1002,6 +1109,7 @@ watch(diagramData, (newData) => {
|
|||
height: 100%;
|
||||
pointer-events: auto; /* 修复:允许线被点击 */
|
||||
z-index: 50;
|
||||
overflow: visible; /* 确保超出SVG范围的内容也能显示 */
|
||||
}
|
||||
|
||||
.wires-layer path {
|
||||
|
@ -1035,7 +1143,6 @@ watch(diagramData, (newData) => {
|
|||
outline: 3px dashed;
|
||||
outline-color: #e74c3c #f39c12 #3498db #2ecc71;
|
||||
outline-offset: 3px;
|
||||
z-index: 999 !important; /* 使用更高的z-index确保始终在顶层 */
|
||||
}
|
||||
|
||||
/* 禁用状态 */
|
||||
|
@ -1044,6 +1151,11 @@ watch(diagramData, (newData) => {
|
|||
filter: grayscale(70%);
|
||||
}
|
||||
|
||||
/* 隐藏引脚状态 */
|
||||
.component-hidepins :deep([data-pin-wrapper]) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 为黑暗模式设置不同的网格线颜色 */
|
||||
/* :root[data-theme="dark"] .diagram-container {
|
||||
background-image:
|
||||
|
|
|
@ -0,0 +1,195 @@
|
|||
<template>
|
||||
<div class="property-editor">
|
||||
<div v-if="!componentData" 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">{{ componentData?.type }}</h4>
|
||||
<p class="text-xs text-base-content opacity-70">ID: {{ componentData?.id }}</p>
|
||||
<p class="text-xs text-base-content opacity-70">类型: {{ componentData?.type }}</p>
|
||||
</div>
|
||||
|
||||
<!-- 通用属性部分 -->
|
||||
<CollapsibleSection
|
||||
title="通用属性"
|
||||
v-model:isExpanded="generalPropsExpanded"
|
||||
status="default"
|
||||
>
|
||||
<div class="space-y-4">
|
||||
<div v-for="prop in getGeneralProps()" :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 === 'number'"
|
||||
type="number"
|
||||
:placeholder="prop.label || prop.name"
|
||||
class="input input-bordered input-sm w-full"
|
||||
:value="getPropValue(componentData, prop.name)"
|
||||
:disabled="prop.isReadOnly"
|
||||
:min="prop.min"
|
||||
:max="prop.max"
|
||||
:step="prop.step"
|
||||
@input="updateDirectProp(componentData.id, prop.name, parseFloat(($event.target as HTMLInputElement).value) || prop.default)"
|
||||
/>
|
||||
<input
|
||||
v-else-if="prop.type === 'string'"
|
||||
type="text"
|
||||
:placeholder="prop.label || prop.name"
|
||||
class="input input-bordered input-sm w-full"
|
||||
:value="getPropValue(componentData, prop.name)"
|
||||
:disabled="prop.isReadOnly"
|
||||
@input="updateDirectProp(componentData.id, prop.name, ($event.target as HTMLInputElement).value)"
|
||||
/>
|
||||
<div v-else-if="prop.type === 'boolean'" class="flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox checkbox-sm mr-2"
|
||||
:checked="getPropValue(componentData, prop.name)"
|
||||
:disabled="prop.isReadOnly"
|
||||
@change="updateDirectProp(componentData.id, prop.name, ($event.target as HTMLInputElement).checked)"
|
||||
/>
|
||||
<span>{{ prop.label || prop.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
|
||||
<!-- 组件特有属性部分 -->
|
||||
<CollapsibleSection
|
||||
title="组件特有属性"
|
||||
v-model:isExpanded="componentPropsExpanded"
|
||||
status="default"
|
||||
class="mt-4"
|
||||
>
|
||||
<div v-if="componentConfig && componentConfig.props" class="space-y-4">
|
||||
<div v-for="prop in getComponentProps()" :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="componentData?.attrs?.[prop.name]"
|
||||
:disabled="prop.isReadOnly"
|
||||
@input="updateProp(componentData.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="componentData?.attrs?.[prop.name]"
|
||||
:disabled="prop.isReadOnly"
|
||||
:min="prop.min"
|
||||
:max="prop.max"
|
||||
:step="prop.step"
|
||||
@input="updateProp(componentData.id, prop.name, parseFloat(($event.target as HTMLInputElement).value) || prop.default)"
|
||||
/>
|
||||
<div v-else-if="prop.type === 'boolean'" class="flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox checkbox-sm mr-2"
|
||||
:checked="componentData?.attrs?.[prop.name]"
|
||||
:disabled="prop.isReadOnly"
|
||||
@change="updateProp(componentData.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="componentData?.attrs?.[prop.name]"
|
||||
:disabled="prop.isReadOnly"
|
||||
@change="(event) => {
|
||||
const selectElement = event.target as HTMLSelectElement;
|
||||
const value = selectElement.value;
|
||||
if (componentData) {
|
||||
updateProp(componentData.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="componentData && !componentConfig" class="text-base-content opacity-70 text-sm">
|
||||
正在加载组件配置...
|
||||
</div>
|
||||
<div v-else-if="componentData && componentConfig && getComponentProps().length === 0" class="text-base-content opacity-70 text-sm">
|
||||
此组件没有特有属性可配置。
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import CollapsibleSection from './CollapsibleSection.vue';
|
||||
import { type DiagramPart } from '@/components/diagramManager';
|
||||
import {
|
||||
type PropertyConfig,
|
||||
getPropValue
|
||||
} from '@/components/equipments/componentConfig';
|
||||
|
||||
// 定义属性
|
||||
const props = defineProps<{
|
||||
componentData: DiagramPart | null;
|
||||
componentConfig: { props: PropertyConfig[] } | null;
|
||||
}>();
|
||||
|
||||
// 定义事件
|
||||
const emit = defineEmits<{
|
||||
(e: 'updateProp', componentId: string, propName: string, value: any): void;
|
||||
(e: 'updateDirectProp', componentId: string, propName: string, value: any): void;
|
||||
}>();
|
||||
|
||||
// 控制折叠面板状态
|
||||
const generalPropsExpanded = ref(true);
|
||||
const componentPropsExpanded = ref(true);
|
||||
|
||||
// 更新组件属性方法
|
||||
function updateProp(componentId: string, propName: string, value: any) {
|
||||
emit('updateProp', componentId, propName, value);
|
||||
}
|
||||
|
||||
// 更新组件的直接属性
|
||||
function updateDirectProp(componentId: string, propName: string, value: any) {
|
||||
emit('updateDirectProp', componentId, propName, value);
|
||||
}
|
||||
|
||||
// 获取通用属性(直接属性)
|
||||
function getGeneralProps(): PropertyConfig[] {
|
||||
return props.componentConfig?.props.filter(p => p.isDirectProp && p.name !== 'pins') || [];
|
||||
}
|
||||
|
||||
// 获取组件特有属性(非直接属性)
|
||||
function getComponentProps(): PropertyConfig[] {
|
||||
return props.componentConfig?.props.filter(p => !p.isDirectProp && p.name !== 'pins') || [];
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.property-editor {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 添加一些垂直间距 */
|
||||
.form-control {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
/* 针对黑暗模式的文本颜色调整 */
|
||||
:deep(.label-text) {
|
||||
color: hsl(var(--bc));
|
||||
opacity: 0.9;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,152 @@
|
|||
<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>
|
||||
|
||||
<!-- 如果选中的组件有pins属性,则显示引脚配置区域 -->
|
||||
<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"
|
||||
/>
|
||||
</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"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
|
||||
<!-- 未来可以在这里添加更多的分区 -->
|
||||
<!-- 例如:
|
||||
<CollapsibleSection
|
||||
title="连线管理"
|
||||
v-model:isExpanded="wireSectionExpanded"
|
||||
status="default"
|
||||
>
|
||||
<div>连线管理内容</div>
|
||||
</CollapsibleSection>
|
||||
-->
|
||||
</div>
|
||||
</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 { ref, computed, watch } from 'vue';
|
||||
|
||||
// 定义Pin接口
|
||||
interface Pin {
|
||||
pinId: string;
|
||||
constraint: string;
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
// 定义属性
|
||||
const props = defineProps<{
|
||||
componentData: DiagramPart | null;
|
||||
componentConfig: { props: PropertyConfig[] } | null;
|
||||
}>();
|
||||
|
||||
// 控制各个分区的展开状态
|
||||
const propertySectionExpanded = ref(true);
|
||||
const pinsSectionExpanded = ref(false);
|
||||
const wireSectionExpanded = ref(false);
|
||||
|
||||
// 本地维护一个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属性
|
||||
const hasPinsProperty = computed(() => {
|
||||
if (!props.componentData || !props.componentData.attrs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查配置中是否有pins属性
|
||||
if (props.componentConfig && props.componentConfig.props) {
|
||||
return props.componentConfig.props.some(prop => prop.name === 'pins' && prop.isArrayType);
|
||||
}
|
||||
|
||||
// 或直接检查attrs中是否有pins属性
|
||||
return 'pins' in props.componentData.attrs;
|
||||
});
|
||||
|
||||
// 定义事件
|
||||
const emit = defineEmits<{
|
||||
(e: 'updateProp', componentId: string, propName: string, value: any): void;
|
||||
(e: 'updateDirectProp', componentId: string, propName: string, value: any): void;
|
||||
}>();
|
||||
|
||||
// 更新pins属性
|
||||
function updatePins() {
|
||||
if (props.componentData && props.componentData.id) {
|
||||
emit('updateProp', props.componentData.id, 'pins', componentPins.value);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.property-panel {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.pin-item {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.pin-item:hover {
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
</style>
|
|
@ -18,8 +18,9 @@ export interface DiagramPart {
|
|||
rotate: number;
|
||||
group: string;
|
||||
positionlock: boolean;
|
||||
hide: boolean;
|
||||
hidepins: boolean;
|
||||
isOn: boolean;
|
||||
index?: number; // 显示层级,数值越大显示越靠前
|
||||
}
|
||||
|
||||
// 连接类型定义 - 使用元组类型表示四元素数组
|
||||
|
@ -170,17 +171,38 @@ export function updatePartAttribute(
|
|||
};
|
||||
}
|
||||
|
||||
// 删除组件
|
||||
// 删除组件及同组组件
|
||||
export function deletePart(data: DiagramData, partId: string): DiagramData {
|
||||
// 首先找到要删除的组件
|
||||
const component = data.parts.find(part => part.id === partId);
|
||||
if (!component) return data;
|
||||
|
||||
// 收集需要删除的组件ID列表
|
||||
const componentsToDelete: string[] = [partId];
|
||||
|
||||
// 如果组件属于一个组,则找出所有同组的组件
|
||||
if (component.group && component.group !== '') {
|
||||
const groupMembers = data.parts.filter(
|
||||
p => p.group === component.group && p.id !== partId
|
||||
);
|
||||
|
||||
// 将同组组件ID添加到删除列表
|
||||
componentsToDelete.push(...groupMembers.map(p => p.id));
|
||||
console.log(`删除组件 ${partId} 及其组 ${component.group} 中的 ${groupMembers.length} 个组件`);
|
||||
}
|
||||
|
||||
return {
|
||||
...data,
|
||||
parts: data.parts.filter(part => part.id !== partId),
|
||||
// 同时删除与此组件相关的所有连接
|
||||
// 删除所有标记的组件
|
||||
parts: data.parts.filter(part => !componentsToDelete.includes(part.id)),
|
||||
// 删除与这些组件相关的所有连接
|
||||
connections: data.connections.filter(conn => {
|
||||
const [startPin, endPin] = conn;
|
||||
const startCompId = startPin.split(':')[0];
|
||||
const endCompId = endPin.split(':')[0];
|
||||
return startCompId !== partId && endCompId !== partId;
|
||||
|
||||
// 检查连接两端的组件是否在删除列表中
|
||||
return !componentsToDelete.includes(startCompId) && !componentsToDelete.includes(endCompId);
|
||||
})
|
||||
};
|
||||
}
|
||||
|
|
|
@ -26,9 +26,7 @@
|
|||
<stop stop-color="#171717" offset="0" />
|
||||
<stop stop-color="#4b4b4b" offset="1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- 按钮底座 -->
|
||||
</defs> <!-- 按钮底座 -->
|
||||
<rect width="800" height="800" x="400" y="400" fill="#464646" rx="20" />
|
||||
<rect width="700" height="700" x="450" y="450" fill="#eaeaea" rx="20" />
|
||||
|
||||
|
@ -51,9 +49,9 @@
|
|||
@mouseleave="toggleButtonState(false)"
|
||||
style="pointer-events: auto; transition: all 20ms ease-in-out; cursor: pointer;"
|
||||
/>
|
||||
<!-- 按键文字 -->
|
||||
<!-- 按键文字 - 仅显示绑定的按键 -->
|
||||
<text
|
||||
v-if="displayText"
|
||||
v-if="bindKeyDisplay"
|
||||
x="800"
|
||||
y="800"
|
||||
font-size="310"
|
||||
|
@ -62,24 +60,30 @@
|
|||
fill="#ccc"
|
||||
style="font-family: Arial; filter: url(#btn-shadow); user-select: none; pointer-events: none; mix-blend-mode: overlay;"
|
||||
>
|
||||
{{ displayText }}
|
||||
{{ bindKeyDisplay }}
|
||||
</text>
|
||||
</svg>
|
||||
|
||||
<!-- 嵌入Pin组件,覆盖在按钮上 -->
|
||||
<div class="pin-wrapper" :style="{
|
||||
position: 'absolute',
|
||||
top: '80%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, 20%)',
|
||||
zIndex: 3,
|
||||
pointerEvents: 'auto'
|
||||
}">
|
||||
<!-- 渲染自定义引脚数组 -->
|
||||
<div v-for="pin in props.pins" :key="pin.pinId"
|
||||
:style="{
|
||||
position: 'absolute',
|
||||
left: `${pin.x * props.size}px`,
|
||||
top: `${pin.y * props.size}px`,
|
||||
transform: 'translate(-50%, -50%)',
|
||||
zIndex: 3,
|
||||
pointerEvents: 'auto'
|
||||
}"
|
||||
:data-pin-wrapper="`${pin.pinId}`"
|
||||
:data-pin-x="`${pin.x * props.size}`"
|
||||
:data-pin-y="`${pin.y * props.size}`">
|
||||
<Pin
|
||||
ref="pinRef"
|
||||
:ref="el => { if(el) pinRefs[pin.pinId] = el }"
|
||||
direction="output"
|
||||
type="digital"
|
||||
:constraint="props.constraint"
|
||||
:label="pin.pinId"
|
||||
:constraint="pin.constraint"
|
||||
:pinId="pin.pinId"
|
||||
:size="0.8"
|
||||
:componentId="props.componentId"
|
||||
@value-change="handlePinValueChange"
|
||||
|
@ -94,47 +98,42 @@ import { ref, onMounted, onUnmounted, computed } from 'vue';
|
|||
import Pin from './Pin.vue';
|
||||
import { notifyConstraintChange } from '../../stores/constraints';
|
||||
|
||||
const pinRef = ref<any>(null);
|
||||
|
||||
// 从Pin组件继承属性
|
||||
interface PinProps {
|
||||
constraint?: string;
|
||||
componentId?: string; // 添加componentId属性
|
||||
// 这些属性被预设为固定值,但仍然包含在类型中以便完整继承
|
||||
direction?: 'input' | 'output' | 'inout';
|
||||
type?: 'digital' | 'analog';
|
||||
}
|
||||
// 存储多个Pin引用
|
||||
const pinRefs = ref<Record<string, any>>({});
|
||||
|
||||
// 按钮特有属性
|
||||
interface ButtonProps {
|
||||
size?: number;
|
||||
bindKey?: string;
|
||||
buttonText?: string;
|
||||
componentId?: string;
|
||||
pins?: {
|
||||
pinId: string;
|
||||
constraint: string;
|
||||
x: number;
|
||||
y: number;
|
||||
}[];
|
||||
}
|
||||
|
||||
// 组合两个接口
|
||||
interface Props extends PinProps, ButtonProps {}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
const props = withDefaults(defineProps<ButtonProps>(), {
|
||||
size: 1,
|
||||
bindKey: '',
|
||||
buttonText: '',
|
||||
constraint: '',
|
||||
componentId: 'button-default', // 添加默认componentId
|
||||
// 这些值会被覆盖,但需要默认值以满足类型要求
|
||||
direction: 'output',
|
||||
type: 'digital'
|
||||
componentId: 'button-default',
|
||||
pins: () => [
|
||||
{
|
||||
pinId: 'BTN',
|
||||
constraint: '',
|
||||
x: 80,
|
||||
y: 140
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// 计算实际宽高
|
||||
const width = computed(() => 160 * props.size);
|
||||
const height = computed(() => 160 * props.size);
|
||||
|
||||
// 计算文本显示内容
|
||||
const displayText = computed(() => {
|
||||
if (props.buttonText) return props.buttonText;
|
||||
return props.bindKey ? props.bindKey.toUpperCase() : '';
|
||||
});
|
||||
// 显示绑定的按键
|
||||
const bindKeyDisplay = computed(() => props.bindKey ? props.bindKey.toUpperCase() : '');
|
||||
|
||||
// 定义组件发出的事件
|
||||
const emit = defineEmits([
|
||||
|
@ -171,15 +170,24 @@ function toggleButtonState(isPressed: boolean) {
|
|||
if (isPressed) {
|
||||
emit('press');
|
||||
// 如果有约束,通知约束状态变化为高电平
|
||||
if (props.constraint) {
|
||||
notifyConstraintChange(props.constraint, 'high');
|
||||
// 对所有引脚应用相同的状态
|
||||
if (props.pins) {
|
||||
props.pins.forEach(pin => {
|
||||
if (pin.constraint) {
|
||||
notifyConstraintChange(pin.constraint, 'high');
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
emit('release');
|
||||
emit('click');
|
||||
// 如果有约束,通知约束状态变化为低电平
|
||||
if (props.constraint) {
|
||||
notifyConstraintChange(props.constraint, 'low');
|
||||
if (props.pins) {
|
||||
props.pins.forEach(pin => {
|
||||
if (pin.constraint) {
|
||||
notifyConstraintChange(pin.constraint, 'low');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -207,18 +215,39 @@ defineExpose({
|
|||
getInfo: () => ({
|
||||
// 按钮特有属性
|
||||
bindKey: props.bindKey,
|
||||
buttonText: props.buttonText,
|
||||
constraint: props.constraint,
|
||||
componentId: props.componentId, // 添加componentId
|
||||
// 固定的Pin属性
|
||||
direction: 'output',
|
||||
type: 'digital'
|
||||
componentId: props.componentId,
|
||||
pins: props.pins
|
||||
}),
|
||||
// 代理 getPinPosition 到内部 Pin
|
||||
getPinPosition: (pinLabel: string) => {
|
||||
if (pinRef.value && pinRef.value.getPinPosition) {
|
||||
return pinRef.value.getPinPosition(pinLabel);
|
||||
// 获取引脚位置
|
||||
getPinPosition: (pinId: string) => {
|
||||
console.log(`[MechanicalButton] 调用getPinPosition,寻找pinId: ${pinId}`);
|
||||
console.log(`[MechanicalButton] 组件ID: ${props.componentId}, 当前尺寸: ${props.size}, 组件宽高: ${width.value}x${height.value}`);
|
||||
console.log(`[MechanicalButton] 当前存在的pins:`, props.pins);
|
||||
|
||||
// 如果是自定义的引脚ID
|
||||
if (props.pins && props.pins.length > 0) {
|
||||
const customPin = props.pins.find(p => p.pinId === pinId);
|
||||
|
||||
if (customPin) {
|
||||
console.log(`[MechanicalButton] 找到自定义引脚: ${pinId},配置位置:`, { x: customPin.x, y: customPin.y });
|
||||
|
||||
// 考虑组件尺寸的缩放
|
||||
// 这里的x和y是针对标准尺寸(size=1)的坐标,需要根据实际size调整
|
||||
const scaledX = customPin.x * props.size;
|
||||
const scaledY = customPin.y * props.size;
|
||||
|
||||
console.log(`[MechanicalButton] 返回缩放后的坐标:`, { x: scaledX, y: scaledY });
|
||||
return {
|
||||
x: scaledX,
|
||||
y: scaledY
|
||||
};
|
||||
} else {
|
||||
console.log(`[MechanicalButton] 未找到pinId: ${pinId}的引脚配置`);
|
||||
}
|
||||
} else {
|
||||
console.log(`[MechanicalButton] 没有配置任何引脚`);
|
||||
}
|
||||
console.log(`[MechanicalButton] 返回null,未找到引脚`);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
@ -230,7 +259,6 @@ export function getDefaultProps() {
|
|||
return {
|
||||
size: 1,
|
||||
bindKey: '',
|
||||
buttonText: '',
|
||||
pins: [
|
||||
{
|
||||
pinId: 'BTN',
|
||||
|
|
|
@ -1,181 +1,31 @@
|
|||
<template> <div class="motherboard-container" :style="{ width: width + 'px', height: height + 'px', position: 'relative' }"> <!-- 主板 SVG -->
|
||||
<img
|
||||
src="../equipments/svg/motherboard.svg"
|
||||
<template>
|
||||
<div class="motherboard-container" :style="{ width: width + 'px', height: height + 'px', position: 'relative' }">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
:width="width"
|
||||
:height="height"
|
||||
alt="主板"
|
||||
class="svg-image"
|
||||
draggable="false"
|
||||
/>
|
||||
|
||||
<!-- 嵌入各种组件 --> <!-- HDMI -->
|
||||
<div class="component-wrapper hdmi-wrapper" :style="{
|
||||
position: 'absolute',
|
||||
top: `${140 * props.size}px`,
|
||||
left: `${-48 * props.size}px`,
|
||||
zIndex: 10
|
||||
}">
|
||||
<HDMI :size="1.5*props.size" />
|
||||
</div>
|
||||
<!-- HDMI -->
|
||||
<div class="component-wrapper hdmi-wrapper" :style="{
|
||||
position: 'absolute',
|
||||
top: `${260 * props.size}px`,
|
||||
left: `${-48 * props.size}px`,
|
||||
zIndex: 10
|
||||
}">
|
||||
<HDMI :size="1.5*props.size" />
|
||||
</div> <!-- ETH -->
|
||||
<div class="component-wrapper eth-wrapper" :style="{
|
||||
position: 'absolute',
|
||||
top: `${365 * props.size}px`,
|
||||
left: `${-10 * props.size}px`,
|
||||
zIndex: 10
|
||||
}">
|
||||
<ETH :size="1.5*props.size" />
|
||||
</div>
|
||||
|
||||
<!-- DDR -->
|
||||
<div class="component-wrapper ddr-wrapper" :style="{
|
||||
position: 'absolute',
|
||||
top: `${224 * props.size}px`,
|
||||
right: `${250 * props.size}px`,
|
||||
zIndex: 10
|
||||
}">
|
||||
<DDR :size="1.2*props.size" />
|
||||
</div>
|
||||
|
||||
<!-- SD -->
|
||||
<div class="component-wrapper sd-wrapper" :style="{
|
||||
position: 'absolute',
|
||||
bottom: `${130 * props.size}px`,
|
||||
right: `${172 * props.size}px`,
|
||||
zIndex: 10
|
||||
}">
|
||||
<SD :size="1.2*props.size" />
|
||||
</div>
|
||||
|
||||
<!-- SFP -->
|
||||
<div class="component-wrapper sfp-wrapper" :style="{
|
||||
position: 'absolute',
|
||||
bottom: `${210 * props.size}px`,
|
||||
right: `${-46 * props.size}px`,
|
||||
zIndex: 10
|
||||
}">
|
||||
<SFP :size="1.84*props.size" />
|
||||
</div>
|
||||
<!-- SFP -->
|
||||
<div class="component-wrapper sfp-wrapper" :style="{
|
||||
position: 'absolute',
|
||||
bottom: `${290 * props.size}px`,
|
||||
right: `${-46 * props.size}px`,
|
||||
zIndex: 10
|
||||
}">
|
||||
<SFP :size="1.84*props.size" />
|
||||
</div>
|
||||
<!-- SMA -->
|
||||
<div class="component-wrapper sma-wrapper" :style="{
|
||||
position: 'absolute',
|
||||
top: `${110 * props.size}px`,
|
||||
right: `${204 * props.size}px`,
|
||||
zIndex: 10
|
||||
}">
|
||||
<SMA :size="0.75*props.size" />
|
||||
</div>
|
||||
<!-- SMA -->
|
||||
<div class="component-wrapper sma-wrapper" :style="{
|
||||
position: 'absolute',
|
||||
top: `${170 * props.size}px`,
|
||||
right: `${204 * props.size}px`,
|
||||
zIndex: 10
|
||||
}">
|
||||
<SMA :size="0.75*props.size" />
|
||||
</div>
|
||||
<!-- SMA -->
|
||||
<div class="component-wrapper sma-wrapper" :style="{
|
||||
position: 'absolute',
|
||||
top: `${250 * props.size}px`,
|
||||
right: `${204 * props.size}px`,
|
||||
zIndex: 10
|
||||
}">
|
||||
<SMA :size="0.75*props.size" />
|
||||
</div>
|
||||
<!-- SMA -->
|
||||
<div class="component-wrapper sma-wrapper" :style="{
|
||||
position: 'absolute',
|
||||
top: `${310 * props.size}px`,
|
||||
right: `${204 * props.size}px`,
|
||||
zIndex: 10
|
||||
}">
|
||||
<SMA :size="0.75*props.size" />
|
||||
</div>
|
||||
<!-- BUTTON -->
|
||||
<div class="component-wrapper button-wrapper" :style="{
|
||||
position: 'absolute',
|
||||
bottom: `${140 * props.size}px`,
|
||||
right: `${430 * props.size}px`,
|
||||
zIndex: 10
|
||||
}">
|
||||
<MechanicalButton :size="0.175*props.size" />
|
||||
</div>
|
||||
<div class="component-wrapper button-wrapper" :style="{
|
||||
position: 'absolute',
|
||||
bottom: `${140 * props.size}px`,
|
||||
right: `${397 * props.size}px`,
|
||||
zIndex: 10
|
||||
}">
|
||||
<MechanicalButton :size="0.175*props.size" />
|
||||
</div>
|
||||
<div class="component-wrapper button-wrapper" :style="{
|
||||
position: 'absolute',
|
||||
bottom: `${140 * props.size}px`,
|
||||
right: `${364 * props.size}px`,
|
||||
zIndex: 10
|
||||
}">
|
||||
<MechanicalButton :size="0.175*props.size" />
|
||||
</div>
|
||||
<div class="component-wrapper button-wrapper" :style="{
|
||||
position: 'absolute',
|
||||
bottom: `${140 * props.size}px`,
|
||||
right: `${331 * props.size}px`,
|
||||
zIndex: 10
|
||||
}">
|
||||
<MechanicalButton :size="0.175*props.size" />
|
||||
</div>
|
||||
<div class="component-wrapper button-wrapper" :style="{
|
||||
position: 'absolute',
|
||||
bottom: `${140 * props.size}px`,
|
||||
right: `${298 * props.size}px`,
|
||||
zIndex: 10
|
||||
}">
|
||||
<MechanicalButton :size="0.175*props.size" />
|
||||
</div>
|
||||
<div class="component-wrapper button-wrapper" :style="{
|
||||
position: 'absolute',
|
||||
bottom: `${140 * props.size}px`,
|
||||
right: `${265 * props.size}px`,
|
||||
zIndex: 10
|
||||
}">
|
||||
<MechanicalButton :size="0.175*props.size" />
|
||||
</div>
|
||||
:viewBox="`0 0 800 600`"
|
||||
class="motherboard-svg"
|
||||
>
|
||||
<image
|
||||
href="../equipments/svg/motherboard.svg"
|
||||
width="100%"
|
||||
height="100%"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import HDMI from './HDMI.vue';
|
||||
import DDR from './DDR.vue';
|
||||
import ETH from './ETH.vue';
|
||||
import SD from './SD.vue';
|
||||
import SFP from './SFP.vue';
|
||||
import SMA from './SMA.vue';
|
||||
import MechanicalButton from './MechanicalButton.vue';
|
||||
|
||||
interface Props {
|
||||
// 主板特有属性
|
||||
interface MotherBoardProps {
|
||||
size?: number;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
const props = withDefaults(defineProps<MotherBoardProps>(), {
|
||||
size: 1
|
||||
});
|
||||
|
||||
|
@ -186,11 +36,23 @@ const height = computed(() => 600 * props.size);
|
|||
// 向外暴露方法
|
||||
defineExpose({
|
||||
getInfo: () => ({
|
||||
size: props.size
|
||||
})
|
||||
size: props.size,
|
||||
type: 'motherboard'
|
||||
}),
|
||||
// 主板没有引脚,但为了接口一致性,提供一个空的getPinPosition方法
|
||||
getPinPosition: () => null
|
||||
});
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
// 添加一个静态方法来获取默认props
|
||||
export function getDefaultProps() {
|
||||
return {
|
||||
size: 1
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.motherboard-container {
|
||||
display: block;
|
||||
|
@ -201,20 +63,9 @@ defineExpose({
|
|||
-ms-user-select: none; /* IE/Edge */
|
||||
}
|
||||
|
||||
.svg-image {
|
||||
.motherboard-svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
pointer-events: none; /* 禁止鼠标交互 */
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
}
|
||||
|
||||
.component-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
<template>
|
||||
<div class="chip-container" :style="{ width: width + 'px', height: height + 'px', position: 'relative' }"> <svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
:width="width"
|
||||
:height="height"
|
||||
viewBox="0 0 400 400"
|
||||
class="fbg676-chip"
|
||||
> <!-- 芯片外边框 - 用多段path代替rect,这样可以实现缺角 -->
|
||||
<path
|
||||
d="M30,0 H390 Q400,0 400,10 V390 Q400,400 390,400 H10 Q0,400 0,390 V30 L30,0 Z"
|
||||
fill="#1C4E2D"
|
||||
/>
|
||||
|
||||
<!-- 芯片内层 - 增大尺寸 -->
|
||||
<rect width="280" height="280" x="60" y="60" fill="#0F1211" rx="2" ry="2" />
|
||||
</svg>
|
||||
<!-- 渲染芯片引脚 --> <div v-for="pin in computedPins" :key="pin.pinId"
|
||||
:style="{
|
||||
position: 'absolute',
|
||||
left: `${(pin.x || 0) * props.size * 0.37}px`,
|
||||
top: `${(pin.y || 0) * props.size * 0.37}px`,
|
||||
transform: 'translate(-50%, -50%)'
|
||||
}"
|
||||
:data-pin-wrapper="`${pin.pinId}`"
|
||||
:data-pin-x="`${(pin.x || 0) * props.size * 0.37}`"
|
||||
:data-pin-y="`${(pin.y || 0) * props.size * 0.37}`"> <Pin
|
||||
:ref="el => { if(el) pinRefs[pin.pinId] = el }"
|
||||
:label="pin.pinId"
|
||||
:constraint="pin.constraint"
|
||||
:pinId="pin.pinId"
|
||||
:size="0.35"
|
||||
@pin-click="$emit('pin-click', $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import Pin from './Pin.vue';
|
||||
|
||||
// 存储多个Pin引用
|
||||
const pinRefs = ref<Record<string, any>>({});
|
||||
|
||||
// 芯片特有属性
|
||||
interface ChipProps {
|
||||
size?: number;
|
||||
pins?: {
|
||||
pinId: string;
|
||||
constraint: string;
|
||||
x?: number; // x坐标现在是可选的
|
||||
y?: number; // y坐标现在是可选的
|
||||
}[];
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<ChipProps>(), {
|
||||
size: 1,
|
||||
pins: () => []
|
||||
});
|
||||
|
||||
// 计算尺寸
|
||||
const width = computed(() => 400 * props.size * 0.37);
|
||||
const height = computed(() => 400 * props.size * 0.37);
|
||||
|
||||
// 生成676个引脚(26x26)
|
||||
const computedPins = computed(() => {
|
||||
// 设置BGA矩阵布局参数
|
||||
const pinRows = 26;
|
||||
const pinColumns = 26;
|
||||
const margin = 25; // 四周边距
|
||||
const xStart = margin;
|
||||
const yStart = margin;
|
||||
const xEnd = 400 - margin;
|
||||
const yEnd = 400 - margin;
|
||||
const xStep = (xEnd - xStart) / (pinColumns - 1);
|
||||
const yStep = (yEnd - yStart) / (pinRows - 1);
|
||||
|
||||
// 如果提供了pins,则合并引脚信息和位置信息
|
||||
if (props.pins && props.pins.length > 0) {
|
||||
// 复制一份提供的pins
|
||||
const mergedPins = [...props.pins];
|
||||
|
||||
// 对于不包含xy坐标的引脚,生成默认的xy坐标
|
||||
for (let i = 0; i < mergedPins.length; i++) {
|
||||
const pin = mergedPins[i];
|
||||
// 如果没有提供xy坐标,根据引脚索引计算位置
|
||||
if (pin.x === undefined || pin.y === undefined) {
|
||||
const row = Math.floor(i / pinColumns);
|
||||
const col = i % pinColumns;
|
||||
|
||||
pin.x = xStart + col * xStep;
|
||||
pin.y = yStart + row * yStep;
|
||||
}
|
||||
}
|
||||
|
||||
return mergedPins;
|
||||
}
|
||||
|
||||
// 否则生成默认的676个引脚,按BGA格式矩阵排列
|
||||
const pins = [];
|
||||
let pinIndex = 0;
|
||||
// 生成BGA封装的矩阵引脚
|
||||
for (let row = 0; row < pinRows; row++) {
|
||||
for (let col = 0; col < pinColumns; col++) {
|
||||
pins.push({
|
||||
pinId: `pin_${pinIndex++}`,
|
||||
constraint: '',
|
||||
x: xStart + col * xStep,
|
||||
y: yStart + row * yStep
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return pins;
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
getInfo: () => ({
|
||||
chipType: 'PG2L100H_FBG676',
|
||||
direction: 'inout',
|
||||
type: 'digital',
|
||||
pins: computedPins.value
|
||||
}),
|
||||
getPinPosition: (pinId: string) => {
|
||||
// 返回指定引脚ID的位置
|
||||
if (computedPins.value && computedPins.value.length > 0) {
|
||||
const customPin = computedPins.value.find(p => p.pinId === pinId);
|
||||
|
||||
if (customPin && customPin.x !== undefined && customPin.y !== undefined) {
|
||||
// 考虑组件尺寸的缩放,应用0.37的系数确保居中对齐
|
||||
const scaledX = customPin.x * props.size * 0.37;
|
||||
const scaledY = customPin.y * props.size * 0.37;
|
||||
|
||||
return {
|
||||
x: scaledX,
|
||||
y: scaledY
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
// 添加一个静态方法来获取默认props
|
||||
export function getDefaultProps() {
|
||||
return {
|
||||
size: 1,
|
||||
pins: [] // 默认不提供引脚,由computedPins计算生成
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chip-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
}
|
||||
|
||||
.fbg676-chip {
|
||||
display: block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
line-height: 0;
|
||||
font-size: 0;
|
||||
box-sizing: content-box;
|
||||
overflow: visible;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
}
|
||||
</style>
|
|
@ -4,17 +4,28 @@
|
|||
:height="height"
|
||||
:viewBox="'0 0 ' + viewBoxWidth + ' ' + viewBoxHeight"
|
||||
class="pin-component"
|
||||
>
|
||||
<g :transform="`translate(${viewBoxWidth/2}, ${viewBoxHeight/2})`">
|
||||
> <g :transform="`translate(${viewBoxWidth/2}, ${viewBoxHeight/2})`">
|
||||
<g>
|
||||
<g transform="translate(-12.5, -12.5)"> <circle
|
||||
<g transform="translate(-12.5, -12.5)"> <!-- 添加一个透明的更大区域来增强点击能力,但比原来小一点 -->
|
||||
<circle
|
||||
cx="12.5"
|
||||
cy="12.5"
|
||||
r="5"
|
||||
fill="transparent"
|
||||
class="interactive"
|
||||
@click.stop="handlePinClick"
|
||||
@mousedown.stop
|
||||
@touchstart.stop
|
||||
:data-pin-element="`${props.pinId}`"
|
||||
:data-component-id="props.componentId" />
|
||||
<!-- 实际可见的引脚圆点 -->
|
||||
<circle
|
||||
:style="{ fill: pinColor }"
|
||||
cx="12.5"
|
||||
cy="12.5"
|
||||
r="3.75"
|
||||
class="interactive"
|
||||
@click.stop="handlePinClick"
|
||||
:data-pin-element="`${props.pinId}`" />
|
||||
pointer-events="none"
|
||||
:data-pin-visual="`${props.pinId}`" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
|
@ -32,6 +43,7 @@ interface Props {
|
|||
direction?: 'input' | 'output' | 'inout';
|
||||
type?: 'digital' | 'analog';
|
||||
pinId?: string; // 添加引脚ID属性,用于唯一标识
|
||||
componentId?: string; // 添加组件ID属性,关联到父组件
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
|
@ -41,6 +53,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|||
direction: 'input',
|
||||
type: 'digital',
|
||||
pinId: 'pin-default', // 默认ID
|
||||
componentId: '' // 默认空字符串
|
||||
});
|
||||
|
||||
const emit = defineEmits([
|
||||
|
@ -70,10 +83,10 @@ function handlePinClick(event: MouseEvent) {
|
|||
});
|
||||
}
|
||||
|
||||
const width = computed(() => 40 * props.size);
|
||||
const height = computed(() => 20 * props.size);
|
||||
const viewBoxWidth = computed(() => 40);
|
||||
const viewBoxHeight = computed(() => 20);
|
||||
const width = computed(() => 15 * props.size);
|
||||
const height = computed(() => 15 * props.size);
|
||||
const viewBoxWidth = computed(() => 15);
|
||||
const viewBoxHeight = computed(() => 15);
|
||||
|
||||
const getColorByType = computed(() => {
|
||||
return props.type === 'analog' ? '#2a6099' : '#444';
|
||||
|
@ -130,7 +143,8 @@ function updateAnalogValue(value: number) {
|
|||
|
||||
defineExpose({
|
||||
setAnalogValue: updateAnalogValue,
|
||||
getAnalogValue: () => analogValue.value, getInfo: () => ({
|
||||
getAnalogValue: () => analogValue.value,
|
||||
getInfo: () => ({
|
||||
label: props.label,
|
||||
constraint: props.constraint,
|
||||
direction: props.direction,
|
||||
|
@ -138,16 +152,12 @@ defineExpose({
|
|||
pinId: props.pinId
|
||||
}),
|
||||
getPinPosition: () => {
|
||||
// 获取当前Pin元素的引脚圆点位置
|
||||
const circle = document.querySelector(`circle[data-pin-element="${props.pinId}"]`);
|
||||
if (circle) {
|
||||
const rect = circle.getBoundingClientRect();
|
||||
return {
|
||||
x: rect.left + rect.width / 2,
|
||||
y: rect.top + rect.height / 2
|
||||
};
|
||||
}
|
||||
return null;
|
||||
// Pin组件自身的getPinPosition应该返回相对于父组件的位置
|
||||
// 在MechanicalButton等组件中已经明确传递了position,所以这里实际上可能不会被使用
|
||||
return {
|
||||
x: viewBoxWidth.value / 2,
|
||||
y: viewBoxHeight.value / 2
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -180,12 +190,18 @@ export function getDefaultProps() {
|
|||
display: block;
|
||||
user-select: none;
|
||||
position: relative;
|
||||
z-index: 5; /* 提高引脚组件的z-index */
|
||||
pointer-events: auto; /* 确保可以接收点击事件 */
|
||||
overflow: visible; /* 确保可以看到引脚 */
|
||||
}
|
||||
.interactive {
|
||||
cursor: pointer;
|
||||
transition: filter 0.2s;
|
||||
pointer-events: auto; /* 确保可以接收点击事件 */
|
||||
}
|
||||
.interactive:hover {
|
||||
filter: brightness(1.2);
|
||||
stroke: rgba(255, 255, 255, 0.3); /* 添加边框以便更容易看到点击区域 */
|
||||
stroke-width: 1;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -44,10 +44,14 @@
|
|||
<div v-for="pin in props.pins" :key="pin.pinId"
|
||||
:style="{
|
||||
position: 'absolute',
|
||||
left: `${pin.x}px`,
|
||||
top: `${pin.y}px`,
|
||||
left: `${pin.x * props.size}px`,
|
||||
top: `${pin.y * props.size}px`,
|
||||
transform: 'translate(-50%, -50%)'
|
||||
}"> <Pin
|
||||
}"
|
||||
:data-pin-wrapper="`${pin.pinId}`"
|
||||
:data-pin-x="`${pin.x * props.size}`"
|
||||
:data-pin-y="`${pin.y * props.size}`">
|
||||
<Pin
|
||||
:ref="el => { if(el) pinRefs[pin.pinId] = el }"
|
||||
:label="pin.pinId"
|
||||
:constraint="pin.constraint"
|
||||
|
@ -70,7 +74,6 @@ const pinRefs = ref<Record<string, any>>({});
|
|||
interface LEDProps {
|
||||
size?: number;
|
||||
color?: string;
|
||||
initialOn?: boolean;
|
||||
brightness?: number;
|
||||
pins?: {
|
||||
pinId: string;
|
||||
|
@ -83,7 +86,6 @@ interface LEDProps {
|
|||
const props = withDefaults(defineProps<LEDProps>(), {
|
||||
size: 1,
|
||||
color: 'red',
|
||||
initialOn: false,
|
||||
brightness: 80,
|
||||
pins: () => [
|
||||
{
|
||||
|
@ -169,21 +171,31 @@ defineExpose({
|
|||
direction: 'input',
|
||||
type: 'digital',
|
||||
pins: props.pins
|
||||
}), getPinPosition: (pinId: string) => {
|
||||
}),
|
||||
getPinPosition: (pinId: string) => {
|
||||
// 如果是自定义的引脚ID
|
||||
if (props.pins && props.pins.length > 0) {
|
||||
console.log('Pin ID:', pinId);
|
||||
console.log('SMT_LED查找Pin ID:', pinId);
|
||||
console.log('SMT_LED组件尺寸:', props.size, '宽高:', width.value, 'x', height.value);
|
||||
const customPin = props.pins.find(p => p.pinId === pinId);
|
||||
console.log('Custom Pin:', customPin);
|
||||
console.log('Pin Refs:', pinRefs.value[pinId]);
|
||||
console.log('找到的引脚配置:', customPin);
|
||||
|
||||
if (customPin) {
|
||||
// 调用对应Pin组件的getPinPosition方法
|
||||
// 考虑组件尺寸的缩放
|
||||
const scaledX = customPin.x * props.size;
|
||||
const scaledY = customPin.y * props.size;
|
||||
|
||||
console.log('使用Pin缩放后的坐标:', scaledX, scaledY);
|
||||
return {
|
||||
x: customPin.x,
|
||||
y: customPin.y
|
||||
}
|
||||
} return null;
|
||||
} return null;
|
||||
x: scaledX,
|
||||
y: scaledY
|
||||
};
|
||||
}
|
||||
console.log('未找到匹配的引脚');
|
||||
return null;
|
||||
}
|
||||
console.log('没有引脚配置');
|
||||
return null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -194,7 +206,6 @@ export function getDefaultProps() {
|
|||
return {
|
||||
size: 1,
|
||||
color: 'red',
|
||||
initialOn: false,
|
||||
brightness: 80,
|
||||
pins: [
|
||||
{
|
||||
|
|
|
@ -0,0 +1,256 @@
|
|||
<template>
|
||||
<div class="seven-segment-display" :style="{ width: width + 'px', height: height + 'px', position: 'relative' }"> <svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
:width="width"
|
||||
:height="height"
|
||||
viewBox="0 0 120 220"
|
||||
class="display"
|
||||
>
|
||||
<!-- 数码管基座 -->
|
||||
<rect width="120" height="180" x="0" y="0" fill="#222" rx="10" ry="10" />
|
||||
<rect width="110" height="170" x="5" y="5" fill="#333" rx="5" ry="5" />
|
||||
<!-- 7段 + 小数点,每个段由多边形表示,重新设计点位置使其更接近实际数码管 -->
|
||||
<!-- a段 (顶部横线) -->
|
||||
<polygon
|
||||
:points="'30,20 90,20 98,28 82,36 38,36 22,28'"
|
||||
:fill="isSegmentActive('a') ? segmentColor : inactiveColor"
|
||||
:style="{ opacity: isSegmentActive('a') ? 1 : 0.15 }"
|
||||
class="segment"
|
||||
/>
|
||||
|
||||
<!-- b段 (右上竖线) -->
|
||||
<polygon
|
||||
:points="'100,30 108,38 108,82 100,90 92,82 92,38'"
|
||||
:fill="isSegmentActive('b') ? segmentColor : inactiveColor"
|
||||
:style="{ opacity: isSegmentActive('b') ? 1 : 0.15 }"
|
||||
class="segment"
|
||||
/>
|
||||
|
||||
<!-- c段 (右下竖线) -->
|
||||
<polygon
|
||||
:points="'100,90 108,98 108,142 100,150 92,142 92,98'"
|
||||
:fill="isSegmentActive('c') ? segmentColor : inactiveColor"
|
||||
:style="{ opacity: isSegmentActive('c') ? 1 : 0.15 }"
|
||||
class="segment"
|
||||
/>
|
||||
|
||||
<!-- d段 (底部横线) -->
|
||||
<polygon
|
||||
:points="'30,160 90,160 98,152 82,144 38,144 22,152'"
|
||||
:fill="isSegmentActive('d') ? segmentColor : inactiveColor"
|
||||
:style="{ opacity: isSegmentActive('d') ? 1 : 0.15 }"
|
||||
class="segment"
|
||||
/>
|
||||
|
||||
<!-- e段 (左下竖线) -->
|
||||
<polygon
|
||||
:points="'20,90 28,98 28,142 20,150 12,142 12,98'"
|
||||
:fill="isSegmentActive('e') ? segmentColor : inactiveColor"
|
||||
:style="{ opacity: isSegmentActive('e') ? 1 : 0.15 }"
|
||||
class="segment"
|
||||
/>
|
||||
|
||||
<!-- f段 (左上竖线) -->
|
||||
<polygon
|
||||
:points="'20,30 28,38 28,82 20,90 12,82 12,38'"
|
||||
:fill="isSegmentActive('f') ? segmentColor : inactiveColor"
|
||||
:style="{ opacity: isSegmentActive('f') ? 1 : 0.15 }"
|
||||
class="segment"
|
||||
/>
|
||||
|
||||
<!-- g段 (中间横线) -->
|
||||
<polygon
|
||||
:points="'30,90 38,82 82,82 90,90 82,98 38,98'"
|
||||
:fill="isSegmentActive('g') ? segmentColor : inactiveColor"
|
||||
:style="{ opacity: isSegmentActive('g') ? 1 : 0.15 }"
|
||||
class="segment"
|
||||
/> <!-- dp段 (小数点) -->
|
||||
<circle
|
||||
cx="108"
|
||||
cy="154"
|
||||
r="6"
|
||||
:fill="isSegmentActive('dp') ? segmentColor : inactiveColor"
|
||||
:style="{ opacity: isSegmentActive('dp') ? 1 : 0.15 }"
|
||||
class="segment"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
<!-- 引脚 -->
|
||||
<div v-for="pin in pins" :key="pin.pinId"
|
||||
:style="{
|
||||
position: 'absolute',
|
||||
left: `${pin.x * props.size}px`,
|
||||
top: `${pin.y * props.size}px`,
|
||||
transform: 'translate(-50%, -50%)'
|
||||
}"
|
||||
:data-pin-wrapper="`${pin.pinId}`"
|
||||
:data-pin-x="`${pin.x * props.size}`"
|
||||
:data-pin-y="`${pin.y * props.size}`">
|
||||
<Pin
|
||||
:ref="el => { if(el) pinRefs[pin.pinId] = el }"
|
||||
:label="pin.pinId"
|
||||
:constraint="pin.constraint"
|
||||
:pinId="pin.pinId"
|
||||
@pin-click="$emit('pin-click', $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, onMounted, onUnmounted } from 'vue';
|
||||
import { getConstraintState, onConstraintStateChange } from '../../stores/constraints';
|
||||
import Pin from './Pin.vue';
|
||||
|
||||
// 存储Pin引用
|
||||
const pinRefs = ref<Record<string, any>>({});
|
||||
|
||||
// 数码管属性
|
||||
interface SevenSegmentDisplayProps {
|
||||
size?: number;
|
||||
color?: string;
|
||||
pins?: {
|
||||
pinId: string;
|
||||
constraint: string;
|
||||
x: number;
|
||||
y: number;
|
||||
}[];
|
||||
cathodeType?: 'common' | 'anode'; // 共阴极或共阳极
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<SevenSegmentDisplayProps>(), {
|
||||
size: 1,
|
||||
color: 'red',
|
||||
cathodeType: 'common', // 默认为共阴极
|
||||
pins: () => [
|
||||
{ pinId: 'a', constraint: '', x: 10 , y: 170 }, // a段
|
||||
{ pinId: 'b', constraint: '', x: 25-1 , y: 170 }, // b段
|
||||
{ pinId: 'c', constraint: '', x: 40-2 , y: 170 }, // c段
|
||||
{ pinId: 'd', constraint: '', x: 55-3 , y: 170 }, // d段
|
||||
{ pinId: 'e', constraint: '', x: 70-4 , y: 170 }, // e段
|
||||
{ pinId: 'f', constraint: '', x: 85-5 , y: 170 }, // f段
|
||||
{ pinId: 'g', constraint: '', x: 100-6, y: 170 }, // g段
|
||||
{ pinId: 'dp', constraint: '', x: 115-7, y: 170 }, // 小数点
|
||||
{ pinId: 'COM', constraint: '', x: 60 , y: 10 } // 公共端,稍微低一点
|
||||
]
|
||||
});
|
||||
|
||||
const width = computed(() => 120 * props.size);
|
||||
const height = computed(() => 220 * props.size);
|
||||
|
||||
// 计算段颜色和非激活状态颜色
|
||||
const segmentColor = computed(() => props.color || 'red');
|
||||
const inactiveColor = computed(() => '#FFFFFF');
|
||||
|
||||
// 监听props变化
|
||||
watch(
|
||||
() => props,
|
||||
(newProps) => {
|
||||
console.log('SevenSegmentDisplay props changed:', newProps);
|
||||
updateSegmentStates();
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 段引脚状态
|
||||
const segmentStates = ref({
|
||||
a: false,
|
||||
b: false,
|
||||
c: false,
|
||||
d: false,
|
||||
e: false,
|
||||
f: false,
|
||||
g: false,
|
||||
dp: false,
|
||||
});
|
||||
|
||||
// 判断段是否激活
|
||||
function isSegmentActive(segment: 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'dp'): boolean {
|
||||
return segmentStates.value[segment];
|
||||
}
|
||||
|
||||
// 更新引脚状态的函数
|
||||
function updateSegmentStates() {
|
||||
for (const pin of props.pins) {
|
||||
if (['a', 'b', 'c', 'd', 'e', 'f', 'g', 'dp'].includes(pin.pinId)) {
|
||||
// 如果constraint为空,则默认为未激活状态
|
||||
if (!pin.constraint) {
|
||||
segmentStates.value[pin.pinId as keyof typeof segmentStates.value] = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
const pinState = getConstraintState(pin.constraint);
|
||||
|
||||
// 根据阴极/阳极类型反转逻辑
|
||||
if (props.cathodeType === 'common') {
|
||||
// 共阴极: 高电平激活段
|
||||
segmentStates.value[pin.pinId as keyof typeof segmentStates.value] = pinState === 'high';
|
||||
} else {
|
||||
// 共阳极: 低电平激活段
|
||||
segmentStates.value[pin.pinId as keyof typeof segmentStates.value] = pinState === 'low';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 监听约束状态变化
|
||||
function onConstraintChange(constraint: string, level: string) {
|
||||
const affectedPin = props.pins.find(pin => pin.constraint === constraint);
|
||||
if (affectedPin && ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'dp'].includes(affectedPin.pinId)) {
|
||||
updateSegmentStates();
|
||||
}
|
||||
}
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
updateSegmentStates();
|
||||
onConstraintStateChange(onConstraintChange);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
// 清理约束状态监听
|
||||
});
|
||||
|
||||
// 暴露属性和方法
|
||||
defineExpose({
|
||||
updateSegmentStates
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.seven-segment-display {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.segment {
|
||||
transition: opacity 0.2s, fill 0.2s;
|
||||
}
|
||||
|
||||
/* 数码管发光效果 */
|
||||
.segment[style*="opacity: 1"] {
|
||||
filter: drop-shadow(0 0 4px v-bind(segmentColor)) drop-shadow(0 0 2px v-bind(segmentColor));
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- 导出默认属性函数供外部使用 -->
|
||||
<script lang="ts">
|
||||
export function getDefaultProps() {
|
||||
return {
|
||||
size: 1,
|
||||
color: 'red',
|
||||
cathodeType: 'common',
|
||||
pins: [
|
||||
{ pinId: 'a', constraint: '', x: 10 , y: 170 },
|
||||
{ pinId: 'b', constraint: '', x: 25-1 , y: 170 },
|
||||
{ pinId: 'c', constraint: '', x: 40-2 , y: 170 },
|
||||
{ pinId: 'd', constraint: '', x: 55-3 , y: 170 },
|
||||
{ pinId: 'e', constraint: '', x: 70-4 , y: 170 },
|
||||
{ pinId: 'f', constraint: '', x: 85-5 , y: 170 },
|
||||
{ pinId: 'g', constraint: '', x: 100-6, y: 170 },
|
||||
{ pinId: 'dp', constraint: '', x: 115-7, y: 170 },
|
||||
{ pinId: 'COM', constraint: '', x: 60 , y: 10 }
|
||||
]
|
||||
};
|
||||
}
|
||||
</script>
|
|
@ -113,7 +113,7 @@ function calculatePathFromCommands(
|
|||
|
||||
// 分割命令为起点和终点两部分
|
||||
const startCommands = commands.slice(0, splitterIndex);
|
||||
const endCommands = commands.slice(splitterIndex + 1).reverse();
|
||||
const endCommands = commands.slice(splitterIndex + 1);
|
||||
|
||||
// 从起点开始生成路径
|
||||
let currentX = startX;
|
||||
|
@ -129,50 +129,62 @@ function calculatePathFromCommands(
|
|||
currentY = newY;
|
||||
pathPoints.push([currentX, currentY]);
|
||||
}
|
||||
|
||||
// 从终点开始反向处理
|
||||
// 从终点开始反向处理
|
||||
let endCurrentX = endX;
|
||||
let endCurrentY = endY;
|
||||
|
||||
// 保存终点路径点,最后会反转
|
||||
const endPathPoints: [number, number][] = [[endCurrentX, endCurrentY]];
|
||||
|
||||
// 解析并执行终点命令(反向)
|
||||
for (const cmd of endCommands) {
|
||||
// 解析并执行终点命令(需要从后向前执行反向命令)
|
||||
for (let i = endCommands.length - 1; i >= 0; i--) {
|
||||
const cmd = reversePathCommand(endCommands[i]);
|
||||
const { newX, newY } = executePathCommand(endCurrentX, endCurrentY, cmd);
|
||||
endCurrentX = newX;
|
||||
endCurrentY = newY;
|
||||
endPathPoints.push([endCurrentX, endCurrentY]);
|
||||
}
|
||||
|
||||
// 反转终点路径点并去掉第一个(终点)
|
||||
const reversedEndPoints = endPathPoints.slice(1).reverse();
|
||||
} // 反转终点路径点,保留所有点
|
||||
const reversedEndPoints = [...endPathPoints].reverse();
|
||||
|
||||
// 将两部分路径连接起来
|
||||
const allPoints = [...pathPoints, ...reversedEndPoints];
|
||||
|
||||
// 如果起点和终点路径没有连接上,添加连接线段
|
||||
if (allPoints.length >= 2) {
|
||||
const startFinalPoint = allPoints[pathPoints.length - 1];
|
||||
const endFirstPoint = allPoints[pathPoints.length];
|
||||
if (startFinalPoint && endFirstPoint &&
|
||||
(startFinalPoint[0] !== endFirstPoint[0] || startFinalPoint[1] !== endFirstPoint[1])) {
|
||||
// 添加连接点 - 这里使用正交连接
|
||||
const middlePoints = generateOrthogonalConnection(
|
||||
startFinalPoint[0], startFinalPoint[1],
|
||||
endFirstPoint[0], endFirstPoint[1]
|
||||
);
|
||||
|
||||
// 将起点路径、连接路径和终点路径拼接起来
|
||||
allPoints.splice(pathPoints.length, 0, ...middlePoints);
|
||||
}
|
||||
// 如果终点路径的第一个点与起点路径的最后一个点相同,则去掉重复点
|
||||
let combinedPoints;
|
||||
if (pathPoints.length > 0 && reversedEndPoints.length > 0 &&
|
||||
pathPoints[pathPoints.length - 1][0] === reversedEndPoints[0][0] &&
|
||||
pathPoints[pathPoints.length - 1][1] === reversedEndPoints[0][1]) {
|
||||
combinedPoints = [...pathPoints, ...reversedEndPoints.slice(1)];
|
||||
} else {
|
||||
combinedPoints = [...pathPoints, ...reversedEndPoints];
|
||||
}
|
||||
|
||||
// 生成SVG路径
|
||||
const allPoints = combinedPoints;
|
||||
// 检查是否需要添加中间连接点
|
||||
if (allPoints.length >= 2) {
|
||||
const startPathEndPoint = pathPoints.length > 0 ? pathPoints[pathPoints.length - 1] : null;
|
||||
const endPathStartPoint = reversedEndPoints.length > 0 ? reversedEndPoints[0] : null;
|
||||
|
||||
// 只有当起点路径和终点路径不相连时才添加连接线
|
||||
if (startPathEndPoint && endPathStartPoint &&
|
||||
(startPathEndPoint[0] !== endPathStartPoint[0] || startPathEndPoint[1] !== endPathStartPoint[1])) {
|
||||
// 使用正交连接或直接连接
|
||||
let middlePoints: [number, number][] = [];
|
||||
|
||||
// 正交连接,添加额外点以确保路径是正交的
|
||||
middlePoints = generateOrthogonalConnection(
|
||||
startPathEndPoint[0], startPathEndPoint[1],
|
||||
endPathStartPoint[0], endPathStartPoint[1]
|
||||
);
|
||||
|
||||
// 在起点路径和终点路径之间插入中间点
|
||||
allPoints.splice(pathPoints.length, 0, ...middlePoints);
|
||||
}
|
||||
} // 生成SVG路径
|
||||
if (allPoints.length < 2) {
|
||||
// 如果没有足够的点,直接从起点到终点画一条线
|
||||
return `M ${startX} ${startY} L ${endX} ${endY}`;
|
||||
}
|
||||
|
||||
// 使用所有点生成路径字符串
|
||||
let pathStr = `M ${allPoints[0][0]} ${allPoints[0][1]}`;
|
||||
for (let i = 1; i < allPoints.length; i++) {
|
||||
pathStr += ` L ${allPoints[i][0]} ${allPoints[i][1]}`;
|
||||
|
@ -184,38 +196,48 @@ function calculatePathFromCommands(
|
|||
// 执行单个路径命令
|
||||
function executePathCommand(x: number, y: number, command: string): { newX: number; newY: number } {
|
||||
// 解析命令,例如 "down10", "right20", "downright5" 等
|
||||
let newX = x;
|
||||
let newY = y;
|
||||
|
||||
if (command.startsWith('right')) {
|
||||
const distance = parseInt(command.substring(5), 10) || 10;
|
||||
return { newX: x + distance, newY: y };
|
||||
newX = x + distance;
|
||||
newY = y;
|
||||
} else if (command.startsWith('left')) {
|
||||
const distance = parseInt(command.substring(4), 10) || 10;
|
||||
return { newX: x - distance, newY: y };
|
||||
newX = x - distance;
|
||||
newY = y;
|
||||
} else if (command.startsWith('down')) {
|
||||
if (command.startsWith('downright')) {
|
||||
const distance = parseInt(command.substring(9), 10) || 10;
|
||||
return { newX: x + distance, newY: y + distance };
|
||||
newX = x + distance;
|
||||
newY = y + distance;
|
||||
} else if (command.startsWith('downleft')) {
|
||||
const distance = parseInt(command.substring(8), 10) || 10;
|
||||
return { newX: x - distance, newY: y + distance };
|
||||
newX = x - distance;
|
||||
newY = y + distance;
|
||||
} else {
|
||||
const distance = parseInt(command.substring(4), 10) || 10;
|
||||
return { newX: x, newY: y + distance };
|
||||
newX = x;
|
||||
newY = y + distance;
|
||||
}
|
||||
} else if (command.startsWith('up')) {
|
||||
if (command.startsWith('upright')) {
|
||||
const distance = parseInt(command.substring(7), 10) || 10;
|
||||
return { newX: x + distance, newY: y - distance };
|
||||
newX = x + distance;
|
||||
newY = y - distance;
|
||||
} else if (command.startsWith('upleft')) {
|
||||
const distance = parseInt(command.substring(6), 10) || 10;
|
||||
return { newX: x - distance, newY: y - distance };
|
||||
newX = x - distance;
|
||||
newY = y - distance;
|
||||
} else {
|
||||
const distance = parseInt(command.substring(2), 10) || 10;
|
||||
return { newX: x, newY: y - distance };
|
||||
newX = x;
|
||||
newY = y - distance;
|
||||
}
|
||||
}
|
||||
|
||||
// 默认情况下不移动
|
||||
return { newX: x, newY: y };
|
||||
return { newX, newY };
|
||||
}
|
||||
|
||||
// 生成两点之间的正交连接点
|
||||
|
@ -268,6 +290,48 @@ function calculateOrthogonalPath(startX: number, startY: number, endX: number, e
|
|||
}
|
||||
}
|
||||
|
||||
// 添加反转命令函数 - 将方向命令反转
|
||||
function reversePathCommand(command: string): string {
|
||||
// 提取距离部分
|
||||
const distanceMatch = command.match(/\d+$/);
|
||||
const distance = distanceMatch ? distanceMatch[0] : "10"; // 默认距离是10
|
||||
|
||||
// 根据命令类型返回反向命令
|
||||
// 水平方向反转
|
||||
if (command.startsWith('right')) {
|
||||
return `left${distance}`;
|
||||
}
|
||||
else if (command.startsWith('left')) {
|
||||
return `right${distance}`;
|
||||
}
|
||||
// 垂直和斜向反转
|
||||
else if (command.startsWith('down')) {
|
||||
if (command.startsWith('downright')) {
|
||||
return `upleft${distance}`;
|
||||
}
|
||||
else if (command.startsWith('downleft')) {
|
||||
return `upright${distance}`;
|
||||
}
|
||||
else {
|
||||
return `up${distance}`;
|
||||
}
|
||||
}
|
||||
else if (command.startsWith('up')) {
|
||||
if (command.startsWith('upright')) {
|
||||
return `downleft${distance}`;
|
||||
}
|
||||
else if (command.startsWith('upleft')) {
|
||||
return `downright${distance}`;
|
||||
}
|
||||
else {
|
||||
return `down${distance}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 默认情况下,无法反转就返回原命令
|
||||
return command;
|
||||
}
|
||||
|
||||
// 监听约束状态变化
|
||||
let unsubscribe: (() => void) | null = null;
|
||||
|
||||
|
|
|
@ -0,0 +1,186 @@
|
|||
// componentConfig.ts 提供通用的组件配置功能
|
||||
import type { DiagramPart } from '../diagramManager';
|
||||
|
||||
// 属性配置接口
|
||||
export interface PropertyConfig {
|
||||
name: string;
|
||||
type: string;
|
||||
label: string;
|
||||
default: any;
|
||||
options?: Array<{ value: any; label: string }>; // 添加 options 字段用于 select 类型
|
||||
isDirectProp?: boolean; // 标记是否为直接属性
|
||||
isReadOnly?: boolean; // 标记是否为只读属性
|
||||
min?: number; // 用于数值类型的最小值
|
||||
max?: number; // 用于数值类型的最大值
|
||||
step?: number; // 用于数值类型的步长
|
||||
isArrayType?: boolean; // 标记数组类型属性
|
||||
}
|
||||
|
||||
// 所有基础属性的标签映射
|
||||
const basePropertyLabels: Record<number, string> = {
|
||||
0: 'ID',
|
||||
1: '组件类型',
|
||||
2: '水平坐标',
|
||||
3: '垂直坐标',
|
||||
4: '角度',
|
||||
5: '分组',
|
||||
6: '锁定位置',
|
||||
7: '隐藏引脚',
|
||||
8: '激活状态',
|
||||
9: '层级'
|
||||
};
|
||||
|
||||
// 只读属性索引列表
|
||||
const readOnlyPropertyIndexes = [0, 1]; // 移除了isOn对应的索引8
|
||||
|
||||
/**
|
||||
* 从组件数据中自动生成属性配置
|
||||
* @param componentData 组件数据
|
||||
* @returns 属性配置数组
|
||||
*/
|
||||
export function generatePropertyConfigs(componentData: DiagramPart): PropertyConfig[] {
|
||||
const configs: PropertyConfig[] = [];
|
||||
|
||||
// 获取基础属性(排除attrs)
|
||||
const directPropKeys = Object.keys(componentData).filter(key => key !== 'attrs');
|
||||
|
||||
// 为每个直接属性创建配置
|
||||
directPropKeys.forEach((propName, index) => {
|
||||
let propValue: any = (componentData as any)[propName];
|
||||
let propType = typeof propValue;
|
||||
|
||||
// 对于undefined的属性,提供默认值
|
||||
if (propValue === undefined) {
|
||||
if (propType === 'boolean') propValue = false;
|
||||
else if (propType === 'number') propValue = 0;
|
||||
else propValue = '';
|
||||
}
|
||||
|
||||
// 创建配置对象
|
||||
const propConfig: PropertyConfig = {
|
||||
name: propName,
|
||||
label: basePropertyLabels[index] || propName,
|
||||
type: propType as 'string' | 'number' | 'boolean' | 'select',
|
||||
default: propValue,
|
||||
isDirectProp: true,
|
||||
isReadOnly: readOnlyPropertyIndexes.includes(index)
|
||||
};
|
||||
|
||||
// 数值类型的特殊设置
|
||||
if (propType === 'number') {
|
||||
if (index === 9) { // 层级
|
||||
propConfig.min = 0;
|
||||
propConfig.max = 100;
|
||||
} else if (index === 4) { // 角度
|
||||
propConfig.min = 0;
|
||||
propConfig.max = 360;
|
||||
}
|
||||
propConfig.step = 1;
|
||||
}
|
||||
|
||||
configs.push(propConfig);
|
||||
});
|
||||
|
||||
return configs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从组件模块的getDefaultProps方法生成属性配置
|
||||
* @param defaultProps 默认属性对象
|
||||
* @returns 属性配置数组
|
||||
*/
|
||||
export function generatePropsFromDefault(defaultProps: Record<string, any>): PropertyConfig[] {
|
||||
const configs: PropertyConfig[] = [];
|
||||
|
||||
for (const [propName, propValue] of Object.entries(defaultProps)) {
|
||||
// 特殊处理pins属性
|
||||
if (propName === 'pins') {
|
||||
const propConfig: PropertyConfig = {
|
||||
name: propName,
|
||||
label: 'Pins',
|
||||
default: propValue,
|
||||
type: 'array',
|
||||
isArrayType: true
|
||||
};
|
||||
configs.push(propConfig);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 根据属性类型创建配置
|
||||
let propType = typeof propValue;
|
||||
let propConfig: PropertyConfig = {
|
||||
name: propName,
|
||||
label: propName.charAt(0).toUpperCase() + propName.slice(1), // 首字母大写作为标签
|
||||
default: propValue,
|
||||
type: propType as 'string' | 'number' | 'boolean' | 'select'
|
||||
};
|
||||
|
||||
// 根据值类型设置表单控件类型
|
||||
if (propType === 'string') {
|
||||
propConfig.type = 'string';
|
||||
} else if (propType === 'number') {
|
||||
propConfig.type = 'number';
|
||||
propConfig.min = 0;
|
||||
propConfig.max = 100;
|
||||
propConfig.step = 0.1;
|
||||
} else if (propType === 'boolean') {
|
||||
propConfig.type = 'boolean';
|
||||
} else if (propType === 'object' && propValue !== null && propValue.hasOwnProperty('options')) {
|
||||
// 如果是含有options的对象,认为它是select类型
|
||||
propConfig.type = 'select';
|
||||
propConfig.options = (propValue as any).options;
|
||||
}
|
||||
|
||||
configs.push(propConfig);
|
||||
}
|
||||
|
||||
return configs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从属性对象生成配置
|
||||
* @param attrs 属性对象
|
||||
* @returns 属性配置数组
|
||||
*/
|
||||
export function generatePropsFromAttrs(attrs: Record<string, any>): PropertyConfig[] {
|
||||
const configs: PropertyConfig[] = [];
|
||||
|
||||
for (const [propName, propValue] of Object.entries(attrs)) {
|
||||
// 特殊处理pins属性
|
||||
if (propName === 'pins') {
|
||||
const propConfig: PropertyConfig = {
|
||||
name: propName,
|
||||
label: 'Pins',
|
||||
default: propValue,
|
||||
type: 'array',
|
||||
isArrayType: true
|
||||
};
|
||||
configs.push(propConfig);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 根据属性值类型创建配置
|
||||
let propType = typeof propValue;
|
||||
let propConfig: PropertyConfig = {
|
||||
name: propName,
|
||||
label: propName.charAt(0).toUpperCase() + propName.slice(1), // 首字母大写作为标签
|
||||
default: propValue || '',
|
||||
type: propType as 'string' | 'number' | 'boolean' | 'select'
|
||||
};
|
||||
|
||||
configs.push(propConfig);
|
||||
}
|
||||
|
||||
return configs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全地从组件对象获取属性值
|
||||
* @param component 组件对象
|
||||
* @param propName 属性名
|
||||
* @returns 属性值
|
||||
*/
|
||||
export function getPropValue(component: DiagramPart, propName: string): any {
|
||||
if (!component) return undefined;
|
||||
return (component as any)[propName];
|
||||
}
|
|
@ -7,20 +7,60 @@
|
|||
viewBox="0 0 189.71826 110.06672"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.4 (86a8ad7, 2024-10-11)"
|
||||
sodipodi:docname="pin.svg"
|
||||
xml:space="preserve"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:zoom="0.70710678"
|
||||
inkscape:cx="769.33218"
|
||||
inkscape:cy="744.58344"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g4028-2"
|
||||
inkscape:export-bgcolor="#ffffff00"
|
||||
showguides="false"><inkscape:page
|
||||
x="0"
|
||||
y="-1.2139753e-13"
|
||||
width="189.71826"
|
||||
height="110.06673"
|
||||
id="page2"
|
||||
margin="0"
|
||||
bleed="0" /></sodipodi:namedview><defs
|
||||
id="defs1" /><g
|
||||
inkscape:label="图层 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-14.20163,-182.98715)"><g
|
||||
id="g4491-5"
|
||||
transform="translate(-2.671235,131.40328)"><g
|
||||
id="g4489-4"
|
||||
transform="translate(0.71912207,-18.325919)"><g
|
||||
transform="translate(0.71912207,-18.325919)"
|
||||
inkscape:export-filename="motherboard.svg"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"><g
|
||||
id="g4028-2"><path
|
||||
style="fill:#222222;fill-opacity:1;fill-rule:nonzero;stroke-width:0.264999;stroke-dasharray:none"
|
||||
d="M 16.153743,72.454427 V 173.22414 c 0.209579,1.02406 0.641226,1.91539 2.158085,2.15808 h 13.173985 v -7.90761 h 18.279908 v 7.90115 h 7.88052 v -6.44562 c 0,-2.21895 3.644172,-2.24344 3.644172,0 v 11.02362 h 11.023621 v -7.47055 c 0,-1.06741 1.935964,-1.06294 1.935964,0 v 7.49332 H 95.197651 V 167.4585 H 203.13414 c 1.48786,0.0739 2.45207,-0.52544 2.73787,-1.99703 V 71.729696 c -0.0202,-1.117201 -0.51125,-1.824404 -1.81988,-1.819878 H 18.666141 c -1.750373,0.260441 -2.319686,1.28129 -2.512398,2.544609 z"
|
||||
id="path4026-5" /><rect
|
||||
id="path4026-5"
|
||||
sodipodi:nodetypes="ccccccccccccccccccccccc"
|
||||
inkscape:export-filename="path4026-5.svg"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96" /><rect
|
||||
style="fill:#353535;fill-opacity:1;fill-rule:nonzero;stroke:#aa8800;stroke-width:0.46;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
|
||||
id="rect4026-3"
|
||||
width="78.259003"
|
||||
|
@ -37,22 +77,11 @@
|
|||
y="74.100662"
|
||||
id="text4027-6"
|
||||
transform="scale(0.93589337,1.0684978)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4027-8"
|
||||
x="34.243221"
|
||||
y="74.100662"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.45312px;font-family:'Microsoft Sans Serif';-inkscape-font-specification:'Microsoft Sans Serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.20475">MES2L676-BASE-V1.2</tspan></text><rect
|
||||
style="fill:#002b11;fill-opacity:0.772973;fill-rule:nonzero;stroke:none;stroke-width:0.459999;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
|
||||
id="rect4027-2"
|
||||
width="30"
|
||||
height="30"
|
||||
x="84.577484"
|
||||
y="103.45676" /><rect
|
||||
style="fill:#1a1a1a;fill-opacity:0.772973;fill-rule:nonzero;stroke:none;stroke-width:0.459999;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
|
||||
id="rect4028-6"
|
||||
width="25"
|
||||
height="25"
|
||||
x="87.246216"
|
||||
y="106.04675" /></g><g
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.45312px;font-family:'Microsoft Sans Serif';-inkscape-font-specification:'Microsoft Sans Serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;stroke-width:0.20475">MES2L676-BASE-V1.2</tspan></text></g><g
|
||||
id="g4488-6"><rect
|
||||
style="font-variation-settings:'wght' 700;opacity:1;fill:none;fill-opacity:0.968317;fill-rule:nonzero;stroke:#898989;stroke-width:0.347;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
|
||||
id="rect4479-3"
|
||||
|
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
@ -0,0 +1,837 @@
|
|||
{
|
||||
"version": 1,
|
||||
"author": "template",
|
||||
"editor": "system",
|
||||
"parts": [
|
||||
{ "id": "board","type": "MotherBoard","x": 0,"y": 0,
|
||||
"attrs": {
|
||||
"size": 1
|
||||
},
|
||||
"rotate": 0,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": false,"isOn": true,"index": 0
|
||||
},
|
||||
{
|
||||
"id": "FPGA_chip", "type": "PG2L100H_FBG676","x": 250,"y": 200,
|
||||
"attrs": {
|
||||
"size": 1,
|
||||
"pins": [
|
||||
{"pinId": "pin_0","constraint": ""},
|
||||
{"pinId": "pin_1","constraint": ""},
|
||||
{"pinId": "pin_2","constraint": ""},
|
||||
{"pinId": "pin_3","constraint": ""},
|
||||
{"pinId": "pin_4","constraint": ""},
|
||||
{"pinId": "pin_5","constraint": ""},
|
||||
{"pinId": "pin_6","constraint": ""},
|
||||
{"pinId": "pin_7","constraint": ""},
|
||||
{"pinId": "pin_8","constraint": ""},
|
||||
{"pinId": "pin_9","constraint": ""},
|
||||
{"pinId": "pin_10","constraint": ""},
|
||||
{"pinId": "pin_11","constraint": ""},
|
||||
{"pinId": "pin_12","constraint": ""},
|
||||
{"pinId": "pin_13","constraint": ""},
|
||||
{"pinId": "pin_14","constraint": ""},
|
||||
{"pinId": "pin_15","constraint": ""},
|
||||
{"pinId": "pin_16","constraint": ""},
|
||||
{"pinId": "pin_17","constraint": ""},
|
||||
{"pinId": "pin_18","constraint": ""},
|
||||
{"pinId": "pin_19","constraint": ""},
|
||||
{"pinId": "pin_20","constraint": ""},
|
||||
{"pinId": "pin_21","constraint": ""},
|
||||
{"pinId": "pin_22","constraint": ""},
|
||||
{"pinId": "pin_23","constraint": ""},
|
||||
{"pinId": "pin_24","constraint": ""},
|
||||
{"pinId": "pin_25","constraint": ""},
|
||||
{"pinId": "pin_26","constraint": ""},
|
||||
{"pinId": "pin_27","constraint": ""},
|
||||
{"pinId": "pin_28","constraint": ""},
|
||||
{"pinId": "pin_29","constraint": ""},
|
||||
{"pinId": "pin_30","constraint": ""},
|
||||
{"pinId": "pin_31","constraint": ""},
|
||||
{"pinId": "pin_32","constraint": ""},
|
||||
{"pinId": "pin_33","constraint": ""},
|
||||
{"pinId": "pin_34","constraint": ""},
|
||||
{"pinId": "pin_35","constraint": ""},
|
||||
{"pinId": "pin_36","constraint": ""},
|
||||
{"pinId": "pin_37","constraint": ""},
|
||||
{"pinId": "pin_38","constraint": ""},
|
||||
{"pinId": "pin_39","constraint": ""},
|
||||
{"pinId": "pin_40","constraint": ""},
|
||||
{"pinId": "pin_41","constraint": ""},
|
||||
{"pinId": "pin_42","constraint": ""},
|
||||
{"pinId": "pin_43","constraint": ""},
|
||||
{"pinId": "pin_44","constraint": ""},
|
||||
{"pinId": "pin_45","constraint": ""},
|
||||
{"pinId": "pin_46","constraint": ""},
|
||||
{"pinId": "pin_47","constraint": ""},
|
||||
{"pinId": "pin_48","constraint": ""},
|
||||
{"pinId": "pin_49","constraint": ""},
|
||||
{"pinId": "pin_50","constraint": ""},
|
||||
{"pinId": "pin_51","constraint": ""},
|
||||
{"pinId": "pin_52","constraint": ""},
|
||||
{"pinId": "pin_53","constraint": ""},
|
||||
{"pinId": "pin_54","constraint": ""},
|
||||
{"pinId": "pin_55","constraint": ""},
|
||||
{"pinId": "pin_56","constraint": ""},
|
||||
{"pinId": "pin_57","constraint": ""},
|
||||
{"pinId": "pin_58","constraint": ""},
|
||||
{"pinId": "pin_59","constraint": ""},
|
||||
{"pinId": "pin_60","constraint": ""},
|
||||
{"pinId": "pin_61","constraint": ""},
|
||||
{"pinId": "pin_62","constraint": ""},
|
||||
{"pinId": "pin_63","constraint": ""},
|
||||
{"pinId": "pin_64","constraint": ""},
|
||||
{"pinId": "pin_65","constraint": ""},
|
||||
{"pinId": "pin_66","constraint": ""},
|
||||
{"pinId": "pin_67","constraint": ""},
|
||||
{"pinId": "pin_68","constraint": ""},
|
||||
{"pinId": "pin_69","constraint": ""},
|
||||
{"pinId": "pin_70","constraint": ""},
|
||||
{"pinId": "pin_71","constraint": ""},
|
||||
{"pinId": "pin_72","constraint": ""},
|
||||
{"pinId": "pin_73","constraint": ""},
|
||||
{"pinId": "pin_74","constraint": ""},
|
||||
{"pinId": "pin_75","constraint": ""},
|
||||
{"pinId": "pin_76","constraint": ""},
|
||||
{"pinId": "pin_77","constraint": ""},
|
||||
{"pinId": "pin_78","constraint": ""},
|
||||
{"pinId": "pin_79","constraint": ""},
|
||||
{"pinId": "pin_80","constraint": ""},
|
||||
{"pinId": "pin_81","constraint": ""},
|
||||
{"pinId": "pin_82","constraint": ""},
|
||||
{"pinId": "pin_83","constraint": ""},
|
||||
{"pinId": "pin_84","constraint": ""},
|
||||
{"pinId": "pin_85","constraint": ""},
|
||||
{"pinId": "pin_86","constraint": ""},
|
||||
{"pinId": "pin_87","constraint": ""},
|
||||
{"pinId": "pin_88","constraint": ""},
|
||||
{"pinId": "pin_89","constraint": ""},
|
||||
{"pinId": "pin_90","constraint": ""},
|
||||
{"pinId": "pin_91","constraint": ""},
|
||||
{"pinId": "pin_92","constraint": ""},
|
||||
{"pinId": "pin_93","constraint": ""},
|
||||
{"pinId": "pin_94","constraint": ""},
|
||||
{"pinId": "pin_95","constraint": ""},
|
||||
{"pinId": "pin_96","constraint": ""},
|
||||
{"pinId": "pin_97","constraint": ""},
|
||||
{"pinId": "pin_98","constraint": ""},
|
||||
{"pinId": "pin_99","constraint": ""},
|
||||
{"pinId": "pin_100","constraint": ""},
|
||||
{"pinId": "pin_101","constraint": ""},
|
||||
{"pinId": "pin_102","constraint": ""},
|
||||
{"pinId": "pin_103","constraint": ""},
|
||||
{"pinId": "pin_104","constraint": ""},
|
||||
{"pinId": "pin_105","constraint": ""},
|
||||
{"pinId": "pin_106","constraint": ""},
|
||||
{"pinId": "pin_107","constraint": ""},
|
||||
{"pinId": "pin_108","constraint": ""},
|
||||
{"pinId": "pin_109","constraint": ""},
|
||||
{"pinId": "pin_110","constraint": ""},
|
||||
{"pinId": "pin_111","constraint": ""},
|
||||
{"pinId": "pin_112","constraint": ""},
|
||||
{"pinId": "pin_113","constraint": ""},
|
||||
{"pinId": "pin_114","constraint": ""},
|
||||
{"pinId": "pin_115","constraint": ""},
|
||||
{"pinId": "pin_116","constraint": ""},
|
||||
{"pinId": "pin_117","constraint": ""},
|
||||
{"pinId": "pin_118","constraint": ""},
|
||||
{"pinId": "pin_119","constraint": ""},
|
||||
{"pinId": "pin_120","constraint": ""},
|
||||
{"pinId": "pin_121","constraint": ""},
|
||||
{"pinId": "pin_122","constraint": ""},
|
||||
{"pinId": "pin_123","constraint": ""},
|
||||
{"pinId": "pin_124","constraint": ""},
|
||||
{"pinId": "pin_125","constraint": ""},
|
||||
{"pinId": "pin_126","constraint": ""},
|
||||
{"pinId": "pin_127","constraint": ""},
|
||||
{"pinId": "pin_128","constraint": ""},
|
||||
{"pinId": "pin_129","constraint": ""},
|
||||
{"pinId": "pin_130","constraint": ""},
|
||||
{"pinId": "pin_131","constraint": ""},
|
||||
{"pinId": "pin_132","constraint": ""},
|
||||
{"pinId": "pin_133","constraint": ""},
|
||||
{"pinId": "pin_134","constraint": ""},
|
||||
{"pinId": "pin_135","constraint": ""},
|
||||
{"pinId": "pin_136","constraint": ""},
|
||||
{"pinId": "pin_137","constraint": ""},
|
||||
{"pinId": "pin_138","constraint": ""},
|
||||
{"pinId": "pin_139","constraint": ""},
|
||||
{"pinId": "pin_140","constraint": ""},
|
||||
{"pinId": "pin_141","constraint": ""},
|
||||
{"pinId": "pin_142","constraint": ""},
|
||||
{"pinId": "pin_143","constraint": ""},
|
||||
{"pinId": "pin_144","constraint": ""},
|
||||
{"pinId": "pin_145","constraint": ""},
|
||||
{"pinId": "pin_146","constraint": ""},
|
||||
{"pinId": "pin_147","constraint": ""},
|
||||
{"pinId": "pin_148","constraint": ""},
|
||||
{"pinId": "pin_149","constraint": ""},
|
||||
{"pinId": "pin_150","constraint": ""},
|
||||
{"pinId": "pin_151","constraint": ""},
|
||||
{"pinId": "pin_152","constraint": ""},
|
||||
{"pinId": "pin_153","constraint": ""},
|
||||
{"pinId": "pin_154","constraint": ""},
|
||||
{"pinId": "pin_155","constraint": ""},
|
||||
{"pinId": "pin_156","constraint": ""},
|
||||
{"pinId": "pin_157","constraint": ""},
|
||||
{"pinId": "pin_158","constraint": ""},
|
||||
{"pinId": "pin_159","constraint": ""},
|
||||
{"pinId": "pin_160","constraint": ""},
|
||||
{"pinId": "pin_161","constraint": ""},
|
||||
{"pinId": "pin_162","constraint": ""},
|
||||
{"pinId": "pin_163","constraint": ""},
|
||||
{"pinId": "pin_164","constraint": ""},
|
||||
{"pinId": "pin_165","constraint": ""},
|
||||
{"pinId": "pin_166","constraint": ""},
|
||||
{"pinId": "pin_167","constraint": ""},
|
||||
{"pinId": "pin_168","constraint": ""},
|
||||
{"pinId": "pin_169","constraint": ""},
|
||||
{"pinId": "pin_170","constraint": ""},
|
||||
{"pinId": "pin_171","constraint": ""},
|
||||
{"pinId": "pin_172","constraint": ""},
|
||||
{"pinId": "pin_173","constraint": ""},
|
||||
{"pinId": "pin_174","constraint": ""},
|
||||
{"pinId": "pin_175","constraint": ""},
|
||||
{"pinId": "pin_176","constraint": ""},
|
||||
{"pinId": "pin_177","constraint": ""},
|
||||
{"pinId": "pin_178","constraint": ""},
|
||||
{"pinId": "pin_179","constraint": ""},
|
||||
{"pinId": "pin_180","constraint": ""},
|
||||
{"pinId": "pin_181","constraint": ""},
|
||||
{"pinId": "pin_182","constraint": ""},
|
||||
{"pinId": "pin_183","constraint": ""},
|
||||
{"pinId": "pin_184","constraint": ""},
|
||||
{"pinId": "pin_185","constraint": ""},
|
||||
{"pinId": "pin_186","constraint": ""},
|
||||
{"pinId": "pin_187","constraint": ""},
|
||||
{"pinId": "pin_188","constraint": ""},
|
||||
{"pinId": "pin_189","constraint": ""},
|
||||
{"pinId": "pin_190","constraint": ""},
|
||||
{"pinId": "pin_191","constraint": ""},
|
||||
{"pinId": "pin_192","constraint": ""},
|
||||
{"pinId": "pin_193","constraint": ""},
|
||||
{"pinId": "pin_194","constraint": ""},
|
||||
{"pinId": "pin_195","constraint": ""},
|
||||
{"pinId": "pin_196","constraint": ""},
|
||||
{"pinId": "pin_197","constraint": ""},
|
||||
{"pinId": "pin_198","constraint": ""},
|
||||
{"pinId": "pin_199","constraint": ""},
|
||||
{"pinId": "pin_200","constraint": ""},
|
||||
{"pinId": "pin_201","constraint": ""},
|
||||
{"pinId": "pin_202","constraint": ""},
|
||||
{"pinId": "pin_203","constraint": ""},
|
||||
{"pinId": "pin_204","constraint": ""},
|
||||
{"pinId": "pin_205","constraint": ""},
|
||||
{"pinId": "pin_206","constraint": ""},
|
||||
{"pinId": "pin_207","constraint": ""},
|
||||
{"pinId": "pin_208","constraint": ""},
|
||||
{"pinId": "pin_209","constraint": ""},
|
||||
{"pinId": "pin_210","constraint": ""},
|
||||
{"pinId": "pin_211","constraint": ""},
|
||||
{"pinId": "pin_212","constraint": ""},
|
||||
{"pinId": "pin_213","constraint": ""},
|
||||
{"pinId": "pin_214","constraint": ""},
|
||||
{"pinId": "pin_215","constraint": ""},
|
||||
{"pinId": "pin_216","constraint": ""},
|
||||
{"pinId": "pin_217","constraint": ""},
|
||||
{"pinId": "pin_218","constraint": ""},
|
||||
{"pinId": "pin_219","constraint": ""},
|
||||
{"pinId": "pin_220","constraint": ""},
|
||||
{"pinId": "pin_221","constraint": ""},
|
||||
{"pinId": "pin_222","constraint": ""},
|
||||
{"pinId": "pin_223","constraint": ""},
|
||||
{"pinId": "pin_224","constraint": ""},
|
||||
{"pinId": "pin_225","constraint": ""},
|
||||
{"pinId": "pin_226","constraint": ""},
|
||||
{"pinId": "pin_227","constraint": ""},
|
||||
{"pinId": "pin_228","constraint": ""},
|
||||
{"pinId": "pin_229","constraint": ""},
|
||||
{"pinId": "pin_230","constraint": ""},
|
||||
{"pinId": "pin_231","constraint": ""},
|
||||
{"pinId": "pin_232","constraint": ""},
|
||||
{"pinId": "pin_233","constraint": ""},
|
||||
{"pinId": "pin_234","constraint": ""},
|
||||
{"pinId": "pin_235","constraint": ""},
|
||||
{"pinId": "pin_236","constraint": ""},
|
||||
{"pinId": "pin_237","constraint": ""},
|
||||
{"pinId": "pin_238","constraint": ""},
|
||||
{"pinId": "pin_239","constraint": ""},
|
||||
{"pinId": "pin_240","constraint": ""},
|
||||
{"pinId": "pin_241","constraint": ""},
|
||||
{"pinId": "pin_242","constraint": ""},
|
||||
{"pinId": "pin_243","constraint": ""},
|
||||
{"pinId": "pin_244","constraint": ""},
|
||||
{"pinId": "pin_245","constraint": ""},
|
||||
{"pinId": "pin_246","constraint": ""},
|
||||
{"pinId": "pin_247","constraint": ""},
|
||||
{"pinId": "pin_248","constraint": ""},
|
||||
{"pinId": "pin_249","constraint": ""},
|
||||
{"pinId": "pin_250","constraint": ""},
|
||||
{"pinId": "pin_251","constraint": ""},
|
||||
{"pinId": "pin_252","constraint": ""},
|
||||
{"pinId": "pin_253","constraint": ""},
|
||||
{"pinId": "pin_254","constraint": ""},
|
||||
{"pinId": "pin_255","constraint": ""},
|
||||
{"pinId": "pin_256","constraint": ""},
|
||||
{"pinId": "pin_257","constraint": ""},
|
||||
{"pinId": "pin_258","constraint": ""},
|
||||
{"pinId": "pin_259","constraint": ""},
|
||||
{"pinId": "pin_260","constraint": ""},
|
||||
{"pinId": "pin_261","constraint": ""},
|
||||
{"pinId": "pin_262","constraint": ""},
|
||||
{"pinId": "pin_263","constraint": ""},
|
||||
{"pinId": "pin_264","constraint": ""},
|
||||
{"pinId": "pin_265","constraint": ""},
|
||||
{"pinId": "pin_266","constraint": ""},
|
||||
{"pinId": "pin_267","constraint": ""},
|
||||
{"pinId": "pin_268","constraint": ""},
|
||||
{"pinId": "pin_269","constraint": ""},
|
||||
{"pinId": "pin_270","constraint": ""},
|
||||
{"pinId": "pin_271","constraint": ""},
|
||||
{"pinId": "pin_272","constraint": ""},
|
||||
{"pinId": "pin_273","constraint": ""},
|
||||
{"pinId": "pin_274","constraint": ""},
|
||||
{"pinId": "pin_275","constraint": ""},
|
||||
{"pinId": "pin_276","constraint": ""},
|
||||
{"pinId": "pin_277","constraint": ""},
|
||||
{"pinId": "pin_278","constraint": ""},
|
||||
{"pinId": "pin_279","constraint": ""},
|
||||
{"pinId": "pin_280","constraint": ""},
|
||||
{"pinId": "pin_281","constraint": ""},
|
||||
{"pinId": "pin_282","constraint": ""},
|
||||
{"pinId": "pin_283","constraint": ""},
|
||||
{"pinId": "pin_284","constraint": ""},
|
||||
{"pinId": "pin_285","constraint": ""},
|
||||
{"pinId": "pin_286","constraint": ""},
|
||||
{"pinId": "pin_287","constraint": ""},
|
||||
{"pinId": "pin_288","constraint": ""},
|
||||
{"pinId": "pin_289","constraint": ""},
|
||||
{"pinId": "pin_290","constraint": ""},
|
||||
{"pinId": "pin_291","constraint": ""},
|
||||
{"pinId": "pin_292","constraint": ""},
|
||||
{"pinId": "pin_293","constraint": ""},
|
||||
{"pinId": "pin_294","constraint": ""},
|
||||
{"pinId": "pin_295","constraint": ""},
|
||||
{"pinId": "pin_296","constraint": ""},
|
||||
{"pinId": "pin_297","constraint": ""},
|
||||
{"pinId": "pin_298","constraint": ""},
|
||||
{"pinId": "pin_299","constraint": ""},
|
||||
{"pinId": "pin_300","constraint": ""},
|
||||
{"pinId": "pin_301","constraint": ""},
|
||||
{"pinId": "pin_302","constraint": ""},
|
||||
{"pinId": "pin_303","constraint": ""},
|
||||
{"pinId": "pin_304","constraint": ""},
|
||||
{"pinId": "pin_305","constraint": ""},
|
||||
{"pinId": "pin_306","constraint": ""},
|
||||
{"pinId": "pin_307","constraint": ""},
|
||||
{"pinId": "pin_308","constraint": ""},
|
||||
{"pinId": "pin_309","constraint": ""},
|
||||
{"pinId": "pin_310","constraint": ""},
|
||||
{"pinId": "pin_311","constraint": ""},
|
||||
{"pinId": "pin_312","constraint": ""},
|
||||
{"pinId": "pin_313","constraint": ""},
|
||||
{"pinId": "pin_314","constraint": ""},
|
||||
{"pinId": "pin_315","constraint": ""},
|
||||
{"pinId": "pin_316","constraint": ""},
|
||||
{"pinId": "pin_317","constraint": ""},
|
||||
{"pinId": "pin_318","constraint": ""},
|
||||
{"pinId": "pin_319","constraint": ""},
|
||||
{"pinId": "pin_320","constraint": ""},
|
||||
{"pinId": "pin_321","constraint": ""},
|
||||
{"pinId": "pin_322","constraint": ""},
|
||||
{"pinId": "pin_323","constraint": ""},
|
||||
{"pinId": "pin_324","constraint": ""},
|
||||
{"pinId": "pin_325","constraint": ""},
|
||||
{"pinId": "pin_326","constraint": ""},
|
||||
{"pinId": "pin_327","constraint": ""},
|
||||
{"pinId": "pin_328","constraint": ""},
|
||||
{"pinId": "pin_329","constraint": ""},
|
||||
{"pinId": "pin_330","constraint": ""},
|
||||
{"pinId": "pin_331","constraint": ""},
|
||||
{"pinId": "pin_332","constraint": ""},
|
||||
{"pinId": "pin_333","constraint": ""},
|
||||
{"pinId": "pin_334","constraint": ""},
|
||||
{"pinId": "pin_335","constraint": ""},
|
||||
{"pinId": "pin_336","constraint": ""},
|
||||
{"pinId": "pin_337","constraint": ""},
|
||||
{"pinId": "pin_338","constraint": ""},
|
||||
{"pinId": "pin_339","constraint": ""},
|
||||
{"pinId": "pin_340","constraint": ""},
|
||||
{"pinId": "pin_341","constraint": ""},
|
||||
{"pinId": "pin_342","constraint": ""},
|
||||
{"pinId": "pin_343","constraint": ""},
|
||||
{"pinId": "pin_344","constraint": ""},
|
||||
{"pinId": "pin_345","constraint": ""},
|
||||
{"pinId": "pin_346","constraint": ""},
|
||||
{"pinId": "pin_347","constraint": ""},
|
||||
{"pinId": "pin_348","constraint": ""},
|
||||
{"pinId": "pin_349","constraint": ""},
|
||||
{"pinId": "pin_350","constraint": ""},
|
||||
{"pinId": "pin_351","constraint": ""},
|
||||
{"pinId": "pin_352","constraint": ""},
|
||||
{"pinId": "pin_353","constraint": ""},
|
||||
{"pinId": "pin_354","constraint": ""},
|
||||
{"pinId": "pin_355","constraint": ""},
|
||||
{"pinId": "pin_356","constraint": ""},
|
||||
{"pinId": "pin_357","constraint": ""},
|
||||
{"pinId": "pin_358","constraint": ""},
|
||||
{"pinId": "pin_359","constraint": ""},
|
||||
{"pinId": "pin_360","constraint": ""},
|
||||
{"pinId": "pin_361","constraint": ""},
|
||||
{"pinId": "pin_362","constraint": ""},
|
||||
{"pinId": "pin_363","constraint": ""},
|
||||
{"pinId": "pin_364","constraint": ""},
|
||||
{"pinId": "pin_365","constraint": ""},
|
||||
{"pinId": "pin_366","constraint": ""},
|
||||
{"pinId": "pin_367","constraint": ""},
|
||||
{"pinId": "pin_368","constraint": ""},
|
||||
{"pinId": "pin_369","constraint": ""},
|
||||
{"pinId": "pin_370","constraint": ""},
|
||||
{"pinId": "pin_371","constraint": ""},
|
||||
{"pinId": "pin_372","constraint": ""},
|
||||
{"pinId": "pin_373","constraint": ""},
|
||||
{"pinId": "pin_374","constraint": ""},
|
||||
{"pinId": "pin_375","constraint": ""},
|
||||
{"pinId": "pin_376","constraint": ""},
|
||||
{"pinId": "pin_377","constraint": ""},
|
||||
{"pinId": "pin_378","constraint": ""},
|
||||
{"pinId": "pin_379","constraint": ""},
|
||||
{"pinId": "pin_380","constraint": ""},
|
||||
{"pinId": "pin_381","constraint": ""},
|
||||
{"pinId": "pin_382","constraint": ""},
|
||||
{"pinId": "pin_383","constraint": ""},
|
||||
{"pinId": "pin_384","constraint": ""},
|
||||
{"pinId": "pin_385","constraint": ""},
|
||||
{"pinId": "pin_386","constraint": ""},
|
||||
{"pinId": "pin_387","constraint": ""},
|
||||
{"pinId": "pin_388","constraint": ""},
|
||||
{"pinId": "pin_389","constraint": ""},
|
||||
{"pinId": "pin_390","constraint": ""},
|
||||
{"pinId": "pin_391","constraint": ""},
|
||||
{"pinId": "pin_392","constraint": ""},
|
||||
{"pinId": "pin_393","constraint": ""},
|
||||
{"pinId": "pin_394","constraint": ""},
|
||||
{"pinId": "pin_395","constraint": ""},
|
||||
{"pinId": "pin_396","constraint": ""},
|
||||
{"pinId": "pin_397","constraint": ""},
|
||||
{"pinId": "pin_398","constraint": ""},
|
||||
{"pinId": "pin_399","constraint": ""},
|
||||
{"pinId": "pin_400","constraint": ""},
|
||||
{"pinId": "pin_401","constraint": ""},
|
||||
{"pinId": "pin_402","constraint": ""},
|
||||
{"pinId": "pin_403","constraint": ""},
|
||||
{"pinId": "pin_404","constraint": ""},
|
||||
{"pinId": "pin_405","constraint": ""},
|
||||
{"pinId": "pin_406","constraint": ""},
|
||||
{"pinId": "pin_407","constraint": ""},
|
||||
{"pinId": "pin_408","constraint": ""},
|
||||
{"pinId": "pin_409","constraint": ""},
|
||||
{"pinId": "pin_410","constraint": ""},
|
||||
{"pinId": "pin_411","constraint": ""},
|
||||
{"pinId": "pin_412","constraint": ""},
|
||||
{"pinId": "pin_413","constraint": ""},
|
||||
{"pinId": "pin_414","constraint": ""},
|
||||
{"pinId": "pin_415","constraint": ""},
|
||||
{"pinId": "pin_416","constraint": ""},
|
||||
{"pinId": "pin_417","constraint": ""},
|
||||
{"pinId": "pin_418","constraint": ""},
|
||||
{"pinId": "pin_419","constraint": ""},
|
||||
{"pinId": "pin_420","constraint": ""},
|
||||
{"pinId": "pin_421","constraint": ""},
|
||||
{"pinId": "pin_422","constraint": ""},
|
||||
{"pinId": "pin_423","constraint": ""},
|
||||
{"pinId": "pin_424","constraint": ""},
|
||||
{"pinId": "pin_425","constraint": ""},
|
||||
{"pinId": "pin_426","constraint": ""},
|
||||
{"pinId": "pin_427","constraint": ""},
|
||||
{"pinId": "pin_428","constraint": ""},
|
||||
{"pinId": "pin_429","constraint": ""},
|
||||
{"pinId": "pin_430","constraint": ""},
|
||||
{"pinId": "pin_431","constraint": ""},
|
||||
{"pinId": "pin_432","constraint": ""},
|
||||
{"pinId": "pin_433","constraint": ""},
|
||||
{"pinId": "pin_434","constraint": ""},
|
||||
{"pinId": "pin_435","constraint": ""},
|
||||
{"pinId": "pin_436","constraint": ""},
|
||||
{"pinId": "pin_437","constraint": ""},
|
||||
{"pinId": "pin_438","constraint": ""},
|
||||
{"pinId": "pin_439","constraint": ""},
|
||||
{"pinId": "pin_440","constraint": ""},
|
||||
{"pinId": "pin_441","constraint": ""},
|
||||
{"pinId": "pin_442","constraint": ""},
|
||||
{"pinId": "pin_443","constraint": ""},
|
||||
{"pinId": "pin_444","constraint": ""},
|
||||
{"pinId": "pin_445","constraint": ""},
|
||||
{"pinId": "pin_446","constraint": ""},
|
||||
{"pinId": "pin_447","constraint": ""},
|
||||
{"pinId": "pin_448","constraint": ""},
|
||||
{"pinId": "pin_449","constraint": ""},
|
||||
{"pinId": "pin_450","constraint": ""},
|
||||
{"pinId": "pin_451","constraint": ""},
|
||||
{"pinId": "pin_452","constraint": ""},
|
||||
{"pinId": "pin_453","constraint": ""},
|
||||
{"pinId": "pin_454","constraint": ""},
|
||||
{"pinId": "pin_455","constraint": ""},
|
||||
{"pinId": "pin_456","constraint": ""},
|
||||
{"pinId": "pin_457","constraint": ""},
|
||||
{"pinId": "pin_458","constraint": ""},
|
||||
{"pinId": "pin_459","constraint": ""},
|
||||
{"pinId": "pin_460","constraint": ""},
|
||||
{"pinId": "pin_461","constraint": ""},
|
||||
{"pinId": "pin_462","constraint": ""},
|
||||
{"pinId": "pin_463","constraint": ""},
|
||||
{"pinId": "pin_464","constraint": ""},
|
||||
{"pinId": "pin_465","constraint": ""},
|
||||
{"pinId": "pin_466","constraint": ""},
|
||||
{"pinId": "pin_467","constraint": ""},
|
||||
{"pinId": "pin_468","constraint": ""},
|
||||
{"pinId": "pin_469","constraint": ""},
|
||||
{"pinId": "pin_470","constraint": ""},
|
||||
{"pinId": "pin_471","constraint": ""},
|
||||
{"pinId": "pin_472","constraint": ""},
|
||||
{"pinId": "pin_473","constraint": ""},
|
||||
{"pinId": "pin_474","constraint": ""},
|
||||
{"pinId": "pin_475","constraint": ""},
|
||||
{"pinId": "pin_476","constraint": ""},
|
||||
{"pinId": "pin_477","constraint": ""},
|
||||
{"pinId": "pin_478","constraint": ""},
|
||||
{"pinId": "pin_479","constraint": ""},
|
||||
{"pinId": "pin_480","constraint": ""},
|
||||
{"pinId": "pin_481","constraint": ""},
|
||||
{"pinId": "pin_482","constraint": ""},
|
||||
{"pinId": "pin_483","constraint": ""},
|
||||
{"pinId": "pin_484","constraint": ""},
|
||||
{"pinId": "pin_485","constraint": ""},
|
||||
{"pinId": "pin_486","constraint": ""},
|
||||
{"pinId": "pin_487","constraint": ""},
|
||||
{"pinId": "pin_488","constraint": ""},
|
||||
{"pinId": "pin_489","constraint": ""},
|
||||
{"pinId": "pin_490","constraint": ""},
|
||||
{"pinId": "pin_491","constraint": ""},
|
||||
{"pinId": "pin_492","constraint": ""},
|
||||
{"pinId": "pin_493","constraint": ""},
|
||||
{"pinId": "pin_494","constraint": ""},
|
||||
{"pinId": "pin_495","constraint": ""},
|
||||
{"pinId": "pin_496","constraint": ""},
|
||||
{"pinId": "pin_497","constraint": ""},
|
||||
{"pinId": "pin_498","constraint": ""},
|
||||
{"pinId": "pin_499","constraint": ""},
|
||||
{"pinId": "pin_500","constraint": ""},
|
||||
{"pinId": "pin_501","constraint": ""},
|
||||
{"pinId": "pin_502","constraint": ""},
|
||||
{"pinId": "pin_503","constraint": ""},
|
||||
{"pinId": "pin_504","constraint": ""},
|
||||
{"pinId": "pin_505","constraint": ""},
|
||||
{"pinId": "pin_506","constraint": ""},
|
||||
{"pinId": "pin_507","constraint": ""},
|
||||
{"pinId": "pin_508","constraint": ""},
|
||||
{"pinId": "pin_509","constraint": ""},
|
||||
{"pinId": "pin_510","constraint": ""},
|
||||
{"pinId": "pin_511","constraint": ""},
|
||||
{"pinId": "pin_512","constraint": ""},
|
||||
{"pinId": "pin_513","constraint": ""},
|
||||
{"pinId": "pin_514","constraint": ""},
|
||||
{"pinId": "pin_515","constraint": ""},
|
||||
{"pinId": "pin_516","constraint": ""},
|
||||
{"pinId": "pin_517","constraint": ""},
|
||||
{"pinId": "pin_518","constraint": ""},
|
||||
{"pinId": "pin_519","constraint": ""},
|
||||
{"pinId": "pin_520","constraint": ""},
|
||||
{"pinId": "pin_521","constraint": ""},
|
||||
{"pinId": "pin_522","constraint": ""},
|
||||
{"pinId": "pin_523","constraint": ""},
|
||||
{"pinId": "pin_524","constraint": ""},
|
||||
{"pinId": "pin_525","constraint": ""},
|
||||
{"pinId": "pin_526","constraint": ""},
|
||||
{"pinId": "pin_527","constraint": ""},
|
||||
{"pinId": "pin_528","constraint": ""},
|
||||
{"pinId": "pin_529","constraint": ""},
|
||||
{"pinId": "pin_530","constraint": ""},
|
||||
{"pinId": "pin_531","constraint": ""},
|
||||
{"pinId": "pin_532","constraint": ""},
|
||||
{"pinId": "pin_533","constraint": ""},
|
||||
{"pinId": "pin_534","constraint": ""},
|
||||
{"pinId": "pin_535","constraint": ""},
|
||||
{"pinId": "pin_536","constraint": ""},
|
||||
{"pinId": "pin_537","constraint": ""},
|
||||
{"pinId": "pin_538","constraint": ""},
|
||||
{"pinId": "pin_539","constraint": ""},
|
||||
{"pinId": "pin_540","constraint": ""},
|
||||
{"pinId": "pin_541","constraint": ""},
|
||||
{"pinId": "pin_542","constraint": ""},
|
||||
{"pinId": "pin_543","constraint": ""},
|
||||
{"pinId": "pin_544","constraint": ""},
|
||||
{"pinId": "pin_545","constraint": ""},
|
||||
{"pinId": "pin_546","constraint": ""},
|
||||
{"pinId": "pin_547","constraint": ""},
|
||||
{"pinId": "pin_548","constraint": ""},
|
||||
{"pinId": "pin_549","constraint": ""},
|
||||
{"pinId": "pin_550","constraint": ""},
|
||||
{"pinId": "pin_551","constraint": ""},
|
||||
{"pinId": "pin_552","constraint": ""},
|
||||
{"pinId": "pin_553","constraint": ""},
|
||||
{"pinId": "pin_554","constraint": ""},
|
||||
{"pinId": "pin_555","constraint": ""},
|
||||
{"pinId": "pin_556","constraint": ""},
|
||||
{"pinId": "pin_557","constraint": ""},
|
||||
{"pinId": "pin_558","constraint": ""},
|
||||
{"pinId": "pin_559","constraint": ""},
|
||||
{"pinId": "pin_560","constraint": ""},
|
||||
{"pinId": "pin_561","constraint": ""},
|
||||
{"pinId": "pin_562","constraint": ""},
|
||||
{"pinId": "pin_563","constraint": ""},
|
||||
{"pinId": "pin_564","constraint": ""},
|
||||
{"pinId": "pin_565","constraint": ""},
|
||||
{"pinId": "pin_566","constraint": ""},
|
||||
{"pinId": "pin_567","constraint": ""},
|
||||
{"pinId": "pin_568","constraint": ""},
|
||||
{"pinId": "pin_569","constraint": ""},
|
||||
{"pinId": "pin_570","constraint": ""},
|
||||
{"pinId": "pin_571","constraint": ""},
|
||||
{"pinId": "pin_572","constraint": ""},
|
||||
{"pinId": "pin_573","constraint": ""},
|
||||
{"pinId": "pin_574","constraint": ""},
|
||||
{"pinId": "pin_575","constraint": ""},
|
||||
{"pinId": "pin_576","constraint": ""},
|
||||
{"pinId": "pin_577","constraint": ""},
|
||||
{"pinId": "pin_578","constraint": ""},
|
||||
{"pinId": "pin_579","constraint": ""},
|
||||
{"pinId": "pin_580","constraint": ""},
|
||||
{"pinId": "pin_581","constraint": ""},
|
||||
{"pinId": "pin_582","constraint": ""},
|
||||
{"pinId": "pin_583","constraint": ""},
|
||||
{"pinId": "pin_584","constraint": ""},
|
||||
{"pinId": "pin_585","constraint": ""},
|
||||
{"pinId": "pin_586","constraint": ""},
|
||||
{"pinId": "pin_587","constraint": ""},
|
||||
{"pinId": "pin_588","constraint": ""},
|
||||
{"pinId": "pin_589","constraint": ""},
|
||||
{"pinId": "pin_590","constraint": ""},
|
||||
{"pinId": "pin_591","constraint": ""},
|
||||
{"pinId": "pin_592","constraint": ""},
|
||||
{"pinId": "pin_593","constraint": ""},
|
||||
{"pinId": "pin_594","constraint": ""},
|
||||
{"pinId": "pin_595","constraint": ""},
|
||||
{"pinId": "pin_596","constraint": ""},
|
||||
{"pinId": "pin_597","constraint": ""},
|
||||
{"pinId": "pin_598","constraint": ""},
|
||||
{"pinId": "pin_599","constraint": ""},
|
||||
{"pinId": "pin_600","constraint": ""},
|
||||
{"pinId": "pin_601","constraint": ""},
|
||||
{"pinId": "pin_602","constraint": ""},
|
||||
{"pinId": "pin_603","constraint": ""},
|
||||
{"pinId": "pin_604","constraint": ""},
|
||||
{"pinId": "pin_605","constraint": ""},
|
||||
{"pinId": "pin_606","constraint": ""},
|
||||
{"pinId": "pin_607","constraint": ""},
|
||||
{"pinId": "pin_608","constraint": ""},
|
||||
{"pinId": "pin_609","constraint": ""},
|
||||
{"pinId": "pin_610","constraint": ""},
|
||||
{"pinId": "pin_611","constraint": ""},
|
||||
{"pinId": "pin_612","constraint": ""},
|
||||
{"pinId": "pin_613","constraint": ""},
|
||||
{"pinId": "pin_614","constraint": ""},
|
||||
{"pinId": "pin_615","constraint": ""},
|
||||
{"pinId": "pin_616","constraint": ""},
|
||||
{"pinId": "pin_617","constraint": ""},
|
||||
{"pinId": "pin_618","constraint": ""},
|
||||
{"pinId": "pin_619","constraint": ""},
|
||||
{"pinId": "pin_620","constraint": ""},
|
||||
{"pinId": "pin_621","constraint": ""},
|
||||
{"pinId": "pin_622","constraint": ""},
|
||||
{"pinId": "pin_623","constraint": ""},
|
||||
{"pinId": "pin_624","constraint": ""},
|
||||
{"pinId": "pin_625","constraint": ""},
|
||||
{"pinId": "pin_626","constraint": ""},
|
||||
{"pinId": "pin_627","constraint": ""},
|
||||
{"pinId": "pin_628","constraint": ""},
|
||||
{"pinId": "pin_629","constraint": ""},
|
||||
{"pinId": "pin_630","constraint": ""},
|
||||
{"pinId": "pin_631","constraint": ""},
|
||||
{"pinId": "pin_632","constraint": ""},
|
||||
{"pinId": "pin_633","constraint": ""},
|
||||
{"pinId": "pin_634","constraint": ""},
|
||||
{"pinId": "pin_635","constraint": ""},
|
||||
{"pinId": "pin_636","constraint": ""},
|
||||
{"pinId": "pin_637","constraint": ""},
|
||||
{"pinId": "pin_638","constraint": ""},
|
||||
{"pinId": "pin_639","constraint": ""},
|
||||
{"pinId": "pin_640","constraint": ""},
|
||||
{"pinId": "pin_641","constraint": ""},
|
||||
{"pinId": "pin_642","constraint": ""},
|
||||
{"pinId": "pin_643","constraint": ""},
|
||||
{"pinId": "pin_644","constraint": ""},
|
||||
{"pinId": "pin_645","constraint": ""},
|
||||
{"pinId": "pin_646","constraint": ""},
|
||||
{"pinId": "pin_647","constraint": ""},
|
||||
{"pinId": "pin_648","constraint": ""},
|
||||
{"pinId": "pin_649","constraint": ""},
|
||||
{"pinId": "pin_650","constraint": ""},
|
||||
{"pinId": "pin_651","constraint": ""},
|
||||
{"pinId": "pin_652","constraint": ""},
|
||||
{"pinId": "pin_653","constraint": ""},
|
||||
{"pinId": "pin_654","constraint": ""},
|
||||
{"pinId": "pin_655","constraint": ""},
|
||||
{"pinId": "pin_656","constraint": ""},
|
||||
{"pinId": "pin_657","constraint": ""},
|
||||
{"pinId": "pin_658","constraint": ""},
|
||||
{"pinId": "pin_659","constraint": ""},
|
||||
{"pinId": "pin_660","constraint": ""},
|
||||
{"pinId": "pin_661","constraint": ""},
|
||||
{"pinId": "pin_662","constraint": ""},
|
||||
{"pinId": "pin_663","constraint": ""},
|
||||
{"pinId": "pin_664","constraint": ""},
|
||||
{"pinId": "pin_665","constraint": ""},
|
||||
{"pinId": "pin_666","constraint": ""},
|
||||
{"pinId": "pin_667","constraint": ""},
|
||||
{"pinId": "pin_668","constraint": ""},
|
||||
{"pinId": "pin_669","constraint": ""},
|
||||
{"pinId": "pin_670","constraint": ""},
|
||||
{"pinId": "pin_671","constraint": ""},
|
||||
{"pinId": "pin_672","constraint": ""},
|
||||
{"pinId": "pin_673","constraint": ""},
|
||||
{"pinId": "pin_674","constraint": ""},
|
||||
{"pinId": "pin_675","constraint": ""}
|
||||
]
|
||||
},
|
||||
"rotate": 0,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": true,"isOn": true,"index": 0
|
||||
},
|
||||
{ "id": "hdmi_out","type": "HDMI","x": -40,"y": 135,
|
||||
"attrs": {
|
||||
"size": 1.25
|
||||
},
|
||||
"rotate": 0,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": false,"isOn": false,"index": 0
|
||||
},
|
||||
{
|
||||
"id": "hdmi_in","type": "HDMI","x": -40,"y": 230,
|
||||
"attrs": {
|
||||
"size": 1.25
|
||||
},
|
||||
"rotate": 0,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": false,"isOn": false,"index": 0
|
||||
},
|
||||
{
|
||||
"id": "eth0","type": "ETH","x": 0,"y": 350,
|
||||
"attrs": {
|
||||
"size": 1.25
|
||||
},
|
||||
"rotate": 0,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": false,"isOn": false,"index": 0
|
||||
},
|
||||
{
|
||||
"id": "ddr3","type": "DDR","x": 405,"y": 225,
|
||||
"attrs": {
|
||||
"size": 1.25
|
||||
},
|
||||
"rotate": 0,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": false,"isOn": false,"index": 0
|
||||
},
|
||||
{
|
||||
"id": "sfp0","type": "SFP","x": 650,"y": 260,
|
||||
"attrs": {
|
||||
"size": 1.6
|
||||
},
|
||||
"rotate": 0,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": false,"isOn": false,"index": 0
|
||||
},
|
||||
{
|
||||
"id": "sfp1","type": "SFP","x": 650,"y": 340,
|
||||
"attrs": {
|
||||
"size": 1.6
|
||||
},
|
||||
"rotate": 0,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": false,"isOn": false,"index": 0
|
||||
},
|
||||
{
|
||||
"id": "sma_RXN","type": "SMA","x": 575,"y": 125,
|
||||
"attrs": {
|
||||
"size": 0.7
|
||||
},
|
||||
"rotate": 0,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": false,"isOn": false,"index": 0
|
||||
},
|
||||
{
|
||||
"id": "sma_RXP","type": "SMA","x": 575,"y": 175,
|
||||
"attrs": {
|
||||
"size": 0.7
|
||||
},
|
||||
"rotate": 0,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": false,"isOn": false,"index": 0
|
||||
},
|
||||
{
|
||||
"id": "sma_TXN","type": "SMA","x": 575,"y": 260,
|
||||
"attrs": {
|
||||
"size": 0.7
|
||||
},
|
||||
"rotate": 0,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": false,"isOn": false,"index": 0
|
||||
},
|
||||
{
|
||||
"id": "sma_TXP","type": "SMA","x": 575,"y": 310,
|
||||
"attrs": {
|
||||
"size": 0.7
|
||||
},
|
||||
"rotate": 0,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": false,"isOn": false,"index": 0
|
||||
},
|
||||
{
|
||||
"id": "sd0","type": "SD","x": 540,"y": 410,
|
||||
"attrs": {
|
||||
"size": 1
|
||||
},
|
||||
"rotate": 0,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": false,"isOn": false,"index": 0
|
||||
},
|
||||
{"id": "key0","type": "MechanicalButton","x": 335,"y": 435,
|
||||
"attrs": {"size": 0.17,"bindKey": "","pins": [{"pinId": "BTN","constraint": "","x": 80,"y": 140}]},
|
||||
"rotate": 0,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": true,"isOn": false,"index": 0},
|
||||
{"id": "key1","type": "MechanicalButton","x": 370,"y": 435,
|
||||
"attrs": {"size": 0.17,"bindKey": "","pins": [{"pinId": "BTN","constraint": "","x": 80,"y": 140}]},
|
||||
"rotate": 0,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": true,"isOn": false,"index": 0},
|
||||
{"id": "key2","type": "MechanicalButton","x": 405,"y": 435,
|
||||
"attrs": {"size": 0.17,"bindKey": "","pins": [{"pinId": "BTN","constraint": "","x": 80,"y": 140}]},
|
||||
"rotate": 0,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": true,"isOn": false,"index": 0},
|
||||
{"id": "key3","type": "MechanicalButton","x": 440,"y": 435,
|
||||
"attrs": {"size": 0.17,"bindKey": "","pins": [{"pinId": "BTN","constraint": "","x": 80,"y": 140}]},
|
||||
"rotate": 0,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": true,"isOn": false,"index": 0},
|
||||
{"id": "key4","type": "MechanicalButton","x": 475,"y": 435,
|
||||
"attrs": {"size": 0.17,"bindKey": "","pins": [{"pinId": "BTN","constraint": "","x": 80,"y": 140}]},
|
||||
"rotate": 0,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": true,"isOn": false,"index": 0},
|
||||
{"id": "key_rest","type": "MechanicalButton","x": 510,"y": 435,
|
||||
"attrs": {"size": 0.17,"bindKey": "","pins": [{"pinId": "BTN","constraint": "","x": 80,"y": 140}]},
|
||||
"rotate": 0,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": true,"isOn": false,"index": 0},
|
||||
{"id": "led0","type": "SMT_LED","x": 340,"y": 405,
|
||||
"attrs": {"size": 0.17,"color": "green","brightness": 80,"pins": [{"pinId": "LED","constraint": "","x": 50,"y": 30}]},
|
||||
"rotate": 90,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": true,"isOn": false,"index": 0},
|
||||
{"id": "led1","type": "SMT_LED","x": 360,"y": 405,
|
||||
"attrs": {"size": 0.17,"color": "green","brightness": 80,"pins": [{"pinId": "LED","constraint": "","x": 50,"y": 30}]},
|
||||
"rotate": 90,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": true,"isOn": false,"index": 0},
|
||||
{"id": "led2","type": "SMT_LED","x": 380,"y": 405,
|
||||
"attrs": {"size": 0.17,"color": "green","brightness": 80,"pins": [{"pinId": "LED","constraint": "","x": 50,"y": 30}]},
|
||||
"rotate": 90,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": true,"isOn": false,"index": 0},
|
||||
{"id": "led3","type": "SMT_LED","x": 400,"y": 405,
|
||||
"attrs": {"size": 0.17,"color": "green","brightness": 80,"pins": [{"pinId": "LED","constraint": "","x": 50,"y": 30}]},
|
||||
"rotate": 90,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": true,"isOn": false,"index": 0},
|
||||
{"id": "led4","type": "SMT_LED","x": 420,"y": 405,
|
||||
"attrs": {"size": 0.17,"color": "green","brightness": 80,"pins": [{"pinId": "LED","constraint": "","x": 50,"y": 30}]},
|
||||
"rotate": 90,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": true,"isOn": false,"index": 0},
|
||||
{"id": "led5","type": "SMT_LED","x": 440,"y": 405,
|
||||
"attrs": {"size": 0.17,"color": "green","brightness": 80,"pins": [{"pinId": "LED","constraint": "","x": 50,"y": 30}]},
|
||||
"rotate": 90,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": true,"isOn": false,"index": 0},
|
||||
{"id": "led6","type": "SMT_LED","x": 460,"y": 405,
|
||||
"attrs": {"size": 0.17,"color": "green","brightness": 80,"pins": [{"pinId": "LED","constraint": "","x": 50,"y": 30}]},
|
||||
"rotate": 90,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": true,"isOn": false,"index": 0},
|
||||
{"id": "led7","type": "SMT_LED","x": 480,"y": 405,
|
||||
"attrs": {"size": 0.17,"color": "green","brightness": 80,"pins": [{"pinId": "LED","constraint": "","x": 50,"y": 30}]},
|
||||
"rotate": 90,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": true,"isOn": false,"index": 0},
|
||||
{"id": "led_power","type": "SMT_LED","x": 515,"y": 405,
|
||||
"attrs": {"size": 0.17,"color": "green","brightness": 80,"pins": [{"pinId": "LED","constraint": "","x": 50,"y": 30}]},
|
||||
"rotate": 90,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": true,"isOn": true,"index": 0},
|
||||
{"id": "led_init","type": "SMT_LED","x": 215,"y": 340,
|
||||
"attrs": {"size": 0.17,"color": "green","brightness": 80,"pins": [{"pinId": "LED","constraint": "","x": 50,"y": 30}]},
|
||||
"rotate": 0,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": true,"isOn": true,"index": 0},
|
||||
{"id": "led_done","type": "SMT_LED","x": 215,"y": 330,
|
||||
"attrs": {"size": 0.17,"color": "green","brightness": 80,"pins": [{"pinId": "LED","constraint": "","x": 50,"y": 30}]},
|
||||
"rotate": 0,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": true,"isOn": true,"index": 0},
|
||||
{"id": "led_center_power","type": "SMT_LED","x": 215,"y": 320,
|
||||
"attrs": {"size": 0.17,"color": "green","brightness": 80,"pins": [{"pinId": "LED","constraint": "","x": 50,"y": 30}]},
|
||||
"rotate": 0,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": true,"isOn": true,"index": 0},
|
||||
{"id": "led8","type": "SMT_LED","x": 215,"y": 310,
|
||||
"attrs": {"size": 0.17,"color": "green","brightness": 80,"pins": [{"pinId": "LED","constraint": "","x": 50,"y": 30}]},
|
||||
"rotate": 0,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": true,"isOn": false,"index": 0},
|
||||
{"id": "led9","type": "SMT_LED","x": 215,"y": 300,
|
||||
"attrs": {"size": 0.17,"color": "green","brightness": 80,"pins": [{"pinId": "LED","constraint": "","x": 50,"y": 30}]},
|
||||
"rotate": 0,"group": "PG2L00H_MotherBoard","positionlock": false,"hidepins": true,"isOn": false,"index": 0}
|
||||
],
|
||||
"connections": [
|
||||
["led_done:LED","FPGA_chip:pin_644",0.5,["right10","downright1","*","upright3"]]
|
||||
]
|
||||
}
|
|
@ -2,7 +2,8 @@
|
|||
<div class="h-screen flex flex-col overflow-hidden">
|
||||
<div class="flex flex-1 overflow-hidden relative">
|
||||
<!-- 左侧图形化区域 -->
|
||||
<div class="relative bg-base-200 overflow-hidden" :style="{ width: leftPanelWidth + '%' }"> <DiagramCanvas
|
||||
<div class="relative bg-base-200 overflow-hidden h-full" :style="{ width: leftPanelWidth + '%' }">
|
||||
<DiagramCanvas
|
||||
ref="diagramCanvas"
|
||||
:componentModules="componentModules"
|
||||
@component-selected="handleComponentSelected"
|
||||
|
@ -23,84 +24,22 @@
|
|||
></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?.type }}</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?.attrs?.[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?.attrs?.[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?.attrs?.[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?.attrs?.[prop.name]"
|
||||
@change="(event) => {
|
||||
const selectElement = event.target as HTMLSelectElement;
|
||||
const value = selectElement.value;
|
||||
console.log('选择的值:', value, '类型:', typeof value);
|
||||
if (selectedComponentData) {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 class="bg-base-100 h-full overflow-hidden flex flex-col" :style="{ width: (100 - leftPanelWidth) + '%' }">
|
||||
<div class="overflow-y-auto p-4 flex-1">
|
||||
<PropertyPanel
|
||||
:componentData="selectedComponentData"
|
||||
:componentConfig="selectedComponentConfig"
|
||||
@updateProp="updateComponentProp"
|
||||
@updateDirectProp="updateComponentDirectProp"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 元器件选择组件 -->
|
||||
</div> <!-- 元器件选择组件 -->
|
||||
<ComponentSelector
|
||||
:open="showComponentsMenu"
|
||||
@update:open="showComponentsMenu = $event"
|
||||
@add-component="handleAddComponent"
|
||||
@add-template="handleAddTemplate"
|
||||
@close="showComponentsMenu = false"
|
||||
/>
|
||||
</div>
|
||||
|
@ -112,8 +51,16 @@
|
|||
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 PropertyPanel from '@/components/PropertyPanel.vue';
|
||||
import type { DiagramData, DiagramPart, ConnectionArray } from '@/components/diagramManager';
|
||||
import { validateDiagramData } from '@/components/diagramManager';
|
||||
import {
|
||||
type PropertyConfig,
|
||||
generatePropertyConfigs,
|
||||
generatePropsFromDefault,
|
||||
generatePropsFromAttrs,
|
||||
getPropValue
|
||||
} from '@/components/equipments/componentConfig'; // 引入组件配置工具
|
||||
|
||||
// --- 元器件管理 ---
|
||||
const showComponentsMenu = ref(false);
|
||||
|
@ -136,18 +83,12 @@ interface ComponentModule {
|
|||
default: any;
|
||||
getDefaultProps?: () => Record<string, any>;
|
||||
config?: {
|
||||
props?: Array<{
|
||||
name: string;
|
||||
type: string;
|
||||
label?: string;
|
||||
default: any;
|
||||
options?: Array<{ value: any; label: string }>; // 添加 options 字段用于 select 类型
|
||||
}>;
|
||||
props?: Array<PropertyConfig>;
|
||||
};
|
||||
}
|
||||
|
||||
const componentModules = shallowRef<Record<string, ComponentModule>>({});
|
||||
const selectedComponentConfig = shallowRef<ComponentModule['config'] | null>(null); // 存储选中组件的配置
|
||||
const selectedComponentConfig = shallowRef<{ props: PropertyConfig[] } | null>(null); // 存储选中组件的配置
|
||||
|
||||
// 动态加载组件定义
|
||||
async function loadComponentModule(type: string) {
|
||||
|
@ -228,56 +169,53 @@ async function handleAddComponent(componentData: { type: string; name: string; p
|
|||
// 获取画布容器和位置信息
|
||||
const canvasInstance = diagramCanvas.value as any;
|
||||
|
||||
// 默认位置(当无法获取画布信息时使用)
|
||||
let posX = 100;
|
||||
let posY = 100;
|
||||
// 获取当前画布的位置信息
|
||||
let position = { x: 100, y: 100 };
|
||||
let scale = 1;
|
||||
|
||||
try {
|
||||
if (canvasInstance) {
|
||||
if (canvasInstance && canvasInstance.getCanvasPosition && canvasInstance.getScale) {
|
||||
position = canvasInstance.getCanvasPosition();
|
||||
scale = canvasInstance.getScale();
|
||||
|
||||
// 获取画布容器
|
||||
const canvasContainer = canvasInstance.$el as HTMLElement;
|
||||
if (canvasContainer) {
|
||||
// 获取当前画布的位置和缩放信息
|
||||
const canvasPosition = canvasInstance.getCanvasPosition ?
|
||||
canvasInstance.getCanvasPosition() :
|
||||
{ x: 0, y: 0 };
|
||||
const scale = canvasInstance.getScale ?
|
||||
canvasInstance.getScale() :
|
||||
1;
|
||||
|
||||
// 计算可视区域中心点在画布坐标系中的位置
|
||||
const viewportWidth = canvasContainer.clientWidth;
|
||||
const viewportHeight = canvasContainer.clientHeight;
|
||||
|
||||
// 计算画布中心点的坐标
|
||||
posX = (viewportWidth / 2 - canvasPosition.x) / scale;
|
||||
posY = (viewportHeight / 2 - canvasPosition.y) / scale;
|
||||
position.x = (viewportWidth / 2 - position.x) / scale;
|
||||
position.y = (viewportHeight / 2 - position.y) / scale;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error getting canvas position:', error);
|
||||
// 使用默认位置
|
||||
console.error('获取画布位置时出错:', error);
|
||||
}
|
||||
|
||||
// 添加一些随机偏移,避免元器件重叠
|
||||
const offsetX = Math.floor(Math.random() * 100) - 50;
|
||||
const offsetY = Math.floor(Math.random() * 100) - 50;
|
||||
|
||||
// 将原有的 ComponentItem 转换为 DiagramPart
|
||||
// 创建新组件,使用diagramManager接口定义
|
||||
const newComponent: DiagramPart = {
|
||||
id: `component-${Date.now()}`,
|
||||
type: componentData.type,
|
||||
x: Math.round(posX + offsetX),
|
||||
y: Math.round(posY + offsetY),
|
||||
attrs: componentData.props, // 使用从 ComponentSelector 传递的默认属性
|
||||
x: Math.round(position.x + offsetX),
|
||||
y: Math.round(position.y + offsetY),
|
||||
attrs: componentData.props,
|
||||
rotate: 0,
|
||||
group: '',
|
||||
positionlock: false,
|
||||
hide: false,
|
||||
isOn: false
|
||||
hidepins: true,
|
||||
isOn: true,
|
||||
index: 0
|
||||
};
|
||||
console.log('Adding new component:', newComponent);
|
||||
// 通过 diagramCanvas 添加组件
|
||||
|
||||
console.log('添加新组件:', newComponent);
|
||||
|
||||
// 通过画布实例添加组件
|
||||
if (canvasInstance && canvasInstance.getDiagramData && canvasInstance.setDiagramData) {
|
||||
const currentData = canvasInstance.getDiagramData();
|
||||
currentData.parts.push(newComponent);
|
||||
|
@ -285,6 +223,133 @@ async function handleAddComponent(componentData: { type: string; name: string; p
|
|||
}
|
||||
}
|
||||
|
||||
// 处理模板添加事件
|
||||
async function handleAddTemplate(templateData: { id: string; name: string; template: any }) {
|
||||
console.log('添加模板:', templateData);
|
||||
console.log('=== 模板组件数量:', templateData.template?.parts?.length || 0);
|
||||
|
||||
// 获取画布实例
|
||||
const canvasInstance = diagramCanvas.value as any;
|
||||
if (!canvasInstance || !canvasInstance.getDiagramData || !canvasInstance.setDiagramData) {
|
||||
console.error('没有可用的画布实例添加模板');
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取当前图表数据
|
||||
const currentData = canvasInstance.getDiagramData();
|
||||
console.log('=== 当前图表组件数量:', currentData.parts.length);
|
||||
|
||||
// 生成唯一ID前缀,以确保添加的组件ID不重复
|
||||
const idPrefix = `template-${Date.now()}-`;
|
||||
// 处理模板组件并添加到图表
|
||||
if (templateData.template && templateData.template.parts) {
|
||||
// 获取当前视口中心位置
|
||||
let viewportCenter = { x: 300, y: 200 }; // 默认值
|
||||
try {
|
||||
if (canvasInstance && canvasInstance.getCanvasPosition && canvasInstance.getScale) {
|
||||
const position = canvasInstance.getCanvasPosition();
|
||||
const scale = canvasInstance.getScale();
|
||||
|
||||
// 获取画布容器
|
||||
const canvasContainer = canvasInstance.$el as HTMLElement;
|
||||
if (canvasContainer) {
|
||||
// 计算可视区域中心点在画布坐标系中的位置
|
||||
const viewportWidth = canvasContainer.clientWidth;
|
||||
const viewportHeight = canvasContainer.clientHeight;
|
||||
|
||||
// 计算视口中心点的坐标 (与handleAddComponent函数中的方法相同)
|
||||
viewportCenter.x = (viewportWidth / 2 - position.x) / scale;
|
||||
viewportCenter.y = (viewportHeight / 2 - position.y) / scale;
|
||||
console.log(`=== 计算的视口中心: x=${viewportCenter.x}, y=${viewportCenter.y}, scale=${scale}`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取视口中心位置时出错:', error);
|
||||
}
|
||||
|
||||
console.log('=== 使用视口中心添加模板组件:', viewportCenter);
|
||||
|
||||
// 找到模板中的主要组件(假设是第一个组件)
|
||||
const mainPart = templateData.template.parts[0];
|
||||
|
||||
// 创建带有新位置的组件
|
||||
const newParts = templateData.template.parts.map((part: any) => {
|
||||
// 创建组件副本并分配新ID
|
||||
const newPart = JSON.parse(JSON.stringify(part));
|
||||
newPart.id = `${idPrefix}${part.id}`;
|
||||
|
||||
// 计算相对于主要组件的偏移量,保持模板内部组件的相对位置关系
|
||||
if (typeof newPart.x === 'number' && typeof newPart.y === 'number') {
|
||||
const oldX = newPart.x;
|
||||
const oldY = newPart.y;
|
||||
|
||||
// 计算相对位置(相对于主要组件)
|
||||
const relativeX = part.x - mainPart.x;
|
||||
const relativeY = part.y - mainPart.y;
|
||||
|
||||
// 应用到视口中心位置
|
||||
newPart.x = viewportCenter.x + relativeX;
|
||||
newPart.y = viewportCenter.y + relativeY;
|
||||
|
||||
console.log(`=== 组件[${newPart.id}]位置调整: (${oldX},${oldY}) -> (${newPart.x},${newPart.y})`);
|
||||
}
|
||||
|
||||
return newPart;
|
||||
});
|
||||
|
||||
// 向图表添加新组件
|
||||
currentData.parts.push(...newParts);
|
||||
|
||||
// 处理连接关系
|
||||
if (templateData.template.connections) {
|
||||
// 创建一个映射表用于转换旧组件ID到新组件ID
|
||||
const idMap: Record<string, string> = {};
|
||||
templateData.template.parts.forEach((part: any) => {
|
||||
idMap[part.id] = `${idPrefix}${part.id}`;
|
||||
});
|
||||
|
||||
// 添加连接,更新组件ID引用
|
||||
const newConnections = templateData.template.connections.map((conn: any) => {
|
||||
// 处理连接数据 (格式为 [from, to, type, path])
|
||||
if (Array.isArray(conn)) {
|
||||
const [from, to, type, path] = conn;
|
||||
|
||||
// 从连接字符串中提取组件ID和引脚ID
|
||||
const fromParts = from.split(':');
|
||||
const toParts = to.split(':');
|
||||
|
||||
if (fromParts.length === 2 && toParts.length === 2) {
|
||||
const fromComponentId = fromParts[0];
|
||||
const fromPinId = fromParts[1];
|
||||
const toComponentId = toParts[0];
|
||||
const toPinId = toParts[1];
|
||||
|
||||
// 创建新的连接字符串,使用新的组件ID
|
||||
const newFrom = `${idMap[fromComponentId] || fromComponentId}:${fromPinId}`;
|
||||
const newTo = `${idMap[toComponentId] || toComponentId}:${toPinId}`;
|
||||
|
||||
return [newFrom, newTo, type, path];
|
||||
}
|
||||
}
|
||||
return conn; // 如果格式不匹配,保持原样
|
||||
});
|
||||
|
||||
// 添加到当前连接列表
|
||||
currentData.connections.push(...newConnections);
|
||||
}
|
||||
|
||||
// 更新图表数据
|
||||
canvasInstance.setDiagramData(currentData);
|
||||
console.log('=== 更新图表数据完成,新组件数量:', currentData.parts.length);
|
||||
|
||||
// 显示成功消息
|
||||
showToast(`已添加 ${templateData.name} 模板`, 'success');
|
||||
} else {
|
||||
console.error('模板格式错误,缺少parts数组');
|
||||
showToast('模板格式错误', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 处理组件选中事件
|
||||
async function handleComponentSelected(componentData: DiagramPart | null) {
|
||||
selectedComponentId.value = componentData ? componentData.id : null;
|
||||
|
@ -296,42 +361,19 @@ async function handleComponentSelected(componentData: DiagramPart | null) {
|
|||
|
||||
if (moduleRef) {
|
||||
try {
|
||||
// 尝试使用组件导出的getDefaultProps方法获取配置
|
||||
// 使用组件配置工具创建配置项
|
||||
const propConfigs: PropertyConfig[] = [];
|
||||
|
||||
// 1. 自动获取组件属性信息 - 利用DiagramPart接口结构
|
||||
const directPropConfigs = generatePropertyConfigs(componentData);
|
||||
propConfigs.push(...directPropConfigs);
|
||||
|
||||
// 2. 尝试使用组件导出的getDefaultProps方法获取配置
|
||||
if (typeof moduleRef.getDefaultProps === 'function') {
|
||||
// 从getDefaultProps方法构建配置
|
||||
const defaultProps = moduleRef.getDefaultProps();
|
||||
const propConfigs = [];
|
||||
|
||||
for (const [propName, propValue] of Object.entries(defaultProps)) {
|
||||
// 跳过pins属性,它是一个特殊的数组属性
|
||||
if (propName === 'pins') continue;
|
||||
|
||||
// 根据属性类型创建配置
|
||||
let propType = typeof propValue;
|
||||
let propConfig: any = {
|
||||
name: propName,
|
||||
label: propName.charAt(0).toUpperCase() + propName.slice(1), // 首字母大写作为标签
|
||||
default: propValue
|
||||
};
|
||||
|
||||
// 根据值类型设置表单控件类型
|
||||
if (propType === 'string') {
|
||||
propConfig.type = 'string';
|
||||
} else if (propType === 'number') {
|
||||
propConfig.type = 'number';
|
||||
propConfig.min = 0;
|
||||
propConfig.max = 100;
|
||||
propConfig.step = 0.1;
|
||||
} else if (propType === 'boolean') {
|
||||
propConfig.type = 'boolean';
|
||||
} else if (propType === 'object' && propValue !== null && propValue.hasOwnProperty('options')) {
|
||||
// 如果是含有options的对象,认为它是select类型
|
||||
propConfig.type = 'select';
|
||||
propConfig.options = propValue.options;
|
||||
}
|
||||
|
||||
propConfigs.push(propConfig);
|
||||
}
|
||||
const defaultPropConfigs = generatePropsFromDefault(defaultProps);
|
||||
propConfigs.push(...defaultPropConfigs);
|
||||
|
||||
selectedComponentConfig.value = { props: propConfigs };
|
||||
console.log(`Built config for ${componentData.type} from getDefaultProps:`, selectedComponentConfig.value);
|
||||
|
@ -339,22 +381,8 @@ async function handleComponentSelected(componentData: DiagramPart | null) {
|
|||
console.warn(`Component ${componentData.type} does not export getDefaultProps method.`);
|
||||
// 创建一个空配置,只显示组件提供的属性
|
||||
const attrs = componentData.attrs || {};
|
||||
const propConfigs = [];
|
||||
|
||||
for (const [propName, propValue] of Object.entries(attrs)) {
|
||||
// 跳过pins属性
|
||||
if (propName === 'pins') continue;
|
||||
// 根据属性值类型创建配置
|
||||
let propType = typeof propValue;
|
||||
let propConfig: any = {
|
||||
name: propName,
|
||||
label: propName.charAt(0).toUpperCase() + propName.slice(1), // 首字母大写作为标签
|
||||
default: propValue || '',
|
||||
type: propType
|
||||
};
|
||||
|
||||
propConfigs.push(propConfig);
|
||||
}
|
||||
const attrPropConfigs = generatePropsFromAttrs(attrs);
|
||||
propConfigs.push(...attrPropConfigs);
|
||||
|
||||
selectedComponentConfig.value = { props: propConfigs };
|
||||
}
|
||||
|
@ -386,22 +414,45 @@ function handleComponentMoved(moveData: { id: string; x: number; y: number }) {
|
|||
|
||||
// 处理组件删除事件
|
||||
function handleComponentDelete(componentId: string) {
|
||||
// 查找要删除的组件索引
|
||||
const index = diagramData.value.parts.findIndex(p => p.id === componentId);
|
||||
if (index !== -1) {
|
||||
// 从数组中移除该组件
|
||||
diagramData.value.parts.splice(index, 1);
|
||||
// 查找要删除的组件
|
||||
const component = diagramData.value.parts.find(p => p.id === componentId);
|
||||
if (!component) return;
|
||||
|
||||
// 同时删除与该组件相关的所有连接
|
||||
diagramData.value.connections = diagramData.value.connections.filter(
|
||||
connection => !connection[0].startsWith(`${componentId}:`) && !connection[1].startsWith(`${componentId}:`)
|
||||
// 收集需要删除的组件ID列表(包括当前组件和同组组件)
|
||||
const componentsToDelete: string[] = [componentId];
|
||||
|
||||
// 如果组件属于一个组,则找出所有同组的组件
|
||||
if (component.group && component.group !== '') {
|
||||
const groupMembers = diagramData.value.parts.filter(
|
||||
p => p.group === component.group && p.id !== componentId
|
||||
);
|
||||
|
||||
// 如果删除的是当前选中的组件,清除选中状态
|
||||
if (selectedComponentId.value === componentId) {
|
||||
selectedComponentId.value = null;
|
||||
selectedComponentConfig.value = null;
|
||||
// 将同组组件ID添加到删除列表
|
||||
componentsToDelete.push(...groupMembers.map(p => p.id));
|
||||
console.log(`删除组件 ${componentId} 及其组 ${component.group} 中的 ${groupMembers.length} 个组件`);
|
||||
}
|
||||
|
||||
// 删除所有标记的组件
|
||||
diagramData.value.parts = diagramData.value.parts.filter(
|
||||
p => !componentsToDelete.includes(p.id)
|
||||
);
|
||||
|
||||
// 同时删除与这些组件相关的所有连接
|
||||
diagramData.value.connections = diagramData.value.connections.filter(
|
||||
connection => {
|
||||
for (const id of componentsToDelete) {
|
||||
if (connection[0].startsWith(`${id}:`) || connection[1].startsWith(`${id}:`)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
// 如果删除的是当前选中的组件,清除选中状态
|
||||
if (selectedComponentId.value && componentsToDelete.includes(selectedComponentId.value)) {
|
||||
selectedComponentId.value = null;
|
||||
selectedComponentConfig.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -409,7 +460,7 @@ function handleComponentDelete(componentId: string) {
|
|||
function updateComponentProp(componentId: string, propName: string, value: any) {
|
||||
const canvasInstance = diagramCanvas.value as any;
|
||||
if (!canvasInstance || !canvasInstance.getDiagramData || !canvasInstance.setDiagramData) {
|
||||
console.error('Canvas instance not available for property update');
|
||||
console.error('没有可用的画布实例进行属性更新');
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -417,21 +468,49 @@ function updateComponentProp(componentId: string, propName: string, value: any)
|
|||
if (value !== null && typeof value === 'object' && 'value' in value) {
|
||||
value = value.value;
|
||||
}
|
||||
|
||||
const currentData = canvasInstance.getDiagramData();
|
||||
const part = currentData.parts.find((p: DiagramPart) => p.id === componentId);
|
||||
|
||||
if (part) {
|
||||
if (!part.attrs) {
|
||||
part.attrs = {};
|
||||
// 检查是否为基本属性
|
||||
if (propName in part) {
|
||||
(part as any)[propName] = value;
|
||||
} else {
|
||||
// 否则当作attrs中的属性处理
|
||||
if (!part.attrs) {
|
||||
part.attrs = {};
|
||||
}
|
||||
part.attrs[propName] = value;
|
||||
}
|
||||
|
||||
part.attrs[propName] = value;
|
||||
|
||||
canvasInstance.setDiagramData(currentData);
|
||||
console.log(`Updated ${componentId} prop ${propName} to:`, value, typeof value);
|
||||
console.log(`更新组件${componentId}的属性${propName}为:`, value, typeof value);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新组件的直接属性
|
||||
function updateComponentDirectProp(componentId: string, propName: string, value: any) {
|
||||
const canvasInstance = diagramCanvas.value as any;
|
||||
if (!canvasInstance || !canvasInstance.getDiagramData || !canvasInstance.setDiagramData) {
|
||||
console.error('没有可用的画布实例进行属性更新');
|
||||
return;
|
||||
}
|
||||
|
||||
const currentData = canvasInstance.getDiagramData();
|
||||
const part = currentData.parts.find((p: DiagramPart) => p.id === componentId);
|
||||
|
||||
if (part) {
|
||||
// @ts-ignore: 动态属性赋值
|
||||
part[propName] = value;
|
||||
|
||||
canvasInstance.setDiagramData(currentData);
|
||||
console.log(`更新组件${componentId}的直接属性${propName}为:`, value, typeof value);
|
||||
}
|
||||
}
|
||||
|
||||
// 专门用于更新组件的显示层级 - 这个方法可以删除,直接使用updateComponentProp即可
|
||||
|
||||
// 处理连线创建事件
|
||||
function handleWireCreated(wireData: any) {
|
||||
console.log('Wire created:', wireData);
|
||||
|
@ -473,6 +552,9 @@ function showToast(message: string, type: 'success' | 'error' | 'info' = 'info',
|
|||
}
|
||||
// 显示通知
|
||||
|
||||
// --- 组件属性处理辅助函数 ---
|
||||
// 直接使用 componentConfig.ts 中导入的 getPropValue 函数
|
||||
|
||||
// --- 生命周期钩子 ---
|
||||
onMounted(async () => {
|
||||
// 初始化画布设置
|
||||
|
@ -514,7 +596,7 @@ onUnmounted(() => {
|
|||
.resizer {
|
||||
width: 6px;
|
||||
height: 100%;
|
||||
background-color: var(--b3);
|
||||
background-color: hsl(var(--b3));
|
||||
cursor: col-resize;
|
||||
transition: background-color 0.3s;
|
||||
z-index: 10;
|
||||
|
@ -544,4 +626,12 @@ onUnmounted(() => {
|
|||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 确保滚动行为仅在需要时出现 */
|
||||
html, body {
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Reference in New Issue