vue @click 赋值_vue 手写一个时间选择器
vue 手寫一個時間選擇器
最近研究了 DatePicker 的實現原理后做了一個 vue 的 DatePicker 組件,今天帶大家一步一步實現 DatePicker 的 vue 組件。
原理
DatePicker 的原理是——計算日歷面板中當月或選中月份的總天數及前后月份相近的日子,根據點擊事件計算日歷面板顯示內容,以及將所選值賦值給<input/>標簽。實現
- CSS 代碼于文章末尾處
1. 構思頁面結構
DatePicker 組件由輸入框和日歷面板組成,寫好頁面主體結構。
<div class="date-picker"><input class="input" v-model="dateValue" @click="openPanel"/><transition name="fadeDownBig"><div class="date-panel" v-show="panelState"></div></transiton> </div>輸入框<input>點擊顯示或隱藏日歷面板,openPanel()方法改變 panelState 布爾值控制日歷面板的顯示隱藏。
日歷面板由頂部條和面板兩部分組成,而面板則由年份選擇面板,月份選擇面板,日期選擇面板所組成,結構如下:
<div class="date-panel" v-show="panelState"><!-- 頂部按鈕及年月顯示條 --><div class="topbar"><span @click="leftBig"><<</span><span @click="left"><</span><span class="year" @click="panelType = 'year'">{{tmpYear}}</span><span class="month" @click="panelType = 'month'">{{changeTmpMonth}}</span><span @click="right">></span><span @click="rightBig">>></span></div><!-- 年面板 --><div class="type-year" v-show="panelType === 'year'"><ul class="year-list"><li v-for="(item, index) in yearList":key="index"@click="selectYear(item)"><span :class="{selected: item === tmpYear}" >{{item}}</span></li></ul></div><!-- 月面板 --><div class="type-year" v-show="panelType === 'month'"><ul class="year-list"><li v-for="(item, index) in monthList":key="index"@click="selectMonth(item)"><span :class="{selected: item.value === tmpMonth}" >{{item.label}}</span></li></ul></div><!-- 日期面板 --><div class="date-group" v-show="panelType === 'date'"><span v-for="(item, index) in weekList" :key="index" class="weekday">{{item.label}}</span><ul class="date-list"><li v-for="(item, index) in dateList"v-text="item.value":class="{preMonth: item.previousMonth, nextMonth: item.nextMonth,selected: date === item.value && month === tmpMonth && item.currentMonth, invalid: validateDate(item)}":key="index" @click="selectDate(item)"></li></ul></div> </div>2. 頁面數據實現
DatePicker 所對應的 data 代碼
data() {return {dateValue: "", // 輸入框顯示日期date: new Date().getDate(), // 當前日期panelState: false, // 初始值,默認panel關閉tmpMonth: new Date().getMonth(), // 臨時月份,可修改month: new Date().getMonth(),tmpYear: new Date().getFullYear(), // 臨時年份,可修改weekList: [{ label: "Sun", value: 0 },{ label: "Mon", value: 1 },{ label: "Tue", value: 2 },{ label: "Wed", value: 3 },{ label: "Thu", value: 4 },{ label: "Fri", value: 5 },{ label: "Sat", value: 6 }], // 周monthList: [{ label: "Jan", value: 0 },{ label: "Feb", value: 1 },{ label: "Mar", value: 2 },{ label: "Apr", value: 3 },{ label: "May", value: 4 },{ label: "Jun", value: 5 },{ label: "Jul", value: 6 },{ label: "Aug", value: 7 },{ label: "Sept", value: 8 },{ label: "Oct", value: 9 },{ label: "Nov", value: 10 },{ label: "Dec", value: 11 }], // 月nowValue: 0, // 當前選中日期值panelType: "date" // 面板狀態}; },DatePicker 的核心在于日期面板的數據。我們知道,一個月最多31天,最少28天。面板按周日至周六設計,最極端的情況如下:
最多的極端情況:
最少的極端情況:
根據上表我們可以得知一個月最多占六個星期,最少四個星期,所以日歷面板必須設計為 6 行,剩余的用下個月的日期補上,最多補14天。因此日期數組可以這么設計:
computed: {dateList() {//獲取當月的天數let currentMonthLength = new Date(this.tmpYear,this.tmpMonth + 1,0).getDate();//先將當月的日期塞入dateListlet dateList = Array.from({ length: currentMonthLength },(val, index) => {return {currentMonth: true,value: index + 1};});// 獲取當月1號的星期是為了確定在1號前需要插多少天let startDay = new Date(this.tmpYear, this.tmpMonth, 1).getDay();// 確認上個月一共多少天let previousMongthLength = new Date(this.tmpYear,this.tmpMonth,0).getDate();// 在1號前插入上個月日期for (let i = 0, len = startDay; i < len; i++) {dateList = [{ previousMonth: true, value: previousMongthLength - i }].concat(dateList);}// 補全剩余位置,至少14天,則 i < 15for (let i = 1, item = 1; i < 15; i++, item++) {dateList[dateList.length] = { nextMonth: true, value: i };}return dateList;}, }changeTmpMonth 為選擇月份后顯示的文案,yearList 為年份列表,為了與月份數量保持一致,我們也設長度為12.
computed: {changeTmpMonth() {return this.monthList[this.tmpMonth].label;},// 通過改變this.tmpYear則可以改變年份數組yearList() {return Array.from({ length: 12 }, (value, index) => this.tmpYear + index);} }3. 實現頁面功能
(1)面板切換功能
- 點擊輸入框,除了打開日歷面板,同時也默認為日期面板
openPanel() {this.panelState = !this.panelState;this.panelType = "date"; },- 點擊 2018 年份進入年份面板,點擊相對應年份顯示該年份并進入月份選擇面板
- 點擊 Aug 月份進入月份面板,點擊相對應月份顯示該月份并進入日期選擇面板
點擊日期選擇日期,關閉面板同時賦值給輸入框
// methods selectDate(item) {// 賦值 當前 nowValue,用于控制樣式突出顯示當前月份日期this.nowValue = item.value;// 選擇了上個月if (item.previousMonth) this.tmpMonth--;// 選擇了下個月if (item.nextMonth) this.tmpMonth++;// 獲取選中日期的 datelet selectDay = new Date(this.tmpYear, this.tmpMonth, this.nowValue);// 格式日期為字符串后,賦值給 inputthis.dateValue = this.formatDate(selectDay.getTime());// 關閉面板this.panelState = !this.panelState; }, // 日期格式方法 formatDate(date, fmt = this.format) {if (date === null || date === "null") {return "--";}date = new Date(Number(date));var o = {"M+": date.getMonth() + 1, // 月份"d+": date.getDate(), // 日"h+": date.getHours(), // 小時"m+": date.getMinutes(), // 分"s+": date.getSeconds(), // 秒"q+": Math.floor((date.getMonth() + 3) / 3), // 季度S: date.getMilliseconds() // 毫秒};if (/(y+)/.test(fmt))fmt = fmt.replace(RegExp.$1,(date.getFullYear() + "").substr(4 - RegExp.$1.length));for (var k in o) {if (new RegExp("(" + k + ")").test(fmt))fmt = fmt.replace(RegExp.$1,RegExp.$1.length === 1? o[k]: ("00" + o[k]).substr(("" + o[k]).length));}return fmt; }, // 確認是否為當前月份 validateDate(item) {if (this.nowValue === item.value && item.currentMonth) return true; },(2)topbar 中左右箭頭功能,具體詳看下面方法
// < left() {if (this.panelType === "year") this.tmpYear--;else {if (this.tmpMonth === 0) {this.tmpYear--;this.tmpMonth = 11;} else this.tmpMonth--;} }, // << leftBig() {if (this.panelType === "year") this.tmpYear -= 12;else this.tmpYear--; }, // > right() {if (this.panelType === "year") this.tmpYear++;else {if (this.tmpMonth === 11) {this.tmpYear++;this.tmpMonth = 0;} else this.tmpMonth++;} }, // >> rightBig() {if (this.panelType === "year") this.tmpYear += 12;else this.tmpYear++; },(3) 實現輸入框的雙向綁定及格式規定
props
props: {value: {type: [Date, String],default: ""},format: {type: String,default: "yyyy-MM-dd"} },其中 value 支持日期格式和字符串,當設置了props時,則需在monted鉤子函數中初始化input 值。format 默認值為 "yyyy-MM-dd", 當然你也可以設置為 "dd-MM-yyyy"等。
mounted() {if (this.value) {this.dateValue = this.formatDate(new Date(this.value).getTime());} },雙向綁定父組件賦值 props 為 value, 子組件傳遞的事件為input, 因此需在 selectDate 方法中 emit 事件及數據給父組件
selectDate(item) {...this.$emit("input", selectDay); },這樣,父組件便可以進行雙向綁定了
<Datepicker v-model="time" format="dd-MM-yyyy"/>(4)點擊頁面其他位置收起日歷面板
原理監聽頁面的點擊事件,檢測到有點擊事件時關閉面板,但點擊組件內容時也會觸發點擊事件,因此需要在組件內部阻止冒泡。同時,當組件銷毀時,也要及時清除該監聽器。
組件最外層阻止冒泡
<div class="date-picker" @click.stop></div>頁面創建設置監聽
mounted() {...window.addEventListener("click", this.eventListener); }頁面銷毀清除監聽
destroyed() {window.removeEventListener("click", this.eventListener); }公共方法
eventListener() {this.panelState = false; },項目Demo
項目源碼
有用就點個贊唄~
最后,貼上 CSS 代碼...
- fadeDownBig 后面的樣式為 vue <transiton> 的動畫特效.
總結
以上是生活随笔為你收集整理的vue @click 赋值_vue 手写一个时间选择器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: windows安装anaconda_[计
- 下一篇: redissession有容量上限吗_传