父组件
<V-Select
:placeholder="'提示'"
:value="传入的值"
:disabled="是否禁用"
:optionArr="下拉框总数组"
:index="表格下标"
:optionLabel="'下拉框label值的key'"
:optionValue="'下拉框value值的key'"
:popperClass="'下拉框class值'"
@productChange="选中事件"
/>
<script setup>
// 名称选中
const productChange = async (val, index) => {
表格数组.value[index].绑定的值= val
}
<script/>
子组件
<template>
<div class="select">
<el-select
class="m-2"
v-model="model"
:disabled="disabled"
:placeholder="placeholder"
:value-key="typeof value === 'object' ? valueKey : ''"
filterable
remote
reserve-keyword
remote-show-suffix
:remote-method="(query) => remoteMethod(query, '.' + popperClass)"
@visible-change="(e) => visibleChange(e, model, '.' + popperClass)"
@change="(val) => _this.emit('productChange', val, index)"
:popper-class="popperClass"
>
<el-option
v-for="item in dataArr"
:key="item[optionLabel]"
:label="item[optionLabel]"
:value="item[optionValue]"
>
<div style="text-align: center">
{{ item[optionLabel] }}
</div>
</el-option>
<div
v-if="screenArr.length !== dataArr.length"
style="text-align: center; color: #606266"
>
<el-icon class="is-loading"><Loading /></el-icon>
加载中...
</div>
<div
v-if="dataArr.length === screenArr.length && screenArr.length > 100"
style="text-align: center; color: #606266"
>
到底了!!!
</div>
</el-select>
</div>
</template>
<script setup name="select">
import { ref, watch, getCurrentInstance } from 'vue'
import { Loading } from '@element-plus/icons-vue'
const props = defineProps({
// *为必传
value: [String, Number, Array, Object], // * 绑定的值
index: Number, // * 表格下标
optionArr: Array, // * 列表数组
popperClass: String, // * 下拉框绑定的clsaa值(通过class值获滚动条)
optionLabel: [String, Number, Array, Object], // * 列表label
optionValue: [String, Number, Array, Object], // * 列表value
placeholder: String, // 默认提示
valueKey: String, // 值为对象时绑定的唯一key
disabled: {
type: Boolean,
default: false
} // 是否禁用
})
const model = ref('') // 绑定的值
const numSearch = ref(1) // 废件编码名称下拉加载计数
const dataArr = ref([]) // 列表数组
const screenArr = ref(props.optionArr) // 废件编码名称筛选数组
const _this = getCurrentInstance()
// 打开下拉框
const visibleChange = (e, src, name) => {
if (e) {
dataArr.value = []
if (src) {
screenArr.value.forEach((item, index) => {
if (item[props.optionValue] === src) {
screenArr.value.splice(index, 1)
screenArr.value.unshift(item)
}
})
src = screenArr.value[0][props.optionValue]
}
if (screenArr.value.length > 100) {
dataArr.value = screenArr.value.slice(
(numSearch.value - 1) * 100,
numSearch.value * 100
)
if (name) {
const SELECTWRAP_DOM = document
.querySelector(name)
.querySelector('.el-select-dropdown__wrap')
if (SELECTWRAP_DOM) {
SELECTWRAP_DOM.addEventListener('scroll', function () {
const condition =
this.scrollHeight - this.scrollTop - 1 <= this.clientHeight
if (condition) {
if (dataArr.value.length !== screenArr.value.length) {
numSearch.value += 1
dataArr.value.push(
...screenArr.value.slice(
(numSearch.value - 1) * 100,
numSearch.value > ~~(screenArr.value.length / 100)
? screenArr.value.length
: numSearch.value * 100
)
)
}
}
})
}
}
} else {
dataArr.value = screenArr.value
}
}
}
// 名称搜索
const remoteMethod = (query, name) => {
if (query) {
screenArr.value = props.optionArr.filter((item) => {
return item[props.popperClass].toUpperCase().includes(query.toUpperCase())
})
} else {
screenArr.value = props.optionArr
numSearch.value = 1
}
visibleChange(true, '', name)
}
// 监听数据更新
watch(
() => props.value,
(val) => {
if (val) {
visibleChange(true, val)
model.value = val
} else {
model.value = ''
}
},
{ immediate: true, deep: true }
)
</script>
<style scoped lang="scss"></style>
main.js全局挂载
import App from './App.vue'
import VSelect from '@/components/select.vue'
const app = createApp(App)
app.component('V-Select', VSelect)