最近做的一个需求中涉及到一个日历组件,范围选择日期,效果如下:
一开始想找插件来用,后来发现没有相匹配的样式,如果在插件上改动十分麻烦,所以就手动写了一个,主要用到Date对象的api.
首先先介绍一下其中用到的Date api:
let dateObj = new Date() //返回时间对象,不传参数,返回的是当前时间点的时间对象,传递一个事件戳,返回的是传递时间戳的事件对象
dateObj.setDate(1) //将时间对象的日期指定到当前月份的1号
dateObj.setMonth(1) //将时间对象的月份指定到当年的2月份(0-11表示1-12月)
dateObj.setHours(0,0,0,0) //将事件对象指定到0点0分0秒
dateObj.setMonth(12) //当指定到12时,年份自动加1,时间对象会指定到下一年的1月份,组件中就是用这个api来推算之后的月份的.在推算月份之前要调用setDate(1),这样做的目的是因为每个月都有1号,如果不设置setDate,而是取当前的日期,如果当前日期是31号,而下个月份并没有31号,setMonth时就会出现问题.这样每次setMonth,然后调用getTime,获取每个月1号0时0分0秒的事件戳,再通过这个时间戳,获取年,月,日
// 其中还需要计算每个月的天数,方法是先获取月的最后一天的日期,然后就能得知这个月的总天数,方法如下.
let dateObj = new Date()
dateObj.setDate(1) //将日期指定到当前月份的1号,这样做是为了防止当前天是31号,下个月如果没有31号,setMonth会出错
dateObj.setMonth(dateObj.getMonth() + 1) //将月份指定到下一个月的1号
dateObj.setDate(0) //再将日期指定为0,表示是将日期指定为了上一个月的最后一天
dateObj.getDate() //返回最后一天的日期,即为月份的总天数
然后计算出所有的日期,再将日期按着每7天分成一组进行遍历就可以对应出星期.
或者将日期按着星期一-星期日进行分组,进行遍历,下面的代码采用的是第一种方式.
以上就是用到的计算日期的主要api,下面是完整的代码:
<template>
<div class="calendar">
<div>
<div class="header">选择日期</div>
<ul class="week">
<li class="item active">日</li>
<li class="item">一</li>
<li class="item">二</li>
<li class="item">三</li>
<li class="item">四</li>
<li class="item">五</li>
<li class="item active">六</li>
</ul>
</div>
<div class="calendar-days" ref="calendarLayout">
<div ref="calendarCont">
<div v-for="(calendarItem, index) in calendarList" :key="index">
<div class="year-month">
{{ calendarItem.year }}年{{
calendarItem.month + 1 >= 10
? calendarItem.month + 1
: `0${calendarItem.month + 1}`
}}月
</div>
<div
class="days-list"
v-for="(item, index) in calendarItem.daysList"
:key="index"
>
<div
class="item"
v-for="(day, index) in item"
:key="index"
@click="select(day.time)"
>
<div
:class="[
'item-cont',
{
today: todayTime === day.time,
'before-day': day.time < todayTime,
'select-start': day.time && day.time === selectTime.start,
'select-end': day.time && day.time === selectTime.end,
select:
day.time &&
day.time > selectTime.start &&
day.time < selectTime.end
}
]"
>
<div class="left-side"></div>
<div class="date">{{ day.date }}</div>
<div class="right-side"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
calendarList: [
// {
// year: 2020,
// month: 1,
// daysList: [
// [{
// date: "08",
// time: ""
// }]
// ]
// }
],
todayTime: "",
selectTime: {
start: "",
end: ""
}
};
},
methods: {
select(time) {
if (time < this.todayTime) {
return false;
}
if (!this.selectTime.start && !this.selectTime.end) {
this.selectTime.start = time;
} else if (this.selectTime.start && this.selectTime.end) {
this.selectTime.start = time;
this.selectTime.end = "";
} else if (this.selectTime.start && this.selectTime.start < time) {
this.selectTime.end = time;
} else if (this.selectTime.start && this.selectTime.start > time) {
this.selectTime.start = time;
this.selectTime.end = "";
}
},
handScroll() {
this.$refs["calendarLayout"].addEventListener("scroll", () => {
const bottom = this.$refs["calendarCont"].getBoundingClientRect()
.bottom;
const clientH = document.documentElement.clientHeight;
if (Math.ceil(bottom - clientH) <= 100) {
let from;
if (this.calendarList.length) {
// 获取开始计算的月份
let monthTime = this.calendarList[this.calendarList.length - 1]
.daysList[1][0].time;
let dateObj = new Date(monthTime);
dateObj.setMonth(new Date(monthTime).getMonth() + 1);
dateObj.setDate(1);
from = dateObj.getTime();
}
// 再展示一年
this.calendarList = this.calendarList.concat(
this.getSomeDate(from, 12)
);
}
});
},
getSomeDate(from, howManyMonth = 12) {
let returnData = [];
if (!from) {
let dateObj = new Date();
// 将时间点设置成每个月的1号
dateObj.setDate(1);
from = dateObj.getTime();
}
let monthTimeList = [];
let dataObj = new Date(from);
// 将时间点设置成每个月的1号
dataObj.setDate(1);
monthTimeList.push(dataObj.getTime());
for (let i = 1; i < howManyMonth; i++) {
monthTimeList.push(dataObj.setMonth(dataObj.getMonth() + 1));
}
monthTimeList.forEach(item => {
returnData.push({
year: new Date(item).getFullYear(),
month: new Date(item).getMonth(),
daysList: this.getDates(item)
});
});
return returnData;
},
getDates(monthTime) {
let daysArry = [];
let dateObj = new Date(monthTime);
let firstDayWeek = dateObj.getDay();
// 获取这个月的最后一天
dateObj.setDate(1);
dateObj.setMonth(dateObj.getMonth() + 1);
dateObj.setDate(0);
let lastDayWeek = dateObj.getDay();
// 获取这个月的最后一天的日期,也就是这个月的总天数
let days = dateObj.getDate();
// 获取这个月份每一天的日期和0点的时间戳
for (let date = 1; date <= days; date++) {
let dateObj = new Date(monthTime);
dateObj.setDate(date);
dateObj.setHours(0, 0, 0, 0);
daysArry.push({ date: date, time: dateObj.getTime() });
}
for (let i = 0; i <= firstDayWeek - 1; i++) {
daysArry.unshift({ date: "", time: "" });
}
for (let i = lastDayWeek + 1; i <= 6; i++) {
daysArry.push({ date: "", time: "" });
}
// 按周分组
let group = Math.ceil(daysArry.length / 7);
let daysList = [];
for (let i = 0; i < group; i++) {
//分组规律
// 0 0,7
// 1 7,14
// 2 14,20
// 3 21,27
// 4 28,34
daysList.push(daysArry.slice(i * 7, (i + 1) * 7));
}
return daysList;
},
// 获取今天的日期和时间戳
getTodayTime() {
let dateObj = new Date();
dateObj.setHours(0, 0, 0, 0);
this.todayTime = dateObj.getTime();
}
},
mounted() {
this.calendarList = this.getSomeDate();
this.getTodayTime();
this.handScroll();
}
};
</script>
<style lang="scss" scoped>
.calendar {
display: flex;
flex-direction: column;
height: 100vh;
.calendar-days {
height: 500px;
flex: 1 1 auto;
overflow: auto;
}
.header {
font-size: 36px;
font-weight: 500;
text-align: center;
line-height: 36px;
padding: 48px 0;
color: #333;
}
.week {
color: #a6a6a6;
.active {
color: #ff3300;
}
}
.days-list {
height: 104px;
line-height: 104px;
}
.week,
.days-list {
display: flex;
justify-content: space-between;
.item {
width: 100px;
height: 104px;
flex-grow: 1;
text-align: center;
.item-cont {
font-size: 28px;
font-weight: 500;
height: 100%;
position: relative;
.left-side,
.right-side {
width: 50%;
height: 100%;
}
.left-side {
float: left;
}
.right-side {
float: right;
}
.date {
position: absolute;
width: 72px;
height: 100%;
top: 0;
left: 50%;
margin-left: -36px;
// background: rgba(255, 51, 0, 1);
border-radius: 12px;
// opacity: 0.06;
}
}
.today {
.date {
&::before {
position: absolute;
content: "今天";
top: -30px;
left: 8px;
// left: 30px;
}
}
}
.before-day {
color: #a6a6a6;
}
.select-start,
.select-end {
.date {
background: rgba(255, 92, 52, 1);
color: #fff;
&::before {
position: absolute;
top: -30px;
left: 8px;
content: "开始";
}
}
}
.select-end {
.date {
&::before {
content: "结束";
}
}
}
.select-start {
.right-side {
background: #fff3f1;
}
}
.select-end {
.left-side {
background: #fff3f1;
}
}
.select {
background: #fff3f1;
}
}
}
.calendar-days {
.year-month {
font-weight: 500;
font-size: 32px;
color: #333;
text-align: center;
padding: 32px 0 16px;
line-height: 32px;
}
}
}
</style>