开发过程中遇到一个令人发指的,一个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 })