feat: 增加存储功能

This commit is contained in:
2026-08-22 21:38:14 +08:00
parent 2bb55368cd
commit af0f396ed8
12 changed files with 369 additions and 208 deletions

View File

@@ -65,7 +65,7 @@
title="时间" title="时间"
prop="time" prop="time"
is-link is-link
:value="formatTime(recordStore.bpForm.time)" :value="formatDateTime(recordStore.bpForm.time)"
placeholder="请选择记录时间" placeholder="请选择记录时间"
@click="showTimePicker = true" @click="showTimePicker = true"
/> />
@@ -89,13 +89,14 @@
<wd-datetime-picker <wd-datetime-picker
v-model="recordStore.bpForm.time" v-model="recordStore.bpForm.time"
v-model:visible="showTimePicker" v-model:visible="showTimePicker"
:max-date="Date.now()"
placeholder="请选择记录时间" placeholder="请选择记录时间"
/> />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useRecordStore } from '@/store/record' import { useRecordStore } from '@/store/record'
import { formatTime } from '@/utils' import { formatDateTime } from '@/utils'
const showTimePicker = ref(false) const showTimePicker = ref(false)
@@ -127,11 +128,8 @@ function saveBp() {
return return
} }
// TODO: 接接口保存 recordStore.submitBp()
console.log('保存血压:', recordStore.bpForm) uni.showToast({ title: '血压记录成功', icon: 'success' })
uni.showToast({ title: '血压记录已保存 ✅', icon: 'success' })
recordStore.showBpPopup = false
handeClosePopup() handeClosePopup()
} }

View File

@@ -51,7 +51,7 @@
title="时间" title="时间"
prop="time" prop="time"
is-link is-link
:value="formatTime(recordStore.glucoseForm.time)" :value="formatDateTime(recordStore.glucoseForm.time)"
placeholder="请选择记录时间" placeholder="请选择记录时间"
@click="showTimePicker = true" @click="showTimePicker = true"
/> />
@@ -75,13 +75,14 @@
<wd-datetime-picker <wd-datetime-picker
v-model="recordStore.glucoseForm.time" v-model="recordStore.glucoseForm.time"
v-model:visible="showTimePicker" v-model:visible="showTimePicker"
:max-date="Date.now()"
placeholder="请选择记录时间" placeholder="请选择记录时间"
/> />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useRecordStore } from '@/store/record' import { useRecordStore } from '@/store/record'
import { formatTime } from '@/utils' import { formatDateTime } from '@/utils'
const showTimePicker = ref(false) const showTimePicker = ref(false)
@@ -122,11 +123,8 @@ function saveGlucose() {
return return
} }
// TODO: 接接口保存 recordStore.submitGlucose()
console.log('保存血糖:', recordStore.glucoseForm) uni.showToast({ title: '血糖记录成功', icon: 'success' })
uni.showToast({ title: '血糖记录已保存 ✅', icon: 'success' })
recordStore.showGlucosePopup = false
handeClosePopup() handeClosePopup()
} }

View File

@@ -47,58 +47,22 @@ function onSelect(item: { key: string, label: string }) {
recordStore.showRecordPopup = false recordStore.showRecordPopup = false
if (item.key === 'weight') { if (item.key === 'weight') {
recordStore.weightForm.value = 70 recordStore.openWeight()
recordStore.weightForm.time = Date.now()
recordStore.weightForm.note = ''
setTimeout(() => {
recordStore.showWeightPopup = true
}, 300)
} }
else if (item.key === 'sleep') { else if (item.key === 'sleep') {
recordStore.sleepForm.startTime = Date.now() - 8 * 60 * 60 * 1000 recordStore.openSleep()
recordStore.sleepForm.endTime = Date.now()
recordStore.sleepForm.time = Date.now()
recordStore.sleepForm.note = ''
setTimeout(() => {
recordStore.showSleepPopup = true
}, 300)
} }
else if (item.key === 'sport') { else if (item.key === 'sport') {
recordStore.sportForm.type = '跑步' recordStore.openSport()
recordStore.sportForm.duration = 30
recordStore.sportForm.time = Date.now()
recordStore.sportForm.note = ''
setTimeout(() => {
recordStore.showSportPopup = true
}, 300)
} }
else if (item.key === 'bp') { else if (item.key === 'bp') {
recordStore.bpForm.systolic = 120 recordStore.openBp()
recordStore.bpForm.diastolic = 80
recordStore.bpForm.heartRate = 80
recordStore.bpForm.time = Date.now()
recordStore.bpForm.note = ''
setTimeout(() => {
recordStore.showBpPopup = true
}, 300)
} }
else if (item.key === 'glucose') { else if (item.key === 'glucose') {
recordStore.glucoseForm.value = 5.4 recordStore.openGlucose()
recordStore.glucoseForm.period = '空腹'
recordStore.glucoseForm.time = Date.now()
recordStore.glucoseForm.note = ''
setTimeout(() => {
recordStore.showGlucosePopup = true
}, 300)
} }
else if (item.key === 'temp') { else if (item.key === 'temp') {
recordStore.tempForm.value = 36.5 recordStore.openTemp()
recordStore.tempForm.location = '腋下'
recordStore.tempForm.time = Date.now()
recordStore.tempForm.note = ''
setTimeout(() => {
recordStore.showTempPopup = true
}, 300)
} }
else { else {
// 其他类型暂时提示 // 其他类型暂时提示

View File

@@ -23,7 +23,7 @@
title="开始" title="开始"
prop="startTime" prop="startTime"
is-link is-link
:value="formatTime(recordStore.sleepForm.startTime)" :value="formatDateTime(recordStore.sleepForm.startTime)"
placeholder="请选择开始时间" placeholder="请选择开始时间"
@click="showStartPicker = true" @click="showStartPicker = true"
/> />
@@ -32,7 +32,7 @@
title="结束" title="结束"
prop="endTime" prop="endTime"
is-link is-link
:value="formatTime(recordStore.sleepForm.endTime)" :value="formatDateTime(recordStore.sleepForm.endTime)"
placeholder="请选择结束时间" placeholder="请选择结束时间"
@click="showEndPicker = true" @click="showEndPicker = true"
/> />
@@ -48,7 +48,7 @@
title="时间" title="时间"
prop="time" prop="time"
is-link is-link
:value="formatTime(recordStore.sleepForm.time)" :value="formatDateTime(recordStore.sleepForm.time)"
placeholder="请选择记录时间" placeholder="请选择记录时间"
@click="showTimePicker = true" @click="showTimePicker = true"
/> />
@@ -85,13 +85,14 @@
<wd-datetime-picker <wd-datetime-picker
v-model="recordStore.sleepForm.time" v-model="recordStore.sleepForm.time"
v-model:visible="showTimePicker" v-model:visible="showTimePicker"
:max-date="Date.now()"
placeholder="请选择记录时间" placeholder="请选择记录时间"
/> />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useRecordStore } from '@/store/record' import { useRecordStore } from '@/store/record'
import { formatTime } from '@/utils' import { formatDateTime } from '@/utils'
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
const recordStore = useRecordStore() const recordStore = useRecordStore()
@@ -149,14 +150,8 @@ function saveSleep() {
return return
} }
// TODO: 接接口保存 recordStore.submitSleep()
console.log('保存睡眠:', { uni.showToast({ title: '睡眠记录成功', icon: 'success' })
...recordStore.sleepForm,
duration: sleepDuration.value,
})
uni.showToast({ title: '睡眠记录已保存 ✅', icon: 'success' })
recordStore.showSleepPopup = false
handeClosePopup() handeClosePopup()
} }

View File

@@ -20,8 +20,8 @@
border border
> >
<!-- 运动类型 --> <!-- 运动类型 -->
<wd-form-item title="类型" prop="type"> <wd-form-item title="类型" prop="category">
<wd-radio-group v-model="recordStore.sportForm.type" type="button"> <wd-radio-group v-model="recordStore.sportForm.category" type="button">
<wd-radio <wd-radio
v-for="item in sportTypeColumns" v-for="item in sportTypeColumns"
:key="item.value" :key="item.value"
@@ -51,7 +51,7 @@
title="时间" title="时间"
prop="time" prop="time"
is-link is-link
:value="formatTime(recordStore.sportForm.time)" :value="formatDateTime(recordStore.sportForm.time)"
placeholder="请选择记录时间" placeholder="请选择记录时间"
@click="showTimePicker = true" @click="showTimePicker = true"
/> />
@@ -74,13 +74,14 @@
<wd-datetime-picker <wd-datetime-picker
v-model="recordStore.sportForm.time" v-model="recordStore.sportForm.time"
v-model:visible="showTimePicker" v-model:visible="showTimePicker"
:max-date="Date.now()"
placeholder="请选择记录时间" placeholder="请选择记录时间"
/> />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useRecordStore } from '@/store/record' import { useRecordStore } from '@/store/record'
import { formatTime } from '@/utils' import { formatDateTime } from '@/utils'
import { ref } from 'vue' import { ref } from 'vue'
const showTimePicker = ref(false) const showTimePicker = ref(false)
@@ -103,9 +104,9 @@ function handeClosePopup() {
} }
function saveSport() { function saveSport() {
const { type, duration, time } = recordStore.sportForm const { category, duration, time } = recordStore.sportForm
if (!type) { if (!category) {
uni.showToast({ title: '请选择运动类型', icon: 'none' }) uni.showToast({ title: '请选择运动类型', icon: 'none' })
return return
} }
@@ -118,11 +119,8 @@ function saveSport() {
return return
} }
// TODO: 接接口保存 recordStore.submitSport()
console.log('保存运动:', recordStore.sportForm) uni.showToast({ title: '运动记录成功', icon: 'success' })
uni.showToast({ title: '运动记录已保存 ✅', icon: 'success' })
recordStore.showSportPopup = false
handeClosePopup() handeClosePopup()
} }

View File

@@ -51,7 +51,7 @@
title="时间" title="时间"
prop="time" prop="time"
is-link is-link
:value="formatTime(recordStore.tempForm.time)" :value="formatDateTime(recordStore.tempForm.time)"
placeholder="请选择记录时间" placeholder="请选择记录时间"
@click="showTimePicker = true" @click="showTimePicker = true"
/> />
@@ -75,13 +75,14 @@
<wd-datetime-picker <wd-datetime-picker
v-model="recordStore.tempForm.time" v-model="recordStore.tempForm.time"
v-model:visible="showTimePicker" v-model:visible="showTimePicker"
:max-date="Date.now()"
placeholder="请选择记录时间" placeholder="请选择记录时间"
/> />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useRecordStore } from '@/store/record' import { useRecordStore } from '@/store/record'
import { formatTime } from '@/utils' import { formatDateTime } from '@/utils'
import { ref } from 'vue' import { ref } from 'vue'
const showTimePicker = ref(false) const showTimePicker = ref(false)
@@ -121,11 +122,8 @@ function saveTemp() {
return return
} }
// TODO: 接接口保存 recordStore.submitTemp()
console.log('保存体温:', recordStore.tempForm) uni.showToast({ title: '体温记录成功', icon: 'success' })
uni.showToast({ title: '体温记录已保存 ✅', icon: 'success' })
recordStore.showTempPopup = false
handeClosePopup() handeClosePopup()
} }

View File

@@ -24,7 +24,7 @@
title="时间" title="时间"
prop="time" prop="time"
is-link is-link
:value="formatTime(recordStore.weightForm.time)" :value="formatDateTime(recordStore.weightForm.time)"
placeholder="请选择记录时间" placeholder="请选择记录时间"
@click="showTimePicker = true" @click="showTimePicker = true"
/> />
@@ -46,13 +46,14 @@
<wd-datetime-picker <wd-datetime-picker
v-model="recordStore.weightForm.time" v-model="recordStore.weightForm.time"
v-model:visible="showTimePicker" v-model:visible="showTimePicker"
:max-date="Date.now()"
placeholder="请选择记录时间" placeholder="请选择记录时间"
/> />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useRecordStore } from '@/store/record' import { useRecordStore } from '@/store/record'
import { formatTime } from '@/utils' import { formatDateTime } from '@/utils'
const showTimePicker = ref(false) const showTimePicker = ref(false)
@@ -69,14 +70,8 @@ function saveWeight() {
uni.showToast({ title: '请输入体重', icon: 'none' }) uni.showToast({ title: '请输入体重', icon: 'none' })
} }
else { else {
// TODO: 这里接接口保存 recordStore.submitWeight()
console.log('保存体重:', recordStore.weightForm) uni.showToast({ title: '体重记录成功', icon: 'success' })
uni.showToast({ title: '体重已保存 ✅', icon: 'success' })
recordStore.showWeightPopup = false
// 保存后刷新首页概览TODO: 接真实数据后打开)
// overviewItems[0].value = weightForm.value + 'kg'
} }
handeClosePopup() handeClosePopup()

View File

@@ -79,6 +79,7 @@
<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'
defineOptions({ defineOptions({
name: 'Home', name: 'Home',
@@ -110,6 +111,13 @@ const plans = [
{ icon: '🛏️', label: '23:00 前入睡', done: false }, { icon: '🛏️', label: '23:00 前入睡', done: false },
] ]
onMounted(() => {
const result = getDayRecords(getDateStr(Date.now()))
console.log(result)
console.log(getMonthRecords('2026-08'))
})
function handelFabClick() { function handelFabClick() {
recordStore.showRecordPopup = true recordStore.showRecordPopup = true
recordStore.showFab = false recordStore.showFab = false

View File

@@ -41,7 +41,7 @@
<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.datetime }}</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 }} {{ item.status }}
</wd-tag> </wd-tag>
@@ -52,7 +52,7 @@
<view class="h-7 w-7 flex items-center justify-center rounded-lg text-sm" :class="item.color"> <view class="h-7 w-7 flex items-center justify-center rounded-lg text-sm" :class="item.color">
<text>{{ item.icon }}</text> <text>{{ item.icon }}</text>
</view> </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> </view>
<!-- 第三行数值 --> <!-- 第三行数值 -->
@@ -116,6 +116,7 @@
<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'
definePage({ definePage({
style: { style: {
@@ -133,91 +134,96 @@ const displayDate = computed(() => {
return `${d.getFullYear()}${d.getMonth() + 1}` return `${d.getFullYear()}${d.getMonth() + 1}`
}) })
const records = ref([])
onMounted(() => {
records.value = getMonthRecords(getMonthStr(currentDate.value))
})
function changeMonth(offset: number) { function changeMonth(offset: number) {
const d = new Date(currentDate.value) const d = new Date(currentDate.value)
d.setMonth(d.getMonth() + offset) d.setMonth(d.getMonth() + offset)
currentDate.value = d.getTime() currentDate.value = d.getTime()
records.value = getMonthRecords(getMonthStr(currentDate.value))
} }
function goToday() { function goToday() {
currentDate.value = Date.now() currentDate.value = Date.now()
records.value = getMonthRecords(getMonthStr(currentDate.value))
} }
function openDatePicker() { function openDatePicker() {
showDatePicker.value = true showDatePicker.value = true
} }
function onDateConfirm() { function onDateConfirm({ value }) {
// picker 确认后 currentDate 已自动更新 records.value = getMonthRecords(getMonthStr(currentDate.value))
} }
// ===== 时间轴数据 ===== // ===== 时间轴数据 =====
const records = ref([ // const records = ref([
{ // {
icon: '😴', // icon: '😴',
label: '睡眠', // label: '睡眠',
value: '7.5 h', // value: '7.5 h',
datetime: '2026-08-20 07:30', // datetime: '2026-08-20 07:30',
note: '质量良好深睡2.1h', // note: '质量良好深睡2.1h',
color: 'bg-[#2E5B8C]', // color: 'bg-[#2E5B8C]',
status: '良好', // status: '良好',
}, // },
{ // {
icon: '⚖️', // icon: '⚖️',
label: '体重', // label: '体重',
value: '68.5 kg', // value: '68.5 kg',
datetime: '2026-08-20 08:30', // datetime: '2026-08-20 08:30',
note: '早起空腹,较昨日 -0.3kg', // note: '早起空腹,较昨日 -0.3kg',
color: 'bg-[#E89E3A]', // color: 'bg-[#E89E3A]',
status: '正常', // status: '正常',
}, // },
{ // {
icon: '❤️', // icon: '❤️',
label: '血压', // label: '血压',
value: '118/76 mmHg', // value: '118/76 mmHg',
datetime: '2026-08-20 09:00', // datetime: '2026-08-20 09:00',
note: '服药后测量静坐5分钟', // note: '服药后测量静坐5分钟',
color: 'bg-[#C04A3C]', // color: 'bg-[#C04A3C]',
status: '正常', // status: '正常',
}, // },
{ // {
icon: '📈', // icon: '📈',
label: '血糖', // label: '血糖',
value: '5.2 mmol/L', // value: '5.2 mmol/L',
datetime: '2026-08-20 10:30', // datetime: '2026-08-20 10:30',
note: '早餐后2小时', // note: '早餐后2小时',
color: 'bg-[#9C27B0]', // color: 'bg-[#9C27B0]',
status: '正常', // status: '正常',
}, // },
{ // {
icon: '🏃', // icon: '🏃',
label: '运动', // label: '运动',
value: '30 min', // value: '30 min',
datetime: '2026-08-20 14:00', // datetime: '2026-08-20 14:00',
note: '快走,公园环道', // note: '快走,公园环道',
color: 'bg-[#3A7C5B]', // color: 'bg-[#3A7C5B]',
status: '达标', // status: '达标',
}, // },
{ // {
icon: '💓', // icon: '💓',
label: '心率', // label: '心率',
value: '68 bpm', // value: '68 bpm',
datetime: '2026-08-20 16:00', // datetime: '2026-08-20 16:00',
note: '静息心率', // note: '静息心率',
color: 'bg-[#D32F2F]', // color: 'bg-[#D32F2F]',
status: '正常', // status: '正常',
}, // },
]) // ])
const aiSummary = ref({ const aiSummary = ref({
dateRange: '基于今日数据', dateRange: '基于今日数据',
text: '今日整体健康状态良好。体重平稳,血压正常,建议增加有氧运动频率,保持当前作息规律。', text: '今日整体健康状态良好。体重平稳,血压正常,建议增加有氧运动频率,保持当前作息规律。',
tags: ['🟢 体重稳定', '❤️ 血压正常', '🏃 建议多运动', '😴 睡眠良好'], tags: ['🟢 体重稳定', '❤️ 血压正常', '🏃 建议多运动', '😴 睡眠良好'],
}) })
onMounted(() => {
// 这里可以接接口拉取当天的记录数据
})
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -1,79 +1,154 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { addBloodPressure, addBloodSugar, addSleep, addSport, addTemperature, addWeight } from '@/utils/record'
export const useRecordStore = defineStore('record', { export const useRecordStore = defineStore('record', {
state: () => ({ state: () => ({
list: [] as any[],
loading: false,
showFab: true, showFab: true,
showRecordPopup: false, showRecordPopup: false, // 指标选择弹窗
showWeightPopup: false, showWeightPopup: false,
weightForm: {
value: 1,
note: '',
time: '' as number | string,
},
showSleepPopup: false, showSleepPopup: false,
sleepForm: {
startTime: '' as number | string,
endTime: '' as number | string,
note: '',
time: '' as number | string,
},
showSportPopup: false, showSportPopup: false,
sportForm: {
type: '',
duration: 0,
note: '',
time: '' as number | string,
},
showBpPopup: false, 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: { bpForm: {
systolic: 0, systolic: 0,
diastolic: 0, diastolic: 0,
heartRate: 0, heartRate: 0,
time: Date.now(),
note: '', note: '',
time: Date.now(),
}, },
showGlucosePopup: false,
glucoseForm: { glucoseForm: {
value: 0, value: 0,
period: '' as string, period: '',
time: Date.now(),
note: '', note: '',
time: Date.now(),
}, },
showTempPopup: false,
tempForm: { tempForm: {
value: 36.5, value: 36.5,
location: '' as string, location: '',
time: Date.now(),
note: '', note: '',
time: Date.now(),
}, },
}), }),
actions: { actions: {
setLoading(val: boolean) { openWeight() {
this.loading = val this.resetWeight()
this.showWeightPopup = true
}, },
openSleep() {
async fetchList() { this.resetSleep()
this.loading = true this.showSleepPopup = true
try {
const res = await fetch('/api/record/list')
this.list = await res.json()
}
finally {
this.loading = false
}
}, },
openSport() {
addRecord(item: any) { this.resetSport()
this.list.push(item) this.showSportPopup = true
}, },
openBp() {
removeRecord(id: string) { this.resetBp()
this.list = this.list.filter(item => item.id !== id) 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() }
}, },
}, },
}) })

View File

@@ -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 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) if (!ts)
return '' return ''
const d = new Date(ts) const d = new Date(ts)

126
src/utils/record.ts Normal file
View 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)
})
}