年至年的选择仿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);
},
相关推荐
喜欢敲代码的程序员5 分钟前
SpringBoot+Mybatis+MySQL+Vue+ElementUI前后端分离版:项目搭建(一)
spring boot·mysql·elementui·vue·mybatis
foxhuli22915 分钟前
禁止ifrmare标签上的文件,实现自动下载功能,并且隐藏工具栏
前端
青皮桔1 小时前
CSS实现百分比水柱图
前端·css
失落的多巴胺1 小时前
使用deepseek制作“喝什么奶茶”随机抽签小网页
javascript·css·css3·html5
DataGear1 小时前
如何在DataGear 5.4.1 中快速制作SQL服务端分页的数据表格看板
javascript·数据库·sql·信息可视化·数据分析·echarts·数据可视化
影子信息1 小时前
vue 前端动态导入文件 import.meta.glob
前端·javascript·vue.js
青阳流月1 小时前
1.vue权衡的艺术
前端·vue.js·开源
样子20181 小时前
Vue3 之dialog弹框简单制作
前端·javascript·vue.js·前端框架·ecmascript
kevin_水滴石穿1 小时前
Vue 中报错 TypeError: crypto$2.getRandomValues is not a function
前端·javascript·vue.js
翻滚吧键盘1 小时前
vue文本插值
javascript·vue.js·ecmascript