公司业务新开了一个商家管理微信H5移动端项目,日历控件是商家管理员查看通过日程来筛选获取某日用户的订单等数据。 如图: 假设今天为2018-09-02
90天前:
90天后;
产品需求:
展示当前日期(服务器时间)前后90天,一共181天的日期。
日历可以左右滑动切换月份。
当月份的如果不在181天区间的,需要置灰并且不可点击。
点击日历绑定的节点的外部,关闭弹窗。
涉及内容:
获取服务器时间,渲染日历数据
vue-touch监听手势滑动事件
ios日期兼容处理
clickOutSide自定义指令
mock模拟数据
开发:
参考了 基于Vue开发一个日历组件 - 掘金 日历的年月日计算方式。 核心思想:假设当前月份是二月份,根据二月和三月的1号是星期几,来对二月进行布局。(如果需要在二月显示一月和三月的日期,还需要知道一月份有多少天)
在项目开发中,为了与后台同事并行开发。项目采用来mock模拟数据来拦截接口。
日历展盘
// calendar.vue <template> <div> <v-touch @swipeleft="handleNextMonth" @swiperight="handlePreMonth"> <div > <span v-for="(item, index) in calendarHeader" :key="index">{{item}}</span> <div :class="`item-con ${todayStyle(item.content) && 'item-con-today'} ${item.type === 'disabled' && 'disableStyle'}`" :style="{opacity: isChangeMonth ? 0 : 1}" @click.stop="handleDayClick(item)" v-for="(item, index) in getMonthDays(selectedYear, selectedMonth)" :key="item.type + item.content + `${index}`"> <span :class="`main-content ${selectedDateStyle(item.content) && 'selectedColor'}`"> {{setContent(item.content)}}</span> <span :class="`${selectedDateStyle(item.content) && 'item-con-point'}`" ></span> </div> </div> </v-touch> </div> </template>
初始化数据 针对服务器时间进行初始数据处理
// calendar.vue // 设置初始数据 initData () { this.today = this.currentDate || getDateStr(0) // 如果没有服务器时间,拿本地时间 this.prevDate = getDateStr(-90, this.currentDate) this.nextDate = getDateStr(90, this.currentDate) // 是否有手动选中的日期 let selectedFullDate = this.storeSelectedFullDate if (!this.storeSelectedFullDate) { selectedFullDate = this.currentDate || getDateStr(0) // 如果没有服务器时间,拿本地时间 } this.selectedYear = Number(selectedFullDate.split('-')[0]) this.selectedMonth = Number(selectedFullDate.split('-')[1]) - 1 this.selectedDate = Number(selectedFullDate.split('-')[2]) this.selectedFullDate = `${this.selectedYear}-${this.selectedMonth + 1}-${this.selectedDate}` }, / 渲染日期 getMonthDays(year, month) { // 定义每个月的天数,如果是闰年第二月改为29天 let daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) { daysInMonth[1] = 29; } // 当月第一天为周几 let targetDay = new Date(year, month, 1).getDay(); let calendarDateList = []; let preNum = targetDay; let nextNum = 0; if (targetDay > 0) { // 当前月份1号前的自然周剩余日期,置空 for (let i = 0; i < preNum; i++) { let obj = { type: 'pre', content: '' }; calendarDateList.push(obj); } } // 判断当前年月份 let formatMonth = month + 1 >= 10 ? month + 1 : '0' + (month + 1) this.prevYearMonthBoolean = (`${year}-${formatMonth}` === this.prevYearMonth) this.nextYearMonthBoolean = (`${year}-${formatMonth}` === this.nextYearMonth) for (let i = 0; i < daysInMonth[month]; i++) { // 正常显示的日期 let obj = { type: 'normal', content: i + 1 }; // 判断是否为最往前或者最往后的月份,筛选出不可点击的日期 if (this.prevYearMonthBoolean) { let prevDay = this.prevDate.split('-')[2] if (i + 1 < prevDay) { obj.type = 'disabled' } } else if (this.nextYearMonthBoolean) { let nextDay = this.nextDate.split('-')[2] if (i + 1 > nextDay) { obj.type = 'disabled' } } calendarDateList.push(obj); } nextNum = 6 - new Date(year, month + 1, 0).getDay() // 当前月份最后一天的自然周剩余日期,置空 for (let i = 0; i < nextNum; i++) { let obj = { type: 'next', content: '' }; calendarDateList.push(obj); } return calendarDateList; }, // 设置日期 setContent (content) { if (!content) return '' return `${this.selectedYear}-${this.tf(this.selectedMonth + 1)}-${this.tf(content)}` === this.today ? '今天' : content }, // '今天'样式开关 todayStyle (content) { if (!content) return false // Toast(`${this.selectedYear}-${this.tf(this.selectedMonth + 1)}-${this.tf(content)}`) return `${this.selectedYear}-${this.tf(this.selectedMonth + 1)}-${this.tf(content)}` === this.today }, // 当前选中的日期样式开关 selectedDateStyle (content) { if (!content) return false return `${this.selectedYear}-${this.selectedMonth + 1}-${content}` === this.selectedFullDate },