基于vue手写仿钉钉 滑动日历
template:
<div id="calendar_content" class="calendar_content" @touchstart="handleStart" @touchmove="handleMove" @touchend="handleEnd"> <div id="calendar_content_wrapper_prev" class="calendar_content_wrapper prev"> <div class="week_wrap" v-for="(week, index) in prevDaysArr" :key="index"> <div class="day_wrap" v-for="(day, indexDay) in week" :key="indexDay" :class="[6, 7].includes(day.week) ? 'rest_day' : ''"> <div @click="changeActiveDay(day)"> <p class="day_text" :class="activeDay === day.timeStr ? 'active_day' : ''">{{day.timeStr ? new Date(day.timeStr).getDate() : ''}}</p> </div> <template v-if="day.id"> <span v-if="day.status == 0" class="normalCircle"></span> <span v-else class="abnormalCircle"></span> </template> </div> </div> </div> <div id="calendar_content_wrapper" class="calendar_content_wrapper middle"> <div class="week_wrap" v-for="(week, index) in daysArr" :key="index"> <div class="day_wrap" v-for="(day, indexDay) in week" :key="indexDay" :class="[6, 7].includes(day.week) ? 'rest_day' : ''"> <div @click="changeActiveDay(day)"> <p class="day_text" :class="activeDay === day.timeStr ? 'active_day' : ''">{{day.timeStr ? new Date(day.timeStr).getDate() : ''}}</p> </div> <template v-if="day.id"> <span v-if="day.status == 0" class="normalCircle"></span> <span v-else class="abnormalCircle"></span> </template> </div> </div> </div> <div id="calendar_content_wrapper_next" class="calendar_content_wrapper next"> <div class="week_wrap" v-for="(week, index) in nextDaysArr" :key="index"> <div class="day_wrap" v-for="(day, indexDay) in week" :key="indexDay" :class="[6, 7].includes(day.week) ? 'rest_day' : ''"> <div @click="changeActiveDay(day)"> <p class="day_text" :class="activeDay === day.timeStr ? 'active_day' : ''">{{day.timeStr ? new Date(day.timeStr).getDate() : ''}}</p> </div> <template v-if="day.id"> <span v-if="day.status == 0" class="normalCircle"></span> <span v-else class="abnormalCircle"></span> </template> </div> </div> </div> </div>
data:
nowDay: '', nowWeekDay: '', prevDaysArr: [], daysArr: [], nextDaysArr: [], activeDay: '', activeDayInfo: {},// 坐标 startClientX: '', isReset: false,
methods:
// 组装查询月份的数组 assembleDayArr (key, date) { let allDays = this.getMonthDay(date || this.nowDay); // 查询每周一的下标 let indexArr = []; allDays.map((val, index) => { if(val.week === 1){ indexArr.push(index); } }); // 如果第一天正好是 周一 则剔除indexArr[0] if(indexArr[0] === 0){ indexArr.splice(0, 1); }; // 查询有几周 let weeks = indexArr.length + 1; // 将 每周塞进数组 let arr = []; for(let i = 0;i < weeks;i++){ if(i === 0){ arr[i] = allDays.slice(0, indexArr[0]); }else{ arr[i] = allDays.slice(indexArr[i-1], indexArr[i]); } if(arr[i].length < 7 && arr[i][0].week === 1){ let diff = 7 - arr[i].length; for(let j = 0;j < diff;j++){ arr[i].push({}); } }else if(arr[i].length < 7 && arr[i][0].week !== 1){ let diff = 7 - arr[i].length; for(let j = 0;j < diff;j++){ arr[i].unshift({}); } } }; this[key] = arr;
}, // 选中日期改变事件 changeActiveDay (day) { if(day.timeStr && day.timeStr !== this.activeDayInfo.timeStr){ this.activeDay = day.timeStr; this.activeDayInfo = {...day}; this.nowDay = day.timeStr; this.nowWeekDay = this.findNowWeek(); } }, // 头部时间修改 changeMonth (flag, key) { let resultMonth; let nowChooseMonth = new Date(this.nowDay).getMonth(); if(flag === 'reduce'){ resultMonth = new Date(this.nowDay).setMonth(nowChooseMonth - 1, 1); }else { resultMonth = new Date(this.nowDay).setMonth(nowChooseMonth + 1, 1); if(resultMonth > new Date().getTime()){ this.$toast.fail('不能选择未来月份'); return; }; }; // 设置头部显示日期 查看是否是当前月份 if(dateFormat(new Date(), 'yyyy-MM') === dateFormat(new Date(resultMonth), 'yyyy-MM')){ this.nowDay = dateFormat(new Date(), 'yyyy-MM-dd'); }else{ this.nowDay = dateFormat(new Date(resultMonth), 'yyyy-MM-01'); }; this.activeDay = this.nowDay; this.nowWeekDay = this.findNowWeek(); this.assembleDayArr(key || 'daysArr'); this.getList(); }, // 滑动开始 handleStart (e) { this.startClientX = e.changedTouches[0].clientX; }, // 滑动中 handleMove (e) { e.preventDefault(); // isReset 防止过多次 计算前后数组; if(!this.isReset && Math.abs(e.changedTouches[0].clientX - this.startClientX) > 1){ let nowChooseMonth = new Date(this.nowDay).getMonth(); let prevMonth = new Date(this.nowDay).setMonth(nowChooseMonth - 1); let nextMonth = new Date(this.nowDay).setMonth(nowChooseMonth + 1); this.assembleDayArr('prevDaysArr', dateFormat(new Date(prevMonth), 'yyyy-MM-dd')); this.assembleDayArr('nextDaysArr', dateFormat(new Date(nextMonth), 'yyyy-MM-dd')); this.isReset = true; } let dom = document.getElementById('calendar_content'); dom.style.marginLeft = (e.changedTouches[0].clientX - this.startClientX) + 'px'; }, // 滑动结束 handleEnd (e) { let diffX = e.changedTouches[0].clientX - this.startClientX; let dom = document.getElementById('calendar_content'); if(Math.abs(diffX) === 0){ return; } if(Math.abs(diffX) > 100){ if(diffX < 0){ let nowChooseMonth = new Date(this.nowDay).getMonth(); let resultMonth = new Date(this.nowDay).setMonth(nowChooseMonth + 1); if(resultMonth > new Date().getTime()){ goBack(dom, diffX); this.$toast.fail('不能选择未来月份'); return; }; } let animation = document.getElementById("calendar_content").animate([ { marginLeft: `${diffX}px`}, { marginLeft: diffX > 0 ? '7.5rem' : '-7.5rem'} ], { duration: 500, delay: 0 }); animation.play(); setTimeout(() => { dom.style.marginLeft = 0; if(diffX > 0){ this.changeMonth('reduce', 'daysArr'); }; if(diffX < 0){ this.changeMonth('plus', 'daysArr'); }; this.isReset = false; }, 501); }else{ goBack(dom, diffX); }; // 回到远点 手指移动距离不超过100 以及 不能选择未来月份情况 function goBack (rDom, reference) { let animation = document.getElementById("calendar_content").animate([ { marginLeft: `${reference}px`}, { marginLeft: 0} ], { duration: 500, delay: 0 }); animation.play(); setTimeout(() => { rDom.style.marginLeft = 0; }, 501) } },
// 返回文字 星期几 findNowWeek () { let nowWeek = new Date(this.nowDay).getDay(); let weekInfo = { 0: '星期日', 1: '星期一', 2: '星期二', 3: '星期三', 4: '星期四', 5: '星期五', 6: '星期六', }; return weekInfo[nowWeek]; }, // 获取 月份 对应的所有日期 getMonthDay (date) { let now = new Date(date); let current_month_num = this.mGetDate(date); let current_month = []; for (let i = 1; i <= current_month_num; i++) { let day = now.setDate(i); let everyDay = dateFormat(new Date(day), 'yyyy-MM-dd'); current_month.push({ timeStr: everyDay, week: new Date(everyDay).getDay() === 0 ? 7 :new Date(everyDay).getDay(), foreInfo: {}, afterInfo: {}, }); }; return current_month; }, // 获取当前月的天数 mGetDate (date) { var date = new Date(date); var year = date.getFullYear(); var month = date.getMonth() + 1; var d = new Date(year, month, 0); return d.getDate(); },
css:
.calendar_content{ position: relative; width: 22.5rem; // top: .6rem; left: -7.5rem; display: flex; .calendar_content_wrapper{ flex: 1; &.prev{ left: 0; } &.middle{ left: 7.5rem; } &.next{ right: 0; } } } .week_wrap{ display: flex; .day_wrap{ flex: 1; &.rest_day{ color: #BABABA; } p{ margin: .1rem auto; &.day_text{ width: .54rem; height: .54rem; line-height: .54rem; border-radius: 100%; } &.active_day{ background: #3f6ad3; color: white; } } } }