一、前言
需求:根据日期去筛选数据,分月度季度年度+单选多选连选。 Element-UI组件中的el-date-picker只能实现月份单选、月份连选和年份单选,其他都需要自己再写组件整合。所以我封装了一个这个组件供之后使用。实现效果如下图所示:
二、MergeDatePicker内组件清单
| 单选 | 多选 | 连续选择 | |
|---|---|---|---|
| 月度 | el-date-picker | MultipleMonthQuarterYear | el-date-picker |
| 季度 | SingleQuarter | MultipleMonthQuarterYear | 两个SingleQuarter组合 |
| 年度 | el-date-picker | MultipleMonthQuarterYear | YearsRange:两个单选年度组合 |
文件列表如下图所示:
三、代码实现
1、Demos父组件——引用日期选择器MergeDatePicker
<template>
<div>
<el-tabs v-model="activeName" type="border-card" style="height: calc(100vh - 62px);">
<el-tab-pane label="按需导出Excel" name="first">按需导出Excel</el-tab-pane>
<el-tab-pane label="封装日期选择器" name="second">
<!-- 引用日期选择器 -->
<MergeDatePicker :language="language" :isFilter="false" @postFilterCondition="getFilterCondition"></MergeDatePicker>
</el-tab-pane>
<el-tab-pane label="下拉框嵌入表格" name="third">下拉框嵌入表格</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import MergeDatePicker from './MergeDatePicker'
export default {
name:"Demos",
components:{
MergeDatePicker,
},
data() {
return {
activeName: 'first',
}
},
computed:{
language(){
//zh是中文,en是英文,实际做项目时是从$store缓存的vuex中获取
return 'zh' // return this.$store.state.language
}
},
methods:{
getFilterCondition(filterDate,category,choicetype){
console.log(filterDate,category,choicetype);
//得到筛选条件进行筛选...
},
}
}
</script>
2、MergeDatePicker日期选择器——整合组件
<template>
<div>
<el-tabs :style="'text-align:left;width:'+width" type="card" tab-position="left" v-model="category">
<el-tab-pane class="tabpanebox" v-for="item in periodModel" :key="item.name" :label="getPropertyByLg(item,'name')" :name="item.name_en">
<el-radio-group v-model="choicetype" style="width:300px">
<el-radio v-for="item in choiceModel" :key="item.name" :label="item.model">{{getPropertyByLg(item,'name')}}</el-radio>
</el-radio-group>
<!-- 月度 -->
<div v-show="choicetype === 'month' && category === 'monthly'">
<el-date-picker v-model="filterDate.monthly.Single" type="month" popper-class="selectItemStyle" :placeholder="language==='zh'?'请选择月份':'Please select the month'" value-format="yyyy-MM" :picker-options="pickerOptions"></el-date-picker>
</div>
<div v-show="choicetype === 'months' && category === 'monthly'">
<MultipleMonthQuarterYear :isHasfilter="isFilter" :canSelectList="myallList" :language="language" :category="'monthly'" @getMultiple="getMultipleMonth"></MultipleMonthQuarterYear>
</div>
<div v-show="choicetype === 'monthrange' && category === 'monthly'">
<el-date-picker :default-value="String(years-1)" v-model="filterDate.monthly.Serial" type="monthrange" :picker-options="pickerOptions" align="center" value-format="yyyy-MM" range-separator="~" :start-placeholder="language==='zh'?'开始月份':'start month'" :end-placeholder="language==='zh'?'结束季度':'end month'"></el-date-picker>
</div>
<!-- 季度 -->
<div v-show="choicetype === 'month' && category === 'quarterly'">
<SingleQuarter :isHasfilter="isFilter" :canSelectList="myallList" :language="language" :status="'single'" @getsinglequarter="getsinglequarter"></SingleQuarter>
</div>
<div v-show="choicetype === 'months' && category === 'quarterly'">
<MultipleMonthQuarterYear :isHasfilter="isFilter" :canSelectList="myallList" :language="language" :category="'quarterly'" @getMultiple="getMultiplequarter"></MultipleMonthQuarterYear>
</div>
<div v-show="choicetype === 'monthrange' && category === 'quarterly'">
<div class="flexbox" style="width:50%">
<SingleQuarter :isHasfilter="isFilter" :canSelectList="myallList" :language="language" :inputwidth="'160px'" :status="'start'" @getsinglequarter="getsinglequarter"></SingleQuarter>
<span style="margin-left: 5px; margin-right: 5px;">~</span>
<SingleQuarter :isHasfilter="isFilter" :canSelectList="myallList" :language="language" :inputwidth="'160px'" :status="'end'" @getsinglequarter="getsinglequarter"></SingleQuarter>
</div>
</div>
<!-- 年度 -->
<div v-show="choicetype === 'month' && category === 'annual'">
<el-date-picker v-model="filterDate.annual.Single" type="year" popper-class="selectItemStyle" :picker-options="pickerOptions" value-format="yyyy" :placeholder="language==='zh'?'请选择年份':'Please select the year'"></el-date-picker>
</div>
<div v-show="choicetype === 'months' && category === 'annual'">
<MultipleMonthQuarterYear :isHasfilter="isFilter" :canSelectList="myallList" :language="language" :category="'annual'" @getMultiple="getMultipleYear"></MultipleMonthQuarterYear>
</div>
<div v-show="choicetype === 'monthrange' && category === 'annual'">
<YearsRange :isHasfilter="isFilter" :canSelectList="myallList" :language="language" @getYearRange="getYearRange"></YearsRange>
</div>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import MultipleMonthQuarterYear from './MultipleMonthQuarterYear'
import SingleQuarter from './SingleQuarter'
import YearsRange from './YearsRange'
export default {
name: "MergeDatePicker",
components:{
MultipleMonthQuarterYear,
SingleQuarter,
YearsRange,
},
props:{
isFilter:{ //是否过滤可选择的日期
type: Boolean,
default:true,
},
myallList:{ //isFilter为true时才需要传,有值时只能选择myallList包含的日期数据
type: Array,
default:() => [],
},
width:{
type:String,
default:'100%',
},
language:{
type:String,
default:'zh',
}
},
data() {
return {
periodModel:[{id:1,name:'月度',name_en:'monthly'},{id:2,name:'季度',name_en:'quarterly'},{id:3,name:'年度',name_en:'annual'}],
choiceModel:[{id:1,name:'单选',name_en:'Single',model:'month'},{id:2,name:'多选',name_en:'Multiple',model:'months'},{id:3,name:'连续选择',name_en:'Serial',model:'monthrange'}],
filterDate:{ //选择的筛选条件数据
monthly:{ Single:'', Multiple:[], Serial:[]},
quarterly:{ Single:'', Multiple:[], Serial:[]},
annual:{ Single:'', Multiple:[], Serial:[]},
},
choicetype:'month',//当前的选择方式:单选、多选、连选
category:'monthly',//当前的选择周期:月度、季度、年度
pickerOptions: {
disabledDate: this.optionsDate
},
years:new Date().getFullYear(),//获取现在所处的年份
startmonth:["01-03", "04-06", "07-09", "10-12"],//转季度Q1、Q2、Q3、Q4需要用
};
},
watch:{
choicetype:{
handler(val){
this.postDate()
},
},
category:{
handler(val){
this.postDate()
},
},
watchfiltetData:{
handler(newVal,oldVal){
if(newVal.monthly.Single !== oldVal.monthly.Single || JSON.stringify(newVal.monthly.Serial) !== JSON.stringify(oldVal.monthly.Serial) || newVal.annual.Single !== oldVal.annual.Single){
this.postDate()
}
},
deep:true
},
},
computed:{
watchfiltetData(){
return JSON.parse(JSON.stringify(this.filterDate))
},
getPropertyByLg(){
return function retVal(data, property){
return this.language === 'zh' ? data[property] : data[property + '_en']
}
},
},
methods: {
//el-date-picker日期选择器过滤日期
optionsDate(time){
if(this.isFilter){
//单选年度时过滤可选年份
if(this.choicetype === 'month' && this.category === 'annual'){
let date = time.getFullYear()
let timeList = this.myallList.map((item) =>{
return item.start_time.slice(0,4)
})
if(timeList.includes(JSON.stringify(date))){
return false
}else{
return true
}
}else{//单选月度时过滤可选月份
let months = ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"]
let date = time.getFullYear()+'-'+months[time.getMonth()]
let timeList = this.myallList.map((item) =>{
return item.start_time.slice(0,4)+'-'+item.start_time.slice(5,7)
})
if(timeList.includes(date)){
return false
}else{
return true
}
}
}else{//最新日期以上可选
return time.getTime() >= Date.now() - 8.64e7
}
},
// 多选月份
getMultipleMonth(resultList){
this.$set(this.filterDate.monthly,'Multiple',resultList)
this.postDate()
},
//多选季度
getMultiplequarter(resultList){
this.$set(this.filterDate.quarterly,'Multiple',[])
//将2022-Q1转换成2022-01即第一季度起始月
resultList.filter(showValue => {
let start = showValue.slice(0,4)
let startquarter = showValue.slice(6,7)
this.startmonth.filter((item,index) => {
if(index+1 == startquarter){
var multiplestart = start + '-' + item.slice(0,2)
this.filterDate.quarterly.Multiple.push(multiplestart)
}
})
})
this.postDate()
},
// 多选年份
getMultipleYear(resultList){
this.$set(this.filterDate.annual,'Multiple',resultList)
this.postDate()
},
// 单选季度+连续选择季度
getsinglequarter(showValue,status){
let start = showValue.slice(0,4)
let startquarter = showValue.slice(6,7)
this.startmonth.filter((item,index) => {
let value = start + '-' + item.slice(0,2)
if(index+1 == startquarter){
if(status == 'start'){
this.filterDate.quarterly.Serial.splice(0,1,value)
if(this.filterDate.quarterly.Serial.length === 2) this.verifySelectQuarterRange()
}else if(status == 'end'){
this.filterDate.quarterly.Serial.splice(1,1,value)
if(this.filterDate.quarterly.Serial.length === 2) this.verifySelectQuarterRange()
}else if(status == 'single'){
this.filterDate.quarterly.Single = value
this.postDate()
}
}
})
},
// 验证连续选择的季度并提交
verifySelectQuarterRange() {
let start = this.filterDate.quarterly.Serial[0].slice(0,4)
let startquarter = this.filterDate.quarterly.Serial[0].slice(5,7)
let end = this.filterDate.quarterly.Serial[1].slice(0,4)
let endquarter = this.filterDate.quarterly.Serial[1].slice(5,7)
// console.log(start,end,startquarter,endquarter);
if (Number(start) < 1991 || Number(end) > this.years) {
this.$message({
type: 'error',
message: this.language==='zh'?'超过选择范围,请重新选择!':'Out of the selection range, please select again!'
})
} else if (Number(start) > Number(end) || (Number(start) == Number(end) && Number(startquarter) > Number(endquarter))) {
this.$message.error(this.language==='zh'?'结束季度须大于等于开始季度,请重新选择!':'The end quarter must be greater than or equal to the start quarter, please select again!')
} else {
this.postDate()
}
},
// 连续选择年
getYearRange(start, end){
this.filterDate.annual.Serial.splice(0,1,JSON.stringify(start))
this.filterDate.annual.Serial.splice(1,1,JSON.stringify(end))
this.postDate()
},
// 提交筛选数据
postDate(){
var item = this.periodModel.find((p)=>{return p.name_en === this.category})
var item1 = this.choiceModel.find((p)=>{return p.model === this.choicetype})
this.$emit('postFilterCondition',this.filterDate,item,item1)
},
},
};
</script>
<style scoped>
::v-deep .el-tabs__item{
height: 26px;
line-height: 26px;
}
::v-deep .el-tabs__item.is-active {
color: #5f9aff;
background-color: #ebf1ff;
}
::v-deep .el-tabs--left .el-tabs__header.is-left{
margin-right: 0px;
}
::v-deep .el-tabs--left.el-tabs--card .el-tabs__item.is-left.is-active{
border-right-color: #5f9aff;
}
.tabpanebox{
background-color: #ebf1ff70;
padding: 0 18px;
height: 80px;
line-height: 30px;
border-radius: 0 8px 8px 0;
}
.flexbox{
display: flex;
justify-content: left;
align-items: center;
}
</style>
<style>
.selectItemStyle .el-month-table td .cell:hover,
.selectItemStyle .el-month-table td.current:not(.disabled) .cell,
.selectItemStyle .el-year-table td .cell:hover,
.selectItemStyle .el-year-table td.current:not(.disabled) .cell {
background-color: #409EFF;
color: white;
border-radius: 18px;
}
.selectItemStyle .el-year-table td .cell{
border-radius: 18px;
}
</style>
3 、MultipleMonthQuarterYear组件——多选月度季度年度子组件
<template>
<div>
<el-popover placement="bottom" width="320" trigger="click" v-model="showBox">
<!-- el-input输入框:readonly和clearable属性不能同时使用 -->
<el-input slot="reference" @focus="focusselect" class="inputStyle" v-model="inputValue" type="text" readonly :placeholder="language==='zh'?'请选择月份':'Please select the month'" v-if="category === 'monthly'">
<i slot="prefix" class="el-input__icon el-icon-date"></i>
<!-- 清空图标:有内容的时候渲染出来,鼠标hover到input框的时候再显示出来(即:输入框有内容并且鼠标悬浮时显示该图标) -->
<i slot="suffix" class="el-input__icon el-icon-circle-close clearIconStyle" v-if="showClear" @click="resetMonth"></i>
</el-input>
<el-input slot="reference" @focus="focusselect" class="inputStyle" v-model="inputValue" type="text" readonly :placeholder="language==='zh'?'请选择季度':'Please select the quarter'" v-else-if="category === 'quarterly'">
<i slot="prefix" class="el-input__icon el-icon-date"></i>
<i slot="suffix" class="el-input__icon el-icon-circle-close clearIconStyle" v-if="showClear" @click="resetMonth"></i>
</el-input>
<el-input slot="reference" @focus="focusselect" class="inputStyle" v-model="inputValue" type="text" readonly :placeholder="language==='zh'?'请选择年份':'Please select the year'" v-else-if="category === 'annual'">
<i slot="prefix" class="el-input__icon el-icon-date"></i>
<i slot="suffix" class="el-input__icon el-icon-circle-close clearIconStyle" v-if="showClear" @click="resetMonth"></i>
</el-input>
<!-- 年份月份选择弹框 -->
<div>
<div class="contentArea">
<!-- header年份 -->
<div v-if="category !== 'annual'" style="display:flex;padding:0 0 10px 0;;border-bottom: 1px solid #e5e5e5;text-align:center">
<div v-if="curIndex == DateList.length - 1" style="width: 15%;"><i class="el-icon-d-arrow-left" style="opacity:0.3;"></i></div>
<div v-else @click="reduceYear" style="width: 15%;"><i class="el-icon-d-arrow-left"></i></div>
<div style="width: 70%;">{{OneY}}</div>
<div v-if="curIndex == 0" style="width: 15%;"><i class="el-icon-d-arrow-right" style="opacity:0.3;"></i></div>
<div v-else @click="addYear" style="width: 15%;"><i class="el-icon-d-arrow-right"></i></div>
</div>
<div v-else style="display:flex;padding: 15px 0;border-bottom: 1px solid #e5e5e5;text-align:center">
<div v-if="isReduce" style="width: 15%;"><i class="el-icon-d-arrow-left" style="opacity:0.3;"></i></div>
<div v-else @click="reduceYear" style="width: 15%;"><i class="el-icon-d-arrow-left"></i></div>
<div style="width: 70%;">{{yearStart}}--{{yearEnd}}</div>
<div v-if="isAdd" style="width: 15%;"><i class="el-icon-d-arrow-right" style="opacity:0.3;"></i></div>
<div v-else @click="addYear" style="width: 15%;"><i class="el-icon-d-arrow-right"></i></div>
</div>
<!-- 月份 -->
<div class="conterList" v-if="category === 'monthly'">
<el-checkbox-group v-model="optTime[curIndex].queryTime" @change="onChange" v-if="language === 'zh'">
<el-checkbox :disabled="isDisabled[index]" class="onSelect onMonthlySelect" v-for="(item,index) in DateList[curIndex].queryTime" :key="index" :label="`${DateList[curIndex].TimeYear}-${(item<=9)?`0${item}`:item}`">
{{monthMap[item]}}
</el-checkbox>
</el-checkbox-group>
<el-checkbox-group v-model="optTime[curIndex].queryTime" @change="onChange" v-else-if="language === 'en'">
<el-checkbox :disabled="isDisabled[index]" class="onSelect onMonthlySelect" v-for="(item,index) in DateList[curIndex].queryTime" :key="index" :label="`${DateList[curIndex].TimeYear}-${(item<=9)?`0${item}`:item}`">
{{monthMapEn[item]}}
</el-checkbox>
</el-checkbox-group>
</div>
<!-- 季度 -->
<div class="conterList" v-else-if="category === 'quarterly'">
<el-checkbox-group ref="checkList" v-model="optTime[curIndex].queryTime" @change="onChange" v-if="language === 'zh'">
<el-checkbox :disabled="isDisabled[index]" class="onSelect onQuarterlySelect" v-for="(item,index) in DateList[curIndex].queryTime" :key="index" :label="`${DateList[curIndex].TimeYear}-Q${item}`">
{{quarterMap[item]}}
</el-checkbox>
</el-checkbox-group>
<el-checkbox-group v-model="optTime[curIndex].queryTime" @change="onChange" v-else-if="language === 'en'">
<el-checkbox :disabled="isDisabled[index]" class="onSelect onQuarterlySelect" v-for="(item,index) in DateList[curIndex].queryTime" :key="index" :label="`${DateList[curIndex].TimeYear}-Q${item}`">
{{quarterMapEn[item]}}
</el-checkbox>
</el-checkbox-group>
</div>
<!-- 年度 -->
<div class="conterList" v-else-if="category === 'annual'">
<el-checkbox-group v-model="optTime" @change="onChange">
<el-checkbox :disabled="isDisabled[index]" class="onSelect onAnnualSelect" v-for="(item,index) in DateList" :key="index" :label="`${item}`">
{{item}}
</el-checkbox>
</el-checkbox-group>
</div>
</div>
<!-- 按钮 -->
<div class="buttonBox">
<el-button size="mini" type="primary" plain @click.stop="handleSubmit">{{language==='zh'?'确定':'OK'}}</el-button>
<el-button size="mini" plain @click.stop="resetMonth">{{language==='zh'?'重置':'Reset'}}</el-button>
</div>
</div>
</el-popover>
</div>
</template>
<script>
export default {
name:"MultipleMonthQuarterYear",
props:{
category:{ type: String,default:'monthly'},
canSelectList: { type: Array,default:()=>[]},//可选择的日期列表
isHasfilter:{ type: Boolean,default:false},//为true需要传canSelectList
language:{ type: String,default:'zh'},
},
data(){
return{
DateList: [], // 年份月份数组
optTime: [], // 当前选中的结果数组
OneY: '', // 当前年份
curIndex: 0, // 当前年份下标值
yearStart: 2002, // 当前开始年份
yearEnd: 2021, // 当前结束年份
optTimes: [], // 点击月份时的所有选中结果
resultTimes: [], // 点击“确定”按钮后的选择结果
showBox: false, // 是否显示月份选择弹框
inputValue: '', // 输入框的绑定值
showClear: false, // 是否显示输入框右边的“清空”小图标
monthMap: {'1': '一月', '2': '二月', '3': '三月', '4': '四月', '5': '五月', '6': '六月', '7': '七月', '8': '八月', '9': '九月', '10': '十月', '11': '十一月', '12': '十二月'},
monthMapEn: {'1': 'Jan', '2': 'Feb', '3': 'Mar', '4': 'Apr', '5': 'May', '6': 'Jun', '7': 'Jul', '8': 'Aug', '9': 'Sep', '10': 'Oct', '11': 'Nov', '12': 'Dec'},
quarterMap: {'1': '第1季度', '2': '第2季度', '3': '第3季度', '4': '第4季度'},
quarterMapEn: {'1': 'Q1th', '2': 'Q2nd', '3': 'Q3rd', '4': 'Q4th'},
selectList:[],//可选择的日期数据
months:["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"],
quarters:["01", "04", "07", "10"],
isDisabled:Array.from({length:12}).map(()=>{return false}),
}
},
created(){
this.init();
},
watch: {
canSelectList:{
handler(newVal){
this.selectList = JSON.parse(JSON.stringify(newVal))
this.selectList.map((item) =>{
return item.start_time.slice(0,4)+'-'+item.start_time.slice(5,7)
})
},
deep: true,
},
},
computed:{
isAdd(){
let Dates = new Date();
return this.yearEnd === Dates.getFullYear()
},
isReduce(){
let Dates = new Date();
return this.yearEnd === Dates.getFullYear()-11
}
},
methods:{
//聚焦input时设定可选择项
focusselect(){
this.$nextTick(()=>{
switch (this.category) {
case 'monthly':
var lis = document.getElementsByClassName("onMonthlySelect");
break;
case 'quarterly':
var lis = document.getElementsByClassName("onQuarterlySelect");
break;
case 'annual':
var lis = document.getElementsByClassName("onAnnualSelect");
break;
}
const lists = Array.from(lis)
this.isDisabled = Array.from({length:lists.length}).map(()=>{return true})
if(this.isHasfilter){
for(var j = 0; j < this.selectList.length; j++){
for(var i = 0; i < lists.length; i++){
if(this.category === 'monthly'){
var text = this.changeMonthFomat(lists[i].innerText)
if(Number(this.selectList[j].start_time.slice(0,4)) == this.OneY && text === this.selectList[j].start_time.slice(5,7)){
this.isDisabled.splice(i,1,false)
}
}else if(this.category === 'quarterly'){
var text = this.changeQuarterFomat(lists[i].innerText)
if(Number(this.selectList[j].start_time.slice(0,4)) == this.OneY && text === this.selectList[j].start_time.slice(5,7)){
this.isDisabled.splice(i,1,false)
}
}else if(this.category === 'annual'){
var text = lists[i].innerText
if(Number(this.selectList[j].start_time.slice(0,4)) == text){
this.isDisabled.splice(i,1,false)
}
}
}
}
}else{
this.isDisabled = Array.from({length:lists.length}).map(()=>{return false})
}
})
},
//将'一月/Jan'格式化为'01'格式
changeMonthFomat(text){
let ret = Object.values(this.language=== 'zh'?this.monthMap:this.monthMapEn)
var index = 0
for(var i = 0; i < ret.length; i++){
if(text.trim() == ret[i]){
index = i
}
}
return this.months[index]
},
//将'第1季度'格式化为'01'格式、'第2季度'格式化为'04'格式
changeQuarterFomat(text){
let ret = Object.values(this.language=== 'zh'?this.quarterMap:this.quarterMapEn)
var index = 0
for(var i = 0; i < ret.length; i++){
if(text.trim() == ret[i]){
index = i
}
}
return this.quarters[index]
},
// 初始化数据,获取前20年,然后循环 每一年里面都有12个月的 得到数组 opTime 和 DateList
init(){
if(this.category !== 'annual'){
let _opt = [];
let _optTime = []
let arr = []
if(this.category === 'quarterly'){
arr = new Array(4);
}else if(this.category === 'monthly'){
arr = new Array(12);
}
let optDate = this.getDateList();
optDate.map((item,index)=>{
// 月份选择时el-checkbox-group绑定的值
_optTime[index] = {
TimeYear: item,
queryTime: []
}
// 给每一年份设置12个月份,el-checkbox初始化显示时使用
_opt[index] = {
TimeYear: item,
queryTime: []
}
for(let i = 1; i<= arr.length; i++){
_opt[index].queryTime.push(i)
}
})
this.optTime = _optTime
this.DateList = _opt;
}else{
let Dates = new Date();
this.yearEnd = Dates.getFullYear();
this.yearStart = this.yearEnd-11;
let optDate = [];
for( let i = this.yearEnd - 11; i <= this.yearEnd; i++ ){
optDate.push(i)
}
this.DateList = optDate
}
},
// 获取近20年年份列表,倒序排列,最新一年在最前面
getDateList(){
let Dates = new Date();
let year = Dates.getFullYear();
this.OneY = year;
let optDate = [];
for( let i = year - 20; i <= year; i++ ){
optDate.push(i)
}
return optDate.reverse()
},
// 确定
handleSubmit(){
// 更新输入框的值
this.inputValue = this.optTimes.join(',')
// 根据输入框是否有值来判断清空图标是否渲染
this.showClear = this.inputValue == '' ? false : true
// 将点击“确定”按钮的选择结果保存起来(该值将在哪里使用:在点击弹框以外区域关闭弹框时使用,mounted中)
this.resultTimes = this.optTimes
// 关闭弹框
this.showBox = false
this.$emit('getMultiple',this.resultTimes,this.category)
},
// 重置
resetMonth() {
// 将年份重置到当前年份
let Dates = new Date();
let year = Dates.getFullYear();
if(this.category === 'annual'){
this.yearStart = year-11;
this.yearEnd = year;
// 将已选择的月份清空
this.optTime =[]
}else{
this.OneY = year;
// 将已选择的月份清空
for( let i in this.optTime){
this.optTime[i].queryTime =[]
}
}
// 将已选择的月份清空
this.optTimes = [];
// 将输入框清空
this.inputValue = ''
// 根据输入框是否有值来判断清空图标是否渲染,此处必然不渲染
this.showClear = false
// 将点击“确定”按钮的选择结果清空
this.resultTimes = []
// 关闭月份选择弹框
this.showBox = false
this.$emit('getMultiple',[])
if(this.category !== 'annual') this.curIndex = 0
},
// 左上角年份减少
reduceYear() {
if(this.category !== 'annual'){
// 如果已经是最后一年了,则年份不能再减少了
if(this.curIndex == this.DateList.length - 1) return;
// 当前下标值+1,根据下标值获取年份值
this.curIndex = this.curIndex + 1
this.OneY = this.DateList[this.curIndex].TimeYear
//自定义禁用的checkBox
this.focusselect()
}else{
let Dates = new Date();
if(this.yearEnd === Dates.getFullYear()-11) return;
this.yearStart -= 11
this.yearEnd -= 11
let optDate = [];
for( let i = this.yearEnd - 11; i <= this.yearEnd; i++ ){
optDate.push(i)
}
this.DateList = optDate
}
},
// 左上角年份增加
addYear() {
if(this.category !== 'annual'){
// 如果已经是当前年份了,则年份不能再增加了
if(this.curIndex == 0) return;
// 当前下标值-1,根据下标值获取年份值
this.curIndex = this.curIndex - 1
this.OneY = this.DateList[this.curIndex].TimeYear
this.focusselect()
}else{
let Dates = new Date();
if(this.yearEnd === Dates.getFullYear()) return;
this.yearStart += 11
this.yearEnd += 11
let optDate = [];
for( let i = this.yearEnd - 11; i <= this.yearEnd; i++ ){
optDate.push(i)
}
this.DateList = optDate
}
},
// 选择日期
onChange(){
const _this = this;
// 遍历optTime中已选择的日期,将已选结果塞到optTimes数组中
let _opt = _this.optTime;
let arr = [];
if(this.category !== 'annual'){
for(let item in _opt ){
if(_opt[item].queryTime.length > 0){
_opt[item].queryTime = _opt[item].queryTime.map( p => {
if(this.category === 'quarterly'){
p = p.slice(0,4)+'-Q'+p.slice(6,7)
}
arr.push(p)
return p
})
}
}
}else{
if(_opt.length > 0){
_opt = _opt.map( p => {
p = p.slice(0,4)
arr.push(p)
return p
})
}
}
this.optTimes = arr
// 更新输入框的值
this.inputValue = this.optTimes.join(',')
// 根据输入框是否有值来判断清空图标是否渲染
this.showClear = this.inputValue == '' ? false : true
}
}
}
</script>
<style scoped>
.inputStyle {
width: 300px;
}
.clearIconStyle {
display: none;
}
.inputStyle:hover .clearIconStyle{
display: block;
}
.buttonBox {
border-top: 1px solid #e5e5e5;
padding: 7px;
text-align: center;
}
.contentArea {
width: 320px;
}
.conterList .onSelect{
width: 60px!important;
height: 36px;
line-height: 36px;
margin: 10px;
text-align: center;
border-radius: 20px;
}
.conterList .onSelect:hover{
color: #FFF;
background-color: #409EFF;
}
::v-deep .el-checkbox__input.is-checked+.el-checkbox__label{
color: #FFF;
background-color: #409EFF;
width: 60px!important;
height: 36px;
line-height: 36px;
text-align: center;
border-radius: 20px;
padding: 0;
}
::v-deep .el-checkbox__label{
padding: 0;
}
::v-deep .el-checkbox__inner{
display: none;
}
</style>
4、SingleQuarter组件——单选季度与连选季度子组件
<template>
<div>
<el-popover placement="bottom" width="320" trigger="click" v-model="showSeason">
<el-input @focus="focusselect" slot="reference" size="medium" class="inputStyle"
:placeholder="status==='single'?(language==='zh'?'请选择季度':'Please select the quarter'):(status==='start'?(language==='zh'?'开始季度':'start quarter'):(language==='zh'?'结束季度':'end quarter'))"
v-model="showValue"
:style="'width:'+(inputwidth)+';padding:5px'">
<i slot="prefix" class="el-input__icon el-icon-date"></i>
<i slot="suffix" class="el-input__icon el-icon-circle-close clearIconStyle" v-if="showClear" @click="resetQuarter"></i>
</el-input>
<div style="width:320px;margin-top:10px;">
<div style="display:flex;padding:0 0 10px 0;border-bottom: 1px solid #e5e5e5;text-align:center" class="clearfix">
<div v-if="isReduce" style="width: 15%;"><i class="el-icon-d-arrow-left" style="opacity:0.3;"></i></div>
<div v-else aria-label="前一年" style="width: 15%;" @click="prev"><i class="el-icon-d-arrow-left"></i></div>
<span style="width: 70%;">{{ year }}</span>
<div v-if="isAdd" style="width: 15%;"><i class="el-icon-d-arrow-right" style="opacity:0.3;"></i></div>
<div v-else aria-label="后一年" style="width: 15%;" @click="next"><i class="el-icon-d-arrow-right"></i></div>
</div>
<div class="textitem">
<el-radio-group v-model="optTime[curIndex].queryTime" @change="selectSeason()">
<el-radio :disabled="isDisabled[index]" v-for="(item,index) in DateList[curIndex].queryTime" :key="index" :label="`${DateList[curIndex].TimeYear}-Q${item}`" class="oneitem QuarterlySelect">
{{getPropertyByLg(findDataById(quarterName, 'id', item),'name')}}
</el-radio>
</el-radio-group>
</div>
</div>
</el-popover>
</div>
</template>
<script>
export default {
name: "SingleQuarter",
props: {
status:{
default:'start',
type:String,
},
inputwidth:{
default: "220px",
type: String,
},
canSelectList: { //可选择的日期列表
type: Array,
default:()=>[],
},
isHasfilter:{ //默认为false,为true需要传canSelectList
type: Boolean,
default:false,
},
language:{
type:String,
default:'zh',
}
},
data() {
return {
showSeason: false,
year: new Date().getFullYear(),
showValue: "",
showClear: false, // 是否显示输入框右边的“清空”小图标
optTime:[],
DateList:{},
curIndex:0,
quarterName:[{id:1,name:'第1季度',name_en:'Q1th',data:'01'},{id:2,name:'第2季度',name_en:'Q2nd',data:'04'},{id:3,name:'第3季度',name_en:'Q3rd',data:'07'},{id:4,name:'第4季度',name_en:'Q4th',data:'10'},],
isDisabled:Array.from({length:4}).map(()=>{return false}),
selectList:[],//接收的可选择canSelectList数据
};
},
created() {
this.init()
},
watch: {
canSelectList:{
handler(newVal){
this.selectList = JSON.parse(JSON.stringify(newVal))
this.selectList.map((item) =>{
return item.start_time.slice(0,4)+'-'+item.start_time.slice(5,7)
})
},
deep: true,
},
},
computed:{
findDataById(){
return function(dataList, searchIndex, value){
var data = dataList.find((data) => {
return data[searchIndex] === value;
});
return data ? data : {};
}
},
getPropertyByLg(){
return function retVal(data, property){
return this.language === 'zh' ? data[property] : data[property + '_en']
}
},
isAdd(){
let Dates = new Date();
return this.year === Dates.getFullYear()
},
isReduce(){
let Dates = new Date();
return this.year === Dates.getFullYear()-20
}
},
methods: {
//初始化
init(){
let _opt = []
let _optTime = []
let optDate = this.getDateList();
optDate.map((item,index)=>{
_optTime[index] = {
TimeYear: item,
queryTime: '',
}
_opt[index] = {
TimeYear: item,
queryTime: [1,2,3,4],
}
})
this.optTime = _optTime
this.DateList = _opt
},
//聚焦input事件设定可选择项
focusselect(){
this.$nextTick(()=>{
var lis = document.getElementsByClassName("QuarterlySelect");
var lists = Array.from(lis)
this.isDisabled = Array.from({length:lists.length}).map(()=>{return true})
if(this.isHasfilter){
for(var j = 0; j < this.selectList.length; j++){
for(var i = 0; i < lists.length; i++){
var text = this.changeQuarterFomat(lists[i].innerText)
if(Number(this.selectList[j].start_time.slice(0,4)) === Number(this.year) && text === this.selectList[j].start_time.slice(5,7)){
this.isDisabled.splice(i,1,false)
}
}
}
}else{
this.isDisabled = Array.from({length:lists.length}).map(()=>{return false})
}
})
},
//将“第1季度”转为“01”格式、“第2季度”转为“04”格式...
changeQuarterFomat(text){
var ret = this.quarterName.find((p)=>{
return text.trim() == this.getPropertyByLg(p,'name')
})
return ret.data
},
// 获取近20年年份列表,倒序排列,最新一年在最前面
getDateList(){
let Dates = new Date();
let year = Dates.getFullYear();
this.OneY = year;
let optDate = [];
for( let i = year - 20; i <= year; i++ ){
optDate.push(i)
}
return optDate.reverse()
},
prev() {
if(this.curIndex == this.DateList.length - 1) return;
// 当前下标值-1,根据下标值获取年份值
this.curIndex = this.curIndex + 1
this.year = this.DateList[this.curIndex].TimeYear
this.focusselect()
},
next() {
if(this.curIndex == 0) return;
// 当前下标值-1,根据下标值获取年份值
this.curIndex = this.curIndex - 1
this.year = this.DateList[this.curIndex].TimeYear
this.focusselect()
},
selectSeason() {
this.showValue = this.optTime[this.curIndex].queryTime
// 根据输入框是否有值来判断清空图标是否渲染
this.showSeason = false
this.optTime.map((p)=>{
if(p.queryTime !== '' && p.TimeYear !== this.year){
this.$set(p,'queryTime','')
}
})
this.showClear = this.showValue == '' ? false : true
this.$emit('getsinglequarter',this.showValue,this.status)
},
// 重置
resetQuarter() {
// 将年份重置到当前年份
this.year = new Date().getFullYear();
// 将已选择的月份清空
for( let i in this.optTime){
this.optTime[i].queryTime = ''
}
// 将输入框清空
this.showValue = ''
// 根据输入框是否有值来判断清空图标是否渲染,此处必然不渲染
this.showClear = false
this.curIndex = 0
this.$emit('getMultiple',[])
},
},
};
</script>
<style scoped>
::v-deep .el-radio__input{
display: none;
}
::v-deep .el-radio__label{
display: inline-block;
padding: 0;
}
::v-deep .el-radio__input.is-checked+.el-radio__label{
color: #FFF;
background-color: #409EFF;
width: 60px!important;
height: 36px;
line-height: 36px;
text-align: center;
border-radius: 20px;
padding: 0;
}
.textitem{
text-align: center;
}
.oneitem{
color: #606266;
width: 60px!important;
height: 36px;
line-height: 36px;
margin: 10px;
text-align: center;
border-radius: 20px;
}
.oneitem:hover{
color: #FFF;
background-color: #409EFF;
}
.clearIconStyle {
display: none;
}
.inputStyle:hover .clearIconStyle{
display: block;
}
</style>
5、YearsRange组件——连选年度子组件
<template>
<div>
<div class="year-picker-box">
<el-date-picker :clearable="false" :picker-options="pickerOptions"
v-model="annual.start"
type="year"
popper-class="selectItemStyle"
size="medium"
style="width: 120px"
:placeholder="language==='zh'?'开始年份':'start year'">
</el-date-picker>
<span style="margin-left: 5px; margin-right: 5px">~</span>
<el-date-picker :clearable="false" :picker-options="pickerOptions"
v-model="annual.end"
type="year"
popper-class="selectItemStyle"
size="medium"
style="width: 120px; margin-right: 2px"
:placeholder="language==='zh'?'结束年份':'end year'">
</el-date-picker>
</div>
</div>
</template>
<script>
export default {
name: 'YearsRange',
props:{
canSelectList:{ type: Array,default:()=>[]},//可选择的日期列表
isHasfilter:{ type: Boolean,default:false},//默认为false,为true需要传canSelectList
language:{ type: String,default:'zh'},
},
data() {
return {
years: [1991,new Date().getFullYear()],
annual:{
start: null,
end: null,
},
pickerOptions: {
disabledDate: this.optionsDate
},
}
},
watch:{
annual:{
deep:true,
handler(val){
if(val.start && val.end){
let start = val.start.getFullYear()
let end = val.end.getFullYear()
if (start < this.years[0] || end > this.years[this.years.length - 1]) {
this.$message({ type: 'error', message: this.language==='zh'?'超过年份范围选择,请重新选择。':'The year range is exceeded. Please select a new one.' })
} else if (val.start > val.end) {
this.$message.error(this.language==='zh'?'结束年份须大于起始年份,请重新选择。':'The end year must be greater than the start year. Please select a new one.')
} else {
this.$emit('getYearRange', start, end)
}
}
}
}
},
methods:{
optionsDate(time){
if(this.isHasfilter){
let date = time.getFullYear()
let timeList = this.canSelectList.map((item) =>{
return item.start_time.slice(0,4)
})
if(timeList.includes(JSON.stringify(date))){
return false
}else{
return true
}
}else{
return time.getTime() >= Date.now() - 8.64e7
}
},
}
}
</script>
<style scoped>
.year-picker-box {
padding: 5px;
width: 340px;
}
</style>
四、总结
这个通用组件结合了elementUI日期选择器组件和多个自定义子组件,通过Demos父组件getFilterCondition函数获取筛选条件对表格或者列表数据进行筛选。可以通过设置isFilter变量,修改日期选择器是否设定只可选择筛选列表包含的日期。其中elementUI日期选择器组件的英文化需要安装vue-i18n,并在入口文件引入使用。