| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337 |
- <template>
- <!--
- ss-datetime-picker 智能日期时间选择组件
- 基于 uview-plus 的 datetime-picker
- 自动绑定ValidatedTd的事件处理函数
- -->
- <view class="smart-datetime-picker">
- <up-datetime-picker
- ref="datetimePicker"
- :show="show"
- v-model="innerValue"
- :mode="mode"
- :min-date="minDate"
- :max-date="maxDate"
- :formatter="formatter"
- :filter="filter"
- :default-index="defaultIndex"
- :item-height="itemHeight"
- :cancel-text="cancelText"
- :confirm-text="confirmText"
- :cancel-color="cancelColor"
- :confirm-color="confirmColor"
- :visible-item-count="visibleItemCount"
- :close-on-click-overlay="closeOnClickOverlay"
- :safe-area-inset-bottom="safeAreaInsetBottom"
- @confirm="handleConfirm"
- @cancel="handleCancel"
- @close="handleClose"
- />
-
- <!-- 显示区域 -->
- <view
- class="smart-datetime-picker__display"
- @click="openPicker"
- >
- <text
- v-if="displayValue"
- class="smart-datetime-picker__value"
- >
- {{ displayValue }}
- </text>
- <text
- v-else
- class="smart-datetime-picker__placeholder"
- >
- {{ placeholder }}
- </text>
- <Icon name="icon-xiangxiajiantou" size="32" color="#999"/>
- </view>
- </view>
- </template>
- <script setup>
- import { ref, computed, inject, watch } from 'vue'
- import Icon from '@/components/icon/index.vue';
- import dayjs from 'dayjs'
- const props = defineProps({
- modelValue: {
- type: [String, Number, Date],
- default: ''
- },
- // 选择器模式:date-日期,time-时间,datetime-日期时间
- mode: {
- type: String,
- default: 'date',
- validator: (value) => ['date', 'time', 'datetime'].includes(value)
- },
- // 最小日期
- minDate: {
- type: [String, Number, Date],
- default: () => dayjs().subtract(10, 'year').valueOf()
- },
- // 最大日期
- maxDate: {
- type: [String, Number, Date],
- default: () => dayjs().add(10, 'year').valueOf()
- },
- // 占位符
- placeholder: {
- type: String,
- default: '请选择'
- },
- // 格式化函数
- formatter: {
- type: Function,
- default: null
- },
- // 过滤函数
- filter: {
- type: Function,
- default: null
- },
- // 默认选中的索引
- defaultIndex: {
- type: Array,
- default: () => []
- },
- // 选项高度
- itemHeight: {
- type: [String, Number],
- default: 44
- },
- // 取消按钮文字
- cancelText: {
- type: String,
- default: '取消'
- },
- // 确认按钮文字
- confirmText: {
- type: String,
- default: '确认'
- },
- // 取消按钮颜色
- cancelColor: {
- type: String,
- default: '#909193'
- },
- // 确认按钮颜色
- confirmColor: {
- type: String,
- default: '#3c9cff'
- },
- // 可见选项数量
- visibleItemCount: {
- type: [String, Number],
- default: 5
- },
- // 点击遮罩是否关闭
- closeOnClickOverlay: {
- type: Boolean,
- default: true
- },
- // 是否开启底部安全区适配
- safeAreaInsetBottom: {
- type: Boolean,
- default: false
- },
- // 显示格式
- displayFormat: {
- type: String,
- default: ''
- }
- })
- const emit = defineEmits(['update:modelValue', 'confirm', 'cancel', 'change'])
- // 从ValidatedTd注入事件处理函数(兼容旧方式)
- const onInput = inject('onInput', null)
- const onBlur = inject('onBlur', null)
- // 控制选择器显示
- const show = ref(false)
- // 内部值
- const innerValue = ref(Date.now())
- // 日期选择器引用
- const datetimePicker = ref(null)
- // 初始化内部值
- const initInnerValue = () => {
- if (props.mode === 'time') {
- // 时间模式下,直接使用时间字符串
- if (props.modelValue) {
- innerValue.value = props.modelValue
- } else {
- // 如果没有值,使用当前时间
- const now = new Date()
- const hours = now.getHours().toString().padStart(2, '0')
- const minutes = now.getMinutes().toString().padStart(2, '0')
- innerValue.value = `${hours}:${minutes}`
- }
- } else {
- // 日期和日期时间模式
- if (props.modelValue) {
- if (typeof props.modelValue === 'string') {
- innerValue.value = dayjs(props.modelValue).valueOf()
- } else if (typeof props.modelValue === 'number') {
- innerValue.value = props.modelValue
- } else if (props.modelValue instanceof Date) {
- innerValue.value = props.modelValue.getTime()
- }
- } else {
- innerValue.value = dayjs().valueOf()
- }
- }
- }
- // 监听 modelValue 变化
- watch(() => props.modelValue, () => {
- initInnerValue()
- }, { immediate: true })
- // 计算显示值
- const displayValue = computed(() => {
- if (!props.modelValue) return ''
-
- if (props.mode === 'time') {
- // 时间模式下直接显示值
- return props.modelValue
- }
-
- let format = props.displayFormat
- if (!format) {
- switch (props.mode) {
- case 'date':
- format = 'YYYY-MM-DD'
- break
- case 'datetime':
- format = 'YYYY-MM-DD HH:mm'
- break
- default:
- format = 'YYYY-MM-DD'
- }
- }
-
- const date = dayjs(props.modelValue)
- return date.isValid() ? date.format(format) : props.modelValue
- })
- // 打开选择器
- const openPicker = () => {
- show.value = true
- console.log('打开日期选择器', show.value)
- }
- // 确认选择
- const handleConfirm = (value) => {
- console.log('原始值:', value)
- let formattedValue = ''
-
- try {
- if (props.mode === 'time') {
- // 时间模式下,value.value 已经是 "HH:mm" 格式
- formattedValue = value.value
-
- console.log('处理后的时间值:', formattedValue)
- } else {
- // 日期和日期时间模式
- const date = dayjs(value.value)
- switch (props.mode) {
- case 'date':
- formattedValue = date.format('YYYY-MM-DD')
- break
- case 'datetime':
- formattedValue = date.format('YYYY-MM-DD HH:mm')
- break
- default:
- formattedValue = date.format('YYYY-MM-DD')
- }
- }
-
- // 1. 支持v-model
- emit('update:modelValue', formattedValue)
-
- // 2. 兼容旧的inject方式
- if (onInput) {
- onInput({ detail: { value: formattedValue } })
- }
-
- // 3. 触发确认事件
- emit('confirm', { value: formattedValue, timestamp: value.value })
- emit('change', formattedValue)
-
- } catch (error) {
- console.error('时间处理错误:', error)
- formattedValue = ''
- }
-
- show.value = false
- console.log(`SsDatetimePicker handleConfirm: ${formattedValue}`)
- }
- // 取消选择
- const handleCancel = () => {
- show.value = false
- emit('cancel')
- }
- // 关闭选择器
- const handleClose = () => {
- show.value = false
-
- // 触发失焦事件
- if (onBlur) {
- onBlur()
- }
- }
- // 初始化
- initInnerValue()
- </script>
- <style lang="scss" scoped>
- .smart-datetime-picker {
- width: 100%;
-
- &__display {
- width: 100%;
- height: 60rpx;
- display: flex;
- align-items: center;
- justify-content: space-between;
- font-size: 32rpx;
- line-height: 60rpx;
- color: #333;
- background-color: transparent;
- border: none;
- outline: none;
- box-sizing: border-box;
- cursor: pointer;
- }
-
- &__value {
- flex: 1;
- color: #333;
- font-size: 32rpx;
- }
-
- &__placeholder {
- flex: 1;
- color: #999;
- font-size: 32rpx;
- }
-
- &__icon {
- margin-left: 16rpx;
- transition: transform 0.3s ease;
- }
-
- &__display:active &__icon {
- transform: rotate(180deg);
- }
- }
- </style>
|