Files
EmailBill/openspec/changes/unified-popup-component/design.md
SunCheng a88556c784 fix
2026-02-15 10:10:28 +08:00

6.0 KiB
Raw Blame History

Context

当前前端项目使用 Vue 3 + Vant UI 构建,虽然已有封装的 PopupContainer.vue 组件,但项目中存在多种弹窗实现方式:

  1. PopupContainer.vue: 统一的底部弹出式弹窗,支持标题、副标题、固定头部/页脚、内容滚动
  2. AddClassifyDialog.vue: 基于 van-dialog 的独立对话框组件,用于新增分类
  3. CategoryBillPopup.vue: 基于 van-popup 的独立底部弹窗组件,用于展示分类账单列表
  4. 直接使用 van-dialog: 多个视图文件中直接使用(如 ClassificationEdit.vue、BillAnalysisView.vue
  5. 直接使用 van-popup: 多个视图文件中用于选择器、表单等场景(如 PeriodicRecord.vue、statisticsV2/Index.vue

这种分散的实现导致:

  • 弹窗风格不统一(标题样式、圆角、间距、字体大小等)
  • 代码重复度高,维护成本高
  • 用户体验不一致

Goals / Non-Goals

Goals:

  • 统一所有弹窗组件使用 PopupContainer 或其扩展组件
  • 确保弹窗的视觉风格和交互行为一致
  • 减少代码重复,提高可维护性
  • 扩展 PopupContainer 功能以覆盖现有所有使用场景

Non-Goals:

  • 不修改 Vant UI 组件库本身
  • 不改变弹窗的业务逻辑(仅重构 UI 层)
  • 不影响后端 API

Decisions

1. 扩展现有 PopupContainer 组件而非创建新组件

选择理由:

  • PopupContainer.vue 已有成熟的底部弹窗布局和样式
  • 仅需扩展其功能即可覆盖大部分使用场景
  • 避免引入新的组件,减少学习成本

扩展点:

  • 添加 confirmcancel 事件,支持简单确认对话框场景
  • 添加 showConfirmButtonshowCancelButton props控制按钮显示
  • 添加 confirmTextcancelText props自定义按钮文本
  • 保留插槽机制,支持复杂表单场景

2. 保留 PopupContainer 的插槽设计

选择理由:

  • 插槽提供最大的灵活性,支持任意复杂的内容
  • 现有代码已使用插槽模式header-actions、footer保持向后兼容
  • 避免 API 过度设计,保持组件简洁

3. 渐进式迁移策略

选择理由:

  • 42+ 处弹窗修改,一次性重构风险高
  • 分批迁移便于测试和回滚
  • 降低对现有功能的影响

迁移顺序:

  1. 扩展 PopupContainer 组件
  2. 迁移 AddClassifyDialog.vueCategoryBillPopup.vue
  3. 迁移 views/ 目录中的弹窗,按功能模块分批

4. 不删除 AddClassifyDialog 和 CategoryBillPopup 组件文件

选择理由:

  • 这两个组件封装了特定的业务逻辑(如表单验证、数据加载)
  • 可以将它们改造为使用 PopupContainer 的组合组件,保留业务逻辑
  • 避免破坏其他引用这些组件的地方

改造方式:

  • AddClassifyDialog.vue 改造为使用 PopupContainer 的组合组件
  • CategoryBillPopup.vue 改造为使用 PopupContainer 的组合组件
  • 组件 API 保持不变,仅内部实现变更

5. 使用 Vant UI 的 CSS 变量

选择理由:

  • 项目已使用 Vant UI保持视觉一致性
  • 支持深色/浅色主题切换
  • 减少自定义 CSS 代码量

Risks / Trade-offs

1. 扩展 PopupContainer 可能导致 API 膨胀

风险: 扩展功能过多后,组件变得复杂,难以维护

缓解措施:

  • 保持核心 API 简洁,使用插槽处理复杂场景
  • 添加清晰的文档和示例
  • 考虑拆分为多个专用组件(如 ConfirmDialog、FormDialog如果过于复杂

2. 迁移过程中可能引入 Bug

风险: 重构可能破坏现有功能

缓解措施:

  • 渐进式迁移,每批迁移后进行完整测试
  • 保留旧代码作为参考,直到新代码稳定
  • 重点测试弹窗的打开/关闭、表单验证、数据加载等交互

3. 某些场景可能不适用 PopupContainer

风险: 一些特殊的弹窗场景(如全屏弹窗、居中弹窗)无法用底部弹窗实现

缓解措施:

  • 识别这些特殊场景,评估是否需要保留 van-dialog 或创建新的专用组件
  • 优先统一常见场景,特殊场景可以例外

4. 性能影响

风险: 组件封装层可能增加轻微的性能开销

缓解措施:

  • 保持组件轻量化,避免不必要的响应式依赖
  • 使用 Vue 3 的 <script setup> 语法,减少运行时开销
  • 进行性能测试,确保没有明显退化

Migration Plan

阶段 1: 扩展 PopupContainer 组件

  1. 添加确认对话框相关 propsshowConfirmButtonshowCancelButtonconfirmTextcancelText
  2. 添加 confirmcancel 事件
  3. 更新样式,确保与现有设计一致
  4. 编写组件文档和示例

阶段 2: 改造现有封装组件

  1. 改造 AddClassifyDialog.vue,使用 PopupContainer 实现
  2. 改造 CategoryBillPopup.vue,使用 PopupContainer 实现
  3. 测试改造后的组件功能

阶段 3: 迁移 views/ 目录中的弹窗

按功能模块分批迁移:

  1. 分类相关视图ClassificationEdit.vue、ClassificationBatch.vue 等)
  2. 账单相关视图PeriodicRecord.vue、TransactionsRecord.vue 等)
  3. 统计相关视图statisticsV2/Index.vue 等)
  4. 预算相关视图budgetV2/*.vue
  5. 其他视图BillAnalysisView.vue、calendarV2/Index.vue 等)

阶段 4: 验证和优化

  1. 全量测试所有弹窗功能
  2. 性能测试和优化
  3. 代码审查和清理
  4. 更新文档

回滚策略

  • 使用 Git 分支进行开发,主分支保持稳定
  • 每个阶段完成后打 Tag便于快速回滚
  • 保留旧的实现代码,直到新代码完全稳定

Open Questions

  1. 是否需要创建专门的 ConfirmDialog 组件?

    • 当前考虑扩展 PopupContainer 以支持确认对话框场景
    • 如果确认对话框的使用场景较多且简单,可以考虑独立组件
  2. 如何处理全屏弹窗场景?

    • 识别项目中是否存在全屏弹窗需求
    • 如有,评估是否需要扩展 PopupContainer 或创建新组件
  3. 弹窗动画是否需要统一?

    • 当前依赖 Vant UI 的默认动画
    • 如有特殊需求,可能需要自定义动画