实际开发场景中,涉及到用户签到数据需要展示日历及每天是否签到数据状态标识,下面为作者实际开发时使用的日历组件,其为去除掉实际业务代码后的通用代码。
前言
1、使用组件使用为 uview 库
2、时间格式化插件 dayjs
calendar日历组件
<template>
<view class="calendar">
<view class="calendar-title">
<text>我的日历</text>
<view class="close" @click="$emit('close')">
<u-icon class="" name="close" size="44rpx"></u-icon>
</view>
</view>
<view class="calendar-years">
<u-icon class="" name="arrow-left" size="13px" @click="prevMonth"></u-icon>
<view class="calendar-year">{{ year }}年{{ month }}月</view>
<u-icon v-if="showNextMonth" name="arrow-right" size="13px" @click="nextMonth"></u-icon>
</view>
<view class="calendar-body">
<view class="calendar-weekdays" v-if="showWeekdays">
<view class="calendar-weekday" v-for="weekday in weekdays" :key="weekday">
{{ weekday }}
</view>
</view>
<view class="calendar-dates">
<view class="calendar-date" v-for="(date, index) in dates" :key="index">
<view class="calendar-date-null" v-if="!date"></view>
<view class="calendar-date-item" v-else @click="itemHandle(date)">
<view class="calendar-date-item-day">
<text>{{isTodayText(date)}}</text>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: "calendar",
props: {
// 月份日期列表
datesd: {
type: Array,
default: () => {
return []
}
},
// 是否显示周标题
showWeekdays: {
type: Boolean,
default: true
},
open: {
type: Boolean,
default: false
}
},
data() {
return {
// 当前所处日期 年月日
nowDate: this.$jy.formatDate(new Date(), 'YYYY-MM-DD'),
// 当前所处年
year: new Date().getFullYear(),
// 当前所处月
month: new Date().getMonth() + 1,
weekdays: ["一", "二", "三", "四", "五", "六", "日"],
showNextMonth: true, //是否显示查看下个月操作箭头,暂时只能看后一个月
};
},
computed: {
dates() {
const firstDayOfMonth = new Date(this.year, this.month - 1, 1);
const lastDayOfMonth = new Date(this.year, this.month, 0);
const dates = [];
for (let i = 0, len = this.datesd.length; i < len; i++) {
dates.push(this.datesd[i]);
}
if (firstDayOfMonth.getDay() == 0) {
// 第一行只有一天的情况
for (let i = 1; i <= 6; i++) {
dates.unshift(null);
}
} else {
for (let i = 1; i < firstDayOfMonth.getDay(); i++) {
dates.unshift(null);
}
}
return dates;
},
},
watch: {
open: {
handler(val) {
if (val) {
this.month = new Date().getMonth() + 1
}
},
deep: true
},
month: {
handler() {
// 日期格式化获取当前月份
const todayMonth = uni.$jy.formatDate(new Date(), 'M');
// 日期格式化获取当前年
const y = uni.$jy.formatDate(new Date(), 'YYYY')
if (Number(todayMonth) == 12) {
if (this.year > Number(y)) {
this.showNextMonth = false
return
}
}
if (Number(y) < this.year) {
this.showNextMonth = true
return
}
if (Number(y) == this.year) {
if (this.month < Number(todayMonth)) {
this.showNextMonth = true
return
}
let a = Math.abs(this.month - Number(todayMonth))
if (a == 1) {
this.showNextMonth = false
return
}
}
this.showNextMonth = true
return
},
}
},
methods: {
/* 上个月 */
prevMonth() {
if (this.month === 1) {
this.year -= 1;
this.month = 12;
} else {
this.month -= 1;
}
let callMonth = this.month >= 10 ? String(this.month) : '0' + String(this.month)
let callBackDate = String(this.year) + "-" + callMonth
this.$emit('prev', callBackDate)
},
/* 下个月 */
nextMonth() {
if (this.month === 12) {
this.year += 1;
this.month = 1;
} else {
this.month += 1;
}
let callMonth = this.month >= 10 ? String(this.month) : '0' + String(this.month)
let callBackDate = String(this.year) + "-" + callMonth
this.$emit("next", callBackDate)
},
/** 回显每日日期文本
* @param {Object} date
*/
isTodayText(date) {
if (date === this.nowDate) {
return '今天'
}
return uni.$jy.formatDate(date, 'D')
},
/* 每日点击事件 */
itemHandle(item) {
this.$emit('item-cilck', item)
},
},
};
</script>
<style scoped lang="scss">
.calendar {
padding: 35rpx;
width: 624rpx;
min-height: 705rpx;
border-radius: 19rpx;
background: #FFFFFF;
}
.calendar-title {
padding-bottom: 25rpx;
display: flex;
justify-content: space-between;
font-size: 28rpx;
font-family: Source Han Sans CN;
font-weight: 400;
color: #000000;
line-height: 28rpx;
border-bottom: 2rpx solid #F5F5F5;
.close {
position: relative;
width: 44rpx;
height: 44rpx;
&::before {
position: absolute;
left: -12rpx;
top: -12rpx;
right: -12rpx;
bottom: -12rpx;
content: " ";
}
}
}
.calendar-years {
margin-top: 25rpx;
margin-bottom: 20rpx;
display: flex;
justify-content: center;
align-items: center;
.calendar-year {
margin: 0 40rpx;
font-size: 28rpx;
line-height: 28rpx;
font-weight: 600;
}
}
.calendar-body {
padding: 0 4rpx;
display: flex;
flex-direction: column;
font-size: 26rpx;
font-family: DIN Pro;
font-weight: 400;
color: #000000;
line-height: 26rpx;
.calendar-weekdays {
display: flex;
.calendar-weekday {
flex: 1;
padding: 8rpx;
text-align: center;
}
}
.calendar-dates {
display: flex;
flex-wrap: wrap;
.calendar-date {
margin-bottom: 20rpx;
width: calc(100% / 7);
height: 80rpx;
text-align: center;
.calendar-date-item,
.calendar-date-null {
position: relative;
margin: 0 auto;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 59rpx;
height: 100%;
border-radius: 16rpx;
.calendar-date-item-day {
display: flex;
align-items: flex-end;
justify-content: center;
height: 41rpx;
}
}
}
}
}
</style>
日历组件中涉及到的时间格式化工具 js 方法,使用 day.js 轻量级日期格式化插件。
import dayjs from "dayjs" //轻量级日期格式化插件 ;
/** 时间日期格式化
* @param {Object} time-时间信息
* @param {Object} format-格式化样式
* @return {String} 返回格式化后的时间字符串
*/
function formatDate(time, format) {
if (!time) {
return '';
}
return dayjs(time).format(format || "YYYY-MM-DD HH:mm:ss");
}
组件使用
<template>
<view class="content">
<button @click="openPopup">打开日历弹窗</button>
<!-- 日历弹窗 -->
<u-popup :show="calendarPopup" mode="center" round="9px">
<calendar :datesd="datesd" :open="calendarPopup" @prev="getRili" @next="getRili" @close="calendarPopup=false;">
</calendar>
</u-popup>
</view>
</template>
<script>
import calendar from "../calendar/index.vue"
export default {
components: {
calendar
},
data() {
return {
calendarPopup: false,
//日历数据
datesd: [],
}
},
onLoad() {},
methods: {
openPopup() {
// 模拟一个月的日期数据
let total = 30
for (let i = 1; i <= total; i++) {
if (i < 10) {
this.datesd.push(`2024-11-0${i}`)
} else {
this.datesd.push(`2024-11-${i}`)
}
}
//弹窗打开
this.calendarPopup = true
},
//获取上月/下月数据
getRili() {
}
}
}
</script>