el-select下拉获取数据
1.解决问题
场景:下拉数据量过大,后端提供一个分页查询接口;需要每次滚动加载下一页的下拉数据
且单选的状态,需要支持回显,通过
name
名称查询回显;--本文已包含如果是多选回显,可以让后端提供一个根据
idList
能反向找到对应id的下拉集合的接口;--可自己试试
2.封装MyScrollSelect组件
javascript
<template>
<div>list长度:{{ list.length }}</div>
<div>$attrs:{{ $attrs }}</div>
<el-select @change="changeVal" v-bind="$attrs" :remote-method="remoteMethod" style="width: 100%">
<div v-infinite-scroll="loadMore" style="overflow: hidden">
<el-option v-for="item in list" :key="item[valueKey]" :label="item[labelKey]" :value="item[valueKey]" />
<!-- 下拉底部加载提示 -->
<div v-if="loading" class="loading-text">加载中...</div>
</div>
</el-select>
</template>
<script setup >
import { ref, watch, onMounted } from "vue"
import { debounce } from "lodash"
const emit = defineEmits(['update:searchName']);
const props = defineProps({
// v-model绑定值不为空时传递初始数据列表
initialOptions: {
type: Array,
default: () => []
},
// 传入对应的列表加载api
methods: {
type: Function,
required: true // 或者 true,取决于它是否必须被传递
},
// 传入查询关键字
searchKey: {
type: String,
default: ""
},
// 所选key对用name
searchName: {
type: String,
default: undefined
},
labelKey: {
type: String,
default: "name"
},
valueKey: {
type: String,
default: "id"
},
// 查询的其他参数
queryData: {
type: Object,
default: () => { }
},
})
const isMounted = ref(false)
const loading = ref(false)
const list = ref([]) // 选项列表
const queryFrom = ref({
pageNum: 1,
totalPage: 1,
pageSize: 20
})
// 自定义远程搜索方法
const remoteMethod = (query) => {
queryFrom.value.pageNum = 1
list.value = []
queryFrom.value[props.searchKey] = query
queryFrom.value = { ...queryFrom.value, ...props.queryData }
getList()
}
// 调用props.methods获取下拉数据
const getList = () => {
loading.value = true
props.methods(queryFrom.value).then(res => {
console.log('%c【' + 'res' + '】打印', 'color:#fff;background:#0f0', res)
list.value = [...list.value, ...res.records]
queryFrom.value.totalPage = Math.ceil(res.total / 20) // 计算总页数 不是总数
}).finally(() => {
loading.value = false
})
}
// 无限滚动触底加载
const loadMore = debounce(() => {
if (queryFrom.value.pageNum >= queryFrom.value.totalPage || loading.value) return
queryFrom.value.pageNum++
getList()
}, 200)
// 根据id回显name
const changeVal = (e) => {
list.value.forEach(ele => {
if (ele[props.valueKey] === e) {
emit('update:searchName', ele[props.labelKey])
}
})
}
// 监听 initialOptions 的变化,用于加载初始值
watch(
() => props.initialOptions,
newVal => {
// 如果 modelValue 中的值还未加载到选项中,加载这些数据
if (newVal && newVal.length > 0) {
list.value.push(...props.initialOptions)
}
},
{ immediate: true }
)
onMounted(() => {
isMounted.value = true
// 获取初始数据
if (props.searchName) {
remoteMethod(props.searchName) // 根据name回显
} else {
getList()
}
})
</script>
<style scoped>
.loading-text {
padding: 5px;
text-align: center;
color: #999;
font-size: 12px;
}
</style>
3.使用MyScrollSelect组件
javascript
<template>
<div class="page-view wbg pall">
<pre>{{ form }}</pre>
<div style="margin-top: 50px">多选:只能存id</div>
<MyScrollSelect
v-if="isMounted"
ref="reviewStageRef"
v-model="form.idList1"
:placeholder="'滚动加载或搜索-单选'"
clearable
filterable
remote
collapse-tags
collapse-tags-tooltip
multiple
:initialOptions="initialOptions"
:methods="getDeviceNameListApi"
searchKey="terminalDeviceName"
valueKey="id"
labelKey="terminalDeviceName"
/>
<div style="margin-top: 50px">单选:可存id和name 根据name可回显</div>
<MyScrollSelect
v-if="isMounted"
ref="reviewStageRef"
v-model="form.terminalDeviceId"
v-model:searchName="form.terminalDeviceName"
:placeholder="'滚动加载或搜索-单选'"
clearable
filterable
remote
:initialOptions="initialOptions"
:methods="getDeviceNameListApi"
searchKey="terminalDeviceName"
valueKey="id"
labelKey="terminalDeviceName"
/>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { getDeviceNameListApi } from "@/api/ipManagement.js" // 后端获取下拉分页接口
defineOptions({
name: 'FactorySiteAddressLedger'
})
const isMounted = ref(false)
const form = ref({
idList1: [], // 多选参数
terminalDeviceId: '710241160000004443', // 单选参数
terminalDeviceName: '益海电厂网监工作站',
})
const reviewStageRef = ref(null)
const initialOptions = ref([]) // 初始下拉数据
onMounted(() => {
isMounted.value = true
})
</script>
<style lang="scss" scoped></style>