feat: 优化日期选择器

This commit is contained in:
digua
2026-01-31 10:30:42 +08:00
parent 10407b1f9d
commit ddd738a513
4 changed files with 114 additions and 3 deletions

View File

@@ -0,0 +1,105 @@
<script setup lang="ts">
/**
* 日期选择器组件
* 基于 UPopover + UCalendar 封装
*/
import { ref, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import dayjs from 'dayjs'
import { CalendarDate } from '@internationalized/date'
const { t, locale } = useI18n()
const props = withDefaults(
defineProps<{
/** 日期值 (YYYY-MM-DD 格式字符串) */
modelValue: string
/** 占位符文本 */
placeholder?: string
/** 是否显示清空按钮 */
clearable?: boolean
/** 按钮宽度类 */
widthClass?: string
}>(),
{
placeholder: '',
clearable: true,
widthClass: 'w-32',
}
)
const emit = defineEmits<{
(e: 'update:modelValue', value: string): void
}>()
// Popover 开关状态
const popoverOpen = ref(false)
// 日历组件的 locale
const calendarLocale = computed(() => (locale.value === 'zh-CN' ? 'zh-CN' : 'en-US'))
// 辅助函数:将字符串日期转换为 CalendarDate
function stringToCalendarDate(dateStr: string): CalendarDate | undefined {
if (!dateStr) return undefined
const d = dayjs(dateStr)
return new CalendarDate(d.year(), d.month() + 1, d.date())
}
// 辅助函数:将 CalendarDate 转换为字符串
function calendarDateToString(date: CalendarDate | undefined): string {
if (!date) return ''
return `${date.year}-${String(date.month).padStart(2, '0')}-${String(date.day).padStart(2, '0')}`
}
// CalendarDate 对象(用于 UCalendar 双向绑定)
const dateObj = computed<CalendarDate | undefined>({
get: () => stringToCalendarDate(props.modelValue),
set: (val) => {
emit('update:modelValue', calendarDateToString(val))
if (val) popoverOpen.value = false
},
})
// 格式化显示的日期
const dateDisplay = computed(() => (props.modelValue ? dayjs(props.modelValue).format('YYYY/MM/DD') : ''))
// 清空日期
function clearDate() {
emit('update:modelValue', '')
popoverOpen.value = false
}
</script>
<template>
<UPopover v-model:open="popoverOpen" :ui="{ content: 'z-[100]' }">
<UButton
:label="dateDisplay || placeholder || t('selectDate')"
icon="i-heroicons-calendar-days"
variant="outline"
color="neutral"
size="sm"
:class="[widthClass, 'justify-start', { 'text-gray-400': !dateDisplay }]"
/>
<template #content>
<UCalendar v-model="dateObj" :number-of-months="1" :fixed-weeks="false" :locale="calendarLocale" />
<div v-if="clearable" class="px-2 pb-2">
<UButton variant="ghost" size="xs" color="neutral" class="w-full" @click="clearDate">
{{ t('clearDate') }}
</UButton>
</div>
</template>
</UPopover>
</template>
<i18n>
{
"zh-CN": {
"selectDate": "选择日期",
"clearDate": "清空日期"
},
"en-US": {
"selectDate": "Select Date",
"clearDate": "Clear"
}
}
</i18n>

View File

@@ -10,3 +10,4 @@ export { default as UITabs } from './Tabs.vue'
export { default as SubTabs } from './SubTabs.vue'
export { default as PageAnchorsNav } from './PageAnchorsNav.vue'
export { default as FileDropZone } from './FileDropZone.vue'
export { default as DatePicker } from './DatePicker.vue'

View File

@@ -149,7 +149,7 @@ watch(
v-model:open="layoutStore.showChatRecordDrawer"
direction="right"
:handle="false"
:ui="{ content: 'z-[10000]' }"
:ui="{ content: 'z-50' }"
>
<template #content>
<div class="flex h-full w-[680px] flex-col bg-white dark:bg-gray-900" style="-webkit-app-region: no-drag">

View File

@@ -6,6 +6,7 @@
import { ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import dayjs from 'dayjs'
import { DatePicker } from '@/components/UI'
import type { ChatRecordQuery, FilterFormData } from './types'
const { t } = useI18n()
@@ -122,9 +123,9 @@ function resetFilter() {
<UInput v-model="formData.messageId" type="number" :placeholder="t('messageId')" size="sm" class="w-24" />
<UInput v-model="formData.memberName" :placeholder="t('memberNotSupported')" size="sm" class="w-28" disabled />
<div class="flex items-center gap-2">
<UInput v-model="formData.startDate" type="date" size="sm" class="w-32" />
<DatePicker v-model="formData.startDate" :placeholder="t('startDate')" />
<span class="text-xs text-gray-400">~</span>
<UInput v-model="formData.endDate" type="date" size="sm" class="w-32" />
<DatePicker v-model="formData.endDate" :placeholder="t('endDate')" />
</div>
</div>
@@ -155,6 +156,8 @@ function resetFilter() {
"messageId": "消息 ID",
"memberNotSupported": "成员(暂不支持)",
"keywordsPlaceholder": "关键词,多个用逗号分隔,回车搜索",
"startDate": "起始日期",
"endDate": "结束日期",
"reset": "重置",
"filter": "筛选"
},
@@ -162,6 +165,8 @@ function resetFilter() {
"messageId": "Message ID",
"memberNotSupported": "Member (not supported)",
"keywordsPlaceholder": "Keywords, comma separated, Enter to search",
"startDate": "Start Date",
"endDate": "End Date",
"reset": "Reset",
"filter": "Filter"
}