年至年的选择仿elementui的样式

javascript 复制代码
组件:
javascript 复制代码
<!--
 * @Author: liuyu liuyu@xizhengtech.com
 * @Date: 2023-02-01 16:57:27
 * @LastEditors: wangping wangping@xizhengtech.com
 * @LastEditTime: 2023-06-30 17:25:14
 * @Description: 时间选择年 - 年
-->
<template>
  <div class="yearPicker" ref="yearPicker" :width="width">
    <input class="_inner" :style="bindInputStyle" ref="inputLeft" v-model="startShowYear" @focus="onFocus" @blur="onBlur" type="text" name="yearInput" @keyup="checkStartInput($event)" placeholder="开始年份" />
    <span>{{ sp }}</span>
    <input class="_inner" :style="bindInputStyle" ref="inputRight" v-model="endShowYear" @focus="onFocus" @blur="onBlur" type="text" name="yearInput" @keyup="checkEndInput($event)" placeholder="结束年份" />
    <!-- <i class="dateIcon el-icon-date"></i> 按照自己标准库里面的图标设置-->
    <!-- <span class="_inner labelText"></span> -->
    <i class="_inner labelText el-icon-date"></i>
    <div class="_inner floatPanel" v-if="showPanel">
      <div class="_inner leftPanel">
        <div class="_inner panelHead">
          <i class="_inner el-icon-d-arrow-left" @click="onClickLeft"></i>
          {{ leftYearList[0] + " - " + leftYearList[9] }}
        </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="line"></div>
      <div class="_inner rightPanel">
        <div class="_inner panelHead">
          <i class="_inner el-icon-d-arrow-right" @click="onClickRight"></i>
          {{ rightYearList[0] + " - " + rightYearList[9] }}
        </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>
</template>
 
<script>
import moment from "moment";
const SELECT_STATE = {
  unselect: 0,
  selecting: 1,
  selected: 2,
};
export default {
  name: "yearPicker",
  computed: {
    bindInputStyle() {
      return {
        "--placeholderColor": "#c0c4cc",
      };
    },
    oneSelected() {
      return (
        this.curState === SELECT_STATE.selecting &&
        (this.startYear === this.endYear || this.endYear == null)
      );
    },
    startDate() {
      return this.startYear;
    },
    leftYearList() {
      return this.yearList.slice(0, 10);
    },
    rightYearList() {
      return this.yearList.slice(10, 20);
    },
    startYearFormat() {
      if (this._.isNil(this.startYear)) {
        return null;
      }
      return moment(this.startYear).startOf("year").format("yyyy");
    },
    endYearFormat() {
      if (this._.isNil(this.endYear)) {
        return null;
      }
      return moment(this.endYear).endOf("year").format("yyyy");
    },
  },
  props: {
    width: {
      default: 200,
    },
    labelWidth: {
      default: 40,
    },
    sp: {
      default: "-",
    },
    initYear: {
      default: null,
    },
  },
  data() {
    return {
      itemBg: {},
      startShowYear: null,
      endShowYear: null,
      yearList: [],
      showPanel: false,
      startYear: null,
      endYear: null,
      curYear: 0,
      curSelectedYear: 0,
      curState: SELECT_STATE.unselect,
    };
  },
  methods: {
    checkStartInput(event) {
      if (isNaN(this.startShowYear)) {
        this.startShowYear = this.startYear;
      } else {
        this.startYear = this.startShowYear * 1;
        this.changeYear();
      }
    },

    checkEndInput() {
      if (isNaN(this.endShowYear)) {
        this.endShowYear = this.endYear;
      } else {
        this.endYear = this.endShowYear * 1;
        this.changeYear();
      }
    },
    changeYear() {
      if (this.startYear > this.endYear) {
        let tmp = this.endYear;
        this.endYear = this.startYear;
        this.startYear = tmp;
        this.startShowYear = this.startYear;
        this.endShowYear = this.endYear;
      }
      if (this.startYear && this.endYear) {
        this.$emit("updateTimeRange", {
          startYear: moment(this.startYear + "")
            .startOf("year")
            .valueOf(),
          endYear:
            moment(this.endYear + "")
              .endOf("year")
              .valueOf() + 1,
        });
      } else {
        console.warn("WARN:年份不合法", this.startYear, this.endYear);
      }
    },
    onHoverItem(iYear) {
      if (this.curState === SELECT_STATE.selecting) {
        let tmpStart = this.curSelectedYear;
        this.endYear = Math.max(tmpStart, iYear);
        this.startYear = Math.min(tmpStart, iYear);
      }
    },
    onClickItem(iYear) {
      if (
        this.curState === SELECT_STATE.unselect ||
        this.curState === SELECT_STATE.selected
      ) {
        this.startYear = iYear;
        this.curSelectedYear = iYear;
        this.endYear = null;
        this.curState = SELECT_STATE.selecting;
      } else if (this.curState === SELECT_STATE.selecting) {
        this.endShowYear = this.endYear;
        this.startShowYear = this.startYear;
        this.curState = SELECT_STATE.selected;
        this.$emit("updateTimeRange", {
          startYear: moment(this.startYear + "")
            .startOf("year")
            .valueOf(),
          endYear:
            moment(this.endYear + "")
              .endOf("year")
              .valueOf() + 1,
        });

        setTimeout(() => {
          //为动画留的时间,可优化
          this.showPanel = false;
        }, 300);
      }
    },
    onFocus() {
      this.$nextTick(() => {
        this.showPanel = true;
      });
    },
    onBlur() {
      //   this.showPanel = false;
    },
    updateYearList() {
      let iStart = Math.floor(this.curYear / 10) * 10 - 10;
      iStart = iStart < 0 ? 0 : iStart;
      this.yearList = [];
      for (let index = 0; index < 20; index++) {
        this.yearList.push(iStart + index);
      }
    },
    closePanel(e) {
      if (!this.showPanel) {
        return;
      }
      if (typeof e.target.className !== "string") {
        this.$nextTick(() => {
          this.showPanel = false;
        });
        return;
      }
      if (
        e.target.className.indexOf("_inner") === -1 ||
        (e.target.name === "yearInput" &&
          e.target !== this.$refs.inputLeft &&
          e.target !== this.$refs.inputRight)
      ) {
        this.$nextTick(() => {
          this.showPanel = false;
        });
      }

      e.stopPropagation();
      return false;
    },
    onClickLeft() {
      this.curYear = this.curYear * 1 - 10;
      this.updateYearList();
    },
    onClickRight() {
      this.curYear = this.curYear * 1 + 10;
      this.updateYearList();
    },

    //------------------对外接口------------------------
    //直接传时间戳
    setYear(startYearStamp, endYearStamp) {
      if (!isNaN(startYearStamp) && !isNaN(endYearStamp)) {
        let startYear = moment(startYearStamp).format("yyyy");
        let endYear = moment(endYearStamp).format("yyyy");
        this.startYear = startYear * 1;
        this.endYear = endYear * 1;
        this.endShowYear = endYear * 1;
        this.startShowYear = startYear * 1;
      }
    },
  },

  created() {
    this.curYear = moment().format("yyyy");
    this.updateYearList();
  },
  beforeUnmount() {
    document.removeEventListener("click", this.closePanel.bind(this));
  },

  mounted() {
    this.$refs.yearPicker.style = "padding-left:" + this.labelWidth + "px";
    document.addEventListener("click", this.closePanel.bind(this));
  },
};
</script>
<style lang="scss" scoped>
.yearPicker {
  font-size: 14px;
  display: flex;
  position: relative;
  transition: all 0.3s;
  input {
    &::placeholder {
      color: var(--placeholderColor); // 动态值
    }
  }
  input:first-child {
    text-align: center;
  }
  .labelText {
    position: absolute;
    left: 10px;
    top: 10px;
    color: #c4c6d1;
  }
  background-color: #fff;
  span {
    padding: 0 8px;
    height: 36px;
    line-height: 36px;
  }
  border: 1px solid #eff1f3;
  height: 36px;
  line-height: 36px;
  border-radius: 4px;
  padding: 0 28px 0 8px;
  box-sizing: border-box;
  .floatPanel {
    > div {
      width: 50%;
    }
    // padding: 16px;
    position: absolute;
    display: flex;
    background-color: #fff;
    z-index: 9999 !important;
    border-radius: 4px;
    // width: 650px;
    height: 252px;
    top: 50px;
    left: 0px;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
    border: 1px solid #dfe4ed;
    .panelContent {
      display: grid;
      grid-template-columns: 25% 25% 25% 25%;
      // flex-wrap: wrap;
      width: 100%;
      height: calc(100% - 15px);
      margin-top: 8px;
      .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: 6px 0;
        // border-radius: 24px;
        text-align: center;
        a {
          display: inline-block;
          width: 60px;
          height: 36px;
          cursor: pointer;
          line-height: 36px;
          border-radius: 18px;
          color: #606266;
        }
        .selected {
          background-color: #1890ff;
          color: #fff;
        }
      }
    }
    .panelHead {
      height: 38px;
      line-height: 38px;
      position: relative;
      text-align: center;
      font-size: 16px;
      border-bottom: 1px solid #dfe4ed;
      i {
        position: absolute;
        cursor: pointer;
        &:hover {
          color: #1890ff;
        }
      }
    }
    .rightPanel {
      // padding-left: 8px;
      margin: 16px;
      display: flex;
      flex-direction: column;
    }
    .line {
      width: 1px;
      height: 100%;
      background: #dfe4ed;
    }
    .leftPanel {
      margin: 16px;
      display: flex;
      flex-direction: column;
    }
    .leftPanel .panelHead i {
      left: 0px;
      top: 10px;
      font-size: 14px;
      color: #717273;
    }
    .rightPanel .panelHead i {
      right: 0px;
      top: 8px;
    }
    .leftPanel .panelHead i:hover,
    .rightPanel .panelHead i:hover {
      cursor: pointer;
    }
  }
  .floatPanel::before {
    content: "";
    border-bottom: 6.5px solid #797979;
    border-left: 6.5px solid transparent;
    border-right: 6.5px solid transparent;
    position: absolute;
    left: 44px;
    -webkit-transform: translateX(-50%);
    transform: translateX(-50%);
    top: -5.5px;
    border-radius: 5px;
  }
  .floatPanel::after {
    content: "";
    border-bottom: 8px solid #fff;
    border-left: 8px solid transparent;
    border-right: 8px solid transparent;
    position: absolute;
    left: 44px;
    -webkit-transform: translateX(-50%);
    transform: translateX(-50%);
    top: -5px;
    border-radius: 5px;
  }
}
input {
  width: 100px;
  border: none;
  height: 37px;
  line-height: 37px;
  box-sizing: border-box;
  background-color: transparent;
  text-align: center;
  color: #606266;
}
input:focus {
  outline: none;
  background-color: transparent;
}
.yearPicker:hover {
  border-color: #1890ff;
}
.dateIcon {
  position: absolute;
  right: 16px;
  top: 9px;
  color: #adb2bc;
}
</style>
javascript 复制代码
使用:
javascript 复制代码
<YearYear1 style="width:300px" :initYear="dateValue2" @updateTimeRange="updateStatisticYear" />

dateValue2: [],

updateStatisticYear(val) {
 console.log("年", val);
},
相关推荐
m0_748256147 分钟前
前端 MYTED单篇TED词汇学习功能优化
前端·学习
小马哥编程1 小时前
Function.prototype和Object.prototype 的区别
javascript
小白学前端6661 小时前
React Router 深入指南:从入门到进阶
前端·react.js·react
web130933203982 小时前
前端下载后端文件流,文件可以下载,但是打不开,显示“文件已损坏”的问题分析与解决方案
前端
王小王和他的小伙伴2 小时前
解决 vue3 中 echarts图表在el-dialog中显示问题
javascript·vue.js·echarts
学前端的小朱2 小时前
处理字体图标、js、html及其他资源
开发语言·javascript·webpack·html·打包工具
outstanding木槿2 小时前
react+antd的Table组件编辑单元格
前端·javascript·react.js·前端框架
好名字08212 小时前
前端取Content-Disposition中的filename字段与解码(vue)
前端·javascript·vue.js·前端框架
摇光932 小时前
js高阶-async与事件循环
开发语言·javascript·事件循环·宏任务·微任务
隐形喷火龙3 小时前
element ui--下拉根据拼音首字母过滤
前端·vue.js·ui