基于Vue實(shí)現(xiàn)可選擇不連續(xù)的時(shí)間范圍的日期選擇器
省流:
npm包:sta-datepicker
效果圖

需求
普通的時(shí)間選擇器要么只能單選,要么只能選范圍,不可以隨意選擇若干個(gè)時(shí)間,同時(shí)大多數(shù)現(xiàn)成的時(shí)間選擇器選擇結(jié)束會(huì)收起來,很不方便。現(xiàn)在需求如下 1、可以自己控制展開收起 2、可以選擇不連續(xù)的多個(gè)時(shí)間范圍的日期 3、可以批量選中日期,不需要一個(gè)個(gè)點(diǎn)擊
實(shí)現(xiàn)過程
分幾個(gè)步驟,具體可以看源碼
1、生成一個(gè)日歷
頂部為固定的幾個(gè)按鈕,可以綁定切換年份月份的函數(shù)
中間為固定的星期,一個(gè)七個(gè)
底部為具體日期,由三部分組成,即:上個(gè)月底幾天,這個(gè)月整個(gè)月,下個(gè)月初幾天
- 算好平年閏年,輸出當(dāng)前月份第一天是周幾
- 根據(jù)當(dāng)前月份的第一天的星期數(shù),計(jì)算日歷要展示上個(gè)月月底的幾天
- 根據(jù)當(dāng)前月份最后一天的星期數(shù),計(jì)算日歷要展示下個(gè)月月初的幾天
日期部分使用div遍歷三個(gè)數(shù)組,左浮動(dòng)或者彈性盒直接堆起來即可
created() {
this.trueDateBox()
},
methods: {
trueDateBox() {
if (this.date === "") {
const date = new Date()
this.year = date.getFullYear()
this.updateLeapYear()
this.month = date.getMonth() + 1
this.day = null
}
this.dayScreen()
},
// 設(shè)置算好閏年平年
updateLeapYear() {
if (this.isLeapYear(this.year)) {
this.monthDay[1] = 29
} else {
this.monthDay[1] = 28
}
},
isLeapYear(year) {
return year % 100 === 0 ? year % 400 === 0 : year % 4 === 0
},
// 日期顯示
dayScreen() {
// 渲染上個(gè)月,第一行
const firstDate = new Date(this.year, this.month - 1, 1)
const firstWeek = firstDate.getDay()
let preMonthDay = null
if (this.month === 1) {
preMonthDay = this.monthDay[11]
} else {
preMonthDay = this.monthDay[this.month - 2]
}
console.log("preMonthDay", this.monthDay[11], this.month)
for (let i = 0; i < preMonthDay; i++) {
this.previousMonth[i] = i + 1
}
if (firstWeek === 0) {
this.previousMonth = this.previousMonth.slice(-7)
} else {
this.previousMonth = this.previousMonth.slice(-firstWeek)
console.log(33, this.previousMonth)
}
// 渲染下個(gè)月, 最后一行
const endDate = new Date(
this.year,
this.month - 1,
this.monthDay[this.month - 1]
)
const endWeek = endDate.getDay()
let nextMonthDay = null
if (this.month === 12) {
nextMonthDay = this.monthDay[0]
} else {
nextMonthDay = this.monthDay[this.month]
}
for (let i = 0; i < nextMonthDay; i++) {
this.nextMonth[i] = i + 1
}
if (endWeek === 6) {
this.nextMonth = this.nextMonth.slice(0, 7)
} else {
this.nextMonth = this.nextMonth.slice(0, 6 - endWeek)
}
},
}2、綁定四個(gè)固定的函數(shù)
點(diǎn)擊上一年,下一年,上個(gè)月,下個(gè)月時(shí),需要計(jì)算跨年的情況
// 年份的增減
addYear() {
this.year++
this.updateLeapYear()
},
reduceYear() {
this.year--
this.updateLeapYear()
},
// 月份的增減
addMonth() {
this.month++
if (this.month > 12) {
this.month = 1
this.addYear()
}
},
reduceMonth() {
this.month--
if (this.month < 1) {
this.month = 12
this.reduceYear()
}
},3、點(diǎn)擊具體日期時(shí),確定狀態(tài)
使用數(shù)組存起當(dāng)前已選的日期,使用一個(gè)變量記錄當(dāng)前半選的日期
通過一個(gè)函數(shù)isActive給每個(gè)日期綁定類名,從而在視圖上顯示出來,同時(shí)可以確定狀態(tài)的切換
- 如果點(diǎn)擊了已選日期的數(shù)據(jù),需要剔除,改為空白狀態(tài)
- 如果點(diǎn)擊了半選態(tài)日期,則直接選中當(dāng)前日期,變?yōu)橐堰x日期
- 如果點(diǎn)擊了空白狀態(tài)日期,則可能有兩種情況,一是已存在半選態(tài)日期,等待閉合,而是不存在半選態(tài)日期,當(dāng)前設(shè)置為半選
methods: {
// 突出顯示當(dāng)前日期
isActive(index) {
const date = new Date()
const y = date.getFullYear()
const m = date.getMonth() + 1
const d = date.getDate()
const obj = {}
if (this.year === y && this.month === m && index === d) {
obj.today = true
}
const newIndexStr = index < 10 ? `0${index}` : `${index}`
const newMonthStr = this.month < 10 ? `0${this.month}` : `${this.month}`
const item = `${this.year}/${newMonthStr}/${newIndexStr}`
if (item === this.partialSelect) {
obj.active = true
}
if (this.selctDate.includes(item)) {
obj.activeRange = true
}
return obj
},
selectDay(e, type) {
const iText = e.target.innerText
const sDate = Number(iText) < 10 ? `0${iText}` : `${iText}`
if (type === "previousMonth") {
if (this.month === 1) {
this.month = 12
this.reduceYear()
} else {
this.month = this.month - 1
}
} else if (type === "nextMonth") {
if (this.month === 12) {
this.month = 1
this.addYear()
} else {
this.month = this.month + 1
}
}
let arr = this.selctDate.map((i) => new Date(i).getTime())
const newMonthStr = this.month < 10 ? `0${this.month}` : `${this.month}`
const curSelectTime = `${this.year}/${newMonthStr}/${sDate}`
const curSelectTimeStamp = new Date(curSelectTime).getTime()
const clsName = e.target.className // 通過類名判斷當(dāng)前是什么狀態(tài)
if (clsName.includes("activeRange")) {
// 點(diǎn)擊了范圍內(nèi)的數(shù)據(jù),需要剔除
arr = arr.filter((i) => i !== curSelectTimeStamp)
} else if (clsName.includes("active") && !clsName.includes("activeRange")) {
// 點(diǎn)擊了一個(gè)半選狀態(tài)的日期,準(zhǔn)備擴(kuò)展范圍或者單選一個(gè)
if (this.selctDate.length) {
const itemTime = arr[0]
const itemTime2 = arr[arr.length - 1]
const selectTime = curSelectTimeStamp
if (selectTime < itemTime) {
console.log("點(diǎn)擊了范圍之前的時(shí)間")
} else if (selectTime > itemTime2) {
console.log("點(diǎn)擊了范圍之后的時(shí)間")
} else {
console.log("點(diǎn)擊了范圍內(nèi)的空白,直接加上一個(gè)")
}
arr = [...arr, curSelectTimeStamp]
console.log(arr)
} else {
// 第一次選擇日期,而且雙擊了,直接單獨(dú)確定這個(gè)
arr = [curSelectTimeStamp]
}
// 此時(shí)選擇完日前了,半選的日期消費(fèi)掉了,清空
this.partialSelect = null
} else {
console.log("不是半選情況")
// 即沒有點(diǎn)擊范圍內(nèi),又不是半選狀態(tài),可能是已經(jīng)存在一個(gè)半選,等待這個(gè)日期來閉合范圍,也可能是第一次打開點(diǎn)擊
if (this.partialSelect) {
// 需要和已存在的半選態(tài)日期閉合
const itemTime = new Date(this.partialSelect).getTime()
const itemTime2 = curSelectTimeStamp
const timeArr = [itemTime, itemTime2].sort((a, b) => a - b) // 排序,因?yàn)椴恢勒l在前面
for (let i = timeArr[0]; i <= timeArr[1]; i += 86400000) {
arr.push(i)
}
// 此時(shí)確定好范圍了,半選的日期消費(fèi)掉了,清空
this.partialSelect = null
} else if (this.selctDate.length) {
// 存在一個(gè)范圍,同時(shí)點(diǎn)擊范圍外,此時(shí)設(shè)置半選
this.day = sDate
this.partialSelect = curSelectTime
} else {
// 不存在一個(gè)范圍,所以是第一次點(diǎn)擊
this.day = sDate
this.partialSelect = curSelectTime
}
}
let filterArr = Array.from(new Set(arr))
filterArr = filterArr.sort((a, b) => a - b)
this.selctDate = filterArr.map((i) => this.formatTime(new Date(i)))
this.$emit("input", this.selctDate)
this.$emit("change", this.selctDate)
this.day = parseInt(sDate)
},
}以上就是基于Vue實(shí)現(xiàn)可選擇不連續(xù)的時(shí)間范圍的日期選擇器的詳細(xì)內(nèi)容,更多關(guān)于Vue日期選擇器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue中如何動(dòng)態(tài)設(shè)置css樣式的hover
這篇文章主要介紹了vue中如何動(dòng)態(tài)設(shè)置css樣式的hover,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10
關(guān)于Nuxt的五種渲染模式的差異和使用場(chǎng)景全解析
這篇文章主要介紹了關(guān)于Nuxt的五種渲染模式的差異和使用場(chǎng)景全解析,在過去傳統(tǒng)開發(fā)中,頁面渲染任務(wù)是由服務(wù)端完成的,那么Nuxt是如何渲染的呢,需要的朋友可以參考下2023-04-04
element-plus+Vue3實(shí)現(xiàn)表格數(shù)據(jù)動(dòng)態(tài)渲染
在Vue中,el-table是element-ui提供的強(qiáng)大表格組件,可以用于展示靜態(tài)和動(dòng)態(tài)表格數(shù)據(jù),本文主要介紹了element-plus+Vue3實(shí)現(xiàn)表格數(shù)據(jù)動(dòng)態(tài)渲染,感興趣的可以了解一下2024-03-03
vue前端開發(fā)輔助函數(shù)狀態(tài)管理詳解示例
vue的應(yīng)用狀態(tài)管理提供了mapState、mapGetters、mapMutations、mapActions四個(gè)輔助函數(shù),所謂的輔助函數(shù)分別對(duì)State、Getters、Mutations、Actions在完成狀態(tài)的使用進(jìn)行簡化2021-10-10
使用Vue動(dòng)態(tài)生成form表單的實(shí)例代碼
這篇文章主要介紹了使用Vue動(dòng)態(tài)生成form表單的實(shí)例代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2018-04-04
vue工程全局設(shè)置ajax的等待動(dòng)效的方法
這篇文章主要介紹了vue工程全局設(shè)置ajax的等待動(dòng)效的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-02-02
用electron 打包發(fā)布集成vue2.0項(xiàng)目的操作過程
這篇文章主要介紹了用electron 打包發(fā)布集成vue2.0項(xiàng)目的操作步驟,把electron 加入到自己項(xiàng)目中各種不兼容,升級(jí)版本踩坑無數(shù)個(gè),今天通過本文給大家分享下詳細(xì)過程,需要的朋友可以參考下2022-10-10

