Files
health-mp/src/pages/index/index.vue
2026-08-24 22:58:08 +08:00

199 lines
5.7 KiB
Vue

<template>
<view class="min-h-screen flex flex-col gap-3 bg-[#f2f4f3]">
<view class="border-b border-gray-100 bg-white px-4 pb-3">
<view class="mt-2 flex items-center justify-between">
<view class="flex flex-col">
<text class="text-lg text-gray-800 font-semibold">
{{ greeting }}
</text>
<text class="mt-0.5 text-xs text-gray-400">
{{ today }}
</text>
</view>
</view>
</view>
<wd-card title="📊 今日概览" custom-class="!mx-2 !mb-0">
<view class="grid grid-cols-2 gap-3">
<view
v-for="item in overviewItems"
:key="item.label"
class="rounded-xl bg-[#f2f4f3] p-3 shadow-sm"
>
<view class="flex items-center gap-2">
<view class="h-8 w-8 flex items-center justify-center rounded-full" :class="item.bg">
<text class="text-sm">
{{ item.icon }}
</text>
</view>
<text class="text-xs text-gray-400">
{{ item.label }}
</text>
</view>
<text class="mt-2 block text-xl text-gray-800 font-bold">
{{ item.value }}
</text>
<text class="mt-1 text-xs text-gray-400">
{{ item.sub }}
</text>
</view>
</view>
</wd-card>
<!-- 今日计划 -->
<wd-card title="🎯 今日计划" custom-class="!mx-2 !mb-0">
<view v-for="(plan, idx) in plans" :key="idx">
<view class="flex items-center justify-between py-2.5">
<view class="flex items-center gap-2.5">
<text class="text-lg">{{ plan.icon }}</text>
<text class="text-sm">{{ plan.label }}</text>
</view>
<wd-tag v-if="plan.done" type="success" round>
已完成
</wd-tag>
<wd-tag v-else type="danger" round>
未完成
</wd-tag>
</view>
<view v-if="idx < plans.length - 1" class="h-1px w-full bg-[#f2f4f3]" />
</view>
</wd-card>
<record-popup />
<weight-popup />
<sleep-popup />
<sport-popup />
<bp-popup />
<glucose-popup />
<temp-popup />
<wd-fab v-if="recordStore.showFab" type="primary" :expandable="false" @click="handelFabClick" />
</view>
</template>
<script lang="ts" setup>
import RecordPopup from "@/components/Home/RecordPopup.vue";
import WeightPopup from "@/components/Home/WeightPopup.vue";
import SleepPopup from "@/components/Home/SleepPopup.vue";
import SportPopup from "@/components/Home/SportPopup.vue";
import BpPopup from "@/components/Home/BpPopup.vue";
import GlucosePopup from "@/components/Home/GlucosePopup.vue";
import TempPopup from "@/components/Home/TempPopup.vue";
import { ref, computed, onMounted, watch } from 'vue'
import { useRecordStore } from '@/store/record'
import type { IHealthRecord } from '@/utils/record'
import { getDateStr, getDayRecords } from '@/utils/record'
defineOptions({
name: 'Home',
})
const recordStore = useRecordStore()
const todayRecords = ref<IHealthRecord[]>([])
const now = new Date()
const hour = now.getHours()
const greeting = computed(() => {
if (hour < 6)
return '夜深了 🌙'
if (hour < 12)
return '早上好 ☀️'
if (hour < 18)
return '下午好 👋'
return '晚上好 🌆'
})
const today = computed(() => {
const m = now.getMonth() + 1
const d = now.getDate()
const w = ['日', '一', '二', '三', '四', '五', '六'][now.getDay()]
return `${m}${d}日 周${w}`
})
onMounted(() => {
todayRecords.value = getDayRecords(getDateStr(Date.now()))
})
watch(() => recordStore.isRecord, () => {
todayRecords.value = getDayRecords(getDateStr(Date.now()))
})
// 今日概览
const overviewItems = computed(() => {
const list = todayRecords.value
// 体重:取最新一条
const weight = list.filter(r => r.type === 'weight').pop()
// 血压:取最新一条
const bp = list.filter(r => r.type === 'bloodPressure').pop()
// 睡眠:取最新一条
const sleep = list.filter(r => r.type === 'sleep').pop()
// 运动:全部累加
const sports = list.filter(r => r.type === 'sport')
const sportTotal = sports.reduce((sum, r) => sum + (r.duration || 0), 0)
return [
{
icon: '⚖️',
label: '体重',
value: weight ? `${weight.value} kg` : '未记录',
sub: '',
bg: 'bg-[#E89E3A]',
},
{
icon: '😴',
label: '睡眠',
value: sleep ? `${sleep.duration}h` : '未记录',
sub: '',
bg: 'bg-[#2E5B8C]',
},
{
icon: '🏃',
label: '运动',
value: sportTotal > 0 ? `${sportTotal}min` : '0min',
sub: sportTotal >= 30 ? '已达标' : '目标 30min',
bg: 'bg-[#3A7C5B]',
},
{
icon: '❤️',
label: '血压',
value: bp ? `${bp.systolic}/${bp.diastolic}` : '未记录',
sub: '',
bg: 'bg-[#C04A3C]',
},
]
})
// 今日计划
const plans = computed(() => {
const list = todayRecords.value
const hasWeight = list.some(r => r.type === 'weight')
const sportTotal = list
.filter(r => r.type === 'sport')
.reduce((sum, r) => sum + (r.duration || 0), 0)
const sleepOnTime = list
.filter(r => r.type === 'sleep')
.some((r) => {
const hour = new Date(r.startTime).getHours()
return hour <= 23
})
const hasBp = list.some(r => r.type === 'bloodPressure')
return [
{ icon: '⚖️', label: '记录体重', done: hasWeight },
{ icon: '❤️', label: '测量血压', done: hasBp },
{ icon: '🏃', label: '运动30分钟', done: sportTotal >= 30 },
{ icon: '🛏️', label: '23:00 前入睡', done: sleepOnTime },
]
})
function handelFabClick() {
recordStore.showRecordPopup = true
recordStore.showFab = false
}
</script>