Vue文件封装日历组件
封装就是要具有灵活性,样式自适应,调用的时候传入props就可以变成自己想要的样式。
效果展示网址:
源代码:
1 <template> 2 <div :style="mainboxstyle"> 3 <div :style="toplinestyle"> 4 <div @click="timeboxshow=false;setnowtime(true)" 5 :style="[iptstyle,{float:'left'}]">{{curiptdate}}</div> 6 <div @click="timeboxshow=true;settimeipt(true)" 7 :style="[iptstyle,{float:'right'}]">{{curipttime}}</div> 8 </div> 9 <div :style="timeboxstyle" v-show="timeboxshow"> 10 <div :style="[timealiststyle, 11 {paddingleft:setwidth(12),width:setwidth(70)}]" 12 @mousewheel.stop="setindex($event,1)" 13 v-on:dommousescroll.stop="setindex($event,1)"> 14 <div :style="[{zindex:99,width:'100%', 15 height:setheight(30),position:'absolute', 16 top:setheight(60),left:setwidth(12),borderbottom:border, 17 bordertop:border}]"></div> 18 <div v-for="(item,i) in hourarr" 19 @click="curshowhourindex=(i-2)" 20 :style="[timeonestyle,i==0? 21 {margintop:setheight((i-curshowhourindex)*30), 22 transition:'margin-top 0.2s'}:{},(i-curshowhourindex)==2? 23 {display:'flex',alignitems:'center'}:{}, 24 (i-curshowhourindex)==5||(i-curshowhourindex)==-1? 25 {color:'rgba(0,0,0,0)'}:{}]" :key="i">{{item}}</div> 26 </div> 27 <div :style="timealiststyle" 28 @mousewheel.stop="setindex($event,2)" 29 v-on:dommousescroll.stop="setindex($event,2)"> 30 <div :style="[{zindex:99,width:'100%',height:setheight(30), 31 position:'absolute',top:setheight(60),left:0, 32 borderbottom:border,bordertop:border}]"></div> 33 <div v-for="(item,i) in minarr" 34 @click="curshowminindex=(i-2)" 35 :style="[timeonestyle,i==0? 36 {margintop:setheight((i-curshowminindex)*30),transition:'margin-top 0.2s'}:{}, 37 (i-curshowminindex)==2?{display:'flex',alignitems:'center'}:{}, 38 (i-curshowminindex)==5||(i-curshowminindex)==-1?{color:'rgba(0,0,0,0)'}:{}]" 39 :key="i">{{item}}</div> 40 </div> 41 <div @mousewheel.stop="setindex($event,3)" 42 v-on:dommousescroll.stop="setindex($event,3)" 43 :style="[timealiststyle,{width:setwidth(30), 44 paddingright:setwidth(12)}]"> 45 <div :style="[{zindex:99,width:setwidth(30),paddingright:setwidth(12), 46 height:setheight(30),position:'absolute',top:setheight(60),left:0, 47 borderbottom:border,bordertop:border}]"></div> 48 <div v-for="(item,i) in secarr" @click="curshowsecindex=(i-2)" 49 :style="[timeonestyle,i==0?{margintop:setheight((i-curshowsecindex)*30), 50 transition:'margin-top 0.2s'}:{}, 51 (i-curshowsecindex)==2?{display:'flex',alignitems:'center'}:{}, 52 (i-curshowsecindex)==5||(i-curshowsecindex)==-1?{color:'rgba(0,0,0,0)'}:{}]" 53 :key="i">{{item}}</div> 54 </div> 55 <div :style="{height:setheight(33),float:'left',width:'100%'}"> 56 <div @click="timeboxshow=false" :style="[bottomtimebtnstyle,{color:'#1e90ff'}]">确认</div> 57 <div @click="timeboxshow=false;curipttime=''" :style="bottomtimebtnstyle">取消</div> 58 </div> 59 </div> 60 <div :style="[onelinestyle,{height:setheight(37)}]"> 61 <div :style="[ 62 onebtnstyle, 63 i<2?{float:'left'}:{float:'right'}, 64 { 65 height:setheight(34) 66 } 67 ]" 68 v-for="(item,i) in [0,1,2,3]" 69 :key="i"> 70 <div 71 :style="[ 72 sanjiaostyle, 73 i<2?{float:'left'}: 74 {float:'right'} 75 ]" 76 :class="{'iconfont':true,'icon-zuoshuangjiantou':i==0, 77 'icon-icon-copy':i==1,'icon-icon':i==3,'icon-zuoshuangjiantou-copy':i==2}" 78 @click="i==0?setdate(curyear-1,curmonth): 79 i==1?setdate(curyear,curmonth-1): 80 i==3?setdate(curyear,curmonth+1):setdate(curyear+1,curmonth)" 81 ></div> 82 </div> 83 <div :style="titlestyle"> 84 <span>{{curyear}}</span>年 85 <span>{{curmonth}}</span>月 86 </div> 87 </div> 88 <div :style="[onelinestyle,{height:setheight(34)}]"> 89 <div v-for="(item,j) in [0,1,2,3,4,5,6]" :key="j" 90 :style="[onebtnstyle,{height:setheight(34),fontsize:setheight(16)}, 91 j==0?{clear:'both'}:{}]">{{weeklist[j]}}</div> 92 </div> 93 <div @mouseleave="curenterday=0" :style="[onelinestyle,{height:setheight(30*arr.length/7)}]"> 94 <div @mouseenter="i>=arr.indexof(1)&&i<arr.lastindexof(1)?curenterday=item:''" 95 @click="i>=arr.indexof(1)&&i<arr.lastindexof(1) 96 ?clickfn(item):i<arr.indexof(1) 97 ?switchmonthclick('-1',item):i>=arr.lastindexof(1) 98 ?switchmonthclick('+1',item):()=>{}" 99 :style="[ 100 onebtnstyle, 101 {color:'#999999'}, 102 i>=arr.indexof(1)&&i<arr.lastindexof(1)? 103 (item==curclickday&&curmonth==curclickmonth&&curyear==curclickyear)? 104 selectstyle: 105 (item==curclickday+1&&curmonth==curclickmonth&&curyear==curclickyear&&(i+1)%7==1) 106 ?{color:'#333333',clear:'both'}: 107 curenterday==item||(demodate.getfullyear()==curyear 108 &&demodate.getmonth()==curmonth-1 109 &&demodate.getdate()==item)? 110 {color:'#1e90ff'}:{color:'#333333'} 111 :{}, 112 ]" 113 114 v-for="(item,i) in arr" :key="i">{{item}}</div> 115 </div> 116 <div :style="[onelinestyle,{padding:0,paddingtop:setheight(14),height:setheight(54)}]"> 117 <div :style="bottomlinestyle"> 118 <div @click="emitdate()" :style="bottombtnstyle">确定</div> 119 <div @click="setnowtime()" :style="[bottombtnstyle,{border:'none',color:'#1e90ff'}]">此刻</div> 120 </div> 121 </div> 122 </div> 123 </template> 124 125 <script> 126 export default { 127 name: "calendar", 128 data() { 129 return { 130 mainboxstyle: {}, 131 sanjiaostyle: {}, 132 onebtnstyle: {}, 133 onelinestyle: {}, 134 titlestyle: {}, 135 toplinestyle:{}, 136 bottomlinestyle:{}, 137 bottombtnstyle:{}, 138 selectstyle:{}, 139 timeboxstyle:{}, 140 bottomtimebtnstyle:{}, 141 iptstyle:{}, 142 timealiststyle:{}, 143 curyear: 0, 144 curmonth: 0, 145 curfirstweek: "", 146 weeklist: ["日", "一", "二", "三", "四", "五", "六"], 147 datenumlist: [], 148 arr: [], 149 curdate: {}, 150 curiptdate:'', 151 curipttime:'', 152 curenterday:0, 153 curclickday:0, 154 curclickmonth:0, 155 curclickyear:0, 156 curclickhour:'00', 157 curclickmin:'00', 158 curclicksec:'00', 159 demodate:{}, 160 hourarr:[], 161 minarr:[], 162 secarr:[], 163 timeonestyle:{}, 164 curshowhourindex:0, 165 curshowminindex:0, 166 curshowsecindex:0, 167 timeboxshow:false, 168 }; 169 }, 170 created() { 171 for(var i=0;i<60;i++){ 172 if(i<24){ 173 i<10?this.hourarr.push('0'+i):this.hourarr.push(i+'') 174 } 175 i<10?this.minarr.push('0'+i):this.minarr.push(i+'') 176 i<10?this.secarr.push('0'+i):this.secarr.push(i+'') 177 } 178 this.curdate = new date() 179 if(this.value){ 180 this.setdate( 181 this.value.slice(0,4), 182 this.value.slice(5,7), 183 this.value.slice(this.value.lastindexof(this.format[1])+1,this.value.lastindexof(this.format[1])+3), 184 this.value.slice(this.value.lastindexof(this.format[1])+4,this.value.lastindexof(this.format[1])+6), 185 this.value.slice(this.value.lastindexof(this.format[1])+7,this.value.lastindexof(this.format[1])+9) 186 ) 187 this.clickfn(this.value.slice(8,10)*1) 188 } 189 var cw = (this.setwidth(33,true)-this.setheight(28,true)).tofixed(2) 190 var dw = (cw/2).tofixed(2) 191 this.selectstyle = { 192 background:'#1e90ff', 193 width:this.setheight(28), 194 height:this.setheight(28), 195 margin:this.setheight(1)+' 0', 196 marginleft:dw+this.unit, 197 marginright:cw-dw+this.unit, 198 float:'left', 199 color:'#fff', 200 borderradius:'50%', 201 } 202 this.demodate = new date() 203 this.timeboxstyle = { 204 width: this.setwidth(160), 205 height: this.setheight(208), 206 background: this.background, 207 fontsize: this.fontsize + this.unit, 208 color:this.color, 209 border:this.border, 210 position:'absolute', 211 top:this.setheight(46), 212 left:this.setwidth(130), 213 boxshadow:`0 0 ${this.setheight(6)} rgba(0,0,0,0.3)` 214 } 215 this.timealiststyle = { 216 width:this.setwidth(58), 217 height:this.setheight(161), 218 borderbottom:this.border, 219 overflow:'hidden', 220 // display:'flex', 221 // flexdirection:'column', 222 float:'left', 223 position:'relative', 224 margintop:this.setheight(10), 225 } 226 this.timeonestyle = { 227 width:'100%', 228 height:this.setheight(30), 229 lineheight:this.setheight(30), 230 cursor:'pointer' 231 } 232 this.bottombtnstyle = { 233 width:this.setwidth(45), 234 height:this.setheight(23), 235 border:this.border, 236 borderradius:this.setheight(3), 237 textalign:'center', 238 lineheight:this.setheight(21), 239 float:'right', 240 marginright:this.setheight(4), 241 margintop:this.setheight(8), 242 marginbottom:this.setheight(8), 243 cursor: "pointer", 244 } 245 this.bottomtimebtnstyle = { 246 width:this.setwidth(42), 247 height:this.setheight(33), 248 textalign:'center', 249 lineheight:this.setheight(33), 250 float:'right', 251 cursor: "pointer", 252 } 253 this.bottomlinestyle = { 254 width:'100%', 255 height:this.setheight(40), 256 bordertop:this.border, 257 } 258 this.iptstyle = { 259 width:this.setwidth(116), 260 height:this.setheight(27), 261 border:this.border, 262 borderradius:this.setheight(3), 263 textalign:'center', 264 lineheight:this.setheight(27), 265 margintop:this.setheight(9), 266 cursor:'pointer' 267 } 268 this.toplinestyle = { 269 width:'100%', 270 height:this.setheight(47), 271 borderbottom:this.border, 272 padding:'0 '+this.setwidth(7), 273 } 274 this.mainboxstyle = { 275 width: this.w + this.unit, 276 height: this.h, 277 float:'left', 278 background: this.background, 279 fontsize: this.fontsize + this.unit, 280 color:this.color, 281 border:this.border, 282 position:'relative', 283 boxshadow:`0 0 ${this.setheight(6)} rgba(0,0,0,0.3)`, 284 }; 285 this.onebtnstyle = { 286 width:this.setwidth(33), 287 height:this.setheight(30), 288 fontsize: this.fontsize + this.unit, 289 float: "left", 290 background:this.background, 291 display: "flex", 292 alignitems: "center", 293 justifycontent: "center", 294 cursor: "pointer", 295 }; 296 this.sanjiaostyle = { 297 fontsize:this.setheight(22) 298 }; 299 this.titlestyle = { 300 width:this.setwidth(96), 301 height:this.setheight(32), 302 fontsize: this.setheight(16), 303 lineheight:this.setheight(32), 304 float: "left", 305 textalign: "center", 306 marginbottom:this.setheight(5), 307 }; 308 this.onelinestyle = { 309 width: "100%", 310 paddingleft:this.setwidth(12), 311 }; 312 this.init(); 313 }, 314 props: { 315 format:{ 316 type:array, 317 default:function(){ 318 return ['-',' ',':'] 319 } 320 }, 321 fn:{ 322 type:function, 323 required:true 324 }, 325 value:{ 326 type:string, 327 }, 328 w: { 329 type: [number,string], 330 default: 2.57 331 }, 332 h: { 333 type: [number,string], 334 default: 'auto' 335 }, 336 unit: { 337 type: string, 338 default: "rem" 339 }, 340 fontsize: { 341 type: [number,string], 342 default: 0.14 343 }, 344 color: { 345 type: string, 346 default: "#333" 347 }, 348 border:{ 349 type:string, 350 default:'0.01rem solid #cccccc' 351 }, 352 background:{ 353 type:string, 354 default:'#fff' 355 }, 356 }, 357 methods: { 358 setheight(h,flag){ 359 if(flag){ 360 var height = (this.unit=='rem'?(this.fontsize*(h*0.01/0.14)).tofixed(2):math.round(this.fontsize*(h/14)))*1 361 }else{ 362 var height = (this.unit=='rem'?(this.fontsize*(h*0.01/0.14)).tofixed(2):math.round(this.fontsize*(h/14)))+this.unit 363 } 364 return height 365 }, 366 setwidth(w,flag){ 367 if(flag){ 368 var width = (this.unit=='rem'?(this.w*(w*0.01/2.57)).tofixed(2):math.round(this.w*(w/257)))*1 369 }else{ 370 var width = (this.unit=='rem'?(this.w*(w*0.01/2.57)).tofixed(2):math.round(this.w*(w/257)))+this.unit 371 } 372 return width 373 }, 374 getmonthdaycount(year, month) { 375 var isleapyear = false; 376 if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { 377 isleapyear = true; 378 } 379 var daycount = 0; 380 switch (month) { 381 case 0: 382 case 2: 383 case 4: 384 case 6: 385 case 7: 386 case 9: 387 case 11: 388 daycount = 31; 389 break; 390 case 1: 391 if (isleapyear) { 392 daycount = 29; 393 } else { 394 daycount = 28; 395 } 396 break; 397 default: 398 daycount = 30; 399 break; 400 } 401 return daycount; 402 }, 403 setdate(year, month,hour,min,sec) { 404 if(year){ 405 this.curdate.setfullyear(year*1); 406 } 407 if(month){ 408 this.curdate.setmonth(month-1); 409 } 410 if(hour){ 411 this.curdate.sethours(hour*1) 412 } 413 if(min){ 414 this.curdate.setminutes(min*1) 415 } 416 if(sec){ 417 this.curdate.setseconds(sec*1) 418 } 419 this.init(); 420 }, 421 init() { 422 var activedate = new date(); 423 activedate.setfullyear(this.curdate.getfullyear()) 424 activedate.setmonth(this.curdate.getmonth()) 425 activedate.setdate(this.curdate.getdate()) 426 activedate.sethours(this.curdate.gethours()) 427 activedate.setminutes(this.curdate.getminutes()) 428 activedate.setseconds(this.curdate.getseconds()) 429 this.arr = [] 430 this.curyear = activedate.getfullyear(); 431 this.curmonth = activedate.getmonth() + 1; 432 if(this.value){ 433 this.curshowhourindex = this.hourarr.indexof(activedate.gethours()<10?'0'+activedate.gethours():''+activedate.gethours())-2 434 this.curshowminindex = this.minarr.indexof(activedate.getminutes()<10?'0'+activedate.getminutes():''+activedate.getminutes())-2 435 this.curshowsecindex = this.secarr.indexof(activedate.getseconds()<10?'0'+activedate.getseconds():''+activedate.getseconds())-2 436 } 437 var len = this.getmonthdaycount( 438 activedate.getfullyear(), 439 activedate.getmonth() 440 ); 441 for (var i = 0; i < len; i++) { 442 this.arr.push(i + 1); 443 } 444 activedate.setdate(0); 445 var lastmonthdaycount = this.getmonthdaycount( 446 activedate.getfullyear(), 447 activedate.getmonth() 448 ); 449 var weekindex = activedate.getday(); 450 451 for (var i = 0; i < weekindex + 1; i++) { 452 this.arr.unshift(lastmonthdaycount - i); 453 } 454 var len2 = this.arr.length; 455 for (var i = 0; i < 42 - len2; i++) { 456 this.arr.push(i + 1); 457 if(this.arr.length%7==0){ 458 break; 459 } 460 } 461 }, 462 clickfn(item){ 463 this.timeboxshow = false 464 this.curclickday=item; 465 this.curclickmonth=this.curmonth; 466 this.curclickyear=this.curyear; 467 this.curiptdate=this.curclickyear+this.format[0]+(this.curclickmonth<10?'0'+this.curclickmonth:this.curclickmonth)+this.format[0]+(this.curclickday<10?'0'+this.curclickday:this.curclickday) 468 }, 469 switchmonthclick(num,item){ 470 this.setdate(this.curyear,this.curmonth*1+num*1) 471 settimeout(() => { 472 this.clickfn(item) 473 }, 1); 474 }, 475 setnowtime(flag){ 476 if(!flag){ 477 this.settimeipt(true) 478 } 479 var nowdate = new date() 480 this.curclickyear=nowdate.getfullyear(); 481 this.curclickmonth=nowdate.getmonth()+1; 482 this.curclickday=nowdate.getdate(); 483 this.curiptdate=this.curclickyear+this.format[0]+(this.curclickmonth<10?'0'+this.curclickmonth:this.curclickmonth)+this.format[0]+(this.curclickday<10?'0'+this.curclickday:this.curclickday) 484 this.setdate(this.curclickyear,this.curclickmonth) 485 }, 486 setindex(evt,num){ 487 488 if(evt.wheeldelta){ 489 if(evt.wheeldelta>0){ 490 if(num==1){ 491 if(this.curshowhourindex<this.hourarr.length-3){ 492 this.curshowhourindex += 1; 493 } 494 } 495 if(num==2){ 496 if(this.curshowminindex<this.minarr.length-3){ 497 this.curshowminindex += 1; 498 } 499 } 500 if(num==3){ 501 if(this.curshowsecindex<this.secarr.length-3){ 502 this.curshowsecindex += 1; 503 } 504 } 505 } 506 if(evt.wheeldelta<0){ 507 if(num==1){ 508 if(this.curshowhourindex>-2){ 509 this.curshowhourindex -= 1; 510 } 511 } 512 if(num==2){ 513 if(this.curshowminindex>-2){ 514 this.curshowminindex -= 1; 515 } 516 } 517 if(num==3){ 518 if(this.curshowsecindex>-2){ 519 this.curshowsecindex -= 1; 520 } 521 } 522 } 523 }else if(evt.detail){ 524 if(evt.detail>0){ 525 if(num==1){ 526 if(this.curshowhourindex<this.hourarr.length-3){ 527 this.curshowhourindex += 1; 528 } 529 } 530 if(num==2){ 531 if(this.curshowminindex<this.minarr.length-3){ 532 this.curshowminindex += 1; 533 } 534 } 535 if(num==3){ 536 if(this.curshowsecindex<this.secarr.length-3){ 537 this.curshowsecindex += 1; 538 } 539 } 540 } 541 if(evt.detail<0){ 542 if(num==1){ 543 if(this.curshowhourindex>-2){ 544 this.curshowhourindex -= 1; 545 } 546 } 547 if(num==2){ 548 if(this.curshowminindex>-2){ 549 this.curshowminindex -= 1; 550 } 551 } 552 if(num==3){ 553 if(this.curshowsecindex>-2){ 554 this.curshowsecindex -= 1; 555 } 556 } 557 } 558 } 559 }, 560 settimeipt(flag){ 561 this.timeboxshow = true 562 if(flag){ 563 var newdatetime = new date() 564 this.curshowhourindex = newdatetime.gethours()-2 565 this.curshowminindex = newdatetime.getminutes()-2 566 this.curshowsecindex = newdatetime.getseconds()-2 567 } 568 this.curclickhour = this.hourarr[this.curshowhourindex*1+2] 569 this.curclickmin = this.minarr[this.curshowminindex*1+2] 570 this.curclicksec = this.secarr[this.curshowsecindex*1+2] 571 this.curipttime=this.curclickhour+this.format[2]+this.curclickmin+this.format[2]+this.curclicksec 572 }, 573 emitdate(){ 574 if(this.curiptdate&&this.curipttime){ 575 var valuefb = this.curiptdate+this.format[1]+this.curipttime 576 this.fn(false) 577 this.$emit('input',valuefb) 578 } 579 } 580 }, 581 watch:{ 582 curshowhourindex(newval,oldval){ 583 this.settimeipt() 584 }, 585 curshowminindex(newval,oldval){ 586 this.settimeipt() 587 }, 588 curshowsecindex(newval,oldval){ 589 this.settimeipt() 590 }, 591 } 592 }; 593 </script> 594 595 <style scoped> 596 @import 'https://at.alicdn.com/t/font_1043142_ucomoekqib.css'; 597 span { 598 display: inline-block; 599 } 600 .clear-fix:after{ 601 content:'.'; 602 clear:both; 603 display:block; 604 height:0; 605 overflow:hidden; 606 visibility:hidden; 607 } 608 div{ 609 -moz-user-select: none; 610 -o-user-select:none; 611 -khtml-user-select:none; 612 -webkit-user-select:none; 613 -ms-user-select:none; 614 user-select:none; 615 } 616 h1{ 617 flex-direction: column; 618 justify-content: flex-end 619 } 620 </style>
--------------------------------------------------------------------------------------------------------------------------------------------------------
1.建一个vue文件,打出一个 < 然后按tab键 自动创建vue的模板 首先在script标签里写好name和props属性
props写成校验写法,这里我先定义了宽度w,高度h,单位rem,字体大小fontsize,字体颜色color,边框border,背景颜色background,这些都是基本的组件封装需要的props。如图:
这些props我都给它设置了一个默认值,当调用时不传其中的某个参数就是默认的值,传的话就是指定值;
然后定义交互的props: v-model需要的props:value v-model绑定的是显示在父组件某个容器里的值 显示的日期格式化单位format 控制显隐的点击函数fn。如图:
类型为array或者object的props设置默认值需要用回调函数返回。
-------------------------------------------------------------------------------------------------------------------------------------------------
2.props定义好后就开始写结构和样式,这里的样式不能写在style标签里面,需要用:style动态添加到标签上面。
先写data定义好几个基础的样式变量。如图:
接着在methods里面写两个设置宽度样式setwidth和高度样式setheight的方法。如图:
setheight与setwidth这两个方法的第一个参数为设置的像素大小(即设计图量的多少px就传多少,不带单位),第二个参数可选,传入true时返回一个纯数字,不传时返回一个带单位的值
这里我用了三元运算符,后面将会大量用到三元运算符(有嵌套用法)。this.w与this.fontsize即传入的props。
this.w*(w/257)与this.fontsize*(h/14) 这里的w / 257就是你要设置的宽相对于设计图的最外层大盒子的宽的占比。h/14就是设置的高相对于字体大小的占比。
这里不推荐用h / this.h 因为这里的组件高度需要自适应。
-------------------------------------------------------------------------------------------------------------------------------
3.template里面创建好基本结构 。如图:
接着在created里面初始化样式 。如图:
上面的代码可能就是最后的this.selectstyle有点不好理解
这里的cw就是用按钮的实际的宽度去减选中时的高度。因为选中的时候宽度需要写成和高度一样的才能成为一个圆形。
这时就需要设置选中的按钮左右的margin。因为cw 有可能是奇数,所以除以2的时候再四舍五入就不是平均值了。
所以margin-right就是用cw减去margin-left的值,避免盒子被挤到下一行。
-------------------------------------------------------------------------------------------------------
4.逻辑部分=>创建结构里需要显示的数据。如图:
在template里将数据渲染到标签上。如图:
接着在methods里面写一个初始化方法 init :
这个方法用来渲染页面的日期数据,这里需要写一个获取指定年份和月份所对应的天数。
在methods里面再定义一个方法getmonthdaycount。如图:
------------------------------------------------------------------------未完待续------------------------------------------------------------------