• 微信小程序 | 吐血整理的日历及日程时间管理


    📌个人主页个人主页
    ​🧀 推荐专栏小程序开发成神之路 --【这是一个为想要入门和进阶小程序开发专门开启的精品专栏!从个人到商业的全套开发教程,实打实的干货分享,确定不来看看? 😻😻】
    📝作者简介一个读过研、创过业,从全栈工程师转行做大数据开发的大厂人!
    ⭐️您的小小关注是我持续输出的动力!⭐️


    干货内容推荐

    🥇入门和进阶小程序开发,不可错误的精彩内容🥇 :



    需求背景

    在小程序项目开发过程中,我们要实现时间管理功能,除了从时间维度方面入手,很多时候日历与日程的管理也是众多时间管理场景中必备的一些功能。

    本文总结了时下常用的时间管理功能,从简单的日历显示,到日程规划,再到与日历息息相关的打卡签到功能也是全方面覆盖!收藏起来吧,总有一天你会感谢我的😁😂🤣😃😄😅😆


    一、效果预览

    1.1 日历带日程管理

    在这里插入图片描述

    1.2 农历阳历结合

    在这里插入图片描述

    1.3 日历带日程样式优化

    在这里插入图片描述

    1.4 日历之打卡签到与补签

    在这里插入图片描述


    二、源码获取

    2.1 日历带日程管理

    2.1.1 应用建议

    该日历是一个常规的日历显示,优点在于配套了日程管理以及背景样式有当前月份显示。应用时可以根据自己的需求将日程管理进行优化!


    2.1.2 完整源码
    <template>
    	<view>
    		<view class="date-box">
    			<view class="box-list" :style="{'margin-bottom' : list.length > 0 ? '20rpx' : '0'}">
    				<view class="date-top">
    					<view class="icon left-icon" @click="LastYear">
    						<view class="iconfont icon-jiantou_xiangzuoliangci"></view>
    					</view>
    					<view class="conter-text">
    						<view class="icon left-icon" @click="LastMonth">
    							<view class="iconfont icon-xiangzuo1"></view>
    						</view>
    						<text class="month">{{year}}{{month}}</text>
    						<view class="icon rigth-icon" @click="NextMonth">
    							<view class="iconfont icon-xiangyou1"></view>
    						</view>
    					</view>
    					<view class="icon rigth-icon" @click="NextYear">
    						<view class="iconfont icon-jiantou_xiangyouliangci"></view>
    					</view>
    				</view>
    				<view class="date-week">
    					<view class="week-item" v-for="item in weekList" :key="item"><text>{{item}}</text></view>
    				</view>
    				<view class="day-content" :style="{height: isOpen ? '100rpx' : 'auto'}" v-if="dayList.length > 0">
    					<view class="day-item day-month" v-if="!isOpen"><text>{{month < 10 ? `0${month}` : month}}</text></view>
    					<view 
    						class="day-item" 
    						v-for="(item, index) in dayList"
    						:key="index"
    						:data-index="index"
    						@click="toActive(item, index)">
    						<text class="day-text" v-if="item.day" :class="{ 'actives' : item.day === day }" >{{item.day ? item.day : ''}}</text>
    						<text class="value-text" v-if="item.data.status">{{item.data.value}}</text>
    						<text class="value-text text-red" v-else>{{item.data.value}}</text>
    						<text class="day-dot" v-if="item.data.dot && item.data.active"></text>
    						<text class="day-dot dot-gray" v-if="item.data.dot && !item.data.active"></text>
    					</view>
    				</view>
    				<view style="width: 100%;"  v-if="isShrink">
    					<view class="toggle" v-if="isOpen" @click="toShrinkClose">
    						<view class="iconfont icon-shousuo"></view>
    					</view>
    					<view class="toggle" v-else @click="toShrink">
    						<view class="iconfont icon-zhankai"></view>
    					</view>
    				</view>
    			</view>
    			<slot name="task">
    			<view class="task-box" v-if="list.length > 0">
    				<view class="task-item" v-for="(item, index) in list" :key="index" @click="toTask(item, index)">
    					<view class="avatar-box">
    						<view class="avatar">
    							 <image :src="item.avatar"></image>
    						</view>
    						<view class="title-box">
    							<view class="title"><text>{{item.title}}</text></view>
    							<view class="date"><text class="branch">时间:{{item.time}}</text><text>{{item.details}}</text></view>
    						</view>
    					</view>
    					<view class="time"><text>{{item.date}}</text></view>
    				</view>
    			</view>
    			</slot>
    		</view>
    		<view class="modal" v-if="show">
    			<view class="mask" @click="close" v-if="closeOnClickOverlay"></view>
    			<view class="z-content">
    				<view class="modal-content">
    					<view class="z-modal" :style="{width: width}">
    						<view class="modal-title"><slot name="title"><text>{{title}}</text></slot></view>
    						<view class="z-modal-content"><slot name="content"><text>{{content}}</text></slot></view>
    						<view class="line"></view>
    						<view class="modal-foot">
    							<slot name="footer">
    								<view class="cancel" @click="cancel" v-if="showCancelButton">
    									<text :style="{color: cancelColor}">{{cancelText}}</text>
    								</view>
    								<view class="foot-line" v-if="showCancelButton && showConfirmButton"></view>
    								<view class="confirm" @click="confirm" v-if="showConfirmButton">
    									<text :style="{color: confirmColor}">{{confirmText}}</text>
    								</view>
    							</slot>
    						</view>
    					</view>
    				</view>
    			</view>
    		</view>
    	</view>
    </template>
    
    <script>
    	export default {
    		name: 'zyfDate',
    		props:{
    			list:{
    				type: Array,
    				default: () => {
    					return []
    				}
    			},
    			weekList:{
    				type:Array,
    				default:() => ['日', '一', '二', '三', '四', '五', '六']
    			},
    			date:{
    				type:Object,
    				default:() => {
    					return {
    						year: new Date().getFullYear(),
    						month: parseInt(new Date().getMonth() + 1),
    						day: parseInt(new Date().getDate())
    					}
    				}
    			},
    			extraData: {
    				type: Array,
    				default: ()=> {
    					return [{date: '2022-7-20', value: '签到', status: true, dot: true, active: false},{date: '2022-7-19', value: '未签到', status: false, dot: true, active: true}] // {date: '2020-6-3', value: '签到', dot: true, active: true}
    				}
    			},
    
    			show:{
    				type: Boolean,
    				default: false
    			},
    			
    			title:{
    				type: String,
    				default: ''
    			},
    			
    			content:{
    				type: String,
    				default: '--'
    			},
    			
    			confirmText:{
    				type: String,
    				default: '确认'
    			},
    			
    			cancelText:{
    				type: String,
    				default: '取消'
    			},
    			
    			showConfirmButton:{
    				type: Boolean,
    				default: true
    			},
    			
    			showCancelButton:{
    				type: Boolean,
    				default: false
    			},
    			
    			confirmColor:{
    				type: String,
    				default: '#2979ff'
    			},
    			
    			cancelColor:{
    				type: String,
    				default: '#606266'
    			},
    			
    			closeOnClickOverlay:{
    				type: Boolean,
    				default: true
    			},
    			
    			width:{
    				type: [Number,String],
    				default: '650rpx'
    			},
    			isShrink:{
    				type: Boolean,
    				default: false
    			},
    			isUnfold:{
    				type: Boolean,
    				default: false
    			}
    		},
    		data() {
    			return {
    				dayList:[],
    				year: 2022,
    				month: 10,
    				day: 10,
    				isOpen: false
    			}
    		},
    		onLoad() {
    			
    		},
    		created() {
    			this.isOpen = this.isUnfold
    			const { year, month, day  } = this.date
    			this.year = year
    			this.month = month
    			this.day = day
    			this.initTime()
    			this.initApi(this.year, this.month)
    		},
    		onNavigationBarButtonTap(e){
    			console.log(e)
    			uni.showToast({
    				title: '分享',
    				duration: 2000
    			});
    		},
    		methods: {
    			initTime(){
    				const { year, month, day } = this.getTiemNowDate()
    				this.year = year
    				this.month = month
    				this.day = day
    				//console.log('今日时间为:' + this.year + '-' + this.month + '-' +this.day )
    			},
    			toShrink(){
    				let falg = null
    				const dateArr = this.getTime(this.year, this.month);
    				const line = dateArr.map((item,index) => {
    					if((index % 7) && this.day == item.day){
    						falg = Math.floor(index/7)
    						return Math.floor(index/7)
    					}
    				})
    				
    							this.dayList = dateArr.slice(falg * 7, (falg + 1) * 7)
    				this.isOpen = true
    			},
    			toShrinkClose(){
    				this.dayList = this.getTime(this.year, this.month)
    				this.isOpen = false
    			},
    			getTiemNowDate(){
    				var date 	= new Date()
    				var year 	= date.getFullYear()
    				var month = parseInt(date.getMonth() + 1)
    				var day 	= date.getDate()
    				if(month < 10){
    					month = '0' + month
    				}
    				
    				if(day < 10){
    					day = '0' + day
    				}
    				
    				const resultDate = {
    					year:year,
    					month: parseInt(month),
    					day:parseInt(day)
    				}
    				
    				return resultDate
    			},
    			
    			initApi(year, month) {
    				if(this.isShrink && this.isOpen){
    					this.toShrink()
    				} else {
    					
    					this.dayList = this.getTime(year, month)
    				}
    			},
    			getTime(year, month){
    				
    				return this.creatDayList(year, month)
    			},
    			creatDayList(year, month){
    				const count = this.getDayNum(year, month)
    				const week = new Date(year + '/' + month + '/1').getDay()
    				let list = []
    				for(let i = 1; i <= count; i++ ){
    					let data = {};
    					for(let item of this.extraData){
    						let dateString = item.date;
    						let dateArr = dateString.indexOf('-') !== -1 ? dateString.split('-') : dateString.indexOf('/') !== -1 ? dateString.split('/') :  [];
    						if(dateArr.length === 3 && Number(dateArr[0]) === Number(this.year) && Number(dateArr[1]) === Number(this.month) && Number(dateArr[2]) === Number(i)){
    							data = item
    						}
    					}
    					
    					let obj = {	day:i, data }
    					list.push(obj)
    				}
    				for(let i = 0; i < week; i++){
    					// list.unshift(this.getDayNum(year, month - 1) -i)
    					list.unshift({ day:null, data:{}})
    				}
    				return list
    			},
    			
    			getDayNum(year, month){
    				let dayNum = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    				if((year % 4 !== 0) && (year % 100 === 0) || (year % 400 !== 0)){
    					dayNum[1] = 28
    				}
    				return dayNum[month - 1]
    			},
    			
    			toActive(item, index){
    				this.day = item.day
    				this.$emit('click-active', {year:this.year, month:this.month, day:item.day, date:this.year + '-' + this.month + '-' +this.day, index: index})
    			},
    			
    			toTask(item, index){
    				this.$emit('click-task', {row: item, index: index})
    			},
    			
    			LastMonth(){
    				if(this.month > 1){
    					this.month = this.month - 1
    					this.initApi(this.year, this.month)
    				} else {
    					this.LastYear(false)
    					this.month = 12
    					this.initApi(this.year, this.month)
    				}
    				
    			},
    			
    			NextMonth(){
    				if(this.month < 12){
    					this.month = this.month + 1
    				} else {
    					this.NextYear(false)
    					this.month = 1
    				}
    				this.initApi(this.year, this.month)
    			},
    			
    			LastYear(flag = true){
    				if(this.year > 2000){
    					this.year = this.year - 1
    					if(flag){
    						this.initApi(this.year, this.month)
    					}
    					
    				}
    			},
    			
    			NextYear(flag = true){
    				this.year = this.year + 1
    				this.initApi(this.year, this.month)
    			},
    			
    			confirm(){
    				this.$emit('confirm')
    			},
    			
    			cancel(){
    				this.$emit('cancel')
    			},
    			
    			close(){
    				this.$emit('close')
    			}
    		}
    	}
    </script>
    
    <style lang="scss" scoped>
    	@import "iconfont.css";
    	.date-box{
    		display: flex;
    		flex-direction: column;
    		flex: 1;
    		padding: 20rpx;
    		.box-list{
    			background-color: white;
    			border-radius: 20rpx;
    			.date-top{
    				display: flex;
    				justify-content: space-between;
    				align-items: center;
    				padding: 40rpx 20rpx;
    				.icon{
    					width: 50rpx;
    					height: 50rpx;
    					line-height: 50rpx;
    					image {
    						width: 50rpx;
    						height: 50rpx;
    					}
    				}
    				.conter-text{
    					font-size: 32rpx;
    					font-weight: bold;
    					display: flex;
    					flex-direction: row;
    					align-items: center;
    					justify-content: space-between;
    					.month{
    						padding: 0 40rpx;
    					}
    				}
    			}
    			.date-week{
    				display: flex;
    				align-items: center;
    				justify-content: space-between;
    				flex: 1;
    				padding: 20rpx;
    				border-bottom: 1rpx solid #f3f4f6;
    				.week-item{
    					display: flex;
    					justify-content: center;
    					align-items: center;
    					width: calc(100%/7);
    					height: 52rpx;
    					text-align: center;
    					font-size: 30rpx;
    					color: #909193;
    				}
    			}
    			.day-content{
    				display: flex;
    				flex-direction: row;
    				flex-wrap: wrap;
    				align-items: center;
    				padding: 10rpx 20rpx;
    				position: relative;
    				.day-item{
    					display: flex;
    					flex-direction: column;
    					// justify-content: center;
    					align-items: center;
    					width: calc(100%/7);
    					height: 95rpx;
    					text-align: center;
    					font-size: 32rpx;
    					z-index: 2;
    					position: relative;
    					.day-text{
    						width: 65rpx;
    						height: 65rpx;
    						line-height: 65rpx;
    						// margin-bottom: 5rpx;
    						&.actives{
    							color: #fff;
    							box-sizing: border-box;
    							background-color: #2b85e4;
    							border-radius: 6rpx;
    							border-radius: 50%;
    						}
    					}
    					.value-text{
    						font-size: 24rpx;
    						color: #18b566;
    						&.text-red {
    							color: #dd6161;
    						}
    					}
    					.day-dot{
    						margin-top: 5rpx;
    						background: #dd6161;
    						border-radius: 50%;
    						padding: 6rpx;
    						position: absolute;
    						bottom: 36rpx;
    						&.dot-gray {
    							background: #18b566;
    						}
    					}
    				}
    				.day-month{
    					position: absolute;
    					top: 0;
    					bottom: 0;
    					left: 0;
    					right: 0;
    					display: flex;
    					flex-direction: row;
    					justify-content: center;
    					align-items: center;
    					width: 100%;
    					height: 100%;
    					// color: rgba(231,232,234,.83);
    					font-size: 200px;
    					font-weight: 700;
    					color: #999;
    					opacity: .1;
    					text-align: center;
    					line-height: 1;
    					z-index: 1;
    				}
    			}
    			.toggle{
    				position: relative;
    				padding: 10rpx 0;
    				margin: 10rpx 20rpx 0;
    				display: flex;
    				justify-content: center;
    				&:before{
    					width: calc(50% - 30rpx);
    					border-top: solid 1px #EAEAEA;
    					content: '';
    					display: block;
    					position: absolute;
    					top: 50%;
    					left: 0;
    					transform: translateY(-50%);
    				}
    				&:after{
    					width: calc(50% - 30rpx);
    					border-top: solid 1px #EAEAEA;
    					content: '';
    					display: block;
    					position: absolute;
    					top: 50%;
    					right: 0;
    					transform: translateY(-50%);
    				}
    			}
    		}
    		.task-box{
    			display: flex;
    			flex-direction: column;
    			.task-item{
    				display: flex;
    				flex-direction: row;
    				align-items: center;
    				justify-content: space-between;
    				background-color: white;
    				padding: 20rpx;
    				box-sizing: border-box;
    				border-radius: 10rpx;
    				margin-bottom: 20rpx;
    				.avatar-box{
    					display: flex;
    					.avatar{
    						width: 100rpx;
    						height: 100rpx;
    						margin-right: 20rpx;
    						border-radius: 50%;
    						image{
    							width: 100rpx;
    							height: 100rpx;
    							border-radius: 50%;
    						}
    					}
    					.title-box{
    						display: flex;
    						flex-direction: column;
    						align-content: space-between;
    						.title{
    							font-size: 30rpx;
    							color: #303133;
    							margin-bottom: 15rpx;
    						}
    						.date{
    							font-size: 26rpx;
    							color: #909193;
    							.branch{
    								margin-right: 15rpx;
    							}
    						}
    					}
    				}
    			}
    		}
    	}
    	.modal{
    		display: flex;
    		flex-direction: column;
    		flex: 1;
    		.mask{
    			transition-duration: 450ms;
    			transition-timing-function: ease-out;
    			position: fixed;
    			inset: 0px;
    			z-index: 10070;
    			background-color: rgba(0, 0, 0, 0.5);
    		}
    		.z-content{
    			z-index: 10075;
    			position: fixed;
    			display: flex;
    			align-items: center;
    			justify-content: center;
    			inset: 0px;
    			.modal-content{
    				border-radius: 6px;
    				overflow: hidden;
    				margin-top: 0px;
    				// height: 200px;
    				background-color: #fff;
    				position: relative;
    				.z-modal{
    					width: 289px;
    					border-radius: 6px;
    					overflow: hidden;
    					.modal-title{
    						font-size: 16px;
    						font-weight: 700;
    						color: #606266;
    						text-align: center;
    						padding-top: 25px;
    					}
    					.z-modal-content{
    						padding: 12px 25px 25px 25px;
    						display: flex;
    						flex-direction: row;
    						justify-content: center;
    						font-size: 15px;
    						color: #606266;
    					}
    					.line{
    						margin: 0px;
    						border-bottom: 1px solid rgb(214, 215, 217);
    						width: 100%;
    						transform: scaleY(0.5);
    						border-top-color: rgb(214, 215, 217);
    						border-right-color: rgb(214, 215, 217);
    						border-left-color: rgb(214, 215, 217);
    						vertical-align: middle;
    					}
    					.modal-foot{
    						display: flex;
    						flex-direction: row;
    						font-size: 16px;
    						text-align: center;
    						color: rgb(96, 98, 102);
    						.cancel{
    							flex: 1;
    							display: flex;
    							flex-direction: row;
    							justify-content: center;
    							align-items: center;
    							height: 48px;
    						}
    						.foot-line{
    							margin: 0px;
    							border-left: 1px solid rgb(214, 215, 217);
    							height: 48px;
    							transform: scaleX(0.5);
    							border-top-color: rgb(214, 215, 217);
    							border-right-color: rgb(214, 215, 217);
    							border-bottom-color: rgb(214, 215, 217);
    						}
    						.confirm{
    							flex: 1;
    							display: flex;
    							flex-direction: row;
    							justify-content: center;
    							align-items: center;
    							height: 48px;
    							text{
    								color: rgb(41, 121, 255);
    							}
    						}
    					}
    				}
    				
    			}
    		}
    	}
    </style>
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451
    • 452
    • 453
    • 454
    • 455
    • 456
    • 457
    • 458
    • 459
    • 460
    • 461
    • 462
    • 463
    • 464
    • 465
    • 466
    • 467
    • 468
    • 469
    • 470
    • 471
    • 472
    • 473
    • 474
    • 475
    • 476
    • 477
    • 478
    • 479
    • 480
    • 481
    • 482
    • 483
    • 484
    • 485
    • 486
    • 487
    • 488
    • 489
    • 490
    • 491
    • 492
    • 493
    • 494
    • 495
    • 496
    • 497
    • 498
    • 499
    • 500
    • 501
    • 502
    • 503
    • 504
    • 505
    • 506
    • 507
    • 508
    • 509
    • 510
    • 511
    • 512
    • 513
    • 514
    • 515
    • 516
    • 517
    • 518
    • 519
    • 520
    • 521
    • 522
    • 523
    • 524
    • 525
    • 526
    • 527
    • 528
    • 529
    • 530
    • 531
    • 532
    • 533
    • 534
    • 535
    • 536
    • 537
    • 538
    • 539
    • 540
    • 541
    • 542
    • 543
    • 544
    • 545
    • 546
    • 547
    • 548
    • 549
    • 550
    • 551
    • 552
    • 553
    • 554
    • 555
    • 556
    • 557
    • 558
    • 559
    • 560
    • 561
    • 562
    • 563
    • 564
    • 565
    • 566
    • 567
    • 568
    • 569
    • 570
    • 571
    • 572
    • 573
    • 574
    • 575
    • 576
    • 577
    • 578
    • 579
    • 580
    • 581
    • 582
    • 583
    • 584
    • 585
    • 586
    • 587
    • 588
    • 589
    • 590
    • 591
    • 592
    • 593
    • 594
    • 595
    • 596
    • 597
    • 598
    • 599
    • 600
    • 601
    • 602
    • 603
    • 604
    • 605
    • 606
    • 607
    • 608
    • 609
    • 610
    • 611
    • 612
    • 613
    • 614
    • 615
    • 616
    • 617
    • 618
    • 619
    • 620
    • 621
    • 622
    • 623
    • 624
    • 625
    • 626
    • 627
    • 628
    • 629
    • 630
    • 631
    • 632
    • 633
    • 634
    • 635
    • 636
    • 637
    • 638
    • 639
    • 640
    • 641
    • 642
    • 643
    • 644
    • 645
    • 646
    • 647
    • 648
    • 649
    • 650
    • 651
    • 652
    • 653
    • 654
    • 655
    • 656
    • 657
    • 658
    • 659
    • 660
    • 661
    • 662
    • 663
    • 664

    2.2 农历与阳历结合

    2.2.1 应用建议

    该日历因为配置了每天的农历信息,所以很合适用在需要展示农历以及需要根据农历去拓展相应业务的场景。


    2.2.2 完整源码
    <template>
    	<view class="lunc-calendar">
    		<!-- 头部按钮及月份 -->
    		<view class="header">
    			<view class="head-icon head-pre-month" @click="goPreMonth" v-if="shouChangeBtn"><text></text></view>
    			<view class="head-month">{{ selDate.year }}{{ selDate.month<10?'0'+selDate.month:selDate.month }}</view>
    			<view class="head-icon head-next-month" @click="goNextMonth" v-if="shouChangeBtn"><text></text></view>
    			<view class="go-to-today" v-show="showToday" @click="goToday">回到今天</view>
    		</view>
    		<!-- 星期 -->
    		<view class="week-area">
    			<view class="week-font" v-for="(item, index) in weekArr" :key="index ">{{ item }}</view>
    		</view>
    		<view ref="calendar" class="data-container">
    			<!-- 日期 -->
    			<swiper class="calendar-swiper" :current="tranIndex" circular :duration="tranDuration" @change="changeMonth" @animationfinish="tranChangeEnd">
    				<swiper-item class="swiper-item" v-for="(item, index) in allDataArr" :key="index">
    					<view class="bg-month" v-show="showMonthBg">{{ item[15].sMonth<10?'0'+item[15].sMonth:item[15].sMonth }}</view>
    					<view class="month-days">
    						<view class="day" v-for="(d, j) in item" :key="j" @click="clickDay(d)">
    							<view class="day-info" :class="[d.dayClass, (selDate.year == d.sYear && selDate.month == d.sMonth && selDate.day == d.day)?'is-sel':'']">
    								<text class="day-solar">{{d.day}}</text>
    								<text class="day-tag" v-if="d.daySign && JSON.stringify(d.daySign) != '{}'"></text>
    								<text class="day-sign" v-if="d.daySign && JSON.stringify(d.daySign) != '{}'">{{d.daySign.title}}</text>
    								<text class="day-lunar" v-else v-show="showLunar">{{d.dayLunar}}</text>
    							</view>
    						</view>
    					</view>
    				</swiper-item>
    			</swiper>
    			<!-- 收缩按钮 -->
    			<view></view>
    		</view>
    	</view>
    </template>
    <script>
    	let { calendar } = require("./calendar.js");
    	/**
    	 * @property {Boolean} showLunar = [true|false] 是否显示农历,默认false
    	 * @property {String} firstDayOfWeek = [monday|sunday] 周几为每周的第一天,默认monday
    	 *  @value monday 每周从周一开始(默认)
    	 *  @value sunday 每周从周日开始
    	 * @property {Boolean} showMonthBg = [true|false] 是否显示月份背景,默认true
    	 * @property {Boolean} weekend = [true|false] 周末标红(周六周日日期用红色字体),默认false
    	 * @property {Boolean} shouChangeBtn = [true|false] 是否显示上月下月箭头按钮,默认false
    	 * @property {Array} signList 标记数组,若当前有多个标记,则显示最后一个,期待格式[{date: '2021-09-10', title: '生日', info: '八月初四张三生日'}]
    	 * @property {Boolean} isShrink = [true|false] (暂不可用,待完善)是否可收缩,收起来后以周显示,默认false
    	 *  @value true 收缩,只显示当前一周的日期
    	 *  @value false 不收缩,显示当前一个月的日期(默认)
    	 * @event {Function()} dayChange 点击日期触发事件
    	 * @event {Function()} monthChange 切换月份触发事件
    	 */
    	export default {
    		name: 'LuncCalendar',
    		props: {
    			//是否显示农历
    			showLunar: {
    				type: Boolean,
    				default: false
    			},
    			//每周的周几为第一天
    			firstDayOfWeek: {
    				type: String,
    				default: 'monday'
    			},
    			//是否显示月份背景
    			showMonthBg: {
    				type: Boolean,
    				default: true
    			},
    			//周末标红
    			weekend: {
    				type: Boolean,
    				default: false
    			},
    			//是否显示上月下月按钮
    			shouChangeBtn: {
    				type: Boolean,
    				default: false
    			},
    			//标记
    			signList: {
    				type: Array,
    				default () {
    					return []
    				}
    			},
    			//是否可收缩,收起来后以周显示(待完善)
    			isShrink: {
    				type: Boolean,
    				default: false
    			}
    		},
    		data() {
    			return {
    				weekArr: ['一', '二', '三', '四', '五', '六', '日'], //星期数组
    				allDataArr: [], //轮播数组
    				tranIndex: 1, //轮播所在位置
    				selDate: {}, //当前轮播日期信息 -> year, month, day, week
    				today: {}, //今天日期 -> year, month, day, week
    				tranDuration: 500, //轮播时间(单位毫秒)
    				showToday: false, //显示回到今天(非当月才显示)
    			}
    		},
    		created() {
    			let nd = new Date();
    			this.today = {
    				year: nd.getFullYear(),
    				month: nd.getMonth() + 1,
    				day: nd.getDate()
    			}
    			if (this.firstDayOfWeek == "sunday") this.weekArr = ['日', '一', '二', '三', '四', '五', '六'];
    			else this.weekArr = ['一', '二', '三', '四', '五', '六', '日'];
    			this.initDateTime();
    		},
    		watch:{
    			signList(newList){
    				newList.map(info => {
    					let [year, month, day] = info.date.split("-");
    					let allDataArr = this.allDataArr;
    					let mList = [allDataArr[0][15].sMonth, allDataArr[1][15].sMonth, allDataArr[2][15].sMonth];
    					let num = mList.indexOf(parseInt(month));
    					let dayInfo = this.allDataArr[num].find(d => d.sYear == parseInt(year)&&d.sMonth == parseInt(month)&&d.day == parseInt(day));
    					dayInfo.daySign = info;
    				})
    			}
    		},
    		methods: {
    			initDateTime() { //初始化日期时间
    				this.selDate = JSON.parse(JSON.stringify(this.today));
    				//获取月数据
    				let thisMonth = this.getMonthData(this.selDate, true);
    				let preMonth = this.getMonthData(this.getPreMonth(this.selDate), false);
    				let nextMonth = this.getMonthData(this.getNextMonth(this.selDate), false);
    				this.allDataArr = [preMonth, thisMonth, nextMonth];
    				this.tranIndex = 1;
    			},
    			//获取指定月份的数据,selMonth:是否为显示月
    			getMonthData(date, selMonth) {
    				const { year, month, day } = date; //指定的月份
    				let maxDay = new Date(year, month, 0).getDate(); //当前月最大日期
    				let firstWeek = new Date(year + "-" + month + "-1").getDay(); //月份1号的星期数
    				if (this.firstDayOfWeek == "sunday") firstWeek = firstWeek;
    				else firstWeek = firstWeek - 1 < 0 ? 6 : firstWeek - 1;
    				let list = [];
    				//每月显示42天,6周,每周7天
    				for (var i = 0; i < 42; i++) {
    					let dayInfo = {};
    					if (i < firstWeek) { //指定月份上月的最后几天
    						let preYear = this.getPreMonth(date).year; //指定月份的上月
    						let preMonth = this.getPreMonth(date).month;
    						let preMaxDay = new Date(preYear, preMonth, 0).getDate(); //上月最大日期
    						let day = preMaxDay - firstWeek + i + 1;
    						dayInfo = this.getDayInfo({ year: preYear, month: preMonth, day: day }, 'pre');
    					} else if (i > maxDay + firstWeek - 1) { //指定月份下月的前几天
    						let nextYear = this.getNextMonth(date).year; //指定月份的下个月
    						let nextMonth = this.getNextMonth(date).month;
    						let day = i - maxDay - firstWeek + 1;
    						dayInfo = this.getDayInfo({ year: nextYear, month: nextMonth, day: day }, 'next');
    					} else {
    						let day = i - firstWeek + 1;
    						dayInfo = this.getDayInfo({ year: year, month: month, day: day }, 'normal');
    					}
    					list.push(dayInfo)
    				}
    				return list;
    			},
    			//获取当前日期的详细信息
    			getDayInfo(date, showDay) {
    				const { year, month, day } = date;
    				let dayType = showDay; //日期是否在指定月份
    				let isToday = false; //是否今天
    				if (year == this.today.year && month == this.today.month && day == this.today.day) isToday = true;
    				let lunar = calendar.solar2lunar(year, month, day); //农历
    				let dayLunar = lunar.IDayCn == '初一' ? lunar.IMonthCn + lunar.IDayCn : lunar.IDayCn;
    				let isFestival = false; //是否为节假日
    				if (lunar.lunarFestival || lunar.festival) {
    					if (lunar.lunarFestival) dayLunar = lunar.lunarFestival;
    					if (lunar.festival) dayLunar = lunar.festival;
    					isFestival = true;
    				}
    				let week = new Date(year + "-" + month + "-" + day).getDay(); //星期数
    				let daySign = {}; //日历中显示标记
    				this.signList.map(item => {
    					let sY = new Date(item.date).getFullYear();
    					let sM = new Date(item.date).getMonth() + 1;
    					let sD = new Date(item.date).getDate();
    					if (sY == year && sM == month && sD == day) daySign = item;
    				});
    				let dayInfo = { day, sYear: year, sMonth: month, week, lunar, dayLunar, isFestival, isToday, dayType, daySign }
    				let dayClass = this.getDayClass(dayInfo);
    				if (dayClass) dayInfo["dayClass"] = dayClass;
    				return dayInfo;
    			},
    			//根据日期详细信息添加对应的class
    			getDayClass(dayInfo) {
    				const { year, month, day } = this.selDate;
    				let dClass = "";
    				if (dayInfo.isToday) dClass += ' is-today';
    				if (this.showLunar && dayInfo.isFestival) dClass += ' is-festival';
    				if (dayInfo.dayType != 'normal') dClass += ' un-month';
    				if (this.weekend && (dayInfo.week == 0 || dayInfo.week == 6)) dClass += ' week-end';
    				return dClass
    			},
    			//滑动切换轮播图
    			changeMonth(e) {
    				let current = e.detail.current;
    				let oldIndex = this.tranIndex;
    				this.tranIndex = current;
    				if (current != oldIndex) {
    					if (oldIndex - current == -1 || oldIndex - current == 2) this.getNextData();
    					else this.getPreData();
    				}
    				//判断是否为本月
    				if (this.selDate.year == this.today.year && this.selDate.month == this.today.month) this.showToday = false;
    				else this.showToday = true;
    			},
    			tranChangeEnd() {
    				this.tranDuration = 500; // 动画结束重新设置动画时间
    			},
    			//回到今天按钮
    			goToday() {
    				if (this.tranDuration != 0) this.tranDuration = 0;
    				this.initDateTime();
    
    				let today = this.today;
    				let dayInfo = this.allDataArr[this.tranIndex].filter(item => (item.sYear == today.year && item.sMonth ==
    					today.month && item.day == today.day))[0];
    				if (dayInfo) this.clickDay(dayInfo);
    				this.$emit('monthChange', {
    					year: new Date().getFullYear(),
    					month: new Date().getMonth() + 1,
    					type: "today"
    				});
    			},
    			//点击上月按钮
    			goPreMonth() {
    				if (this.tranDuration != 0) this.tranDuration = 0;
    				let current = this.tranIndex - 1 < 0 ? 2 : this.tranIndex - 1;
    				this.tranIndex = current;
    				this.getPreData();
    			},
    			//点击下月按钮
    			goNextMonth() {
    				if (this.tranDuration != 0) this.tranDuration = 0;
    				let current = this.tranIndex + 1 > 2 ? 0 : this.tranIndex + 1;
    				this.tranIndex = current;
    				this.getNextData();
    			},
    			//获取上上月数据
    			getPreData() {
    				let num = this.tranIndex - 1 < 0 ? 2 : this.tranIndex - 1; // 上上月数据在 所有数据数组中的下标
    				let preMonth = this.getPreMonth(this.selDate); //获取上月月份
    				let preMonthData = this.getMonthData(this.getPreMonth(preMonth), false); //获取上上月的月份数据
    				this.allDataArr.splice(num, 1, preMonthData); //上上月的数据
    				this.selDate = preMonth;
    
    				let dayInfo = this.allDataArr[this.tranIndex].filter(item => (item.sYear == preMonth.year && item.sMonth ==
    					preMonth.month && item.day == preMonth.day))[0];
    				if (dayInfo) this.clickDay(dayInfo);
    				this.$emit('monthChange', {
    					year: preMonth.year,
    					month: preMonth.month,
    					type: "pre"
    				});
    			},
    			//获取下下月数据
    			getNextData() {
    				let num = this.tranIndex + 1 > 2 ? 0 : this.tranIndex + 1;
    				let nextMonth = this.getNextMonth(this.selDate);
    				let nextMonthData = this.getMonthData(this.getNextMonth(nextMonth), false);
    				this.allDataArr.splice(num, 1, nextMonthData);
    				this.selDate = nextMonth;
    				
    				let dayInfo = this.allDataArr[this.tranIndex].filter(item => (item.sYear == nextMonth.year && item
    					.sMonth == nextMonth.month && item.day == nextMonth.day))[0];
    				if (dayInfo) this.clickDay(dayInfo);
    				this.$emit('monthChange', {
    					year: nextMonth.year,
    					month: nextMonth.month,
    					type: "next"
    				});
    			},
    			//获取上月的月份
    			getPreMonth(date) {
    				let year = date.month - 1 < 1 ? date.year - 1 : date.year;
    				let month = date.month - 1 < 1 ? 12 : date.month - 1;
    				let day = this.getOtherMonthDay(year, month, date.day);
    				return {
    					year,
    					month,
    					day
    				}
    			},
    			//获取下月的月份
    			getNextMonth(date) {
    				let year = date.month + 1 > 12 ? date.year + 1 : date.year;
    				let month = date.month + 1 > 12 ? 1 : date.month + 1;
    				let day = this.getOtherMonthDay(year, month, date.day);
    				return {
    					year,
    					month,
    					day
    				}
    			},
    			getOtherMonthDay(year, month, day){
    				let monthDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    				let februaryDay = new Date(year, month, 0).getDate();
    				monthDays.splice(1, 1, februaryDay);
    				let maxDay = day;
    				if (maxDay > monthDays[month - 1]) maxDay = monthDays[month - 1];
    				return maxDay;
    			},
    			//点击日期
    			clickDay(dayInfo) {
    				if (this.selDate.day == dayInfo.day && this.selDate.month == dayInfo.month && this.selDate.year == dayInfo.year) return;
    				this.selDate.day = dayInfo.day;
    				if (dayInfo.dayType == 'normal') {
    					this.selDate = {
    						year: dayInfo.sYear,
    						month: dayInfo.sMonth,
    						day: dayInfo.day
    					}
    				} else if (dayInfo.dayType == 'pre') {
    					this.goPreMonth();
    					return;
    				} else if (dayInfo.dayType == 'next') {
    					this.goNextMonth();
    					return;
    				}
    				let WeekArr = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"];
    				let lunar = {
    					Animal: dayInfo.lunar.Animal,
    					gzYear: dayInfo.lunar.gzYear,
    					gzMonth: dayInfo.lunar.gzMonth,
    					gzDay: dayInfo.lunar.gzDay,
    					IMonthCn: dayInfo.lunar.IMonthCn,
    					IDayCn: dayInfo.lunar.IDayCn,
    					festival: dayInfo.lunar.festival,
    					lunarFestival: dayInfo.lunar.lunarFestival
    				}
    				let returnData = {
    					date: dayInfo.sYear + "-" + (dayInfo.sMonth < 10 ? '0' + dayInfo.sMonth : dayInfo.sMonth) + "-" + (
    						dayInfo.day < 10 ? '0' + dayInfo.day : dayInfo.day),
    					day: dayInfo.day,
    					month: dayInfo.sMonth,
    					year: dayInfo.sYear,
    					week: WeekArr[dayInfo.week],
    					daySign: dayInfo.daySign
    				}
    				if (this.showLunar) returnData["lunar"] = lunar;
    				this.$emit('dayChange', returnData);
    			}
    		}
    	}
    </script>
    <style lang="scss">
    	.lunc-calendar {
    		background-color: #FFF;
    		margin-bottom: 40rpx;
    
    		.header {
    			display: flex;
    			flex-direction: row;
    			justify-content: center;
    			position: relative;
    			height: 90rpx;
    			line-height: 90rpx;
    			border-bottom: 1px solid #DDD;
    
    			.head-month {
    				font-size: 36rpx;
    				text-align: center;
    				padding: 0 40rpx;
    			}
    
    			.head-icon {
    				width: 60rpx;
    				text-align: center;
    				align-items: center;
    				justify-content: center;
    
    				text {
    					display: inline-block;
    					width: 20rpx;
    					height: 20rpx;
    					border-top: 4rpx solid #AAA;
    					border-left: 4rpx solid #AAA;
    				}
    			}
    
    			.head-pre-month {
    				transform: rotate(-45deg);
    			}
    
    			.head-next-month {
    				transform: rotate(135deg);
    			}
    
    			.go-to-today {
    				position: absolute;
    				right: 0;
    				top: 20rpx;
    				padding: 8rpx 12rpx 8rpx 22rpx;
    				background-color: rgba(255, 184, 0, 1);
    				border-radius: 22rpx 0 0 22rpx;
    				font-size: 24rpx;
    				line-height: 24rpx;
    				color: #FFF;
    			}
    		}
    
    		.week-area {
    			display: flex;
    			flex-direction: row;
    			align-items: center;
    			border-bottom: 1px solid #EEE;
    			padding: 16rpx 0;
    			margin: 0 20rpx;
    
    			.week-font {
    				flex: 1;
    				text-align: center;
    				color: #666;
    				font-size: 14px;
    			}
    		}
    
    		.data-container {
    			margin: 0 20rpx;
    			
    			.calendar-swiper{
    				height: 550rpx;
    			}
    
    			.swiper-item {
    				position: relative;
    				display: -webkit-box;
    
    				.bg-month {
    					position: absolute;
    					font-size: 200px;
    					font-weight: bold;
    					top: 50%;
    					left: 50%;
    					transform: translate(-50%, -55%);
    					opacity: 0.05;
    					z-index: -1;
    					word-break: initial;
    				}
    
    				.month-days {
    					margin-top: 15rpx;
    					display: block;
    
    					.day {
    						width: 14.28%;
    						display: inline-block;
    						text-align: center;
    						height: 84rpx;
    						color: #000;
    						padding: 0 6rpx;
    						box-sizing: border-box;
    
    						.day-info {
    							height: 100%;
    							overflow: hidden;
    							display: flex;
    							flex-direction: column;
    							justify-content: center;
    							align-items: center;
    							position: relative;
    
    							.day-solar {
    								display: block;
    								font-size: 34rpx;
    								line-height: 34rpx;
    							}
    
    							.day-lunar {
    								color: #666;
    								font-size: 24rpx;
    								line-height: 24rpx;
    								transform: scale(0.8);
    								white-space: nowrap;
    							}
    
    							.day-sign {
    								color: rgba(247, 88, 88, 1);
    								font-size: 24rpx;
    								line-height: 24rpx;
    								transform: scale(0.8);
    								white-space: nowrap;
    							}
    
    							.day-tag {
    								position: absolute;
    								top: 6rpx;
    								right: 14rpx;
    								width: 10rpx;
    								height: 10rpx;
    								border-radius: 50%;
    								display: inline-block;
    								background-color: rgba(247, 88, 88, 1);
    							}
    						}
    
    						//非当前月日期
    						.un-month text {
    							opacity: 0.25;
    						}
    
    						//周末
    						.week-end .day-solar {
    							color: rgba(255, 137, 137, 1);
    						}
    
    						//节假日
    						.is-festival .day-lunar,
    						.is-festival .day-solar {
    							color: rgba(247, 88, 88, 1);
    						}
    
    						//今天日期
    						.is-today .day-solar,
    						.is-today .day-lunar {
    							color: #409eff;
    						}
    
    						//选择日期
    						.is-sel {
    							background-color: rgba(198, 226, 255, 1);
    						}
    					}
    
    				}
    			}
    		}
    	}
    </style>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451
    • 452
    • 453
    • 454
    • 455
    • 456
    • 457
    • 458
    • 459
    • 460
    • 461
    • 462
    • 463
    • 464
    • 465
    • 466
    • 467
    • 468
    • 469
    • 470
    • 471
    • 472
    • 473
    • 474
    • 475
    • 476
    • 477
    • 478
    • 479
    • 480
    • 481
    • 482
    • 483
    • 484
    • 485
    • 486
    • 487
    • 488
    • 489
    • 490
    • 491
    • 492
    • 493
    • 494
    • 495
    • 496
    • 497
    • 498
    • 499
    • 500
    • 501
    • 502
    • 503
    • 504
    • 505
    • 506
    • 507
    • 508
    • 509
    • 510
    • 511
    • 512
    • 513
    • 514
    • 515
    • 516
    • 517
    • 518
    • 519
    • 520
    • 521
    • 522
    • 523
    • 524
    • 525
    • 526
    • 527
    • 528
    • 529
    • 530
    • 531
    • 532
    • 533
    • 534
    • 535
    • 536
    • 537
    • 538
    • 539
    • 540
    • 541

    2.3 日历带日程样式优化

    2.3.1 应用建议

    该样例的效果在于日历配合了日程相使用,而且如果用户的某一天的日程安排过多的话,可以实现动态折叠效果。

    2.3.2 完整源码
    <template>
    	
    	
    	<view>
    	<view class="calendar">
    		<view class="top-bar">
    			<image class="top-change-month" @click="turning('prev')" src="../../static/attend-calendar/attendance_left.png"></image>
    			<view class="top-bar-ym">{{ y }}{{ text.year }}{{ m + 1 }}{{ text.month }}</view>
    			<image class="top-change-month" @click="turning('next')" src="../../static/attend-calendar/attendance_right.png"></image>
    		</view>
    
    		<view class="week">
    			<view class="week-day" v-for="(item, index) in weekDay" :key="index">{{ item }}</view>
    		</view>
    
    		<view :class="{ hide: !monthOpen }" class="content" :style="{ height: height }">
    			<view :style="{ top: positionTop + 'upx' }" class="days">
    				<view class="item" v-for="(item, index) in dates" :key="index">
    					<view class="day" @click="selectOne(item, $event)"
    						:class="{ choose: choose == `${item.year}-${item.month + 1}-${item.date}`, nolm: !item.lm }">
    						{{ item.date }}</view>
    					<view class="late" v-if="isLateed(item.year, item.month + 1, item.date)"></view>
    					<view class="truancy" v-if="isTruancyed(item.year, item.month + 1, item.date)"></view>
    					<view class="today-text" v-if="isToday(item.year, item.month, item.date)"></view>
    				</view>
    			</view>
    		</view>
    
    		<image src="../../static/attend-calendar/attendance-arrow-up.png" mode="scaleToFill" @click="trgWeek()" class="weektoggel"
    			:class="{ down: !monthOpen }"></image>
    			
    	</view>
    	<view class="task-box" v-if="list.length > 0">
    		<view class="task-item" v-for="(item, index) in list" :key="index" @click="toTask(item, index)">
    			<view class="avatar-box">
    				<view class="avatar">
    					 <image :src="item.avatar"></image>
    				</view>
    				<view class="title-box">
    					<view class="title"><text>{{item.title}}</text></view>
    					<view class="date"><text class="branch">时间:{{item.time}}</text><text>{{item.details}}</text></view>
    				</view>
    			</view>
    			<view class="time"><text>{{item.date}}</text></view>
    		</view>
    	</view>
    	
    	<view class="modal" v-if="show">
    		<view class="mask" @click="close" v-if="closeOnClickOverlay"></view>
    		<view class="z-content">
    			<view class="modal-content">
    				<view class="z-modal" :style="{width: width}">
    					<view class="modal-title"><slot name="title"><text>{{title}}</text></slot></view>
    					<view class="z-modal-content"><slot name="content"><text>{{content}}</text></slot></view>
    					<view class="line"></view>
    					<view class="modal-foot">
    						<slot name="footer">
    							<view class="cancel" @click="cancel" v-if="showCancelButton">
    								<text :style="{color: cancelColor}">{{cancelText}}</text>
    							</view>
    							<view class="foot-line" ></view>
    							<view class="confirm" @click="confirm">
    								<text style=" color: rgb(71 190 190);">确定</text>
    							</view>
    						</slot>
    					</view>
    				</view>
    			</view>
    		</view>
    	</view>
    	
    	</view>
    </template>
    
    <script>
    	export default {
    		name: 'attend-calendar',
    		props: {
    			// 第一列星期几
    			weekstart: {
    				type: Number,
    				default: 1
    			},
    			// 只有迟到的日期
    			lateddates: {
    				type: Array,
    				default: () => []
    			},
    			// 有旷课的日期
    			truancyeddates: {
    				type: Array,
    				default: () => []
    			},
    			// 是否展开
    			open: {
    				type: Boolean,
    				default: true
    			}
    		},
    		data() {
    			return {
    				show:false,
    				title:'添加日程',
    				content:'与迪丽热巴约会',
    				list: [
    				           {
    				               avatar: 'https://bjetxgzv.cdn.bspapp.com/VKCEYUGU-uni-app-doc/6acec660-4f31-11eb-a16f-5b3e54966275.jpg',
    				               title: '与古力娜扎约会',
    				               time: '45分钟',
    				               details:'首要任务',
    				               date: '10:30'
    				           }
    				        
    				       ],
    				text: {
    					year: '年',
    					month: '月',
    					week: ['一', '二', '三', '四', '五', '六', '日'],
    					today: '今'
    				},
    				y: new Date().getFullYear(), // 年
    				m: new Date().getMonth(), // 月
    				dates: [], // 当前月日期集合
    				positionTop: 0,
    				monthOpen: true,
    				choose: ''
    			}
    		},
    		created() {
    		this.dates = this.monthDay(this.y, this.m)
    		!this.open && this.trgWeek()
    		},
    		mounted() {
    			let date = new Date()
    			let y = date.getFullYear()
    			let m = date.getMonth()
    			let d = date.getDate()
    			this.choose = `${y}-${m + 1}-${d}`
    			console.log(this.choose)
    		},
    		computed: {
    			// 顶部星期栏目
    			weekDay() {
    				return this.text.week.slice(this.weekstart - 1).concat(this.text.week.slice(0, this.weekstart - 1))
    			},
    			height() {
    				return (this.dates.length / 7) * 80 + 'upx'
    			}
    		},
    		methods: {
    			// 获取当前月份天数
    			monthDay(y, m) {
    				let firstDayOfMonth = new Date(y, m, 1).getDay() // 当月第一天星期几
    				let lastDateOfMonth = new Date(y, m + 1, 0).getDate() // 当月最后一天
    				let lastDayOfLastMonth = new Date(y, m, 0).getDate() // 上一月的最后一天
    				let dates = [] // 所有渲染日历
    				let weekstart = this.weekstart == 7 ? 0 : this.weekstart // 方便进行日期计算,默认星期从0开始
    				let startDay = (() => {
    					// 周初有几天是上个月的
    					if (firstDayOfMonth == weekstart) {
    						return 0
    					} else if (firstDayOfMonth > weekstart) {
    						return firstDayOfMonth - weekstart
    					} else {
    						return 7 - weekstart + firstDayOfMonth
    					}
    				})()
    				let endDay = 7 - ((startDay + lastDateOfMonth) % 7) // 结束还有几天是下个月的
    				for (let i = 1; i <= startDay; i++) {
    					dates.push({
    						date: lastDayOfLastMonth - startDay + i,
    						day: weekstart + i - 1 || 7,
    						month: m - 1 >= 0 ? m - 1 : 12,
    						year: m - 1 >= 0 ? y : y - 1
    					})
    				}
    				for (let j = 1; j <= lastDateOfMonth; j++) {
    					dates.push({
    						date: j,
    						day: (j % 7) + firstDayOfMonth - 1 || 7,
    						month: m,
    						year: y,
    						lm: true
    					})
    				}
    				for (let k = 1; k <= endDay; k++) {
    					dates.push({
    						date: k,
    						day: (lastDateOfMonth + startDay + weekstart + k - 1) % 7 || 7,
    						month: m + 1 <= 11 ? m + 1 : 0,
    						year: m + 1 <= 11 ? y : y + 1
    					})
    				}
    				return dates
    			},
    			// 迟到处理
    			isLateed(y, m, d) {
    				let flag = false
    				for (let i = 0; i < this.lateddates.length; i++) {
    					let dy = `${y}-${m}-${d}`
    					if (this.lateddates[i] == dy) {
    						flag = true
    						break
    					}
    				}
    				return flag
    			},
    			// 旷课处理
    			isTruancyed(y, m, d) {
    				let flag = false
    				for (let i = 0; i < this.truancyeddates.length; i++) {
    					let dy = `${y}-${m}-${d}`
    					if (this.truancyeddates[i] == dy) {
    						flag = true
    						break
    					}
    				}
    				return flag
    			},
    			isToday(y, m, d) {
    				let date = new Date()
    				return y == date.getFullYear() && m == date.getMonth() && d == date.getDate()
    			},
    			// 切换成周模式
    			trgWeek() {
    				this.monthOpen = !this.monthOpen
    				if (this.monthOpen) {
    					this.positionTop = 0
    				} else {
    					let index = -1
    					this.dates.forEach((i, x) => {
    						this.isToday(i.year, i.month, i.date) && (index = x)
    					})
    					this.positionTop = -((Math.ceil((index + 1) / 7) || 1) - 1) * 80
    				}
    			},
    			// 点击回调
    			selectOne(i, event) {
    				let date = `${i.year}-${i.month + 1}-${i.date}`
    				if (i.month != this.m) {
    					console.log('不在可选范围内')
    					return false
    				}
    				this.choose = date
    				this.$emit('on-click', date)
    				this.show = true
    			},
    			// 上个月,下个月
    			turning(_action) {
    				if (_action === 'next') {
    					if (this.m + 1 == 12) {
    						this.m = 0
    						this.y = this.y + 1
    					} else {
    						this.m = this.m + 1
    					}
    				} else {
    					if (this.m + 1 == 1) {
    						this.m = 11
    						this.y = this.y - 1
    					} else {
    						this.m = this.m - 1
    					}
    				}
    
    				this.dates = this.monthDay(this.y, this.m)
    			},
    			confirm(){
    				this.show=false
    				this.list.push( {
    				               avatar: 'https://bjetxgzv.cdn.bspapp.com/VKCEYUGU-uni-app-doc/6acec660-4f31-11eb-a16f-5b3e54966275.jpg',
    				               title: '与迪丽热巴约会',
    				               time: '45分钟',
    				               details:'首要任务',
    				               date: '10:30'
    				           })
    			}
    		}
    	}
    </script>
    
    <style lang="scss" scoped>
    	.calendar {
    		color: #fff;
    		font-size: 28upx;
    		text-align: center;
    		background-color: #2356FE;
    		padding-bottom: 10upx;
    		.top-bar {
    			display: flex;
    			height: 80upx;
    			>view {
    				flex: 1;
    			}
    			.top-bar-ym{
    				font-size: 32upx;
    				height: 80upx;
    				line-height: 80upx;
    			}
    			.top-change-month{
    				height: 80upx;
    				width: 80upx;
    			}
    		}
    
    		.week {
    			display: flex;
    			align-items: center;
    			height: 80upx;
    			line-height: 80upx;
    			view {
    				flex: 1;
    			}
    			.week-day{
    				font-size: 24upx;
    				color: #A9C4FF;
    			}
    		}
    
    		.content {
    			position: relative;
    			overflow: hidden;
    			transition: height 0.4s ease;
    
    			.days {
    				transition: top 0.3s;
    				display: flex;
    				align-items: center;
    				flex-wrap: wrap;
    				position: relative;
    
    				.item {
    					position: relative;
    					display: block;
    					height: 80upx;
    					line-height: 80upx;
    					width: calc(100% / 7);
    
    					.day {
    						font-style: normal;
    						display: inline-block;
    						vertical-align: middle;
    						width: 60upx;
    						height: 60upx;
    						line-height: 60upx;
    						overflow: hidden;
    						border-radius: 14upx;
    
    						&.choose {
    							background-color: #3DB7BA;
    							color: #FFFFFF;
    						}
    
    						&.nolm {
    							color: #fff;
    							opacity: 0.3;
    						}
    					}
    
    					.late {
    						bottom: 0;
    						left: 50%;
    						font-style: normal;
    						width: 12upx;
    						height: 12upx;
    						background: #F7B300;
    						border-radius: 6upx;
    						position: absolute;
    						margin-left: -6upx;
    						pointer-events: none;
    					}
    					.truancy {
    						bottom: 0;
    						left: 50%;
    						font-style: normal;
    						width: 12upx;
    						height: 12upx;
    						background: #FF2222;
    						border-radius: 6upx;
    						position: absolute;
    						margin-left: -6upx;
    						pointer-events: none;
    					}
    
    					.today-text {
    						position: absolute;
    						font-size: 20upx;
    						font-weight: normal;
    						width: 20upx;
    						height: 20upx;
    						line-height: 20upx;
    						right: 0;
    						top: 10upx;
    						color: #fff;
    					}
    				}
    			}
    		}
    
    		.hide {
    			height: 80upx !important;
    		}
    
    		.weektoggel {
    			width: 80upx;
    			height: 40upx;
    			margin: 10upx auto 0;
    
    			&.down {
    				transform: rotate(180deg);
    			}
    		}
    		
    		
    	}
    	.modal{
    		display: flex;
    		flex-direction: column;
    		flex: 1;
    		.mask{
    			transition-duration: 450ms;
    			transition-timing-function: ease-out;
    			position: fixed;
    			inset: 0px;
    			z-index: 10070;
    			background-color: rgba(0, 0, 0, 0.5);
    		}
    		.z-content{
    			z-index: 10075;
    			position: fixed;
    			display: flex;
    			align-items: center;
    			justify-content: center;
    			inset: 0px;
    			.modal-content{
    				border-radius: 6px;
    				overflow: hidden;
    				margin-top: 0px;
    				// height: 200px;
    				background-color: #fff;
    				position: relative;
    				.z-modal{
    					width: 289px;
    					border-radius: 6px;
    					overflow: hidden;
    					.modal-title{
    						font-size: 16px;
    						font-weight: 700;
    						color: #606266;
    						text-align: center;
    						padding-top: 25px;
    					}
    					.z-modal-content{
    						padding: 12px 25px 25px 25px;
    						display: flex;
    						flex-direction: row;
    						justify-content: center;
    						font-size: 15px;
    						color: #606266;
    					}
    					.line{
    						margin: 0px;
    						border-bottom: 1px solid rgb(214, 215, 217);
    						width: 100%;
    						transform: scaleY(0.5);
    						border-top-color: rgb(214, 215, 217);
    						border-right-color: rgb(214, 215, 217);
    						border-left-color: rgb(214, 215, 217);
    						vertical-align: middle;
    					}
    					.modal-foot{
    						display: flex;
    						flex-direction: row;
    						font-size: 16px;
    						text-align: center;
    						color: rgb(96, 98, 102);
    						.cancel{
    							flex: 1;
    							display: flex;
    							flex-direction: row;
    							justify-content: center;
    							align-items: center;
    							height: 48px;
    						}
    						.foot-line{
    							margin: 0px;
    							border-left: 1px solid rgb(214, 215, 217);
    							height: 48px;
    							transform: scaleX(0.5);
    							border-top-color: rgb(214, 215, 217);
    							border-right-color: rgb(214, 215, 217);
    							border-bottom-color: rgb(214, 215, 217);
    						}
    						.confirm{
    							flex: 1;
    							display: flex;
    							flex-direction: row;
    							justify-content: center;
    							align-items: center;
    							height: 48px;
    							text{
    								color: rgb(41, 121, 255);
    							}
    						}
    					}
    				}
    				
    			}
    		}
    	}
    		
    	
    	
    		.task-box{
    			display: flex;
    			flex-direction: column;
    			.task-item{
    				display: flex;
    				flex-direction: row;
    				align-items: center;
    				justify-content: space-between;
    				background-color: white;
    				padding: 20rpx;
    				box-sizing: border-box;
    				border-radius: 10rpx;
    				margin-bottom: 20rpx;
    				
    				-moz-box-shadow:2px 1px 10px #3333334f; -webkit-box-shadow:2px 1px 10px #3333334f; box-shadow:2px 1px 10px #3333334f;
    				border-radius: 10px;
    				margin: 5px 5px;
    				
    				.avatar-box{
    					display: flex;
    					.avatar{
    						width: 100rpx;
    						height: 100rpx;
    						margin-right: 20rpx;
    						border-radius: 50%;
    						image{
    							width: 100rpx;
    							height: 100rpx;
    							border-radius: 50%;
    						}
    					}
    					.title-box{
    						display: flex;
    						flex-direction: column;
    						align-content: space-between;
    						.title{
    							font-size: 30rpx;
    							color: #303133;
    							margin-bottom: 15rpx;
    						}
    						.date{
    							font-size: 26rpx;
    							color: #909193;
    							.branch{
    								margin-right: 15rpx;
    							}
    						}
    					}
    				}
    			}
    		}
    		
    </style>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451
    • 452
    • 453
    • 454
    • 455
    • 456
    • 457
    • 458
    • 459
    • 460
    • 461
    • 462
    • 463
    • 464
    • 465
    • 466
    • 467
    • 468
    • 469
    • 470
    • 471
    • 472
    • 473
    • 474
    • 475
    • 476
    • 477
    • 478
    • 479
    • 480
    • 481
    • 482
    • 483
    • 484
    • 485
    • 486
    • 487
    • 488
    • 489
    • 490
    • 491
    • 492
    • 493
    • 494
    • 495
    • 496
    • 497
    • 498
    • 499
    • 500
    • 501
    • 502
    • 503
    • 504
    • 505
    • 506
    • 507
    • 508
    • 509
    • 510
    • 511
    • 512
    • 513
    • 514
    • 515
    • 516
    • 517
    • 518
    • 519
    • 520
    • 521
    • 522
    • 523
    • 524
    • 525
    • 526
    • 527
    • 528
    • 529
    • 530
    • 531
    • 532
    • 533
    • 534
    • 535
    • 536
    • 537
    • 538
    • 539
    • 540
    • 541
    • 542
    • 543
    • 544
    • 545
    • 546
    • 547
    • 548
    • 549
    • 550
    • 551
    • 552
    • 553
    • 554
    • 555
    • 556
    • 557
    • 558
    • 559
    • 560
    • 561
    • 562
    • 563
    • 564
    • 565
    • 566
    • 567

    2.4 日历之打卡签到与补签

    2.4.1 应用建议

    该样例在满足了日期展示的前提下,又兼顾了用户的考勤功能。你可以根据用户的打卡信息对用户的考勤数据进行管理。

    2.4.2 完整源码
    <template>
    	<!-- 打卡日历页面 -->
    	<view class='all'>
    		<view class="bar">
    			<!-- 上一个月 -->
    			<view class="previous" @click="changeMonth(-1)">
    				<button class="barbtn">{{langType=='ch'?'上一月':'Last'}}</button>
    			</view>
    			<!-- 显示年月 -->
    			<view class="date">{{nowYear || "--"}}{{nowMonth || "--"}}</view>
    			<!-- 下一个月 -->
    			<view class="next" @click="changeMonth(1)">
    				<button class="barbtn">{{langType=='ch'?'下一月':'Nex/'}}</button>
    			</view>
    		</view>
    		<!-- 显示星期 -->
    		<view class="week-area" >
    			<view class="week-txt" v-for="(item,index) in weeksTxt[langType]" :key="index">{{item}}</view>
    		</view>
    		
    		<view  class="myDateTable">
    			<view v-for="(item,j) in calendarDays" :key="j" class='dateCell'>
    				<view v-if="item.date==undefined||item.date == null" class='cell'></view>
    				<template v-else>
    					<!-- 已签到日期 -->
    					<view v-if="item.isSign == true" class='cell greenColor bgWhite'>
    						{{item.date}}
    					</view>
    					<!-- 漏签 -->
    					<view @click="clickSign(item.date,0)" class="cell outSignStyle"  v-else-if="item.isBeforeToday&&item.isThisMonth">
    						<!-- redColor bgGray -->
    						{{item.date}}
    						<view class="redDot"></view>
    					</view>
    					<!-- 今日未签到-->
    					<view @click="clickSign(item.date,1)" class="cell whiteColor bgBlue" v-else-if="item.date==today&&nowMonth==toMonth&&nowYear==toYear">
    						签到
    					</view>
    					<!-- 当前日期之后 -->
    					<view class="whiteColor cell" v-else>
    						{{item.date}}
    					</view>
    				</template>
    
    			</view>
    		</view>
    	
    	</view>
    </template>
    
    <script>
    	export default {
    		data() {
    			return {
    				calendarDays: [],
    				SignData: [],// 已经签到的数据
    				nowYear: 0, //当前选的年
    				nowMonth: 0, //当前选的月
    				today: parseInt(new Date().getDate()), //系统本日
    				toMonth: parseInt(new Date().getMonth() + 1), //系统本月
    				toYear: parseInt(new Date().getFullYear()), //系统本年
    				weeksTxt: {
    					ch:['日', '一', '二', '三', '四', '五', '六'],
    					en: ['Sun', 'Mon', 'Tues', 'Wed', 'Thur', 'Fri', 'Sat'],
    				},
    			};
    		},
    		props: {
    			isReplenishSign: { // 是否允许过期补签
    				type: Boolean,
    				default: false
    			},
    			isFullCalendar: { // 是否需要填满日历,前后空的格子填入上/下个月的日期
    				type: Boolean,
    				default: true
    			},
    			yearMonth: { // 2022-01 这种格式,默认当前年月
    				type: String,
    				default: new Date().getFullYear()+'-'+  parseInt(new Date().getMonth() + 1)
    			},
    			dataSource: { //已签到的数据源,例: 5、6号已签到: ["2022-01-05","2022-01-06"],兼容个位数前可加可不加0
    				type: Array,
    				default: () => {
    					return []
    				}
    			},
    			langType: { //只是示例一个翻译而已,要想所有都翻译自己可以再加加
    				type: String,
    				default: "ch" //en
    			},
    		},
    		created() {
    			if(!(/en|ch/g.test(this.langType))){
    				this.langType='ch'; // 非中英,则固定中文
    			}
    			const ymArr=this.yearMonth.split('-');
    			this.buildCalendar(ymArr[0], ymArr[1]);
    			this.onSignDataChange(this.dataSource);
    		},
    		watch: {
    			dataSource: 'onSignDataChange',
    		},
    		methods: {
    			clickSign(date, type) { //type=0补签,type=1当日签到	
    				var strTip = "签到";
    				
    				if (type == 0) {
    					if(!this.isReplenishSign){ // 未开启补签,阻止继续执行
    						console.log("————补签功能未开启————");
    						return;
    					}
    					strTip = "补签";
    				}
    				uni.showToast({
    					title:  date + "号" + strTip + "成功",
    					icon: 'success',
    					position:"bottom",
    				});
    				this.$emit('clickChange', this.nowYear + "-" + this.nowMonth + "-" + date); //传给调用模板页面
    				// 父页面要在clickChange里去修改输入的数据源dataSource,否则视图不更新的!
    			},
    			//构建日历数据
    			buildCalendar(y, m){
    				this.nowYear = y;
    				this.nowMonth = m;
    				this.calculateEmptyGrids(y, m);
    				this.calculateDays(y, m);
    				if(this.isFullCalendar){
    					this.fullCell()
    				}
    			},
    			// 监听到签到数据源改变
    			onSignDataChange(newData, oldData = []) {
    				this.SignData = newData;
    				this.matchSign();
    			},
    			//匹配标记已经签到的日子
    			matchSign() {
    				var signs = this.SignData;
    				var daysArr = this.calendarDays;
    				for (var i = 0; i < signs.length; i++) {
    					var current = new Date(this.toIOSDate(signs[i])).getTime(); // ios只认/格式的日期
    					for (var j = 0; j < daysArr.length; j++) {
    						if(current == new Date(this.toIOSDate(daysArr[j].fullDate)).getTime()){
    								daysArr[j].isSign = true;	
    						}
    					}
    				}
    				this.calendarDays = daysArr;
    				console.log(this.calendarDays );
    			},
    			// 计算当月1号前空了几个格子,把它填充在calendarDays数组的前面
    			calculateEmptyGrids(year, month) {
    				//计算每个月时要清零
    				this.calendarDays = [];
    				const firstDayOfWeek = this.getFirstDayOfWeek(year, month);
    				if (firstDayOfWeek > 0) {
    					for (let i = 0; i < firstDayOfWeek; i++) {
    						this.calendarDays.push({
    							date: null, // 显示的日期
    							fullDate: null, // 日期yyyy-mm-dd格式
    							isBeforeToday: true, // 今日之前
    							isSign: false, // 是否签到
    							isThisMonth:false, // 是本月
    						});
    					}
    				}
    			},
    			// 绘制当月天数占的格子,并把它放到days数组中
    			calculateDays(year, month) {
    				const thisMonthDays = this.getMonthDayLength(year, month);
    				const toDate= new Date(this.toYear+'/'+this.toMonth+'/'+this.today);
    				for (let i = 1; i <= thisMonthDays; i++) {
    					const fullDate=year+'-'+month+'-'+ i;
    					const isBeforeToday=new Date(this.toIOSDate(fullDate)) < toDate;
    					this.calendarDays.push({
    						date: i,
    						fullDate,
    						isBeforeToday,
    						isSign: false,
    						isThisMonth:true,
    					});
    				}
    				//console.log(this.calendarDays);
    			},
    			// 切换控制年月,上一个月,下一个月
    			changeMonth(type) {
    				const nowYear = parseInt(this.nowYear);
    				const nowMonth = parseInt(this.nowMonth);
    				const newObj=this.getOperateMonthDate(nowYear,nowMonth,type);
    				this.buildCalendar(newObj.year, newObj.month); // 重新构建日历数据
    				this.$emit('dateChange', this.nowYear + "-" + this.nowMonth); //传给调用模板页面去拿新数据				
    			},
    			// 获取当月共多少天,也就是获取月的最后一天
    			getMonthDayLength(year, month) {
    				return new Date(year, month, 0).getDate()
    			},
    			// 获取当月第一天星期几
    			getFirstDayOfWeek(year, month, day=1) {
    				return new Date(Date.UTC(year, month - 1, day)).getDay();
    			},
    			toIOSDate(strDate){ // iso不认识"-"拼接的日期,所以转/
    				return strDate?strDate.replace(/-/g,'/'):strDate;
    			},
    			// 需要填满格子,上/下个月的日期拉出来填满格子
    			fullCell(){
    				const endDay= this.getMonthDayLength(this.nowYear, this.nowMonth);
    				const beforeEmptyLength = this.getFirstDayOfWeek(this.nowYear, this.nowMonth);
    				const afterEmptyLength =  6 - this.getFirstDayOfWeek(this.nowYear, this.nowMonth, endDay);
    
    
    				const last=this.getOperateMonthDate(this.nowYear,this.nowMonth,-1);
    				const lastMonthEndDay=this.getMonthDayLength(last.year, last.month);
    				for (let i = 0; i < beforeEmptyLength; i++) {
    					const date=lastMonthEndDay - beforeEmptyLength + i + 1;
    					this.calendarDays[i].date=date;
    					this.calendarDays[i].fullDate=last.year+"-"+last.month+"-"+date;
    				}
    				const next=this.getOperateMonthDate(this.nowYear,this.nowMonth,1);
    				for (let i = 1; i <= afterEmptyLength; i++) {
    					this.calendarDays.push({
    						date: i, // 显示的日期
    						fullDate: next.year+"-"+next.month+"-"+i, // 日期yyyy-mm-dd格式
    						isBeforeToday: false, // 今日之前
    						isSign: false, // 是否签到
    						isThisMonth:false, // 是本月
    					});
    				}
    				// console.log(beforeEmptyLength,afterEmptyLength,lastMonthEndDay);
    				
    				// console.log(this.calendarDays);
    			},
    			// 获取加/减一个月的日期
    			getOperateMonthDate(yy,mm,num){
    				let month=parseInt(mm) + 	parseInt(num);
    				let year=parseInt(yy);
    				if(month>12){
    					month=1;
    					year++;
    				}else if(month<1){
    					month=12;
    					year--;
    				}
    				return {
    					month,
    					year,
    				}
    			},
    		}
    	}
    </script>
    
    <style>
    	
    
    	.all {
    		margin-top: 20rpx;
    	}
    
    	.all .bar {
    		display: flex;
    		flex-direction: row;
    		justify-content: space-between;
    		margin: 30rpx 20rpx;
    		padding: 10rpx;
    	}
    
    	.bar .barbtn {
    		height: 30px;
    		line-height: 30px;
    		font-size: 12px;
    	}
    
    	.all .week-area {
    		display: flex;
    		justify-content: space-between;
    		padding: 10px 0;
    		box-sizing: border-box;
    		width: 91vw;
    		margin: 10px auto;
    		border-radius: 10px;
    	}
    	.all .week-txt {
    		text-align: center;
    		width: 13vw;
    	}
    	.myDateTable {
    		margin: 0 auto;
    		width: 91vw;
    		padding: 2vw;
    		border-radius: 10px;
    		background: linear-gradient(#74AADA, #94db98);
    	}
    	.myDateTable .dateCell {
    		width: 13vw;
    		padding: 1vw;
    		display: inline-block;
    		text-align: center;
    		font-size: 16px;
    		box-sizing: border-box;
    		overflow: hidden;
    	}
    
    	.dateCell .cell {
    		display: flex;
    		border-radius: 50%;
    		height: 11vw;
    		justify-content: center;
    		align-items: center;
    		box-sizing: border-box;
    		flex-direction: column;
    	}
    
    
    	.whiteColor {
    		color: #fff;
    	}
    
    	.greenColor {
    		color: #01b90b;
    		font-weight: bold;
    	}
    
    	.bgWhite {
    		background-color: #fff;
    	}
    
    	.bgGray {
    		background-color: rgba(255, 255, 255, 0.42);
    	}
    
    	.bgBlue {
    		font-size: 14px;
    		background-color: #4b95e6;
    	}
    
    	.redColor {
    		color: #ff0000;
    	}
    	.outSignStyle{
    		border:1px solid #ffffff;
    		color: #ffffff;
    	}
    	.redDot{
    		width: 3px;
    		height: 3px;
    		border-radius: 50%;
    		background-color: red;
    	}
    	
    </style>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
  • 相关阅读:
    腾讯WXG前端面经
    Day1--什么是网络安全?网络安全常用术语
    java面试强基(16)
    HBase 与 Hive 的对比
    Linux 网络编程
    SpringBoot在静态方法或工具类中注入Bean及配置参数
    基于Python的MNIST解析图片文件和标签文件实验报告
    【DL】使用pytorch的线性回归简洁实现
    智能指针一些实现分析
    使用Pytorch测试cuda设备的性能(单卡或多卡并行)
  • 原文地址:https://blog.csdn.net/weixin_37797592/article/details/127838772