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:
alivender
2025-05-07 15:42:35 +08:00
parent 1c75aa621a
commit 47cfe17d16
10 changed files with 1457 additions and 890 deletions

View File

@@ -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
}),
// 更新连线位置