业务场景 选择职级范围,ant-design-vue的适用的组件是两个select并用,前值后值充当区间范围,这样会比较麻烦,对用户不友好,与是开发一个 模拟下拉select,但是可选择区间,回显样式类似于 A -B
注意组件文档注释采用js-doc规范,生成说明文档,js-doc使用详见我之前的文章
https://blog.csdn.net/weixin_38500689/article/details/111921114?spm=1001.2014.3001.5502
看图:
这样一个select就可以完成区间选择了。
调用:
可直接传 codeId 调通过用接口(这个接口就自行定义参数吧,这里只是我的实现)获取码表数据源
也可以通过 selectList 外部传入数据源。
valueName和codeName 数据源的关键字以及回显字段名 默认 code和name
<eg-select-range
v-model="selectRange"
codeId="GW_JOBLEVEL"
:valueName="'code'"
:codeName="'name'"
></eg-select-range>
下面看代码:
首先基于antdv的基础组件 dropdown、input、icon、menu
template部分:这个ant基础组件的使用就不在这里解释了
<template>
<a-dropdown
class="eg-select-range"
:getPopupContainer="getContainer"
:trigger="triggerArr"
:placement="placement"
:overlayClassName="'dropconditonSelectWidth'"
v-model="dropVisible"
>
<a-input class="selectrangeinput" :disabled="disabled" v-model="selectRangeLabels">
<a-icon @click.stop="deleteRnage" class="selectrangeclose" slot="suffix" type="close-circle" />
</a-input>
<a-menu slot="overlay" @click="handleMenuClick" :key="count">
<template v-for="(item, index) in sourceList">
<a-menu-item
:key="index"
:disabled="item.disabled"
:class="{
egselectrangeselectRow: isSelect(index),
egselectrangerangeRow: isRange(index),
egselectrangeenterrow: isenter(index)
}"
><p @mouseenter="menuEnter(index)" @mouseleave="menuLeave" class="menu-select-range">
{{ item.name }}
</p></a-menu-item
>
</template>
</a-menu>
</a-dropdown>
</template>
重点讲解script部分
data部分:
selectRangeLabels:回显字段
selectKeyRange:选中项对应的关键字(码值或者id,是要传回给后端的)
sourceList: 下拉数据源
enterIndex: 鼠标hover下拉项的索引
prop部分:
/**
- Props 接受父组件的传值 重要参数
- @prop {String} valueName 数据源的关键字字段名 默认 code
- @prop {String} labelName 数据源的显示字段名 默认 name
- @prop {String} placement 下拉框显示位置 可选 bottomLeft bottomCenter bottomRight topLeft topCenter topRight 默认 bottomCenter
- @prop {String} codeId 此处为通用接口的码表名称,通过codeId获取数据源
- @prop {Array} triggerArr dropDown的触发方式,默认 click
- @prop {String} formItemKey 此处为在form中使用时的表单项key(单独使用selectRange不需要)
- @prop {String} triggerNode 定义dropDown的pop的挂载节点 属性getPopupContainer
- @prop {String} value v-model的值
- @prop {Array} selectList 数据源,父组件传入
*/
methods 部分
getselectLabels方法:
/**
* @function getselectLabels
* @param {Array} val - 当前选中值
* @description 解析选中项,得到页面显示内容 (根据val 获取对应的name,设置回显)
*/
getselectLabels(val) {
if (val) {
let label = [];
this.sourceList.forEach(item => {
if (val.indexOf(item[this.valueName]) > -1) {
label.push(item.name);
}
});
if (val.split(",")[0] == val.split(",")[1]) {
this.selectRangeLabels = label[0] ? label[0] : "";
} else {
let str1 = label[0] ? label[0] : "";
let str2 = label[1] ? label[1] : "";
this.selectRangeLabels = str2 + "-" + str1;
}
}
},
方法上边都有注释,也就不一一拿出来解释一遍了。
代码双手奉上,如果觉得有帮助请点赞关注评论,谢谢
<template>
<a-dropdown
class="eg-select-range"
:getPopupContainer="getContainer"
:trigger="triggerArr"
:placement="placement"
:overlayClassName="'dropconditonSelectWidth'"
v-model="dropVisible"
>
<a-input class="selectrangeinput" :disabled="disabled" v-model="selectRangeLabels">
<a-icon @click.stop="deleteRnage" class="selectrangeclose" slot="suffix" type="close-circle" />
</a-input>
<a-menu slot="overlay" @click="handleMenuClick" :key="count">
<template v-for="(item, index) in sourceList">
<a-menu-item
:key="index"
:disabled="item.disabled"
:class="{
egselectrangeselectRow: isSelect(index),
egselectrangerangeRow: isRange(index),
egselectrangeenterrow: isenter(index)
}"
><p @mouseenter="menuEnter(index)" @mouseleave="menuLeave" class="menu-select-range">
{{ item.name }}
</p></a-menu-item
>
</template>
</a-menu>
</a-dropdown>
</template>
<script>
import Vue from "vue";
import { Dropdown, Menu } from "ant-design-vue";
Vue.use(Dropdown);
Vue.use(Menu);
import "ant-design-vue/lib/menu/style/css";
import "ant-design-vue/lib/dropdown/style/css";
/**
* 自定义下拉选择区间组件
* @module EgSelectRange
* @author 何志强
* @description 自定义下拉选择区间组件,可以传selectList数据源, 也可以传codeId 组件自行请求数据源,对外暴露 onChange 事件
*/
export default {
name: "EgSelectRange",
data() {
return {
dropVisible: false,
selectRangeLabels: "",
selectKeyRange: [],
count: 0,
enterIndex: "",
sourceList: []
};
},
/**
* Props 接受父组件的传值 重要参数
* @prop {String} valueName 数据源的关键字字段名 默认 code
* @prop {String} labelName 数据源的显示字段名 默认 name
* @prop {String} placement 下拉框显示位置 可选 bottomLeft bottomCenter bottomRight topLeft topCenter topRight 默认 bottomCenter
* @prop {String} codeId 此处为通用接口的码表名称,通过codeId获取数据源
* @prop {Array} triggerArr dropDown的触发方式,默认 click
* @prop {String} formItemKey 此处为在form中使用时的表单项key(单独使用selectRange不需要)
* @prop {String} triggerNode 定义dropDown的pop的挂载节点 属性getPopupContainer
* @prop {String} value v-model的值
* @prop {Array} selectList 数据源,父组件传入
*/
props: {
disabled: {
default: false
},
valueName: {
default: "code"
},
labelName: {
default: "name"
},
codeId: {
type: String
},
triggerArr: {
type: Array,
default: function() {
return ["click"];
}
},
formItemKey: {
type: String
},
placement: {
type: String,
default: "bottomCenter"
},
triggerNode: {
default: "parent"
},
value: { type: String },
selectList: {
type: Array,
default: function() {
return [
// {
// id: "01",
// name: "程咬金",
// disabled: false
// },
// {
// id: "02",
// name: "亚瑟",
// disabled: false
// },
// {
// id: "03",
// name: "牛魔",
// disabled: false
// },
// {
// id: "04",
// name: "李元芳",
// disabled: false
// },
// {
// id: "05",
// name: "后裔",
// disabled: false
// },
// {
// id: "06",
// name: "狄仁杰",
// disabled: false
// }
];
}
}
},
watch: {
selectList: {
deep: true,
immediate: true,
handler: function(list) {
if (list && list.length > 0) {
this.sourceList = list;
this.getselectLabels(this.value);
}
}
}
},
mounted() {
if (this.codeId) {
this.loadData();
}
},
methods: {
deleteRnage() {
this.selectKeyRange = [];
this.selectRangeLabels = "";
this.$emit("input", "");
this.count++;
},
loadData() {
let par = {
serviceName: "newPlugin",
servicePath: "code",
code: this.codeId, // 类型:String 必有字段 备注:代码表表名
sync: "1", // 类型:String 可有字段 备注:是否一次性返回代码全部数据,默认为"1"
filter: this.filter ? this.filter : "", // 类型:String 可有字段 备注:过滤条件,根据加载的代码表自行拼写SQL条件
parentCode: "mock" // 类型:String 可有字段 备注:父代码,分级加载时使用
};
this.$request(par).then(res => {
if (res.status === "1") {
this.sourceList = res.msgDetail;
this.getselectLabels(this.value);
}
});
},
/**
* @function getselectLabels
* @param {Array} val - 当前选中值
* @description 解析选中项,得到页面显示内容 (根据val 获取对应的name,设置回显)
*/
getselectLabels(val) {
if (val) {
let label = [];
this.sourceList.forEach(item => {
if (val.indexOf(item[this.valueName]) > -1) {
label.push(item.name);
}
});
if (val.split(",")[0] == val.split(",")[1]) {
this.selectRangeLabels = label[0] ? label[0] : "";
} else {
let str1 = label[0] ? label[0] : "";
let str2 = label[1] ? label[1] : "";
this.selectRangeLabels = str2 + "-" + str1;
}
}
},
getContainer(Node) {
if (!this.triggerNode) return Node.parentNode;
if (this.triggerNode == "parent") {
return Node.parentNode;
} else if (this.triggerNode == "body") {
return document.body;
} else {
let x = Node;
while (x.tagName != this.triggerNode) {
x = x.parentNode;
}
return x;
}
},
/**
* @function menuEnter
* @param {Number} index - 鼠标移入项的索引
* @description 鼠标移入触发 记录鼠标所在位置
*/
menuEnter(index) {
this.enterIndex = index;
// this.count++;
},
menuLeave() {
// this.enterIndex = "";
// this.count++;
},
/**
* @function isenter
* @param {Number} index - 遍历时 下拉选项的索引
* @description 判断当前索引值是否等于鼠标移入项
*/
isenter(index) {
if (this.enterIndex !== "" && this.selectKeyRange.length == 1) {
let first = this.selectKeyRange[0];
if (this.enterIndex < first) {
if (index > this.enterIndex && index < first) {
return true;
} else {
return false;
}
} else if (this.enterIndex > first) {
if (index > first && index < this.enterIndex) {
return true;
} else {
return false;
}
}
return false;
} else return false;
},
/**
* @function isSelect
* @param {Number} index - 遍历时 下拉选项的索引
* @description 判断当前索引值是否等于已选中节点的索引值
*/
isSelect(index) {
if (this.selectKeyRange.indexOf(index) > -1) {
return true;
} else {
return false;
}
},
/**
* @function isRange
* @param {Number} index - 遍历时 下拉选项的索引
* @description 判断当前下拉项是否在已选中节点的区间内
*/
isRange(index) {
let first = this.selectKeyRange[0];
let second = this.selectKeyRange[1];
if (index > first && index < second) {
return true;
} else return false;
},
handleMenuClick(s) {
let index = s.key;
if (this.selectKeyRange.length == 1) {
let first = this.selectKeyRange[0];
if (index > first) {
this.selectKeyRange[1] = index;
} else {
this.selectKeyRange = [index, first];
}
// 选择结束
let ids = [
this.sourceList[this.selectKeyRange[0]][this.valueName],
this.sourceList[this.selectKeyRange[1]][this.valueName]
].join(",");
this.$emit("input", ids);
this.getselectLabels(ids);
this.$emit("onChange", { type: "change", key: this.formItemKey, labels: this.selectRangeLabels });
this.dropVisible = false;
} else if (this.selectKeyRange.length == 2) {
this.selectKeyRange = [index];
} else {
this.selectKeyRange.push(index);
}
this.count++;
}
}
};
</script>
<style lang="scss">
.selectrangeinput {
.dropconditonSelectWidth {
overflow: auto;
max-height: 400px;
}
.selectrangeclose {
font-size: 12px;
display: none !important;
color: rgba(0, 0, 0, 0.25);
cursor: pointer;
&:hover {
color: rgba(0, 0, 0, 0.45);
}
}
&:hover {
.selectrangeclose {
display: block !important;
}
}
}
.egselectrangeselectRow {
background-color: #3f8fff;
color: #ffffff !important;
&:hover {
background-color: #3f8fff !important;
}
}
.egselectrangerangeRow {
background-color: #e6f7ff;
}
.egselectrangeenterrow {
background-color: #e6f7ff;
}
.menu-select-range {
margin: 0;
}
</style>