引言
Element 1.x 停止维护了,但是有些项目中仍在使用,但是有些需求在这个版本中并不能实现,所以可以重构一下组件,不行就写一个组件,这个需求相对来说比之前写的mintui日期选择器更加简单。
理解需求
只要是和下面这个官网样例差不多就行
coding
<template>
<div>
<el-popover
v-model="showFlag"
placement="bottom"
width="600">
<div class="month-select-con">
<div class="left-con">
<div class="header-con">
<i class="el-icon-d-arrow-left" @click="toLastYear" style="cursor: pointer"></i>
<span>{{ leftYear }} 年</span>
<i></i>
</div>
<ul class="month-con">
<li v-for="item in monthList"
:key="item.code"
@mouseover="hoverStyle(leftYear + '' + item.code)"
@click="selectMonth('left', item.code)">
<span :id="leftYear + '' + item.code" >{{ item.name }}</span>
</li>
</ul>
</div>
<div class="right-con">
<div class="header-con">
<i></i>
<span>{{ rightYear }} 年</span>
<i class="el-icon-d-arrow-right" @click="toRightYear" style="cursor: pointer"></i>
</div>
<ul class="month-con">
<li v-for="item in monthList"
:key="item.code"
@mouseover="hoverStyle(rightYear + '' + item.code)"
@click="selectMonth('right', item.code)">
<span :id="rightYear + '' + item.code" >{{ item.name }}</span>
</li>
</ul>
</div>
</div>
<el-input slot="reference" placeholder="" v-model="period"></el-input>
</el-popover>
</div>
</template>
<script>
export default {
data () {
return {
startMonth: null,
endMonth: null,
temp: [], // 选择过程中的临时变量
showFlag: false,
leftYear: new Date().getFullYear(),
monthList: [
{ code: '01', name: '一月' },
{ code: '02', name: '二月' },
{ code: '03', name: '三月' },
{ code: '04', name: '四月' },
{ code: '05', name: '五月' },
{ code: '06', name: '六月' },
{ code: '07', name: '七月' },
{ code: '08', name: '八月' },
{ code: '09', name: '九月' },
{ code: '10', name: '十月' },
{ code: '11', name: '十一月' },
{ code: '12', name: '十二月' }
]
}
},
computed: {
period () {
if (!this.startMonth) return ''
return this.startMonth + ' 至 ' + this.endMonth
},
rightYear () { return this.leftYear + 1 }
},
mounted () {},
methods: {
toLastYear () { // 上一年的事件
this.leftYear = this.leftYear - 1
this.flushStyle()
},
toRightYear () { // 下一年的事件
this.leftYear = this.leftYear + 1
this.flushStyle()
},
selectMonth (type, month) { // 鼠标点击月份的事件
switch (this.temp.length) { // 临时变量长度
case 0: // 此时并没有选择过,所以直接加进来就好
this.temp.push(this[`${type}Year`] + '' + month)
this.flushStyle()
break
case 1: // 这时候选择的是第二个月份
this.temp.push(this[`${type}Year`] + '' + month)
this.temp.sort((a, b) => a - b) // 排序日期,第一个是小的,第二个是大的
this.startMonth = this.temp[0] // 赋值日期变量
this.endMonth = this.temp[1]
this.flushStyle() // 刷新样式
this.leftYear = Number(this.startMonth.substr(0, 4)) // 再次打开,左侧年份为最小月份的年
this.showFlag = false // 关闭弹窗
break
case 2: // 此时开始结束月份都有了,再点击的话,就重置
this.temp = []
this.temp.push(this[`${type}Year`] + '' + month) // 此时又是重复第一次点击的情况
this.startMonth = 0 // 只不过重新开始了,开始结束月份要归零
this.endMonth = 0
this.flushStyle() // 刷新样式
break
}
},
hoverStyle (date) { // 鼠标指向时的事件
if (this.temp.length === 1) { // 只有选择过一个日期才会执行
this.temp.push(date)// 假装指向的就是选择的
this.temp.sort((a, b) => a - b)
this.startMonth = this.temp[0]
this.endMonth = this.temp[1]
this.flushStyle(date) // 刷新样式,但是有参数,这个地方有个细节
}
},
flushStyle (tmp = 0) {
this.$nextTick(() => { // 需要延迟回调
document.querySelectorAll('.month-select-con li span').forEach(item => { // 初始化所有元素央视
item.style.color = '#000'
item.style.background = '#fff'
item.parentElement.style.background = '#fff'
item.style.borderRadius = '0'
item.parentElement.style.borderRadius = 0
})
document.querySelectorAll('.month-select-con li span').forEach(item => { // 范围内的日期需要修改的样式
if (Number(item.id) > this.startMonth && Number(item.id) < this.endMonth) {
item.parentElement.style.background = '#f2f6fc'
item.style.background = '#f2f6fc'
}
})
if (document.getElementById(this.temp[0])) { // 最小月份的样式
document.getElementById(this.temp[0]).style.background = '#409eff'
document.getElementById(this.temp[0]).style.color = '#fff'
document.getElementById(this.temp[0]).style.borderRadius = '18px'
document.getElementById(this.temp[0]).parentElement.style.background = '#f2f6fc'
document.getElementById(this.temp[0]).parentElement.style.borderRadius = '12px 0 0 12px' // 最小月份外围的边框
}
if (document.getElementById(this.temp[1])) { // 最大月份的样式
document.getElementById(this.temp[1]).style.background = '#409eff'
document.getElementById(this.temp[1]).style.color = '#fff'
document.getElementById(this.temp[1]).style.borderRadius = '18px'
document.getElementById(this.temp[1]).parentElement.style.background = '#f2f6fc'
document.getElementById(this.temp[1]).parentElement.style.borderRadius = '0 12px 12px 0' // 最大月份外围的边框
}
if (this.temp.length === 1 && document.getElementById(this.temp[0]))document.getElementById(this.temp[0]).parentElement.style.borderRadius = '12px 12px 12px 12px'
// 如果不存在最大月份,那么边框就是全部都是圆角的
if (this.temp[0] === this.temp[1] && document.getElementById(this.temp[0]) && document.getElementById(this.temp[1])) {
// 如果最小和最大月份相等,那么边框时圆角的
document.getElementById(this.temp[1]).parentElement.style.borderRadius = '12px 12px 12px 12px'
}
if (tmp) { // 这个地方就是鼠标指向时的渲染恢复代码,因为当时把指向的当作了选择的,这时候需要还原回来。
this.startMonth = 0
this.endMonth = 0// 初始化开始结束月份
this.temp.splice(this.temp.indexOf(tmp), 1) // 找到鼠标指向的元素,并且删除掉
}
console.info('flushDone', this.temp)
})
}
}
}
</script>
<style lang="scss" scoped>
.month-select-con {
display: flex;
justify-content: space-between;
.header-con {
display: flex;
justify-content: space-between;
}
.month-con {
display: flex;
justify-content: start;
flex-wrap: wrap;
width:100%;
padding-left: 5px;
li {
width:25%;
list-style: none;
display: flex;
justify-content: center;
align-items: center;
width:72px;
height: 48px;
margin-bottom: 5px;
cursor: pointer;
span{
width: 85%;
height: 85%;
text-align: center;
line-height: 36px;
}
}
}
}
</style>
代码已经加上了注释,方便理解,并且可以看一下控制台输出,也许会带来灵感。
截图
结语
本人并非专业前端开发工程师,此代码可能存在一些瑕疵,大佬轻喷 :)