From 1592d5adaaee59395888a4972c73a031e8163f7e Mon Sep 17 00:00:00 2001 From: Cxx0822 <1556464090@qq.com> Date: Tue, 1 Sep 2026 15:56:34 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E7=BB=9F=E8=AE=A1?= =?UTF-8?q?=E5=8D=A1=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Stats/SummaryCard.vue | 38 +++++++++ src/pages/index/index.vue | 70 +++++++++++++--- src/pages/stats/index.vue | 114 +++++++++++++++++++++++++++ src/store/app.ts | 2 +- src/type/profile.ts | 4 +- src/utils/record.ts | 5 ++ 6 files changed, 222 insertions(+), 11 deletions(-) create mode 100644 src/components/Stats/SummaryCard.vue diff --git a/src/components/Stats/SummaryCard.vue b/src/components/Stats/SummaryCard.vue new file mode 100644 index 0000000..3a81aed --- /dev/null +++ b/src/components/Stats/SummaryCard.vue @@ -0,0 +1,38 @@ + + + diff --git a/src/pages/index/index.vue b/src/pages/index/index.vue index 864db6c..061d213 100644 --- a/src/pages/index/index.vue +++ b/src/pages/index/index.vue @@ -36,9 +36,16 @@ @click="addClick(item.type)"/> - - {{ item.value }} - + + + {{ item.value }} + + + + {{ item.trend }} + + {{ item.sub }} @@ -122,7 +129,7 @@ import TemperaturePopup from "@/components/Home/TemperaturePopup.vue"; import { ref, computed, watch } from 'vue' import { useRecordStore } from '@/store/record' import { useAppStore } from '@/store/app' -import { getDateStr, getDayRecords, getTimeStr } from '@/utils/record' +import { getDateStr, getDayRecords, getRecentRecords, getTimeStr } from '@/utils/record' import type { IHealthRecord } from "@/type/record"; import { onShow } from "@dcloudio/uni-app"; import { getOverview, getPlan, getProfile } from "@/utils/profile"; @@ -231,20 +238,46 @@ const overviewItems = computed(() => { overviewList.forEach((item) => { if (item === 'weight') { const profile = getProfile() + const recent = getRecentRecords('weight') + let trendValue = 0 + let isTrend = false + if (recent.length > 1 && weight) { + const prev = recent[recent.length - 2].value + const curr = weight.value + trendValue = curr - prev + isTrend = true + } + + const absTrend = Math.abs(trendValue).toFixed(1) result.push({ type: 'weight', icon: '⚖️', label: '体重', value: weight ? `${weight.value} kg` : '未记录', + trend: isTrend ? trendValue > 0 ? `▲${absTrend} kg` : `▼${absTrend} kg` : '', + trendColor: `${trendValue > 0 ? 'red' : 'green'}`, sub: weight ? profile.height ? `BMI:${(weight.value / Math.pow(profile.height / 100, 2)).toFixed(2)} ` : '身高未知' : '', }) } else if (item === 'sleep') { + const recent = getRecentRecords('sleep') + let trendValue = 0 + let isTrend = false + if (recent.length > 1 && sleep) { + const prev = recent[recent.length - 2].duration + const curr = sleep.duration + trendValue = curr - prev + isTrend = true + } + + const absTrend = Math.abs(trendValue).toFixed(1) result.push({ type: 'sleep', icon: '😴', label: '睡眠', - value: sleep ? `${sleep.duration}h` : '未记录', + value: sleep ? `${sleep.duration} h` : '未记录', + trend: isTrend ? trendValue > 0 ? `▲${absTrend} h` : `▼${absTrend} h` : '', + trendColor: `${trendValue > 0 ? 'green' : 'red'}`, sub: sleep ? `${getTimeStr(sleep.startTime)} ~ ${getTimeStr(sleep.endTime)}` : '', }) } @@ -253,7 +286,9 @@ const overviewItems = computed(() => { type: 'sport', icon: '🏃', label: '运动', - value: sportTotal > 0 ? `${sportTotal}min` : '未记录', + value: sportTotal > 0 ? `${sportTotal} min` : '未记录', + trend: '', + trendColor: '', sub: sportTotal >= 30 ? '已达标' : '目标 30min', }) } @@ -263,6 +298,8 @@ const overviewItems = computed(() => { icon: '❤️', label: '血压', value: bloodPressure ? `${bloodPressure.systolic}/${bloodPressure.diastolic}` : '未记录', + trend: '', + trendColor: '', sub: bloodPressure ? `心率:${bloodPressure.heartRate}` : '', }) } @@ -271,16 +308,32 @@ const overviewItems = computed(() => { type: 'bloodSugar', icon: '🩸', label: '血糖', - value: bloodSugar ? `${bloodSugar.value}mmol/L` : '未记录', + value: bloodSugar ? `${bloodSugar.value} mmol/L` : '未记录', + trend: '', + trendColor: '', sub: bloodSugar ? `${bloodSugar.period}` : '', }) } else if (item === 'temperature') { + const recent = getRecentRecords('temperature') + let trendValue = 0 + let isTrend = false + if (recent.length > 1 && temperature) { + const prev = recent[recent.length - 2].value + const curr = temperature.value + trendValue = curr - prev + isTrend = true + } + + const absTrend = Math.abs(trendValue).toFixed(1) + result.push({ type: 'temperature', icon: '🌡️', label: '体温', - value: temperature ? `${temperature.value}℃` : '未记录', + value: temperature ? `${temperature.value} ℃` : '未记录', + trend: isTrend ? trendValue > 0 ? `▲${absTrend} ℃` : `▼${absTrend} ℃` : '', + trendColor: `${trendValue > 0 ? 'red' : 'green'}`, sub: temperature ? `${temperature.location}` : '', }) } @@ -303,7 +356,6 @@ const plans = computed(() => { const hasBloodSugar = list.some(r => r.type === 'bloodSugar') const hasTemperature = list.some(r => r.type === 'temperature') - const planList = getPlan() const result = [] as IPlan[] diff --git a/src/pages/stats/index.vue b/src/pages/stats/index.vue index 8caf543..9217785 100644 --- a/src/pages/stats/index.vue +++ b/src/pages/stats/index.vue @@ -17,6 +17,9 @@ /> + + @@ -28,6 +31,9 @@ /> + + @@ -39,6 +45,9 @@ /> + + @@ -50,6 +59,11 @@ /> + + + @@ -61,6 +75,9 @@ /> + + @@ -72,6 +89,9 @@ /> + + @@ -83,7 +103,12 @@ /> + + + + @@ -95,6 +120,7 @@ import { useProfileStore } from '@/store/profile' import { useAppStore } from '@/store/app' import { getStats } from "@/utils/profile"; import { getThemeStyle } from "@/utils/themes"; +import SummaryCard from "@/components/Stats/SummaryCard.vue"; const profileStore = useProfileStore() const appStore = useAppStore() @@ -103,30 +129,62 @@ const weightChartData = ref({ categories: [] as string[], series: [] as any[] }) +const maxWeight = ref(0) +const minWeight = ref(0) +const avgWeight = ref(0) + const sleepChartData = ref({ categories: [] as string[], series: [] as any[] }) +const maxSleep = ref(0) +const minSleep = ref(0) +const avgSleep = ref(0) + const sportChartData = ref({ categories: [] as string[], series: [] as any[] }) + +const maxSport = ref(0) +const minSport = ref(0) +const avgSport = ref(0) + const bloodPressureChartData = ref({ categories: [] as string[], series: [] as any[] }) +const maxSystolic = ref(0) +const minSystolic = ref(0) +const avgSystolic = ref(0) + +const maxDiastolic = ref(0) +const minDiastolic = ref(0) +const avgDiastolic = ref(0) + const heartRateChartData = ref({ categories: [] as string[], series: [] as any[] }) +const maxHeartRate = ref(0) +const minHeartRate = ref(0) +const avgHeartRate = ref(0) + const bloodSugarChartData = ref({ categories: [] as string[], series: [] as any[] }) +const maxBloodSugar = ref(0) +const minBloodSugar = ref(0) +const avgBloodSugar = ref(0) + const temperatureChartData = ref({ categories: [] as string[], series: [] as any[] }) +const maxTemperature = ref(0) +const minTemperature = ref(0) +const avgTemperature = ref(0) // 图表配置 const chartOpts = { @@ -162,6 +220,13 @@ function getWeightChartData() { }, ], } + + const data = weightChartData.value.series[0].data + const sum = data.reduce((a:number, b:number) => a + b, 0) + + avgWeight.value = sum / data.length + maxWeight.value = Math.max(...data) + minWeight.value = Math.min(...data) } else { weightChartData.value = { categories: [], series: [] @@ -184,6 +249,13 @@ function getSleepChartData() { }, ], } + + const data = sleepChartData.value.series[0].data + const sum = data.reduce((a:number, b:number) => a + b, 0) + + avgSleep.value = sum / data.length + maxSleep.value = Math.max(...data) + minSleep.value = Math.min(...data) } else { sleepChartData.value = { categories: [], series: [] @@ -206,6 +278,13 @@ function getSportChartData() { }, ], } + + const data = sportChartData.value.series[0].data + const sum = data.reduce((a:number, b:number) => a + b, 0) + + avgSport.value = sum / data.length + maxSport.value = Math.max(...data) + minSport.value = Math.min(...data) } else { sportChartData.value = { categories: [], series: [] @@ -226,6 +305,20 @@ function getBloodPressureChartData() { { name: '舒张压(mmHg)', data: list.map((r) => r.diastolic) }, ], } + + const data1 = list.map((r) => r.systolic) + const sum1 = data1.reduce((a:number, b:number) => a + b, 0) + + avgSystolic.value = sum1 / data1.length + maxSystolic.value = Math.max(...data1) + minSystolic.value = Math.min(...data1) + + const data2 = list.map((r) => r.diastolic) + const sum2 = data2.reduce((a:number, b:number) => a + b, 0) + + avgDiastolic.value = sum2 / data2.length + maxDiastolic.value = Math.max(...data2) + minDiastolic.value = Math.min(...data2) } else { bloodPressureChartData.value = { categories: [], series: [] @@ -245,6 +338,13 @@ function getHeartRateChartData() { { name: '心率(bpm)', data: list.map((r) => r.heartRate) }, ], } + + const data = heartRateChartData.value.series[0].data + const sum = data.reduce((a:number, b:number) => a + b, 0) + + avgHeartRate.value = sum / data.length + maxHeartRate.value = Math.max(...data) + minHeartRate.value = Math.min(...data) } else { heartRateChartData.value = { categories: [], series: [] @@ -267,6 +367,13 @@ function getBloodSugarChartData() { }, ], } + + const data = bloodSugarChartData.value.series[0].data + const sum = data.reduce((a:number, b:number) => a + b, 0) + + avgBloodSugar.value = sum / data.length + maxBloodSugar.value = Math.max(...data) + minBloodSugar.value = Math.min(...data) } else { bloodSugarChartData.value = { categories: [], series: [] @@ -289,6 +396,13 @@ function getTemperatureChartData() { }, ], } + + const data = temperatureChartData.value.series[0].data + const sum = data.reduce((a:number, b:number) => a + b, 0) + + avgTemperature.value = sum / data.length + maxTemperature.value = Math.max(...data) + minTemperature.value = Math.min(...data) } else { temperatureChartData.value = { categories: [], series: [] diff --git a/src/store/app.ts b/src/store/app.ts index e875c53..abf4ff3 100644 --- a/src/store/app.ts +++ b/src/store/app.ts @@ -3,7 +3,7 @@ import type { ITip } from "@/type"; export const useAppStore = defineStore('app', { state: () => ({ - version: '1.1.0.26090101', + version: '1.1.0.26090102', viewRecordType: 'calendar', points: 0, recordCount: 0, diff --git a/src/type/profile.ts b/src/type/profile.ts index 22ea9ee..5909c17 100644 --- a/src/type/profile.ts +++ b/src/type/profile.ts @@ -9,6 +9,8 @@ export interface IOverview { icon: string label: string value: string + trendColor: string + trend: string sub: string } @@ -26,4 +28,4 @@ export interface IDefaultRecordValue { bloodPressureHeartRate: number bloodSugarValue: number temperatureValue: number -} \ No newline at end of file +} diff --git a/src/utils/record.ts b/src/utils/record.ts index 640ad1f..170dd4b 100644 --- a/src/utils/record.ts +++ b/src/utils/record.ts @@ -118,6 +118,11 @@ function removeFromRecentIndex(type: string, id: string) { } } +/** + * 获取近期记录 + * @param type 类型 + * @param limit 最大记录数 + */ export function getRecentRecords(type: string, limit = 7): IHealthRecord[] { const key = `recent_${type}` const recent = wx.getStorageSync(key) || []