feat: 增加记录类型弹窗
This commit is contained in:
119
src/components/Home/BpPopup.vue
Normal file
119
src/components/Home/BpPopup.vue
Normal file
@@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<wd-popup
|
||||
v-model="recordStore.showBpPopup"
|
||||
position="bottom"
|
||||
:safe-area-inset-bottom="true"
|
||||
:close-on-click-modal="true"
|
||||
custom-style="border-radius: 32rpx 32rpx 0 0; padding: 40rpx 32rpx 150rpx;"
|
||||
@close="handeClosePopup"
|
||||
>
|
||||
<view class="mb-5 text-center text-base text-gray-700 font-semibold">
|
||||
🩺 记录血压
|
||||
</view>
|
||||
|
||||
<wd-form
|
||||
ref="form"
|
||||
:model="recordStore.bpForm"
|
||||
layout="horizontal"
|
||||
title-width="80"
|
||||
size="large"
|
||||
border
|
||||
>
|
||||
<!-- 收缩压(高压) -->
|
||||
<wd-form-item title="收缩压" prop="systolic">
|
||||
<view class="flex items-center gap-2">
|
||||
<wd-input-number
|
||||
v-model="recordStore.bpForm.systolic"
|
||||
:min="60"
|
||||
:max="250"
|
||||
:step="1"
|
||||
:decimal-length="0"
|
||||
/>
|
||||
<text class="shrink-0 text-sm text-gray-400">mmHg</text>
|
||||
</view>
|
||||
</wd-form-item>
|
||||
|
||||
<!-- 舒张压(低压) -->
|
||||
<wd-form-item title="舒张压" prop="diastolic">
|
||||
<view class="flex items-center gap-2">
|
||||
<wd-input-number
|
||||
v-model="recordStore.bpForm.diastolic"
|
||||
:min="40"
|
||||
:max="180"
|
||||
:step="1"
|
||||
:decimal-length="0"
|
||||
/>
|
||||
<text class="shrink-0 text-sm text-gray-400">mmHg</text>
|
||||
</view>
|
||||
</wd-form-item>
|
||||
|
||||
<wd-form-item title="心率" prop="heartRate">
|
||||
<view class="flex items-center gap-2">
|
||||
<wd-input-number
|
||||
v-model="recordStore.bpForm.heartRate"
|
||||
:min="30"
|
||||
:max="220"
|
||||
:step="1"
|
||||
:decimal-length="0"
|
||||
/>
|
||||
<text class="shrink-0 text-sm text-gray-400">bpm</text>
|
||||
</view>
|
||||
</wd-form-item>
|
||||
|
||||
<!-- 备注 -->
|
||||
<wd-form-item title="备注" prop="note">
|
||||
<wd-input
|
||||
v-model="recordStore.bpForm.note"
|
||||
:maxlength="20"
|
||||
show-word-limit
|
||||
placeholder="请输入备注信息"
|
||||
/>
|
||||
</wd-form-item>
|
||||
|
||||
<wd-button type="success" class="mt-10 w-full" @click="saveBp">
|
||||
保存
|
||||
</wd-button>
|
||||
</wd-form>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRecordStore } from '@/store/record'
|
||||
|
||||
const recordStore = useRecordStore()
|
||||
|
||||
function handeClosePopup() {
|
||||
setTimeout(() => {
|
||||
recordStore.showFab = true
|
||||
}, 300)
|
||||
}
|
||||
|
||||
function saveBp() {
|
||||
const { systolic, diastolic, time } = recordStore.bpForm
|
||||
|
||||
if (!systolic) {
|
||||
uni.showToast({ title: '请输入收缩压', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!diastolic) {
|
||||
uni.showToast({ title: '请输入舒张压', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (systolic <= diastolic) {
|
||||
uni.showToast({ title: '收缩压应高于舒张压', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!time) {
|
||||
uni.showToast({ title: '请选择记录时间', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: 接接口保存
|
||||
console.log('保存血压:', recordStore.bpForm)
|
||||
|
||||
uni.showToast({ title: '血压记录已保存 ✅', icon: 'success' })
|
||||
recordStore.showBpPopup = false
|
||||
|
||||
handeClosePopup()
|
||||
}
|
||||
</script>
|
||||
114
src/components/Home/GlucosePopup.vue
Normal file
114
src/components/Home/GlucosePopup.vue
Normal file
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<wd-popup
|
||||
v-model="recordStore.showGlucosePopup"
|
||||
position="bottom"
|
||||
:safe-area-inset-bottom="true"
|
||||
:close-on-click-modal="true"
|
||||
custom-style="border-radius: 32rpx 32rpx 0 0; padding: 40rpx 32rpx 150rpx;"
|
||||
@close="handeClosePopup"
|
||||
>
|
||||
<view class="mb-5 text-center text-base text-gray-700 font-semibold">
|
||||
💉 记录血糖
|
||||
</view>
|
||||
|
||||
<wd-form
|
||||
ref="form"
|
||||
:model="recordStore.glucoseForm"
|
||||
layout="horizontal"
|
||||
title-width="80"
|
||||
size="large"
|
||||
border
|
||||
>
|
||||
<!-- 血糖值 -->
|
||||
<wd-form-item title="血糖值" prop="value">
|
||||
<view class="flex items-center gap-2">
|
||||
<wd-input-number
|
||||
v-model="recordStore.glucoseForm.value"
|
||||
:min="1"
|
||||
:max="30"
|
||||
:step="0.1"
|
||||
:precision="1"
|
||||
/>
|
||||
<text class="shrink-0 text-sm text-gray-400">mmol/L</text>
|
||||
</view>
|
||||
</wd-form-item>
|
||||
|
||||
<!-- 测量时段 -->
|
||||
<wd-form-item title="时段" prop="period">
|
||||
<wd-radio-group v-model="recordStore.glucoseForm.period" type="button">
|
||||
<wd-radio
|
||||
v-for="item in periodColumns"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</wd-radio>
|
||||
</wd-radio-group>
|
||||
</wd-form-item>
|
||||
|
||||
<!-- 备注 -->
|
||||
<wd-form-item title="备注" prop="note">
|
||||
<wd-input
|
||||
v-model="recordStore.glucoseForm.note"
|
||||
:maxlength="20"
|
||||
show-word-limit
|
||||
placeholder="请输入备注信息"
|
||||
/>
|
||||
</wd-form-item>
|
||||
|
||||
<wd-button type="success" class="mt-10 w-full" @click="saveGlucose">
|
||||
保存
|
||||
</wd-button>
|
||||
</wd-form>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRecordStore } from '@/store/record'
|
||||
|
||||
const recordStore = useRecordStore()
|
||||
|
||||
const periodColumns = [
|
||||
{ label: '空腹', value: '空腹' },
|
||||
{ label: '餐后1h', value: '餐后1h' },
|
||||
{ label: '餐后2h', value: '餐后2h' },
|
||||
{ label: '睡前', value: '睡前' },
|
||||
{ label: '随机', value: '随机' },
|
||||
]
|
||||
|
||||
function handeClosePopup() {
|
||||
setTimeout(() => {
|
||||
recordStore.showFab = true
|
||||
}, 300)
|
||||
}
|
||||
|
||||
// ========== 保存 ==========
|
||||
function saveGlucose() {
|
||||
const { value, period, time } = recordStore.glucoseForm
|
||||
|
||||
if (!value) {
|
||||
uni.showToast({ title: '请输入血糖值', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (value < 1 || value > 30) {
|
||||
uni.showToast({ title: '血糖值范围异常,请确认', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!period) {
|
||||
uni.showToast({ title: '请选择测量时段', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!time) {
|
||||
uni.showToast({ title: '请选择记录时间', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: 接接口保存
|
||||
console.log('保存血糖:', recordStore.glucoseForm)
|
||||
|
||||
uni.showToast({ title: '血糖记录已保存 ✅', icon: 'success' })
|
||||
recordStore.showGlucosePopup = false
|
||||
|
||||
handeClosePopup()
|
||||
}
|
||||
</script>
|
||||
115
src/components/Home/RecordPopup.vue
Normal file
115
src/components/Home/RecordPopup.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<wd-popup
|
||||
v-model="recordStore.showRecordPopup"
|
||||
position="bottom"
|
||||
:safe-area-inset-bottom="true"
|
||||
:close-on-click-modal="true"
|
||||
custom-style="border-radius: 32rpx 32rpx 0 0; padding: 40rpx 32rpx 200rpx;"
|
||||
@close="handeClosePopup"
|
||||
>
|
||||
<view class="mb-5 text-center text-base text-gray-700 font-semibold">
|
||||
选择记录类型
|
||||
</view>
|
||||
<view class="grid grid-cols-3 gap-5">
|
||||
<view
|
||||
v-for="item in recordTypes"
|
||||
:key="item.key"
|
||||
class="flex flex-col items-center gap-2"
|
||||
@click="onSelect(item)"
|
||||
>
|
||||
<view
|
||||
class="h-14 w-14 flex items-center justify-center rounded-2xl text-xl"
|
||||
:class="item.bg"
|
||||
>
|
||||
<text>{{ item.icon }}</text>
|
||||
</view>
|
||||
<text class="text-xs text-gray-600">{{ item.label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useRecordStore } from '@/store/record'
|
||||
|
||||
const recordStore = useRecordStore()
|
||||
|
||||
const recordTypes = [
|
||||
{ key: 'weight', icon: '⚖️', label: '体重', bg: 'bg-[#fff3e0]' },
|
||||
{ key: 'sleep', icon: '😴', label: '睡眠', bg: 'bg-[#e3f2fd]' },
|
||||
{ key: 'sport', icon: '🏃', label: '运动', bg: 'bg-[#e8f5e9]' },
|
||||
{ key: 'bp', icon: '❤️', label: '血压', bg: 'bg-[#fce4ec]' },
|
||||
{ key: 'glucose', icon: '🩸', label: '血糖', bg: 'bg-[#f3e5f5]' },
|
||||
{ key: 'temp', icon: '🌡️', label: '体温', bg: 'bg-[#fbe9e7]' },
|
||||
]
|
||||
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
else {
|
||||
// 其他类型暂时提示
|
||||
uni.showToast({ title: `暂未开放:${item.label}`, icon: 'none' })
|
||||
handeClosePopup()
|
||||
}
|
||||
}
|
||||
|
||||
function handeClosePopup() {
|
||||
setTimeout(() => {
|
||||
recordStore.showFab = true
|
||||
}, 300)
|
||||
}
|
||||
</script>
|
||||
146
src/components/Home/SleepPopup.vue
Normal file
146
src/components/Home/SleepPopup.vue
Normal file
@@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<wd-popup
|
||||
v-model="recordStore.showSleepPopup"
|
||||
position="bottom"
|
||||
:safe-area-inset-bottom="true"
|
||||
:close-on-click-modal="true"
|
||||
custom-style="border-radius: 32rpx 32rpx 0 0; padding: 40rpx 32rpx 150rpx;"
|
||||
@close="handeClosePopup"
|
||||
>
|
||||
<view class="mb-5 text-center text-base text-gray-700 font-semibold">
|
||||
💤 记录睡眠
|
||||
</view>
|
||||
|
||||
<wd-form
|
||||
ref="form"
|
||||
:model="recordStore.sleepForm"
|
||||
layout="horizontal"
|
||||
title-width="80"
|
||||
size="large"
|
||||
border
|
||||
>
|
||||
<wd-form-item
|
||||
title="开始"
|
||||
prop="startTime"
|
||||
is-link
|
||||
:value="formatTime(recordStore.sleepForm.startTime)"
|
||||
placeholder="请选择开始时间"
|
||||
@click="showStartPicker = true"
|
||||
/>
|
||||
|
||||
<wd-form-item
|
||||
title="结束"
|
||||
prop="endTime"
|
||||
is-link
|
||||
:value="formatTime(recordStore.sleepForm.endTime)"
|
||||
placeholder="请选择结束时间"
|
||||
@click="showEndPicker = true"
|
||||
/>
|
||||
|
||||
<wd-form-item title="时长">
|
||||
<view class="py-1 text-gray-800">
|
||||
{{ sleepDuration }}
|
||||
</view>
|
||||
</wd-form-item>
|
||||
|
||||
<wd-form-item title="备注" prop="note">
|
||||
<wd-input
|
||||
v-model="recordStore.sleepForm.note"
|
||||
:maxlength="20"
|
||||
show-word-limit
|
||||
placeholder="请输入备注信息"
|
||||
/>
|
||||
</wd-form-item>
|
||||
|
||||
<wd-button type="success" class="mt-10 w-full" @click="saveSleep">
|
||||
保存
|
||||
</wd-button>
|
||||
</wd-form>
|
||||
</wd-popup>
|
||||
|
||||
<!-- 开始时间选择器 -->
|
||||
<wd-datetime-picker
|
||||
v-model="recordStore.sleepForm.startTime"
|
||||
v-model:visible="showStartPicker"
|
||||
placeholder="请选择开始时间"
|
||||
/>
|
||||
|
||||
<!-- 结束时间选择器 -->
|
||||
<wd-datetime-picker
|
||||
v-model="recordStore.sleepForm.endTime"
|
||||
v-model:visible="showEndPicker"
|
||||
placeholder="请选择结束时间"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRecordStore } from '@/store/record'
|
||||
import { formatTime } from '@/utils'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const recordStore = useRecordStore()
|
||||
|
||||
const showStartPicker = ref(false)
|
||||
const showEndPicker = ref(false)
|
||||
|
||||
const sleepDuration = computed(() => {
|
||||
const start = recordStore.sleepForm.startTime
|
||||
const end = recordStore.sleepForm.endTime
|
||||
|
||||
if (!start || !end)
|
||||
return '请选择开始和结束时间'
|
||||
|
||||
const diffMs = new Date(end).getTime() - new Date(start).getTime()
|
||||
|
||||
if (diffMs <= 0)
|
||||
return '结束时间需晚于开始时间'
|
||||
|
||||
const totalMinutes = Math.floor(diffMs / 60000)
|
||||
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() {
|
||||
setTimeout(() => {
|
||||
recordStore.showFab = true
|
||||
}, 300)
|
||||
}
|
||||
|
||||
function saveSleep() {
|
||||
const { startTime, endTime, time, note } = recordStore.sleepForm
|
||||
|
||||
if (!startTime) {
|
||||
uni.showToast({ title: '请选择开始时间', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!endTime) {
|
||||
uni.showToast({ title: '请选择结束时间', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (new Date(endTime).getTime() <= new Date(startTime).getTime()) {
|
||||
uni.showToast({ title: '结束时间需晚于开始时间', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!time) {
|
||||
uni.showToast({ title: '请选择记录时间', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: 接接口保存
|
||||
console.log('保存睡眠:', {
|
||||
...recordStore.sleepForm,
|
||||
duration: sleepDuration.value,
|
||||
})
|
||||
|
||||
uni.showToast({ title: '睡眠记录已保存 ✅', icon: 'success' })
|
||||
recordStore.showSleepPopup = false
|
||||
|
||||
handeClosePopup()
|
||||
}
|
||||
</script>
|
||||
109
src/components/Home/SportPopup.vue
Normal file
109
src/components/Home/SportPopup.vue
Normal file
@@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<wd-popup
|
||||
v-model="recordStore.showSportPopup"
|
||||
position="bottom"
|
||||
:safe-area-inset-bottom="true"
|
||||
:close-on-click-modal="true"
|
||||
custom-style="border-radius: 32rpx 32rpx 0 0; padding: 40rpx 32rpx 150rpx;"
|
||||
@close="handeClosePopup"
|
||||
>
|
||||
<view class="mb-5 text-center text-base text-gray-700 font-semibold">
|
||||
🏃 记录运动
|
||||
</view>
|
||||
|
||||
<wd-form
|
||||
ref="form"
|
||||
:model="recordStore.sportForm"
|
||||
layout="horizontal"
|
||||
title-width="80"
|
||||
size="large"
|
||||
border
|
||||
>
|
||||
<!-- 运动类型 -->
|
||||
<wd-form-item title="类型" prop="type">
|
||||
<wd-radio-group v-model="recordStore.sportForm.type" type="button">
|
||||
<wd-radio
|
||||
v-for="item in sportTypeColumns"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</wd-radio>
|
||||
</wd-radio-group>
|
||||
</wd-form-item>
|
||||
|
||||
<!-- 运动时长 -->
|
||||
<wd-form-item title="时长" prop="duration">
|
||||
<view class="flex items-center gap-2">
|
||||
<wd-input-number
|
||||
v-model="recordStore.sportForm.duration"
|
||||
:min="1"
|
||||
:max="600"
|
||||
:step="5"
|
||||
:decimal-length="0"
|
||||
/>
|
||||
<text class="shrink-0 text-sm text-gray-400">分钟</text>
|
||||
</view>
|
||||
</wd-form-item>
|
||||
|
||||
<wd-form-item title="备注" prop="note">
|
||||
<wd-input
|
||||
v-model="recordStore.sportForm.note"
|
||||
:maxlength="20"
|
||||
show-word-limit
|
||||
placeholder="请输入备注信息"
|
||||
/>
|
||||
</wd-form-item>
|
||||
|
||||
<wd-button type="success" class="mt-10 w-full" @click="saveSport">
|
||||
保存
|
||||
</wd-button>
|
||||
</wd-form>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRecordStore } from '@/store/record'
|
||||
|
||||
const recordStore = useRecordStore()
|
||||
|
||||
const sportTypeColumns = [
|
||||
{ label: '🏃 跑步', value: '跑步' },
|
||||
{ label: '🏊 游泳', value: '游泳' },
|
||||
{ label: '🏋️ 力量', value: '力量' },
|
||||
{ label: '⚽ 球类', value: '球类' },
|
||||
{ label: '🏊 跳绳', value: '跳绳' },
|
||||
{ label: '其他', value: '其他' },
|
||||
]
|
||||
|
||||
function handeClosePopup() {
|
||||
setTimeout(() => {
|
||||
recordStore.showFab = true
|
||||
}, 300)
|
||||
}
|
||||
|
||||
function saveSport() {
|
||||
const { type, duration, time } = recordStore.sportForm
|
||||
|
||||
if (!type) {
|
||||
uni.showToast({ title: '请选择运动类型', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!duration || duration <= 0) {
|
||||
uni.showToast({ title: '请输入运动时长', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!time) {
|
||||
uni.showToast({ title: '请选择记录时间', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: 接接口保存
|
||||
console.log('保存运动:', recordStore.sportForm)
|
||||
|
||||
uni.showToast({ title: '运动记录已保存 ✅', icon: 'success' })
|
||||
recordStore.showSportPopup = false
|
||||
|
||||
handeClosePopup()
|
||||
}
|
||||
</script>
|
||||
112
src/components/Home/TempPopup.vue
Normal file
112
src/components/Home/TempPopup.vue
Normal file
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<wd-popup
|
||||
v-model="recordStore.showTempPopup"
|
||||
position="bottom"
|
||||
:safe-area-inset-bottom="true"
|
||||
:close-on-click-modal="true"
|
||||
custom-style="border-radius: 32rpx 32rpx 0 0; padding: 40rpx 32rpx 150rpx;"
|
||||
@close="handeClosePopup"
|
||||
>
|
||||
<view class="mb-5 text-center text-base text-gray-700 font-semibold">
|
||||
🌡️ 记录体温
|
||||
</view>
|
||||
|
||||
<wd-form
|
||||
ref="form"
|
||||
:model="recordStore.tempForm"
|
||||
layout="horizontal"
|
||||
title-width="80"
|
||||
size="large"
|
||||
border
|
||||
>
|
||||
<!-- 体温值 -->
|
||||
<wd-form-item title="体温" prop="value">
|
||||
<view class="flex items-center gap-2">
|
||||
<wd-input-number
|
||||
v-model="recordStore.tempForm.value"
|
||||
:min="35"
|
||||
:max="42"
|
||||
:step="0.1"
|
||||
:precision="1"
|
||||
/>
|
||||
<text class="shrink-0 text-sm text-gray-400">℃</text>
|
||||
</view>
|
||||
</wd-form-item>
|
||||
|
||||
<!-- 测量部位 -->
|
||||
<wd-form-item title="部位" prop="location">
|
||||
<wd-radio-group v-model="recordStore.tempForm.location" type="button">
|
||||
<wd-radio
|
||||
v-for="item in locationColumns"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</wd-radio>
|
||||
</wd-radio-group>
|
||||
</wd-form-item>
|
||||
|
||||
<!-- 备注 -->
|
||||
<wd-form-item title="备注" prop="note">
|
||||
<wd-input
|
||||
v-model="recordStore.tempForm.note"
|
||||
:maxlength="20"
|
||||
show-word-limit
|
||||
placeholder="请输入备注信息"
|
||||
/>
|
||||
</wd-form-item>
|
||||
|
||||
<wd-button type="success" class="mt-10 w-full" @click="saveTemp">
|
||||
保存
|
||||
</wd-button>
|
||||
</wd-form>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRecordStore } from '@/store/record'
|
||||
|
||||
const recordStore = useRecordStore()
|
||||
|
||||
const locationColumns = [
|
||||
{ label: '腋下', value: '腋下' },
|
||||
{ label: '口腔', value: '口腔' },
|
||||
{ label: '耳温', value: '耳温' },
|
||||
{ label: '额温', value: '额温' },
|
||||
]
|
||||
|
||||
function handeClosePopup() {
|
||||
setTimeout(() => {
|
||||
recordStore.showFab = true
|
||||
}, 300)
|
||||
}
|
||||
|
||||
function saveTemp() {
|
||||
const { value, location, time } = recordStore.tempForm
|
||||
|
||||
if (!value) {
|
||||
uni.showToast({ title: '请输入体温', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (value < 35 || value > 42) {
|
||||
uni.showToast({ title: '体温范围异常,请确认', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!location) {
|
||||
uni.showToast({ title: '请选择测量部位', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!time) {
|
||||
uni.showToast({ title: '请选择记录时间', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: 接接口保存
|
||||
console.log('保存体温:', recordStore.tempForm)
|
||||
|
||||
uni.showToast({ title: '体温记录已保存 ✅', icon: 'success' })
|
||||
recordStore.showTempPopup = false
|
||||
|
||||
handeClosePopup()
|
||||
}
|
||||
</script>
|
||||
69
src/components/Home/WeightPopup.vue
Normal file
69
src/components/Home/WeightPopup.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<wd-popup
|
||||
v-model="recordStore.showWeightPopup"
|
||||
position="bottom"
|
||||
:safe-area-inset-bottom="true"
|
||||
:close-on-click-modal="true"
|
||||
custom-style="border-radius: 32rpx 32rpx 0 0; padding: 40rpx 32rpx 150rpx;"
|
||||
@close="handeClosePopup"
|
||||
>
|
||||
<view class="mb-5 text-center text-base text-gray-700 font-semibold">
|
||||
⚖️ 记录体重
|
||||
</view>
|
||||
|
||||
<wd-form ref="form" :model="recordStore.weightForm" layout="horizontal" title-width="80" size="large" border>
|
||||
<wd-form-item title="体重">
|
||||
<view class="flex items-center gap-2">
|
||||
<wd-input-number v-model="recordStore.weightForm.value" :min="1" :max="200" />
|
||||
<text class="shrink-0 text-sm text-gray-400">Kg</text>
|
||||
</view>
|
||||
</wd-form-item>
|
||||
|
||||
<wd-form-item title="备注" prop="budget">
|
||||
<wd-input
|
||||
v-model="recordStore.weightForm.note"
|
||||
:maxlength="20"
|
||||
show-word-limit
|
||||
placeholder="请输入备注信息"
|
||||
/>
|
||||
</wd-form-item>
|
||||
<wd-button type="success" class="mt-10 w-full" @click="saveWeight">
|
||||
保存
|
||||
</wd-button>
|
||||
</wd-form>
|
||||
</wd-popup>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRecordStore } from '@/store/record'
|
||||
|
||||
const recordStore = useRecordStore()
|
||||
|
||||
function handeClosePopup() {
|
||||
setTimeout(() => {
|
||||
recordStore.showFab = true
|
||||
}, 300)
|
||||
}
|
||||
|
||||
function saveWeight() {
|
||||
if (!recordStore.weightForm) {
|
||||
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'
|
||||
}
|
||||
|
||||
handeClosePopup()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
@@ -64,85 +64,21 @@
|
||||
</view>
|
||||
</wd-card>
|
||||
|
||||
<wd-popup
|
||||
v-model="showPopup"
|
||||
position="bottom"
|
||||
:safe-area-inset-bottom="true"
|
||||
:close-on-click-modal="true"
|
||||
custom-style="border-radius: 32rpx 32rpx 0 0; padding: 40rpx 32rpx 200rpx;"
|
||||
@close="handeClosePopup"
|
||||
>
|
||||
<view class="mb-5 text-center text-base text-gray-700 font-semibold">
|
||||
选择记录类型
|
||||
</view>
|
||||
<view class="grid grid-cols-4 gap-5">
|
||||
<view
|
||||
v-for="item in recordTypes"
|
||||
:key="item.key"
|
||||
class="flex flex-col items-center gap-2"
|
||||
@click="onSelect(item)"
|
||||
>
|
||||
<view
|
||||
class="h-14 w-14 flex items-center justify-center rounded-2xl text-xl"
|
||||
:class="item.bg"
|
||||
>
|
||||
<text>{{ item.icon }}</text>
|
||||
</view>
|
||||
<text class="text-xs text-gray-600">{{ item.label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</wd-popup>
|
||||
<record-popup />
|
||||
|
||||
<wd-popup
|
||||
v-model="showWeightPopup"
|
||||
position="bottom"
|
||||
:safe-area-inset-bottom="true"
|
||||
:close-on-click-modal="true"
|
||||
custom-style="border-radius: 32rpx 32rpx 0 0; padding: 40rpx 32rpx 150rpx;"
|
||||
@close="handeClosePopup"
|
||||
>
|
||||
<view class="mb-5 text-center text-base text-gray-700 font-semibold">
|
||||
⚖️ 记录体重(Kg)
|
||||
</view>
|
||||
<weight-popup />
|
||||
<sleep-popup />
|
||||
<sport-popup />
|
||||
<bp-popup />
|
||||
<glucose-popup />
|
||||
<temp-popup />
|
||||
|
||||
<wd-form ref="form" :model="weightForm" layout="horizontal" title-width="60" size="large" border>
|
||||
<wd-form-item title="体重">
|
||||
<wd-slider
|
||||
ref="sliderRef" v-model="weightForm.value"
|
||||
:marks="[40, 60, 80, 100]"
|
||||
:min="40" :max="100"
|
||||
/>
|
||||
</wd-form-item>
|
||||
<wd-form-item
|
||||
title="时间"
|
||||
prop="time"
|
||||
is-link
|
||||
:value="formatTime(weightForm.time)"
|
||||
placeholder="请选择时间"
|
||||
@click="showTimePicker = true"
|
||||
/>
|
||||
<wd-form-item title="备注" prop="budget">
|
||||
<wd-input
|
||||
v-model="weightForm.note"
|
||||
:maxlength="20"
|
||||
show-word-limit
|
||||
placeholder="请输入备注信息"
|
||||
/>
|
||||
</wd-form-item>
|
||||
<wd-button type="success" class="mt-10 w-full" @click="saveWeight">
|
||||
保存
|
||||
</wd-button>
|
||||
</wd-form>
|
||||
</wd-popup>
|
||||
|
||||
<wd-datetime-picker v-model="weightForm.time" v-model:visible="showTimePicker" placeholder="请选择时间" />
|
||||
|
||||
<wd-fab v-if="showFab" type="success" :gap="{ bottom: 60 }" :expandable="false" @click="handelFabClick" />
|
||||
<wd-fab v-if="recordStore.showFab" type="success" :gap="{ bottom: 60 }" :expandable="false" @click="handelFabClick" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { useRecordStore } from '@/store/record'
|
||||
|
||||
defineOptions({
|
||||
name: 'Home',
|
||||
@@ -155,10 +91,7 @@ definePage({
|
||||
},
|
||||
})
|
||||
|
||||
const showFab = ref(true)
|
||||
const showPopup = ref(false)
|
||||
const showWeightPopup = ref(false)
|
||||
const showTimePicker = ref(false)
|
||||
const recordStore = useRecordStore()
|
||||
|
||||
const greeting = '下午好,小明 👋'
|
||||
const today = '8月19日 周三'
|
||||
@@ -177,73 +110,8 @@ const plans = [
|
||||
{ icon: '🛏️', label: '23:00 前入睡', done: false },
|
||||
]
|
||||
|
||||
const recordTypes = [
|
||||
{ key: 'weight', icon: '⚖️', label: '体重', bg: 'bg-[#fff3e0]' },
|
||||
{ key: 'sleep', icon: '😴', label: '睡眠', bg: 'bg-[#e3f2fd]' },
|
||||
{ key: 'exercise', icon: '🏃', label: '运动', bg: 'bg-[#e8f5e9]' },
|
||||
{ key: 'bp', icon: '❤️', label: '血压', bg: 'bg-[#fce4ec]' },
|
||||
{ key: 'glucose', icon: '🩸', label: '血糖', bg: 'bg-[#f3e5f5]' },
|
||||
{ key: 'hr', icon: '💓', label: '心率', bg: 'bg-[#ffebee]' },
|
||||
{ key: 'temp', icon: '🌡️', label: '体温', bg: 'bg-[#fbe9e7]' },
|
||||
]
|
||||
|
||||
const weightForm = reactive({
|
||||
value: 70,
|
||||
time: '' as number | string,
|
||||
note: '',
|
||||
})
|
||||
|
||||
function handelFabClick() {
|
||||
showPopup.value = true
|
||||
showFab.value = false
|
||||
}
|
||||
|
||||
function onSelect(item: { key: string, label: string }) {
|
||||
showPopup.value = false
|
||||
|
||||
if (item.key === 'weight') {
|
||||
weightForm.value = 70
|
||||
weightForm.time = Date.now()
|
||||
weightForm.note = ''
|
||||
setTimeout(() => {
|
||||
showWeightPopup.value = true
|
||||
}, 300)
|
||||
}
|
||||
else {
|
||||
// 其他类型暂时提示
|
||||
uni.showToast({ title: `暂未开放:${item.label}`, icon: 'none' })
|
||||
}
|
||||
}
|
||||
|
||||
function formatTime(ts: string | number) {
|
||||
if (!ts)
|
||||
return ''
|
||||
const d = new Date(ts)
|
||||
const pad = (n: number) => String(n).padStart(2, '0')
|
||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
||||
}
|
||||
|
||||
function handeClosePopup() {
|
||||
setTimeout(() => {
|
||||
showFab.value = true
|
||||
}, 300)
|
||||
}
|
||||
|
||||
function saveWeight() {
|
||||
if (!weightForm.value) {
|
||||
uni.showToast({ title: '请输入体重', icon: 'none' })
|
||||
}
|
||||
else {
|
||||
// TODO: 这里接接口保存
|
||||
console.log('保存体重:', weightForm.value, 'kg', weightForm.note)
|
||||
|
||||
uni.showToast({ title: '体重已保存 ✅', icon: 'success' })
|
||||
showWeightPopup.value = false
|
||||
|
||||
// 保存后刷新首页概览(TODO: 接真实数据后打开)
|
||||
// overviewItems[0].value = weightForm.value + 'kg'
|
||||
}
|
||||
|
||||
handeClosePopup()
|
||||
recordStore.showRecordPopup = true
|
||||
recordStore.showFab = false
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
<template>
|
||||
<!-- 页面容器 -->
|
||||
<view class="min-h-screen bg-[#f2f4f3]">
|
||||
<!-- 顶部日期栏:左右箭头 + 日期居中 + 回到今天 -->
|
||||
<view class="sticky top-0 z-10 bg-white p-2 shadow-sm">
|
||||
<view class="sticky top-0 z-10 bg-white p-2">
|
||||
<view class="flex items-center justify-evenly">
|
||||
<wd-button icon="caret-left" type="success" :round="true" size="small" @click="changeDay(-1)" />
|
||||
<view class="flex flex-col items-center" @click="openDatePicker">
|
||||
@@ -17,7 +15,7 @@
|
||||
</view>
|
||||
|
||||
<!-- 时间轴区域 -->
|
||||
<view class="px-5 py-6">
|
||||
<view class="px-5 py-2">
|
||||
<!-- 时间轴容器 -->
|
||||
<view class="relative pl-6">
|
||||
<view
|
||||
|
||||
79
src/store/record.ts
Normal file
79
src/store/record.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useRecordStore = defineStore('record', {
|
||||
state: () => ({
|
||||
list: [] as any[],
|
||||
loading: false,
|
||||
showFab: true,
|
||||
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,
|
||||
bpForm: {
|
||||
systolic: 0,
|
||||
diastolic: 0,
|
||||
heartRate: 0,
|
||||
time: Date.now(),
|
||||
note: '',
|
||||
},
|
||||
showGlucosePopup: false,
|
||||
glucoseForm: {
|
||||
value: 0,
|
||||
period: '' as string,
|
||||
time: Date.now(),
|
||||
note: '',
|
||||
},
|
||||
showTempPopup: false,
|
||||
tempForm: {
|
||||
value: 36.5,
|
||||
location: '' as string,
|
||||
time: Date.now(),
|
||||
note: '',
|
||||
},
|
||||
}),
|
||||
actions: {
|
||||
setLoading(val: boolean) {
|
||||
this.loading = val
|
||||
},
|
||||
|
||||
async fetchList() {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await fetch('/api/record/list')
|
||||
this.list = await res.json()
|
||||
}
|
||||
finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
addRecord(item: any) {
|
||||
this.list.push(item)
|
||||
},
|
||||
|
||||
removeRecord(id: string) {
|
||||
this.list = this.list.filter(item => item.id !== id)
|
||||
},
|
||||
reset() {
|
||||
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -149,3 +149,11 @@ export const isDoubleTokenMode = import.meta.env.VITE_AUTH_MODE === 'double'
|
||||
* 通常为 /pages/index/index
|
||||
*/
|
||||
export const HOME_PAGE = `/${(pages as PageMetaDatum[]).find(page => page.type === 'home')?.path || (pages as PageMetaDatum[])[0].path}`
|
||||
|
||||
export function formatTime(ts: string | number) {
|
||||
if (!ts)
|
||||
return ''
|
||||
const d = new Date(ts)
|
||||
const pad = (n: number) => String(n).padStart(2, '0')
|
||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user