亲宝软件园·资讯

展开

vue左右滑动选择日期组件封装

紫菀檀ss 人气:0

现在做的项目中遇到了左右滑动选择日期的一个功能,然后我封装了一下这个组件,现在分享给大家看一下:

效果图:

1、安装dayjs日期文件

npm install dayjs --save

2、封装的组件:

<template>
  <div class="m-calendar" ref="calendar">
    <div class="m-toolbar">
      <div class="m-year-selector">
        <!-- <a class="m-prev-btn" @click="changeYear('prev')"></a> -->
        <span>{{showDate.year}}{{yearName}}</span>
        <!-- <a class="m-next-btn" @click="changeYear('next')"></a> -->
      </div>
      <div class="m-month-selector">
        <!-- <a class="m-prev-btn" @click="changeMonth('prev')"></a> -->
        <span>{{monthNames[showDate.month-1]}}</span>
        <!-- <a class="m-next-btn" @click="changeMonth('next')"></a> -->
      </div>
    </div>
    <div class="m-week-header">
      <div
        class="m-week-day"
        v-for="item in weekNames"
        :key="item"
      >
        {{item}}
      </div>
    </div>
    <div
      class="m-months-container"
      @touchstart="touchstart"
      @touchmove="touchmove"
      @touchend="touchend"
    >
      <div
        class="m-months-wrapper"
        :style="{'transform': `translate3d(${-translateX*100}%, 0, 0)`}"
      >
        <div
          class="m-months"
          v-for="(month,monthIndex) in fullDate"
          :key="monthIndex"
          :style="{
            transform: `translate3d(${(monthIndex-1+translateX + (isTouching ? touch.x : 0))*100}%, 0, 0)`,
            transitionDuration: isTouching ? '0s' : '.3s',
          }"
        >
          <div
            class="m-row"
            v-for="(week,weekIndex) in month"
            :key="weekIndex"
          >
            <div
              class="m-day"
              v-for="(day,dayIndex) in week"
              :key="dayIndex"
              @click="onDayClick(day)"
            >
              <span
                :class="{
                  'm-day-num':true,
                  'm-grey': day.isGrey,
                  'm-today': day.isToday,
                  'm-disable': day.isDisable,
                  'm-select': day.isSelect,
                }"
              >
                  <!-- 'm-during': day.isDuring -->
                {{day.value}}
              </span>
              <slot name="day" :date="day" />
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import dayjs from 'dayjs';
let touchStartPosition;
let touchEndPosition;
let timeStamp;

export default {
  name: 'inlineCalendar',
  props: {
    defaultDate: {
      type: [Date, Number, Array, String, dayjs],
    },
    disabledDate: {
      type: Array,
      default() {
        return [];
      },
    },
    minDate: {
      type: [Date, Number, Array, String, dayjs],
    },
    maxDate: {
      type: [Date, Number, Array, String, dayjs],
    },
    mode: {
      type: String,
      default: 'single',
    },
    dayClick: {
      type: Function,
      default() {
        return function() {
          return true;
        };
      },
    },
    enableTouch: {
      type: Boolean,
      default: true,
    },
    monthNames: {
      type: Array,
      default() {
        return ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'];
      },
    },
    weekNames: {
      type: Array,
      default() {
        return ['一', '二', '三', '四', '五', '六', '日'];
      },
    },
    yearName: {
      type: String,
      default: '年',
    },
    restrictCurrentMonth: {
      type: Boolean,
      default: false,
    },
  },
  watch: {
    mode() {
      this.init();
    },
  },
  data() {
    return {
      fullDate: [[], [], []],
      translateX: 0,
      showDate: {
        year: undefined,
        month: undefined,
      },
      dateNow: {
        year: dayjs().year(),
        month: dayjs().month() + 1,
        date: dayjs().date(),
      },
      selectDate: [],
      touch: {
        x: 0,
        y: 0,
      },
      isTouching: false,
    };
  },
  created() {
    this.init();
  },
  methods: {
    init(date) {
      this.selectDate = [];
      let { defaultDate, mode } = this;
      if (date) {
        defaultDate = date;
      }
      let dateToShow = dayjs().startOf('month');
      if (mode === 'single' && defaultDate) {
        this.selectDate = dayjs(defaultDate).startOf('day');
        dateToShow = this.selectDate.startOf('month');
      }
      if (mode === 'multiple' && Array.isArray(defaultDate)) {
        if (defaultDate.length > 0) {
          this.selectDate = defaultDate.map((item) => dayjs(item).startOf('day'));
        }
      }
      if (mode === 'during' && Array.isArray(defaultDate)) {
        if (defaultDate.length === 2) {
          const startDate = dayjs(defaultDate[0]).startOf('day');
          const endDate = dayjs(defaultDate[1]).startOf('day');
          if (startDate.isBefore(endDate) || startDate.isSame(endDate)) {
            this.selectDate = [startDate, endDate];
          }
        }
      }
      this.showDate = {
        year: dateToShow.year(),
        month: dateToShow.month() + 1,
      };
      this.getFullDate(this.showDate);
    },
    touchstart(event) {
      if (this.enableTouch) {
        touchStartPosition = event.touches[0].clientX;
        touchEndPosition = event.touches[0].clientY;
        timeStamp = event.timeStamp;
        this.touch = {
          x: 0,
          y: 0,
        };
        this.isTouching = true;
      }
    },
    touchmove(event) {
      if (this.enableTouch) {
        this.touch = {
          x: (event.touches[0].clientX - touchStartPosition) / this.$refs.calendar.offsetWidth,
          y: (event.touches[0].clientY - touchEndPosition) / this.$refs.calendar.offsetHeight,
        };
      }
    },
    touchend(event) {
      if (this.enableTouch) {
        this.isTouching = false;
        const during = dayjs(event.timeStamp).diff(timeStamp);
        if (Math.abs(this.touch.x) > Math.abs(this.touch.y) && Math.abs(this.touch.x * this.$refs.calendar.offsetWidth) > 20) {
          if (this.touch.x > 0) {
            this.changeMonth('prev');
          } else if (this.touch.x < 0) {
            this.changeMonth('next');
          }
        } else {
          this.touch = {
            x: 0,
            y: 0,
          };
        }
      }
    },
    // 触发change事件
    emitChange() {
      this.$emit('change', this.selectDate);
    },
    // 触发切换年月事件
    emitSwitch(showDate) {
      if (this.restrictCurrentMonth) {
        this.selectDate = [];
      }
      this.$emit('switch', showDate);
    },
    // 日期点击事件
    onDayClick(day) {
      if (!this.dayClick(day.dateTime)) {
        return;
      }
      switch (this.$props.mode) {
      case 'single':
        if (!day.isSelect && !day.isDisable) {
          this.selectDate = day.dateTime;
          this.getFullDate(this.showDate);
          this.emitChange();
        }
        break;
      case 'multiple':
        if (!day.isSelect && !day.isDisable) {
          this.selectDate.push(day.dateTime);
          this.getFullDate(this.showDate);
          this.emitChange();
        } else {
          if (this.selectDate.length > 1) {
            this.selectDate = this.selectDate.filter((item) => !item.isSame(day.dateTime));
            this.getFullDate(this.showDate);
            this.emitChange();
          }
        }
        break;
      case 'during':
        if (day.isDisable) return;
        if (this.restrictCurrentMonth && day.isGrey) return;
        if (this.selectDate.length === 0) {
          this.selectDate = [day.dateTime];
        } else if (this.selectDate.length === 1) {
          this.selectDate.push(day.dateTime);
          if (this.selectDate[1].isBefore(this.selectDate[0])) {
            this.selectDate.reverse();
          }
        } else if (this.selectDate.length === 2) {
          this.selectDate = [day.dateTime];
        }
        this.getFullDate(this.showDate);
        this.emitChange();
        break;
      }
    },
    // 切换年份
    changeYear(action) {
      const date = dayjs(`${this.showDate.year}-${this.showDate.month}`);
      let computedDate;
      switch (action) {
      case 'prev':
        this.translateX += 1;
        computedDate = date.subtract(1, 'year');
        break;
      case 'next':
        this.translateX -= 1;
        computedDate = date.add(1, 'year');
        break;
      }
      this.showDate = {
        year: computedDate.year(),
        month: computedDate.month() + 1,
      };
      this.emitSwitch(this.showDate);
      this.getFullDate(this.showDate);
    },
    // 切换月份
    changeMonth(action) {
      const date = dayjs(`${this.showDate.year}-${this.showDate.month}`);
      let computedDate;
      switch (action) {
      case 'prev':
        this.translateX += 1;
        computedDate = date.subtract(1, 'month');
        break;
      case 'next':
        this.translateX -= 1;
        computedDate = date.add(1, 'month');
        break;
      }
      this.showDate = {
        year: computedDate.year(),
        month: computedDate.month() + 1,
      };
      this.emitSwitch(this.showDate);
      this.getFullDate(this.showDate);
    },
    // 暴露出去的方法:切换已选的时间
    changeDate(date) {
      if (dayjs(date).isValid() || Array.isArray(date)) {
        this.init(date);
      } else {
        console.error('Type of parameter is invalid!');
      }
    },
    // 暴露出去的方法:切换当前显示的时间
    changeDateView(date = dayjs()) {
      const changeDate = dayjs(date);
      this.showDate = {
        year: changeDate.year(),
        month: changeDate.month() + 1,
      };
      this.getFullDate(this.showDate);
    },
    getFullDate() {
      const date = dayjs(`${this.showDate.year}-${this.showDate.month}`);
      const thisDate = this.getDate(date);
      const prevDate = this.getDate(date.subtract(1, 'month'));
      const nextDate = this.getDate(date.add(1, 'month'));
      this.fullDate = [
        prevDate.fullDate,
        thisDate.fullDate,
        nextDate.fullDate,
      ];
    },
    // 当前日期是否被选中
    isSelect(date) {
        // console.log(date)
      let select = false;
      switch (this.$props.mode) {
      case 'single':
        if (this.selectDate && date.isSame(this.selectDate)) {
          select = true;
        }
        break;
      case 'multiple':
        if (this.selectDate.length > 0 && this.selectDate.some((item) => date.isSame(item))) {
          select = true;
        }
        break;
      }
      return select;
    },
    // 当前时间是否在selectDate之间
    isBetting(date) {
      if (this.mode === 'during') {
        const startDate = this.selectDate[0];
        const endDate = this.selectDate[1];
        if (this.selectDate.length === 1) {
          return date.isSame(startDate);
        } else if (this.selectDate.length === 2) {
          return (date.isAfter(startDate) && date.isBefore(endDate)) || date.isSame(startDate) || date.isSame(endDate);
        }
      }
      return false;
    },
    getIsDisable(dateTime) {
      let isDisable = false;
      const disabledDate = this.disabledDate.map((item) => dayjs(item).startOf('day'));
      if (this.minDate || this.maxDate) {
        if (this.minDate) {
          const minDate = dayjs(this.minDate).startOf('day');
          isDisable = dateTime.isBefore(minDate);
        }
        if (!isDisable && this.maxDate) {
          const maxDate = dayjs(this.maxDate).endOf('day');
          isDisable = dateTime.isAfter(maxDate);
        }
      } else if (disabledDate.length > 0) {
        if (this.mode !== 'during') {
          isDisable = disabledDate.some((item) => item.isSame(dateTime));
        }
      }
      return isDisable;
    },
    getDate(thisDate) {
      let date = [];
      const prevDate = thisDate.subtract(1, 'month');
      const nextDate = thisDate.add(1, 'month');
      const firstDayOfWeek = thisDate.day() || 7;
      const dayCountOfThisMonth = thisDate.daysInMonth();
      const dayCountOfPrevMonth = prevDate.daysInMonth();
      const prevIndexOfThisMonth = firstDayOfWeek - 1;
      const NextIndexOfThisMonth = firstDayOfWeek + dayCountOfThisMonth - 2;
      const disabledDate = this.disabledDate.map((item) => dayjs(item).startOf('day'));
      for (let i = 0; i < 7 * 6; i++) {
        // 上月
        if (i < prevIndexOfThisMonth) {
          const value = dayCountOfPrevMonth - (firstDayOfWeek - i - 2);
          const dateTime = prevDate.date(value);
          date[i] = {
            value,
            dateTime,
            isGrey: true,
            isToday: dateTime.isSame(dayjs().startOf('day')),
            isSelect: this.isSelect(dateTime),
            isDisable: this.getIsDisable(dateTime),
            isDuring: this.isBetting(dateTime),
          };
        }
        // 当月
        if (
          i >= prevIndexOfThisMonth &&
          i <= NextIndexOfThisMonth
        ) {
          const value = i - firstDayOfWeek + 2;
          const dateTime = thisDate.date(value);
          date[i] = {
            value,
            dateTime,
            isGrey: false,
            isToday: dateTime.isSame(dayjs().startOf('day')),
            isSelect: this.isSelect(dateTime),
            isDisable: this.getIsDisable(dateTime),
            isDuring: this.isBetting(dateTime),
          };
        }
        // 下月
        if (i > NextIndexOfThisMonth) {
          const value = i - firstDayOfWeek - dayCountOfThisMonth + 2;
          const dateTime = nextDate.date(value);
          date[i] = {
            value,
            dateTime,
            isGrey: true,
            isToday: dateTime.isSame(dayjs().startOf('day')),
            isSelect: this.isSelect(dateTime),
            isDisable: this.getIsDisable(dateTime),
            isDuring: this.isBetting(dateTime),
          };
        }
      }
      const fullDate = [];
      for (let i = 0; i < 6; i++) {
        fullDate.push(date.slice(i * 7, (i + 1) * 7));
      }
      return {
        fullDate,
      };
    },
  },
};
</script>
<style lang="less" scoped>
@import './style.css';
</style>

相关的style.css文件

.m-calendar {
  background: #fff;
  box-shadow: 0px 2px 6px 0px rgba(183, 183, 183, 0.2);
}
.m-calendar .m-toolbar {
  padding-bottom: 0.36266667rem;
}
.m-calendar .m-toolbar {
  display: flex;
  height: 2.56rem;
}
.m-calendar .m-toolbar .m-month-selector,
.m-calendar .m-toolbar .m-year-selector {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 0.74666667rem;
}
.m-calendar .m-toolbar .m-month-selector,
.m-calendar .m-toolbar .m-year-selector {
  line-height: 1.06666667rem;
}
.m-calendar .m-toolbar .m-month-selector,
.m-calendar .m-toolbar .m-year-selector {
  font-size: 0.768rem;
  font-family: PingFangSC-Medium, PingFangSC;
  font-weight: 500;
  color: #29262a;
}
.m-calendar .m-toolbar .m-year-selector {
  padding-left: 0.91733333rem;
}
.m-calendar .m-week-header {
  padding: 0 0.91733333rem;
}
.m-calendar .m-week-header {
  padding-bottom: 0.512rem;
}
.m-calendar .m-week-header {
  position: relative;
  display: flex;
  box-sizing: border-box;
  justify-content: space-between;
  font-size: 0.59733333rem;
}
.m-calendar .m-week-header .m-week-day {
  text-align: left;
  line-height: 0.85333333rem;
  font-family: PingFangSC-Regular, PingFangSC;
  font-weight: 400;
  color: #222222;
}
.m-calendar .m-months-container {
  position: relative;
  box-sizing: border-box;
  height: 12.37333333rem;
  overflow: hidden;
}
.m-calendar .m-months-container .m-months-wrapper {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}
.m-calendar .m-months-container .m-months-wrapper .m-months {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  will-change: transform;
  width: 16rem;
}
.m-calendar .m-months-container .m-months-wrapper .m-months .m-row {
  padding-top: 0.512rem;
}
.m-calendar .m-months-container .m-months-wrapper .m-months .m-row {
  width: 16rem;
}
.m-calendar .m-months-container .m-months-wrapper .m-months .m-row {
  position: relative;
  display: flex;
  height: 1.408rem;
}
.m-calendar .m-months-container .m-months-wrapper .m-months .m-row .m-day {
  margin-right: 0.87466667rem;
}
.m-calendar .m-months-container .m-months-wrapper .m-months .m-row .m-day:nth-child(1) {
  margin-left: 0.66133333rem;
}
.m-calendar .m-months-container .m-months-wrapper .m-months .m-row .m-day {
  font-size: 0.59733333rem;
  font-family: PingFangSC-Medium, PingFangSC;
  font-weight: 500;
  color: #222222;
}
.m-calendar .m-months-container .m-months-wrapper .m-months .m-row .m-day {
  position: relative;
  height: 1.408rem;
  width: 1.408rem;
  line-height: 1.408rem;
  text-align: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  border-radius: 50%;
}
.m-calendar .m-months-container .m-months-wrapper .m-months .m-row .m-day .m-day-num {
  width: 1.408rem;
  display: inline-block;
  border-radius: 100%;
}
.m-calendar .m-months-container .m-months-wrapper .m-months .m-row .m-day .m-day-num {
  height: 1.408rem;
  line-height: 1.408rem;
}
.m-calendar .m-months-container .m-months-wrapper .m-months .m-row .m-day .m-grey {
  color: #b8b8b8;
}
.m-calendar .m-months-container .m-months-wrapper .m-months .m-row .m-day .m-today {
  background: #5DABF3;
  color: #fff;
}
.m-calendar .m-months-container .m-months-wrapper .m-months .m-row .m-day .m-disable {
  color: #b8b8b8;
  text-decoration: line-through;
}
.m-calendar .m-months-container .m-months-wrapper .m-months .m-row .m-day .m-select {
  background: #007aff;
  color: #fff;
}

3、使用到的页面

<div class="data">
      <inlineCalendar :dayClick="dayClick" />
</div>
<script>
import inlineCalendar from '../components/inlineCalendar';
export default {
  name: "home",
  data() {
    return { };
  },
  components: {
    inlineCalendar
  },
  methods: {
    dayClick(date) {
      console.log('date---->', date);
      console.log(date.format('YYYY-MM-DD'));
      let dates = date.format('YYYY-MM-DD');
    },
  }
};
</script>
<style lang="less" scoped>
    .data {
      position: fixed;
      top: 1.87733333rem;
      width: 100%;
      height: 100%;
    }
</style>

加载全部内容

相关教程
猜你喜欢
用户评论