vue實現(xiàn)一個炫酷的日歷組件
公司業(yè)務新開了一個商家管理微信H5移動端項目,日歷控件是商家管理員查看通過日程來篩選獲取某日用戶的訂單等數(shù)據(jù)。 如圖: 假設今天為2018-09-02
90天前:
90天后;
產(chǎn)品需求:
- 展示當前日期(服務器時間)前后90天,一共181天的日期。
- 日歷可以左右滑動切換月份。
- 當月份的如果不在181天區(qū)間的,需要置灰并且不可點擊。
- 點擊日歷綁定的節(jié)點的外部,關閉彈窗。
涉及內(nèi)容:
- 獲取服務器時間,渲染日歷數(shù)據(jù)
- vue-touch監(jiān)聽手勢滑動事件
- ios日期兼容處理
- clickOutSide自定義指令
- mock模擬數(shù)據(jù)
開發(fā):
參考了 基于Vue開發(fā)一個日歷組件 - 掘金 日歷的年月日計算方式。 核心思想:假設當前月份是二月份,根據(jù)二月和三月的1號是星期幾,來對二月進行布局。(如果需要在二月顯示一月和三月的日期,還需要知道一月份有多少天)
在項目開發(fā)中,為了與后臺同事并行開發(fā)。項目采用來mock模擬數(shù)據(jù)來攔截接口。
日歷展盤
// calendar.vue <template> <div class="cp-calendar"> <v-touch @swipeleft="handleNextMonth" @swiperight="handlePreMonth" class="calendar"> <div class="calendar-main" > <span class="item-con header-item" 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>
初始化數(shù)據(jù) 針對服務器時間進行初始數(shù)據(jù)處理
// calendar.vue // 設置初始數(shù)據(jù) 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) { // 定義每個月的天數(shù),如果是閏年第二月改為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 },
// src/config/utils.js // 公共方法 /** * @param AddDayCount 必傳 今天前后N天的日期 * @param dateStr: 非必傳 獲取傳入日期前后N天的日期:'2018-01-20' * @param type 非必傳 'lhRili'類型格式如'2018-7-3' * @return 返回日期'2018/01/20' */ export const getDateStr = (AddDayCount, dateStr, type) => { // console.log('getDateStr', AddDayCount, dateStr, type) var dd if (!dateStr) { dd = new Date() } else { // 判斷是否為IOS const isIOS = !!navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); let formatDateStr = isIOS ? dateStr.replace(/-/g, '/') : dateStr dd = new Date((formatDateStr.length < 12) ? formatDateStr + ' 00:00:00' : formatDateStr) } dd.setDate(dd.getDate() + AddDayCount) // 獲取AddDayCount天后的日期 let y = dd.getFullYear() let m let d if (type === 'lhRili') { m = dd.getMonth() + 1 d = dd.getDate() } else { let currentMon = (dd.getMonth() + 1) let getDate = dd.getDate() m = currentMon < 10 ? '0' + currentMon : currentMon // 獲取當前月份的日期,不足10補0 d = getDate < 10 ? '0' + getDate : getDate // 獲取當前幾號,不足10補0 } let time = y + '-' + m + '-' + d return time }
左右觸摸滑動事件 判斷是否月份還可以繼續(xù)滑動
// calendar.vue // 上一個月 handlePreMonth() { if (this.prevYearMonthBoolean) { return } if (this.selectedMonth === 0) { this.selectedYear = this.selectedYear - 1 this.selectedMonth = 11 this.selectedDate = 1 } else { this.selectedMonth = this.selectedMonth - 1 this.selectedDate = 1 } }, // 下一個月 handleNextMonth() { if (this.nextYearMonthBoolean) { return } if (this.selectedMonth === 11) { this.selectedYear = this.selectedYear + 1 this.selectedMonth = 0 this.selectedDate = 1 } else { this.selectedMonth = this.selectedMonth + 1 this.selectedDate = 1 } },
vuex存儲數(shù)據(jù)
// src/store/schedule.js const schedule = { state: { selectedDate: '', // 手動點擊選中的日期 currentDate: '' // 服務器當前日期 }, getters: { getSelectedDate: state => state.selectedDate, getCurrentDate: state => state.currentDate }, mutations: { SET_SELECTED_DATE: (state, data) => { state.selectedDate = data }, SET_CURRENT_DATE: (state, data) => { state.currentDate = data } }, actions: { setSelectedDate: ({ commit }, data) => commit('SET_SELECTED_DATE', data), setCurrentDate: ({ commit }, data) => commit('SET_CURRENT_DATE', data) } }; export default schedule;
clickOutSide指令 指令方法監(jiān)聽
// src/directive/click-out-side.js export default{ bind (el, binding, vnode) { function documentHandler (e) { if (el.contains(e.target)) { return false; } if (binding.expression) { binding.value(e); } } el.__vueClickOutside__ = documentHandler; document.addEventListener('click', documentHandler); }, unbind (el, binding) { document.removeEventListener('click', el.__vueClickOutside__); delete el.__vueClickOutside__; } }
注冊指令
// src/directive/index.js import clickOutSide from './click-out-side' const install = function (Vue) { Vue.directive('click-outside', clickOutSide) } if (window.Vue) { window.clickOutSide = clickOutSide Vue.use(install); // eslint-disable-line } clickOutSide.install = install export default clickOutSide
// src/main.js import clickOutSide from '@/directive/click-out-side/index' Vue.use(clickOutSide)
使用方式:當某節(jié)點外部需要觸發(fā)事件時,掛載到該節(jié)點上
// calendar.vue <div class="cp-calendar" v-click-outside="spaceClick"> .... </div>
這里需要使用 fastclick 庫來消除解決移動端點擊事件300ms延時
// src/mian.js import FastClick from 'fastclick' // 在移動端,手指點擊一個元素,會經(jīng)過:touchstart --> touchmove -> touchend --> click。 FastClick.attach(document.body);
mock數(shù)據(jù)
// src/mock/index.js // mock數(shù)據(jù)入口 import Mock from 'mockjs' import currentTime from './currentTime' // 攔截接口請求 Mock.mock(/\/schedule\/getCurrentTime/, 'get', currentTime) export default Mock // src/mock/currentTime.js import Mock from 'mockjs' export default { getList: () => { return { 'status': 'true', 'code': '200', 'msg': null, 'info': { 'currentDate': '2018-09-02' } } } } // src/main.js // 開發(fā)環(huán)境引入mock if (process.env.NODE_ENV === 'development') { require('./mock') // 需要在這里引入mock數(shù)據(jù)才可以全局攔截請求 }
坑點
- 在微信內(nèi)置瀏覽器中,ios的日期格式跟安卓的日期格式分別是:YY/MM/DD和YY-MM-DD。這里需要對微信內(nèi)置瀏覽器User Agent進行判斷。
- 獲取服務器時間的異步問題,把獲取到的服務器時間保存在vuex里面,在calendar.vue頁面監(jiān)聽當前日期的變化。及時將日歷數(shù)據(jù)計算渲染出來。
推薦:
感興趣的朋友可以關注小編的微信公眾號【碼農(nóng)那點事兒】,更多網(wǎng)頁制作特效源碼及學習干貨哦?。?!
總結
以上所述是小編給大家介紹的vue實現(xiàn)一個炫酷的日歷組件,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關文章
vueCli?4.x升級5.x報錯:Progress?Plugin?Invalid?Options的解決方法
本文主要介紹了vueCli?4.x升級5.x報錯:Progress?Plugin?Invalid?Options的解決方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2024-01-01vue頁面渲染數(shù)組中數(shù)據(jù)文案后添加逗號最后不加
這篇文章主要為大家介紹了vue頁面渲染數(shù)組中數(shù)據(jù)文案后添加逗號最后不加逗號示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-08-08Vue3+TypeScript+printjs實現(xiàn)標簽批量打印功能的完整過程
最近在做vue項目時使用到了print-js打印,這里給大家分享下,這篇文章主要給大家介紹了關于Vue3+TypeScript+printjs實現(xiàn)標簽批量打印功能的完整過程,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2024-09-09