效果图
组件介绍
原生小程序编写,简单轻便,拿来即用。
gitee地址:https://gitee.com/qq_connect-EC6BCC0B556624342/wx-calendar
代码部分(这里可能不是最新的推荐去gitee克隆代码)
calendar.wxml
<!--component/calendar/calendar.wxml-->
<view class="calendar">
<view class="title">
<view class="header-wrap">
<view class="flex">
<view class="title">{{title}}</view>
<view class="month">
<block wx:if="{{title}}">
(
</block>
{{selectDay.year}}年{{selectDay.month}}月
<block wx:if="{{title}}">
)
</block>
</view>
</view>
<block wx:if="{{goNow}}">
<view wx:if="{{open && !(nowDay.year==selectDay.year&&nowDay.month==selectDay.month&&nowDay.day==selectDay.day)}}" class="today" bindtap="switchNowDate">
今日
</view>
</block>
</view>
</view>
<!-- 日历头部 -->
<view class="flex-around calendar-week">
<view class="view">一</view>
<view class="view">二</view>
<view class="view">三</view>
<view class="view">四</view>
<view class="view">五</view>
<view class="view">六</view>
<view class="view">日</view>
</view>
<!-- 日历主体 -->
<swiper class="swiper" bindchange="swiperChange" circular="{{true}}" current="{{swiperCurrent}}" duration="{{swiperDuration}}">
<swiper-item wx:for="{{[dateList0, dateList1, dateList2]}}" wx:for-index="listIndex" wx:for-item="listItem" wx:key="listIndex">
<view class="flex-start flex-wrap calendar-main">
<view wx:for="{{listItem}}" wx:key="dateList" class="day">
<view class="bg {{item.month === selectDay.month?spotMap['y'+item.year+'m'+item.month+'d'+item.day]?spotMap['y'+item.year+'m'+item.month+'d'+item.day]:'':''}} {{(item.year === nowDay.year && item.month === nowDay.month && item.day === nowDay.day) ? 'now' : ''}} {{(item.year === selectDay.year && item.month === selectDay.month) ? (item.day === selectDay.day && oldCurrent === listIndex ?'select':''): 'other-month'}}" catchtap="selectChange" data-day="{{item.day}}" data-year="{{item.year}}" data-month="{{item.month}}">
{{item.day}}
</view>
</view>
</view>
</swiper-item>
</swiper>
<view catchtap="openChange" class="flex list-open">
<view class="icon {{open?'fold':'unfold'}}"></view>
</view>
</view>
calendar.js
// component/calendar/calendar.js
Component({
/**
* 组件的属性列表
*/
properties: {
spotMap: { //标点的日期
type: Object,
value: {}
},
defaultTime: { //标记的日期,默认为今日
type: String,
value: ''
},
title: { //标题
type: String,
value: ''
},
goNow: { // 是否有快速回到今天的功能
type: Boolean,
value: true,
}
},
/**
* 组件的初始数据
*/
data: {
selectDay: {}, //选中时间
nowDay: {}, //现在时间
open: false,
swiperCurrent: 1, //选中时间
oldCurrent: 1, //之前选中时间
dateList0: [], //0位置的日历数组
dateList1: [], //1位置的日历数组
dateList2: [], //2位置的日历数组
swiperDuration: 500,
swiperHeight: 0,
backChange: false, //跳过change切换
},
/**
* 组件的方法列表
*/
methods: {
swiperChange(e) { // 日历滑动时触发的方法
if (this.data.backChange) {
this.setData({
backChange: false
})
return
}
//计算第三个索引
let rest = 3 - e.detail.current - this.data.oldCurrent
let dif = e.detail.current - this.data.oldCurrent
let date
if (dif === -2 || (dif > 0 && dif !== 2)) { //向右划的情况,日期增加
if (this.data.open) {
date = new Date(this.data.selectDay.year, this.data.selectDay.month)
this.setMonth(date.getFullYear(), date.getMonth() + 1, undefined)
this.getIndexList({
setYear: this.data.selectDay.year,
setMonth: this.data.selectDay.month,
dateIndex: rest
})
} else {
date = new Date(this.data.selectDay.year, this.data.selectDay.month - 1, this.data.selectDay.day + 7)
this.setMonth(date.getFullYear(), date.getMonth() + 1, date.getDate())
this.getIndexList({
setYear: this.data.selectDay.year,
setMonth: this.data.selectDay.month - 1,
setDay: this.data.selectDay.day + 7,
dateIndex: rest
})
}
} else { //向左划的情况,日期减少
if (this.data.open) {
date = new Date(this.data.selectDay.year, this.data.selectDay.month - 2)
this.setMonth(date.getFullYear(), date.getMonth() + 1, undefined)
this.getIndexList({
setYear: this.data.selectDay.year,
setMonth: this.data.selectDay.month - 2,
dateIndex: rest
})
} else {
date = new Date(this.data.selectDay.year, this.data.selectDay.month - 1, this.data.selectDay.day - 7)
this.setMonth(date.getFullYear(), date.getMonth() + 1, date.getDate())
this.getIndexList({
setYear: this.data.selectDay.year,
setMonth: this.data.selectDay.month - 1,
setDay: this.data.selectDay.day - 7,
dateIndex: rest
})
}
}
this.setData({
oldCurrent: e.detail.current
})
this.setSwiperHeight(e.detail.current)
},
setSwiperHeight(index) { // 根据指定位置数组的大小计算长度
this.setData({
swiperHeight: this.data[`dateList${index}`].length / 7 * 82 + 18
})
},
//更新指定的索引和月份的列表
getIndexList({
setYear,
setMonth,
setDay = void 0,
dateIndex
}) {
let appointMonth
if (setDay)
appointMonth = new Date(setYear, setMonth, setDay)
else
appointMonth = new Date(setYear, setMonth)
let listName = `dateList${dateIndex}`
this.setData({
[listName]: this.dateInit({
setYear: appointMonth.getFullYear(),
setMonth: appointMonth.getMonth() + 1,
setDay: appointMonth.getDate(),
hasBack: true
}),
})
},
//设置月份
setMonth(setYear, setMonth, setDay) {
const day = Math.min(new Date(setYear, setMonth, 0).getDate(), this.data.selectDay.day)
if (this.data.selectDay.year !== setYear || this.data.selectDay.month !== setMonth) {
const data = {
selectDay: {
year: setYear,
month: setMonth,
day: setDay ? setDay : day
},
}
if (!setDay) {
data.open = true
}
this.setData(data, () => {
this.triggerEvent("selectDay", this.data.selectDay)
})
} else {
const data = {
selectDay: {
year: setYear,
month: setMonth,
day: setDay ? setDay : day
},
}
this.setData(data, () => {
this.triggerEvent("selectDay", this.data.selectDay)
})
}
},
//展开收起
openChange() {
this.setData({
open: !this.data.open
})
this.triggerEvent("aaa", {
a: 0
})
// 更新数据
const selectDate = new Date(this.data.selectDay.year, this.data.selectDay.month - 1, this.data.selectDay.day)
if (this.data.oldCurrent === 0) {
this.updateList(selectDate, -1, 2)
this.updateList(selectDate, 0, 0)
this.updateList(selectDate, 1, 1)
} else if (this.data.oldCurrent === 1) {
this.updateList(selectDate, -1, 0)
this.updateList(selectDate, 0, 1)
this.updateList(selectDate, 1, 2)
} else if (this.data.oldCurrent === 2) {
this.updateList(selectDate, -1, 1)
this.updateList(selectDate, 0, 2)
this.updateList(selectDate, 1, 0)
}
this.setSwiperHeight(this.data.oldCurrent)
},
// 选中并切换今日日期
switchNowDate() {
const now = new Date()
const selectDate = new Date(this.data.selectDay.year, this.data.selectDay.month - 1, this.data.selectDay.day)
let dateDiff = (selectDate.getFullYear() - now.getFullYear()) * 12 + (selectDate.getMonth() - now.getMonth())
let diff = dateDiff === 0 ? 0 : dateDiff > 0 ? -1 : 1
const diffSum = (x) => (3 + (x % 3)) % 3
if (this.data.oldCurrent === 0) {
this.updateList(now, -1, diffSum(2 + diff))
this.updateList(now, 0, diffSum(0 + diff))
this.updateList(now, 1, diffSum(1 + diff))
} else if (this.data.oldCurrent === 1) {
this.updateList(now, -1, diffSum(0 + diff))
this.updateList(now, 0, diffSum(1 + diff))
this.updateList(now, 1, diffSum(2 + diff))
} else if (this.data.oldCurrent === 2) {
this.updateList(now, -1, diffSum(1 + diff))
this.updateList(now, 0, diffSum(2 + diff))
this.updateList(now, 1, diffSum(0 + diff))
}
this.setData({
swiperCurrent: diffSum(this.data.oldCurrent + diff),
oldCurrent: diffSum(this.data.oldCurrent + diff),
backChange: dateDiff !== 0,
})
this.setData({
selectDay: {
year: now.getFullYear(),
month: now.getMonth() + 1,
day: now.getDate()
}
}, () => {
this.triggerEvent("selectDay", this.data.selectDay)
})
this.setSwiperHeight(this.data.oldCurrent)
},
//日历主体的渲染方法
dateInit({
setYear,
setMonth,
setDay = this.data.selectDay.day,
hasBack = false
} = {
setYear: this.data.selectDay.year,
setMonth: this.data.selectDay.month,
setDay: this.data.selectDay.day,
hasBack: false
}) {
let dateList = []; //需要遍历的日历数组数据
let now = new Date(setYear, setMonth - 1) //当前月份的1号
let startWeek = now.getDay(); //目标月1号对应的星期
let resetStartWeek = startWeek == 0 ? 6 : startWeek - 1; //重新定义星期将星期天替换为6其余-1
let dayNum = new Date(setYear, setMonth, 0).getDate() //当前月有多少天
let forNum = Math.ceil((resetStartWeek + dayNum) / 7) * 7 //当前月跨越的周数
let selectDay = setDay ? setDay : this.data.selectDay.day
this.triggerEvent("getDateList", {
setYear: now.getFullYear(),
setMonth: now.getMonth() + 1
})
if (this.data.open) {
//展开状态,需要渲染完整的月份
for (let i = 0; i < forNum; i++) {
const now2 = new Date(now)
now2.setDate(i - resetStartWeek + 1)
let obj = {};
obj = {
day: now2.getDate(),
month: now2.getMonth() + 1,
year: now2.getFullYear()
};
dateList[i] = obj;
}
} else {
//非展开状态,只需要渲染当前周
for (let i = 0; i < 7; i++) {
const now2 = new Date(now)
//当前周的7天
now2.setDate(Math.ceil((selectDay + (startWeek - 1)) / 7) * 7 - 6 - (startWeek - 1) + i)
let obj = {};
obj = {
day: now2.getDate(),
month: now2.getMonth() + 1,
year: now2.getFullYear()
};
dateList[i] = obj;
}
}
if (hasBack) {
return dateList
}
this.setData({
dateList1: dateList
})
},
//一天被点击时
selectChange(e) {
const year = e.currentTarget.dataset.year
const month = e.currentTarget.dataset.month
const day = e.currentTarget.dataset.day
const selectDay = {
year: year,
month: month,
day: day,
}
if (this.data.open && (this.data.selectDay.year !== year || this.data.selectDay.month !== month)) {
if ((year * 12 + month) > (this.data.selectDay.year * 12 + this.data.selectDay.month)) { // 下个月
if (this.data.oldCurrent == 2)
this.setData({
swiperCurrent: 0
})
else
this.setData({
swiperCurrent: this.data.oldCurrent + 1
})
} else { // 点击上个月
if (this.data.oldCurrent == 0)
this.setData({
swiperCurrent: 2
})
else
this.setData({
swiperCurrent: this.data.oldCurrent - 1
})
}
this.setData({
['selectDay.day']: day
}, () => {
this.triggerEvent("selectDay", this.data.selectDay)
})
} else if (this.data.selectDay.day !== day) {
this.setData({
selectDay: selectDay
}, () => {
this.triggerEvent("selectDay", this.data.selectDay)
})
}
},
updateList(date, offset, index) {
if (this.data.open) { //打开状态
const setDate = new Date(date.getFullYear(), date.getMonth() + offset * 1) //取得当前日期的上个月日期
this.getIndexList({
setYear: setDate.getFullYear(),
setMonth: setDate.getMonth(),
dateIndex: index
})
} else {
const setDate = new Date(date.getFullYear(), date.getMonth(), date.getDate() + offset * 7) //取得当前日期的七天后的日期
this.getIndexList({
setYear: setDate.getFullYear(),
setMonth: setDate.getMonth(),
setDay: setDate.getDate(),
dateIndex: index
})
}
},
},
lifetimes: {
attached() {
let now = this.data.defaultTime ? new Date(this.data.defaultTime) : new Date()
let selectDay = {
year: now.getFullYear(),
month: now.getMonth() + 1,
day: now.getDate()
}
this.setData({
nowDay: {
year: now.getFullYear(),
month: now.getMonth() + 1,
day: now.getDate()
}
})
this.setMonth(selectDay.year, selectDay.month, selectDay.day)
this.updateList(now, -1, 0)
this.updateList(now, 0, 1)
this.updateList(now, 1, 2)
this.setSwiperHeight(1)
}
},
observers: {}
})
calendar.json
{
"component": true,
"usingComponents": {}
}
calendar.wxss
/* component/calendar/calendar.wxss */
.icon {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACcUlEQVRYhe2WvWtUURDFf8dNVk1ETViMX4WNna22CopELBQUkYASgzFFUPBPsLGSgAqJGDRGBEUsRA0KIthaip2I/4CEoIR8JyP33lldl7fuR1AL9xSP3Xfvmzlz7pl5jyaa+O+hq0NDqDEV8sBGYA3wDZirP4TFhxvFApAD1gPz5TGqF2Xx2pK5puwA9uuPPUA30A48Bd43Ukg2gWpQTHwe2Iax5GRuA6//BoFuwQDQaUF6xaPYBFzCYryXf4yAoAc44We/INjgJvzqGwYwWg2e1Rqzogmt9MwTjgGngBXvgLB8Axh1HyiuiV7BwVoJ1KpAqPqMu70dtFbYNWDC7SqwfoMp0r9BLHbHi2qBa2nDk0A/aAmUF9oscR1pAikJIR4g3RXqUFIoxD0nOLQqAoLjwFlgOuyVKCDGve3KMY54BOqMrBTVDSQON0rgqLfajJCF5IJ7SmdeifBIICgISgQSbUCPYH+9BI4Ag8BidLzY4VXf+V01TmIM9BhU8Gc7gNOCfVn7s0y4N1SuNNuXhbYDDwU3qyX/QUIMm7Eo6HNj7gQueKe8KyfQ4guh2gPAZW+zWdAW4Al1JC+RYjTMBKA3xYpdcZE0O974rlxIXvDeDhv6gF1Kg6UrnrcYqTv5TwwLpi0VFRTd6oTmQB/AckUPrMS5LjpR9MU6n+2rSV7EfRQH1orSwOoC2w0WlQ+XZZ9qs/57CnQFLMnk47A4FdXYx8MYMJl8ECN9hPgSyxdNGM7qC3DLibyt9Ea3rHtmFddK8DzlURvYZ1c5GrA4ueZBr5S8EPp3JjNgyc061QgvsE9gk/4x0+qKN9FEE/8QwHd9qo6ectzgFAAAAABJRU5ErkJggg==");
background-size: 100% auto;
width: 32rpx;
height: 32rpx;
}
.flex {
display: flex;
justify-content: space-between;
align-items: center;
}
.swiper {
transition: height 0.3s;
}
.header-wrap {
display: flex;
justify-content: space-between;
align-items: center;
}
.today {
width: 88rpx;
height: 42rpx;
background: #F3F4F4;
border-radius: 22rpx;
font-size: 24rpx;
line-height: 42rpx;
color: #868D8D;
text-align: center;
margin-right: 6rpx;
}
.today:active {
background: #dfdfdf;
color: #5f6464;
}
.direction-column {
flex-direction: column;
}
.flex1 {
flex: 1;
}
.flex-center {
display: flex;
justify-content: center;
align-items: center;
}
.flex-start {
display: flex;
justify-content: flex-start;
align-items: center;
}
.flex-end {
display: flex;
justify-content: flex-end;
align-items: center;
}
.flex-around {
display: flex;
justify-content: space-around;
align-items: center;
}
.flex-wrap {
flex-wrap: wrap;
}
.align-start {
align-items: flex-start;
}
.align-end {
align-items: flex-end;
}
.align-stretch {
align-items: stretch;
}
.calendar {
font-family: "PingFang SC", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", Helvetica, Arial, "Hiragino Sans GB", "Source Han Sans", "Noto Sans CJK Sc", "Microsoft YaHei", "Microsoft Jhenghei", sans-serif;
}
.calendar .title {
padding: 10rpx 16rpx 10rpx 20rpx;
line-height: 60rpx;
font-size: 32rpx;
font-weight: 600;
color: #1C2525;
line-height: 44px;
letter-spacing:1px;
}
.calendar .title .year-month {
margin-right: 20rpx;
}
.calendar .title .icon {
padding: 0 16rpx;
font-size: 32rpx;
color: #999;
}
.calendar .title .open {
background-color: #f6f6f6;
color: #999;
font-size: 22rpx;
line-height: 36rpx;
border-radius: 18rpx;
padding: 0 14rpx;
}
.list-open {
position: relative;
justify-content: center;
}
.list-open .icon::after {
content: '';
position: absolute;
top: 16rpx;
right: 60rpx;
display: block;
width: 278rpx;
height: 0rpx;
border-bottom: 2rpx solid rgba(214, 219, 219, 0.68);
}
.list-open .icon::before {
content: '';
position: absolute;
top: 16rpx;
left: 60rpx;
display: block;
width: 278rpx;
height: 0rpx;
border-bottom: 2rpx solid rgba(214, 219, 219, 0.68);
}
.fold {
transform: rotate(0deg);
}
.unfold {
transform: rotate(180deg);
}
.calendar .calendar-week {
line-height: 40rpx;
padding: 0 25rpx;
font-size: 28rpx;
color: #999;
}
.calendar .calendar-week .view {
width: 100rpx;
text-align: center;
}
.calendar .calendar-main {
padding: 18rpx 25rpx 0rpx;
transition: height 0.3s;
align-content: flex-start;
overflow: hidden;
}
.calendar .calendar-main .day {
position: relative;
width: 100rpx;
color: #666;
text-align: center;
height: 82rpx;
}
.calendar .calendar-main .day .bg {
height: 66rpx;
line-height: 66rpx;
font-size: 28rpx;
color: #333;
}
.calendar .calendar-main .day .now {
width: 66rpx;
border-radius: 50%;
text-align: center;
color: #0EC0B8;
background: rgba(14, 192, 184, 0.2);
margin: 0 auto;
}
.calendar .calendar-main .day .select {
width: 66rpx;
border-radius: 50%;
text-align: center;
color: #fff;
background: #0EC0B8;
margin: 0 auto;
}
.calendar .calendar-main .day .spot::after {
position: absolute;
content: "";
display: block;
width: 8rpx;
height: 8rpx;
bottom: 22rpx;
background: #0EC0B8;
border-radius: 50%;
left: 0;
right: 0;
margin: auto;
}
.calendar .calendar-main .day .deep-spot::after {
position: absolute;
content: "";
display: block;
width: 8rpx;
height: 8rpx;
bottom: 22rpx;
background: #FF7416;
border-radius: 50%;
left: 0;
right: 0;
margin: auto;
}
.calendar .calendar-main .day .other-month {
color: #ccc;
}
.header-wrap .month {
font-size: 28rpx;
color: #929797;
line-height: 40rpx;
}