vue3自定义日历

原理

  • 现在的日历分为两种开头:
    1. '日', '一', '二', '三', '四', '五', '六'
    2. '一', '二', '三', '四', '五', '六', '日'
  • 一行7个日期,一共6行

  1. 其实不管哪种都一样,首先要确定第一行1号在哪个位置。
    如果说是 '日', '一', '二', '三', '四', '五', '六',那么getDay()是几,它前面就该补几位。当前是2023-7-1,这天是星期六,getDay() === 6前面补六天,如果是星期天getDay()===0前面不用补。这里就要去取上个月多少天了,减去这六天,就可以把前面的空补上。例如:上个月是6月,有30天,30-6+1=25,从25补到30就可以了。这里是需要判断闰年这些的,防止在2月取的不对。
  2. 第一行补多少天算完,直接循环当月的天数。7月是31天,直接循环渲染31天
  3. 现在算最后面还需要补多少天,6行一共42天,现在渲染了6+31=37天,还需要补42-37=5天。
2526272829301

在这里插入图片描述

  1. 其实不管哪种都一样,首先要确定第一行1号在哪个位置。
    如果说是 '一', '二', '三', '四', '五', '六','日',这里就有点小区别了。假设n=getDay(),应该补n-1天, 但是星期日例外,星期日的n=0但是它要补6天。当前是2023-7-1,这天是星期六,getDay() === 6前面补五天,如果是星期天getDay()===0前面补六天。
  2. 第一行补多少天算完,直接循环当月的天数。7月是31天,直接循环渲染31天
  3. 现在算最后面还需要补多少天,6行一共42天,现在渲染了5+31=36天,还需要补42-36=6天。
262728293012

在这里插入图片描述

源码

<template>
  <div>
    <div class="toolbar">
      <div class="btn" @click="preMonth">上一月</div>
      <div class="date">{{ currentYear }}年{{ currentMonth }}月</div>
      <div class="btn" @click="nextMonth">下一月</div>
    </div>
    <div class="container">
      <div v-for="(item, index) in header" :key="index" class="grid-item">{{ item }}</div>
      <div v-for="(item, index) in lastMonthSurplusDayArray" :key="index" class="grid-item">
        {{ item }}
      </div>
      <div
        v-for="(item, index) in currentMonthDayCount"
        :key="index"
        :class="[
          'grid-item',
          Math.random() * 30 < 10
            ? 'very-hot'
            : Math.random() * 30 < 20
            ? 'middle-hot'
            : 'normal-hot'
        ]"
      >
        {{ item }}
      </div>
      <div v-for="(item, index) in nextMonthSurplusDayArray" :key="index" class="grid-item">
        {{ item }}
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { reactive, ref, onMounted } from 'vue'
// const header = reactive<string[]>(['日', '一', '二', '三', '四', '五', '六'])
const header = reactive<string[]>(['一', '二', '三', '四', '五', '六', '日'])
// 上个月剩余天数
const lastMonthSurplusDay = ref<number>(0)
const lastMonthSurplusDayArray = ref<number[]>([])
// 下个月剩余天数
const nextMonthSurplusDay = ref<number>(0)
const nextMonthSurplusDayArray = ref<number[]>([])
// 当前月份总天数
const currentMonthDayCount = ref<number>(0)
const currentYear = ref<number>(0)
const currentMonth = ref<number>(0)
const currentDate = ref<number>(0)
// 闰年
const leapMonthDay = reactive<number[]>([31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31])
// 平年
const normalMonthDay = reactive<number[]>([31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31])
// 是否为闰年
function isLeapYear(year: number) {
  return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0
}
// 获取这个月1号的星期数
function getMonthFirstDay(year: number, month: number) {
  return new Date(year, month - 1, 1).getDay()
}
// 计算日期
function calculateDays() {
  // 获取本月第一天星期几(星期几就补多少个空)
  // lastMonthSurplusDay.value = getMonthFirstDay(currentYear.value, currentMonth.value)
  lastMonthSurplusDay.value =
    getMonthFirstDay(currentYear.value, currentMonth.value) === 0
      ? 6
      : getMonthFirstDay(currentYear.value, currentMonth.value) - 1
  // 获取当前月有多少天
  currentMonthDayCount.value = isLeapYear(currentYear.value)
    ? leapMonthDay[currentMonth.value - 1]
    : normalMonthDay[currentMonth.value - 1]
  let prevMonthLastDate = 0
  if (currentMonth.value === 1) {
    // 当前是1月还要用去年的去判断
    prevMonthLastDate = isLeapYear(currentYear.value - 1)
      ? leapMonthDay[leapMonthDay.length - 1]
      : normalMonthDay[normalMonthDay.length - 1]
  } else {
    prevMonthLastDate = isLeapYear(currentYear.value)
      ? leapMonthDay[currentMonth.value - 2]
      : normalMonthDay[currentMonth.value - 2]
  }
  // 获取还需要渲染多少天
  nextMonthSurplusDay.value = 42 - (lastMonthSurplusDay.value + currentMonthDayCount.value)
  const prevtemp = []
  const nexttemp = []
  for (let i = prevMonthLastDate - lastMonthSurplusDay.value + 1; i <= prevMonthLastDate; i++) {
    prevtemp.push(i)
  }
  for (let i = 1; i <= nextMonthSurplusDay.value; i++) {
    nexttemp.push(i)
  }
  lastMonthSurplusDayArray.value = prevtemp
  nextMonthSurplusDayArray.value = nexttemp
}
// 上个月
function preMonth() {
  if (currentMonth.value === 1) {
    currentMonth.value = 12
    --currentYear.value
  } else {
    --currentMonth.value
  }
  calculateDays()
}
// 下个月
function nextMonth() {
  if (currentMonth.value === 12) {
    currentMonth.value = 1
    ++currentYear.value
  } else {
    ++currentMonth.value
  }
  calculateDays()
}
// 获取当前日期
function getCurrentDate() {
  const d = new Date()
  const year = d.getFullYear()
  const month = d.getMonth() + 1
  const date = d.getDate()
  return {
    year,
    month,
    date
  }
}
// 初始化日历
function initCalendar() {
  const { year, month, date } = getCurrentDate()
  currentYear.value = year
  currentMonth.value = month
  currentDate.value = date
  calculateDays()
}
onMounted(() => {
  initCalendar()
})
</script>

<style scoped>
.toolbar {
  display: flex;
  width: 100%;
  height: 50px;
}

.toolbar .date {
  flex: 1;
  color: #333;
  font-size: 14px;
  font-weight: bold;
  text-align: center;
  line-height: 30px;
  border-top: 1px solid #eee;
  padding: 10px;
  box-sizing: border-box;
}
.toolbar .btn {
  flex: 1;
  color: #1d84f6;
  text-align: center;
  line-height: 30px;
  border-top: 1px solid #eee;
  border-left: 1px solid #eee;
  border-right: 1px solid #eee;
  padding: 10px;
  box-sizing: border-box;
}
.container {
  display: grid;
  box-sizing: border-box;
  grid-template-columns: auto auto auto auto auto auto auto;
  border-top: 1px solid #eee;
  border-left: 1px solid #eee;
  border-right: 1px solid #eee;
}
.grid-item {
  width: 50px;
  height: 50px;
  line-height: 50px;
  border-bottom: 1px solid #eee;
  border-right: 1px solid #eee;
  font-size: 14px;
  text-align: center;
}

.grid-item:nth-child(7n) {
  border-right: none;
}

.very-hot {
  background-color: #e10231;
}
.middle-hot {
  background-color: #fe4806;
}
.normal-hot {
  background-color: #ffb508;
}
</style>
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值