vue封装个日历,样式参考vant
结果展示
<template>
<div class="report">
<Header title="工作日历" :isShowLeft="true" @onClickBack="goBack" />
<div class="report-content">
<van-icon name="arrow-left" class="img3" size="13" @click="prevYear()" />
<van-icon name="arrow-left" class="img1" size="13" @click="prevMonth()" />
<van-icon name="arrow" class="img2" size="13" @click="nextMonth()" />
<van-icon name="arrow" class="img4" size="13" @click="nextYear()" />
<div class="calendar">
<div class="header">
<span>{{ year }}年{{ month }}月</span>
</div>
<div class="box">
<div class="box-top">
<div class="box-top-content">
<div class="week-box">日</div>
<div class="week-box">一</div>
<div class="week-box">二</div>
<div class="week-box">三</div>
<div class="week-box">四</div>
<div class="week-box">五</div>
<div class="week-box">六</div>
</div>
</div>
<div class="box-bottom">
<div class="box-mark">{{ month }}</div>
<div class="box-bottom-content" v-for="(week, index) in weeks" :key="index">
<div class="box-bottom-content-box" v-for="(day, i) in week" :key="i" :class="{ today: day.today, selected: day.selected }" @click="selectDay(day)">
{{ day.date && day.date.getDate() }}
</div>
</div>
</div>
</div>
</div>
</div>
<FloatIcons :padding="'10 10 60 10'" class="icons-warp">
<div @click="backToday">
<div class="float-icon-item">
<span style="font-size: 15px;">今</span>
</div>
</div>
</FloatIcons>
</div>
</template>
<script>
import Header from "@/components/header/Header.vue";
import { useRouter } from 'vue-router'
import FloatIcons from "@/components/s-icons";
export default {
components:{
Header,
FloatIcons
},
data() {
return {
router: useRouter(),
year: new Date().getFullYear(),
month: new Date().getMonth() + 1 ,
selectedDate: null,
};
},
computed: {
weeks() {
const weeks = [];
const firstDayOfMonth = new Date(this.year, this.month - 1, 1);
const lastDayOfMonth = new Date(this.year, this.month, 0);
const daysInMonth = lastDayOfMonth.getDate();
let dayOfWeek = firstDayOfMonth.getDay();
let date = 1;
for (let i = 0; i < 6; i++) {
const week = [];
for (let j = 0; j < 7; j++) {
if (i === 0 && j < dayOfWeek) {
week.push({ date: null });
} else if (date > daysInMonth) {
week.push({ date: null });
} else {
const today = new Date();
const selected = this.selectedDate && this.selectedDate.getTime() === new Date(this.year, this.month - 1, date).getTime();
week.push({ date: new Date(this.year, this.month - 1, date), today: today.getTime() === new Date(this.year, this.month - 1, date).getTime(), selected });
date++;
}
}
weeks.push(week);
}
return weeks.filter((e, i) => i === 5 ? !!e[0].date : true);
},
},
mounted(){
this.setSelectDate()
},
methods: {
setSelectDate(){
this.selectedDate = new Date(this.year, this.month - 1, this.selectedDate?.getDate() || new Date().getDate())
},
prevMonth() {
if (this.month === 1) {
this.year--;
this.month = 12;
} else {
this.month--;
}
this.setSelectDate()
},
prevYear(){
this.year--;
this.setSelectDate()
},
nextMonth() {
if (this.month === 12) {
this.year++;
this.month = 1;
} else {
this.month++;
}
this.setSelectDate()
},
nextYear(){
this.year++;
this.setSelectDate()
},
selectDay(day) {
if (day.date) {
this.selectedDate = day.date;
}
},
/** 返回 */
goBack(){
this.router.go(-1)
},
/** 返回今天 */
backToday(){
this.year = new Date().getFullYear()
this.month = new Date().getMonth() + 1
this.selectedDate = new Date(this.year, this.month - 1, new Date().getDate())
},
},
};
</script>
<style scoped lang="less">
.report{
height: 100%;
width: 100%;
/* background: #F3F8FC; */
.report-content{
height: calc(~'100% - 46px');
width: 100%;
position: relative;
// overflow: scroll;
.calendar {
font-family: Arial, sans-serif;
/* width: 238px; */
background: #fff;
margin: 0 auto;
.header {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 10px;
height: 34px;
}
.header span {
background-color: transparent;
font-size: 18px;
}
.box {
width: 100%;
border-collapse: collapse;
padding: 0;
}
.box-top{
box-shadow: 0 2px 10px rgba(125, 126, 128, .16);;
}
.box-top-content {
display: flex;
justify-content: space-between;
}
.box-top-content > div {
width: 34px;
height: 34px;
display: flex;
justify-content: center;
align-items: center;
}
.box-mark{
position: absolute;
top: 164px;
left: 50%;
z-index: 0;
color: red;
font-size: 160px;
transform: translate(-50%,-50%);
pointer-events: none;
}
.box-bottom {
display: flex;
flex-direction: column;
}
.box-bottom-content {
display: flex;
justify-content: space-between;
}
.box-bottom-content-box {
width: 34px;
height: 34px;
display: flex;
justify-content: center;
align-items: center;
position: relative;
font-size: 14px;
font-family: PingFangSC, PingFangSC-Regular;
font-weight: 400;
text-align: center;
// line-height: 34px;
}
.today {
background-color: #eee;
}
.selected {
background-color: #007bff;
color: #fff;
border-radius: 50%;
}
}
}
}
.week-box{
opacity: 0.65;
font-size: 14px;
font-family: PingFangSC, PingFangSC-Medium;
font-weight: 500;
color: #666666;
}
.img1,
.img2,
.img3,
.img4 {
width: 22px;
height: 22px;
line-height: 22px;
position: absolute;
top: 8px;
background: #f2f2f2;
border-radius: 6px;
}
.img1 {
left: 85px;
}
.img2 {
right: 85px;
}
.img3 {
left: 45px;
}
.img4 {
right: 45px;
}
.icons-warp {
border-radius: 20px;
background-color: #99a9bf;
z-index: 99999;
height: 35px;
line-height: 35px;
width: 35px;
border-radius: 50%;
}
</style>