# Phase 2 UI 组件库实现文档 **阶段**: Week 3-4 **交付日期**: 2026-03-07 **负责人**: UI/UX 设计师 **状态**: ✅ 已完成 --- ## 📦 交付内容 ### 任务 1:工具栏组件 ✅ **文件**: `lib/presentation/widgets/toolbar_widget.dart` **功能特性**: - ✅ 顶部工具栏:撤销/重做/保存/设置 - ✅ 底部工具栏:元件库/走线模式/选择模式 - ✅ 支持可折叠/隐藏 - ✅ 模式切换状态管理 - ✅ 动画过渡效果 - ✅ 响应式布局 **核心 API**: ```dart ToolbarWidget( showTopToolbar: true, showBottomToolbar: true, collapsible: true, onUndo: () => ..., onRedo: () => ..., onSave: () => ..., onSettings: () => ..., onComponentLibrary: () => ..., onWireMode: () => ..., onSelectMode: () => ..., ) ``` **设计亮点**: - 采用 Material Design 卡片式设计 - 顶部工具栏支持滑动手势收起/展开 - 底部工具栏模式按钮有高亮状态 - 工具栏按钮带有 Tooltip 提示 --- ### 任务 2:属性面板组件 ✅ **文件**: `lib/presentation/widgets/property_panel_widget.dart` **功能特性**: - ✅ 弹出式属性编辑(元件值、封装、网络名) - ✅ 实时预览修改效果 - ✅ 输入验证与错误提示 - ✅ 位号格式验证(R1, C2, U3) - ✅ 元件值格式验证(10k, 100n 等) - ✅ 封装格式验证 - ✅ 旋转控制(0°, 90°, 180°, 270°) - ✅ 镜像控制(水平/垂直) - ✅ 未保存状态提示 **核心 API**: ```dart // 直接使用组件 PropertyPanelWidget( propertyData: PropertyData( refDesignator: 'R1', value: '10k', footprint: '0805', componentType: ComponentType.resistor, ), onPropertyChanged: (data) => ..., onPreview: (data) => ..., ) // 或使用辅助函数 final result = await showPropertyPanel( context, propertyData: ..., onPropertyChanged: (data) => ..., ); ``` **数据模型**: ```dart class PropertyData { String? refDesignator; // 位号 String? value; // 值 String? footprint; // 封装 String? netName; // 网络名 ComponentType componentType; String? symbolName; int rotation; // 0, 90, 180, 270 bool mirrorX; bool mirrorY; } ``` **验证规则**: - 位号:必须为 `字母 + 数字` 格式(如 R1, C2, U3) - 电阻值:支持 `10k`, `4.7M`, `100R` 等格式 - 电容值:支持 `10u`, `100n`, `1p` 等格式 - 封装:必须包含字母和数字(如 0805, SOT23) **设计亮点**: - 底部抽屉式弹出,符合移动端交互习惯 - 实时输入验证,错误即时提示 - 旋转按钮可视化展示角度 - 未保存状态有明显提示 --- ### 任务 3:元件库面板组件 ✅ **文件**: `lib/presentation/widgets/component_library_panel.dart` **功能特性**: - ✅ 网格/列表双视图切换 - ✅ 搜索与筛选(按类别、封装、厂商) - ✅ 拖拽元件到画布 - ✅ 类别筛选(FilterChip) - ✅ 封装筛选 - ✅ 搜索防抖(300ms) - ✅ 长按查看详情 - ✅ 可拖拽拖动手柄 **核心 API**: ```dart // 直接使用组件 ComponentLibraryPanel( initialViewMode: LibraryViewMode.grid, onComponentSelected: (item) => ..., onDragStarted: (item) => ..., onFilterChanged: (filters) => ..., ) // 或使用辅助函数(抽屉式) await showComponentLibraryDrawer( context, initialViewMode: LibraryViewMode.grid, onComponentSelected: (item) => ..., ); ``` **数据模型**: ```dart class ComponentLibraryItem { final String name; final String category; final String footprint; final String? description; final String? manufacturer; final String? symbolData; } enum LibraryViewMode { grid, // 网格视图 list, // 列表视图 } ``` **筛选功能**: - 类别:电源、被动元件、半导体、连接器、光电器件、集成电路 - 封装:0402, 0603, 0805, 1206, SOT23, SOT223, SOIC8, DIP8, QFN16 - 搜索:支持名称和描述模糊匹配 **拖拽支持**: - 使用 Flutter Draggable 组件 - 拖拽时显示半透明原位置 - 拖拽反馈组件带有高亮边框 - 支持长按查看详情后再拖拽 **设计亮点**: - 网格视图:2 列布局,卡片式设计 - 列表视图:ListTile 布局,信息更详细 - 搜索栏带清除按钮 - 筛选面板可展开/收起 - 空状态友好提示 --- ## 🎨 设计规范 ### 遵循 Phase 1 触摸交互规范 v1.0 1. **最小触控区域**: 所有按钮 ≥ 44x44pt(iOS 人机指南) 2. **手势支持**: - 单击:选择/触发 - 长按:上下文菜单/详情 - 拖拽:元件放置 - 双指缩放:画布导航(编辑器层面) 3. **视觉反馈**: - 按钮按下有涟漪效果 - 选中状态有高亮 - 拖拽时有半透明提示 4. **动画过渡**: 200ms 标准动画时长 ### Material Design 3 风格 - 圆角:8px(小组件)、12px(卡片)、16px(面板) - 阴影:轻度阴影(模糊 8px,偏移 0,2) - 配色:使用 Theme.of(context).primaryColor - 字体:系统默认字体,字号 10-20sp --- ## 🔌 集成指南 ### 1. 在原理图编辑器中集成 ```dart import 'package:mobile_eda/presentation/widgets/widgets.dart'; class SchematicEditorScreen extends ConsumerStatefulWidget { @override Widget build(BuildContext context) { return Scaffold( body: Stack( children: [ // 画布 SchematicCanvas(), // 工具栏 ToolbarWidget( onUndo: _undo, onRedo: _redo, onSave: _save, onSettings: _showSettings, onComponentLibrary: _showLibrary, onWireMode: _setWireMode, onSelectMode: _setSelectMode, ), ], ), // 或作为底部抽屉显示元件库 bottomSheet: ComponentLibraryPanel( onComponentSelected: _addComponent, ), ); } } ``` ### 2. 属性编辑集成 ```dart // 双击元件时显示属性面板 void _onComponentDoubleTap(SchematicComponent component) async { final propertyData = PropertyData( refDesignator: component.ref, value: component.value, footprint: component.footprint, componentType: _mapToComponentType(component.type), rotation: component.rotation, mirrorX: component.mirrorX, mirrorY: component.mirrorY, ); final result = await showPropertyPanel( context, propertyData: propertyData, onPreview: (data) { // 实时预览:临时更新画布显示 _previewComponentChanges(data); }, onPropertyChanged: (data) { // 应用更改 _updateComponent(component.id, data); }, ); if (result != null) { // 保存成功 _commitChanges(); } else { // 取消,恢复原状 _revertPreview(); } } ``` ### 3. 元件拖拽集成 ```dart // 在画布上接收拖拽 class SchematicCanvas extends StatefulWidget { @override Widget build(BuildContext context) { return DragTarget( onWillAccept: (data) => true, onAccept: (component) { // 在拖拽位置放置元件 _placeComponent(component, _lastDragPosition); }, builder: (context, candidateData, rejectedData) { return CustomPaint(painter: SchematicPainter()); }, ); } } ``` ### 4. Riverpod 状态管理集成 ```dart // 创建状态提供者 final toolbarModeProvider = StateProvider((ref) { return ToolbarMode.select; }); final propertyPanelProvider = StateProvider((ref) { return null; }); final componentLibraryFilterProvider = StateProvider((ref) { return FilterOptions(); }); // 在组件中使用 class _SchematicEditorScreenState extends ConsumerState { @override Widget build(BuildContext context) { final mode = ref.watch(toolbarModeProvider); return ToolbarWidget( onSelectMode: () => ref.read(toolbarModeProvider.notifier).state = ToolbarMode.select, onWireMode: () => ref.read(toolbarModeProvider.notifier).state = ToolbarMode.wire, ); } } ``` --- ## 📁 文件结构 ``` mobile-eda/lib/presentation/widgets/ ├── widgets.dart # 导出文件(barrel) ├── toolbar_widget.dart # 工具栏组件 ├── property_panel_widget.dart # 属性面板组件 └── component_library_panel.dart # 元件库面板组件 ``` --- ## 🧪 测试建议 ### 单元测试 ```dart test('PropertyData copy creates independent copy', () { final original = PropertyData( refDesignator: 'R1', value: '10k', componentType: ComponentType.resistor, ); final copy = original.copy(); copy.refDesignator = 'R2'; expect(original.refDesignator, equals('R1')); expect(copy.refDesignator, equals('R2')); }); test('ToolbarWidget callbacks are invoked', () { var undoCalled = false; final widget = ToolbarWidget( onUndo: () => undoCalled = true, ); // 使用 tester.tap() 模拟点击撤销按钮 // expect(undoCalled, isTrue); }); ``` ### 组件测试 ```dart testWidgets('PropertyPanelWidget validates ref designator', (tester) async { await tester.pumpWidget( MaterialApp( home: PropertyPanelWidget( propertyData: PropertyData( componentType: ComponentType.resistor, ), ), ), ); // 输入无效位号 await tester.enterText(find.byType(TextFormField).first, 'invalid'); await tester.pump(); // 验证错误提示 expect(find.text('位号格式错误 (如 R1, C2, U3)'), findsOneWidget); }); ``` --- ## 🚀 后续优化建议 ### 性能优化 1. **元件库懒加载**: 当元件数量 > 100 时,使用分页或虚拟列表 2. **图片缓存**: 元件符号图片使用 cached_network_image 3. **防抖优化**: 搜索已实现 300ms 防抖 ### 功能增强 1. **最近使用**: 添加"最近使用元件"快速访问 2. **收藏功能**: 允许用户收藏常用元件 3. **自定义元件**: 支持用户创建和导入自定义元件 4. **批量编辑**: 支持多选元件批量修改属性 ### 用户体验 1. **快捷键支持**: 桌面端可添加键盘快捷键(Ctrl+Z 撤销等) 2. **语音输入**: 支持语音输入元件值("十千欧") 3. **AR 预览**: 使用 AR 查看元件实物图 --- ## 📞 与 EDA 引擎专家协作 ### 需要对接的 API 1. **元件放置**: ```dart // 需要 EDA 引擎提供 void placeComponent(ComponentLibraryItem item, Offset position); ``` 2. **属性更新**: ```dart // 需要 EDA 引擎提供 void updateComponentProperties(String componentId, PropertyData properties); ``` 3. **模式切换**: ```dart // 需要 EDA 引擎提供 void setEditorMode(EditorMode mode); ``` 4. **撤销/重做**: ```dart // 需要 EDA 引擎提供 void undo(); void redo(); bool get canUndo; bool get canRedo; ``` ### 数据格式约定 ```dart // 元件唯一标识 typedef ComponentId = String; // 坐标系统 // - 画布坐标:逻辑像素,原点在左上角 // - 网格吸附:默认 10.0 网格大小 // 旋转角度 // - 0: 默认方向 // - 90: 顺时针 90 度 // - 180: 顺时针 180 度 // - 270: 顺时针 270 度 ``` --- ## ✅ 验收标准 - [x] 工具栏组件可正常显示/隐藏 - [x] 工具栏按钮点击有响应 - [x] 属性面板可弹出/关闭 - [x] 属性输入有验证提示 - [x] 元件库支持网格/列表切换 - [x] 元件库支持搜索筛选 - [x] 元件可拖拽(Draggable) - [x] 代码符合 Flutter 规范(flutter analyze 通过) - [x] 组件文档完整 --- ## 📝 更新日志 **2026-03-07** - 初始版本 - ✅ 完成工具栏组件 - ✅ 完成属性面板组件 - ✅ 完成元件库面板组件 - ✅ 编写集成文档 --- **交付完成** 🎉 所有组件已实现并经过基本测试,可集成到原理图编辑器中使用。