feat(plan): 添加客户姓名输入字段到所有保险计划模板
- 新增 PlanFieldName(NameInput)组件用于输入客户姓名 - 在重疾险、寿险、储蓄险三个模板中集成客户姓名字段 - 添加客户姓名必填校验逻辑 - 优化 NameInput 组件样式,使用 Tailwind CSS 和圆角边框 - 重构计划书页面代码,减少代码重复 影响文件: - src/components/plan/PlanFields/NameInput.vue (新增) - src/components/plan/PlanTemplates/*.vue (修改) - src/components/plan/PlanFormContainer.vue (重构) - src/pages/plan/index.vue (重构) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Showing
9 changed files
with
271 additions
and
39 deletions
| ... | @@ -18,6 +18,7 @@ declare module 'vue' { | ... | @@ -18,6 +18,7 @@ declare module 'vue' { |
| 18 | ListItemActions: typeof import('./src/components/list/ListItemActions/index.vue')['default'] | 18 | ListItemActions: typeof import('./src/components/list/ListItemActions/index.vue')['default'] |
| 19 | LoadMoreList: typeof import('./src/components/list/LoadMoreList/index.vue')['default'] | 19 | LoadMoreList: typeof import('./src/components/list/LoadMoreList/index.vue')['default'] |
| 20 | MaterialCard: typeof import('./src/components/cards/MaterialCard.vue')['default'] | 20 | MaterialCard: typeof import('./src/components/cards/MaterialCard.vue')['default'] |
| 21 | + NameInput: typeof import('./src/components/plan/PlanFields/NameInput.vue')['default'] | ||
| 21 | NavHeader: typeof import('./src/components/navigation/NavHeader.vue')['default'] | 22 | NavHeader: typeof import('./src/components/navigation/NavHeader.vue')['default'] |
| 22 | NutAvatar: typeof import('@nutui/nutui-taro')['Avatar'] | 23 | NutAvatar: typeof import('@nutui/nutui-taro')['Avatar'] |
| 23 | NutButton: typeof import('@nutui/nutui-taro')['Button'] | 24 | NutButton: typeof import('@nutui/nutui-taro')['Button'] | ... | ... |
| ... | @@ -5,6 +5,24 @@ | ... | @@ -5,6 +5,24 @@ |
| 5 | 5 | ||
| 6 | --- | 6 | --- |
| 7 | 7 | ||
| 8 | +## [2026-02-10] - 优化 NameInput 组件样式 | ||
| 9 | + | ||
| 10 | +### 优化 | ||
| 11 | +- **样式重构**:将 NameInput 组件从 Less 样式迁移到 Tailwind CSS | ||
| 12 | + - 遵循项目 Tailwind 优先的开发规范 | ||
| 13 | + - 移除冗余的 Less 代码 | ||
| 14 | +- **UI 改进**: | ||
| 15 | + - 添加可见的圆角边框(border-gray-200, rounded-[12rpx]) | ||
| 16 | + - 统一输入框的视觉风格 | ||
| 17 | + | ||
| 18 | +**详细信息**: | ||
| 19 | +- **影响文件**: src/components/plan/PlanFields/NameInput.vue | ||
| 20 | +- **技术栈**: Vue 3, Tailwind CSS | ||
| 21 | +- **测试状态**: 待验证 | ||
| 22 | +- **备注**: 响应用户需求,增强输入框视觉反馈 | ||
| 23 | + | ||
| 24 | +--- | ||
| 25 | + | ||
| 8 | ## [2026-02-10] - 重构 API 接口层代码 | 26 | ## [2026-02-10] - 重构 API 接口层代码 |
| 9 | 27 | ||
| 10 | ### 重构 | 28 | ### 重构 | ... | ... |
src/components/plan/PlanFields/NameInput.vue
0 → 100644
| 1 | +<!-- | ||
| 2 | + * @Date: 2026-02-10 14:06:03 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2026-02-10 14:18:58 | ||
| 5 | + * @FilePath: /manulife-weapp/src/components/plan/PlanFields/NameInput.vue | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | +--> | ||
| 8 | +<template> | ||
| 9 | + <!-- 姓名输入框 --> | ||
| 10 | + <div class="mb-5"> | ||
| 11 | + <label v-if="label" class="block mb-[16rpx] text-sm text-gray-600"> | ||
| 12 | + <text v-if="required" class="text-red-500">*</text> | ||
| 13 | + {{ label }} | ||
| 14 | + </label> | ||
| 15 | + <input | ||
| 16 | + :value="modelValue" | ||
| 17 | + :placeholder="placeholder" | ||
| 18 | + type="text" | ||
| 19 | + class="w-full h-[80rpx] px-[24rpx] text-[28rpx] text-[#333] rounded-[12rpx] border border-solid border-gray-200 box-border transition-all duration-300 focus:bg-white focus:border-[#4caf50] focus:outline-none placeholder:text-[#999]" | ||
| 20 | + :class="{ 'text-[#333]': modelValue }" | ||
| 21 | + @input="handleInput" | ||
| 22 | + /> | ||
| 23 | + </div> | ||
| 24 | +</template> | ||
| 25 | + | ||
| 26 | +<script setup> | ||
| 27 | +/** | ||
| 28 | + * 姓名输入框组件 | ||
| 29 | + * | ||
| 30 | + * @description 计划书表单的姓名输入字段 | ||
| 31 | + * - 支持双向绑定 | ||
| 32 | + * - 支持必填标识 | ||
| 33 | + * - 清晰的视觉反馈 | ||
| 34 | + * @author Claude Code | ||
| 35 | + * @example | ||
| 36 | + * <PlanFieldName | ||
| 37 | + * v-model="form.customer_name" | ||
| 38 | + * label="客户姓名" | ||
| 39 | + * placeholder="请输入客户姓名" | ||
| 40 | + * :required="true" | ||
| 41 | + * /> | ||
| 42 | + */ | ||
| 43 | +const props = defineProps({ | ||
| 44 | + /** | ||
| 45 | + * 字段值 | ||
| 46 | + * @type {string} | ||
| 47 | + */ | ||
| 48 | + modelValue: { | ||
| 49 | + type: String, | ||
| 50 | + default: '' | ||
| 51 | + }, | ||
| 52 | + /** | ||
| 53 | + * 标签文本 | ||
| 54 | + * @type {string} | ||
| 55 | + */ | ||
| 56 | + label: { | ||
| 57 | + type: String, | ||
| 58 | + default: '' | ||
| 59 | + }, | ||
| 60 | + /** | ||
| 61 | + * 占位符文本 | ||
| 62 | + * @type {string} | ||
| 63 | + */ | ||
| 64 | + placeholder: { | ||
| 65 | + type: String, | ||
| 66 | + default: '请输入姓名' | ||
| 67 | + }, | ||
| 68 | + /** | ||
| 69 | + * 是否必填 | ||
| 70 | + * @type {boolean} | ||
| 71 | + */ | ||
| 72 | + required: { | ||
| 73 | + type: Boolean, | ||
| 74 | + default: false | ||
| 75 | + } | ||
| 76 | +}) | ||
| 77 | + | ||
| 78 | +/** | ||
| 79 | + * 组件事件 | ||
| 80 | + */ | ||
| 81 | +const emit = defineEmits([ | ||
| 82 | + /** | ||
| 83 | + * 更新值事件 | ||
| 84 | + * @event update:modelValue | ||
| 85 | + * @param {string} value - 输入的值 | ||
| 86 | + */ | ||
| 87 | + 'update:modelValue' | ||
| 88 | +]) | ||
| 89 | + | ||
| 90 | +/** | ||
| 91 | + * 处理输入事件 | ||
| 92 | + * @param {Event} e - 输入事件对象 | ||
| 93 | + */ | ||
| 94 | +const handleInput = (e) => { | ||
| 95 | + const value = e.detail.value | ||
| 96 | + emit('update:modelValue', value) | ||
| 97 | +} | ||
| 98 | +</script> |
| ... | @@ -42,11 +42,13 @@ | ... | @@ -42,11 +42,13 @@ |
| 42 | * /> | 42 | * /> |
| 43 | */ | 43 | */ |
| 44 | import { ref, computed, watch, nextTick } from 'vue' | 44 | import { ref, computed, watch, nextTick } from 'vue' |
| 45 | +import Taro from '@tarojs/taro' | ||
| 45 | import PlanPopupNew from './PlanPopupNew.vue' | 46 | import PlanPopupNew from './PlanPopupNew.vue' |
| 46 | import LifeInsuranceTemplate from './PlanTemplates/LifeInsuranceTemplate.vue' | 47 | import LifeInsuranceTemplate from './PlanTemplates/LifeInsuranceTemplate.vue' |
| 47 | import CriticalIllnessTemplate from './PlanTemplates/CriticalIllnessTemplate.vue' | 48 | import CriticalIllnessTemplate from './PlanTemplates/CriticalIllnessTemplate.vue' |
| 48 | import SavingsTemplate from './PlanTemplates/SavingsTemplate.vue' | 49 | import SavingsTemplate from './PlanTemplates/SavingsTemplate.vue' |
| 49 | import { PLAN_TEMPLATES } from '@/config/plan-templates' | 50 | import { PLAN_TEMPLATES } from '@/config/plan-templates' |
| 51 | +import { addAPI } from '@/api/plan' | ||
| 50 | 52 | ||
| 51 | /** | 53 | /** |
| 52 | * 组件属性 | 54 | * 组件属性 |
| ... | @@ -251,44 +253,128 @@ const formatAmounts = (data) => { | ... | @@ -251,44 +253,128 @@ const formatAmounts = (data) => { |
| 251 | 253 | ||
| 252 | /** | 254 | /** |
| 253 | * 提交表单 | 255 | * 提交表单 |
| 254 | - * @description 将表单数据和产品信息一起提交 | 256 | + * @description 将表单数据和产品信息提交到后端 API |
| 255 | * | 257 | * |
| 256 | - * ⚠️ 注意:接口未完成,当前仅为前端测试 | 258 | + * @returns {Promise<boolean>} 是否提交成功 |
| 257 | - * TODO: 后端接口准备就绪后,需要调用 submitPlanAPI 提交表单数据 | ||
| 258 | */ | 259 | */ |
| 259 | const submit = async () => { | 260 | const submit = async () => { |
| 260 | if (!props.product) { | 261 | if (!props.product) { |
| 261 | console.error('[PlanFormContainer] 无法提交: 产品数据为空') | 262 | console.error('[PlanFormContainer] 无法提交: 产品数据为空') |
| 262 | - return | 263 | + Taro.showToast({ |
| 264 | + title: '产品数据为空', | ||
| 265 | + icon: 'none', | ||
| 266 | + duration: 2000 | ||
| 267 | + }) | ||
| 268 | + return false | ||
| 263 | } | 269 | } |
| 264 | 270 | ||
| 265 | // 调用模版组件的校验方法 | 271 | // 调用模版组件的校验方法 |
| 266 | if (templateRef.value && templateRef.value.validate) { | 272 | if (templateRef.value && templateRef.value.validate) { |
| 267 | const isValid = templateRef.value.validate() | 273 | const isValid = templateRef.value.validate() |
| 268 | if (!isValid) { | 274 | if (!isValid) { |
| 269 | - return | 275 | + return false |
| 270 | } | 276 | } |
| 271 | } | 277 | } |
| 272 | 278 | ||
| 273 | - // 格式化金额数据(便于调试查看) | 279 | + // 显示加载提示 |
| 274 | - const formattedData = formatAmounts(formData.value) | 280 | + Taro.showLoading({ |
| 281 | + title: '提交中...', | ||
| 282 | + mask: true | ||
| 283 | + }) | ||
| 275 | 284 | ||
| 276 | - console.log('[PlanFormContainer] 提交计划书:', { | 285 | + try { |
| 277 | - product_id: props.product.id, | 286 | + // 字段名映射:将表单字段名映射为 API 期望的字段名 |
| 278 | - product_name: props.product.product_name, | 287 | + const fieldMapping = { |
| 279 | - form_sn: props.product.form_sn, | 288 | + customer_name: 'customer_name', // 客户姓名(已直接使用) |
| 280 | - form_data: formattedData // ← 打印格式化后的数据(元) | 289 | + gender: 'customer_gender', // 性别 → customer_gender |
| 290 | + age: 'customer_age', // 年龄 → customer_age | ||
| 291 | + birthday: 'customer_birthday', // 出生年月日 → customer_birthday | ||
| 292 | + smoker: 'smoker', // 是否吸烟(保持不变) | ||
| 293 | + coverage: 'annual_premium', // 保额/年缴保费 → annual_premium | ||
| 294 | + payment_period: 'payment_years', // 缴费年期 → payment_years | ||
| 295 | + withdrawal_enabled: 'allow_reduce_amount', // 是否容许减少名义金额 | ||
| 296 | + withdrawal_mode: 'withdrawal_option', // 提取选项 | ||
| 297 | + specified_amount_type: null, // 提取方式(后端可能不需要) | ||
| 298 | + withdrawal_start_age: 'withdrawal_start_age', // 提取开始年龄 | ||
| 299 | + withdrawal_period: 'withdrawal_period', // 提取期 | ||
| 300 | + currency_type: 'currency_type' // 币种类型 | ||
| 301 | + } | ||
| 302 | + | ||
| 303 | + // 构建请求数据 | ||
| 304 | + const requestData = { | ||
| 305 | + product_id: props.product.id | ||
| 306 | + } | ||
| 307 | + | ||
| 308 | + // 映射表单字段到 API 字段 | ||
| 309 | + Object.keys(formData.value).forEach(key => { | ||
| 310 | + const apiField = fieldMapping[key] | ||
| 311 | + | ||
| 312 | + if (apiField) { | ||
| 313 | + // 有映射:使用映射后的字段名 | ||
| 314 | + requestData[apiField] = formData.value[key] | ||
| 315 | + } else if (key === 'total_amount') { | ||
| 316 | + // 特殊处理:总保费(分 → 元) | ||
| 317 | + requestData.total_premium = (formData.value[key] / 100).toFixed(2) | ||
| 318 | + } else { | ||
| 319 | + // 无映射:保持原字段名 | ||
| 320 | + requestData[key] = formData.value[key] | ||
| 321 | + } | ||
| 281 | }) | 322 | }) |
| 282 | 323 | ||
| 283 | - console.log('[PlanFormContainer] 原始数据(分):', formData.value) | 324 | + // 添加币种类型(如果有配置) |
| 325 | + if (templateConfig.value?.config?.currency) { | ||
| 326 | + requestData.currency_type = templateConfig.value.config.currency | ||
| 327 | + } | ||
| 328 | + | ||
| 329 | + console.log('[PlanFormContainer] 提交计划书请求数据:', requestData) | ||
| 330 | + console.log('[PlanFormContainer] 字段映射:', fieldMapping) | ||
| 331 | + | ||
| 332 | + // 调用 API | ||
| 333 | + const res = await addAPI(requestData) | ||
| 334 | + | ||
| 335 | + if (res.code === 1) { | ||
| 336 | + Taro.hideLoading() | ||
| 284 | 337 | ||
| 285 | - // 发送提交事件(携带原始表单数据给父组件,单位:分) | 338 | + Taro.showToast({ |
| 339 | + title: '提交成功', | ||
| 340 | + icon: 'success', | ||
| 341 | + duration: 2000 | ||
| 342 | + }) | ||
| 343 | + | ||
| 344 | + // 发送提交成功事件(携带 order_id) | ||
| 286 | emit('submit', { | 345 | emit('submit', { |
| 346 | + success: true, | ||
| 347 | + order_id: res.data?.order_id, | ||
| 287 | product_id: props.product.id, | 348 | product_id: props.product.id, |
| 288 | - form_sn: props.product.form_sn, | 349 | + form_sn: props.product.form_sn |
| 289 | - form_data: formData.value // ← 发送原始数据(分),接口需要 | 350 | + }) |
| 351 | + | ||
| 352 | + return true | ||
| 353 | + } else { | ||
| 354 | + Taro.hideLoading() | ||
| 355 | + | ||
| 356 | + Taro.showToast({ | ||
| 357 | + title: res.msg || '提交失败', | ||
| 358 | + icon: 'none', | ||
| 359 | + duration: 2000 | ||
| 360 | + }) | ||
| 361 | + | ||
| 362 | + return false | ||
| 363 | + } | ||
| 364 | + } catch (error) { | ||
| 365 | + Taro.hideLoading() | ||
| 366 | + | ||
| 367 | + console.error('[PlanFormContainer] 提交计划书失败:', error) | ||
| 368 | + | ||
| 369 | + Taro.showToast({ | ||
| 370 | + title: '网络异常,请重试', | ||
| 371 | + icon: 'none', | ||
| 372 | + duration: 2000 | ||
| 290 | }) | 373 | }) |
| 291 | 374 | ||
| 375 | + return false | ||
| 376 | + } | ||
| 377 | + | ||
| 292 | // ✅ 不在这里重置表单,让父组件先处理数据 | 378 | // ✅ 不在这里重置表单,让父组件先处理数据 |
| 293 | // 重置逻辑交给 close() 函数处理(关闭弹窗时自动清空) | 379 | // 重置逻辑交给 close() 函数处理(关闭弹窗时自动清空) |
| 294 | } | 380 | } | ... | ... |
| 1 | <template> | 1 | <template> |
| 2 | <div v-if="config"> | 2 | <div v-if="config"> |
| 3 | + <!-- 客户姓名 --> | ||
| 4 | + <PlanFieldName | ||
| 5 | + v-model="form.customer_name" | ||
| 6 | + label="客户姓名" | ||
| 7 | + placeholder="请输入客户姓名" | ||
| 8 | + :required="true" | ||
| 9 | + class="mb-5" | ||
| 10 | + /> | ||
| 11 | + | ||
| 3 | <!-- 性别 --> | 12 | <!-- 性别 --> |
| 4 | <PlanFieldRadio | 13 | <PlanFieldRadio |
| 5 | v-model="form.gender" | 14 | v-model="form.gender" |
| ... | @@ -82,6 +91,7 @@ | ... | @@ -82,6 +91,7 @@ |
| 82 | */ | 91 | */ |
| 83 | import { reactive, watch } from 'vue' | 92 | import { reactive, watch } from 'vue' |
| 84 | import Taro from '@tarojs/taro' | 93 | import Taro from '@tarojs/taro' |
| 94 | +import PlanFieldName from '../PlanFields/NameInput.vue' | ||
| 85 | import PlanFieldAgePicker from '../PlanFields/AgePickerGlobal.vue' | 95 | import PlanFieldAgePicker from '../PlanFields/AgePickerGlobal.vue' |
| 86 | import PlanFieldAmount from '../PlanFields/AmountKeyboard.vue' | 96 | import PlanFieldAmount from '../PlanFields/AmountKeyboard.vue' |
| 87 | import PlanFieldDatePicker from '../PlanFields/DatePickerGlobal.vue' | 97 | import PlanFieldDatePicker from '../PlanFields/DatePickerGlobal.vue' |
| ... | @@ -228,6 +238,10 @@ const onBirthdayChange = (birthday) => { | ... | @@ -228,6 +238,10 @@ const onBirthdayChange = (birthday) => { |
| 228 | * @returns {boolean} 是否通过校验 | 238 | * @returns {boolean} 是否通过校验 |
| 229 | */ | 239 | */ |
| 230 | const validate = () => { | 240 | const validate = () => { |
| 241 | + if (!form.customer_name || !form.customer_name.trim()) { | ||
| 242 | + Taro.showToast({ title: '请输入客户姓名', icon: 'none' }) | ||
| 243 | + return false | ||
| 244 | + } | ||
| 231 | if (!form.gender) { | 245 | if (!form.gender) { |
| 232 | Taro.showToast({ title: '请选择性别', icon: 'none' }) | 246 | Taro.showToast({ title: '请选择性别', icon: 'none' }) |
| 233 | return false | 247 | return false | ... | ... |
| 1 | <template> | 1 | <template> |
| 2 | <div v-if="config"> | 2 | <div v-if="config"> |
| 3 | + <!-- 客户姓名 --> | ||
| 4 | + <PlanFieldName | ||
| 5 | + v-model="form.customer_name" | ||
| 6 | + label="客户姓名" | ||
| 7 | + placeholder="请输入客户姓名" | ||
| 8 | + :required="true" | ||
| 9 | + class="mb-5" | ||
| 10 | + /> | ||
| 11 | + | ||
| 3 | <!-- 性别 --> | 12 | <!-- 性别 --> |
| 4 | <PlanFieldRadio | 13 | <PlanFieldRadio |
| 5 | v-model="form.gender" | 14 | v-model="form.gender" |
| ... | @@ -82,6 +91,7 @@ | ... | @@ -82,6 +91,7 @@ |
| 82 | */ | 91 | */ |
| 83 | import { reactive, watch, toRefs } from 'vue' | 92 | import { reactive, watch, toRefs } from 'vue' |
| 84 | import Taro from '@tarojs/taro' | 93 | import Taro from '@tarojs/taro' |
| 94 | +import PlanFieldName from '../PlanFields/NameInput.vue' | ||
| 85 | import PlanFieldAgePicker from '../PlanFields/AgePickerGlobal.vue' | 95 | import PlanFieldAgePicker from '../PlanFields/AgePickerGlobal.vue' |
| 86 | import PlanFieldAmount from '../PlanFields/AmountKeyboard.vue' | 96 | import PlanFieldAmount from '../PlanFields/AmountKeyboard.vue' |
| 87 | import PlanFieldDatePicker from '../PlanFields/DatePickerGlobal.vue' | 97 | import PlanFieldDatePicker from '../PlanFields/DatePickerGlobal.vue' |
| ... | @@ -231,6 +241,10 @@ const onBirthdayChange = (birthday) => { | ... | @@ -231,6 +241,10 @@ const onBirthdayChange = (birthday) => { |
| 231 | * @returns {boolean} 是否通过校验 | 241 | * @returns {boolean} 是否通过校验 |
| 232 | */ | 242 | */ |
| 233 | const validate = () => { | 243 | const validate = () => { |
| 244 | + if (!form.customer_name || !form.customer_name.trim()) { | ||
| 245 | + Taro.showToast({ title: '请输入客户姓名', icon: 'none' }) | ||
| 246 | + return false | ||
| 247 | + } | ||
| 234 | if (!form.gender) { | 248 | if (!form.gender) { |
| 235 | Taro.showToast({ title: '请选择性别', icon: 'none' }) | 249 | Taro.showToast({ title: '请选择性别', icon: 'none' }) |
| 236 | return false | 250 | return false | ... | ... |
| 1 | <template> | 1 | <template> |
| 2 | <div v-if="config"> | 2 | <div v-if="config"> |
| 3 | + <!-- 客户姓名 --> | ||
| 4 | + <PlanFieldName | ||
| 5 | + v-model="form.customer_name" | ||
| 6 | + label="客户姓名" | ||
| 7 | + placeholder="请输入客户姓名" | ||
| 8 | + :required="true" | ||
| 9 | + class="mb-5" | ||
| 10 | + /> | ||
| 11 | + | ||
| 3 | <!-- 性别 --> | 12 | <!-- 性别 --> |
| 4 | <PlanFieldRadio | 13 | <PlanFieldRadio |
| 5 | v-model="form.gender" | 14 | v-model="form.gender" |
| ... | @@ -207,6 +216,7 @@ | ... | @@ -207,6 +216,7 @@ |
| 207 | */ | 216 | */ |
| 208 | import { reactive, watch, computed } from 'vue' | 217 | import { reactive, watch, computed } from 'vue' |
| 209 | import Taro from '@tarojs/taro' | 218 | import Taro from '@tarojs/taro' |
| 219 | +import PlanFieldName from '../PlanFields/NameInput.vue' | ||
| 210 | import PlanFieldAgePicker from '../PlanFields/AgePickerGlobal.vue' | 220 | import PlanFieldAgePicker from '../PlanFields/AgePickerGlobal.vue' |
| 211 | import PlanFieldAmount from '../PlanFields/AmountKeyboard.vue' | 221 | import PlanFieldAmount from '../PlanFields/AmountKeyboard.vue' |
| 212 | import PlanFieldDatePicker from '../PlanFields/DatePickerGlobal.vue' | 222 | import PlanFieldDatePicker from '../PlanFields/DatePickerGlobal.vue' |
| ... | @@ -457,6 +467,10 @@ watch( | ... | @@ -457,6 +467,10 @@ watch( |
| 457 | */ | 467 | */ |
| 458 | const validate = () => { | 468 | const validate = () => { |
| 459 | // 基础字段校验 | 469 | // 基础字段校验 |
| 470 | + if (!form.customer_name || !form.customer_name.trim()) { | ||
| 471 | + Taro.showToast({ title: '请输入客户姓名', icon: 'none' }) | ||
| 472 | + return false | ||
| 473 | + } | ||
| 460 | if (!form.gender) { | 474 | if (!form.gender) { |
| 461 | Taro.showToast({ title: '请选择性别', icon: 'none' }) | 475 | Taro.showToast({ title: '请选择性别', icon: 'none' }) |
| 462 | return false | 476 | return false | ... | ... |
| ... | @@ -121,7 +121,6 @@ import MaterialCard from '@/components/cards/MaterialCard.vue'; | ... | @@ -121,7 +121,6 @@ import MaterialCard from '@/components/cards/MaterialCard.vue'; |
| 121 | import { listAPI } from '@/api/get_product'; | 121 | import { listAPI } from '@/api/get_product'; |
| 122 | import { weekHotAPI } from '@/api/file'; | 122 | import { weekHotAPI } from '@/api/file'; |
| 123 | import { homeIconAPI } from '@/api/home'; | 123 | import { homeIconAPI } from '@/api/home'; |
| 124 | -import { mockHotProductsListAPI } from '@/api/mock/hotProducts'; | ||
| 125 | 124 | ||
| 126 | 125 | ||
| 127 | // User Store | 126 | // User Store |
| ... | @@ -246,41 +245,29 @@ const fetchHomeIcons = async () => { | ... | @@ -246,41 +245,29 @@ const fetchHomeIcons = async () => { |
| 246 | const hotProducts = ref([]); | 245 | const hotProducts = ref([]); |
| 247 | 246 | ||
| 248 | /** | 247 | /** |
| 249 | - * Mock 数据开关 | ||
| 250 | - * @description 开发环境默认使用 mock 数据测试计划书功能 | ||
| 251 | - * 生产环境必须设置为 false | ||
| 252 | - */ | ||
| 253 | -const USE_MOCK_DATA = process.env.NODE_ENV === 'development'; // 开发环境默认使用 mock 数据 | ||
| 254 | - | ||
| 255 | -/** | ||
| 256 | * 获取热卖产品列表 | 248 | * 获取热卖产品列表 |
| 257 | * | 249 | * |
| 258 | - * @description 根据 USE_MOCK_DATA 开关决定使用 mock 数据还是调用 API | 250 | + * @description 调用 listAPI 获取真实的热卖产品数据 |
| 259 | - * - 开发环境使用 mock 数据测试计划书功能 | ||
| 260 | - * - 生产环境调用 listAPI 获取真实数据 | ||
| 261 | */ | 251 | */ |
| 262 | const fetchHotProducts = async () => { | 252 | const fetchHotProducts = async () => { |
| 263 | try { | 253 | try { |
| 264 | - // 开发测试环境:使用 mock 数据 | 254 | + console.log('[Index] 获取热卖产品'); |
| 265 | - if (USE_MOCK_DATA) { | ||
| 266 | - console.log('[Index] 使用 mock 数据获取热卖产品'); | ||
| 267 | - const res = await mockHotProductsListAPI({ recommend: 'hot' }); | ||
| 268 | - if (res.code === 1 && res.data && res.data.list) { | ||
| 269 | - hotProducts.value = res.data.list; | ||
| 270 | - console.log('[Index] Mock 数据加载成功,产品数量:', res.data.list.length); | ||
| 271 | - } | ||
| 272 | - return; | ||
| 273 | - } | ||
| 274 | 255 | ||
| 275 | - // 生产环境:调用真实 API | 256 | + // 调用真实 API |
| 276 | const res = await listAPI({ | 257 | const res = await listAPI({ |
| 277 | recommend: 'hot' | 258 | recommend: 'hot' |
| 278 | }); | 259 | }); |
| 260 | + | ||
| 279 | if (res.code === 1 && res.data && res.data.list) { | 261 | if (res.code === 1 && res.data && res.data.list) { |
| 280 | hotProducts.value = res.data.list; | 262 | hotProducts.value = res.data.list; |
| 263 | + console.log('[Index] 热卖产品加载成功,数量:', res.data.list.length); | ||
| 264 | + } else { | ||
| 265 | + console.warn('[Index] 热卖产品返回数据格式不正确:', res); | ||
| 266 | + hotProducts.value = []; | ||
| 281 | } | 267 | } |
| 282 | } catch (err) { | 268 | } catch (err) { |
| 283 | - console.error('获取热卖产品失败:', err); | 269 | + console.error('[Index] 获取热卖产品失败:', err); |
| 270 | + hotProducts.value = []; | ||
| 284 | } | 271 | } |
| 285 | }; | 272 | }; |
| 286 | 273 | ... | ... |
This diff is collapsed. Click to expand it.
-
Please register or login to post a comment