在h5开发中, UI框架样式往往很难满足设计需求, 下面是一个自定义的日历组件
效果图:
代码
html,
body {
margin: 0;
padding: 1rem;
}
* {
box-sizing: border-box;
}
[v-cloak] {
display: none !important;
}
.square {
position: relative;
height: 0;
padding-top: 100%;
}
.square .square__item {
height: 100%;
width: 100%;
position: absolute;
left: 0;
top: 0;
}
.square .square__item::after {
content: "";
padding-top: inherit;
display: block;
}
.calendar-card {
position: relative;
background: #05e2ba;
border-radius: 2rem;
overflow: hidden;
margin: 0 2rem;
width: calc(100% - 4rem);
box-shadow: 0px 1.2rem 3rem 0px rgba(149, 192, 216, 0.5);
font-weight: 500;
z-index: 10;
}
.calendar-card .calendar-title {
/* width: 146px; */
width: 100%;
height: 4rem;
margin: auto;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 1rem;
font-weight: bold;
font-family: Baseus Sans SC, Baseus Sans SC;
}
.calendar-card .calendar-title .date-b {
padding: 1rem;
color: #272c2e;
text-align: center;
}
.calendar-card .calendar-title .date-b.is-prev {
transform: rotateZ(180deg);
transform-origin: center;
}
.calendar-card .calendar-title .now-date {
min-width: 8rem;
height: 2.3rem;
font-family: PingFangSC-Medium;
font-size: 1.6rem;
color: #333333;
text-align: center;
font-weight: 500;
}
.calendar-card .calendar-week {
background: #fff;
border-radius: 2rem;
overflow: hidden;
padding: 1.6rem 0.2rem 1.6rem;
}
.calendar-card .calendar-week .calendar__weekdays {
display: flex;
flex-wrap: wrap;
}
.calendar-card .calendar-week .calendar__weekdays .calendar__weekday {
flex: 1;
line-height: 3rem;
text-align: center;
font-family: PingFangSC-Regular;
font-size: 1.4rem;
color: #333333;
text-align: center;
font-weight: 500;
margin: 0 0.3rem;
}
.calendar-card
.calendar-week
.calendar__weekdays
.calendar__weekday
.calendar__weekday__item {
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.calendar-card .calendar-week .calendar__weekdays .calendar__week {
display: inline-block;
width: 14.2857142857%;
gap: 0.4rem;
padding: 0.4rem;
}
.calendar-card .calendar-week .calendar__weekdays .calendar__week .solar {
position: relative;
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
color: #797d8044;
}
.calendar-card .calendar-week .calendar__week .solar.this__month {
color: #797d80;
}
/* 已签到 */
.calendar-card
.calendar-week
.calendar__weekdays
.calendar__week
.solar.is__signed {
border-radius: 50%;
background: #fff000;
color: #181a20;
}
/* 已签到且连续7天的日期 */
.calendar-card
.calendar-week
.calendar__weekdays
.calendar__week
.solar.is__signed__week {
color: #181a20;
}
.calendar-card
.calendar-week
.calendar__weekdays
.calendar__week
.solar.is__signed__week
.sign__week__bg {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%) rotateZ(45deg);
background: #fff000;
border-radius: 0.4rem;
width: 3.2rem;
height: 3.2rem;
}
.calendar-card
.calendar-week
.calendar__weekdays
.calendar__week
.solar.is__signed__week
.sign__week__bg::after {
position: absolute;
left: 0;
top: 0;
content: "";
transform: rotateZ(45deg);
background: #fff000;
border-radius: 0.4rem;
width: 3.2rem;
height: 3.2rem;
}
.calendar-card
.calendar-week
.calendar__weekdays
.calendar__week
.solar__text {
position: relative;
z-index: 1;
/* color: #181a20; */
}
.calendar-card .calendar-week .calendar__weekdays .calendar__week i {
display: none;
}
.calendar-card
.calendar-week
.calendar__weekdays
.calendar__week
.is__today
i {
display: inline-block;
position: absolute;
left: 50%;
bottom: -0.6rem;
width: 0.6rem;
height: 0.6rem;
transform: translateX(-50%);
border-radius: 0.3rem;
background-color: #181a20;
}
<div id="app" v-cloak>
<!-- 日历组件 -->
<section class="calendar-card">
<div class="calendar-title">
<span class="date-b is-prev" @click="handlePrevious()">
<van-icon
name="play"
:style="{color: showMonth == 0 ? '#0FAB90': '#272C2E'}"
/>
</span>
<span class="now-date">{{ showDate | showDateFilter }}</span>
<span class="date-b" @click="handleNext()">
<van-icon
name="play"
:style="{color: showMonth < 11 ? '#272C2E' : '#0FAB90'}"
/>
</span>
</div>
<div class="calendar-week">
<div class="calendar__weekdays">
<div class="calendar__weekday" v-for="i in weekdays" :key="i">
<div class="square">
<div class="square__item">
<div class="calendar__weekday__item">{{ i }}</div>
</div>
</div>
</div>
</div>
<div class="calendar__weekdays calendar__months">
<div
class="calendar__week"
v-for="date in thisMonth"
:class="{'active-day': selectDay == date.fullDate}"
:key="date.fullDate"
>
<div class="square">
<div class="square__item">
<div
class="solar"
:class="{
'this__month': date.month == showMonth,
}"
>
<span class="solar__text">
{{date.date}}
<i></i>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- 日历组件 end -->
</div>
js需要引入自适应尺寸代码, 保证组件自适应: H5响应式尺寸适配
new Vue({
el: "#app",
data() {
return {
// 日历中展示的日期
showDate: new Date(),
// 日历中展示的月份
showMonth: new Date().getMonth(),
// 日历中展示的年份
showYear: new Date().getFullYear(),
// 现在的日期
nowDate: new Date(),
selectDay: "",
weekdays: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
thisMonth: [],
isCurMonth: true,
};
},
filters: {
showDateFilter(date) {
var year = date.getFullYear();
var month = date.getMonth() + 1; // 月份从0开始,因此需要加1
return `${year} ${month.toString().padStart(2, "0")}`;
},
},
watch: {
showDate: {
handler(newd) {
if (newd) {
this.getMonthList();
this.showMonth = newd.getMonth();
this.showYear = newd.getFullYear();
this.isCurMonth = this.showMonth == this.nowDate.getMonth();
}
},
},
},
mounted() {
this.getMonthList();
},
methods: {
isToday(str) {
var now = new Date().setHours(0, 0, 0, 0);
var showDate = new Date(str).setHours(0, 0, 0, 0);
return showDate - now == 0;
},
changeMonthIndex(date) {
return date.getFullYear() + date.getMonth();
},
getMonthList() {
// 获取当前日期
var currentDate = new Date(this.showDate);
// 将日期设置为当月的第一天
currentDate.setDate(1);
// 获取当前月份
var currentMonth = currentDate.getMonth();
// 创建一个数组来存储结果
var monthDates = [];
// 补充前面的周日和周一数据
var firstDayOfWeek = currentDate.getDay(); // 当月第一天是星期几
var daysBefore = (firstDayOfWeek + 7) % 7; // 前面需要补充的天数
let startDate = "";
if (currentDate.getMonth() == 0 && daysBefore > 0) {
startDate = new Date(
currentDate.getFullYear() - 1,
12,
1 - daysBefore
);
} else {
startDate = new Date(
currentDate.getFullYear(),
currentDate.getMonth(),
1 - daysBefore
);
}
for (let i = 0; i < daysBefore; i++) {
var date = new Date(
startDate.getFullYear(),
startDate.getMonth(),
startDate.getDate() + i
);
monthDates.push({
fullDate: this.dateFormat(date, "yyyyMMdd"),
unDate: date, // 未格式化的日期
date: date.getDate(), // 格式化公历日期
month: date.getMonth(),
});
}
// 循环获取当月的日期和农历日期
while (currentDate.getMonth() === currentMonth) {
var date = new Date(currentDate);
// 组合日期和农历日期,并添加到结果数组中
monthDates.push({
fullDate: this.dateFormat(date, "yyyyMMdd"),
unDate: date, // 未格式化的日期
date: date.getDate(), // 格式化公历日期
month: date.getMonth(),
});
currentDate.setDate(currentDate.getDate() + 1);
}
// 下个月补的天数
var nextMonthDays =
monthDates.length % 7 === 0 ? 0 : 7 - (monthDates.length % 7);
if (nextMonthDays !== 0) {
for (let i = 0; i < nextMonthDays; i++) {
var date = new Date(currentDate);
// 组合日期和农历日期,并添加到结果数组中
monthDates.push({
fullDate: this.dateFormat(date, "yyyyMMdd"),
unDate: date, // 未格式化的日期
date: date.getDate(), // 格式化公历日期
month: date.getMonth(),
});
currentDate.setDate(currentDate.getDate() + 1);
}
}
this.thisMonth = monthDates;
},
// 日期格式化
dateFormat(oldDate, format = "yyyyMMdd") {
const date = new Date(oldDate);
const y = date.getFullYear();
const M = (date.getMonth() + 1).toString().padStart(2, "0");
const d = date.getDate().toString().padStart(2, "0");
const h = date.getHours().toString().padStart(2, "0");
const m = date.getMinutes().toString().padStart(2, "0");
const s = date.getSeconds().toString().padStart(2, "0");
const nFormat = format
.replace("yyyy", y)
.replace("MM", M)
.replace("dd", d)
.replace("hh", h)
.replace("mm", m)
.replace("ss", s);
return nFormat;
},
},
});
根据设计稿,如果为375,则设计稿上10px = 1rem,可修改recalc方法中的375为750,以方便在750设计稿中使用10=1rem的比例