feat(plan): 新增多阶段提取方案功能
- 新增产品:宏挚传承保障计划(多阶段) (savings-gs-multistage) - 年龄 < 12岁:固定显示 3 个阶段 - 年龄 ≥ 12岁:初始 1 个阶段,可添加至 4 个 - 提取期新增"一笔过"选项 - 递增百分比改为可选字段(不填传 null) - 提交数据格式:withdrawal_stages 数组 - 删除冗余的 mock 文件 (src/api/mock/hotProducts.js) - 文档归档到 docs/tasks/2026-02-25/archive/ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Showing
7 changed files
with
729 additions
and
452 deletions
| ... | @@ -2,6 +2,29 @@ | ... | @@ -2,6 +2,29 @@ |
| 2 | 2 | ||
| 3 | 记录项目开发过程中的重要变更和完成任务。 | 3 | 记录项目开发过程中的重要变更和完成任务。 |
| 4 | 4 | ||
| 5 | +## 2026-02-25 | ||
| 6 | + | ||
| 7 | +### 21:00:00 - feat(plan): 新增多阶段提取方案功能 | ||
| 8 | + | ||
| 9 | +**影响文件**: | ||
| 10 | +- `src/config/plan-templates.js` - 新增多阶段产品配置和 `savings-gs-multistage` 产品 | ||
| 11 | +- `src/components/plan/PlanTemplates/SavingsTemplate.vue` - 实现多阶段提取渲染和交互逻辑 | ||
| 12 | +- `src/api/mock/mock/hotProducts.js` - 添加多阶段产品 mock 数据 | ||
| 13 | + | ||
| 14 | +**变更摘要**: | ||
| 15 | +- 新增产品:宏挚传承保障计划(多阶段) (`savings-gs-multistage`) | ||
| 16 | +- 多阶段功能: | ||
| 17 | + - 年龄 < 12岁:固定显示 3 个阶段 | ||
| 18 | + - 年龄 ≥ 12岁:初始 1 个阶段,可添加至 4 个 | ||
| 19 | + - 提取期新增"一笔过"选项 | ||
| 20 | + - 递增百分比改为可选字段(不填传 null) | ||
| 21 | +- 提交数据格式:`withdrawal_stages` 数组 | ||
| 22 | +- 样式优化:阶段卡片白色背景 | ||
| 23 | + | ||
| 24 | +**相关文档**: | ||
| 25 | +- `docs/tasks/2026-02-25/archive/多阶段提取方案设计.md` | ||
| 26 | +- `docs/tasks/2026-02-25/archive/客户新需求-2026-02-25.md` | ||
| 27 | + | ||
| 5 | ## 2026-02-24 | 28 | ## 2026-02-24 |
| 6 | 29 | ||
| 7 | ### 23:59:18 - 完成任务 | 30 | ### 23:59:18 - 完成任务 |
| ... | @@ -15,3 +38,48 @@ | ... | @@ -15,3 +38,48 @@ |
| 15 | 38 | ||
| 16 | **变更摘要**: | 39 | **变更摘要**: |
| 17 | - 无详细描述 | 40 | - 无详细描述 |
| 41 | +## 2026-02-25 | ||
| 42 | + | ||
| 43 | +### 20:09:02 - 完成任务 | ||
| 44 | + | ||
| 45 | +**影响文件**: | ||
| 46 | +- `src/components/plan/PlanTemplates/CriticalIllnessTemplate.vue` | ||
| 47 | +- `src/components/plan/PlanTemplates/LifeInsuranceTemplate.vue` | ||
| 48 | +- `src/components/plan/PlanTemplates/SavingsTemplate.vue` | ||
| 49 | +- `src/config/plan-templates.js` | ||
| 50 | + | ||
| 51 | +**变更摘要**: | ||
| 52 | +- 无详细描述 | ||
| 53 | + | ||
| 54 | +### 20:11:21 - 完成任务 | ||
| 55 | + | ||
| 56 | +**影响文件**: | ||
| 57 | +- `src/components/plan/PlanTemplates/CriticalIllnessTemplate.vue` | ||
| 58 | +- `src/components/plan/PlanTemplates/LifeInsuranceTemplate.vue` | ||
| 59 | +- `src/components/plan/PlanTemplates/SavingsTemplate.vue` | ||
| 60 | +- `src/config/plan-templates.js` | ||
| 61 | + | ||
| 62 | +**变更摘要**: | ||
| 63 | +- 无详细描述 | ||
| 64 | + | ||
| 65 | +### 20:12:04 - 完成任务 | ||
| 66 | + | ||
| 67 | +**影响文件**: | ||
| 68 | +- `src/components/plan/PlanTemplates/CriticalIllnessTemplate.vue` | ||
| 69 | +- `src/components/plan/PlanTemplates/LifeInsuranceTemplate.vue` | ||
| 70 | +- `src/components/plan/PlanTemplates/SavingsTemplate.vue` | ||
| 71 | +- `src/config/plan-templates.js` | ||
| 72 | + | ||
| 73 | +**变更摘要**: | ||
| 74 | +- 无详细描述 | ||
| 75 | + | ||
| 76 | +### 20:14:14 - 完成任务 | ||
| 77 | + | ||
| 78 | +**影响文件**: | ||
| 79 | +- `src/components/plan/PlanTemplates/CriticalIllnessTemplate.vue` | ||
| 80 | +- `src/components/plan/PlanTemplates/LifeInsuranceTemplate.vue` | ||
| 81 | +- `src/components/plan/PlanTemplates/SavingsTemplate.vue` | ||
| 82 | +- `src/config/plan-templates.js` | ||
| 83 | + | ||
| 84 | +**变更摘要**: | ||
| 85 | +- 无详细描述 | ... | ... |
| 1 | # 多阶段提取方案设计文档 | 1 | # 多阶段提取方案设计文档 |
| 2 | 2 | ||
| 3 | > **创建时间**: 2026-02-25 | 3 | > **创建时间**: 2026-02-25 |
| 4 | -> **状态**: ⏳ 待客户确认需求 | 4 | +> **状态**: ✅ 需求已确认 |
| 5 | > **设计师**: Claude Code | 5 | > **设计师**: Claude Code |
| 6 | -> **版本**: 1.1.0 | 6 | +> **版本**: 2.0.0 |
| 7 | -> **更新内容**: 新增需求说明章节,辨析多阶段与多方案概念 | 7 | +> **更新内容**: 客户确认只需多阶段提取,不需要多方案对比功能 |
| 8 | 8 | ||
| 9 | --- | 9 | --- |
| 10 | 10 | ||
| 11 | ## 📋 目录 | 11 | ## 📋 目录 |
| 12 | 12 | ||
| 13 | -1. [需求说明(待客户确认)](#需求说明待客户确认) | 13 | +1. [需求说明(已确认)](#需求说明已确认) |
| 14 | 2. [需求背景](#需求背景) | 14 | 2. [需求背景](#需求背景) |
| 15 | 3. [现状分析](#现状分析) | 15 | 3. [现状分析](#现状分析) |
| 16 | 4. [方案设计](#方案设计) | 16 | 4. [方案设计](#方案设计) |
| ... | @@ -18,25 +18,20 @@ | ... | @@ -18,25 +18,20 @@ |
| 18 | 6. [组件设计](#组件设计) | 18 | 6. [组件设计](#组件设计) |
| 19 | 7. [Schema 配置](#schema-配置) | 19 | 7. [Schema 配置](#schema-配置) |
| 20 | 8. [字段映射](#字段映射) | 20 | 8. [字段映射](#字段映射) |
| 21 | -9. [多方案需求分析](#多方案需求分析) | 21 | +9. [实施清单](#实施清单) |
| 22 | -10. [实施清单](#实施清单) | 22 | +10. [待确认事项](#待确认事项) |
| 23 | -11. [待确认事项](#待确认事项) | ||
| 24 | 23 | ||
| 25 | --- | 24 | --- |
| 26 | 25 | ||
| 27 | -## 📖 需求说明(待客户确认) | 26 | +## 📖 需求说明(已确认) |
| 28 | 27 | ||
| 29 | -> **重要说明**:以下是我们对客户资料的理解分析,**请客户确认实际需求**。 | 28 | +> **✅ 客户已确认**:2026-02-25 |
| 30 | 29 | ||
| 31 | -### 概念辨析:多阶段 vs 多方案 | 30 | +### 需求概述 |
| 32 | 31 | ||
| 33 | -客户提供的资料中出现了两个容易混淆的概念,需要明确区分: | 32 | +**多阶段提取(Multi-Stage Withdrawal)**:**一个提取方案**中,包含**多个不同阶段**的提取计划。 |
| 34 | 33 | ||
| 35 | -#### 1️⃣ 多阶段提取(Multi-Stage Withdrawal) | 34 | +**示例**(教育+创业+退休组合): |
| 36 | - | ||
| 37 | -**定义**:**一个提取方案**中,包含**多个不同阶段**的提取计划。 | ||
| 38 | - | ||
| 39 | -**示例**(3岁小公主 - 方案一): | ||
| 40 | ``` | 35 | ``` |
| 41 | 同一个方案内的三个阶段: | 36 | 同一个方案内的三个阶段: |
| 42 | ├─ 第一阶段:18-21岁,每年5万(教育基金) | 37 | ├─ 第一阶段:18-21岁,每年5万(教育基金) |
| ... | @@ -44,72 +39,11 @@ | ... | @@ -44,72 +39,11 @@ |
| 44 | └─ 第三阶段:50-100岁,每年7万(退休养老年金) | 39 | └─ 第三阶段:50-100岁,每年7万(退休养老年金) |
| 45 | ``` | 40 | ``` |
| 46 | 41 | ||
| 47 | -**特点**: | 42 | +### 客户确认要点 |
| 48 | -- ✅ 这是一个方案 | ||
| 49 | -- ✅ 包含多个提取阶段 | ||
| 50 | -- ✅ 每个阶段针对不同人生阶段的需求 | ||
| 51 | - | ||
| 52 | -#### 2️⃣ 多方案(Multiple Scenarios) | ||
| 53 | - | ||
| 54 | -**定义**:**同一份计划书**中,展示**多个备选提取方案**供客户选择。 | ||
| 55 | - | ||
| 56 | -**示例**(3岁小公主 - 3个方案): | ||
| 57 | -``` | ||
| 58 | -方案一:多阶段分时提取 | ||
| 59 | - └─ 教育(18-21岁) + 创业(30岁) + 退休(50-100岁) | ||
| 60 | - | ||
| 61 | -方案二:长期定期提取 | ||
| 62 | - └─ 9-100岁,每年1.75万 | ||
| 63 | - | ||
| 64 | -方案三:财富传承 | ||
| 65 | - └─ 不提取,展示保单价值增长表 | ||
| 66 | -``` | ||
| 67 | - | ||
| 68 | -**特点**: | ||
| 69 | -- ✅ 这是三个独立的方案 | ||
| 70 | -- ✅ 每个方案代表不同的提取策略 | ||
| 71 | -- ✅ 客户可以在方案之间对比选择 | ||
| 72 | - | ||
| 73 | -### 对比总结 | ||
| 74 | - | ||
| 75 | -| 维度 | 多阶段 | 多方案 | | ||
| 76 | -|------|--------|--------| | ||
| 77 | -| **是什么** | 一个方案内的多个提取阶段 | 多个独立的备选方案 | | ||
| 78 | -| **为什么** | 满足不同人生阶段的资金需求 | 提供不同风险/收益的提取策略 | | ||
| 79 | -| **数据量** | 1个方案,3-5个阶段 | 3个方案,每个方案可能有多个阶段 | | ||
| 80 | -| **客户选择** | 无需选择,按阶段执行 | 客户需要选择采用哪个方案 | | ||
| 81 | - | ||
| 82 | -### 需要客户确认的问题 | ||
| 83 | - | ||
| 84 | -#### 问题 1:是否需要多阶段提取? | ||
| 85 | - | ||
| 86 | -**说明**:是否需要在一个方案中设置多个提取阶段(如:教育→创业→退休)? | ||
| 87 | - | ||
| 88 | -- [ ] 是,需要多阶段提取 | ||
| 89 | -- [ ] 否,每个方案只需要一种提取方式 | ||
| 90 | - | ||
| 91 | -#### 问题 2:是否需要多方案? | ||
| 92 | - | ||
| 93 | -**说明**:是否需要在同一份计划书中展示多个备选方案供客户选择? | ||
| 94 | - | ||
| 95 | -- [ ] 是,需要多方案对比(如:保守方案 vs 积极方案 vs 传承方案) | ||
| 96 | -- [ ] 否,每个客户只需要一个方案 | ||
| 97 | - | ||
| 98 | -#### 问题 3:实施方案选择 | ||
| 99 | 43 | ||
| 100 | -如果需要多方案,请选择实施方案: | 44 | +- ✅ 一份计划书里只有一个提取方案,不会有多个 |
| 101 | - | 45 | +- ✅ 需要支持在一个方案中设置多个不同阶段的提取计划 |
| 102 | -**选项 A:单次提交,多方案对比** | 46 | +- ✅ 阶段数量建议 1-5 个 |
| 103 | -- 优点:一次性输入多个方案,计划书可直接对比展示 | ||
| 104 | -- 缺点:需要后端改动支持,开发周期较长 | ||
| 105 | - | ||
| 106 | -**选项 B:多次提交,分别生成**(推荐) | ||
| 107 | -- 优点:复用现有逻辑,无需后端改动,快速上线 | ||
| 108 | -- 缺点:每个方案单独生成,需要人工对比 | ||
| 109 | - | ||
| 110 | -**选项 C:混合方案** | ||
| 111 | -- 第一阶段:先实现选项B(快速上线) | ||
| 112 | -- 第二阶段:再升级为选项A(完整体验) | ||
| 113 | 47 | ||
| 114 | --- | 48 | --- |
| 115 | 49 | ||
| ... | @@ -596,189 +530,43 @@ const savingsSubmitMapping = { | ... | @@ -596,189 +530,43 @@ const savingsSubmitMapping = { |
| 596 | 530 | ||
| 597 | --- | 531 | --- |
| 598 | 532 | ||
| 599 | -## 多方案需求分析 | ||
| 600 | - | ||
| 601 | -> **说明**:如果客户确认需要多方案功能,以下是技术实施建议。 | ||
| 602 | - | ||
| 603 | -### 现有系统能力评估 | ||
| 604 | - | ||
| 605 | -| 功能 | 现有支持 | 客户需求 | 差距 | | ||
| 606 | -|------|---------|---------|------| | ||
| 607 | -| 单个提取方案 | ✅ 完全支持 | ✅ 需要 | 无 | | ||
| 608 | -| 多阶段分时提取 | ❌ 不支持 | ✅ 需要 | 本文档已设计方案 | | ||
| 609 | -| **多个提取方案** | ❌ 不支持 | ⏳ 待确认 | **需确认后设计** | | ||
| 610 | - | ||
| 611 | -### 多方案架构设计建议 | ||
| 612 | -```javascript | ||
| 613 | -formData = { | ||
| 614 | - // ... 基础字段 | ||
| 615 | - | ||
| 616 | - withdrawal_enabled: '是', | ||
| 617 | - | ||
| 618 | - // 多方案模式 | ||
| 619 | - withdrawal_scenarios: [ | ||
| 620 | - { | ||
| 621 | - scenario_id: 1, | ||
| 622 | - scenario_name: '方案一:教育+创业+退休', | ||
| 623 | - withdrawal_mode: '多阶段分时提取', | ||
| 624 | - withdrawal_stages: [ | ||
| 625 | - { stage_name: '教育基金', start_age: 18, end_age: 21, withdrawal_type: '每年提取', annual_amount: 50000 }, | ||
| 626 | - { stage_name: '创业金', start_age: 30, end_age: 30, withdrawal_type: '一笔过', lump_sum_amount: 400000 }, | ||
| 627 | - { stage_name: '退休金', start_age: 50, end_age: 100, withdrawal_type: '每年提取', annual_amount: 70000 } | ||
| 628 | - ] | ||
| 629 | - }, | ||
| 630 | - { | ||
| 631 | - scenario_id: 2, | ||
| 632 | - scenario_name: '方案二:长期年金', | ||
| 633 | - withdrawal_mode: '单阶段定期提取', | ||
| 634 | - withdrawal_start_age: 9, | ||
| 635 | - withdrawal_period: 92, // 9-100岁 | ||
| 636 | - annual_withdrawal_amount: 17500 | ||
| 637 | - }, | ||
| 638 | - { | ||
| 639 | - scenario_id: 3, | ||
| 640 | - scenario_name: '方案三:财富传承', | ||
| 641 | - withdrawal_mode: '不提取', | ||
| 642 | - withdrawal_stages: [] | ||
| 643 | - } | ||
| 644 | - ] | ||
| 645 | -} | ||
| 646 | -``` | ||
| 647 | - | ||
| 648 | -#### 方案 B:多次提交,分别生成方案(推荐) | ||
| 649 | - | ||
| 650 | -**优点**: | ||
| 651 | -- ✅ 表单简单,复用现有逻辑 | ||
| 652 | -- ✅ 无需后端改动 | ||
| 653 | -- ✅ 用户可灵活组合不同方案 | ||
| 654 | - | ||
| 655 | -**缺点**: | ||
| 656 | -- ❌ 每个方案单独生成,无法直接对比 | ||
| 657 | -- ❌ 用户需要多次操作 | ||
| 658 | - | ||
| 659 | -**用户流程**: | ||
| 660 | -``` | ||
| 661 | -用户填写方案一 → 提交 → 生成计划书1(方案一) | ||
| 662 | - ↓ | ||
| 663 | -用户点击"添加方案" → 填写方案二 → 提交 → 生成计划书2(方案二) | ||
| 664 | - ↓ | ||
| 665 | -用户点击"添加方案" → 填写方案三 → 提交 → 生成计划书3(方案三) | ||
| 666 | - ↓ | ||
| 667 | -计划书列表展示 3 个方案,用户可选择查看/分享 | ||
| 668 | -``` | ||
| 669 | - | ||
| 670 | -#### 方案 C:混合方案(最佳用户体验) | ||
| 671 | - | ||
| 672 | -**第一阶段**:快速实现 | ||
| 673 | -- 支持多次提交(方案B) | ||
| 674 | -- 在计划书列表页添加"同一客户的多个方案"分组展示 | ||
| 675 | - | ||
| 676 | -**第二阶段**:完整方案 | ||
| 677 | -- 支持单次提交多方案(方案A) | ||
| 678 | -- 计划书生成后可展示方案对比表格 | ||
| 679 | - | ||
| 680 | -### 实施建议 | ||
| 681 | - | ||
| 682 | -#### 短期(MVP) | ||
| 683 | -1. **实现多阶段提取**(本文档原有方案) | ||
| 684 | -2. **支持多次提交不同方案** | ||
| 685 | - - 在计划书列表中按客户/产品分组 | ||
| 686 | - - 允许用户快速复制已有方案创建新方案 | ||
| 687 | - | ||
| 688 | -#### 中期(完整多方案) | ||
| 689 | -1. **改造表单支持多方案** | ||
| 690 | - - 添加"方案管理"组件 | ||
| 691 | - - 支持添加/删除/切换方案 | ||
| 692 | -2. **后端支持多方案数据结构** | ||
| 693 | - - 新增 `withdrawal_scenarios_json` 字段 | ||
| 694 | -3. **计划书生成支持方案对比** | ||
| 695 | - | ||
| 696 | -#### 技术架构调整 | ||
| 697 | - | ||
| 698 | -```javascript | ||
| 699 | -// 数据结构扩展 | ||
| 700 | -formData = { | ||
| 701 | - // ... 基础字段 | ||
| 702 | - | ||
| 703 | - // 新增:方案模式 | ||
| 704 | - scenario_mode: 'single' | 'multiple', // 单方案 | 多方案 | ||
| 705 | - | ||
| 706 | - // 单方案模式(现有逻辑复用) | ||
| 707 | - withdrawal_enabled: '是', | ||
| 708 | - withdrawal_mode: '单阶段定期提取' | '多阶段分时提取', | ||
| 709 | - // ... 单方案字段 | ||
| 710 | - | ||
| 711 | - // 多方案模式(新增) | ||
| 712 | - withdrawal_scenarios: [ | ||
| 713 | - { | ||
| 714 | - scenario_id: Date.now(), | ||
| 715 | - scenario_name: '方案一', | ||
| 716 | - withdrawal_mode: '多阶段分时提取', | ||
| 717 | - withdrawal_stages: [...] | ||
| 718 | - }, | ||
| 719 | - { | ||
| 720 | - scenario_id: Date.now() + 1, | ||
| 721 | - scenario_name: '方案二', | ||
| 722 | - withdrawal_mode: '单阶段定期提取', | ||
| 723 | - // ... 单方案字段 | ||
| 724 | - } | ||
| 725 | - ] | ||
| 726 | -} | ||
| 727 | -``` | ||
| 728 | - | ||
| 729 | ---- | ||
| 730 | - | ||
| 731 | ## 待确认事项 | 533 | ## 待确认事项 |
| 732 | 534 | ||
| 733 | ### 与客户确认 | 535 | ### 与客户确认 |
| 734 | 536 | ||
| 735 | #### 多阶段提取功能 | 537 | #### 多阶段提取功能 |
| 736 | -- [ ] 确认是否需要"多阶段分时提取"功能 | 538 | +- [x] 确认需要"多阶段分时提取"功能 ✅ |
| 539 | +- [x] 确认一份计划书只有一个提取方案 ✅ | ||
| 737 | - [ ] 确认最多支持多少个阶段(设计方案:1-5个) | 540 | - [ ] 确认最多支持多少个阶段(设计方案:1-5个) |
| 738 | - [ ] 确认预设方案是否满足需求(教育+创业+退休、退休年金、财富传承) | 541 | - [ ] 确认预设方案是否满足需求(教育+创业+退休、退休年金、财富传承) |
| 739 | - [ ] 确认后端是否能够接收 `withdrawal_stages_json` 字段 | 542 | - [ ] 确认后端是否能够接收 `withdrawal_stages_json` 字段 |
| 740 | - [ ] 确认多阶段提取的计划书生成方式 | 543 | - [ ] 确认多阶段提取的计划书生成方式 |
| 741 | 544 | ||
| 742 | -#### 多方案功能(重要) | ||
| 743 | -- [ ] **确认是否需要在一个计划书中展示多个提取方案** | ||
| 744 | -- [ ] 确认方案数量上限(客户资料中显示3个方案) | ||
| 745 | -- [ ] 确认实施方案: | ||
| 746 | - - **方案A**:单次提交多方案(需要后端改动) | ||
| 747 | - - **方案B**:多次提交不同方案(快速实现,无需后端改动) | ||
| 748 | - - **方案C**:混合方案(先B后A) | ||
| 749 | -- [ ] 确认后端是否能够接收 `withdrawal_scenarios_json` 字段 | ||
| 750 | - | ||
| 751 | ### 技术细节 | 545 | ### 技术细节 |
| 752 | 546 | ||
| 753 | - [ ] 确认一笔过提取时的年龄表示方式(start_age = end_age) | 547 | - [ ] 确认一笔过提取时的年龄表示方式(start_age = end_age) |
| 754 | - [ ] 确认金额单位(前端:分,后端:元) | 548 | - [ ] 确认金额单位(前端:分,后端:元) |
| 755 | - [ ] 确认阶段数量上限(设计方案:5个) | 549 | - [ ] 确认阶段数量上限(设计方案:5个) |
| 756 | -- [ ] 确认多方案UI交互设计(Tab切换?卡片列表?) | ||
| 757 | 550 | ||
| 758 | --- | 551 | --- |
| 759 | 552 | ||
| 760 | ## 附录 | 553 | ## 附录 |
| 761 | 554 | ||
| 762 | -### 客户文档提取方案参考 | 555 | +### 多阶段提取方案示例 |
| 763 | 556 | ||
| 764 | -**方案一(3岁小公主)**: | 557 | +**示例:教育+创业+退休组合** |
| 765 | -``` | ||
| 766 | -18-21岁:每年 5万(教育基金) | ||
| 767 | -30岁:一笔过 40万(创业金/婚嫁金)← "一笔过" = 一次性提取 | ||
| 768 | -50-100岁:每年 7万(退休养老年金) | ||
| 769 | ``` | 558 | ``` |
| 559 | +阶段1 - 教育基金: | ||
| 560 | + 18-21岁,每年提取 5万 | ||
| 770 | 561 | ||
| 771 | -> **术语说明**:"一笔过"是客户使用的术语,等同于"一次性提取",即在特定年龄一次性全额提取保单价值,而非分期提取。 | 562 | +阶段2 - 创业金/婚嫁金: |
| 563 | + 30岁,一笔过提取 40万 | ||
| 772 | 564 | ||
| 773 | -**方案二(37岁)**: | 565 | +阶段3 - 退休养老年金: |
| 774 | -``` | 566 | + 50-100岁,每年提取 7万 |
| 775 | -43-100岁:每年 3.75万(退休年金) | ||
| 776 | ``` | 567 | ``` |
| 777 | 568 | ||
| 778 | -**方案三(财富传承)**: | 569 | +> **术语说明**:"一笔过"是客户使用的术语,等同于"一次性提取",即在特定年龄一次性全额提取保单价值,而非分期提取。 |
| 779 | -``` | ||
| 780 | -不填写提取计划,展示保单价值增长表 | ||
| 781 | -``` | ||
| 782 | 570 | ||
| 783 | ### 现有系统架构参考 | 571 | ### 现有系统架构参考 |
| 784 | 572 | ... | ... |
| 1 | +# 客户新需求记录(2026-02-25) | ||
| 2 | + | ||
| 3 | +> **创建时间**: 2026-02-25 | ||
| 4 | +> **状态**: ⏳ 待确认方案 | ||
| 5 | +> **优先级**: 中 | ||
| 6 | + | ||
| 7 | +--- | ||
| 8 | + | ||
| 9 | +## 📋 需求清单 | ||
| 10 | + | ||
| 11 | +### 需求 1:重疾类产品缴费年期新增选项 | ||
| 12 | + | ||
| 13 | +**描述**:重疾类产品申请时,模板内"缴费年期"增加"直至65岁"选项 | ||
| 14 | + | ||
| 15 | +**影响产品**: | ||
| 16 | +- ✅ MPC 守护无间重疾(已有产品) | ||
| 17 | +- ✅ MBC PRO 活跃人生重疾保 PRO(已有产品) | ||
| 18 | +- ✅ MBC2 活跃人生重疾保 2(已有产品) | ||
| 19 | + | ||
| 20 | +**当前状态**: | ||
| 21 | +- 现有缴费年期选项:10年、20年、25年 | ||
| 22 | +- 需要新增:直至65岁 | ||
| 23 | + | ||
| 24 | +**技术实现**: | ||
| 25 | +- 文件:`src/config/plan-templates.js` | ||
| 26 | +- 修改三款重疾产品的 `payment_periods` 配置 | ||
| 27 | + | ||
| 28 | +--- | ||
| 29 | + | ||
| 30 | +### 需求 2:年龄与出生年月日二选一填写 | ||
| 31 | + | ||
| 32 | +**描述**:所有产品申请时,年龄与出生年月日改为二选一进行填写 | ||
| 33 | + | ||
| 34 | +**影响范围**: | ||
| 35 | +- ✅ 所有产品类型(人寿、重疾、储蓄) | ||
| 36 | + | ||
| 37 | +**当前状态**: | ||
| 38 | +- 现有:必填出生年月日,年龄自动计算 | ||
| 39 | +- 需求:二选一填写(填年龄自动计算生日,或填生日自动计算年龄) | ||
| 40 | + | ||
| 41 | +**UI 交互设计**: | ||
| 42 | +``` | ||
| 43 | +┌─────────────────────────────────┐ | ||
| 44 | +│ 出生日期(二选一) │ | ||
| 45 | +├─────────────────────────────────┤ | ||
| 46 | +│ ○ 填写年龄 │ | ||
| 47 | +│ 年龄: [ 30 ] 岁 │ | ||
| 48 | +│ │ | ||
| 49 | +│ ○ 填写出生年月日 │ | ||
| 50 | +│ 生日: [ 1990-01-01 ] │ | ||
| 51 | +└─────────────────────────────────┘ | ||
| 52 | +``` | ||
| 53 | + | ||
| 54 | +**技术实现**: | ||
| 55 | +- 新增表单字段:`age_input_mode` | ||
| 56 | +- 修改 Schema:条件显示年龄或生日输入 | ||
| 57 | +- 添加自动计算逻辑 | ||
| 58 | + | ||
| 59 | +--- | ||
| 60 | + | ||
| 61 | +### 需求 3:储蓄类产品多阶段提取方案 | ||
| 62 | + | ||
| 63 | +**描述**:储蓄类产品的提取方案改为可设置多阶段 | ||
| 64 | + | ||
| 65 | +**客户确认**: | ||
| 66 | +- ✅ 一份计划书里只有一个提取方案,不会有多个 | ||
| 67 | +- ✅ 需要支持在一个方案中设置多个不同阶段的提取计划 | ||
| 68 | + | ||
| 69 | +**示例**(教育+创业+退休组合): | ||
| 70 | +``` | ||
| 71 | +┌──────────────────────────────────────────┐ | ||
| 72 | +│ 方案一:多阶段分时提取 │ | ||
| 73 | +├──────────────────────────────────────────┤ | ||
| 74 | +│ 阶段1: 教育基金 │ | ||
| 75 | +│ 18-21岁,每年提取 5万 │ | ||
| 76 | +│ │ | ||
| 77 | +│ 阶段2: 创业金/婚嫁金 │ | ||
| 78 | +│ 30岁,一笔过提取 40万 │ | ||
| 79 | +│ │ | ||
| 80 | +│ 阶段3: 退休养老年金 │ | ||
| 81 | +│ 50-100岁,每年提取 7万 │ | ||
| 82 | +└──────────────────────────────────────────┘ | ||
| 83 | +``` | ||
| 84 | + | ||
| 85 | +**设计文档**:参见本目录下的 `多阶段提取方案设计.md` | ||
| 86 | + | ||
| 87 | +--- | ||
| 88 | + | ||
| 89 | +## 📊 实施计划 | ||
| 90 | + | ||
| 91 | +### 优先级排序 | ||
| 92 | + | ||
| 93 | +| 优先级 | 需求 | 预估工时 | 依赖 | | ||
| 94 | +|--------|------|----------|------| | ||
| 95 | +| **P1** | 需求1:重疾缴费年期 | 30分钟 | 无 | | ||
| 96 | +| **P2** | 需求2:年龄生日二选一 | 2-3小时 | 无 | | ||
| 97 | +| **P3** | 需求3:多阶段提取 | 4-5小时 | 需求2 | | ||
| 98 | + | ||
| 99 | +### 实施步骤 | ||
| 100 | + | ||
| 101 | +#### 第 1 步:重疾缴费年期新增选项(30分钟) | ||
| 102 | + | ||
| 103 | +**文件**:`src/config/plan-templates.js` | ||
| 104 | + | ||
| 105 | +**修改内容**: | ||
| 106 | +```javascript | ||
| 107 | +// 为三款重疾产品的 payment_periods 添加 "直至65岁" 选项 | ||
| 108 | +payment_periods: [ | ||
| 109 | + '10 年(15 日 - 65 岁)', | ||
| 110 | + '20 年(15 日 - 65 岁)', | ||
| 111 | + '25 年(15 日 - 60 岁)', | ||
| 112 | + '直至65岁' // 新增 | ||
| 113 | +] | ||
| 114 | +``` | ||
| 115 | + | ||
| 116 | +--- | ||
| 117 | + | ||
| 118 | +#### 第 2 步:年龄与出生年月日二选一(2-3小时) | ||
| 119 | + | ||
| 120 | +**涉及文件**: | ||
| 121 | +1. `src/config/plan-templates.js` - Schema 配置 | ||
| 122 | +2. `src/components/plan/PlanFields/` - 新增组件 | ||
| 123 | +3. `src/components/plan/PlanTemplates/` - 模板组件逻辑 | ||
| 124 | + | ||
| 125 | +**Schema 扩展**: | ||
| 126 | +```javascript | ||
| 127 | +base_fields: [ | ||
| 128 | + // 新增:年龄输入模式选择 | ||
| 129 | + { | ||
| 130 | + id: 'age_input_mode', | ||
| 131 | + key: 'age_input_mode', | ||
| 132 | + type: 'radio', | ||
| 133 | + label: '请选择输入方式', | ||
| 134 | + options: ['填写年龄', '填写出生年月日'], | ||
| 135 | + required: true, | ||
| 136 | + default: '填写出生年月日' | ||
| 137 | + }, | ||
| 138 | + // 年龄输入(条件显示) | ||
| 139 | + { | ||
| 140 | + id: 'age', | ||
| 141 | + key: 'age', | ||
| 142 | + type: 'age', | ||
| 143 | + label: '年龄', | ||
| 144 | + placeholder: '请输入年龄', | ||
| 145 | + required: true, | ||
| 146 | + show_when: { field: 'age_input_mode', op: 'eq', value: '填写年龄' }, | ||
| 147 | + clear_when_hidden: true | ||
| 148 | + }, | ||
| 149 | + // 出生年月日输入(条件显示) | ||
| 150 | + { | ||
| 151 | + id: 'birthday', | ||
| 152 | + key: 'birthday', | ||
| 153 | + type: 'date', | ||
| 154 | + label: '出生年月日', | ||
| 155 | + placeholder: '请选择年月日', | ||
| 156 | + required: true, | ||
| 157 | + show_when: { field: 'age_input_mode', op: 'eq', value: '填写出生年月日' }, | ||
| 158 | + clear_when_hidden: true | ||
| 159 | + } | ||
| 160 | +] | ||
| 161 | +``` | ||
| 162 | + | ||
| 163 | +**自动计算逻辑**: | ||
| 164 | +- 填写年龄 → 自动计算出生年月日(默认当年生日) | ||
| 165 | +- 填写出生年月日 → 自动计算年龄(当前年份 - 出生年份) | ||
| 166 | + | ||
| 167 | +--- | ||
| 168 | + | ||
| 169 | +#### 第 3 步:多阶段提取方案(4-5小时) | ||
| 170 | + | ||
| 171 | +详见 `多阶段提取方案设计.md` | ||
| 172 | + | ||
| 173 | +--- | ||
| 174 | + | ||
| 175 | +## 📝 待确认事项 | ||
| 176 | + | ||
| 177 | +### 客户确认 | ||
| 178 | + | ||
| 179 | +- [ ] **需求1确认**: | ||
| 180 | + - [ ] 确认"直至65岁"选项文字表述 | ||
| 181 | + - [ ] 确认是否需要年龄限制(如:最大投保年龄) | ||
| 182 | + | ||
| 183 | +- [ ] **需求2确认**: | ||
| 184 | + - [ ] 确认默认选项(建议:填写出生年月日) | ||
| 185 | + - [ ] 确认填写年龄时的默认生日(建议:当年生日) | ||
| 186 | + | ||
| 187 | +- [ ] **需求3确认**: | ||
| 188 | + - [ ] 确认阶段数量上限(设计方案:1-5个) | ||
| 189 | + - [ ] 确认预设方案模板(教育+创业+退休、退休年金、财富传承) | ||
| 190 | + | ||
| 191 | +### 技术确认 | ||
| 192 | + | ||
| 193 | +- [ ] 后端能否接收新增字段: | ||
| 194 | + - `age_input_mode` | ||
| 195 | + - `withdrawal_stages_json` | ||
| 196 | +- [ ] 后端年龄计算逻辑是否与前端一致 | ||
| 197 | + | ||
| 198 | +--- | ||
| 199 | + | ||
| 200 | +## 🔗 相关文档 | ||
| 201 | + | ||
| 202 | +- [多阶段提取方案设计](./多阶段提取方案设计.md) | ||
| 203 | +- [计划书架构文档](../../plan/plan-entry-architecture.md) | ||
| 204 | +- [Schema 配置规范](../../plan/plan-config-schema-reference.md) | ||
| 205 | + | ||
| 206 | +--- | ||
| 207 | + | ||
| 208 | +**文档版本**: 1.0.0 | ||
| 209 | +**最后更新**: 2026-02-25 |
src/api/mock/hotProducts.js
deleted
100644 → 0
| 1 | -/** | ||
| 2 | - * 热卖产品 Mock 数据 | ||
| 3 | - * | ||
| 4 | - * @description 包含项目所有保险类型的产品 mock 数据,用于测试计划书模板显示 | ||
| 5 | - * 遵循项目 mock 数据规范,集成到 src/utils/mockData.js | ||
| 6 | - * @module api/mock/hotProducts | ||
| 7 | - * @author Claude Code | ||
| 8 | - * @created 2026-02-09 | ||
| 9 | - */ | ||
| 10 | - | ||
| 11 | -/** | ||
| 12 | - * 热卖产品 Mock 数据列表 | ||
| 13 | - * @description 包含 9 种产品,覆盖所有计划书模板类型 | ||
| 14 | - */ | ||
| 15 | -const HOT_PRODUCTS = [ | ||
| 16 | - // ====== 人寿保险 (LifeInsuranceTemplate) ====== | ||
| 17 | - | ||
| 18 | - { | ||
| 19 | - id: 1, | ||
| 20 | - product_name: 'WIOP3E 盈传创富保障计划 3 - 优选版', | ||
| 21 | - name: 'WIOP3E 盈传创富保障计划 3 - 优选版', | ||
| 22 | - recommend: 'hot', | ||
| 23 | - form_sn: 'life-insurance-wiop3e', // 对应 LifeInsuranceTemplate | ||
| 24 | - categories: [ | ||
| 25 | - { id: 'life', name: '人寿保险' } | ||
| 26 | - ], | ||
| 27 | - tags: [ | ||
| 28 | - { id: 't1', name: '终身寿险', bg_color: '#DBEAFE', text_color: '#1E40AF' }, | ||
| 29 | - { id: 't2', name: '美元产品', bg_color: '#DCFCE7', text_color: '#166534' } | ||
| 30 | - ], | ||
| 31 | - cover_image: 'https://picsum.photos/seed/wiop3e/400/300' | ||
| 32 | - }, | ||
| 33 | - | ||
| 34 | - { | ||
| 35 | - id: 2, | ||
| 36 | - product_name: 'WIOP3 盈传创富保障计划 3', | ||
| 37 | - name: 'WIOP3 盈传创富保障计划 3', | ||
| 38 | - recommend: 'hot', | ||
| 39 | - form_sn: 'life-insurance-wiop3', // 对应 LifeInsuranceTemplate | ||
| 40 | - categories: [ | ||
| 41 | - { id: 'life', name: '人寿保险' } | ||
| 42 | - ], | ||
| 43 | - tags: [ | ||
| 44 | - { id: 't1', name: '终身寿险', bg_color: '#DBEAFE', text_color: '#1E40AF' }, | ||
| 45 | - { id: 't2', name: '美元产品', bg_color: '#DCFCE7', text_color: '#166534' } | ||
| 46 | - ], | ||
| 47 | - cover_image: 'https://picsum.photos/seed/wiop3/400/300' | ||
| 48 | - }, | ||
| 49 | - | ||
| 50 | - // ====== 重疾保险 (CriticalIllnessTemplate) ====== | ||
| 51 | - | ||
| 52 | - { | ||
| 53 | - id: 3, | ||
| 54 | - product_name: 'MPC 守护无间重疾', | ||
| 55 | - name: 'MPC 守护无间重疾', | ||
| 56 | - recommend: 'hot', | ||
| 57 | - form_sn: 'critical-illness-mpc', // 对应 CriticalIllnessTemplate | ||
| 58 | - categories: [ | ||
| 59 | - { id: 'critical', name: '重疾保险' } | ||
| 60 | - ], | ||
| 61 | - tags: [ | ||
| 62 | - { id: 't1', name: '重疾保障', bg_color: '#FEF3C7', text_color: '#92400E' }, | ||
| 63 | - { id: 't2', name: '终身保障', bg_color: '#E0E7FF', text_color: '#3730A3' } | ||
| 64 | - ], | ||
| 65 | - cover_image: 'https://picsum.photos/seed/mpc/400/300' | ||
| 66 | - }, | ||
| 67 | - | ||
| 68 | - { | ||
| 69 | - id: 4, | ||
| 70 | - product_name: 'MBC PRO 活跃人生重疾保 PRO', | ||
| 71 | - name: 'MBC PRO 活跃人生重疾保 PRO', | ||
| 72 | - recommend: 'hot', | ||
| 73 | - form_sn: 'critical-illness-mbc-pro', // 对应 CriticalIllnessTemplate | ||
| 74 | - categories: [ | ||
| 75 | - { id: 'critical', name: '重疾保险' } | ||
| 76 | - ], | ||
| 77 | - tags: [ | ||
| 78 | - { id: 't1', name: '重疾保障', bg_color: '#FEF3C7', text_color: '#92400E' }, | ||
| 79 | - { id: 't2', name: 'PRO 版本', bg_color: '#FEE2E2', text_color: '#991B1B' } | ||
| 80 | - ], | ||
| 81 | - cover_image: 'https://picsum.photos/seed/mbc-pro/400/300' | ||
| 82 | - }, | ||
| 83 | - | ||
| 84 | - { | ||
| 85 | - id: 5, | ||
| 86 | - product_name: 'MBC2 活跃人生重疾保 2', | ||
| 87 | - name: 'MBC2 活跃人生重疾保 2', | ||
| 88 | - recommend: 'hot', | ||
| 89 | - form_sn: 'critical-illness-mbc2', // 对应 CriticalIllnessTemplate | ||
| 90 | - categories: [ | ||
| 91 | - { id: 'critical', name: '重疾保险' } | ||
| 92 | - ], | ||
| 93 | - tags: [ | ||
| 94 | - { id: 't1', name: '重疾保障', bg_color: '#FEF3C7', text_color: '#92400E' }, | ||
| 95 | - { id: 't2', name: '升级版', bg_color: '#DCFCE7', text_color: '#166534' } | ||
| 96 | - ], | ||
| 97 | - cover_image: 'https://picsum.photos/seed/mbc2/400/300' | ||
| 98 | - }, | ||
| 99 | - | ||
| 100 | - // ====== 储蓄型产品 (SavingsTemplate) ====== | ||
| 101 | - | ||
| 102 | - { | ||
| 103 | - id: 6, | ||
| 104 | - product_name: 'GS 宏挚传承保障计划', | ||
| 105 | - name: 'GS 宏挚传承保障计划', | ||
| 106 | - recommend: 'hot', | ||
| 107 | - form_sn: 'savings-gs', // 对应 SavingsTemplate | ||
| 108 | - categories: [ | ||
| 109 | - { id: 'savings', name: '储蓄保险' } | ||
| 110 | - ], | ||
| 111 | - tags: [ | ||
| 112 | - { id: 't1', name: '储蓄型', bg_color: '#E0E7FF', text_color: '#3730A3' }, | ||
| 113 | - { id: 't2', name: '传承规划', bg_color: '#F3E8FF', text_color: '#6B21A8' } | ||
| 114 | - ], | ||
| 115 | - cover_image: 'https://picsum.photos/seed/gs/400/300' | ||
| 116 | - }, | ||
| 117 | - | ||
| 118 | - { | ||
| 119 | - id: 7, | ||
| 120 | - product_name: 'GC 宏挚家传保险计划', | ||
| 121 | - name: 'GC 宏挚家传保险计划', | ||
| 122 | - recommend: 'hot', | ||
| 123 | - form_sn: 'savings-gc', // 对应 SavingsTemplate | ||
| 124 | - categories: [ | ||
| 125 | - { id: 'savings', name: '储蓄保险' } | ||
| 126 | - ], | ||
| 127 | - tags: [ | ||
| 128 | - { id: 't1', name: '储蓄型', bg_color: '#E0E7FF', text_color: '#3730A3' }, | ||
| 129 | - { id: 't2', name: '家庭保障', bg_color: '#FEE2E2', text_color: '#991B1B' } | ||
| 130 | - ], | ||
| 131 | - cover_image: 'https://picsum.photos/seed/gc/400/300' | ||
| 132 | - }, | ||
| 133 | - | ||
| 134 | - { | ||
| 135 | - id: 8, | ||
| 136 | - product_name: 'FA 宏浚传承保障计划', | ||
| 137 | - name: 'FA 宏浚传承保障计划', | ||
| 138 | - recommend: 'hot', | ||
| 139 | - form_sn: 'savings-fa', // 对应 SavingsTemplate | ||
| 140 | - categories: [ | ||
| 141 | - { id: 'savings', name: '储蓄保险' } | ||
| 142 | - ], | ||
| 143 | - tags: [ | ||
| 144 | - { id: 't1', name: '储蓄型', bg_color: '#E0E7FF', text_color: '#3730A3' }, | ||
| 145 | - { id: 't2', name: '财富传承', bg_color: '#FEF3C7', text_color: '#92400E' } | ||
| 146 | - ], | ||
| 147 | - cover_image: 'https://picsum.photos/seed/fa/400/300' | ||
| 148 | - }, | ||
| 149 | - | ||
| 150 | - { | ||
| 151 | - id: 9, | ||
| 152 | - product_name: 'LV2 赤霞珠终身寿险计划2', | ||
| 153 | - name: 'LV2 赤霞珠终身寿险计划2', | ||
| 154 | - recommend: 'hot', | ||
| 155 | - form_sn: 'savings-lv2', // 对应 SavingsTemplate | ||
| 156 | - categories: [ | ||
| 157 | - { id: 'savings', name: '储蓄保险' } | ||
| 158 | - ], | ||
| 159 | - tags: [ | ||
| 160 | - { id: 't1', name: '储蓄型', bg_color: '#E0E7FF', text_color: '#3730A3' }, | ||
| 161 | - { id: 't2', name: '终身寿险', bg_color: '#DBEAFE', text_color: '#1E40AF' } | ||
| 162 | - ], | ||
| 163 | - cover_image: 'https://picsum.photos/seed/lv2/400/300' | ||
| 164 | - } | ||
| 165 | -] | ||
| 166 | - | ||
| 167 | -/** | ||
| 168 | - * Mock: listAPI (热卖产品) | ||
| 169 | - * @description 专门用于首页热卖产品的 Mock API,支持 form_sn 字段 | ||
| 170 | - * @param {Object} params - 请求参数 | ||
| 171 | - * @param {string} params.recommend - 推荐位(必须为 'hot') | ||
| 172 | - * @returns {Promise<Object>} 模拟的 API 响应 | ||
| 173 | - */ | ||
| 174 | -export async function mockHotProductsListAPI(params) { | ||
| 175 | - const { recommend } = params | ||
| 176 | - | ||
| 177 | - // 只返回热卖产品 | ||
| 178 | - if (recommend !== 'hot') { | ||
| 179 | - return { code: 0, msg: '只支持热卖产品查询', data: { list: [], total: 0 } } | ||
| 180 | - } | ||
| 181 | - | ||
| 182 | - // 模拟网络延迟 | ||
| 183 | - await new Promise(resolve => setTimeout(resolve, 200)) | ||
| 184 | - | ||
| 185 | - console.log('[Mock] hotProductsListAPI - 返回热卖产品', HOT_PRODUCTS.length, '条') | ||
| 186 | - | ||
| 187 | - return { | ||
| 188 | - code: 1, | ||
| 189 | - msg: 'success', | ||
| 190 | - data: { | ||
| 191 | - list: HOT_PRODUCTS, | ||
| 192 | - total: HOT_PRODUCTS.length, | ||
| 193 | - categories: [ | ||
| 194 | - { id: 'life', name: '人寿保险' }, | ||
| 195 | - { id: 'critical', name: '重疾保险' }, | ||
| 196 | - { id: 'savings', name: '储蓄保险' } | ||
| 197 | - ] | ||
| 198 | - } | ||
| 199 | - } | ||
| 200 | -} | ||
| 201 | - | ||
| 202 | -/** | ||
| 203 | - * 导出 Mock API 函数供其他模块使用 | ||
| 204 | - */ | ||
| 205 | -export default { | ||
| 206 | - mockHotProductsListAPI | ||
| 207 | -} |
| ... | @@ -11,10 +11,10 @@ | ... | @@ -11,10 +11,10 @@ |
| 11 | /** | 11 | /** |
| 12 | * 热卖产品 Mock 数据列表 | 12 | * 热卖产品 Mock 数据列表 |
| 13 | * | 13 | * |
| 14 | - * @description 包含 9 种产品,覆盖所有计划书模板类型: | 14 | + * @description 包含 10 种产品,覆盖所有计划书模板类型: |
| 15 | * - 人寿保险 (2 种): WIOP3E、WIOP3 | 15 | * - 人寿保险 (2 种): WIOP3E、WIOP3 |
| 16 | * - 重疾保险 (3 种): MPC、MBC PRO、MBC2 | 16 | * - 重疾保险 (3 种): MPC、MBC PRO、MBC2 |
| 17 | - * - 储蓄型产品 (4 种): GS、GC、FA、LV2 | 17 | + * - 储蓄型产品 (5 种): GS、GC、FA、LV2、GS(多阶段) |
| 18 | */ | 18 | */ |
| 19 | export const hotProductsMockData = [ | 19 | export const hotProductsMockData = [ |
| 20 | // ====== 人寿保险 (LifeInsuranceTemplate) ====== | 20 | // ====== 人寿保险 (LifeInsuranceTemplate) ====== |
| ... | @@ -165,6 +165,25 @@ export const hotProductsMockData = [ | ... | @@ -165,6 +165,25 @@ export const hotProductsMockData = [ |
| 165 | ], | 165 | ], |
| 166 | cover_image: 'https://picsum.photos/seed/lv2/400/300', | 166 | cover_image: 'https://picsum.photos/seed/lv2/400/300', |
| 167 | created_time: '2026-01-09 00:00:00' | 167 | created_time: '2026-01-09 00:00:00' |
| 168 | + }, | ||
| 169 | + | ||
| 170 | + // ====== 储蓄型产品 - 多阶段提取(新增 2026-02-25) ====== | ||
| 171 | + | ||
| 172 | + { | ||
| 173 | + id: 10, | ||
| 174 | + product_name: 'GS 宏挚传承保障计划(多阶段)', | ||
| 175 | + recommend: 'hot', | ||
| 176 | + form_sn: 'savings-gs-multistage', // 对应 SavingsTemplate 多阶段模式 | ||
| 177 | + categories: [ | ||
| 178 | + { id: 'savings', name: '储蓄保险' } | ||
| 179 | + ], | ||
| 180 | + tags: [ | ||
| 181 | + { id: 't1', name: '多阶段提取', bg_color: '#F3E8FF', text_color: '#6B21A8' }, | ||
| 182 | + { id: 't2', name: '新品', bg_color: '#DCFCE7', text_color: '#166534' }, | ||
| 183 | + { id: 't3', name: '灵活规划', bg_color: '#FEF3C7', text_color: '#92400E' } | ||
| 184 | + ], | ||
| 185 | + cover_image: 'https://picsum.photos/seed/gs-multistage/400/300', | ||
| 186 | + created_time: '2026-02-25 00:00:00' | ||
| 168 | } | 187 | } |
| 169 | ] | 188 | ] |
| 170 | 189 | ||
| ... | @@ -288,6 +307,7 @@ export function getAllTemplateTypes() { | ... | @@ -288,6 +307,7 @@ export function getAllTemplateTypes() { |
| 288 | * - ✅ savings-gc - GC 宏挚家传保险计划 | 307 | * - ✅ savings-gc - GC 宏挚家传保险计划 |
| 289 | * - ✅ savings-fa - FA 宏浚传承保障计划 | 308 | * - ✅ savings-fa - FA 宏浚传承保障计划 |
| 290 | * - ✅ savings-lv2 - LV2 赤霞珠终身寿险计划2 | 309 | * - ✅ savings-lv2 - LV2 赤霞珠终身寿险计划2 |
| 310 | + * - ✅ savings-gs-multistage - GS 宏挚传承保障计划(多阶段) - 支持多阶段提取 | ||
| 291 | * | 311 | * |
| 292 | * ## 测试步骤 | 312 | * ## 测试步骤 |
| 293 | * | 313 | * | ... | ... |
| ... | @@ -25,7 +25,92 @@ | ... | @@ -25,7 +25,92 @@ |
| 25 | 25 | ||
| 26 | <div class="border-t border-gray-200 my-6"></div> | 26 | <div class="border-t border-gray-200 my-6"></div> |
| 27 | 27 | ||
| 28 | - <div v-if="config.withdrawal_plan?.enabled" class="withdrawal-plan-section"> | 28 | + <!-- 多阶段提取计划(优先显示) --> |
| 29 | + <div v-if="isMultiStageMode" class="multi-stage-withdrawal-section"> | ||
| 30 | + <h3 class="text-base font-semibold text-gray-900 mb-4"> | ||
| 31 | + 款项提取(允许减少名义金额) | ||
| 32 | + </h3> | ||
| 33 | + | ||
| 34 | + <!-- 阶段卡片列表 --> | ||
| 35 | + <div | ||
| 36 | + v-for="(stage, index) in stages" | ||
| 37 | + :key="index" | ||
| 38 | + class="stage-card bg-white border border-gray-200 rounded-lg p-4 mb-4" | ||
| 39 | + > | ||
| 40 | + <!-- 阶段标题 --> | ||
| 41 | + <div class="flex items-center justify-between mb-3"> | ||
| 42 | + <h4 class="text-sm font-medium text-gray-900">阶段{{ index + 1 }}</h4> | ||
| 43 | + <!-- 删除按钮(≥12岁且至少有2个阶段时显示) --> | ||
| 44 | + <nut-button | ||
| 45 | + v-if="canRemoveStage && index > 0" | ||
| 46 | + size="small" | ||
| 47 | + type="danger" | ||
| 48 | + @click="removeStage(index)" | ||
| 49 | + > | ||
| 50 | + 删除 | ||
| 51 | + </nut-button> | ||
| 52 | + </div> | ||
| 53 | + | ||
| 54 | + <!-- 每年提取金额 --> | ||
| 55 | + <PlanFieldAmount | ||
| 56 | + v-model="stage.annual_withdrawal_amount" | ||
| 57 | + label="每年提取金额" | ||
| 58 | + placeholder="请输入每年提取金额" | ||
| 59 | + inputLabel="请输入每年提取金额" | ||
| 60 | + :required="true" | ||
| 61 | + :currency="config.withdrawal_plan?.default_currency || config.currency" | ||
| 62 | + class="mb-3" | ||
| 63 | + /> | ||
| 64 | + | ||
| 65 | + <!-- 由几岁开始 --> | ||
| 66 | + <PlanFieldAgePicker | ||
| 67 | + v-model="stage.withdrawal_start_age" | ||
| 68 | + label="由几岁开始" | ||
| 69 | + placeholder="请输入开始提取年龄" | ||
| 70 | + :required="true" | ||
| 71 | + class="mb-3" | ||
| 72 | + /> | ||
| 73 | + | ||
| 74 | + <!-- 提取期 --> | ||
| 75 | + <PlanFieldSelect | ||
| 76 | + v-model="stage.withdrawal_period" | ||
| 77 | + label="提取期" | ||
| 78 | + placeholder="请选择提取期" | ||
| 79 | + :required="true" | ||
| 80 | + :options="multiStagePeriodOptions" | ||
| 81 | + class="mb-3" | ||
| 82 | + /> | ||
| 83 | + | ||
| 84 | + <!-- 每年递增提取之百分比(可选) --> | ||
| 85 | + <div class="percentage-field"> | ||
| 86 | + <div class="text-sm text-gray-700 mb-2 flex items-center"> | ||
| 87 | + <span>每年递增提取之百分比(%)</span> | ||
| 88 | + <span class="text-gray-400 text-xs ml-2">(可选)</span> | ||
| 89 | + </div> | ||
| 90 | + <nut-input | ||
| 91 | + v-model="stage.annual_increase_percentage" | ||
| 92 | + type="digit" | ||
| 93 | + placeholder="请输入递增百分比" | ||
| 94 | + @input="(value) => onPercentageInput(value, `stages.${index}.annual_increase_percentage`)" | ||
| 95 | + class="w-full" | ||
| 96 | + /> | ||
| 97 | + </div> | ||
| 98 | + </div> | ||
| 99 | + | ||
| 100 | + <!-- 添加阶段按钮(≥12岁且未达上限时显示) --> | ||
| 101 | + <nut-button | ||
| 102 | + v-if="canAddStage" | ||
| 103 | + type="primary" | ||
| 104 | + block | ||
| 105 | + @click="addStage" | ||
| 106 | + class="add-stage-btn" | ||
| 107 | + > | ||
| 108 | + + 添加阶段 | ||
| 109 | + </nut-button> | ||
| 110 | + </div> | ||
| 111 | + | ||
| 112 | + <!-- 单阶段提取计划(原有逻辑) --> | ||
| 113 | + <div v-else-if="config.withdrawal_plan?.enabled" class="withdrawal-plan-section"> | ||
| 29 | <template v-for="field in withdrawalFields" :key="field.id || field.key"> | 114 | <template v-for="field in withdrawalFields" :key="field.id || field.key"> |
| 30 | <h3 v-if="field.section_title" class="text-base font-semibold text-gray-900 mb-4"> | 115 | <h3 v-if="field.section_title" class="text-base font-semibold text-gray-900 mb-4"> |
| 31 | {{ field.section_title }} | 116 | {{ field.section_title }} |
| ... | @@ -75,7 +160,7 @@ | ... | @@ -75,7 +160,7 @@ |
| 75 | * :config="templateConfig" | 160 | * :config="templateConfig" |
| 76 | * /> | 161 | * /> |
| 77 | */ | 162 | */ |
| 78 | -import { reactive, watch, computed } from 'vue' | 163 | +import { reactive, watch, computed, ref } from 'vue' |
| 79 | import Taro from '@tarojs/taro' | 164 | import Taro from '@tarojs/taro' |
| 80 | import PlanFieldName from '../PlanFields/NameInput.vue' | 165 | import PlanFieldName from '../PlanFields/NameInput.vue' |
| 81 | import PlanFieldAgePicker from '../PlanFields/AgePickerGlobal.vue' | 166 | import PlanFieldAgePicker from '../PlanFields/AgePickerGlobal.vue' |
| ... | @@ -224,6 +309,158 @@ const getFieldProps = (field) => { | ... | @@ -224,6 +309,158 @@ const getFieldProps = (field) => { |
| 224 | 309 | ||
| 225 | const { isFieldVisible } = useFieldDependencies(form, fieldDefinitions) | 310 | const { isFieldVisible } = useFieldDependencies(form, fieldDefinitions) |
| 226 | 311 | ||
| 312 | +// ====== 多阶段提取计划逻辑 ====== | ||
| 313 | + | ||
| 314 | +/** | ||
| 315 | + * 是否为多阶段模式 | ||
| 316 | + * @description 检查产品配置是否启用了 multi_stage_withdrawal | ||
| 317 | + */ | ||
| 318 | +const isMultiStageMode = computed(() => { | ||
| 319 | + return props.config?.multi_stage_withdrawal?.enabled === true | ||
| 320 | +}) | ||
| 321 | + | ||
| 322 | +/** | ||
| 323 | + * 多阶段配置对象(便捷访问) | ||
| 324 | + */ | ||
| 325 | +const multiStageConfig = computed(() => { | ||
| 326 | + return props.config?.multi_stage_withdrawal || {} | ||
| 327 | +}) | ||
| 328 | + | ||
| 329 | +/** | ||
| 330 | + * 多阶段提取期选项(包含"一笔过") | ||
| 331 | + */ | ||
| 332 | +const multiStagePeriodOptions = computed(() => { | ||
| 333 | + return multiStageConfig.value.withdrawal_periods || | ||
| 334 | + props.config?.withdrawal_plan?.withdrawal_periods || | ||
| 335 | + [] | ||
| 336 | +}) | ||
| 337 | + | ||
| 338 | +/** | ||
| 339 | + * 阶段数据数组 | ||
| 340 | + * @description 每个阶段包含:annual_withdrawal_amount, withdrawal_start_age, withdrawal_period, annual_increase_percentage | ||
| 341 | + */ | ||
| 342 | +const stages = ref([]) | ||
| 343 | + | ||
| 344 | +/** | ||
| 345 | + * 是否可添加阶段 | ||
| 346 | + * @description 年龄 ≥ 阈值且未达上限 | ||
| 347 | + */ | ||
| 348 | +const canAddStage = computed(() => { | ||
| 349 | + const age = parseInt(form.age) || 0 | ||
| 350 | + const threshold = multiStageConfig.value.age_threshold || 12 | ||
| 351 | + const limit = multiStageConfig.value.stage_limit || 4 | ||
| 352 | + return age >= threshold && stages.value.length < limit | ||
| 353 | +}) | ||
| 354 | + | ||
| 355 | +/** | ||
| 356 | + * 是否可删除阶段 | ||
| 357 | + * @description 年龄 ≥ 阈值且至少有2个阶段 | ||
| 358 | + */ | ||
| 359 | +const canRemoveStage = computed(() => { | ||
| 360 | + const age = parseInt(form.age) || 0 | ||
| 361 | + const threshold = multiStageConfig.value.age_threshold || 12 | ||
| 362 | + return age >= threshold && stages.value.length > 1 | ||
| 363 | +}) | ||
| 364 | + | ||
| 365 | +/** | ||
| 366 | + * 创建空的阶段数据 | ||
| 367 | + * @returns {Object} 空阶段对象 | ||
| 368 | + */ | ||
| 369 | +const createStage = () => ({ | ||
| 370 | + annual_withdrawal_amount: null, | ||
| 371 | + withdrawal_start_age: null, | ||
| 372 | + withdrawal_period: null, | ||
| 373 | + annual_increase_percentage: null | ||
| 374 | +}) | ||
| 375 | + | ||
| 376 | +/** | ||
| 377 | + * 初始化阶段数据 | ||
| 378 | + * @description 根据年龄初始化阶段数量: | ||
| 379 | + * - 年龄 < 12岁:固定 3 组 | ||
| 380 | + * - 年龄 ≥ 12岁:初始 1 组 | ||
| 381 | + */ | ||
| 382 | +const initializeStages = () => { | ||
| 383 | + const age = parseInt(form.age) || 0 | ||
| 384 | + const threshold = multiStageConfig.value.age_threshold || 12 | ||
| 385 | + | ||
| 386 | + if (age < threshold) { | ||
| 387 | + // 固定 3 组 | ||
| 388 | + stages.value = [createStage(), createStage(), createStage()] | ||
| 389 | + } else { | ||
| 390 | + // 初始 1 组 | ||
| 391 | + stages.value = [createStage()] | ||
| 392 | + } | ||
| 393 | +} | ||
| 394 | + | ||
| 395 | +/** | ||
| 396 | + * 添加新阶段 | ||
| 397 | + * @description 在当前阶段列表末尾添加一个空阶段 | ||
| 398 | + */ | ||
| 399 | +const addStage = () => { | ||
| 400 | + if (!canAddStage.value) return | ||
| 401 | + stages.value.push(createStage()) | ||
| 402 | +} | ||
| 403 | + | ||
| 404 | +/** | ||
| 405 | + * 删除指定阶段 | ||
| 406 | + * @param {number} index - 要删除的阶段索引 | ||
| 407 | + */ | ||
| 408 | +const removeStage = (index) => { | ||
| 409 | + if (!canRemoveStage.value) return | ||
| 410 | + stages.value.splice(index, 1) | ||
| 411 | +} | ||
| 412 | + | ||
| 413 | +/** | ||
| 414 | + * 同步阶段数据到表单 | ||
| 415 | + * @description 将 stages 数组同步到 form.withdrawal_stages,以便父组件获取 | ||
| 416 | + * 同时清理 undefined 值为 null,确保提交数据格式正确 | ||
| 417 | + */ | ||
| 418 | +watch( | ||
| 419 | + stages, | ||
| 420 | + (newStages) => { | ||
| 421 | + // 清理每个阶段的 undefined 值为 null | ||
| 422 | + const cleanedStages = newStages.map(stage => ({ | ||
| 423 | + annual_withdrawal_amount: stage.annual_withdrawal_amount ?? null, | ||
| 424 | + withdrawal_start_age: stage.withdrawal_start_age ?? null, | ||
| 425 | + withdrawal_period: stage.withdrawal_period ?? null, | ||
| 426 | + annual_increase_percentage: stage.annual_increase_percentage ?? null | ||
| 427 | + })) | ||
| 428 | + form.withdrawal_stages = cleanedStages | ||
| 429 | + }, | ||
| 430 | + { deep: true } | ||
| 431 | +) | ||
| 432 | + | ||
| 433 | +// 监听年龄变化,重新初始化阶段(仅在多阶段模式下) | ||
| 434 | +watch( | ||
| 435 | + () => form.age, | ||
| 436 | + (newAge, oldAge) => { | ||
| 437 | + if (isMultiStageMode.value && newAge !== oldAge && newAge !== undefined) { | ||
| 438 | + const threshold = multiStageConfig.value.age_threshold || 12 | ||
| 439 | + const newAgeInt = parseInt(newAge) || 0 | ||
| 440 | + const oldAgeInt = parseInt(oldAge) || 0 | ||
| 441 | + | ||
| 442 | + // 跨越阈值时重新初始化 | ||
| 443 | + if ((newAgeInt < threshold && oldAgeInt >= threshold) || | ||
| 444 | + (newAgeInt >= threshold && oldAgeInt < threshold)) { | ||
| 445 | + initializeStages() | ||
| 446 | + } | ||
| 447 | + } | ||
| 448 | + } | ||
| 449 | +) | ||
| 450 | + | ||
| 451 | +// 组件挂载时初始化阶段(多阶段模式) | ||
| 452 | +watch( | ||
| 453 | + isMultiStageMode, | ||
| 454 | + (enabled) => { | ||
| 455 | + if (enabled && stages.value.length === 0) { | ||
| 456 | + initializeStages() | ||
| 457 | + } | ||
| 458 | + }, | ||
| 459 | + { immediate: true } | ||
| 460 | +) | ||
| 461 | + | ||
| 462 | +// ====== 原有表单逻辑 ====== | ||
| 463 | + | ||
| 227 | /** | 464 | /** |
| 228 | * 获取 Schema 默认值 | 465 | * 获取 Schema 默认值 |
| 229 | * @param {Object} value - 当前表单数据 | 466 | * @param {Object} value - 当前表单数据 |
| ... | @@ -371,7 +608,7 @@ watch( | ... | @@ -371,7 +608,7 @@ watch( |
| 371 | /** | 608 | /** |
| 372 | * 百分比输入清洗,避免非法字符 | 609 | * 百分比输入清洗,避免非法字符 |
| 373 | * @param {string|number} value - 输入值 | 610 | * @param {string|number} value - 输入值 |
| 374 | - * @param {string} key - 目标字段 key | 611 | + * @param {string} key - 目标字段 key(支持多阶段路径:stages.${index}.annual_increase_percentage) |
| 375 | */ | 612 | */ |
| 376 | const onPercentageInput = (value, key) => { | 613 | const onPercentageInput = (value, key) => { |
| 377 | // 转换为字符串(处理 value 为 null 或其他类型的情况) | 614 | // 转换为字符串(处理 value 为 null 或其他类型的情况) |
| ... | @@ -401,7 +638,17 @@ const onPercentageInput = (value, key) => { | ... | @@ -401,7 +638,17 @@ const onPercentageInput = (value, key) => { |
| 401 | } | 638 | } |
| 402 | } | 639 | } |
| 403 | 640 | ||
| 404 | - form[key] = cleaned | 641 | + // 处理多阶段路径(如 stages.0.annual_increase_percentage) |
| 642 | + if (key.startsWith('stages.')) { | ||
| 643 | + const pathParts = key.split('.') | ||
| 644 | + const stageIndex = parseInt(pathParts[1]) | ||
| 645 | + const fieldKey = pathParts[2] | ||
| 646 | + if (!Number.isNaN(stageIndex) && stages.value[stageIndex]) { | ||
| 647 | + stages.value[stageIndex][fieldKey] = cleaned | ||
| 648 | + } | ||
| 649 | + } else { | ||
| 650 | + form[key] = cleaned | ||
| 651 | + } | ||
| 405 | } | 652 | } |
| 406 | 653 | ||
| 407 | const isEmptyValue = (value) => { | 654 | const isEmptyValue = (value) => { |
| ... | @@ -434,6 +681,12 @@ const isFieldRequired = (field) => { | ... | @@ -434,6 +681,12 @@ const isFieldRequired = (field) => { |
| 434 | * @returns {boolean} 校验是否通过 | 681 | * @returns {boolean} 校验是否通过 |
| 435 | */ | 682 | */ |
| 436 | const validate = () => { | 683 | const validate = () => { |
| 684 | + // 多阶段模式校验 | ||
| 685 | + if (isMultiStageMode.value) { | ||
| 686 | + return validateMultiStage() | ||
| 687 | + } | ||
| 688 | + | ||
| 689 | + // 单阶段模式校验(原有逻辑) | ||
| 437 | const fields = [...baseFields.value, ...(props.config.withdrawal_plan?.enabled ? withdrawalFields.value : [])] | 690 | const fields = [...baseFields.value, ...(props.config.withdrawal_plan?.enabled ? withdrawalFields.value : [])] |
| 438 | 691 | ||
| 439 | // 年龄与出生年月日二选一校验 | 692 | // 年龄与出生年月日二选一校验 |
| ... | @@ -484,6 +737,77 @@ const validate = () => { | ... | @@ -484,6 +737,77 @@ const validate = () => { |
| 484 | } | 737 | } |
| 485 | 738 | ||
| 486 | /** | 739 | /** |
| 740 | + * 多阶段表单校验 | ||
| 741 | + * @description 校验每个阶段的必填字段 | ||
| 742 | + * @returns {boolean} 校验是否通过 | ||
| 743 | + */ | ||
| 744 | +const validateMultiStage = () => { | ||
| 745 | + // 基础字段校验(年龄与出生年月日) | ||
| 746 | + const hasAge = !isEmptyValue(form.age) | ||
| 747 | + const hasBirthday = !isEmptyValue(form.birthday) | ||
| 748 | + | ||
| 749 | + if (!hasAge && !hasBirthday) { | ||
| 750 | + Taro.showToast({ title: '年龄与出生年月日至少填写一项', icon: 'none' }) | ||
| 751 | + return false | ||
| 752 | + } | ||
| 753 | + | ||
| 754 | + // 如果都填写了,以生日为准,重新计算年龄 | ||
| 755 | + if (hasAge && hasBirthday) { | ||
| 756 | + const birthYear = new Date(form.birthday).getFullYear() | ||
| 757 | + const currentYear = new Date().getFullYear() | ||
| 758 | + form.age = currentYear - birthYear | ||
| 759 | + } | ||
| 760 | + | ||
| 761 | + // 基础字段校验(其他字段) | ||
| 762 | + for (const field of baseFields.value) { | ||
| 763 | + if (field.key === 'age') continue // 已处理 | ||
| 764 | + | ||
| 765 | + if (isFieldRequired(field)) { | ||
| 766 | + const value = form[field.key] | ||
| 767 | + if (isEmptyValue(value)) { | ||
| 768 | + Taro.showToast({ title: getRequiredMessage(field), icon: 'none' }) | ||
| 769 | + return false | ||
| 770 | + } | ||
| 771 | + } | ||
| 772 | + } | ||
| 773 | + | ||
| 774 | + // 多阶段字段校验 | ||
| 775 | + for (let i = 0; i < stages.value.length; i++) { | ||
| 776 | + const stage = stages.value[i] | ||
| 777 | + const stageLabel = `阶段${i + 1}` | ||
| 778 | + | ||
| 779 | + // 每年提取金额(必填) | ||
| 780 | + if (isEmptyValue(stage.annual_withdrawal_amount)) { | ||
| 781 | + Taro.showToast({ title: `${stageLabel}:请输入每年提取金额`, icon: 'none' }) | ||
| 782 | + return false | ||
| 783 | + } | ||
| 784 | + | ||
| 785 | + // 由几岁开始(必填) | ||
| 786 | + if (isEmptyValue(stage.withdrawal_start_age)) { | ||
| 787 | + Taro.showToast({ title: `${stageLabel}:请输入由几岁开始`, icon: 'none' }) | ||
| 788 | + return false | ||
| 789 | + } | ||
| 790 | + | ||
| 791 | + // 提取期(必填) | ||
| 792 | + if (isEmptyValue(stage.withdrawal_period)) { | ||
| 793 | + Taro.showToast({ title: `${stageLabel}:请选择提取期`, icon: 'none' }) | ||
| 794 | + return false | ||
| 795 | + } | ||
| 796 | + | ||
| 797 | + // 每年递增提取之百分比(可选,校验范围) | ||
| 798 | + if (!isEmptyValue(stage.annual_increase_percentage)) { | ||
| 799 | + const percentage = parseFloat(stage.annual_increase_percentage) | ||
| 800 | + if (Number.isNaN(percentage) || percentage < 0 || percentage > 100) { | ||
| 801 | + Taro.showToast({ title: `${stageLabel}:递增百分比请输入0-100之间的数值`, icon: 'none' }) | ||
| 802 | + return false | ||
| 803 | + } | ||
| 804 | + } | ||
| 805 | + } | ||
| 806 | + | ||
| 807 | + return true | ||
| 808 | +} | ||
| 809 | + | ||
| 810 | +/** | ||
| 487 | * 清除验证错误 | 811 | * 清除验证错误 |
| 488 | * @description 由于使用 Toast 显示错误,无需清除状态 | 812 | * @description 由于使用 Toast 显示错误,无需清除状态 |
| 489 | * 保留此方法以保持接口一致性 | 813 | * 保留此方法以保持接口一致性 |
| ... | @@ -506,4 +830,30 @@ defineExpose({ | ... | @@ -506,4 +830,30 @@ defineExpose({ |
| 506 | padding-left: 20rpx !important; | 830 | padding-left: 20rpx !important; |
| 507 | } | 831 | } |
| 508 | } | 832 | } |
| 833 | + | ||
| 834 | +/* 多阶段提取计划样式 */ | ||
| 835 | +.multi-stage-withdrawal-section { | ||
| 836 | + .stage-card { | ||
| 837 | + background: #ffffff; | ||
| 838 | + border-radius: 12rpx; | ||
| 839 | + padding: 32rpx; | ||
| 840 | + margin-bottom: 32rpx; | ||
| 841 | + border: 1rpx solid #e5e7eb; | ||
| 842 | + } | ||
| 843 | + | ||
| 844 | + .stage-title { | ||
| 845 | + font-size: 28rpx; | ||
| 846 | + font-weight: 600; | ||
| 847 | + color: #111827; | ||
| 848 | + } | ||
| 849 | + | ||
| 850 | + .percentage-field { | ||
| 851 | + margin-top: 24rpx; | ||
| 852 | + } | ||
| 853 | + | ||
| 854 | + .add-stage-btn { | ||
| 855 | + margin-top: 24rpx; | ||
| 856 | + border-radius: 12rpx; | ||
| 857 | + } | ||
| 858 | +} | ||
| 509 | </style> | 859 | </style> | ... | ... |
| ... | @@ -111,6 +111,23 @@ const savingsFormSchema = { | ... | @@ -111,6 +111,23 @@ const savingsFormSchema = { |
| 111 | // 当 withdrawal_mode 切换时,不可见的字段会自动清空 | 111 | // 当 withdrawal_mode 切换时,不可见的字段会自动清空 |
| 112 | } | 112 | } |
| 113 | 113 | ||
| 114 | +/** | ||
| 115 | + * 多阶段提取计划配置 | ||
| 116 | + * @description 用于"宏挚传承保障计划(多阶段)"等支持多阶段提取的产品 | ||
| 117 | + * @updated 2026-02-25 - 新增多阶段提取功能 | ||
| 118 | + */ | ||
| 119 | +const multiStageWithdrawalConfig = { | ||
| 120 | + enabled: true, | ||
| 121 | + stage_limit: 4, // 阶段上限(预留修改空间) | ||
| 122 | + age_threshold: 12, // 年龄阈值(<12岁固定3组,≥12岁可添加) | ||
| 123 | + withdrawal_periods: [ | ||
| 124 | + '1年', '2年', '3年', '5年', | ||
| 125 | + '10年', '15年', '20年', '终身', | ||
| 126 | + '一笔过' // 新增:一次性提取选项 | ||
| 127 | + ], | ||
| 128 | + percentage_optional: true // 递增百分比可选 | ||
| 129 | +} | ||
| 130 | + | ||
| 114 | export const PLAN_TEMPLATES = { | 131 | export const PLAN_TEMPLATES = { |
| 115 | // 人寿保险产品 - WIOP3E | 132 | // 人寿保险产品 - WIOP3E |
| 116 | 'life-insurance-wiop3e': { | 133 | 'life-insurance-wiop3e': { |
| ... | @@ -208,7 +225,7 @@ export const PLAN_TEMPLATES = { | ... | @@ -208,7 +225,7 @@ export const PLAN_TEMPLATES = { |
| 208 | 225 | ||
| 209 | // ====== 储蓄型产品(统一逻辑) ====== | 226 | // ====== 储蓄型产品(统一逻辑) ====== |
| 210 | 227 | ||
| 211 | - // GS - 宏挚传承保障计划 | 228 | + // GS - 宏挚传承保障计划(普通模式) |
| 212 | 'savings-gs': { | 229 | 'savings-gs': { |
| 213 | name: '宏挚传承保障计划', | 230 | name: '宏挚传承保障计划', |
| 214 | component: 'SavingsTemplate', | 231 | component: 'SavingsTemplate', |
| ... | @@ -351,6 +368,38 @@ export const PLAN_TEMPLATES = { | ... | @@ -351,6 +368,38 @@ export const PLAN_TEMPLATES = { |
| 351 | submit_mapping: savingsSubmitMapping | 368 | submit_mapping: savingsSubmitMapping |
| 352 | } | 369 | } |
| 353 | }, | 370 | }, |
| 371 | + | ||
| 372 | + // GS - 宏挚传承保障计划(多阶段模式)⭐ 新增 | ||
| 373 | + // @description 支持多阶段提取计划,根据年龄控制阶段数量 | ||
| 374 | + // @updated 2026-02-25 - 新增多阶段提取功能 | ||
| 375 | + 'savings-gs-multistage': { | ||
| 376 | + name: '宏挚传承保障计划(多阶段)', | ||
| 377 | + component: 'SavingsTemplate', | ||
| 378 | + category: 'savings', | ||
| 379 | + config: { | ||
| 380 | + currency: 'USD', | ||
| 381 | + payment_periods: [ | ||
| 382 | + '整付', | ||
| 383 | + '3 年', | ||
| 384 | + '5 年', | ||
| 385 | + '10 年', | ||
| 386 | + '15 年', | ||
| 387 | + ], | ||
| 388 | + age_range: { min: 0, max: 100 }, | ||
| 389 | + insurance_period: '终身', | ||
| 390 | + // 多阶段提取计划配置 | ||
| 391 | + multi_stage_withdrawal: multiStageWithdrawalConfig, | ||
| 392 | + withdrawal_plan: { | ||
| 393 | + enabled: true, | ||
| 394 | + currencies: ['HKD', 'USD', 'CNY'], | ||
| 395 | + default_currency: 'USD', | ||
| 396 | + withdrawal_modes: ['指定提取金额'], // 多阶段模式只支持指定提取金额 | ||
| 397 | + withdrawal_periods: multiStageWithdrawalConfig.withdrawal_periods | ||
| 398 | + }, | ||
| 399 | + form_schema: savingsFormSchema, | ||
| 400 | + submit_mapping: savingsSubmitMapping | ||
| 401 | + } | ||
| 402 | + }, | ||
| 354 | } | 403 | } |
| 355 | 404 | ||
| 356 | /** | 405 | /** | ... | ... |
-
Please register or login to post a comment