前言:由于项目需求,需要实现月份多选,但是目前组件中只有日期多选,于是自己写了个月份多选组件,样式和组件基本上保持一致。

首先,来看下实现的效果图吧:

 

 

 功能说明:

1. 选择年份:

获取前20年,然后循环 每一年里面都有12个月 得到数组 opTime 和 DateList,默认显示当前年份;

通过点击顶部的左右箭头来切换年份;

2. 选择月份:

点击月份进行月份多项选择,也可以先选择年份,再选择那一年的某几个月份;

3. 删除已选月份:

通过再次点击已选择的月份进行删除;

4. 点击确定按钮:

(1)将所选的月份值放进了一个数组里面,便于父组件取值;

(2)关闭弹框;

5. 点击重置按钮:

(1)重置所有选项为初始值,包括年份、月份和输入框内容;

(2)关闭弹框;

6. 点击输入框的清空按钮:

(1)当输入框有值时,鼠标悬浮在输入框上才会显示出清空按钮,点击清空按钮后,将会重置所有选项为初始值,包括年份、月份和输入框内容;

(2)关闭弹框;

7. 点击弹框以外区域:

(1)判断目前选择项与之前点击“确定”按钮时的选择项是否一致,不一致时恢复到上次点击“确定”按钮时的值;(因为只有在点击了“查询”按钮后才能查询结果)

(2)关闭弹框;

组件代码(SelectMonth.vue):

<template>
   <div class="selectMonthBoxSquare rel clearFixed" id="boxArea">
       <!-- el-input输入框:readonly和clearable属性不能同时使用 -->
        <el-input class="inputStyle" v-model="inputValue" type="text" placeholder="请选择查询月份" readonly @focus="showBox = true">
            <i slot="prefix" class="el-input__icon el-icon-date"></i>
            <!-- 清空图标:有内容的时候渲染出来,鼠标hover到input框的时候再显示出来(即:输入框有内容并且鼠标悬浮时显示该图标) -->
            <i slot="suffix" class="el-input__icon el-icon-circle-close clearIconStyle" v-if="showClear" @click="resetMonth"></i>
        </el-input>
        <!-- 年份月份选择弹框 -->
        <div class="selectContentBox" v-if="showBox">
           <div class="contentArea">
               <!-- 年份 -->
                <div class="flex flex-wrap flex-around" style="padding: 15px 0;border-bottom: 1px solid #e5e5e5;">
                    <div class="cursor" v-if="curIndex == DateList.length - 1" style="width: 15%;"><img src="../../../images/left_icon_gray.png" alt="" /></div>
                    <div class="cursor" v-else @click="reduceYear" style="width: 15%;"><img src="../../../images/left_icon.png" alt="" /></div>
                    <div>{{OneY}}年</div>
                    <div class="cursor t-r" v-if="curIndex == 0" style="width: 15%;"><img src="../../../images/right_icon_gray.png" alt="" /></div>
                    <div class="cursor t-r" v-else @click="addYear" style="width: 15%;"><img src="../../../images/right_icon.png" alt="" /></div>
                </div>
                <!-- 月份 -->
                <div class="conterList">
                    <el-checkbox-group class="flex flex-wrap" v-model="optTime[curIndex].queryTime" @change="onChange" >
                        <el-checkbox class="onSelect flex-x-center" v-for="(item,index) in DateList[curIndex].queryTime" :key="index" :label="`${DateList[curIndex].TimeYear}-${(item<=9)?`0${item}`:item}`">
                        {{monthMap[item]}}月
                        </el-checkbox>
                    </el-checkbox-group>
                </div>
            </div>
            <!-- 按钮 -->
            <div class="buttonBox t-r">
                <el-button class="buttonStyle" size="mini" type="primary" plain @click.stop="handleSubmit">确定</el-button>
                <el-button class="buttonStyle" size="mini" plain @click.stop="resetMonth">重置</el-button>
            </div>
       </div>
   </div>
</template>
<script>
export default {
    data(){
        return{
            DateList: [], // 年份月份数组
            optTime: [], // 月份选中结果数组
            OneY: '', // 当前年份
            curIndex: 0, // 当前年份下标值
            optTimes: [], // 点击月份时的所有选中结果
            resultTimes: [], // 点击“确定”按钮后的选择结果
            showBox: false, // 是否显示月份选择弹框
            inputValue: '', // 输入框的绑定值
            showClear: false, // 是否显示输入框右边的“清空”小图标
            monthMap: { // 月份显示为中文
                '1': '一',
                '2': '二',
                '3': '三',
                '4': '四',
                '5': '五',
                '6': '六',
                '7': '七',
                '8': '八',
                '9': '九',
                '10': '十',
                '11': '十一',
                '12': '十二',
            }
        }
    },
    created(){
        this.init();
    },
    mounted() {
        //点击弹框外的任意位置关闭区域弹窗
        document.addEventListener('click',(e) => {
            //获取弹窗对象
            const boxArea = document.getElementById('boxArea');
            //判断弹窗对象中是否包含点击对象
            if(boxArea && !boxArea.contains(e.target)) {
                // 判断当前选中月份与上次点击“确定”按钮时的选择结果是否一致
                let equalArr = this.resultTimes.sort().toString() == this.optTimes.sort().toString()
                if(!equalArr) {
                    // 如果不一致(因为是多选,所以必须是点击了“确定”按钮后才能进行查询):
                    // 将选择结果恢复到上次点击“确定”按钮时的结果
                    this.optTimes = this.resultTimes
                    // 将输入框的值恢复到上次点击“确定”按钮时的值
                    this.inputValue = this.optTimes.join(',')
                    // 根据输入框是否有值来判断清空图标是否渲染
                    this.showClear = this.inputValue == '' ? false : true
                    // 将月份选中结果恢复到上次点击“确定”按钮时的选中月份
                    let _opt = this.resultTimes.map(v => { return v.substring(0,4) });
                    for( let item in this.optTime ){
                        this.optTime[item].queryTime = []
                        _opt.map((items,indexs)=>{
                            if( items == this.optTime[item].TimeYear ){
                                this.optTime[item].queryTime.push(this.resultTimes[indexs])
                            }
                        })
                    }
                }
                // 关闭弹框
                this.showBox = false;
            }
        })
    },
    methods:{
        // 初始化数据,获取前20年,然后循环 每一年里面都有12个月的 得到数组 opTime 和 DateList
        init(){
            const _this = this;
            let _opt = [];
            let _optTime = []
            let arr = new Array(12);
            let optDate = this.getDateList();
            optDate.map((item,index)=>{
                // 月份选择时el-checkbox-group绑定的值
                _optTime[index] = {
                    TimeYear: item,
                    queryTime: []
                }
                // 给每一年份设置12个月份,el-checkbox初始化显示时使用
                _opt[index] = {
                    TimeYear: item, 
                    queryTime: []
                }
                for(let i = 1; i<= arr.length; i++){
                     _opt[index].queryTime.push(i)
                }
            })
            _this.optTime = _optTime
            _this.DateList = _opt;
        },
        // 获取近20年年份列表,倒序排列,最新一年在最前面
        getDateList(){
            let Dates = new Date();
            let year = Dates.getFullYear();
            this.OneY = year;
            let  optDate = [];
            for( let i = year - 20; i <= year; i++ ){
                optDate.push(i)
            }
            return optDate.reverse()
        },
        // 确定
        handleSubmit(){
            const _this = this;
            // 更新输入框的值
            _this.inputValue = _this.optTimes.join(',')
            // 根据输入框是否有值来判断清空图标是否渲染
            _this.showClear = _this.inputValue == '' ? false : true
            // 将点击“确定”按钮的选择结果保存起来(该值将在哪里使用:在点击弹框以外区域关闭弹框时使用,mounted中)
            _this.resultTimes = _this.optTimes
            // 关闭弹框
            _this.showBox = false
            _this.$emit('submitBtn',_this.resultTimes)
        },
        // 重置
        resetMonth() {
            const _this = this;
            // 将年份重置到当前年份
            let Dates = new Date();
            let year = Dates.getFullYear();
            _this.OneY = year;
            // 将已选择的月份清空
            _this.optTimes = [];
            for( let i in _this.optTime  ){
                _this.optTime[i].queryTime =[]
            }
            // 将输入框清空
            _this.inputValue = ''
            // 根据输入框是否有值来判断清空图标是否渲染,此处必然不渲染
            this.showClear = false
            // 将点击“确定”按钮的选择结果清空
            _this.resultTimes = []
            // 关闭月份选择弹框
            _this.showBox = false
            _this.$emit('resetBtn')
        },
        // 左上角年份减少
        reduceYear() {
            const _this = this;
            // 如果已经是最后一年了,则年份不能再减少了
            if(_this.curIndex == _this.DateList.length - 1) return;
            // 当前下标值+1,根据下标值获取年份值
            _this.curIndex = _this.curIndex + 1
            _this.OneY = _this.DateList[_this.curIndex].TimeYear
        },
        // 左上角年份增加
        addYear() {
            const _this = this;
            // 如果已经是当前年份了,则年份不能再增加了
            if(_this.curIndex == 0) return;
            // 当前下标值-1,根据下标值获取年份值
            _this.curIndex = _this.curIndex - 1
            _this.OneY = _this.DateList[_this.curIndex].TimeYear
        },
        // 选择月份
        onChange(){
            const _this = this;
            // 遍历optTime中已选择的月份,将已选结果塞到optTimes数组中
            let _opt = _this.optTime;
            let arr = [];
             for(let item in _opt ){
                if(_opt[item].queryTime.length > 0)  _opt[item].queryTime.filter( v => { arr.push(v) })
             }
            _this.optTimes = arr
            // 更新输入框的值
            _this.inputValue = _this.optTimes.join(',')
            // 根据输入框是否有值来判断清空图标是否渲染
            _this.showClear = _this.inputValue == '' ? false : true
        }
    }
}
</script>
<style lang="scss">
.flex {
    display: -webkit-box;
    display: -webkit-flex;
    display: flex;
}
.flex-wrap {
    flex-wrap: wrap;
}
.flex-around {
    justify-content: space-around;
}
.flex-x-center {
    display: -webkit-box;
    display: -webkit-flex;
    display: flex;
    -webkit-box-pack: center;
    -webkit-justify-content: center;
    -ms-flex-pack: center;
    justify-content: center;
}
.selectMonthBoxSquare {
    margin-top: 10px;
    width: 250px;
    .inputStyle {
        width: 250px;
    }
    .clearIconStyle {
        display: none;
    }
    .inputStyle:hover .clearIconStyle{
        display: block;
    }
    .selectContentBox {
        position: absolute;
        top: 35px;
        left: 0;
        z-index: 2021;
        background: #ffffff;
        border: 1px solid #e5e5e5;
        border-radius: 3px;
        
        .contentArea {
            width: 330px;
        }
    }
    .conterList{
        .onSelect{
            width: 25% !important;
            margin: 20px 0 !important;
        }
        .columWidth {
            width: 33.33%;
        }
        .el-checkbox__input {
            display: none !important;
        }
        .el-checkbox__label {
            padding-left: 0px !important;
        }
    }
    .selectBox {
        width: 100px;
        
        input {
            height: 25px;
            line-height: 25px;
        }
        .el-input .el-input__icon {
            line-height: 25px;
        }
    }
    .tagStyle {
        margin-right: 10px;
        height: 25px;
        line-height: 25px;
    }
    .lableStyle {
        font-size: 14px;
    }
    .el-button--mini {
        padding: 5px 15px;
        font-size: 12px;
        border-radius: 3px;
    }
    .buttonBox {
        border-top: 1px solid #e5e5e5;
        padding: 10px 10px 10px 0;
    }
}
</style>

 点击 “确定”按钮 和 “重置”按钮会传出方法给父组件,便于父组件中再做其它操作。

所需的小图标:

                        

简单的月份多选组件完成啦~~~

Logo

前往低代码交流专区

更多推荐