fix(plan): 修复表单提交数据为空问题并添加金额格式化
修复问题: - 修复表单提交时数据为空的问题(submit 中立即重置导致) - 添加金额字段格式化显示(分 → 元),便于调试查看 代码改动: - 移除 submit() 中的 resetForm() 调用,让父组件先处理数据 - 新增 formatAmounts() 函数转换金额单位(分 → 元) - 优化日志输出:同时显示格式化数据(元)和原始数据(分) - 表单重置逻辑统一由 close() 函数处理 文档新增: - docs/接口联调注意事项.md - 完整的 API 联调指南 - 数据单位规范(金额字段单位为"分") - 接口联调流程和注意事项 - 金额转换工具函数 - 常见问题和解决方案 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Showing
5 changed files
with
490 additions
and
11 deletions
| ... | @@ -10,6 +10,7 @@ declare module 'vue' { | ... | @@ -10,6 +10,7 @@ declare module 'vue' { |
| 10 | AgePicker: typeof import('./src/components/PlanFields/AgePicker.vue')['default'] | 10 | AgePicker: typeof import('./src/components/PlanFields/AgePicker.vue')['default'] |
| 11 | AgePickerGlobal: typeof import('./src/components/PlanFields/AgePickerGlobal.vue')['default'] | 11 | AgePickerGlobal: typeof import('./src/components/PlanFields/AgePickerGlobal.vue')['default'] |
| 12 | AmountInput: typeof import('./src/components/PlanFields/AmountInput.vue')['default'] | 12 | AmountInput: typeof import('./src/components/PlanFields/AmountInput.vue')['default'] |
| 13 | + AmountKeyboard: typeof import('./src/components/PlanFields/AmountKeyboard.vue')['default'] | ||
| 13 | CriticalIllnessTemplate: typeof import('./src/components/PlanTemplates/CriticalIllnessTemplate.vue')['default'] | 14 | CriticalIllnessTemplate: typeof import('./src/components/PlanTemplates/CriticalIllnessTemplate.vue')['default'] |
| 14 | DatePicker: typeof import('./src/components/PlanFields/DatePicker.vue')['default'] | 15 | DatePicker: typeof import('./src/components/PlanFields/DatePicker.vue')['default'] |
| 15 | DatePickerGlobal: typeof import('./src/components/PlanFields/DatePickerGlobal.vue')['default'] | 16 | DatePickerGlobal: typeof import('./src/components/PlanFields/DatePickerGlobal.vue')['default'] |
| ... | @@ -28,6 +29,7 @@ declare module 'vue' { | ... | @@ -28,6 +29,7 @@ declare module 'vue' { |
| 28 | NutDatePicker: typeof import('@nutui/nutui-taro')['DatePicker'] | 29 | NutDatePicker: typeof import('@nutui/nutui-taro')['DatePicker'] |
| 29 | NutEmpty: typeof import('@nutui/nutui-taro')['Empty'] | 30 | NutEmpty: typeof import('@nutui/nutui-taro')['Empty'] |
| 30 | NutInput: typeof import('@nutui/nutui-taro')['Input'] | 31 | NutInput: typeof import('@nutui/nutui-taro')['Input'] |
| 32 | + NutNumberKeyboard: typeof import('@nutui/nutui-taro')['NumberKeyboard'] | ||
| 31 | NutPicker: typeof import('@nutui/nutui-taro')['Picker'] | 33 | NutPicker: typeof import('@nutui/nutui-taro')['Picker'] |
| 32 | NutPopup: typeof import('@nutui/nutui-taro')['Popup'] | 34 | NutPopup: typeof import('@nutui/nutui-taro')['Popup'] |
| 33 | NutRadio: typeof import('@nutui/nutui-taro')['Radio'] | 35 | NutRadio: typeof import('@nutui/nutui-taro')['Radio'] |
| ... | @@ -38,7 +40,7 @@ declare module 'vue' { | ... | @@ -38,7 +40,7 @@ declare module 'vue' { |
| 38 | PdfPreview: typeof import('./src/components/PdfPreview.vue')['default'] | 40 | PdfPreview: typeof import('./src/components/PdfPreview.vue')['default'] |
| 39 | Picker: typeof import('./src/components/time-picker-data/picker.vue')['default'] | 41 | Picker: typeof import('./src/components/time-picker-data/picker.vue')['default'] |
| 40 | PlanFormContainer: typeof import('./src/components/PlanFormContainer.vue')['default'] | 42 | PlanFormContainer: typeof import('./src/components/PlanFormContainer.vue')['default'] |
| 41 | - PlanPopup: typeof import('./src/components/PlanPopup/index.vue')['default'] | 43 | + PlanPopup: typeof import('./src/components/PlanSchemes/PlanPopup.vue')['default'] |
| 42 | PlanPopupNew: typeof import('./src/components/PlanPopupNew.vue')['default'] | 44 | PlanPopupNew: typeof import('./src/components/PlanPopupNew.vue')['default'] |
| 43 | PosterBuilder: typeof import('./src/components/PosterBuilder/index.vue')['default'] | 45 | PosterBuilder: typeof import('./src/components/PosterBuilder/index.vue')['default'] |
| 44 | ProductCard: typeof import('./src/components/ProductCard.vue')['default'] | 46 | ProductCard: typeof import('./src/components/ProductCard.vue')['default'] | ... | ... |
| ... | @@ -13,6 +13,14 @@ export default { | ... | @@ -13,6 +13,14 @@ export default { |
| 13 | quiet: false, | 13 | quiet: false, |
| 14 | stats: true | 14 | stats: true |
| 15 | }, | 15 | }, |
| 16 | - mini: {}, | 16 | + mini: { |
| 17 | - h5: {} | 17 | + // 开启 vConsole 调试工具 |
| 18 | + debug: true | ||
| 19 | + }, | ||
| 20 | + h5: { | ||
| 21 | + // H5 端也可以使用 eruda(另一种调试工具) | ||
| 22 | + devServer: { | ||
| 23 | + port: 10086 | ||
| 24 | + } | ||
| 25 | + } | ||
| 18 | } | 26 | } | ... | ... |
docs/接口联调注意事项.md
0 → 100644
| 1 | +# 接口联调注意事项 | ||
| 2 | + | ||
| 3 | +> **文档用途**:记录计划书表单接口联调的关键注意事项和规范 | ||
| 4 | +> | ||
| 5 | +> **最后更新**:2026-02-09 | ||
| 6 | +> **维护者**:Claude Code | ||
| 7 | + | ||
| 8 | +## 📋 目录 | ||
| 9 | + | ||
| 10 | +- [数据单位规范](#数据单位规范) | ||
| 11 | +- [接口联调流程](#接口联调流程) | ||
| 12 | +- [关键注意事项](#关键注意事项) | ||
| 13 | +- [调试技巧](#调试技巧) | ||
| 14 | +- [常见问题](#常见问题) | ||
| 15 | + | ||
| 16 | +--- | ||
| 17 | + | ||
| 18 | +## 数据单位规范 | ||
| 19 | + | ||
| 20 | +### ⚠️ 金额字段单位:分(非元) | ||
| 21 | + | ||
| 22 | +**重要**:所有金额字段内部存储单位都是 **分**,不是元! | ||
| 23 | + | ||
| 24 | +| 字段名 | 类型 | 单位 | 示例 | 显示 | | ||
| 25 | +|--------|------|------|------|------| | ||
| 26 | +| `coverage` | Number | 分 | `10000` | `100.00` 元 | | ||
| 27 | +| `premium` | Number | 分 | `5000` | `50.00` 元 | | ||
| 28 | +| `amount` | Number | 分 | `1000` | `10.00` 元 | | ||
| 29 | +| `total_amount` | Number | 分 | `15000` | `150.00` 元 | | ||
| 30 | + | ||
| 31 | +**转换公式**: | ||
| 32 | +```javascript | ||
| 33 | +// 元 → 分(提交给后端) | ||
| 34 | +const cents = Math.round(yuan * 100) | ||
| 35 | + | ||
| 36 | +// 分 → 元(前端显示) | ||
| 37 | +const yuan = (cents / 100).toFixed(2) | ||
| 38 | +``` | ||
| 39 | + | ||
| 40 | +**为什么使用"分"?** | ||
| 41 | +- ✅ 避免浮点数精度问题(`0.1 + 0.2 !== 0.3`) | ||
| 42 | +- ✅ 整数运算精确可靠 | ||
| 43 | +- ✅ 金融行业标准做法 | ||
| 44 | + | ||
| 45 | +--- | ||
| 46 | + | ||
| 47 | +## 接口联调流程 | ||
| 48 | + | ||
| 49 | +### 1. 提交计划书表单 | ||
| 50 | + | ||
| 51 | +**接口路径**:`/srv/?a=submit_plan`(待确认) | ||
| 52 | + | ||
| 53 | +**请求参数**: | ||
| 54 | +```javascript | ||
| 55 | +{ | ||
| 56 | + product_id: 1, // 产品ID | ||
| 57 | + form_sn: 'life-insurance-wiop3e', // 表单模版标识 | ||
| 58 | + form_data: { // 表单数据 | ||
| 59 | + coverage: 10000, // 保额(分)← 注意单位 | ||
| 60 | + gender: 'male', | ||
| 61 | + age: 30, | ||
| 62 | + birthday: '1994-01-01', | ||
| 63 | + smoker: false, | ||
| 64 | + payment_period: 20, | ||
| 65 | + // ... 其他字段 | ||
| 66 | + } | ||
| 67 | +} | ||
| 68 | +``` | ||
| 69 | + | ||
| 70 | +**响应格式**: | ||
| 71 | +```javascript | ||
| 72 | +{ | ||
| 73 | + code: 1, // 1 表示成功 | ||
| 74 | + data: { | ||
| 75 | + plan_id: 123, // 计划书ID | ||
| 76 | + status: 'processing', // 状态:processing(生成中) | generated(已完成) | ||
| 77 | + download_url: '' // 下载链接(生成完成后才有) | ||
| 78 | + }, | ||
| 79 | + msg: '提交成功' | ||
| 80 | +} | ||
| 81 | +``` | ||
| 82 | + | ||
| 83 | +### 2. 查询计划书状态 | ||
| 84 | + | ||
| 85 | +**接口路径**:`/srv/?a=get_plan_status` | ||
| 86 | + | ||
| 87 | +**请求参数**: | ||
| 88 | +```javascript | ||
| 89 | +{ | ||
| 90 | + plan_id: 123 // 计划书ID | ||
| 91 | +} | ||
| 92 | +``` | ||
| 93 | + | ||
| 94 | +**响应格式**: | ||
| 95 | +```javascript | ||
| 96 | +{ | ||
| 97 | + code: 1, | ||
| 98 | + data: { | ||
| 99 | + plan_id: 123, | ||
| 100 | + status: 'generated', // processing | generated | ||
| 101 | + download_url: 'https://...' // PDF 下载链接 | ||
| 102 | + }, | ||
| 103 | + msg: '' | ||
| 104 | +} | ||
| 105 | +``` | ||
| 106 | + | ||
| 107 | +--- | ||
| 108 | + | ||
| 109 | +## 关键注意事项 | ||
| 110 | + | ||
| 111 | +### ⚠️ 1. 金额字段单位必须是"分" | ||
| 112 | + | ||
| 113 | +**错误示例**: | ||
| 114 | +```javascript | ||
| 115 | +// ❌ 错误:直接发送"元" | ||
| 116 | +{ | ||
| 117 | + coverage: 100.00 // 后端会解析错误或精度丢失 | ||
| 118 | +} | ||
| 119 | +``` | ||
| 120 | + | ||
| 121 | +**正确示例**: | ||
| 122 | +```javascript | ||
| 123 | +// ✅ 正确:发送"分" | ||
| 124 | +{ | ||
| 125 | + coverage: 10000 // 后端接收后再除以100 | ||
| 126 | +} | ||
| 127 | +``` | ||
| 128 | + | ||
| 129 | +### ⚠️ 2. 检查响应码 | ||
| 130 | + | ||
| 131 | +**必须检查 `res.code === 1`**: | ||
| 132 | +```javascript | ||
| 133 | +const res = await submitPlanAPI(params) | ||
| 134 | + | ||
| 135 | +if (res.code === 1) { | ||
| 136 | + // 成功 | ||
| 137 | + console.log('提交成功:', res.data) | ||
| 138 | +} else { | ||
| 139 | + // 失败 | ||
| 140 | + Taro.showToast({ | ||
| 141 | + title: res.msg || '提交失败', | ||
| 142 | + icon: 'none' | ||
| 143 | + }) | ||
| 144 | +} | ||
| 145 | +``` | ||
| 146 | + | ||
| 147 | +### ⚠️ 3. 错误处理 | ||
| 148 | + | ||
| 149 | +**所有 API 调用必须有 `try-catch`**: | ||
| 150 | +```javascript | ||
| 151 | +try { | ||
| 152 | + const res = await submitPlanAPI(params) | ||
| 153 | + | ||
| 154 | + if (res.code === 1) { | ||
| 155 | + Taro.showToast({ title: '提交成功', icon: 'success' }) | ||
| 156 | + } else { | ||
| 157 | + Taro.showToast({ title: res.msg || '提交失败', icon: 'none' }) | ||
| 158 | + } | ||
| 159 | +} catch (err) { | ||
| 160 | + console.error('[SubmitPlan] 提交失败:', err) | ||
| 161 | + Taro.showToast({ | ||
| 162 | + title: '网络异常,请重试', | ||
| 163 | + icon: 'none' | ||
| 164 | + }) | ||
| 165 | +} | ||
| 166 | +``` | ||
| 167 | + | ||
| 168 | +### ⚠️ 4. 加载状态 | ||
| 169 | + | ||
| 170 | +**提交时显示 loading**: | ||
| 171 | +```javascript | ||
| 172 | +const loading = ref(false) | ||
| 173 | + | ||
| 174 | +const submit = async () => { | ||
| 175 | + loading.value = true | ||
| 176 | + | ||
| 177 | + try { | ||
| 178 | + await submitPlanAPI(params) | ||
| 179 | + } finally { | ||
| 180 | + loading.value = false | ||
| 181 | + } | ||
| 182 | +} | ||
| 183 | +``` | ||
| 184 | + | ||
| 185 | +--- | ||
| 186 | + | ||
| 187 | +## 调试技巧 | ||
| 188 | + | ||
| 189 | +### 1. 打印请求数据 | ||
| 190 | + | ||
| 191 | +**提交前打印完整数据**(已实现): | ||
| 192 | +```javascript | ||
| 193 | +console.log('[PlanFormContainer] 提交计划书:', { | ||
| 194 | + product_id: props.product.id, | ||
| 195 | + product_name: props.product.product_name, | ||
| 196 | + form_sn: props.product.form_sn, | ||
| 197 | + form_data: formattedData // ← 格式化后的数据(元) | ||
| 198 | +}) | ||
| 199 | + | ||
| 200 | +console.log('[PlanFormContainer] 原始数据(分):', formData.value) | ||
| 201 | +``` | ||
| 202 | + | ||
| 203 | +**打印效果**: | ||
| 204 | +```javascript | ||
| 205 | +// 格式化后(便于查看) | ||
| 206 | +form_data: { | ||
| 207 | + coverage: '100.00', | ||
| 208 | + gender: 'male', | ||
| 209 | + age: 30 | ||
| 210 | +} | ||
| 211 | + | ||
| 212 | +// 原始数据(实际发送) | ||
| 213 | +form_data: { | ||
| 214 | + coverage: 10000, | ||
| 215 | + gender: 'male', | ||
| 216 | + age: 30 | ||
| 217 | +} | ||
| 218 | +``` | ||
| 219 | + | ||
| 220 | +### 2. 网络请求拦截器 | ||
| 221 | + | ||
| 222 | +**检查 `src/utils/request.js` 中的拦截器配置**: | ||
| 223 | +- ✅ 请求拦截器已自动注入 sessionid | ||
| 224 | +- ✅ 响应拦截器已处理 401 自动刷新 | ||
| 225 | +- ✅ 超时配置:5 秒 | ||
| 226 | + | ||
| 227 | +**查看请求日志**: | ||
| 228 | +```javascript | ||
| 229 | +// request.js 中的日志 | ||
| 230 | +console.log('[Request] URL:', url) | ||
| 231 | +console.log('[Request] Data:', params) | ||
| 232 | +console.log('[Response] Data:', res) | ||
| 233 | +``` | ||
| 234 | + | ||
| 235 | +### 3. 开发者工具 | ||
| 236 | + | ||
| 237 | +**微信开发者工具**: | ||
| 238 | +1. 点击"调试器" → "Network" | ||
| 239 | +2. 找到对应的请求(`submit_plan`) | ||
| 240 | +3. 查看"Headers"和"Payload" | ||
| 241 | +4. 确认金额字段是整数(分) | ||
| 242 | + | ||
| 243 | +--- | ||
| 244 | + | ||
| 245 | +## 常见问题 | ||
| 246 | + | ||
| 247 | +### Q1: 后端接收到的金额是 `0.00`? | ||
| 248 | + | ||
| 249 | +**原因**:前端发送的值是 `100.00`(浮点数),但后端期望的是整数(分)。 | ||
| 250 | + | ||
| 251 | +**解决方案**:确保发送的是整数(分): | ||
| 252 | +```javascript | ||
| 253 | +// ❌ 错误 | ||
| 254 | +form_data: { | ||
| 255 | + coverage: 100.00 // 浮点数 | ||
| 256 | +} | ||
| 257 | + | ||
| 258 | +// ✅ 正确 | ||
| 259 | +form_data: { | ||
| 260 | + coverage: 10000 // 整数 | ||
| 261 | +} | ||
| 262 | +``` | ||
| 263 | + | ||
| 264 | +### Q2: 后端返回的金额如何显示? | ||
| 265 | + | ||
| 266 | +**后端返回的金额单位应该是"分"**: | ||
| 267 | +```javascript | ||
| 268 | +// 后端返回 | ||
| 269 | +{ | ||
| 270 | + code: 1, | ||
| 271 | + data: { | ||
| 272 | + coverage: 10000 // 分 | ||
| 273 | + } | ||
| 274 | +} | ||
| 275 | + | ||
| 276 | +// 前端显示 | ||
| 277 | +const displayYuan = (res.data.coverage / 100).toFixed(2) | ||
| 278 | +// displayYuan = '100.00' | ||
| 279 | +``` | ||
| 280 | + | ||
| 281 | +### Q3: 如何验证金额是否正确? | ||
| 282 | + | ||
| 283 | +**验证方法**: | ||
| 284 | +```javascript | ||
| 285 | +// 输入 100.00 元 | ||
| 286 | +console.log('输入值:', '100.00') | ||
| 287 | + | ||
| 288 | +// 存储的值(分) | ||
| 289 | +console.log('存储值:', 10000) | ||
| 290 | + | ||
| 291 | +// 显示的值(元) | ||
| 292 | +console.log('显示值:', (10000 / 100).toFixed(2)) // '100.00' | ||
| 293 | +``` | ||
| 294 | + | ||
| 295 | +--- | ||
| 296 | + | ||
| 297 | +## 接口联调清单 | ||
| 298 | + | ||
| 299 | +### 提交前检查 | ||
| 300 | + | ||
| 301 | +- [ ] 后端接口地址已配置(`src/utils/config.js`) | ||
| 302 | +- [ ] 请求路径正确(`/srv/?a=xxx`) | ||
| 303 | +- [ ] 请求参数格式已确认 | ||
| 304 | +- [ ] 响应格式已确认(`{ code, data, msg }`) | ||
| 305 | +- [ ] 金额字段单位已确认(分) | ||
| 306 | + | ||
| 307 | +### 提交时验证 | ||
| 308 | + | ||
| 309 | +- [ ] 金额字段是整数(分) | ||
| 310 | +- [ ] 检查 `res.code === 1` | ||
| 311 | +- [ ] 错误提示用户友好 | ||
| 312 | +- [ ] Loading 状态正确显示 | ||
| 313 | + | ||
| 314 | +### 提交后处理 | ||
| 315 | + | ||
| 316 | +- [ ] 成功后跳转到计划书列表页 | ||
| 317 | +- [ ] 失败后停留在表单页 | ||
| 318 | +- [ ] 网络异常有重试机制 | ||
| 319 | +- [ ] 轮询状态(如果是生成中的计划书) | ||
| 320 | + | ||
| 321 | +--- | ||
| 322 | + | ||
| 323 | +## 接口定义示例 | ||
| 324 | + | ||
| 325 | +### 1. 提交计划书 | ||
| 326 | + | ||
| 327 | +**定义位置**:`src/api/index.js` | ||
| 328 | + | ||
| 329 | +```javascript | ||
| 330 | +/** | ||
| 331 | + * 提交计划书表单 | ||
| 332 | + * | ||
| 333 | + * @param {Object} params - 请求参数 | ||
| 334 | + * @param {number} params.product_id - 产品ID | ||
| 335 | + * @param {string} params.form_sn - 表单模版标识 | ||
| 336 | + * @param {Object} params.form_data - 表单数据 | ||
| 337 | + * @returns {Promise<{code: number, data: Object, msg: string}>} | ||
| 338 | + * | ||
| 339 | + * @example | ||
| 340 | + * const res = await submitPlanAPI({ | ||
| 341 | + * product_id: 1, | ||
| 342 | + * form_sn: 'life-insurance-wiop3e', | ||
| 343 | + * form_data: { | ||
| 344 | + * coverage: 10000, // 保额(分) | ||
| 345 | + * gender: 'male', | ||
| 346 | + * age: 30 | ||
| 347 | + * } | ||
| 348 | + * }) | ||
| 349 | + */ | ||
| 350 | +export const submitPlanAPI = (params) => { | ||
| 351 | + return buildApiUrl('submit_plan', params) | ||
| 352 | +} | ||
| 353 | +``` | ||
| 354 | + | ||
| 355 | +### 2. 查询计划书状态 | ||
| 356 | + | ||
| 357 | +```javascript | ||
| 358 | +/** | ||
| 359 | + * 查询计划书生成状态 | ||
| 360 | + * | ||
| 361 | + * @param {Object} params - 请求参数 | ||
| 362 | + * @param {number} params.plan_id - 计划书ID | ||
| 363 | + * @returns {Promise<{code: number, data: Object, msg: string}>} | ||
| 364 | + */ | ||
| 365 | +export const getPlanStatusAPI = (params) => { | ||
| 366 | + return buildApiUrl('get_plan_status', params) | ||
| 367 | +} | ||
| 368 | +``` | ||
| 369 | + | ||
| 370 | +--- | ||
| 371 | + | ||
| 372 | +## 快速参考 | ||
| 373 | + | ||
| 374 | +### 金额转换工具函数 | ||
| 375 | + | ||
| 376 | +**位置**:`src/utils/amount.js`(建议创建) | ||
| 377 | + | ||
| 378 | +```javascript | ||
| 379 | +/** | ||
| 380 | + * 元转分 | ||
| 381 | + * @param {number|string} yuan - 元 | ||
| 382 | + * @returns {number} 分 | ||
| 383 | + */ | ||
| 384 | +export const yuanToCents = (yuan) => { | ||
| 385 | + return Math.round(Number(yuan) * 100) | ||
| 386 | +} | ||
| 387 | + | ||
| 388 | +/** | ||
| 389 | + * 分转元 | ||
| 390 | + * @param {number} cents - 分 | ||
| 391 | + * @returns {string} 元(带2位小数) | ||
| 392 | + */ | ||
| 393 | +export const centsToYuan = (cents) => { | ||
| 394 | + return (cents / 100).toFixed(2) | ||
| 395 | +} | ||
| 396 | + | ||
| 397 | +/** | ||
| 398 | + * 格式化金额显示 | ||
| 399 | + * @param {number} cents - 分 | ||
| 400 | + * @returns {string} 格式化后的金额(如:"10,000.00") | ||
| 401 | + */ | ||
| 402 | +export const formatAmount = (cents) => { | ||
| 403 | + const yuan = (cents / 100).toFixed(2) | ||
| 404 | + const parts = yuan.split('.') | ||
| 405 | + parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',') | ||
| 406 | + return parts.join('.') | ||
| 407 | +} | ||
| 408 | +``` | ||
| 409 | + | ||
| 410 | +### 使用示例 | ||
| 411 | + | ||
| 412 | +```javascript | ||
| 413 | +import { yuanToCents, centsToYuan, formatAmount } from '@/utils/amount' | ||
| 414 | + | ||
| 415 | +// 用户输入 | ||
| 416 | +const input = '100.00' | ||
| 417 | +const cents = yuanToCents(input) // 10000 | ||
| 418 | + | ||
| 419 | +// 存储 | ||
| 420 | +formData.value.coverage = cents | ||
| 421 | + | ||
| 422 | +// 显示 | ||
| 423 | +const display = formatAmount(cents) // '10,000.00' 或 '100.00' | ||
| 424 | +``` | ||
| 425 | + | ||
| 426 | +--- | ||
| 427 | + | ||
| 428 | +## 维护日志 | ||
| 429 | + | ||
| 430 | +- **2026-02-09**:创建文档,记录金额字段单位规范和联调注意事项 | ||
| 431 | + | ||
| 432 | +--- | ||
| 433 | + | ||
| 434 | +## 相关文档 | ||
| 435 | + | ||
| 436 | +- [项目 CLAUDE.md](../CLAUDE.md) - 项目开发规范 | ||
| 437 | +- [API 集成日志](../api-integration-log.md) - API 联调记录 | ||
| 438 | +- [变更日志](../CHANGELOG.md) - 版本更新历史 |
| ... | @@ -57,4 +57,6 @@ export default { | ... | @@ -57,4 +57,6 @@ export default { |
| 57 | navigationBarTitleText: '西园寺预约', | 57 | navigationBarTitleText: '西园寺预约', |
| 58 | navigationBarTextStyle: 'black', | 58 | navigationBarTextStyle: 'black', |
| 59 | }, | 59 | }, |
| 60 | + // 开发环境开启调试模式 | ||
| 61 | + ...(process.env.NODE_ENV === 'development' ? { debug: true } : {}), | ||
| 60 | } | 62 | } | ... | ... |
| ... | @@ -217,8 +217,35 @@ const close = async () => { | ... | @@ -217,8 +217,35 @@ const close = async () => { |
| 217 | } | 217 | } |
| 218 | 218 | ||
| 219 | /** | 219 | /** |
| 220 | + * 格式化金额数据(分 → 元) | ||
| 221 | + * @description 将金额从"分"转换为"元"(带2位小数),便于调试和查看 | ||
| 222 | + * @param {Object} data - 表单数据 | ||
| 223 | + * @returns {Object} 格式化后的表单数据 | ||
| 224 | + * @example | ||
| 225 | + * formatAmounts({ coverage: 10000 }) // 返回 { coverage: '100.00' } | ||
| 226 | + */ | ||
| 227 | +const formatAmounts = (data) => { | ||
| 228 | + const formatted = { ...data } | ||
| 229 | + | ||
| 230 | + // 金额字段列表(单位:分) | ||
| 231 | + const amountFields = ['coverage', 'premium', 'amount', 'total_amount'] | ||
| 232 | + | ||
| 233 | + for (const field of amountFields) { | ||
| 234 | + if (formatted[field] !== null && formatted[field] !== undefined) { | ||
| 235 | + // 分 → 元,保留2位小数 | ||
| 236 | + formatted[field] = (formatted[field] / 100).toFixed(2) | ||
| 237 | + } | ||
| 238 | + } | ||
| 239 | + | ||
| 240 | + return formatted | ||
| 241 | +} | ||
| 242 | + | ||
| 243 | +/** | ||
| 220 | * 提交表单 | 244 | * 提交表单 |
| 221 | * @description 将表单数据和产品信息一起提交 | 245 | * @description 将表单数据和产品信息一起提交 |
| 246 | + * | ||
| 247 | + * ⚠️ 注意:接口未完成,当前仅为前端测试 | ||
| 248 | + * TODO: 后端接口准备就绪后,需要调用 submitPlanAPI 提交表单数据 | ||
| 222 | */ | 249 | */ |
| 223 | const submit = async () => { | 250 | const submit = async () => { |
| 224 | if (!props.product) { | 251 | if (!props.product) { |
| ... | @@ -234,25 +261,27 @@ const submit = async () => { | ... | @@ -234,25 +261,27 @@ const submit = async () => { |
| 234 | } | 261 | } |
| 235 | } | 262 | } |
| 236 | 263 | ||
| 264 | + // 格式化金额数据(便于调试查看) | ||
| 265 | + const formattedData = formatAmounts(formData.value) | ||
| 266 | + | ||
| 237 | console.log('[PlanFormContainer] 提交计划书:', { | 267 | console.log('[PlanFormContainer] 提交计划书:', { |
| 238 | product_id: props.product.id, | 268 | product_id: props.product.id, |
| 239 | product_name: props.product.product_name, | 269 | product_name: props.product.product_name, |
| 240 | form_sn: props.product.form_sn, | 270 | form_sn: props.product.form_sn, |
| 241 | - form_data: formData.value | 271 | + form_data: formattedData // ← 打印格式化后的数据(元) |
| 242 | }) | 272 | }) |
| 243 | 273 | ||
| 244 | - // 发送提交事件 | 274 | + console.log('[PlanFormContainer] 原始数据(分):', formData.value) |
| 275 | + | ||
| 276 | + // 发送提交事件(携带原始表单数据给父组件,单位:分) | ||
| 245 | emit('submit', { | 277 | emit('submit', { |
| 246 | product_id: props.product.id, | 278 | product_id: props.product.id, |
| 247 | form_sn: props.product.form_sn, | 279 | form_sn: props.product.form_sn, |
| 248 | - form_data: formData.value | 280 | + form_data: formData.value // ← 发送原始数据(分),接口需要 |
| 249 | }) | 281 | }) |
| 250 | 282 | ||
| 251 | - // ⚠️ 等待父组件处理提交事件(可能需要关闭弹窗) | 283 | + // ✅ 不在这里重置表单,让父组件先处理数据 |
| 252 | - await nextTick() | 284 | + // 重置逻辑交给 close() 函数处理(关闭弹窗时自动清空) |
| 253 | - | ||
| 254 | - // 提交成功后重置表单,避免下次打开时保留旧数据 | ||
| 255 | - resetForm() | ||
| 256 | } | 285 | } |
| 257 | 286 | ||
| 258 | /** | 287 | /** | ... | ... |
-
Please register or login to post a comment