// 定义 diagram.json 的类型结构 export interface DiagramData { version: number; author: string; editor: string; parts: DiagramPart[]; connections: ConnectionArray[]; exportTime?: string; // 导出时的时间戳 } // 组件部分的类型定义 export interface DiagramPart { id: string; type: string; x: number; y: number; attrs: Record; rotate: number; group: string; positionlock: boolean; hidepins: boolean; isOn: boolean; index?: number; // 显示层级,数值越大显示越靠前 } // 连接类型定义 - 使用元组类型表示四元素数组 export type ConnectionArray = [string, string, number, string[]]; // 解析连接字符串为组件ID和引脚ID export function parseConnectionPin(connectionPin: string): { componentId: string; pinId: string } { const [componentId, pinId] = connectionPin.split(':'); return { componentId, pinId }; } // 将连接数组转换为适用于渲染的格式 export function connectionArrayToWireItem( connection: ConnectionArray, index: number, startPos = { x: 0, y: 0 }, endPos = { x: 0, y: 0 } ): WireItem { const [startPinStr, endPinStr, width, path] = connection; const { componentId: startComponentId, pinId: startPinId } = parseConnectionPin(startPinStr); const { componentId: endComponentId, pinId: endPinId } = parseConnectionPin(endPinStr); return { id: `wire-${index}`, startX: startPos.x, startY: startPos.y, endX: endPos.x, endY: endPos.y, startComponentId, startPinId, endComponentId, endPinId, strokeWidth: width, color: '#4a5568', // 默认颜色 routingMode: 'path', pathCommands: path, showLabel: false }; } // WireItem 接口定义 export interface WireItem { id: string; startX: number; startY: number; endX: number; endY: number; startComponentId: string; startPinId?: string; endComponentId: string; endPinId?: string; strokeWidth: number; color: string; routingMode: 'orthogonal' | 'path'; constraint?: string; pathCommands?: string[]; showLabel: boolean; } // 从本地存储加载图表数据 export async function loadDiagramData(): Promise { try { // 先尝试从本地存储加载 const savedData = localStorage.getItem('diagramData'); if (savedData) { return JSON.parse(savedData); } // 如果本地存储没有,从文件加载 const response = await fetch('/src/components/diagram.json'); if (!response.ok) { throw new Error(`Failed to load diagram.json: ${response.statusText}`); } const data = await response.json(); return data; } catch (error) { console.error('Error loading diagram data:', error); // 返回空的默认数据结构 return createEmptyDiagram(); } } // 创建空的图表数据 export function createEmptyDiagram(): DiagramData { return { version: 1, author: 'user', editor: 'user', parts: [], connections: [] }; } // 保存图表数据到本地存储 export function saveDiagramData(data: DiagramData): void { try { localStorage.setItem('diagramData', JSON.stringify(data)); } catch (error) { console.error('Error saving diagram data:', error); } } // 添加新组件到图表数据 export function addPart(data: DiagramData, part: DiagramPart): DiagramData { return { ...data, parts: [...data.parts, part] }; } // 更新组件位置 export function updatePartPosition( data: DiagramData, partId: string, x: number, y: number ): DiagramData { return { ...data, parts: data.parts.map(part => part.id === partId ? { ...part, x, y } : part ) }; } // 更新组件属性 export function updatePartAttribute( data: DiagramData, partId: string, attrName: string, value: any ): DiagramData { return { ...data, parts: data.parts.map(part => part.id === partId ? { ...part, attrs: { ...part.attrs, [attrName]: value } } : part ) }; } // 删除组件及同组组件 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 => !componentsToDelete.includes(part.id)), // 删除与这些组件相关的所有连接 connections: data.connections.filter(conn => { const [startPin, endPin] = conn; const startCompId = startPin.split(':')[0]; const endCompId = endPin.split(':')[0]; // 检查连接两端的组件是否在删除列表中 return !componentsToDelete.includes(startCompId) && !componentsToDelete.includes(endCompId); }) }; } // 添加连接 export function addConnection( data: DiagramData, startComponentId: string, startPinId: string, endComponentId: string, endPinId: string, width: number = 2, path: string[] = [] ): DiagramData { const newConnection: ConnectionArray = [ `${startComponentId}:${startPinId}`, `${endComponentId}:${endPinId}`, width, path ]; return { ...data, connections: [...data.connections, newConnection] }; } // 删除连接 export function deleteConnection( data: DiagramData, connectionIndex: number ): DiagramData { return { ...data, connections: data.connections.filter((_, index) => index !== connectionIndex) }; } // 查找与组件关联的所有连接 export function findConnectionsByPart( data: DiagramData, partId: string ): { connection: ConnectionArray; index: number }[] { return data.connections .map((connection, index) => ({ connection, index })) .filter(({ connection }) => { const [startPin, endPin] = connection; const startCompId = startPin.split(':')[0]; const endCompId = endPin.split(':')[0]; return startCompId === partId || endCompId === partId; }); } // 基于组的移动相关组件 export function moveGroupComponents( data: DiagramData, groupId: string, deltaX: number, deltaY: number ): DiagramData { if (!groupId) return data; return { ...data, parts: data.parts.map(part => part.group === groupId ? { ...part, x: part.x + deltaX, y: part.y + deltaY } : part ) }; } // 添加验证diagram.json文件的函数 export function validateDiagramData(data: any): { isValid: boolean; errors: string[] } { const errors: string[] = []; // 检查版本号 if (!data.version) { errors.push('缺少version字段'); } // 检查parts数组 if (!Array.isArray(data.parts)) { errors.push('parts字段不是数组'); } else { // 验证parts中的每个对象 data.parts.forEach((part: any, index: number) => { if (!part.id) errors.push(`parts[${index}]缺少id`); if (!part.type) errors.push(`parts[${index}]缺少type`); if (typeof part.x !== 'number') errors.push(`parts[${index}]缺少有效的x坐标`); if (typeof part.y !== 'number') errors.push(`parts[${index}]缺少有效的y坐标`); }); } // 检查connections数组 if (!Array.isArray(data.connections)) { errors.push('connections字段不是数组'); } else { // 验证connections中的每个数组 data.connections.forEach((conn: any, index: number) => { if (!Array.isArray(conn) || conn.length < 3) { errors.push(`connections[${index}]不是有效的连接数组`); return; } const [startPin, endPin, width] = conn; if (typeof startPin !== 'string' || !startPin.includes(':')) { errors.push(`connections[${index}]的起始针脚格式无效`); } if (typeof endPin !== 'string' || !endPin.includes(':')) { errors.push(`connections[${index}]的结束针脚格式无效`); } if (typeof width !== 'number') { errors.push(`connections[${index}]的宽度不是有效的数字`); } }); } return { isValid: errors.length === 0, errors }; }