|
|
@@ -224,6 +224,27 @@
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
+ <!-- 成长历史记录(按日期分组,变更B) -->
|
|
|
+ <view class="section history-group-section">
|
|
|
+ <view class="section-header">
|
|
|
+ <text class="section-title">📅 成长历史记录</text>
|
|
|
+ </view>
|
|
|
+ <view v-if="dateGroups.length === 0" class="history-empty">
|
|
|
+ <text class="history-empty-text">暂无记录,去记录第一笔成长吧</text>
|
|
|
+ </view>
|
|
|
+ <view v-else class="history-group-list">
|
|
|
+ <view class="history-group" v-for="group in dateGroups" :key="group.date">
|
|
|
+ <view class="history-group-title">
|
|
|
+ <text class="history-group-date">{{ group.date }}</text>
|
|
|
+ <text class="history-group-count">{{ group.records.length }} 条</text>
|
|
|
+ </view>
|
|
|
+ <view class="history-group-item" v-for="(rec, idx) in group.records" :key="getHistoryItemKey(group.date, idx)">
|
|
|
+ <text class="history-group-item-title">{{ getHistoryItemTitle(rec) }}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
<DimensionActivities :isLoggedIn="true" @activityClick="goActivityDetail" @moreActivities="goMoreActivities" style="margin-bottom:60rpx" />
|
|
|
|
|
|
<!-- 推荐阅读 -->
|
|
|
@@ -254,7 +275,7 @@ import LoginGuideCard from '../../components/LoginGuideCard.vue'
|
|
|
import DimensionActivities from '../../components/DimensionActivities.vue'
|
|
|
import DimensionProducts from '../../components/DimensionProducts.vue'
|
|
|
import ArticleFeed from '../../components/ArticleFeed.vue'
|
|
|
-import { healthCheckinList, getHealthMealList, getGrowthRecommendations, getExerciseRecords, getSleepRecords, getEmotionCheckinList, getCheckinList, getTodayTasks, reportAppEnter } from '../../utils/api.js'
|
|
|
+import { healthCheckinList, getHealthMealList, getGrowthRecommendations, getExerciseRecords, getSleepRecords, getEmotionCheckinList, getCheckinList, getTodayTasks, reportAppEnter, getGrowthRecordList } from '../../utils/api.js'
|
|
|
import { parseDate, recordDateOf } from '../../utils/format.js'
|
|
|
import fontScaleMixin from '@/mixins/font-scale.js'
|
|
|
import shareMixin from '../../components/share-mixin.js'
|
|
|
@@ -264,6 +285,7 @@ export default {
|
|
|
data() { return { memberId: '', todayDate: '', hasIncomplete: false, incompleteCount: 0, progressItems: [], todayTaskList: [], streakDays: 0, activeChallenges: 0, completeness: 0,
|
|
|
healthArticles: [],
|
|
|
growthArticles: [],
|
|
|
+ dateGroups: [],
|
|
|
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 },
|
|
|
@@ -359,6 +381,75 @@ export default {
|
|
|
if (!d) return ''
|
|
|
return this.pad(d.getMonth() + 1) + '-' + this.pad(d.getDate())
|
|
|
},
|
|
|
+ /** 按日期分组(变更B):把打卡/档案记录流按业务日期 recordDate>startTime>sleepTime>checkinDate>createdAt
|
|
|
+ * 分组合并——同日合并、分组键 yyyy-MM-dd、多日倒序(新在前)、无有效日期的记录平稳跳过 */
|
|
|
+ groupByDate: function(records) {
|
|
|
+ var list = Array.isArray(records) ? records : []
|
|
|
+ var map = {}
|
|
|
+ var order = []
|
|
|
+ for (var i = 0; i < list.length; i++) {
|
|
|
+ var r = list[i]
|
|
|
+ if (!r) continue
|
|
|
+ var key = this.recordDateKeyOf(r)
|
|
|
+ if (!key) continue
|
|
|
+ if (!map[key]) {
|
|
|
+ map[key] = { date: key, records: [] }
|
|
|
+ order.push(key)
|
|
|
+ }
|
|
|
+ map[key].records.push(r)
|
|
|
+ }
|
|
|
+ order.sort(function(a, b) { return a < b ? 1 : -1 })
|
|
|
+ var groups = []
|
|
|
+ for (var j = 0; j < order.length; j++) {
|
|
|
+ groups.push(map[order[j]])
|
|
|
+ }
|
|
|
+ return groups
|
|
|
+ },
|
|
|
+ getHistoryItemKey: function(dateKey, idx) { return dateKey + '-' + idx },
|
|
|
+ getHistoryItemTitle: function(rec) {
|
|
|
+ if (!rec) return ''
|
|
|
+ var t = rec.recordTitle || rec.name || rec.note || rec.mood || rec.content || ''
|
|
|
+ if (t) return t
|
|
|
+ if (rec.type) return rec.type
|
|
|
+ return '记录'
|
|
|
+ },
|
|
|
+ /** 单条记录的业务日期键(分组口径):recordDate>startTime>sleepTime>checkinDate,
|
|
|
+ * 无效业务日期(空串/无法解析,如手填 'not-a-date')视为无日期记录予以跳过;不取 createdAt 兜底 */
|
|
|
+ recordDateKeyOf: function(rec) {
|
|
|
+ if (!rec) return ''
|
|
|
+ var fields = ['recordDate', 'startTime', 'sleepTime', 'checkinDate']
|
|
|
+ for (var i = 0; i < fields.length; i++) {
|
|
|
+ var key = fields[i]
|
|
|
+ var v = rec[key]
|
|
|
+ if (v === null || v === undefined || v === '') continue
|
|
|
+ var d = parseDate(v)
|
|
|
+ if (!d) continue
|
|
|
+ return d.getFullYear() + '-' + this.pad(d.getMonth() + 1) + '-' + this.pad(d.getDate())
|
|
|
+ }
|
|
|
+ return ''
|
|
|
+ },
|
|
|
+ /** 成长历史记录加载:档案列表与全部打卡记录汇成一条记录流,按日期分组存入 dateGroups */
|
|
|
+ async loadGrowthHistoryGroups() {
|
|
|
+ var self = this
|
|
|
+ var streams = []
|
|
|
+ try {
|
|
|
+ var gRes = await getGrowthRecordList()
|
|
|
+ if (gRes && gRes.code === 200 && Array.isArray(gRes.data)) streams = streams.concat(gRes.data)
|
|
|
+ } catch(e) {}
|
|
|
+ var checkinStreams = [
|
|
|
+ getHealthMealList(self.memberId ? { memberId: self.memberId } : {}),
|
|
|
+ getExerciseRecords(self.memberId ? { memberId: self.memberId } : {}),
|
|
|
+ getSleepRecords(self.memberId ? { memberId: self.memberId } : {}),
|
|
|
+ getEmotionCheckinList({ page: 1, size: 50, memberId: self.memberId || undefined }),
|
|
|
+ getCheckinList(self.memberId ? { memberId: self.memberId } : {})
|
|
|
+ ]
|
|
|
+ var results = await Promise.all(checkinStreams.map(function(p) { return p.catch(function() { return null }) }))
|
|
|
+ for (var i = 0; i < results.length; i++) {
|
|
|
+ var res = results[i]
|
|
|
+ if (res && res.code === 200 && Array.isArray(res.data)) streams = streams.concat(res.data)
|
|
|
+ }
|
|
|
+ self.dateGroups = self.groupByDate(streams)
|
|
|
+ },
|
|
|
mealTypeLabel: function(type) {
|
|
|
var m = { breakfast: '早餐', lunch: '午餐', dinner: '晚餐', snack: '加餐', extra: '零食' }
|
|
|
if (!type) return '饮食'
|
|
|
@@ -377,6 +468,7 @@ export default {
|
|
|
try { await self.loadProgressItems() } catch(e) {}
|
|
|
try { await self.loadGrowthRecommendations() } catch(e) {}
|
|
|
try { await self.loadMilestones() } catch(e) {}
|
|
|
+ try { await self.loadGrowthHistoryGroups() } catch(e) {}
|
|
|
},
|
|
|
async loadProgressItems() {
|
|
|
var self = this
|