vue2实现多选季度选择器

注:本控件需要依赖element-ui的控件和样式来使用

参考资源:vue + element-ui 季度选择器组件 el-quarter-picker-优快云博客,并在此基础上实现多选季度的功能

1.导入element-ui

import elementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

2.新建一个vue文件 ElQuarterPicker.vue,实现代码如下

<template>
    <div class="el-quarter-picker" style="width: 100%;">
      <el-popover
        v-model="visible"
        :disabled="!canPopover"
        :tabindex="null"
        placement="bottom-start"
        transition="el-zoom-in-top"
        trigger="click">
   
        <div class="el-date-picker">
          <div class="el-picker-panel__body">
            <div class="el-date-picker__header el-date-picker__header--bordered" style="margin:0px; line-height:30px">
              <button
                type="button"
                @click="clickLast"
                aria-label="前一年"
                class="el-picker-panel__icon-btn el-date-picker__prev-btn el-icon-d-arrow-left"></button>
              <span role="button" class="el-date-picker__header-label" @click="clickYear">{{ title }}</span>
              <button
                type="button"
                @click="clickNext"
                aria-label="后一年"
                class="el-picker-panel__icon-btn el-date-picker__next-btn el-icon-d-arrow-right"></button>
            </div>
            <div class="el-picker-panel__content" style="margin:0px; width:100%">
              <table class="el-month-table" style="">
                <tbody>
                <tr v-for="line in lineCount" :key="line">
                  <td v-for="index in (line * 4 <= viewList.length ? 4 : viewList.length - (line - 1) * 4)" :key="index" :class="{ today: viewList[(line - 1) * 4 + index - 1].current, current: viewList[(line - 1) * 4 + index - 1].active }">
                    <div :class="{'div_disabled': viewList[(line - 1) * 4 + index - 1].disabled}"><a class="cell" @click="clickItem(viewList[(line - 1) * 4 + index - 1])">{{ viewList[(line - 1) * 4 + index - 1].label }}</a></div>
                  </td>
                </tr>
                </tbody>
              </table>
            </div>
          </div>
        </div>
   
        <el-input
          slot="reference"
          @change="changeText"
          @mouseenter.native="mouseEnter"
          @mouseleave.native="mouseLeave"
          :placeholder="placeholder"
          v-model="text"
          :size="size"
          :readonly="!canEdit"
          :disabled="disabled">
          <i slot="prefix" class="el-input__icon el-icon-date"></i>
          <i slot="suffix" class="el-input__icon el-icon-circle-close" v-show="showClear" style="cursor:pointer" @click.stop="clear"></i>
        </el-input>
      </el-popover>
    </div>
</template>
   
<script>
export default {
    name: 'ElQuarterPicker',
    props: {
        placeholder: {
            type: String,
            default: ''
        },
        size: {
            type: String,
            default: ''
        },
        readonly: {
            type: Boolean,
            default: false
        },
        clearable: {
            type: Boolean,
            default: true
        },
        editable: {
            type: Boolean,
            default: true
        },
        disabled: {
            type: Boolean,
            default: false
        },
        format: {
            type: String,
            default: 'yyyy年第Q季度'
        },
        valueFormat: {
            type: String,
            default: 'yyyy-qq'
        },
        value: {
            type: Array,
            default: []
        },
        multiple: { // 是否可多选
            type: Boolean,
            default: false
        },
        isHighLightCurYearQ: { //是否需要高亮显示当前的年和季度
            type: Boolean,
            default: false
        }
    },
    model: {
        prop: 'value',
        event: 'change'
    },
    watch: {
        value (val) {
        // console.log('change-------', val)
            this.changeValue(val)
        },
        readonly (val) {
            this.canEdit = !val && this.editable
            this.canPopover = !this.disabled && !val
        },
        editable (val) {
            this.canEdit = !this.readonly && val
        },
        disabled (val) {
            this.canPopover = !val && !this.readonly
        }
    },
    data () {
        return {
            isHighLightCurYearQ: false, // 是否需要高亮显示当前的年和当前的季度
            isDisabled: false,
            visible: false,
            showClear: false, // 控制清空按钮展示
            canEdit: true, // 是否可编辑
            canPopover: true, // 选择器弹出是否可用
            text: '', // 文本框值
            texts: [], // 所有选择的年度和季度,例2020-1,2020-2
            viewType: 1, // 视图类型,1季度,2年度
            viewYear: 0, // 当前年份
            viewList: [], // 数据列表
            lineCount: 0, // 数据行数
            title: '', // 选择器标题
            data: [0, 0], // 当前选择年度-季度
            curYear: 0, //当前年
            curQuarter: 0 //当前季度
        }
    },
    mounted () {
        // console.log('mounted--------', this.value)
        this.changeValue(this.value)
  
        // 设置文本框是否可编辑
        this.canEdit = !this.readonly && this.editable
        this.canPopover = !this.disabled && !this.readonly
  
    },
    destroyed () {
        document.onkeydown = null
    },
    methods: {
        //季度文本变更
        changeText () {
            const textSplits = this.text.split(',')
            var nTexts = []
            var allVerifySuccess = true
            textSplits.map(item => {
                if (! this.multiple && nTexts.length > 0) {
                    return
                }
                if (this.checkFormat(this.format, item)) {
                    this.formatFrom(item, this.format)
                    if (this.data[0] < 1 
                        || this.data[1] < 1
                        || this.data[0] > this.curYear
                        || (this.data[0] == this.curYear && this.data[1] > this.curQuarter)
                    ) {
                        allVerifySuccess = false
                    } else {
                        const ct = this.formatTo([this.data[0], this.data[1]], this.valueFormat)
                        if (!nTexts.includes(ct)) {
                            this.viewYear = this.data[0]
                            nTexts.push(ct)
                        }
                    }
                } else {
                    allVerifySuccess = false
                }
            })
            if (allVerifySuccess) {
                this.texts = nTexts.slice() // 记录新值
            //   this.$emit('change', this.formatTo(this.data, this.valueFormat))
                this.$emit('change', this.texts)
            } else { // 输入了无效的格式,还原回原来的值
                this.text = ''
                this.texts.map(item => {
                    this.formatFrom(item, this.valueFormat)
                    this.text += this.formatTo(this.data, this.format) + ','
                })
                if (this.text.indexOf(',') !== -1) { // 移除最后一个,
                    this.text = this.text.slice(0, -1)
                }
                this.viewYear = this.data[0]
            }
        },
        // 鼠标进入
        mouseEnter () {
            if (!this.disabled && !this.readonly && this.clearable && this.text !== '') {
                this.showClear = true
            }
        },
        // 鼠标离开
        mouseLeave () {
            if (!this.disabled && this.clearable) {
                this.showClear = false
            }
        },
        // 清除季度
        clear () {
            this.showClear = false
            this.visible = false
            this.texts = []
            this.$emit('change', this.texts)
        },
        // 季度值变更
        changeValue (val) {
            console.log(`changeValue>>>val:${JSON.stringify(val)}`)
            this.viewType = 1
            if (val && val.length > 0) {
                // 反向格式化
                this.text = ''
                val.map(item => {
                    this.formatFrom(item, this.valueFormat)
                    this.text += this.formatTo(this.data, this.format) + ','
                })
                if (this.text.indexOf(',') !== -1) { // 移除最后一个,
                    this.text = this.text.slice(0, -1)
                }
                this.viewYear = this.data[0]
            } else {
                this.text = ''
                this.texts = []
                this.data = [0, 0]
                this.viewYear = new Date().getFullYear()
            }
            this.initView()
        },
        // 初始化视图数据
        initView () {
            const list = []
            const curDate = new Date()
            this.curYear = curDate.getFullYear()
            this.curQuarter = parseInt(curDate.getMonth() / 3) + 1
            if (this.viewType === 1) {
                let index = 0
                for (const i of '一二三四') {
                    index++
                    const item = { label: '第' + i + '季度', year: this.viewYear, quarter: index, current: false, active: false, disabled: false }
                    if (this.viewYear === this.curYear && index === this.curQuarter && this.isHighLightCurYearQ) {
                        item.current = true
                    } else {
                        this.texts.map(it => {
                            this.formatFrom(it, this.valueFormat)
                            if (this.viewYear === +this.data[0] && index === +this.data[1]) {
                                item.active = true
                            }
                        })
                    }
                    if (this.viewYear > this.curYear) {
                        item.disabled = true
                    } else if (this.viewYear == this.curYear && index > this.curQuarter) {
                        item.disabled = true
                    }
                    list.push(item)
                }
                this.title = this.viewYear + ' 年'
            } else {
                const start = parseInt(this.viewYear / 10) * 10
                this.viewYear = start
                for (let i = 0; i < 10; i++) {
                    const year = start + i
                    const item = { label: year + '', year: year, current: false, active: false }
                    if (year === this.curYear && this.isHighLightCurYearQ) {
                        item.current = true
                    } else {
                        this.texts.map(it => {
                            this.formatFrom(it, this.valueFormat)
                            if (year === +this.data[0]) {
                                item.active = true
                            }
                        })
                    }
                    if (year > this.curYear) {
                        item.disabled = true
                    }
                    list.push(item)
                }
                this.title = start + ' 年 - ' + (start + 9) + ' 年'
            }
            this.viewList = list
            this.lineCount = parseInt(list.length / 4)
            if (list.length % 4 > 0) {
                this.lineCount++
            }
        },
        // 校验季度格式是否正确
        checkFormat (pattern, val) {
            // 格式转成正则表达式
            let text = ''
            for (const char of pattern) {
                const dict = '\\^$.+?*[]{}!'
                if (dict.indexOf(char) === -1) {
                    text += char
                } else {
                    text += '\\' + char
                }
            }
            text = text.replace('yyyy', '[1-9]\\d{3}')
            text = text.replace('qq', '0[1-4]')
            text = text.replace('q', '[1-4]')
            text = text.replace('Q', '[一二三四]')
            text = '^' + text + '$'
            const patt = new RegExp(text)
            return patt.test(val)
        },
        // 格式化季度到指定格式
        formatTo (data, pattern) {
            let text = pattern.replace('yyyy', '' + data[0])
            text = text.replace('qq', '0' + data[1])
            text = text.replace('q', '' + data[1])
            text = text.replace('Q', '一二三四'.substr(data[1] - 1, 1))
            return text
        },
        // 以指定格式解析季度
        formatFrom (str, pattern) {
            const year = this.findText(str, pattern, 'yyyy')
            const quarter = this.findText(str, pattern, ['qq', 'q', 'Q'])
            this.data = [year, quarter]
        },
        // 查找文本数值
        findText (str, pattern, find) {
            if (find instanceof Array) {
                for (const f of find) {
                    const val = this.findText(str, pattern, f)
                    if (val !== -1) {
                        return val
                    }
                }
                return -1
            }
            const index = pattern.indexOf(find)
            if (index === -1) {
                return index
            }
            const val = str.substr(index, find.length)
            if (find === 'Q') {
                return '一二三四'.indexOf(val) + 1
            } else {
                return parseInt(val)
            }
        },
        // 年份点击
        clickYear () {
            if (this.viewType !== 1) {
                return
            }
            // 切换年度选择器
            this.viewType = 2
            this.initView()
        },
        // 季度选择
        clickItem (item) {
        //   console.log('select--------', JSON.stringify(item))
            if (this.viewType === 1) {
                // 选择季度
                this.visible = false
                // this.$emit('change', this.formatTo([item.year, item.quarter], this.valueFormat))
                const dataTemp = this.formatTo([item.year, item.quarter], this.valueFormat)
                if (! this.multiple) {
                    this.texts = []
                }
                // 判断是否已经包含
                if (!this.texts.includes(dataTemp)) {
                    this.viewYear = item.year
                    this.texts.push(dataTemp)
                }
                this.$emit('change', this.texts)
            } else {
                // 选择年度
                this.viewType = 1
                this.viewYear = item.year
                this.initView()
            }
        },
        // 上一年
        clickLast () {
            if (this.viewYear > 1000) {
                if (this.viewType === 1) {
                    this.viewYear--
                    this.initView()
                } else {
                    this.viewYear = this.viewYear - 10
                    this.initView()
                }
            }
        },
        // 下一年
        clickNext () {
            if (this.viewYear < 9999) {
                if (this.viewType === 1) {
                    this.viewYear++
                    this.initView()
                } else {
                    this.viewYear = this.viewYear + 10
                    this.initView()
                }
            }
        }
    }
}
</script>
   
<style>
.el-quarter-picker {
    width: 220px;
    display: inline-block;
}
.div_disabled {
    opacity: 0.6;
    pointer-events: none; /* 阻止鼠标事件 */
    cursor: not-allowed; /* 改变鼠标指针样式 */
}
</style>

3.在需要使用季度选择器的页面中导入后使用,使用方式如下:

<el-quarter-picker v-model="quarters" placeholder="请选择季度" multiple isHighLightCurYearQ/>

其中,multiple参数表示是否需要多选季度,isHighLightCurYearQ参数表示是否需要高亮显示当前的年份和季度,返回值为选择的季度值的数组

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值