Files
EmailBill/openspec/changes/archive/2026-02-20-refactor-popup-container-component/design.md
SunCheng 6e95568906
All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 18s
Docker Build & Deploy / Deploy to Production (push) Successful in 6s
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 0s
Docker Build & Deploy / WeChat Notification (push) Successful in 1s
fix
2026-02-20 13:56:29 +08:00

5.0 KiB
Raw Blame History

Context

项目中已有 PopupContainer.vue 通用弹窗组件(位于 Web/src/components/PopupContainer.vue),但其样式设计与 TransactionDetailSheet.vue 不同:

  • PopupContainer: 使用 Vant 主题变量,默认高度 80%,标准化的布局
  • TransactionDetailSheet: 使用 Inter 字体16px 圆角,纯白背景(#ffffff / #18181b更现代化的视觉风格

TransactionDetailSheet.vue 当前实现(位于 Web/src/components/Transaction/TransactionDetailSheet.vue

  • 直接使用 van-popup + 自定义样式
  • 头部:自定义标题 + 关闭按钮(.sheet-header
  • 内容区域:金额、表单字段、分类选择器(无固定滚动容器)
  • 底部操作按钮:删除、保存(.actions-section
  • 样式特点:borderTopLeftRadius: 16px, Inter 字体, #ffffff 背景, gap: 24px

当前问题:头部和底部未固定,内容较多时滚动体验不佳。

Goals / Non-Goals

Goals:

  • 创建 PopupContainerV2.vue 通用组件,采用 TransactionDetailSheet 的样式风格
  • 提供固定头部(标题 + 可选关闭按钮)、可滚动内容、固定底部的布局能力
  • 支持暗色模式(#18181b 背景)
  • 将 TransactionDetailSheet 重构为使用 PopupContainerV2
  • 保持 TransactionDetailSheet 所有现有功能和对外 API 不变

Non-Goals:

  • 不修改现有的 PopupContainer.vuev1 版本保持不变)
  • 不强制项目中其他组件迁移到 v2自愿迁移
  • 不改变 TransactionDetailSheet 的业务逻辑
  • 不引入新的 UI 库或依赖

Decisions

决策 1创建新组件 PopupContainerV2 而不是修改 PopupContainer

选择: 创建新的 PopupContainerV2.vue 组件

理由:

  • PopupContainer 已在项目中广泛使用CategoryBillPopup 等),修改可能影响现有组件
  • TransactionDetailSheet 的样式风格更现代,适合作为新版本
  • v1 和 v2 可以并存,逐步迁移,降低风险
  • 替代方案: 直接修改 PopupContainer → 不采用,破坏性太大,需要验证所有使用方

决策 2PopupContainerV2 的样式来源

选择: 完全采用 TransactionDetailSheet 的样式风格

理由:

  • TransactionDetailSheet 的样式已经过验证,用户体验良好
  • Inter 字体、16px 圆角、纯白背景是现代化设计趋势
  • 保持样式一致性,避免混合不同的设计语言
  • 替代方案: 混合 PopupContainer 和 TransactionDetailSheet 的样式 → 不采用,会导致样式不统一

决策 3PopupContainerV2 的 API 设计

选择: 提供 title prop、default 插槽、footer 插槽,关闭按钮默认显示

理由:

  • title prop 简化使用,覆盖 80% 场景
  • default 插槽放可滚动内容,最大灵活性
  • footer 插槽放固定底部操作按钮
  • 关闭按钮默认显示,符合 TransactionDetailSheet 的行为
  • 替代方案: 提供 header 插槽替代 title prop → 不采用,增加使用复杂度,大多数场景只需简单标题

决策 4内容区域的 padding 处理

选择: PopupContainerV2 的内容插槽不提供默认 padding由使用方控制

理由:

  • TransactionDetailSheet 的不同区域有不同的 padding 需求(金额区域 16px 16px 24px表单区域 0 16px 16px
  • 组件不应预设 padding保持灵活性
  • 使用方可以根据内容自行调整间距
  • 替代方案: 提供统一的 padding → 不采用,会限制布局灵活性

决策 5TransactionDetailSheet 的迁移策略

选择: 完全移除原有的头部和外层布局代码,使用 PopupContainerV2 的插槽

理由:

  • 避免代码重复,减少维护成本
  • PopupContainerV2 已提供所有需要的布局能力
  • 保持 TransactionDetailSheet 的职责单一(业务逻辑)
  • 替代方案: 保留部分原有代码 → 不采用,会造成样式冲突和维护混乱

Risks / Trade-offs

风险 1新增 PopupContainerV2 组件增加项目复杂度

缓解措施:

  • 在组件文件顶部添加清晰的文档注释,说明 v1 和 v2 的区别
  • v2 组件设计简洁API 清晰,易于理解和使用
  • 在使用 TransactionDetailSheet 时验证 v2 的稳定性后,再考虑推广到其他组件

风险 2样式迁移可能遗漏细节

缓解措施:

  • 仔细对比 TransactionDetailSheet 的原有样式
  • 使用 Chrome DevTools 对比重构前后的渲染效果
  • 验证暗色模式的样式一致性

风险 3日期选择器van-datetime-picker的嵌套弹窗可能存在 z-index 冲突

缓解措施: van-datetime-picker 使用 teleport="body",应与 PopupContainerV2 的弹窗层级独立,测试时重点验证

Trade-off 1创建 v2 而不是统一到一个组件

影响: 项目中会同时存在两个弹窗组件,增加学习成本
权衡: 保护现有代码稳定性的收益大于维护两个组件的成本,且 v2 可以逐步替代 v1

Trade-off 2PopupContainerV2 不提供默认 padding

影响: 使用方需要自行管理内容区域的间距
权衡: 灵活性优于便利性,避免样式冲突