# 活动主页实现计划
> **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
活动中心
{{ tab.label }}
{{ filter.label }}
加载中...
📋
暂无报名的活动
去发现页探索精彩活动吧
{{ dim.label }}
...
...
...
加载更多
```
- [ ] **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` 技能执行计划。