# 多功能工具箱架构设计文档 > Feature: feat/08-multifunctional-toolbox > Created: 2025-12-08 > Status: 设计阶段 ## 📋 概述 多功能工具箱是一个允许用户在画布上嵌入第三方工具网页的功能。工具以 iframe 形式"钉在"画布上,成为画布的原生元素,支持拖拽、缩放、旋转等完整交互能力。 ### 核心特性 - ✅ 左侧工具箱抽屉,展示可用工具列表 - ✅ 工具作为画布元素(PlaitTool),完全集成到 Plait 坐标系统 - ✅ 使用 SVG foreignObject 嵌入 iframe - ✅ 自动继承 Plait 的拖拽、缩放、旋转、选中等能力 - ✅ 支持内置工具和自定义工具 - ✅ 工具状态持久化(随画板数据保存) --- ## 🏗️ 整体架构 ``` ┌─────────────────────────────────────────────────────────────────┐ │ Drawnix 应用层 │ │ ┌──────────────────┐ ┌───────────────────────────┐ │ │ │ ToolboxDrawer │ │ Plait Canvas (SVG) │ │ │ │ (左侧抽屉) │ │ │ │ │ │ │ │ ┌─────────────────────┐ │ │ │ │ 🍌 香蕉提示词 │ click │ │ PlaitTool Element │ │ │ │ │ 📝 小红薯工具 │ ─────> │ │ (foreignObject) │ │ │ │ │ ⚙️ 批处理工具 │ │ │ └─ iframe │ │ │ │ │ │ │ └─────────────────────┘ │ │ │ └──────────────────┘ └───────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ Plait 插件层 (withTool) │ │ │ │ - 注册 ToolComponent │ │ │ │ - 提供 ToolTransforms API │ │ │ └──────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## 📐 核心组件设计 ### 1. 数据模型层 #### PlaitTool 元素(画布元素) ```typescript /** * 工具元素 - 画布上的工具实例 * 继承 PlaitElement,成为画布的原生元素 */ export interface PlaitTool extends PlaitElement { type: 'tool'; // 位置和尺寸(画布坐标) points: [Point, Point]; // [左上角, 右下角] // 旋转角度 angle: number; // 工具标识 toolId: string; // 工具定义ID url: string; // iframe URL // 可选元数据 metadata?: { name?: string; category?: string; permissions?: string[]; }; } ``` #### ToolDefinition(工具配置) ```typescript /** * 工具定义 - 工具箱中的工具配置 */ export interface ToolDefinition { id: string; // 唯一标识 name: string; // 工具名称 description?: string; // 工具描述 icon?: string; // 图标(emoji 或 icon name) category?: string; // 分类 url: string; // iframe URL defaultWidth?: number; // 默认宽度(画布单位) defaultHeight?: number; // 默认高度(画布单位) permissions?: string[]; // iframe sandbox 权限 } ``` --- ### 2. Plait 插件层 #### withTool 插件 ```typescript /** * 工具插件 - 注册 ToolComponent 到 Plait */ export const withTool: PlaitPlugin = (board: PlaitBoard) => { // 1. 注册元素类型 board.drawElement = (element: PlaitDrawElement) => { if (element.type === 'tool') { return ToolComponent; } return board.drawElement(element); }; return board; }; ``` #### ToolTransforms API ```typescript /** * 工具元素操作 API */ export const ToolTransforms = { // 插入工具到画布 insertTool( board: PlaitBoard, toolId: string, url: string, position: Point, size: { width: number; height: number } ): void; // 更新工具尺寸 resizeTool( board: PlaitBoard, element: PlaitTool, newSize: { width: number; height: number } ): void; // 删除工具 removeTool(board: PlaitBoard, elementId: string): void; }; ``` --- ### 3. 渲染层 #### ToolComponent(画布组件) ```typescript /** * 工具元素渲染组件 * 继承 CommonElementFlavour,集成到 Plait 渲染流程 */ export class ToolComponent extends CommonElementFlavour implements OnContextChanged { toolGenerator: ToolGenerator; // 初始化生成器 initializeGenerator(): void; // 组件初始化 initialize(): void; // 响应元素变化 onContextChanged(value, previous): void; // 清理资源 destroy(): void; } ``` #### ToolGenerator(渲染生成器) ```typescript /** * 工具元素渲染生成器 * 负责实际的 SVG/HTML 渲染 */ export class ToolGenerator extends Generator { // 绘制工具元素 draw(element: PlaitTool): SVGGElement; // 更新工具元素 updateImage(nodeG: SVGGElement, previous: PlaitTool, current: PlaitTool): void; // 创建 foreignObject + iframe private createForeignObject(element: PlaitTool): SVGForeignObjectElement; // 创建 iframe private createIframe(element: PlaitTool): HTMLIFrameElement; // 清理资源 destroy(): void; } ``` --- ### 4. UI 组件层 #### ToolboxDrawer(工具箱抽屉) ```typescript /** * 工具箱侧边栏 * 展示可用工具列表,点击后插入到画布 */ export const ToolboxDrawer: React.FC<{ isOpen: boolean; onOpenChange: (open: boolean) => void; }> = ({ isOpen, onOpenChange }) => { // 工具列表 const tools = useToolboxService().getAvailableTools(); // 点击工具 -> 插入到画布 const handleToolClick = (tool: ToolDefinition) => { ToolTransforms.insertTool(board, tool.id, tool.url, ...); }; return (
); }; ``` --- ### 5. 服务层 #### ToolboxService(工具管理) ```typescript /** * 工具箱管理服务 * 管理内置工具和自定义工具 */ export class ToolboxService { private builtInTools: ToolDefinition[]; private customTools: ToolDefinition[]; // 获取所有工具 getAvailableTools(): ToolDefinition[]; // 根据 ID 获取工具 getToolById(id: string): ToolDefinition | null; // 添加自定义工具 addCustomTool(tool: ToolDefinition): void; // 移除自定义工具 removeCustomTool(id: string): void; } ``` --- ## 🔄 数据流 ### 插入工具流程 ``` 用户点击工具箱中的工具 ↓ ToolboxDrawer.handleToolClick() ↓ ToolTransforms.insertTool(board, toolId, url, position, size) ↓ 创建 PlaitTool 元素对象 ↓ DrawTransforms.insertElement(board, toolElement) ↓ Plait 触发重新渲染 ↓ withTool 插件识别 type='tool' ↓ 实例化 ToolComponent ↓ ToolGenerator.draw() 渲染 foreignObject + iframe ↓ 工具显示在画布上 ``` ### 拖拽/缩放流程 ``` 用户拖拽工具元素 ↓ Plait 原生拖拽系统处理 ↓ 更新 PlaitTool.points ↓ 触发 ToolComponent.onContextChanged() ↓ ToolGenerator.updateImage() 更新位置 ↓ foreignObject transform 更新 ↓ 工具随画布移动/缩放 ``` --- ## 🎨 技术实现要点 ### 1. SVG foreignObject 嵌入 HTML ```xml