فهرست منبع

fix: 恢复行首页redirectTo;修复CircleRanking WXML编译错误;新增plans占位页

liaoxg 3 ماه پیش
والد
کامیت
652f670ee2

+ 7 - 1
cfc-frontend/components/CircleRanking.vue

@@ -5,7 +5,7 @@
     </view>
     <view class="cr-list">
       <view class="cr-item" v-for="(item, idx) in rankings" :key="idx">
-        <text class="cr-rank">{{ '🥇🥈🥉'[idx] || '#' + (idx + 1) }}</text>
+        <text class="cr-rank">{{ rankIcon(idx) }}</text>
         <view class="cr-info">
           <text class="cr-family">家庭 {{ idx + 1 }}</text>
           <text class="cr-members">{{ item.memberCount || 0 }} 位成员</text>
@@ -25,6 +25,12 @@ export default {
     circleName: { type: String, default: '' },
     rankings: { type: Array, default: function() { return [] } },
     loading: { type: Boolean, default: false }
+  },
+  methods: {
+    rankIcon: function(idx) {
+      var medals = ['🥇', '🥈', '🥉']
+      return medals[idx] || '#' + (idx + 1)
+    }
   }
 }
 </script>

+ 5 - 1
cfc-frontend/pages/home-pages/parent-index.vue

@@ -56,7 +56,7 @@
           </view>
           <view class="circle-ranking-preview">
             <view class="circle-rank-row" v-for="(item, idx) in circleRankings.slice(0, 3)" :key="idx">
-              <text class="circle-rank-badge">{{ '🥇🥈🥉'[idx] || '#' + (idx + 1) }}</text>
+              <text class="circle-rank-badge">{{ rankIcon(idx) }}</text>
               <text class="circle-rank-family">家庭 {{ idx + 1 }}</text>
               <text class="circle-rank-score">{{ item.totalScore || 0 }} 分</text>
             </view>
@@ -616,6 +616,10 @@ export default {
     })
   },
   methods: {
+    rankIcon: function(idx) {
+      var medals = ['🥇', '🥈', '🥉']
+      return medals[idx] || '#' + (idx + 1)
+    },
     async loadData() {
       this.loading = true
       try {

+ 12 - 17
cfc-frontend/pages/index-home/index.vue

@@ -47,8 +47,8 @@
   <!-- Tabbar 切换过渡页 -->
   <tab-transition v-if="showTabTransition" dimCode="action" />
 
-  <!-- 发现页/引流内容,登录后也保留 TabBar -->
-  <view class="discover-container">
+  <!-- 未登录态 — 发现页/引流内容,直接内嵌,不跳转 -->
+  <view v-if="!currentRole" class="discover-container">
     <!-- 品牌头部 -->
     <PageBanner theme="home"
       tagline="家庭健康成长俱乐部"
@@ -201,16 +201,17 @@
       </view>
     </view>
 
-    <!-- 底部按钮:未登录显示登录,已登录显示进入首页 -->
+    <!-- 底部登录按钮 -->
     <view class="login-footer">
-      <button class="login-btn" v-if="!isLoggedIn()" @click="handleLogin">登录 / 注册</button>
-      <button class="login-btn home-btn" v-else @click="goHomeDashboard">进入家庭首页</button>
-      <text class="login-footer-text" v-if="!isLoggedIn()">登录即表示同意《用户协议》和《隐私政策》</text>
+      <button class="login-btn" @click="handleLogin">登录 / 注册</button>
+      <text class="login-footer-text">登录即表示同意《用户协议》和《隐私政策》</text>
     </view>
 
     <!-- 底部占位 -->
     <view style="height: 120rpx;"></view>
   </view>
+
+  <!-- 登录态 — 已迁移到 home 分包,通过 redirectTo 跳转,不再静态渲染 -->
 </view>
 </template>
 
@@ -384,11 +385,11 @@ export default {
         uni.removeStorageSync('inviteType')
         return
       }
-      // 登录用户:留在 TabBar 页,显示推荐内容
-      // 不再 redirectTo 到 home 分包(分包页无 TabBar)
-      this.loadProducts()
-      this.loadDimensionActivities()
-      this.loadFeaturedArticles()
+      // 登录用户:跳转到 home 分包页面,不再静态渲染组件
+      var targetUrl = this.currentRole === 'child'
+        ? '/pages/home-pages/child-index'
+        : '/pages/home-pages/parent-index'
+      uni.redirectTo({ url: targetUrl })
     },
     isLoggedIn() {
       return !!uni.getStorageSync('token')
@@ -510,12 +511,6 @@ export default {
     handleLogin() {
       uni.navigateTo({ url: '/pages/login/login?redirect=' + encodeURIComponent('/pages/index-home/index') })
     },
-    goHomeDashboard() {
-      var targetUrl = this.currentRole === 'child'
-        ? '/pages/home-pages/child-index'
-        : '/pages/home-pages/parent-index'
-      uni.navigateTo({ url: targetUrl })
-    },
     async handleParentInvite(code) {
       // 延迟显示,等待登录完成
       setTimeout(async () => {

+ 118 - 0
cfc-frontend/pages/parent/plans/detail.vue

@@ -0,0 +1,118 @@
+<template>
+  <view class="container">
+    <!-- 加载中 -->
+    <view class="loading" v-if="loading">
+      <text>加载中...</text>
+    </view>
+
+    <!-- 计划详情 -->
+    <template v-else-if="plan">
+      <view class="header">
+        <text class="title">{{ plan.title }}</text>
+        <view class="status-badge" :class="'status-' + (plan.status || 0)">
+          <text>{{ statusText(plan.status) }}</text>
+        </view>
+      </view>
+
+      <view class="info-card" v-if="plan.description">
+        <text class="info-label">计划说明</text>
+        <text class="info-value">{{ plan.description }}</text>
+      </view>
+
+      <view class="info-card">
+        <text class="info-label">孩子</text>
+        <text class="info-value">{{ plan.childName || '--' }}</text>
+      </view>
+
+      <view class="info-card" v-if="plan.guideName">
+        <text class="info-label">规划师</text>
+        <text class="info-value">{{ plan.guideName }}</text>
+      </view>
+
+      <view class="info-card">
+        <text class="info-label">创建时间</text>
+        <text class="info-value">{{ plan.createTime || '--' }}</text>
+      </view>
+
+      <!-- 计划条目 -->
+      <view class="items-section" v-if="plan.items && plan.items.length > 0">
+        <text class="section-title">计划内容</text>
+        <view class="item-card" v-for="item in plan.items" :key="item.id">
+          <view class="item-top">
+            <text class="item-title">{{ item.title }}</text>
+            <text class="item-status" v-if="item.completed">✅ 已完成</text>
+          </view>
+          <text class="item-desc" v-if="item.description">{{ item.description }}</text>
+        </view>
+      </view>
+    </template>
+
+    <!-- 空状态 -->
+    <view class="empty" v-else>
+      <text class="empty-icon">📋</text>
+      <text class="empty-text">计划不存在</text>
+    </view>
+  </view>
+</template>
+
+<script>
+import { getGuidePlanDetail } from '../../../utils/api.js'
+
+export default {
+  data() {
+    return {
+      loading: false,
+      plan: null
+    }
+  },
+  onLoad: function(options) {
+    if (options.planId) {
+      this.planId = options.planId
+      this.loadDetail()
+    }
+  },
+  methods: {
+    loadDetail: function() {
+      if (!this.planId) return
+      this.loading = true
+      var self = this
+      getGuidePlanDetail({ planId: this.planId }).then(function(res) {
+        self.loading = false
+        if (res.code === 200 && res.data) {
+          self.plan = res.data
+        }
+      }).catch(function() {
+        self.loading = false
+      })
+    },
+    statusText: function(status) {
+      var map = { 0: '待审核', 1: '进行中', 2: '已完成', 3: '已取消' }
+      return map[status] || '未知'
+    }
+  }
+}
+</script>
+
+<style scoped>
+.container { min-height: 100vh; background: #f8f8f8; padding-bottom: 30rpx; }
+.loading, .empty { display: flex; flex-direction: column; align-items: center; padding: 100rpx 0; }
+.empty-icon { font-size: 80rpx; }
+.empty-text { font-size: 30rpx; color: #666; margin-top: 20rpx; }
+.header { background: #fff; padding: 40rpx 30rpx; display: flex; justify-content: space-between; align-items: center; }
+.title { font-size: 36rpx; font-weight: bold; color: #333; flex: 1; }
+.status-badge { font-size: 24rpx; padding: 6rpx 20rpx; border-radius: 20rpx; }
+.status-0 { background: #FFF3E0; color: #F97316; }
+.status-1 { background: #E8F5E9; color: #4CAF50; }
+.status-2 { background: #E3F2FD; color: #2196F3; }
+.status-3 { background: #F5F5F5; color: #999; }
+.info-card { background: #fff; margin: 20rpx 30rpx; padding: 24rpx 30rpx; border-radius: 16rpx; box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06); }
+.info-label { font-size: 26rpx; color: #999; display: block; margin-bottom: 8rpx; }
+.info-value { font-size: 28rpx; color: #333; display: block; }
+.items-section { margin: 20rpx 30rpx; }
+.section-title { font-size: 30rpx; font-weight: bold; color: #333; display: block; margin-bottom: 16rpx; }
+.item-card { background: #fff; border-radius: 12rpx; padding: 24rpx; margin-bottom: 16rpx; box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04); }
+.item-top { display: flex; justify-content: space-between; align-items: center; }
+.item-title { font-size: 28rpx; font-weight: bold; color: #333; }
+.item-status { font-size: 24rpx; color: #4CAF50; }
+.item-desc { font-size: 26rpx; color: #666; margin-top: 10rpx; display: block; line-height: 1.5; }
+</style>

+ 100 - 0
cfc-frontend/pages/parent/plans/index.vue

@@ -0,0 +1,100 @@
+<template>
+  <view class="container">
+    <view class="header">
+      <text class="title">我的计划</text>
+      <text class="subtitle">查看和管理家庭成长计划</text>
+    </view>
+
+    <!-- 加载中 -->
+    <view class="loading" v-if="loading">
+      <text>加载中...</text>
+    </view>
+
+    <!-- 空状态 -->
+    <view class="empty" v-else-if="plans.length === 0">
+      <text class="empty-icon">📋</text>
+      <text class="empty-text">暂无计划</text>
+      <text class="empty-hint">规划师可为您创建专属成长计划</text>
+    </view>
+
+    <!-- 计划列表 -->
+    <view class="plan-list" v-else>
+      <view class="plan-card" v-for="plan in plans" :key="plan.id" @click="goDetail(plan.id)">
+        <view class="card-top">
+          <text class="plan-title">{{ plan.title }}</text>
+          <view class="plan-status" :class="'status-' + (plan.status || 0)">
+            <text>{{ statusText(plan.status) }}</text>
+          </view>
+        </view>
+        <text class="plan-desc" v-if="plan.description">{{ plan.description }}</text>
+        <view class="card-meta">
+          <text class="meta-item">孩子:{{ plan.childName || '--' }}</text>
+          <text class="meta-item">创建时间:{{ plan.createTime || '--' }}</text>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+import { listFamilyPlans } from '../../../utils/api.js'
+
+export default {
+  data() {
+    return {
+      loading: false,
+      plans: []
+    }
+  },
+  onShow() {
+    this.loadPlans()
+  },
+  methods: {
+    loadPlans: function() {
+      this.loading = true
+      var self = this
+      listFamilyPlans({}).then(function(res) {
+        self.loading = false
+        if (res.code === 200 && Array.isArray(res.data)) {
+          self.plans = res.data
+        } else if (res.code === 200 && res.data && res.data.records) {
+          self.plans = res.data.records
+        }
+      }).catch(function() {
+        self.loading = false
+        self.plans = []
+      })
+    },
+    goDetail: function(id) {
+      uni.navigateTo({ url: './detail?planId=' + id })
+    },
+    statusText: function(status) {
+      var map = { 0: '待审核', 1: '进行中', 2: '已完成', 3: '已取消' }
+      return map[status] || '未知'
+    }
+  }
+}
+</script>
+
+<style scoped>
+.container { min-height: 100vh; background: #f8f8f8; padding-bottom: 30rpx; }
+.header { padding: 40rpx 30rpx 20rpx; }
+.title { font-size: 36rpx; font-weight: bold; color: #333; display: block; }
+.subtitle { font-size: 26rpx; color: #999; margin-top: 8rpx; display: block; }
+.loading, .empty { display: flex; flex-direction: column; align-items: center; padding: 100rpx 0; }
+.empty-icon { font-size: 80rpx; }
+.empty-text { font-size: 30rpx; color: #666; margin-top: 20rpx; }
+.empty-hint { font-size: 26rpx; color: #bbb; margin-top: 10rpx; }
+.plan-list { padding: 0 30rpx; }
+.plan-card { background: #fff; border-radius: 16rpx; padding: 30rpx; margin-bottom: 20rpx; box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.06); }
+.card-top { display: flex; justify-content: space-between; align-items: center; }
+.plan-title { font-size: 30rpx; font-weight: bold; color: #333; flex: 1; }
+.plan-status { font-size: 22rpx; padding: 4rpx 16rpx; border-radius: 20rpx; }
+.status-0 { background: #FFF3E0; color: #F97316; }
+.status-1 { background: #E8F5E9; color: #4CAF50; }
+.status-2 { background: #E3F2FD; color: #2196F3; }
+.status-3 { background: #F5F5F5; color: #999; }
+.plan-desc { font-size: 26rpx; color: #666; margin-top: 12rpx; display: block; line-height: 1.5; }
+.card-meta { display: flex; flex-direction: column; margin-top: 16rpx; gap: 6rpx; }
+.meta-item { font-size: 24rpx; color: #bbb; }
+</style>