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

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

效果图:

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>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 基于vue2.0+vuex的日期选择组件功能实现

    calendar vue日期选择组件 一个选择日期的vue组件 基于vue2.0 + vuex 原本是想找这样的一个组件的,查看了vuex后,发现vuex的写法还不是基于2.0的,所以就自己动手做了 demo展示&&项目中的使用 目录结构 demo 用vue-cli 的webpack-simple构建的 calendar |--dist build生成的目录 |--doc 展示图片 |--src |--assets 资源 |--components |--calendar 日期组件 |--

  • vue实现移动端轻量日期组件不依赖第三方库的方法

    不需要依赖第三方组件的vue日期移动端组件  小轮子 轻量可复用:   https://github.com/BeckReed/datepicker-for-vue 2.用法:参见 src/view/demo.vue 文件的用法,简单易懂 <div> <h3>三列(年月日)日期弹窗示例--带标题)</h3> <button class="blue-btn" @click="togglePicker2">显示三列带标题日

  • vue实现自定义日期组件功能的实例代码

    实现一个日期组件,如图: components.js代码如下: Vue.component('sc-calendar',{ template:'<div class="scCalendar">' + '<div class="calendar_header">' + '<div class="prev" @click="prevMonth"> < </div>' + '&l

  • vue使用自定义事件的表单输入组件用法详解【日期组件与货币组件】

    本文实例讲述了vue使用自定义事件的表单输入组件用法.分享给大家供大家参考,具体如下: 自定义事件可以用来创建自定义的表单输入组件,使用 v-model 来进行数据双向绑定. v-model的实现原理 : <input v-model="something"> 这不过是以下示例的语法糖: <input v-bind:value="something" v-on:input="something = $event.target.value&

  • vue日期组件 支持vue1.0和2.0

    vue-datetime 使用vue编写的时间组件,小巧实用,支持vue1.0,vue2.0 v1.0 功能: 1.支持同时展开多个日期选择框 2.支持单击选中和取消,可配置单选和多选 3.支持双击启动连续选择,支持正向连续,逆向连续和跳跃不可选日期 4.支持在日期选择框内直接切换月份 5.支持初始化不可点击日期(剩余的可选择) 6.支持初始化已选择日期(已选择日期高亮) 7.支持初始化可选择日期(剩余的不可选择) 8.同时初始化不可点击和可点击日期,将以可点击日期为准 v1.1: 1.修复已知

  • Vue无限滑动周选择日期的组件的示例代码

    之前在做一个手机端项目的时候,需要一个左右滑动(按周滑动)选择日期插件,而且当时这个项目没有用到Vue.当时又没有找到合适的第三方插件,就花了点时间用原生JavaScript写了出来,当时心中就想把它写成基于Vue的组件,这短时间闲了把它弄出来了!,在这个过程中遇到了一个坑,后面会提出来! 先看效果  思路 根据用户传入日期(不传默认今天),获取上一周,当周,下一周对应的日期放数组dates里 let vm = this this.dates.push( { date: moment(vm.de

  • 详解vue移动端日期选择组件

    先给大家分享一下源码:https://github.com/lx544690189/vue-mobile-calendar Build Setup # install dependencies npm install # build for production with minification npm run build Usage install npm install vue-mobile-calendar or:(from the dist folder) <script src=&quo

  • Vue日期时间选择器组件使用方法详解

    本文实例为大家分享了Vue日期时间选择器组件的具体代码,供大家参考,具体内容如下 1.效果图如下 单选日期选择器 多选日期选择器 日期时间选择器 2.准备 Date原型格式化工具方法 Date.prototype.format = function(fmt) { //author: meizz var o = { "M+": this.getMonth() + 1, //月份 "d+": this.getDate(), //日 "h+": thi

  • 基于Vue组件化的日期联动选择器功能的实现代码

    我们的社区前端工程用的是element组件库,后台管理系统用的是iview,组件库都很棒,但是日期.时间选择器没有那种" 年份 - 月份 -天数 " 联动选择的组件.虽然两个组件库给出的相关组件也很棒,但是有时候确实不是太好用,不太明白为什么很多组件库都抛弃了日期联动选择.因此考虑自己动手做一个. 将时间戳转换成日期格式 // timestamp 为时间戳 new Date(timestamp) //获取到时间标砖对象,如:Sun Sep 02 2018 00:00:00 GMT+08

  • vue-calendar-component 封装多日期选择组件的实例代码

    实现效果 安装vue-calendar-component日历组件 cnpm i vue-calendar-component --save //国内镜像 引入 import Calendar from "vue-calendar-component"; export default { components: { Calendar }, } 封装 <template> <div class="x-f"> <Calendar ref=&

随机推荐