feat: 增加存储功能
This commit is contained in:
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
// 其他类型暂时提示
|
// 其他类型暂时提示
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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() }
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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
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