实现入下图所示需求:
组件的具体代码:
<template>
<div class="calendar">
<div class="calendar-box">
<div class="title-area">
<div class="title-div">日期选择</div>
<i @click="cancelDate" class="iconfont icon-guanbi title-close"></i>
</div>
<div class="calendar-body">
<div class="calendar-body-text">
<div style="cursor: pointer" @click="month--">《 </div>
<div class="text-title">{{timeValue}}</div>
<div style="cursor: pointer" @click="month++"> 》</div>
</div>
<table cellspacing="0" cellpadding="0">
<thead>
<tr>
<th v-for="(h, i) in teheadList" :key="i">{{h}}</th>
</tr>
</thead>
<tbody>
<tr v-for="(b, i) in tbodyList" :key="i">
<td @click="getCurrentDay(o)" v-for="(o, index) in b" :key="index"
:class="[checkStart == (`${o.year}/${o.month}/${o.name}`)||checkEnd == (`${o.year}/${o.month}/${o.name}`) ? 'activeDay' : '',
new Date(checkStart) < (new Date(`${o.year}/${o.month}/${o.name}`))&&new Date(checkEnd) > (new Date(`${o.year}/${o.month}/${o.name}`)) ? 'durationTime' : '',
{'firstActive':checkStart == (`${o.year}/${o.month}/${o.name}`)&&checkEnd!=''},{'lastActive':checkEnd == (`${o.year}/${o.month}/${o.name}`)}]">
<span :class="o.class" v-if="o.name == 'default'">{{o.name}}</span>
<span :class="o.class + (day == o.name ? ' currentMonth' : '')" v-else>{{o.name}}</span>
<div v-show="checkStart == (`${o.year}/${o.month}/${o.name}`)" class="tip start">开始</div>
<div v-show="checkEnd == (`${o.year}/${o.month}/${o.name}`)" class="tip end">结束</div>
</td>
</tr>
</tbody>
</table>
</div>
<footer class="calendar-foot">
<div class="surebtn" @click="sureDate">确定</div>
</footer>
</div>
</div>
</template>
<script>
export default {
name: 'calendar',
data: () => {
return {
timeValue: '',
teheadList: ['日', '一', '二', '三', '四', '五', '六'],
year: '',
month: 0,
day: '',
tbodyList: [],
currentDay: '',
checkStart: '', //开始日期
checkEnd: '' //结束日期
};
},
props: [],
mounted() {
this.getInitTime();
},
watch: {
month() {
if (this.month < 1) {
this.month = 12;
this.year--;
} else if (this.month > 12) {
this.month = 1;
this.year++;
}
this.tbodyList = [];
this.timeValue = this.year + '年' + this.month + '月';
this.handleAetCalendar();
}
},
methods: {
//确定时间
sureDate() {
if (this.checkStart == '' || this.checkEnd == '') {
this.Tool.tip('请选择开始时间和结束时间!');
return;
}
let para = {
type: 'sure',
start: this.checkStart,
end: this.checkEnd
};
this.$emit('sureDate', para);
},
//关闭弹窗
cancelDate() {
let para = {
type: 'cancel',
start: this.checkStart,
end: this.checkEnd
};
this.$emit('sureDate', para);
},
//处理当前点击时间
getCurrentDay(item) {
let newVal = `${item.year}/${item.month}/${item.name}`;
if (!this.checkStart || this.checkStart == '') {
//开始时间赋值
this.checkStart = newVal;
} else {
//已选定开始日期
if (!this.checkEnd || this.checkEnd == '') {
//未选择结束
let start = new Date(`${this.checkStart}`);
let current = new Date(`${newVal}`);
if (start >= current) {
//开始时间晚于结束时间
this.checkStart = newVal;
} else {
this.checkEnd = newVal;
}
} else {
//已选择结束时间
this.checkStart = newVal;
this.checkEnd = '';
}
}
},
//初始化时间
getInitTime() {
let date = new Date();
this.year = date.getFullYear(); // 获取当年
this.month = date.getMonth() + 1; // 获取本月
this.day = date.getDate(); // 获取当天
this.timeValue = date.getFullYear() + '年' + (date.getMonth() + 1) + '月';
this.handleAetCalendar();
},
// 生成档期数据
handleAetCalendar() {
this.tbodyList = [];
let days = new Date(this.year, this.month, 0).getDate(); // 当月总天数
let week = new Date(this.year, this.month - 1, 1).getDay(); // 当月有几周
let last_month = new Date(this.year, this.month + 1, 0).getDate(); // 当月的上一个月的最后一天
this.tbodyList[0] = [];
for (let i = 0; i < Math.ceil((days + week) / 7) * 7; i++) {
let nub = Math.floor(i / 7);
if (i < week) {
this.tbodyList[nub].push({
class: 'default',
name: last_month + i - week + 1,
month: this.month == 0 ? 12 : this.month - 1,
year: this.month == 1 ? this.year - 1 : this.year
});
} else {
if (!this.tbodyList[nub]) {
this.tbodyList[nub] = [];
}
let day = i - week + 1;
let className = 'actives';
let month = this.month;
let year = this.year;
if (day > days) {
day -= days;
className = 'default';
month = this.month + 1 > 12 ? 1 : this.month + 1;
year = this.month + 1 > 12 ? this.year + 1 : this.year;
}
this.tbodyList[nub].push({
class: className,
name: day,
month: month,
year: year
});
}
}
let arr = this.tbodyList[Math.floor((week + days) / 7)];
if (arr && arr.length !== 7) {
this.tbodyList[Math.floor((week + days) / 7)] = arr.concat(
new Array(7 - arr.length).fill('')
);
}
}
}
};
</script>
<style lang="scss" scoped>
.calendar {
width: 100%;
background: #dcdcdc;
border-radius: 18px 18px 0px 0px;
.calendar-box {
width: 100%;
background: #fff;
height: 100%;
position: relative;
border-radius: 18px 18px 0px 0px;
.title-area {
position: relative;
.title-div {
width: 100%;
text-align: center;
font-size: 0.75rem;
padding: 0.6rem;
}
.title-close {
position: absolute;
right: 0.75rem;
top: 0.75rem;
font-size: 0.65rem;
}
}
.calendar-body {
width: 100%;
margin: 0 auto;
padding: 0 0.7rem 0.7rem;
font-size: 0.6rem;
.calendar-body-text {
width: 100%;
height: 30px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
.text-title {
margin: 0 20px;
font-size: 0.75rem;
}
}
}
table {
font-size: 0;
}
table tr th {
color: #666666;
}
table tr th,
table tr td {
width: 3.5rem;
height: 50px;
text-align: center;
font-size: 0.6rem;
}
table td {
position: relative;
&.activeDay {
background: #3a72dc;
span {
color: #fff;
}
}
&.durationTime {
background: #ecf2fb;
span {
color: #3a72dc;
}
}
.tip {
color: #fff;
}
.currentMonth {
color: #00bfff;
}
&.firstActive {
border-radius: 5px 0px 0px 5px;
}
&.lastActive {
border-radius: 0px 5px 5px 0px;
}
}
table td span {
color: #343434;
cursor: pointer;
}
table td span.default {
color: #9a9a9a;
}
table td .tip {
position: absolute;
left: 0.7rem;
}
.calendar-foot {
text-align: center;
padding-bottom: 0.7rem;
.surebtn {
width: 7rem;
display: inline-block;
padding: 0.3rem;
background: #3a72dc;
color: #fff;
font-size: 0.65rem;
border-radius: 15px;
}
}
}
}
</style>
调用该组件的页面:
<mt-popup class="datePopup" v-model="datePopupVisible" position="bottom">
<CalendarCustom @sureDate="CheckedDate"></CalendarCustom>
</mt-popup>
点击确定后的传值:
CheckedDate(dateInfo) {
this.datePopupVisible = false;
if (dateInfo.type === 'cancel') {
return;
}
this.assetDate = dateInfo.start + '到' + dateInfo.end;
},
.datePopup {
width: 100%;
border-radius: 18px 18px 0px 0px;
}
注:部分动画及提示框基于mintui,可修改、替换