手写自定义日历选择器

<template>
    <div class="calendar" v-show="boxShow" :class="boxShow ? 'start' : 'close'">
        <div class="flex fl-j-b head">
            <div class="prev">
                <i @click="iconClick(-2)" class="prevPlus el-icon-d-arrow-left c-pointer"></i>
                <i @click="iconClick(-1)" class="prevs el-icon-arrow-left c-pointer"></i>
            </div>
            <div class="title">{{year}}年{{month}}月</div>
            <div class="next">
                <i @click="iconClick(1)" class="next el-icon-arrow-right c-pointer"></i>
                <i @click="iconClick(2)" class="nextPlus el-icon-d-arrow-right c-pointer"></i>
            </div>
        </div>
        <div class="week">
            <div>日</div>
            <div>一</div>
            <div>二</div>
            <div>三</div>
            <div>四</div>
            <div>五</div>
            <div>六</div>
        </div>
        <div class="day">
            <span
                @click="handleChange(item)"
                v-for="(item,index) in calendarList"
                :key="index"
                class="transition"
                :class="[
                    item.prev || item.next ? 'prev':'',
                    item.now ? 'now' : '',
                    item.select ? 'select' : ''
                ]"
                >
                {{ item.day }}
            </span>
        </div>
    </div>
</template>
<script>
export default{
    props:{
        value:{type:Boolean},
        dateList:{type:Array,default:[]}
    },
    computed:{
        boxShow:{
            get(){
              return this.value;
            }
        }
    },
    data(){
        return {
          calendarList:[],
          year: new Date().getFullYear(),
          month:new Date().getMonth() + 1
        }
    },
    created(){
        this.createCalendar()
    },
    methods:{
        handleChange(item){
            if(!item.month)return;
            item.id = Number(item.date.replace(/\//g,''));
            this.$emit('emitDate',item);
        },
        iconClick(num){
            const nums = this.month + num;
            if (nums > 12) {
                this.year += 1;
                this.month = 1;
            } else if (nums < 1) {
                this.year -= 1;
                this.month = 12;
            } else {
                this.month = nums;
            }
            this.createCalendar();
        },
        createCalendar() {
            const calendarList = [];
            const now = new Date();
            const year = this.year;
            const month = this.month;
            const daysInMonth = new Date(year, month, 0).getDate();
            const firstDayOfWeek = new Date(year, month - 1, 1).getDay();
            let date = 1;
            let prevMonthDays = new Date(year, month - 1, 0).getDate();
            for(let i = 0; i < 6; i++){
                for(let j = 0; j < 7; j++){
                    if(i == 0 && j < firstDayOfWeek){
                        calendarList.push({
                            day:prevMonthDays - (firstDayOfWeek - j - 1),
                            month:false,
                            prev: true,
                            select: false
                        });
                    }else if(date > daysInMonth){
                        calendarList.push({
                            day:date - daysInMonth,
                            month:false,
                            select:false,
                            next:true,
                        })
                        date++;
                    }else{
                        const isNowDay = date === now.getDate() && month === now.getMonth() + 1 && year === now.getFullYear();
                        calendarList.push({
                            day:date,
                            now:isNowDay,
                            month:true,
                            select:false,
                            date:`${year}/${month}/${date}`,
                            showDate:`${month}.${date}`
                        });
                        date++;
                    }
                }
            }
            this.calendarList = this.pdSelect(calendarList);
        },
        pdSelect(calendarList){
            const props = JSON.parse(JSON.stringify(this.dateList));
            return calendarList.map(dateItem => {
                const isSelect = props.length > 0 && props.some(item => item.date === dateItem.date);
                dateItem.select = isSelect;
                return dateItem;
            })
        },
    },
    watch:{
        dateList(){
            this.pdSelect(this.calendarList);
        }
    }
}
</script>
<style lang="scss">
.start{
    animation: starts 0.5s cubic-bezier(0.075, 0.82, 0.165, 1) forwards;
    transform-origin: top left;
}
.close{
    animation: closes 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
    transform-origin: top left;
}
@keyframes starts {
    from{
      scale: 0;
    }
    to{
      scale: 1;
    }
}
@keyframes closes {
    from{
        transform: translateX(0);
    }
    to{
        transform: translateX(-10%);
    }
}
.calendar{
    position: absolute;
    width: 240px;
    padding: 8px;
    box-shadow: 0px 0px 4px 0px rgba(0,0,0,0.2);
    border-radius: 3px;
    font-size: 13px;
    font-family: PingFangSC-Regular, PingFang SC;
    font-weight: 400;
    .week{
        margin-top: 8px;
        color: rgba(0,17,34,0.8);
        display: grid;
        grid:auto-flow /repeat(7, 34.28px);
        text-align: center;
        padding-bottom: 8px;
        border-bottom: 1px solid #D9DCDE;
    }
    .day{
        display: grid;
        grid: auto-flow / repeat(7, 34.28px);
        color: rgba(0,17,34,0.8);
        text-align: center;
        >span{
            margin: 4px;
            height: 26.28px;
            line-height: 26.28px;
            background-color: #fff;
            border-radius: 3px;
            cursor: pointer;
            user-select: none;
        }
        >span:hover{
            background: #FFECDB;
        }
        .select{
            background-color: #FF963A !important;
            color: #fff !important;
        }
        .prev{
            color: rgba(0,17,34,0.4) !important;
        }
        .now{
            color: #FF963A;
        }
    }
}
</style>

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我来为你介绍一下如何滚动日期选择。我们可以利用Vue框架中的指令来实现这个效果。 首先,我们需要在HTML中创建一个容来放置日期选择组件,如下所示: ```html <div class="date-picker" v-click-outside="hidePicker"> <input type="text" v-model="selectedDate" @click="showPicker" readonly> <div class="picker-container" v-show="show"> <div class="picker-header"> <button @click="prevMonth"><</button> <span>{{ currentYear }}年{{ currentMonth }}月</span> <button @click="nextMonth">></button> </div> <div class="picker-body"> <div class="picker-row" v-for="week in weeks"> <div class="picker-cell" v-for="day in week" :class="{ 'is-today': isToday(day), 'is-selected': isSelected(day) }" @click="selectDate(day)"> {{ day }} </div> </div> </div> </div> </div> ``` 在上面的代码中,我们创建了一个`date-picker`的容,其中包含一个`input`输入框和一个日期选择的弹出框。输入框中绑定了`selectedDate`变量,用来显示当前选择的日期。日期选择的弹出框使用了Vue的指令`v-show`来控制其显示和隐藏状态。 接下来,我们需要在Vue组件的`data`属性中定义一些变量,如下所示: ```javascript data() { return { selectedDate: '', show: false, currentYear: new Date().getFullYear(), currentMonth: new Date().getMonth() + 1, currentDate: new Date().getDate(), weeks: [], days: [] } } ``` 在上面的代码中,我们定义了一些重要的变量。`selectedDate`用来保存用户选择的日期,`show`用来控制日期选择的显示和隐藏状态,`currentYear`、`currentMonth`和`currentDate`用来记录当前日期,`weeks`和`days`用来保存渲染日期选择所需要的数据。 接下来,我们需要在Vue组件的`mounted`钩子函数中初始化日期选择的数据,如下所示: ```javascript mounted() { // 初始化日期选择数据 this.initWeeks() this.initDays() } ``` 在上面的代码中,我们调用了`initWeeks`和`initDays`两个函数来初始化日期选择的数据。 `initWeeks`函数用来初始化日期选择的星期数据,如下所示: ```javascript initWeeks() { this.weeks = [ ['日', '一', '二', '三', '四', '五', '六'] ] } ``` 在上面的代码中,我们定义了一个数组`weeks`,其中包含了一周内的所有日期。 `initDays`函数用来初始化日期选择的日期数据,如下所示: ```javascript initDays() { const firstDay = new Date(this.currentYear, this.currentMonth - 1, 1).getDay() // 当月第一天是星期几 const lastDay = new Date(this.currentYear, this.currentMonth, 0).getDate() // 当月最后一天是几号 let days = [] let day = 1 for (let i = 0; i < 6; i++) { let week = [] for (let j = 0; j < 7; j++) { if (day > lastDay) { break } if (i === 0 && j < firstDay) { week.push('') } else { week.push(day++) } } days.push(week) } this.days = days } ``` 在上面的代码中,我们使用了`Date`对象来获取当前日期所在月份的第一天和最后一天,然后使用一个嵌套循环来遍历当前月份的所有日期,并将它们保存在一个二维数组中。 接下来,我们需要在Vue组件中定义一些方法来处理用户的交互操作,如下所示: ```javascript methods: { // 显示日期选择 showPicker() { this.show = true }, // 隐藏日期选择 hidePicker() { this.show = false }, // 上一个月 prevMonth() { if (this.currentMonth === 1) { this.currentYear -= 1 this.currentMonth = 12 } else { this.currentMonth -= 1 } this.initDays() }, // 下一个月 nextMonth() { if (this.currentMonth === 12) { this.currentYear += 1 this.currentMonth = 1 } else { this.currentMonth += 1 } this.initDays() }, // 选择日期 selectDate(day) { if (day === '') { return } this.selectedDate = `${this.currentYear}-${this.currentMonth}-${day}` this.hidePicker() }, // 判断是否为今天 isToday(day) { return this.currentYear === new Date().getFullYear() && this.currentMonth === new Date().getMonth() + 1 && day === new Date().getDate() }, // 判断是否为选择状态 isSelected(day) { return this.selectedDate === `${this.currentYear}-${this.currentMonth}-${day}` } } ``` 在上面的代码中,我们定义了一些方法来处理用户的交互操作。`showPicker`和`hidePicker`方法用来显示和隐藏日期选择的弹出框。`prevMonth`和`nextMonth`方法用来切换日期选择的月份。`selectDate`方法用来选择日期并保存到`selectedDate`变量中。`isToday`方法用来判断一个日期是否为今天。`isSelected`方法用来判断一个日期是否为选择状态。 最后,我们需要在Vue组件中使用`v-click-outside`指令来处理日期选择的点击外部区域隐藏弹出框的功能,如下所示: ```javascript directives: { 'click-outside': { bind(el, binding, vnode) { el.clickOutsideEvent = function(event) { if (!(el === event.target || el.contains(event.target))) { vnode.context[binding.expression](event) } } document.body.addEventListener('click', el.clickOutsideEvent) }, unbind(el) { document.body.removeEventListener('click', el.clickOutsideEvent) } } } ``` 在上面的代码中,我们使用了`v-click-outside`指令来处理日期选择的点击外部区域隐藏弹出框的功能。我们定义了一个`click-outside`指令,并在其中使用了`addEventListener`和`removeEventListener`方法来监听和移除点击事件。 以上就是滚动日期选择的Vue实现方法。希望对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值