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

docs: 添加登录前浏览体验实现计划(7个任务)

User 5 месяцев назад
Родитель
Сommit
56d8ff4901
1 измененных файлов с 826 добавлено и 0 удалено
  1. 826 0
      docs/superpowers/plans/2026-06-04-pre-login-discovery.md

+ 826 - 0
docs/superpowers/plans/2026-06-04-pre-login-discovery.md

@@ -0,0 +1,826 @@
+# 登录前浏览体验 Implementation Plan
+
+> **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:** 未登录用户打开小程序即可浏览发现页(五行沙盘+活动+商品)和商城,无需看到登录页
+
+**Architecture:** 未登录时隐藏原生 TabBar,使用自定义底部导航(发现/商城);发现页为静态 HTML 内容(零后端依赖)。登录后恢复原生 4Tab。
+
+**Tech Stack:** uni-app Vue 2 (微信小程序)
+
+---
+
+### Task 1: 品牌更名 溪艾福→汐艾福
+
+**Files:**
+- Modify: `zxyj-frontend/pages/index/parent-index.vue` (4处)
+- Modify: `zxyj-frontend/pages/index/child-index.vue` (2处)
+- Modify: `zxyj-frontend/pages.json` (1处)
+- Modify: `zxyj-frontend/pages/profile/profile.vue` (1处)
+- Modify: `zxyj-frontend/utils/api.js` (1处)
+- Modify: `zxyj-backend/src/main/java/com/zxyj/config/DatabaseInitializer.java` (4处)
+
+- [ ] **Step 1: 替换所有前端「溪艾福」→「汐艾福」**
+
+Replace in parent-index.vue:
+- Line 68: 五行能量沙盘(溪艾福)→ 五行能量沙盘(汐艾福)
+- Line 125: 会员Banner(溪福俱乐部)→ 会员Banner(汐福俱乐部)
+- Line 1213: 溪福俱乐部 → 汐福俱乐部
+- Line 1249: 五行能量沙盘(溪艾福)→ 五行能量沙盘(汐艾福)
+
+Replace in child-index.vue:
+- Line 54: 五行能量沙盘(溪艾福)→ 五行能量沙盘(汐艾福)
+- Line 881: 五行能量沙盘(溪艾福)→ 五行能量沙盘(汐艾福)
+
+Replace in pages.json:
+- Line 329: 溪福俱乐部 → 汐福俱乐部
+
+Replace in profile.vue:
+- Line 52: 溪福俱乐部 → 汐福俱乐部
+
+Replace in api.js:
+- Line 722: 溪福俱乐部 → 汐福俱乐部
+
+Replace in DatabaseInitializer.java (comments only):
+- "溪福俱乐部" → "汐福俱乐部" (4 occurrences)
+
+- [ ] **Step 2: Commit**
+
+```bash
+git add $(rtk list -u --modified -- zxyj-frontend/ zxyj-backend/)
+git commit -m "chore: 品牌名溪艾福/溪福俱乐部统一改为汐艾福/汐福俱乐部"
+```
+
+---
+
+### Task 2: 提取五行沙盘为独立组件
+
+**Files:**
+- Create: `zxyj-frontend/components/wuxing-sandbox.vue`
+- Modify: `zxyj-frontend/pages/index/parent-index.vue`
+- Modify: `zxyj-frontend/pages/index/child-index.vue`
+
+- [ ] **Step 1: 创建 wuxing-sandbox.vue 组件**
+
+```vue
+<template>
+  <view class="wuxing-sandbox">
+    <view class="sandbox-header">
+      <text class="sandbox-title">五行能量沙盘</text>
+      <text class="sandbox-badge">{{ badgeText }}</text>
+    </view>
+    <view class="sandbox-star">
+      <view class="star-point star-point-top" @click="onPointClick('mind')">
+        <text class="point-icon">🔥</text>
+        <text class="point-label">心·火</text>
+        <text class="point-status" v-if="mode === 'preview'">点此登录</text>
+      </view>
+      <view class="star-point star-point-left" @click="onPointClick('action')">
+        <text class="point-icon">🌿</text>
+        <text class="point-label">行·木</text>
+        <text class="point-status" v-if="mode === 'preview'">点此登录</text>
+      </view>
+      <view class="star-point star-point-right" @click="onPointClick('wealth')">
+        <text class="point-icon">💧</text>
+        <text class="point-label">富·水</text>
+        <text class="point-status" v-if="mode === 'preview'">点此登录</text>
+      </view>
+      <view class="star-point star-point-bl" @click="onPointClick('wisdom')">
+        <text class="point-icon">⚔️</text>
+        <text class="point-label">智·金</text>
+        <text class="point-status" v-if="mode === 'preview'">点此登录</text>
+      </view>
+      <view class="star-point star-point-br" @click="onPointClick('body')">
+        <text class="point-icon">🌏</text>
+        <text class="point-label">身·土</text>
+        <text class="point-status" v-if="mode === 'preview'">点此登录</text>
+      </view>
+      <view class="star-center">
+        <text class="center-text">五行平衡</text>
+        <text class="center-sub">共同成长</text>
+      </view>
+    </view>
+    <view class="shenke-hint" v-if="mode === 'preview'">
+      <text class="hint-text">🌱 完成测评,点亮「身·土」</text>
+    </view>
+    <view class="dimension-bar" v-if="mode !== 'preview'">
+      <view class="dim-item inactive">🌿行</view>
+      <text class="dim-arrow">→</text>
+      <view class="dim-item inactive">🔥心</view>
+      <text class="dim-arrow">→</text>
+      <view class="dim-item inactive">🌏身</view>
+      <text class="dim-arrow">→</text>
+      <view class="dim-item inactive">⚔️智</view>
+      <text class="dim-arrow">→</text>
+      <view class="dim-item inactive">💧富</view>
+    </view>
+  </view>
+</template>
+
+<script>
+export default {
+  props: {
+    mode: { type: String, default: 'preview' }, // preview | parent | child
+    badgeText: { type: String, default: '综合成长力 0%' }
+  },
+  methods: {
+    onPointClick(domain) {
+      this.$emit('point-click', domain)
+    }
+  }
+}
+</script>
+
+<style scoped>
+.wuxing-sandbox {
+  margin: 20rpx 30rpx;
+  padding: 30rpx;
+  background: linear-gradient(135deg, #1a1a2e, #16213e);
+  border-radius: 24rpx;
+  position: relative;
+  overflow: hidden;
+}
+.sandbox-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 30rpx;
+}
+.sandbox-title {
+  font-size: 28rpx;
+  font-weight: bold;
+  color: #FFD700;
+}
+.sandbox-badge {
+  font-size: 22rpx;
+  color: #aaa;
+  background: rgba(255,255,255,0.1);
+  padding: 6rpx 16rpx;
+  border-radius: 20rpx;
+}
+.sandbox-star {
+  position: relative;
+  height: 400rpx;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+.star-point {
+  position: absolute;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  width: 120rpx;
+}
+.star-point-top { top: 0; left: 50%; transform: translateX(-50%); }
+.star-point-left { top: 120rpx; left: 20rpx; }
+.star-point-right { top: 120rpx; right: 20rpx; }
+.star-point-bl { bottom: 40rpx; left: 60rpx; }
+.star-point-br { bottom: 40rpx; right: 60rpx; }
+.point-icon { font-size: 50rpx; margin-bottom: 6rpx; }
+.point-label { font-size: 22rpx; color: #fff; font-weight: bold; }
+.point-status { font-size: 18rpx; color: #FF6B6B; margin-top: 4rpx; }
+.star-center {
+  width: 120rpx; height: 120rpx;
+  background: radial-gradient(circle, rgba(255,215,0,0.2), rgba(255,215,0,0.05));
+  border: 2rpx solid rgba(255,215,0,0.3);
+  border-radius: 50%;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+}
+.center-text { font-size: 22rpx; color: #FFD700; font-weight: bold; }
+.center-sub { font-size: 18rpx; color: #aaa; }
+.shenke-hint { text-align: center; margin-top: 10rpx; }
+.hint-text { font-size: 22rpx; color: rgba(255,215,0,0.7); }
+.dimension-bar {
+  display: flex; align-items: center; justify-content: center;
+  margin-top: 20rpx; gap: 8rpx;
+}
+.dim-item {
+  font-size: 22rpx; color: #FFD700;
+  background: rgba(255,215,0,0.1);
+  padding: 6rpx 14rpx; border-radius: 12rpx;
+}
+.dim-arrow { font-size: 20rpx; color: rgba(255,215,0,0.3); }
+</style>
+```
+
+- [ ] **Step 2: 替换 parent-index.vue 内联沙盘为组件**
+
+Remove the inline wuxing-sandbox HTML+CSS block from parent-index.vue (the entire block from `<!-- 五行能量沙盘(汐艾福) -->` to its closing `</view>`), replace with:
+
+```vue
+      <wuxing-sandbox mode="parent" badgeText="综合成长力 0%" @point-click="goToDomain" />
+```
+
+Add import:
+```javascript
+import WuxingSandbox from '../../components/wuxing-sandbox.vue'
+```
+
+Add to components:
+```javascript
+components: { WuxingSandbox }
+```
+
+- [ ] **Step 3: 替换 child-index.vue 内联沙盘为组件**
+
+Same pattern as Step 2 — replace inline sandbox with `<wuxing-sandbox mode="child" badgeText="综合 0%" @point-click="goToDomain" />`
+
+Add import + components registration.
+
+Remove the entire inline `<style>` block for `.wuxing-sandbox` CSS from both parent-index.vue and child-index.vue (since it now lives in the component).
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add zxyj-frontend/components/wuxing-sandbox.vue zxyj-frontend/pages/index/parent-index.vue zxyj-frontend/pages/index/child-index.vue
+git commit -m "refactor: 提取五行沙盘为独立组件(wuxing-sandbox.vue)支持三种模式"
+```
+
+---
+
+### Task 3: 创建自定义底部导航组件
+
+**Files:**
+- Create: `zxyj-frontend/components/bottom-nav.vue`
+
+- [ ] **Step 1: 创建 bottom-nav.vue 组件**
+
+```vue
+<template>
+  <view class="bottom-nav">
+    <view
+      class="nav-item"
+      :class="{ active: current === 'discover' }"
+      @click="switchTab('discover')"
+    >
+      <text class="nav-icon">🔍</text>
+      <text class="nav-text">发现</text>
+    </view>
+    <view
+      class="nav-item"
+      :class="{ active: current === 'shop' }"
+      @click="switchTab('shop')"
+    >
+      <text class="nav-icon">🛒</text>
+      <text class="nav-text">商城</text>
+    </view>
+  </view>
+</template>
+
+<script>
+export default {
+  props: {
+    current: { type: String, default: 'discover' }
+  },
+  methods: {
+    switchTab(tab) {
+      if (tab === this.current) return
+      if (tab === 'discover') {
+        uni.redirectTo({ url: '/pages/discover/index' })
+      } else if (tab === 'shop') {
+        uni.redirectTo({ url: '/pages/shop/index' })
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+.bottom-nav {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  height: 100rpx;
+  background: #fff;
+  border-top: 1rpx solid #eee;
+  display: flex;
+  align-items: center;
+  justify-content: space-around;
+  padding-bottom: env(safe-area-inset-bottom);
+  z-index: 100;
+}
+.nav-item {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  flex: 1;
+  height: 100%;
+}
+.nav-item.active .nav-text { color: #FF6B6B; }
+.nav-icon { font-size: 40rpx; }
+.nav-text { font-size: 20rpx; color: #999; margin-top: 4rpx; }
+.nav-item.active .nav-text { font-weight: bold; }
+</style>
+```
+
+- [ ] **Step 2: Commit**
+
+```bash
+git add zxyj-frontend/components/bottom-nav.vue
+git commit -m "feat: 创建自定义底部导航组件(bottom-nav)用于未登录态2Tab展示"
+```
+
+---
+
+### Task 4: 重构 App.vue 登录态 + TabBar 切换逻辑
+
+**Files:**
+- Modify: `zxyj-frontend/App.vue`
+
+- [ ] **Step 1: 修改 App.vue onLaunch**
+
+Add login state check: if no token → hide native TabBar, redirect to discover. If has token → show native TabBar, proceed normally.
+
+```vue
+<script>
+export default {
+  onLaunch: function() {
+    console.log('App Launch')
+    const token = uni.getStorageSync('token')
+    if (token) {
+      this.$store.commit('setToken', token)
+      // 已登录:确保原生 TabBar 显示
+      uni.showTabBar()
+    } else {
+      // 未登录:隐藏原生 TabBar,跳转到发现页
+      uni.hideTabBar()
+      // 如果当前不在发现页或商城页,则跳转
+      const pages = getCurrentPages()
+      const currentRoute = pages.length > 0 ? pages[pages.length - 1].route : ''
+      if (currentRoute !== 'pages/discover/index' && currentRoute !== 'pages/shop/index') {
+        uni.reLaunch({ url: '/pages/discover/index' })
+      }
+    }
+  },
+  onShow: function() {
+    // 每次显示时检查登录状态,同步 TabBar
+    const token = uni.getStorageSync('token')
+    if (token) {
+      uni.showTabBar()
+    } else {
+      uni.hideTabBar()
+    }
+  },
+  // ... rest of existing methods (tryAutoLogin, saveLoginInfo, request)
+}
+</script>
+```
+
+- [ ] **Step 2: 确保 login.vue 登录成功后隐藏原生 TabBar + 跳转发现页**
+
+The existing login success handler does `uni.reLaunch({ url: '/pages/index/index' })`. This should be changed to NOT re-show the native TabBar for unauthenticated users — but since login succeeded, they ARE authenticated, so showing the TabBar is correct.
+
+Actually, the flow should be:
+1. User logs in successfully
+2. `uni.showTabBar()` → restore native TabBar
+3. `uni.reLaunch({ url: '/pages/index/index' })` → go to home page (which now has TabBar)
+
+This should already work with the App.vue changes. No need to modify login.vue TabBar logic — just ensure the navigateToHome function works.
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add zxyj-frontend/App.vue
+git commit -m "feat: App.vue 添加登录态TabBar切换逻辑(未登录隐藏原生TabBar)"
+```
+
+---
+
+### Task 5: 重构发现页(核心页面)
+
+**Files:**
+- Modify: `zxyj-frontend/pages/discover/index.vue`
+
+- [ ] **Step 1: 完整重写 discover/index.vue**
+
+```vue
+<template>
+  <view class="discover-page">
+    <!-- Banner -->
+    <view class="banner">
+      <view class="banner-content">
+        <text class="banner-brand">🌟 汐艾福</text>
+        <text class="banner-title">Care Family Club</text>
+        <text class="banner-subtitle">以五行五维为内核的家庭主动健康管理平台</text>
+        <view class="banner-tags">
+          <text class="tag">🏆 任务积分</text>
+          <text class="tag">⭐ 五行沙盘</text>
+          <text class="tag">👨‍👩‍👧 家庭共育</text>
+        </view>
+      </view>
+    </view>
+
+    <!-- 五行能量沙盘 -->
+    <wuxing-sandbox mode="preview" @point-click="requireLogin" />
+
+    <!-- 精选活动 -->
+    <view class="section">
+      <view class="section-header">
+        <text class="section-title">🎯 精选活动</text>
+      </view>
+      <scroll-view class="activity-scroll" scroll-x>
+        <view
+          class="activity-card"
+          v-for="(item, idx) in activities"
+          :key="idx"
+          @click="requireLogin('activity')"
+        >
+          <view class="activity-icon">{{ item.icon }}</view>
+          <text class="activity-name">{{ item.name }}</text>
+          <text class="activity-desc">{{ item.desc }}</text>
+          <text class="activity-price">{{ item.price }}</text>
+          <view class="activity-btn">查看详情 →</view>
+        </view>
+      </scroll-view>
+    </view>
+
+    <!-- 推荐商品 -->
+    <view class="section">
+      <view class="section-header">
+        <text class="section-title">🛍️ 推荐商品</text>
+      </view>
+      <view
+        class="product-card"
+        v-for="(item, idx) in products"
+        :key="idx"
+        @click="requireLogin('product')"
+      >
+        <view class="product-icon">{{ item.icon }}</view>
+        <view class="product-info">
+          <text class="product-name">{{ item.name }}</text>
+          <view class="product-meta">
+            <text class="product-rating">{{ item.rating }}</text>
+            <text class="product-sold">{{ item.sold }}</text>
+          </view>
+          <text class="product-desc">{{ item.desc }}</text>
+          <text class="product-price">{{ item.price }}</text>
+        </view>
+      </view>
+    </view>
+
+    <!-- 登录引导 -->
+    <view class="login-cta" @click="requireLogin('cta')">
+      <text class="cta-title">🔒 登录解锁完整功能</text>
+      <text class="cta-sub">创建家庭 · 管理任务 · 查看五行能量沙盘</text>
+      <view class="cta-btn">立即登录 →</view>
+    </view>
+
+    <!-- 自定义底部导航 -->
+    <bottom-nav current="discover" />
+
+    <!-- 底部占位 -->
+    <view style="height: 120rpx;"></view>
+  </view>
+</template>
+
+<script>
+import WuxingSandbox from '../../components/wuxing-sandbox.vue'
+import BottomNav from '../../components/bottom-nav.vue'
+
+export default {
+  components: { WuxingSandbox, BottomNav },
+  data() {
+    return {
+      activities: [
+        { icon: '🧘', name: '亲子瑜伽体验课', desc: '适合5-12岁儿童与家长', price: '¥199', tag: '🔥 心·火' },
+        { icon: '💬', name: '情绪管理工作坊', desc: '帮助孩子识别和管理情绪', price: '免费', tag: '🔥 心·火' },
+        { icon: '🦠', name: '肠道菌群调养讲座', desc: '从肠道健康到大脑发育', price: '¥299', tag: '🌏 身·土' }
+      ],
+      products: [
+        { icon: '🧘', name: '亲子瑜伽体验课', desc: '适合5-12岁儿童与家长共同参与的瑜伽课程', rating: '★★★★☆', sold: '已售230份', price: '¥199' },
+        { icon: '🎮', name: '舒尔特方格专注力训练', desc: '经典注意力训练游戏,每日5分钟', rating: '★★★★★', sold: '已售568份', price: '¥49' }
+      ]
+    }
+  },
+  methods: {
+    requireLogin(action) {
+      const token = uni.getStorageSync('token')
+      if (token) return
+      uni.navigateTo({ url: '/pages/login/login?redirect=' + encodeURIComponent('/pages/discover/index') })
+    }
+  }
+}
+</script>
+
+<style scoped>
+.discover-page {
+  min-height: 100vh;
+  background: #f8f8f8;
+  padding-bottom: 20rpx;
+}
+
+/* Banner */
+.banner {
+  margin: 20rpx 30rpx;
+  padding: 40rpx 30rpx;
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  border-radius: 24rpx;
+}
+.banner-content { display: flex; flex-direction: column; }
+.banner-brand { font-size: 36rpx; font-weight: bold; color: #fff; }
+.banner-title { font-size: 26rpx; color: rgba(255,255,255,0.8); margin-top: 4rpx; }
+.banner-subtitle { font-size: 24rpx; color: rgba(255,255,255,0.7); margin-top: 12rpx; line-height: 1.5; }
+.banner-tags { display: flex; gap: 12rpx; margin-top: 20rpx; flex-wrap: wrap; }
+.tag { background: rgba(255,255,255,0.2); padding: 6rpx 16rpx; border-radius: 20rpx; font-size: 22rpx; color: #fff; }
+
+/* Sections */
+.section { margin: 20rpx 30rpx; }
+.section-header { margin-bottom: 16rpx; }
+.section-title { font-size: 28rpx; font-weight: bold; color: #333; }
+
+/* Activity scroll */
+.activity-scroll { display: flex; flex-direction: row; white-space: nowrap; overflow-x: auto; }
+.activity-card {
+  display: inline-flex;
+  flex-direction: column;
+  width: 240rpx;
+  background: #fff;
+  border-radius: 16rpx;
+  padding: 24rpx 20rpx;
+  margin-right: 20rpx;
+  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
+}
+.activity-icon { font-size: 60rpx; margin-bottom: 12rpx; }
+.activity-name { font-size: 26rpx; font-weight: bold; color: #333; white-space: normal; }
+.activity-desc { font-size: 22rpx; color: #999; margin-top: 8rpx; white-space: normal; }
+.activity-price { font-size: 28rpx; color: #FF6B6B; font-weight: bold; margin-top: 12rpx; }
+.activity-btn {
+  margin-top: 12rpx;
+  padding: 8rpx 0;
+  background: #667eea;
+  color: #fff;
+  text-align: center;
+  border-radius: 16rpx;
+  font-size: 22rpx;
+}
+
+/* Product cards */
+.product-card {
+  display: flex;
+  background: #fff;
+  border-radius: 16rpx;
+  padding: 24rpx;
+  margin-bottom: 16rpx;
+  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
+}
+.product-icon { font-size: 72rpx; margin-right: 20rpx; }
+.product-info { flex: 1; }
+.product-name { font-size: 28rpx; font-weight: bold; color: #333; display: block; }
+.product-meta { display: flex; gap: 16rpx; margin-top: 6rpx; }
+.product-rating { font-size: 22rpx; color: #FFA500; }
+.product-sold { font-size: 22rpx; color: #999; }
+.product-desc { font-size: 22rpx; color: #888; margin-top: 8rpx; display: block; }
+.product-price { font-size: 32rpx; color: #FF6B6B; font-weight: bold; margin-top: 8rpx; display: block; }
+
+/* Login CTA */
+.login-cta {
+  margin: 20rpx 30rpx;
+  padding: 32rpx;
+  background: linear-gradient(135deg, #667eea, #764ba2);
+  border-radius: 20rpx;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+.cta-title { font-size: 28rpx; font-weight: bold; color: #fff; }
+.cta-sub { font-size: 22rpx; color: rgba(255,255,255,0.8); margin-top: 8rpx; }
+.cta-btn {
+  margin-top: 16rpx;
+  padding: 12rpx 48rpx;
+  background: #fff;
+  color: #667eea;
+  border-radius: 30rpx;
+  font-size: 26rpx;
+  font-weight: bold;
+}
+</style>
+```
+
+- [ ] **Step 2: Commit**
+
+```bash
+git add zxyj-frontend/pages/discover/index.vue
+git commit -m "feat: 重构发现页为预登录首页(沙盘+活动+商品+登录引导)"
+```
+
+---
+
+### Task 6: 商城页添加未登录态预览
+
+**Files:**
+- Modify: `zxyj-frontend/pages/shop/index.vue`
+
+- [ ] **Step 1: 重写 shop/index.vue**
+
+Replace placeholder with a preview layout showing product categories + product cards, plus bottom nav and login gate.
+
+```vue
+<template>
+  <view class="shop-page">
+    <!-- 分类标签 -->
+    <view class="category-bar">
+      <view class="category-item active">全部</view>
+      <view class="category-item">课程</view>
+      <view class="category-item">活动</view>
+      <view class="category-item">商品</view>
+    </view>
+
+    <!-- 商品列表(静态预览) -->
+    <view class="product-grid">
+      <view class="product-card" v-for="(item, idx) in products" :key="idx" @click="requireLogin">
+        <view class="product-img">{{ item.icon }}</view>
+        <view class="product-body">
+          <text class="product-name">{{ item.name }}</text>
+          <text class="product-price">{{ item.price }}</text>
+        </view>
+      </view>
+    </view>
+
+    <!-- 登录引导 -->
+    <view class="login-hint">
+      <text class="hint-text">🔒 登录查看全部商品和服务</text>
+    </view>
+
+    <!-- 自定义底部导航 -->
+    <bottom-nav current="shop" />
+
+    <!-- 底部占位 -->
+    <view style="height: 120rpx;"></view>
+  </view>
+</template>
+
+<script>
+import BottomNav from '../../components/bottom-nav.vue'
+
+export default {
+  components: { BottomNav },
+  data() {
+    return {
+      products: [
+        { icon: '🧘', name: '亲子瑜伽体验课', price: '¥199' },
+        { icon: '🎮', name: '舒尔特方格专注力训练', price: '¥49' },
+        { icon: '📖', name: '儿童情绪管理绘本套装', price: '¥89' },
+        { icon: '🧩', name: '家庭专注力训练礼盒', price: '¥159' }
+      ]
+    }
+  },
+  methods: {
+    requireLogin() {
+      const token = uni.getStorageSync('token')
+      if (token) return
+      uni.navigateTo({ url: '/pages/login/login?redirect=' + encodeURIComponent('/pages/shop/index') })
+    }
+  }
+}
+</script>
+
+<style scoped>
+.shop-page {
+  min-height: 100vh;
+  background: #f8f8f8;
+  padding-bottom: 20rpx;
+}
+.category-bar {
+  display: flex;
+  gap: 16rpx;
+  padding: 20rpx 30rpx;
+  background: #fff;
+  border-bottom: 1rpx solid #f0f0f0;
+}
+.category-item {
+  padding: 8rpx 24rpx;
+  border-radius: 20rpx;
+  font-size: 24rpx;
+  color: #666;
+  background: #f5f5f5;
+}
+.category-item.active {
+  background: #667eea;
+  color: #fff;
+}
+.product-grid {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 16rpx;
+  padding: 20rpx 30rpx;
+}
+.product-card {
+  background: #fff;
+  border-radius: 16rpx;
+  overflow: hidden;
+  box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06);
+}
+.product-img {
+  height: 180rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 80rpx;
+  background: #f9f9f9;
+}
+.product-body { padding: 16rpx; }
+.product-name { font-size: 24rpx; font-weight: bold; color: #333; display: block; }
+.product-price { font-size: 28rpx; color: #FF6B6B; font-weight: bold; margin-top: 8rpx; display: block; }
+.login-hint {
+  text-align: center;
+  padding: 24rpx;
+  font-size: 24rpx;
+  color: #999;
+}
+</style>
+```
+
+- [ ] **Step 2: Commit**
+
+```bash
+git add zxyj-frontend/pages/shop/index.vue
+git commit -m "feat: 商城页添加未登录预览态(静态商品展示+登录引导)"
+```
+
+---
+
+### Task 7: Login 页添加 redirect 参数支持
+
+**Files:**
+- Modify: `zxyj-frontend/pages/login/login.vue`
+
+- [ ] **Step 1: 修改 login.vue onLoad 接受 redirect 参数**
+
+```vue
+  onLoad(options) {
+    // 处理邀请码
+    if (options.inviteCode) {
+      uni.setStorageSync('inviteCode', options.inviteCode)
+      uni.setStorageSync('inviteType', options.inviteType || 'family')
+    }
+    // 存储登录后跳转地址
+    if (options.redirect) {
+      this.redirectUrl = decodeURIComponent(options.redirect)
+    }
+  },
+```
+
+Add to data:
+```javascript
+  data() {
+    return {
+      // ... existing data
+      redirectUrl: ''
+    }
+  },
+```
+
+- [ ] **Step 2: 修改 navigateToHome 使用 redirect 参数**
+
+Replace the existing navigateToHome method:
+
+```javascript
+    navigateToHome(role) {
+      if (this.redirectUrl) {
+        // 有 redirect 则跳回来源页
+        const redirect = this.redirectUrl
+        this.redirectUrl = ''
+        uni.reLaunch({ url: redirect })
+      } else {
+        // 默认通过 TabBar 首页跳转
+        uni.reLaunch({ url: '/pages/index/index' })
+      }
+    }
+```
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add zxyj-frontend/pages/login/login.vue
+git commit -m "feat: 登录页支持redirect参数(登录后返回来源页)"
+```
+
+---
+
+### Self-Review
+
+**1. Spec coverage:**
+- 品牌更名: ✅ Task 1 覆盖所有 9 处前端 + 4 处后端注释
+- 发现页重构 (沙盘+活动+商品): ✅ Task 5
+- 五行沙盘交互点(五角→登录): ✅ Task 2 (emits point-click), Task 5 (requireLogin handler)
+- 活动区: ✅ Task 5 (3张活动卡片,点击→requireLogin)
+- 商品区: ✅ Task 5 (2张商品卡片,点击→requireLogin)
+- 商城页未登录预览: ✅ Task 6
+- 预登录 TabBar 仅显示发现+商城: ✅ Task 3 (bottom-nav) + Task 4 (App.vue hideTabBar)
+- 登录页不改造: ✅ Task 7 只加 redirect 参数,不改造 UI
+- 登录后恢复原生 TabBar: ✅ Task 4
+- 退出登录回到未登录态: ✅ implicit via App.vue onShow check
+- 沙盘组件复用: ✅ Task 2 (preview/parent/child 三模式)
+
+**2. Placeholder scan:** No TBD, TODO, or empty sections.
+
+**3. Type consistency:** 
+- `@point-click` used in component emit and parent handler ✅
+- `requireLogin()` used consistently across discover and shop pages ✅
+- `redirect` parameter format consistent ✅