uniapp | 日历时间选择器,可配置选天、半天(上下午)、小时

参考钉钉的交互,可配置按天、半天或者小时选择的日历时间选择器
通过 format 配置选天或选小时
通过绑定值 defaultVal 的 isHalf 配置是否为选半天(上、下午)

uniapp | 日历时间选择器,可配置选天、半天(上下午)、小时_第1张图片

t-picker组件模板

<template>
	<u-popup v-model="show" mode="bottom" @click="colse()">
		<view class="t-pop" @tap.stop>
			<view class="pop-main">
				<view class="top">
					<view class="top-l">
						<view @click="changeSwp(1)" class="date-c" :style="{ color: currentId == 1 ? '#1E79FF' : '#333333' }">
							<text>{{ checkyear }}-{{ checkmonth < 10 ? '0' + checkmonth : checkmonth }}-{{ checkdate < 10 ? '0' + checkdate : checkdate }}</text>
						</view>
						<view v-if="!myDefaultVal.isHalf && !['yyyy-MM-dd', 'YYYY-MM-DD'].includes(format)" @click="changeSwp(2)" class="time-c" :style="{ color: currentId == 2 ? '#1E79FF' : '#333333' }">
							<text>{{ checkhour < 10 ? '0' + checkhour : checkhour }}:{{ checkminute < 10 ? '0' + checkminute : checkminute }}</text>
						</view>
						<view v-if="myDefaultVal.isHalf" @click="changeSwp(3)" class="time-c" :style="{ color: currentId == 3 ? '#1E79FF' : '#333333' }">
							<text>{{ myDefaultVal.halfDayType === 0 ? '上午' : '下午' }}</text>
						</view>
					</view>
					<view class="top-r" @click="onOK()">
						<text>确定</text>
					</view>
				</view>
				<swiper class="swiper" circular :current-item-id="String(currentId)" @change="(e)=>changeSwp(e.detail.currentItemId)">
					<swiper-item item-id="1">
						<view class="mid">
							<view class="calendar">
								<view class="ca-top" v-for="item in textList" :key="item">
									{{ item }}
								</view>
							</view>
							<swiper :vertical="true" style="height: 520rpx;" @change="changeCalendarSwiper" :current="currentCalendarSwiperIndex">
								<swiper-item v-for="(item, index) in mList" :key="index">
									<view class="calendar-title">{{item.year}}{{item.month}}</view>
									<view class="calendar">
										<!-- <view class="cabg">{{ item.month }}</view> -->
										<view @click="check(item, i)" class="ca-top" :style="{ marginLeft: i == 1 ? item.firstDay * 14.2 + 'vw' : 0 }"
											v-for="i in item.daynums" :key="i">
											<view class="cell" style="color:#ccc"
												v-if="nowDate > i && nowYear == item.year && nowMonth == item.month && !canToday">
												{{ i }}
											</view>
											<view v-else
												:class="checkyear == item.year && checkmonth == item.month && checkdate == i ? 'cell cell-active' : 'cell'">
												{{ i }}
											</view>
										</view>
									</view>
								</swiper-item>
							</swiper>
						</view>
					</swiper-item>
					<!-- 时间选择 -->
					<swiper-item item-id="2" v-if="!myDefaultVal.isHalf && !['yyyy-MM-dd', 'YYYY-MM-DD'].includes(format)">
						<picker-view :indicator-style="indicatorStyle" :value="timePickerValue" @change="changeTime" class="picker-view">
							<picker-view-column>
								<view class="item" v-for="(item, index) in 24" :key="index">
									{{ item - 1 < 10 ? '0' + (item - 1) : item - 1 }}</view>
							</picker-view-column>
							<picker-view-column>
								<view class="item" v-for="(item, index) in 60" :key="index">
									{{ item - 1 < 10 ? '0' + (item - 1) : item - 1 }}</view>
							</picker-view-column>
						</picker-view>
					</swiper-item>
					<!-- 上下午选择 -->
					<swiper-item item-id="3" v-if="myDefaultVal.isHalf">
						<picker-view :indicator-style="indicatorStyle" :value="halfDayTypePickerValue" @change="changeHalfDayType" class="picker-view">
							<picker-view-column>
								<view class="item" :key="0">上午</view>
								<view class="item" :key="1">下午</view>
							</picker-view-column>
						</picker-view>
					</swiper-item>
				</swiper>

			</view>
		</view>
	</u-popup>
</template>

<script>
import dayjs from 'dayjs'
export default {
	name: "t-datetime",
	props: {
		show: {
			type: Boolean,
			default: false
		},
		delayMin: {
			type: Number,
			default: 0 //默认时间后推0分钟
		},
		canToday: { //是否可选择当天之前的时间
			type: Boolean,
			default: true
		},
		defaultVal: {
			type: Object,
			default: ()=>{
				return {
					timeValue: '', // 时间字符串2023-07-20 12:00:00
					isHalf: false,  // 是否开启半天(上、下午)
					halfDayType: 0  // 上、下午对应的绑定值 0:上午, 1: 下午
				}
			}
		},
		format: {
			type: String,
			default: 'YYYY-MM-DD' // YYYY-MM-DD
		},
	},
	data() {
		return {
			myDefaultVal: {
				timeValue: '',
				isHalf: false, 
				halfDayType: 0 
			},
			currentCalendarSwiperIndex: 10,
			textList: ['日', '一', '二', '三', '四', '五', '六'],
			mList: [],
			checkyear: 0,
			checkmonth: 0,
			checkdate: 0,
			checkhour: 0,
			checkminute: 0,
			indicatorStyle: `height: 50px;`,
			timePickerValue: [0, 0],
			halfDayTypePickerValue: [0],
			currentId: 1,
			nowYear: 0,
			nowMonth: 0,
			nowDate: 0
		};
	},
	watch: {
		show(newVal, oldVal) {
			if (newVal) {
				this.initDefaultVal(this.defaultVal)
				this.changeSwp(1)
				const dt = this.myDefaultVal.timeValue ? dayjs(this.myDefaultVal.timeValue).unix()*1000: Date.now()
				const dtafter = dt + 1000 * 60 * this.delayMin
				this.checkyear = new Date(dtafter).getFullYear()
				this.checkmonth = new Date(dtafter).getMonth() + 1
				this.checkdate = new Date(dtafter).getDate()
				this.checkhour = new Date(dtafter).getHours()
				this.checkminute = new Date(dtafter).getMinutes()
				this.timePickerValue = [this.checkhour, this.checkminute]
				this.halfDayTypePickerValue = [this.myDefaultVal.halfDayType]
				this.nowYear = new Date(dtafter).getFullYear()
				this.nowMonth = new Date(dtafter).getMonth() + 1
				this.nowDate = new Date(dtafter).getDate()
				this.currentCalendarSwiperIndex = this.mList.findIndex(v=>v.year === this.nowYear && v.month === this.nowMonth)
			}
		},

	},
	created() {
		this.init()
	},
	methods: {
		// 往下增加10份日历
		addNextList() {
			let startyear = this.mList.length ? this.mList[this.mList.length - 1].year : new Date().getFullYear()
			let startmonth = this.mList.length ? this.mList[this.mList.length - 1].month + 1 : new Date().getMonth() + 1
			for (let i = 0; i < 10; i++) {
				let year = startmonth + i > 12 ? startyear + 1 : startyear;
				let month = startmonth + i > 12 ? startmonth + i - 12 : startmonth + i;
				let daynums = this.getLastDay(year, month)
				let firstDay = this.getFirstDay(year, month)
				this.mList.push({
					year: year,
					month: month,
					daynums: daynums,
					firstDay: firstDay
				})
			}
		},
		// 往上增加10份日历
		addPrevList() {
			let startyear = this.mList.length ? this.mList[0].year : new Date().getFullYear()
			let startmonth = this.mList.length ? this.mList[0].month - 1 : new Date().getMonth() - 1
			for (let i = 0; i < 10; i++) {
				let year = startmonth - i < 1 ? startyear - 1 : startyear;
				let month = startmonth - i < 1 ? startmonth - i + 12 : startmonth - i;
				let daynums = this.getLastDay(year, month)
				let firstDay = this.getFirstDay(year, month)
				this.mList.unshift({
					year: year,
					month: month,
					daynums: daynums,
					firstDay: firstDay
				})
			}
		},
		init() {
			this.addNextList();
			this.addPrevList();
		},
		initDefaultVal(newVal){
			if(typeof newVal === 'string'){
				this.myDefaultVal.timeValue = newVal;
			}else{
				this.myDefaultVal = JSON.parse(JSON.stringify(newVal))
			}
		},
		changeCalendarSwiper(e) {
			let current = e.detail.current;
			if(current === 0){
				this.addPrevList();
				this.currentCalendarSwiperIndex = current + 10;
			}else if(current === this.mList.length - 1){
				this.addNextList();
				this.currentCalendarSwiperIndex = current;
			}
		},
		check(item, i) {
			if (this.nowDate > (i + 1) && this.nowYear == item.year && this.nowMonth == item.month && !this.canToday)
				return
			this.checkyear = item.year
			this.checkmonth = item.month
			this.checkdate = i
			// this.currentId = 2
		},
		getFirstDay(year, month) {
			const date1 = new Date(month + '/1/' + year)
			return date1.getDay()
		},
		getLastDay(year, month) {
			const date1 = new Date(year, month, 0)
			return date1.getDate()
		},
		colse() {
			this.$emit('update:show', false)
		},
		changeTime(e) {
			const val = e.detail.value
			this.timePickerValue = [val[0], val[1]]
			this.checkhour = val[0]
			this.checkminute = val[1]
		},
		changeHalfDayType(e) {
			const val = e.detail.value
			this.halfDayTypePickerValue = [val[0]]
		},
		changeSwp(i) {
			this.currentId = i
		},
		onOK() {
			this.myDefaultVal.timeValue = dayjs(`${this.checkyear}-${this.checkmonth}-${this.checkdate}T${this.checkhour}:${this.checkminute}:00`).format('YYYY-MM-DD[T]HH:mm:ss')
			this.myDefaultVal.halfDayType = this.halfDayTypePickerValue[0]
			this.$emit('confirm', this.myDefaultVal)
			this.$emit('update:show', false)
		}
	}
}
</script>

<style lang="scss" scoped>
.t-pop {
	width: 100%;
	display: flex;
	justify-content: center;
	align-items: center;
	overflow: hidden;

	.pop-main {
		display: flex;
		flex-direction: column;
		justify-content: space-between;
		align-items: center;
		background-color: #fff;
		border-radius: 24px;
		height: 660rpx;
		width: 100%;

	}
}

.swiper {
	height: 660rpx;
	width: 100vw;
}

.top {
	display: flex;
	flex-direction: row;
	justify-content: space-between;
	align-items: center;
	width: 100%;
	margin: 20rpx 0;

	.top-l {
		display: flex;
		flex-direction: row;
		margin-left: 30rpx;
	}

	.top-r {
		margin-right: 30rpx;
		color: #1E79FF;
	}

	.date-c {
		width: 210rpx;
	}

	.time-c {
		// margin-left: 20rpx;
	}

}
.calendar-title{
	margin-top: 10rpx;
	text-align: center;
	color: #999;
}
.calendar {
	display: flex;
	flex-wrap: wrap;
	flex-direction: row;
	align-items: center;
	width: 100vw;
	position: relative;
	.ca-title{
		text-align: center;
	}
}

.ca-top {
	width: 14.2vw;
	display: flex;
	justify-content: center;
	align-items: center;
	height: 66rpx;
	z-index: 10;
}

.cell {
	width: 60rpx;
	height: 60rpx;
	display: flex;
	justify-content: center;
	align-items: center;
	align-content: center;
	border-radius: 30rpx;
}

.cell-active {
	background-color: #1E79FF;
	color: #fff;
}

.cabg {
	display: flex;
	justify-content: center;
	width: 100vw;
	font-size: 180rpx;
	color: beige;
	position: absolute;
	z-index: 9;
}

.picker-view {
	width: 750rpx;
	height: 660rpx;
	margin-top: 20rpx;
}

.item {
	height: 50px;
	display: flex;
	align-items: center;
	justify-content: center;
	text-align: center;
}
</style>

使用方法

<template>
  <div>
    <u-input
      v-bind="inputConfig"
      type="select" 
      :height="height"
      :border="border" 
      :placeholder="placeholder"
      v-model="activeLabel"
      :select-open="selectShow"
      @tap="!disabled && (selectShow = true);"
    />

    <t-picker v-bind="pickerConfig" v-model:show="selectShow" :defaultVal="modelValue" :format="format" @confirm="handleConfirm"></t-picker>
  </div>
</template>
<script lang='ts' setup>
import { ref, reactive, onMounted, PropType, watch, watchEffect } from 'vue';
import dayjs from 'dayjs'
import tPicker from './t-picker.vue'

const prop = defineProps({
  inputConfig: {
    type: Object,
    default: ()=>{},
  },
  pickerConfig: {
    type: Object,
    default: ()=>{},
  },
  height: {
    type: [String, Number],
    default: '88',
  },
  border: {
    type: Boolean,
    default: true,
  },
  placeholder: {
    type: String,
    default: '请选择',
  },
  modelValue: { 
    type: Object,
    default: ()=>{
      return {
        timeValue:'',
        isHalf:false, 
        halfDayType: 0 
      }
    },
  },
  format: {
    type: String,
    default: 'yyyy-MM-dd'  // yyyy-MM-dd[T]HH:mm:ss
  },
  valueFormat: {
    type: String,
    default: 'yyyy-MM-dd'  // yyyy-MM-dd[T]HH:mm:ss
  },
  params: {},
  disabled: {
    type: Boolean,
    default: false,
  },
})

const emit = defineEmits([
  "update:modelValue",
  "change",
]);

let selectShow = ref(false)
let activeLabel = ref('')
let halfDatMap = ref({
  0: '上午',
  1: '下午'
})

onMounted(()=>{
  setActiveLabel(prop.modelValue)
})


 // 回显格式处理
const setActiveLabel = (val)=>{
  if(val && val.timeValue){
    let { timeValue, isHalf, halfDayType } = val;
    if(isHalf){ // 半天(上午、下午)
      activeLabel.value = dayjs(timeValue).format('YYYY-MM-DD[ ]') + halfDatMap.value[halfDayType]
    }else if(['yyyy-MM-dd HH:mm', 'YYYY-MM-DD HH:mm'].includes(prop.format)){ // 小时
      activeLabel.value = dayjs(timeValue).format('YYYY-MM-DD HH:mm')
    }else{ // 天
      activeLabel.value = dayjs(timeValue).format('YYYY-MM-DD')
    }
  }else{
    activeLabel.value = ''
  }
}

const handleConfirm = (val)=>{
  selectShow.value = false;
  setActiveLabel(val)
  // emit('update:modelValue', val)
  emit('change', val)
}


// 更改旧值的格式
watch(()=>[prop.format, prop.valueFormat, prop.modelValue], (arrVal)=>{
  setActiveLabel(prop.modelValue)
}, {deep: true})

</script>
<style lang='scss' scoped>
  
</style>

你可能感兴趣的:(uni-app)