Browse Source

feat: 小程序前端功能增强和重构

- 家长首页(parent-index): 全面重构UI布局和积分展示

- 登录页(login): 优化验证码和登录流程

- 心愿/任务/兑换: 交互细节优化

- 成长档案/规划师注册: 页面完善

- 新增 child-detail 孩子详情页

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
User 5 months ago
parent
commit
72d2c89a11

+ 3 - 16
zxyj-frontend/App.vue

@@ -31,22 +31,8 @@ export default {
           }
         }
 
-        // 2. openid自动登录失败,尝试微信静默登录
-        const loginRes = await new Promise((resolve, reject) => {
-          uni.login({
-            provider: 'weixin',
-            success: resolve,
-            fail: reject
-          })
-        })
-
-        if (loginRes.code) {
-          const res = await this.request('/api/auth/silent-login', 'POST', { code: loginRes.code })
-          if (res && res.data) {
-            this.saveLoginInfo(res.data)
-            console.log('微信静默登录成功')
-          }
-        }
+        // 2. openid自动登录失败,不自动调用uni.login(避免DevTools环境超时阻塞渲染层)
+        //    用户会看到登录页,手动选择登录方式
       } catch (e) {
         console.log('自动登录失败,需要手动登录', e)
       }
@@ -73,6 +59,7 @@ export default {
           url: 'http://localhost:8080' + url,
           method: method,
           data: data,
+          timeout: 10000,
           header: { 'Content-Type': 'application/json' },
           success: (res) => {
             if (res.data.code === 200) {

+ 6 - 0
zxyj-frontend/pages.json

@@ -235,6 +235,12 @@
     "navigationBarTitleText": "心愿管理"
   }
 },
+    {
+      "path": "pages/parent/child-detail",
+      "style": {
+        "navigationBarTitleText": "孩子详情"
+      }
+    },
     {
       "path": "pages/parent/invite/index",
       "style": {

+ 11 - 2
zxyj-frontend/pages/growth/index.vue

@@ -64,10 +64,12 @@ export default {
       children: [],
       selectedChild: null,
       selectedChildName: '',
-      records: []
+      records: [],
+      childId: ''
     }
   },
-  onLoad() {
+  onLoad(options) {
+    this.childId = options.childId || ''
     this.loadChildren()
   },
   methods: {
@@ -77,6 +79,13 @@ export default {
         const res = await getChildren()
         if (res.code === 200) {
           this.children = res.data || []
+          // 如果指定了 childId,自动选中该孩子并加载档案
+          if (this.childId && this.children.length > 0) {
+            const matched = this.children.find(c => String(c.id) === String(this.childId))
+            if (matched) {
+              this.viewChildRecords(matched)
+            }
+          }
         }
       } catch (e) {
         console.error('加载孩子列表失败', e)

+ 34 - 0
zxyj-frontend/pages/guide/register/index.vue

@@ -51,6 +51,37 @@
         />
       </view>
 
+      <!-- 地址 -->
+      <view class="form-item">
+        <text class="label">联系地址</text>
+        <input 
+          class="input" 
+          v-model="formData.address" 
+          placeholder="请输入联系地址"
+        />
+      </view>
+
+      <!-- 银行卡号(选填) -->
+      <view class="form-item">
+        <text class="label">银行卡号 <text class="optional">(选填,用于佣金结算)</text></text>
+        <input 
+          class="input" 
+          v-model="formData.bankCard" 
+          placeholder="请输入银行卡号"
+          maxlength="30"
+        />
+      </view>
+
+      <!-- 开户行(选填) -->
+      <view class="form-item">
+        <text class="label">开户行 <text class="optional">(选填)</text></text>
+        <input 
+          class="input" 
+          v-model="formData.bankName" 
+          placeholder="请输入开户行名称,如:中国工商银行广州天河支行"
+        />
+      </view>
+
       <!-- 成长规划师认证信息(选填) -->
       <view class="section-divider">
         <text class="divider-text">成长规划师认证信息(选填)</text>
@@ -117,6 +148,9 @@ export default {
         realName: '',
         phone: '',
         idCard: '',
+        address: '',
+        bankCard: '',
+        bankName: '',
         teacherNo: '',
         teacherPhoto: '',
         certificateImage: ''

+ 235 - 34
zxyj-frontend/pages/index/parent-index.vue

@@ -9,12 +9,55 @@
         </view>
         <view class="sun-icon">☀️</view>
       </view>
-      <view class="child-status-card" v-if="selectedChild">
-        <view class="child-avatar-large">{{ selectedChild.nickname ? selectedChild.nickname[0] : '?' }}</view>
-        <view class="child-status-info">
-          <text class="child-name">{{ selectedChild.nickname }}正在健康成长中</text>
-          <text class="child-motto">🌱 每天进步一点点</text>
+
+      <!-- 孩子卡片行 -->
+      <view class="children-row" v-if="children.length > 0">
+        <scroll-view
+          class="children-scroll"
+          scroll-x
+          :scroll-with-animation="true"
+          :enhanced="true"
+          :show-scrollbar="false"
+        >
+          <!-- 一孩时,用大卡片 -->
+          <view
+            v-for="child in children"
+            :key="child.id"
+            class="child-card"
+            :class="{ 'child-card-single': children.length === 1 }"
+            @click="goToChildDetail(child)"
+          >
+            <view class="child-avatar" :class="'child-avatar-color-' + (child.id % 6)">
+              <text class="child-avatar-text">{{ child.nickname ? child.nickname[0] : '?' }}</text>
+            </view>
+            <view class="child-card-body">
+              <text class="child-card-name">{{ child.nickname }}</text>
+              <view class="child-card-stats">
+                <text class="child-card-points">⭐ {{ child.totalPoints || 0 }}</text>
+                <text class="child-card-dan" v-if="child.danLevel">DAN: {{ child.danLevel }}</text>
+              </view>
+              <view class="child-card-badges">
+                <text class="badge badge-review" v-if="child.pendingReviewCount > 0">待审 {{ child.pendingReviewCount }}</text>
+                <text class="badge badge-wish" v-if="child.pendingWishCount > 0">心愿 {{ child.pendingWishCount }}</text>
+              </view>
+            </view>
+            <view class="child-card-arrow">›</view>
+          </view>
+        </scroll-view>
+        <!-- 多孩提示 -->
+        <view class="scroll-hint" v-if="children.length > 1">
+          <text class="scroll-hint-text">← 左右滑动查看更多 →</text>
+        </view>
+      </view>
+
+      <!-- 无孩子时引导 -->
+      <view class="empty-children-card" v-else @click="goToAddChild">
+        <view class="empty-children-icon">👶</view>
+        <view class="empty-children-text">
+          <text class="empty-children-title">还没有添加孩子</text>
+          <text class="empty-children-hint">点击添加孩子,开始成长之旅</text>
         </view>
+        <view class="empty-children-arrow">›</view>
       </view>
     </view>
 
@@ -138,10 +181,6 @@
           <view class="action-icon reward-icon">🎁</view>
           <text class="action-label">奖励中心</text>
         </view>
-        <view class="action-item" @click="goToGrowth">
-          <view class="action-icon growth-icon">🌱</view>
-          <text class="action-label">成长档案</text>
-        </view>
         <view class="action-item" @click="inviteFriend">
           <view class="action-icon invite-icon">📤</view>
           <text class="action-label">邀请家人</text>
@@ -152,7 +191,7 @@
 </template>
 
 <script>
-import { getFamilyMembers, getChildren, getPendingReviewTasks, approveTask, rejectTask, switchToChild, switchBackToParent } from '../../utils/api.js'
+import { getFamilyMembers, getChildren, getPendingReviewTasks, approveTask, rejectTask, getPendingWishes } from '../../utils/api.js'
 
 export default {
   data() {
@@ -162,6 +201,7 @@ export default {
       children: [],
       selectedChild: null,
       pendingReviewTasks: [],
+      pendingReviewWishes: [],
       pendingTasks: [],
       taskCompletionRate: 85,
       habitCount: 3,
@@ -220,7 +260,6 @@ export default {
             .map(c => ({ ...c, role: 'child' }))
           this.familyMembers = [...parents, ...children]
 
-          // 设置选中的孩子
           if (children.length > 0) {
             this.selectedChild = children[0]
           }
@@ -228,14 +267,49 @@ export default {
           console.log('获取家庭成员失败', e)
         }
 
+        // 获取孩子列表
         const childrenRes = await getChildren()
-        this.children = childrenRes.data || []
+        const rawChildren = childrenRes.data || []
+        this.children = rawChildren
         if (this.children.length > 0 && !this.selectedChild) {
           this.selectedChild = this.children[0]
         }
 
+        // 获取待审核任务
         const reviewRes = await getPendingReviewTasks()
-        this.pendingReviewTasks = (reviewRes.data || []).slice(0, 3)
+        const allReviewTasks = reviewRes.data || []
+        this.pendingReviewTasks = allReviewTasks.slice(0, 3)
+
+        // 获取待处理心愿
+        try {
+          const wishRes = await getPendingWishes()
+          this.pendingReviewWishes = wishRes.data || []
+        } catch (e) {
+          console.log('获取待处理心愿失败', e)
+          this.pendingReviewWishes = []
+        }
+
+        // 为每个孩子计算待处理数量
+        const reviewMap = {}
+        const wishMap = {}
+        for (const task of allReviewTasks) {
+          const cid = task.childId || task.assigneeId
+          if (cid) {
+            reviewMap[cid] = (reviewMap[cid] || 0) + 1
+          }
+        }
+        for (const wish of (this.pendingReviewWishes || [])) {
+          const cid = wish.childId
+          if (cid) {
+            wishMap[cid] = (wishMap[cid] || 0) + 1
+          }
+        }
+        // 在 children 对象上附加待处理计数
+        this.children = this.children.map(c => ({
+          ...c,
+          pendingReviewCount: reviewMap[c.id] || 0,
+          pendingWishCount: wishMap[c.id] || 0
+        }))
 
         // 模拟待完成任务数据
         this.pendingTasks = [
@@ -268,6 +342,11 @@ export default {
         uni.showToast({ title: '操作失败', icon: 'none' })
       }
     },
+    goToChildDetail(child) {
+      uni.navigateTo({
+        url: '/pages/parent/child-detail?childId=' + child.id + '&childName=' + encodeURIComponent(child.nickname || '')
+      })
+    },
     addTask() {
       uni.navigateTo({ url: '/pages/tasks/create-task' })
     },
@@ -286,8 +365,8 @@ export default {
     inviteFriend() {
       uni.navigateTo({ url: '/pages/parent/invite/index' })
     },
-    goToGrowth() {
-      uni.navigateTo({ url: '/pages/growth/index' })
+    goToAddChild() {
+      uni.navigateTo({ url: '/pages/profile/create-child' })
     }
   }
 }
@@ -332,44 +411,170 @@ export default {
   font-size: 64rpx;
 }
 
-.child-status-card {
+/* 孩子卡片行 */
+.children-row {
+  margin-top: 24rpx;
+}
+
+.children-scroll {
   display: flex;
+  flex-direction: row;
+  white-space: nowrap;
+  overflow: hidden;
+}
+
+.child-card {
+  display: inline-flex;
   align-items: center;
   background: #FFFFFF;
-  border-radius: 24rpx;
-  padding: 24rpx;
+  border-radius: 20rpx;
+  padding: 20rpx 24rpx;
+  margin-right: 20rpx;
   box-shadow: 0 4px 20px rgba(255, 140, 66, 0.08);
+  min-width: 440rpx;
+  width: auto;
+  position: relative;
+}
+
+.child-card-single {
+  width: 100%;
+  margin-right: 0;
 }
 
-.child-avatar-large {
-  width: 96rpx;
-  height: 96rpx;
+.child-avatar {
+  width: 80rpx;
+  height: 80rpx;
   border-radius: 50%;
-  background: linear-gradient(135deg, #FF8C42, #FF7A2E);
-  color: #FFFFFF;
   display: flex;
   align-items: center;
   justify-content: center;
-  font-size: 40rpx;
+  flex-shrink: 0;
+}
+
+.child-avatar-color-0 { background: linear-gradient(135deg, #FF8C42, #FF7A2E); }
+.child-avatar-color-1 { background: linear-gradient(135deg, #667eea, #764ba2); }
+.child-avatar-color-2 { background: linear-gradient(135deg, #4ECDC4, #44B09E); }
+.child-avatar-color-3 { background: linear-gradient(135deg, #FF6B6B, #FF8E53); }
+.child-avatar-color-4 { background: linear-gradient(135deg, #A8E063, #56AB2F); }
+.child-avatar-color-5 { background: linear-gradient(135deg, #F093FB, #F5576C); }
+
+.child-avatar-text {
+  color: #FFFFFF;
+  font-size: 36rpx;
   font-weight: 600;
-  margin-right: 24rpx;
 }
 
-.child-status-info {
+.child-card-body {
   flex: 1;
+  margin-left: 16rpx;
+  min-width: 0;
 }
 
-.child-status-info .child-name {
-  font-size: 28rpx;
+.child-card-name {
+  font-size: 30rpx;
   font-weight: 600;
   color: #2D3436;
   display: block;
+  line-height: 1.3;
+}
+
+.child-card-stats {
+  display: flex;
+  align-items: center;
+  margin-top: 6rpx;
+  gap: 16rpx;
 }
 
-.child-status-info .child-motto {
+.child-card-points {
   font-size: 24rpx;
-  color: #4ECDC4;
+  color: #FF8C42;
+  font-weight: 600;
+}
+
+.child-card-dan {
+  font-size: 22rpx;
+  color: #667eea;
+  font-weight: 500;
+}
+
+.child-card-badges {
+  display: flex;
+  gap: 8rpx;
   margin-top: 8rpx;
+  flex-wrap: wrap;
+}
+
+.badge {
+  font-size: 20rpx;
+  padding: 4rpx 12rpx;
+  border-radius: 20rpx;
+  font-weight: 500;
+}
+
+.badge-review {
+  background: #FFF5EB;
+  color: #FF8C42;
+}
+
+.badge-wish {
+  background: #FFF0F0;
+  color: #FF6B6B;
+}
+
+.child-card-arrow {
+  font-size: 36rpx;
+  color: #D5D5D5;
+  margin-left: 8rpx;
+  flex-shrink: 0;
+}
+
+.scroll-hint {
+  text-align: center;
+  margin-top: 12rpx;
+}
+
+.scroll-hint-text {
+  font-size: 22rpx;
+  color: #B2BEC3;
+}
+
+/* 无孩子引导 */
+.empty-children-card {
+  display: flex;
+  align-items: center;
+  background: #FFFFFF;
+  border-radius: 20rpx;
+  padding: 24rpx;
+  box-shadow: 0 4px 20px rgba(255, 140, 66, 0.08);
+  margin-top: 24rpx;
+}
+
+.empty-children-icon {
+  font-size: 56rpx;
+  margin-right: 16rpx;
+}
+
+.empty-children-text {
+  flex: 1;
+}
+
+.empty-children-title {
+  font-size: 28rpx;
+  font-weight: 600;
+  color: #2D3436;
+  display: block;
+}
+
+.empty-children-hint {
+  font-size: 24rpx;
+  color: #B2BEC3;
+  margin-top: 6rpx;
+  display: block;
+}
+
+.empty-children-arrow {
+  font-size: 36rpx;
+  color: #D5D5D5;
 }
 
 /* 区块标题 */
@@ -705,10 +910,6 @@ export default {
   background: rgba(0, 184, 148, 0.15);
 }
 
-.growth-icon {
-  background: rgba(78, 205, 196, 0.15);
-}
-
 .action-label {
   font-size: 24rpx;
   color: #636E72;

+ 1 - 52
zxyj-frontend/pages/login/login.vue

@@ -16,6 +16,7 @@
           v-model="phone" 
           placeholder="请输入手机号" 
           maxlength="11"
+          cursor-spacing="100"
         />
       </view>
       
@@ -66,7 +67,6 @@
         <text class="link" @click="showPrivacy">《隐私政策》</text>
       </view>
     </view>
-  </view>
 </template>
 
 <script>
@@ -85,14 +85,6 @@ export default {
     }
   },
   onLoad(options) {
-    // 获取用户信息
-    uni.getUserProfile({
-      desc: '用于完善用户资料',
-      success: (res) => {
-        this.nickname = res.userInfo.nickName
-        this.avatar = res.userInfo.avatarUrl
-      }
-    })
     // 处理邀请码
     if (options.inviteCode) {
       uni.setStorageSync('inviteCode', options.inviteCode)
@@ -120,49 +112,6 @@ export default {
         showCancel: false
       })
     },
-    // 手机号输入完成时检查(11位时触发)
-    onPhoneInput() {
-      if (this.phone.length === 11 && /^1[3-9]\d{9}$/.test(this.phone)) {
-        // 重置检查状态,准备检查新手机号
-        if (this.lastCheckedPhone !== this.phone) {
-          this.codeChecked = false
-        }
-      }
-    },
-    onRoleChange(e) {
-      this.role = e.detail.value
-    },
-    onBirthdayChange(e) {
-      this.birthday = e.detail.value
-    },
-    onGenderChange(e) {
-      this.gender = e.detail.value
-    },
-    // 从微信获取昵称
-    getNicknameFromWechat() {
-      return this.nickname || '用户'
-    },
-    // 从微信获取头像
-    getAvatarFromWechat() {
-      return this.avatar || ''
-    },
-    onAgreementChange(e) {
-      this.agreed = e.detail.value.length > 0
-    },
-    showAgreement() {
-      uni.showModal({
-        title: '用户协议',
-        content: '这里是用户协议内容...',
-        showCancel: false
-      })
-    },
-    showPrivacy() {
-      uni.showModal({
-        title: '隐私政策',
-        content: '这里是隐私政策内容...',
-        showCancel: false
-      })
-    },
     async sendCode() {
       if (!this.phone) {
         uni.showToast({ title: '请输入手机号', icon: 'none' })

+ 486 - 0
zxyj-frontend/pages/parent/child-detail.vue

@@ -0,0 +1,486 @@
+<template>
+  <view class="container">
+    <!-- 加载状态 -->
+    <view class="loading-container" v-if="loading">
+      <text class="loading-text">加载中...</text>
+    </view>
+
+    <template v-if="!loading">
+      <!-- 孩子信息卡片 -->
+      <view class="child-info-card">
+        <view class="child-info-avatar" :class="'child-avatar-color-' + (childId % 6)">
+          <text class="child-avatar-text">{{ childName ? childName[0] : '?' }}</text>
+        </view>
+        <view class="child-info-body">
+          <text class="child-info-name">{{ childName }}</text>
+          <view class="child-info-stats">
+            <text class="stat-item stat-points">⭐ {{ childInfo.totalPoints || 0 }} 分</text>
+            <text class="stat-item stat-dan" v-if="childInfo.danLevel">DAN: {{ childInfo.danLevel }}</text>
+            <text class="stat-item stat-streak" v-if="childInfo.streakDays > 0">🔥 连续 {{ childInfo.streakDays }} 天</text>
+          </view>
+          <view class="child-info-progress" v-if="todayTasks.length > 0">
+            <text class="progress-label">
+              今日任务 {{ completedCount }}/{{ todayTasks.length }} 完成
+            </text>
+            <view class="progress-bar-bg">
+              <view class="progress-bar-fill" :style="{ width: taskProgressPercent + '%' }"></view>
+            </view>
+          </view>
+        </view>
+      </view>
+
+      <!-- 操作区 -->
+      <view class="action-section">
+        <view class="section-title">快捷操作</view>
+        <view class="action-grid">
+          <view class="action-btn" @click="goToCreateTask">
+            <view class="action-btn-icon create-icon">📝</view>
+            <text class="action-btn-label">创建任务</text>
+          </view>
+          <view class="action-btn" @click="goToWishReview">
+            <view class="action-btn-icon wish-icon">💝</view>
+            <text class="action-btn-label">心愿审核</text>
+            <text class="action-btn-badge" v-if="pendingWishCount > 0">{{ pendingWishCount }}</text>
+          </view>
+          <view class="action-btn" @click="goToGrowth">
+            <view class="action-btn-icon growth-icon">📊</view>
+            <text class="action-btn-label">成长档案</text>
+          </view>
+        </view>
+      </view>
+
+      <!-- 今日任务 -->
+      <view class="tasks-section">
+        <view class="section-title">今日任务</view>
+        <view class="tasks-card" v-if="todayTasks.length > 0">
+          <view
+            class="task-item"
+            v-for="task in todayTasks"
+            :key="task.id"
+          >
+            <text class="task-status-icon">{{ task.status === 'completed' ? '✅' : '⏳' }}</text>
+            <view class="task-info">
+              <text class="task-title">{{ task.title }}</text>
+              <text class="task-points" v-if="task.points">+{{ task.points }}分</text>
+            </view>
+          </view>
+        </view>
+        <view class="empty-card" v-else>
+          <text class="empty-icon">🎉</text>
+          <text class="empty-text">今日暂无任务</text>
+        </view>
+      </view>
+
+      <!-- 待处理 -->
+      <view class="pending-section">
+        <view class="section-title">待处理</view>
+        <view class="pending-card" v-if="pendingReviewTasks.length > 0 || pendingWishes.length > 0">
+          <view
+            class="pending-item"
+            v-for="task in pendingReviewTasks"
+            :key="'task-' + task.id"
+          >
+            <text class="pending-icon">📋</text>
+            <text class="pending-desc">{{ task.title }}</text>
+            <text class="pending-tag tag-review">待审核</text>
+          </view>
+          <view
+            class="pending-item"
+            v-for="wish in pendingWishes"
+            :key="'wish-' + wish.id"
+          >
+            <text class="pending-icon">💝</text>
+            <text class="pending-desc">{{ wish.title }}</text>
+            <text class="pending-tag tag-wish">{{ wish.status === 'draft' ? '待定价' : '待审批' }}</text>
+          </view>
+        </view>
+        <view class="empty-card" v-else>
+          <text class="empty-icon">✅</text>
+          <text class="empty-text">全部已处理</text>
+        </view>
+      </view>
+    </template>
+  </view>
+</template>
+
+<script>
+import { getChildren, getTodayTasks, getPendingReviewTasks, getPendingWishes } from '../../utils/api.js'
+
+export default {
+  data() {
+    return {
+      loading: true,
+      childId: '',
+      childName: '',
+      childInfo: {},
+      todayTasks: [],
+      pendingReviewTasks: [],
+      pendingWishes: []
+    }
+  },
+  computed: {
+    completedCount() {
+      return this.todayTasks.filter(t => t.status === 'completed').length
+    },
+    taskProgressPercent() {
+      if (this.todayTasks.length === 0) return 0
+      return Math.round((this.completedCount / this.todayTasks.length) * 100)
+    },
+    pendingWishCount() {
+      return this.pendingWishes.length
+    }
+  },
+  onLoad(options) {
+    this.childId = options.childId || ''
+    this.childName = options.childName || '孩子'
+    this.loadChildData()
+  },
+  methods: {
+    async loadChildData() {
+      this.loading = true
+      try {
+        // 获取孩子信息
+        const childrenRes = await getChildren()
+        const children = childrenRes.data || []
+        const matched = children.find(c => String(c.id) === String(this.childId))
+        if (matched) {
+          this.childInfo = matched
+          if (!this.childName || this.childName === '孩子') {
+            this.childName = matched.nickname || matched.name || '孩子'
+          }
+        }
+
+        // 并行加载今日任务和待处理
+        const [tasksRes, reviewRes, wishRes] = await Promise.all([
+          getTodayTasks(this.childId),
+          getPendingReviewTasks(),
+          getPendingWishes()
+        ])
+
+        this.todayTasks = tasksRes.data || []
+
+        // 过滤属于该孩子的待审核任务
+        const allReviewTasks = reviewRes.data || []
+        this.pendingReviewTasks = allReviewTasks.filter(t => {
+          return String(t.childId || t.assigneeId) === String(this.childId)
+        }).slice(0, 5)
+
+        // 过滤属于该孩子的待处理心愿
+        const allWishes = wishRes.data || []
+        this.pendingWishes = allWishes.filter(w => String(w.childId) === String(this.childId)).slice(0, 5)
+      } catch (e) {
+        console.error('加载孩子详情失败', e)
+        uni.showToast({ title: '加载失败', icon: 'none' })
+      } finally {
+        this.loading = false
+      }
+    },
+    goToCreateTask() {
+      uni.navigateTo({
+        url: '/pages/tasks/create-task?childId=' + this.childId
+      })
+    },
+    goToWishReview() {
+      uni.navigateTo({
+        url: '/pages/parent/wishes/index?childId=' + this.childId
+      })
+    },
+    goToGrowth() {
+      uni.navigateTo({
+        url: '/pages/growth/index?childId=' + this.childId
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+.container {
+  padding: 30rpx;
+  background: #FFF9F0;
+  min-height: 100vh;
+}
+
+.loading-container {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 200rpx 0;
+}
+
+.loading-text {
+  font-size: 28rpx;
+  color: #B2BEC3;
+}
+
+/* 孩子信息卡片 */
+.child-info-card {
+  display: flex;
+  align-items: flex-start;
+  background: #FFFFFF;
+  border-radius: 24rpx;
+  padding: 30rpx;
+  margin-bottom: 32rpx;
+  box-shadow: 0 4px 20px rgba(255, 140, 66, 0.08);
+}
+
+.child-info-avatar {
+  width: 96rpx;
+  height: 96rpx;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-shrink: 0;
+  margin-right: 20rpx;
+}
+
+.child-avatar-color-0 { background: linear-gradient(135deg, #FF8C42, #FF7A2E); }
+.child-avatar-color-1 { background: linear-gradient(135deg, #667eea, #764ba2); }
+.child-avatar-color-2 { background: linear-gradient(135deg, #4ECDC4, #44B09E); }
+.child-avatar-color-3 { background: linear-gradient(135deg, #FF6B6B, #FF8E53); }
+.child-avatar-color-4 { background: linear-gradient(135deg, #A8E063, #56AB2F); }
+.child-avatar-color-5 { background: linear-gradient(135deg, #F093FB, #F5576C); }
+
+.child-avatar-text {
+  color: #FFFFFF;
+  font-size: 40rpx;
+  font-weight: 600;
+}
+
+.child-info-body {
+  flex: 1;
+  min-width: 0;
+}
+
+.child-info-name {
+  font-size: 36rpx;
+  font-weight: 700;
+  color: #2D3436;
+  display: block;
+}
+
+.child-info-stats {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 12rpx;
+  margin-top: 12rpx;
+}
+
+.stat-item {
+  font-size: 24rpx;
+  font-weight: 500;
+}
+
+.stat-points { color: #FF8C42; }
+.stat-dan { color: #667eea; }
+.stat-streak { color: #FF6B6B; }
+
+.child-info-progress {
+  margin-top: 16rpx;
+}
+
+.progress-label {
+  font-size: 22rpx;
+  color: #636E72;
+  display: block;
+  margin-bottom: 8rpx;
+}
+
+.progress-bar-bg {
+  height: 10rpx;
+  background: #FFF5EB;
+  border-radius: 9999rpx;
+  overflow: hidden;
+}
+
+.progress-bar-fill {
+  height: 100%;
+  background: linear-gradient(90deg, #4ECDC4, #44B09E);
+  border-radius: 9999rpx;
+  transition: width 0.3s ease;
+}
+
+/* 区块标题 */
+.section-title {
+  font-size: 30rpx;
+  font-weight: 600;
+  color: #2D3436;
+  margin-bottom: 20rpx;
+}
+
+/* 操作区 */
+.action-section {
+  margin-bottom: 40rpx;
+}
+
+.action-grid {
+  display: flex;
+  gap: 20rpx;
+}
+
+.action-btn {
+  flex: 1;
+  background: #FFFFFF;
+  border-radius: 20rpx;
+  padding: 24rpx 16rpx;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  box-shadow: 0 4px 20px rgba(255, 140, 66, 0.08);
+  position: relative;
+}
+
+.action-btn-icon {
+  width: 72rpx;
+  height: 72rpx;
+  border-radius: 20rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 36rpx;
+  margin-bottom: 12rpx;
+}
+
+.create-icon { background: rgba(255, 140, 66, 0.15); }
+.wish-icon { background: rgba(255, 107, 107, 0.15); }
+.growth-icon { background: rgba(78, 205, 196, 0.15); }
+
+.action-btn-label {
+  font-size: 24rpx;
+  color: #636E72;
+  font-weight: 500;
+}
+
+.action-btn-badge {
+  position: absolute;
+  top: 12rpx;
+  right: 12rpx;
+  background: #FF6B6B;
+  color: #FFFFFF;
+  font-size: 20rpx;
+  min-width: 32rpx;
+  height: 32rpx;
+  line-height: 32rpx;
+  text-align: center;
+  border-radius: 16rpx;
+  padding: 0 8rpx;
+}
+
+/* 今日任务 */
+.tasks-section {
+  margin-bottom: 40rpx;
+}
+
+.tasks-card {
+  background: #FFFFFF;
+  border-radius: 20rpx;
+  padding: 8rpx 24rpx;
+  box-shadow: 0 4px 20px rgba(255, 140, 66, 0.08);
+}
+
+.task-item {
+  display: flex;
+  align-items: center;
+  padding: 20rpx 0;
+  border-bottom: 1rpx solid #F5F0EB;
+}
+
+.task-item:last-child {
+  border-bottom: none;
+}
+
+.task-status-icon {
+  font-size: 32rpx;
+  margin-right: 16rpx;
+}
+
+.task-info {
+  flex: 1;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.task-title {
+  font-size: 28rpx;
+  color: #2D3436;
+}
+
+.task-points {
+  font-size: 24rpx;
+  color: #FF8C42;
+  font-weight: 600;
+}
+
+.empty-card {
+  background: #FFFFFF;
+  border-radius: 20rpx;
+  padding: 48rpx;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  box-shadow: 0 4px 20px rgba(255, 140, 66, 0.08);
+}
+
+.empty-icon {
+  font-size: 48rpx;
+  margin-bottom: 12rpx;
+}
+
+.empty-text {
+  font-size: 28rpx;
+  color: #B2BEC3;
+}
+
+/* 待处理 */
+.pending-section {
+  margin-bottom: 48rpx;
+}
+
+.pending-card {
+  background: #FFFFFF;
+  border-radius: 20rpx;
+  padding: 8rpx 24rpx;
+  box-shadow: 0 4px 20px rgba(255, 140, 66, 0.08);
+}
+
+.pending-item {
+  display: flex;
+  align-items: center;
+  padding: 20rpx 0;
+  border-bottom: 1rpx solid #F5F0EB;
+}
+
+.pending-item:last-child {
+  border-bottom: none;
+}
+
+.pending-icon {
+  font-size: 32rpx;
+  margin-right: 16rpx;
+}
+
+.pending-desc {
+  flex: 1;
+  font-size: 28rpx;
+  color: #2D3436;
+}
+
+.pending-tag {
+  font-size: 22rpx;
+  padding: 4rpx 14rpx;
+  border-radius: 20rpx;
+  font-weight: 500;
+}
+
+.tag-review {
+  background: #FFF5EB;
+  color: #FF8C42;
+}
+
+.tag-wish {
+  background: #FFF0F0;
+  color: #FF6B6B;
+}
+</style>

+ 20 - 2
zxyj-frontend/pages/parent/wishes/index.vue

@@ -155,6 +155,8 @@ export default {
     return {
       activeTab: 'pending',
       familyId: '',
+      childId: '',
+      childName: '',
       pendingWishes: [],
       allWishes: [],
       priceDialogVisible: false,
@@ -180,16 +182,32 @@ export default {
     this.familyId = uni.getStorageSync('familyId')
     this.loadData()
   },
+  onLoad(options) {
+    if (options.childId) {
+      this.childId = options.childId
+      this.childName = options.childName || '该孩子'
+    }
+  },
   methods: {
     async loadData() {
       try {
         // 获取待处理列表
         const pendingRes = await getPendingWishes(this.familyId)
-        this.pendingWishes = pendingRes.data || []
+        let wishes = pendingRes.data || []
+
+        // 如果指定了 childId,按孩子过滤
+        if (this.childId) {
+          wishes = wishes.filter(w => String(w.childId) === String(this.childId))
+        }
+        this.pendingWishes = wishes
         
         // 获取全部心愿
         const allRes = await getWishes({ familyId: this.familyId })
-        this.allWishes = allRes.data || []
+        let allWishes = allRes.data || []
+        if (this.childId) {
+          allWishes = allWishes.filter(w => String(w.childId) === String(this.childId))
+        }
+        this.allWishes = allWishes
       } catch (e) {
         console.error('加载数据失败', e)
         uni.showToast({ title: '加载失败', icon: 'none' })

+ 1 - 1
zxyj-frontend/pages/rewards/rewards.vue

@@ -143,7 +143,7 @@ export default {
       return new Promise((resolve, reject) => {
         uni.request({
           url: 'http://localhost:8080/api/user/info',
-          method: 'GET',
+          method: 'POST',
           header: {
             'Authorization': 'Bearer ' + uni.getStorageSync('token')
           },

+ 11 - 3
zxyj-frontend/pages/tasks/create-task.vue

@@ -112,16 +112,24 @@ dateTimeIndex: [0, 0, 0, 0],
 deadlineText: ''
 }
 },
-  onLoad() {
-    this.loadChildren()
+  onLoad(options) {
+    this.loadChildren(options.childId)
     this.loadMiniGames()
     this.initDateTimePicker()
   },
 methods: {
-    async loadChildren() {
+    async loadChildren(preselectedChildId) {
       try {
         const res = await getChildren()
         this.children = res.data || []
+        // 如果有 preselect childId,自动选中该孩子
+        if (preselectedChildId && this.children.length > 0) {
+          const matched = this.children.find(c => String(c.id) === String(preselectedChildId))
+          if (matched) {
+            this.selectedChild = matched
+            this.form.childId = matched.id
+          }
+        }
       } catch (e) {
         console.error(e)
       }

+ 10 - 2
zxyj-frontend/pages/teacher/index.vue

@@ -217,10 +217,18 @@ export default {
   methods: {
     formatDate(date) {
       const months = ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月']
-      return `${months[date.getMonth()]}${date.getDate()}日`
+      return `${months[date.getMonth()]}${date.getMonth() + 1}月${date.getDate()}日`
     },
     loadData() {
-      // 模拟数据加载
+      const userInfo = uni.getStorageSync('userInfo')
+      if (userInfo && userInfo.nickname) {
+        this.teacherInfo = {
+          nickname: userInfo.nickname,
+          avatar: uni.getStorageSync('avatar') || '/static/default-avatar.png',
+          level: '规划师',
+          rating: '5.0'
+        }
+      }
     },
     switchToParent() {
       uni.switchTab({ url: '/pages/index/parent-index' })

+ 1 - 1
zxyj-frontend/utils/api.js

@@ -297,7 +297,7 @@ export const createParentWishlist = (data) => {
 }
 
 export const getParentWishlist = () => {
-  return request('/api/parent/wishlist', 'POST')
+  return request('/api/parent/wishlist/list', 'POST')
 }
 
 export const getFamilyWishlist = () => {