您现在的位置是:首页 >技术杂谈 >js原生实现pc端日历组件网站首页技术杂谈

js原生实现pc端日历组件

qq_23128255 2024-06-17 11:18:53
简介js原生实现pc端日历组件

业务场景

客户要求日期选择使用pc端的样式,前端在x5平台上,也就是需要用原始html、js、css等实现,笔者使用于 原生javascript封装的PC端日历插件,不依赖任何第三方插件,复制的代码文件较长,可跳到 总结 处直接下载代码。
效果图如下:
在这里插入图片描述
其他效果
在这里插入图片描述

代码实现

主要用js与css完成,有个缺陷:选择月份时,不能跳到选择年,只能选了月份后在主弹框中选择年。

使用方式

浏览器端使用

<!-- 引入相应的js和css, 可以压缩为zane-calendar.min.css和min.js使用 -->
<link href="./zane-calendar.css">
<script src="./zane-calendar.js"></script>

<!-- 需要加时间插件的输入框 -->
<input type="" name="" id="zane-calendar">

初始化
zaneDate({
	elem:'#zane-calendar',
})

也有Webpack 使用,但笔者并不是 vue或者react项目,可参考:
原文github

所有参数说明(组合随意)

{
	elem:'#zane-calendar',  控件的dom原生 注意:仅限制于id选择器(必填参,其他参数皆为可选参)
	type:'day',   			可选类型 day year month time doubleday doubleyear doublemonth doubletime
	lang:'cn',   			可选择语言类型 cn , en 
	width:280,  			插件宽度配置   250 <= X <= 500
	height:300, 			插件高度配置   250 <= X <= 350
	behindTop:10,   		插件与输入框的距离
	format:'yyyy-MM-dd HH:mm:ss',  时间格式化
	begintime:'',  			开始时间  (单选择器默认选择此项)
	endtime:'',             结束时间  (double选择器需要)
	min:'',  				可选取时间最小范围 1900-10-01
	max: '',  				可选取时间最大范围 2099-12-31
	position:'fixed',  		定位方式  暂时只支持 fixed
	event:'click',   		事件方式 暂时只支持 click 
	zindex:100,   			z-index值
	showtime:true,  		是否显示选择时间
	showclean:true,  		是否显示清除按钮
	shownow:true,  			是否显示当前按钮
	showsubmit:true, 		是否显示提交按钮
	haveBotBtns:true, 		是否有底部按钮列表
	mounted:()=>{}, 		插件加载完成之后调用
	change:(fulltime,begintime,endtime)=>{}, 时间变更之后调用
	done:(fulltime,begintime,endtime)=>{},   选择完成之后调用
}

html

使用input或者div等都可,注意 dom节点必须要设置id属性,js中去初始化组件

<input id="demo20" type="text" class="input" placeholder="yyyy-MM-dd">
//或者
<div class="input" id="demo19">我是div框</div>

//初始化
zaneDate({
	elem:'#demo20',
})

js

代码较长,约1500行左右,无法上传为文件,全部代码如下 zane-calendar.js , 可直接跳到文末 总结处,分享了百度云供代码下载

/*!
 * zaneDate Javascript Library 1.1.0
 * https://github.com/wangweianger/zane-data-time-calendar
 * Date : 2017-09-22
 * auther :zane
 */
;( function( global, factory ) {
	"use strict";
	if ( typeof module === "object" && typeof module.exports === "object" ) {
		module.exports = global.document ?
			factory( global, true ) :
			function( w ) {
				if ( !w.document ) {
					throw new Error( "zaneDate requires a window with a document" );
				}
				return factory( w );
			};
	} else {
		factory( global );
	}

} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {

if(noGlobal) require ('./zane-calendar.min.css')

if(!new Date().Format){
	Date.prototype.Format = function (fmt) { //author: meizz 
	    var o = {
	        "M+": this.getMonth() + 1, //月份 
	        "d+": this.getDate(), //日 
	        "h+": this.getHours()>12?this.getHours()-12:this.getHours(), //小时 
			"H+": this.getHours(),
	        "m+": this.getMinutes(), //分 
	        "s+": this.getSeconds(), //秒 
	        "q+": Math.floor((this.getMonth() + 3) / 3), //季度 
	        "S": this.getMilliseconds() //毫秒 
	    };
	    if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
	    for (var k in o)
	    if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
	    return fmt;
	}
};

let  doc 	= document,
	query   = 'querySelector',
	quall 	= 'querySelectorAll';

// 日期插件
class calendar{
	constructor(json={}){
		this.config={
			//控件的dom原生仅限制于id
			elem:'#zane-calendar',
			//可选 day year month time doubleday doubleyear doublemonth doubletime
			type:'day', 
			//absolute , fixed   
			position:'fixed', 
			//cn , en 
			lang:'cn', 
			// 宽度
			width:250,
			// 插件高度配置
			height:280,
			//插件于输入框的高度 
			behindTop:10,
			// 格式化
			format:'yyyy/MM/dd', //'yyyy-MM-dd HH:mm:ss'
			// 初始默认值
			value:'',
			// 可选取时间最小范围
			min:'', //'1900-10-01',
			// 可选取时间最大范围
			max: '', //'2099-12-31',
			//事件方式 click 
			event:'click',
			// z-index的值
			zindex:100,  
			//是否显示选择时间
			showtime:false,  
			//是否显示清除按钮
			showclean:true, 
			//是否显示当前按钮
			shownow:true,  
			//是否显示提交按钮
			showsubmit:true,
			// 是否有底部按钮列表
			haveBotBtns:true,
			// type='time'时是否显示秒单位
			showsecond:true,

			calendarName:'',
			isDouble:false,
			// 插件加载完成之后调用
			mounted:()=>{},
			//时间变更之后调用
			change:()=>{},
			//选择完成之后调用
			done:()=>{},
		}
		this.config = this.extend(this.config,json);
		//校验时间格式
		if(!this.config.value)this.config.value = ''
		if(!this.config.min)this.config.min = ''
		if(!this.config.max)this.config.max = ''

		// 初始化
		this.init();
	}

	// 生成对象obj
	generateCalendarObj(){
		this.obj={
			input:doc[query](this.config.elem),
			calendar:null,
			id:`#zane-calendar-${this.config.calendarName}`,
			$obj:null,
			fulldatas:{},
			$noDoubleObj:null,
			isDoubleOne:false,
			handleType:'date',
			initVal:'',//每次进来的初始值
			// 选择年时展示的数量
			totalYear:18,
			cn:{
		        weeks: ['日', '一', '二', '三', '四', '五', '六'],
		        time: ['时', '分', '秒'],
		        timeTips: '选择时间',
		        startTime: '开始时间',
		        endTime: '结束时间',
		        dateTips: '返回日期',
		        month: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
		        tools: {
		          	confirm: '确定',
		          	clear: '清空',
		          	now: '现在'
		        }
		     },
		     en:{
		        weeks: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
		        time: ['Hours', 'Minutes', 'Seconds'],
		        timeTips: 'Select Time',
		        startTime: 'Start Time',
		        endTime: 'End Time',
		        dateTips: 'Select Date',
		        month: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
		        tools: {
		          	confirm: 'Confirm',
		          	clear: 'Clear',
		          	now: 'Now'
		        }
		      }
		}

		this.vision = '2.0.9'
		this.auther = 'zane'	

		this.obj.lang = this.obj[this.config.lang];

		if(this.config.type == 'year' || this.config.type == 'month'){
			this.config.haveBotBtns = false;
		}

		if(this.config.type == 'time'){
			this.config.showtime =false;
		}

		// double 处理
		if(this.config.isDouble){
			this.obj.input.nodeName !== 'INPUT'?
				this.obj.input.textContent	=  this.config.doublevalue :
				this.obj.input.value  		=  this.config.doublevalue;
		}else if(!this.config.isDouble){
			this.obj.input.nodeName !== 'INPUT'?
				this.obj.input.textContent	=	this.config.value :
				this.obj.input.value  		=  this.config.value;
		}
	}

	init(){
		this.generateCalendarObj()
		this.on(this.obj.input,this.config.event, (e)=>{
			e.preventDefault();
			e.stopPropagation();

			// 隐藏其他时间插件框
			let objs = doc[quall]('.zane-calendar');
			this.forEach(objs,(index,item)=>{
				let itemId = item.getAttribute('id')
				if (('#' + itemId).replace(/DOUBLE/,'') !== this.obj.id.replace(/DOUBLE/,'') ){
					itemId = itemId.replace('zane-calendar-','')
					this.removeCalendar()
				}
			})

			let obj = doc[query](this.obj.id);

			if(obj){
				this.obj.calendar 	= obj;
				this.$obj 			= obj;
			};

			
			// double 赋值
			this.obj.isDoubleOne = this.config.calendarName.indexOf('DOUBLE') != -1?true:false;
			if(this.obj.isDoubleOne ){
				let noDoubleObj 		=  this.config.calendarName.replace(/DOUBLE/,'')
				this.obj.$noDoubleObj 	=  window[noDoubleObj];
				window[noDoubleObj].obj.$noDoubleObj = this;
			};

			// 设置默认值
			let defaultValue,inpValue;
			defaultValue = this.obj.input.nodeName === 'INPUT'?this.obj.input.value.trim():this.obj.input.textContent.trim()
			if(defaultValue){
				// 中文处理
				defaultValue = defaultValue.replace(/[u4e00-u9fa5]+/g,function($a,$b){
					if($a=='年'||$a=='月'){
						return '/'
					}else if($a=='时'||$a=='分'){
						return ':'
					}else if($a=='秒'||$a=='日'){
						return ''
					}
				})
				if(this.config.isDouble){
					let arr = defaultValue.split('-')
					let begintime = arr[1].trim()
					let endtime = arr[0].trim()
					this.config.value = this.obj.isDoubleOne ? begintime : endtime
					this.config.begintime = endtime
					this.config.endtime = begintime
				}else{
					this.config.value = defaultValue
				}

			}

			// 过滤重复生成
			if (doc[query](this.obj.id)) return;

			// 获得年月日
			let html 	= this.objHTML();//生成时间选择器HTML
			var divElement = doc.createElement("div");
			divElement.innerHTML = html
			doc.body.appendChild(divElement)
			
			this.$obj 	= doc[query](this.obj.id);

			switch(this.config.type){
				case 'day':
					this.judgeCalendarRender('day',this.config.value)
					break;
				case 'year':
					this.getYearHtml(this.config.value);
					break;
				case 'month':
					this.getMonthHtml(this.config.value);
					break;
				case 'time':
					this.getTimeHtml(this.config.value);
					break;			
			}
			
			//定位并显示选择器
			this.elemEventPoint(e);
			this.documentClick();
			this.calendarClick(); 
			
			this.obj.initVal = this.obj.input.value;
		});
		this.config.mounted&&this.config.mounted();
	}

	//生成时间选择器区域
	objHTML(json){
		let html =`<div class="zane-calendar" style="width:${this.config.width}px;z-index:${this.config.zindex}" id="${this.obj.id.substring(1)}">
					<div class="zane-model-miss"><div class="zane-model-mask"></div><div class="zane-model-content"></div></div>
					<div class="zane-calendar-one left" style="width:${this.config.width}px;">
						<div class="zane-date-top">
							<div class="common-top top-check-day"></div>
							<div class="common-top top-check-year"></div>	
							<div class="common-top top-check-month"></div>	
							<div class="common-top top-check-time"></div>		
						</div>
						<div class="zane-date-main" style="height:${this.config.height-80}px">
							<div class="common-main main-check-day"></div>
							<div class="common-main main-check-year"></div>
							<div class="common-main main-check-month"></div>
							<div class="common-main main-check-time"></div>
						</div>
						<div class="zane-date-bottom" style="display:${this.config.haveBotBtns||this.config.isDouble?'block':'none'};
												border-left:${this.obj.isDoubleOne?'none':'solid 1px #eee'};">
							<div class="btn-select-time" style="display:${this.config.showtime?'blcok':'none'}">
								<div class="zane-date-left button btn-select-time-item" onclick="${this.config.calendarName}.getTimeHtml()">${this.obj.lang.timeTips}</div>
							</div>	
				 			<div class="zane-date-right">
								<div class="button ${this.config.shownow?'no-right-line':''}" 
									style="display:${this.config.showclean?'blcok':'none'}"
									onclick="${this.config.calendarName}.cleanInputVal()">${this.obj.lang.tools.clear}</div>
								<div class="button ${this.config.showsubmit?'no-right-line':''}"
									style="display:${this.config.shownow&&!this.config.min||this.config.shownow&&!this.config.max?'blcok':'none'}" 
									onclick="${this.config.calendarName}.changeToToday()">${this.obj.lang.tools.now}</div>
								<div class="button" 
									style="display:${this.config.showsubmit?'blcok':'none'}"
									onclick="${this.config.calendarName}.makeSureSelectTime()">${this.obj.lang.tools.confirm}</div>
							</div>
						</div>
					</div>
				</div>`
		return html
	}
	// day - top html   时间选择器选择年月块
	topCheckDayHTML(json){
		let html =`	
		<div onclick="${this.config.calendarName}.preMonth(${json.year},${json.month})" class="zane-date-icom zane-icon-left"></div>`
		if (this.config.lang == 'cn'){
			html += `<div class="zane-icon-center">
				<span onclick="${this.config.calendarName}.getYearHtml(${json.year})">${json.year}</span>
				<span onclick="${this.config.calendarName}.getMonthHtml(${json.month})">${json.month}</span>
			</div>`
		}else{
			html += `<div class="zane-icon-center">
				<span onclick="${this.config.calendarName}.getMonthHtml(${json.month})">${this.weekToEn(json.month)}</span>
				<span onclick="${this.config.calendarName}.getYearHtml(${json.year})">${json.year}</span>
			</div>`
		}
		html +=`<div onclick="${this.config.calendarName}.nextMonth(${json.year},${json.month})" class="zane-date-icom zane-icon-right"></div>`
		return html;
	}

	// day - main html 时间选择器选择日期块
	mainCheckDayHTML(json){
		let html =`
		<div class="week-day"><table class="day" border="0" cellspacing="0">
			<tr>`
			for (let j = 0,len = 7; j<len; j++){
				html += `<th>${this.obj.lang.weeks[j]}</th>`
			}
		html +=`</tr>`
			for (let i = 0,len=json.datalist.length; i < len; i++) {
				let className = json.datalist[i].class||"";
				let sele_act = json.datalist[i].sele_act || '';
				if(json.datalist[i].day === parseInt(json.today)&&json.datalist[i].daytype==='now'){
					className += this.config.isDouble ? ` act_block` : ` active`
				}
				//如果超出min时间或者max时间的,给禁止选中样式
				if((this.config.min!='' && new Date(json.datalist[i].fullday).getTime()<new Date(this.config.min).getTime()) || (this.config.max!='' && new Date(json.datalist[i].fullday).getTime()>new Date(this.config.max).getTime())){
					className+=` calendar-disabled`
				}
				if(i == 0){
					html += `<tr><td data-time="${json.datalist[i].fullday}" class="${className} ${sele_act}"><span>${json.datalist[i].day}</span></td>`;
				}else if(i == len-1){
					html += `<td data-time="${json.datalist[i].fullday}" class="${className} ${sele_act}"><span>${json.datalist[i].day}</span></td></tr>`;
				}else{
					if((i+1)%7 == 0){
						html += `<td data-time="${json.datalist[i].fullday}" class="${className} ${sele_act}"><span>${json.datalist[i].day}</span></td></tr><tr>`;
					}else{
						html += `<td data-time="${json.datalist[i].fullday}" class="${className} ${sele_act}"><span>${json.datalist[i].day}</span></td>`;
					}
				}
			}

		html+=`</table></div>`
		return html;
	}

	// year - top html 时间选择器选择年份状态头部
	topCheckYearHTML(json){
		let html=`
		<div class="zane-date-icom zane-icon-left" onclick="${this.config.calendarName}.perYear(${json.nowyear})"></div>
		<div class="zane-icon-center">
			<span>${json.firstYear}${this.config.lang=='cn'?'年':''}</span>-
			<span>${json.lastYear}${this.config.lang=='cn'?'年':''}</span>
		</div>
		<div class="zane-date-icom zane-icon-right" onclick="${this.config.calendarName}.nextYear(${json.nowyear})"></div>`
		return html;
	}
	// year - main html 时间选择器选择年份状态内容块
	mainCheckYearHTML(json){
		let html=`<div class="week-day">
			<table class="day border-day" border="0" cellspacing="0">`
				for (let i = 0,len=json.datalist.length; i < len; i++) {
					let className = json.datalist[i].class||"";
					if(json.datalist[i].year === json.nowyear){
						className+=` active`
					}
					if(i == 0){
						html+=`<tr><td class="${className}"><span data-year="${json.datalist[i].year}">${json.datalist[i].year}</span></td>`;
					}else if(i == len-1){
						html+=`<td class="${className}"><span data-year="${json.datalist[i].year}">${json.datalist[i].year}</span></td></tr>`;
					}else{
						if((i+1)%3 == 0){
							html+=`<td class="${className}"><span data-year="${json.datalist[i].year}">${json.datalist[i].year}</span></td></tr><tr>`;
						}else{
							html+=`<td class="${className}"><span data-year="${json.datalist[i].year}">${json.datalist[i].year}</span></td>`;
						}
					}
				}
		html+=`</table>
		</div>`
		return html;
	}

	// month -top html 时间选择器选择月份头部
	topCheckMonthHTML(json){
		let html=`
		<div class="zane-date-icom zane-icon-left" style="display:${this.obj.handleType=='month'?'none':'block'}" onclick="${this.config.calendarName}.perMonthYear(${json.year},${json.nowmonth})"></div>
		<div class="zane-icon-center">
			<span>${json.year}年</span>
		</div>
		<div class="zane-date-icom zane-icon-right" style="display:${this.obj.handleType=='month'?'none':'block'}" onclick="${this.config.calendarName}.nextMonthYear(${json.year},${json.nowmonth})"></div>`
		return html;	
	}
	// month -main html 时间选择器选择月份状态内容块
	mainCheckMonthHTML(json){
		let html=`<div class="week-day">
			<table class="day border-day" border="0" cellspacing="0">`
				for (let i = 0,len=json.datalist.length; i < len; i++) {
					let className = json.datalist[i].class||"";
					if((i+1) === parseInt(json.nowmonth)){
						className+=` active`
					}
					if(i == 0){
						html+=`<tr><td class="${className}"><span data-year="${json.year}" data-month="${i+1}">${json.datalist[i]}</span></td>`;
					}else if(i == len-1){
						html+=`<td class="${className}"><span data-year="${json.year}" data-month="${i+1}">${json.datalist[i]}</span></td></tr>`;
					}else{
						if((i+1)%3 == 0){
							html+=`<td class="${className}"><span data-year="${json.year}" data-month="${i+1}">${json.datalist[i]}</span></td></tr><tr>`;
						}else{
							html+=`<td class="${className}"><span data-year="${json.year}" data-month="${i+1}">${json.datalist[i]}</span></td>`;
						}
					}
				}
		html+=`</table>
		</div>`
		return html;
	}

	// time -top  html 时间选择器选择时间状态头部
	topCheckTimeHTML(){
		let html=`<div class="zane-icon-center"><span>${this.obj.lang.timeTips}</span></div>`
		return html;	
	}
	// time -main html 时间选择器选择时间状态内容块
	mainCheckTimeHTML(json){
		let html = `<div class="week-day"><ul class="nav ${this.config.showsecond?'':'nav-1'}">
						<li>${this.obj.lang.time[0]}</li>
						<li>${this.obj.lang.time[1]}</li>
						${this.config.showsecond?'<li>'+this.obj.lang.time[2]+'</li>':''}
					</ul><div class="select-time ${this.config.showsecond?'':'select-time-1'}">
				<ul class="hour">`
				for (let i = 0,len=json.hours.length; i < len; i++) {
					let className='';
					if(json.hours[i] == json.hour) className='active';
					html +=`<li class="${className}" data-time="${json.hours[i]}">${json.hours[i]}</li>`
				}
			html+=`</ul><ul class="minute">`
				for (let i = 0,len=json.minutes.length; i < len; i++) {
					let className='';
					if(json.minutes[i] == json.minute) className='active';
					html +=`<li class="${className}" data-time="${json.minutes[i]}">${json.minutes[i]}</li>`
				}
			if(this.config.showsecond){
				html+=`</ul><ul class="second">`
					for (let i = 0,len=json.seconds.length; i < len; i++) {
						let className='';
						if(json.seconds[i] == json.second) className='active';
						html +=`<li class="${className}" data-time="${json.seconds[i]}">${json.seconds[i]}</li>`
					}
			}	
			html+=`</ul></div></div>`
		return html;
	}

	// time -bottom html 时间选择器日期/时间切换块
	bottomCheckTimeHTML(){
		let html = '';
		if(this.obj.handleType === 'time'){
			html+= `<div class="zane-date-left button" onclick="${this.config.calendarName}.backDateHtml()">${this.obj.lang.dateTips}</div>`
		}else{
			html+= `<div class="zane-date-left button" onclick="${this.config.calendarName}.getTimeHtml()">${this.obj.lang.timeTips}</div>`
		}
		return html;
	}

	// 插件位置定位并显示
	elemEventPoint(e){
		let secElement = e.srcElement || e.target
		this.obj.calendar 		= this.$obj;
		let rectObject = secElement.getBoundingClientRect()
		let objOffsetLeft = rectObject.left
		let objOffsetTop = rectObject.top
		let winWidth  			= doc.documentElement.clientWidth
		let screenClientHeight 	= doc.documentElement.clientHeight
		let screenScrolTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;
		let objOffsetHeight		= e.target.offsetHeight
		let objBotton 			= screenClientHeight-(objOffsetTop+objOffsetHeight+this.config.behindTop)
		let betweenRight 		=  winWidth-objOffsetLeft-this.config.width
		let calendHeight 		= this.$obj.offsetHeight
		this.obj.calendar.style.display = 'block';

		// 设置插件point位置
		if(this.obj.isDoubleOne&&betweenRight>=this.config.width){
			this.obj.calendar.style.left 	=	objOffsetLeft+this.config.width+'px';
		}else{
			this.obj.calendar.style.left 	=	objOffsetLeft+'px';
		};
		//double 处理
		if(objBotton > calendHeight){
			//插件在input框之下 
			this.config.isDouble&&this.obj.isDoubleOne&&betweenRight<this.config.width?
			this.obj.calendar.style.top = objOffsetTop+screenScrolTop+objOffsetHeight+this.config.behindTop+calendHeight-2-40+'px'
			:this.obj.calendar.style.top = objOffsetTop+screenScrolTop+objOffsetHeight+this.config.behindTop+'px';
		}else{
			//插件在input框之上
			this.config.isDouble&&!this.obj.isDoubleOne&&betweenRight<this.config.width?
			this.obj.calendar.style.top = objOffsetTop+screenScrolTop-this.config.behindTop-calendHeight*2+42+'px'
			:this.obj.calendar.style.top = objOffsetTop+screenScrolTop-this.config.behindTop-calendHeight+'px';
		}
	}

	// 插件数据渲染
	getTimeDates(deraultDay,clickType){
		let timeDatas 	= [];
		let date    	= deraultDay?new Date(deraultDay):new Date()
		let year		= date.getFullYear()
		let month   	= date.getMonth()+1
		let toDate  	= date.getDate()
		let weekday 	= date.getDay()
		let hour 		= date.getHours()
		let minute 		= date.getMinutes()
		let second 		= date.getSeconds()
		// double 处理
		if(this.config.isDouble&&this.obj.isDoubleOne&&clickType=='next'){
			if(month > 12){
				year 	= year + 1;
				month 	= 1;
			}
		}else if(this.config.isDouble&&!this.obj.isDoubleOne&&clickType=='pre'){
			if(month < 1){
				year 	= year - 1;
				month 	= 12;
			}
		}
		month 			= (month+'').length<2? '0'+month : month;
		toDate 			= (toDate+'').length<2? '0'+toDate : toDate;
		hour 			= (hour+'').length<2? '0'+hour : hour;
		minute 			= (minute+'').length<2? '0'+minute:minute;
		second 			= (second+'').length<2? '0'+second:second;
		// 计算当前这个月的天数
		let monTotalDay = new Date(year,month,0).getDate()
		// 计算第一天是周几
		let firstDayMeek = new Date(`${year}/${month}/1`).getDay()
		let lastDayMeek  = new Date(`${year}/${month}/${monTotalDay}`).getDay()
		let preweek	 = null;
		let preday      = null;	
		// 计算需要补充的时间
		if(firstDayMeek>0){
			let preMonTotalDay =  new Date(year,month-1,0).getDate()
			let preyear  = year;
			let premonth = month-1;
			if(premonth === 0){
				preyear = year-1;
				premonth = 12;
			}	
			for (var i = 0,len=firstDayMeek; i < len; i++) {
				let day 	= preMonTotalDay-i;
				premonth 	= (premonth+'').length<2?'0'+premonth:premonth;
				day 		= (day+'').length<2?'0'+day:day;
				let fullday = `${preyear}/${premonth}/${day}`;
				let sele_act = this.hasSelectAct(fullday, this.config.begintime, this.config.endtime) ? 'sele_act' : '';

				timeDatas.push({
					day:preMonTotalDay-i,
					week:len-1-i,
					class:'light',
					sele_act: sele_act,
					daytype:`pre`,
					fullday: fullday
				})
			}
		}
		timeDatas = timeDatas.reverse();
		for (let i = 0,len=monTotalDay; i < len; i++) {
			let weekday 	= (firstDayMeek+i)%7;
			let premonth 	= month;
			let day 		= i+1
				premonth 	= (premonth+'').length<2?'0'+premonth:premonth;
				day 		= (day+'').length<2?'0'+day:day;
			let fullday = `${year}/${premonth}/${day}`;
			let sele_act 	= this.hasSelectAct(fullday, this.config.begintime, this.config.endtime) ? 'sele_act' : '';

			timeDatas.push({
				day:i+1,
				week:weekday,
				daytype:`now`,
				sele_act: sele_act,
				fullday: fullday
			})
			if(i === len-1){
				preweek 	= weekday;
				preday 		= i+1;
			}
		}

		let totalLength 	= timeDatas.length;
		let haveNeedLength 	= 42-totalLength;

		let preyear  	= year;
		let premonth 	= parseInt(month)+1;
		if(premonth === 13){
			preyear  	= year+1;
			premonth 	= 1;
		}

		for (var i = 0; i < haveNeedLength; i++) {
			let weekday 	= (preweek+1+i)%7;
			let day 		= i+1;
				premonth 	= (premonth+'').length<2?'0'+premonth:premonth;
				day 		= (day+'').length<2?'0'+day:day;
			let fullday 	= `${preyear}/${premonth}/${day}`;
			let sele_act 	= this.hasSelectAct(fullday, this.config.begintime, this.config.endtime) ? 'sele_act' : '';

			timeDatas.push({
				day:i+1,
				week:weekday,
				class:'light',
				sele_act: sele_act,
				daytype:`next`,
				fullday: fullday
			})
		}
		setTimeout(()=>{if (!this.config.begintime && !this.config.endtime) this.setBeginEndTime(`${year}/${month}/${toDate}`);},1000);
		return {
			year:year,
			month:month,
			today:toDate,
			hour:hour,
			minute:minute,
			second:second,
			datalist:timeDatas
		}
	}

	hasSelectAct(nowTime,beginTime,endTime){
		let result = false;
		if (this.config.isDouble  && this.config.begintime && this.config.endtime) {
			nowTime = typeof (nowTime) === 'number' ? nowTime : new Date(nowTime).getTime()
			beginTime = typeof (beginTime) === 'number' ? beginTime : new Date(beginTime.replace(/-/g, '/')).getTime();
			endTime = typeof (endTime) === 'number' ? endTime : new Date(endTime.replace(/-/g, '/')).getTime();
			if (nowTime >= beginTime && nowTime <= endTime) result = true;
		}
		return result;
	}

	// 选择上一月
	preMonth(year,month){
		month = month-1
		if(month == 0) {
			month 	= 12;
			year 	= year-1
		}
		let today = this.obj.fulldatas.today
		let totalday = new Date(year, month, 0).getDate()
		if (today > totalday) today = totalday
		let fulldate = `${year}/${month}/${today}`
		let isreset 	= this.config.isDouble&&this.obj.isDoubleOne?true:false
		this.setBeginEndTime(fulldate);
		this.judgeCalendarRender('day',fulldate,isreset,'pre')
	}

	// 选择下一月
	nextMonth(year,month){
		month = month+1
		if(month == 13) {
			month 	= 1;
			year 	= year+1
		} 
		let today = this.obj.fulldatas.today
		let totalday = new Date(year, month, 0).getDate()
		if (today > totalday) today = totalday
		let fulldate = `${year}/${month}/${today}`
		let isreset 	= this.config.isDouble&&!this.obj.isDoubleOne?true:false
		this.setBeginEndTime(fulldate);
		this.judgeCalendarRender('day',fulldate,isreset,'next')
	}

	setBeginEndTime(dataTime){
		if (this.config.isDouble) {
			if (!this.obj.isDoubleOne) {
				this.config.begintime = dataTime;
				this.obj.$noDoubleObj.config.begintime = dataTime;
			} else {
				this.config.endtime = dataTime;
				this.obj.$noDoubleObj.config.endtime = dataTime;
			}
		}
	}

	// 获得年月日,如果showtime=true,日期加样式,如果为false,直接设置当前选择的日期
	getDay(){
		let _this=this;
		let objs = this.$obj
		[query]('.main-check-day')[quall]('td');
		// 绑定单击
		this.on(objs,'click',function(e){
			let node = e.target.nodeName=='SPAN'?e.target.parentNode:e.target
			if(!_this.hasClass(node,'calendar-disabled')){//有calendar-disabled样式的不赋予事件
				let dataTime 				= 	this.getAttribute('data-time');
				let arr 					=	dataTime.split('/')
				let fulldatas = {
					year: arr[0],
					month: arr[1],
					today: arr[2],
					hour: _this.obj.fulldatas.hour,
					minute: _this.obj.fulldatas.minute,
					second: _this.obj.fulldatas.second
				}
				_this.setBeginEndTime(dataTime)
				if (_this.config.isDouble) {
					const result = _this.compareTimes('getDay', _this.obj.$noDoubleObj.obj.fulldatas, fulldatas);
					if (result) return;
				}
				_this.obj.fulldatas.year = arr[0]
				_this.obj.fulldatas.month = arr[1]
				_this.obj.fulldatas.today = arr[2]
				//选择具体日期添加样式
				_this.addOrRemoveClass(this);
				// double 处理
				if(!_this.config.showtime && !_this.config.isDouble){
					let value = `${_this.obj.fulldatas.year}/${_this.obj.fulldatas.month}/${_this.obj.fulldatas.today}`
					_this.getYearMonthAndDay(value,true)
				}
			}
		})
		// 绑定双击
		!this.config.isDouble&&this.on(objs,'dblclick',function(e){
			let node = e.target.nodeName=='SPAN'?e.target.parentNode:e.target
			if(e.type === 'dblclick'&&!_this.hasClass(node,'calendar-disabled')) _this.makeSureSelectTime();
		})
	}

	addOrRemoveClass(that){
		const _this = this;
		let objs = this.$obj[query]('.main-check-day')[quall]('td');
		if (_this.config.isDouble) {
			let otherobjs = _this.obj.$noDoubleObj.$obj[query]('.main-check-day')[quall]('td');
			_this.forEach(objs, (index, item) => {
				let newtime = item.getAttribute('data-time')
				let result = _this.hasSelectAct(newtime, _this.config.begintime, _this.config.endtime)
				if (that) _this.removeClass(item, 'act_block');
				if (result){
					if (!_this.hasClass(item, 'sele_act')) _this.addClass(item, 'sele_act');
				} else {
					_this.removeClass(item, 'sele_act')
				}
			})
			_this.forEach(otherobjs, (index, item) => {
				let newtime = item.getAttribute('data-time')
				let result = _this.hasSelectAct(newtime, _this.config.begintime, _this.config.endtime)
				if (result) {
					if (!_this.hasClass(item, 'sele_act')) _this.addClass(item, 'sele_act');
				}else{
					_this.removeClass(item, 'sele_act');
				}
			})
			if (that) _this.addClass(that, 'act_block');
		} else if (that){
			_this.forEach(objs, (index, item) => {
				_this.removeClass(item, 'active');
			})
			_this.addClass(that, 'active');
		}
	}

	compareTimes(type,preT,nextT){
		let preTimes, nextTimes, modelText;
		if (type === 'getDay'){
			preTimes = `${preT.year}/${preT.month}/${preT.today} ${preT.hour}:${preT.minute}:${preT.second}`;
			preTimes = new Date(preTimes).getTime();
			nextTimes = `${nextT.year}/${nextT.month}/${nextT.today} ${nextT.hour}:${nextT.minute}:${nextT.second}`;
			nextTimes = new Date(nextTimes).getTime();
			modelText = this.config.lang === 'cn' ? '结束时间必须大于开始时间!' : 'The end time must be greater than the start time.';
		} else if (type === 'getYear'){
			preTimes = preT.year;
			nextTimes = nextT.year;
			modelText = this.config.lang === 'cn' ? '结束年份必须大于开始年份!' : 'The end year must be greater than the beginning year.';
		} else if (type === 'getMonth'){
			preTimes = parseInt(preT.month);
			nextTimes = parseInt(nextT.month);
			modelText = this.config.lang === 'cn' ? '结束月份必须大于开始月份!' : 'The end month must be greater than the beginning month.';
		} else if (type === 'selectTime'){
			preTimes = `2018/01/01 ${preT.hour}:${preT.minute}:${preT.second}`;
			preTimes = new Date(preTimes).getTime();
			nextTimes = `2018/01/01 ${nextT.hour}:${nextT.minute}:${nextT.second}`;
			nextTimes = new Date(nextTimes).getTime();
			modelText = this.config.lang === 'cn' ? '结束时间必须大于开始时间!' : 'The end time must be greater than the start time.';
		}

		let result = false;
		let isShowTime = this.config.showtime || this.obj.$noDoubleObj.config.showtime;
		if (
			(this.obj.isDoubleOne && nextTimes <= preTimes && !isShowTime) || 
			(this.obj.isDoubleOne && nextTimes < preTimes && isShowTime)
		) result = true;
		if (
			(!this.obj.isDoubleOne && nextTimes >= preTimes && !isShowTime) || 
			(!this.obj.isDoubleOne && nextTimes > preTimes && isShowTime)
		) result = true;
		let timer = null;
		if (result) {
			clearTimeout(timer);
			const modelObj = this.$obj[query]('.zane-model-miss')
			modelObj.style.display = 'block'
			this.$obj[query]('.zane-model-content').innerHTML = modelText;
			timer = setTimeout(() => { modelObj.style.display = 'none' }, 1000)
		}
		return result;
	}

	// 获得年html
	getYearHtml(year,isreset,clickType){
		year = year || new Date().getFullYear();
		year = parseInt(year)
		// double 处理
		if(this.config.isDouble&&this.obj.isDoubleOne&&clickType=='next'){
			year = year + this.obj.totalYear;
		}else if(this.config.isDouble&&!this.obj.isDoubleOne&&clickType=='pre'){
			year = year - this.obj.totalYear;
		}
		let yearDatas 	= {
			nowyear:year,
			datalist:[]
		};
		for (var i = 0; i < this.obj.totalYear; i++) {
			let getyear = year-Math.floor(this.obj.totalYear/2)+i
			if(i === 0) yearDatas.firstYear = getyear;
			if(i === this.obj.totalYear-1) yearDatas.lastYear = getyear;
			yearDatas.datalist.push({
				class:'',
				year:getyear
			})
		}

		this.obj.fulldatas.year = this.config.isDouble?yearDatas.nowyear:''
		this.judgeCalendarRender('year',yearDatas,isreset,clickType);
	}

	// 上一年
	perYear(year){
		year = year-this.obj.totalYear
		let isreset 	= this.config.isDouble&&this.obj.isDoubleOne?true:false
		this.getYearHtml(year,isreset,'pre')
	}

	// 下一年
	nextYear(year){
		year = year+this.obj.totalYear
		let isreset 	= this.config.isDouble&&!this.obj.isDoubleOne?true:false
		this.getYearHtml(year,isreset,'next')
	}

	// 获得年
	getYear(){
		let _this = this;
		let objs = this.$obj
		[query]('.main-check-year')[quall]('td');
		this.on(objs,'click',function(e){
			let year = e.target.nodeName === 'TD' ? e.target.children[0].getAttribute('data-year') : e.target.getAttribute('data-year')
			let fulldate 	= `${year}/${_this.obj.fulldatas.month}/${_this.obj.fulldatas.today}`
			if(_this.config.type === 'year'){
				if (_this.config.isDouble){
					const result = _this.compareTimes('getYear', _this.obj.$noDoubleObj.obj.fulldatas, { year: year });
					if (result) return;
				}
				_this.config.isDouble ? _this.obj.fulldatas.year = year : _this.getYearMonthAndDay(year,false)
			}else{
				//double 处理
				let clickType = _this.obj.isDoubleOne?'pre':'';
				_this.setBeginEndTime(fulldate);
				_this.judgeCalendarRender('day',fulldate,true,clickType)
			}

			//选择具体日期添加样式
			_this.forEach(objs, (index, item) => {
				_this.removeClass(item, 'active');
			})
			_this.addClass(this, 'active');
		})
	}

	// 获得month html
	getMonthHtml(month){
		let date 	= new Date();
		let year 	= this.obj.fulldatas.year || date.getFullYear();
		month 		= month || date.getMonth()+1
		let monthDatas 	= {
			nowmonth:month,
			year:year,
			datalist:this.obj.lang.month
		};
		this.obj.fulldatas.month = this.config.isDouble?monthDatas.nowmonth:''
		this.judgeCalendarRender('month',monthDatas);
	}

	// 上一月
	perMonthYear(year,month){
		let monthDatas 	= {
			nowmonth:month,
			year:year-1,
			datalist:this.obj.lang.month
		};
		this.judgeCalendarRender('month',monthDatas);
	}

	// 下一月
	nextMonthYear(year,month){
		let monthDatas 	= {
			nowmonth:month,
			year:year+1,
			datalist:this.obj.lang.month
		};
		this.judgeCalendarRender('month',monthDatas);
	}

	// 获得月
	getMonth(){
		let _this = this;
		let objs = this.$obj
		[query]('.main-check-month')[quall]('td');
		this.on(objs,'click',function(e){
			let obj = e.target.nodeName === 'TD' ? e.target.children[0] : e.target;
			let year 	= obj.getAttribute('data-year')
			let month 	= obj.getAttribute('data-month')
			let fulldate 	= `${year}/${month}/${_this.obj.fulldatas.today}`
			if(_this.config.type === 'month'){
				if (_this.config.isDouble) {
					const result = _this.compareTimes('getMonth', _this.obj.$noDoubleObj.obj.fulldatas, { month: month });
					if (result) return;
				}
				_this.config.isDouble ? _this.obj.fulldatas.month = month : _this.getYearMonthAndDay(month,false)
			}else{
				let clickType = _this.obj.isDoubleOne ? 'pre' : '';
				_this.setBeginEndTime(fulldate);
				_this.judgeCalendarRender('day', fulldate, true, clickType)
			}
			//选择具体日期添加样式
			_this.forEach(objs, (index, item) => {
				_this.removeClass(item, 'active');
			})
			_this.addClass(this, 'active');
		})
	}

	// 获得时间HTML
	getTimeHtml(time){
		//double 处理
		if(this.config.isDouble&&!this.obj.isDoubleOne&&this.config.type=='day') this.obj.$noDoubleObj.getTimeHtml();
		let nowday = new Date().Format('yyyy/MM/dd')
		let date 	= time?new Date(nowday+' '+time):new Date();
		let hour 	= date.getHours();
		let minute 	= date.getMinutes();
		let second 	= date.getSeconds();
		hour 			= (hour+'').length<2? '0'+hour : hour;
		minute 			= (minute+'').length<2? '0'+minute:minute;
		second 			= (second+'').length<2? '0'+second:second;

		this.obj.fulldatas.hour = this.obj.fulldatas.hour||hour
		this.obj.fulldatas.minute = this.obj.fulldatas.minute||minute
		this.obj.fulldatas.second = this.obj.fulldatas.second||second

		let datas ={
			hour:this.obj.fulldatas.hour,
			minute:this.obj.fulldatas.minute,
			second:this.obj.fulldatas.second,
			hours:[],
			minutes:[],
			seconds:[]
		}
		for (var i = 0; i < 24; i++) {
			if(i<10){
				datas.hours.push('0'+i)
			}else{
				datas.hours.push(i+'')
			}
		}
		for (var i = 0; i < 60; i++) {
			if(i<10){
				datas.minutes.push('0'+i)
				datas.seconds.push('0'+i)
			}else{
				datas.minutes.push(i+'')
				datas.seconds.push(i+'')
			}
		}
		this.judgeCalendarRender('time',datas);
	}

	// 选择时间
	selectTime(){
		let _this 		= this
		let hourObjs 	= this.$obj[query]('ul.hour')[quall]('li')
		let minuteObjs 	= this.$obj[query]('ul.minute')[quall]('li')

		let secondObjs  = null;
		if(this.config.showsecond){
			secondObjs 	= this.$obj[query]('ul.second')[quall]('li')
		}
		
		this.on(hourObjs,'click',function(e){
			_this.forEach(hourObjs,(index,item)=>{
				_this.removeClass(item,'active');
			})
			_this.addClass(this,'active');
			_this.obj.fulldatas.hour = this.getAttribute('data-time');
		})

		this.on(minuteObjs,'click',function(e){
			if(_this.config.isDouble && !_this.config.showsecond){
				const result = _this.compareTimes(
					'selectTime',
					_this.obj.$noDoubleObj.obj.fulldatas,
					Object.assign({}, _this.obj.fulldatas, { minute: this.getAttribute('data-time') })
				);
				if (result) return;
			}
			_this.forEach(minuteObjs,(index,item)=>{
				_this.removeClass(item,'active');
			})
			_this.addClass(this,'active');
			_this.obj.fulldatas.minute = this.getAttribute('data-time');
		})

		if(this.config.showsecond){
			this.on(secondObjs,'click',function(e){
				if (_this.config.isDouble) {
					const result = _this.compareTimes(
						'selectTime',
						_this.obj.$noDoubleObj.obj.fulldatas,
						Object.assign({}, _this.obj.fulldatas, { second: this.getAttribute('data-time') })
					);
					if (result) return;
				}
				_this.forEach(secondObjs,(index,item)=>{
					_this.removeClass(item,'active');
				})
				_this.addClass(this,'active');
				_this.obj.fulldatas.second = this.getAttribute('data-time');
			})
		}
	}

	// 返回日期
	backDateHtml(){
		//double 处理
		if(this.config.isDouble&&!this.obj.isDoubleOne&&this.config.type=='day') this.obj.$noDoubleObj.backDateHtml();
		this.obj.handleType = 'date';
		let bottomHTML  	= this.bottomCheckTimeHTML();
		this.renderCommonHtml('day','','',bottomHTML,false);
	}

	// 今天
	changeToToday(){
		let json 		= 	this.getTimeDates();
		let value 		=	null;
		let isFormat 	= true;
		if(this.config.showtime){
			value = `${json.year}/${json.month}/${json.today} ${json.hour}:${json.minute}:${json.second}`
		}else if(this.config.type == 'time'){
			isFormat 	= false;
			value = `${json.hour}:${json.minute}:${json.second}`
		}else{
			value = `${json.year}/${json.month}/${json.today}`
		}
		this.getYearMonthAndDay(value,isFormat)
	}

	// 清空
	cleanInputVal(){
		let value = ""
		this.getYearMonthAndDay(value,false)
	}

	// 确定
	makeSureSelectTime(){
		let value 		= null;
		let isFormat 	= true;
		if(this.config.showtime){
			value = `${this.obj.fulldatas.year}/${this.obj.fulldatas.month}/${this.obj.fulldatas.today} ${this.obj.fulldatas.hour}:${this.obj.fulldatas.minute}`
			if(this.config.showsecond){
				value = `${value}:${this.obj.fulldatas.second}`
			}
		}else if(this.config.type == 'time'&&!this.config.isDouble){
			isFormat 	= false;
			value = `${this.obj.fulldatas.hour}:${this.obj.fulldatas.minute}`
			if(this.config.showsecond){
				value = `${value}:${this.obj.fulldatas.second}`
			}
		}else{
			//doubule 处理
			if(this.config.isDouble){
				let noDoubleData 		=  this.obj.$noDoubleObj.obj.fulldatas;
				let noDoubleStr,haveDoubleStr

				switch(this.config.type){
					case 'day':
						if(this.obj.$noDoubleObj.config.showtime){
							noDoubleStr		=  `${noDoubleData.year}/${noDoubleData.month}/${noDoubleData.today} ${noDoubleData.hour}:${noDoubleData.minute}`
							haveDoubleStr	=  `${this.obj.fulldatas.year}/${this.obj.fulldatas.month}/${this.obj.fulldatas.today} ${this.obj.fulldatas.hour}:${this.obj.fulldatas.minute}`
							if(this.obj.$noDoubleObj.config.showsecond){
								noDoubleStr 	= `${noDoubleStr}:${noDoubleData.second}`
								haveDoubleStr 	= `${haveDoubleStr}:${this.obj.fulldatas.second}`
							}
						}else{
							noDoubleStr		=  `${noDoubleData.year}/${noDoubleData.month}/${noDoubleData.today}`
							haveDoubleStr	=  `${this.obj.fulldatas.year}/${this.obj.fulldatas.month}/${this.obj.fulldatas.today}`
						};
						break;
					case 'year':
						isFormat = false
						noDoubleStr 	=  `${noDoubleData.year}`
						haveDoubleStr 	=  `${this.obj.fulldatas.year}`
						break;
					case 'month':
						isFormat = false
						noDoubleStr 	=  `${noDoubleData.month}`
						haveDoubleStr 	=  `${this.obj.fulldatas.month}`
						break;	
					case 'time' :		
						isFormat = false
						noDoubleStr 	=  `${noDoubleData.hour}:${noDoubleData.minute}`
						haveDoubleStr 	=  `${this.obj.fulldatas.hour}:${this.obj.fulldatas.minute}`
						if(this.config.showsecond){
							noDoubleStr 	= `${noDoubleStr}:${noDoubleData.second}`
							haveDoubleStr 	= `${haveDoubleStr}:${this.obj.fulldatas.second}`
						}
						break;		
				};
				value					=  noDoubleStr +'|'+ haveDoubleStr
			}else{
				value = `${this.obj.fulldatas.year}/${this.obj.fulldatas.month}/${this.obj.fulldatas.today}`
			}
		}
		this.getYearMonthAndDay(value,isFormat)
	}

	// 确定年月日的值并在input里面显示,时间选择器隐藏
	getYearMonthAndDay(datatime,isFormat=true){
		let formatTime = null;
		let begintime='';
		let endtime='';

		//doubule 处理
		if(datatime&&datatime.indexOf('|') != -1){
			let arr 	= datatime.split('|');
			let val1 	= null
			let val2 	= null
			if(isFormat){
				val1 = begintime = new Date(arr[0]).Format(this.config.format)
				val2 = endtime = new Date(arr[1]).Format(this.config.format)
			}else{
				val1 = begintime = arr[0]
				val2 = endtime = arr[1]
			}
			formatTime = val1 +' - '+ val2
		}else{
			formatTime = begintime = isFormat?new Date(datatime).Format(this.config.format):datatime;
		}
		 
		if(this.obj.input.nodeName !== 'INPUT'){
			this.obj.input.textContent	=	formatTime;
		}else{
			this.obj.input.value  		= formatTime;
		}

		// 移除事件插件dom元素
		this.removeCalendar();

		this.config.done&&this.config.done(formatTime,begintime,endtime);
		if(this.obj.initVal!=formatTime&&this.config.change)this.config.change(formatTime,begintime,endtime)
	}

	// 判断插件渲染类型 day | year | month | time
	judgeCalendarRender(type,any,isreset,clickType){
		let mainHTML,topHTML,bottomHTML
		switch(type){
			case 'day':
				this.obj.handleType = 'day';
				let json 			= this.getTimeDates(any,clickType);
				this.obj.fulldatas 	= json;
				// double 处理
				this.compareSize(isreset,clickType);
				topHTML 			= this.topCheckDayHTML(json)
				mainHTML 			= this.mainCheckDayHTML(json);
				this.renderCommonHtml('day',topHTML,mainHTML);
				// 计算表格高度
				this.countHeight('.main-check-day',7);
				this.getDay();
				setTimeout(()=>{
					this.addOrRemoveClass();
				},200)
				
				break;
			case 'year':
				this.obj.handleType = 'year';
				// double 处理
				this.compareSize(isreset,clickType);
				mainHTML 	= this.mainCheckYearHTML(any);
				topHTML		= this.topCheckYearHTML(any);
				this.renderCommonHtml('year',topHTML,mainHTML);
				// 计算表格高度
				this.countHeight('.main-check-year',6);
				this.getYear();
				break;	
			case 'month':
				this.obj.handleType = 'month';
				mainHTML 	= this.mainCheckMonthHTML(any);
				topHTML		= this.topCheckMonthHTML(any);
				this.renderCommonHtml('month',topHTML,mainHTML);
				// 计算表格高度
				this.countHeight('.main-check-month',4);
				this.getMonth();
				break;
			case 'time':
				this.obj.handleType = 'time';
				mainHTML 	= this.mainCheckTimeHTML(any);
				topHTML		= this.topCheckTimeHTML();
				bottomHTML  = this.bottomCheckTimeHTML();
				this.renderCommonHtml('time',topHTML,mainHTML,bottomHTML);
				this.$obj[query]('.select-time').style.height = this.config.height-115 +'px'
				let hourScrollTop = this.$obj[query]('ul.hour')[query]('li.active').offsetTop
				let minuteScrollTop = this.$obj[query]('ul.minute')[query]('li.active').offsetTop
				
				this.$obj[query]('ul.hour').scrollTop 		= 	hourScrollTop-150
				this.$obj[query]('ul.minute').scrollTop 	= 	minuteScrollTop-150

				if(this.config.showsecond){
					let secondScrollTop = this.$obj[query]('ul.second')[query]('li.active').offsetTop
					this.$obj[query]('ul.second').scrollTop 	=	secondScrollTop-150
				} 

				this.selectTime();
				break;		
		}
	}

	renderCommonHtml(type,topHTML,mainHTML,bottomHTML,isrender=true){
		if(type == 'time'|| !isrender) this.$obj[query](`.btn-select-time`).innerHTML = bottomHTML;
		if(isrender){
			this.$obj[query](`.top-check-${type}`).innerHTML 	= topHTML;
			this.$obj[query](`.main-check-${type}`).innerHTML 	= mainHTML;
		};
		this.showOrHide(this.$obj[quall]('.common-top'),'hide')
		this.showOrHide(this.$obj[quall]('.common-main'),'hide')
		this.$obj[query](`.main-check-${type}`).style.display 	= 'block'
		this.$obj[query](`.top-check-${type}`).style.display 	= 'block'
	}

	// 比较double数据之间的大小,并从新赋值
	compareSize(isreset,clickType){
		if(!isreset) return;
		// double 处理
		let prev = this.obj.fulldatas
		let next = this.obj.$noDoubleObj
		if(this.config.isDouble&&prev&&next){
			next = next.obj.fulldatas
			if(this.obj.isDoubleOne){
				this.getDoubleTime({
					prev:next,
					next:prev,
					clickType:clickType
				})
			}else{
				this.getDoubleTime({
					prev:prev,
					next:next,
					clickType:clickType
				})
			}
		};
	}

	getDoubleTime(json){
		let nextfullstr,prefullstr,perTime,nextTime
		switch(this.config.type){
			case 'day':
				prefullstr 		= `${json.prev.year}/${json.prev.month}/${json.prev.today}`
				nextfullstr 	= `${json.next.year}/${json.next.month}/${json.next.today}`
				perTime 		= new Date(prefullstr).getTime()
				nextTime 		= new Date(nextfullstr).getTime()
				if(perTime >= nextTime-86400000) {
					const times = this.obj.isDoubleOne ? nextTime - 86400000 : perTime + 86400000;
					this.obj.$noDoubleObj.setBeginEndTime(times)
					this.obj.$noDoubleObj.judgeCalendarRender('day', times,false,json.clickType)
				}
				break;
			case 'year':
				perTime 	= `${json.prev.year}`
				nextTime 	= `${json.next.year}`
				if(perTime >= nextTime) {
					this.obj.$noDoubleObj.getYearHtml(nextTime,false,json.clickType)
				}
				break;	
		}

	}

	//插件自身点击阻止冒泡
	calendarClick(){
		this.on(this.obj.calendar,'click',(e)=>{
			e.preventDefault();
			e.stopPropagation();	
		})
	} 

	// 继承方法
	extend(obj1,obj2){
		return Object.assign(obj1,obj2);
	}

	hasClass(obj, cls) {  
    	return obj.className.match(new RegExp('(\s|^)' + cls + '(\s|$)'));  
	}  
	  
	addClass(obj, cls) {  
	    if (!this.hasClass(obj, cls)) obj.className += " " + cls;  
	}  
	  
	removeClass(obj, cls) {  
	    if (this.hasClass(obj, cls)) {  
	        var reg = new RegExp('(\s|^)' + cls + '(\s|$)');  
	        obj.className = obj.className.replace(reg, ' ');  
	    }  
	} 

	//对象遍历
  	forEach(obj, fn){
	    let key
	    if(typeof fn !== 'function') return this;
	    obj = obj || [];
	    if(Object.prototype.toString.call(obj) == '[object Object]'){
		    for(key in obj){
		        	if(fn.call(obj[key], key, obj[key])) break;
		    }
	    } else if(Object.prototype.toString.call(obj) =='[object NodeList]' || Object.prototype.toString.call(obj) =='[object Array]'){
	      	for(key = 0; key < obj.length; key++){
	        	if(fn.call(obj[key], key, obj[key])) break;
	      	}	
	    }else{
	    	fn.call(obj,0,obj);  
	    }
	    return this;
	  };

	//事件绑定
  	on(obj,eventName, fn){
	    return this.forEach(obj,(index, item)=>{
	      	item.attachEvent ? item.attachEvent('on' + eventName, function(e){
	        	e.target = e.srcElement;
	        	fn.call(item, e);
	      	}) : item.addEventListener(eventName, fn, false);
	    });
	};

	//中英文月份枚举
	weekToEn(val){
		let num = typeof val == 'string'?parseInt(val):val;
		return this.obj.en.month[num-1];
	}

	// 
	showOrHide(obj,type){
		for (var i = 0,len=obj.length; i < len; i++) {
			if(type==='hide'){
				obj[i].style.display = 'none'
			}else{
				obj[i].style.display = 'block'
			}
		}
	}

  	// 计算table tr高度
  	countHeight(elename,length){
  		let mainH  		=  	this.$obj[query]('.zane-date-main').offsetHeight;
  		let trObj		= 	this.$obj[query](elename)[quall]('tr')
  		let itemH 		=  Math.floor(mainH/length)
  		this.forEach(trObj,(index,item)=>{
  			item.style.height = itemH + 'px';
  		})
  	}

  	// document点击隐藏插件
  	documentClick(){
  		this.on(doc,'click',(e)=>{
  			this.removeCalendar()
  		})
  	}

  	// 移除事件选择器
  	removeCalendar(calobj){
  		let zaneCalendarObjs = doc[quall]('.zane-calendar')
  		if(zaneCalendarObjs&&zaneCalendarObjs.length){
  			zaneCalendarObjs.forEach(item=>{
  				let parent = item.parentElement;
				let parents = parent.parentElement;
				let removed = parents.removeChild(parent);
			})
  		}
  	}

};

// 实例化日期插件 双选择器DOUBLE区分
let zaneDate = function(option){
	let begintime,endtime,format;
	format 	= option.format?option.format.replace(/-/g,'/'):'yyyy/MM/dd'
	if(option.type){
		if(option.type.indexOf('time')!=-1) format 	= 'HH:mm:ss';
		if(option.type.indexOf('year')!=-1) format 	= 'yyyy';
		if(option.type.indexOf('month')!=-1) format = 'MM';
	}
	option.type = option.type || 'day'

	//处理begintime
	if(option.begintime&&typeof(option.begintime)==='string'){
		begintime 	= option.begintime.replace(/-/g,'/')
		if(option.type&&option.type.indexOf('time')==-1 || !option.type){
			begintime 	= new Date(begintime).Format(format)
		}
	}else if(option.begintime&&typeof(option.begintime)==='number'){
		begintime 	= new Date(option.begintime).Format(format)
	}

	// 处理begintime
	if(option.endtime&&typeof(option.endtime)==='string'){
		endtime 	= option.endtime.replace(/-/g,'/')
		if(option.type&&option.type.indexOf('time')==-1 || !option.type){
			endtime 	= new Date(endtime).Format(format)
		}
	}else if(option.endtime&&typeof(option.endtime)==='number'){
		endtime 	= new Date(option.endtime).Format(format)
	}
	
	if(option.type.indexOf('double') != -1){
		option.type = option.type.replace(/double/,'');
		createCalendar({
			showclean:false,
			shownow:false,
			showsubmit:false,
			isDouble:true,
			value:begintime,
			format:format,
			doublevalue:begintime&&endtime?begintime+' - '+endtime:''
		});
		createCalendar({
			shownow:false,
			showtime:false,
			isDouble:true,
			double:'DOUBLE',
			value:endtime,
			format:format,
			doublevalue:begintime&&endtime?begintime+' - '+endtime:''
		});
	}else{
		createCalendar({
			format:format,
			value:begintime,
		});
	}
	// 新建日期插件
	function createCalendar(json={}){
		let calendarName 		= option.elem.substring(1);
		calendarName 			= calendarName.replace(/[_-]/g,'').toUpperCase();
		option.calendarName 	= json&&json.double ?calendarName+json.double:calendarName;
		if(option.width){
			option.width = option.width<220?220:option.width
			option.width = option.width>500?500:option.width
		}
		if(option.height){
			option.height = option.height<240?240:option.height
			option.height = option.height>350?350:option.height
		}

		let cloneOption = Object.assign(extendDeep(option),json);
		window[option.calendarName] 	= new calendar(cloneOption)
	}
	//深度复制
	function extendDeep(parent, child) {
		child = child || {};
		for(var i in parent) {
			if(parent.hasOwnProperty(i)) {
				if(typeof parent[i] === "object") {
					child[i] = (Object.prototype.toString.call(parent[i]) === "[object Array]") ? [] : {};
					extendDeep(parent[i], child[i]);
				} else {
					child[i] = parent[i];
				}
			}
		}
		return child;
	};
		
}
if ( !noGlobal ) window.zaneDate = zaneDate;

return zaneDate;

});

css 代码如下

@charset "UTF-8";
* {
  padding: 0;
  margin: 0; }

ul, li {
  list-style: none; }

.zane-calendar {
  box-sizing: initial;
  position: absolute;
  left: 0;
  top: 0;
  font-size: 14px;
  width: 280px;
  border: solid 1px #eee;
  border-left: none;
  background: #fff;
  overflow: hidden;
  -moz-user-select: none;
  -o-user-select: none;
  -khtml-user-select: none;
  -webkit-user-select: none;
  -ms-user-select: none;
  user-select: none; }
  .zane-calendar .zane-model-miss {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    display: none; }
    .zane-calendar .zane-model-miss .zane-model-mask {
      position: absolute;
      left: 0;
      top: 0;
      width: 100%;
      height: 100%;
      z-index: 99; }
    .zane-calendar .zane-model-miss .zane-model-content {
      position: absolute;
      left: 50%;
      top: 50%;
      z-index: 100;
      width: auto;
      text-align: center;
      padding: 10px 15px;
      border-radius: 20px;
      transform: translate(-50%, -50%);
      font-size: 12px;
      background: rgba(0, 0, 0, 0.8);
      color: #fff;
      min-width: 180px; }
  .zane-calendar .main-color {
    color: #46d7a2; }
  .zane-calendar .zane-date-left {
    float: left; }
  .zane-calendar .zane-calendar-one {
    width: 280px; }
    .zane-calendar .zane-calendar-one .zane-date-top {
      height: 40px;
      line-height: 40px;
      position: relative;
      text-align: center;
      border-bottom: solid 1px #eee;
      border-left: solid 1px #eee;
      font-size: 16px; }
      .zane-calendar .zane-calendar-one .zane-date-top .zane-date-icom {
        width: 40px;
        height: 40px;
        position: absolute; }
      .zane-calendar .zane-calendar-one .zane-date-top .zane-icon-left {
        left: 0;
        top: 0;
        background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAA6ElEQVRYR9XXXQ7CIAwHcDiBeAN5oM8eSW+mJ9Fz8FIPQlJDshjnPigbLXGvI/x/oaHdrOn82M755n8BiHjKp+e9f+05xU0nEGM8W2sfOTiEcFQFfIU7IroDwEUN0Do8w9klkAhnA6TCWQDJ8CJAOnwVoBG+CNAKnwVohk8A2uEjACK6lBIaY5p0OG53/DSiAZAHy4GIbgBw5W6yZ92oEw4leGoiJq1YGzE7CzQRi8NIC7E6DTUQxXEsjSgC8hWTRLAAkgg2QApRBZBAVAN+EPmz3DVrxTUbdf0xqYGW1m4qQWnTmvfdAW+Xz8YhHJFj1gAAAABJRU5ErkJggg==") no-repeat center center;
        background-size: 55%; }
      .zane-calendar .zane-calendar-one .zane-date-top .zane-icon-right {
        position: absolute;
        top: 0;
        right: 0;
        background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAA2klEQVRYR+XXwQ3CMAxAUWeY+MwIjFI2gElgA2ASVuBsDxMUCaTSQ2s7NlFLj1Wl/+RISZqg85M692EbACLaIeLTMs3mCTDzEQDOpZQbIh60CA/AHgAeNWxBNANqmIiGlNLVgnABtCDcAFaEK8CCcAdoESEADSIMIEWEAiSIcMAS4ieAKQIATjnnS33/H4DJVn1HxOFzaIVPYC4evgRL8VCAJB4GkMZDAJq4O0AbdwVY4m4Aa9wFwMzjS+nXJiO5ITdvRKNruTruMoH3QdPvx0Qy5rlvmpdg9YAX88PCIUoA0kEAAAAASUVORK5CYII=") no-repeat center center;
        background-size: 55%; }
      .zane-calendar .zane-calendar-one .zane-date-top .zane-icon-center span:hover {
        color: #46d7a2;
        cursor: pointer; }
      .zane-calendar .zane-calendar-one .zane-date-top .zane-icon-center span:nth-child(1) {
        margin-right: 8px; }
    .zane-calendar .zane-calendar-one .zane-date-main {
      overflow: hidden;
      height: 220px;
      border-left: solid 1px #eee; }
      .zane-calendar .zane-calendar-one .zane-date-main .week-day table.day {
        width: 100%; }
      .zane-calendar .zane-calendar-one .zane-date-main .week-day th {
        font-size: 13px; }
      .zane-calendar .zane-calendar-one .zane-date-main .week-day th, .zane-calendar .zane-calendar-one .zane-date-main .week-day td {
        text-align: center; }
      .zane-calendar .zane-calendar-one .zane-date-main .week-day th.light, .zane-calendar .zane-calendar-one .zane-date-main .week-day td.light {
        color: #aaa; }
      .zane-calendar .zane-calendar-one .zane-date-main .week-day td {
        font-size: 12px;
        cursor: pointer; }
        .zane-calendar .zane-calendar-one .zane-date-main .week-day td.calendar-disabled {
          cursor: not-allowed;
          color: #ddd; }
        .zane-calendar .zane-calendar-one .zane-date-main .week-day td span {
          box-sizing: initial;
          display: inline-block;
          width: 10px;
          height: 10px;
          padding: 9px;
          padding-left: 8px;
          padding-right: 10px;
          text-align: center;
          line-height: 10px;
          border-radius: 100%; }
      .zane-calendar .zane-calendar-one .zane-date-main .week-day td:hover span {
        background: #f4f4f4; }
      .zane-calendar .zane-calendar-one .zane-date-main .week-day td.active {
        color: #fff; }
        .zane-calendar .zane-calendar-one .zane-date-main .week-day td.active span {
          background: #46d7a2; }
      .zane-calendar .zane-calendar-one .zane-date-main .week-day td.sele_act {
        background: #8cffd4; }
      .zane-calendar .zane-calendar-one .zane-date-main .week-day td.act_block {
        background: #46d7a2;
        color: #fff; }
      .zane-calendar .zane-calendar-one .zane-date-main .week-day td.act_block:hover, .zane-calendar .zane-calendar-one .zane-date-main .week-day td.sele_act:hover {
        background: #46d7a2;
        color: #fff; }
        .zane-calendar .zane-calendar-one .zane-date-main .week-day td.act_block:hover span, .zane-calendar .zane-calendar-one .zane-date-main .week-day td.sele_act:hover span {
          background: none; }
      .zane-calendar .zane-calendar-one .zane-date-main .week-day .border-day td span {
        width: auto;
        border-radius: 15px; }
      .zane-calendar .zane-calendar-one .zane-date-main .main-check-month .week-day td {
        width: 33.333%; }
        .zane-calendar .zane-calendar-one .zane-date-main .main-check-month .week-day td.calendar-disabled {
          cursor: not-allowed;
          color: #ccc; }
      .zane-calendar .zane-calendar-one .zane-date-main .main-check-year .week-day td {
        width: 33.333%; }
        .zane-calendar .zane-calendar-one .zane-date-main .main-check-year .week-day td.calendar-disabled {
          cursor: not-allowed;
          color: #ccc; }
      .zane-calendar .zane-calendar-one .zane-date-main .main-check-time .week-day {
        padding: 5px; }
        .zane-calendar .zane-calendar-one .zane-date-main .main-check-time .week-day .nav {
          overflow: hidden;
          height: 25px;
          line-height: 25px; }
          .zane-calendar .zane-calendar-one .zane-date-main .main-check-time .week-day .nav li {
            width: 33.333%;
            float: left;
            text-align: center; }
        .zane-calendar .zane-calendar-one .zane-date-main .main-check-time .week-day .nav-1 li {
          width: 50%; }
        .zane-calendar .zane-calendar-one .zane-date-main .main-check-time .week-day .select-time {
          overflow: hidden;
          text-align: center;
          height: 176px;
          /*滚动条样式*/ }
          .zane-calendar .zane-calendar-one .zane-date-main .main-check-time .week-day .select-time ul {
            height: 99%;
            display: inline-block;
            overflow-y: scroll;
            width: 28%;
            margin: 0 2%;
            float: left;
            border: solid 1px #eee; }
            .zane-calendar .zane-calendar-one .zane-date-main .main-check-time .week-day .select-time ul li {
              height: 25px;
              line-height: 25px;
              cursor: pointer; }
              .zane-calendar .zane-calendar-one .zane-date-main .main-check-time .week-day .select-time ul li:hover {
                background: #f4f4f4; }
              .zane-calendar .zane-calendar-one .zane-date-main .main-check-time .week-day .select-time ul li.active {
                background: #46d7a2;
                color: #fff; }
          .zane-calendar .zane-calendar-one .zane-date-main .main-check-time .week-day .select-time ul::-webkit-scrollbar {
            width: 4px;
            height: 4px; }
          .zane-calendar .zane-calendar-one .zane-date-main .main-check-time .week-day .select-time ul::-webkit-scrollbar-thumb {
            border-radius: 5px;
            -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
            background: rgba(0, 0, 0, 0.6); }
          .zane-calendar .zane-calendar-one .zane-date-main .main-check-time .week-day .select-time ul::-webkit-scrollbar-track {
            -webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
            border-radius: 0;
            background: rgba(0, 0, 0, 0.1); }
        .zane-calendar .zane-calendar-one .zane-date-main .main-check-time .week-day .select-time-1 ul {
          width: 45%;
          margin: 0 1%; }
    .zane-calendar .zane-calendar-one .zane-date-bottom {
      border-top: solid 1px #eee;
      border-left: solid 1px #eee;
      overflow: hidden;
      height: 40px;
      padding: 0 8px;
      font-size: 12px; }
      .zane-calendar .zane-calendar-one .zane-date-bottom .zane-date-right {
        float: right; }
        .zane-calendar .zane-calendar-one .zane-date-bottom .zane-date-right .button.no-right-line {
          border-right: none; }
      .zane-calendar .zane-calendar-one .zane-date-bottom .button {
        margin-top: 6px;
        cursor: pointer;
        padding: 6px 8px;
        float: left;
        background: #fff;
        color: #999; }
        .zane-calendar .zane-calendar-one .zane-date-bottom .button:hover {
          color: #46d7a2; }

总结

该组件简单使用,实际就用了2个文件zane-calendar.css,zane-calendar.js,可自行系修改js或css,改成符合自己业务需要的样子;
复杂使用请下载代码:链接: https://pan.baidu.com/s/1A001A6l-HYtqvSTb4TptvA?pwd=wgha 提取码: wgha
原帖地址不记得了,只有github地址:添加链接描述

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。