|
|
@@ -1,120 +1,188 @@
|
|
|
<template>
|
|
|
<view class="container">
|
|
|
- <PageBanner theme="action" tagline="干预 · 记录 · 成长" quote="知行合一,每天进步一点点" />
|
|
|
- <FamilyMemberStrip @select="onMemberSelect" />
|
|
|
- <view class="reminder-bar" v-if="hasIncomplete">
|
|
|
- <text class="reminder-text">⚠ 今日还有 {{ incompleteCount }} 项未记录</text>
|
|
|
- <text class="reminder-close" @click="dismissReminder">✕</text>
|
|
|
- </view>
|
|
|
- <view class="section">
|
|
|
- <view class="progress-card">
|
|
|
- <view class="progress-header">
|
|
|
- <text class="progress-title">今日干预进度</text>
|
|
|
- <text class="progress-date">{{ todayDate }}</text>
|
|
|
+ <view v-if="!isLoggedIn">
|
|
|
+ <PageBanner theme="action" tagline="干预 · 记录 · 成长" quote="知行合一,每天进步一点点" />
|
|
|
+ <!-- 品牌定位语 -->
|
|
|
+ <view class="brand-strap"><text class="brand-strap-text">看见改变正在发生</text></view>
|
|
|
+ <!-- 理念卡 -->
|
|
|
+ <view class="concept-card">
|
|
|
+ <text class="concept-quote">记录不是目的,看见改变才是。</text>
|
|
|
+ <text class="concept-desc">每一份饮食、运动、心情的记录,都在为成长提供依据。</text>
|
|
|
+ </view>
|
|
|
+ <!-- 示例案例 -->
|
|
|
+ <view class="case-card">
|
|
|
+ <view class="case-header">
|
|
|
+ <text class="case-label">📖 真实案例(脱敏)</text>
|
|
|
</view>
|
|
|
- <view class="progress-item" v-for="(item, idx) in progressItems" :key="getProgressKey(item)">
|
|
|
- <text class="progress-icon">{{ item.icon }}</text>
|
|
|
- <text class="progress-label">{{ item.label }}</text>
|
|
|
- <text class="progress-status" :class="item.done ? 'status-done' : 'status-pending'">{{ item.done ? '✔ 已完成' : '' }}</text>
|
|
|
- <text class="progress-btn" v-if="!item.done" @click="handleProgressClick(item)">{{ item.actionText || '补录' }}</text>
|
|
|
+ <view class="case-body">
|
|
|
+ <text class="case-name">小明(10岁,体校生)</text>
|
|
|
+ <text class="case-change">3个月干预前后:</text>
|
|
|
+ <view class="case-metrics">
|
|
|
+ <view class="metric-item">
|
|
|
+ <text class="metric-up">↑ 5分</text>
|
|
|
+ <text class="metric-label">身维度</text>
|
|
|
+ </view>
|
|
|
+ <view class="metric-item">
|
|
|
+ <text class="metric-up">↑ 3分</text>
|
|
|
+ <text class="metric-label">智维度</text>
|
|
|
+ </view>
|
|
|
+ <view class="metric-item">
|
|
|
+ <text class="metric-up">↑ 8天</text>
|
|
|
+ <text class="metric-label">连续打卡</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <text class="case-note">通过饮食记录 + 睡眠打卡,免疫力显著提升</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <!-- 功能预览 -->
|
|
|
+ <view class="guest-features">
|
|
|
+ <view class="feat-card" @click="goPage('/pages/health/daily-checkin')">
|
|
|
+ <text class="feat-icon">✅</text>
|
|
|
+ <text class="feat-title">每日打卡</text>
|
|
|
+ <text class="feat-desc">饮食·运动·睡眠·心情</text>
|
|
|
</view>
|
|
|
- <view class="progress-footer">
|
|
|
- <text class="footer-text">今日完成 {{ doneCount }}/{{ totalCount }}</text>
|
|
|
- <text class="footer-streak" v-if="streakDays > 0">🔥 连续打卡 {{ streakDays }} 天</text>
|
|
|
+ <view class="feat-card" @click="goPage('/pages/health/survey-questionnaire?dim=all')">
|
|
|
+ <text class="feat-icon">📋</text>
|
|
|
+ <text class="feat-title">定期调研</text>
|
|
|
+ <text class="feat-desc">每周家庭健康问卷</text>
|
|
|
+ </view>
|
|
|
+ <view class="feat-card">
|
|
|
+ <text class="feat-icon">🏆</text>
|
|
|
+ <text class="feat-title">里程碑</text>
|
|
|
+ <text class="feat-desc">连续打卡·挑战完成</text>
|
|
|
+ </view>
|
|
|
+ <view class="feat-card" @click="goPage('/pages/activity/index')">
|
|
|
+ <text class="feat-icon">🎯</text>
|
|
|
+ <text class="feat-title">家庭挑战</text>
|
|
|
+ <text class="feat-desc">全家参与的互动任务</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
+ <!-- 登录引导 -->
|
|
|
+ <view class="login-guide-section">
|
|
|
+ <LoginGuideCard
|
|
|
+ title="解锁完整成长档案"
|
|
|
+ :items="[
|
|
|
+ '查看干预进度与追踪记录',
|
|
|
+ '完成每日打卡累积连续记录',
|
|
|
+ '参与家庭挑战获得里程碑奖励'
|
|
|
+ ]" />
|
|
|
+ </view>
|
|
|
</view>
|
|
|
- <view class="section">
|
|
|
- <view class="tracking-card">
|
|
|
- <view class="tracking-tabs">
|
|
|
- <view class="tracking-tab" :class="activeTab === tab.key ? 'tab-active' : ''" v-for="tab in trackingTabs" :key="tab.key" @click="switchTab(tab.key)">
|
|
|
- <text class="tab-text" :class="activeTab === tab.key ? 'tab-active-text' : ''">{{ tab.label }}</text>
|
|
|
+ <view v-else>
|
|
|
+ <PageBanner theme="action" tagline="干预 · 记录 · 成长" quote="知行合一,每天进步一点点" />
|
|
|
+ <FamilyMemberStrip @select="onMemberSelect" />
|
|
|
+ <view class="reminder-bar" v-if="hasIncomplete">
|
|
|
+ <text class="reminder-text">⚠ 今日还有 {{ incompleteCount }} 项未记录</text>
|
|
|
+ <text class="reminder-close" @click="dismissReminder">✕</text>
|
|
|
+ </view>
|
|
|
+ <view class="section">
|
|
|
+ <view class="progress-card">
|
|
|
+ <view class="progress-header">
|
|
|
+ <text class="progress-title">今日干预进度</text>
|
|
|
+ <text class="progress-date">{{ todayDate }}</text>
|
|
|
</view>
|
|
|
- </view>
|
|
|
- <view class="tracking-content">
|
|
|
- <view class="tracking-empty" v-if="currentRecords.length === 0">
|
|
|
- <text class="empty-icon">📊</text>
|
|
|
- <text class="empty-text">还没有记录</text>
|
|
|
- <text class="empty-hint">开始记录{{ activeTabLabel }}吧</text>
|
|
|
+ <view class="progress-item" v-for="(item, idx) in progressItems" :key="getProgressKey(item)">
|
|
|
+ <text class="progress-icon">{{ item.icon }}</text>
|
|
|
+ <text class="progress-label">{{ item.label }}</text>
|
|
|
+ <text class="progress-status" :class="item.done ? 'status-done' : 'status-pending'">{{ item.done ? '✔ 已完成' : '' }}</text>
|
|
|
+ <text class="progress-btn" v-if="!item.done" @click="handleProgressClick(item)">{{ item.actionText || '补录' }}</text>
|
|
|
</view>
|
|
|
- <view class="tracking-record" v-for="(rec, rIdx) in currentRecords" :key="getRecordKey(rec)">
|
|
|
- <text class="record-date">{{ rec._date || rec._text }}</text>
|
|
|
- <text class="record-content">{{ rec._content }}</text>
|
|
|
+ <view class="progress-footer">
|
|
|
+ <text class="footer-text">今日完成 {{ doneCount }}/{{ totalCount }}</text>
|
|
|
+ <text class="footer-streak" v-if="streakDays > 0">🔥 连续打卡 {{ streakDays }} 天</text>
|
|
|
</view>
|
|
|
- <view class="tracking-more" v-if="currentRecords.length > 0" @click="viewAllRecords">查看全部 ›</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
- </view>
|
|
|
- <view class="section">
|
|
|
- <view class="milestone-card">
|
|
|
- <text class="section-title-row">🏆 里程碑</text>
|
|
|
- <view class="milestone-grid">
|
|
|
- <view class="milestone-item">
|
|
|
- <text class="milestone-icon">🔥</text>
|
|
|
- <text class="milestone-value">{{ streakDays }}天</text>
|
|
|
- <text class="milestone-label">连续打卡</text>
|
|
|
- </view>
|
|
|
- <view class="milestone-item">
|
|
|
- <text class="milestone-icon">🎯</text>
|
|
|
- <text class="milestone-value">{{ activeChallenges }}</text>
|
|
|
- <text class="milestone-label">进行中挑战</text>
|
|
|
+ <view class="section">
|
|
|
+ <view class="tracking-card">
|
|
|
+ <view class="tracking-tabs">
|
|
|
+ <view class="tracking-tab" :class="activeTab === tab.key ? 'tab-active' : ''" v-for="tab in trackingTabs" :key="tab.key" @click="switchTab(tab.key)">
|
|
|
+ <text class="tab-text" :class="activeTab === tab.key ? 'tab-active-text' : ''">{{ tab.label }}</text>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
- <view class="milestone-item">
|
|
|
- <text class="milestone-icon">📈</text>
|
|
|
- <text class="milestone-value">{{ completeness }}%</text>
|
|
|
- <text class="milestone-label">档案完整度</text>
|
|
|
+ <view class="tracking-content">
|
|
|
+ <view class="tracking-empty" v-if="currentRecords.length === 0">
|
|
|
+ <text class="empty-icon">📊</text>
|
|
|
+ <text class="empty-text">还没有记录</text>
|
|
|
+ <text class="empty-hint">开始记录{{ activeTabLabel }}吧</text>
|
|
|
+ </view>
|
|
|
+ <view class="tracking-record" v-for="(rec, rIdx) in currentRecords" :key="getRecordKey(rec)">
|
|
|
+ <text class="record-date">{{ rec._date || rec._text }}</text>
|
|
|
+ <text class="record-content">{{ rec._content }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="tracking-more" v-if="currentRecords.length > 0" @click="viewAllRecords">查看全部 ›</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
- </view>
|
|
|
- <view class="section">
|
|
|
- <view class="survey-card">
|
|
|
- <text class="section-title-row">📋 定期调研</text>
|
|
|
- <view class="survey-pending" v-if="pendingSurvey">
|
|
|
- <text class="survey-alert">⚠ 本周还未完成调研</text>
|
|
|
- <text class="survey-title-text">{{ pendingSurvey.title }}</text>
|
|
|
- <text class="survey-btn" @click="startSurvey">[开始填写]</text>
|
|
|
+ <view class="section">
|
|
|
+ <view class="milestone-card">
|
|
|
+ <text class="section-title-row">🏆 里程碑</text>
|
|
|
+ <view class="milestone-grid">
|
|
|
+ <view class="milestone-item">
|
|
|
+ <text class="milestone-icon">🔥</text>
|
|
|
+ <text class="milestone-value">{{ streakDays }}天</text>
|
|
|
+ <text class="milestone-label">连续打卡</text>
|
|
|
+ </view>
|
|
|
+ <view class="milestone-item">
|
|
|
+ <text class="milestone-icon">🎯</text>
|
|
|
+ <text class="milestone-value">{{ activeChallenges }}</text>
|
|
|
+ <text class="milestone-label">进行中挑战</text>
|
|
|
+ </view>
|
|
|
+ <view class="milestone-item">
|
|
|
+ <text class="milestone-icon">📈</text>
|
|
|
+ <text class="milestone-value">{{ completeness }}%</text>
|
|
|
+ <text class="milestone-label">档案完整度</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
- <view class="survey-done" v-else-if="allDone"><text class="survey-done-text">✅ 所有调研已完成</text></view>
|
|
|
- <view class="survey-history-list" v-if="surveyHistory.length > 0">
|
|
|
- <text class="history-label">历史调研:</text>
|
|
|
- <view class="history-item" v-for="(h, hIdx) in surveyHistory" :key="getHistoryKey(h)">
|
|
|
- <text class="history-date">{{ h._date }}</text>
|
|
|
- <text class="history-content">{{ h._content }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="section">
|
|
|
+ <view class="survey-card">
|
|
|
+ <text class="section-title-row">📋 定期调研</text>
|
|
|
+ <view class="survey-pending" v-if="pendingSurvey">
|
|
|
+ <text class="survey-alert">⚠ 本周还未完成调研</text>
|
|
|
+ <text class="survey-title-text">{{ pendingSurvey.title }}</text>
|
|
|
+ <text class="survey-btn" @click="startSurvey">[开始填写]</text>
|
|
|
+ </view>
|
|
|
+ <view class="survey-done" v-else-if="allDone"><text class="survey-done-text">✅ 所有调研已完成</text></view>
|
|
|
+ <view class="survey-history-list" v-if="surveyHistory.length > 0">
|
|
|
+ <text class="history-label">历史调研:</text>
|
|
|
+ <view class="history-item" v-for="(h, hIdx) in surveyHistory" :key="getHistoryKey(h)">
|
|
|
+ <text class="history-date">{{ h._date }}</text>
|
|
|
+ <text class="history-content">{{ h._content }}</text>
|
|
|
+ </view>
|
|
|
+ <text class="survey-more" @click="viewAllHistory">查看全部调研 ›</text>
|
|
|
</view>
|
|
|
- <text class="survey-more" @click="viewAllHistory">查看全部调研 ›</text>
|
|
|
+ <view class="survey-empty" v-else-if="!pendingSurvey"><text class="survey-empty-text">暂无待完成调研</text></view>
|
|
|
</view>
|
|
|
- <view class="survey-empty" v-else-if="!pendingSurvey"><text class="survey-empty-text">暂无待完成调研</text></view>
|
|
|
</view>
|
|
|
+ <DimensionActivities :isLoggedIn="true" @activityClick="goActivityDetail" @moreActivities="goMoreActivities" style="margin-bottom:60rpx" />
|
|
|
</view>
|
|
|
- <DimensionActivities :isLoggedIn="true" @activityClick="goActivityDetail" @moreActivities="goMoreActivities" style="margin-bottom:60rpx" />
|
|
|
</view>
|
|
|
</template>
|
|
|
<script>
|
|
|
import PageBanner from '../../components/PageBanner.vue'
|
|
|
+import LoginGuideCard from '../../components/LoginGuideCard.vue'
|
|
|
import FamilyMemberStrip from '../../components/FamilyMemberStrip.vue'
|
|
|
import DimensionActivities from '../../components/DimensionActivities.vue'
|
|
|
import { healthCheckinList, getDietDailyRecords, getSurveyStatus, getSurveyHistory } from '../../utils/api.js'
|
|
|
export default {
|
|
|
- components: { PageBanner, FamilyMemberStrip, DimensionActivities },
|
|
|
- data() { return { memberId: '', todayDate: '', hasIncomplete: false, incompleteCount: 0, progressItems: [], activeTab: 'diet', trackingTabs: [{ key: 'diet', label: '饮食' }, { key: 'exercise', label: '运动' }, { key: 'sleep', label: '睡眠' }, { key: 'emotion', label: '心情' }, { key: 'survey', label: '调研' }], trackingData: { diet: [], exercise: [], sleep: [], emotion: [], survey: [] }, streakDays: 0, activeChallenges: 0, completeness: 0, pendingSurvey: null, allDone: false, surveyHistory: [] } },
|
|
|
+ components: { PageBanner, LoginGuideCard, FamilyMemberStrip, DimensionActivities },
|
|
|
+ data() { return { memberId: '', todayDate: '', hasIncomplete: false, incompleteCount: 0, progressItems: [], activeTab: 'diet', trackingTabs: [{ key: 'diet', label: '饮食' }, { key: 'exercise', label: '运动' }, { key: 'sleep', label: '睡眠' }, { key: 'emotion', label: '心情' }, { key: 'survey', label: '调研' }], trackingData: { diet: [], exercise: [], sleep: [], emotion: [], survey: [] }, streakDays: 0, activeChallenges: 0, completeness: 0, pendingSurvey: null, allDone: false, surveyHistory: [], isLoggedIn: false } },
|
|
|
computed: {
|
|
|
doneCount: function() { var c = 0; for (var i = 0; i < this.progressItems.length; i++) if (this.progressItems[i] && this.progressItems[i].done) c++; return c },
|
|
|
totalCount: function() { return this.progressItems.length },
|
|
|
activeTabLabel: function() { var m = { diet: '饮食', exercise: '运动', sleep: '睡眠', emotion: '心情', survey: '调研' }; return m[this.activeTab] || '' },
|
|
|
currentRecords: function() { return this.trackingData[this.activeTab] || [] }
|
|
|
},
|
|
|
- onLoad(options) {
|
|
|
- this.memberId = options.memberId || uni.getStorageSync('currentChildId') || ''
|
|
|
- var now = new Date()
|
|
|
- this.todayDate = now.getFullYear() + '-' + this.pad(now.getMonth() + 1) + '-' + this.pad(now.getDate())
|
|
|
- this.loadAllData()
|
|
|
- },
|
|
|
onShow() {
|
|
|
- if (!this.memberId) {
|
|
|
+ this.isLoggedIn = !!uni.getStorageSync('token')
|
|
|
+ if (this.isLoggedIn) {
|
|
|
this.memberId = uni.getStorageSync('currentChildId') || ''
|
|
|
+ var now = new Date()
|
|
|
+ this.todayDate = now.getFullYear() + '-' + this.pad(now.getMonth() + 1) + '-' + this.pad(now.getDate())
|
|
|
+ this.loadAllData()
|
|
|
}
|
|
|
- this.loadAllData()
|
|
|
},
|
|
|
methods: {
|
|
|
onMemberSelect: function(member) {
|
|
|
@@ -122,9 +190,9 @@ export default {
|
|
|
this.loadAllData()
|
|
|
},
|
|
|
pad: function(n) { return n < 10 ? '0' + n : '' + n },
|
|
|
- getProgressKey(item) { return item && item.label ? item.label : 'p-' + Math.random() },
|
|
|
- getRecordKey(rec) { return rec && rec._text ? rec._text + rec._content : 'r-' + Math.random() },
|
|
|
- getHistoryKey(h) { return h && h._date ? h._date + h._content : 'h-' + Math.random() },
|
|
|
+ getProgressKey: function(item) { return item && item.label ? item.label : 'p-' + Math.random() },
|
|
|
+ getRecordKey: function(rec) { return rec && rec._text ? rec._text + rec._content : 'r-' + Math.random() },
|
|
|
+ getHistoryKey: function(h) { return h && h._date ? h._date + h._content : 'h-' + Math.random() },
|
|
|
dismissReminder: function() { this.hasIncomplete = false },
|
|
|
switchTab: function(key) { this.activeTab = key; this.loadTrackingData(key) },
|
|
|
handleProgressClick: function(item) {
|
|
|
@@ -140,6 +208,7 @@ export default {
|
|
|
viewAllHistory: function() { uni.navigateTo({ url: '/pages/health/survey-history?memberId=' + this.memberId }) },
|
|
|
goActivityDetail: function(act) { uni.navigateTo({ url: '/pages/activity/activity-detail/activity-detail?id=' + act.id }) },
|
|
|
goMoreActivities: function() { uni.navigateTo({ url: '/pages/activity/index' }) },
|
|
|
+ goPage: function(url) { uni.navigateTo({ url: url }) },
|
|
|
loadTrackingData: function(key) { return this.trackingData[key] || [] },
|
|
|
async loadAllData() {
|
|
|
var self = this
|
|
|
@@ -150,7 +219,6 @@ export default {
|
|
|
async loadProgressItems() {
|
|
|
var self = this
|
|
|
var items = []
|
|
|
- // 今日打卡
|
|
|
var checkinDone = false
|
|
|
try {
|
|
|
var ciRes = await healthCheckinList({ yearMonth: self.todayDate.substring(0, 7) })
|
|
|
@@ -159,7 +227,6 @@ export default {
|
|
|
}
|
|
|
} catch(e) {}
|
|
|
items.push({ key: 'checkin', icon: '✅', label: '今日打卡', done: checkinDone, actionText: '打卡' })
|
|
|
- // 饮食
|
|
|
var dietDone = false
|
|
|
try {
|
|
|
var dRes = await getDietDailyRecords({ date: self.todayDate })
|
|
|
@@ -208,6 +275,30 @@ export default {
|
|
|
<style scoped>
|
|
|
.container{background:#FFF7ED;min-height:100vh;padding-bottom:120rpx}
|
|
|
.section{margin:16rpx 24rpx}
|
|
|
+/* 游客态 */
|
|
|
+.brand-strap{margin:0 24rpx 16rpx;background:linear-gradient(135deg,#F0FFF4,rgba(16,185,129,0.1));border-radius:16rpx;padding:20rpx 24rpx}
|
|
|
+.brand-strap-text{font-size:26rpx;color:#10B981;font-weight:600}
|
|
|
+.concept-card{margin:0 24rpx 16rpx;background:#fff;border-radius:20rpx;padding:28rpx;border-left:6rpx solid #10B981;box-shadow:0 2rpx 12rpx rgba(0,0,0,0.06)}
|
|
|
+.concept-quote{font-size:30rpx;font-weight:bold;color:#333;display:block;margin-bottom:8rpx}
|
|
|
+.concept-desc{font-size:24rpx;color:#888;display:block;line-height:1.6}
|
|
|
+.case-card{margin:0 24rpx 16rpx;background:#fff;border-radius:20rpx;padding:24rpx;box-shadow:0 2rpx 12rpx rgba(0,0,0,0.06)}
|
|
|
+.case-header{margin-bottom:12rpx}
|
|
|
+.case-label{font-size:24rpx;color:#10B981;font-weight:bold}
|
|
|
+.case-body{padding:8rpx 0}
|
|
|
+.case-name{font-size:26rpx;color:#333;font-weight:600;display:block;margin-bottom:6rpx}
|
|
|
+.case-change{font-size:24rpx;color:#666;display:block;margin-bottom:12rpx}
|
|
|
+.case-metrics{display:flex;gap:16rpx;margin-bottom:12rpx}
|
|
|
+.metric-item{display:flex;flex-direction:column;align-items:center;flex:1;background:#F0FFF4;border-radius:12rpx;padding:12rpx 8rpx}
|
|
|
+.metric-up{font-size:26rpx;font-weight:bold;color:#10B981}
|
|
|
+.metric-label{font-size:20rpx;color:#999;margin-top:4rpx}
|
|
|
+.case-note{font-size:22rpx;color:#888;display:block}
|
|
|
+.guest-features{display:flex;flex-wrap:wrap;margin:0 24rpx 16rpx}
|
|
|
+.feat-card{width:calc(50% - 8rpx);background:#fff;border-radius:20rpx;padding:24rpx;margin-bottom:16rpx;margin-right:16rpx;box-shadow:0 2rpx 12rpx rgba(0,0,0,0.06)}
|
|
|
+.feat-icon{font-size:40rpx;display:block;margin-bottom:8rpx}
|
|
|
+.feat-title{font-size:26rpx;font-weight:600;color:#333;display:block;margin-bottom:4rpx}
|
|
|
+.feat-desc{font-size:22rpx;color:#999;display:block}
|
|
|
+.login-guide-section{margin:0 24rpx 24rpx}
|
|
|
+/* 登录后原有样式 */
|
|
|
.reminder-bar{margin:12rpx 24rpx;background:#FFF3E0;border:1rpx solid #FFB74D;border-radius:12rpx;padding:14rpx 20rpx;display:flex;align-items:center;justify-content:space-between}
|
|
|
.reminder-text{font-size:24rpx;color:#E65100}
|
|
|
.reminder-close{font-size:28rpx;color:#999}
|