feat: 更新每日记录模块
This commit is contained in:
@@ -39,7 +39,7 @@
|
|||||||
|
|
||||||
<wd-form-item title="时长">
|
<wd-form-item title="时长">
|
||||||
<view class="py-1 text-gray-800">
|
<view class="py-1 text-gray-800">
|
||||||
{{ sleepDuration }}
|
{{ sleepDuration }} 小时
|
||||||
</view>
|
</view>
|
||||||
</wd-form-item>
|
</wd-form-item>
|
||||||
|
|
||||||
@@ -106,22 +106,14 @@ const sleepDuration = computed(() => {
|
|||||||
const end = recordStore.sleepForm.endTime
|
const end = recordStore.sleepForm.endTime
|
||||||
|
|
||||||
if (!start || !end)
|
if (!start || !end)
|
||||||
return '请选择开始和结束时间'
|
return 0
|
||||||
|
|
||||||
const diffMs = new Date(end).getTime() - new Date(start).getTime()
|
const diffMs = new Date(end).getTime() - new Date(start).getTime()
|
||||||
|
|
||||||
if (diffMs <= 0)
|
if (diffMs <= 0)
|
||||||
return '结束时间需晚于开始时间'
|
return 0
|
||||||
|
|
||||||
const totalMinutes = Math.floor(diffMs / 60000)
|
return Math.round((diffMs / 3600000) * 10) / 10
|
||||||
const hours = Math.floor(totalMinutes / 60)
|
|
||||||
const minutes = totalMinutes % 60
|
|
||||||
|
|
||||||
if (hours > 0 && minutes > 0)
|
|
||||||
return `${hours}小时${minutes}分钟`
|
|
||||||
if (hours > 0)
|
|
||||||
return `${hours}小时`
|
|
||||||
return `${minutes}分钟`
|
|
||||||
})
|
})
|
||||||
|
|
||||||
function handeClosePopup() {
|
function handeClosePopup() {
|
||||||
@@ -131,7 +123,7 @@ function handeClosePopup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function saveSleep() {
|
function saveSleep() {
|
||||||
const { startTime, endTime, time, note } = recordStore.sleepForm
|
const { startTime, endTime, time } = recordStore.sleepForm
|
||||||
|
|
||||||
if (!startTime) {
|
if (!startTime) {
|
||||||
uni.showToast({ title: '请选择开始时间', icon: 'none' })
|
uni.showToast({ title: '请选择开始时间', icon: 'none' })
|
||||||
@@ -150,6 +142,7 @@ function saveSleep() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
recordStore.sleepForm.duration = sleepDuration.value
|
||||||
recordStore.submitSleep()
|
recordStore.submitSleep()
|
||||||
uni.showToast({ title: '睡眠记录成功', icon: 'success' })
|
uni.showToast({ title: '睡眠记录成功', icon: 'success' })
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,8 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useRecordStore } from '@/store/record'
|
import { useRecordStore } from '@/store/record'
|
||||||
import { getDateStr, getDayRecords, getMonthRecords } from '@/utils/record'
|
import { getDateStr, getDayRecords } from '@/utils/record'
|
||||||
|
import type { IHealthRecord } from '@/types/record'
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'Home',
|
name: 'Home',
|
||||||
@@ -94,28 +95,103 @@ definePage({
|
|||||||
|
|
||||||
const recordStore = useRecordStore()
|
const recordStore = useRecordStore()
|
||||||
|
|
||||||
const greeting = '下午好,小明 👋'
|
const todayRecords = ref<IHealthRecord[]>([])
|
||||||
const today = '8月19日 周三'
|
|
||||||
|
|
||||||
// 今日概览数据
|
const now = new Date()
|
||||||
const overviewItems = [
|
const hour = now.getHours()
|
||||||
{ icon: '⚖️', label: '体重', value: '68.5kg', sub: '较昨日 -0.3kg', color: 'text-[#1F2D28]', bg: 'bg-[#E89E3A]' },
|
|
||||||
{ icon: '😴', label: '睡眠', value: '7.5h', sub: '深睡 2.1h', color: 'text-[#1F2D28]', bg: 'bg-[#2E5B8C]' },
|
|
||||||
{ icon: '🏃', label: '运动', value: '30min', sub: '目标 30min', color: 'text-[#1F2D28]', bg: 'bg-[#3A7C5B]' },
|
|
||||||
{ icon: '❤️ ', label: '血压', value: '120/80', sub: '正常', color: 'text-[#1F2D28]', bg: 'bg-[#C04A3C]' },
|
|
||||||
]
|
|
||||||
|
|
||||||
// 今日计划
|
const greeting = computed(() => {
|
||||||
const plans = [
|
if (hour < 6)
|
||||||
{ icon: '🏃', label: '运动30分钟', done: true },
|
return '夜深了 🌙'
|
||||||
{ icon: '🛏️', label: '23:00 前入睡', done: false },
|
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(() => {
|
onMounted(() => {
|
||||||
const result = getDayRecords(getDateStr(Date.now()))
|
todayRecords.value = getDayRecords(getDateStr(Date.now()))
|
||||||
console.log(result)
|
})
|
||||||
|
|
||||||
console.log(getMonthRecords('2026-08'))
|
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() {
|
function handelFabClick() {
|
||||||
|
|||||||
@@ -18,57 +18,64 @@
|
|||||||
|
|
||||||
<!-- 时间轴区域 -->
|
<!-- 时间轴区域 -->
|
||||||
<view class="px-5 py-2">
|
<view class="px-5 py-2">
|
||||||
<!-- 时间轴容器 -->
|
<view v-if="grouped.length === 0" class="py-16 text-center">
|
||||||
|
<text class="text-4xl">📭</text>
|
||||||
|
<text class="mt-3 block text-sm text-gray-400">暂无记录</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 外层:每个日期分组 -->
|
||||||
|
<view v-for="group in grouped" :key="group.date" class="mb-6">
|
||||||
|
<!-- 日期分类标题 -->
|
||||||
|
<view class="mb-2 flex items-center gap-2 pl-1">
|
||||||
|
<text class="text-sm text-gray-500 font-medium">{{ group.date }}</text>
|
||||||
|
<text class="text-xs text-gray-400">周{{ group.weekday }}</text>
|
||||||
|
<view class="ml-2 h-[1px] flex-1 bg-gray-200" />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 该日期下的时间轴 -->
|
||||||
<view class="relative pl-6">
|
<view class="relative pl-6">
|
||||||
<view
|
<view
|
||||||
class="absolute bottom-1 top-1 w-[2px] from-[#07c160] via-[#2E5B8C] to-[#dde3e0] bg-gradient-to-b"
|
class="absolute bottom-1 top-1 w-[2px] from-[#07c160] via-[#2E5B8C] to-[#dde3e0] bg-gradient-to-b"
|
||||||
style="left: 0;"
|
style="left: 0;"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 每条记录 -->
|
|
||||||
<view
|
<view
|
||||||
v-for="(item, idx) in records"
|
v-for="(item, idx) in group.items"
|
||||||
:key="idx"
|
:key="item.id || idx"
|
||||||
class="relative mb-5 last:mb-0"
|
class="relative mb-5 last:mb-0"
|
||||||
>
|
>
|
||||||
<view
|
<view
|
||||||
class="absolute border-[2.5px] border-white rounded-full shadow-sm"
|
class="absolute border-[2.5px] border-white rounded-full shadow-sm"
|
||||||
style="width: 14px; height: 14px; left: -30px; top: 0; z-index: 2;"
|
style="width: 14px; height: 14px; left: -30px; top: 0; z-index: 2;"
|
||||||
:class="item.color"
|
:class="getRecordMeta(item.type).color"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 卡片 -->
|
<!-- 卡片 -->
|
||||||
<view class="rounded-xl bg-white p-3.5 shadow-sm">
|
<view class="rounded-xl bg-white p-3.5 shadow-sm">
|
||||||
<!-- 第一行:日期时间 + 状态标签 -->
|
|
||||||
<view class="flex items-center justify-between">
|
<view class="flex items-center justify-between">
|
||||||
<text class="text-xs text-gray-400 font-mono">{{ item.time }}</text>
|
<text class="text-xs text-gray-400 font-mono">{{ item.time }}</text>
|
||||||
<wd-tag type="primary" round>
|
<wd-tag type="primary" round>
|
||||||
{{ item.status }}
|
{{ getRecordStatus(item) }}
|
||||||
</wd-tag>
|
</wd-tag>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 第二行:图标 + 指标名 -->
|
|
||||||
<view class="mt-1.5 flex items-center gap-2">
|
<view class="mt-1.5 flex items-center gap-2">
|
||||||
<view class="h-7 w-7 flex items-center justify-center rounded-lg text-sm" :class="item.color">
|
<view
|
||||||
<text>{{ item.icon }}</text>
|
class="h-7 w-7 flex items-center justify-center rounded-lg text-sm"
|
||||||
|
:class="getRecordMeta(item.type).color"
|
||||||
|
>
|
||||||
|
<text>{{ getRecordMeta(item.type).icon }}</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="text-sm text-gray-700 font-medium">{{ item.type }}</text>
|
<text class="text-sm text-gray-700 font-medium">{{ getRecordMeta(item.type).label }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 第三行:数值 -->
|
<text class="mt-1.5 block text-xl text-gray-800 font-bold">{{ getRecordValue(item) }}</text>
|
||||||
<text class="mt-1.5 block text-xl text-gray-800 font-bold">{{ item.value }}</text>
|
|
||||||
|
|
||||||
<!-- 第四行:备注 -->
|
|
||||||
<text v-if="item.note" class="mt-0.5 block text-xs text-gray-400 leading-relaxed">
|
<text v-if="item.note" class="mt-0.5 block text-xs text-gray-400 leading-relaxed">
|
||||||
{{ item.note }}
|
{{ item.note }}
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 无数据 -->
|
|
||||||
<view v-if="records.length === 0" class="py-16 text-center">
|
|
||||||
<text class="text-4xl">📭</text>
|
|
||||||
<text class="mt-3 block text-sm text-gray-400">这一天还没有记录</text>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -116,7 +123,8 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
import { getMonthRecords, getMonthStr } from '@/utils/record'
|
import { getMonthRecords, getMonthStr, getRecordMeta, getRecordStatus, getRecordValue, groupByDate } from '@/utils/record'
|
||||||
|
import type { IHealthRecord } from '@/types/record'
|
||||||
|
|
||||||
definePage({
|
definePage({
|
||||||
style: {
|
style: {
|
||||||
@@ -134,10 +142,17 @@ const displayDate = computed(() => {
|
|||||||
return `${d.getFullYear()}年${d.getMonth() + 1}月`
|
return `${d.getFullYear()}年${d.getMonth() + 1}月`
|
||||||
})
|
})
|
||||||
|
|
||||||
const records = ref([])
|
const records = ref<IHealthRecord[]>([])
|
||||||
|
const grouped = ref<ReturnType<typeof groupByDate>>([])
|
||||||
|
|
||||||
|
function loadRecords() {
|
||||||
|
const all = getMonthRecords(getMonthStr(currentDate.value))
|
||||||
|
records.value = all
|
||||||
|
grouped.value = groupByDate(all)
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
records.value = getMonthRecords(getMonthStr(currentDate.value))
|
loadRecords()
|
||||||
})
|
})
|
||||||
|
|
||||||
function changeMonth(offset: number) {
|
function changeMonth(offset: number) {
|
||||||
@@ -145,12 +160,12 @@ function changeMonth(offset: number) {
|
|||||||
d.setMonth(d.getMonth() + offset)
|
d.setMonth(d.getMonth() + offset)
|
||||||
currentDate.value = d.getTime()
|
currentDate.value = d.getTime()
|
||||||
|
|
||||||
records.value = getMonthRecords(getMonthStr(currentDate.value))
|
loadRecords()
|
||||||
}
|
}
|
||||||
|
|
||||||
function goToday() {
|
function goToday() {
|
||||||
currentDate.value = Date.now()
|
currentDate.value = Date.now()
|
||||||
records.value = getMonthRecords(getMonthStr(currentDate.value))
|
loadRecords()
|
||||||
}
|
}
|
||||||
|
|
||||||
function openDatePicker() {
|
function openDatePicker() {
|
||||||
@@ -158,67 +173,9 @@ function openDatePicker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onDateConfirm({ value }) {
|
function onDateConfirm({ value }) {
|
||||||
records.value = getMonthRecords(getMonthStr(currentDate.value))
|
loadRecords()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== 时间轴数据 =====
|
|
||||||
// const records = ref([
|
|
||||||
// {
|
|
||||||
// icon: '😴',
|
|
||||||
// label: '睡眠',
|
|
||||||
// value: '7.5 h',
|
|
||||||
// datetime: '2026-08-20 07:30',
|
|
||||||
// note: '质量良好,深睡2.1h',
|
|
||||||
// color: 'bg-[#2E5B8C]',
|
|
||||||
// status: '良好',
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// icon: '⚖️',
|
|
||||||
// label: '体重',
|
|
||||||
// value: '68.5 kg',
|
|
||||||
// datetime: '2026-08-20 08:30',
|
|
||||||
// note: '早起空腹,较昨日 -0.3kg',
|
|
||||||
// color: 'bg-[#E89E3A]',
|
|
||||||
// status: '正常',
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// icon: '❤️',
|
|
||||||
// label: '血压',
|
|
||||||
// value: '118/76 mmHg',
|
|
||||||
// datetime: '2026-08-20 09:00',
|
|
||||||
// note: '服药后测量,静坐5分钟',
|
|
||||||
// color: 'bg-[#C04A3C]',
|
|
||||||
// status: '正常',
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// icon: '📈',
|
|
||||||
// label: '血糖',
|
|
||||||
// value: '5.2 mmol/L',
|
|
||||||
// datetime: '2026-08-20 10:30',
|
|
||||||
// note: '早餐后2小时',
|
|
||||||
// color: 'bg-[#9C27B0]',
|
|
||||||
// status: '正常',
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// icon: '🏃',
|
|
||||||
// label: '运动',
|
|
||||||
// value: '30 min',
|
|
||||||
// datetime: '2026-08-20 14:00',
|
|
||||||
// note: '快走,公园环道',
|
|
||||||
// color: 'bg-[#3A7C5B]',
|
|
||||||
// status: '达标',
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// icon: '💓',
|
|
||||||
// label: '心率',
|
|
||||||
// value: '68 bpm',
|
|
||||||
// datetime: '2026-08-20 16:00',
|
|
||||||
// note: '静息心率',
|
|
||||||
// color: 'bg-[#D32F2F]',
|
|
||||||
// status: '正常',
|
|
||||||
// },
|
|
||||||
// ])
|
|
||||||
|
|
||||||
const aiSummary = ref({
|
const aiSummary = ref({
|
||||||
dateRange: '基于今日数据',
|
dateRange: '基于今日数据',
|
||||||
text: '今日整体健康状态良好。体重平稳,血压正常,建议增加有氧运动频率,保持当前作息规律。',
|
text: '今日整体健康状态良好。体重平稳,血压正常,建议增加有氧运动频率,保持当前作息规律。',
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ export const useRecordStore = defineStore('record', {
|
|||||||
showBpPopup: false,
|
showBpPopup: false,
|
||||||
showGlucosePopup: false,
|
showGlucosePopup: false,
|
||||||
showTempPopup: false,
|
showTempPopup: false,
|
||||||
|
|
||||||
weightForm: {
|
weightForm: {
|
||||||
value: 0,
|
value: 0,
|
||||||
note: '',
|
note: '',
|
||||||
@@ -20,14 +19,13 @@ export const useRecordStore = defineStore('record', {
|
|||||||
sleepForm: {
|
sleepForm: {
|
||||||
startTime: Date.now(),
|
startTime: Date.now(),
|
||||||
endTime: Date.now(),
|
endTime: Date.now(),
|
||||||
|
duration: 0,
|
||||||
note: '',
|
note: '',
|
||||||
time: Date.now(),
|
time: Date.now(),
|
||||||
},
|
},
|
||||||
sportForm: {
|
sportForm: {
|
||||||
category: '',
|
category: '',
|
||||||
duration: 0,
|
duration: 0,
|
||||||
calories: 0,
|
|
||||||
distance: 0,
|
|
||||||
note: '',
|
note: '',
|
||||||
time: Date.now(),
|
time: Date.now(),
|
||||||
},
|
},
|
||||||
@@ -50,6 +48,7 @@ export const useRecordStore = defineStore('record', {
|
|||||||
note: '',
|
note: '',
|
||||||
time: Date.now(),
|
time: Date.now(),
|
||||||
},
|
},
|
||||||
|
isRecord: false,
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
openWeight() {
|
openWeight() {
|
||||||
@@ -79,17 +78,18 @@ export const useRecordStore = defineStore('record', {
|
|||||||
submitWeight() {
|
submitWeight() {
|
||||||
addWeight(this.weightForm.value, this.weightForm.note, this.weightForm.time)
|
addWeight(this.weightForm.value, this.weightForm.note, this.weightForm.time)
|
||||||
this.showWeightPopup = false
|
this.showWeightPopup = false
|
||||||
this.resetWeight()
|
this.isRecord = !this.isRecord
|
||||||
},
|
},
|
||||||
submitSleep() {
|
submitSleep() {
|
||||||
addSleep(
|
addSleep(
|
||||||
this.sleepForm.startTime,
|
this.sleepForm.startTime,
|
||||||
this.sleepForm.endTime,
|
this.sleepForm.endTime,
|
||||||
|
this.sleepForm.duration,
|
||||||
this.sleepForm.note,
|
this.sleepForm.note,
|
||||||
this.sleepForm.time,
|
this.sleepForm.time,
|
||||||
)
|
)
|
||||||
this.showSleepPopup = false
|
this.showSleepPopup = false
|
||||||
this.resetSleep()
|
this.isRecord = !this.isRecord
|
||||||
},
|
},
|
||||||
submitSport() {
|
submitSport() {
|
||||||
addSport(
|
addSport(
|
||||||
@@ -99,7 +99,7 @@ export const useRecordStore = defineStore('record', {
|
|||||||
this.sportForm.time,
|
this.sportForm.time,
|
||||||
)
|
)
|
||||||
this.showSportPopup = false
|
this.showSportPopup = false
|
||||||
this.resetSport()
|
this.isRecord = !this.isRecord
|
||||||
},
|
},
|
||||||
submitBp() {
|
submitBp() {
|
||||||
addBloodPressure(
|
addBloodPressure(
|
||||||
@@ -110,7 +110,7 @@ export const useRecordStore = defineStore('record', {
|
|||||||
this.bpForm.time,
|
this.bpForm.time,
|
||||||
)
|
)
|
||||||
this.showBpPopup = false
|
this.showBpPopup = false
|
||||||
this.resetBp()
|
this.isRecord = !this.isRecord
|
||||||
},
|
},
|
||||||
submitGlucose() {
|
submitGlucose() {
|
||||||
addBloodSugar(
|
addBloodSugar(
|
||||||
@@ -120,7 +120,7 @@ export const useRecordStore = defineStore('record', {
|
|||||||
this.glucoseForm.time,
|
this.glucoseForm.time,
|
||||||
)
|
)
|
||||||
this.showGlucosePopup = false
|
this.showGlucosePopup = false
|
||||||
this.resetGlucose()
|
this.isRecord = !this.isRecord
|
||||||
},
|
},
|
||||||
submitTemp() {
|
submitTemp() {
|
||||||
addTemperature(
|
addTemperature(
|
||||||
@@ -130,16 +130,16 @@ export const useRecordStore = defineStore('record', {
|
|||||||
this.tempForm.time,
|
this.tempForm.time,
|
||||||
)
|
)
|
||||||
this.showTempPopup = false
|
this.showTempPopup = false
|
||||||
this.resetTemp()
|
this.isRecord = !this.isRecord
|
||||||
},
|
},
|
||||||
resetWeight() {
|
resetWeight() {
|
||||||
this.weightForm = { value: 70, note: '', time: Date.now() }
|
this.weightForm = { value: 70, note: '', time: Date.now() }
|
||||||
},
|
},
|
||||||
resetSleep() {
|
resetSleep() {
|
||||||
this.sleepForm = { startTime: Date.now() - 8 * 60 * 60 * 1000, endTime: Date.now(), note: '', time: Date.now() }
|
this.sleepForm = { startTime: Date.now() - 8 * 60 * 60 * 1000, endTime: Date.now(), duration: 8, note: '', time: Date.now() }
|
||||||
},
|
},
|
||||||
resetSport() {
|
resetSport() {
|
||||||
this.sportForm = { category: '跑步', duration: 30, calories: 0, distance: 0, note: '', time: Date.now() }
|
this.sportForm = { category: '跑步', duration: 30, note: '', time: Date.now() }
|
||||||
},
|
},
|
||||||
resetBp() {
|
resetBp() {
|
||||||
this.bpForm = { systolic: 120, diastolic: 70, heartRate: 80, note: '', time: Date.now() }
|
this.bpForm = { systolic: 120, diastolic: 70, heartRate: 80, note: '', time: Date.now() }
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import type { IHealthRecord, IRecordMeta } from '@/types/record'
|
||||||
|
|
||||||
const MONTH_KEEP = 12
|
const MONTH_KEEP = 12
|
||||||
|
|
||||||
function getMonthKey(timestamp: number) {
|
function getMonthKey(timestamp: number) {
|
||||||
@@ -56,8 +58,8 @@ export function addWeight(value: number, note: string, timestamp: number) {
|
|||||||
addRecord('weight', { value, note }, timestamp)
|
addRecord('weight', { value, note }, timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addSleep(startTime: number, endTime: number, note: string, timestamp: number) {
|
export function addSleep(startTime: number, endTime: number, duration: number, note: string, timestamp: number) {
|
||||||
addRecord('sleep', { startTime, endTime, note }, timestamp)
|
addRecord('sleep', { startTime, endTime, duration, note }, timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addSport(category: string, duration: number, note: string, timestamp: number) {
|
export function addSport(category: string, duration: number, note: string, timestamp: number) {
|
||||||
@@ -92,20 +94,20 @@ function getMonthKeyForDate(dateStr: string): string {
|
|||||||
return `health_${y}-${m}`
|
return `health_${y}-${m}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDayRecords(date: string): Record<string, any>[] {
|
export function getDayRecords(date: string): IHealthRecord[] {
|
||||||
const key = getMonthKeyForDate(date)
|
const key = getMonthKeyForDate(date)
|
||||||
const all = wx.getStorageSync(key) || {}
|
const all = wx.getStorageSync(key) || {}
|
||||||
const dayRecords = all[date] || []
|
const dayRecords = all[date] || []
|
||||||
return [...dayRecords].sort((a, b) => a.time.localeCompare(b.time))
|
return [...dayRecords].sort((a, b) => a.time.localeCompare(b.time))
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMonthRecords(yearMonth: string, type?: string): Record<string, any>[] {
|
export function getMonthRecords(yearMonth: string, type?: string): IHealthRecord[] {
|
||||||
const key = `health_${yearMonth}`
|
const key = `health_${yearMonth}`
|
||||||
const all = wx.getStorageSync(key) || {}
|
const all = wx.getStorageSync(key) || {}
|
||||||
|
|
||||||
const result: Record<string, any>[] = []
|
const result: IHealthRecord[] = []
|
||||||
Object.keys(all).forEach((date) => {
|
Object.keys(all).forEach((date) => {
|
||||||
all[date].forEach((r: Record<string, any>) => {
|
all[date].forEach((r: IHealthRecord) => {
|
||||||
if (!type || r.type === type) {
|
if (!type || r.type === type) {
|
||||||
result.push({ date, ...r })
|
result.push({ date, ...r })
|
||||||
}
|
}
|
||||||
@@ -116,7 +118,7 @@ export function getMonthRecords(yearMonth: string, type?: string): Record<string
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 排序:先按 date 正序,再按 time 正序
|
// 排序:先按 date 正序,再按 time 正序
|
||||||
function sortRecords(records: Record<string, any>[]): Record<string, any>[] {
|
function sortRecords(records: IHealthRecord[]): IHealthRecord[] {
|
||||||
return records.sort((a, b) => {
|
return records.sort((a, b) => {
|
||||||
const dateCmp = a.date.localeCompare(b.date)
|
const dateCmp = a.date.localeCompare(b.date)
|
||||||
if (dateCmp !== 0)
|
if (dateCmp !== 0)
|
||||||
@@ -124,3 +126,106 @@ function sortRecords(records: Record<string, any>[]): Record<string, any>[] {
|
|||||||
return a.time.localeCompare(b.time)
|
return a.time.localeCompare(b.time)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const META_MAP: Record<string, IRecordMeta> = {
|
||||||
|
weight: { icon: '⚖️', label: '体重', color: 'bg-[#E89E3A]' },
|
||||||
|
sleep: { icon: '😴', label: '睡眠', color: 'bg-[#2E5B8C]' },
|
||||||
|
bloodPressure: { icon: '❤️', label: '血压', color: 'bg-[#C04A3C]' },
|
||||||
|
bloodSugar: { icon: '📈', label: '血糖', color: 'bg-[#9C27B0]' },
|
||||||
|
sport: { icon: '🏃', label: '运动', color: 'bg-[#3A7C5B]' },
|
||||||
|
temperature: { icon: '🌡️', label: '体温', color: 'bg-[#D32F2F]' },
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRecordMeta(type: string): IRecordMeta {
|
||||||
|
return META_MAP[type] || { icon: '📋', label: '其他', color: 'bg-gray-400' }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRecordValue(record: IHealthRecord): string {
|
||||||
|
switch (record.type) {
|
||||||
|
case 'weight':
|
||||||
|
return `${record.value} kg`
|
||||||
|
case 'bloodPressure':
|
||||||
|
return `${record.systolic}/${record.diastolic} mmHg${record.heartRate ? ` · 心率 ${record.heartRate}` : ''}`
|
||||||
|
case 'bloodSugar':
|
||||||
|
return `${record.value} mmol/L · ${record.period}`
|
||||||
|
case 'heartRate':
|
||||||
|
return `${record.value} bpm`
|
||||||
|
case 'temperature':
|
||||||
|
return `${record.value} ℃${record.location ? ` · ${record.location}` : ''}`
|
||||||
|
case 'sport':
|
||||||
|
return `${record.category} · ${record.duration}min`
|
||||||
|
case 'sleep':
|
||||||
|
return `${new Date(record.startTime).toTimeString().slice(0, 5)} ~ ${new Date(record.endTime).toTimeString().slice(0, 5)}(${record.duration}h)`
|
||||||
|
default:
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRecordStatus(record: IHealthRecord): string {
|
||||||
|
switch (record.type) {
|
||||||
|
case 'bloodPressure':
|
||||||
|
if (record.systolic >= 140 || record.diastolic >= 90)
|
||||||
|
return '偏高'
|
||||||
|
if (record.systolic < 90 || record.diastolic < 60)
|
||||||
|
return '偏低'
|
||||||
|
return '正常'
|
||||||
|
|
||||||
|
case 'bloodSugar':
|
||||||
|
if (record.period.includes('空腹')) {
|
||||||
|
return record.value >= 6.1 ? '偏高' : '正常'
|
||||||
|
}
|
||||||
|
// 餐后
|
||||||
|
return record.value >= 7.8 ? '偏高' : '正常'
|
||||||
|
|
||||||
|
case 'heartRate':
|
||||||
|
if (record.value < 60)
|
||||||
|
return '偏慢'
|
||||||
|
if (record.value <= 100)
|
||||||
|
return '正常'
|
||||||
|
return '偏快'
|
||||||
|
|
||||||
|
case 'temperature':
|
||||||
|
if (record.value < 36.1)
|
||||||
|
return '偏低'
|
||||||
|
if (record.value <= 37.2)
|
||||||
|
return '正常'
|
||||||
|
return '偏高'
|
||||||
|
|
||||||
|
case 'sport':
|
||||||
|
return record.duration >= 30 ? '达标' : '偏少'
|
||||||
|
|
||||||
|
case 'sleep':
|
||||||
|
if (record.duration >= 7)
|
||||||
|
return '充足'
|
||||||
|
if (record.duration >= 6)
|
||||||
|
return '良好'
|
||||||
|
return '不足'
|
||||||
|
|
||||||
|
default:
|
||||||
|
return '未知'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const WEEKDAY = ['日', '一', '二', '三', '四', '五', '六']
|
||||||
|
|
||||||
|
export function groupByDate(records: IHealthRecord[]): Array<{
|
||||||
|
date: string
|
||||||
|
weekday: string
|
||||||
|
items: IHealthRecord[]
|
||||||
|
}> {
|
||||||
|
const map: Record<string, IHealthRecord[]> = {}
|
||||||
|
records.forEach((r) => {
|
||||||
|
if (!r.date)
|
||||||
|
return
|
||||||
|
if (!map[r.date])
|
||||||
|
map[r.date] = []
|
||||||
|
map[r.date].push(r)
|
||||||
|
})
|
||||||
|
return Object.keys(map)
|
||||||
|
.sort((a, b) => b.localeCompare(a)) // 日期倒序,最新的在上
|
||||||
|
.map(date => ({
|
||||||
|
date,
|
||||||
|
weekday: WEEKDAY[new Date(date).getDay()],
|
||||||
|
items: map[date].sort((a, b) => a.time.localeCompare(b.time)), // 当天按时间正序
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user