Files
verify/frontend/src/components/ui/date-picker/DateTimePicker.vue
T
admin c653ed071d refactor: 将 @iconify/vue 图标替换为 lucide-vue-next 本地组件
- 所有 lucide: 前缀的图标字符串替换为 lucide-vue-next 组件直接引用
- 动态图标绑定改为 <component :is> 渲染
- h(Icon, { icon: 'lucide:xxx' }) 改为 h(Xxx, { class: ... })
- app-card.vue 的 icon_url 改为 <img> 标签(实际是图片路径)
- 修复 Webhook 标识符冲突(重命名为 WebhookIcon)
- payment-channels 保留 @iconify/vue 用于品牌图标(支付宝、微信、Stripe、PayPal、USDT)
2026-05-03 04:25:34 +08:00

142 lines
4.2 KiB
Vue

<script setup lang="ts">
import type { DateValue } from '@internationalized/date'
import type { HTMLAttributes } from 'vue'
import { parseDate } from '@internationalized/date'
import { cn } from '@/lib/utils'
import { CalendarClock } from 'lucide-vue-next'
import { computed, ref, watch } from 'vue'
import UiButton from '@/components/ui/button/Button.vue'
import UiPopover from '@/components/ui/popover/Popover.vue'
import UiPopoverTrigger from '@/components/ui/popover/PopoverTrigger.vue'
import UiPopoverContent from '@/components/ui/popover/PopoverContent.vue'
import UiCalendar from '@/components/ui/calendar/Calendar.vue'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
const props = defineProps<{
modelValue?: string
placeholder?: string
class?: HTMLAttributes['class']
disabled?: boolean
}>()
const emit = defineEmits<{
'update:modelValue': [value: string]
}>()
const open = ref(false)
const selectedDate = ref<DateValue | undefined>()
const selectedHour = ref('00')
const selectedMinute = ref('00')
const hours = Array.from({ length: 24 }, (_, i) => i.toString().padStart(2, '0'))
const minutes = Array.from({ length: 60 }, (_, i) => i.toString().padStart(2, '0'))
const displayText = computed(() => {
if (props.modelValue) {
return props.modelValue.replace('T', ' ')
}
return ''
})
function handleDateSelect(date: DateValue | undefined) {
if (!date)
return
selectedDate.value = date
emitValue()
}
function emitValue() {
if (selectedDate.value) {
emit('update:modelValue', `${selectedDate.value.toString()}T${selectedHour.value}:${selectedMinute.value}`)
}
}
function resetTime() {
selectedDate.value = undefined
selectedHour.value = '00'
selectedMinute.value = '00'
emit('update:modelValue', '')
open.value = false
}
function initFromModelValue() {
if (props.modelValue) {
const parts = props.modelValue.split('T')
try {
selectedDate.value = parseDate(parts[0])
if (parts[1]) {
const [h, m] = parts[1].split(':')
selectedHour.value = h || '00'
selectedMinute.value = m || '00'
}
}
catch {}
}
}
watch(() => props.modelValue, initFromModelValue, { immediate: true })
</script>
<template>
<UiPopover v-model:open="open">
<UiPopoverTrigger as-child>
<UiButton
variant="outline"
:class="cn(
'justify-start text-left font-normal truncate h-8',
!displayText && 'text-muted-foreground',
props.class,
)"
:disabled="disabled"
>
<CalendarClock class="mr-2 h-4 w-4 shrink-0" />
<span class="truncate text-sm">{{ displayText || placeholder || '选择日期时间' }}</span>
</UiButton>
</UiPopoverTrigger>
<UiPopoverContent class="w-auto p-0" align="start">
<div class="p-3">
<UiCalendar
:model-value="selectedDate"
@update:model-value="handleDateSelect"
/>
</div>
<div class="border-t px-3 py-2">
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<Select v-model="selectedHour" @update:model-value="emitValue">
<SelectTrigger size="sm" class="w-[65px] h-8">
<SelectValue placeholder="时" />
</SelectTrigger>
<SelectContent class="max-h-[180px]">
<SelectItem v-for="h in hours" :key="h" :value="h">
{{ h }}
</SelectItem>
</SelectContent>
</Select>
<span class="text-muted-foreground font-bold text-sm">:</span>
<Select v-model="selectedMinute" @update:model-value="emitValue">
<SelectTrigger size="sm" class="w-[65px] h-8">
<SelectValue placeholder="分" />
</SelectTrigger>
<SelectContent class="max-h-[180px]">
<SelectItem v-for="m in minutes" :key="m" :value="m">
{{ m }}
</SelectItem>
</SelectContent>
</Select>
</div>
<UiButton variant="ghost" size="sm" class="h-8 px-2 text-xs" @click="resetTime">
重置
</UiButton>
</div>
</div>
</UiPopoverContent>
</UiPopover>
</template>