vue2 自定义移动端日历日程组件 可切换年月日周

本文介绍了一个用于移动端的日程组件,利用lunar-calendar插件实现日期的阳历和农历转换,详细展示了如何初始化一周和一个月的日期列表,包括获取当前日期、计算农历等关键代码片段。
摘要由CSDN通过智能技术生成

最近写了一个组件可以切换年月日周的日历日程组件,用于展示在移动端上,提供日程服务,demo示例如下

用到插件: lunar-calendar 将当前日期转化为农历日期,所以需要先安装此插件哦

关键代码,主要是初始化日历展示数据,详情可以看注释中表述的

    // 获取一周的阳历与阴历
    getWeekList () {
      // 获取当前日期
      const currentDate = this.nowDate;
      // 获取当前周的第一天(周日)
      const firstDayOfWeek = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate() - currentDate.getDay());
      // 创建一个数组来存储结果
      const weekDates = [];
      // 循环获取一周的日期和农历日期
      for (let i = 0; i < 7; i++) {
        const date = new Date(firstDayOfWeek.getFullYear(), firstDayOfWeek.getMonth(), firstDayOfWeek.getDate() + i);
        const lunarDate = LunarCalendar.solarToLunar(date.getFullYear(), date.getMonth() + 1, date.getDate());
        // 组合日期和农历日期,并添加到结果数组中
        weekDates.push({
          fullDate: `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`,
          unDate: date, // 未格式化的日期
          date: date.getDate(), // 格式化公历日期
          lunarDate: lunarDate.lunarDayName // 格式化农历日期
        });
      }
      this.thisWeek = weekDates
    },
    getMonthList() {
      // 获取当前日期
      const currentDate = new Date(this.nowDate);
      // 将日期设置为当月的第一天
      currentDate.setDate(1);
      // 获取当前月份
      const currentMonth = currentDate.getMonth();
      // 创建一个数组来存储结果
      const monthDates = [];
      // 补充前面的周日和周一数据
      const firstDayOfWeek = currentDate.getDay(); // 当月第一天是星期几
      const daysBefore = (firstDayOfWeek + 7) % 7; // 前面需要补充的天数
      let startDate = ''
      if (currentDate.getMonth() == 0 && daysBefore > 0) {
        startDate = new Date(currentDate.getFullYear() - 1, 12, 1 - daysBefore);
      } else{
        startDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1 - daysBefore);
      }
      for (let i = 0; i < daysBefore; i++) {
        const date = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate() + i);
        const lunarDate = LunarCalendar.solarToLunar(date.getFullYear(), date.getMonth() + 1, date.getDate());
        monthDates.push({
          fullDate: `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`,
          unDate: date, // 未格式化的日期
          date: date.getDate(), // 格式化公历日期
          lunarDate: lunarDate.lunarDayName // 格式化农历日期
        });
      }
      // 循环获取当月的日期和农历日期
      while (currentDate.getMonth() === currentMonth) {
        const date = new Date(currentDate);
        const lunarDate = LunarCalendar.solarToLunar(date.getFullYear(), date.getMonth() + 1, date.getDate());
        // 组合日期和农历日期,并添加到结果数组中
        monthDates.push({
          fullDate: `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`,
          unDate: date, // 未格式化的日期
          date: date.getDate(), // 格式化公历日期
          lunarDate: lunarDate.lunarDayName // 格式化农历日期
        });
        currentDate.setDate(currentDate.getDate() + 1);
      }
      this.thisMonth = monthDates
    },

完整代码如下,一共380行,为了简洁明了,很多地方可以复用的代码没有进行二次封装,有需要的可以自行封装一下!

<template>
  <section class="calendar-card">
    <div class="calendar-title">
      <span class="date-b" @click="handlePrevious(false)">
        <van-icon name="arrow-left" />
      </span>
      <span class="now-date" @click="showChooseMonth = true">{{ nowDate | nowDateFilter }}</span>
      <span class="date-b" @click="handleNext(false)">
        <van-icon name="arrow" />
      </span>
    </div>
    <div class="calendar-week">
      <div class="calendar__weekdays">
        <span class="calendar__weekday" v-for="i in weekdays" :key="i">
          {{ i }}
        </span>
      </div>
      <div v-show="!showUp" class="calendar__weekdays">
        <div
          class="calendar__week"
          v-for="date in thisWeek"
          :class="{'active-day': selectDay == date.fullDate}"
          :key="date.fullDate"
          @click="selectDate(1, date)"
        >
          <span class="solar">{{ date.date }}</span>
          <span class="lunar">{{ date.lunarDate }}</span>
          <!-- 展示下方小圆点 此处可以处理当日有无日程 可以接对应数据来渲染 -->
          <i></i>
        </div>
      </div>
      <div v-show="showUp" class="calendar__weekdays calendar__months">
        <div
          class="calendar__week"
          v-for="date in thisMonth"
          :class="{'active-day': selectDay == date.fullDate}"
          :key="date.fullDate"
          @click="selectDate(2, date)"
        >
          <span class="solar">{{ date.date }}</span>
          <span class="lunar">{{ date.lunarDate }}</span>
          <!-- 展示下方小圆点 此处可以处理当日有无日程 可以接对应数据来渲染 -->
          <i></i>
        </div>
      </div>
      <van-divider>
        <img class="down-img" :class="{'up-img' : showUp}" @click="handleShowMonth" src="@/assets/prev-b.png" alt="">
      </van-divider>
    </div>
    <van-popup
      v-model="showChooseMonth"
      closeable
      position="bottom"
    >
      <section class="choose-month">
        <div class="calendar-title">
          <span class="date-b" @click="handlePrevious(true)">
            <img src="@/assets/prev-b.png" alt="">
          </span>
          <span class="now-date">{{ nowYear }}年</span>
          <span class="date-b next-b" @click="handleNext(true)">
            <img src="@/assets/prev-b.png" alt="">
          </span>
        </div>
        <van-divider />
        <!-- 时间代理处理切换月份 -->
        <ul class="month-box" @click="chooseMonth">
          <li
            v-for="(month, index) in monthList"
            :data-id="index"
            :class="{'active-month': changeMonthIndex(nowDate) == nowYear+index}"
            :key="index"
          >
            {{ month }}
          </li>
        </ul>
      </section>
    </van-popup>
  </section>
</template>

<script>
import LunarCalendar from 'lunar-calendar';

export default {
  name: 'CalendarCard',
  data() {
    return {
      nowDate: new Date(),
      nowYear: new Date().getFullYear(),
      showChooseMonth: false,
      showUp: false,
      selectDay: '',
      weekdays: ['日', '一', '二', '三','四', '五','六'],
      monthList: ['一月', '二月', '三月','四月', '五月','六月','七月', '八月', '九月','十月', '十一月','十二月'],
      thisWeek: [],
      thisMonth: [],
    };
  },
  filters: {
    nowDateFilter (date) {
      const year = date.getFullYear();
      const month = date.getMonth() + 1; // 月份从0开始,因此需要加1
      return `${year}年${month}月`;
    }
  },
  watch: {
    nowDate: {
      handler(newd) {
        if(newd) {
          this.getWeekList()
          if(this.showUp) {
            this.getMonthList()
          }
        }
      }
    }
  },
  mounted() {
    this.selectDay = `${this.nowDate.getFullYear()}-${this.nowDate.getMonth()}-${this.nowDate.getDate()}`,
    this.getWeekList()
  },
  methods: {
    changeMonthIndex(date) {
      return date.getFullYear()+date.getMonth()
    },
    // 获取一周的阳历与阴历
    getWeekList () {
      // 获取当前日期
      const currentDate = this.nowDate;
      // 获取当前周的第一天(周日)
      const firstDayOfWeek = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate() - currentDate.getDay());
      // 创建一个数组来存储结果
      const weekDates = [];
      // 循环获取一周的日期和农历日期
      for (let i = 0; i < 7; i++) {
        const date = new Date(firstDayOfWeek.getFullYear(), firstDayOfWeek.getMonth(), firstDayOfWeek.getDate() + i);
        const lunarDate = LunarCalendar.solarToLunar(date.getFullYear(), date.getMonth() + 1, date.getDate());
        // 组合日期和农历日期,并添加到结果数组中
        weekDates.push({
          fullDate: `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`,
          unDate: date, // 未格式化的日期
          date: date.getDate(), // 格式化公历日期
          lunarDate: lunarDate.lunarDayName // 格式化农历日期
        });
      }
      this.thisWeek = weekDates
    },
    getMonthList() {
      // 获取当前日期
      const currentDate = new Date(this.nowDate);
      // 将日期设置为当月的第一天
      currentDate.setDate(1);
      // 获取当前月份
      const currentMonth = currentDate.getMonth();
      // 创建一个数组来存储结果
      const monthDates = [];
      // 补充前面的周日和周一数据
      const firstDayOfWeek = currentDate.getDay(); // 当月第一天是星期几
      const daysBefore = (firstDayOfWeek + 7) % 7; // 前面需要补充的天数
      let startDate = ''
      if (currentDate.getMonth() == 0 && daysBefore > 0) {
        startDate = new Date(currentDate.getFullYear() - 1, 12, 1 - daysBefore);
      } else{
        startDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1 - daysBefore);
      }
      for (let i = 0; i < daysBefore; i++) {
        const date = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate() + i);
        const lunarDate = LunarCalendar.solarToLunar(date.getFullYear(), date.getMonth() + 1, date.getDate());
        monthDates.push({
          fullDate: `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`,
          unDate: date, // 未格式化的日期
          date: date.getDate(), // 格式化公历日期
          lunarDate: lunarDate.lunarDayName // 格式化农历日期
        });
      }
      // 循环获取当月的日期和农历日期
      while (currentDate.getMonth() === currentMonth) {
        const date = new Date(currentDate);
        const lunarDate = LunarCalendar.solarToLunar(date.getFullYear(), date.getMonth() + 1, date.getDate());
        // 组合日期和农历日期,并添加到结果数组中
        monthDates.push({
          fullDate: `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`,
          unDate: date, // 未格式化的日期
          date: date.getDate(), // 格式化公历日期
          lunarDate: lunarDate.lunarDayName // 格式化农历日期
        });
        currentDate.setDate(currentDate.getDate() + 1);
      }
      this.thisMonth = monthDates
    },
    selectDate (type, date) {
      // 点击月份的某一天时候 将周置为当前点击的一周
      if (type === 2) {
        this.nowDate = date.unDate
        this.showUp = false
      }
      this.selectDay = date.fullDate
    },
    // 上一周 或 上一年
    handlePrevious(bool) {
      if(bool) {
        this.nowYear -= 1
        return
      }
      let lastDate
      // 月份切换
      if (this.showUp) {
        // 选择下一月的逻辑
        lastDate = new Date(this.nowDate);
        lastDate.setMonth(lastDate.getMonth() - 1);
        } else {
        // 选择下一周的逻辑
        lastDate  = new Date(this.nowDate);
        lastDate.setDate(lastDate.getDate() - 7); // 减去7天
      }

      this.nowDate = lastDate;
    },
    // 下一周 或 下一年
    handleNext(bool) {
      if(bool) {
        this.nowYear += 1
        return
      }
      let nextDate = new Date(this.nowDate);
      // 月份切换
      if (this.showUp) {
        // 选择下一月的逻辑
        nextDate.setMonth(nextDate.getMonth() + 1);
        } else {
        // 选择下一周的逻辑
        nextDate.setDate(nextDate.getDate() + 7); // 加上7天
      }

      this.nowDate = nextDate;
    },
    handleShowMonth() {
      this.showUp = !this.showUp
      if(this.showUp) {
        this.getMonthList()
      }
    },
    // 选择对应的月份
    chooseMonth (event) {
      const year = this.nowYear
      const month = event.target.getAttribute('data-id')
      let nowDate = this.nowDate
      // 创建一个新的日期对象,以便 Vue 能够检测到属性变化, 否则不会触发watch的监听
      const newDate = new Date(nowDate);
      // 将传入的年份和月份设置给 nowDate
      newDate.setFullYear(year);
      newDate.setMonth(month);
      this.$set(this, 'nowDate', newDate)
      this.showChooseMonth = false
    }
  },
};
</script>

<style lang="scss" scoped>
.calendar-card {
  .calendar-title {
    width: 146px;
    margin: auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    .date-b {
      width: 17px;
      height: 17px;
      background: #ffdada;
      border-radius: 2px;
      color: #ff3636;
      text-align: center;
    }
    .now-date {
      min-width: 80px;
      height: 23px;
      font-family: PingFangSC-Medium;
      font-size: 16px;
      color: #333333;
      text-align: center;
      font-weight: 500;
    }
  }
  .calendar-week {
    .calendar__weekdays {
      display: flex;
      flex-wrap: wrap;
      .calendar__weekday {
        flex: 1;
        font-size: 12px;
        line-height: 30px;
        text-align: center;
        font-family: PingFangSC-Regular;
        font-size: 14px;
        color: #333333;
        text-align: center;
        font-weight: 400;
      }
      .calendar__week {
        width: calc(100% / 7);
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 4px;
        padding: 4px 0;
        .solar {
          font-family: PingFangSC-Regular;
          font-size: 14px;
          color: #333333;
          text-align: center;
          font-weight: 400;
        }
        .lunar {
          font-family: PingFangSC-Regular;
          font-size: 12px;
          color: #666666;
          text-align: center;
          font-weight: 400;
        }
        i {
          display: inline-block;
          width: 6px;
          height: 6px;
          border-radius: 3px;
          background-color: #ff3636;
        }
      }
      .active-day {
        height: 100%;
        background-image: linear-gradient(180deg, #f31520 4%, #fc9297 100%);
        box-shadow: 0px 4px 11px 0px rgba(243, 31, 42, 0.3);
        border-radius: 10px;
        .solar,
        .lunar {
          color: #ffffff;
        }
        i {
          background-color: #ffffff;
        }
      }
    }
    .down-img {
      transform: rotate(-90deg);
      transition: transform 0.5s ease-in-out;
    }
    .up-img {
      transform: rotate(90deg);
      transition: transform 0.5s ease-in-out;
    }
  }
  .choose-month {
    .calendar-title {
      height: 40px;
    }
    .date-b {
      background: unset;
    }
    .next-b {
      img {
        transform: rotate(180deg);
      }
    }
    .month-box {
      display: flex;
      flex-wrap: wrap;
      li {
        width: 25%;
        height: 40px;
        text-align: center;
      }
      .active-month {
        color: #e12617;
      }
    }
  }
}
</style>

  • 9
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
对于自定义日历日程组件,你可以使用Vue.js框架来实现。下面是一个简单的示例代码,可以帮助你开始构建这个组件: ```vue <template> <div class="calendar"> <div class="header"> <button @click="previousMonth"><</button> <h2>{{ currentMonth }}</h2> <button @click="nextMonth">></button> </div> <div class="days"> <div v-for="day in days" :key="day" class="day">{{ day }}</div> </div> <div class="events"> <div v-for="event in events" :key="event.id" class="event"> <p>{{ event.date }}</p> <p>{{ event.title }}</p> </div> </div> </div> </template> <script> export default { data() { return { currentMonth: '', days: [], events: [ { id: 1, date: '2022-01-05', title: 'Event 1' }, { id: 2, date: '2022-01-15', title: 'Event 2' }, { id: 3, date: '2022-01-20', title: 'Event 3' }, ], }; }, mounted() { this.setCurrentMonth(); this.setDays(); }, methods: { setCurrentMonth() { const date = new Date(); const options = { month: 'long', year: 'numeric' }; this.currentMonth = date.toLocaleDateString('en-US', options); }, setDays() { const date = new Date(); const year = date.getFullYear(); const month = date.getMonth(); const daysInMonth = new Date(year, month + 1, 0).getDate(); this.days = Array.from({ length: daysInMonth }, (_, index) => index + 1); }, previousMonth() { // 实现切换到上一个月的逻辑 }, nextMonth() { // 实现切换到下一个月的逻辑 }, }, }; </script> <style> .calendar { /* 样式 */ } .header { /* 样式 */ } .days { /* 样式 */ } .day { /* 样式 */ } .events { /* 样式 */ } .event { /* 样式 */ } </style> ``` 这个例子中的日历组件包含一个头部,显示当前月份,以及上一个月和下一个月的按钮。接下来是一个天数的区域,以及事件的区域。你可以根据自己的需求来自定义样式。 这个示例中的事件是固定的,你可以根据你的具体需求从后端获取事件数据,并在`events`数组中进行动态渲染。 在`previousMonth`和`nextMonth`方法中,你可以实现切换到上一个月和下一个月的逻辑,例如更新`currentMonth`和`days`数据,以及获取新月份的事件数据。 希望这个示例能帮到你!如果有任何问题,请随时提问。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值