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 @@
+
+
+
+
+ 均值
+ {{ avg.toFixed(2) }}
+
+
+
+ 最高
+ {{ max.toFixed(2) }}
+
+
+
+ 最低
+ {{ min.toFixed(2) }}
+
+
+
+
+
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) || []