周范围选择器
周范围选择器
原因:
在翻找了不少大佬造的轮子后始终没有找到一个符合我需求的,于是决定自己造一个
方案:
使用date-fns来实现获取周总数,第几周等数据,其他逻辑自行处理(其实可以依托element-ui部分组件,但是当时脑子没转过来)
话不多上代码
<template>
<div class="week_picker flex">
<div class="week_box">
<div class="flex flex_column_center">
<div ref="startWeekSelect" :class="['week_block',{'week_open': weekIsOpen.start}]">
<div class="week_top flex flex-content-center" @click="weekIsOpen.start = !weekIsOpen.start">
<div class="week_txt">{{ `${yearData.start}年 第${weekData.startWeek}周` }}</div>
<div class="icon el-icon-arrow-down"></div>
</div>
<div class="week_select_box">
<div class="week_select">
<div class="year_box flex flex_column_center flex_row_justify">
<i class="year_btn el-icon-d-arrow-left" @click="yearIconClick('start','down')"></i>
<div class="year_txt">{{ yearData.start }}</div>
<i class="year_btn el-icon-d-arrow-right" @click="yearIconClick('start','up')"></i>
</div>
<div class="week_option_box">
<div :class="['week_option',{'active':item.value === weekData.startWeek}]"
v-for="item in startWeekList"
:value="item.value"
@click="weekOptionClick(item.value)"
>{{ item.label }}
</div>
</div>
<div class="arrow_icon"></div>
</div>
</div>
</div>
<p style="margin: 0 15px">至</p>
<div ref="endWeekSelect" :class="['week_block',{'week_open': weekIsOpen.end}]">
<div class="week_top flex flex-content-center" @click="weekIsOpen.end = !weekIsOpen.end">
<div class="week_txt">{{ `${yearData.end}年 第${weekData.endWeek}周` }}</div>
<div class="icon el-icon-arrow-down"></div>
</div>
<div class="week_select_box">
<div class="week_select">
<div class="year_box flex flex_column_center flex_row_justify">
<i :class="['year_btn el-icon-d-arrow-left',{'disabled':this.yearData.end <= this.yearData.start}]"
@click="yearIconClick('end','down')"
></i>
<p class="year_txt">{{ yearData.end }}</p>
<i class="year_btn el-icon-d-arrow-right" @click="yearIconClick('end','up')"></i>
</div>
<div class="week_option_box">
<div
:class="['week_option',{'active':item.value === weekData.endWeek},{'disabled':endWeekDisabled(item.value)}]"
v-for="item in endWeekList"
:value="item.value"
@click="weekOptionClick(item.value,'end')"
>{{ item.label }}
</div>
</div>
<div class="arrow_icon"></div>
</div>
</div>
</div>
</div>
<div class="txt">{{ dateScope }}</div>
</div>
</div>
</template>
<script>
import { getWeekStartAndEnd } from '@/utils/ruoyi'
import { getISOWeek, getISOWeeksInYear, getYear } from 'date-fns'
export default {
name: 'WeekPicker',
data() {
return {
yearData: {// 年份
start: '',
end: ''
},
weekTal: { // 一年有多少周
start: 0,
end: 0
},
weekData: { // 周数
startWeek: '',
endWeek: ''
},
dateData: {
start: '',
end: ''
},
weekIsOpen: {
start: false,
end: false
}
}
},
computed: {
// 开始周列表
startWeekList() {
console.log('初始化')
let weekList = []
const date = new Date()
let yearType = this.yearData.start === date.getFullYear()
for (let i = 1; i <= this.weekTal.start; i++) {
weekList.push({ value: i, label: i === getISOWeek(new Date()) && yearType ? `第${i}周(本周)` : `第${i}周` })
}
return weekList
},
// 结束周列表
endWeekList() {
let weekList = []
const date = new Date()
let yearType = this.yearData.end === date.getFullYear()
for (let i = 1; i <= this.weekTal.end; i++) {
weekList.push({ value: i, label: i === getISOWeek(new Date()) && yearType ? `第${i}周(本周)` : `第${i}周` })
}
return weekList
},
// 日期范围
dateScope() {
let monday = ''
let sunday = ''
if (this.weekData.startWeek) {
monday = getWeekStartAndEnd(this.yearData.start, this.weekData.startWeek, false).monday
this.dateData.start = getWeekStartAndEnd(this.yearData.start, this.weekData.startWeek, true).monday
}
if (this.weekData.endWeek) {
sunday = getWeekStartAndEnd(this.yearData.end, this.weekData.endWeek, false).sunday
this.dateData.end = getWeekStartAndEnd(this.yearData.end, this.weekData.endWeek, true).sunday
}
if (this.weekData.startWeek && this.weekData.endWeek) {
return `(${monday} 至 ${sunday})`
}
return ''
}
},
created() {
this.initDate()
// 绑定全局点击事件
document.addEventListener('click', this.weekInsideClick)
},
methods: {
// 初始化时间
initDate() {
this.yearData.end = this.yearData.start = getYear(new Date(new Date().getFullYear(), 4, 1))
this.weekTal.end = this.weekTal.start = getISOWeeksInYear(new Date(new Date().getFullYear(), 4, 1))
this.weekData.startWeek = getISOWeek(new Date())
this.weekData.endWeek = getISOWeek(new Date())
},
// 结束周禁用选项
endWeekDisabled(data) {
if (this.yearData.start === this.yearData.end) {
return this.weekData.startWeek > data
} else {
return false
}
},
// 周改变
weekOptionClick(data, type = 'start') {
let isClose = false
if (type === 'start') {
isClose = true
this.weekData.startWeek = data
if (this.yearData.start === this.yearData.end && this.weekData.startWeek > this.weekData.endWeek) {
this.weekData.endWeek = data
}
} else {
if (this.yearData.start === this.yearData.end) {
if (this.weekData.startWeek <= data) {
this.weekData.endWeek = data
isClose = true
}
} else if (this.yearData.start < this.yearData.end) {
this.weekData.endWeek = data
isClose = true
}
}
if (isClose) {
this.weekIsOpen.start = false
this.weekIsOpen.end = false
this.$emit('change', this.dateData)
}
},
// 点击外部触发
weekInsideClick(event) {
const isClickedInsideStart = this.$refs.startWeekSelect.contains(event.target)
const isClickedInsideEnd = this.$refs.endWeekSelect.contains(event.target)
if (!isClickedInsideStart) {
this.weekIsOpen.start = false
}
if (!isClickedInsideEnd) {
this.weekIsOpen.end = false
}
},
/** 年份改变
*
* @param type // start | end 点击的是开始周还是结束周
* @param direction // up | down 点击的是增加还是减少
* @returns {boolean}
*/
yearIconClick(type, direction) {
// 结束年份小于等于开始年份并且点击减少结束年份,禁止点击
if (type === 'end' && direction === 'down' && this.yearData.end <= this.yearData.start) {
return false
}
if (direction === 'up') {
this.yearData[type]++
} else {
if (this.yearData[type] >= 1950) {
this.yearData[type]--
}
}
if (type === 'start') {
this.weekData.startWeek = 1
if (this.yearData.start > this.yearData.end) {
this.weekData.endWeek = 1
this.yearData.end = this.yearData.start
}
}
}
},
beforeDestroy() {
// 移除全局点击事件
document.removeEventListener('click', this.weekInsideClick)
}
}
</script>
<style lang="scss">
.week_box {
.txt {
margin-top: 10px;
}
}
.week_block {
line-height: 34px;
border-radius: 4px;
border: 1px solid #DCDFE6;
position: relative;
white-space: nowrap;
width: 150px;
font-size: 14px;
color: #606266;
padding: 0 10px;
&.week_open {
.week_select_box {
opacity: 1;
pointer-events: auto;
max-height: 400px;
}
}
.week_txt {
padding-right: 10px;
flex: 1;
cursor: pointer;
}
.icon {
line-height: 34px;
font-size: 14px;
color: #C0C4CC;
cursor: pointer;
}
.week_select_box {
z-index: 50;
position: absolute;
top: 100%;
left: -1px;
right: -6px;
white-space: nowrap;
line-height: 34px;
font-size: 14px;
padding: 10px 5px 10px 0;
overflow: hidden;
max-height: 0;
opacity: 0;
pointer-events: none;
transition: all .3s;
.week_select {
position: relative;
border: solid 1px #dfe4ed;
border-radius: 4px;
background-color: #fff;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.year_box {
padding: 0 5px;
font-size: 18px;
.year_btn {
cursor: pointer;
padding: 0 5px;
line-height: 46px;
&.disabled {
cursor: no-drop;
color: #c7cace !important;
}
&:hover {
color: #1890ff;
}
}
.year_txt {
margin: 0 10px;
}
}
.week_option_box {
padding: 5px 0;
height: 268px;
overflow-y: auto;
border-top: 1px solid #dfe4ed;
}
.week_option {
padding: 0 15px;
cursor: pointer;
color: #606266;
&.active {
color: #1890ff;
font-weight: 600;
}
&.disabled {
cursor: no-drop;
color: #c7cace !important;
background-color: #fff !important;
}
&:hover {
background-color: #f5f7fa;
}
}
.arrow_icon {
position: absolute;
top: -6px;
border-width: 6px;
border-top-width: 0;
display: block;
width: 0;
height: 0;
left: 35px;
border-color: transparent;
border-style: solid;
border-bottom-color: #e6ebf5;
&:after {
content: ' ';
position: absolute;
top: 1px;
border-width: 5px;
border-top-width: 0;
display: block;
width: 0;
height: 0;
left: 0;
margin-left: -5px;
border-color: transparent;
border-style: solid;
border-bottom-color: #FFFFFF;
}
}
}
}
</style>
效果如图


浙公网安备 33010602011771号