VUE自定义(有限)库存日历插件
开发过程中遇到一个令人发指的,一个element-ui无法满足的日历需求, 改造其日历插件的代价太大,于是索性自己手写一个,需求如下:
1. 根据开始、结束时间计算时间覆盖的月份,渲染有限的可选择日期
2. 日期下显示每日库存,库存为0的日期不可选择,同时不可作为中间日期被包含选择
3. 根据传入的默认选择日期初始化选择状态,若没有传入默认选择日期,初始化选择开始与结束时间
3. 其余功能与正常日历插件相同
基本思路: 1. 根据传入日期列表的开始与结束时间计算一共有几个月份,根据月份生成数组,数组内存放每月的覆盖天数,
2. 遍历月份数组 ,计算当前月份的一号是周几,根据这个补全当月时间,设置每天的选择状态(0:未选择,1:连带选择,2:被点击选择)为0,并初始化每个覆盖天数的库存,补全的其余天数库存设为0
3. 将每月补全的数据放入新数组,作为渲染每月Tab页的数据源
4. 设置全局的开始与结束日期的选择状态,点击日期,判断设置开始日期选择状态为false,则记录开始时间并将状态设置为true,若开始日期状态为true则记录结束日期并设置结束日期选择状态为true,若结束日期状态为true则关闭选择框,并为父组件的开始与结束时间重新赋值
5. 每次开启选择框则设置开始与结束日期选择状态为false
插件使用:
<data-picker :datedata="planDateList" :initstartdate='initstartdate' :initenddate='initenddate' :startdate='saleDate[0]' :enddate='saleDate[1]' :planid='resSalePlanId'></data-picker>
效果图 :
插件代码如下:
1 Vue.component('data-picker', { 2 props: [ 3 'datedata', 4 'startdate', 5 'enddate', 6 'planid', 7 'initstartdate', 8 'initenddate' 9 ], 10 data: function () { 11 return { 12 currentDay: 1, 13 currentMonth: 1, 14 premonthDays: 0, 15 currentYear: 2002, 16 currentWeek: 1, 17 days: [], 18 daysTemplate: [01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31], 19 monthCountArr: [31,29,31,30,31,30,31,31,30,31,30,31], 20 index: 0, 21 startWeekNum: 1, 22 monthArr: [], 23 monthDataArr:[],//整月数据 24 startDate: '', 25 startDateFlag: false, 26 startIndex: 0, 27 endDate: '', 28 endDateFlag: false, 29 endIndex: 0, 30 selStartMonthIndex: 0, 31 selEndMonthIndex: 0, 32 show: false, 33 errorFlag: true,//选择库存错误标志 34 allMonthData:[],//存放所有月份处理过后的数据 35 allMonthData:[],//存放所有月份处理过后的数据 36 37 } 38 }, 39 mounted: function() { //在vue初始化时调用 40 this.initData(); 41 }, 42 component:{}, 43 methods: { 44 initData: function(){ 45 this.monthArr = [] 46 47 this.startDate = this.startdate; 48 this.endDate = this.enddate; 49 //计算出有几个月 [{11:'11',days:['2-18-10-22']},{}] 50 var dateArr = this.datedata, 51 obj = {}, 52 count = 0; 53 //按月份分组放数据 54 for(var i=0; i<dateArr.length;i++){ 55 var month = dateArr[i].startDate.split("-")[1]; 56 if(!obj[month]){ 57 count++; 58 obj[month] = month; 59 var dateObj = {}; 60 //dateObj.leftAmount = dateArr[i].leftAmount; 61 dateObj.days = []; 62 dateObj.days.push({date:dateArr[i].startDate, leftAmount:dateArr[i].leftAmount, hasAmount: true}); 63 dateObj[month] = month; 64 this.monthArr.push(dateObj); 65 }else{ 66 this.monthArr[count-1].days.push({date:dateArr[i].startDate, leftAmount:dateArr[i].leftAmount, hasAmount: true}) 67 } 68 } 69 //console.log(this.monthArr); 70 71 //初始化所有月数据 72 for(var m=0; m<this.monthArr.length;m++){ 73 this.getCurMonthData(m); 74 } 75 76 //this.getCurMonthData(this.index); 77 78 //初始化选择 79 var start='',end=''; 80 this.startDateFlag = false; 81 this.endDateFlag = false; 82 if(this.initstartdate && this.initenddate ){//有初始值 83 start = this.initstartdate; 84 end = this.initenddate; 85 }else{ 86 start = this.startDate; 87 end = this.endDate; 88 } 89 for(var j=0; j<this.allMonthData.length; j++){ 90 for(var k=0; k<this.allMonthData[j].length; k++){ 91 92 if(this.allMonthData[j][k].date == start){ 93 this.index = j; 94 this.monthDataArr = this.allMonthData[j]; 95 this.selectDay(k, this.allMonthData[j][k]); 96 } 97 if(this.allMonthData[j][k].date == end){ 98 this.index = j; 99 this.monthDataArr = this.allMonthData[j]; 100 this.selectDay(k, this.allMonthData[j][k]); 101 break; 102 } 103 } 104 105 } 106 107 //初始化第一页 108 this.index = 0; 109 this.currentMonth = this.monthArr[0].days[0].date.split("-")[1]; 110 this.currentYear = this.monthArr[0].days[0].date.split("-")[0]; 111 this.monthDataArr = this.allMonthData[0]; 112 this.show = false; 113 }, 114 pickPre: function(currentYear,currentMonth,index){ 115 if(this.index >0){ 116 this.index--; 117 if(!this.allMonthData[this.index]){//allMonthData 118 //更新数据 119 this.getCurMonthData(this.index); 120 }else{ 121 this.currentMonth = this.monthArr[this.index].days[0].date.split("-")[1]; 122 this.currentYear = this.monthArr[this.index].days[0].date.split("-")[0]; 123 this.monthDataArr = this.allMonthData[this.index]; 124 //重置其他月份的选择状态 125 //this.clearSelect(this.index); 126 } 127 }else{ 128 return; 129 } 130 }, 131 pickNext: function(currentYear,currentMonth,index){ 132 if(this.index < this.monthArr.length-1){ 133 this.index++; 134 if(!this.allMonthData[this.index]){//allMonthData 135 //更新数据 136 this.getCurMonthData(this.index); 137 }else{ 138 this.currentMonth = this.monthArr[this.index].days[0].date.split("-")[1]; 139 this.currentYear = this.monthArr[this.index].days[0].date.split("-")[0]; 140 this.monthDataArr = this.allMonthData[this.index]; 141 //重置其他月份的选择状态 142 //this.clearSelect(this.index); 143 } 144 }else{ 145 return; 146 } 147 }, 148 //重置其他月份的选择状态 149 clearSelect: function(curIndex){ 150 for(var i=0; i<this.allMonthData.length; i++){ 151 if(i !== curIndex){ 152 for(var j=0; j<this.allMonthData[i].length; j++){ 153 this.allMonthData[i][j].selectStatus = 0; 154 } 155 } 156 } 157 }, 158 //根据当前选择月份整理数据 index 159 getCurMonthData: function(index){ 160 this.currentMonth = this.monthArr[index].days[0].date.split("-")[1]; 161 this.currentYear = this.monthArr[index].days[0].date.split("-")[0]; 162 163 //判断是否是闰年 164 if(this.isLeapYear(this.currentYear)){ 165 this.monthCountArr = [31,28,31,30,31,30,31,31,30,31,30,31]; 166 }else{ 167 this.monthCountArr = [31,29,31,30,31,30,31,31,30,31,30,31]; 168 } 169 170 //计算当前月的第一天是周几 dateArr[0].startDate 171 this.startWeekNum = this.getStartWeekNum(this.monthArr[index].days[0].date); 172 173 //初始化当前默认月的天数 174 var monthDays = this.monthCountArr[parseInt(this.currentMonth)-1]; 175 this.days = this.daysTemplate.slice(0,monthDays); 176 //获取前一个月有多少天 177 this.premonthDays=0; 178 if(this.currentMonth == 1){ 179 this.premonthDays = 31; 180 }else{ 181 this.premonthDays = this.monthCountArr[parseInt(this.currentMonth)-2]; 182 } 183 184 //处理整月数据并格式化当前月的日期 31 -- 2018-10-31 185 for(var n=0;n<this.days.length; n++){ 186 this.days[n] = this.currentYear + '-' + this.currentMonth + '-' + (this.days[n] > 9 ? this.days[n] : '0'+ this.days[n]); 187 } 188 //初始化当前框的日期数组 比如: 一号在周四 则数组前加入 28,29,30,31 或 27,28,29,30 或,26,27,28,29 或25,26,27,28 189 for(var k=parseInt(this.startWeekNum); k>0; k--){ 190 this.days.unshift( (this.currentMonth == 1 ? this.currentYear-1 : this.currentYear) + '-' + (this.currentMonth == 1 ? 12 : this.currentMonth-1) + '-' + (this.daysTemplate[this.premonthDays-k] > 9 ? this.daysTemplate[this.premonthDays-k] : '0'+this.daysTemplate[this.premonthDays-k]) ) 191 } 192 //console.log(this.days); 193 //处理形成整月数据 194 this.handelTheMonthData(this.days,this.monthArr,index); 195 }, 196 //处理形成整月数据 monthDataArr 197 handelTheMonthData: function(days,monthArr,index){ 198 199 this.monthDataArr = []; 200 var curMonthData = this.monthArr[index].days; 201 for(var n=0;n<days.length; n++){ 202 var obj={},flag = false,leftAmount=0; 203 for(var m=0; m<curMonthData.length; m++){ 204 if(new Date(curMonthData[m].date).getTime() == new Date(days[n]).getTime() ){//当前日期存在库存 205 flag = true; 206 leftAmount = curMonthData[m].leftAmount; 207 } 208 } 209 if(flag){ 210 obj.date = days[n]; 211 obj.leftAmount = leftAmount; 212 obj.selectStatus = 0; //0:未选择 1:连带选择 2:被选中 213 this.monthDataArr.push(obj); 214 }else{ 215 obj.date = days[n]; 216 obj.leftAmount = 0; 217 obj.selectStatus = 0; //0:未选择 1:连带选择 2:被选中 218 this.monthDataArr.push(obj); 219 } 220 } 221 this.allMonthData[index] = this.monthDataArr; 222 //console.log(this.allMonthData); 223 return this.monthDataArr; 224 225 }, 226 //计算当前月的第一天是周几 227 getStartWeekNum: function(date){ 228 var date = new Date(date); 229 date.setDate(1); 230 //console.log(date.getDay()) 231 return date.getDay(); 232 }, 233 //判断是否是闰年 234 isLeapYear: function(year){ 235 if((year%4==0 && year%100!=0) || year%400==0){ 236 return true; 237 }else{ 238 return false; 239 } 240 }, 241 pickUp: function(){ 242 this.show = !this.show; 243 this.startDateFlag = false; 244 this.endDateFlag = false; 245 }, 246 selectDay: function(index,dayobject){//this.startDate this.endDate 247 //判断当前是否可点击 248 if(dayobject.leftAmount == 0){ 249 return; 250 } 251 //console.log(dayobject); 252 253 self.errorFlag = true; 254 //初始化各日期selectStatus值 255 for(var i=0; i<this.monthDataArr.length; i++){ 256 this.monthDataArr[i].selectStatus = 0; 257 } 258 259 if(this.startDateFlag){ 260 this.endDate = dayobject.date; 261 this.endDateFlag = true; 262 this.endIndex = index; 263 this.selEndMonthIndex = this.index; 264 }else{ 265 this.startDate = dayobject.date; 266 this.startDateFlag = true; 267 this.startIndex = index; 268 this.selStartMonthIndex = this.index; 269 //重置其他月份的选择状态 270 this.clearSelect(this.index); 271 } 272 //设置selectStatus值 :class="{{"select":(dayobject.selectStatus == 2),"subSelect":(dayobject.selectStatus == 1) }}" 273 this.monthDataArr[index].selectStatus = 2; 274 275 if(this.endDateFlag){//判断是否可连续选择 计算连续时间 若可连续设置selectStatus值 关闭弹窗 276 //判断大小是否颠倒 277 if(this.selStartMonthIndex > this.selEndMonthIndex || (this.selStartMonthIndex==this.selEndMonthIndex && this.startIndex>this.endIndex) ){ 278 var box,dateBox,indexBox; 279 box = this.selStartMonthIndex; 280 this.selStartMonthIndex = this.selEndMonthIndex; 281 this.selEndMonthIndex = box; 282 283 indexBox = this.startIndex; 284 this.startIndex = this.endIndex; 285 this.endIndex = indexBox; 286 287 dateBox = this.startDate; 288 this.startDate = this.endDate; 289 this.endDate = dateBox; 290 } 291 292 //判断是否跨月 293 var startMonth = this.startDate.split("-")[1]; 294 if(this.selEndMonthIndex !== this.selStartMonthIndex){//不同月 startMonth !== this.currentMont 295 //获取开始月的index 开始月到月尾 开始月到结束月的中间月的全部 以及结束月到点击时间 selectStatus变为1 296 for(var i=this.selStartMonthIndex; i<this.selEndMonthIndex+1; i++){ 297 for(var j=0; j<this.allMonthData[i].length; j++){ 298 299 // if(this.allMonthData[i][j].leftAmount == 0){// 300 // self.errorFlag = false; 301 // return; 302 // } 303 304 if(i==this.selStartMonthIndex && j == this.startIndex && this.allMonthData[i][j].leftAmount !==0){//开始月点击的第一天 305 this.allMonthData[i][j].selectStatus = 2; 306 }else if(i==this.selStartMonthIndex && j > this.startIndex && this.allMonthData[i][j].leftAmount !==0){//开始月其它天 307 this.allMonthData[i][j].selectStatus = 1; 308 }else if(i==this.selEndMonthIndex && j == this.endIndex && this.allMonthData[i][j].leftAmount !==0){//结束月电机的最后一天 309 this.allMonthData[i][j].selectStatus = 2; 310 }else if(i==this.selEndMonthIndex && j < this.endIndex && this.allMonthData[i][j].leftAmount !==0){ 311 this.allMonthData[i][j].selectStatus = 1; 312 }else if(i<this.selEndMonthIndex && i>this.selStartMonthIndex && this.allMonthData[i][j].leftAmount !==0){//中间月 313 this.allMonthData[i][j].selectStatus = 1; 314 } 315 } 316 } 317 }else{//同一月 318 for(var i=this.startIndex; i<this.endIndex+1; i++){ 319 if(this.monthDataArr[i].leftAmount !== 0){ 320 if(i == this.startIndex || i == this.endIndex){ 321 this.monthDataArr[i].selectStatus = 2; 322 } 323 // if(i == this.endIndex){ 324 // this.monthDataArr[i].selectStatus = 2; 325 // } 326 else { 327 this.monthDataArr[i].selectStatus = 1; 328 } 329 }else{ 330 self.errorFlag = false; 331 return; 332 } 333 334 } 335 } 336 //设置值 337 this.$parent.$parent.$parent.$parent.curEditData.saleDate=[]; 338 this.$parent.$parent.$parent.$parent.curEditData.saleDate.push(this.startDate); 339 this.$parent.$parent.$parent.$parent.curEditData.saleDate.push(this.endDate); 340 341 //console.log('111111111~~~'); 342 //console.log(this.allMonthData) 343 344 this.pickUp(); 345 } 346 347 } 348 }, 349 watch: { 350 'planid': function(){ 351 this.clearSelect(-1); 352 this.allMonthData = []; 353 this.initData(true); 354 } 355 }, 356 template: 357 '<div class="calendarBox">'+ 358 '<div id="calendarDate" class="calendarInput" @click="pickUp()">'+ 359 '<span>{{startDate}}</span> <span> ~ </span> <span>{{endDate}} <i slot="suffix" class="el-input__icon el-icon-date"></i></span>'+ 360 '</div> '+ 361 '<div class="calendar" v-show="show">'+ 362 '<div class="month">'+ 363 '<ul class="clearfix">'+ 364 '<li v-if="index >= 1" class="arrow" @click="pickPre(currentYear,currentMonth,index)"> ❮ </li>'+ 365 '<li v-if="index < 1" class="disabledArrow" @click="pickPre(currentYear,currentMonth,index)"> ❮ </li>'+ 366 '<li class="year-month" @click="pickYear(currentYear,currentMonth)">'+ 367 '<span class="choose-year">{{ currentYear }} 年 </span>'+ 368 '<span class="choose-month">{{ currentMonth }} 月</span>'+ 369 '</li>'+ 370 '<li v-if="index < monthArr.length-1" class="arrow" style="text-align:right;" @click="pickNext(currentYear,currentMonth,index)"> ❯ </li>'+ 371 '<li v-if="index >= monthArr.length-1" class="disabledArrow" style="text-align:right;" @click="pickNext(currentYear,currentMonth,index)"> ❯ </li>'+ 372 '</ul>'+ 373 '</div>'+ 374 '<ul class="weekdays clearfix">'+ 375 '<li>日</li>'+ 376 '<li>一</li>'+ 377 '<li>二</li>'+ 378 '<li>三</li>'+ 379 '<li>四</li>'+ 380 '<li>五</li>'+ 381 '<li>六</li>'+ 382 '<p v-show="!errorFlag">不可选择库存为0的连续时间段</p>'+ 383 '</ul>'+ 384 '<ul class="days clearfix">'+ 385 '<li v-for="(dayobject,index) in monthDataArr" @click="selectDay(index,dayobject)">'+ 386 '<template v-if="dayobject.selectStatus == 0">'+ 387 '<span v-if="dayobject.leftAmount == 0" class="other-month">{{ new Date(dayobject.date).getDate() }}</span>'+ 388 '<span v-else >'+ 389 '<span v-if="new Date(dayobject.date).getFullYear() == new Date().getFullYear() && new Date(dayobject.date).getMonth() == new Date().getMonth() && new Date(dayobject.date).getDate() == new Date().getDate()" class="active">'+ 390 '{{ new Date(dayobject.date).getDate() }}'+ 391 '<span style="color:#409EFF">库存:{{ dayobject.leftAmount }}</span>'+ 392 '</span>'+ 393 '<span v-else>{{ new Date(dayobject.date).getDate() }}</br><span style="color:#409EFF">库存:{{ dayobject.leftAmount }}</span></span>'+ 394 '</span>'+ 395 '</template>'+ 396 '<template v-if="dayobject.selectStatus == 1">'+ 397 '<span v-if="dayobject.leftAmount == 0" class="other-month subSelect">{{ new Date(dayobject.date).getDate() }}</span>'+ 398 '<span class="subSelect" v-else >'+ 399 '<span v-if="new Date(dayobject.date).getFullYear() == new Date().getFullYear() && new Date(dayobject.date).getMonth() == new Date().getMonth() && new Date(dayobject.date).getDate() == new Date().getDate()" class="active">'+ 400 '{{ new Date(dayobject.date).getDate() }}'+ 401 '<span class="inventory">库存:{{ dayobject.leftAmount }}</span>'+ 402 '</span>'+ 403 '<span v-else>{{ new Date(dayobject.date).getDate() }}</br><span class="inventory">库存:{{ dayobject.leftAmount }}</span></span>'+ 404 '</span>'+ 405 '</template>'+ 406 '<template v-if="dayobject.selectStatus == 2">'+ 407 '<span v-if="dayobject.leftAmount == 0" class="other-month select">{{ new Date(dayobject.date).getDate() }}</span>'+ 408 '<span class="select" v-else >'+ 409 // '<span class="selectFlag" v-if="dayobject.selectStatus == 1">开始</span>'+ 410 // '<span class="selectFlag" v-if="dayobject.selectStatus == 2">结束</span>'+ 411 '<span v-if="new Date(dayobject.date).getFullYear() == new Date().getFullYear() && new Date(dayobject.date).getMonth() == new Date().getMonth() && new Date(dayobject.date).getDate() == new Date().getDate()" class="active">'+ 412 '{{ new Date(dayobject.date).getDate() }}'+ 413 '<span class="inventory">库存:{{ dayobject.leftAmount }}</span>'+ 414 '</span>'+ 415 '<span v-else>{{ new Date(dayobject.date).getDate() }}</br><span class="inventory">库存:{{ dayobject.leftAmount }}</span></span>'+ 416 '</span>'+ 417 '</template>'+ 418 '</li>'+ 419 '</ul>'+ 420 '</div> '+ 421 '</div> ' 422 })