Initial commit: 网络验证平台
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
<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 { Icon } from '@iconify/vue'
|
||||
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"
|
||||
>
|
||||
<Icon icon="lucide:calendar-clock" 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>
|
||||
Reference in New Issue
Block a user