父组件:
<div class="calendar">
<Calendar
ref="calendar"
:date="new Date()"
@enter="mouseenter"
@leave="mouseleave"
@changeMonth="changeMonth"
@startTime="queryStartTime"
@endTime="queryEndTime"
style="width:100%;height:350px"
>
<!-- 可使用动态插槽名设置,v-slot:[slotName] -->
<!-- <template v-slot:2021-5-21>
<div style="position:absolute;bottom:-20px">今天</div>
</template> -->
</Calendar>
</div>
日历组件:
<template>
<div class="cal_con">
<div class="cal_header">
<div class="cal_h_left">
<span class="cal_h_time">{{ year }} 年 {{ month }} 月</span>
</div>
<div class="cal_h_right">
<div class="cal_h_btn" @click="preMonth">
<img src="submodule/ccp/assets/images/up_icon_1.png" />
</div>
<div class="cal_h_btn" @click="nextMonth">
<img src="submodule/ccp/assets/images/down_icon_1.png" />
</div>
</div>
</div>
<div class="cal_month">
<div class="cal_m_weeks">
<span v-for="item in weeks" :key="item" class="cal_m_day_cell">{{ item }}</span>
</div>
<div class="cal_m_days">
<div
v-for="(ds, index) in monthData"
:key="index"
class="cal_m_day_line"
>
<div
v-for="item in ds"
:key="item.day"
:style="{
color: getCellColor(item),
background: getCellBackgroundColor(item),
'border-radius': getBorderRadius(item),
}"
@mouseenter="mouseenter(item, $event)"
@mouseleave="mouseleave(item, $event)"
@click="selectDateTime(item)"
:class="[
'cal_m_day_cell',
{
'select-start':
item.date && item.date.getTime() == selectTime.start,
'select-end':
item.date && item.date.getTime() == selectTime.end,
select:
item.date &&
item.date.getTime() > selectTime.start &&
item.date.getTime() < selectTime.end,
},
]"
>
<div class="date">{{ item.day }}</div>
<slot
:name="item.fullYear + '-' + item.month + '-' + item.day"
></slot>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Calendar",
props: {
date: {
type: Date,
default: () => new Date(),
},
},
data() {
return {
now: this.date,
year: 0,
month: 0,
weeks: ["日", "一", "二", "三", "四", "五", "六"],
monthData: [],
currentYear: new Date().getFullYear(),
currentMonth: new Date().getMonth() + 1,
currentDay: new Date().getDate(),
todayTime: "",
selectTime: {
start: "",
end: "",
},
};
},
computed: {},
async mounted() {
this.setYearMonth(this.now);
await this.generateMonth(this.now);
console.log("===monthData====", this.monthData);
},
methods: {
selectDateTime(time) {
if (!this.selectTime.start && !this.selectTime.end) {
this.selectTime.start = time.date.getTime();
} else if (this.selectTime.start && this.selectTime.end) {
this.selectTime.start = time.date.getTime();
this.selectTime.end = "";
} else if (
this.selectTime.start &&
this.selectTime.start < time.date.getTime()
) {
this.selectTime.end = time.date.getTime();
} else if (
this.selectTime.start &&
this.selectTime.start > time.date.getTime()
) {
this.selectTime.start = time.date.getTime();
this.selectTime.end = "";
}
if (this.selectTime.start) {
this.$emit("startTime", this.selectTime.start);
}
if (this.selectTime.end) {
this.$emit("endTime", this.selectTime.end);
}
},
setYearMonth(now) {
this.year = now.getFullYear();
this.month = now.getMonth() + 1;
},
preYear() {
let n = this.now;
let date = new Date(
n.getFullYear() - 1,
n.getMonth(),
n.getDate(),
n.getHours(),
n.getMinutes(),
n.getSeconds(),
n.getMilliseconds()
);
this.setYearMonthInfos(date);
},
preMonth() {
let n = this.now;
let date = new Date(
n.getFullYear(),
n.getMonth() - 1,
n.getDate(),
n.getHours(),
n.getMinutes(),
n.getSeconds(),
n.getMilliseconds()
);
this.setYearMonthInfos(date);
},
nextYear() {
let n = this.now;
let date = new Date(
n.getFullYear() + 1,
n.getMonth(),
n.getDate(),
n.getHours(),
n.getMinutes(),
n.getSeconds(),
n.getMilliseconds()
);
this.setYearMonthInfos(date);
},
nextMonth() {
let n = this.now;
let date = new Date(
n.getFullYear(),
n.getMonth() + 1,
n.getDate(),
n.getHours(),
n.getMinutes(),
n.getSeconds(),
n.getMilliseconds()
);
this.setYearMonthInfos(date);
},
setYearMonthInfos(date) {
this.setYearMonth(date);
this.generateMonth(date);
this.now = date;
this.dateChange();
},
generateMonth(date) {
date.setDate(1);
let weekStart = date.getDay();
let endDate = new Date(date.getFullYear(), date.getMonth() + 1, 0);
let dayEnd = endDate.getDate();
let weeEnd = endDate.getDay();
let milsStart = date.getTime();
let dayMils = 24 * 60 * 60 * 1000;
let milsEnd = endDate.getTime() + dayMils;
let monthDatas = [];
let current;
for (let i = 0; i < weekStart; i++) {
current = new Date(milsStart - (weekStart - i) * dayMils);
monthDatas.push({
type: -1,
date: current,
fullYear: current.getFullYear(),
month: current.getMonth() + 1,
day: current.getDate(),
});
}
for (let i = 0; i < dayEnd; i++) {
current = new Date(milsStart + i * dayMils);
monthDatas.push({
type: 0,
date: current,
fullYear: current.getFullYear(),
month: current.getMonth() + 1,
day: current.getDate(),
});
}
for (let i = 0; i < 6 - weeEnd; i++) {
current = new Date(milsEnd + i * dayMils);
monthDatas.push({
type: 1,
date: current,
fullYear: current.getFullYear(),
month: current.getMonth() + 1,
day: current.getDate(),
});
}
this.monthData = [];
for (let i = 0; i < monthDatas.length; i++) {
let mi = i % 7;
if (mi == 0) {
this.monthData.push([]);
}
this.monthData[Math.floor(i / 7)].push(monthDatas[i]);
}
if (this.monthData.length <= 5) {
milsStart = current.getTime();
let lastLine = [];
for (let i = 1; i <= 7; i++) {
current = new Date(milsStart + i * dayMils);
lastLine.push({
type: 1,
date: current,
fullYear: current.getFullYear(),
month: current.getMonth() + 1,
day: current.getDate(),
});
}
this.monthData.push(lastLine);
}
},
getCellColor(d) {
if (
d.fullYear == this.currentYear &&
d.month == this.currentMonth &&
d.day == this.currentDay
) {
return "#409eff";
}
},
getCellBackgroundColor(d) {
if (
d.fullYear == this.currentYear &&
d.month == this.currentMonth &&
d.day == this.currentDay
) {
return "rgba(220,235,253,0.80)";
}
},
getBorderRadius(d) {
if (
d.fullYear == this.currentYear &&
d.month == this.currentMonth &&
d.day == this.currentDay
) {
return "2px";
}
},
mouseenter(d, event) {
this.$emit("enter", event, d);
},
mouseleave(d, event) {
this.$emit("leave", event, d);
},
dateChange() {
let fullYear = this.now.getFullYear();
let month = this.now.getMonth();
let startDay = new Date(fullYear, month, 1);
let endDay = new Date(fullYear, month + 1, 0, 23, 59, 59);
this.$emit("changeMonth", startDay, endDay);
},
},
};
</script>
<style scoped lang="scss">
.cal_con {
width: 100%;
height: 100%;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
color: #606266;
background: #fff;
border-radius: 4px;
margin: auto;
.cal_header {
height: 24px;
padding: 5px;
font-size: 14px;
line-height: 24px;
display: flex;
justify-content: space-between;
justify-items: center;
.cal_h_time {
display: inline-block;
font-family: MicrosoftYaHei-Bold;
font-size: 18px;
color: #363232;
letter-spacing: 0;
font-weight: 700;
}
.cal_h_time:hover {
color: #409eff;
}
.cal_h_right {
height: 100%;
display: flex;
.cal_h_btn {
height: 100%;
width: 24px;
cursor: pointer;
margin-left: 0.5rem;
img {
width: 1rem;
height: 0.6rem;
}
}
.cal_h_l_icon {
height: 16px;
width: 16px;
margin: auto;
text-align: center;
}
}
}
.cal_month {
font-size: 12px;
text-align: center;
height: calc(100% - 34px);
.cal_m_day_cell {
width: 60%;
height: 60%;
line-height: 100%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.cal_m_day_cell:hover {
color: #409eff;
font-weight: 600;
}
.select-start,
.select-end {
background: #427bf1;
border-radius: 4.93px;
.date {
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: rgba(220, 235, 253, 0.8);
border-radius: 2px;
}
.cal_m_weeks {
height: 16px;
padding: 8px;
display: flex;
justify-content: space-around;
justify-items: center;
border-bottom: 1px solid #e4e7ed;
}
.cal_m_days {
height: calc(100% - 49px);
padding: 8px;
display: flex;
justify-content: space-around;
justify-items: center;
flex-wrap: wrap;
.cal_m_day_line {
width: 100%;
display: flex;
justify-content: space-around;
justify-items: center;
align-items: center;
}
}
}
}
</style>