elementUI 年份范围选择器实现

elementUI 不支持年份范围的选择器,依照下面的文章进行修改和完善 el-year-picker

element日期选择范围、选择年份范围_elemet 两个日期 选择的年份范围必须在三年之内-CSDN博客


 el-year-picker 组件:

依赖包:moment
属性:
  • sp:默认 ‘至’
  • value:默认 [ ] 
  • size:默认 large,支持 small、mini
  • clearable:默认 true
  • startPlaceholder:默认 开始年份
  • endPlaceholder:默认 结束年份

事件:
  • input:参数 value[ ]

 效果图

代码

index.vue 
<template>
  <el-popover
    ref="popover"
    placement="bottom"
    v-model="showPanel"
    popper-class="custom_year_range"
    trigger="manual"
    v-clickoutside="() => { showPanel = false }"
  >
    <div class="_inner floatPanel">
      <div class="_inner leftPanel">
        <div class="_inner panelHead">
          <i class="_inner el-icon-d-arrow-left" @click="onClickLeft"></i>
          <span>{{ leftYearList[0] + '年 ' + '- ' + leftYearList[9] + '年' }}</span>
        </div>
        <div class="_inner panelContent">
          <div
            :class="{
            oneSelected: item === startYear && oneSelected,
            startSelected: item === startYear,
            endSelected: item === endYear,
            betweenSelected: item > startYear && item < endYear,
          }"
            v-for="item in leftYearList"
            :key="item"
          >
            <a
              :class="{
              cell: true,
              _inner: true,
              selected: item === startYear || item === endYear,
            }"
              @click="onClickItem(item)"
              @mouseover="onHoverItem(item)"
            >{{ item }}</a>
          </div>
        </div>
      </div>
      <div class="_inner rightPanel">
        <div class="_inner panelHead">
          <i class="_inner el-icon-d-arrow-right" @click="onClickRight"></i>
          <span>{{ rightYearList[0] + '年 ' + '- ' + rightYearList[9] + '年' }}</span>
        </div>
        <div class="_inner panelContent">
          <div
            :class="{
            startSelected: item === startYear,
            endSelected: item === endYear,
            betweenSelected: item > startYear && item < endYear,
          }"
            v-for="item in rightYearList"
            :key="item"
          >
            <a
              :class="{
              cell: true,
              _inner: true,
              selected: item === endYear || item === startYear,
            }"
              @click="onClickItem(item)"
              @mouseover="onHoverItem(item)"
            >{{ item }}</a>
          </div>
        </div>
      </div>
    </div>
    <div slot="reference">
      <div
        ref="yearPicker"
        style="width: 100%"
        class="el-date-editor el-range-editor el-input__inner el-date-editor--daterange yearPicker"
        :class="['el-range-editor--' + size, showPanel ? 'is-active' : '', startShowYear ? 'is-val' : '']"
        @mouseover="handleHover(true)"
        @mouseleave="handleHover(false)"
      >
        <i class="el-input__icon el-range__icon el-icon-date"></i>
        <input
          class="_inner range_input"
          ref="inputLeft"
          type="text"
          name="yearInput"
          :placeholder="startPlaceholder"
          v-model="startShowYear"
          @focus="onFocus"
          @keyup="handleInput('start')"
        />
        <span class="el-range-separator">{{ sp }}</span>
        <input
          class="_inner range_input"
          ref="inputRight"
          type="text"
          name="yearInput"
          :placeholder="endPlaceholder"
          v-model="endShowYear"
          @focus="onFocus"
          @keyup="handleInput('end')"
        />
        <i
          class="el-input__icon el-range__close-icon"
          :class="[startShowYear && isHover && clearable ? 'el-icon-circle-close' : '']"
          @click="onClear"
        ></i>
      </div>
    </div>
  </el-popover>
</template>

<script>
import moment from 'moment'
import { clickoutside, SELECT_STATE } from './utils.js'
export default {
  name: 'ElYearPicker',
  directives: { clickoutside },
  computed: {
    oneSelected () {
      return this.curState === SELECT_STATE.selecting && (this.startYear === this.endYear || this.endYear == null)
    },
    leftYearList () {
      return this.yearList.slice(0, 10)
    },
    rightYearList () {
      return this.yearList.slice(10, 20)
    }
  },
  props: {
    sp: {
      default: '至'
    },
    value: {
      type: Array,
      default: [],
    },
    size: {
      type: String,
      default: 'large'
    },
    clearable: {
      type: Boolean,
      default: true
    },
    startPlaceholder: {
      type: String,
      default: '开始年份'
    },
    endPlaceholder: {
      type: String,
      default: '结束年份'
    }
  },
  data () {
    return {
      itemBg: {},
      startShowYear: null,
      endShowYear: null,
      yearList: [],
      showPanel: false,
      startYear: null,
      endYear: null,
      curYear: 0,
      curSelectedYear: 0,
      curState: SELECT_STATE.unselect,
      isHover: false
    }
  },
  methods: {
    handleInput (type) {
      switch (type) {
        case 'start':
          if (isNaN(this.startShowYear)) {
            this.startShowYear = this.startYear
            return
          }
          this.startYear = this.startShowYear * 1
          break
        case 'end':
          if (isNaN(this.endShowYear)) {
            this.endShowYear = this.endYear
            return
          }
          this.endYear = this.endShowYear * 1
          break
      }
      [this.startYear, this.endYear] = [this.endYear, this.startYear]
      this.startShowYear = this.startYear
      this.endShowYear = this.endYear
    },

    onHoverItem (iYear) {
      if (this.curState === SELECT_STATE.selecting) {
        const tmpStart = this.curSelectedYear
        this.endYear = Math.max(tmpStart, iYear)
        this.startYear = Math.min(tmpStart, iYear)
      }
    },

    async onClickItem (selectYear) {
      if (
        this.curState === SELECT_STATE.unselect ||
        this.curState === SELECT_STATE.selected
      ) {
        this.startYear = selectYear
        this.curSelectedYear = selectYear
        this.endYear = null
        this.curState = SELECT_STATE.selecting
      } else if (this.curState === SELECT_STATE.selecting) {
        this.endShowYear = this.endYear || this.startYear
        this.startShowYear = this.startYear
        this.curState = SELECT_STATE.selected
        await this.$nextTick()
        this.showPanel = false
        this.$parent.$parent.$parent.$parent.$parent.clearValidate()
      }
    },

    async onFocus () {
      await this.$nextTick()
      this.showPanel = true
    },

    handleHover (flag) {
      this.isHover = flag
    },

    updateYearList () {
      const startYear = ~~(this.curYear / 10) * 10
      console.log(startYear, this.curYear, 'this.curYearthis.curYearthis.curYear')
      this.yearList = []
      for (let index = 0; index < 20; index++) {
        this.yearList.push(startYear + index)
      }
    },

    onClickLeft () {
      this.curYear = this.curYear * 1 - 10
      this.updateYearList()
    },

    onClickRight () {
      this.curYear = this.curYear * 1 + 10
      this.updateYearList()
    },

    onClear () {
      if (this.startShowYear && this.isHover && this.clearable) {
        this.startYear = null
        this.endYear = null
        this.curSelectedYear = 0
        this.curState = SELECT_STATE.unselect

        this.showPanel = false;
        this.curYear = moment().format('yyyy')
        this.updateYearList()
        this.startShowYear = ''
        this.endShowYear = ''
        this.$emit('input', [])
      }
    }
  },
  watch: {
    value: {
      handler (val) {
        if (val.length == 0) {
          this.startShowYear = ''
          this.endShowYear = ''
        } else {
          const [first, end] = val || []
          this.startShowYear = val[0]
          this.endShowYear = val[1]
        }

      },
      immediate: true,
      deep: true
    },

    startShowYear: {
      handler (val) {
        this.$emit('input', [val, this.endShowYear || ''])
      },
      immediate: true,
      deep: true
    },

    endShowYear: {
      handler (val) {
        this.$emit('input', [this.startShowYear || '', val])
      },
      immediate: true,
      deep: true
    }
  },
  created () {
    console.log('value', this.value)
    const [startYear, endYear] = this.value || []
    if (startYear) {
      this.startYear = Number(startYear)
      this.endYear = Number(endYear)
      this.curState = SELECT_STATE.selected
      this.curYear = startYear
    } else {
      this.curYear = moment().format('yyyy')
    }
    this.updateYearList()
  },

  mounted () {
    window.Vue = this
  }
}
</script>
<style lang="scss">
.custom_year_range {
  border-radius: 10px;
  .floatPanel {
    > div {
      width: 50%;
    }

    padding: 0 16px;
    // position: absolute;
    display: flex;
    background-color: #fff;
    z-index: 2000;
    border-radius: 4px;
    width: 650px;
    height: 250px;
    top: 40px;
    left: -50px;

    .panelContent {
      display: flex;
      flex-wrap: wrap;
      width: 100%;
      height: calc(100% - 70px);

      .oneSelected {
        border-top-right-radius: 24px;
        border-bottom-right-radius: 24px;
      }

      .startSelected {
        background-color: #f2f6fc;
        border-top-left-radius: 24px;
        border-bottom-left-radius: 24px;
      }

      .endSelected {
        background-color: #f2f6fc;
        border-top-right-radius: 24px;
        border-bottom-right-radius: 24px;
      }

      .betweenSelected {
        background-color: #f2f6fc;
      }

      > div {
        width: 75px;
        height: 48px;
        line-height: 48px;
        margin: 3px 0;
        // border-radius: 24px;
        text-align: center;

        a {
          display: inline-block;
          width: 60px;
          height: 36px;
          cursor: pointer;
          line-height: 36px;
          border-radius: 18px;

          &:hover {
            color: #409eff;
          }
        }

        .selected {
          background-color: #409eff;
          color: #fff;

          &:hover {
            color: #fff !important;
          }
        }
      }
    }

    .panelHead {
      position: relative;
      height: 46px;
      line-height: 46px;
      text-align: center;
      display: flex;
      align-items: center;
      justify-content: center;

      span {
        font-size: 16px;
        font-weight: 500;
        padding: 0 5px;
        line-height: 22px;
        text-align: center;
        cursor: pointer;
        color: #606266;

        &:hover {
          color: #409eff;
        }
      }

      i {
        position: absolute;
        cursor: pointer;

        &:hover {
          color: #3e77fc;
        }
      }
    }

    .rightPanel {
      padding-left: 8px;
    }

    .leftPanel .panelHead i {
      left: 20px;
    }

    .rightPanel .panelHead i {
      right: 20px;
    }
  }

  .floatPanel::before {
    content: '';
    height: 100%;
    top: 0;
    position: absolute;
    left: 50%;
    width: 1px;
    border-left: 1px solid #e4e4e4;
  }

  .cell._inner {
    color: #606266;
    text-decoration: none;
  }
}
</style>
<style lang="scss" scoped>
.range_input {
  appearance: none;
  border: none;
  outline: 0;
  padding: 0;
  width: 130px;
  color: #606266;
  line-height: 1;
  height: 100%;
  margin: 0;
  text-align: center;
  display: inline-block;
}

.yearPicker {
  .el-icon-circle-close {
    color: #c0c4cc;
    &:hover {
      color: #909399;
    }
  }
  &.is-val {
  }
  &.is-active {
    border-color: #0052d9 !important;
  }
}

input {
  width: 60px;
  border: none;
  height: 32px;
  line-height: 32px;
  box-sizing: border-box;
  background-color: transparent;
}

input:focus {
  outline: none;
  background-color: transparent;
}
.dateIcon {
  position: absolute;
  right: 16px;
  top: 9px;
  color: #adb2bc;
}
</style>
utils.js
export const clickoutside = {
  bind(el, binding, vnode) {
    function documentHandler(e) {
      // 这里判断点击的元素是否是本身,是本身,则返回
      if (el.contains(e.target)) {
        return false
      }
      // 判断指令中是否绑定了函数
      if (binding && binding.expression) {
        // 如果绑定了函数 则调用那个函数,此处binding.value就是handleClose方法
        if (binding.value && binding.value(e)) {
          binding.value(e)
        }
      }
    }
    // 给当前元素绑定个私有变量,方便在unbind中可以解除事件监听
    el.__vueClickOutside__ = documentHandler
    document.addEventListener('click', documentHandler)
  },
  unbind(el, binding) {
    // 解除事件监听
    document.removeEventListener('click', el.__vueClickOutside__)
    delete el.__vueClickOutside__
  }
}

export const SELECT_STATE = {
  unselect: 0,
  selecting: 1,
  selected: 2
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/764241.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

赛灵思FFT的IP核——非实时模式 Non real time

一、IP核配置 使用非实时模式配置如下 二、时序 三、资源消耗 在implement查看两者的资源消耗差不多

怎么测试远程服务器能否连通

远程服务器连接测试的方法很多&#xff0c;下面简单介绍下其中两种方法。 ping命令 按WINR快截键&#xff0c;打开“运行”对话框&#xff0c;输入cmd&#xff0c;回车&#xff0c;打开命令提示符。 输入ping IP地址或ping 域名即可&#xff0c;如ping360服务器通不通&#xf…

前端接入chatgpt,实现流式文字的显示

前端接入chatgpt,实现流式文字的显示 业务需求&#xff1a; 项目需要接入chatgpt提供的api&#xff0c;后端返回流式的字符&#xff0c;前端接收并实时显示。 相关技术原理&#xff1a; 1. JS中的Stream流: 在JavaScript中&#xff0c;使用Stream流通常指的是处理数据流的…

RK3568驱动指南|第十五篇 I2C-第172章 I2C 驱动框架编写

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

吃瓜Llama3-V之余,看多模态大模型架构演变!

今天最大的瓜莫过于&#xff1a;斯坦福 Llama3-V PK 清华 MiniCPM-Llama3-V-2.5&#xff0c;详细证据&#xff1a; https://github.com/OpenBMB/MiniCPM-V/issues/196吃瓜之余&#xff0c;来看一下多模态大模型架构演变&#xff01; 一篇优秀的论文综述了多模态AI架构——包含…

无线领夹麦克风哪个牌子好,口碑最好的麦克风品牌推荐!

自媒体的兴起极大地推动了音频设备技术的发展&#xff0c;尤其是麦克风&#xff0c;它已成为自媒体创作中不可或缺的工具。从早期的新闻采访到当下流行的网络直播和Vlog&#xff0c;麦克风的应用场景不断扩展。一个视频的音频质量直接影响观众的观看体验&#xff0c;因此&#…

使用Netty框架实现WebSocket服务端与客户端通信(附ssl)

仓库地址&#xff1a; https://gitee.com/lfw1024/netty-websocket 导入后可直接运行 预览页面 自签证书&#xff1a; #换成自己的本地ip keytool -genkey -alias server -keyalg RSA -validity 3650 -keystore D:\mystore.jks -ext sanip:192.168.3.7,ip:127.0.0.1,dns:lo…

【存储】相关内容

【存储】相关内容 1. 存储类型1. 块存储2. 文件存储3. 对象存储4. 三种存储类型对比 2. 常见的存储分类1. DAS2. SAN3. NAS4. 存储分类分析比较 3. 一些存储的概念1. LUN2. volume3. HBA4. iSCSI 1. 存储类型 块存储和文件存储是我们比较熟悉的两种主流的存储类型&#xff0c;…

《昇思25天学习打卡营第21天 | 昇思MindSporePix2Pix实现图像转换》

21天 本节学习了通过Pix2Pix实现图像转换。 Pix2Pix是基于条件生成对抗网络&#xff08;cGAN&#xff09;实现的一种深度学习图像转换模型。可以实现语义/标签到真实图片、灰度图到彩色图、航空图到地图、白天到黑夜、线稿图到实物图的转换。Pix2Pix是将cGAN应用于有监督的图…

JavaSE--基础语法--类和对象(第二期)

&#xff08;一&#xff09;.面向对象的初步认知 1.1什么是面向对象&#xff1f; Java是一门纯面向对象的语言(Object Oriented Program&#xff0c;简称OOP)&#xff0c;在面向对象的世界里&#xff0c;一切皆为对象。面向对象是解决问题的一种思想&#xff0c;主要依靠对象…

新手使用超市收银系统应该注意哪些问题?

大部分小型超市都没怎么使用过智能收银系统&#xff0c;都是采用的传统手工收银方式&#xff0c;盘点、进货、库存也都是靠手工记录&#xff0c;完全没有接触过智能收银系统带来的优势和便利。超市收银软件特别是小区里面的超市&#xff0c;就跟传统的夫妻便利店的营销模式差不…

教育心理学期末考试重点

人本主义学习理论 人本主义主张&#xff0c;心理学应当把人作为一个整体来研究&#xff0c;而不是将人的心理肢解为不完整的几个部分&#xff0c;应该研究正常的人&#xff0c;而且更应该关注人的高级心理活动&#xff0c;如热情、信念、生命、尊严等内容。人本主义的学习理论…

【C++】使用C++在线程中动态记录数据到外部文件

在现代软件开发中&#xff0c;多线程编程已成为处理并发任务、提高程序性能的重要手段。而在多线程环境下&#xff0c;如何有效地管理和记录数据&#xff0c;尤其是将动态生成的数据安全地写入外部文件&#xff0c;是许多应用程序必须面对的问题。本文将深入探讨如何在C中使用多…

【运维】Windows server 2022 开启 telnet 功能

控制面板》启动或关闭Windows 功能 仪表盘》添加角色和功能》功能》telnet客户端

python-糖果俱乐部(赛氪OJ)

[题目描述] 为了庆祝“华为杯”的举办&#xff0c;校园中开展了许多有趣的热身小活动。小理听到这个消息非常激动&#xff0c;他赶忙去参加了糖果俱乐部的活动。 该活动的规则是这样的&#xff1a;摊位上有 n 堆糖果&#xff0c;第 i 堆糖果有 ai​ 个&#xff0c;参与的同学可…

全平台7合一自定义小程序源码系统功能强大 前后端分离 带完整的安装代码包以及搭建教程

系统概述 这款全平台 7 合一自定义小程序源码系统是专为满足各种业务需求而设计的。它整合了多种功能&#xff0c;能够在不同平台上运行&#xff0c;为用户提供了全方位的体验。无论你是企业主、开发者还是创业者&#xff0c;这款系统都能为你提供强大的支持。 代码示例 系统…

MATLAB code 生成C代码样式

Matlab code 生成C代码需要以下产品&#xff1a; MATLABMATLAB CoderC 编译器 MATLAB Coder 将查找并使用支持的已安装编译器。 可以使用 mex -setup 更改默认编译器。 在本地工作文件夹中创建文件 创建一个本地工作文件夹&#xff0c;例如 c:\ecoder\work。创建包含以下代…

【Python】Python的安装与环境搭建

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️Python】 文章目录 前言Python下载环境配置测试环境变量是否配置成功配置环境变量 运行Python交互式解释器&#xff1a;命令行脚本集成开发环境&#xff08;IDE&#xff1a;Integrated Development E…

电脑IP地址自动获取:操作指南与优势分析

在数字化时代&#xff0c;网络连接已成为我们日常生活和工作中的重要组成部分。而在建立网络连接的过程中&#xff0c;IP地址的设置无疑是至关重要的一环。IP地址&#xff0c;作为网络设备的唯一标识&#xff0c;其设置方式直接影响到网络的稳定性和安全性。本文将详细介绍如何…

大数据、人工智能、云计算、物联网、区块链序言【大数据导论】

各位大佬好 &#xff0c;这里是阿川的博客&#xff0c;祝您变得更强 个人主页&#xff1a;在线OJ的阿川 大佬的支持和鼓励&#xff0c;将是我成长路上最大的动力 阿川水平有限&#xff0c;如有错误&#xff0c;欢迎大佬指正 本篇序言前 必看 【大数据导论】—大数据序言 这是…