feat: 增加存储功能
This commit is contained in:
@@ -65,7 +65,7 @@
|
||||
title="时间"
|
||||
prop="time"
|
||||
is-link
|
||||
:value="formatTime(recordStore.bpForm.time)"
|
||||
:value="formatDateTime(recordStore.bpForm.time)"
|
||||
placeholder="请选择记录时间"
|
||||
@click="showTimePicker = true"
|
||||
/>
|
||||
@@ -89,13 +89,14 @@
|
||||
<wd-datetime-picker
|
||||
v-model="recordStore.bpForm.time"
|
||||
v-model:visible="showTimePicker"
|
||||
:max-date="Date.now()"
|
||||
placeholder="请选择记录时间"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRecordStore } from '@/store/record'
|
||||
import { formatTime } from '@/utils'
|
||||
import { formatDateTime } from '@/utils'
|
||||
|
||||
const showTimePicker = ref(false)
|
||||
|
||||
@@ -127,11 +128,8 @@ function saveBp() {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: 接接口保存
|
||||
console.log('保存血压:', recordStore.bpForm)
|
||||
|
||||
uni.showToast({ title: '血压记录已保存 ✅', icon: 'success' })
|
||||
recordStore.showBpPopup = false
|
||||
recordStore.submitBp()
|
||||
uni.showToast({ title: '血压记录成功', icon: 'success' })
|
||||
|
||||
handeClosePopup()
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
title="时间"
|
||||
prop="time"
|
||||
is-link
|
||||
:value="formatTime(recordStore.glucoseForm.time)"
|
||||
:value="formatDateTime(recordStore.glucoseForm.time)"
|
||||
placeholder="请选择记录时间"
|
||||
@click="showTimePicker = true"
|
||||
/>
|
||||
@@ -75,13 +75,14 @@
|
||||
<wd-datetime-picker
|
||||
v-model="recordStore.glucoseForm.time"
|
||||
v-model:visible="showTimePicker"
|
||||
:max-date="Date.now()"
|
||||
placeholder="请选择记录时间"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRecordStore } from '@/store/record'
|
||||
import { formatTime } from '@/utils'
|
||||
import { formatDateTime } from '@/utils'
|
||||
|
||||
const showTimePicker = ref(false)
|
||||
|
||||
@@ -122,11 +123,8 @@ function saveGlucose() {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: 接接口保存
|
||||
console.log('保存血糖:', recordStore.glucoseForm)
|
||||
|
||||
uni.showToast({ title: '血糖记录已保存 ✅', icon: 'success' })
|
||||
recordStore.showGlucosePopup = false
|
||||
recordStore.submitGlucose()
|
||||
uni.showToast({ title: '血糖记录成功', icon: 'success' })
|
||||
|
||||
handeClosePopup()
|
||||
}
|
||||
|
||||
@@ -47,58 +47,22 @@ function onSelect(item: { key: string, label: string }) {
|
||||
recordStore.showRecordPopup = false
|
||||
|
||||
if (item.key === 'weight') {
|
||||
recordStore.weightForm.value = 70
|
||||
recordStore.weightForm.time = Date.now()
|
||||
recordStore.weightForm.note = ''
|
||||
setTimeout(() => {
|
||||
recordStore.showWeightPopup = true
|
||||
}, 300)
|
||||
recordStore.openWeight()
|
||||
}
|
||||
else if (item.key === 'sleep') {
|
||||
recordStore.sleepForm.startTime = Date.now() - 8 * 60 * 60 * 1000
|
||||
recordStore.sleepForm.endTime = Date.now()
|
||||
recordStore.sleepForm.time = Date.now()
|
||||
recordStore.sleepForm.note = ''
|
||||
setTimeout(() => {
|
||||
recordStore.showSleepPopup = true
|
||||
}, 300)
|
||||
recordStore.openSleep()
|
||||
}
|
||||
else if (item.key === 'sport') {
|
||||
recordStore.sportForm.type = '跑步'
|
||||
recordStore.sportForm.duration = 30
|
||||
recordStore.sportForm.time = Date.now()
|
||||
recordStore.sportForm.note = ''
|
||||
setTimeout(() => {
|
||||
recordStore.showSportPopup = true
|
||||
}, 300)
|
||||
recordStore.openSport()
|
||||
}
|
||||
else if (item.key === 'bp') {
|
||||
recordStore.bpForm.systolic = 120
|
||||
recordStore.bpForm.diastolic = 80
|
||||
recordStore.bpForm.heartRate = 80
|
||||
recordStore.bpForm.time = Date.now()
|
||||
recordStore.bpForm.note = ''
|
||||
setTimeout(() => {
|
||||
recordStore.showBpPopup = true
|
||||
}, 300)
|
||||
recordStore.openBp()
|
||||
}
|
||||
else if (item.key === 'glucose') {
|
||||
recordStore.glucoseForm.value = 5.4
|
||||
recordStore.glucoseForm.period = '空腹'
|
||||
recordStore.glucoseForm.time = Date.now()
|
||||
recordStore.glucoseForm.note = ''
|
||||
setTimeout(() => {
|
||||
recordStore.showGlucosePopup = true
|
||||
}, 300)
|
||||
recordStore.openGlucose()
|
||||
}
|
||||
else if (item.key === 'temp') {
|
||||
recordStore.tempForm.value = 36.5
|
||||
recordStore.tempForm.location = '腋下'
|
||||
recordStore.tempForm.time = Date.now()
|
||||
recordStore.tempForm.note = ''
|
||||
setTimeout(() => {
|
||||
recordStore.showTempPopup = true
|
||||
}, 300)
|
||||
recordStore.openTemp()
|
||||
}
|
||||
else {
|
||||
// 其他类型暂时提示
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
title="开始"
|
||||
prop="startTime"
|
||||
is-link
|
||||
:value="formatTime(recordStore.sleepForm.startTime)"
|
||||
:value="formatDateTime(recordStore.sleepForm.startTime)"
|
||||
placeholder="请选择开始时间"
|
||||
@click="showStartPicker = true"
|
||||
/>
|
||||
@@ -32,7 +32,7 @@
|
||||
title="结束"
|
||||
prop="endTime"
|
||||
is-link
|
||||
:value="formatTime(recordStore.sleepForm.endTime)"
|
||||
:value="formatDateTime(recordStore.sleepForm.endTime)"
|
||||
placeholder="请选择结束时间"
|
||||
@click="showEndPicker = true"
|
||||
/>
|
||||
@@ -48,7 +48,7 @@
|
||||
title="时间"
|
||||
prop="time"
|
||||
is-link
|
||||
:value="formatTime(recordStore.sleepForm.time)"
|
||||
:value="formatDateTime(recordStore.sleepForm.time)"
|
||||
placeholder="请选择记录时间"
|
||||
@click="showTimePicker = true"
|
||||
/>
|
||||
@@ -85,13 +85,14 @@
|
||||
<wd-datetime-picker
|
||||
v-model="recordStore.sleepForm.time"
|
||||
v-model:visible="showTimePicker"
|
||||
:max-date="Date.now()"
|
||||
placeholder="请选择记录时间"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRecordStore } from '@/store/record'
|
||||
import { formatTime } from '@/utils'
|
||||
import { formatDateTime } from '@/utils'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const recordStore = useRecordStore()
|
||||
@@ -149,14 +150,8 @@ function saveSleep() {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: 接接口保存
|
||||
console.log('保存睡眠:', {
|
||||
...recordStore.sleepForm,
|
||||
duration: sleepDuration.value,
|
||||
})
|
||||
|
||||
uni.showToast({ title: '睡眠记录已保存 ✅', icon: 'success' })
|
||||
recordStore.showSleepPopup = false
|
||||
recordStore.submitSleep()
|
||||
uni.showToast({ title: '睡眠记录成功', icon: 'success' })
|
||||
|
||||
handeClosePopup()
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
border
|
||||
>
|
||||
<!-- 运动类型 -->
|
||||
<wd-form-item title="类型" prop="type">
|
||||
<wd-radio-group v-model="recordStore.sportForm.type" type="button">
|
||||
<wd-form-item title="类型" prop="category">
|
||||
<wd-radio-group v-model="recordStore.sportForm.category" type="button">
|
||||
<wd-radio
|
||||
v-for="item in sportTypeColumns"
|
||||
:key="item.value"
|
||||
@@ -51,7 +51,7 @@
|
||||
title="时间"
|
||||
prop="time"
|
||||
is-link
|
||||
:value="formatTime(recordStore.sportForm.time)"
|
||||
:value="formatDateTime(recordStore.sportForm.time)"
|
||||
placeholder="请选择记录时间"
|
||||
@click="showTimePicker = true"
|
||||
/>
|
||||
@@ -74,13 +74,14 @@
|
||||
<wd-datetime-picker
|
||||
v-model="recordStore.sportForm.time"
|
||||
v-model:visible="showTimePicker"
|
||||
:max-date="Date.now()"
|
||||
placeholder="请选择记录时间"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRecordStore } from '@/store/record'
|
||||
import { formatTime } from '@/utils'
|
||||
import { formatDateTime } from '@/utils'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const showTimePicker = ref(false)
|
||||
@@ -103,9 +104,9 @@ function handeClosePopup() {
|
||||
}
|
||||
|
||||
function saveSport() {
|
||||
const { type, duration, time } = recordStore.sportForm
|
||||
const { category, duration, time } = recordStore.sportForm
|
||||
|
||||
if (!type) {
|
||||
if (!category) {
|
||||
uni.showToast({ title: '请选择运动类型', icon: 'none' })
|
||||
return
|
||||
}
|
||||
@@ -118,11 +119,8 @@ function saveSport() {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: 接接口保存
|
||||
console.log('保存运动:', recordStore.sportForm)
|
||||
|
||||
uni.showToast({ title: '运动记录已保存 ✅', icon: 'success' })
|
||||
recordStore.showSportPopup = false
|
||||
recordStore.submitSport()
|
||||
uni.showToast({ title: '运动记录成功', icon: 'success' })
|
||||
|
||||
handeClosePopup()
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
title="时间"
|
||||
prop="time"
|
||||
is-link
|
||||
:value="formatTime(recordStore.tempForm.time)"
|
||||
:value="formatDateTime(recordStore.tempForm.time)"
|
||||
placeholder="请选择记录时间"
|
||||
@click="showTimePicker = true"
|
||||
/>
|
||||
@@ -75,13 +75,14 @@
|
||||
<wd-datetime-picker
|
||||
v-model="recordStore.tempForm.time"
|
||||
v-model:visible="showTimePicker"
|
||||
:max-date="Date.now()"
|
||||
placeholder="请选择记录时间"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRecordStore } from '@/store/record'
|
||||
import { formatTime } from '@/utils'
|
||||
import { formatDateTime } from '@/utils'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const showTimePicker = ref(false)
|
||||
@@ -121,11 +122,8 @@ function saveTemp() {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: 接接口保存
|
||||
console.log('保存体温:', recordStore.tempForm)
|
||||
|
||||
uni.showToast({ title: '体温记录已保存 ✅', icon: 'success' })
|
||||
recordStore.showTempPopup = false
|
||||
recordStore.submitTemp()
|
||||
uni.showToast({ title: '体温记录成功', icon: 'success' })
|
||||
|
||||
handeClosePopup()
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
title="时间"
|
||||
prop="time"
|
||||
is-link
|
||||
:value="formatTime(recordStore.weightForm.time)"
|
||||
:value="formatDateTime(recordStore.weightForm.time)"
|
||||
placeholder="请选择记录时间"
|
||||
@click="showTimePicker = true"
|
||||
/>
|
||||
@@ -46,13 +46,14 @@
|
||||
<wd-datetime-picker
|
||||
v-model="recordStore.weightForm.time"
|
||||
v-model:visible="showTimePicker"
|
||||
:max-date="Date.now()"
|
||||
placeholder="请选择记录时间"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRecordStore } from '@/store/record'
|
||||
import { formatTime } from '@/utils'
|
||||
import { formatDateTime } from '@/utils'
|
||||
|
||||
const showTimePicker = ref(false)
|
||||
|
||||
@@ -69,14 +70,8 @@ function saveWeight() {
|
||||
uni.showToast({ title: '请输入体重', icon: 'none' })
|
||||
}
|
||||
else {
|
||||
// TODO: 这里接接口保存
|
||||
console.log('保存体重:', recordStore.weightForm)
|
||||
|
||||
uni.showToast({ title: '体重已保存 ✅', icon: 'success' })
|
||||
recordStore.showWeightPopup = false
|
||||
|
||||
// 保存后刷新首页概览(TODO: 接真实数据后打开)
|
||||
// overviewItems[0].value = weightForm.value + 'kg'
|
||||
recordStore.submitWeight()
|
||||
uni.showToast({ title: '体重记录成功', icon: 'success' })
|
||||
}
|
||||
|
||||
handeClosePopup()
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useRecordStore } from '@/store/record'
|
||||
import { getDateStr, getDayRecords, getMonthRecords } from '@/utils/record'
|
||||
|
||||
defineOptions({
|
||||
name: 'Home',
|
||||
@@ -110,6 +111,13 @@ const plans = [
|
||||
{ icon: '🛏️', label: '23:00 前入睡', done: false },
|
||||
]
|
||||
|
||||
onMounted(() => {
|
||||
const result = getDayRecords(getDateStr(Date.now()))
|
||||
console.log(result)
|
||||
|
||||
console.log(getMonthRecords('2026-08'))
|
||||
})
|
||||
|
||||
function handelFabClick() {
|
||||
recordStore.showRecordPopup = true
|
||||
recordStore.showFab = false
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
<view class="rounded-xl bg-white p-3.5 shadow-sm">
|
||||
<!-- 第一行:日期时间 + 状态标签 -->
|
||||
<view class="flex items-center justify-between">
|
||||
<text class="text-xs text-gray-400 font-mono">{{ item.datetime }}</text>
|
||||
<text class="text-xs text-gray-400 font-mono">{{ item.time }}</text>
|
||||
<wd-tag type="primary" round>
|
||||
{{ item.status }}
|
||||
</wd-tag>
|
||||
@@ -52,7 +52,7 @@
|
||||
<view class="h-7 w-7 flex items-center justify-center rounded-lg text-sm" :class="item.color">
|
||||
<text>{{ item.icon }}</text>
|
||||
</view>
|
||||
<text class="text-sm text-gray-700 font-medium">{{ item.label }}</text>
|
||||
<text class="text-sm text-gray-700 font-medium">{{ item.type }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 第三行:数值 -->
|
||||
@@ -116,6 +116,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { getMonthRecords, getMonthStr } from '@/utils/record'
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
@@ -133,91 +134,96 @@ const displayDate = computed(() => {
|
||||
return `${d.getFullYear()}年${d.getMonth() + 1}月`
|
||||
})
|
||||
|
||||
const records = ref([])
|
||||
|
||||
onMounted(() => {
|
||||
records.value = getMonthRecords(getMonthStr(currentDate.value))
|
||||
})
|
||||
|
||||
function changeMonth(offset: number) {
|
||||
const d = new Date(currentDate.value)
|
||||
d.setMonth(d.getMonth() + offset)
|
||||
currentDate.value = d.getTime()
|
||||
|
||||
records.value = getMonthRecords(getMonthStr(currentDate.value))
|
||||
}
|
||||
|
||||
function goToday() {
|
||||
currentDate.value = Date.now()
|
||||
records.value = getMonthRecords(getMonthStr(currentDate.value))
|
||||
}
|
||||
|
||||
function openDatePicker() {
|
||||
showDatePicker.value = true
|
||||
}
|
||||
|
||||
function onDateConfirm() {
|
||||
// picker 确认后 currentDate 已自动更新
|
||||
function onDateConfirm({ value }) {
|
||||
records.value = getMonthRecords(getMonthStr(currentDate.value))
|
||||
}
|
||||
|
||||
// ===== 时间轴数据 =====
|
||||
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 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({
|
||||
dateRange: '基于今日数据',
|
||||
text: '今日整体健康状态良好。体重平稳,血压正常,建议增加有氧运动频率,保持当前作息规律。',
|
||||
tags: ['🟢 体重稳定', '❤️ 血压正常', '🏃 建议多运动', '😴 睡眠良好'],
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
// 这里可以接接口拉取当天的记录数据
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -1,79 +1,154 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { addBloodPressure, addBloodSugar, addSleep, addSport, addTemperature, addWeight } from '@/utils/record'
|
||||
|
||||
export const useRecordStore = defineStore('record', {
|
||||
state: () => ({
|
||||
list: [] as any[],
|
||||
loading: false,
|
||||
showFab: true,
|
||||
showRecordPopup: false,
|
||||
showRecordPopup: false, // 指标选择弹窗
|
||||
showWeightPopup: false,
|
||||
weightForm: {
|
||||
value: 1,
|
||||
note: '',
|
||||
time: '' as number | string,
|
||||
},
|
||||
showSleepPopup: false,
|
||||
sleepForm: {
|
||||
startTime: '' as number | string,
|
||||
endTime: '' as number | string,
|
||||
note: '',
|
||||
time: '' as number | string,
|
||||
},
|
||||
showSportPopup: false,
|
||||
sportForm: {
|
||||
type: '',
|
||||
duration: 0,
|
||||
note: '',
|
||||
time: '' as number | string,
|
||||
},
|
||||
showBpPopup: false,
|
||||
showGlucosePopup: false,
|
||||
showTempPopup: false,
|
||||
|
||||
weightForm: {
|
||||
value: 0,
|
||||
note: '',
|
||||
time: Date.now(),
|
||||
},
|
||||
sleepForm: {
|
||||
startTime: Date.now(),
|
||||
endTime: Date.now(),
|
||||
note: '',
|
||||
time: Date.now(),
|
||||
},
|
||||
sportForm: {
|
||||
category: '',
|
||||
duration: 0,
|
||||
calories: 0,
|
||||
distance: 0,
|
||||
note: '',
|
||||
time: Date.now(),
|
||||
},
|
||||
bpForm: {
|
||||
systolic: 0,
|
||||
diastolic: 0,
|
||||
heartRate: 0,
|
||||
time: Date.now(),
|
||||
note: '',
|
||||
time: Date.now(),
|
||||
},
|
||||
showGlucosePopup: false,
|
||||
glucoseForm: {
|
||||
value: 0,
|
||||
period: '' as string,
|
||||
time: Date.now(),
|
||||
period: '',
|
||||
note: '',
|
||||
time: Date.now(),
|
||||
},
|
||||
showTempPopup: false,
|
||||
tempForm: {
|
||||
value: 36.5,
|
||||
location: '' as string,
|
||||
time: Date.now(),
|
||||
location: '',
|
||||
note: '',
|
||||
time: Date.now(),
|
||||
},
|
||||
}),
|
||||
actions: {
|
||||
setLoading(val: boolean) {
|
||||
this.loading = val
|
||||
openWeight() {
|
||||
this.resetWeight()
|
||||
this.showWeightPopup = true
|
||||
},
|
||||
|
||||
async fetchList() {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await fetch('/api/record/list')
|
||||
this.list = await res.json()
|
||||
}
|
||||
finally {
|
||||
this.loading = false
|
||||
}
|
||||
openSleep() {
|
||||
this.resetSleep()
|
||||
this.showSleepPopup = true
|
||||
},
|
||||
|
||||
addRecord(item: any) {
|
||||
this.list.push(item)
|
||||
openSport() {
|
||||
this.resetSport()
|
||||
this.showSportPopup = true
|
||||
},
|
||||
|
||||
removeRecord(id: string) {
|
||||
this.list = this.list.filter(item => item.id !== id)
|
||||
openBp() {
|
||||
this.resetBp()
|
||||
this.showBpPopup = true
|
||||
},
|
||||
reset() {
|
||||
|
||||
openGlucose() {
|
||||
this.resetGlucose()
|
||||
this.showGlucosePopup = true
|
||||
},
|
||||
openTemp() {
|
||||
this.resetTemp()
|
||||
this.showTempPopup = true
|
||||
},
|
||||
submitWeight() {
|
||||
addWeight(this.weightForm.value, this.weightForm.note, this.weightForm.time)
|
||||
this.showWeightPopup = false
|
||||
this.resetWeight()
|
||||
},
|
||||
submitSleep() {
|
||||
addSleep(
|
||||
this.sleepForm.startTime,
|
||||
this.sleepForm.endTime,
|
||||
this.sleepForm.note,
|
||||
this.sleepForm.time,
|
||||
)
|
||||
this.showSleepPopup = false
|
||||
this.resetSleep()
|
||||
},
|
||||
submitSport() {
|
||||
addSport(
|
||||
this.sportForm.category,
|
||||
this.sportForm.duration,
|
||||
this.sportForm.note,
|
||||
this.sportForm.time,
|
||||
)
|
||||
this.showSportPopup = false
|
||||
this.resetSport()
|
||||
},
|
||||
submitBp() {
|
||||
addBloodPressure(
|
||||
this.bpForm.systolic,
|
||||
this.bpForm.diastolic,
|
||||
this.bpForm.heartRate || undefined,
|
||||
this.bpForm.note,
|
||||
this.bpForm.time,
|
||||
)
|
||||
this.showBpPopup = false
|
||||
this.resetBp()
|
||||
},
|
||||
submitGlucose() {
|
||||
addBloodSugar(
|
||||
this.glucoseForm.value,
|
||||
this.glucoseForm.period,
|
||||
this.glucoseForm.note,
|
||||
this.glucoseForm.time,
|
||||
)
|
||||
this.showGlucosePopup = false
|
||||
this.resetGlucose()
|
||||
},
|
||||
submitTemp() {
|
||||
addTemperature(
|
||||
this.tempForm.value,
|
||||
this.tempForm.location,
|
||||
this.tempForm.note,
|
||||
this.tempForm.time,
|
||||
)
|
||||
this.showTempPopup = false
|
||||
this.resetTemp()
|
||||
},
|
||||
resetWeight() {
|
||||
this.weightForm = { value: 70, note: '', time: Date.now() }
|
||||
},
|
||||
resetSleep() {
|
||||
this.sleepForm = { startTime: Date.now() - 8 * 60 * 60 * 1000, endTime: Date.now(), note: '', time: Date.now() }
|
||||
},
|
||||
resetSport() {
|
||||
this.sportForm = { category: '跑步', duration: 30, calories: 0, distance: 0, note: '', time: Date.now() }
|
||||
},
|
||||
resetBp() {
|
||||
this.bpForm = { systolic: 120, diastolic: 70, heartRate: 80, note: '', time: Date.now() }
|
||||
},
|
||||
resetGlucose() {
|
||||
this.glucoseForm = { value: 5.4, period: '空腹', note: '', time: Date.now() }
|
||||
},
|
||||
resetTemp() {
|
||||
this.tempForm = { value: 36.5, location: '腋下', note: '', time: Date.now() }
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -150,7 +150,7 @@ export const isDoubleTokenMode = import.meta.env.VITE_AUTH_MODE === 'double'
|
||||
*/
|
||||
export const HOME_PAGE = `/${(pages as PageMetaDatum[]).find(page => page.type === 'home')?.path || (pages as PageMetaDatum[])[0].path}`
|
||||
|
||||
export function formatTime(ts: string | number) {
|
||||
export function formatDateTime(ts: string | number) {
|
||||
if (!ts)
|
||||
return ''
|
||||
const d = new Date(ts)
|
||||
|
||||
126
src/utils/record.ts
Normal file
126
src/utils/record.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
const MONTH_KEEP = 12
|
||||
|
||||
function getMonthKey(timestamp: number) {
|
||||
const d = new Date(timestamp)
|
||||
return `health_${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`
|
||||
}
|
||||
|
||||
export function getDateStr(timestamp: number) {
|
||||
const d = new Date(timestamp)
|
||||
return d.toISOString().slice(0, 10)
|
||||
}
|
||||
|
||||
export function getMonthStr(timestamp: number): string {
|
||||
const d = new Date(timestamp)
|
||||
const y = d.getFullYear()
|
||||
const m = String(d.getMonth() + 1).padStart(2, '0')
|
||||
return `${y}-${m}`
|
||||
}
|
||||
|
||||
export function getTimeStr(timestamp: number) {
|
||||
const d = new Date(timestamp)
|
||||
return d.toTimeString().slice(0, 5)
|
||||
}
|
||||
|
||||
function cleanOldMonths() {
|
||||
const cutoff = new Date()
|
||||
cutoff.setMonth(cutoff.getMonth() - MONTH_KEEP)
|
||||
const cutoffKey = `health_${cutoff.getFullYear()}-${String(cutoff.getMonth() + 1).padStart(2, '0')}`
|
||||
const keys = wx.getStorageInfoSync().keys
|
||||
keys.forEach((key) => {
|
||||
if (key.startsWith('health_') && key < cutoffKey) {
|
||||
wx.removeStorageSync(key)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function addRecord(type: string, data: Record<string, any>, timestamp: number) {
|
||||
const key = getMonthKey(timestamp)
|
||||
const all = wx.getStorageSync(key) || {}
|
||||
const date = getDateStr(timestamp)
|
||||
|
||||
if (!all[date])
|
||||
all[date] = []
|
||||
|
||||
all[date].push({
|
||||
id: Date.now() + Math.random().toString(36).slice(2, 6),
|
||||
time: getTimeStr(timestamp),
|
||||
type,
|
||||
...data,
|
||||
})
|
||||
|
||||
wx.setStorageSync(key, all)
|
||||
}
|
||||
|
||||
export function addWeight(value: number, note: string, timestamp: number) {
|
||||
addRecord('weight', { value, note }, timestamp)
|
||||
}
|
||||
|
||||
export function addSleep(startTime: number, endTime: number, note: string, timestamp: number) {
|
||||
addRecord('sleep', { startTime, endTime, note }, timestamp)
|
||||
}
|
||||
|
||||
export function addSport(category: string, duration: number, note: string, timestamp: number) {
|
||||
addRecord('sport', { category, duration, note }, timestamp)
|
||||
}
|
||||
|
||||
export function addBloodPressure(systolic: number, diastolic: number, heartRate: number, note: string, timestamp: number) {
|
||||
addRecord('bloodPressure', { systolic, diastolic, heartRate, note }, timestamp)
|
||||
}
|
||||
|
||||
export function addBloodSugar(value: number, period: string, note: string, timestamp: number) {
|
||||
addRecord('bloodSugar', { value, period, note }, timestamp)
|
||||
}
|
||||
|
||||
export function addTemperature(value: number, location: string, note: string, timestamp: number) {
|
||||
addRecord('temperature', { value, location, note }, timestamp)
|
||||
}
|
||||
|
||||
function getAllMonthKeys(): string[] {
|
||||
return wx.getStorageInfoSync().keys.filter(k => k.startsWith('health_')).sort()
|
||||
}
|
||||
|
||||
// ─── 根据时间戳反推它属于哪个月的 key ───
|
||||
function getMonthKeyForTimestamp(timestamp: number): string {
|
||||
const d = new Date(timestamp)
|
||||
return `health_${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`
|
||||
}
|
||||
|
||||
// ─── 根据 "YYYY-MM-DD" 反推月份 key ───
|
||||
function getMonthKeyForDate(dateStr: string): string {
|
||||
const [y, m] = dateStr.split('-')
|
||||
return `health_${y}-${m}`
|
||||
}
|
||||
|
||||
export function getDayRecords(date: string): Record<string, any>[] {
|
||||
const key = getMonthKeyForDate(date)
|
||||
const all = wx.getStorageSync(key) || {}
|
||||
const dayRecords = all[date] || []
|
||||
return [...dayRecords].sort((a, b) => a.time.localeCompare(b.time))
|
||||
}
|
||||
|
||||
export function getMonthRecords(yearMonth: string, type?: string): Record<string, any>[] {
|
||||
const key = `health_${yearMonth}`
|
||||
const all = wx.getStorageSync(key) || {}
|
||||
|
||||
const result: Record<string, any>[] = []
|
||||
Object.keys(all).forEach((date) => {
|
||||
all[date].forEach((r: Record<string, any>) => {
|
||||
if (!type || r.type === type) {
|
||||
result.push({ date, ...r })
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return sortRecords(result)
|
||||
}
|
||||
|
||||
// 排序:先按 date 正序,再按 time 正序
|
||||
function sortRecords(records: Record<string, any>[]): Record<string, any>[] {
|
||||
return records.sort((a, b) => {
|
||||
const dateCmp = a.date.localeCompare(b.date)
|
||||
if (dateCmp !== 0)
|
||||
return dateCmp
|
||||
return a.time.localeCompare(b.time)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user