Refactor component configuration and diagram management
- Removed the component configuration from `componentConfig.ts` to streamline the codebase. - Introduced a new `diagram.json` file to define the initial structure for diagrams. - Created a `diagramManager.ts` to handle diagram data, including loading, saving, and validating diagram structures. - Updated `ProjectView.vue` to integrate the new diagram management system, including handling component selection and property updates. - Enhanced the component property management to support dynamic attributes and improved error handling. - Added functions for managing connections between components within the diagram.
This commit is contained in:
@@ -79,7 +79,6 @@
|
||||
ref="pinRef"
|
||||
direction="output"
|
||||
type="digital"
|
||||
:label="props.label"
|
||||
:constraint="props.constraint"
|
||||
:size="0.8"
|
||||
:componentId="props.componentId"
|
||||
@@ -99,7 +98,6 @@ const pinRef = ref<any>(null);
|
||||
|
||||
// 从Pin组件继承属性
|
||||
interface PinProps {
|
||||
label?: string;
|
||||
constraint?: string;
|
||||
componentId?: string; // 添加componentId属性
|
||||
// 这些属性被预设为固定值,但仍然包含在类型中以便完整继承
|
||||
@@ -121,7 +119,6 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
size: 1,
|
||||
bindKey: '',
|
||||
buttonText: '',
|
||||
label: 'BTN',
|
||||
constraint: '',
|
||||
componentId: 'button-default', // 添加默认componentId
|
||||
// 这些值会被覆盖,但需要默认值以满足类型要求
|
||||
@@ -142,7 +139,6 @@ const displayText = computed(() => {
|
||||
// 定义组件发出的事件
|
||||
const emit = defineEmits([
|
||||
'update:bindKey',
|
||||
'update:label',
|
||||
'update:constraint',
|
||||
'press',
|
||||
'release',
|
||||
@@ -212,8 +208,6 @@ defineExpose({
|
||||
// 按钮特有属性
|
||||
bindKey: props.bindKey,
|
||||
buttonText: props.buttonText,
|
||||
// 继承自Pin的属性
|
||||
label: props.label,
|
||||
constraint: props.constraint,
|
||||
componentId: props.componentId, // 添加componentId
|
||||
// 固定的Pin属性
|
||||
@@ -230,6 +224,25 @@ defineExpose({
|
||||
});
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
// 添加一个静态方法来获取默认props
|
||||
export function getDefaultProps() {
|
||||
return {
|
||||
size: 1,
|
||||
bindKey: '',
|
||||
buttonText: '',
|
||||
pins: [
|
||||
{
|
||||
pinId: 'BTN',
|
||||
constraint: '',
|
||||
x: 80,
|
||||
y: 140
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.button-container {
|
||||
display: flex;
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
<template>
|
||||
<svg
|
||||
<svg
|
||||
:width="width"
|
||||
:height="height"
|
||||
:viewBox="'0 0 ' + viewBoxWidth + ' ' + viewBoxHeight"
|
||||
class="pin-component"
|
||||
:data-component-id="props.componentId"
|
||||
:data-pin-label="props.label"
|
||||
>
|
||||
<g :transform="`translate(${viewBoxWidth/2}, ${viewBoxHeight/2})`">
|
||||
<g>
|
||||
<g transform="translate(-12.5, -12.5)">
|
||||
<circle
|
||||
<g transform="translate(-12.5, -12.5)"> <circle
|
||||
:style="{ fill: pinColor }"
|
||||
cx="12.5"
|
||||
cy="12.5"
|
||||
r="3.75"
|
||||
class="interactive"
|
||||
@click.stop="handlePinClick"
|
||||
:data-pin-element="`${props.componentId}`" />
|
||||
:data-pin-element="`${props.pinId}`" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
@@ -28,16 +25,13 @@
|
||||
import { ref, computed, reactive, watch, onMounted, onUnmounted } from 'vue';
|
||||
import { getConstraintColor, getConstraintState, onConstraintStateChange, notifyConstraintChange } from '../../stores/constraints';
|
||||
|
||||
// 生成唯一ID
|
||||
const uniqueId = `pin-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
||||
|
||||
interface Props {
|
||||
size?: number;
|
||||
label?: string;
|
||||
constraint?: string;
|
||||
direction?: 'input' | 'output' | 'inout';
|
||||
type?: 'digital' | 'analog';
|
||||
componentId?: string; // 添加组件ID属性,用于唯一标识
|
||||
pinId?: string; // 添加引脚ID属性,用于唯一标识
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
@@ -46,7 +40,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
constraint: '',
|
||||
direction: 'input',
|
||||
type: 'digital',
|
||||
componentId: 'pin-default' // 默认ID
|
||||
pinId: 'pin-default', // 默认ID
|
||||
});
|
||||
|
||||
const emit = defineEmits([
|
||||
@@ -136,49 +130,51 @@ function updateAnalogValue(value: number) {
|
||||
|
||||
defineExpose({
|
||||
setAnalogValue: updateAnalogValue,
|
||||
getAnalogValue: () => analogValue.value,
|
||||
getInfo: () => ({
|
||||
getAnalogValue: () => analogValue.value, getInfo: () => ({
|
||||
label: props.label,
|
||||
constraint: props.constraint,
|
||||
direction: props.direction,
|
||||
type: props.type,
|
||||
componentId: props.componentId
|
||||
pinId: props.pinId
|
||||
}),
|
||||
getPinPosition: (componentId: string) => {
|
||||
if (componentId !== props.componentId) return null;
|
||||
console.log('getPinPosition', componentId, props.componentId);
|
||||
const uniqueSelector = `[data-pin-element="${props.componentId}"]`;
|
||||
console.log('uniqueSelector', uniqueSelector);
|
||||
const pinElements = document.querySelectorAll(uniqueSelector);
|
||||
console.log('pinElements', pinElements);
|
||||
if (pinElements.length === 0) return null;
|
||||
if (pinElements.length === 1) {
|
||||
const rect = pinElements[0].getBoundingClientRect();
|
||||
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
|
||||
};
|
||||
}
|
||||
for (const pinElement of pinElements) {
|
||||
let parentSvg = pinElement.closest('svg.pin-component');
|
||||
if (!parentSvg) continue;
|
||||
if (parentSvg.getAttribute('data-component-id') === props.componentId) {
|
||||
const rect = pinElement.getBoundingClientRect();
|
||||
return {
|
||||
x: rect.left + rect.width / 2,
|
||||
y: rect.top + rect.height / 2
|
||||
};
|
||||
}
|
||||
}
|
||||
const rect = pinElements[0].getBoundingClientRect();
|
||||
return {
|
||||
x: rect.left + rect.width / 2,
|
||||
y: rect.top + rect.height / 2
|
||||
};
|
||||
return null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
// 添加一个静态方法来获取默认props
|
||||
export function getDefaultProps() {
|
||||
return {
|
||||
size: 1,
|
||||
label: 'PIN',
|
||||
constraint: '',
|
||||
direction: 'input',
|
||||
type: 'digital',
|
||||
pinId: 'pin-default',
|
||||
componentId: '',
|
||||
pins: [
|
||||
{
|
||||
pinId: 'PIN',
|
||||
constraint: '',
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pin-component {
|
||||
display: block;
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
<rect
|
||||
width="70"
|
||||
height="30"
|
||||
x="15"
|
||||
y="15"
|
||||
x="15"
|
||||
y="15"
|
||||
:fill="ledColor"
|
||||
:style="{ opacity: isOn ? 1 : 0.2 }"
|
||||
rx="15"
|
||||
rx="15"
|
||||
ry="15"
|
||||
class="interactive"
|
||||
/>
|
||||
@@ -39,13 +39,19 @@
|
||||
ry="18"
|
||||
filter="blur(5px)"
|
||||
class="glow"
|
||||
/>
|
||||
</svg>
|
||||
<!-- 新增:数字输入引脚Pin,放在LED左侧居中 -->
|
||||
<div style="position:absolute;left:-18px;top:50%;transform:translateY(-50%);">
|
||||
<Pin
|
||||
ref="pinRef"
|
||||
v-bind="props"
|
||||
/> </svg>
|
||||
<!-- 渲染自定义引脚数组 -->
|
||||
<div v-for="pin in props.pins" :key="pin.pinId"
|
||||
:style="{
|
||||
position: 'absolute',
|
||||
left: `${pin.x}px`,
|
||||
top: `${pin.y}px`,
|
||||
transform: 'translate(-50%, -50%)'
|
||||
}"> <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>
|
||||
@@ -57,34 +63,36 @@ import { ref, computed, watch, onMounted, onUnmounted } from 'vue';
|
||||
import { getConstraintState, onConstraintStateChange } from '../../stores/constraints';
|
||||
import Pin from './Pin.vue';
|
||||
|
||||
// --- 关键:暴露getPinPosition,代理到内部Pin ---
|
||||
const pinRef = ref<any>(null);
|
||||
|
||||
// 从Pin组件继承属性
|
||||
interface PinProps {
|
||||
label?: string;
|
||||
constraint?: string;
|
||||
componentId?: string; // 添加componentId属性
|
||||
// 这些属性被预设为固定值,但仍然包含在类型中以便完整继承
|
||||
direction?: 'input' | 'output' | 'inout';
|
||||
type?: 'digital' | 'analog';
|
||||
}
|
||||
// 存储多个Pin引用
|
||||
const pinRefs = ref<Record<string, any>>({});
|
||||
|
||||
// LED特有属性
|
||||
interface LEDProps {
|
||||
size?: number;
|
||||
color?: string;
|
||||
initialOn?: boolean;
|
||||
brightness?: number;
|
||||
pins?: {
|
||||
pinId: string;
|
||||
constraint: string;
|
||||
x: number;
|
||||
y: number;
|
||||
}[];
|
||||
}
|
||||
|
||||
// 组合两个接口
|
||||
interface Props extends PinProps, LEDProps {}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
const props = withDefaults(defineProps<LEDProps>(), {
|
||||
size: 1,
|
||||
color: 'red',
|
||||
constraint: '',
|
||||
label: 'LED',
|
||||
componentId: ''
|
||||
initialOn: false,
|
||||
brightness: 80,
|
||||
pins: () => [
|
||||
{
|
||||
pinId: 'LED',
|
||||
constraint: '',
|
||||
x: 50,
|
||||
y: 30
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const width = computed(() => 100 * props.size);
|
||||
@@ -106,18 +114,26 @@ const ledColor = computed(() => {
|
||||
return colorMap[props.color.toLowerCase()] || props.color;
|
||||
});
|
||||
|
||||
// 获取LED的constraint值
|
||||
const ledConstraint = computed(() => {
|
||||
if (props.pins && props.pins.length > 0) {
|
||||
return props.pins[0].constraint;
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
// 监听约束状态变化
|
||||
let unsubscribe: (() => void) | null = null;
|
||||
|
||||
onMounted(() => {
|
||||
if (props.constraint) {
|
||||
if (ledConstraint.value) {
|
||||
unsubscribe = onConstraintStateChange((constraint, level) => {
|
||||
if (constraint === props.constraint) {
|
||||
if (constraint === ledConstraint.value) {
|
||||
isOn.value = (level === 'high');
|
||||
}
|
||||
});
|
||||
// 初始化LED状态
|
||||
const currentState = getConstraintState(props.constraint);
|
||||
const currentState = getConstraintState(ledConstraint.value);
|
||||
isOn.value = (currentState === 'high');
|
||||
}
|
||||
});
|
||||
@@ -128,7 +144,7 @@ onUnmounted(() => {
|
||||
}
|
||||
});
|
||||
|
||||
watch(() => props.constraint, (newConstraint) => {
|
||||
watch(() => ledConstraint.value, (newConstraint) => {
|
||||
if (unsubscribe) {
|
||||
unsubscribe();
|
||||
unsubscribe = null;
|
||||
@@ -149,20 +165,49 @@ defineExpose({
|
||||
getInfo: () => ({
|
||||
color: props.color,
|
||||
isOn: isOn.value,
|
||||
constraint: props.constraint,
|
||||
componentId: props.componentId,
|
||||
constraint: ledConstraint.value,
|
||||
direction: 'input',
|
||||
type: 'digital'
|
||||
}),
|
||||
getPinPosition: (componentId: string) => {
|
||||
if (pinRef.value && pinRef.value.getPinPosition) {
|
||||
return pinRef.value.getPinPosition(componentId);
|
||||
}
|
||||
return null;
|
||||
type: 'digital',
|
||||
pins: props.pins
|
||||
}), getPinPosition: (pinId: string) => {
|
||||
// 如果是自定义的引脚ID
|
||||
if (props.pins && props.pins.length > 0) {
|
||||
console.log('Pin ID:', pinId);
|
||||
const customPin = props.pins.find(p => p.pinId === pinId);
|
||||
console.log('Custom Pin:', customPin);
|
||||
console.log('Pin Refs:', pinRefs.value[pinId]);
|
||||
if (customPin) {
|
||||
// 调用对应Pin组件的getPinPosition方法
|
||||
return {
|
||||
x: customPin.x,
|
||||
y: customPin.y
|
||||
}
|
||||
} return null;
|
||||
} return null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
// 添加一个静态方法来获取默认props
|
||||
export function getDefaultProps() {
|
||||
return {
|
||||
size: 1,
|
||||
color: 'red',
|
||||
initialOn: false,
|
||||
brightness: 80,
|
||||
pins: [
|
||||
{
|
||||
pinId: 'LED',
|
||||
constraint: '',
|
||||
x: 50,
|
||||
y: 30
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.led-container {
|
||||
display: flex;
|
||||
|
||||
@@ -33,16 +33,18 @@ interface Props {
|
||||
strokeColor?: string;
|
||||
strokeWidth?: number;
|
||||
isActive?: boolean;
|
||||
routingMode?: 'auto' | 'orthogonal' | 'direct';
|
||||
routingMode?: 'auto' | 'orthogonal' | 'direct' | 'path';
|
||||
// 针脚引用属性
|
||||
startComponentId?: string;
|
||||
startPinLabel?: string;
|
||||
startPinId?: string;
|
||||
endComponentId?: string;
|
||||
endPinLabel?: string;
|
||||
endPinId?: string;
|
||||
// 添加约束属性
|
||||
constraint?: string;
|
||||
// 显示标签
|
||||
showLabel?: boolean;
|
||||
// 路径命令 - 对应diagram.json中的线放置迷你语言
|
||||
pathCommands?: string[];
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
@@ -81,9 +83,168 @@ const labelPosition = computed(() => {
|
||||
});
|
||||
|
||||
const pathData = computed(() => {
|
||||
return calculateOrthogonalPath(props.startX, props.startY, props.endX, props.endY);
|
||||
// 如果有路径命令,使用路径布线模式
|
||||
if (props.routingMode === 'path' && props.pathCommands && props.pathCommands.length > 0) {
|
||||
return calculatePathFromCommands(
|
||||
props.startX,
|
||||
props.startY,
|
||||
props.endX,
|
||||
props.endY,
|
||||
props.pathCommands
|
||||
);
|
||||
}
|
||||
// 否则使用正交路径
|
||||
return calculateOrthogonalPath(props.startX, props.startY, props.endX, props.endY);
|
||||
});
|
||||
|
||||
function calculatePathFromCommands(
|
||||
startX: number,
|
||||
startY: number,
|
||||
endX: number,
|
||||
endY: number,
|
||||
commands: string[]
|
||||
) {
|
||||
// 找到分隔符索引,通常是 "*"
|
||||
const splitterIndex = commands.indexOf('*');
|
||||
if (splitterIndex === -1) {
|
||||
// 如果没有分隔符,回退到正交路径
|
||||
return calculateOrthogonalPath(startX, startY, endX, endY);
|
||||
}
|
||||
|
||||
// 分割命令为起点和终点两部分
|
||||
const startCommands = commands.slice(0, splitterIndex);
|
||||
const endCommands = commands.slice(splitterIndex + 1).reverse();
|
||||
|
||||
// 从起点开始生成路径
|
||||
let currentX = startX;
|
||||
let currentY = startY;
|
||||
|
||||
// 处理起点路径命令
|
||||
const pathPoints: [number, number][] = [[currentX, currentY]];
|
||||
|
||||
// 解析并执行起点命令
|
||||
for (const cmd of startCommands) {
|
||||
const { newX, newY } = executePathCommand(currentX, currentY, cmd);
|
||||
currentX = newX;
|
||||
currentY = newY;
|
||||
pathPoints.push([currentX, currentY]);
|
||||
}
|
||||
|
||||
// 从终点开始反向处理
|
||||
let endCurrentX = endX;
|
||||
let endCurrentY = endY;
|
||||
|
||||
// 保存终点路径点,最后会反转
|
||||
const endPathPoints: [number, number][] = [[endCurrentX, endCurrentY]];
|
||||
|
||||
// 解析并执行终点命令(反向)
|
||||
for (const cmd of endCommands) {
|
||||
const { newX, newY } = executePathCommand(endCurrentX, endCurrentY, cmd);
|
||||
endCurrentX = newX;
|
||||
endCurrentY = newY;
|
||||
endPathPoints.push([endCurrentX, endCurrentY]);
|
||||
}
|
||||
|
||||
// 反转终点路径点并去掉第一个(终点)
|
||||
const reversedEndPoints = endPathPoints.slice(1).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);
|
||||
}
|
||||
}
|
||||
|
||||
// 生成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]}`;
|
||||
}
|
||||
|
||||
return pathStr;
|
||||
}
|
||||
|
||||
// 执行单个路径命令
|
||||
function executePathCommand(x: number, y: number, command: string): { newX: number; newY: number } {
|
||||
// 解析命令,例如 "down10", "right20", "downright5" 等
|
||||
if (command.startsWith('right')) {
|
||||
const distance = parseInt(command.substring(5), 10) || 10;
|
||||
return { newX: x + distance, newY: y };
|
||||
} else if (command.startsWith('left')) {
|
||||
const distance = parseInt(command.substring(4), 10) || 10;
|
||||
return { 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 };
|
||||
} else if (command.startsWith('downleft')) {
|
||||
const distance = parseInt(command.substring(8), 10) || 10;
|
||||
return { newX: x - distance, newY: y + distance };
|
||||
} else {
|
||||
const distance = parseInt(command.substring(4), 10) || 10;
|
||||
return { 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 };
|
||||
} else if (command.startsWith('upleft')) {
|
||||
const distance = parseInt(command.substring(6), 10) || 10;
|
||||
return { newX: x - distance, newY: y - distance };
|
||||
} else {
|
||||
const distance = parseInt(command.substring(2), 10) || 10;
|
||||
return { newX: x, newY: y - distance };
|
||||
}
|
||||
}
|
||||
|
||||
// 默认情况下不移动
|
||||
return { newX: x, newY: y };
|
||||
}
|
||||
|
||||
// 生成两点之间的正交连接点
|
||||
function generateOrthogonalConnection(x1: number, y1: number, x2: number, y2: number): [number, number][] {
|
||||
const dx = x2 - x1;
|
||||
const dy = y2 - y1;
|
||||
|
||||
if (dx === 0 || dy === 0) {
|
||||
// 如果在同一水平或垂直线上,不需要额外点
|
||||
return [];
|
||||
}
|
||||
|
||||
// 选择先水平移动还是先垂直移动
|
||||
const middlePoints: [number, number][] = [];
|
||||
|
||||
if (Math.abs(dx) > Math.abs(dy)) {
|
||||
// 先水平后垂直
|
||||
middlePoints.push([x1 + dx / 2, y1]);
|
||||
middlePoints.push([x1 + dx / 2, y2]);
|
||||
} else {
|
||||
// 先垂直后水平
|
||||
middlePoints.push([x1, y1 + dy / 2]);
|
||||
middlePoints.push([x2, y1 + dy / 2]);
|
||||
}
|
||||
|
||||
return middlePoints;
|
||||
}
|
||||
|
||||
// 计算正交路径
|
||||
function calculateOrthogonalPath(startX: number, startY: number, endX: number, endY: number) {
|
||||
// 计算两点之间的水平和垂直距离
|
||||
const dx = endX - startX;
|
||||
@@ -145,13 +306,12 @@ watch(() => props.constraint, (newConstraint, oldConstraint) => {
|
||||
});
|
||||
|
||||
// 暴露方法,用于获取这条连线的信息
|
||||
defineExpose({ id: props.id,
|
||||
getInfo: () => ({
|
||||
defineExpose({ id: props.id, getInfo: () => ({
|
||||
id: props.id,
|
||||
startComponentId: props.startComponentId,
|
||||
startPinLabel: props.startPinLabel,
|
||||
startPinId: props.startPinId,
|
||||
endComponentId: props.endComponentId,
|
||||
endPinLabel: props.endPinLabel,
|
||||
endPinId: props.endPinId,
|
||||
constraint: props.constraint
|
||||
}),
|
||||
// 更新连线位置
|
||||
|
||||
@@ -1,363 +0,0 @@
|
||||
// 组件配置声明
|
||||
export type PropType = 'string' | 'number' | 'boolean' | 'select';
|
||||
|
||||
// 定义选择类型选项
|
||||
export interface PropOption {
|
||||
value: string | number | boolean;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface PropConfig {
|
||||
name: string;
|
||||
type: string;
|
||||
label: string;
|
||||
default: any;
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
options?: PropOption[];
|
||||
description?: string;
|
||||
category?: string; // 用于在UI中分组属性
|
||||
}
|
||||
|
||||
export interface ComponentConfig {
|
||||
props: PropConfig[];
|
||||
}
|
||||
|
||||
// 存储所有组件的配置
|
||||
const componentConfigs: Record<string, ComponentConfig> = {
|
||||
MechanicalButton: {
|
||||
props: [
|
||||
{
|
||||
name: 'bindKey',
|
||||
type: 'string',
|
||||
label: '绑定按键',
|
||||
default: '',
|
||||
description: '触发按钮按下的键盘按键'
|
||||
},
|
||||
{
|
||||
name: 'size',
|
||||
type: 'number',
|
||||
label: '大小',
|
||||
default: 1,
|
||||
min: 0.5,
|
||||
max: 3,
|
||||
step: 0.1,
|
||||
description: '按钮的相对大小,1代表标准大小'
|
||||
},
|
||||
{
|
||||
name: 'buttonText',
|
||||
type: 'string',
|
||||
label: '按钮文本',
|
||||
default: '',
|
||||
description: '按钮上显示的自定义文本,优先级高于绑定按键'
|
||||
},
|
||||
{
|
||||
name: 'label',
|
||||
type: 'string',
|
||||
label: '引脚标签',
|
||||
default: 'BTN',
|
||||
description: '引脚的标签文本'
|
||||
},
|
||||
{
|
||||
name: 'constraint',
|
||||
type: 'string',
|
||||
label: '引脚约束',
|
||||
default: '',
|
||||
description: '相同约束字符串的引脚将被视为有电气连接'
|
||||
}
|
||||
]
|
||||
},
|
||||
Switch: {
|
||||
props: [
|
||||
{
|
||||
name: 'size',
|
||||
type: 'number',
|
||||
label: '大小',
|
||||
default: 1,
|
||||
min: 0.5,
|
||||
max: 3,
|
||||
step: 0.1,
|
||||
description: '开关的相对大小,1代表标准大小'
|
||||
},
|
||||
{
|
||||
name: 'switchCount',
|
||||
type: 'number',
|
||||
label: '开关数量',
|
||||
default: 6,
|
||||
min: 1,
|
||||
max: 12,
|
||||
step: 1,
|
||||
description: '可翻转开关的数量'
|
||||
},
|
||||
{
|
||||
name: 'showLabels',
|
||||
type: 'boolean',
|
||||
label: '显示标签',
|
||||
default: true,
|
||||
description: '是否显示开关编号标签'
|
||||
},
|
||||
{
|
||||
name: 'initialValues',
|
||||
type: 'string',
|
||||
label: '初始状态',
|
||||
default: '',
|
||||
description: '开关的初始状态,格式为逗号分隔的0/1,如"1,0,1"表示第1、3个开关打开'
|
||||
}
|
||||
]
|
||||
},
|
||||
Pin: {
|
||||
props: [
|
||||
{
|
||||
name: 'size',
|
||||
type: 'number',
|
||||
label: '大小',
|
||||
default: 1,
|
||||
min: 0.5,
|
||||
max: 3,
|
||||
step: 0.1,
|
||||
description: '引脚的相对大小,1代表标准大小'
|
||||
},
|
||||
{
|
||||
name: 'label',
|
||||
type: 'string',
|
||||
label: '引脚标签',
|
||||
default: 'PIN',
|
||||
description: '用于标识引脚的名称'
|
||||
},
|
||||
{
|
||||
name: 'constraint',
|
||||
type: 'string',
|
||||
label: '引脚约束',
|
||||
default: '',
|
||||
description: '相同约束字符串的引脚将被视为有电气连接'
|
||||
},
|
||||
{
|
||||
name: 'direction',
|
||||
type: 'select',
|
||||
label: '输入/输出特性',
|
||||
default: 'input',
|
||||
options: [
|
||||
{ value: 'input', label: '输入' },
|
||||
{ value: 'output', label: '输出' },
|
||||
{ value: 'inout', label: '双向' }
|
||||
],
|
||||
description: '引脚的输入/输出特性'
|
||||
},
|
||||
{
|
||||
name: 'type',
|
||||
type: 'select',
|
||||
label: '模数特性',
|
||||
default: 'digital',
|
||||
options: [
|
||||
{ value: 'digital', label: 'digital' },
|
||||
{ value: 'analog', label: 'analog' }
|
||||
],
|
||||
description: '引脚的模数特性,数字或模拟'
|
||||
}
|
||||
]
|
||||
},
|
||||
HDMI: {
|
||||
props: [
|
||||
{
|
||||
name: 'size',
|
||||
type: 'number',
|
||||
label: '大小',
|
||||
default: 1,
|
||||
min: 0.5,
|
||||
max: 3,
|
||||
step: 0.1,
|
||||
description: 'HDMI接口的相对大小,1代表标准大小'
|
||||
}
|
||||
]
|
||||
},
|
||||
DDR: {
|
||||
props: [
|
||||
{
|
||||
name: 'size',
|
||||
type: 'number',
|
||||
label: '大小',
|
||||
default: 1,
|
||||
min: 0.5,
|
||||
max: 3,
|
||||
step: 0.1,
|
||||
description: 'DDR内存的相对大小,1代表标准大小'
|
||||
}
|
||||
]
|
||||
},
|
||||
ETH: {
|
||||
props: [
|
||||
{
|
||||
name: 'size',
|
||||
type: 'number',
|
||||
label: '大小',
|
||||
default: 1,
|
||||
min: 0.5,
|
||||
max: 3,
|
||||
step: 0.1,
|
||||
description: '以太网接口的相对大小,1代表标准大小'
|
||||
}
|
||||
]
|
||||
},
|
||||
SD: {
|
||||
props: [
|
||||
{
|
||||
name: 'size',
|
||||
type: 'number',
|
||||
label: '大小',
|
||||
default: 1,
|
||||
min: 0.5,
|
||||
max: 3,
|
||||
step: 0.1,
|
||||
description: 'SD卡插槽的相对大小,1代表标准大小'
|
||||
}
|
||||
]
|
||||
},
|
||||
SFP: {
|
||||
props: [
|
||||
{
|
||||
name: 'size',
|
||||
type: 'number',
|
||||
label: '大小',
|
||||
default: 1,
|
||||
min: 0.5,
|
||||
max: 3,
|
||||
step: 0.1,
|
||||
description: 'SFP光纤模块的相对大小,1代表标准大小'
|
||||
}
|
||||
]
|
||||
},
|
||||
SMA: {
|
||||
props: [
|
||||
{
|
||||
name: 'size',
|
||||
type: 'number',
|
||||
label: '大小',
|
||||
default: 1,
|
||||
min: 0.5,
|
||||
max: 3,
|
||||
step: 0.1,
|
||||
description: 'SMA连接器的相对大小,1代表标准大小'
|
||||
}
|
||||
]
|
||||
}, MotherBoard: {
|
||||
props: [
|
||||
{
|
||||
name: 'size',
|
||||
type: 'number',
|
||||
label: '大小',
|
||||
default: 1,
|
||||
min: 0.5,
|
||||
max: 2,
|
||||
step: 0.1,
|
||||
description: '主板的相对大小,1代表标准大小'
|
||||
}
|
||||
]
|
||||
}, SMT_LED: {
|
||||
props: [
|
||||
{
|
||||
name: 'size',
|
||||
type: 'number',
|
||||
label: '大小',
|
||||
default: 1,
|
||||
min: 0.5,
|
||||
max: 3,
|
||||
step: 0.1,
|
||||
description: 'LED的相对大小,1代表标准大小'
|
||||
},
|
||||
{
|
||||
name: 'color',
|
||||
type: 'select',
|
||||
label: '颜色',
|
||||
default: 'red',
|
||||
options: [
|
||||
{ value: 'red', label: '红色' },
|
||||
{ value: 'green', label: '绿色' },
|
||||
{ value: 'blue', label: '蓝色' },
|
||||
{ value: 'yellow', label: '黄色' },
|
||||
{ value: 'orange', label: '橙色' },
|
||||
{ value: 'white', label: '白色' },
|
||||
{ value: 'purple', label: '紫色' }
|
||||
],
|
||||
description: 'LED的颜色'
|
||||
},
|
||||
{
|
||||
name: 'initialOn',
|
||||
type: 'boolean',
|
||||
label: '初始状态',
|
||||
default: false,
|
||||
description: 'LED的初始开关状态'
|
||||
},
|
||||
{
|
||||
name: 'brightness',
|
||||
type: 'number',
|
||||
label: '亮度(%)',
|
||||
default: 80,
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 5,
|
||||
description: 'LED的亮度百分比,范围0-100'
|
||||
},
|
||||
{
|
||||
name: 'constraint',
|
||||
type: 'string',
|
||||
label: '引脚约束',
|
||||
default: '',
|
||||
description: '相同约束字符串的引脚将被视为有电气连接'
|
||||
}
|
||||
]
|
||||
},
|
||||
// 线缆配置
|
||||
Wire: {
|
||||
props: [
|
||||
{
|
||||
name: 'routingMode',
|
||||
type: 'select',
|
||||
label: '路由方式',
|
||||
default: 'orthogonal',
|
||||
options: [
|
||||
{ value: 'orthogonal', label: '直角' },
|
||||
{ value: 'direct', label: '直线' },
|
||||
{ value: 'auto', label: '自动' }
|
||||
],
|
||||
description: '线路连接方式'
|
||||
},
|
||||
{
|
||||
name: 'strokeColor',
|
||||
type: 'string',
|
||||
label: '线条颜色',
|
||||
default: '#4a5568',
|
||||
description: '线条颜色,使用CSS颜色值'
|
||||
},
|
||||
{
|
||||
name: 'strokeWidth',
|
||||
type: 'number',
|
||||
label: '线条宽度',
|
||||
default: 2,
|
||||
min: 1,
|
||||
max: 10,
|
||||
step: 0.5,
|
||||
description: '线条宽度'
|
||||
},
|
||||
{
|
||||
name: 'constraint',
|
||||
type: 'string',
|
||||
label: '约束名称',
|
||||
default: '',
|
||||
description: '线路约束名称,用于标识连接关系'
|
||||
},
|
||||
{
|
||||
name: 'showLabel',
|
||||
type: 'boolean',
|
||||
label: '显示标签',
|
||||
default: false,
|
||||
description: '是否显示连线上的约束标签'
|
||||
}
|
||||
]
|
||||
},
|
||||
};
|
||||
|
||||
// 获取组件配置的函数
|
||||
export function getComponentConfig(type: string): ComponentConfig | null {
|
||||
return componentConfigs[type] || null;
|
||||
}
|
||||
Reference in New Issue
Block a user