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