手写一个日历


<template>
  <div>
    <!-- 输入框和日历图标 -->
    <input type="text" v-model="selectedDate" @focus="showCalendar" readonly>
    <span @click="showCalendar">📅</span>

    <!-- 日历选择器 -->
    <div v-if="isCalendarVisible" class="calendar" >
      <div class="calendar-header">
        <button @click="previousMonth">&lt;</button>
        <span>{{ currentMonth }}</span>
        <button @click="nextMonth">&gt;</button>
      </div>
      <div class="calendar-body">
        <div class="weekdays">
          <span v-for="day in daysOfWeek" :key="day">{{ day }}</span>
        </div>
        <div class="dates">
          <span v-for="date in datesInMonth" :key="date"
                @mouseenter="hoverDate(date)" @mouseleave="clearHover"
                @click="selectDate(date)"
                :class="{ 'hovered': date === hoveredDate, 'selected': date === selectedDate }">{{ date }}</span>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      selectedDate: '', // 选定的日期
      isCalendarVisible: false, // 日历是否可见
      currentDate: new Date(), // 当前日期
      hoveredDate: '', // 悬浮的日期
      daysOfWeek: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] // 一周的日期
    };
  },
  computed: {
    currentMonth() {
      return this.currentDate.toLocaleString('default', { month: 'long', year: 'numeric' });
    },
    datesInMonth() {
      const year = this.currentDate.getFullYear();
      const month = this.currentDate.getMonth();
      const firstDayOfMonth = new Date(year, month, 1);
      const lastDayOfMonth = new Date(year, month + 1, 0);
      const dates = [];

      for (let i = 1; i <= lastDayOfMonth.getDate(); i++) {
        dates.push(i);
      }

      // 补充前面的空白日期
      for (let i = 0; i < firstDayOfMonth.getDay(); i++) {
        dates.unshift('');
      }

      return dates;
    }
  },
  methods: {
    showCalendar() {
      this.isCalendarVisible = true;
      document.addEventListener('mousedown', this.handleClickOutside);
    },
    hideCalendar() {
      this.isCalendarVisible = false;
      document.removeEventListener('mousedown', this.handleClickOutside);
    },
    handleClickOutside(event) {
      if (this.$el && !this.$el.contains(event.target)) {
        this.hideCalendar();
      }
    },
    previousMonth() {
      this.currentDate.setMonth(this.currentDate.getMonth() - 1);
      this.currentDate = new Date(this.currentDate); // 触发依赖于 currentDate 的计算属性更新
    },
    nextMonth() {
      this.currentDate.setMonth(this.currentDate.getMonth() + 1);
      this.currentDate = new Date(this.currentDate); // 触发依赖于 currentDate 的计算属性更新
    },
    selectDate(date) {
      if (date !== '') {
        const year = this.currentDate.getFullYear();
        const month = this.currentDate.getMonth() + 1;
        const formattedDate = `${year}-${month < 10 ? '0' + month : month}-${date < 10 ? '0' + date : date}`;
        this.selectedDate = formattedDate;
        this.hideCalendar();
      }
    },
    hoverDate(date) {
      if (!isNaN(date)) {
        this.hoveredDate = date;
      }
    },
    clearHover() {
      this.hoveredDate = '';
    }
  },
  beforeDestroy() {
    document.removeEventListener('mousedown', this.handleClickOutside);
  }
};
</script>

<style scoped>
.calendar {
  position: absolute;
  background-color: #fff;
  border: 1px solid #ccc;
  padding: 10px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.calendar-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.calendar-header button {
  background-color: transparent;
  border: none;
  cursor: pointer;
  font-size: 14px;
}

.weekdays {
  display: flex;
  justify-content: space-between;
  padding: 5px 0;
}

.weekdays span {
  width: calc(100% / 7);
  text-align: center;
}

.dates {
  display: flex;
  flex-wrap: wrap;
}

.dates span {
  width: calc(100% / 7);
  text-align: center;
  padding: 5px 0;
  cursor: pointer;
}

.dates span.hovered {
  background-color: #f0f0f0;
}

.dates span.selected {
  color: #fff;
}
</style>

posted @ 2024-05-09 09:45  茫茫人间刺骨凉  阅读(26)  评论(0)    收藏  举报