Vue2 elementUI年份区间选择组件
一、显现效果

二、组件封装 yearPicker.vue
<template><div class="yearPicker" ref="yearPicker" :style="{ width: width + 'px' }"><div class="_inner labelText" :style="{ width: labelWidth + 'px' }">{{ labelText }}</div><input class="_inner" ref="inputLeft" v-model.number="startShowYear" @focus="onFocus" type="text" @click="clickInput"name="yearInput" @input="checkStartInput($event)" placeholder="选择年份" /><span>{{ sp }}</span><input class="_inner" ref="inputRight" v-model.number="endShowYear" @focus="onFocus" type="text" @click="clickInput"name="yearInput" @input="checkEndInput($event)" placeholder="选择年份" /><div class="_inner floatPanel" v-if="showPanel"><div class="_inner leftPanel"><div class="_inner panelHead"><i class="_inner el-icon-d-arrow-left" @click="onClickLeft"></i>{{ leftYearList[0] + "-" + leftYearList[9] }}</div><div class="_inner panelContent"><div v-for="item in leftYearList" :class="{disabled: checkValidYear(item) != 0,oneSelected: item === startYear && oneSelected,startSelected: item === startYear,endSelected: item === endYear,_inner: true,betweenSelected: item > startYear && item < endYear}" :key="item"><a :class="{cell: true,_inner: true,selected: item === startYear || item === endYear}" @click="onClickItem(item)" @mouseover="onHoverItem(item)">{{ item }}</a></div></div></div><div class="_inner rightPanel"><div class="_inner panelHead"><i class="_inner el-icon-d-arrow-right" @click="onClickRight"></i>{{ rightYearList[0] + "-" + rightYearList[9] }}</div><div class="_inner panelContent"><div :class="{disabled: checkValidYear(item) != 0,startSelected: item === startYear,endSelected: item === endYear,betweenSelected: item > startYear && item < endYear}" v-for="item in rightYearList" :key="item"><a :class="{cell: true,_inner: true,selected: item === endYear || item === startYear}" @click="onClickItem(item)" @mouseover="onHoverItem(item)">{{ item }}</a></div></div></div></div></div>
</template><script>
const SELECT_STATE = {unselect: 0, //未选择selecting: 1, //选中一个selected: 2, //全部选中
};
export default {name: "YearPicker",computed: {oneSelected () {return (this.curState === SELECT_STATE.selecting && (this.startYear === this.endYear || this.endYear == null));},startDate () {return this.startYear;},leftYearList () {return this.yearList.slice(0, 10);},rightYearList () {return this.yearList.slice(10, 20);}},props: {width: {default: 200,},labelWidth: {default: 80,},labelText: {default: "时间标签",},sp: {default: "至",},initYear: {default: null,},},data () {return {startShowYear: null,//输入框 开始年份endShowYear: null,//输入框 结束年份yearList: [], //选择框里可选择的年份showPanel: false, //选择框是否显示startYear: null, //选择框选中的开始年份endYear: null, //选择框选中的结束年份curYear: 0,curSelectedYear: 0,curState: SELECT_STATE.unselect,};},methods: {// 重置时间范围onReset () {this.startYear = null;this.endYear = null;this.startShowYear = null;this.endShowYear = null;this.$emit("updateTimeRange", {startYear: null,endYear: null,});},// 检查年份是否在可选区间内 是否禁用checkValidYear (iYear) {if (this.initYear) {if (iYear > this.initYear.endYear) {return 1} else if (iYear < this.initYear.startYear) {return -1}}return 0},// 开始年份 输入框校验 并将选择框对应开始年份选中checkStartInput (event) {if (isNaN(this.startShowYear)) {this.startShowYear = this.startYear;} else {this.startYear = this.startShowYear * 1;}},// 结束年份 输入框校验 并将选择框对应结束年份选中checkEndInput () {if (isNaN(this.endShowYear)) {this.endShowYear = this.endYear;} else {this.endYear = this.endShowYear * 1;}},// 选择框 鼠标悬停事件 选中年份范围onHoverItem (iYear) {if (this.checkValidYear(iYear) != 0) {return;}if (this.curState === SELECT_STATE.selecting) {let tmpStart = this.curSelectedYear;this.endYear = Math.max(tmpStart, iYear);this.startYear = Math.min(tmpStart, iYear);}},// 选择框 点击事件 选中年份范围onClickItem (iYear) {if (this.checkValidYear(iYear) != 0) {// 点击禁用年份 直接返回return;}if (this.curState === SELECT_STATE.unselect || this.curState === SELECT_STATE.selected) {// 点击未选择或全部选中状态 开始年份直接选中当前年份this.startYear = iYear;this.curSelectedYear = iYear;this.endYear = null;this.curState = SELECT_STATE.selecting;} else if (this.curState === SELECT_STATE.selecting) {// 处于选中一个,然后再点击 // 如果 开始年份和结束年份是一样 但 结束年份值为空 则 结束年份 == 开始年份if(this.startYear && !this.endYear){this.endYear = this.startYear;}this.endShowYear = this.endYear;this.startShowYear = this.startYear;this.curState = SELECT_STATE.selected;this.$emit("updateTimeRange", {startYear: this.startYear,endYear: this.endYear,});setTimeout(() => {//为动画留的时间,可优化this.showPanel = false;}, 300);}},//使 input 获取焦点onFocus () {this.$nextTick(() => {this.showPanel = true;if(this.startShowYear && this.endShowYear){// 点击选择框 已选择年份范围 则将选择框对应年份选中this.endYear = this.endShowYear;this.startYear = this.startShowYear;this.curState = SELECT_STATE.selected;}});},// 点击 input 阻止事件冒泡clickInput (e) {e.stopPropagation();return false;},// 点击空白区域关闭选择框closePanel (e) {if (!this.showPanel) {return;}if (typeof e.target.className !== "string" || e.target.className === "") {this.$nextTick(() => {this.changeYear();this.showPanel = false;});return;}if (e.target.className.indexOf("_inner") === -1 ||(e.target.name === "yearInput" &&e.target !== this.$refs.inputLeft &&e.target !== this.$refs.inputRight)) {this.$nextTick(() => {this.changeYear();this.showPanel = false;});}e.stopPropagation();return false;},// 点击空白区域关闭选择框 时间检验回显changeYear () {if (!this.startYear || !this.endYear) {return;}if (this.startYear > this.endYear) {let tmp = this.endYear;this.endYear = this.startYear;this.startYear = tmp;}if (this.initYear) {this.startYear = Math.max(this.startYear, this.initYear.startYear)this.endYear = Math.min(this.endYear, this.initYear.endYear)}this.startShowYear = this.startYear;this.endShowYear = this.endYear;if (this.startYear && this.endYear) {this.$emit("updateTimeRange", {startYear: this.startYear,endYear: this.endYear + ""});} else {console.warn("WARN:年份不合法", this.startYear, this.endYear);}},// 点击左箭头 切换到上一个十年onClickLeft () {this.curYear = this.curYear * 1 - 10;this.updateYearList();},// 点击右箭头 切换到下一个十年onClickRight () {this.curYear = this.curYear * 1 + 10;this.updateYearList();},// 更新年份列表updateYearList () {let iStart = Math.floor(this.curYear / 10) * 10 - 10;iStart = iStart < 0 ? 0 : iStart;this.yearList = [];for (let index = 0; index < 20; index++) {this.yearList.push(iStart + index);}},//------------------对外接口------------------------// //直接传时间戳// setYear(startYearStamp, endYearStamp) {// if (!isNaN(startYearStamp) && !isNaN(endYearStamp)) {// let startYear = moment(startYearStamp).format("yyyy");// let endYear = moment(endYearStamp).format("yyyy");// this.startYear = startYear * 1;// this.endYear = endYear * 1;// this.endShowYear = endYear * 1;// this.startShowYear = startYear * 1;// }// },},created () {this.curYear = new Date().getFullYear();this.updateYearList();},beforeUnmount () {// 组件销毁时移除事件监听// 点击空白区域关闭选择框document.removeEventListener("click", this.closePanel.bind(this));},mounted () {// 组件挂载时添加事件监听// 点击空白区域关闭选择框document.addEventListener("click", this.closePanel.bind(this));},
};
</script>
<style lang="scss" scoped>
.yearPicker {font-size: 14px;display: flex;position: relative;transition: all 0.3s;input {text-align: center;}input:first-child {text-align: right;}background-color: #fff;.labelText {text-align: center;}span {padding: 0 8px;height: 38px;line-height: 38px;}border: 1px solid #eff1f3;height: 40px;line-height: 40px;border-radius: 4px;padding: 0 28px 0 8px;box-sizing: border-box;.floatPanel {>div {width: 50%;}padding: 0 16px;position: absolute;display: flex;background-color: #fff;z-index: 2000;border-radius: 4px;width: 650px;height: 250px;top: 40px;left: -10px;box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);.panelContent {display: flex;flex-wrap: wrap;width: 100%;height: calc(100% - 70px);.disabled {color: #ccc;}.oneSelected {border-top-right-radius: 24px;border-bottom-right-radius: 24px;}.startSelected {background-color: #f6f6f7;border-top-left-radius: 24px;border-bottom-left-radius: 24px;}.endSelected {background-color: #f6f6f7;border-top-right-radius: 24px;border-bottom-right-radius: 24px;}.betweenSelected {background-color: #f6f6f7;}>div {width: 75px;height: 48px;line-height: 48px;margin: 3px 0;// border-radius: 24px;text-align: center;a {display: inline-block;width: 60px;height: 36px;cursor: pointer;line-height: 36px;border-radius: 18px;}.selected {background-color: #3e77fc;color: #fff;}}}.panelHead {position: relative;height: 46px;line-height: 46px;text-align: center;i {position: absolute;cursor: pointer;&:hover {color: #3e77fc;}}}.rightPanel {padding-left: 8px;}.leftPanel .panelHead i {left: 20px;top: 15px;}.rightPanel .panelHead i {right: 20px;top: 15px;}}.floatPanel::before {content: "";height: 100%;position: absolute;left: 50%;width: 1px;border-left: 1px solid #e4e4e4;}
}input {width: 60px;border: none;height: 40px;line-height: 40px;box-sizing: border-box;background-color: transparent;
}input:focus {outline: none;background-color: transparent;
}.yearPicker:hover {border-color: #3e77fc;
}.dateIcon {position: absolute;right: 16px;top: 9px;color: #adb2bc;
}
</style>三、组件使用
<template>
<div class="moneyType">
<div class="moneyType_con">
<el-form :inline="true" :model="formInline" class="demo-form-inline">
<el-form-item label="年度">
<YearPicker ref="yearPicker" labelText="" :label-width="0" :initYear="initYear" @updateTimeRange="updateStatisticYear"></YearPicker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="getData(1)">查询</el-button>
<el-button type="success" icon="el-icon-refresh" @click="onReset(1)">重置</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
import YearPicker from "../components/yearPicker.vue";
export default {
components: {
YearPicker
},
data () {
return {
initYear: {
startYear: 1945,
endYear: new Date().getFullYear()
},//年份区间选择框初始时间
formInline: {
nd: '',
nds: '',
},
};
},
mounted () {
},
methods: {
// 更新时间范围
updateStatisticYear({startYear, endYear}){
this.formInline.nd = startYear
this.formInline.nds = endYear
},
// 查询
getData () {
},
// 重置
onReset () {
this.formInline = {
nd: '',
nds: '',
}
this.$refs.yearPicker.onReset()
this.getData();
},
},
};
</script>
四、实现功能
1、可选年份区间(区间外禁止点击和选择,同时校正输入)。
2、实现选择开始年和结束年一样,如2024-2024。
3、实现弹框显示时回显当前选择的开始年和结束年。
