hookehuyr

docs(plan): 创建计划书模块完整文档和经验教训总结

- 创建计划书模块架构和经验教训总结文档
- 创建快速使用指南(5分钟快速集成)
- 更新架构文档,明确提取计划三层结构
- 添加 SavingsTemplate 组件(储蓄型产品模板)
- 修复 PlanFormContainer 中的模板导入路径

影响文件:
- docs/lessons-learned/plan-entry-module-summary.md (新增)
- docs/plan/plan-entry-quick-guide.md (新增)
- docs/CHANGELOG.md (更新)
- docs/plan/plan-entry-architecture.md (更新)
- src/components/PlanTemplates/SavingsTemplate.vue (新增)
- src/components/PlanFormContainer.vue (修复)
......@@ -5,6 +5,76 @@
---
## [2026-02-06] - 计划书生成模块架构与经验教训总结
### 文档
- 创建完整的计划书生成模块架构文档
- 总结所有历史问题和解决方案
- 整理最佳实践和常见陷阱
### 核心要点
- **架构模式**:配置驱动 + 动态组件加载
- **关键经验**:响应式表单、字段联动、事件处理
- **常见问题**:AmountInput 报错、提取计划字段结构、模板导入路径
---
## [2026-02-06] - 新增储蓄型保险计划书模板并修正提取计划逻辑
### 新增
- 创建 `SavingsTemplate.vue` 组件,支持 GS/GC/FA/LV2 等储蓄型产品
- 实现提取计划配置功能(完全符合产品需求)
### 修正
- 修正提取计划的字段结构,完全符合需求文档
- 修正提取计划的层级关系和字段联动逻辑
### 功能特性
- **基础字段**:性别、出生年月日、年龄、是否吸烟、年缴保费、缴费年期
- **提取计划**(完全符合小程序端需求):
1. **第一层确认**:是否希望生成一份容许减少名义金额的提取说明?(是/否)
2. **提取选项**(仅当选择"是"时显示):
- **指定提取金额**
- **按年岁**:由几岁开始、提取期(年)、每年递增提取之百分比(%)
- **按保单年度**:由几岁开始、提取期(年)
- **最高固定提取金额**
- **按年岁**:由几岁开始、提取期(年)
- **小程序端币种固定**:使用配置中的默认币种(HKD),不需要用户选择
### 字段说明
- `withdrawal_enabled`: 是否启用提取计划(是/否)
- `withdrawal_mode`: 提取选项(指定提取金额/最高固定提取金额)
- `specified_amount_type`: 指定提取金额的方式(按年岁/按保单年度)
- `withdrawal_start_age`: 开始提取年龄
- `withdrawal_period`: 提取年期
- `increase_rate`: 每年递增提取之百分比(%)
- `annual_amount`: 每年提取金额(小程序端不需要此字段)
### 技术实现
- 组件路径:`src/components/PlanTemplates/SavingsTemplate.vue`
- 使用 `computed` 动态读取提取计划配置
- 实现多层条件渲染:根据"是否启用"→"提取选项"→"提取方式"动态显示字段
- 使用 `watch` 监听字段变化,自动清理不相关数据
- 修复 `PlanFormContainer.vue` 中的 `SavingsTemplate` 导入路径
### 影响范围
- 新增文件:`src/components/PlanTemplates/SavingsTemplate.vue`
- 修改文件:`src/components/PlanFormContainer.vue`(修复导入路径)
### 修正原因
- 原实现缺少第一层确认("是否希望生成提取说明")
- 原实现缺少"按年岁/按保单年度"的子选项
- 原实现缺少"每年递增提取之百分比"字段
- 原实现将"提取币种"作为独立字段,实际小程序端不需要此字段(币种固定使用配置中的默认币种)
- 原实现字段分配错误:"按保单年度"在需求中只需要两个字段,不是四个字段
### 最终字段分配(小程序端)
- **按年岁**:由几岁开始、提取期(年)、每年递增提取之百分比(3个字段)
- **按保单年度**:由几岁开始、提取期(年)(2个字段)
- **最高固定提取金额**:按年岁:由几岁开始、提取期(年)(2个字段)
---
## [2026-02-06] - 修复保额输入组件输入报错及体验问题
### 修复
......
This diff is collapsed. Click to expand it.
# 计划书生成模块 - 快速使用指南
> **适用场景**:需要在页面中添加"计划书"功能
---
## 🚀 5分钟快速集成
### 1. 在页面中引入组件
```vue
<script setup>
import { ref } from 'vue'
import PlanFormContainer from '@/components/PlanFormContainer.vue'
const showPlanPopup = ref(false)
const selectedProduct = ref(null)
</script>
```
### 2. 绑定按钮点击事件
```vue
<template>
<!-- 在产品列表或产品详情页 -->
<nut-button
type="primary"
@click="openPlanPopup(product)"
>
计划书
</nut-button>
</template>
```
### 3. 打开计划书弹窗
```javascript
const openPlanPopup = (product) => {
// 确保产品有 form_sn 字段
if (!product.form_sn) {
console.error('产品缺少 form_sn 字段', product)
return
}
selectedProduct.value = product
showPlanPopup.value = true
}
```
### 4. 处理提交
```javascript
const handleClose = () => {
showPlanPopup.value = false
}
const handleSubmit = (formData) => {
console.log('提交计划书:', formData)
// 调用 API 提交
submitPlanAPI({
product_id: selectedProduct.value.id,
form_sn: selectedProduct.value.form_sn,
form_data: formData
})
handleClose()
}
```
### 5. 渲染弹窗组件
```vue
<template>
<PlanFormContainer
v-model:visible="showPlanPopup"
:product="selectedProduct"
@close="handleClose"
@submit="handleSubmit"
/>
</template>
```
---
## ✅ 完整示例
```vue
<template>
<div class="product-page">
<!-- 产品信息 -->
<nut-card>
<h2>{{ product.product_name }}</h2>
<nut-button
type="primary"
@click="openPlanPopup(product)"
>
生成计划书
</nut-button>
</nut-card>
<!-- 计划书弹窗 -->
<PlanFormContainer
v-model:visible="showPlanPopup"
:product="selectedProduct"
@close="handleClose"
@submit="handleSubmit"
/>
</div>
</template>
<script setup>
import { ref } from 'vue'
import PlanFormContainer from '@/components/PlanFormContainer.vue'
import { submitPlanAPI } from '@/api/plan'
const product = ref({
id: 1,
product_name: "WIOP3E 盈传创富保障计划 3",
form_sn: "life-insurance-wiop3e"
})
const showPlanPopup = ref(false)
const selectedProduct = ref(null)
const openPlanPopup = (prod) => {
if (!prod.form_sn) {
Taro.showToast({
title: '产品配置错误',
icon: 'error'
})
return
}
selectedProduct.value = prod
showPlanPopup.value = true
}
const handleClose = () => {
showPlanPopup.value = false
}
const handleSubmit = async (formData) => {
try {
Taro.showLoading({ title: '提交中...' })
const res = await submitPlanAPI({
product_id: selectedProduct.value.id,
form_sn: selectedProduct.value.form_sn,
form_data: formData
})
if (res.code === 1) {
Taro.showToast({
title: '提交成功',
icon: 'success'
})
// 跳转到结果页
Taro.navigateTo({
url: `/pages/plan-submit-result/index?id=${res.data.plan_id}`
})
} else {
Taro.showToast({
title: res.msg || '提交失败',
icon: 'error'
})
}
} catch (err) {
console.error('提交计划书失败:', err)
Taro.showToast({
title: '网络错误',
icon: 'error'
})
} finally {
Taro.hideLoading()
}
handleClose()
}
</script>
```
---
## 🔧 添加新产品支持
### 步骤 1: 添加配置
`src/config/plan-templates.js` 中添加:
```javascript
export const PLAN_TEMPLATES = {
// ... 现有配置
'your-product-form-sn': {
name: '产品名称',
component: 'YourProductTemplate',
config: {
currency: 'USD',
payment_periods: ['5 年', '10 年'],
age_range: { min: 0, max: 65 },
insurance_period: '终身'
}
}
}
```
### 步骤 2: 创建模板
`src/components/PlanTemplates/YourProductTemplate.vue`
```vue
<template>
<div v-if="config">
<!-- 使用通用字段组件 -->
<PlanFieldRadio v-model="form.gender" label="性别" :options="['男', '女']" />
<!-- ... 其他字段 -->
</div>
</template>
<script setup>
import { reactive, watch } from 'vue'
import PlanFieldRadio from '../PlanFields/RadioGroup.vue'
const props = defineProps({
modelValue: { type: Object, default: () => ({}) },
config: { type: Object, required: true }
})
const emit = defineEmits(['update:modelValue'])
const form = reactive({ ...props.modelValue })
watch(() => form, (newVal) => emit('update:modelValue', newVal), { deep: true })
</script>
```
### 步骤 3: 注册组件
`src/components/PlanFormContainer.vue` 中:
```javascript
import YourProductTemplate from './PlanTemplates/YourProductTemplate.vue'
const componentMap = {
// ... 现有组件
'YourProductTemplate': YourProductTemplate
}
```
---
## 🐛 常见问题
### Q1: 弹窗显示"未找到对应的计划书模版"
**原因**:产品缺少 `form_sn` 字段
**解决**
```javascript
// 检查产品数据
console.log(product.form_sn) // 应该有值,如 "life-insurance-wiop3e"
// 如果没有,联系后端添加
```
---
### Q2: 表单输入时出现 `value.replace is not a function`
**原因**:这是已修复的问题
**解决**:确保使用最新版本的 `AmountInput.vue`
---
### Q3: 年龄选择器显示为 018,提交需要 18
**说明**:这是正常行为
- **显示**:3 位数字格式(018)
- **提交**:普通数字(18)
组件会自动处理转换,无需手动处理。
---
### Q4: 提取计划字段显示不正确
**说明**:提取计划有复杂的条件渲染逻辑
- 第一层:是否启用(是/否)
- 第二层:提取选项(指定提取金额/最高固定提取金额)
- 第三层:具体方式(按年岁/按保单年度)
确保按顺序选择,相关字段会自动显示。
---
## 📚 更多信息
- [完整架构文档](./plan-entry-architecture.md)
- [经验教训总结](../lessons-learned/plan-entry-module-summary.md)
- [API 联调日志](../api-integration-log.md)
---
**最后更新**: 2026-02-06
This diff is collapsed. Click to expand it.
......@@ -43,7 +43,7 @@ import { ref, computed, watch } from 'vue'
import PlanPopup from './PlanPopup/index.vue'
import LifeInsuranceTemplate from './PlanTemplates/LifeInsuranceTemplate.vue'
import CriticalIllnessTemplate from './PlanTemplates/CriticalIllnessTemplate.vue'
import SavingsTemplate from './SavingsTemplate.vue'
import SavingsTemplate from './PlanTemplates/SavingsTemplate.vue'
import { PLAN_TEMPLATES } from '@/config/plan-templates'
/**
......
This diff is collapsed. Click to expand it.
......@@ -103,17 +103,19 @@ export const PLAN_TEMPLATES = {
// ====== 储蓄型产品(统一逻辑) ======
// GS - 宏摯傳承保障計劃
// GS - 宏挚传承保障计划
'savings-gs': {
name: '宏摯傳承保障計劃',
name: '宏挚传承保障计划',
component: 'SavingsTemplate',
category: 'savings', // 储蓄型产品
config: {
currency: 'USD', // 默认美元
payment_periods: [
'整付(0-100 岁)',
'5 年(0-80 岁)',
'10 年(0-75 岁)'
'整付',
'3 年',
'5 年',
'10 年',
'15 年',
],
age_range: { min: 0, max: 100 },
insurance_period: '终身',
......@@ -140,17 +142,17 @@ export const PLAN_TEMPLATES = {
}
},
// GC - 宏摯家傳承保險計劃
// GC - 宏挚家传保险计划
'savings-gc': {
name: '宏摯家傳承保險計劃',
name: '宏挚家传保险计划',
component: 'SavingsTemplate',
category: 'savings',
config: {
currency: 'USD',
payment_periods: [
'整付(0-100 岁)',
'5 年(0-80 岁)',
'10 年(0-75 岁)'
'整付',
'3 年',
'5 年',
],
age_range: { min: 0, max: 100 },
insurance_period: '终身',
......@@ -173,17 +175,17 @@ export const PLAN_TEMPLATES = {
}
},
// FA - 宏浚傳承保障計劃
// FA - 宏浚传承保障计划
'savings-fa': {
name: '宏浚傳承保障計劃',
name: '宏浚传承保障计划',
component: 'SavingsTemplate',
category: 'savings',
config: {
currency: 'USD',
payment_periods: [
'整付(0-100 岁)',
'5 年(0-80 岁)',
'10 年(0-75 岁)'
'整付',
'2 年',
'5 年',
],
age_range: { min: 0, max: 100 },
insurance_period: '终身',
......@@ -206,17 +208,18 @@ export const PLAN_TEMPLATES = {
}
},
// LV2 - 赤霞珠終身壽險計劃2(储蓄型终身寿险)
// LV2 - 赤霞珠终身寿险计划2(储蓄型终身寿险)
'savings-lv2': {
name: '赤霞珠終身壽險計劃2',
name: '赤霞珠终身寿险计划2',
component: 'SavingsTemplate',
category: 'savings',
config: {
currency: 'USD',
payment_periods: [
'整付(0-100 岁)',
'5 年(0-80 岁)',
'10 年(0-75 岁)'
'5 年',
'8 年',
'12 年',
'15 年',
],
age_range: { min: 0, max: 100 },
insurance_period: '终身',
......