mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-04-26 14:27:22 +08:00
feat: 优化日期选择器
This commit is contained in:
105
src/components/UI/DatePicker.vue
Normal file
105
src/components/UI/DatePicker.vue
Normal 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>
|
||||
@@ -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'
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user