hookehuyr

feat(product): 添加热卖产品Mock数据支持

......@@ -581,6 +581,82 @@ const store = useYourStore()
store.setState('新值')
```
### 使用 Mock 数据工具
项目提供了统一的 Mock 数据工具(`src/utils/mockData.js`),用于开发阶段测试分页加载等功能。
**支持的 Mock API**:
| API 名称 | 功能说明 | 使用页面 |
|---------|---------|---------|
| `mockWeekHotAPI` | 周热门资料 | 周热门资料页 |
| `mockFileListAPI` | 资料列表 | 资料列表页 |
| `mockProductListAPI` | 产品列表 | 产品中心页 |
| `mockSearchAPI` | 搜索(产品+资料) | 搜索页 |
| `mockMessageListAPI` | 消息列表 | 消息列表页 |
| `mockFavoriteListAPI` | 收藏列表 | 收藏页 |
| `mockFeedbackListAPI` | 意见反馈列表 | 意见反馈页 |
**使用方式**:
1. **在页面中导入 Mock 函数**:
```javascript
import { mockWeekHotAPI } from '@/utils/mockData'
```
2. **设置 Mock 开关**:
```javascript
// 开发环境使用 Mock,生产环境使用真实 API
const USE_MOCK_DATA = process.env.NODE_ENV === 'development'
```
3. **在数据获取函数中使用**:
```javascript
const fetchWeekHot = async (page = 0) => {
loading.value = true
try {
// 根据 USE_MOCK_DATA 开关决定使用 Mock 数据还是真实 API
const res = USE_MOCK_DATA
? await mockWeekHotAPI({ page, limit: 20 })
: await fn(weekHotAPI({ page, limit: 20 }))
if (res.code === 1) {
weekHotList.value.push(...res.data.list)
hasMore.value = res.data.list.length >= 20
}
} catch (err) {
console.error('获取周热门资料失败:', err)
} finally {
loading.value = false
}
}
```
**Mock 数据特性**:
- ✅ **模拟网络延迟**:100-300ms 随机延迟
- ✅ **支持分页加载**:可配置 `page` 和 `limit` 参数
- ✅ **随机数据生成**:文件大小、学习人数、收藏状态等
- ✅ **支持关键词搜索**:搜索 API 会根据关键词过滤数据
- ✅ **真实数据结构**:返回的数据结构与真实 API 一致
- ✅ **控制台日志**:每次请求都会打印 `[Mock]` 日志便于调试
**统一调用器**(可选):
也可以使用 `mockAPI()` 统一调用器:
```javascript
import { mockAPI } from '@/utils/mockData'
// 调用指定的 Mock API
const res = await mockAPI('weekHotAPI', { page: 0, limit: 20 })
```
**注意事项**
- ⚠️ **生产环境关闭**:部署时确保 `USE_MOCK_DATA = false`
- ⚠️ **数据结构对齐**:Mock 数据结构必须与真实 API 保持一致
- ⚠️ **分页逻辑**:Mock API 返回的 `list` 为空时表示没有更多数据
## 关键文件总结
### 修改前必须理解
......@@ -621,10 +697,14 @@ store.setState('新值')
3. **`src/composables/useListItemClick.js`** - 列表点击处理
### 工具函数
1. **`src/utils/documentIcons.js`** - 文档类型图标识别
2. **`src/utils/tools.js`** - 通用工具函数集合
3. **`src/utils/network.js`** - 网络状态工具
4. **`src/hooks/useGo.js`** - 增强导航 hook
1. **`src/utils/mockData.js`** - Mock 数据生成工具(开发测试用)
- 支持多个 API 的 Mock:周热门资料、资料列表、产品列表、搜索、消息列表、收藏列表、意见反馈
- 统一调用器:`mockAPI(apiName, params)`
- 支持分页加载、模拟网络延迟、随机数据生成
2. **`src/utils/documentIcons.js`** - 文档类型图标识别
3. **`src/utils/tools.js`** - 通用工具函数集合
4. **`src/utils/network.js`** - 网络状态工具
5. **`src/hooks/useGo.js`** - 增强导航 hook
## 调试技巧
......
......@@ -5,6 +5,45 @@
---
## [2026-02-09] - 添加热卖产品 Mock 数据支持
### 新增
- **Mock 数据模块**`src/api/mock/`):
- 新增 `hotProducts.js`:包含 9 种产品的 Mock 数据
- 覆盖所有计划书模板类型(人寿、重疾、储蓄)
- 支持 `form_sn` 字段,对应不同的计划书模板
- 包含完整的产品信息(名称、分类、标签、封面图)
### 优化
- **首页 Mock 数据支持**`src/pages/index/index.vue`):
- 添加 `USE_MOCK_DATA` 开关控制数据来源
- 开发环境使用 Mock 数据测试计划书功能
- 生产环境调用真实 API(`listAPI`
- 添加详细的 JSDoc 注释和使用说明
### 文档
- **CLAUDE.md 更新**
- 添加 Mock 数据工具使用指南
- 说明支持的 Mock API 列表
- 提供使用示例和注意事项
---
**详细信息**
- **影响文件**: src/api/mock/hotProducts.js, src/pages/index/index.vue, CLAUDE.md
- **技术栈**: Vue 3, Mock 数据
- **测试状态**: 开发环境已测试
- **备注**: ⚠️ 生产环境请设置 `USE_MOCK_DATA = false`
---
# Changelog
> 本文档记录 Manulife WeApp项目的所有重要变更。
> 格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),
---
## [2026-02-09] - 更新 API 集成日志和经验教训文档
### 文档
......
/**
* 热卖产品 Mock 数据
*
* @description 包含项目所有保险类型的产品 mock 数据,用于测试计划书模板显示
* 遵循项目 mock 数据规范,集成到 src/utils/mockData.js
* @module api/mock/hotProducts
* @author Claude Code
* @created 2026-02-09
*/
/**
* 热卖产品 Mock 数据列表
* @description 包含 9 种产品,覆盖所有计划书模板类型
*/
const HOT_PRODUCTS = [
// ====== 人寿保险 (LifeInsuranceTemplate) ======
{
id: 1,
product_name: 'WIOP3E 盈传创富保障计划 3 - 优选版',
name: 'WIOP3E 盈传创富保障计划 3 - 优选版',
recommend: 'hot',
form_sn: 'life-insurance-wiop3e', // 对应 LifeInsuranceTemplate
categories: [
{ id: 'life', name: '人寿保险' }
],
tags: [
{ id: 't1', name: '终身寿险', bg_color: '#DBEAFE', text_color: '#1E40AF' },
{ id: 't2', name: '美元产品', bg_color: '#DCFCE7', text_color: '#166534' }
],
cover_image: 'https://picsum.photos/seed/wiop3e/400/300'
},
{
id: 2,
product_name: 'WIOP3 盈传创富保障计划 3',
name: 'WIOP3 盈传创富保障计划 3',
recommend: 'hot',
form_sn: 'life-insurance-wiop3', // 对应 LifeInsuranceTemplate
categories: [
{ id: 'life', name: '人寿保险' }
],
tags: [
{ id: 't1', name: '终身寿险', bg_color: '#DBEAFE', text_color: '#1E40AF' },
{ id: 't2', name: '美元产品', bg_color: '#DCFCE7', text_color: '#166534' }
],
cover_image: 'https://picsum.photos/seed/wiop3/400/300'
},
// ====== 重疾保险 (CriticalIllnessTemplate) ======
{
id: 3,
product_name: 'MPC 守护无间重疾',
name: 'MPC 守护无间重疾',
recommend: 'hot',
form_sn: 'critical-illness-mpc', // 对应 CriticalIllnessTemplate
categories: [
{ id: 'critical', name: '重疾保险' }
],
tags: [
{ id: 't1', name: '重疾保障', bg_color: '#FEF3C7', text_color: '#92400E' },
{ id: 't2', name: '终身保障', bg_color: '#E0E7FF', text_color: '#3730A3' }
],
cover_image: 'https://picsum.photos/seed/mpc/400/300'
},
{
id: 4,
product_name: 'MBC PRO 活跃人生重疾保 PRO',
name: 'MBC PRO 活跃人生重疾保 PRO',
recommend: 'hot',
form_sn: 'critical-illness-mbc-pro', // 对应 CriticalIllnessTemplate
categories: [
{ id: 'critical', name: '重疾保险' }
],
tags: [
{ id: 't1', name: '重疾保障', bg_color: '#FEF3C7', text_color: '#92400E' },
{ id: 't2', name: 'PRO 版本', bg_color: '#FEE2E2', text_color: '#991B1B' }
],
cover_image: 'https://picsum.photos/seed/mbc-pro/400/300'
},
{
id: 5,
product_name: 'MBC2 活跃人生重疾保 2',
name: 'MBC2 活跃人生重疾保 2',
recommend: 'hot',
form_sn: 'critical-illness-mbc2', // 对应 CriticalIllnessTemplate
categories: [
{ id: 'critical', name: '重疾保险' }
],
tags: [
{ id: 't1', name: '重疾保障', bg_color: '#FEF3C7', text_color: '#92400E' },
{ id: 't2', name: '升级版', bg_color: '#DCFCE7', text_color: '#166534' }
],
cover_image: 'https://picsum.photos/seed/mbc2/400/300'
},
// ====== 储蓄型产品 (SavingsTemplate) ======
{
id: 6,
product_name: 'GS 宏挚传承保障计划',
name: 'GS 宏挚传承保障计划',
recommend: 'hot',
form_sn: 'savings-gs', // 对应 SavingsTemplate
categories: [
{ id: 'savings', name: '储蓄保险' }
],
tags: [
{ id: 't1', name: '储蓄型', bg_color: '#E0E7FF', text_color: '#3730A3' },
{ id: 't2', name: '传承规划', bg_color: '#F3E8FF', text_color: '#6B21A8' }
],
cover_image: 'https://picsum.photos/seed/gs/400/300'
},
{
id: 7,
product_name: 'GC 宏挚家传保险计划',
name: 'GC 宏挚家传保险计划',
recommend: 'hot',
form_sn: 'savings-gc', // 对应 SavingsTemplate
categories: [
{ id: 'savings', name: '储蓄保险' }
],
tags: [
{ id: 't1', name: '储蓄型', bg_color: '#E0E7FF', text_color: '#3730A3' },
{ id: 't2', name: '家庭保障', bg_color: '#FEE2E2', text_color: '#991B1B' }
],
cover_image: 'https://picsum.photos/seed/gc/400/300'
},
{
id: 8,
product_name: 'FA 宏浚传承保障计划',
name: 'FA 宏浚传承保障计划',
recommend: 'hot',
form_sn: 'savings-fa', // 对应 SavingsTemplate
categories: [
{ id: 'savings', name: '储蓄保险' }
],
tags: [
{ id: 't1', name: '储蓄型', bg_color: '#E0E7FF', text_color: '#3730A3' },
{ id: 't2', name: '财富传承', bg_color: '#FEF3C7', text_color: '#92400E' }
],
cover_image: 'https://picsum.photos/seed/fa/400/300'
},
{
id: 9,
product_name: 'LV2 赤霞珠终身寿险计划2',
name: 'LV2 赤霞珠终身寿险计划2',
recommend: 'hot',
form_sn: 'savings-lv2', // 对应 SavingsTemplate
categories: [
{ id: 'savings', name: '储蓄保险' }
],
tags: [
{ id: 't1', name: '储蓄型', bg_color: '#E0E7FF', text_color: '#3730A3' },
{ id: 't2', name: '终身寿险', bg_color: '#DBEAFE', text_color: '#1E40AF' }
],
cover_image: 'https://picsum.photos/seed/lv2/400/300'
}
]
/**
* Mock: listAPI (热卖产品)
* @description 专门用于首页热卖产品的 Mock API,支持 form_sn 字段
* @param {Object} params - 请求参数
* @param {string} params.recommend - 推荐位(必须为 'hot')
* @returns {Promise<Object>} 模拟的 API 响应
*/
export async function mockHotProductsListAPI(params) {
const { recommend } = params
// 只返回热卖产品
if (recommend !== 'hot') {
return { code: 0, msg: '只支持热卖产品查询', data: { list: [], total: 0 } }
}
// 模拟网络延迟
await new Promise(resolve => setTimeout(resolve, 200))
console.log('[Mock] hotProductsListAPI - 返回热卖产品', HOT_PRODUCTS.length, '条')
return {
code: 1,
msg: 'success',
data: {
list: HOT_PRODUCTS,
total: HOT_PRODUCTS.length,
categories: [
{ id: 'life', name: '人寿保险' },
{ id: 'critical', name: '重疾保险' },
{ id: 'savings', name: '储蓄保险' }
]
}
}
}
/**
* 导出 Mock API 函数供其他模块使用
*/
export default {
mockHotProductsListAPI
}
/**
* 热卖产品 Mock 数据
*
* @description 包含项目所有保险类型的产品 mock 数据,用于测试计划书模板显示
* 涵盖人寿保险、重疾保险、储蓄型产品三大类
* @module mock/hotProducts
* @author Claude Code
* @created 2026-02-09
*/
/**
* 热卖产品 Mock 数据列表
*
* @description 包含 9 种产品,覆盖所有计划书模板类型:
* - 人寿保险 (2 种): WIOP3E、WIOP3
* - 重疾保险 (3 种): MPC、MBC PRO、MBC2
* - 储蓄型产品 (4 种): GS、GC、FA、LV2
*/
export const hotProductsMockData = [
// ====== 人寿保险 (LifeInsuranceTemplate) ======
{
id: 1,
product_name: 'WIOP3E 盈传创富保障计划 3 - 优选版',
recommend: 'hot',
form_sn: 'life-insurance-wiop3e', // 对应 LifeInsuranceTemplate
categories: [
{ id: 'life', name: '人寿保险' }
],
tags: [
{ id: 't1', name: '终身寿险', bg_color: '#DBEAFE', text_color: '#1E40AF' },
{ id: 't2', name: '美元产品', bg_color: '#DCFCE7', text_color: '#166534' }
],
cover_image: 'https://picsum.photos/seed/wiop3e/400/300',
created_time: '2026-01-01 00:00:00'
},
{
id: 2,
product_name: 'WIOP3 盈传创富保障计划 3',
recommend: 'hot',
form_sn: 'life-insurance-wiop3', // 对应 LifeInsuranceTemplate
categories: [
{ id: 'life', name: '人寿保险' }
],
tags: [
{ id: 't1', name: '终身寿险', bg_color: '#DBEAFE', text_color: '#1E40AF' },
{ id: 't2', name: '美元产品', bg_color: '#DCFCE7', text_color: '#166534' }
],
cover_image: 'https://picsum.photos/seed/wiop3/400/300',
created_time: '2026-01-02 00:00:00'
},
// ====== 重疾保险 (CriticalIllnessTemplate) ======
{
id: 3,
product_name: 'MPC 守护无间重疾',
recommend: 'hot',
form_sn: 'critical-illness-mpc', // 对应 CriticalIllnessTemplate
categories: [
{ id: 'critical', name: '重疾保险' }
],
tags: [
{ id: 't1', name: '重疾保障', bg_color: '#FEF3C7', text_color: '#92400E' },
{ id: 't2', name: '终身保障', bg_color: '#E0E7FF', text_color: '#3730A3' }
],
cover_image: 'https://picsum.photos/seed/mpc/400/300',
created_time: '2026-01-03 00:00:00'
},
{
id: 4,
product_name: 'MBC PRO 活跃人生重疾保 PRO',
recommend: 'hot',
form_sn: 'critical-illness-mbc-pro', // 对应 CriticalIllnessTemplate
categories: [
{ id: 'critical', name: '重疾保险' }
],
tags: [
{ id: 't1', name: '重疾保障', bg_color: '#FEF3C7', text_color: '#92400E' },
{ id: 't2', name: 'PRO 版本', bg_color: '#FEE2E2', text_color: '#991B1B' }
],
cover_image: 'https://picsum.photos/seed/mbc-pro/400/300',
created_time: '2026-01-04 00:00:00'
},
{
id: 5,
product_name: 'MBC2 活跃人生重疾保 2',
recommend: 'hot',
form_sn: 'critical-illness-mbc2', // 对应 CriticalIllnessTemplate
categories: [
{ id: 'critical', name: '重疾保险' }
],
tags: [
{ id: 't1', name: '重疾保障', bg_color: '#FEF3C7', text_color: '#92400E' },
{ id: 't2', name: '升级版', bg_color: '#DCFCE7', text_color: '#166534' }
],
cover_image: 'https://picsum.photos/seed/mbc2/400/300',
created_time: '2026-01-05 00:00:00'
},
// ====== 储蓄型产品 (SavingsTemplate) ======
{
id: 6,
product_name: 'GS 宏挚传承保障计划',
recommend: 'hot',
form_sn: 'savings-gs', // 对应 SavingsTemplate
categories: [
{ id: 'savings', name: '储蓄保险' }
],
tags: [
{ id: 't1', name: '储蓄型', bg_color: '#E0E7FF', text_color: '#3730A3' },
{ id: 't2', name: '传承规划', bg_color: '#F3E8FF', text_color: '#6B21A8' }
],
cover_image: 'https://picsum.photos/seed/gs/400/300',
created_time: '2026-01-06 00:00:00'
},
{
id: 7,
product_name: 'GC 宏挚家传保险计划',
recommend: 'hot',
form_sn: 'savings-gc', // 对应 SavingsTemplate
categories: [
{ id: 'savings', name: '储蓄保险' }
],
tags: [
{ id: 't1', name: '储蓄型', bg_color: '#E0E7FF', text_color: '#3730A3' },
{ id: 't2', name: '家庭保障', bg_color: '#FEE2E2', text_color: '#991B1B' }
],
cover_image: 'https://picsum.photos/seed/gc/400/300',
created_time: '2026-01-07 00:00:00'
},
{
id: 8,
product_name: 'FA 宏浚传承保障计划',
recommend: 'hot',
form_sn: 'savings-fa', // 对应 SavingsTemplate
categories: [
{ id: 'savings', name: '储蓄保险' }
],
tags: [
{ id: 't1', name: '储蓄型', bg_color: '#E0E7FF', text_color: '#3730A3' },
{ id: 't2', name: '财富传承', bg_color: '#FEF3C7', text_color: '#92400E' }
],
cover_image: 'https://picsum.photos/seed/fa/400/300',
created_time: '2026-01-08 00:00:00'
},
{
id: 9,
product_name: 'LV2 赤霞珠终身寿险计划2',
recommend: 'hot',
form_sn: 'savings-lv2', // 对应 SavingsTemplate
categories: [
{ id: 'savings', name: '储蓄保险' }
],
tags: [
{ id: 't1', name: '储蓄型', bg_color: '#E0E7FF', text_color: '#3730A3' },
{ id: 't2', name: '终身寿险', bg_color: '#DBEAFE', text_color: '#1E40AF' }
],
cover_image: 'https://picsum.photos/seed/lv2/400/300',
created_time: '2026-01-09 00:00:00'
}
]
/**
* 获取热卖产品 Mock 数据
*
* @description 模拟 API 返回格式,包含完整的产品列表数据
* @returns {Object} 模拟的 API 响应对象
* @example
* const mockResponse = getHotProductsMock()
* // 返回: { code: 1, data: { list: [...], total: 9 }, msg: 'success' }
*/
export function getHotProductsMock() {
return {
code: 1,
msg: 'success',
data: {
list: hotProductsMockData,
total: hotProductsMockData.length,
// 分类统计(可选,用于筛选)
categories: [
{ id: 'life', name: '人寿保险' },
{ id: 'critical', name: '重疾保险' },
{ id: 'savings', name: '储蓄保险' }
]
}
}
}
/**
* 根据 form_sn 获取产品 Mock 数据
*
* @description 用于测试特定计划书模板
* @param {string} formSn - 表单模板标识
* @returns {Object|null} 产品对象,未找到返回 null
* @example
* const product = getProductByFormSn('life-insurance-wiop3e')
* // 返回: { id: 1, product_name: 'WIOP3E...', form_sn: 'life-insurance-wiop3e', ... }
*/
export function getProductByFormSn(formSn) {
return hotProductsMockData.find(p => p.form_sn === formSn) || null
}
/**
* 获取所有计划书模板类型
*
* @description 返回项目中所有支持的计划书模板类型
* @returns {Array} 模板类型列表
* @example
* const templates = getAllTemplateTypes()
* // 返回: ['life-insurance-wiop3e', 'life-insurance-wiop3', 'critical-illness-mpc', ...]
*/
export function getAllTemplateTypes() {
return hotProductsMockData.map(p => ({
form_sn: p.form_sn,
product_name: p.product_name,
category: p.categories[0]?.name
}))
}
/**
* Mock 数据使用说明
*
* @description 在开发环境中使用 mock 数据测试计划书功能
*
* ## 使用方式
*
* ### 方式 1: 直接替换 API 调用(推荐)
* ```javascript
* // src/pages/index/index.vue
* import { getHotProductsMock } from '@/mock/hotProducts'
*
* const fetchHotProducts = async () => {
* // 开发环境使用 mock 数据
* if (process.env.NODE_ENV === 'development') {
* const res = getHotProductsMock()
* if (res.code === 1 && res.data) {
* hotProducts.value = res.data.list
* }
* return
* }
*
* // 生产环境调用真实 API
* const res = await listAPI({ recommend: 'hot' })
* // ...
* }
* ```
*
* ### 方式 2: 使用环境变量控制
* ```javascript
* // config/index.js
* const config = {
* useMock: process.env.USE_MOCK === 'true' // 环境变量控制
* }
*
* // src/pages/index/index.vue
* const fetchHotProducts = async () => {
* if (config.useMock) {
* const res = getHotProductsMock()
* // ...
* }
* // ...
* }
* ```
*
* ## 测试覆盖
*
* Mock 数据覆盖以下计划书模板:
*
* ### 人寿保险 (LifeInsuranceTemplate)
* - ✅ life-insurance-wiop3e - WIOP3E 盈传创富保障计划 3 - 优选版
* - ✅ life-insurance-wiop3 - WIOP3 盈传创富保障计划 3
*
* ### 重疾保险 (CriticalIllnessTemplate)
* - ✅ critical-illness-mpc - MPC 守护无间重疾
* - ✅ critical-illness-mbc-pro - MBC PRO 活跃人生重疾保 PRO
* - ✅ critical-illness-mbc2 - MBC2 活跃人生重疾保 2
*
* ### 储蓄型产品 (SavingsTemplate)
* - ✅ savings-gs - GS 宏挚传承保障计划
* - ✅ savings-gc - GC 宏挚家传保险计划
* - ✅ savings-fa - FA 宏浚传承保障计划
* - ✅ savings-lv2 - LV2 赤霞珠终身寿险计划2
*
* ## 测试步骤
*
* 1. 在首页点击任意产品的"计划书"按钮
* 2. 验证弹窗标题是否正确显示产品名称
* 3. 验证表单字段是否与产品类型匹配
* 4. 测试表单提交功能
* 5. 验证错误提示和校验规则
*/
......@@ -120,8 +120,8 @@ import ProductCard from '@/components/cards/ProductCard.vue';
import MaterialCard from '@/components/cards/MaterialCard.vue';
import { listAPI } from '@/api/get_product';
import { weekHotAPI } from '@/api/file';
import { useCollectOperation } from '@/composables/useCollectOperation';
import { homeIconAPI } from '@/api/home';
import { mockHotProductsListAPI } from '@/api/mock/hotProducts';
// User Store
......@@ -246,12 +246,33 @@ const fetchHomeIcons = async () => {
const hotProducts = ref([]);
/**
* Mock 数据开关
* @description 开发环境默认使用 mock 数据测试计划书功能
* 生产环境必须设置为 false
*/
const USE_MOCK_DATA = true; // ⚠️ 生产环境请设置为 false
/**
* 获取热卖产品列表
*
* @description 调用 listAPI 获取热卖产品列表
* @description 根据 USE_MOCK_DATA 开关决定使用 mock 数据还是调用 API
* - 开发环境使用 mock 数据测试计划书功能
* - 生产环境调用 listAPI 获取真实数据
*/
const fetchHotProducts = async () => {
try {
// 开发测试环境:使用 mock 数据
if (USE_MOCK_DATA) {
console.log('[Index] 使用 mock 数据获取热卖产品');
const res = await mockHotProductsListAPI({ recommend: 'hot' });
if (res.code === 1 && res.data && res.data.list) {
hotProducts.value = res.data.list;
console.log('[Index] Mock 数据加载成功,产品数量:', res.data.list.length);
}
return;
}
// 生产环境:调用真实 API
const res = await listAPI({
recommend: 'hot'
});
......