# 活动主页实现计划 > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** 创建独立活动主页 `/pages/activity/index`,优先展示用户报名的活动,支持发现活动浏览和历史记录查看。 **Architecture:** 活动主页为独立 subPackage(`pages/activity`),三 Tab 结构(我的活动/发现活动/历史记录),数据通过现有 API 获取,无需后端改动。16 个页面的"更多"按钮跳转目标需更新。 **Tech Stack:** Vue 2 Options API / uni-app / pages.json / 现有 API (`getMyActivityRegistrations`, `getActivityList`, `activityCheckin`, `registerActivity`, `cancelActivityRegistration`) --- ## 文件结构 ``` cfc-frontend/ ├── pages/activity/ # 新建独立 subPackage │ ├── index.vue # 活动主页(核心文件) │ └── activity-detail/activity-detail.vue # 活动详情(可复用现有 discover-detail 版本) ├── pages.json # 注册 pages/activity subPackage ├── pages/index/parent-index.vue # 更新 goMoreActivities ├── pages/index/child-index.vue # 更新 goMoreActivities ├── pages/body/index.vue # 更新 goMoreActivities ├── pages/body/member-body-detail.vue # 更新 goMoreActivities ├── pages/body-detail/member-body-detail.vue # 更新 goMoreActivities ├── pages/mind/index.vue # 更新 goMoreActivities ├── pages/mind/member-mind-detail.vue # 更新 goMoreActivities ├── pages/mind-detail/member-mind-detail.vue # 更新 goMoreActivities ├── pages/wisdom/index.vue # 更新 goMoreActivities ├── pages/wisdom/member-wisdom-detail.vue # 更新 goMoreActivities ├── pages/wisdom/wisdom_temp/index.vue # 更新 goMoreActivities ├── pages/wisdom/wisdom_temp/member-wisdom-detail.vue # 更新 goMoreActivities ├── pages/wisdom-detail/member-wisdom-detail.vue # 更新 goMoreActivities ├── pages/action/index.vue # 更新 goMoreActivities ├── pages/action/member-action-detail.vue # 更新 goMoreActivities ├── pages/action-detail/member-action-detail.vue # 更新 goMoreActivities ``` --- ## Task 1: 注册 pages/activity SubPackage **Files:** - Modify: `cfc-frontend/pages.json` - [ ] **Step 1: 在 pages.json 中添加 activity subPackage** 在 `subPackages` 数组末尾添加: ```json { "root": "pages/activity", "pages": [ { "path": "index", "style": { "navigationBarTitleText": "活动中心" } }, { "path": "activity-detail/activity-detail", "style": { "navigationBarTitleText": "活动详情" } } ] } ``` 验证:运行 `grep -n '"root": "pages/activity"' cfc-frontend/pages.json` 确认存在。 - [ ] **Step 2: 提交** ```bash git add cfc-frontend/pages.json git commit -m "feat(activity): register pages/activity subPackage" ``` --- ## Task 2: 创建活动主页 index.vue **Files:** - Create: `cfc-frontend/pages/activity/index.vue` - [ ] **Step 1: 创建文件结构** ```vue ``` - [ ] **Step 2: 实现 data()** ```javascript data() { return { tabs: [ { key: 'mine', label: '我的活动' }, { key: 'discover', label: '发现活动' }, { key: 'history', label: '历史记录' } ], currentTab: 'mine', mineFilters: [ { label: '全部', value: '' }, { label: '待开始', value: 'upcoming' }, { label: '待审核', value: 'pending' }, { label: '已签到', value: 'checked_in' } ], dimensionFilters: [ { label: '全部', value: '' }, { label: '身', value: 'body' }, { label: '心', value: 'mind' }, { label: '智', value: 'wisdom' }, { label: '行', value: 'action' }, { label: '富', value: 'wealth' } ], currentMineFilter: '', currentDimension: '', myActivities: [], discoverActivities: [], historyActivities: [], mineLoading: false, discoverLoading: false, minePage: 1, discoverPage: 1, historyPage: 1, pageSize: 10, hasMore: true } } ``` - [ ] **Step 3: 实现 onLoad** ```javascript onLoad(options) { // 如果带 tab 参数,切换到对应 Tab if (options && options.tab) { this.currentTab = options.tab } this.loadData() } ``` - [ ] **Step 4: 实现 onShow** ```javascript onShow() { // 每次可见时刷新我的活动(用户可能刚报名/签到) if (this.currentTab === 'mine') { this.loadMyActivities(true) } } ``` - [ ] **Step 5: 实现 methods — Tab 切换** ```javascript onTabChange(key) { this.currentTab = key this.loadData() }, loadData() { if (this.currentTab === 'mine') { this.loadMyActivities(true) } else if (this.currentTab === 'discover') { this.loadDiscoverActivities(true) } else if (this.currentTab === 'history') { this.loadHistoryActivities(true) } } ``` - [ ] **Step 6: 实现 loadMyActivities** ```javascript loadMyActivities(refresh) { if (refresh) { this.minePage = 1 this.myActivities = [] this.hasMore = true } if (!this.hasMore) return this.mineLoading = true const childId = uni.getStorageSync('currentChildId') if (!childId) { this.mineLoading = false return } getMyActivityRegistrations({ childId: childId, page: this.minePage, size: this.pageSize, status: this.currentMineFilter || undefined }).then(res => { this.mineLoading = false if (res.code === 200 && res.data) { const records = res.data.records || [] if (refresh) { this.myActivities = records } else { this.myActivities = this.myActivities.concat(records) } this.hasMore = records.length >= this.pageSize this.minePage++ } }).catch(() => { this.mineLoading = false }) } ``` - [ ] **Step 7: 实现 loadDiscoverActivities** ```javascript loadDiscoverActivities(refresh) { if (refresh) { this.discoverPage = 1 this.discoverActivities = [] this.hasMore = true } if (!this.hasMore) return this.discoverLoading = true getActivityList({ dimensionCode: this.currentDimension || undefined, page: this.discoverPage, size: this.pageSize }).then(res => { this.discoverLoading = false if (res.code === 200 && res.data) { const records = res.data.records || [] if (refresh) { this.discoverActivities = records } else { this.discoverActivities = this.discoverActivities.concat(records) } this.hasMore = records.length >= this.pageSize this.discoverPage++ } }).catch(() => { this.discoverLoading = false }) } ``` - [ ] **Step 8: 实现 loadHistoryActivities(类似 loadMyActivities)** ```javascript loadHistoryActivities(refresh) { // 同 loadMyActivities,但 API 参数可能需要 status=ended // 或直接复用 loadMyActivities 通过 filter 状态过滤 } ``` - [ ] **Step 9: 实现筛选切换** ```javascript onMineFilterChange(value) { this.currentMineFilter = value this.loadMyActivities(true) }, onDimensionChange(value) { this.currentDimension = value this.loadDiscoverActivities(true) }, onLoadMore() { if (this.currentTab === 'mine') { this.loadMyActivities(false) } else if (this.currentTab === 'discover') { this.loadDiscoverActivities(false) } else if (this.currentTab === 'history') { this.loadHistoryActivities(false) } } ``` - [ ] **Step 10: 实现 goDetail / switchToDiscover** ```javascript goDetail(act) { uni.navigateTo({ url: '/pages/discover/activity-detail/activity-detail?id=' + act.id }) }, switchToDiscover() { this.currentTab = 'discover' this.loadDiscoverActivities(true) } ``` - [ ] **Step 11: 提交** ```bash git add cfc-frontend/pages/activity/index.vue git commit -m "feat(activity): create activity home page with 3-tab structure" ``` --- ## Task 3: 实现活动卡片组件(可内联在 index.vue 中) **Files:** - Already created in: `cfc-frontend/pages/activity/index.vue` - [ ] **Step 1: 卡片模板(内联在 index.vue 的 activity-card div 内)** ```vue {{ act.title }} {{ act.startTime }} 📍 {{ act.location }} {{ getStatusText(act) }} {{ formatPrice(act.price) }} 免费 ``` - [ ] **Step 2: 卡片样式(scoped CSS)** ```css .activity-card { display: flex; background: #fff; border-radius: 12px; overflow: hidden; margin-bottom: 12px; box-shadow: 0 1px 4px rgba(0,0,0,0.06); } .card-cover { width: 72px; height: 72px; flex-shrink: 0; } .card-info { flex: 1; padding: 10px 12px; display: flex; flex-direction: column; justify-content: space-between; } .card-title { font-size: 13px; font-weight: bold; color: #333; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .card-meta { font-size: 10px; color: #999; margin-top: 2px; } .card-bottom { display: flex; align-items: center; justify-content: space-between; margin-top: 4px; } .card-status { font-size: 9px; padding: 2px 6px; border-radius: 3px; } .status-upcoming { background: #FFF7ED; color: #F97316; } .status-pending { background: #FEF3C7; color: #D97706; } .status-approved { background: #F0FDF4; color: #22C55E; } .status-checked_in { background: #DCFCE7; color: #16A34A; } .status-ended { background: #F5F5F5; color: #999; } .status-rejected { background: #FEE2E2; color: #EF4444; } .card-price { font-size: 11px; color: #F97316; font-weight: 500; } .card-price.free { color: #22C55E; } ``` - [ ] **Step 3: 辅助方法** ```javascript getStatusClass(act) { // 根据 act.status 和时间判断 // pending/approved/rejected/cancelled/checked_in return act.status || 'upcoming' }, getStatusText(act) { const map = { pending: '待审核', approved: '报名成功', rejected: '已拒绝', cancelled: '已取消', checked_in: '已签到' } // 如果没有 status 但有 endTime 且已过,显示已结束 if (!act.status && act.endTime && new Date(act.endTime) < new Date()) { return '已结束' } return map[act.status] || '即将开始' }, formatPrice(price) { return '¥' + (price / 100).toFixed(2) } ``` - [ ] **Step 4: 提交** ```bash git add cfc-frontend/pages/activity/index.vue git commit -m "feat(activity): add activity card component with status display" ``` --- ## Task 4: 实现报名/签到操作按钮 **Files:** - Already modified in: `cfc-frontend/pages/activity/index.vue` - [ ] **Step 1: 在我的活动 Tab 的卡片底部添加操作按钮** 在 `activity-card` 底部添加(需区分当前 Tab): ```vue ``` - [ ] **Step 2: 实现 onCheckin** ```javascript onCheckin(act) { const childId = uni.getStorageSync('currentChildId') if (!childId) { uni.showToast({ title: '请先选择孩子身份', icon: 'none' }) return } activityCheckin({ id: act.id, childId: childId }).then(res => { if (res.code === 200) { uni.showToast({ title: '签到成功', icon: 'success' }) this.loadMyActivities(true) } else { uni.showToast({ title: res.message || '签到失败', icon: 'none' }) } }) } ``` - [ ] **Step 3: 实现 onCancel** ```javascript onCancel(act) { uni.showModal({ title: '提示', content: '确定要取消报名吗?', success: res => { if (res.confirm) { const childId = uni.getStorageSync('currentChildId') cancelActivityRegistration({ id: act.id, childId: childId }).then(res => { if (res.code === 200) { uni.showToast({ title: '已取消报名', icon: 'success' }) this.loadMyActivities(true) } else { uni.showToast({ title: res.message || '取消失败', icon: 'none' }) } }) } } }) } ``` - [ ] **Step 4: 添加操作按钮样式** ```css .card-actions { display: flex; justify-content: flex-end; gap: 6px; padding-top: 6px; border-top: 1px solid #f5f5f5; margin-top: 6px; } .btn-cancel { background: #fff; border: 1px solid #ddd; color: #666; padding: 4px 12px; border-radius: 14px; font-size: 10px; line-height: 1.5; } .btn-checkin { background: linear-gradient(135deg, #F97316, #FB923C); border: none; color: #fff; padding: 4px 12px; border-radius: 14px; font-size: 10px; line-height: 1.5; } ``` - [ ] **Step 5: 提交** ```bash git add cfc-frontend/pages/activity/index.vue git commit -m "feat(activity): add checkin and cancel registration actions" ``` --- ## Task 5: 更新 16 个页面的 goMoreActivities 跳转目标 **Files:** - Modify: 16个 Vue 文件(见文件结构列表) - [ ] **Step 1: 批量更新 parent-index.vue** 在 `pages/index/parent-index.vue` 中找到 `goMoreActivities` 方法: ```javascript // 修改前 goMoreActivities() { uni.switchTab({ url: '/pages/discover/index' }) } // 修改后 goMoreActivities() { uni.navigateTo({ url: '/pages/activity/index' }) } ``` - [ ] **Step 2: 批量更新其余 15 个文件** 对以下每个文件执行相同修改(将 `goMoreActivities` 中的跳转目标从 `discover/index` 或 `switchTab` 改为 `/pages/activity/index`): ``` pages/index/child-index.vue pages/body/index.vue pages/body/member-body-detail.vue pages/body-detail/member-body-detail.vue pages/mind/index.vue pages/mind/member-mind-detail.vue pages/mind-detail/member-mind-detail.vue pages/wisdom/index.vue pages/wisdom/member-wisdom-detail.vue pages/wisdom/wisdom_temp/index.vue pages/wisdom/wisdom_temp/member-wisdom-detail.vue pages/wisdom-detail/member-wisdom-detail.vue pages/action/index.vue pages/action/member-action-detail.vue pages/action-detail/member-action-detail.vue ``` 验证:每个文件中应该只有一处 `goMoreActivities` 方法,搜索确认后修改。 - [ ] **Step 3: 提交** ```bash git add pages/index/parent-index.vue pages/index/child-index.vue pages/body/index.vue pages/body/member-body-detail.vue pages/body-detail/member-body-detail.vue pages/mind/index.vue pages/mind/member-mind-detail.vue pages/mind-detail/member-mind-detail.vue pages/wisdom/index.vue pages/wisdom/member-wisdom-detail.vue pages/wisdom/wisdom_temp/index.vue pages/wisdom/wisdom_temp/member-wisdom-detail.vue pages/wisdom-detail/member-wisdom-detail.vue pages/action/index.vue pages/action/member-action-detail.vue pages/action-detail/member-action-detail.vue git commit -m "refactor(activity): update goMoreActivities to navigate to /pages/activity/index" ``` --- ## Task 6: 创建 activity-detail 页面(可选,复用现有) **Files:** - Create: `cfc-frontend/pages/activity/activity-detail/activity-detail.vue` - [ ] **Step 1: 复制现有 activity-detail 作为基础** 将 `pages/discover-detail/activity-detail/activity-detail.vue` 复制到 `pages/activity/activity-detail/activity-detail.vue`。 此步骤可选,因为活动详情已有两个版本,路由复用已足够。此任务可跳过。 --- ## Task 7: 整体验证 - [ ] **Step 1: 编译验证** ```bash cd cfc-frontend && npm run dev:mp-weixin 2>&1 | head -50 ``` - [ ] **Step 2: 注册验证** 确认 `pages.json` 中 activity subPackage 正确注册。 - [ ] **Step 3: 路由验证** 在微信开发者工具中测试: 1. 从任意维度页点击"更多 ›",应跳转 `/pages/activity/index` 2. 活动主页三个 Tab 切换正常 3. 点击活动卡片,跳转详情页 --- ## 执行方式选择 **两个执行选项:** **1. Subagent-Driven (recommended)** — 每个 Task 由独立 subagent 执行,Task 间有检查点 **2. Inline Execution** — 在当前 session 中顺序执行所有 Task,带检查点 请选择执行方式,我随后通过 `executing-plans` 或 `subagent-driven-development` 技能执行计划。