iview和elementUI编写的季度组件合集

 1:iview的季度组件 是一个博客那里找到的一起学习

原文章changeYear有点小问题,我修复了下,可选择季度和左右切换年份

<template>
    <div v-clickoutside="closePicker" style="width: 100px; display: inline-block">
        <Input v-model="message" :icon="arrowType" readonly
               @on-focus="show"
               @on-click="clickIcon"
               @mouseenter.native="handleInputMouseenter"
               @mouseleave.native="handleInputMouseleave"></Input>
        <div v-show="visible" class="pickerCard">
            <div class="pickHeader">
                <span class="pickerIconBtn leftIcon ivu-date-picker-prev-btn-arrow-double" @click="changeYear('last')">
                    <Icon type="ios-arrow-back"></Icon>
                </span>
                <span>
                    {{year}}年
                </span>
                <span class="pickerIconBtn rightIcon ivu-date-picker-next-btn-arrow-double" @click="changeYear('next')">
				    <Icon type="ios-arrow-forward"></Icon>
			    </span>
            </div>
            <div class="pickQuarterCard">
                <span v-for="item in dataList" :key="item.value" :class="item.disable ?'disableSpan': 'defaultSpan'" @click="handleClick(item)">
                    <em :class="!item.disable && quarter===item.value ? 'selected': 'defaultEm'">{{item.label}}</em>
                </span>
            </div>
        </div>
    </div>
</template>

<script>
    const clickoutside = {
        // 初始化指令
        bind (el, binding, vnode) {
            function documentHandler (e) {
                // 这里判断点击的元素是否是本身,是本身,则返回
                if (el.contains(e.target)) {
                    return false;
                }
                // 判断指令中是否绑定了函数
                if (binding.expression) {
                    // 如果绑定了函数 则调用那个函数,此处binding.value就是handleClose方法
                    binding.value(e);
                }
            }
            // 给当前元素绑定个私有变量,方便在unbind中可以解除事件监听
            el.__vueClickOutside__ = documentHandler;
            document.addEventListener('click', documentHandler);
        },
        unbind (el, binding) {
            // 解除事件监听
            document.removeEventListener('click', el.__vueClickOutside__);
            delete el.__vueClickOutside__;
        }
    };
    export default {
        name: 'quarter-picker',
        props: {
            options: { // 不可选日期
                type: Object,
                default: () => ({})
            },
            clearable: {
                type: Boolean,
                default: true
            },
            value: {
                type: [String],
                default: ''
            }
        },
        data () {
            return {
                visible: false,
                message: null,
                year: null,
                quarter: null,
                currentYear: null,
                currentQuarter: null,
                dataList: [
                    {label: 'Q1', value: 1, disable: false},
                    {label: 'Q2', value: 2, disable: false},
                    {label: 'Q3', value: 3, disable: false},
                    {label: 'Q4', value: 4, disable: false}
                ],
                showClose: false
            };
        },
        computed: {
            arrowType () {
                return this.showClose ? 'ios-close-circle' : 'ios-calendar-outline';
            }
        },
        directives: { clickoutside },
        watch: {
            value () {
                if (this.value !== this.message) {
                    this.initData();
                }
            }
        },
        created () {
            this.currentYear = new Date().getFullYear();
            const currentMonth = new Date().getMonth() + 1;
            if (currentMonth < 4) {
                this.currentQuarter = 1;
            } else if (currentMonth < 7) {
                this.currentQuarter = 2;
            } else if (currentMonth < 10) {
                this.currentQuarter = 3;
            } else {
                this.currentQuarter = 4;
            }
            this.initData();
        },
        methods: {
            initData () {
                if (this.value) {
                    const arr = this.value.split('Q');
                    this.year = arr[0];
                    this.quarter = Number(arr[1]);
                    this.message = this.value;
                } else {
                    this.year = this.currentYear;
                    this.quarter = this.currentQuarter;
                    this.message = '';
                }
            },
            show () {
                this.handleData();
                this.visible = true;
            },
            handleInputMouseenter () {
                this.showClose = this.message && this.clearable;
            },
            handleInputMouseleave () {
                this.showClose = false;
            },
            clickIcon () {
                if (this.showClose) {
                    this.quarter = null;
                    this.message = null;
                    this.$emit('on-change', this.message, this.year, null);
                }
            },
            changeYear (type) {
                if (type === 'last') {
                    this.year--;
                } else {
                    this.year++;
                }
                let val = this.message.split('Q')
                if(this.message){
                    if(this.year === Number(val[0])){
                        this.quarter = Number(val[1])
                    } else {
                        this.quarter = null
                    }
                }
                this.handleData();
            },
            handleData () {
                if (this.options && this.options.disabledDate) {
                    this.dataList.forEach(item => {
                        item.disable = this.options.disabledDate(this.year, item.value);
                    });
                }
            },
            handleClick (item) {
                if (item.disable) return;
                this.quarter = item.value;
                this.message = this.year + 'Q' + item.value;
                this.$emit('on-change', this.message, this.year, item.value);
                this.closePicker();
            },
            closePicker () {
                this.visible = false;
            }
        },
    };
</script>

<style scoped lang="less">
    .pickerCard{
        width: 200px;
        font-size: 12px;
        z-index: 100;
        background: #fff;
        border-radius: 0.3rem;
        position:absolute;
        margin: 5px 0;
        box-shadow: 0 1px 6px rgba(0,0,0,.2);
        .pickHeader{
            height: 32px;
            line-height: 32px;
            text-align: center;
            border-bottom: 1px solid #e8eaec;
            .leftIcon{
                float: left;
            }
            .rightIcon{
                float: right;
            }
            .pickerIconBtn{
                display: inline-block;
                width: 20px;
                height: 24px;
                line-height: 26px;
                margin-top: 2px;
                text-align: center;
                cursor: pointer;
                color: #c5c8ce;
                transition: color .2s ease-in-out;
            }
        }
        .pickQuarterCard{
            margin: 10px 8px;
            white-space: normal;
            span {
                //width: 60px;
                height: 28px;
                line-height: 28px;
                margin: 10px 12px;
                border-radius: 3px;
                cursor: pointer;
                display: inline-block;
                em{
                    padding: 0 10px;
                    height: 28px;
                    line-height: 28px;
                    display: inline-block;
                    margin: 0;
                    font-style: normal;
                    border-radius: 3px;
                    text-align: center;
                    transition: all .2s ease-in-out;
                }
            }
            .disableSpan{
                cursor: not-allowed;
                color: #c5c8ce;
                background: #f7f7f7;
            }
            .defaultSpan:hover .defaultEm {
                background: #e1f0fe;
            }
            .defaultSpan .selected{
                background: #2d8cf0;
                color: #fff;
            }
        }
    }
</style>

 

 

2:elementUi 可选择季度和年份,左右切换年份

<template>
  <div class="el-quarter-picker">
    <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><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: String,
      default: ''
    }
  },
  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 {
      visible: false,
      showClear: false, // 控制清空按钮展示
      canEdit: true, // 是否可编辑
      canPopover: true, // 选择器弹出是否可用
      text: '', // 文本框值
      viewType: 1, // 视图类型,1季度,2年度
      viewYear: 0, // 当前年份
      viewList: [], // 数据列表
      lineCount: 0, // 数据行数
      title: '', // 选择器标题
      data: [0, 0] // 当前选择年度-季度
    }
  },
  mounted() {
    // console.log('mounted--------', this.value)
    this.changeValue(this.value)
 
    // 设置文本框是否可编辑
    this.canEdit = !this.readonly && this.editable
    this.canPopover = !this.disabled && !this.readonly
 
    // 监听按键(上下左右键可以切换季度)
    document.onkeydown = (event) => {
      if (this.visible) {
        const data = [this.data[0], this.data[1]]
        if (data[0] < 1 || data[1] < 1) {
          // 以当前季度为标准
          const curDate = new Date()
          data[0] = curDate.getFullYear()
          data[1] = parseInt(curDate.getMonth() / 3) + 1
        }
        if (event.code === 'ArrowLeft') {
          // 上一个季度
          if (data[1] === 1) {
            data[0] = data[0] - 1
            data[1] = 4
          } else {
            data[1] = data[1] - 1
          }
        } else if (event.code === 'ArrowRight') {
          // 下一个季度
          if (data[1] === 4) {
            data[0] = data[0] + 1
            data[1] = 1
          } else {
            data[1] = data[1] + 1
          }
        } else if (event.code === 'ArrowUp') {
          // 上一年季度
          data[0] = data[0] - 1
        } else if (event.code === 'ArrowDown') {
          // 下一年季度
          data[0] = data[0] + 1
        } else {
          return
        }
 
        // 超过年限的不处理
        if (data[0] < 1000 || data[0] > 9999) {
          return
        }
        this.data = data
        this.viewType = 1
        this.viewYear = data[0]
        this.$emit('change', this.formatTo(data, this.valueFormat))
      }
    }
  },
  destroyed() {
    document.onkeydown = null
  },
  methods: {
    // 季度文本变更
    changeText() {
      if (this.checkFormat(this.format, this.text)) {
        // 设置值
        this.formatFrom(this.text, this.format)
        this.$emit('change', this.formatTo(this.data, this.valueFormat))
      } else {
        // 输入了无效的格式,还原回原来的值
        if (this.data[0] < 1 || this.data[1] < 1) {
          this.text = ''
        } else {
          this.text = this.formatTo(this.data, this.format)
        }
      }
      this.visible = false
    },
    // 鼠标进入
    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.$emit('change', '')
    },
    // 季度值变更
    changeValue(val) {
      this.viewType = 1
      if (val) {
        // 反向格式化
        this.formatFrom(val, this.valueFormat)
        this.text = this.formatTo(this.data, this.format)
        this.viewYear = this.data[0]
      } else {
        this.text = ''
        this.data = [0, 0]
        this.viewYear = new Date().getFullYear()
      }
      this.initView()
    },
    // 初始化视图数据
    initView() {
      const list = []
      const curDate = new Date()
      const curYear = curDate.getFullYear()
      const 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 }
          if (this.viewYear === curYear && index === curQuarter) {
            item.current = true
          } else if (this.viewYear === this.data[0] && index === this.data[1]) {
            item.active = 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 === curYear) {
            item.current = true
          } else if (year === this.data[0]) {
            item.active = 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--------', item)
      if (this.viewType === 1) {
        // 选择季度
        this.$emit('change', this.formatTo([item.year, item.quarter], this.valueFormat))
        this.visible = false
      } 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;
  }
</style>

 

 

 因为公司用的是iview框架,测试的时候发现没办法显示可选年份,后面自己结合这两篇改造了下,代码如下:因为公司需求要第一次进来就要反显当前年季度所以用了init直接输出

this.$emit('quarterTodayDate', this.message)这个方法获取年季度值

<template>
    <div>
        <Input v-model="message" :icon="arrowType" readonly
               @on-focus="show"
               @click="clickIcon"
               @mouseenter.native="handleInputMouseenter"
               @mouseleave.native="handleInputMouseleave"></Input>
        <div v-show="visible" class="pickerCard">
            <div class="pickHeader">
                <span class="pickerIconBtn leftIcon ivu-date-picker-prev-btn-arrow-double" @click="changeYear('last')">
                    <Icon type="ios-arrow-back"></Icon>
                </span>
                <span @click="clickYear">
                    {{year}}年
                </span>
                <span class="pickerIconBtn rightIcon ivu-date-picker-next-btn-arrow-double" @click="changeYear('next')">
				    <Icon type="ios-arrow-forward"></Icon>
			    </span>
            </div>
            <div class="pickQuarterCard">
                <Row v-for="i in Math.ceil(dataList.length / 4)" :key="i">
                    <Col
                       span="6"
                        v-for="(item, index) in dataList.slice(4 * (i - 1), 4 * (i - 1) + 4)"
                        :key="i + '-' + index"
                        :class="item.disable ?'disableSpan': 'defaultSpan'"
                    >
                        <span @click="handleClick(item)" :class="!item.disable && quarter===item.value ? 'selected': 'defaultEm'">{{ item.label }}</span>
                    </Col>
                </Row>
            </div>
        </div>
    </div>
</template>
<script>
    export default {
        name: 'quarter-picker',
        props: {
            options: { // 不可选日期
                type: Object,
                default: () => ({})
            },
            clearable: {
                type: Boolean,
                default: false
            },
            value: {
                type: [String],
                default: ''
            }
        },
        data () {
            return {
                visible: false,
                message: null,
                year: null,
                quarter: null,
                currentQuarter: null,
                quarterList: [
                    {label: 'Q1', value: 1, disable: false},
                    {label: 'Q2', value: 2, disable: false},
                    {label: 'Q3', value: 3, disable: false},
                    {label: 'Q4', value: 4, disable: false}
                ],
                showClose: false,
                dataList: [],
                viewType: 1, // 视图类型,1季度,2年度
                viewYear: 0,
                data: [0, 0] // 当前选择年度-季度
            };
        },
        computed: {
            arrowType () {
                return this.showClose ? 'ios-close-circle' : 'ios-calendar-outline';
            }
        },
        watch: {
            value () {
                if (this.value !== this.message) {
                    this.initData();
                }
            }
        },
        created () {
            this.viewYear = new Date().getFullYear()
            this.currentYear = new Date().getFullYear()
            const currentMonth = new Date().getMonth() + 1;
            if (currentMonth < 4) {
                this.currentQuarter = 1;
            } else if (currentMonth < 7) {
                this.currentQuarter = 2;
            } else if (currentMonth < 10) {
                this.currentQuarter = 3;
            } else {
                this.currentQuarter = 4;
            }
            this.init()
        },
        mounted() {
            this.viewType = 1
        },
        methods: {
            init () {
                this.year = this.currentYear;
                this.quarter = this.currentQuarter;
                this.message = this.viewYear + 'Q' + this.currentQuarter
                this.$emit('quarterTodayDate', this.message)
                this.dataList = this.quarterList
            },
            initData () {
                const list = []
                const arr = this.value.split('Q');
                if (this.viewType === 1) {
                    if(Number(arr[0]) === this.year){
                        this.year = arr[0];
                        this.quarter = Number(arr[1]);
                    } else {
                        this.quarter = null
                    }
                    this.dataList = this.quarterList
                } else {
                    const start = parseInt(this.viewYear / 10) * 10
                    for (let i = 0; i < 10; i++) {
                        const year = start + i
                        const item = { label: year + '', value: year }
                        // 当前选中年
                        if (year === Number(arr[0])) {
                            this.quarter = Number(arr[0])
                        }
                        list.push(item)
                    }
                    this.year = start + '年 - ' + (start + 9)
                    this.dataList = list
                }
            },
            show () {
                this.handleData();
                this.visible = true;
            },
            handleInputMouseenter () {
                this.showClose = this.message && this.clearable;
            },
            handleInputMouseleave () {
                this.showClose = false;
            },
               clickIcon () {
                if (this.showClose) {
                    this.quarter = null;
                    this.message = null;
                    this.$emit('on-change', this.message, this.year, null);
                }
            },
            // 上,下一年
            changeYear (type) {
                if (this.viewType === 1) {
                    if (type === 'last') {
                        this.year--;
                    } else {
                        this.year++;
                    }
                    let val = this.message.split('Q')
                    if(this.message){
                        if(this.year === Number(val[0])){
                            this.quarter = Number(val[1])
                        } else {
                            this.quarter = null
                        }
                    }
                    this.handleData();
                } else {
                    if (type === 'last') {
                        this.viewYear = this.viewYear - 10
                      } else {
                        this.viewYear = this.viewYear + 10
                    }
                    this.initData()
                }
            },
             handleData () {
                if (this.options && this.options.disabledDate) {
                    this.dataList.forEach(item => {
                        item.disable = this.options.disabledDate(this.year, item.value);
                    });
                }
            },
            handleClick (item) {
                if (this.viewType === 1) {
                    // 选择季度
                    if (item.disable) return
                    this.quarter = item.value
                    this.message = this.year + 'Q' + item.value
                    this.closePicker()
                    this.$emit('on-change', this.message, this.year, item.value)
                    this.$emit('quarterTodayDate', this.year + 'Q' +this.quarter,'handleClick')
                } else {
                    // 选择年度
                    this.viewType = 1
                    this.year = item.value
                    this.initData()
                }
            },
            closePicker () {
                this.visible = false;
            },
            // 年份点击
            clickYear() {
                if (this.viewType !== 1) {
                    return
                }
                // 切换年度选择器
                this.viewType = 2
                this.initData()
            },
 },
    };
</script>

效果如下 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值