import { ref, shallowRef, computed, reactive } from "vue"; import { createInjectionState } from "@vueuse/core"; import { saveDiagramData, type DiagramData, type DiagramPart, } from "./diagramManager"; import type { PropertyConfig } from "@/components/equipments/componentConfig"; import { generatePropertyConfigs, generatePropsFromDefault, generatePropsFromAttrs, } from "@/components/equipments/componentConfig"; // 存储动态导入的组件模块 interface ComponentModule { default: any; getDefaultProps?: () => Record; config?: { props?: Array; }; __esModule?: boolean; // 添加 __esModule 属性 } // 定义组件管理器的状态和方法 const [useProvideComponentManager, useComponentManager] = createInjectionState( () => { // --- 状态管理 --- const componentModules = ref>({}); const selectedComponentId = ref(null); const selectedComponentConfig = shallowRef<{ props: PropertyConfig[]; } | null>(null); const diagramCanvas = ref(null); const componentRefs = ref>({}); // 新增:直接管理canvas的位置和缩放 const canvasPosition = reactive({ x: 0, y: 0 }); const canvasScale = ref(1); // 计算当前选中的组件数据 const selectedComponentData = computed(() => { if (!diagramCanvas.value || !selectedComponentId.value) return null; const canvasInstance = diagramCanvas.value as any; if (canvasInstance && canvasInstance.getDiagramData) { const data = canvasInstance.getDiagramData(); return ( data.parts.find( (p: DiagramPart) => p.id === selectedComponentId.value, ) || null ); } return null; }); // --- Canvas 控制方法 --- /** * 设置canvas位置 */ function setCanvasPosition(x: number, y: number) { canvasPosition.x = x; canvasPosition.y = y; } /** * 更新canvas位置(相对偏移) */ function updateCanvasPosition(deltaX: number, deltaY: number) { canvasPosition.x += deltaX; canvasPosition.y += deltaY; } /** * 设置canvas缩放 */ function setCanvasScale(scale: number) { canvasScale.value = Math.max(0.2, Math.min(scale, 10.0)); } /** * 获取canvas位置 */ function getCanvasPosition() { return { x: canvasPosition.x, y: canvasPosition.y }; } /** * 获取canvas缩放 */ function getCanvasScale() { return canvasScale.value; } /** * 缩放到指定位置(以鼠标位置为中心) */ function zoomAtPosition( mouseX: number, mouseY: number, zoomFactor: number, ) { // 计算鼠标在画布坐标系中的位置 const mouseXCanvas = (mouseX - canvasPosition.x) / canvasScale.value; const mouseYCanvas = (mouseY - canvasPosition.y) / canvasScale.value; // 计算新的缩放值 const newScale = Math.max( 0.2, Math.min(canvasScale.value * zoomFactor, 10.0), ); // 计算新的位置,使鼠标指针位置在缩放前后保持不变 canvasPosition.x = mouseX - mouseXCanvas * newScale; canvasPosition.y = mouseY - mouseYCanvas * newScale; canvasScale.value = newScale; return { scale: newScale, position: { x: canvasPosition.x, y: canvasPosition.y }, }; } /** * 将屏幕坐标转换为画布坐标 */ function screenToCanvas(screenX: number, screenY: number) { return { x: (screenX - canvasPosition.x) / canvasScale.value, y: (screenY - canvasPosition.y) / canvasScale.value, }; } /** * 将画布坐标转换为屏幕坐标 */ function canvasToScreen(canvasX: number, canvasY: number) { return { x: canvasX * canvasScale.value + canvasPosition.x, y: canvasY * canvasScale.value + canvasPosition.y, }; } /** * 居中显示指定区域 */ function centerView( bounds: { x: number; y: number; width: number; height: number }, containerWidth: number, containerHeight: number, ) { // 计算合适的缩放比例 const scaleX = containerWidth / bounds.width; const scaleY = containerHeight / bounds.height; const newScale = Math.min(scaleX, scaleY, 1) * 0.8; // 留一些边距 // 计算居中位置 const centerX = bounds.x + bounds.width / 2; const centerY = bounds.y + bounds.height / 2; canvasScale.value = newScale; canvasPosition.x = containerWidth / 2 - centerX * newScale; canvasPosition.y = containerHeight / 2 - centerY * newScale; return { scale: newScale, position: { x: canvasPosition.x, y: canvasPosition.y }, }; } // --- 组件模块管理 --- /** * 动态加载组件模块 */ async function loadComponentModule(type: string) { console.log(`尝试加载组件模块: ${type}`); console.log(`当前已加载的模块:`, Object.keys(componentModules.value)); if (!componentModules.value[type]) { try { console.log(`正在动态导入模块: @/components/equipments/${type}.vue`); const module = await import(`@/components/equipments/${type}.vue`); console.log(`成功导入模块 ${type}:`, module); // 直接设置新的对象引用以触发响应性 componentModules.value = { ...componentModules.value, [type]: module, }; console.log(`模块 ${type} 已添加到 componentModules`); console.log(`更新后的模块列表:`, Object.keys(componentModules.value)); } catch (error) { console.error(`Failed to load component module ${type}:`, error); return null; } } else { console.log(`模块 ${type} 已经存在`); } return componentModules.value[type]; } /** * 预加载所有组件模块 */ async function preloadComponentModules(componentTypes: string[]) { console.log("Preloading component modules:", componentTypes); await Promise.all( componentTypes.map((type) => loadComponentModule(type)), ); console.log("All component modules loaded"); } // --- 组件操作 --- /** * 添加新组件到画布 */ async function addComponent(componentData: { type: string; name: string; props: Record; }) { console.log("=== 开始添加组件 ==="); console.log("组件数据:", componentData); const canvasInstance = diagramCanvas.value as any; if (!canvasInstance) { console.error("没有可用的画布实例"); return; } // 预加载组件模块,确保组件能正常渲染 console.log(`预加载组件模块: ${componentData.type}`); const componentModule = await loadComponentModule(componentData.type); if (!componentModule) { console.error(`无法加载组件模块: ${componentData.type}`); return; } console.log(`组件模块加载成功: ${componentData.type}`, componentModule); // 使用内部管理的位置和缩放信息 let position = { x: 100, y: 100 }; try { const canvasContainer = canvasInstance.$el as HTMLElement; if (canvasContainer) { const viewportWidth = canvasContainer.clientWidth; const viewportHeight = canvasContainer.clientHeight; position.x = (viewportWidth / 2 - canvasPosition.x) / canvasScale.value; position.y = (viewportHeight / 2 - canvasPosition.y) / canvasScale.value; } } catch (error) { console.error("获取画布位置时出错:", error); } // 添加随机偏移 const offsetX = Math.floor(Math.random() * 100) - 50; const offsetY = Math.floor(Math.random() * 100) - 50; // 获取组件能力页面 let capsPage = null; if ( componentModule && componentModule.default && typeof componentModule.default.getCapabilities === "function" ) { try { capsPage = componentModule.default.getCapabilities(); console.log(`获取到${componentData.type}组件的能力页面`); } catch (error) { console.error(`获取${componentData.type}组件能力页面失败:`, error); } } // 创建新组件 const newComponent: DiagramPart = { id: `component-${Date.now()}`, type: componentData.type, x: Math.round(position.x + offsetX), y: Math.round(position.y + offsetY), attrs: componentData.props, rotate: 0, group: "", positionlock: false, hidepins: true, isOn: true, index: 0, }; // 通过画布实例添加组件 if ( canvasInstance.getDiagramData && canvasInstance.updateDiagramDataDirectly ) { const currentData = canvasInstance.getDiagramData(); currentData.parts.push(newComponent); // 使用 updateDiagramDataDirectly 避免触发加载状态 canvasInstance.updateDiagramDataDirectly(currentData); saveDiagramData(currentData); console.log("组件添加完成:", newComponent); // 等待Vue的下一个tick,确保组件模块已经更新 await new Promise((resolve) => setTimeout(resolve, 50)); } } /** * 添加模板到画布 */ async function addTemplate(templateData: { id: string; name: string; template: any; }) { console.log("添加模板:", templateData); const canvasInstance = diagramCanvas.value as any; if ( !canvasInstance?.getDiagramData || !canvasInstance?.updateDiagramDataDirectly ) { console.error("没有可用的画布实例添加模板"); return; } const currentData = canvasInstance.getDiagramData(); console.log("=== 当前图表组件数量:", currentData.parts.length); // 生成唯一ID前缀 const idPrefix = `template-${Date.now()}-`; if (templateData.template?.parts) { // 使用内部管理的位置和缩放信息获取视口中心位置 let viewportCenter = { x: 300, y: 200 }; try { const canvasContainer = canvasInstance.$el as HTMLElement; if (canvasContainer) { const viewportWidth = canvasContainer.clientWidth; const viewportHeight = canvasContainer.clientHeight; viewportCenter.x = (viewportWidth / 2 - canvasPosition.x) / canvasScale.value; viewportCenter.y = (viewportHeight / 2 - canvasPosition.y) / canvasScale.value; } } catch (error) { console.error("获取视口中心位置时出错:", error); } const mainPart = templateData.template.parts[0]; // 创建新组件 const newParts = await Promise.all( templateData.template.parts.map(async (part: any) => { const newPart = JSON.parse(JSON.stringify(part)); newPart.id = `${idPrefix}${part.id}`; // 加载组件模块并获取能力页面 try { const componentModule = await loadComponentModule(part.type); if ( componentModule?.default && typeof componentModule.default.getCapabilities === "function" ) { newPart.capsPage = componentModule.default.getCapabilities(); console.log(`加载模板组件${part.type}组件的能力页面成功`); } } catch (error) { console.error(`加载模板组件${part.type}的能力页面失败:`, error); } // 计算新位置 if ( typeof newPart.x === "number" && typeof newPart.y === "number" ) { const relativeX = part.x - mainPart.x; const relativeY = part.y - mainPart.y; newPart.x = viewportCenter.x + relativeX; newPart.y = viewportCenter.y + relativeY; } return newPart; }), ); currentData.parts.push(...newParts); // 处理连接关系 if (templateData.template.connections) { const idMap: Record = {}; templateData.template.parts.forEach((part: any) => { idMap[part.id] = `${idPrefix}${part.id}`; }); const newConnections = templateData.template.connections.map( (conn: any) => { if (Array.isArray(conn)) { const [from, to, type, path] = conn; 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]; const newFrom = `${idMap[fromComponentId] || fromComponentId}:${fromPinId}`; const newTo = `${idMap[toComponentId] || toComponentId}:${toPinId}`; return [newFrom, newTo, type, path]; } } return conn; }, ); currentData.connections.push(...newConnections); } canvasInstance.updateDiagramDataDirectly(currentData); console.log( "=== 更新图表数据完成,新组件数量:", currentData.parts.length, ); saveDiagramData(currentData); return { success: true, message: `已添加 ${templateData.name} 模板` }; } else { console.error("模板格式错误,缺少parts数组"); return { success: false, message: "模板格式错误" }; } } /** * 删除组件 */ function deleteComponent(componentId: string) { const canvasInstance = diagramCanvas.value as any; if ( !canvasInstance?.getDiagramData || !canvasInstance?.updateDiagramDataDirectly ) { return; } const currentData = canvasInstance.getDiagramData(); const component = currentData.parts.find( (p: DiagramPart) => p.id === componentId, ); if (!component) return; const componentsToDelete: string[] = [componentId]; // 处理组件组 if (component.group && component.group !== "") { const groupMembers = currentData.parts.filter( (p: DiagramPart) => p.group === component.group && p.id !== componentId, ); componentsToDelete.push(...groupMembers.map((p: DiagramPart) => p.id)); console.log( `删除组件 ${componentId} 及其组 ${component.group} 中的 ${groupMembers.length} 个组件`, ); } // 删除组件 currentData.parts = currentData.parts.filter( (p: DiagramPart) => !componentsToDelete.includes(p.id), ); // 删除相关连接 currentData.connections = currentData.connections.filter( (connection: any) => { 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; } canvasInstance.updateDiagramDataDirectly(currentData); saveDiagramData(currentData); } /** * 选中组件 */ async function selectComponent(componentData: DiagramPart | null) { selectedComponentId.value = componentData ? componentData.id : null; selectedComponentConfig.value = null; if (componentData) { const moduleRef = await loadComponentModule(componentData.type); if (moduleRef) { try { const propConfigs: PropertyConfig[] = []; const addedProps = new Set(); // 从 getDefaultProps 方法获取默认配置 if (typeof moduleRef.getDefaultProps === "function") { const defaultProps = moduleRef.getDefaultProps(); const defaultPropConfigs = generatePropsFromDefault(defaultProps); defaultPropConfigs.forEach((config) => { propConfigs.push(config); addedProps.add(config.name); }); } // 添加组件直接属性 const directPropConfigs = generatePropertyConfigs(componentData); const newDirectProps = directPropConfigs.filter( (config) => !addedProps.has(config.name), ); propConfigs.push(...newDirectProps); // 添加 attrs 中的属性 if (componentData.attrs) { const attrPropConfigs = generatePropsFromAttrs( componentData.attrs, ); attrPropConfigs.forEach((attrConfig) => { const existingIndex = propConfigs.findIndex( (p) => p.name === attrConfig.name, ); if (existingIndex >= 0) { propConfigs[existingIndex] = attrConfig; } else { propConfigs.push(attrConfig); } }); } selectedComponentConfig.value = { props: propConfigs }; console.log( `Built config for ${componentData.type}:`, selectedComponentConfig.value, ); } catch (error) { console.error( `Error building config for ${componentData.type}:`, error, ); selectedComponentConfig.value = { props: [] }; } } else { console.warn(`Module for component ${componentData.type} not found.`); selectedComponentConfig.value = { props: [] }; } } } /** * 更新组件属性 */ function updateComponentProp( componentId: string, propName: string, value: any, ) { const canvasInstance = diagramCanvas.value as any; if ( !canvasInstance?.getDiagramData || !canvasInstance?.updateDiagramDataDirectly ) { console.error("没有可用的画布实例进行属性更新"); return; } // 检查值格式 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 (propName in part) { (part as any)[propName] = value; } else { if (!part.attrs) { part.attrs = {}; } part.attrs[propName] = value; } canvasInstance.updateDiagramDataDirectly(currentData); console.log( `更新组件${componentId}的属性${propName}为:`, value, typeof value, ); } } /** * 更新组件直接属性 */ function updateComponentDirectProp( componentId: string, propName: string, value: any, ) { const canvasInstance = diagramCanvas.value as any; if ( !canvasInstance?.getDiagramData || !canvasInstance?.updateDiagramDataDirectly ) { console.error("没有可用的画布实例进行属性更新"); return; } const currentData = canvasInstance.getDiagramData(); const part = currentData.parts.find( (p: DiagramPart) => p.id === componentId, ); if (part) { (part as any)[propName] = value; canvasInstance.updateDiagramDataDirectly(currentData); console.log( `更新组件${componentId}的直接属性${propName}为:`, value, typeof value, ); } } /** * 移动组件 */ function moveComponent(moveData: { id: string; x: number; y: number }) { const canvasInstance = diagramCanvas.value as any; if ( !canvasInstance?.getDiagramData || !canvasInstance?.updateDiagramDataDirectly ) { return; } const currentData = canvasInstance.getDiagramData(); const part = currentData.parts.find( (p: DiagramPart) => p.id === moveData.id, ); if (part) { part.x = moveData.x; part.y = moveData.y; canvasInstance.updateDiagramDataDirectly(currentData); } } /** * 设置画布实例引用 */ function setCanvasRef(canvasRef: any) { diagramCanvas.value = canvasRef; } /** * 设置组件DOM引用 */ function setComponentRef(componentId: string, el: any) { if (el) { componentRefs.value[componentId] = el; } else { delete componentRefs.value[componentId]; } } /** * 获取组件DOM引用 */ function getComponentRef(componentId: string) { return componentRefs.value[componentId]; } /** * 获取当前图表数据 */ function getDiagramData() { const canvasInstance = diagramCanvas.value; if (canvasInstance && canvasInstance.getDiagramData) { return canvasInstance.getDiagramData(); } return { parts: [], connections: [], version: 1, author: "admin", editor: "me", }; } /** * 更新图表数据 */ function updateDiagramData(data: any) { const canvasInstance = diagramCanvas.value; if (canvasInstance && canvasInstance.updateDiagramDataDirectly) { canvasInstance.updateDiagramDataDirectly(data); } } /** * 获取组件定义 */ function getComponentDefinition(type: string) { const module = componentModules.value[type]; if (!module) { console.warn(`No module found for component type: ${type}`); // 尝试异步加载组件模块 loadComponentModule(type); return null; } // 确保我们返回一个有效的组件定义 if (module.default) { return module.default; } else if (module.__esModule && module.default) { // 有时 Vue 的动态导入会将默认导出包装在 __esModule 属性下 return module.default; } else { console.warn( `Module for ${type} found but default export is missing`, module, ); return null; } } /** * 准备组件属性 */ function prepareComponentProps( attrs: Record, componentId?: string, ): Record { const result: Record = { ...attrs }; if (componentId) { result.componentId = componentId; } return result; } /** * 初始化组件管理器 */ async function initialize() { const canvasInstance = diagramCanvas.value as any; if (canvasInstance?.getDiagramData) { const diagramData = canvasInstance.getDiagramData(); // 收集所有组件类型 const componentTypes = new Set(); diagramData.parts.forEach((part: DiagramPart) => { componentTypes.add(part.type); }); // 预加载组件模块 await preloadComponentModules(Array.from(componentTypes)); } } return { // 状态 componentModules, selectedComponentId, selectedComponentData, selectedComponentConfig, componentRefs, // Canvas控制状态 canvasPosition, canvasScale, // 方法 loadComponentModule, preloadComponentModules, addComponent, addTemplate, deleteComponent, selectComponent, updateComponentProp, updateComponentDirectProp, moveComponent, setCanvasRef, setComponentRef, getComponentRef, getDiagramData, updateDiagramData, getComponentDefinition, prepareComponentProps, initialize, // Canvas控制方法 setCanvasPosition, updateCanvasPosition, setCanvasScale, getCanvasPosition, getCanvasScale, zoomAtPosition, screenToCanvas, canvasToScreen, centerView, }; }, ); export { useProvideComponentManager, useComponentManager };