- A+
所属分类:Web前端
效果图
组件介绍
原生小程序编写,简单轻便,拿来即用。
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" style="height:{{swiperHeight}}rpx" 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" style="height:{{listItem.length/7*82}}rpx"> <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; }