最近写了一个组件可以切换年月日周的日历日程组件,用于展示在移动端上,提供日程服务,demo示例如下
用到插件: lunar-calendar 将当前日期转化为农历日期,所以需要先安装此插件哦
关键代码,主要是初始化日历展示数据,详情可以看注释中表述的
// 获取一周的阳历与阴历
getWeekList () {
// 获取当前日期
const currentDate = this.nowDate;
// 获取当前周的第一天(周日)
const firstDayOfWeek = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate() - currentDate.getDay());
// 创建一个数组来存储结果
const weekDates = [];
// 循环获取一周的日期和农历日期
for (let i = 0; i < 7; i++) {
const date = new Date(firstDayOfWeek.getFullYear(), firstDayOfWeek.getMonth(), firstDayOfWeek.getDate() + i);
const lunarDate = LunarCalendar.solarToLunar(date.getFullYear(), date.getMonth() + 1, date.getDate());
// 组合日期和农历日期,并添加到结果数组中
weekDates.push({
fullDate: `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`,
unDate: date, // 未格式化的日期
date: date.getDate(), // 格式化公历日期
lunarDate: lunarDate.lunarDayName // 格式化农历日期
});
}
this.thisWeek = weekDates
},
getMonthList() {
// 获取当前日期
const currentDate = new Date(this.nowDate);
// 将日期设置为当月的第一天
currentDate.setDate(1);
// 获取当前月份
const currentMonth = currentDate.getMonth();
// 创建一个数组来存储结果
const monthDates = [];
// 补充前面的周日和周一数据
const firstDayOfWeek = currentDate.getDay(); // 当月第一天是星期几
const daysBefore = (firstDayOfWeek + 7) % 7; // 前面需要补充的天数
let startDate = ''
if (currentDate.getMonth() == 0 && daysBefore > 0) {
startDate = new Date(currentDate.getFullYear() - 1, 12, 1 - daysBefore);
} else{
startDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1 - daysBefore);
}
for (let i = 0; i < daysBefore; i++) {
const date = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate() + i);
const lunarDate = LunarCalendar.solarToLunar(date.getFullYear(), date.getMonth() + 1, date.getDate());
monthDates.push({
fullDate: `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`,
unDate: date, // 未格式化的日期
date: date.getDate(), // 格式化公历日期
lunarDate: lunarDate.lunarDayName // 格式化农历日期
});
}
// 循环获取当月的日期和农历日期
while (currentDate.getMonth() === currentMonth) {
const date = new Date(currentDate);
const lunarDate = LunarCalendar.solarToLunar(date.getFullYear(), date.getMonth() + 1, date.getDate());
// 组合日期和农历日期,并添加到结果数组中
monthDates.push({
fullDate: `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`,
unDate: date, // 未格式化的日期
date: date.getDate(), // 格式化公历日期
lunarDate: lunarDate.lunarDayName // 格式化农历日期
});
currentDate.setDate(currentDate.getDate() + 1);
}
this.thisMonth = monthDates
},
完整代码如下,一共380行,为了简洁明了,很多地方可以复用的代码没有进行二次封装,有需要的可以自行封装一下!
<template>
<section class="calendar-card">
<div class="calendar-title">
<span class="date-b" @click="handlePrevious(false)">
<van-icon name="arrow-left" />
</span>
<span class="now-date" @click="showChooseMonth = true">{{ nowDate | nowDateFilter }}</span>
<span class="date-b" @click="handleNext(false)">
<van-icon name="arrow" />
</span>
</div>
<div class="calendar-week">
<div class="calendar__weekdays">
<span class="calendar__weekday" v-for="i in weekdays" :key="i">
{{ i }}
</span>
</div>
<div v-show="!showUp" class="calendar__weekdays">
<div
class="calendar__week"
v-for="date in thisWeek"
:class="{'active-day': selectDay == date.fullDate}"
:key="date.fullDate"
@click="selectDate(1, date)"
>
<span class="solar">{{ date.date }}</span>
<span class="lunar">{{ date.lunarDate }}</span>
<!-- 展示下方小圆点 此处可以处理当日有无日程 可以接对应数据来渲染 -->
<i></i>
</div>
</div>
<div v-show="showUp" class="calendar__weekdays calendar__months">
<div
class="calendar__week"
v-for="date in thisMonth"
:class="{'active-day': selectDay == date.fullDate}"
:key="date.fullDate"
@click="selectDate(2, date)"
>
<span class="solar">{{ date.date }}</span>
<span class="lunar">{{ date.lunarDate }}</span>
<!-- 展示下方小圆点 此处可以处理当日有无日程 可以接对应数据来渲染 -->
<i></i>
</div>
</div>
<van-divider>
<img class="down-img" :class="{'up-img' : showUp}" @click="handleShowMonth" src="@/assets/prev-b.png" alt="">
</van-divider>
</div>
<van-popup
v-model="showChooseMonth"
closeable
position="bottom"
>
<section class="choose-month">
<div class="calendar-title">
<span class="date-b" @click="handlePrevious(true)">
<img src="@/assets/prev-b.png" alt="">
</span>
<span class="now-date">{{ nowYear }}年</span>
<span class="date-b next-b" @click="handleNext(true)">
<img src="@/assets/prev-b.png" alt="">
</span>
</div>
<van-divider />
<!-- 时间代理处理切换月份 -->
<ul class="month-box" @click="chooseMonth">
<li
v-for="(month, index) in monthList"
:data-id="index"
:class="{'active-month': changeMonthIndex(nowDate) == nowYear+index}"
:key="index"
>
{{ month }}
</li>
</ul>
</section>
</van-popup>
</section>
</template>
<script>
import LunarCalendar from 'lunar-calendar';
export default {
name: 'CalendarCard',
data() {
return {
nowDate: new Date(),
nowYear: new Date().getFullYear(),
showChooseMonth: false,
showUp: false,
selectDay: '',
weekdays: ['日', '一', '二', '三','四', '五','六'],
monthList: ['一月', '二月', '三月','四月', '五月','六月','七月', '八月', '九月','十月', '十一月','十二月'],
thisWeek: [],
thisMonth: [],
};
},
filters: {
nowDateFilter (date) {
const year = date.getFullYear();
const month = date.getMonth() + 1; // 月份从0开始,因此需要加1
return `${year}年${month}月`;
}
},
watch: {
nowDate: {
handler(newd) {
if(newd) {
this.getWeekList()
if(this.showUp) {
this.getMonthList()
}
}
}
}
},
mounted() {
this.selectDay = `${this.nowDate.getFullYear()}-${this.nowDate.getMonth()}-${this.nowDate.getDate()}`,
this.getWeekList()
},
methods: {
changeMonthIndex(date) {
return date.getFullYear()+date.getMonth()
},
// 获取一周的阳历与阴历
getWeekList () {
// 获取当前日期
const currentDate = this.nowDate;
// 获取当前周的第一天(周日)
const firstDayOfWeek = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate() - currentDate.getDay());
// 创建一个数组来存储结果
const weekDates = [];
// 循环获取一周的日期和农历日期
for (let i = 0; i < 7; i++) {
const date = new Date(firstDayOfWeek.getFullYear(), firstDayOfWeek.getMonth(), firstDayOfWeek.getDate() + i);
const lunarDate = LunarCalendar.solarToLunar(date.getFullYear(), date.getMonth() + 1, date.getDate());
// 组合日期和农历日期,并添加到结果数组中
weekDates.push({
fullDate: `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`,
unDate: date, // 未格式化的日期
date: date.getDate(), // 格式化公历日期
lunarDate: lunarDate.lunarDayName // 格式化农历日期
});
}
this.thisWeek = weekDates
},
getMonthList() {
// 获取当前日期
const currentDate = new Date(this.nowDate);
// 将日期设置为当月的第一天
currentDate.setDate(1);
// 获取当前月份
const currentMonth = currentDate.getMonth();
// 创建一个数组来存储结果
const monthDates = [];
// 补充前面的周日和周一数据
const firstDayOfWeek = currentDate.getDay(); // 当月第一天是星期几
const daysBefore = (firstDayOfWeek + 7) % 7; // 前面需要补充的天数
let startDate = ''
if (currentDate.getMonth() == 0 && daysBefore > 0) {
startDate = new Date(currentDate.getFullYear() - 1, 12, 1 - daysBefore);
} else{
startDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1 - daysBefore);
}
for (let i = 0; i < daysBefore; i++) {
const date = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate() + i);
const lunarDate = LunarCalendar.solarToLunar(date.getFullYear(), date.getMonth() + 1, date.getDate());
monthDates.push({
fullDate: `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`,
unDate: date, // 未格式化的日期
date: date.getDate(), // 格式化公历日期
lunarDate: lunarDate.lunarDayName // 格式化农历日期
});
}
// 循环获取当月的日期和农历日期
while (currentDate.getMonth() === currentMonth) {
const date = new Date(currentDate);
const lunarDate = LunarCalendar.solarToLunar(date.getFullYear(), date.getMonth() + 1, date.getDate());
// 组合日期和农历日期,并添加到结果数组中
monthDates.push({
fullDate: `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`,
unDate: date, // 未格式化的日期
date: date.getDate(), // 格式化公历日期
lunarDate: lunarDate.lunarDayName // 格式化农历日期
});
currentDate.setDate(currentDate.getDate() + 1);
}
this.thisMonth = monthDates
},
selectDate (type, date) {
// 点击月份的某一天时候 将周置为当前点击的一周
if (type === 2) {
this.nowDate = date.unDate
this.showUp = false
}
this.selectDay = date.fullDate
},
// 上一周 或 上一年
handlePrevious(bool) {
if(bool) {
this.nowYear -= 1
return
}
let lastDate
// 月份切换
if (this.showUp) {
// 选择下一月的逻辑
lastDate = new Date(this.nowDate);
lastDate.setMonth(lastDate.getMonth() - 1);
} else {
// 选择下一周的逻辑
lastDate = new Date(this.nowDate);
lastDate.setDate(lastDate.getDate() - 7); // 减去7天
}
this.nowDate = lastDate;
},
// 下一周 或 下一年
handleNext(bool) {
if(bool) {
this.nowYear += 1
return
}
let nextDate = new Date(this.nowDate);
// 月份切换
if (this.showUp) {
// 选择下一月的逻辑
nextDate.setMonth(nextDate.getMonth() + 1);
} else {
// 选择下一周的逻辑
nextDate.setDate(nextDate.getDate() + 7); // 加上7天
}
this.nowDate = nextDate;
},
handleShowMonth() {
this.showUp = !this.showUp
if(this.showUp) {
this.getMonthList()
}
},
// 选择对应的月份
chooseMonth (event) {
const year = this.nowYear
const month = event.target.getAttribute('data-id')
let nowDate = this.nowDate
// 创建一个新的日期对象,以便 Vue 能够检测到属性变化, 否则不会触发watch的监听
const newDate = new Date(nowDate);
// 将传入的年份和月份设置给 nowDate
newDate.setFullYear(year);
newDate.setMonth(month);
this.$set(this, 'nowDate', newDate)
this.showChooseMonth = false
}
},
};
</script>
<style lang="scss" scoped>
.calendar-card {
.calendar-title {
width: 146px;
margin: auto;
display: flex;
align-items: center;
justify-content: space-between;
.date-b {
width: 17px;
height: 17px;
background: #ffdada;
border-radius: 2px;
color: #ff3636;
text-align: center;
}
.now-date {
min-width: 80px;
height: 23px;
font-family: PingFangSC-Medium;
font-size: 16px;
color: #333333;
text-align: center;
font-weight: 500;
}
}
.calendar-week {
.calendar__weekdays {
display: flex;
flex-wrap: wrap;
.calendar__weekday {
flex: 1;
font-size: 12px;
line-height: 30px;
text-align: center;
font-family: PingFangSC-Regular;
font-size: 14px;
color: #333333;
text-align: center;
font-weight: 400;
}
.calendar__week {
width: calc(100% / 7);
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
padding: 4px 0;
.solar {
font-family: PingFangSC-Regular;
font-size: 14px;
color: #333333;
text-align: center;
font-weight: 400;
}
.lunar {
font-family: PingFangSC-Regular;
font-size: 12px;
color: #666666;
text-align: center;
font-weight: 400;
}
i {
display: inline-block;
width: 6px;
height: 6px;
border-radius: 3px;
background-color: #ff3636;
}
}
.active-day {
height: 100%;
background-image: linear-gradient(180deg, #f31520 4%, #fc9297 100%);
box-shadow: 0px 4px 11px 0px rgba(243, 31, 42, 0.3);
border-radius: 10px;
.solar,
.lunar {
color: #ffffff;
}
i {
background-color: #ffffff;
}
}
}
.down-img {
transform: rotate(-90deg);
transition: transform 0.5s ease-in-out;
}
.up-img {
transform: rotate(90deg);
transition: transform 0.5s ease-in-out;
}
}
.choose-month {
.calendar-title {
height: 40px;
}
.date-b {
background: unset;
}
.next-b {
img {
transform: rotate(180deg);
}
}
.month-box {
display: flex;
flex-wrap: wrap;
li {
width: 25%;
height: 40px;
text-align: center;
}
.active-month {
color: #e12617;
}
}
}
}
</style>