feat:增加健康统计图表功能
This commit is contained in:
84
src/components/Health/HealthChart.vue
Normal file
84
src/components/Health/HealthChart.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div class="bill-chart common-chart"
|
||||
v-loading="healthStore.isLoading"
|
||||
element-loading-text="正在加载中...">
|
||||
<div class="chart-container card-item">
|
||||
<div class="content">
|
||||
<span class="title">📊体重趋势</span>
|
||||
</div>
|
||||
<div class="chart" ref="weightDailyChartRef"/>
|
||||
</div>
|
||||
|
||||
<div class="chart-container card-item">
|
||||
<div class="content">
|
||||
<span class="title">📊运动时长统计</span>
|
||||
</div>
|
||||
<div class="chart" ref="healthDurationChartRef"/>
|
||||
</div>
|
||||
|
||||
<div class="chart-container card-item">
|
||||
<div class="content">
|
||||
<span class="title">📊运动类别统计</span>
|
||||
</div>
|
||||
<div class="chart" ref="healthCategoryChartRef"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
import { lineChartOptions, barChartOptions, pieChartOptions } from 'vue3-common/utils/eChartsUtil'
|
||||
import { useHealthStore } from '@/store/health'
|
||||
import { useAppStore } from '@/store/app.ts'
|
||||
|
||||
const healthStore = useHealthStore()
|
||||
const appStore = useAppStore()
|
||||
|
||||
const weightDailyChartRef = ref<HTMLElement>()
|
||||
const healthDurationChartRef = ref<HTMLElement>()
|
||||
const healthCategoryChartRef = ref<HTMLElement>()
|
||||
|
||||
let weightDailyChart: echarts.ECharts
|
||||
let healthDurationChart: echarts.ECharts
|
||||
let healthCategoryChart: echarts.ECharts
|
||||
|
||||
watch(() => healthStore.isRefresh, () => {
|
||||
updateChart()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
weightDailyChart = echarts.init(weightDailyChartRef.value as HTMLElement, appStore.isDark ? 'dark' : null)
|
||||
healthDurationChart = echarts.init(healthDurationChartRef.value as HTMLElement, appStore.isDark ? 'dark' : null)
|
||||
healthCategoryChart = echarts.init(healthCategoryChartRef.value as HTMLElement, appStore.isDark ? 'dark' : null)
|
||||
})
|
||||
|
||||
const updateChart = () => {
|
||||
const MAX_CATEGORY_COUNT = 5
|
||||
|
||||
// 体重趋势统计图
|
||||
const weightXAxis = healthStore.healthRecordList.map((item) => item.date).reverse()
|
||||
const weightYAxis = healthStore.healthRecordList.map((item) => item.weight).reverse()
|
||||
weightDailyChart.setOption(lineChartOptions('日期', weightXAxis, '体重(千克)', weightYAxis))
|
||||
|
||||
// 运动量统计图
|
||||
const sportRecordList = healthStore.healthRecordList.filter((item) => item.sportProject !== '')
|
||||
const durationXAxis = sportRecordList.map((item) => item.date).reverse()
|
||||
const durationYAxis = sportRecordList.map((item) => item.sportDuration).reverse()
|
||||
healthDurationChart.setOption(barChartOptions('日期', durationXAxis, '时间(分钟)', durationYAxis))
|
||||
|
||||
// 类别统计图
|
||||
const healthCategoryList = healthStore.healthRecordList.reduce((acc, item) => {
|
||||
if (item.sportProject && item.sportDuration > 0) {
|
||||
const existing = acc.find((project) => project.name === item.sportProject)
|
||||
if (existing) {
|
||||
existing.value += item.sportDuration
|
||||
} else {
|
||||
acc.push({ name: item.sportProject, value: item.sportDuration })
|
||||
}
|
||||
}
|
||||
return acc
|
||||
}, [])
|
||||
healthCategoryChart.setOption(pieChartOptions('名称', '分钟', healthCategoryList.slice(0, MAX_CATEGORY_COUNT)))
|
||||
}
|
||||
</script>
|
||||
36
src/components/Health/HealthStats.vue
Normal file
36
src/components/Health/HealthStats.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div class="common-statistics">
|
||||
<stats-card
|
||||
title="平均体重" color="#CA6C65" unit="千克" icon="fa-solid fa-weight-scale"
|
||||
:value="formatAmount(healthStats.averageWeight)" />
|
||||
|
||||
<stats-card
|
||||
title="运动时长" color="#6FBB69" unit="分钟" icon="fa-solid fa-person-walking"
|
||||
:value="healthStats.totalDuration" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import StatsCard from '@/components/StatsCard.vue'
|
||||
import { useHealthStore } from '@/store/health'
|
||||
import { ref, watch } from 'vue'
|
||||
import { formatAmount } from 'vue3-common/utils/numberUtil'
|
||||
|
||||
const healthStore = useHealthStore()
|
||||
|
||||
watch(() => healthStore.isRefresh, () => {
|
||||
updateStats()
|
||||
})
|
||||
|
||||
const healthStats = ref({
|
||||
averageWeight: 0,
|
||||
totalDuration: 0
|
||||
})
|
||||
|
||||
const updateStats = () => {
|
||||
const recordList = healthStore.healthRecordList
|
||||
const totalWeight = recordList.reduce((sum, item) => sum + item.weight, 0)
|
||||
healthStats.value.averageWeight = totalWeight / recordList.length
|
||||
healthStats.value.totalDuration = recordList.reduce((sum, item) => sum + item.sportDuration, 0)
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user