原理
- 现在的日历分为两种开头:
1. '日', '一', '二', '三', '四', '五', '六'
2. '一', '二', '三', '四', '五', '六', '日'
- 一行7个日期,一共6行
- 其实不管哪种都一样,首先要确定第一行
1号
在哪个位置。
如果说是 '日', '一', '二', '三', '四', '五', '六'
,那么getDay()
是几,它前面就该补几位。当前是2023-7-1
,这天是星期六,getDay() === 6
前面补六天
,如果是星期天getDay()===0
前面不用补。这里就要去取上个月多少天了,减去这六天,就可以把前面的空补上。例如:上个月是6月,有30天,30-6+1=25
,从25补到30就可以了。这里是需要判断闰年这些的,防止在2月取的不对。 第一行
补多少天算完,直接循环当月的天数。7月是31天,直接循环渲染31天
。- 现在算
最后面
还需要补多少天,6行一共42天,现在渲染了6+31=37
天,还需要补42-37=5
天。
- 其实不管哪种都一样,首先要确定第一行
1号
在哪个位置。
如果说是 '一', '二', '三', '四', '五', '六','日'
,这里就有点小区别了。假设n=getDay()
,应该补n-1
天, 但是星期日例外,星期日的n=0
但是它要补6天
。当前是2023-7-1
,这天是星期六,getDay() === 6
前面补五天
,如果是星期天getDay()===0
前面补六天。 第一行
补多少天算完,直接循环当月的天数。7月是31天,直接循环渲染31天
。- 现在算
最后面
还需要补多少天,6行一共42天,现在渲染了5+31=36
天,还需要补42-36=6
天。
源码
<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 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
}
function getMonthFirstDay(year: number, month: number) {
return new Date(year, month - 1, 1).getDay()
}
function calculateDays() {
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) {
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>