Kaynağa Gözat

feat(growth-main): 成长页运动/睡眠/心情记录真实化 + 修复饮食记录判断bug

E2E Test Bot 1 ay önce
ebeveyn
işleme
c061e0dd96

+ 83 - 6
cfc-frontend/pages/growth-main/index.vue

@@ -289,7 +289,8 @@
 import PageBanner from '../../components/PageBanner.vue'
 import LoginGuideCard from '../../components/LoginGuideCard.vue'
 import DimensionActivities from '../../components/DimensionActivities.vue'
-import { healthCheckinList, getDietDailyRecords, getSurveyStatus, getSurveyHistory, getFoodCautionList, getGrowthRecommendations } from '../../utils/api.js'
+import { healthCheckinList, getDietDailyRecords, getSurveyStatus, getSurveyHistory, getFoodCautionList, getGrowthRecommendations, getExerciseRecords, getSleepRecords, getEmotionCheckinList } from '../../utils/api.js'
+import { parseDate } from '../../utils/format.js'
 export default {
   components: { PageBanner, LoginGuideCard, 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: '心情' }], trackingData: { diet: [], exercise: [], sleep: [], emotion: [] }, streakDays: 0, activeChallenges: 0, completeness: 0, cautionCount: 0,
@@ -316,7 +317,7 @@ export default {
   methods: {
     pad: function(n) { return n < 10 ? '0' + n : '' + n },
     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() },
+    getRecordKey: function(rec) { return rec && (rec._id || rec.id) ? 'r' + (rec._id || rec.id) : (rec && rec._text ? rec._text + rec._content : 'r-' + Math.random()) },
     getHistoryKey: function(h) { return h && h._date ? h._date + h._content : 'h-' + Math.random() },
     getArticleKey: function(item) { return item && item.id ? 'a' + item.id : 'a-' + Math.random() },
     getProductKey: function(item) { return item && item.id ? 'p' + item.id : 'p-' + Math.random() },
@@ -343,6 +344,22 @@ export default {
     goToProduct: function(id) { uni.navigateTo({ url: '/pages/shop/detail/detail?id=' + id }) },
     goPage: function(url) { uni.navigateTo({ url: url }) },
     loadTrackingData: function(key) { return this.trackingData[key] || [] },
+    formatDateKey: function(dateVal) {
+      if (!dateVal) return ''
+      var d = parseDate(dateVal)
+      if (!d) return ''
+      return d.getFullYear() + '-' + this.pad(d.getMonth() + 1) + '-' + this.pad(d.getDate())
+    },
+    formatRecordDate: function(dateVal) {
+      if (!dateVal) return ''
+      var d = parseDate(dateVal)
+      if (!d) return ''
+      return this.pad(d.getMonth() + 1) + '-' + this.pad(d.getDate())
+    },
+    mealTypeLabel: function(type) {
+      var m = { breakfast: '早餐', lunch: '午餐', dinner: '晚餐', snack: '加餐', extra: '零食' }
+      return m[type] || type || '饮食'
+    },
     async loadAllData() {
       var self = this
       try { await self.loadProgressItems() } catch(e) {}
@@ -358,18 +375,78 @@ export default {
       try {
         var ciRes = await healthCheckinList({ yearMonth: self.todayDate.substring(0, 7) })
         if (ciRes.code === 200 && ciRes.data) {
-          for (var ci = 0; ci < ciRes.data.length; ci++) { if (ciRes.data[ci] && ciRes.data[ci].checkinDate && ciRes.data[ci].checkinDate.substring(0, 10) === self.todayDate) { checkinDone = true; break } }
+          for (var ci = 0; ci < ciRes.data.length; ci++) { if (ciRes.data[ci] && self.formatDateKey(ciRes.data[ci].checkinDate) === self.todayDate) { checkinDone = true; break } }
         }
       } catch(e) {}
       items.push({ key: 'checkin', icon: '✅', label: '今日打卡', done: checkinDone, actionText: '打卡' })
+      // 饮食记录:daily 接口返回 {date, records, ...},records 才是数组
       var dietDone = false
       try {
         var dRes = await getDietDailyRecords({ date: self.todayDate })
-        if (dRes.code === 200 && dRes.data && dRes.data.length > 0) dietDone = true
+        var dRecords = (dRes.code === 200 && dRes.data && dRes.data.records) ? dRes.data.records : []
+        if (dRecords.length > 0) dietDone = true
+        self.trackingData.diet = []
+        for (var di = 0; di < dRecords.length; di++) {
+          var dr = dRecords[di]
+          if (!dr) continue
+          var foods = dr.userConfirmedFoods || dr.aiRecognizedFoods || dr.notes || ''
+          self.trackingData.diet.push({ _id: dr.id, _date: self.mealTypeLabel(dr.mealType), _content: foods || '已记录' })
+        }
       } catch(e) {}
       items.push({ key: 'diet', icon: '🍽️', label: '饮食记录', done: dietDone, actionText: '补录' })
-      items.push({ key: 'exercise', icon: '🏃', label: '运动记录', done: false, actionText: '记录' })
-      items.push({ key: 'emotion', icon: '😊', label: '心情记录', done: false, actionText: '记录' })
+      // 运动记录
+      var exerciseDone = false
+      try {
+        var eParams = self.memberId ? { memberId: self.memberId } : {}
+        var eRes = await getExerciseRecords(eParams)
+        var eList = (eRes.code === 200 && eRes.data) ? eRes.data : []
+        self.trackingData.exercise = []
+        for (var ei = 0; ei < eList.length; ei++) {
+          var er = eList[ei]
+          if (!er) continue
+          if (self.formatDateKey(er.startTime || er.createdAt) === self.todayDate) exerciseDone = true
+          if (ei < 5) {
+            var eText = (er.exerciseType || '运动') + (er.durationMinutes ? ' ' + er.durationMinutes + '分钟' : '') + (er.caloriesBurned ? ' · ' + er.caloriesBurned + '千卡' : '')
+            self.trackingData.exercise.push({ _id: er.id, _date: self.formatRecordDate(er.startTime || er.createdAt), _content: eText })
+          }
+        }
+      } catch(e) {}
+      items.push({ key: 'exercise', icon: '🏃', label: '运动记录', done: exerciseDone, actionText: '记录' })
+      // 睡眠记录
+      var sleepDone = false
+      try {
+        var sParams = self.memberId ? { memberId: self.memberId } : {}
+        var sRes = await getSleepRecords(sParams)
+        var sList = (sRes.code === 200 && sRes.data) ? sRes.data : []
+        self.trackingData.sleep = []
+        for (var si = 0; si < sList.length; si++) {
+          var sr = sList[si]
+          if (!sr) continue
+          if (self.formatDateKey(sr.sleepTime || sr.createdAt) === self.todayDate) sleepDone = true
+          if (si < 5) {
+            var sText = (sr.durationMinutes ? sr.durationMinutes + '分钟' : '') + (sr.qualityScore ? ' · 质量' + sr.qualityScore + '分' : '')
+            self.trackingData.sleep.push({ _id: sr.id, _date: self.formatRecordDate(sr.sleepTime || sr.createdAt), _content: sText || '已记录' })
+          }
+        }
+      } catch(e) {}
+      items.push({ key: 'sleep', icon: '😴', label: '睡眠记录', done: sleepDone, actionText: '记录' })
+      // 心情记录:mind/checkin/list 返回 List<EmotionCheckinVO>
+      var emotionDone = false
+      try {
+        var mParams = { page: 1, size: 10 }
+        if (self.memberId) mParams.memberId = self.memberId
+        var mRes = await getEmotionCheckinList(mParams)
+        var mList = (mRes.code === 200 && mRes.data) ? mRes.data : []
+        self.trackingData.emotion = []
+        for (var mi = 0; mi < mList.length; mi++) {
+          var mr = mList[mi]
+          if (!mr) continue
+          if (self.formatDateKey(mr.checkinDate || mr.createdAt) === self.todayDate) emotionDone = true
+          var mText = (mr.moodWeather || '') + (mr.moodScore ? ' · ' + mr.moodScore + '分' : '')
+          self.trackingData.emotion.push({ _id: mr.id, _date: self.formatRecordDate(mr.checkinDate || mr.createdAt), _content: mText || (mr.note || '已记录') })
+        }
+      } catch(e) {}
+      items.push({ key: 'emotion', icon: '😊', label: '心情记录', done: emotionDone, actionText: '记录' })
       items.push({ key: 'survey', icon: '📋', label: '本周调研', done: self.allDone, actionText: self.allDone ? '' : '开始' })
       self.progressItems = items.filter(function(it) { return it && it.key })
       self.incompleteCount = 0

+ 9 - 0
cfc-frontend/utils/api.js

@@ -2629,6 +2629,15 @@ export const getDietWeeklyTrend = (data) => request('/api/diet/record/weekly', '
 export const getMealConfig = (data) => request('/api/diet/meals/config', 'POST', data)
 export const saveMealConfig = (data) => request('/api/diet/meals/config/save', 'POST', data)
 
+// ===== 运动/睡眠/心情 日常记录 =====
+export const getExerciseRecords = (data) => request('/api/health/exercise/list', 'POST', data)
+export const createExerciseRecord = (data) => request('/api/health/exercise/create', 'POST', data)
+export const getSleepRecords = (data) => request('/api/health/sleep/list', 'POST', data)
+export const createSleepRecord = (data) => request('/api/health/sleep/create', 'POST', data)
+export const getEmotionCheckinList = (data) => request('/api/mind/checkin/list', 'POST', data)
+export const getEmotionCheckinLatest = (data) => request('/api/mind/checkin/latest', 'POST', data)
+export const createEmotionCheckin = (data) => request('/api/mind/checkin/create', 'POST', data)
+
 // ===== 北京菌群报告 — 菌群功能分析 =====
 export const getGutFloraAnalysis = (reportId) => request('/api/nutrition/beijing/analysis', 'POST', { reportId })