前言:

        在需要一个移动端的日历效果时,找了很多资料,这里分享下我找的两个资料,一个是使用了插件 vue-hash-calendar ,还有一个是自己封装的组件。

我这边最终实现效果:

一、vue-hash-calendar使用教程:github地址入口

1、npm装插件 

cnpm i vue-hash-calendar

2、main.js中配置

// 在入口文件中(main.js),导入组件库
import vueHashCalendar from 'vue-hash-calendar'
// 引入组件CSS样式
import 'vue-hash-calendar/lib/vue-hash-calendar.css'
// 注册组件库
Vue.use(vueHashCalendar)

3、在页面组件中使用,注意这里有坑,操作不当容易出现空白出不来情况,我这里是用的他的弹框属性

template里面直接使用标签:

<vue-hash-calendar
      :visible.sync="isShowCalendar"
      model="dialog"
      format="YY-MM-DD"
      @confirm="changeTime"
      :disabled-date="disabledDate"
    ></vue-hash-calendar>

data里面定义:

data () {
  return {
    /**
     * 日历弹框
     * */
    isShowCalendar:false,//日历组件


  };
},

methods里面定义他的禁用方法和点击确定事件

methods: {
   /**
     * 改变时间
     * */
    changeTime(data){
      this.showDate = data;//改变页面展示的时间
      this.initPageData();// 初始化更新页面数据
      this.getBarData();//刷新柱图数据
    },


    /**
     * 禁用今天以后的日期
     * */
    disabledDate(date) {
      let timestamp = date.getTime();
      if (timestamp > new Date().getTime()) {
        return true
      }

      return false
    }



}

二、自己封装的移动端日历组件(入口

源码:


<template>
  <div id="calendar" :class="{'change':isChange}">
    <!-- 年份 月份 -->
    <div class="year-month">
      <div class="year-month_left">
        <span class="top-time">{{currentMonth}}月</span>
        <ul>
          <li>周{{weekdays[newWeek]}}</li>
          <li>{{currentYear}}年</li>
        </ul>
      </div>
      <div class="year-month_right"><x-icon @click="addSchedule" type="ios-plus-empty" size="30" class="i-plus-empty"></x-icon></div>
    </div>
    <!-- 星期 -->
    <ul class="weekdays">
      <li v-for="(vo,index) in weekdays" v-text="vo" :key="index"></li>
    </ul>
    <!-- 日期 -->
    <ul
      class="days"
      :class="{'fadeOut':fadeOut,'fadeIn':fadeIn,'fadeOutR':fadeOutR,'fadeInR':fadeInR}"
      @touchstart="allTouchStart"
      @touchend="allTouchEnd"
      @touchstart.stop="touchStart"
      @touchend.stop="touchEnd"
    >
      <!-- 核心 v-for循环 每一次循环用<li>标签创建一天 -->
      <li
        v-for="(dayobject,index) in days"
        :class="{'weekend':(index%7 === 0)||((index+1)%7 === 0)}"
        :key="index"
      >
        <!--本月-->
        <!--如果不是本月 改变类名加灰色-->
        <div
          v-if="dayobject.day.getMonth()+1 !== currentMonth"
          @click="otherMonth(dayobject.day.getDate())"
          class="other-month"
        >{{ dayobject.day.getDate() }}</div>
        <!--如果是本月 还需要判断是不是这一天-->
        <div v-else class="everyDay">
          <!--今天 同年同月同日-->
          <div
            @click="getDayMessage(currentYear,currentMonth,dayobject.day.getDate())"
            v-if="dayobject.day.getFullYear() === new Date().getFullYear() && dayobject.day.getMonth() === new Date().getMonth() && dayobject.day.getDate() === new Date().getDate()"
            class="active"
          >{{ dayobject.day.getDate() }}</div>
          <div
            :class="{'otherday':dayobject.day.getDate() === otherDay}"
            v-else
            @click="getDayMessage(currentYear,currentMonth,dayobject.day.getDate())"
          >{{ dayobject.day.getDate() }}</div>
          <div :class="{'circle':dayobject.status==='3','o':dayobject.status==='2'}"></div>
        </div>
      </li>
    </ul>
    <!--背景色-->
    <div class="background" :class="{'change':isChange}">
      <div v-for="(value,index) in 5" :class="{'dbg':(index%2===0),'lbg':(index%2!==0)}" :key="index"></div>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'Calendar',
    data() {
      return {
        currentDay: 1,
        currentMonth: 1,
        currentYear: 1970,
        currentWeek: 1,
        newWeek:1,
        days: [],
        weekdays:['日', '一', '二', '三', '四', '五', '六'],
        // 上下滑动的鼠标位置
        positionSX: "",
        positionEX: "",
        positionSY: "",
        positionEY: "",
        isChange: false,
        // 左右滑动动画的初始状态
        show: true,
        fadeOut: false,
        fadeIn: false,
        fadeOutR: false,
        fadeInR: false,
        monthList: [],
        status: "",
        otherDay: ""
      };
    },
    created() {
      this.initData(null);
    },
    mounted() {},
    methods: {
      getDayMessage(y, m, d) {
        this.getCurrentWeek(y, m, d);
        const str = this.formatDate(y, m, d);
        this.$emit("change", str, m, d);
        this.otherDay = d;
      },
      getCurrentWeek(y, m, d){
        const w=`${y}-${m}-${d}`
        const weekArr=w.split('-');
        const weeks=new Date(weekArr[0], parseInt(weekArr[1] - 1), weekArr[2]);
        this.newWeek=weeks.getDay();
      },
      otherMonth(day){
        if(day<15){
          this.rightSliding()
        }else if(day>15){
          this.leftSliding()
        }
      },
      addSchedule(){
        this.$emit('add')
      },
      //向下滑监听坐标
      allTouchStart(e) {
        //开始x轴坐标
        this.positionSX = e.changedTouches[0].clientX;
        //开始y轴坐标
        this.positionSY = e.changedTouches[0].clientY;
      },
      allTouchEnd(e) {
        //结束x轴坐标
        this.positionEX = e.changedTouches[0].clientX;
        //结束y轴坐标
        this.positionEY = e.changedTouches[0].clientY;
        const distanceY = this.positionEY - this.positionSY;
        const distanceX = this.positionSX - this.positionEX;
        //判断滑动的方向
        if (distanceY < -30 && Math.abs(distanceY) > Math.abs(distanceX)) {
          this.isChange = true;
        }
        if (distanceY > 30 && Math.abs(distanceY) > Math.abs(distanceX)) {
          this.isChange = false;
        }
      },

      //监听左右滑动坐标
      touchStart(e) {
        //开始x轴坐标
        this.positionSX = e.changedTouches[0].clientX;
        //开始y轴坐标
        this.positionSY = e.changedTouches[0].clientY;
      },
      touchEnd(e) {
        this.show = !this.show;
        //结束x轴坐标
        this.positionEX = e.changedTouches[0].clientX;
        //结束y轴坐标
        this.positionEY = e.changedTouches[0].clientY;
        const distanceY = this.positionEY - this.positionSY;
        const distanceX = this.positionSX - this.positionEX;
        //判断滑动 的方向
        if (distanceX > 30 && Math.abs(distanceY) < Math.abs(distanceX)) {
          this.rightSliding()
        }
        if (distanceX < -30 && Math.abs(distanceY) < Math.abs(distanceX)) {
          this.leftSliding()
        }
      },
      //向右滑动
      rightSliding(){
        const self = this;
        self.pickNext(self.currentYear, self.currentMonth);
        self.fadeOut = true;
        self.fadeIn = false;
        self.fadeInR = false;
        self.fadeOutR = false;
        setTimeout(function() {
          self.fadeIn = true;
          self.fadeOut = false;
          self.fadeOutR = false;
          self.fadeInR = false;
        }, 300);
      },
      //向左滑动
      leftSliding(){
        const self = this;
        self.pickPre(self.currentYear, self.currentMonth);
        self.fadeOutR = true;
        self.fadeInR = false;
        self.fadeOut = false;
        self.fadeIn = false;
        setTimeout(function() {
          self.fadeInR = true;
          self.fadeOutR = false;
          self.fadeOut = false;
          self.fadeIn = false;
        }, 300);
      },
      initData(cur) {
        let date;
        if (cur) {
          date = new Date(cur);
        } else {
          const now = new Date();
          const t = this.formatDate(now.getFullYear(), now.getMonth(), 1);
          const d = new Date(t);
          d.setDate(35);
          date = new Date(this.formatDate(d.getFullYear(), d.getMonth() + 1, 1));
        }

        this.currentDay = date.getDate();
        this.currentYear = date.getFullYear();
        this.currentMonth = date.getMonth() + 1;
        this.currentWeek = date.getDay();
        if(!cur || !this.otherDay){
          this.otherDay= new Date().getDate();
        }
        const str = this.formatDate(
          this.currentYear,
          this.currentMonth,
          this.currentDay
        );
        this.days.length = 0;
        //初始化本周
        for (let i = this.currentWeek; i >= 0; i--) {
          const d = new Date(str);
          d.setDate(d.getDate() - i);
          const dayobject = {}; //用一个对象包装Date对象 以便为以后预定功能添加属性
          dayobject.day = d;
          dayobject.status = "";
          this.days.push(dayobject); //将日期放入data 中的days数组 供页面渲染使用
        }
        //其他周
        for (let i = 1; i <= 34 - this.currentWeek; i++) {
          const d = new Date(str);
          d.setDate(d.getDate() + i);
          const dayobject = {};
          dayobject.day = d;
          dayobject.status = "";
          this.days.push(dayobject);
        }
        //选中日期在其他月份是否超出最大日期判断
        const arr=[]
        for(let j=0, length=this.days.length; j<length; j++){
          if(this.days[j].day.getMonth() + 1 === this.currentMonth){
            arr.push(this.days[j].day.getDate())
          }
        }
        const maxDate=Math.max.apply(null, arr);
        if(this.otherDay > maxDate){
          this.otherDay=maxDate
        }

        this.getDayMessage( this.currentYear, this.currentMonth, this.otherDay)

      },
      //     上个月信息
      pickPre(year, month) {
        const d = new Date(this.formatDate(year, month, 1));
        d.setDate(0);
        this.initData(this.formatDate(d.getFullYear(), d.getMonth() + 1, 1));
      },
      // 下个月信息
      pickNext(year, month) {
        const d = new Date(this.formatDate(year, month, 1));
        d.setDate(35);
        this.initData(this.formatDate(d.getFullYear(), d.getMonth() + 1, 1));
      },
      // 补零处理
      formatDate(year, month, day) {
        let y = year;
        let m = month;
        if (m < 10) m = "0" + m;
        let d = day;
        if (d < 10) d = "0" + d;
        return y + "-" + m + "-" + d;
      }
    }
  };
</script>

<style lang="less" scoped>
  #calendar {
    width: 100%;
    height:auto;
    margin: 0 auto;
    transition: all 0.5s;
    overflow: hidden;
    background: #fafafa;
    .change {
      height: 250px !important;
    }
    .year-month {
      height:80px;
      width: 100%;
      display: flex;
      align-items: center;
      justify-content: space-around;
      .year-month_left{
        height:100%;
        width:80%;
        padding-left: 25px;
        display: flex;
        align-items: center;
        .top-time {
          color:#333;
          font-size:26px;
          font-weight:500;
          margin-right: 15px;
        }
        ul{
          display:flex;
          flex-direction:column;
          li{
            color:#444;
            font-size:16px;
          }
        }
      }
      .year-month_right{
        height:100%;
        width:20%;
        display: flex;
        align-items: center;
        justify-content: flex-end;
        padding-right: 12px;
        .i-plus-empty{
          fill: #f18d2f;
        }
      }
    }
    .weekdays {
      margin: 0;
      height: 40px;
      font-size: 15px;
      display: flex;
      flex-wrap: wrap;
      color: #666;
      justify-content: space-around;
      li{
        display: inline-block;
        width: 13.6%;
        text-align: center;
        display:flex;
        justify-content:center;
        align-items:center;
      }
    }
    .days {
      height: 210px;
      padding: 0;
      margin: 0;
      display: flex;
      flex-wrap: wrap;
      justify-content: space-around;
      overflow: hidden;
      position: relative;
      li{
        list-style-type: none;
        display: inline-block;
        height: 42px;
        width: 13.4%;
        text-align: center;
        font-size: 14px;
        color: #000;
        position: relative;
        .active{
          border-radius: 50%;
          background: #3a8fea;
          color: #fff;
          width: 40px;
          height: 40px;
          margin: auto;
          display: flex;
          justify-content: center;
          align-items: center;
        }
        .other-month{
          width: 40px;
          height: 40px;
          margin: auto;
          display: flex;
          justify-content: center;
          align-items: center;
          color: #a2a2a2;
        }
        .everyDay {
          width: 40px;
          height: 40px;
          margin: auto;
          display: flex;
          justify-content: center;
          align-items: center;
        }
        .circle {
          width: 4px;
          height: 4px;
          border-radius: 50%;
          background-color: #f2553d;
          position: absolute;
          bottom: 6px;
          left: 48%;
        }
        .o {
          width: 4px;
          height: 4px;
          border-radius: 50%;
          border: 1px solid #f2553d;
          position: absolute;
          bottom: 1px;
          left: 49%;
        }
        .otherday {
          width: 38px;
          height: 38px;
          margin: auto;
          display: flex;
          justify-content: center;
          align-items: center;
          border-radius: 50%;
          border:1px solid #b2b2b2;
        }
      }

    }
    .fadeOut {
      animation-name: fadeOut;
      animation-duration: 0.5s;
      animation-timing-function: ease-in-out;
    }
    .fadeOutR {
      animation-name: fadeOutR;
      animation-duration: 0.5s;
      animation-timing-function: ease-in-out;
    }
    .fadeIn {
      animation-name: fadeIn;
      animation-duration: 0.5s;
      animation-timing-function: ease-in-out;
    }
    .fadeInR {
      animation-name: fadeInR;
      animation-duration: 0.5s;
      animation-timing-function: ease-in-out;
    }
    .background {
      position: absolute;
      top: 100px;
      height: 211px;
      width: 100%;
      z-index: -1;
      overflow: hidden;
      transition: all 0.5s;
      .dbg {
        background-color: #e1e1e1;
        height: 42.2px;
      }
      .lbg {
        background-color: #d5d5d5;
        height: 42.2px;
      }
    }
  }

  @keyframes fadeOut {
    0% {
      transform: translateX(0);
      opacity: 1;
    }
    100% {
      transform: translateX(-100%);
      opacity: 0;
    }
  }
  @keyframes fadeIn {
    0% {
      transform: translateX(100%);
      opacity: 0;
    }
    100% {
      transform: translateX(0);
      opacity: 1;
    }
  }
  @keyframes fadeOutR {
    0% {
      transform: translateX(0);
      opacity: 1;
    }
    100% {
      transform: translateX(100%);
      opacity: 0;
    }
  }
  @keyframes fadeInR {
    0% {
      transform: translateX(-100%);
      opacity: 0;
    }
    100% {
      transform: translateX(0);
      opacity: 1;
    }
  }
</style>

 

 

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐