feat: 增加记录日历功能
This commit is contained in:
@@ -1,74 +1,75 @@
|
||||
<template>
|
||||
<view class="min-h-screen bg-[#f2f4f3]">
|
||||
<view class="sticky top-0 z-10 flex flex-col gap-2 bg-white p-2">
|
||||
<view class="flex items-center justify-evenly">
|
||||
<wd-button icon="caret-left" type="primary" :round="true" size="small" @click="changeMonth(-1)" />
|
||||
<view class="flex flex-col items-center" @click="openDatePicker">
|
||||
<text class="text-base text-gray-800 font-semibold">{{ displayDate }}</text>
|
||||
<view class="min-h-screen flex flex-col bg-gradient-to-b from-[#a78bfa] to-[#f3e8ff]">
|
||||
<wd-segmented v-model:value="appStore.viewRecordType" :options="viewTypeSegmentList"
|
||||
@change="handleChangeSegment"
|
||||
custom-style="align-self: center; width: 60%; margin: 10px;">
|
||||
<template #label="{ option }">
|
||||
<view class="name">{{ option.payload }}</view>
|
||||
</template>
|
||||
</wd-segmented>
|
||||
|
||||
<wd-card v-if="appStore.viewRecordType === 'calendar'" custom-class="!mx-2 !mb-0">
|
||||
<wu-calendar :date="selectDate" :selected="calendarSelect" :itemHeight="45" :insert="true" color="#8b5cf6"
|
||||
@change="handleChangeDate" @monthSwitch="monthSwitch"/>
|
||||
</wd-card>
|
||||
|
||||
<wd-card v-if="appStore.viewRecordType === 'list'">
|
||||
<view class="sticky top-0 z-10 flex flex-col gap-2 p-2">
|
||||
<view class="flex items-center justify-evenly">
|
||||
<wd-button icon="caret-left" type="primary" :round="true" size="small" @click="changeMonth(-1)" />
|
||||
<view class="flex flex-col items-center" @click="openDatePicker">
|
||||
<text class="text-base text-gray-800 font-semibold">{{ displayDate }}</text>
|
||||
</view>
|
||||
<wd-button icon="caret-right" type="primary" :round="true" size="small" @click="changeMonth(1)" />
|
||||
</view>
|
||||
<wd-button icon="caret-right" type="primary" :round="true" size="small" @click="changeMonth(1)" />
|
||||
|
||||
<wd-button type="primary" size="small" @click="goToday">
|
||||
回到今天
|
||||
</wd-button>
|
||||
<wd-segmented v-model:value="currentRecordTypeSegment"
|
||||
:options="recordTypeSegmentList"
|
||||
@change="handleChangeSegment">
|
||||
<template #label="{ option }">
|
||||
<view class="name">{{ option.payload }}</view>
|
||||
</template>
|
||||
</wd-segmented>
|
||||
</view>
|
||||
|
||||
<wd-segmented
|
||||
v-model:value="currentSegment"
|
||||
:options="segmentList"
|
||||
@change="handleChangeSegment"
|
||||
>
|
||||
<template #label="{ option }">
|
||||
<view class="name">{{ option.payload }}</view>
|
||||
</template>
|
||||
</wd-segmented>
|
||||
</view>
|
||||
</wd-card>
|
||||
|
||||
<!-- 时间轴区域 -->
|
||||
<view class="px-5 py-2">
|
||||
<wd-card>
|
||||
<wd-empty v-if="grouped.length === 0" icon="no-content" tip="暂无记录" />
|
||||
|
||||
<view v-for="group in grouped" :key="group.date" class="mb-6">
|
||||
<view class="mb-2 flex items-center gap-2 pl-1">
|
||||
<text class="text-sm text-gray-500 font-medium">{{ group.date }}</text>
|
||||
<view v-for="group in grouped" :key="group.date"
|
||||
class="mb-2 p-1 rounded odd:bg-gray-100 even:bg-white">
|
||||
<view class="mb-2 flex items-center gap-2">
|
||||
<text class="text-sm text-gray-500 font-bold">{{ group.date }}</text>
|
||||
<text class="text-sm text-gray-400">周{{ group.weekday }}</text>
|
||||
<view class="ml-2 h-[1px] flex-1 bg-gray-200" />
|
||||
</view>
|
||||
|
||||
<view
|
||||
v-for="(item, idx) in group.items"
|
||||
:key="item.id || idx"
|
||||
class="mb-5 last:mb-0"
|
||||
class="mb-2 last:mb-0"
|
||||
@longpress="deleteClick(item)"
|
||||
>
|
||||
<!-- 卡片 -->
|
||||
<view class="rounded-xl bg-white p-3.5 shadow-sm">
|
||||
<view class="flex items-center justify-between">
|
||||
<text class="text-sm text-gray-400 font-mono">{{ item.time }}</text>
|
||||
<wd-tag :type="getRecordStatus(item).type" round>
|
||||
{{ getRecordStatus(item).label }}
|
||||
</wd-tag>
|
||||
</view>
|
||||
|
||||
<view class="mt-1.5 flex items-center gap-2">
|
||||
<text>{{ getRecordMeta(item.type).icon }}</text>
|
||||
<text class="text-gray-700 font-medium">{{ getRecordMeta(item.type).label }}</text>
|
||||
</view>
|
||||
|
||||
<view class="flex items-center justify-between">
|
||||
<text class="mt-1.5 block text-l text-gray-800 font-bold">{{ getRecordValue(item) }}</text>
|
||||
<wd-button v-if="!item.note" type="danger" icon="delete" size="mini" round
|
||||
@click="deleteClick(item)"></wd-button>
|
||||
</view>
|
||||
|
||||
<view class="flex items-center justify-between">
|
||||
<text class="mt-0.5 block text-sm text-gray-400 leading-relaxed">{{ item.note }}</text>
|
||||
<wd-button v-if="item.note" type="danger" icon="delete" size="mini" round
|
||||
@click="deleteClick(item)"></wd-button>
|
||||
</view>
|
||||
<view class="flex items-center justify-between gap-1">
|
||||
<text class="text-sm text-gray-400 font-mono">{{ item.time }}</text>
|
||||
<wd-tag :type="getRecordStatus(item).type" round>
|
||||
{{ getRecordStatus(item).label }}
|
||||
</wd-tag>
|
||||
</view>
|
||||
|
||||
<view class="mt-1.5 flex items-center gap-2">
|
||||
<text>{{ getRecordMeta(item.type).icon }}</text>
|
||||
<text class="text-gray-700 font-medium">{{ getRecordMeta(item.type).label }}</text>
|
||||
</view>
|
||||
|
||||
<text class="mt-1.5 block text-l text-gray-800 font-bold">{{ getRecordValue(item) }}</text>
|
||||
|
||||
<text class="mt-1.5 block text-sm text-gray-400 leading-relaxed">{{ item.note }}</text>
|
||||
|
||||
<view v-if="idx !== group.items.length - 1" class="mt-1.5 h-[1px] flex-1 bg-gray-200" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</wd-card>
|
||||
|
||||
<!-- 日期选择器 -->
|
||||
<wd-datetime-picker
|
||||
@@ -84,7 +85,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import {
|
||||
deleteRecordByDate,
|
||||
deleteRecordByDate, getDateStr,
|
||||
getMonthRecords,
|
||||
getMonthStr,
|
||||
getRecordMeta,
|
||||
@@ -93,16 +94,29 @@ import {
|
||||
groupByDate,
|
||||
} from '@/utils/record'
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import type { IHealthRecord } from "@/type/record";
|
||||
import type { ICalendarSelect, IGroupHealthRecord, IHealthRecord } from "@/type/record";
|
||||
import { useAppStore } from "@/store/app";
|
||||
|
||||
const currentDate = ref(Date.now())
|
||||
const currentRecordTypeSegment = ref('')
|
||||
const showDatePicker = ref(false)
|
||||
const records = ref<IHealthRecord[]>([])
|
||||
const grouped = ref<IGroupHealthRecord[]>([])
|
||||
const selectMonth = ref<string>(getMonthStr(Date.now()))
|
||||
const calendarSelect = ref<ICalendarSelect[]>([])
|
||||
const selectDate = ref<string>(getDateStr(Date.now()))
|
||||
|
||||
const appStore = useAppStore()
|
||||
|
||||
interface SegmentOption {
|
||||
value: string
|
||||
payload: string
|
||||
}
|
||||
const segmentList = ref<SegmentOption[]>([
|
||||
const viewTypeSegmentList = ref<SegmentOption[]>([
|
||||
{ value: 'calendar', payload: '日历' },
|
||||
{ value: 'list', payload: '列表' }
|
||||
])
|
||||
const recordTypeSegmentList = ref<SegmentOption[]>([
|
||||
{ value: '', payload: '所有' },
|
||||
{ value: 'weight', payload: '体重' },
|
||||
{ value: 'sleep', payload: '睡眠' },
|
||||
@@ -111,37 +125,52 @@ const segmentList = ref<SegmentOption[]>([
|
||||
{ value: 'bloodSugar', payload: '血糖' },
|
||||
{ value: 'temperature', payload: '心率' },
|
||||
])
|
||||
const currentSegment = ref('')
|
||||
|
||||
const displayDate = computed(() => {
|
||||
const d = new Date(currentDate.value)
|
||||
return `${d.getFullYear()}年${d.getMonth() + 1}月`
|
||||
})
|
||||
|
||||
const records = ref<IHealthRecord[]>([])
|
||||
const grouped = ref<ReturnType<typeof groupByDate>>([])
|
||||
|
||||
function loadRecords() {
|
||||
const all = getMonthRecords(getMonthStr(currentDate.value), currentSegment.value)
|
||||
function loadListRecords() {
|
||||
const all = getMonthRecords(getMonthStr(currentDate.value), currentRecordTypeSegment.value)
|
||||
records.value = all
|
||||
grouped.value = groupByDate(all)
|
||||
}
|
||||
|
||||
function loadCalendarRecords() {
|
||||
grouped.value = []
|
||||
const all = getMonthRecords(selectMonth.value)
|
||||
calendarSelect.value = Array.from(new Set(all.map(r => r.date).filter(Boolean))).map(date => ({
|
||||
date: date!,
|
||||
badge: true,
|
||||
}))
|
||||
const result = groupByDate(all)
|
||||
grouped.value = result.filter((item) => item.date === selectDate.value)
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
loadRecords()
|
||||
refreshRecords()
|
||||
})
|
||||
|
||||
function refreshRecords() {
|
||||
if (appStore.viewRecordType === 'calendar') {
|
||||
loadCalendarRecords()
|
||||
} else {
|
||||
loadListRecords()
|
||||
}
|
||||
}
|
||||
|
||||
function changeMonth(offset: number) {
|
||||
const d = new Date(currentDate.value)
|
||||
d.setMonth(d.getMonth() + offset)
|
||||
currentDate.value = d.getTime()
|
||||
|
||||
loadRecords()
|
||||
refreshRecords()
|
||||
}
|
||||
|
||||
function goToday() {
|
||||
currentDate.value = Date.now()
|
||||
loadRecords()
|
||||
refreshRecords()
|
||||
}
|
||||
|
||||
function openDatePicker() {
|
||||
@@ -149,21 +178,32 @@ function openDatePicker() {
|
||||
}
|
||||
|
||||
function onDateConfirm() {
|
||||
loadRecords()
|
||||
refreshRecords()
|
||||
}
|
||||
|
||||
function handleChangeSegment() {
|
||||
loadRecords()
|
||||
selectMonth.value = getMonthStr(Date.now())
|
||||
refreshRecords()
|
||||
}
|
||||
|
||||
function handleChangeDate(value) {
|
||||
selectDate.value = value.fulldate
|
||||
refreshRecords()
|
||||
}
|
||||
|
||||
function monthSwitch(value) {
|
||||
selectMonth.value = value.fullDate
|
||||
refreshRecords()
|
||||
}
|
||||
|
||||
function deleteClick(item: IHealthRecord) {
|
||||
wx.showModal({
|
||||
title: '提示',
|
||||
content: '确定删除这条记录吗?',
|
||||
content: `确定删除这条${getRecordMeta(item.type).label}记录吗?`,
|
||||
success(res) {
|
||||
if (res.confirm) {
|
||||
deleteRecordByDate(item.date, item.id)
|
||||
loadRecords()
|
||||
refreshRecords()
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -171,12 +211,16 @@ function deleteClick(item: IHealthRecord) {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
:deep(.type-segmented) {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
:deep(.wd-segmented) {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:deep(.wd-segmented__item) {
|
||||
min-width: 60px;
|
||||
min-width: 45px;
|
||||
flex-shrink: 0;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user