Просмотр исходного кода

docs: add activity home page design and implementation plan

Sisyphus 4 месяцев назад
Родитель
Сommit
2231f09d4d

+ 712 - 0
cfc-frontend/docs/superpowers/plans/2026-07-02-activity-home-page-implementation-plan.md

@@ -0,0 +1,712 @@
+# 活动主页实现计划
+
+> **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
+<template>
+  <view class="activity-container">
+    <!-- 导航栏 -->
+    <view class="nav-bar">
+      <text class="nav-title">活动中心</text>
+    </view>
+
+    <!-- Tab 栏 -->
+    <view class="tab-bar">
+      <view
+        v-for="tab in tabs"
+        :key="tab.key"
+        :class="['tab-item', currentTab === tab.key ? 'active' : '']"
+        @click="onTabChange(tab.key)"
+      >
+        {{ tab.label }}
+      </view>
+    </view>
+
+    <!-- 内容区 -->
+    <scroll-view scroll-y class="content" @scrolltolower="onLoadMore">
+
+      <!-- 我的活动 Tab -->
+      <view v-if="currentTab === 'mine'" class="tab-content">
+        <!-- 状态筛选 -->
+        <scroll-view scroll-x enable-flex show-scrollbar="false" class="filter-scroll">
+          <view
+            v-for="filter in mineFilters"
+            :key="filter.value"
+            :class="['filter-chip', currentMineFilter === filter.value ? 'active' : '']"
+            @click="onMineFilterChange(filter.value)"
+          >
+            {{ filter.label }}
+          </view>
+        </scroll-view>
+
+        <!-- 加载中 -->
+        <view v-if="mineLoading" class="loading-state">
+          <text class="loading-text">加载中...</text>
+        </view>
+
+        <!-- 空状态 -->
+        <view v-else-if="myActivities.length === 0" class="empty-state">
+          <text class="empty-icon">📋</text>
+          <text class="empty-title">暂无报名的活动</text>
+          <text class="empty-desc">去发现页探索精彩活动吧</text>
+          <button class="empty-btn" @click="switchToDiscover">去发现活动</button>
+        </view>
+
+        <!-- 活动列表 -->
+        <view v-else class="activity-list">
+          <view
+            v-for="act in myActivities"
+            :key="act.id"
+            class="activity-card"
+            @click="goDetail(act)"
+          >
+            <!-- 卡片内容,见 Task 3 -->
+          </view>
+        </view>
+      </view>
+
+      <!-- 发现活动 Tab -->
+      <view v-if="currentTab === 'discover'" class="tab-content">
+        <!-- 维度筛选 -->
+        <scroll-view scroll-x enable-flex show-scrollbar="false" class="filter-scroll">
+          <view
+            v-for="dim in dimensionFilters"
+            :key="dim.value"
+            :class="['filter-chip', currentDimension === dim.value ? 'active' : '']"
+            @click="onDimensionChange(dim.value)"
+          >
+            {{ dim.label }}
+          </view>
+        </scroll-view>
+
+        <!-- 加载中/空状态/列表 -->
+        <view v-if="discoverLoading" class="loading-state">...</view>
+        <view v-else-if="discoverActivities.length === 0" class="empty-state">...</view>
+        <view v-else class="activity-list">...</view>
+      </view>
+
+      <!-- 历史记录 Tab -->
+      <view v-if="currentTab === 'history'" class="tab-content">
+        <!-- 历史筛选 + 列表 -->
+        <!-- 结构同我的活动 Tab -->
+      </view>
+
+      <!-- 上拉加载 -->
+      <view v-if="hasMore && !loading" class="load-more" @click="onLoadMore">
+        <text>加载更多</text>
+      </view>
+
+    </scroll-view>
+  </view>
+</template>
+```
+
+- [ ] **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
+<view class="activity-card" @click="goDetail(act)">
+  <image
+    class="card-cover"
+    :src="act.coverImage || '/static/default-activity.png'"
+    mode="aspectFill"
+  />
+  <view class="card-info">
+    <text class="card-title">{{ act.title }}</text>
+    <text class="card-meta">{{ act.startTime }}</text>
+    <text class="card-meta" v-if="act.location">📍 {{ act.location }}</text>
+    <view class="card-bottom">
+      <text class="card-status" :class="'status-' + getStatusClass(act)">
+        {{ getStatusText(act) }}
+      </text>
+      <text class="card-price" v-if="act.price > 0">{{ formatPrice(act.price) }}</text>
+      <text class="card-price free" v-else>免费</text>
+    </view>
+  </view>
+</view>
+```
+
+- [ ] **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
+<!-- 我的活动 Tab 的卡片有操作按钮 -->
+<view v-if="currentTab === 'mine' && act.status === 'approved'" class="card-actions">
+  <button class="btn-cancel" @click.stop="onCancel(act)">取消报名</button>
+  <button class="btn-checkin" @click.stop="onCheckin(act)">签到</button>
+</view>
+```
+
+- [ ] **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` 技能执行计划。

+ 241 - 0
cfc-frontend/docs/superpowers/specs/2026-07-02-activity-home-page-design.md

@@ -0,0 +1,241 @@
+# 活动主页设计文档
+
+**日期**: 2026-07-02
+**状态**: 进行中
+**负责人**: Sisyphus
+
+---
+
+## 1. 背景与目标
+
+### 背景
+
+目前小程序中各维度页面(身/心/智/行/富)的 `DimensionActivities` 组件点击"更多"按钮后统一跳转到 `discover/index?type=activity`,该页面以商品发现为主,活动仅为筛选类型之一,用户体验不专注。
+
+### 目标
+
+设计独立的**活动主页** `/pages/activity/index`,作为活动聚合入口,统一承接所有维度页"更多"来的流量,优先展示用户报名的活动。
+
+---
+
+## 2. 用户场景
+
+1. **家长/孩子** 从某个维度详情页点击"更多 ›"进入活动主页,查看自己报名的活动或发现新活动
+2. **服务商(规划师)** 进入活动主页,浏览/管理自己发布的活动
+3. **未登录用户** 进入活动主页,仅能浏览公开活动,不能报名/签到
+
+---
+
+## 3. 页面结构
+
+### 路由
+
+```
+/pages/activity/index   (新建,独立subPackage: pages/activity)
+```
+
+### Tab 结构(三 Tab 并列)
+
+| Tab | 内容 | 数据来源 |
+|-----|------|----------|
+| 我的活动 | 用户报名的活动(含状态筛选) | `getMyActivityRegistrations` |
+| 发现活动 | 所有公开活动(含维度筛选) | `getActivityList` |
+| 历史记录 | 已结束/已签到的活动 | `getMyActivityRegistrations(status=ended)` |
+
+**注意**: 未登录用户默认展示"发现活动"Tab,"我的活动"和"历史记录"显示登录引导。
+
+---
+
+## 4. 各 Tab 详细设计
+
+### 4.1 我的活动 Tab(默认激活)
+
+**状态筛选 Chips**(横向滚动):
+- 全部 / 🟢 待开始 / 🟡 待审核 / ✅ 已签到 / ❌ 已取消
+
+**活动卡片展示**:
+
+```
+┌────────────────────────────────────────┐
+│ [封面图]  │  活动标题                     │
+│ 72×72    │  🕐 时间 / 📍 地点              │
+│          │  [状态标签] [倒计时标签]          │
+│          ├────────────────────────────────┤
+│          │  [取消报名]        [签到]        │
+└────────────────────────────────────────┘
+```
+
+**操作按钮逻辑**:
+- `报名成功 + 未到时间` → 显示"签到"按钮
+- `报名成功 + 已到时间 + 未签到` → 显示"签到"按钮
+- `报名待审核` → 显示"取消报名"按钮(灰色)
+- `已签到` → 不显示操作按钮
+- `已结束` → 归入"历史记录"Tab
+
+**空状态**:
+```
+📋 暂无报名的活动
+去发现页探索精彩活动吧
+[去发现活动] 按钮
+```
+
+---
+
+### 4.2 发现活动 Tab
+
+**维度筛选 Chips**(横向滚动):
+- 全部 / 身 / 心 / 智 / 行 / 富
+
+**活动卡片展示**:
+
+```
+┌────────────────────────────────────────┐
+│ [封面图]  │  活动标题                     │
+│ 72×72    │  🕐 时间 / 📍 地点              │
+│          │  [状态标签]  价格               │
+└────────────────────────────────────────┘
+```
+
+**点击卡片** → 跳转 `/pages/discover/activity-detail/activity-detail?id=xxx`
+
+**空状态**:
+```
+🎯 暂无相关活动
+试试其他维度筛选
+```
+
+---
+
+### 4.3 历史记录 Tab
+
+**筛选 Chips**: 全部 / 已结束 / 已签到
+
+**卡片样式同"我的活动"**,但操作按钮均不显示。
+
+---
+
+## 5. API 集成
+
+### 现有 API(无需后端改动)
+
+| 前端函数 | 端点 | 参数 |
+|----------|------|------|
+| `getMyActivityRegistrations(data)` | `POST /api/activity/my-registrations` | `{ childId, page, size, status? }` |
+| `getActivityList(data)` | `POST /api/activity/list` | `{ dimensionCode?, page, size, status? }` |
+| `getActivityDetail(id)` | `POST /api/activity/detail` | `{ id }` |
+| `registerActivity(data)` | `POST /api/activity/register` | `{ id, childId }` |
+| `cancelActivityRegistration(data)` | `POST /api/activity/cancel-registration` | `{ id, childId }` |
+| `activityCheckin(data)` | `POST /api/activity/checkin` | `{ id, childId }` |
+
+### 状态映射
+
+| 后端 status | 前端标签 | 说明 |
+|-------------|----------|------|
+| `pending` | 🟡 待审核 | 报名待审核 |
+| `approved` | 🟢 报名成功 | 已通过审核 |
+| `rejected` | ❌ 已拒绝 | 报名被拒绝 |
+| `cancelled` | ⚪ 已取消 | 用户取消 |
+| `checked_in` | ✅ 已签到 | 签到完成 |
+
+---
+
+## 6. 入口整合
+
+### 需要更新的页面(共16个文件)
+
+所有使用 `DimensionActivities` 组件的页面,其 `goMoreActivities` 方法需改为:
+
+```javascript
+goMoreActivities: function() {
+  uni.navigateTo({ url: '/pages/activity/index' })
+}
+```
+
+**受影响文件**:
+- `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/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`
+- `pages/wisdom/wisdom_temp/index.vue`
+
+---
+
+## 7. 文件清单
+
+### 新增文件
+
+| 文件 | 说明 |
+|------|------|
+| `cfc-frontend/pages/activity/index.vue` | 活动主页主页面 |
+| `cfc-frontend/pages/activity/activity-detail/activity-detail.vue` | 活动详情页(可复用现有,但需迁移路由注册) |
+
+### 需修改文件
+
+| 文件 | 修改内容 |
+|------|----------|
+| `cfc-frontend/pages.json` | 注册 `pages/activity` subPackage |
+| 16个使用 DimensionActivities 的页面 | 更新 `goMoreActivities` 跳转目标 |
+
+### 可复用的现有组件/样式
+
+- `DimensionActivities.vue` 卡片样式参考(但不完全复用,因为是新页面自己管理数据)
+- `pages/discover/activity-detail/activity-detail.vue` 详情页逻辑参考
+- `uni.scss` 全局样式变量
+
+---
+
+## 8. 路由注册(pages.json)
+
+```json
+{
+  "root": "pages/activity",
+  "pages": [
+    {
+      "path": "index",
+      "style": { "navigationBarTitleText": "活动中心" }
+    },
+    {
+      "path": "activity-detail/activity-detail",
+      "style": { "navigationBarTitleText": "活动详情" }
+    }
+  ]
+}
+```
+
+**注意**: `pages/discover/activity-detail/activity-detail.vue` 仍保留(discover-detail subPackage),新路由为独立 activity subPackage。
+
+---
+
+## 9. 待确认事项(TBD)
+
+以下内容需用户确认后才能最终确定:
+
+1. **「历史记录」Tab** — 是否需要?如不需要可简化为两 Tab
+2. **服务商视角** — 是否需要在活动主页展示"我发布的活动"?(如需要,Tab 调整为:我参加的活动 / 我发布的活动 / 发现活动)
+3. **维度筛选 Chips** — 发现活动 Tab 的维度筛选是否需要按五维(身/心/智/行/富)?
+
+---
+
+## 10. 设计风格
+
+- **主色调**: `#F97316`(暖橙,同小程序主色)
+- **卡片风格**: 白色背景 + 轻阴影 + 圆角12px
+- **字体**: 系统默认
+- **图片**: 封面图 72×72 正方形,圆角8px
+- **状态标签色**:
+  - 待开始: `#FFF7ED` + `#F97316`
+  - 待审核: `#FEF3C7` + `#D97706`
+  - 已签到: `#F0FDF4` + `#22C55E`
+  - 已拒绝: `#FEE2E2` + `#EF4444`
+  - 已取消: `#F5F5F5` + `#999`