一个日历的控件,基于vue的,可以日历区间选择,可用于酒店日历区间筛选,动手能力强,可以修改成小程序版本的,先上效果图
里面的颜色样式都是可以修改的
选择范围效果
话不多说,直接上干货,代码可以直接复制访问
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no" /> 6 <meta name="renderer" content="webkit"> 7 <title></title> 8 </head> 9 <style> 10 html,body{padding:0;margin:0;height:100%;} 11 .header{ 12 width:100%; 13 height:40px; 14 text-align: center; 15 position: relative; 16 line-height: 40px; 17 } 18 .body{ 19 width: 100%; 20 height:87%; 21 height: -moz-calc(100% - 80px); 22 height: -webkit-calc(100% - 80px); 23 height: calc(100% - 80px); 24 overflow-y: auto; 25 position:absolute; 26 } 27 .golbal-left{ 28 width: 13px; 29 height: 13px; 30 border-top: 2px solid gainsboro; 31 border-right: 2px solid gainsboro; 32 transform: rotate(225deg); 33 -webkit-transform: rotate(225deg); 34 position: absolute; 35 left: 16px; 36 top: 15px; 37 } 38 .calendar { 39 width: 100vw; 40 text-align: center; 41 } 42 .week-title { 43 overflow: hidden; 44 position: fixed; 45 margin-bottom: 1.5rem; 46 background-color: #f6f6f8; 47 z-index: 2; 48 } 49 .week-title>div { 50 width: 14.28vw; 51 height: 2rem; 52 line-height: 2rem; 53 float: left; 54 } 55 .box { 56 position: absolute; 57 top: 2rem; 58 z-index: 1; 59 } 60 .data-title { 61 height: 2rem; 62 line-height: 2rem; 63 border-top: 1px solid #ededed; 64 border-bottom: 1px solid #ededed; 65 clear: both; 66 } 67 .calendar-data { 68 width: 100vw; 69 clear: both; 70 } 71 .day { 72 width: 14.28vw; 73 height: 3rem; 74 line-height: 1.6rem; 75 float: left; 76 display: flex; 77 flex-direction: column; 78 } 79 .day.disabled{ 80 color:#ddd; 81 } 82 .active-start { 83 color: white; 84 background-color: #30b6af; 85 } 86 .active-start::after { 87 content: '入住'; 88 font-size: .5rem; 89 } 90 .active { 91 color: white; 92 background-color: rgba(63,182,175,.5); 93 } 94 .active-end { 95 color: white; 96 background-color: #30b6af; 97 position: relative; 98 } 99 .active-end::after { 100 content: '离开'; 101 font-size: .5rem; 102 } 103 .active-end i{ 104 position: absolute; 105 top:-120%; 106 width:100%; 107 height:100%; 108 background:rgba(0,0,0,1); 109 opacity:0.6; 110 border-radius:8px; 111 display: flex; 112 align-items: center; 113 justify-content: center; 114 font-style: normal; 115 font-size: 15px; 116 color:#fff; 117 } 118 .active-end i::after{ 119 position: absolute; 120 content: ''; 121 float: left; 122 width: 0; 123 height: 0; 124 border-width: 10px; 125 border-style: solid; 126 border-color:#000 transparent transparent transparent; 127 opacity:1; 128 bottom:-20px; 129 left:50%; 130 margin-left:-10px; 131 } 132 .screenbottom{ 133 height:35px; 134 width:100%; 135 display: flex; 136 } 137 .reset{ 138 width:50%; 139 height:40px; 140 line-height: 40px; 141 color:#3E3E3E; 142 text-align: center; 143 background: #fff; 144 } 145 .determine{ 146 width:50%; 147 height:40px; 148 line-height: 40px; 149 background: #48D8BF; 150 color:#fff; 151 text-align: center; 152 } 153 footer{ 154 position: fixed; 155 bottom:0; 156 width:100%; 157 } 158 [v-cloak] 159 { 160 display: none; 161 } 162 </style> 163 <body> 164 <div id='app' v-cloak> 165 <header class="header"> 166 <span>时间范围</span> 167 </header> 168 <div class="body"> 169 <div class='calendar'> 170 <div class='week-title'> 171 <div>日</div> 172 <div>一</div> 173 <div>二</div> 174 <div>三</div> 175 <div>四</div> 176 <div>五</div> 177 <div>六</div> 178 </div> 179 <div class='box'> 180 <div class='calendar-body'> 181 <div v-for="(item,index) in calendar"> 182 <!-- 标题 --> 183 <div class='data-title'> 184 {{item.fullYear + '年' + item.fullMonth+'月'}} 185 </div> 186 <!-- 日期 --> 187 <div class='calendar-data'> 188 <div class="day" 189 :class="item2.disabled + ' '+ item2.disabled2 + ' ' +item2.start_date + ' ' + item2.end_date +' ' + item2.active_date" 190 v-for="(item2,index) in item.days" 191 @click="selectDate(item2)"> 192 {{item2.day}} 193 </div> 194 </div> 195 </div> 196 </div> 197 </div> 198 </div> 199 </div> 200 <footer> 201 <div class="screenbottom"> 202 <div class="reset">取消</div> 203 <div class="determine">确定</div> 204 </div> 205 </footer> 206 </div> 207 </body> 208 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> 209 <script type="text/javascript"> 210 let vm = new Vue({ 211 el:'#app', 212 data:{ 213 start:'', //开始时间。从当天开始。 214 calendar:[], 215 month_length: 6, //最长预定多少个月后的房间 读取总店配置。 216 max_reserve_days: 0, //最长预定天数。,一个月按30天计划 217 max_reserve_date: '', //最长可预定的日期。例如:2019-09-12 218 select_start_ymd : '', //入住开始提交时间 例如:2019-5-8 219 select_start_show: '', //入住开始显示时间 例如:05月08日 220 select_end_ymd: '', //离店开始提交时间 例如:2019-5-8 221 select_end_show: '', //离店开始显示时间 例如:05月08日 222 select_index:'start', //记录当前点击时间,所对应的时间是开始时间还是结束时间 223 select_all_day:'' 224 }, 225 methods:{ 226 initDate:function(){ 227 var _this = this; 228 // 创建时间对象 229 let date = new Date(); 230 //如果当前时间为凌晨6点前。则当前日期往前一天 231 if (date.getHours() < 6) { 232 date = new Date(date.getTime() - 86400 * 1000); 233 } 234 // 获取完整年月 235 let fullDate = [ 236 date.getFullYear(), 237 date.getMonth() + 1, 238 date.getDate(), 239 date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate() 240 ]; 241 /** 242 * 从缓存拿已经设置的开始和结束日期 243 * 如果第一次用户是第一次进入。则设置默认值为,并且保存进缓存。 244 */ 245 var select_start_ymd = ''; 246 var select_start_ymd_show = ''; 247 var select_end_ymd = ''; 248 var select_end_ymd_show = ''; 249 250 if (select_start_ymd == '' || select_start_ymd == undefined || select_start_ymd == 'undefined' || _this.compareDate(select_start_ymd, fullDate[3]) == 3) { 251 select_start_ymd = date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate(); 252 select_start_ymd_show = _this.parseMonth(date.getMonth() + 1) + '月' + _this.parseDay(date.getDate()) + '日'; 253 } 254 255 if (select_end_ymd == '' || select_end_ymd == undefined || select_end_ymd == 'undefined' || _this.compareDate(select_end_ymd, fullDate[3]) == 3) { 256 let temp_date = new Date(date.getTime() + 86400 * 1000) 257 select_end_ymd = temp_date.getFullYear() + '-' + (temp_date.getMonth() + 1) + '-' + temp_date.getDate(); 258 select_end_ymd_show = _this.parseMonth(temp_date.getMonth() + 1) + '月' + _this.parseDay(temp_date.getDate()) + '日'; 259 } 260 //设置数据。并且保存缓存 261 _this.select_start_ymd = select_start_ymd 262 _this.select_end_ymd = select_end_ymd 263 264 //通过月份。计划最长可预定天数和日期 ,最后一天为离店时间。所以多加一天可选择 265 let max_reserve_days = _this.month_length * 30 + 1; 266 267 //最大天数转换成毫秒数。再转换成时间 268 let max_date = new Date(date.getTime() + max_reserve_days * 24 * 60 * 60 * 1000); 269 let max_reserve_date = max_date.getFullYear() + '-' + (max_date.getMonth() + 1) + '-' + max_date.getDate() + ''; 270 _this.max_reserve_days = max_reserve_days 271 _this.max_reserve_date = max_reserve_date 272 273 //获取当前月份完整日期天数 274 let cur_month_date = new Date(fullDate[0] + '-' + _this.parseMonth(fullDate[1]) + '-01') 275 let cur_month = {}; 276 cur_month.fullYear = fullDate[0]; // 年 277 cur_month.fullMonth = fullDate[1]; //月 278 cur_month.dayLength = _this.getMonthDays(cur_month.fullMonth, cur_month.fullYear);//当前月份总共有多少天 279 cur_month.firstDayWeek = cur_month_date.getDay(); //当前月份第一天星期几0~7 280 cur_month.curDay = date.getDate(); //当前天 281 cur_month.days = []; 282 //初始化天数 283 var item = {}; 284 for (let i = 1; i <= cur_month.dayLength; i++) { 285 item = { 286 ymd: cur_month.fullYear + '-' + cur_month.fullMonth + '-' + i, 287 ymd_cn: _this.parseMonth(cur_month.fullMonth) + '月' + _this.parseDay(i) + '日', 288 day: i, 289 disabled: i < cur_month.curDay ? 'disabled' : '', 290 }; 291 //开始时间 292 item.start_date = _this.compareDate(_this.select_start_ymd, item.ymd) == 2 ? 'active-start' : ''; 293 //中间的日期 294 item.active_date = (_this.compareDate(_this.select_start_ymd, item.ymd) == 3 && _this.compareDate(_this.select_end_ymd, item.ymd) == 1) ? 'active' : ''; 295 //结束时间 296 item.end_date = _this.compareDate(_this.select_end_ymd, item.ymd) == 2 ? 'active-end' : ''; 297 //超过设置最长日期。禁止选择 298 item.disabled2 = _this.compareDate(max_reserve_date, item.ymd) == 3 ? 'disabled' : ''; 299 cur_month['days'].push(item); 300 } 301 302 //前补0 303 if (cur_month.firstDayWeek > 0) { 304 for (let i = 0; i < cur_month.firstDayWeek; i++) { 305 cur_month['days'].unshift(''); 306 } 307 } 308 309 _this.calendar.push(cur_month) 310 311 var next_month_date; 312 var nextfullDate = []; 313 for (let i2 = 0; i2 < _this.month_length; i2++) { 314 //下一个朋的天数信息 315 next_month_date = new Date(fullDate[0], fullDate[1] + i2, '1'); 316 nextfullDate = [ 317 next_month_date.getFullYear(), 318 next_month_date.getMonth() + 1, 319 ] 320 var next_month = {}; 321 next_month.fullYear = nextfullDate[0]; // 年 322 next_month.fullMonth = nextfullDate[1]; //月 323 next_month.dayLength = _this.getMonthDays(next_month.fullMonth, next_month.fullYear);//当前月份总共有多少天 324 next_month.firstDayWeek = next_month_date.getDay(); //当前月份第一天星期几0~6 325 next_month.days = []; 326 //初始化天数 327 for (let i = 1; i <= next_month.dayLength; i++) { 328 item = { 329 ymd: next_month.fullYear + '-' + next_month.fullMonth + '-' + i, 330 ymd_cn: _this.parseMonth(next_month.fullMonth) + '月' + _this.parseDay(i) + '日', 331 day: i, 332 active: '', 333 disabled: '', 334 }; 335 //开始时间 336 item.start_date = _this.compareDate(_this.select_start_ymd, item.ymd) == 2 ? 'active-start' : ''; 337 //中间的日期 338 item.active_date = (_this.compareDate(_this.select_start_ymd, item.ymd) == 3 && _this.compareDate(_this.select_end_ymd, item.ymd) == 1) ? 'active' : ''; 339 //结束时间 340 item.end_date = _this.compareDate(_this.select_end_ymd, item.ymd) == 2 ? 'active-end' : ''; 341 //超过设置最长日期。禁止选择 342 item.disabled2 = _this.compareDate(max_reserve_date, item.ymd) == 3 ? 'disabled' : ''; 343 next_month['days'].push(item); 344 } 345 //前补0 346 if (next_month.firstDayWeek > 0) { 347 for (let i = 0; i < next_month.firstDayWeek; i++) { 348 next_month['days'].unshift(''); 349 } 350 } 351 _this.calendar.push(next_month) 352 } 353 console.log(_this.calendar); 354 }, 355 //格式月份期 356 parseMonth: function(month){ 357 month = parseInt(month); 358 if(month <10){ 359 month = '0'+month 360 } 361 return month; 362 }, 363 364 //格式天 365 parseDay: function (day) { 366 day = parseInt(day); 367 if (day < 10) { 368 day = '0' + day 369 } 370 return day; 371 }, 372 // 获取每个月的天数 373 getMonthDays(m, year) { 374 let days = [0, 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; 375 if (m != 2) { 376 return days[m]; 377 } 378 if ((year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0 && year % 100 === 0)) { 379 return 29 380 } else { 381 return 28 382 } 383 }, 384 compareDate(date1, date2){ 385 var dateone = date1.replace(/-/g,'/'); 386 var datetwo = date2.replace(/-/g,'/'); 387 var oDate1 = new Date(dateone) 388 var oDate2 = new Date(datetwo) 389 if(oDate1.getTime() > oDate2.getTime()){ 390 return 1; //大于 391 } else if (oDate1.getTime() == oDate2.getTime()) { 392 return 2; //等于 393 }else{ 394 return 3; //小于 395 } 396 }, 397 //点击日期按钮 398 selectDate:function(item){ 399 var _this = this; 400 let select_data = item; 401 console.log(select_data) 402 let select_start_ymd = _this.select_start_ymd; 403 let select_end_ymd = _this.select_end_ymd; 404 //如果是点击不能用的地址 405 if (select_data.disabled != ''){ 406 return false; 407 } 408 if (_this.select_index == 'start') { 409 select_start_ymd = select_data.ymd; 410 //如果选择的日期。是当前日期。或者比当前开始日期还早的。就 要把当前日期变为开始日期 411 412 _this.select_start_ymd = select_start_ymd; 413 _this.select_start_ymd_show = select_data.ymd_cn; 414 _this.select_end_ymd = ''; 415 _this.select_end_ymd_show = ''; 416 417 //将索引改为结束时间 418 419 _this.select_index = 'end'; 420 421 } else if (_this.select_index == 'end'){ 422 let v = _this.compareDate(select_start_ymd, select_data.ymd) 423 //如果选择的时间大于开始时间。则有效。否则重置开始时间 424 if(v == 3 ){ 425 _this.select_end_ymd = select_data.ymd; 426 _this.select_end_ymd_show = select_data.ymd_cn; 427 //将索引改为结束时间 428 _this.select_index = 'start'; 429 //保存数据到缓存 430 _this.saveDate(); 431 }else{ 432 _this.select_start_ymd = select_data.ymd; 433 _this.select_start_ymd_show = select_data.ymd_cn; 434 _this.select_end_ymd = ''; 435 _this.select_end_ymd_show = ''; 436 //将索引改为结束时间 437 _this.select_index = 'end'; 438 } 439 440 } 441 _this.resetCalendar(); 442 }, 443 //重新计算一下日历 444 resetCalendar:function(){ 445 let _this = this; 446 let calendar = _this.calendar; 447 if(calendar.length > 0 ){ 448 for (var i in calendar){ 449 if(calendar[i]['days'].length > 0 ){ 450 for (var i2 in calendar[i]['days']) { 451 if (calendar[i]['days'][i2] != ''){ 452 //开始时间 453 calendar[i]['days'][i2]['start_date'] = _this.compareDate(_this.select_start_ymd, calendar[i]['days'][i2]['ymd']) == 2 ? 'active-start' : ''; 454 //中间的日期 455 calendar[i]['days'][i2]['active_date'] = (_this.compareDate(_this.select_start_ymd, calendar[i]['days'][i2]['ymd']) == 3 && _this.compareDate(_this.select_end_ymd, calendar[i]['days'][i2]['ymd']) == 1) ? 'active' : ''; 456 //结束时间 457 calendar[i]['days'][i2]['end_date'] = _this.compareDate(_this.select_end_ymd, calendar[i]['days'][i2]['ymd']) == 2 ? 'active-end' : ''; 458 } 459 } 460 } 461 } 462 } 463 464 _this.calendar = calendar; 465 }, 466 //如果设置结束时间成功。保存一次当前时间。并且计算总天数。到缓存中 467 saveDate:function(){ 468 var _this = this; 469 var date1 = new Date(this.select_start_ymd.replace(/-/g,'/')); 470 var date2 = new Date(this.select_end_ymd.replace(/-/g,'/')); 471 //计算天数 472 var days = parseInt((date2.getTime() - date1.getTime()) / 1000 / 86400); 473 //保存缓存 474 _this.select_all_day = days; 475 } 476 },computed:{ 477 478 },mounted(){ 479 var _this = this; 480 _this.$nextTick(function () { 481 _this.initDate() 482 }) 483 } 484 }) 485 </script> 486 </html>
里面的备注很清晰,需要存起来下个页面用可以在saveDate这个函数中存进去localStorage中。
案例除了vue不基于任何插件,可以自定义修改,自定义程度高