先上效果圖

需求
- 粒度—時間選擇器聯動
- 時間周期不能大於今天。(所以今天以后的時間都不能選)
- 周粒度——因為一周沒過完,所以不能選當前周
- 月粒度——因為本月沒結束,不能選當前月
- 切換粒度的時候自動選擇最近符合的時間。
- 側邊有快捷選擇。
根據需求還有個隱藏bug 周需要特殊處理選擇的日期還有樣式
周粒度是一周一周選擇,所以需要特殊處理
上代碼
父組件使用
<lidu-picker ref="liduPicker" @changeDate="changeDate"></lidu-picker>
import liduPicker from './components/liduPicker'
monted(){
// 默認日粒度 可以父組件調用初始化也可以直接在子組件初始化
// this.$refs['liduPicker'].changeSize(1)
}
組件liduPicker 周粒度的時候需要增加class="is-week-mode"
date-picker的 value-format="yyyy-MM-dd",其他的未測試是否有影響
組件內在最后使用了dateFormat處理時間。沒有的請修改
<template>
<el-form inline size="medium">
<el-form-item>
<el-select v-model="lidu" placeholder="請選擇" @change="changeSize">
<el-option label="日粒度" :value="1"></el-option>
<el-option label="周粒度" :value="2"></el-option>
<el-option label="月粒度" :value="3"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-date-picker
style="width:300px"
v-model="datePicker"
:type="lidu == 3 ? 'monthrange' : 'daterange'"
align="left"
unlink-panels
range-separator="至"
start-placeholder="開始時間"
end-placeholder="結束時間"
:picker-options="pickerOptions"
value-format="yyyy-MM-dd"
@change="changeDate"
@focus="setWeekClass"
></el-date-picker>
</el-form-item>
</el-form>
</template>
<script>
import { dateFormat } from '@/helpers/utils'
export default {
data() {
return {
// 粒度 1:日,2:周,3:月
lidu: 1,
// 時間區間選擇
datePicker: [],
pickerOptions: {
firstDayOfWeek: 1,
shortcuts: [],
},
}
},
methods: {
// 切換粒度
changeSize(val) {
this.datePicker = []
let shortcuts = []
// 一天時間
const day = 3600 * 1000 * 24
const date = new Date()
const end = date - day
// 日粒度
if (val == 1) {
// 初始化時間 默認最近7天
this.changeDate([date - day * 7, date - day])
// 昨日
const latelyDay = this.setShortcut('昨日', end, end)
// 上周
const curDay = date.getDay()
const lastWeek = this.setShortcut('上周', date - day * (7 + curDay), date - day * (1 + curDay))
// 本月
const curMonth = this.setShortcut('本月', new Date().setDate(1), end)
// 上月 上個月第一天 ~ 上個月最后一天
const lastMonth = this.setShortcut('上月', new Date(date.getFullYear(), date.getMonth(), 0).setDate(1), new Date().setDate(0))
// 最近一周
const latelyWeek = this.setShortcut('最近7天', date - day * 7, end)
// 最近15天
const latelyHalfMonth = this.setShortcut('最近15天', date - day * 15, end)
// 最近30天
const latelyMonth = this.setShortcut('最近30天', date - day * 31, end)
// 最近90天
const lately3Month = this.setShortcut('最近90天', date - day * 91, end)
shortcuts.push(latelyDay, lastWeek, curMonth, lastMonth, latelyWeek, latelyHalfMonth, latelyMonth, lately3Month)
// 選擇限制 不能選今天以后
this.pickerOptions.disabledDate = time => {
return time.getTime() + 86400000 >= Date.now()
}
}
// 周粒度
if (val == 2) {
// 初始化時間 默認最近7天
this.changeDate([date - day * 7, date - day])
// 最近7天
const latelyWeek = this.setShortcut('最近7天', date - day * 7, end)
// 最近30天
const latelyMonth = this.setShortcut('最近30天', date - day * 31, end)
// 最近90天
const lately3Month = this.setShortcut('最近90天', date - day * 91, end)
shortcuts.push(latelyWeek, latelyMonth, lately3Month)
// 選擇限制 不能選今天以后
this.pickerOptions.disabledDate = time => {
return time.getTime() + 86400000 >= Date.now()
}
}
// 月粒度 都是到上個月的最后一天
if (val == 3) {
const lastMonth1 = new Date(date.getFullYear(), date.getMonth(), 0).setDate(1)
// 初始化時間 默認上月
this.changeDate([lastMonth1, lastMonth1])
// 上月
const last1M = this.setShortcut('上月', lastMonth1, lastMonth1)
// 今年
const curYear = this.setShortcut('今年', new Date(date.getFullYear(), 0), lastMonth1)
// 最近三個月
const last3M = this.setShortcut('最近三個月', new Date(date.getFullYear(), date.getMonth() - 3), lastMonth1)
// 最近六個月
const last6M = this.setShortcut('最近六個月', new Date(date.getFullYear(), date.getMonth() - 6), lastMonth1)
shortcuts.push(last1M, curYear, last3M, last6M)
// 選擇限制 不能選上個月最后一天之后
this.pickerOptions.disabledDate = time => {
return time.getTime() + 86400000 >= new Date().setDate(0)
}
}
// console.log('快捷設置:', shortcuts)
this.pickerOptions.shortcuts = shortcuts
},
setShortcut(text, start, end) {
return {
text,
onClick(picker) {
picker.$emit('pick', [dateFormat(new Date(start), 'YYYY-MM-dd'), dateFormat(new Date(end), 'YYYY-MM-dd')])
},
}
},
// 所選時間
changeDate(date) {
if (!date) return
// 日粒度
if (this.lidu == 1) {
date = [dateFormat(new Date(date[0]), 'YYYY-MM-dd'), dateFormat(new Date(date[1]), 'YYYY-MM-dd')]
this.datePicker = this.datePicker.length ? this.datePicker : date
}
// 周粒度
if (this.lidu == 2) {
// 取出開始時間和結束時間分別是周幾
const day = 3600 * 1000 * 24
const yesterday = new Date() - day
const start = new Date(date[0])
const end = new Date(date[1])
// 開始時間是距離周一天數
const startDay = start.getDay() == 0 ? 6 : start.getDay() - 1
// 結束時間是距離周日天數
const endDay = end.getDay() == 0 ? 6 : end.getDay() - 1
// 距離上周一天數
const monDay = 13 - new Date().getDay()
const lastMonday = new Date() - day * monDay
// 距離上周日天數
const sunDay = new Date().getDay() == 0 ? 7 : new Date().getDay()
const lastSunday = new Date() - day * sunDay
const res = []
// 計算所選開始時間的周一 (判斷當前所選日期的周一是否 > 上周一)
res[0] = Number(start) - day * startDay > Number(lastMonday) ? Number(lastMonday) : Number(start) - day * startDay
// 計算所選結束時間的周日 (判斷當前所選日期的周日是否 > 上周日)
res[1] = Number(end) + day * (6 - endDay) > Number(lastSunday) ? Number(lastSunday) : Number(end) + day * (6 - endDay)
this.datePicker = [dateFormat(new Date(res[0]), 'YYYY-MM-dd'), dateFormat(new Date(res[1]), 'YYYY-MM-dd')]
}
// 月粒度
if (this.lidu == 3) {
const endTime = new Date(date[1])
endTime.setMonth(endTime.getMonth() + 1)
const endDay = endTime.setDate(0)
// console.log('結束時間的月底', dateFormat(new Date(endDay), 'YYYY-MM-dd'))
this.datePicker = [dateFormat(new Date(date[0]), 'YYYY-MM-dd'), dateFormat(new Date(endDay), 'YYYY-MM-dd')]
}
// console.log('this.datePicker',this.datePicker)
this.$emit('changeDate', this.lidu, this.datePicker)
},
// 如果是周粒度的時候 首次進入需要綁定樣式 is-week-mode
setWeekClass(event) {
if (this.lidu == 2 && event.picker && event.picker.$children[0].$el.className != 'el-date-table is-week-mode') {
// console.log(event.picker)
event.picker.$children.forEach(r => {
// console.log(r.$el)
r.$el.className = `el-date-table is-week-mode`
})
} else if (this.lidu != 2 && event.picker && event.picker.$children[0].$el.className == 'el-date-table is-week-mode') {
event.picker.$children.forEach(r => {
// console.log(r.$el)
r.$el.className = `el-date-table`
})
}
},
},
mounted() {
// 默認日粒度
this.changeSize(1)
},
}
</script>
<style></style>
<
template
>
<
el-form
inline
size=
"
medium
"
>
<
el-form-item
>
<
el-select
v-model
=
"lidu
"
placeholder=
"
請選擇
"
@
change
=
"changeSize
"
>
<
el-option
label=
"
日粒度
"
:
value
=
"
1
"
></
el-option
>
<
el-option
label=
"
周粒度
"
:
value
=
"
2
"
></
el-option
>
<
el-option
label=
"
月粒度
"
:
value
=
"
3
"
></
el-option
>
</
el-select
>
</
el-form-item
>
<
el-form-item
>
<
el-date-picker
style=
"
width:300px
"
v-model
=
"datePicker
"
:
type
=
"lidu
==
3
?
'
monthrange
'
:
'
daterange
'"
align=
"
left
"
unlink-panels
range-separator=
"
至
"
start-placeholder=
"
開始時間
"
end-placeholder=
"
結束時間
"
:
picker-options
=
"pickerOptions
"
value-format=
"
yyyy-MM-dd
"
@
change
=
"changeDate
"
@
focus
=
"setWeekClass
"
></
el-date-picker
>
</
el-form-item
>
</
el-form
>
</
template
>
<
script
>
import
{ dateFormat
}
from
'
@/helpers/utils
'
export
default
{
data
()
{
return
{
// 粒度 1:日,2:周,3:月
lidu
:
1
,
// 時間區間選擇
datePicker
:
[],
pickerOptions
:
{
firstDayOfWeek
:
1
,
shortcuts
:
[],
},
}
},
methods
:
{
// 切換粒度
changeSize
(
val
)
{
this
.datePicker
=
[]
let shortcuts
=
[]
// 一天時間
const
day
=
3600
*
1000
*
24
const
date
=
new
Date
()
const
end
= date
- day
// 日粒度
if
(val
==
1
)
{
// 初始化時間 默認最近7天
this
.
changeDate
([date
- day
*
7
, date
- day
])
// 昨日
const
latelyDay
=
this
.
setShortcut
(
'
昨日
'
, end
, end
)
// 上周
const
curDay
= date
.
getDay
()
const
lastWeek
=
this
.
setShortcut
(
'
上周
'
, date
- day
*
(
7
+ curDay
), date
- day
*
(
1
+ curDay
))
// 本月
const
curMonth
=
this
.
setShortcut
(
'
本月
'
,
new
Date
().
setDate
(
1
), end
)
// 上月 上個月第一天 ~ 上個月最后一天
const
lastMonth
=
this
.
setShortcut
(
'
上月
'
,
new
Date
(date
.
getFullYear
(), date
.
getMonth
(),
0
).
setDate
(
1
),
new
Date
().
setDate
(
0
))
// 最近一周
const
latelyWeek
=
this
.
setShortcut
(
'
最近7天
'
, date
- day
*
7
, end
)
// 最近15天
const
latelyHalfMonth
=
this
.
setShortcut
(
'
最近15天
'
, date
- day
*
15
, end
)
// 最近30天
const
latelyMonth
=
this
.
setShortcut
(
'
最近30天
'
, date
- day
*
31
, end
)
// 最近90天
const
lately3Month
=
this
.
setShortcut
(
'
最近90天
'
, date
- day
*
91
, end
)
shortcuts
.
push
(latelyDay
, lastWeek
, curMonth
, lastMonth
, latelyWeek
, latelyHalfMonth
, latelyMonth
, lately3Month
)
// 選擇限制 不能選今天以后
this
.pickerOptions
.
disabledDate
=
time
=>
{
return time
.
getTime
()
+
86400000
>=
Date
.
now
()
}
}
// 周粒度
if
(val
==
2
)
{
// 初始化時間 默認最近7天
this
.
changeDate
([date
- day
*
7
, date
- day
])
// 最近7天
const
latelyWeek
=
this
.
setShortcut
(
'
最近7天
'
, date
- day
*
7
, end
)
// 最近30天
const
latelyMonth
=
this
.
setShortcut
(
'
最近30天
'
, date
- day
*
31
, end
)
// 最近90天
const
lately3Month
=
this
.
setShortcut
(
'
最近90天
'
, date
- day
*
91
, end
)
shortcuts
.
push
(latelyWeek
, latelyMonth
, lately3Month
)
// 選擇限制 不能選今天以后
this
.pickerOptions
.
disabledDate
=
time
=>
{
return time
.
getTime
()
+
86400000
>=
Date
.
now
()
}
}
// 月粒度 都是到上個月的最后一天
if
(val
==
3
)
{
const
lastMonth1
=
new
Date
(date
.
getFullYear
(), date
.
getMonth
(),
0
).
setDate
(
1
)
// 初始化時間 默認上月
this
.
changeDate
([lastMonth1
, lastMonth1
])
// 上月
const
last1M
=
this
.
setShortcut
(
'
上月
'
, lastMonth1
, lastMonth1
)
// 今年
const
curYear
=
this
.
setShortcut
(
'
今年
'
,
new
Date
(date
.
getFullYear
(),
0
), lastMonth1
)
// 最近三個月
const
last3M
=
this
.
setShortcut
(
'
最近三個月
'
,
new
Date
(date
.
getFullYear
(), date
.
getMonth
()
-
3
), lastMonth1
)
// 最近六個月
const
last6M
=
this
.
setShortcut
(
'
最近六個月
'
,
new
Date
(date
.
getFullYear
(), date
.
getMonth
()
-
6
), lastMonth1
)
shortcuts
.
push
(last1M
, curYear
, last3M
, last6M
)
// 選擇限制 不能選上個月最后一天之后
this
.pickerOptions
.
disabledDate
=
time
=>
{
return time
.
getTime
()
+
86400000
>=
new
Date
().
setDate
(
0
)
}
}
// console.log('快捷設置:', shortcuts)
this
.pickerOptions
.shortcuts
= shortcuts
},
setShortcut
(
text
,
start
,
end
)
{
return
{
text
,
onClick
(
picker
)
{
picker
.
$emit
(
'
pick
'
,
[
dateFormat
(
new
Date
(start
),
'
YYYY-MM-dd
'
),
dateFormat
(
new
Date
(end
),
'
YYYY-MM-dd
'
)])
},
}
},
// 所選時間
changeDate
(
date
)
{
if
(
!date
)
return
if
(
this
.lidu
==
1
)
{
date
=
[
dateFormat
(
new
Date
(date
[
0
]),
'
YYYY-MM-dd
'
),
dateFormat
(
new
Date
(date
[
1
]),
'
YYYY-MM-dd
'
)]
this
.datePicker
=
this
.datePicker
.
length
?
this
.datePicker
: date
}
if
(
this
.lidu
==
2
)
{
// 取出開始時間和結束時間分別是周幾
const
day
=
3600
*
1000
*
24
const
yesterday
=
new
Date
()
- day
const
start
=
new
Date
(date
[
0
])
const
end
=
new
Date
(date
[
1
])
// 開始時間是距離周一天數
const
startDay
= start
.
getDay
()
==
0
?
6
: start
.
getDay
()
-
1
// 結束時間是距離周日天數
const
endDay
= end
.
getDay
()
==
0
?
6
: end
.
getDay
()
-
1
// 距離上周一天數
const
monDay
=
13
-
new
Date
().
getDay
()
const
lastMonday
=
new
Date
()
- day
* monDay
// 距離上周日天數
const
sunDay
=
new
Date
().
getDay
()
==
0
?
7
:
new
Date
().
getDay
()
const
lastSunday
=
new
Date
()
- day
* sunDay
const
res
=
[]
// 計算所選開始時間的周一 (判斷當前所選日期的周一是否 > 上周一)
res
[
0
]
=
Number
(start
)
- day
* startDay
>
Number
(lastMonday
)
?
Number
(lastMonday
)
:
Number
(start
)
- day
* startDay
// 計算所選結束時間的周日 (判斷當前所選日期的周日是否 > 上周日)
res
[
1
]
=
Number
(end
)
+ day
*
(
6
- endDay
)
>
Number
(lastSunday
)
?
Number
(lastSunday
)
:
Number
(end
)
+ day
*
(
6
- endDay
)
this
.datePicker
=
[
dateFormat
(
new
Date
(res
[
0
]),
'
YYYY-MM-dd
'
),
dateFormat
(
new
Date
(res
[
1
]),
'
YYYY-MM-dd
'
)]
}
if
(
this
.lidu
==
3
)
{
const
endTime
=
new
Date
(date
[
1
])
endTime
.
setMonth
(endTime
.
getMonth
()
+
1
)
const
endDay
= endTime
.
setDate
(
0
)
// console.log('結束時間的月底', dateFormat(new Date(endDay), 'YYYY-MM-dd'))
this
.datePicker
=
[
dateFormat
(
new
Date
(date
[
0
]),
'
YYYY-MM-dd
'
),
dateFormat
(
new
Date
(endDay
),
'
YYYY-MM-dd
'
)]
}
// console.log('this.datePicker',this.datePicker)
this
.
$emit
(
'
changeDate
'
,
this
.lidu
,
this
.datePicker
)
},
// 如果是周粒度的時候 首次進入需要綁定樣式 is-week-mode
setWeekClass
(
event
)
{
if
(
this
.lidu
==
2
&& event
.picker
&& event
.picker
.$children
[
0
].$el
.className
!=
'
el-date-table is-week-mode
'
)
{
// console.log(event.picker)
event
.picker
.$children
.
forEach
(
r
=>
{
// console.log(r.$el)
r
.$el
.className
=
`
el-date-table is-week-mode
`
})
}
else
if
(
this
.lidu
!=
2
&& event
.picker
&& event
.picker
.$children
[
0
].$el
.className
==
'
el-date-table is-week-mode
'
)
{
event
.picker
.$children
.
forEach
(
r
=>
{
// console.log(r.$el)
r
.$el
.className
=
`
el-date-table
`
})
}
},
},
mounted
()
{
// 默認日粒度
this
.
changeSize
(
1
)
},
}
</
script
>
<
style
></
style
>
