From dea55bec155e83c34323011a6719c023f38cc9bb Mon Sep 17 00:00:00 2001 From: Cxx0822 <1556464090@qq.com> Date: Sat, 5 Sep 2026 16:19:56 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E4=BC=9A=E5=91=98?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Profile/ProfilePopup.vue | 5 ++- src/pages/user/index.vue | 32 +++++++++++++++++ src/store/profile.ts | 12 +++++-- src/type/profile.ts | 7 ++++ src/utils/index.ts | 4 +++ src/utils/profile.ts | 1 + src/utils/report.ts | 48 ++++++++++++++++--------- 7 files changed, 88 insertions(+), 21 deletions(-) diff --git a/src/components/Profile/ProfilePopup.vue b/src/components/Profile/ProfilePopup.vue index 768c4ba..5b33c40 100644 --- a/src/components/Profile/ProfilePopup.vue +++ b/src/components/Profile/ProfilePopup.vue @@ -10,8 +10,11 @@ 编辑个人信息 + + {{ profileStore.profileForm.id }} + - + diff --git a/src/pages/user/index.vue b/src/pages/user/index.vue index 6234315..af87c11 100644 --- a/src/pages/user/index.vue +++ b/src/pages/user/index.vue @@ -40,6 +40,15 @@ + + + + ⭐ 当前会员:{{ profileStore.user.level }} + 📅 到期日期:{{ profileStore.user.expiry_date }} + + + + @@ -91,6 +100,7 @@ import { getTheme, getThemePrimaryColor } from "@/utils/themes"; import ThemePopup from "@/components/Profile/ThemePopup.vue"; import AboutPopup from "@/components/Profile/AboutPopup.vue"; import { share } from '@/utils/share' +import { generateUniqueId } from "@/utils"; const { onShareAppMessage } = share() onShareAppMessage() @@ -128,12 +138,30 @@ onShow(() => { appStore.points = getPoints() appStore.recordCount = getRecordCount() appStore.achievement = getAchievement() + getUser() }) watch(() => profileStore.isRefresh, () => { profile.value = getProfile() }) +function getUser() { + const profile = getProfile() + if (profile.id) { + wx.request({ + url: `https://cxx0822.s.3q.hair/health-api/user/${profile.id}`, + method: 'GET', + header: { 'Content-Type': 'application/json' }, + success: (res) => { + profileStore.user = res.data + }, + fail: (err) => { + console.error('请求失败:', err); + } + }); + } +} + function openProfile() { Object.assign(profileStore.profileForm, profile.value) if (!profileStore.profileForm.height) { @@ -144,6 +172,10 @@ function openProfile() { profileStore.profileForm.birthYear = 1990 } + if (!profileStore.profileForm.id) { + profileStore.profileForm.id = generateUniqueId() + } + profileStore.showProfilePopup = true } diff --git a/src/store/profile.ts b/src/store/profile.ts index f64bdcb..1024739 100644 --- a/src/store/profile.ts +++ b/src/store/profile.ts @@ -1,7 +1,7 @@ import { defineStore } from 'pinia' -import type {IDefaultRecordValue, IProfile} from "@/type/profile"; -import type {IStats} from "@/type"; -import {countRecordsByMonth, getMaxRecord, getTotalCount} from "@/utils/record"; +import type { IDefaultRecordValue, IProfile, IUser } from "@/type/profile"; +import type { IStats } from "@/type"; +import { countRecordsByMonth, getMaxRecord, getTotalCount } from "@/utils/record"; export const useProfileStore = defineStore('profile', { state: () => ({ @@ -15,6 +15,7 @@ export const useProfileStore = defineStore('profile', { showThemePopup: false, showAboutPopup: false, profileForm: { + id: '', gender: '', height: 170, birthYear: 1990, @@ -23,6 +24,11 @@ export const useProfileStore = defineStore('profile', { goal: '', name: '', } as IProfile, + user: { + id: '', + level: '普通用户', + expiry_date: '无' + } as IUser, themeColor: 'purple', defaultRecordValueForm: { weightValue: 70, diff --git a/src/type/profile.ts b/src/type/profile.ts index 03c3033..3a634bc 100644 --- a/src/type/profile.ts +++ b/src/type/profile.ts @@ -1,4 +1,5 @@ export interface IProfile { + id: string name: string gender: 'male' | 'female' | '' birthYear: number | null @@ -11,6 +12,12 @@ export interface IProfile { goal: string } +export interface IUser { + id: string + level: string + expiry_date: string +} + export interface IOverview { type: string icon: string diff --git a/src/utils/index.ts b/src/utils/index.ts index ee64f08..4c49e79 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -5,3 +5,7 @@ export function formatDateTime(ts: string | number) { const pad = (n: number) => String(n).padStart(2, '0') return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}` } + +export function generateUniqueId() { + return Date.now() + '-' + Math.random().toString(36).slice(2, 6); +} \ No newline at end of file diff --git a/src/utils/profile.ts b/src/utils/profile.ts index 19566eb..d3bdb8f 100644 --- a/src/utils/profile.ts +++ b/src/utils/profile.ts @@ -9,6 +9,7 @@ const DEFAULT_RECORD_VALUE_KEY = 'user_default_record_value' function getEmptyProfile(): IProfile { return { + id: '', gender: '', height: null, birthYear: null, diff --git a/src/utils/report.ts b/src/utils/report.ts index 585d37f..2dbf7db 100644 --- a/src/utils/report.ts +++ b/src/utils/report.ts @@ -155,9 +155,31 @@ function calcSportReport(records: IHealthRecord[]): SportReport | null { const filtered = records.filter(r => r.type === 'sport') as SportRecord[] if (filtered.length === 0) return null const sorted = sortByTime(filtered) - const durations = sorted.map(r => r.duration) - const totalDur = durations.reduce((s, v) => s + v, 0) + // 按天去重,统计运动天数 + const sportDaySet = new Set() + sorted.forEach(r => { + sportDaySet.add(getDateStringFromTimestamp(r.timestamp)) + }) + const sportDays = sportDaySet.size + + // 按天聚合时长 + const dayDurationMap: Record = {} + sorted.forEach(r => { + const day = getDateStringFromTimestamp(r.timestamp) + dayDurationMap[day] = (dayDurationMap[day] || 0) + r.duration + }) + const dailyDurations = Object.values(dayDurationMap) + const totalDur = dailyDurations.reduce((s, v) => s + v, 0) + + // 所有记录涉及的天数(去重) + const allDaySet = new Set() + records.forEach(r => { + allDaySet.add(getDateStringFromTimestamp(r.timestamp)) + }) + const totalDays = allDaySet.size + + // 分类分布(按记录条数,这个没问题) const categoryDist: Record = {} sorted.forEach(r => { const cat = r.category || '其他' @@ -165,19 +187,11 @@ function calcSportReport(records: IHealthRecord[]): SportReport | null { }) // 最长连续不运动天数 - const allDates = new Set() - records.forEach(r => { - allDates.add(getDateStringFromTimestamp(r.timestamp)) - }) - const sportDates = new Set() - sorted.forEach(r => { - sportDates.add(getDateStringFromTimestamp(r.timestamp)) - }) - const sortedDates = [...allDates].sort((a, b) => new Date(a).getTime() - new Date(b).getTime()) + const sortedDates = [...allDaySet].sort((a, b) => new Date(a).getTime() - new Date(b).getTime()) let maxNoSport = 0 let currentNoSport = 0 sortedDates.forEach(date => { - if (sportDates.has(date)) { + if (sportDaySet.has(date)) { currentNoSport = 0 } else { currentNoSport++ @@ -186,12 +200,12 @@ function calcSportReport(records: IHealthRecord[]): SportReport | null { }) return { - totalRecords: records.length, - sportDays: sorted.length, - frequency: percent(sorted.length, records.length), + totalRecords: filtered.length, + sportDays: sportDays, + frequency: percent(sportDays, totalDays), totalDuration: totalDur, - avgDailyDuration: round1(totalDur / records.length), - maxSingleDuration: max(durations), + avgDailyDuration: round1(totalDur / sportDays), + maxSingleDuration: max(sorted.map(r => r.duration)), categoryDistribution: categoryDist, maxConsecutiveNoSport: maxNoSport, }