在 Vue 2 中使用 moment
库绘制一个带有上个月和下个月日期的日历,可以通过以下步骤实现。这个日历将显示当前月份的天数,以及前一个月和下一个月的部分日期(通常为了让日历对齐为6行,每行7天)。
主要步骤:
- 生成当前月份的所有天数。
- 计算上个月的剩余天数(即填充当前月份开始前的日期)。
- 计算下个月的天数(即填充当前月份结束后的日期,直到日历的最后一天)
实现效果
代码实现
- 安装
moment
库
首先,确保已经安装moment
库:
npm install moment
- 在 Vue 组件中绘制日历
<template>
<div class="calendar">
<div class="calendar-top flex">
<div class="calendar-top-same">
<div>{{ month.format("YYYY年MM月") }}</div>
</div>
<el-button @click="prevMonth" size="mini">上个月</el-button>
<el-button @click="currentMonth" size="mini">今天</el-button>
<el-button @click="nextMonth" size="mini">下个月</el-button>
</div>
<div class="calendar-body">
<div v-for="item in weekdays" :key="item" class="calendar-body-week">
{{ item }}
</div>
<div
v-for="(day, index) in days"
:key="index"
class="calendar-body-day"
:class="{
dis: day.isPrevMonth||day.isNextMonth,
today:today===day.date.format('YYYY-MM-DD')
}"
@click="handleClickDay(day)"
>
{{ day.date.date() }}
</div>
</div>
</div>
</template>
<script>
import moment from "moment";
export default {
name: "calendar",
props: {
// 日期
date: {
type: Date,
default() {
return new Date();
},
},
},
data() {
return {
today: '', // 选中的日期
month: moment(), // 当前月份
weekdays: ["日", "一", "二", "三", "四", "五", "六"], // 星期
days: [], // 存储当前日历显示的天数
};
},
mounted() {
this.today = moment(this.date).format("YYYY-MM-DD");
this.generateCalendar();
},
methods: {
// 生成日历
generateCalendar() {
this.days = [];
// 获取当前月份的开始和结束日期
const startOfMonth = this.month.clone().startOf("month");
const endOfMonth = this.month.clone().endOf("month");
// 获取上个月需要显示的天数
const startWeekday = startOfMonth.day();
const prevMonthDays = [];
if (startWeekday > 0) {
const prevMonthEnd = startOfMonth
.clone()
.subtract(1, "month")
.endOf("month");
for (let i = startWeekday - 1; i >= 0; i--) {
prevMonthDays.push({
date: prevMonthEnd.clone().subtract(i, "days"),
isPrevMonth: true,
});
}
}
// 获取当前月份的所有天数
const currentMonthDays = [];
for (let i = 0; i < endOfMonth.date(); i++) {
currentMonthDays.push({
date: startOfMonth.clone().add(i, "days"),
isPrevMonth: false,
isNextMonth: false,
// 是否为今天
isToday: startOfMonth.clone().add(i, "days").isSame(moment(), "day"),
// 是否为今天之后的日期 不含今天
isAfterToday: startOfMonth.clone().add(i, "days").isAfter(
moment(),
"day",
)
});
}
// 获取下个月需要显示的天数
const endWeekday = endOfMonth.day();
const nextMonthDays = [];
if (endWeekday < 6) {
for (let i = 1; i <= 6 - endWeekday; i++) {
nextMonthDays.push({
date: endOfMonth.clone().add(i, "days"),
isNextMonth: true,
isAfterToday: endOfMonth.clone().add(i, "days").isAfter(
moment(),
"day",
)
});
}
}
// 合并上个月、当前月份、下个月的天数
this.days = [...prevMonthDays, ...currentMonthDays, ...nextMonthDays];
},
// 切换到上个月
prevMonth() {
this.month = this.month.clone().subtract(1, "month");
this.generateCalendar();
},
// 切换到当前月份
currentMonth() {
this.today = moment().format("YYYY-MM-DD");
this.month = moment();
this.generateCalendar();
this.$emit("change",this.today)
},
// 切换到下个月
nextMonth() {
this.month = this.month.clone().add(1, "month");
this.generateCalendar();
},
// 今天之前触发
handleClickDay(day){
if(day.isAfterToday) return;
this.today = day.date.format("YYYY-MM-DD");
this.$emit("change",this.today)
}
},
};
</script>
<style lang="scss" scoped>
.calendar {
user-select: none;
&-top {
justify-content: space-between;
border: 1px solid #ccc;
align-items: center;
font-size: 14px;
&-same {
flex: 1;
text-align: center;
}
}
&-body {
font-size: 14px;
padding: 5px;
display: grid;
grid-template-columns: repeat(7, 1fr);
&-week {
background: #f5f7fa;
cursor: progress;
}
> div {
margin: 2px;
text-align: center;
border: #ccc solid 1px;
padding: 5px;
}
&-day {
cursor: pointer;
.km {
font-size: 12px;
line-height: 1;
color: #333;
}
&.dis {
color: #ccc;
// cursor: not-allowed;
}
&.today {
background: #66b1ff;
color: #fff;
}
}
}
}
</style>
解释:
today
:当前选中的日期,默认今天month
:使用moment
来追踪当前月份。generateCalendar
:这个方法用来生成日历,包括上个月的部分日期、当前月份的日期和下个月的部分日期。prevMonth
和nextMonth
: 点击按钮来切换月份。currentMonth
:这个方法是用来跳回到今天。