|
|
@@ -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>
|