自己写的vue组件,仿ios日历,支持年月日时分 - Go语言中文社区

自己写的vue组件,仿ios日历,支持年月日时分


完美主持移动端,rem布局(我用了stylus,你可以改成less或者sass),font-size基础值/75,默认开始时间为当前

效果时间

效果图

贴代码

<template>
  <div class="datepicker" @touchmove.prevent>
    <transition name="fade" mode="out-in">
      <div class="pickerBoxBg" v-show="show" @click="show = false" @touchmove="_stopDef" @mousewheel="_stopDef">
        <div class="pickerBox" v-show="show" @mousewheel="_stopDef">
          <div class="pickerBoxWrapper">
            <div class="pickerBoxTitle">
              <h2>{{title}}</h2>
              <h3>请选择日期</h3>
              <ul>
                <li>年</li>
                <li>月</li>
                <li>日</li>
                <li>时</li>
                <li>分</li>
              </ul>
            </div>
            <div class="pickerBoxContent">
              <div class="pickerBoxContentList">
                <ul
                  :class="{'first_dragging': yearState.dragging}"
                  @touchstart="_onTouchStart('year', $event)"
                  @mousedown="_onTouchStart('year', $event)"
                  :style="{'transform' : 'translate3d(0,' + yearState.translateY + 'px, 0)'}">
                  <li></li>
                  <li></li>
                  <li></li>
                  <li
                    ref="list-box"
                    v-for="(item, index)
                  in yearState.data"
                    :key="index"
                    :class="{
                          'current': item === yearState.selectedId,
                          'node1':  Math.abs(index - yearState.index) == 1,
                          'node2':  Math.abs(index - yearState.index) == 2,
                          'node3':  Math.abs(index - yearState.index) >= 3
                      }">
                    {{item}}
                  </li>
                  <li></li>
                  <li></li>
                  <li></li>
                </ul>
              </div>
              <div class="pickerBoxContentList">
                <ul
                  :class="{'second_dragging': monthState.dragging}"
                  @touchstart="_onTouchStart('month', $event)"
                  @mousedown="_onTouchStart('month', $event)"
                  :style="{'transform' : 'translate3d(0,' + monthState.translateY + 'px, 0)'}">
                  <li></li>
                  <li></li>
                  <li></li>
                  <li
                    v-for="(item, index) in monthState.data"
                    :key="index"
                    :class="{
                          'current': item === monthState.selectedId,
                          'node1':  Math.abs(index - monthState.index) == 1,
                          'node2':  Math.abs(index - monthState.index) == 2,
                          'node3':  Math.abs(index - monthState.index) >= 3
                  }">
                    {{item}}
                  </li>
                  <li></li>
                  <li></li>
                  <li></li>
                </ul>
              </div>
              <div class="pickerBoxContentList">
                <ul
                  ref:area-list
                  :class="{'third_dragging': dayState.dragging}"
                  @touchstart="_onTouchStart('day', $event)"
                  @mousedown="_onTouchStart('day', $event)"
                  :style="{'transform' : 'translate3d(0,' + dayState.translateY + 'px, 0)'}">
                  <li></li>
                  <li></li>
                  <li></li>
                  <li
                    v-for="(item, index) in dayState.data"
                    :key="index"
                    :class="{
                      'current': item === dayState.selectedId,
                      'node1':  Math.abs(index - dayState.index) == 1,
                      'node2':  Math.abs(index - dayState.index) == 2,
                      'node3':  Math.abs(index - dayState.index) >= 3
                  }">
                    {{item}}
                  </li>
                  <li></li>
                  <li></li>
                  <li></li>
                </ul>
              </div>
              <div class="pickerBoxContentList">
                <ul :class="{'third_dragging': hourState.dragging}"
                    @touchstart="_onTouchStart('hour', $event)"
                    @mousedown="_onTouchStart('hour', $event)"
                    :style="{'transform' : 'translate3d(0,' + hourState.translateY + 'px, 0)'}">
                  <li></li>
                  <li></li>
                  <li></li>
                  <li
                    v-for="(item, index) in hourState.data"
                    :key="index"
                    :class="{
                        'current': item == hourState.selectedId,
                        'node1':  index - hourState.index == 1 || index == hourState.selectedId - 1,
                        'node2':  index - hourState.index == 2 || index == hourState.selectedId - 2,
                        'node3':  index - hourState.index >= 3 || index == hourState.selectedId - 3
                    }">
                    {{item}}
                  </li>
                </ul>
              </div>
              <div class="pickerBoxContentList">
                <ul :class="{'third_dragging': minuteState.dragging}"
                    @touchstart="_onTouchStart('minute', $event)"
                    @mousedown="_onTouchStart('minute', $event)"
                    :style="{'transform' : 'translate3d(0,' + minuteState.translateY + 'px, 0)'}">
                  <li></li>
                  <li></li>
                  <li></li>
                  <li
                    v-for="(item, index) in minuteState.data"
                    :key="index"
                    :class="{
                      'current': item == minuteState.selectedId,
                      'node1':  index - minuteState.index == 1 || index == minuteState.selectedId - 1,
                      'node2':  index - minuteState.index == 2 || index == minuteState.selectedId - 2,
                      'node3':  index - minuteState.index >= 3 || index == minuteState.selectedId - 3
                  }">
                    {{item}}
                  </li>
                </ul>
              </div>
            </div>
            <hr class="ProvCitySelectedTop">
            <hr class="ProvCitySelectedBottom">
          </div>
          <div class="pickerBoxHeader">
            <div class="pickerBoxHeaderCancle" @click="esc">{{cancel}}</div>
            <span></span>
            <div class="pickerBoxHeaderConfirm" @click="submit">{{confirm}}</div>
          </div>
        </div>
      </div>
    </transition>
  </div>
</template>

<script>

export default {
  data: function () {
    return {
      show: this.propsShow,
      result: this.propsResult,
      firstShow: true,
      target: '',
      yearState: {
        data: null,
        selectedId: null,
        index: 0,
        startPos: null,
        translateY: 0,
        startTranslateY: 0,
        dragging: false
      },
      monthState: {
        data: null,
        selectedId: null,
        index: 0,
        startPos: null,
        translateY: 0,
        startTranslateY: 0,
        dragging: false
      },
      dayState: {
        data: null,
        selectedId: null,
        index: 0,
        startPos: null,
        translateY: 0,
        startTranslateY: 0,
        dragging: false
      },
      hourState: {
        data: [],
        selectedId: null,
        index: 0,
        startPos: null,
        translateY: 0,
        startTranslateY: 0,
        dragging: false
      },
      minuteState: {
        data: [],
        selectedId: null,
        index: 0,
        startPos: null,
        translateY: 0,
        startTranslateY: 0,
        dragging: false
      },
      delta: 0,
      slideEls: null
    };
  },
  mounted: function () {
    this.initData();
    this._onTouchMove = this._onTouchMove.bind(this);
    this._onTouchEnd = this._onTouchEnd.bind(this);
  },
  methods: {
    computedYear() {
      const year = this.endYear ? this.endYear : new Date().getFullYear();
      let yearData = [];

      for (let i = this.startYear; i <= year; i++) {
        yearData.push(i);
      }

      return yearData;
    },
    computedMonth() {
      let monthData = [];

      const startControl = this.startControlComputed();
      const endControl = this.endControlComputed();
      const sameResult = this.sameComputed();

      // 开始年份限制 计算最小月
      if (startControl.yearFlag && !sameResult.sameYear) {
        const minMonth = this.startMonth;
        for (let i = minMonth; i <= 12; i++) {
          monthData.push(i);
        }
        return monthData;
      }
      // 结束年份限制 计算最大月
      if (endControl.yearFlag && !sameResult.sameYear) {
        const maxMonth = this.endMonth;
        for (let i = 1; i <= maxMonth; i++) {
          monthData.push(i);
        }
        return monthData;
      }
      // 开始与结束年份限制, 且年份相同 计算最小月-最大月
      if (startControl.yearFlag && endControl.yearFlag && sameResult.sameYear) {
        const minMonth = this.startMonth;
        const maxMonth = this.endMonth;
        for (let i = minMonth; i <= maxMonth; i++) {
          monthData.push(i);
        }
        return monthData;
      }

      // 无限制
      for (let i = 1; i <= 12; i++) {
        monthData.push(i);
      }

      return monthData;
    },
    computedDay() {
      let dayData = [];
      const year = this.yearState.selectedId;
      const month = this.monthState.selectedId;
      const current = new Date(year, month, 0);
      var currentDay = current.getDate();

      const startControl = this.startControlComputed();
      const endControl = this.endControlComputed();
      const sameResult = this.sameComputed();

      const startFlag = startControl.yearFlag && startControl.monthFlag;
      const endFlag = endControl.yearFlag && endControl.monthFlag;
      const startEndSame = sameResult.sameYear && sameResult.sameMonth;

      // 开始年月限制 计算最小日
      if (startFlag && !startEndSame) {
        const minDay = this.startDay;
        for (let i = minDay; i <= currentDay; i++) {
          dayData.push(i);
        }
        return dayData;
      }

      // 结束年月限制 计算最最大日
      if (endFlag && !startEndSame) {
        const maxDay = this.endDay;
        for (let i = 1; i <= maxDay; i++) {
          dayData.push(i);
        }
        return dayData;
      }

      // 开始年月和结束年月限制 计算最小日-最大日
      if (startFlag && endFlag && startEndSame) {
        const minDay = this.startDay;
        const maxDay = this.endDay;
        for (let i = minDay; i <= maxDay; i++) {
          dayData.push(i);
        }
        return dayData;
      }

      // 无限制
      for (let i = 1; i < currentDay + 1; i++) {
        dayData.push(i);
      }
      return dayData;
    },
    startControlComputed() {
      let startControlResult = {};
      startControlResult.yearFlag = this.yearState.selectedId === this.startYear;
      startControlResult.monthFlag = this.monthState.selectedId === this.startMonth;
      return startControlResult;
    },
    endControlComputed() {
      let endControlResult = {};
      endControlResult.yearFlag = this.yearState.selectedId === this.endYear;
      endControlResult.monthFlag = this.monthState.selectedId === this.endMonth;
      return endControlResult;
    },
    sameComputed() {
      let sameResult = {};
      sameResult.sameYear = this.startYear === this.endYear;
      sameResult.sameMonth = this.startMonth === this.endMonth;
      return sameResult;
    },
    initData() {
      const curDate = new Date();
      const yearState = this.yearState;
      const monthState = this.monthState;
      const dayState = this.dayState;
      yearState.data = this.computedYear();
      yearState.selectedId = curDate.getFullYear();

      this.filterMonth();
      monthState.selectedId = curDate.getMonth() + 1;

      this.filterDay();
      dayState.selectedId = curDate.getDate();
      this.setHour();
      this.setMinute();
    },
    transToToday() {
      const yearState = this.yearState;
      const monthState = this.monthState;
      const dayState = this.dayState;
      const hourState = this.hourState;
      const minuteState = this.minuteState;

      this.$nextTick(() => {
        setTimeout(() => {
          const height = this.$refs['list-box'][0].getBoundingClientRect().height;
          for (let i = 0; i < yearState.data.length; i++) {
            if (yearState.selectedId === yearState.data[i]) {
              yearState.index = i;
            }
          }
          yearState.translateY = -height * yearState.index;

          for (let i = 0; i < monthState.data.length; i++) {
            if (monthState.selectedId === monthState.data[i]) {
              monthState.index = i;
            }
          }
          monthState.translateY = -height * monthState.index;

          for (let i = 0; i < dayState.data.length; i++) {
            if (dayState.selectedId === dayState.data[i]) {
              dayState.index = i;
            }
          }
          dayState.translateY = -height * dayState.index;
          for (let i = 0; i < hourState.data.length; i++) {
            if (hourState.selectedId === hourState.data[i]) {
              hourState.index = i;
            }
          }
          hourState.translateY = -height * hourState.index;
          for (let i = 0; i < minuteState.data.length; i++) {
            if (minuteState.selectedId === minuteState.data[i]) {
              minuteState.index = i;
            }
          }
          minuteState.translateY = -height * minuteState.index;
        }, 300);
      });
    },
    submit() {
      this.result = {
        'year': this.yearState.data[this.yearState.index],
        'month': this.monthState.data[this.monthState.index],
        'day': this.dayState.data[this.dayState.index],
        'hour': this.hourState.data[this.hourState.index],
        'minute': this.minuteState.data[this.minuteState.index],
        'title': this.title
      };
      this.$emit('setDate', this.result);
      this.show = false;
      this.initData();
      this._onTouchMove = this._onTouchMove.bind(this);
      this._onTouchEnd = this._onTouchEnd.bind(this);
    },
    esc () {
      this.show = false;
      this.$emit('escDate');
      this.initData();
      this._onTouchMove = this._onTouchMove.bind(this);
      this._onTouchEnd = this._onTouchEnd.bind(this);
    },
    filterMonth() {
      this.monthState.data = this.computedMonth();

      this.monthState.selectedId = this.monthState.data[0];
      this.monthState.translateY = 0;
      this.monthState.index = 0;
    },
    filterDay() {
      this.dayState.data = this.computedDay();

      this.dayState.selectedId = this.dayState.data[0];
      this.dayState.translateY = 0;
      this.dayState.index = 0;
    },
    getSelectedData(index) {
      let target = this.target;
      let thisData = this[target + 'State'];
      thisData.selectedId = thisData.data[index];

      if (target === 'year') {
        this.filterMonth();
        this.filterDay();
      }
      if (target === 'month') {
        this.filterDay();
      }
    },
    setHour() {
      for (let i = 0; i < 24; i++) {
        if (i < 10) {
          this.hourState.data.push('0' + i);
        } else {
          this.hourState.data.push(i);
        }
      }
      let oDate = new Date();
      this.hourState.selectedId = oDate.getHours();
      this.hourState.translateY = 0;
      this.hourState.index = 0;
    },
    setMinute() {
      for (let i = 0; i < 60; i++) {
        if (i < 10) {
          this.minuteState.data.push('0' + i);
        } else {
          this.minuteState.data.push(i);
        }
      }
      let oDate = new Date();
      let Currnetminute = oDate.getMinutes();
      if (Currnetminute < 10) {
        Currnetminute = '0' + Currnetminute;
      }
      this.minuteState.selectedId = Currnetminute;
      this.minuteState.translateY = 0;
      this.minuteState.index = 0;
    },
    setPage() {
      let target = this.target;
      let thisData = this[target + 'State'];
      let clientHeight = this.slideEls[0].getBoundingClientRect().height;
      let total = thisData.data.length;
      let goPage = Math.round((thisData.translateY / clientHeight).toFixed(1));
      if (goPage > 0) {
        goPage = 0;
      }
      goPage = total - 1 >= Math.abs(goPage) ? goPage : -(total - 1);
      let index = Math.abs(goPage);
      thisData.index = index;
      this.getSelectedData(index);
      thisData.translateY = goPage * clientHeight;
    },
    _getTouchPos(e) {
      return e.changedTouches ? e.changedTouches[0]['pageY'] : e['pageY'];
    },
    _getElem(e) {
      return Array.from(e.currentTarget.children).slice(3, -3);
    },
    _onTouchStart(target, e) {
      let thisData = this[target + 'State'];
      this.target = target;
      this.slideEls = this._getElem(e);
      this.delta = 0;
      thisData.startPos = this._getTouchPos(e);
      thisData.startTranslateY = thisData.translateY;
      thisData.dragging = true;
      document.addEventListener('touchmove', this._onTouchMove, false);
      document.addEventListener('touchend', this._onTouchEnd, false);
      document.addEventListener('mousemove', this._onTouchMove, false);
      document.addEventListener('mouseup', this._onTouchEnd, false);
    },
    _onTouchMove(e) {
      let target = this.target;
      let thisData = this[target + 'State'];
      this.delta = this._getTouchPos(e) - thisData.startPos;
      thisData.translateY = thisData.startTranslateY + this.delta;
      if (Math.abs(this.delta) > 0) {
        e.preventDefault();
      }
    },
    _onTouchEnd(e) {
      let target = this.target;
      let thisData = this[target + 'State'];
      thisData.dragging = false;
      this.setPage();
      document.removeEventListener('touchmove', this._onTouchMove);
      document.removeEventListener('touchend', this._onTouchEnd);
      document.removeEventListener('mousemove', this._onTouchMove);
      document.removeEventListener('mouseup', this._onTouchEnd);
    },
    _stopDef(e) {
      e.preventDefault();
    }
  },
  watch: {
    propsShow: function (newVal) {
      this.show = newVal;
    },
    show: function (newVal) {
      this.$emit('setDialogVisible', newVal);
      this.transToToday();
      // if (this.firstShow) {
      //   this.transToToday();
      //   this.firstShow = false;
      // }
    }
  },
  props: {
    'propsResult': {
      type: Object,
      default: null
    },
    'propsShow': {
      type: Boolean,
      default: false
    },
    'startYear': {
      type: Number,
      default: 2000
    },
    'startMonth': {
      type: Number,
      default: 1
    },
    'startDay': {
      type: Number,
      default: 1
    },
    'endYear': {
      type: Number,
      default: null
    },
    'endMonth': {
      type: Number,
      default: null
    },
    'endDay': {
      type: Number,
      default: null
    },
    'title': {
      type: String,
      default: '请选择'
    },
    'confirm': {
      type: String,
      default: '确定'
    },
    'cancel': {
      type: String,
      default: '取消'
    }
  }
};
</script>

<style lang="stylus" rel="stylesheet/stylus">
@import '../../common/stylus/mixin'
.datepicker
  width :100%;
  height :100%;
  .pickerBoxBg {
    width :100%;
    height :100%;
    background: rgba(0, 0, 0, .35);
    z-index: 200;
    position: fixed;
    top:0;
    left :0;
  }
  .pickerBox
    position:absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 300;
    background: #fff;
    width :cal(670);
    border-radius :cal(40);
    .pickerBoxWrapper
      width :cal(562);
      margin :0 auto;
    .pickerBoxTitle
      border-bottom :1px solid #C9C9C9;
      overflow :hidden;
      h2
        font-size :cal(36);
        text-align center;
        margin:cal(62) auto cal(24) auto;
        font-weight :bold;
      h3
        font-size :cal(28);
        text-align center;
        margin-bottom :cal(50);
      ul
        width :90%;
        overflow: hidden;
        margin:0 auto cal(24) auto;
        li
          float: left;
          width :20%;
          text-align center;
          font-size :cal(28);
  .pickerBoxHeader
    height: cal(121);
    line-height: cal(121);
    overflow: hidden;
    text-align: center;
    font-size: cal(30)!important;
    border-top :1px solid #C9C9C9;
    span
      float :left;
      width :1px;
      height :cal(121);
      background-color :#C9C9C9;
  .pickerBoxHeaderCancle
    float: left;
    color: #078ffd;
    font-size: cal(30)!important;
    width: 49.5%;
    text-align center
  .pickerBoxHeaderConfirm
    float: right;
    color: #ff5657;
    font-size: cal(30)!important;
    width: 49.5%;
    text-align center
  .pickerBoxContent
    width: 90%
    margin: 0 auto;
    background: #fff;
    overflow: hidden;
    height: cal(450);
    overflow: hidden;
  .pickerBoxContentList
    float: left;
    width: 20%;
    text-align: center;
    ul
      -webkit-transition: all .3s ease;
      transition: all .3s ease;
      &.first_dragging,
      &.second_dragging,
      &.third_dragging
        -webkit-transition: none;
        transition: none;
    li
      line-height: cal(65);
      height: cal(65);
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      font-size: cal(26)!important;
      &.current
        font-size: cal(32)!important;
        font-weight: bold;
      &.node1
        font-size: cal(28)!important;
        opacity: .7;
      &.node2
        font-size: cal(26)!important;
        opacity: .5;
      &.node3
        font-size: cal(24)!important;
        opacity: .3;
  .ProvCitySelectedTop
    width: cal(562);
    border: none;
    border-top: cal(1) solid #eee;
    position: absolute;
    bottom: cal(384);
    margin: 0;
    height: 0;
  .ProvCitySelectedBottom
    width: cal(562);
    border: none;
    border-top: cal(1) solid #eee;
    position: absolute;
    bottom: cal(309);
    margin: 0;
    height: 0;
</style>

 

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/wzzehui/article/details/81181209
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2021-05-29 21:11:51
  • 阅读 ( 945 )
  • 分类:前端

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢