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 @@ ...@@ -5,6 +5,76 @@
5 5
6 --- 6 ---
7 7
8 +## [2026-02-06] - 计划书生成模块架构与经验教训总结
9 +
10 +### 文档
11 +- 创建完整的计划书生成模块架构文档
12 +- 总结所有历史问题和解决方案
13 +- 整理最佳实践和常见陷阱
14 +
15 +### 核心要点
16 +- **架构模式**:配置驱动 + 动态组件加载
17 +- **关键经验**:响应式表单、字段联动、事件处理
18 +- **常见问题**:AmountInput 报错、提取计划字段结构、模板导入路径
19 +
20 +---
21 +
22 +## [2026-02-06] - 新增储蓄型保险计划书模板并修正提取计划逻辑
23 +
24 +### 新增
25 +- 创建 `SavingsTemplate.vue` 组件,支持 GS/GC/FA/LV2 等储蓄型产品
26 +- 实现提取计划配置功能(完全符合产品需求)
27 +
28 +### 修正
29 +- 修正提取计划的字段结构,完全符合需求文档
30 +- 修正提取计划的层级关系和字段联动逻辑
31 +
32 +### 功能特性
33 +- **基础字段**:性别、出生年月日、年龄、是否吸烟、年缴保费、缴费年期
34 +- **提取计划**(完全符合小程序端需求):
35 + 1. **第一层确认**:是否希望生成一份容许减少名义金额的提取说明?(是/否)
36 + 2. **提取选项**(仅当选择"是"时显示):
37 + - **指定提取金额**
38 + - **按年岁**:由几岁开始、提取期(年)、每年递增提取之百分比(%)
39 + - **按保单年度**:由几岁开始、提取期(年)
40 + - **最高固定提取金额**
41 + - **按年岁**:由几岁开始、提取期(年)
42 + - **小程序端币种固定**:使用配置中的默认币种(HKD),不需要用户选择
43 +
44 +### 字段说明
45 +- `withdrawal_enabled`: 是否启用提取计划(是/否)
46 +- `withdrawal_mode`: 提取选项(指定提取金额/最高固定提取金额)
47 +- `specified_amount_type`: 指定提取金额的方式(按年岁/按保单年度)
48 +- `withdrawal_start_age`: 开始提取年龄
49 +- `withdrawal_period`: 提取年期
50 +- `increase_rate`: 每年递增提取之百分比(%)
51 +- `annual_amount`: 每年提取金额(小程序端不需要此字段)
52 +
53 +### 技术实现
54 +- 组件路径:`src/components/PlanTemplates/SavingsTemplate.vue`
55 +- 使用 `computed` 动态读取提取计划配置
56 +- 实现多层条件渲染:根据"是否启用"→"提取选项"→"提取方式"动态显示字段
57 +- 使用 `watch` 监听字段变化,自动清理不相关数据
58 +- 修复 `PlanFormContainer.vue` 中的 `SavingsTemplate` 导入路径
59 +
60 +### 影响范围
61 +- 新增文件:`src/components/PlanTemplates/SavingsTemplate.vue`
62 +- 修改文件:`src/components/PlanFormContainer.vue`(修复导入路径)
63 +
64 +### 修正原因
65 +- 原实现缺少第一层确认("是否希望生成提取说明")
66 +- 原实现缺少"按年岁/按保单年度"的子选项
67 +- 原实现缺少"每年递增提取之百分比"字段
68 +- 原实现将"提取币种"作为独立字段,实际小程序端不需要此字段(币种固定使用配置中的默认币种)
69 +- 原实现字段分配错误:"按保单年度"在需求中只需要两个字段,不是四个字段
70 +
71 +### 最终字段分配(小程序端)
72 +- **按年岁**:由几岁开始、提取期(年)、每年递增提取之百分比(3个字段)
73 +- **按保单年度**:由几岁开始、提取期(年)(2个字段)
74 +- **最高固定提取金额**:按年岁:由几岁开始、提取期(年)(2个字段)
75 +
76 +---
77 +
8 ## [2026-02-06] - 修复保额输入组件输入报错及体验问题 78 ## [2026-02-06] - 修复保额输入组件输入报错及体验问题
9 79
10 ### 修复 80 ### 修复
......
This diff is collapsed. Click to expand it.
1 +# 计划书生成模块 - 快速使用指南
2 +
3 +> **适用场景**:需要在页面中添加"计划书"功能
4 +
5 +---
6 +
7 +## 🚀 5分钟快速集成
8 +
9 +### 1. 在页面中引入组件
10 +
11 +```vue
12 +<script setup>
13 +import { ref } from 'vue'
14 +import PlanFormContainer from '@/components/PlanFormContainer.vue'
15 +
16 +const showPlanPopup = ref(false)
17 +const selectedProduct = ref(null)
18 +</script>
19 +```
20 +
21 +### 2. 绑定按钮点击事件
22 +
23 +```vue
24 +<template>
25 + <!-- 在产品列表或产品详情页 -->
26 + <nut-button
27 + type="primary"
28 + @click="openPlanPopup(product)"
29 + >
30 + 计划书
31 + </nut-button>
32 +</template>
33 +```
34 +
35 +### 3. 打开计划书弹窗
36 +
37 +```javascript
38 +const openPlanPopup = (product) => {
39 + // 确保产品有 form_sn 字段
40 + if (!product.form_sn) {
41 + console.error('产品缺少 form_sn 字段', product)
42 + return
43 + }
44 +
45 + selectedProduct.value = product
46 + showPlanPopup.value = true
47 +}
48 +```
49 +
50 +### 4. 处理提交
51 +
52 +```javascript
53 +const handleClose = () => {
54 + showPlanPopup.value = false
55 +}
56 +
57 +const handleSubmit = (formData) => {
58 + console.log('提交计划书:', formData)
59 +
60 + // 调用 API 提交
61 + submitPlanAPI({
62 + product_id: selectedProduct.value.id,
63 + form_sn: selectedProduct.value.form_sn,
64 + form_data: formData
65 + })
66 +
67 + handleClose()
68 +}
69 +```
70 +
71 +### 5. 渲染弹窗组件
72 +
73 +```vue
74 +<template>
75 + <PlanFormContainer
76 + v-model:visible="showPlanPopup"
77 + :product="selectedProduct"
78 + @close="handleClose"
79 + @submit="handleSubmit"
80 + />
81 +</template>
82 +```
83 +
84 +---
85 +
86 +## ✅ 完整示例
87 +
88 +```vue
89 +<template>
90 + <div class="product-page">
91 + <!-- 产品信息 -->
92 + <nut-card>
93 + <h2>{{ product.product_name }}</h2>
94 + <nut-button
95 + type="primary"
96 + @click="openPlanPopup(product)"
97 + >
98 + 生成计划书
99 + </nut-button>
100 + </nut-card>
101 +
102 + <!-- 计划书弹窗 -->
103 + <PlanFormContainer
104 + v-model:visible="showPlanPopup"
105 + :product="selectedProduct"
106 + @close="handleClose"
107 + @submit="handleSubmit"
108 + />
109 + </div>
110 +</template>
111 +
112 +<script setup>
113 +import { ref } from 'vue'
114 +import PlanFormContainer from '@/components/PlanFormContainer.vue'
115 +import { submitPlanAPI } from '@/api/plan'
116 +
117 +const product = ref({
118 + id: 1,
119 + product_name: "WIOP3E 盈传创富保障计划 3",
120 + form_sn: "life-insurance-wiop3e"
121 +})
122 +
123 +const showPlanPopup = ref(false)
124 +const selectedProduct = ref(null)
125 +
126 +const openPlanPopup = (prod) => {
127 + if (!prod.form_sn) {
128 + Taro.showToast({
129 + title: '产品配置错误',
130 + icon: 'error'
131 + })
132 + return
133 + }
134 +
135 + selectedProduct.value = prod
136 + showPlanPopup.value = true
137 +}
138 +
139 +const handleClose = () => {
140 + showPlanPopup.value = false
141 +}
142 +
143 +const handleSubmit = async (formData) => {
144 + try {
145 + Taro.showLoading({ title: '提交中...' })
146 +
147 + const res = await submitPlanAPI({
148 + product_id: selectedProduct.value.id,
149 + form_sn: selectedProduct.value.form_sn,
150 + form_data: formData
151 + })
152 +
153 + if (res.code === 1) {
154 + Taro.showToast({
155 + title: '提交成功',
156 + icon: 'success'
157 + })
158 +
159 + // 跳转到结果页
160 + Taro.navigateTo({
161 + url: `/pages/plan-submit-result/index?id=${res.data.plan_id}`
162 + })
163 + } else {
164 + Taro.showToast({
165 + title: res.msg || '提交失败',
166 + icon: 'error'
167 + })
168 + }
169 + } catch (err) {
170 + console.error('提交计划书失败:', err)
171 + Taro.showToast({
172 + title: '网络错误',
173 + icon: 'error'
174 + })
175 + } finally {
176 + Taro.hideLoading()
177 + }
178 +
179 + handleClose()
180 +}
181 +</script>
182 +```
183 +
184 +---
185 +
186 +## 🔧 添加新产品支持
187 +
188 +### 步骤 1: 添加配置
189 +
190 +`src/config/plan-templates.js` 中添加:
191 +
192 +```javascript
193 +export const PLAN_TEMPLATES = {
194 + // ... 现有配置
195 +
196 + 'your-product-form-sn': {
197 + name: '产品名称',
198 + component: 'YourProductTemplate',
199 + config: {
200 + currency: 'USD',
201 + payment_periods: ['5 年', '10 年'],
202 + age_range: { min: 0, max: 65 },
203 + insurance_period: '终身'
204 + }
205 + }
206 +}
207 +```
208 +
209 +### 步骤 2: 创建模板
210 +
211 +`src/components/PlanTemplates/YourProductTemplate.vue`
212 +
213 +```vue
214 +<template>
215 + <div v-if="config">
216 + <!-- 使用通用字段组件 -->
217 + <PlanFieldRadio v-model="form.gender" label="性别" :options="['男', '女']" />
218 + <!-- ... 其他字段 -->
219 + </div>
220 +</template>
221 +
222 +<script setup>
223 +import { reactive, watch } from 'vue'
224 +import PlanFieldRadio from '../PlanFields/RadioGroup.vue'
225 +
226 +const props = defineProps({
227 + modelValue: { type: Object, default: () => ({}) },
228 + config: { type: Object, required: true }
229 +})
230 +
231 +const emit = defineEmits(['update:modelValue'])
232 +
233 +const form = reactive({ ...props.modelValue })
234 +
235 +watch(() => form, (newVal) => emit('update:modelValue', newVal), { deep: true })
236 +</script>
237 +```
238 +
239 +### 步骤 3: 注册组件
240 +
241 +`src/components/PlanFormContainer.vue` 中:
242 +
243 +```javascript
244 +import YourProductTemplate from './PlanTemplates/YourProductTemplate.vue'
245 +
246 +const componentMap = {
247 + // ... 现有组件
248 + 'YourProductTemplate': YourProductTemplate
249 +}
250 +```
251 +
252 +---
253 +
254 +## 🐛 常见问题
255 +
256 +### Q1: 弹窗显示"未找到对应的计划书模版"
257 +
258 +**原因**:产品缺少 `form_sn` 字段
259 +
260 +**解决**
261 +```javascript
262 +// 检查产品数据
263 +console.log(product.form_sn) // 应该有值,如 "life-insurance-wiop3e"
264 +
265 +// 如果没有,联系后端添加
266 +```
267 +
268 +---
269 +
270 +### Q2: 表单输入时出现 `value.replace is not a function`
271 +
272 +**原因**:这是已修复的问题
273 +
274 +**解决**:确保使用最新版本的 `AmountInput.vue`
275 +
276 +---
277 +
278 +### Q3: 年龄选择器显示为 018,提交需要 18
279 +
280 +**说明**:这是正常行为
281 +
282 +- **显示**:3 位数字格式(018)
283 +- **提交**:普通数字(18)
284 +
285 +组件会自动处理转换,无需手动处理。
286 +
287 +---
288 +
289 +### Q4: 提取计划字段显示不正确
290 +
291 +**说明**:提取计划有复杂的条件渲染逻辑
292 +
293 +- 第一层:是否启用(是/否)
294 +- 第二层:提取选项(指定提取金额/最高固定提取金额)
295 +- 第三层:具体方式(按年岁/按保单年度)
296 +
297 +确保按顺序选择,相关字段会自动显示。
298 +
299 +---
300 +
301 +## 📚 更多信息
302 +
303 +- [完整架构文档](./plan-entry-architecture.md)
304 +- [经验教训总结](../lessons-learned/plan-entry-module-summary.md)
305 +- [API 联调日志](../api-integration-log.md)
306 +
307 +---
308 +
309 +**最后更新**: 2026-02-06
This diff is collapsed. Click to expand it.
...@@ -43,7 +43,7 @@ import { ref, computed, watch } from 'vue' ...@@ -43,7 +43,7 @@ import { ref, computed, watch } from 'vue'
43 import PlanPopup from './PlanPopup/index.vue' 43 import PlanPopup from './PlanPopup/index.vue'
44 import LifeInsuranceTemplate from './PlanTemplates/LifeInsuranceTemplate.vue' 44 import LifeInsuranceTemplate from './PlanTemplates/LifeInsuranceTemplate.vue'
45 import CriticalIllnessTemplate from './PlanTemplates/CriticalIllnessTemplate.vue' 45 import CriticalIllnessTemplate from './PlanTemplates/CriticalIllnessTemplate.vue'
46 -import SavingsTemplate from './SavingsTemplate.vue' 46 +import SavingsTemplate from './PlanTemplates/SavingsTemplate.vue'
47 import { PLAN_TEMPLATES } from '@/config/plan-templates' 47 import { PLAN_TEMPLATES } from '@/config/plan-templates'
48 48
49 /** 49 /**
......
This diff is collapsed. Click to expand it.
...@@ -103,17 +103,19 @@ export const PLAN_TEMPLATES = { ...@@ -103,17 +103,19 @@ export const PLAN_TEMPLATES = {
103 103
104 // ====== 储蓄型产品(统一逻辑) ====== 104 // ====== 储蓄型产品(统一逻辑) ======
105 105
106 - // GS - 宏摯傳承保障計劃 106 + // GS - 宏挚传承保障计划
107 'savings-gs': { 107 'savings-gs': {
108 - name: '宏摯傳承保障計劃', 108 + name: '宏挚传承保障计划',
109 component: 'SavingsTemplate', 109 component: 'SavingsTemplate',
110 category: 'savings', // 储蓄型产品 110 category: 'savings', // 储蓄型产品
111 config: { 111 config: {
112 currency: 'USD', // 默认美元 112 currency: 'USD', // 默认美元
113 payment_periods: [ 113 payment_periods: [
114 - '整付(0-100 岁)', 114 + '整付',
115 - '5 年(0-80 岁)', 115 + '3 年',
116 - '10 年(0-75 岁)' 116 + '5 年',
117 + '10 年',
118 + '15 年',
117 ], 119 ],
118 age_range: { min: 0, max: 100 }, 120 age_range: { min: 0, max: 100 },
119 insurance_period: '终身', 121 insurance_period: '终身',
...@@ -140,17 +142,17 @@ export const PLAN_TEMPLATES = { ...@@ -140,17 +142,17 @@ export const PLAN_TEMPLATES = {
140 } 142 }
141 }, 143 },
142 144
143 - // GC - 宏摯家傳承保險計劃 145 + // GC - 宏挚家传保险计划
144 'savings-gc': { 146 'savings-gc': {
145 - name: '宏摯家傳承保險計劃', 147 + name: '宏挚家传保险计划',
146 component: 'SavingsTemplate', 148 component: 'SavingsTemplate',
147 category: 'savings', 149 category: 'savings',
148 config: { 150 config: {
149 currency: 'USD', 151 currency: 'USD',
150 payment_periods: [ 152 payment_periods: [
151 - '整付(0-100 岁)', 153 + '整付',
152 - '5 年(0-80 岁)', 154 + '3 年',
153 - '10 年(0-75 岁)' 155 + '5 年',
154 ], 156 ],
155 age_range: { min: 0, max: 100 }, 157 age_range: { min: 0, max: 100 },
156 insurance_period: '终身', 158 insurance_period: '终身',
...@@ -173,17 +175,17 @@ export const PLAN_TEMPLATES = { ...@@ -173,17 +175,17 @@ export const PLAN_TEMPLATES = {
173 } 175 }
174 }, 176 },
175 177
176 - // FA - 宏浚傳承保障計劃 178 + // FA - 宏浚传承保障计划
177 'savings-fa': { 179 'savings-fa': {
178 - name: '宏浚傳承保障計劃', 180 + name: '宏浚传承保障计划',
179 component: 'SavingsTemplate', 181 component: 'SavingsTemplate',
180 category: 'savings', 182 category: 'savings',
181 config: { 183 config: {
182 currency: 'USD', 184 currency: 'USD',
183 payment_periods: [ 185 payment_periods: [
184 - '整付(0-100 岁)', 186 + '整付',
185 - '5 年(0-80 岁)', 187 + '2 年',
186 - '10 年(0-75 岁)' 188 + '5 年',
187 ], 189 ],
188 age_range: { min: 0, max: 100 }, 190 age_range: { min: 0, max: 100 },
189 insurance_period: '终身', 191 insurance_period: '终身',
...@@ -206,17 +208,18 @@ export const PLAN_TEMPLATES = { ...@@ -206,17 +208,18 @@ export const PLAN_TEMPLATES = {
206 } 208 }
207 }, 209 },
208 210
209 - // LV2 - 赤霞珠終身壽險計劃2(储蓄型终身寿险) 211 + // LV2 - 赤霞珠终身寿险计划2(储蓄型终身寿险)
210 'savings-lv2': { 212 'savings-lv2': {
211 - name: '赤霞珠終身壽險計劃2', 213 + name: '赤霞珠终身寿险计划2',
212 component: 'SavingsTemplate', 214 component: 'SavingsTemplate',
213 category: 'savings', 215 category: 'savings',
214 config: { 216 config: {
215 currency: 'USD', 217 currency: 'USD',
216 payment_periods: [ 218 payment_periods: [
217 - '整付(0-100 岁)', 219 + '5 年',
218 - '5 年(0-80 岁)', 220 + '8 年',
219 - '10 年(0-75 岁)' 221 + '12 年',
222 + '15 年',
220 ], 223 ],
221 age_range: { min: 0, max: 100 }, 224 age_range: { min: 0, max: 100 },
222 insurance_period: '终身', 225 insurance_period: '终身',
......