mobile-eda/docs/PHASE2_UI_COMPONENTS.md

12 KiB
Raw Blame History

Phase 2 UI 组件库实现文档

阶段: Week 3-4
交付日期: 2026-03-07
负责人: UI/UX 设计师
状态: 已完成


📦 交付内容

任务 1工具栏组件

文件: lib/presentation/widgets/toolbar_widget.dart

功能特性:

  • 顶部工具栏:撤销/重做/保存/设置
  • 底部工具栏:元件库/走线模式/选择模式
  • 支持可折叠/隐藏
  • 模式切换状态管理
  • 动画过渡效果
  • 响应式布局

核心 API:

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:

// 直接使用组件
PropertyPanelWidget(
  propertyData: PropertyData(
    refDesignator: 'R1',
    value: '10k',
    footprint: '0805',
    componentType: ComponentType.resistor,
  ),
  onPropertyChanged: (data) => ...,
  onPreview: (data) => ...,
)

// 或使用辅助函数
final result = await showPropertyPanel(
  context,
  propertyData: ...,
  onPropertyChanged: (data) => ...,
);

数据模型:

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:

// 直接使用组件
ComponentLibraryPanel(
  initialViewMode: LibraryViewMode.grid,
  onComponentSelected: (item) => ...,
  onDragStarted: (item) => ...,
  onFilterChanged: (filters) => ...,
)

// 或使用辅助函数(抽屉式)
await showComponentLibraryDrawer(
  context,
  initialViewMode: LibraryViewMode.grid,
  onComponentSelected: (item) => ...,
);

数据模型:

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. 最小触控区域: 所有按钮 ≥ 44x44ptiOS 人机指南)
  2. 手势支持:
    • 单击:选择/触发
    • 长按:上下文菜单/详情
    • 拖拽:元件放置
    • 双指缩放:画布导航(编辑器层面)
  3. 视觉反馈:
    • 按钮按下有涟漪效果
    • 选中状态有高亮
    • 拖拽时有半透明提示
  4. 动画过渡: 200ms 标准动画时长

Material Design 3 风格

  • 圆角8px小组件、12px卡片、16px面板
  • 阴影:轻度阴影(模糊 8px偏移 0,2
  • 配色:使用 Theme.of(context).primaryColor
  • 字体:系统默认字体,字号 10-20sp

🔌 集成指南

1. 在原理图编辑器中集成

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. 属性编辑集成

// 双击元件时显示属性面板
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. 元件拖拽集成

// 在画布上接收拖拽
class SchematicCanvas extends StatefulWidget {
  @override
  Widget build(BuildContext context) {
    return DragTarget<ComponentLibraryItem>(
      onWillAccept: (data) => true,
      onAccept: (component) {
        // 在拖拽位置放置元件
        _placeComponent(component, _lastDragPosition);
      },
      builder: (context, candidateData, rejectedData) {
        return CustomPaint(painter: SchematicPainter());
      },
    );
  }
}

4. Riverpod 状态管理集成

// 创建状态提供者
final toolbarModeProvider = StateProvider<ToolbarMode>((ref) {
  return ToolbarMode.select;
});

final propertyPanelProvider = StateProvider<PropertyData?>((ref) {
  return null;
});

final componentLibraryFilterProvider = StateProvider<FilterOptions>((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        # 元件库面板组件

🧪 测试建议

单元测试

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);
});

组件测试

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. 元件放置:

    // 需要 EDA 引擎提供
    void placeComponent(ComponentLibraryItem item, Offset position);
    
  2. 属性更新:

    // 需要 EDA 引擎提供
    void updateComponentProperties(String componentId, PropertyData properties);
    
  3. 模式切换:

    // 需要 EDA 引擎提供
    void setEditorMode(EditorMode mode);
    
  4. 撤销/重做:

    // 需要 EDA 引擎提供
    void undo();
    void redo();
    bool get canUndo;
    bool get canRedo;
    

数据格式约定

// 元件唯一标识
typedef ComponentId = String;

// 坐标系统
// - 画布坐标:逻辑像素,原点在左上角
// - 网格吸附:默认 10.0 网格大小

// 旋转角度
// - 0: 默认方向
// - 90: 顺时针 90 度
// - 180: 顺时针 180 度
// - 270: 顺时针 270 度

验收标准

  • 工具栏组件可正常显示/隐藏
  • 工具栏按钮点击有响应
  • 属性面板可弹出/关闭
  • 属性输入有验证提示
  • 元件库支持网格/列表切换
  • 元件库支持搜索筛选
  • 元件可拖拽Draggable
  • 代码符合 Flutter 规范flutter analyze 通过)
  • 组件文档完整

📝 更新日志

2026-03-07 - 初始版本

  • 完成工具栏组件
  • 完成属性面板组件
  • 完成元件库面板组件
  • 编写集成文档

交付完成 🎉

所有组件已实现并经过基本测试,可集成到原理图编辑器中使用。