vue原生方式实现日历组件

需求背景:
最近做项目遇到一个需求,需要制作一个定制化的日历组件,但市面上的组件或者插件要样式不太美观,要功能不全,要自定义不支持;
需求要求:
可选择年月切换,选中年时展示12个月份;选中月时展示当前选中月份日历;
默认选中当前年、月、日;
可对年、月进行修改,左减右加;
在点击某月或者某日时监听事件,进行其他操作;
按照颜色实现日期区分:当前月份排班信息正常颜色,今天显示深色,其他月份显示浅色。

实现效果

vue原生方式实现日历组件_第1张图片
vue原生方式实现日历组件_第2张图片

实现代码

一:组件准备
1:在components板块新建CalendarMonth文件夹,代码如下:

实现效果:图一

<template>
  <div class="CalendarMonth">
    <!-- 每年月份具体内容 -->
    <div class="year">
        <van-icon name="arrow-left" @click="reduceYear()" />
          <span>{{time.year}}</span>
        <van-icon name="arrow" @click="addYear()" />
    </div>
    <div class="monthList">
        <div :class="time.month == index ? ['currentMonth', 'month'] : 'month'" v-for="(item, index) in monthList" :key="index">
            <div class="content" @click="clickMonth(index)">
                <div class="contentMonth">{{ item }}</div>
                <div class="contentDes">描述内容</div>
            </div>
        </div>
    </div>
  </div>
</template>

<script>
export default {
  props: {
  },
  data() {
    return {
      monthList: ['一', '二', '三', '四', '五', '六','七','八','九','十','十一','十二'],
      time: {
        //获取完整的年份(4位)
        year: new Date().getFullYear(),
        //获取当前月份(0-11,0代表1月)
        month: new Date().getMonth()
      }
    }
  },
  created() {
  },
  methods: {
    // 获取
    reduceYear () {
        this.time.year = this.time.year  - 1
        this.$emit('changeYear', this.time)
    },
    addYear () {
        this.time.year = this.time.year + 1
        this.$emit('changeYear', this.time)
    },
    clickMonth (index) {
        console.log('选择' + index + '月')
    }
  },
  computed: {
  }
}
</script>
<style lang="less" scoped>
.CalendarMonth {
  .year {
    font-weight: 700;
    font-size: 18px;
  }
  .monthList {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    .month {
      display: flex;
      align-items: center;
      width: 50px;
      height: 50px;
      margin: 6px;
      text-align: center;
      background-color: rgb(220, 245, 253);
    }
    .currentMonth {
      background-color: rgb(169, 225, 243);
    }
  }
}
</style>
2:在components板块新建CalendarData文件夹,代码如下:

效果如图二:

<template>
  <div class="date-page">
    <div class="date-header">
        <div class="year">
          <van-icon name="arrow-left" @click="preMonthClick()" />
            <span>{{year}}{{month}}</span>
          <van-icon name="arrow" @click="nextMonthClick()" />
        </div>
    </div>
    <div class="date-content">
      <div class="weekList">
          <div class="week" v-for="item in weekList" :key="item">{{ item }}</div>
      </div>
      <div class="dataList">
        <div
          :class="isActive.data == showDay.data && isActive.year == showDay.year && isActive.month == showDay.month? 'dateActive data': 'data'"
          v-for="(showDay, index) in showData"
          :key="index"
          @click="detailClick(showDay,index)"
          >
          <div :class="item.thisMonth == 'thisMonthNot' ? 'unShowContent' : ''" @click="clickDay(item.day)">
              <div class="contentDay">{{ showDay.data }}</div>
              <div class="contentDes">描述内容描述内容</div>
          </div>
          </div>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      year: "",
      month: "",
      today: "",
      showData: [],
      isActive: {},
      show: false,
      weekList: ['日','一', '二', '三', '四', '五', '六']
    };
  },
  created() {
   this.getToday()
  },
  methods: {
    // 获取当前日期
     getToday() {
      let date = new Date();
      this.year = date.getFullYear();
      this.month = date.getMonth() + 1;
      this.today = date.getDate();//获取这是周几
      //初始化数据
      this.getMonthDays(this.year, this.month - 1);
      //得到当月今天的位置index
      this.isActive = {
        data : this.today,
        year: this.year,
        month: this.month
      }
    },
    // 上一月
     preMonthClick() {
      this.show = true;
      if (this.month === 1) {
        this.month = 12;
        this.year = this.year - 1;
      } else {
        this.month = this.month - 1;
      }
      this.getMonthDays(this.year, this.month - 1);
    },
    // 下一月
   nextMonthClick() {
      let date = new Date();
      let year = date.getFullYear();
      let month = date.getMonth() + 1;
      if (this.year == year) {
        this.month = this.month + 1;
        if (this.month < month) {
          this.show = true;
        } else {
          this.show = false;
          this.getMonthDays(this.year, this.month - 1);
        }
      } else if (this.year < year) {
        this.show = true;
        if (this.month === 12) {
          this.month = 1;
          this.year = this.year + 1;
        } else {
          this.month = this.month + 1;
        }
      }
      this.getMonthDays(this.year, this.month - 1);
    },
    // 获取日期数组
    getMonthDays(year, month) {
      // 定义每个月的天数,如果是闰年第二月改为29天
      let daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
      if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {
        daysInMonth[1] = 29;
      }
      // 当月第一天为周几
      let targetDay = new Date(year, month, 1).getDay();
      let calendarDateList = [];
      let preNum = targetDay;
      let nextNum = 0;
      if (targetDay > 0) {
        // 当前月份1号前的自然周剩余日期,置空
        for (let i = 0; i < preNum; i++) {
          calendarDateList.push({data: "", year: this.year, month: this.month});
        }
      }
      for (let i = 0; i < daysInMonth[month]; i++) {
        // 正常显示的日期
        calendarDateList.push({data: i + 1, year: this.year, month: this.month})
      }
      // 当前月份最后一天的自然周剩余日期,置空
      nextNum = 6 - new Date(year, month + 1, 0).getDay(); 
      for (let i = 0; i < nextNum; i++) {
        calendarDateList.push({data: "", year: this.year, month: this.month});
      }
      this.showData = calendarDateList;
    },
    // 当前点击日期
    detailClick (showData, index) {
     this.isActive = {...showData}
    }
    
  }
}
</script>
<style lang="less" scoped>
.year {
  font-weight: 700;
  font-size: 18px;
}

.weekList {
  display: flex;
  align-items: center;
  .week {
    width: 14.285%;
    background-color: rgb(242, 242, 242);
    padding: 10px 0;
    margin: 5px;
    text-align: center;
  }
}
.dataList {
  display: flex;
  flex-wrap: wrap;
  .date {
    width: 14.285%;
    text-align: center;
    padding: 5px;
    .contentDes {
      padding: 10px 0;
      background-color: rgba(220, 245, 253, 0.3);
    }
  }
  .dateActive {
      background-color: rgb(169, 225, 243);
  }
}
</style>
二:页面整合
<template>
  <div>
     <!-- 操作栏 -->
     <div class="typeBox">
        <div v-for="(item, index) in typeList" :key="index" :class="currentType == index ? ['currentType','type'] : 'type'" @click="clickType(index)">
            {{item.text}}
        </div>
     </div>
     <div class="dataBox" v-if='currentType'>
        <!-- 月日历栏 -->
        <calendarData></calendarData>
      </div>
      <div class="monthBox" v-else>
        <!-- 年月份栏 -->
        <calendarMonth @changeYear="changeYear"></calendarMonth>
      </div>
  </div>
</template>

<script>
// 日历组件
import CalendarData from '@/components/CalendarData/index.vue'
// 月份组件
import CalendarMonth from '@/components/CalendarMonth/index.vue'
export default {
  components: {
    calendarData: CalendarData,
    calendarMonth: CalendarMonth
  },
  data() {
    return {
      typeList: [
        {text: '年'},
        {text: '月'}
      ],
      currentType: 0,
    }
  },
  created() {},
  methods: {
    // 修改年
    changeYear (value) {
      console.log('value', value)
    },
    clickType (index) {
      this.currentType = index
    }
  }
}
</script>
<style lang="less" scoped>
.typeBox {
    display: flex;
    align-items: center;
    margin: 15px 0;
    .type {
        width: 40px;
        height: 30px;
        line-height: 30px;
        font-size: 16px;
        margin-right: 5px;
        border-radius: 5px;
        text-align: center;
        background-color: rgb(220, 245, 253);
    }
    .currentType {
        background-color: rgb(169, 225, 243);
    }
}
.dataBox, .monthBox{
  margin-top: 20px;
}
</style>

你可能感兴趣的:(vue,js,前端小技巧,vue.js,javascript,前端)