日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > vue >内容正文

vue

vue @click 赋值_vue 手写一个时间选择器

發布時間:2025/4/5 vue 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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">&lt;&lt;</span><span @click="left">&lt;</span><span class="year" @click="panelType = 'year'">{{tmpYear}}</span><span class="month" @click="panelType = 'month'">{{changeTmpMonth}}</span><span @click="right">&gt;</span><span @click="rightBig">&gt;&gt;</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 年份進入年份面板,點擊相對應年份顯示該年份并進入月份選擇面板
<span class="year" @click="panelType = 'year'">{{tmpYear}}</span> selectYear(item) {this.tmpYear = item;this.panelType = "month"; },
  • 點擊 Aug 月份進入月份面板,點擊相對應月份顯示該月份并進入日期選擇面板
<span class="month" @click="panelType = 'month'">{{changeTmpMonth}}</span> selectMonth(item) {this.tmpMonth = item.value;this.panelType = "date"; },

點擊日期選擇日期,關閉面板同時賦值給輸入框

// 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> 的動畫特效.
.topbar {padding-top: 8px; } .topbar span {display: inline-block;width: 20px;height: 30px;line-height: 30px;color: #515a6e;cursor: pointer; } .topbar span:hover {color: #2d8cf0; } .topbar .year, .topbar .month {width: 60px; } .year-list {height: 200px;width: 210px; } .year-list .selected {background: #2d8cf0;border-radius: 4px;color: #fff; } .year-list li {display: inline-block;width: 70px;height: 50px;line-height: 50px;border-radius: 10px;cursor: pointer; } .year-list span {display: inline-block;line-height: 16px;padding: 8px; } .year-list span:hover {background: #e1f0fe; } .weekday {display: inline-block;font-size: 13px;width: 30px;color: #c5c8ce;text-align: center; } .date-picker {width: 210px;text-align: center;font-family: "Avenir", Helvetica, Arial, sans-serif; } .date-panel {width: 210px;box-shadow: 0 0 8px #ccc;background: #fff; } ul {list-style: none;padding: 0;margin: 0; } .date-list {width: 210px;text-align: left;height: 180px;overflow: hidden;margin-top: 4px; } .date-list li {display: inline-block;width: 28px;height: 28px;line-height: 30px;text-align: center;cursor: pointer;color: #000;border: 1px solid #fff;border-radius: 4px; } .date-list .selected {border: 1px solid #2d8cf0; } .date-list .invalid {background: #2d8cf0;color: #fff; } .date-list .preMonth, .date-list .nextMonth {color: #c5c8ce; } .date-list li:hover {background: #e1f0fe; } input {display: inline-block;box-sizing: border-box;width: 100%;height: 32px;line-height: 1.5;padding: 4px 7px;font-size: 12px;border: 1px solid #dcdee2;border-radius: 4px;color: #515a6e;background-color: #fff;background-image: none;position: relative;cursor: text;transition: border 0.2s ease-in-out, background 0.2s ease-in-out,box-shadow 0.2s ease-in-out;margin-bottom: 6px; } .fadeDownBig-enter-active, .fadeDownBig-leave-active, .fadeInDownBig {-webkit-animation-duration: 0.5s;animation-duration: 0.5s;-webkit-animation-fill-mode: both;animation-fill-mode: both; } .fadeDownBig-enter-active {-webkit-animation-name: fadeInDownBig;animation-name: fadeInDownBig; } .fadeDownBig-leave-active {-webkit-animation-name: fadeOutDownBig;animation-name: fadeOutDownBig; } @-webkit-keyframes fadeInDownBig {from {opacity: 0.8;-webkit-transform: translate3d(0, -4px, 0);transform: translate3d(0, -4px, 0);}to {opacity: 1;-webkit-transform: none;transform: none;} } @keyframes fadeInDownBig {from {opacity: 0.8;-webkit-transform: translate3d(0, -4px, 0);transform: translate3d(0, -4px, 0);}to {opacity: 1;-webkit-transform: none;transform: none;} } @-webkit-keyframes fadeOutDownBig {from {opacity: 1;}to {opacity: 0.8;-webkit-transform: translate3d(0, -4px, 0);transform: translate3d(0, -4px, 0);} } @keyframes fadeOutDownBig {from {opacity: 1;}to {opacity: 0;} }

總結

以上是生活随笔為你收集整理的vue @click 赋值_vue 手写一个时间选择器的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 亚洲激情| 欧美午夜性 | 亚洲一区视频网站 | 欧美日韩中文在线观看 | 漂亮人妻被中出中文字幕 | 黄a免费网络 | xxx视频网站 | 无码人妻精品一区二区三区蜜桃91 | 免费观看一级视频 | 99热这里只有精品18 | 国产精品人 | 久久久久久久一 | 久久精视频| 影音先锋成人网 | 一女三黑人理论片在线 | 色婷婷综合久久久久中文一区二区 | 日本不卡在线观看 | 六十路息与子猛烈交尾 | 狠狠干2023| 国产精品自在线 | 欧美日韩久久久久久 | 夜夜爱av | 中文字幕一区二区三三 | 欧美一级色片 | 人人曰| 人妻巨大乳hd免费看 | 久久久久久久久福利 | 亚洲va韩国va欧美va精品 | 黄色成年人网站 | 黄色成人av | 一级做a爰片久久毛片 | 午夜影视剧场 | 亚洲av无码专区在线电影 | 免费成人电影在线观看 | 日本最新中文字幕 | 欧美视频第一页 | 国产又粗又猛又黄又爽 | 国产精品永久免费 | 黄色av毛片 | 999zyz玖玖资源站永久 | 在线观看av一区二区 | 亚洲一区二区日韩 | 毛片在线看网站 | 淫辱的世界(调教sm)by | 好吊色视频在线观看 | 日本在线精品视频 | 国产精品久久久久久久久久久久久久 | 国产极品美女高潮无套嗷嗷叫酒店 | 欧美亚洲色综久久精品国产 | 激情小说综合 | 久久精品视频16 | 无码人妻精品一区二区三区9厂 | 国产精品无码网站 | 91网址在线播放 | 日韩精品一区二区三区在线播放 | 成人区人妻精品一区 | 麻豆传媒网站在线观看 | 成人污污视频 | av毛片一区 | 成人淫片| 亚洲国产精品成人综合久久久 | 一级黄色性视频 | 91精品人妻一区二区三区 | 国产美女一区二区三区 | 青春草在线视频观看 | 欧美精品一区二区三区四区五区 | 性喷潮久久久久久久久 | 欧美xxxx69| 成人一卡二卡 | 黄a在线| 亚洲激情短视频 | 亚洲福利影视 | 爱福利视频一区 | 99久久婷婷国产综合精品青牛牛 | 中文字幕乱码在线 | 一本色道久久综合亚洲精品小说 | 激情欧美网站 | 精品看片 | 九九这里只有精品视频 | 久久久久亚洲av无码专区体验 | 成人福利一区二区三区 | 色射影院| 久久人人爽人人爽人人 | 国产婷婷色一区二区在线观看 | 精品国产AV色欲天媒传媒 | 樱花电影最新免费观看国语版 | 欧美精品久久96人妻无码 | 精品视频久久久久久 | 天堂久久精品忘忧草 | 日本伦理一区二区 | 免费不卡av在线 | 性生生活性生交a级 | 欧美一区二区三区在线免费观看 | 国产一区二区三区久久 | 一边摸一边抽搐一进一出视频 | 99视频久 | 日本三级小视频 | 久久亚洲天堂网 | 一区成人 |