vue2日历组件

【效果图】
在这里插入图片描述

<template>
  <div style="width: 100%">
    <!-- <div> -->
    <!-- <div>{{ startDate.getMonth() + 1 + '-' + startDate.getDate() }}</div>
    <div>{{ endDate.getMonth() + 1 + '-' + endDate.getDate() }}</div> -->
    <!-- <button @click="generateDates">生成日期</button> -->
    <div class="lableBoxClass flexBetween">
      <div class="lableClass">请假日期</div>
      <div>{{ selectedDatesStr }}</div>
    </div>
    <div class="calenderBoxClass">
      <!-- <div>选中的日期:{{ selectedDatesStr }}</div> -->
      <div class="date-scale">
        <span v-for="(day, index) in scaleDays" :key="index">{{ day }}</span>
      </div>
      <ul class="date-list">
        <li v-for="(date, index) in showDates" :key="index">
          <div @click="toggleDateSelection(date)" class="date-item" :class="{ grayColor: isDateDisabled(date), selected: isSelected(date) }">{{ date }}</div>
        </li>
      </ul>
    </div>
  </div>
</template>

<script>
import { Toast } from 'vant'
export default {
  name: 'CalendarVue',
  props: {
    startDate: {
      type: Date,
      default() {
        return new Date()
      }
    },
    endDate: {
      type: Date,
      default() {
        return new Date()
      }
    }
  },
  data() {
    return {
      // startDate: new Date('2024-12-19'),
      // endDate: new Date('2025-01-05'),
      showDates: [], // 用于存储页面显示的日期数组
      selectableDates: [], // 用于存储可选的日期数组
      scaleDays: ['一', '二', '三', '四', '五', '六', '日'], // 用于存储日期刻度的数组
      selectedDates: {}, // 用于存储选中的日期,键为日期,值为是否选中
      selectedDatesArr: [] // 用于存储选中的日期数组
    }
  },
  computed: {
    selectedDatesStr() {
      if (this.selectedDatesArr.length > 0) {
        return this.selectedDatesArr.join(',')
      }
      return '请点击需要请假的日期'
    }
  },
  created() {
    // 生成日期
    this.generateDates()
    this.getSelectableDates()
  },
  mounted() {},
  methods: {
    isDateDisabled(date) {
      return !this.selectableDates.includes(date)
    },
    generateDates() {
      if (!this.startDate || !this.endDate) {
        alert('请选择开始日期和结束日期')
        return
      }

      // 重置日期数组和刻度数组
      this.showDates = []
      const startDate = new Date(this.startDate)
      const endDate = new Date(this.endDate)
      console.log(90, this.formatDate(startDate), this.formatDate(endDate))

      // 找到第一个星期一
      let currentDate = new Date(startDate)
      while (currentDate.getDay() !== 1) {
        // getDay() 返回的是 0(星期日) 到 6(星期六)
        currentDate.setDate(currentDate.getDate() + 1)
      }
      // 重新设置currentDate为区间开始日期
      currentDate = new Date(startDate)
      this.showDates = [...this.getDatesFromThisMonday(this.startDate)]
      console.log(100, this.showDates)

      // 生成日期数组
      while (currentDate <= endDate) {
        this.showDates.push(this.formatDate(currentDate))
        currentDate.setDate(currentDate.getDate() + 1)
      }
      console.log(107, this.showDates)

      let nextSundayArr = this.getDatesFromEndDateToNextSunday(this.endDate)
      if (nextSundayArr.length > 0) {
        nextSundayArr.forEach(item => {
          this.showDates.push(item)
        })
      }
      console.log(115, this.showDates)
    },
    // formatDate(date)<-->将日期对象转换为 "月份.日期" 格式
    formatDate(date) {
      const month = date.getMonth() + 1 // 月份从0开始,所以需要+1
      const day = date.getDate()
      return `${month}.${day}`
    },
    getSelectableDates() {
      let startShowDate = new Date(this.startDate)
      let endShowDate = new Date(this.endDate)
      console.log(131, startShowDate, endShowDate)
      while (startShowDate < endShowDate) {
        let formattedDate = startShowDate.getMonth() + 1 + '.' + startShowDate.getDate()
        this.selectableDates.push(formattedDate) // 将格式化后的日期添加到数组中
        startShowDate.setDate(startShowDate.getDate() + 1) // 移动到下一天
      }
      console.log(138000, this.selectableDates)
      // return this.selectableDates
    },
    //getDatesFromThisMonday(startDate)<-->获取从当前周一到起始日期【startDate】的日期数组,并转换为 "月份.日期" 格式(与参数【showDates】(页面展示的日期)相关)
    getDatesFromThisMonday(startDate) {
      // 创建一个当前日期的副本,以避免修改原始日期对象
      let today = new Date(startDate.getTime())

      // 找到这周一的日期
      let thisMonday = new Date(today.getTime())
      thisMonday.setDate(today.getDate() - today.getDay() + (today.getDay() === 0 ? -6 : 1)) // 设置为周一

      // 生成从这周一到今天的日期数组,并转换为 "月份.日期" 格式
      let dates = []
      let current = new Date(thisMonday.getTime()) // 从这周一开始
      while (current < today) {
        // 包含今天
        // 转换日期格式为 "月份.日期"
        let formattedDate = current.getMonth() + 1 + '.' + current.getDate()
        dates.push(formattedDate) // 将格式化后的日期添加到数组中
        current.setDate(current.getDate() + 1) // 移动到下一天
      }
      // console.log(136, dates)
      return dates // 返回日期数组
    },
    //getDatesFromEndDateToNextSunday(endDate)<-->获取从结束日期【endDate】到下一个周日之间的所有日期数组,并转换为 "月份.日期" 格式(与参数【showDates】(页面展示的日期)相关)
    getDatesFromEndDateToNextSunday(endDate) {
      let current = new Date(endDate.getTime())
      // 如果endDate是周日,则将日期设置为下一天(周一)
      if (current.getDay() == 0) {
        return []
      }
      current.setDate(current.getDate() + 1)
      let dates = []
      // 循环直到当前日期是下一个周日
      while (current.getDay() !== 0) {
        // 格式化日期为 "月份.日期"
        let formattedDate = current.getMonth() + 1 + '.' + current.getDate()
        // 将格式化后的日期字符串添加到数组中
        dates.push(formattedDate)
        // 将日期加1天
        current.setDate(current.getDate() + 1)
      }
      // 如果endDate不是周日,我们才添加当前周日
      if (endDate.getDay() !== 0) {
        let formattedSunday = current.getMonth() + 1 + '.' + current.getDate()
        dates.push(formattedSunday)
      }
      console.log(165, dates)

      // 返回包含所有格式化日期的数组
      return dates
    },
    toggleDateSelection(date) {
      // 切换日期的选中状态
      if (this.isDateDisabled(date)) {
        Toast('不可选择范围外的日期')
        return
      }

      // 切换选中状态
      const isSelected = this.isSelected(date)
      this.$set(this.selectedDates, date, !isSelected)

      // 根据新的选中状态更新数组
      if (!isSelected) {
        // 如果之前未选中,现在选中了,则添加到数组中
        this.selectedDatesArr.push(date)
      } else {
        // 如果之前已选中,现在取消了,则从数组中移除
        const index = this.selectedDatesArr.indexOf(date)
        if (index !== -1) {
          this.selectedDatesArr.splice(index, 1)
        }
      }
    },
    isSelected(date) {
      // 检查日期是否被选中
      return this.selectedDates[date] === true
    }
  }
}
</script>
<style lang="scss" scoped>
.lableBoxClass {
  padding: 15px;
  font-size: 15px;
  display: flex;
  .lableClass {
    width: 100px;
    color: #1f2022;
    flex-shrink: 0;
    &::after {
      content: '*'; /* 使用Unicode字符表示红星 */
      color: red;
      margin-left: 5px;
    }
  }
}
.calenderBoxClass {
  // padding-left: 15px;
  // padding-right: 10px;
  // background-color: yellow;
}
</style>

<style scoped>
.date-scale {
  display: flex;
  flex-direction: row;
  justify-content: space-around;
  flex: 0 0 calc(100% / 7 - 10px); /* 假设你想要每个日期项之间有10px的间隙 */
  margin: 5px; /* 这将创建10px的间隙(每个方向5px) */
  text-align: center;
  margin-bottom: 10px;
}
.date-scale span {
  display: inline-block;
  width: 30px; /* 根据需要调整刻度宽度 */
  text-align: center;
}

.date-scale {
  display: flex;
  justify-content: space-between;
}

.date-list {
  display: flex;
  flex-wrap: wrap;
  list-style: none;
  padding: 0;
}

.date-list li {
  flex: 0 0 calc(100% / 7 - 10px); /* 假设你想要每个日期项之间有10px的间隙 */
  /*width: calc(100vw / 7- 10px); */
  text-align: center;
  font-style: 16px;
}
.date-item {
  /*margin: 5px;*/
  background-color: #e6eefc;
  width: calc(100vw / 7);
  margin-bottom: 8px;
  height: 30px;
  display: flex;
  justify-content: center;
  align-items: center;
  cursor: pointer;
}
.date-list:nth-child(3) {
  border-radius: 10px;
}
.grayColor {
  color: gray;
  background-color: white;
}

.selected {
  background-color: rgb(19, 96, 231); /* 选中的日期背景色为蓝色 */
  color: white; /* 选中的日期字体颜色为白色 */
  border-radius: 5px;
}
</style>

调用组件

   <CalendarVue :startDate="startDate" :endDate="endDate" />
以下是一个简单的Vue2日历组件的实现,包括日期选择和月份切换功能: ```html <template> <div class="calendar"> <div class="header"> <span class="prev" @click="prevMonth"><</span> <span class="title">{{ year }}年{{ month }}月</span> <span class="next" @click="nextMonth">></span> </div> <div class="weekdays"> <span v-for="day in weekdays" :key="day">{{ day }}</span> </div> <div class="days"> <span v-for="day in days" :key="day.date" :class="{ today: day.today, selected: day.selected, disabled: day.disabled }" @click="selectDay(day)" >{{ day.day }}</span> </div> </div> </template> <script> export default { props: { value: { type: String, default: '' } }, data() { return { weekdays: ['日', '一', '二', '三', '四', '五', '六'], year: 0, month: 0, days: [] } }, computed: { date() { return this.value ? new Date(this.value) : new Date() }, firstDay() { return new Date(this.year, this.month - 1, 1).getDay() }, lastDate() { return new Date(this.year, this.month, 0).getDate() } }, methods: { init() { this.year = this.date.getFullYear() this.month = this.date.getMonth() + 1 this.days = [] for (let i = 1; i <= this.lastDate; i++) { const date = new Date(this.year, this.month - 1, i) this.days.push({ date, day: i, today: this.isToday(date), selected: this.isSelected(date), disabled: this.isDisabled(date) }) } }, isToday(date) { const today = new Date() return date.getFullYear() === today.getFullYear() && date.getMonth() === today.getMonth() && date.getDate() === today.getDate() }, isSelected(date) { return this.value && date.getTime() === new Date(this.value).getTime() }, isDisabled(date) { return false // 可以根据需要自定义禁用日期的规则 }, selectDay(day) { if (!day.disabled) { this.$emit('input', day.date.toISOString()) } }, prevMonth() { if (this.month === 1) { this.year-- this.month = 12 } else { this.month-- } this.init() }, nextMonth() { if (this.month === 12) { this.year++ this.month = 1 } else { this.month++ } this.init() } }, mounted() { this.init() } } </script> <style> .calendar { width: 200px; border: 1px solid #ccc; border-radius: 4px; font-size: 14px; color: #333; } .header { display: flex; justify-content: space-between; align-items: center; padding: 8px; background-color: #f5f5f5; } .weekdays { display: flex; justify-content: space-around; align-items: center; padding: 8px; background-color: #f5f5f5; } .days { display: flex; flex-wrap: wrap; justify-content: space-around; align-items: center; padding: 8px; } .days span { display: inline-flex; justify-content: center; align-items: center; width: 24px; height: 24px; margin: 4px; cursor: pointer; border-radius: 50%; } .days span.today { background-color: #409eff; color: #fff; } .days span.selected { background-color: #f5f5f5; color: #333; } .days span.disabled { color: #ccc; cursor: not-allowed; } </style> ``` 使用方法: ```html <template> <div> <input type="text" v-model="date" readonly> <calendar v-model="date"></calendar> </div> </template> <script> import Calendar from './Calendar.vue' export default { components: { Calendar }, data() { return { date: '' } } } </script> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值