Skip to main content

一个基于el-select的网络选择组件

element select 组件只有远程搜索,没有滚动加载的功能,想着实现一个基于element的网络选择组件

代码

<template>
<div class="net-select">
<el-select
v-model="model"
v-bind="$attrs"
ref="select"
:filterable="filterable"
:remote="filterable"
:remote-method="remoteMethod"
:loading="searchLoading"
popper-class="net-select-popper"
@change="$emit('change', model)"
>
<el-option
v-for="(item, index) in options"
:key="fieldNames.key ? item[fieldNames.key] : index"
:label="item[fieldNames.label]"
:value="item[fieldNames.value]"
/>
<div class="page-loading" v-if="pageLoading" element-loading-text="加载中" v-loading="pageLoading"></div>
<div class="option-error" v-if="error">{{ error }}</div>
</el-select>
</div>
</template>

<script>
import http from "@/http";
export default {
name: "NetSelect",
model: {
prop: "value",
event: "change"
},
props: {
// 值
value: {
type: [String, Number, Array],
default: null
},
// 接口
url: {
type: String,
required: true,
default: null
},
// 请求参数
data: {
type: Object,
default: () => {}
},
// 请求方式
method: {
type: String,
default: "post"
},
// 自定义节点字段
fieldNames: {
type: Object,
default: () => {
return {
key: "id",
label: "label",
value: "value"
};
}
},
// 是否可搜索
filterable: {
type: Boolean,
default: false
},
// 处理搜索关键词
formatter: {
type: Function,
default: query => query
}
},
data() {
return {
model: this.value,
options: [],
pageSize: 10,
pageNum: 1,
total: 0,
searchLoading: false,
pageLoading: false,
error: "",
keyword: {}
};
},
mounted() {
// 设置一个下拉框滚动到底部的监听
const select = this.$refs.select;
const popper = select.$refs.popper;
popper.$el.addEventListener("scroll", this.handleScroll, 300, true);
// 初始化数据
this.getOptions();
},
methods: {
// 获取数据
getOptions() {
this.pageLoading = true;
this.getOptionsApi(this.getOptionsApi, { ...this.data, ...this.keyword })
.then(res => {
this.pageLoading = false;
this.searchLoading = false;
this.options = [...this.options, ...data.records];
this.total = data.total;
this.pageNum = data.current + 1;
this.pageSize = data.size;
this.error = "";
})
.catch(err => {
this.error = "服务器繁忙";
});
},
// 根据method获取请求
getOptionsApi(params) {
return http[this.method.toLowerCase()](this.url, params);
},
// 监听滚动
handleScroll(e) {
const { scrollTop, scrollHeight, clientHeight } = e.target;
if (scrollTop + clientHeight >= scrollHeight && !this.pageLoading) {
if (this.pageNum * this.pageSize < this.total) {
this.getOptions();
}
}
},
// 处理搜索
remoteMethod(query) {
if (query === "") return;
if (typeof this.formatter !== "function") {
console.warn("formatter 必须是一个函数");
return;
}
this.options = [];
this.pageNum = 1;
this.pageSize = 10;
this.searchLoading = true;
this.keyword = this.formatter(query);
this.getOptions();
}
},
beforeDestroy() {
// 去除监听
const select = this.$refs.select;
const popper = select.$refs.popper;
popper.$el.removeEventListener("scroll", this.handleScroll, true);
}
};
</script>
<style lang="scss" scoped>
.net-select-popper .el-select-dropdown__list .option-error {
margin: 10px 0;
color: var(--color-danger);
text-align: center;
}
.page-loading {
height: 60px;
margin: 0 12px;
text-align: center;
font-size: 12px;
}
</style>

使用说明

基本用法

<template>
<div>
<NetSelect
v-model="value"
@change="change"
multiple
url="/api/lab/base/supplier/page"
:fieldNames="{ label: 'supplierName', value: 'id' }"
:filterable="true"
:formatter="
e => {
return {
supplierName: e
};
}
"
/>
</div>
</template>

参数

参数说明类型默认值必填备注
v-modelv-model 绑定的值String-
url请求地址String-
method请求方式Stringpost
data请求参数Object{}
fieldNames选项映射字段Object{ key: "id", label: "label",value: "value"}
formatter搜索关键词格式化函数function(string)=>stringquery=>query
filterable是否可搜索Booleanfalse为 true 时,自动将 el-select 的 filterableremote 设置为 true
  • 其他属性与 el-select 一致

  • el-form-item中使用,需要校验或者el-form操作

<template>
<el-form :model="formData" :rule="rules" ref="formRef">
<el-form-item label="内容" prop="inputValue" ref="netSelectRef">
<!-- 添加ref,用来调用$emit -->
<net-select v-model="formData.inputValue" url="........." @change="$refs.netSelectRef.$emit('el.form.change',$event)"></net-select>
</el-form-item>
</el-form>
</template>