VUE项目中基于elementUI自定义月日组件 定制化月日组件

月日组件

文章版权声明: 基于 http://t.csdn.cn/LcgIT 文章进行修改
在这里插入图片描述

主要基于原文作者的插件基础上增加了可设置disable月份和日期;

第一步:安装moment 插件
npm install --save moment
第二步:复制month-day-picker.vue组件见下方代码
// month-day-picker.vue
<template>
    <div>
        <el-popover
            placement="bottom"
            width="280"
            v-model="visible">
            <div class="date-month-day">
            //  header 内主要是 左右箭头(上个月和下个月),以及展示当前月份
                <div class="header">
                //  @click="+monthVal !== 1 ? dirClick('left') : ''"  是我设置了月份为1的时候不展示上个月箭头,此处根据各自需求修改,不需要隐藏 click事件把三目事件去掉即可,包括i标签内的v-show="+monthVal !== 1" 箭头改为一直显示;
                    <div class="left-arrow" @click="+monthVal !== 1 ? dirClick('left') : ''">
                        <i v-show="+monthVal !== 1" class="el-icon-arrow-left" />
                    </div>
                    <div v-text="getMonthFormat" @click="monthTile" style="cursor: pointer"></div>
                    // @click="+monthVal !== 12 ? dirClick('right') : ''" 是我设置了所选月份等于12时不展示下个月箭头,此处根据各自需求修改,不需要隐藏 click事件把三目事件去掉即可,包括i标签内的v-show="+monthVal !== 12" 箭头改为一直显示;
                    <div class="right-arrow" @click="+monthVal !== 12 ? dirClick('right') : ''">
                        <i v-show="+monthVal !== 12" class="el-icon-arrow-right" />
                    </div>
                </div>
                //  主要是月份列表
                <div class="content" v-if="monthShow">
                    <div class="month" v-for="(item) in getMonths"
                         :class="{'disable': item.disabled}" :key="item.key"
                         @click="!item.disabled ? monthClick(item) : ''">
                        <span :class="activeMonth(item.key)">{{item.value}}</span></div>
                </div>
                // 主要是所选月份内日期列表
                <div class="content" v-else>
                    <div class="day"
                         v-for="(item) in getDays" :key="item"
                         :class="{'disable': getMonths.length>0&&monthVal ? getMonths[monthVal-1].disabled : ''}"
                         @click="!getMonths[monthVal-1].disabled ? dayClick(item) : ''">
                        <span :class="activeDay(item)">{{item}}</span>
                    </div>
                </div>
            </div>
            <el-input
                slot="reference"
                :placeholder="placeholder"
                prefix-icon="el-icon-date"
                :style="`cursor: pointer;width: ${width} `"
                :clearable="true"
                :readonly="true"
                v-model="dateVal">
            </el-input>
        </el-popover>
    </div>

</template>

<script>
import moment from 'moment'; // 导入日期插件
export default {
    name: 'index',
    props: {
        // 默认值
        dateDefault: {
            type: String,
        },
        // 居中排列
        placeholder: {
            type: String,
            default: '选择日期',
        },
        // 默认年份,闰年
        year: {
            type: String,
            default: '2020',
        },
        disabledStart: {
            type: String,
        },
        disabledEnd: {
            type: String,
        },
        // 宽度
        width: {
            type: String,
            default: '100%',
        },
    },
    data() {
        return {
            visible: false,
            monthShow: false,
            monthFormat: {
                1: '1月',
                2: '2月',
                3: '3月',
                4: '4月',
                5: '5月',
                6: '6月',
                7: '7月',
                8: '8月',
                9: '9月',
                10: '10月',
                11: '11月',
                12: '12月',
            },
            dateVal: '', // 展示在输入框内的值M年D月
            monthVal: '', //当前选中的月份 
            dayVal: '', // 当前选中的日期
        };
    },
    computed: {
        getMonthFormat() {
            return this.monthVal ? this.monthFormat[Number(this.monthVal)] : '';
        },
        // 默认选中天
        activeDay() {
            return (item)=> {
                return Number(this.dayVal) === item ? 'active' : '';
            };
        },
        // 默认选中月
        activeMonth() {
            // eslint-disable-next-line func-names
            return function (item) {
                return this.monthVal === item ? 'active' : '';
            };
        },
        // 获取当前月的天数
        getDays() {
            let days = 30;
            const bigMonth = [1, 3, 5, 7, 8, 10, 12];
            if (this.monthVal && bigMonth.includes(Number(this.monthVal))) {
                days = 31;
            } else if (this.monthVal && Number(this.monthVal) === 2) {
                days = 28;
                if (Number(this.year) % 4 === 0) {
                    days = 29;
                }
            }
            return days;
        },
        // 获取月份
        getMonths() {
            const mon = [];
            // 注意
            Object.keys(this.monthFormat).forEach(m => {
                let monthObj = {
                    key: `${m}`,
                    value: this.monthFormat[m],
                    disabled: false
                }
                // 1、开始和结束限制同时存在 2、仅开始限制; 3、仅结束限制 4、没有限制
                if(!!this.disabledStart){
                    if(m < +this.disabledStart) monthObj.disabled = true
                }
                if(!!this.disabledEnd){
                    if(m > +this.disabledEnd) monthObj.disabled = true
                }
                mon.push(monthObj);
            });
            return mon;
        },
    },
    watch: {
        dateDefault: {
            handler(newVal) {
                if(!newVal){
                    return;
                }
                if (newVal) {
                    let defaultDate = ""
                    if(newVal.split("-").length>2){
                        defaultDate = `${this.year}-${newVal.substring(0,5)}`
                    }else{
                        defaultDate = `${this.year}-${this.dateDefault}`
                    }
                    this.dateVal = moment(defaultDate).format('M月D日');
                    this.monthVal = moment(defaultDate).format('M');
                    this.dayVal = moment(defaultDate).format('D');
                }
            },
            immediate: true, // immediate选项可以开启首次赋值监听
        },
        visible: {
            handler(newVal) {
                if (newVal) {
                    if (this.dateDefault === this.dateVal) {
                        // 按照闰年来算,防止出现29号,算到1号
                        const defaultDate = `${this.year}-${this.dateDefault}`;
                        this.dateVal = moment(defaultDate).format('M月D日');
                        this.monthVal = moment(defaultDate).format('M');
                        this.dayVal = moment(defaultDate).format('D');
                    }
                } else {
                    this.monthShow = false;
                    this.$emit('sureValid');
                }
            },
            immediate: true, // immediate选项可以开启首次赋值监听
        },
    },
    methods: {
        dirClick(type) {
            if (type === 'left') {
                if (Number(this.monthVal) === 1) {
                    this.monthVal = '12';
                } else {
                    this.monthVal = moment(this.monthVal).subtract(1, 'M').format('M');
                }
            }
            if (type === 'right') {
                if (Number(this.monthVal) === 12) {
                    this.monthVal = '1';
                } else {
                    this.monthVal = moment(this.monthVal).add(1, 'M').format('M');
                }
            }

            // 默认选中
            let month = moment().format('M');
            let day = moment().format('D');
            if (this.dateDefault) {
                month = moment(this.dateDefault).format('M');
                day = moment(this.dateDefault).format('D');
            }
            if (month === this.monthVal) {
                this.dayVal = Number(day);
            } else {
                this.dayVal = '';
            }
        },
        monthTile() {
            this.monthShow = true;
        },
        monthClick(month) {
            this.monthVal = month.key;
            this.dirClick();
            this.monthShow = false;
        },
        dayClick(item) {
            this.dayVal = item;
            const day = `${this.dayVal}`;
            const val = {
                day,
                month: this.monthVal,
                date: `${this.monthVal < 10 ? '0' : ''}${this.monthVal}-${day < 10 ? '0' : ''}${day}`,//不足10补0
                dateStr: `${this.monthVal}月${day}日`,
            };
            this.dateVal = val.dateStr;
            this.$emit('update:date', val.date);
            this.$emit('changeDay', val);
            this.visible = false;
            this.$emit('sureValid');
        },
    }
};
</script>

<style lang="less" scoped>
.date-month-day{
    .header{
        display: -webkit-flex; /* Safari */
        display: flex;
        text-align: center;
        flex-direction: row;
        align-items: center;
        justify-content: space-between;
        border-bottom: 1px solid #ebeef5;
        .left-arrow,.right-arrow{
            cursor: pointer;
            width: 30px;
            height: 36px;
            line-height: 36px;
            font-size: 14px;
            color: #42424D;
            z-index: 9;
            background: #fff;
        }
    }
    .content{
        display: -webkit-flex; /* Safari */
        display: flex;
        text-align: center;
        flex-direction: row;
        align-items: center;
        flex-wrap: wrap;
        margin-top: 10px;
        .day{
            width: calc(100% / 7);
            height: 36px;
            padding: 4px 0;
            box-sizing: border-box;
            text-align: center;
            cursor: pointer;
            position: relative;
            span{
                width: 24px;
                height: 24px;
                display: block;
                margin: 0 auto;
                line-height: 24px;
                position: absolute;
                left: 50%;
                transform: translateX(-50%);
                border-radius: 50%;
            }
            .active{
                color: #fff;
                background-color: #409eff;
            }
        }
        .month{
            width: calc(100% / 4);
            height: 48px;
            padding: 6px 0;
            box-sizing: border-box;
            cursor: pointer;
            span{
                width: 60px;
                height: 36px;
                display: block;
                line-height: 36px;
                color: #606266;
                margin: 0 auto;
                border-radius: 18px;
            }
            .active{
                color: #fff;
                background-color: #409eff;
            }
        }
        .disable{
            cursor: not-allowed;
            span{
                color: #C0C4CC;
                background-color: #F5F7FA;
                pointer-events: none;
                cursor: not-allowed;
            }
        }
    }
    ::v-deep{
        .el-input{
            width: 120px;
            z-index: 9;
        }
        .el-input__inner{
            cursor: pointer;
            border: none;
            text-align: center;
            padding: 0px;
            color: #42424D;
        }
    }
}
</style>

第三步: 使用月日组件
// 先引入上述月日组件
import DateMonthDay from "../components/month-day-picker"  // 具体组件地址根据自己的路径修改
// 注册组件
components:{ DateMonthDay  },
// template中组件
// 比如我写在了el-form组件内
<el-form-item prop="endDate">
         <DateMonthDay 
         				@sureValid="endSure"  // 组件校验事件,因为我这边是一个开始日期和一个结束日期,需要校验结束日期需大于开始日期,不需要可删除此行
                       @changeDay="(day) => changeDay(day,'endDate')"
                       :year="form.year"
                       :disabledStart="enabledMonth"
                       :disabledEnd="currentMonth"
                       v-model="form.endDate"
                       :dateDefault="dateDefault">
         </DateMonthDay>
     </el-form-item>

// data中预制参数
data(){
	return{
		form: {
                year: '',// 年份,根据年份才能判断2
                startDate: "",
                endDate: "",
            },
            dateDefault: "03-01",// 组件默认日期,此处填写1月1日,可根据情况自行修改,不跟form.endDate用同一个字段是因为form.endDate会随时变更
            // disabledStartMonth代表这个月份之前的月份禁用,disabledEndMonth代表这个月份以后的月份禁用,禁用日期为空代表没有禁用日期,也可以只填其中一个
            disabledStartMonth: "03",// 3月份之前的日期不可选
            disabledEndMonth: "06", // 6月份之后的日期不可选 ,按照我这个默认设置也就是3月到6月才是可以选择的
	}
}
// script中的事件
// (开始日期和结束日期日期校验)  结尾返回true和false便于在提示后用户仍然不按正确提示修改日期,在保存提交表单时可以再次执行此事件。
        endSure() {
            if (new Date(this.form.startDate) > new Date(this.form.endDate)) {
                this.$message({
                    type: 'warning',
                    message: '结束时间必须在开始时间之后!',
                    showClose: true,
                })
                return false;
            }
            return true
        },

// 切换 月日
        changeDay(day,type){
            let form = this.form;
            if(type === "startDate"){
                this.form.startDate = form.year+'-'+day.date
            }else{
                this.form.endDate = form.year+'-'+day.date
            }
        },
如有疑问欢迎交流
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值