小程序使用原生swiper封装日历组件

先上一个效果图大家就知道布局方式了

在这里插入图片描述
目录介绍:
uni-calendar: (上图绿色框部分,主要逻辑代码实现)主组件展示当前日期、左右的切换(上/下)月份、当月数据展示引入子组件calendar-item
calendar-item:(上图红色框部分) 子组件 展示中间日期部分的内容用于切换(上/下)月份的时候方便使用、把上月/当月/下个当成三个滚动滑块
index :使用组件的页面 (通过上图黄色图标点击判断展示/隐藏日历
特殊:这个日历功能根据业务需求需要调用后台接口把特殊日期的事件加标注(如上图31号标注)

组件间的具体使用方式和传参,我就不多做解释了不清楚的可以去访问微信开发文档

一、先创建uni-calendar组件,页面结构如下图,组件是专门放在component文件夹里面的

在这里插入图片描述

uni-calendar.js

// component/uni-calendar/uni-calendar.js
const app = getApp();

Component({
	// 可以用app.wxss 样式
	options: {
		addGlobalClass: true,
	},

	/**
	 * 组件的属性列表
	 */
	properties: {

	},

	/**
	 * 组件的初始数据
	 */
	data: {
		isShow: false,//是否展示日历
		weekDayZh: ['日', '一', '二', '三', '四', '五', '六'],	/* 星期周期 */
		nowYear: '',	/* 当前选择年份 */
		nowMonth: '',/* 当前月份 */
		chooseDay: -1,		/* 选中日期 */
		chooseIndex:-1,	// 选择下标
		chooseDate:'',	// 选中完整日期
		type:'',// 用来接收日历子组件
		showLineNums: 5,	// 当前月份日期行数默认5行
		/* 当前月份存放天数组,格式为[[{day:21,date:2021-7-21}]],全部日期 */
		presentDayList: [],
		/* 上一个月的日期数组 */
		lastDayList: [],
		/* 下一个月 */
		nextDayList: [],
		/* 滑动页选中角标 ,默认选择中间的*/
		current: 1,
		
		/*子组件 上 当前 下*/
		calenderLast: '',
		calenderCurrent: '',
        calenderNext: '',
        
		//用于存放未改变前的下标
		primary_current:1,
	},
	/**
	 * 组件的方法列表
	 */
	methods: {
	   // 隐藏日历
	   hideCalendar(){ this.setData({ isShow:false })},
		// 重置选择时间
		resetData(){
			this.data.chooseIndex = -1;
			this.data.chooseDay = -1;
		},
		/* 获取日历中dayArr数据 */
		getDateList(value = '', type = 'present') {
			let nowDate = new Date();
			if (value) {
				// 为了适配苹果机型
				value = value.replace(new RegExp(/-/gm), "/");
				nowDate = new Date(value);
			}

			let nowYear = nowDate.getFullYear(); //年份
			let nowMonth = nowDate.getMonth() + 1; //月份
			let nowTime = nowDate.getDate(); //今日日期
			var isToday = false; //存储是否是今日日期

			let firstDay = new Date(nowYear, nowMonth - 1, 1); //当月第一天Date资讯

			let firstDayWeek = firstDay.getDay(); //当月第一天星期几

			let days = this.get_month_days(nowMonth, nowYear)//当月一共多少天
			let lines = Math.ceil((days + firstDayWeek) / 7); //表格所需要行数

			let day_list = [];
			var date = '';
			for (let i = 0; i < lines; i++) {
				/* 所有数据 */
				let allDayArr = [];
				for (let j = 0; j < 7; j++) {
					/* 获取单元格索引 */
					var index = i * 7 + j
					/* 计算日期,小于等于0或者大于当前月份总天数都是无效日期 */
					var dayValue = index - firstDayWeek + 1;
					if (dayValue <= 0) {
						allDayArr.push({
							day: ''
						})
					} else if (dayValue > days) {
						allDayArr.push({
							day: ''
						})
					} else {
						let saveDay = dayValue
						if (dayValue < 10) {
							saveDay = "0" + dayValue
						}
						let saveMonth = nowMonth
						if (nowMonth < 10) {
							saveMonth = "0" + nowMonth
						}
						date = `${nowYear}-${saveMonth}-${saveDay}`

						allDayArr.push({
							day: dayValue,
							date: date
							// isToday: isToday
						})
					}
				}
				day_list.push(allDayArr)
			}

			if (type == 'present') {
				this.setData({
					presentDayList: day_list,
					nowYear: nowYear,
					nowMonth: nowMonth < 10 ? '0' + nowMonth : nowMonth,
					showLineNums: lines,
				});
			} else if (type == 'last') {
				this.setData({
					lastDayList: day_list
				})
			} else if (type == "next") {
				this.setData({
					nextDayList: day_list
				})
			}
			// console.log(day_list)
		},

		/* 获取当年、当月一共有多少天 */
	    get_month_days (nowMonth,nowYear) {
	      return new Date(nowYear, nowMonth,0).getDate()
	    },
	    
		/* 获取上一个月或者下一个月的日期 返回格式为 2021-03-10*/
		getLastAndNextTime(type) {
			let nowMonth = this.data.nowMonth;
			let nowYear = this.data.nowYear;
			if (type == 'last') {
				if (nowMonth == 1) {
					nowMonth = 12;
					nowYear = nowYear - 1;
				} else {
					nowMonth--;
				}
			} else if (type == 'next') {
				if (nowMonth == 12) {
					nowMonth = 1;
					nowYear = nowYear + 1;
				} else {
					nowMonth++;
				}
			}
			var date = `${nowYear}/${nowMonth}/1`
			return date;	
		},
		//上一月
		previousClickEvent() {
			var current = 0;
			this.data.primary_current = this.data.current;
			var preCurrent = this.data.current;
			if(preCurrent == 1){
				current = 0;
			}else if(preCurrent==0){
				current = 2;
			}else if(preCurrent == 2){
				current = 1
			}
			this.setData({
				current:current
			});
		},
		//下一月
		nextClickEvent() {
			var current = 0;
			this.data.primary_current = this.data.current;
			var preCurrent = this.data.current;
			if(preCurrent == 1){
				current = 2;
			}else if(preCurrent==0){
				current = 1;
			}else if(preCurrent == 2){
				current = 0
			}
			this.setData({
				current:current
			});
		},
       // 滑动监听
		changeDate(e) {
			var primary_current = this.data.current
			var current = e.detail.current;
			console.log(this.data.current,primary_current,current)
			let source = e.detail.source;
			console.log(this.data.current,primary_current,current)
			if(!source){//表示是手动切换
				primary_current = this.data.primary_current
			}
			if (primary_current - current == -1 || primary_current - current == 2) {
				// 向后滑动
				this.getDateList(this.getLastAndNextTime('next'));
				this.update_lastAndNext_dateList();
				if (primary_current - current == -1 && current != 1) {
					this.change_date_month('last')
				} else if (primary_current - current == 2) {
					this.change_date_month('next')
				}
			} else {
				// 向前滑动
				this.getDateList(this.getLastAndNextTime('last'));
				this.update_lastAndNext_dateList();
				if (primary_current - current == 1 && current != 1) {
					this.change_date_month('next')
				} else if (primary_current - current == -2) {
					this.change_date_month('last')
				}
			}
	      if(source){//滑动切换更改值
	        this.setData({ current: current })
		  }
			this.getCalendarData();
		},

		// 获取日历数据
		getCalendarData() {//调用后台接口获取数据匹配当月哪天有特殊标注
			var date = `${this.data.nowYear}-${this.data.nowMonth}-01`
			app.http.get(app.api.auctioncalendar,	{date: date}).then(res => {
				if (res.data) {
					var keyArray = [];// 存储日期
					var valuesArray = [];// 存储日期下的数据
					for (let key in res.data) {
					  keyArray.push(key);
					  valuesArray.push(res.data[key]);
					}
					console.log(keyArray,valuesArray)
					this.present_list_update(keyArray,valuesArray);
				}
			});
		},

		/* 更新有数据特殊标注显示*/
		present_list_update(keyArray,valuesArray) {
			let list = []
			var forList = [];
			if(this.data.current == 0){
				forList = this.data.lastDayList;
			}else if(this.data.current == 1){
				forList = this.data.presentDayList;
			}else if(this.data.current == 2){
				forList = this.data.nextDayList;
			}
			forList.map((item, index) => {
				list.push(item.map((dot, index) => {
					var num = keyArray.indexOf(dot.date)
					if (num >= 0) {
						dot.type = valuesArray[num].type
						dot.num = valuesArray[num].num
					}
					return {
						...dot
					}
				}))
			})
			if(this.data.current == 0){
				this.data.lastDayList = list;
			}else if(this.data.current == 1){
				this.data.presentDayList = list;
			}else if(this.data.current == 2){
				this.data.nextDayList = list;
			}

			this.modifyCalendarItemValue();
		},
		/* 滑动后修改三个数组值 */
		change_date_month(type) {
			let day_list = this.data.presentDayList;
			if (type == 'last') {
				this.setData({
					presentDayList: this.data.lastDayList,
					lastDayList: this.data.nextDayList,
					nextDayList: day_list
				})
			} else if (type == 'next') {
				this.setData({
					presentDayList: this.data.nextDayList,
					nextDayList: this.data.lastDayList,
					lastDayList: day_list
				})
			}
			// this.modifyCalendarItemValue();
		},

		// 给子组件设置值
		modifyCalendarItemValue() {
			this.data.calenderLast = this.selectComponent("#calenderLast");
			this.data.calenderCurrent = this.selectComponent("#calenderCurrent");
			this.data.calenderNext = this.selectComponent("#calenderNext");
			
			this.data.calenderCurrent.setData({
				dateList: this.data.presentDayList
			});

			this.data.calenderNext.setData({
				dateList: this.data.nextDayList
			});
			this.data.calenderLast.setData({
				dateList: this.data.lastDayList
			});
			var type = this.data.type;
			if(type == 'pre'){
				this.data.calenderCurrent.setData({
					chooseIndex:this.data.chooseIndex,
					chooseDay:this.data.chooseDay,
			    chooseDate:this.data.chooseDate
				})
			}else if(type == 'last'){
				this.data.calenderLast.setData({
					chooseIndex:this.data.chooseIndex,
					chooseDay:this.data.chooseDay,
					chooseDate:this.data.chooseDate
				});
			}else if(type == 'next'){
				this.data.calenderNext.setData({
					chooseIndex:this.data.chooseIndex,
					chooseDay:this.data.chooseDay,
					chooseDate:this.data.chooseDate
				});
			}
		},

		/* 初始化数据 */
		initCanlendarData() {
			if(app.utils.arrayIsNull(this.data.presentDayList)){// 有数据
				this.modifyCalendarItemValue();
			}else{
				this.getDateList();
				this.getCalendarData();
				this.update_lastAndNext_dateList();
				this.modifyCalendarItemValue();
			}
		},

		/* 日历更新,通知引用者 */
		clickDateEvent(e) {
			this.hideCalendar();
			this.data.chooseDay = e.detail.chooseDay;
			this.data.chooseIndex = e.detail.chooseIndex;
			this.data.type = e.detail.type;
			this.data.chooseDate = e.detail.date;
			this.triggerEvent("chooseDateEvent", {
				date: e.detail.date
			})
		},
		/* 动态改变上下两个月的数据 */
		update_lastAndNext_dateList() {
			this.getDateList(this.getLastAndNextTime('last'), 'last');
			this.getDateList(this.getLastAndNextTime('next'), 'next');
		},

	}
})

uni-calendar.json

需要在这里声明一下做如下配置

{
  "component":true,//证明自己是个组件
  "usingComponents": {//存放使用的子组件
     "uni-icon":"/component/uni-icon/uni-icon", //自己封装的用于展示图片icon的组件,这里不多做介绍了
     "calendar-item":"/component/calendar-item/calendar-item"
   }
}

uni-calendar.wxml

<!-- isShow 控制日历的展示隐藏-->
<view class="box"  wx:if="{{isShow}}">
  <view class="calender-box">
    <view class="itemLine header-box">
      <uni-icon iconSrc="/static/images/arrow_auction_previous.png" iconWidth="13" bindtap="previousClickEvent"></uni-icon>
      <text class="nameBlackText">{{nowYear+'年'+nowMonth+"月"}}</text>
      <uni-icon iconSrc="/static/images/arrow_auction_next.png" iconWidth="13" bindtap="nextClickEvent"></uni-icon>
    </view>
    <view class="itemLine week-day">
      <text class="week-item" wx:for="{{weekDayZh}}" wx:key="index">{{item}}</text>
    </view>
    <!-- 数据展示部分类似于轮播图的效果用户可以通过左右滑动或者点击上方的左右箭头操控月份的切换
       1、showLineNums 计算的当月需要多少行
       2、changeDate 滑动监听
       3、current 记录当前的下边 0 1 2
       4、clickDateEvent  绑定子组件点击选中日期的事件传递参数
       5、type:last、pre、next,用于区分当前/上/下月份
   -->
    <swiper style="height:{{80*showLineNums}}rpx;" circular="{{true}}"  bindchange="changeDate"  current="{{current}}" >
      <swiper-item>
        <calendar-item id="calenderLast" bind:chooseDateEvent="clickDateEvent" type="last"></calendar-item>
      </swiper-item>
      <swiper-item>
        <calendar-item id="calenderCurrent" bind:chooseDateEvent="clickDateEvent" type="pre"></calendar-item>
      </swiper-item>
      <swiper-item>
        <calendar-item id="calenderNext" bind:chooseDateEvent="clickDateEvent" type="next"></calendar-item>
      </swiper-item>
    </swiper>
  </view>
  <!--空白区域 点击触发事件隐藏日历-->
  <view class="emptyBox" bindtap="hideCalendar">
  </view>
</view>

uni-calendar.wxss

/* component/uni-calendar/uni-calendar.wxss */
.box{
  width: 100%;
  height: 90%;
  position: fixed;
  left: 0;
  top:100rpx;
  z-index: 8;
  display:flex;
  flex-direction: column;
}
.calender-box{
  padding-top: 20rpx;
	background-color: #FFFFFF;
	box-shadow: 0 0 10px #CCC;
}
.header-box{
  width: 85%;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.itemLine{
  height: 80rpx;
}
.week-day {
  display: flex;
  width: 100%;
}
.week-item {
  display: flex;
  flex: 1;
  text-align: center;
  align-items: center;
  justify-content: center;
}
.emptyBox{
  flex: 1;
}

二、创建calendar-item组件

calendar-item.wxml

<view class="itemBox" style="display: flex;" wx:for="{{dateList}}" wx:key="index">
  <view class="day-box" wx:for="{{item}}" wx:key="cIndex" wx:for-index="cIndex" bindtap="dayItemClick"
    data-item="{{item}}" data-index="{{index}}">
    <view class="day-item-box">
      <text class="dayBox {{index == chooseIndex&&chooseDay==item.day && item.date == chooseDate?'choose-day':''}}">{{item.day}}</text>
      <view class="auctionWay">{{item.type}}</view>
    </view>
    <view class="auctionNumber" wx:if="{{item.num}}">{{item.num}}</view>
  </view>
</view>

calendar-item.wxss

/* component/calendar-item/calendar-item.wxss */
.day-box {
  flex: 1;
  display: flex;
  height: 80rpx;
  align-items: center;
  justify-content: center;
  position: relative;
}

.day-item-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.dayBox{
  width: 50rpx;
  height: 50rpx;
  display:flex;
  justify-content: center;
  line-height: 50rpx;
  border-radius: 50%;
  color: #333333;
  font-size: 26rpx;
}

.todayBox{
  color:b4292d;
  background:#cae6fe;
}

.choose-day {
  color: #fff;
  background: black;
}

.auctionWay {
  height: 20rpx;
  line-height: 20rpx;
  font-size: var(--textMiniFontSize);
  color: var(--textBlackColor);
  font-weight:400;
}

.auctionNumber{
  color: white;
  background: var(--themeColor);
  border-radius: 50%;
  width: 20rpx;
  height: 20rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  position:absolute;
  top:5rpx;
  right: 15rpx;
  font-size: var(--textMiniFontSize);
}

calendar-item.json

{
  "component":true,
  "usingComponents": {}
}

calendar-item.js

// component/calendar-item/calendar-item.js
Component({
  // 可以用app.wxss 样式
  options: {
    addGlobalClass: true,
  },

  /**
   * 组件的属性列表
   */
  properties: {
    type:{
      type:String,
      value:''
    }
  },

  /**
   * 组件的初始数据
   */
  data: {
    dateList:[],
    chooseIndex:-1,
    chooseDay:-1,
  },

  /**
   * 组件的方法列表
   */
  methods: {
    dayItemClick(e){
      var item = e.currentTarget.dataset.item
      var index = e.currentTarget.dataset.index

      if (item.day) {
        this.setData({
          chooseDay:item.day,
          chooseIndex:index,
          chooseDate:item.date
        });
      }
      this.triggerEvent("chooseDateEvent",{date:item.date,chooseDay:item.day,chooseIndex:index,type:this.data.type})
    },
  }
})

三、创建需要用到日历的页面index

index.wxml

<view>
	<view class="headerBox">
		<text class="headerTitle">拍卖</text>
		<view class="rightBox">
			<uni-icon iconSrc="/static/images/calendar_icon.png" iconWidth="48" bindtap="showCalendar"></uni-icon>
		</view>
	</view>
	<view class="title">
		<view wx:for="{{activeList}}" class="tabItemBox" bindtap="changeIndex" data-index='{{index}}' wx:key="index">
			<text class="tititem {{isActive == index?'active':''}}">{{item}}</text>
			<view class="line" wx:if="{{isActive == index}}"></view>
		</view>
	</view>
</view>
<uni-calendar id="calendarID"  bind:chooseDateEvent="getDataByDate"></uni-calendar>

index.wxss

.headerBox{
  background: var(--themeColor);
  height: var(--homeHeaderTop);
  display: flex;
  width: 100%;
  box-sizing: border-box;
  align-items: center;
  justify-content: space-between;
  padding: 0 3%;
}
.headerTitle{
  color: white;
  font-weight: 500;
  font-size: 34rpx;
}

index.js

// pages/tabBar/user-center/index.js
Page({

  /**
   * 页面的初始数据
   */
  data: {
    // 日历组件
    calendar: '',
  },
  // 选择了日期进行数据查询
  getDataByDate(e) {
    var date = e.detail.date
    if (date) {
      console.log("选中的日期",date)
    } 
  },
  // 显示日历 
  showCalendar() {
    var calendarIsShow = this.data.calendar.data.isShow;
    this.data.calendar.setData({//设置子组件实例中的数据isShow
      isShow: !calendarIsShow
    })
    if (!calendarIsShow) { //只有在显示状态下才调用 initCanlendarData
      this.data.calendar.initCanlendarData(); //调用子组件实例中的方法initCanlendarData()初始化日历数据
    }
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    
  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function () {
    this.data.calendar = this.selectComponent("#calendarID");//获取组件实例
  },
 })
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

swagLi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值