-.- 废话补多说,直接上代码
<template>
<div class="calendar">
<slot name="header">
<div class="top">
<div class="current">{{ time }}</div>
<div class="box">
<div class="btn" @click="btn('prevMonth')">上个月</div>
<div class="btn" @click="btn('currentMonth')">当前月</div>
<div class="btn" @click="btn('nextMonth')">下个月</div>
</div>
</div>
</slot>
<div class="date">
<div class="item">日</div>
<div class="item">一</div>
<div class="item">二</div>
<div class="item">三</div>
<div class="item">四</div>
<div class="item">五</div>
<div class="item">六</div>
<template v-for="(item,index) in list">
<div class="item" :class="{active:item.type === currentSelectDate,current:currentDate == item.type,grey:item.next||item.prve}" :key="item.type" @click="click(item)">
<slot :index="index" :item="item">{{ item.date }}</slot>
</div>
</template>
</div>
</div>
</template>
<script>
export default {
name: "Calendar",
props: {
value: { type: Date, default: () => new Date() },
},
data() {
return {
list: [],
time: "",
currentSelectDate: "",
currentDate: "",
timeData: {},
};
},
watch: {
value(a) {
if (a) this.time = a;
this.btn(0);
},
timeData(a) {
this.getData(a)
},
},
created() {
this.btn();
},
methods: {
// 点击取当前时间-可以做一些操作
getData(item){
console.log(item);
},
click(item) {
this.timeData = item;
if (this.currentSelectDate === item.type) {
// 取消选中
this.currentSelectDate = "";
// 选中日期
} else this.currentSelectDate = item.type;
// 选中日期是上个月
if (item.next) this.btn("nextMonth");
// 选中日期是下个月
if (item.prve) this.btn("prevMonth");
this.$emit("click", { ...item, selectDate: this.currentSelectDate });
this.$emit("update:select-date", this.currentSelectDate);
},
// 默认当前月
btn(type = "currentMonth") {
// 获取指定时间的年份和月份
let currentYear = new Date(this.time).getFullYear();
let currentMonth = new Date(this.time).getMonth() + 1;
// 点击上个月
if (type === "prevMonth") {
// 指定时间的月份为1月,上一月为上一年的12月
if (currentMonth === 1)
(currentMonth = 13), (currentYear = currentYear - 1);
currentMonth--;
}
// 当前月
if (type === "currentMonth") {
// 获取当前时间的年月日
currentYear = new Date().getFullYear();
currentMonth = new Date().getMonth() + 1;
let currentDate = new Date().getDate();
this.currentDate = `${currentYear}-${
currentMonth > 9 ? currentMonth : "0" + currentMonth
}-${currentDate > 9 ? currentDate : "0" + currentDate}`;
}
// 下个月
if (type === "nextMonth") {
// 指定时间的月份为12月,下一月为下一年的1月
if (currentMonth === 12)
(currentMonth = 0), (currentYear = currentYear + 1);
currentMonth++;
}
// 当前日历展示的年月
this.time = `${currentYear}-${
currentMonth > 9 ? currentMonth : "0" + currentMonth
}`;
let date = `${currentYear}-${
currentMonth > 9 ? currentMonth : "0" + currentMonth
}-01`;
this.init(date);
this.$emit("change-month", { currentYear, currentMonth, type, date });
this.$emit("update:current-date", this.currentDate);
},
// 生成日历日期,因每月开始和结束不是周日和周六,需要取上月月末和下月月初补满每周七天,所以生成的总天数共35-42天。
init(time) {
// 获得指定时间年月,月总天数
let date = new Date(time);
let currentYear = date.getFullYear(); // 年
let currentMonth = date.getMonth() + 1; // 月
let currentMonthDate = new Date(currentYear, currentMonth, 0).getDate(); // 当月总天数
let list = new Array(currentMonthDate).fill().map((_, i) => ({
year: currentYear,
month: currentMonth,
date: i + 1,
}));
// 获取上月,需要处理跨年
let preMonth = currentMonth;
if (preMonth === 1) (preMonth = 13), (currentYear = currentYear - 1);
let beforeMonthDate = new Date(currentYear, preMonth - 1, 0).getDate(); // 上月总天数
let beforeDate = new Date(
`${currentYear}-${preMonth - 1}-${beforeMonthDate}`
).getDay();
for (let i = 0; i <= beforeDate; i++)
list.unshift({
prve: true,
year: currentYear,
month: preMonth - 1,
date: beforeMonthDate - i,
});
// 获取下月,需要处理跨年
let nextMonth = currentMonth;
if (nextMonth === 12) (nextMonth = 0), (currentYear = currentYear + 1);
let afterDate = 7 - ((currentMonthDate + beforeDate) % 7); // 下月月头
for (let i = 1; i < afterDate; i++)
list.push({
next: true,
year: currentYear,
month: nextMonth + 1,
date: i,
});
this.list = list.map((e, i) => {
let { year, month, date } = e;
return {
...e,
week: Math.floor(i % 7),
type: `${year}-${month > 9 ? month : "0" + month}-${
date > 9 ? date : "0" + date
}`,
};
});
},
},
};
</script>
<style lang='less' scoped>
.calendar {
min-height: 300px;
.top {
display: flex;
justify-content: space-between;
align-items: center;
height: 40px;
margin: 0 0 20px 0;
padding: 0 10px;
}
.box {
display: flex;
justify-content: space-evenly;
width: 160px;
border: 1px solid #eee;
border-radius: 5px;
margin: 5px;
color: #666;
.btn {
padding: 3px 5px;
cursor: pointer;
transition: all 0.1s;
&:nth-child(2) {
border: 1px solid #eee;
border-top: none;
border-bottom: none;
}
&:hover {
color: #333;
background: #eee;
transition: all 0.1s;
}
}
}
.item {
flex: 0 0 13%;
height: 35px;
margin: 2px;
min-width: 20px;
text-align: center;
color: #444;
padding-top: 5px;
box-sizing: border-box;
border-radius: 5px;
transition: all 0.2s;
&.grey {
color: #999;
}
&:hover {
transition: all 0.2s;
cursor: pointer;
background: #5164e7a0;
color: #fff;
}
&.current {
transition: all 0.2s;
background: #5164e7a0;
color: #fff;
}
&.active {
transition: all 0.2s;
background: #5164e7;
color: #fff;
}
}
.date {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
}
}
</style>