plan-entry-architecture.md 22.2 KB

计划书录入功能模块 - 架构设计方案

创建时间: 2026-02-06 设计师: Claude Code 状态: ✅ 已批准 批准时间: 2026-02-06 版本: v2.1 最后更新: 2026-02-06

更新记录

  • v2.1 (2026-02-06): 更新储蓄型产品提取计划逻辑,明确三层结构和字段分布
  • v2.0 (2026-02-06): 整合储蓄型产品(GS/GC/FA/LV2),新增提取计划功能
  • v1.0 (2026-02-06): 初始版本,支持人寿保险和重疾保险产品

📋 需求总结

核心需求

  1. 产品驱动:一个产品对应一个计划书模版,用户不需要选择模版类型
  2. 保额配置:支持不同币种(人民币、美元等),币种符号可配置
  3. 年龄选择:使用 NutUI Popup + Picker 多列选择器,3位数字,初始值018,传给后端的值是数字18
  4. 缴费年期:key 和 value 相同(如"整付(0-75 岁)")
  5. 日期选择:使用 DatePicker 日期选择器
  6. 扩展性:产品和模版的映射方式需要考虑未来扩展

用户流程

用户在首页/产品详情页点击"计划书"按钮
  ↓
系统根据产品名称/ID自动选择对应的模版
  ↓
显示计划书录入弹窗(包含动态表单)
  ↓
用户填写表单(性别、年龄、出生年月日、是否吸烟、保额、缴费年期等)
  ↓
提交 → 跳转到计划书提交结果页

🔍 现有实现分析

当前架构

首页 (index.vue)
  └─ 点击"计划书"按钮
      └─ 打开 PlanPopup
          ├─ SchemeA (硬编码)
          └─ SchemeB (硬编码)

问题

  1. ❌ 硬编码模版:通过 currentScheme 变量手动选择 SchemeA 或 SchemeB
  2. ❌ 缺少产品-模版映射:没有根据产品自动选择模版的机制
  3. ❌ 表单组件有限:缺少保额(币种配置)、年龄选择器、日期选择器等组件
  4. ❌ 扩展性差:新增产品或模版需要修改代码

现有表单字段

模版 字段 类型
SchemeA 客户姓名、性别、年龄、行业、年收入、家庭结构、保险需求、期望收益率 Input、Radio、Picker、多选标签
SchemeB 币种、计划、附加计划、性别、年龄、保险期间、交费期间、年交保费 固定值、Radio、Input

产品 API 数据结构

{
  id: 1,
  product_name: "WIOP3E 盈传创富保障计划 3 - 优选版",
  form_sn: "xxx",  // 关联表单sn(可能用来关联模版)
  categories: [...],
  tags: [...],
  documents: [...],
  cover_image: "..."
}

计划书文档定义

第一版模版docs/plan/计划书模版.md):

人寿产品(WIOP3E、WIOP3):

  • 核心信息:性别、年龄、出生年月日、是否吸烟
  • 保额
  • 缴费年期:整付(0-75 岁)、5 年(0-70 岁)、10 年(0-70 岁)

重疾产品(MPC、MBC PRO、MBC2):

  • 核心信息:性别、年龄、出生年月日、是否吸烟
  • 保额
  • 缴费年期:10 年(15 日 - 65 岁)、20 年(15 日 - 65 岁)、25 年(15 日 - 60 岁)

第二版模版docs/plan/计划书模版2.docx):

储蓄型产品(统一逻辑):

  • 产品列表:
    • GS(宏摯傳承保障計劃)
    • GC(宏摯家傳承保險計劃)
    • FA(宏浚傳承保障計劃)
    • LV2(赤霞珠終身壽險計劃2)
  • 核心信息:性别、年龄、出生年月日、是否吸烟
  • 保额(年缴保费)
  • 缴费年期:各产品不同(详见配置文件)
  • 提取计划功能(所有储蓄产品通用):

字段结构说明

字段1:是否希望生成一份容许减少名义金额的提取说明?(是/否)

  • 独立字段,不影响下面的提取方案配置
  • 仅用于标识是否需要生成说明文档

字段2:提取选项(二选一):

  • 指定提取金额
  • 最高固定提取金额

字段3-N:根据字段2的选择显示不同字段:

A. 指定提取金额模式: - 提取方式(二选一): 1. 按年岁 2. 按保单年度

- **按年岁**字段(3个):
  - 由几岁开始(withdrawal_start_age)
  - 提取期(年)(withdrawal_period)
  - 每年递增提取之百分比(%)(increase_rate)

- **按保单年度**字段(2个):
  - 由几岁开始(withdrawal_start_age)
  - 提取期(年)(withdrawal_period)

B. 最高固定提取金额模式(2个字段): - 按年岁:由几岁开始(withdrawal_start_age) - 提取期(年)(withdrawal_period)

⚠️ 小程序端特别说明

  • 币种固定(使用配置中的默认币种,如 HKD)
  • 无需手动选择币种字段
  • 无需"每年提取金额"字段(由后端计算)

字段清理逻辑

  • 切换提取方式时,自动清除不相关字段
  • 切换"按年岁"和"按保单年度"时,清除 annual_amount 和 increase_rate
  • "是否希望生成说明"字段不影响任何其他字段

🏗️ 架构设计方案

方案 A:产品 API 驱动(推荐)⭐

核心思路

后端在产品 API 返回数据中已有 form_sn 字段,用于指定该产品使用的计划书模版。

数据结构设计

1. 产品 API 返回数据(已有字段)

// src/api/get_product.js
{
  id: 1,
  product_name: "WIOP3E 盈传创富保障计划 3 - 优选版",
  form_sn: "life-insurance-wiop3e",  // ✅ 已有字段:计划书模版标识(就是模版名称)
  // 注意:如果需要模版配置(如币种、缴费年期选项),可以新增 plan_config 字段
  // 或者前端根据 form_sn 从配置文件中读取对应的配置
  // ... 其他字段
}

2. 模版配置文件

// src/config/plan-templates.js
export const PLAN_TEMPLATES = {
  // form_sn: life-insurance-wiop3e
  'life-insurance-wiop3e': {
    name: 'WIOP3E 盈传创富保障计划 3 - 优选版',
    component: 'LifeInsuranceTemplate',
    // 模版配置(可选:如果后端不提供 plan_config,则使用这里的配置)
    config: {
      currency: "USD",  // 币种:USD/CNY
      payment_periods: [  // 缴费年期选项
        "整付(0-75 岁)",
        "5 年(0-70 岁)",
        "10 年(0-70 岁)"
      ],
      age_range: { min: 0, max: 75 },  // 年龄范围
      insurance_period: "终身"  // 保险期间
    }
  },

  // form_sn: critical-illness-mpc
  'critical-illness-mpc': {
    name: 'MPC 守护无间重疾',
    component: 'CriticalIllnessTemplate',
    config: {
      currency: "CNY",
      payment_periods: [
        "10 年(15 日 - 65 岁)",
        "20 年(15 日 - 65 岁)",
        "25 年(15 日 - 60 岁)"
      ],
      age_range: { min: 0, max: 65 },
      insurance_period: "终身"
    }
  }
}

3. 功能开关配置(未来扩展用)

// src/config/plan-templates.js

/**
 * 全局功能开关
 * @description 用于控制实验性功能或未来扩展功能的开关
 * @example 开启多币种功能:设置 MULTI_CURRENCY_ENABLED = true
 */
export const FEATURE_FLAGS = {
  // 多币种切换功能(当前:关闭,未来:开启)
  // false: 方案 1 - 固定币种(当前实现)
  // true: 方案 2 - 支持多币种切换(未来扩展)
  MULTI_CURRENCY_ENABLED: false,
}

4. 出生日期驱动年龄(推荐方案)

核心逻辑

用户选择"出生年月日"
  ↓
系统自动计算年龄 = 当前年份 - 出生年份
  ↓
年龄字段自动填充
  ↓
用户可以手动修改年龄(特殊情况)

优势

  • 更准确:出生日期是准确的个人信息
  • 自动计算:避免用户手动输入错误
  • 灵活性:用户可以修改年龄
  • 逻辑简单:不需要复杂的日期限制

实现示例

// 用户选择出生日期
const onBirthdayChange = (birthday) => {
  form.birthday = birthday

  // 自动计算年龄
  const birthYear = new Date(birthday).getFullYear()
  const currentYear = new Date().getFullYear()
  form.age = currentYear - birthYear
}

组件架构

PlanFormContainer (计划书表单容器)
  └─ 根据产品 form_sn 动态加载模版组件
      ├─ LifeInsuranceTemplate (人寿保险模版)
      ├─ CriticalIllnessTemplate (重疾保险模版)
      ├─ SavingsTemplate (储蓄型产品模版)
      └─ 其他模版...

动态表单字段组件:
  ├─ AgePicker (年龄选择器)
  ├─ AmountInput (保额输入 + 币种)
  ├─ DatePicker (日期选择器)
  ├─ RadioGroup (单选)
  ├─ SelectPicker (下拉选择)
  └─ WithdrawalPlanSelector (提取计划选择器 - 储蓄产品专用)

提取计划相关组件:
  ├─ WithdrawalPlanSelector.vue (主选择器:方式1/方式2)
  ├─ SpecifiedAmountForm.vue (年龄指定金额表单)
  ├─ FixedAmountForm.vue (最高固定金额表单)
  └─ CurrencySelector.vue (币种选择器)

🎯 推荐方案

选择:方案 A(产品 API 驱动)

理由

  1. 扩展性强:新增产品只需后端配置 plan_template 字段
  2. 维护成本低:产品信息和模版信息都在同一处
  3. 类型安全:前端可以使用 TypeScript 验证模版配置
  4. 支持动态配置:可以通过 CMS 后台管理产品和模版的映射关系

后端配合要求

  1. 产品已有字段form_sn - 模版标识(如 life-insurance-wiop3e

    • ✅ 这个字段已经存在,可以直接使用
  2. 可选增强:如果需要动态配置模版参数(如币种、缴费年期选项),可以新增 plan_config 字段:

    • currency: 币种(USD/CNY)
    • payment_periods: 缴费年期选项数组
    • age_range: 年龄范围({ min, max })
    • insurance_period: 保险期间

注意:如果后端不提供 plan_config,前端会从 src/config/plan-templates.js 配置文件中读取默认配置。


📂 文件结构

src/
├── api/
│   └── plan.js                   # 计划书 API(新增)
│
├── config/
│   └── plan-templates.js         # 计划书模版配置(新增)
│
├── components/
│   ├── PlanFormContainer.vue     # 计划书表单容器(新增)
│   ├── PlanPopup/
│   │   └── index.vue             # 弹窗容器(已有)
│   │
│   ├── PlanTemplates/            # 具体模版组件(新增)
│   │   ├── LifeInsuranceTemplate.vue
│   │   ├── CriticalIllnessTemplate.vue
│   │   ├── SavingsTemplate.vue   # 储蓄型产品模版(GS/GC/FA/LV2)
│   │   └── README.md             # 模版开发文档
│   │
│   └── PlanFields/               # 通用表单字段组件(新增)
│       ├── AgePicker.vue         # 年龄选择器
│       ├── AmountInput.vue       # 保额输入
│       ├── DatePicker.vue        # 日期选择器
│       ├── RadioGroup.vue        # 单选组
│       ├── SelectPicker.vue      # 下拉选择
│       └── WithdrawalPlan/       # 提取计划组件(储蓄产品专用)
│           ├── WithdrawalPlanSelector.vue  # 主选择器
│           ├── SpecifiedAmountForm.vue     # 年龄指定金额表单
│           ├── FixedAmountForm.vue         # 最高固定金额表单
│           └── CurrencySelector.vue        # 币种选择器
│
└── pages/
    ├── index.vue                 # 首页(修改)
    └── product-detail/
        └── index.vue             # 产品详情页(修改)

🔧 表单字段组件设计

1. AgePicker(年龄选择器)

需求

  • 使用 NutUI Popup + Picker 多列样式
  • 年龄范围:0-120 岁
  • 显示格式:3位数字(018)
  • 提交格式:数字(18)

2. AmountInput(保额输入)

需求

  • 支持不同币种(CNY、USD、HKD、EUR)
  • 小数点后2位控制
  • 币种符号可配置

3. DatePicker(出生日期选择器)→ 自动计算年龄

优化方案

  • 用户选择出生年月日
  • 系统自动计算年龄 = 当前年份 - 出生年份
  • 年龄字段自动填充
  • 用户可以手动修改年龄(特殊情况)

4. RadioGroup(单选组)

需求

  • 性别、是否吸烟等单选项

5. SelectPicker(下拉选择)

需求

  • 缴费年期选择
  • key 和 value 相同(如"整付(0-75 岁)")

6. WithdrawalPlanSelector(提取计划选择器)⭐ 储蓄产品专用

业务场景:储蓄型产品(GS/GC/FA/LV2)支持提取计划功能

字段结构

字段1:是否生成说明(独立字段)

问题:是否希望生成一份容许减少名义金额的提取说明?

选项:是 / 否(默认:否)

说明:此字段为独立配置,不影响下面的提取方案

字段2:提取选项

问题:提取选项

选项

  1. 指定提取金额
  2. 最高固定提取金额

字段3-N:具体配置字段(根据字段2选择显示不同字段)

A. 指定提取金额模式

子选项:提取方式

选项

  1. 按年岁
  2. 按保单年度

按年岁字段(3个):

{
  withdrawal_enabled: '是',
  withdrawal_mode: '指定提取金额',
  specified_amount_type: '按年岁',
  withdrawal_start_age: 60,        // 由几岁开始
  withdrawal_period: '10年',        // 提取期(年)
  increase_rate: '5'                // 每年递增提取之百分比(%)
}

按保单年度字段(2个):

{
  withdrawal_enabled: '是',
  withdrawal_mode: '指定提取金额',
  specified_amount_type: '按保单年度',
  withdrawal_start_age: 60,        // 由几岁开始
  withdrawal_period: '10年'         // 提取期(年)
}
B. 最高固定提取金额模式(2个字段)
{
  withdrawal_enabled: '是',
  withdrawal_mode: '最高固定提取金额',
  withdrawal_start_age: 60,        // 按年岁:由几岁开始
  withdrawal_period: '10年'         // 提取期(年)
}

⚠️ 小程序端特别说明

  • 币种固定(从配置文件读取,如 HKD),无需用户选择
  • 无需"每年提取金额"字段(小程序端不需要)
  • 字段清理逻辑:切换模式时自动清除不相关字段

组件设计(独立字段结构)

<template>
  <div>
    <!-- 字段1:是否生成说明(独立字段) -->
    <PlanFieldRadio
      v-model="form.withdrawal_enabled"
      label="是否希望生成一份容许减少名义金额的提取说明?"
      :options="['是', '否']"
    />

    <!-- 字段2:款项提取配置(始终显示) -->
    <h3>款项提取(容许减少名义金额)</h3>

    <!-- 提取选项 -->
    <PlanFieldRadio
      v-model="form.withdrawal_mode"
      label="提取选项"
      :options="['指定提取金额', '最高固定提取金额']"
      @change="onWithdrawalModeChange"
    />

    <!-- 指定提取金额模式 -->
    <template v-if="form.withdrawal_mode === '指定提取金额'">
      <!-- 子选项:提取方式 -->
      <PlanFieldRadio
        v-model="form.specified_amount_type"
        label="提取方式"
        :options="['按年岁', '按保单年度']"
      />

      <!-- 按年岁字段 -->
      <template v-if="form.specified_amount_type === '按年岁'">
        <PlanFieldAgePicker
          v-model="form.withdrawal_start_age"
          label="由几岁开始"
          placeholder="请输入开始提取年龄"
        />

        <PlanFieldSelect
          v-model="form.withdrawal_period"
          label="提取期(年)"
          placeholder="请选择提取期"
          :options="withdrawalPeriods"
        />

        <!-- 每年递增提取之百分比 -->
        <div>
          <div class="text-sm text-gray-700 mb-2">
            每年递增提取之百分比(%)
          </div>
          <nut-input
            v-model="form.increase_rate"
            type="digit"
            placeholder="请输入递增百分比"
          />
        </div>
      </template>

      <!-- 按保单年度字段 -->
      <template v-if="form.specified_amount_type === '按保单年度'">
        <PlanFieldAgePicker
          v-model="form.withdrawal_start_age"
          label="由几岁开始"
          placeholder="请输入开始提取年龄"
        />

        <PlanFieldSelect
          v-model="form.withdrawal_period"
          label="提取期(年)"
          placeholder="请选择提取期"
          :options="withdrawalPeriods"
        />
      </template>
    </template>

    <!-- 最高固定提取金额模式 -->
    <template v-if="form.withdrawal_mode === '最高固定提取金额'">
      <PlanFieldAgePicker
        v-model="form.withdrawal_start_age"
        label="按年岁:由几岁开始"
        placeholder="请输入开始提取年龄"
      />

      <PlanFieldSelect
        v-model="form.withdrawal_period"
        label="提取期(年)"
        placeholder="请选择提取期"
        :options="withdrawalPeriods"
      />
    </template>
  </div>
</template>

<script setup>
// 提取模式切换时的字段清理逻辑
const onWithdrawalModeChange = (mode) => {
  if (mode === '最高固定提取金额') {
    // 最高固定金额模式不需要指定金额的相关字段
    delete form.specified_amount_type
    delete form.increase_rate
  }
}

// 监听提取方式变化
watch(() => form.specified_amount_type, (newType) => {
  // 两种方式都不需要 annual_amount 和 increase_rate(小程序端不需要)
  delete form.annual_amount
  delete form.increase_rate
})
</script>

📊 数据流

用户点击"计划书"按钮
  ↓
openPlanPopup(productId)
  ↓
查找产品对象
  ↓
获取 product.form_sn(已有字段)
  ↓
根据 form_sn 从 PLAN_TEMPLATES 配置中查找对应模版
  ↓
加载对应的模版组件(如 LifeInsuranceTemplate)
  ↓
显示动态表单(使用 product.plan_config 或配置文件中的默认配置)
  ↓
用户填写表单
  ↓
提交 → handlePlanSubmit(formData)
  ↓
跳转到结果页

🚀 实施步骤

阶段 1:基础架构(2-3天)

  1. 创建模版配置文件

    • 创建 src/config/plan-templates.js
    • 定义模版映射关系(人寿、重疾产品)
    • 添加储蓄型产品配置(GS/GC/FA/LV2)
    • 编写模版配置文档
  2. 创建通用表单字段组件

    • AgePicker.vue - 年龄选择器
    • AmountInput.vue - 保额输入
    • DatePicker.vue - 日期选择器
    • RadioGroup.vue - 单选组
    • SelectPicker.vue - 下拉选择
  3. 创建计划书 API

    • 创建 src/api/plan.js
    • 定义提交计划书 API 接口

阶段 2:模版组件(2-3天)

  1. 创建具体模版组件

    • LifeInsuranceTemplate.vue - 人寿保险模版
    • CriticalIllnessTemplate.vue - 重疾保险模版
    • SavingsTemplate.vue - 储蓄型产品模版(GS/GC/FA/LV2)
  2. 创建表单容器

    • PlanFormContainer.vue - 动态模版容器
  3. 创建提取计划组件(储蓄产品专用)

    • WithdrawalPlanSelector.vue - 主选择器
    • SpecifiedAmountForm.vue - 年龄指定金额表单
    • FixedAmountForm.vue - 最高固定金额表单
    • CurrencySelector.vue - 币种选择器

阶段 3:集成测试(1-2天)

  1. 修改首页

    • 集成 PlanFormContainer
    • 传递产品对象
  2. 修改产品详情页

    • 添加"计划书"按钮
    • 集成 PlanFormContainer
  3. 测试

    • 测试不同产品的模版加载
    • 测试表单验证
    • 测试提交流程

📝 API 接口设计

1. 获取产品详情(已存在,包含 form_sn)

请求

GET /srv/?a=get_product&t=detail&i=1

返回

{
  "code": 1,
  "msg": "success",
  "data": {
    "id": 1,
    "product_name": "WIOP3E 盈传创富保障计划 3 - 优选版",
    "form_sn": "life-insurance-wiop3e",  // ✅ 已有字段,用于指定模版
    // 可选:如果需要动态配置模版参数
    "plan_config": {
      "currency": "USD",
      "payment_periods": [
        "整付(0-75 岁)",
        "5 年(0-70 岁)",
        "10 年(0-70 岁)"
      ],
      "age_range": { "min": 0, "max": 75 },
      "insurance_period": "终身"
    },
    // ... 其他现有字段
  }
}

2. 提交计划书(新增)

请求

POST /srv/?a=submit_plan

{
  "product_id": 1,
  "template": "life-insurance-wiop3e",
  "form_data": {
    "gender": "男",
    "age": 18,
    "birthday": "1990-01-01",
    "smoker": "否",
    "coverage": 100000,  // 单位:分
    "payment_period": "10 年(0-70 岁)"
  }
}

返回

{
  "code": 1,
  "msg": "提交成功",
  "data": {
    "plan_id": 123,
    "status": "processing",  // processing | generated
    "estimated_time": 300  // 预计生成时间(秒)
  }
}

✅ 验收标准

  • 用户点击产品"计划书"按钮,自动显示对应的模版
  • 年龄选择器显示3位数字,提交时转换为数字
  • 保额输入支持不同币种,保留2位小数
  • 日期选择器限制年龄范围
  • 缴费年期选项完整,key 和 value 相同
  • 表单验证正常,必填项有提示
  • 提交成功后跳转到结果页
  • 支持扩展新产品模版(无需修改代码)

📚 参考文档


💡 未来扩展

  1. CMS 后台管理

    • 通过后台管理产品和模版的映射关系
    • 动态配置表单字段
    • 无需修改代码即可新增模版
  2. 表单字段扩展

    • 支持更多字段类型(多选、文件上传等)
    • 支持字段联动(如年龄变化影响保额范围)
    • 支持条件显示(如某些产品显示特定字段)
  3. 预览功能

    • 实时预览计划书
    • 保存草稿功能
    • 历史记录查看
  4. 批量生成

    • 支持批量导入客户信息
    • 批量生成计划书
    • 导出为 Excel/PDF

文档版本: v1.0 创建时间: 2026-02-06 最后更新: 2026-02-06