用ul和li模拟一个可以点击的日历,因为需求是如果某天有事件则需要自动标红,效果图如下
注意!!!这处因为需要和时间选择器联动 ,所以把这个页面封装成一个组件,代码
<template>
<div class="dates">
<div id="calendar">
<!-- 年份 月份 -->
<!-- 星期 -->
<ul class="weekdays">
<li>一</li>
<li>二</li>
<li>三</li>
<li>四</li>
<li>五</li>
<li>六</li>
<li>日</li>
</ul>
<!-- 日期 -->
<ul class="days">
<!-- 核心 v-for循环 每一次循环用<li>标签创建一天 -->
<li
v-for="(item, i) in days"
:key="i"
@click="getDayTime(item.day, item)"
:class="{ h5: h5 }"
>
<!--非本月-->
<!--如果不是本月 改变类名加灰色-->
<div
v-if="item.day.getMonth() + 1 != currentMonth"
class="other-month"
>
{{ dateFormat(item.day).split("-")[2] }}
</div>
<!--本月-->
<div
v-else
:class="{
liBox: true,
active: item.day.getTime() <= new Date().getTime(),
danger: item.isSelected,
}"
>
<!-- active: dateFormat(item.day) == nowDay, -->
<div class="dayBox">
<div class="date">
<span>{{ dateFormat(item.day).split("-")[2] }}</span>
<!-- <div class="count"></div> -->
</div>
</div>
</div>
</li>
</ul>
</div>
</div>
</template>
<script>
import moment from "moment";
import { dateFormat } from "@/util/date";
export default {
data() {
return {
attenData: {}, //考勤数据
nowTime: new Date(),
timer: null,
calendarData: [],
currentDate: "",
formLabelWidth: "140px",
days: [], //当月日期
newDate: "",
nowDay: "",
selectedDay: "",
currentDay: 1,
currentMonth: 1,
currentYear: 1970,
currentWeek: 1,
h5: true,
dateThings: [
{
date: "2023-12-01",
things: "测试",
},
{ date: "2023-12-05", things: "测试22" },
],
};
},
props: {
dateVal: {
default: new Date(),
},
},
watch: {
dateVal() {
this.initData();
// this.lastDate = getAllDays(this.dateVal).length - 24;
},
},
created() {
this.initData();
},
methods: {
// 返回new Date()格式
formatToDate(day) {
let arr = day.split("-");
return new Date(arr[0], arr[1] - 1, arr[2]);
},
initData() {
this.nowDay = this.formatDate(
this.dateVal.getFullYear(),
this.dateVal.getMonth() + 1,
this.dateVal.getDate()
);
var dateVal =
typeof this.dateVal == "object"
? new Date(this.dateVal.getFullYear(), this.dateVal.getMonth(), 1)
: new Date(
this.dateVal.split("-")[0],
this.dateVal.split("-")[1] - 1,
1
);
this.currentDay = dateVal.getDate();
this.currentYear = dateVal.getFullYear();
this.currentMonth = dateVal.getMonth() + 1;
this.currentWeek = dateVal.getDay(); // 1...6,0
if (this.currentWeek === 0) {
this.currentWeek = 7;
}
var str = this.formatDate(
this.currentYear,
this.currentMonth,
this.currentDay
);
this.days.length = 0;
let firstWeekDay = new Date(
this.currentYear,
this.currentMonth - 1,
1
).getDay();
if (firstWeekDay == 0) {
firstWeekDay = 7;
}
for (var i = firstWeekDay - 1; i > 0; i--) {
var d2 = new Date(str);
d2.setDate(d2.getDate() - i);
var dayobjectSelf = {}; // 用一个对象包装Date对象 以便为以后预定功能添加属性
dayobjectSelf.day = d2;
this.days.push(dayobjectSelf); // 将日期放入data 中的days数组 供页面渲染使用
}
let maxLength = 35; //最多展示li的个数,5行
this.h5 = true;
if (
((firstWeekDay == 6 || firstWeekDay == 7) &&
new Date(this.currentYear, this.currentMonth, 0).getDate() == 31) ||
(firstWeekDay == 7 &&
new Date(this.currentYear, this.currentMonth, 0).getDate() == 30)
) {
// 如果本月1号是周六或周日并且这个月有31天,那么需要展示6行,如果本月1号是周日并且这个月有30天,也需要展示6行
maxLength = 42; //6行
this.h5 = false;
}
let alreadyLength = this.days.length;
for (var j = 0; j < maxLength - alreadyLength; j++) {
var d3 = new Date(str);
d3.setDate(d3.getDate() + j);
var dayobjectOther = {};
dayobjectOther.day = d3;
this.days.push(dayobjectOther);
}
// 如果某天有事件的话则标红
for (let i = 0; i < this.days.length; i++) {
const dateB = this.dateFormat(this.days[i].day);
for (let j = 0; j < this.dateThings.length; j++) {
const dateA = this.dateThings[j].date;
if (dateB === dateA) {
this.days[i].isSelected = true;
break;
}
}
}
},
dateFormat(date) {
return dateFormat(date).slice(0, 10);
},
//返回字符串个格式的日期
formatDate(year, month, day) {
return (
year +
"-" +
(month - 0 < 10 ? "0" + month : month) +
"-" +
(day - 0 < 10 ? "0" + day : day)
);
},
getDayTime(time, item) {
this.$emit("getDayTime", time, item);
},
},
};
</script>
<style lang="scss" scoped>
// 日历
#calendar {
font-size: 12px;
width: 100%;
margin: 0 auto;
// box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.1),
// 0 1px 5px 0 rgba(0, 0, 0, 0.12);
}
.month {
width: 100%;
color: #333333;
}
.month ul {
margin: 0;
padding: 0;
display: flex;
justify-content: space-between;
height: 35px;
}
.year-month {
display: flex;
align-items: center;
justify-content: space-around;
margin-top: 10px;
}
.choose-month {
text-align: center;
font-size: 12px;
}
.month ul li {
font-size: 12px;
text-transform: uppercase;
letter-spacing: 3px;
}
.weekdays {
margin: 0;
display: flex;
flex-wrap: wrap;
li {
display: inline-block;
width: 14.2%;
text-align: center;
font-size: 14px;
color: #555;
font-weight: bold;
line-height: 14px;
padding: 12px 0;
// border-right: 1px solid #ebeef5;
box-sizing: border-box;
// background: #fafafa;
&:nth-child(7) {
width: 14.8%;
}
}
}
.days {
// padding: 10px;
background: #ffffff;
margin: 0;
display: flex;
flex-wrap: wrap;
li {
display: inline-block;
width: 14.2%;
text-align: center;
height: 31px; //6行
// border-bottom: 1px solid #ebeef5;
// border-right: 1px solid #ebeef5;
cursor: pointer;
position: relative;
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
box-sizing: border-box;
// 5行
&.h5 {
height: 36px;
}
&:hover {
transition: background-color 0.2s;
background-color: #f2f8fe;
}
&:nth-child(7n) {
width: 14.8%;
border-right: none;
}
.other {
padding: 5px;
color: gainsboro;
}
.liBox {
width: 34px;
height: 34px;
// padding: 0 35px 0 16px;
box-sizing: border-box;
// display: flex;
// align-items: center;
// border: 2px solid transparent;
&.active {
// border: 2px solid #dbdbdb;
.dayBox {
.date {
color: #fff;
background: #37da3d;
}
}
}
&.checked {
.dayBox {
.date {
color: #fff;
background: #3377ff;
border-radius: 50%;
}
}
}
&.danger {
.dayBox {
.date {
color: #fff;
background: #fe5e5e;
border-radius: 50%;
}
}
}
.dayBox {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
.date {
width: 25px;
height: 25px;
min-width: 25px;
border-radius: 50%;
text-align: center;
font-size: 13px;
color: #333;
font-weight: 500;
position: relative;
// color: #fff;
// background: #37f;
border-radius: 50%;
span {
display: inline-block;
width: 100%;
height: 100%;
line-height: 25px;
font-size: 13px;
}
.count {
width: 8px;
height: 8px;
border-radius: 4px;
position: absolute;
bottom: 0px;
left: 50%;
background: #3377ff;
display: inline-block;
transform: translateY(0) translateX(-50%);
}
}
.ehecked {
width: 24px;
position: absolute;
left: 0;
top: 0;
}
}
}
.weather {
display: flex;
justify-content: space-between;
align-items: center;
img {
width: 24px;
margin-right: 10px;
}
span {
font-size: 12px;
color: #999;
font-weight: 500;
&.bad {
color: #ed8079;
}
}
}
}
}
.days li {
.other-month {
color: #333;
font-size: 20px;
float: right;
width: 100%;
text-align: right;
padding-right: 35px;
box-sizing: border-box;
display: none;
}
}
.days li:hover > span > span {
padding: 6px 10px;
border-radius: 50%;
background: #e1e1e1;
color: #fff;
}
</style>