## 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` 已有成熟的底部弹窗布局和样式 - 仅需扩展其功能即可覆盖大部分使用场景 - 避免引入新的组件,减少学习成本 **扩展点:** - 添加 `confirm` 和 `cancel` 事件,支持简单确认对话框场景 - 添加 `showConfirmButton` 和 `showCancelButton` props,控制按钮显示 - 添加 `confirmText` 和 `cancelText` props,自定义按钮文本 - 保留插槽机制,支持复杂表单场景 ### 2. 保留 PopupContainer 的插槽设计 **选择理由:** - 插槽提供最大的灵活性,支持任意复杂的内容 - 现有代码已使用插槽模式(header-actions、footer),保持向后兼容 - 避免 API 过度设计,保持组件简洁 ### 3. 渐进式迁移策略 **选择理由:** - 42+ 处弹窗修改,一次性重构风险高 - 分批迁移便于测试和回滚 - 降低对现有功能的影响 **迁移顺序:** 1. 扩展 `PopupContainer` 组件 2. 迁移 `AddClassifyDialog.vue` 和 `CategoryBillPopup.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 的 `