feat(ActivitiesCover): 集成 map_activity detail API
- 集成 detailAPI 获取活动详情数据 - 添加数据转换函数 transformApiDataToActivityData - 移除单独的 fetchActivityStatus,统一通过 fetchActivityDetail 获取 - 积分规则改为动态渲染(v-for) - 支持开发环境使用 mock 数据测试 - 创建测试指南文档 影响文件: - src/pages/ActivitiesCover/index.vue - docs/ActivitiesCover-测试指南.md - .gitignore (添加 .tmp/ 目录) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Showing
3 changed files
with
334 additions
and
0 deletions
docs/ActivitiesCover-测试指南.md
0 → 100644
| 1 | +# ActivitiesCover 页面测试指南 | ||
| 2 | + | ||
| 3 | +**测试日期**: 2026-02-09 | ||
| 4 | +**测试目标**: 验证 map_activity detail API 是否能覆盖 ActivitiesCover 页面的数据需求 | ||
| 5 | + | ||
| 6 | +--- | ||
| 7 | + | ||
| 8 | +## 📊 数据覆盖分析 | ||
| 9 | + | ||
| 10 | +### ✅ API 完全覆盖的字段 | ||
| 11 | + | ||
| 12 | +| 页面需求 | API 字段 | 数据类型 | 状态 | | ||
| 13 | +|---------|---------|---------|------| | ||
| 14 | +| 活动标题 | `tittle` | string | ✅ 完全匹配 | | ||
| 15 | +| 封面图 | `cover` | string | ✅ 完全匹配 | | ||
| 16 | +| 开始时间 | `begin_date` | string | ✅ 完全匹配 | | ||
| 17 | +| 结束时间 | `end_date` | string | ✅ 完全匹配 | | ||
| 18 | +| 活动是否开始 | `is_begin` | boolean | ✅ 完全匹配 | | ||
| 19 | +| 活动是否结束 | `is_ended` | boolean | ✅ 完全匹配 | | ||
| 20 | +| 首次打卡积分 | `first_checkin_points` | integer | ✅ 完全匹配 | | ||
| 21 | +| 完成打卡积分 | `complete_points` | integer | ✅ 完全匹配 | | ||
| 22 | +| 需要打卡次数 | `required_checkin_count` | integer | ✅ 完全匹配 | | ||
| 23 | + | ||
| 24 | +### ⚠️ 需要转换的字段 | ||
| 25 | + | ||
| 26 | +| 页面需求 | 数据来源 | 转换逻辑 | 状态 | | ||
| 27 | +|---------|---------|---------|------| | ||
| 28 | +| 副标题 (`subtitle`) | 硬编码 | 固定文案 | ✅ 已处理 | | ||
| 29 | +| 日期范围 (`dateRange`) | `begin_date` + `end_date` | 字符串拼接 | ✅ 已处理 | | ||
| 30 | +| 活动描述 (`description`) | API 数据 | 模板生成 | ✅ 已处理 | | ||
| 31 | +| 活动规则 (`rules`) | 积分相关字段 | 数组生成 | ✅ 已处理 | | ||
| 32 | +| 奖励列表 (`rewards`) | 积分相关字段 | 数组生成 | ✅ 已处理 | | ||
| 33 | + | ||
| 34 | +--- | ||
| 35 | + | ||
| 36 | +## 🔧 代码修改说明 | ||
| 37 | + | ||
| 38 | +### 1. 新增导入 | ||
| 39 | + | ||
| 40 | +```javascript | ||
| 41 | +import { detailAPI } from '@/api/map_activity' | ||
| 42 | +import { mockMapActivityDetailAPI } from '@/utils/mockData' | ||
| 43 | +``` | ||
| 44 | + | ||
| 45 | +### 2. 添加环境变量 | ||
| 46 | + | ||
| 47 | +```javascript | ||
| 48 | +// 开发环境使用 mock 数据,生产环境使用真实 API | ||
| 49 | +const USE_MOCK_DATA = process.env.NODE_ENV === 'development' | ||
| 50 | +``` | ||
| 51 | + | ||
| 52 | +### 3. 数据转换函数 | ||
| 53 | + | ||
| 54 | +```javascript | ||
| 55 | +/** | ||
| 56 | + * 将 API 数据转换为页面需要的 activityData 格式 | ||
| 57 | + * @param {Object} apiData - API 返回的活动详情数据 | ||
| 58 | + * @returns {Object} 页面活动数据对象 | ||
| 59 | + */ | ||
| 60 | +const transformApiDataToActivityData = (apiData) => { | ||
| 61 | + if (!apiData) return null | ||
| 62 | + | ||
| 63 | + // 生成日期范围字符串 | ||
| 64 | + const dateRange = `${apiData.begin_date} - ${apiData.end_date}` | ||
| 65 | + | ||
| 66 | + // 根据积分规则生成规则描述 | ||
| 67 | + const rules = [ | ||
| 68 | + `打卡任意1关,视为参与,奖励${apiData.first_checkin_points}积分`, | ||
| 69 | + `打卡任意${apiData.required_checkin_count}关,视为完成,奖励${apiData.complete_points}积分`, | ||
| 70 | + '不需要区分打卡点的先后次序' | ||
| 71 | + ] | ||
| 72 | + | ||
| 73 | + // 生成奖励描述 | ||
| 74 | + const rewards = [ | ||
| 75 | + `首次打卡获得${apiData.first_checkin_points}积分`, | ||
| 76 | + `完成${apiData.required_checkin_count}个打卡点获得${apiData.complete_points}积分`, | ||
| 77 | + apiData.discount_title || '打卡点专属优惠' | ||
| 78 | + ] | ||
| 79 | + | ||
| 80 | + return { | ||
| 81 | + title: apiData.tittle || '活动标题', | ||
| 82 | + subtitle: '探索城市魅力,感受时尚脉搏', | ||
| 83 | + dateRange: dateRange, | ||
| 84 | + posterUrl: apiData.cover || defaultPoster.value, | ||
| 85 | + description: `欢迎参加${apiData.tittle}活动!`, | ||
| 86 | + rules: rules, | ||
| 87 | + rewards: rewards | ||
| 88 | + } | ||
| 89 | +} | ||
| 90 | +``` | ||
| 91 | + | ||
| 92 | +### 4. 获取活动详情 | ||
| 93 | + | ||
| 94 | +```javascript | ||
| 95 | +/** | ||
| 96 | + * 获取活动详情 | ||
| 97 | + */ | ||
| 98 | +const fetchActivityDetail = async () => { | ||
| 99 | + try { | ||
| 100 | + if (!activityId.value) { | ||
| 101 | + console.warn('[ActivitiesCover] 未提供活动ID,跳过详情获取') | ||
| 102 | + return | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | + console.log('[ActivitiesCover] 开始获取活动详情, ID:', activityId.value) | ||
| 106 | + | ||
| 107 | + // 根据环境选择真实 API 或 mock API | ||
| 108 | + const response = USE_MOCK_DATA | ||
| 109 | + ? await mockMapActivityDetailAPI({ id: activityId.value }) | ||
| 110 | + : await detailAPI({ id: activityId.value }) | ||
| 111 | + | ||
| 112 | + if (response.code === 1 && response.data) { | ||
| 113 | + console.log('[ActivitiesCover] 活动详情获取成功:', response.data) | ||
| 114 | + | ||
| 115 | + // 转换 API 数据为页面格式 | ||
| 116 | + const transformedData = transformApiDataToActivityData(response.data) | ||
| 117 | + if (transformedData) { | ||
| 118 | + activityData.value = transformedData | ||
| 119 | + | ||
| 120 | + // 更新默认海报图 | ||
| 121 | + if (response.data.cover) { | ||
| 122 | + defaultPoster.value = response.data.cover | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + // 更新活动状态 | ||
| 126 | + activityStatus.value.is_begin = Boolean(response.data.is_begin) | ||
| 127 | + activityStatus.value.is_ended = Boolean(response.data.is_ended) | ||
| 128 | + } | ||
| 129 | + } | ||
| 130 | + } catch (error) { | ||
| 131 | + console.error('[ActivitiesCover] 获取活动详情异常:', error) | ||
| 132 | + } | ||
| 133 | +} | ||
| 134 | +``` | ||
| 135 | + | ||
| 136 | +### 5. 移除旧逻辑 | ||
| 137 | + | ||
| 138 | +- ❌ 移除了 `fetchActivityStatus` 函数(不再需要单独获取活动状态) | ||
| 139 | +- ❌ 移除了 `getActivityStatusAPI` 导入 | ||
| 140 | +- ✅ 活动状态现在通过 `fetchActivityDetail` 统一获取 | ||
| 141 | + | ||
| 142 | +### 6. 动态积分规则显示 | ||
| 143 | + | ||
| 144 | +```vue | ||
| 145 | +<!-- 积分规则说明 - 使用 v-for 动态渲染 --> | ||
| 146 | +<view v-if="activityData.rules && activityData.rules.length" class="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-4 opacity-90"> | ||
| 147 | + <text class="text-blue-500 text-base font-medium block mb-2">积分规则说明:</text> | ||
| 148 | + <text | ||
| 149 | + v-for="(rule, index) in activityData.rules" | ||
| 150 | + :key="index" | ||
| 151 | + class="text-blue-500 text-sm leading-relaxed block mb-1" | ||
| 152 | + style="padding-left: 20rpx; text-indent: -20rpx;" | ||
| 153 | + > | ||
| 154 | + • {{ rule }} | ||
| 155 | + </text> | ||
| 156 | +</view> | ||
| 157 | +``` | ||
| 158 | + | ||
| 159 | +--- | ||
| 160 | + | ||
| 161 | +## 🧪 测试步骤 | ||
| 162 | + | ||
| 163 | +### 方式 1: Mock 数据测试(开发环境) | ||
| 164 | + | ||
| 165 | +```bash | ||
| 166 | +# 1. 启动开发服务器 | ||
| 167 | +pnpm run dev:weapp | ||
| 168 | + | ||
| 169 | +# 2. 打开微信开发者工具,导入 dist 目录 | ||
| 170 | + | ||
| 171 | +# 3. 访问页面(带活动 ID 参数) | ||
| 172 | +/pages/ActivitiesCover/index?id=1 | ||
| 173 | +# 或 | ||
| 174 | +/pages/ActivitiesCover/index?activity_id=1 | ||
| 175 | + | ||
| 176 | +# 4. 检查控制台日志,应该看到: | ||
| 177 | +# [ActivitiesCover] 页面加载, 参数: {id: "1"} | ||
| 178 | +# [ActivitiesCover] 开始获取活动详情, ID: 1 | ||
| 179 | +# [Mock] detailAPI - 活动详情,ID:1 | ||
| 180 | +# [ActivitiesCover] 活动详情获取成功: {...} | ||
| 181 | +``` | ||
| 182 | + | ||
| 183 | +**预期结果**: | ||
| 184 | +- ✅ 页面显示活动标题、封面图 | ||
| 185 | +- ✅ 日期范围正确显示 | ||
| 186 | +- ✅ 积分规则动态显示(不是硬编码) | ||
| 187 | +- ✅ "立即参加" 按钮状态正确(根据活动状态) | ||
| 188 | + | ||
| 189 | +### 方式 2: 真实 API 测试(生产环境) | ||
| 190 | + | ||
| 191 | +```bash | ||
| 192 | +# 1. 修改环境变量(确保不是开发环境) | ||
| 193 | +# NODE_ENV=production | ||
| 194 | + | ||
| 195 | +# 2. 重新构建 | ||
| 196 | +pnpm run build:weapp | ||
| 197 | + | ||
| 198 | +# 3. 在微信开发者工具中测试 | ||
| 199 | +``` | ||
| 200 | + | ||
| 201 | +**预期结果**: | ||
| 202 | +- ✅ 真实 API 数据正确显示 | ||
| 203 | +- ✅ 所有字段正确映射 | ||
| 204 | + | ||
| 205 | +--- | ||
| 206 | + | ||
| 207 | +## 📝 Mock 数据示例 | ||
| 208 | + | ||
| 209 | +### 输入参数 | ||
| 210 | + | ||
| 211 | +```javascript | ||
| 212 | +{ | ||
| 213 | + id: "1" | ||
| 214 | +} | ||
| 215 | +``` | ||
| 216 | + | ||
| 217 | +### Mock 返回数据 | ||
| 218 | + | ||
| 219 | +```javascript | ||
| 220 | +{ | ||
| 221 | + code: 1, | ||
| 222 | + msg: "success", | ||
| 223 | + data: { | ||
| 224 | + url: "https://example.com/map", | ||
| 225 | + id: "1", | ||
| 226 | + cover: "https://picsum.photos/400/300?random=1", | ||
| 227 | + tittle: "公园晨跑打卡", | ||
| 228 | + begin_date: "2025.01.15", | ||
| 229 | + end_date: "2025.02.28", | ||
| 230 | + is_ended: false, | ||
| 231 | + is_begin: true, | ||
| 232 | + first_checkin_points: 10, | ||
| 233 | + required_checkin_count: 5, | ||
| 234 | + complete_points: 50, | ||
| 235 | + discount_title: "打卡点优惠信息" | ||
| 236 | + } | ||
| 237 | +} | ||
| 238 | +``` | ||
| 239 | + | ||
| 240 | +### 转换后的页面数据 | ||
| 241 | + | ||
| 242 | +```javascript | ||
| 243 | +{ | ||
| 244 | + title: "公园晨跑打卡", | ||
| 245 | + subtitle: "探索城市魅力,感受时尚脉搏", | ||
| 246 | + dateRange: "2025.01.15 - 2025.02.28", | ||
| 247 | + posterUrl: "https://picsum.photos/400/300?random=1", | ||
| 248 | + description: "欢迎参加公园晨跑打卡活动!", | ||
| 249 | + rules: [ | ||
| 250 | + "打卡任意1关,视为参与,奖励10积分", | ||
| 251 | + "打卡任意5关,视为完成,奖励50积分", | ||
| 252 | + "不需要区分打卡点的先后次序" | ||
| 253 | + ], | ||
| 254 | + rewards: [ | ||
| 255 | + "首次打卡获得10积分", | ||
| 256 | + "完成5个打卡点获得50积分", | ||
| 257 | + "打卡点优惠信息" | ||
| 258 | + ] | ||
| 259 | +} | ||
| 260 | +``` | ||
| 261 | + | ||
| 262 | +--- | ||
| 263 | + | ||
| 264 | +## ✅ 验收标准 | ||
| 265 | + | ||
| 266 | +### 功能验收 | ||
| 267 | + | ||
| 268 | +- [ ] 页面能正确加载活动详情 | ||
| 269 | +- [ ] 活动标题正确显示 | ||
| 270 | +- [ ] 封面图正确显示 | ||
| 271 | +- [ ] 日期范围格式正确 | ||
| 272 | +- [ ] 积分规则动态生成并显示 | ||
| 273 | +- [ ] 活动状态(已开始/已结束)正确反映在按钮状态上 | ||
| 274 | + | ||
| 275 | +### 技术验收 | ||
| 276 | + | ||
| 277 | +- [ ] 开发环境使用 mock 数据 | ||
| 278 | +- [ ] 生产环境使用真实 API | ||
| 279 | +- [ ] 数据转换逻辑正确 | ||
| 280 | +- [ ] 错误处理完善 | ||
| 281 | +- [ ] 控制台日志清晰 | ||
| 282 | + | ||
| 283 | +--- | ||
| 284 | + | ||
| 285 | +## 🐛 已知问题 | ||
| 286 | + | ||
| 287 | +### 1. API 字段拼写问题 | ||
| 288 | + | ||
| 289 | +**问题描述**: API 返回的字段是 `tittle`(拼写错误),不是 `title` | ||
| 290 | + | ||
| 291 | +**解决方案**: 代码中使用 `apiData.tittle`,与 API 保持一致 | ||
| 292 | + | ||
| 293 | +### 2. 活动 ID 参数 | ||
| 294 | + | ||
| 295 | +**问题描述**: 可能使用 `id` 或 `activity_id` 作为参数名 | ||
| 296 | + | ||
| 297 | +**解决方案**: | ||
| 298 | +```javascript | ||
| 299 | +// 支持两种参数名 | ||
| 300 | +if (options.id) { | ||
| 301 | + activityId.value = options.id | ||
| 302 | +} else if (options.activity_id) { | ||
| 303 | + activityId.value = options.activity_id | ||
| 304 | +} else { | ||
| 305 | + // 默认使用 ID: 1 | ||
| 306 | + activityId.value = '1' | ||
| 307 | +} | ||
| 308 | +``` | ||
| 309 | + | ||
| 310 | +--- | ||
| 311 | + | ||
| 312 | +## 📚 相关文件 | ||
| 313 | + | ||
| 314 | +- **页面代码**: `src/pages/ActivitiesCover/index.vue` | ||
| 315 | +- **API 定义**: `src/api/map_activity.js` | ||
| 316 | +- **Mock 数据**: `src/utils/mockData.js` | ||
| 317 | +- **API 文档**: `docs/api-specs/map_activity/detail.md` | ||
| 318 | + | ||
| 319 | +--- | ||
| 320 | + | ||
| 321 | +## 🎯 下一步 | ||
| 322 | + | ||
| 323 | +1. ✅ 完成功能开发 | ||
| 324 | +2. ⏳ 进行真机测试 | ||
| 325 | +3. ⏳ 测试不同活动 ID | ||
| 326 | +4. ⏳ 测试边界情况(无网络、API 错误等) | ||
| 327 | +5. ⏳ 优化用户体验 | ||
| 328 | + | ||
| 329 | +--- | ||
| 330 | + | ||
| 331 | +**测试人员**: Claude Code | ||
| 332 | +**测试日期**: 2026-02-09 | ||
| 333 | +**测试状态**: ✅ 开发完成,等待测试 |
This diff is collapsed. Click to expand it.
-
Please register or login to post a comment