效果图:

下拉组件实现: wb-select-scroll.vue
<!--
<wb-select-scroll :parentValue.sync="dataList.wbCombinIVos[index].subid" urls="subjectEndScroll" :rowIndex="index" @selectBackFun="dealSelectBackFun"
placeholders="请选择折扣科目" clearable></wb-select-scroll >
5、新增使用那个组件,assembly
shop是分机机构,department是部门,employ是业务员,subject是科目,remark是摘要,billkind100是运输方式,project是项目,customer是客户,money是币种,
paymethod是结算方式,trader是供应商,iotype是出入库类型,assetsType是固定资产类别,assetsAddType是固定资产增加方式,assetsReduceType是固定资产减少方式
voucherRemark是凭证摘要,goodstype是货品类别,brand是品牌,
新改动;下拉刷新,根据传过来的urls值,请求后端接口,要求,所有的接口都要按照分页的格式进行处理,可以参考 goodsList 接口进行开发
urls="goodsList" 货品下拉列表
urls="subjectEndScroll" 末级科目下拉列表
urls="getTraderCustomerScroll" 客户下拉列表
-->
<template>
<div >
<el-popover placement="bottom" :width="width" trigger="click" popper-class="wb-padding" v-model="visible"
:disabled="disabled">
<div ref="divRef">
<div style="overflow: auto; height: 200px; border: 1px solid #ddd;" ref="scrollContainer" v-if="urls == 'goodsList'">
<!-- <el-radio-group v-model="goodstype" style="margin-top: 5px;">
<el-radio :label="3">A类</el-radio>
<el-radio :label="6">B类</el-radio>
<el-radio :label="9">C类</el-radio>
</el-radio-group> -->
<ul class="el-scrollbar__view el-select-dropdown__list" style="list-style: none; padding: 0; margin: 0;">
<!-- 表头 -->
<div>
<li class="el-select-dropdown__item"
style="display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #ddd; padding: 8px 0; background-color: #f8f4f4;font-weight: bold">
<span style="padding:5px;display: inline-block; width: 200px;text-align:center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; border-right: 1px solid #ddd; padding-right: 5px;">
编码
</span>
<span style="padding:5px; width: 250px;text-align:center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; border-right: 1px solid #ddd; padding-right: 5px;">
名称
</span>
<span style="padding:5px; width: 100px;text-align:center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
规格
</span>
</li>
</div>
<!-- 数据行 -->
<div v-for="(it, index) in listArr" :key="index" @click="upData(it)">
<li class="el-select-dropdown__item"
style="padding:5px;display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #ddd; padding: 8px 0;">
<span style="padding:5px;display: inline-block; width: 200px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; border-right: 1px solid #ddd; padding-right: 5px;"
:title="it.code">
{{ it.code }}
</span>
<span style="padding:5px;width: 250px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; border-right: 1px solid #ddd; padding-right: 5px;"
:title="it.name">
{{ it.name }}
</span>
<span style="padding:5px; width: 100px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"
:title="it.specs">
{{ it.specs }}
</span>
</li>
</div>
<!-- 无数据时显示 -->
<div v-if="listArr == null || listArr.length <= 0"
style="display: flex; justify-content: center; align-items: center; text-align: center; width: 100%; height: 200px; border: 1px solid #ddd;">
<span style="padding:5px;">无数据</span>
</div>
</ul>
</div>
<div style="overflow: auto; height: 200px;" ref="scrollContainer" v-else>
<ul class="el-scrollbar__view el-select-dropdown__list" style="list-style: none; padding: 0; margin: 0;">
<!-- 表头 -->
<div>
<li class="el-select-dropdown__item"
style="display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #ddd; padding: 8px 0; background-color: #f8f4f4;font-weight: bold">
<span style="padding:5px;display: inline-block; width: 200px;text-align:center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; border-right: 1px solid #ddd; padding-right: 5px;">
编码
</span>
<span style="padding:5px; width: 250px;text-align:center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; border-right: 1px solid #ddd; padding-right: 5px;">
名称
</span>
</li>
</div>
<!-- 数据行 -->
<div v-for="(it, index) in listArr" :key="index" @click="upData(it)">
<li class="el-select-dropdown__item"
style="display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #ddd; padding: 8px 0;">
<span style="padding:5px;display: inline-block; width: 200px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; border-right: 1px solid #ddd; padding-right: 5px;"
:title="it.code">
{{ it.code }}
</span>
<span style="padding:5px;width: 250px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; border-right: 1px solid #ddd; padding-right: 5px;"
:title="it.name">
{{ it.name }}
</span>
</li>
</div>
<div v-if="listArr == null || listArr.length <= 0"
style="display: flex; justify-content: center; align-items: center; text-align: center; width: 100%; height: 200px;">
<span style="padding:5px;">无数据</span>
</div>
</ul>
</div>
<div :style="'background-color: #fff;width:' + width + ' px;'">
<div v-if="assembly" style="display: flex; line-height: 30px;cursor: pointer;">
<div class="addBorder" style="width: 50%;" @click="adds">新增</div>
<div class="delBorder" style="width: 50%;" @click="empty">重置</div>
</div>
<div v-else style="display: flex; line-height: 30px;cursor: pointer;">
<div class="delBorder" style="width: 100%;" @click="empty">重置</div>
</div>
</div>
</div>
<el-input type="text" ref="myInput" v-model="name" @change="changes" @input="remoteName"
slot="reference" size="mini" style="width: 100%;" :placeholder="placeholders" :disabled="disabled">
<i v-if="disabled == false" slot="suffix"
:class="showIcon ? 'el-input__icon el-icon-arrow-up' : 'el-input__icon el-icon-arrow-down'"
@click="handleClick"></i>
<i v-if="disabled == true" class="el-input__icon el-icon-arrow-up"></i>
</el-input>
</el-popover>
<!-- 组件化新增 -->
<component :is="currentComponent" ref="assemblyAll" @on-added="handleAdded"></component>
</div>
</template>
<script>
export default {
components: {
"areas": () => import('./assembly/area.vue'),
},
props: {
parentValue: "",
placeholders: "",
list: [],
widths: "",
urls: "",
defaultId: "",
assembly: "",
disabled: {
default: false
},
voucherTemSubject: "",
params:{
page: 1,
pageSize: 20
},
rowIndex: {
type: Number,
default: -999
},
},
watch: {
visible(newVal) {
this.showIcon = newVal; // 如果visible为true,箭头向上;false时箭头向下
if (newVal) {
//设置宽度
this.focusSelect();
this.$nextTick(() => {
const scrollContainer = this.$refs.scrollContainer;
if (scrollContainer) {
scrollContainer.addEventListener('scroll', this.handleScroll);
}
this.paramsData.page = 1; // 初始化页码
this.listArr = []; // 清空数据
this.hasMore = true; // 允许加载更多
// this.fetchOptions(); // 加载第一页数据
// console.log('watch监听打开下拉后查询-------newVal--',newVal);
this.selectByKeyWord();
});
} else {
const scrollContainer = this.$refs.scrollContainer;
if (scrollContainer) {
scrollContainer.removeEventListener('scroll', this.handleScroll);
}
}
},
list(newVal) {
//this.listArr = newVal;
},
parentValue: {
handler(newvalue) {
this.$nextTick(() => {
if(this.parentValue){
//默认值
// console.log('watch监听parentValue后查询---------',this.parentValue);
this.selectByKeyWord(1);
}else {
this.name = "";
}
});
}
},
},
data() {
return {
name: '',
goodstype: null,
width: "200",
dataList: { "mshopid": "" },
visible: false,
parentids: this.defaultId,
id: "",
index: 1,
listCp: [],
currentComponent: "",
listArr: [],
inpIndex: true,
showIcon: false,
parentValueInfo: this.parentValue,
paramsData:{
page: 1, // 当前页码
pageSize: 20,
},
// 每页数据量
hasMore: true, // 是否还有更多数据
loading: false,
had_id_one: [],
// list: [{'id':1,"name":"dade"},
// {'id':4,"name":"dade4"}]
};
},
created() {
this.listArr = this.list
},
mounted() {
// console.log('mounted---this.parentValue===',this.parentValue);
// 存在加载下拉框,降低io
if(this.parentValue){
//默认值
// console.log('挂载完成后查询---------',this.parentValue);
this.selectByKeyWord(1);
}
//动态引入组件
if (this.assembly) {
//引入组件
this.currentComponent = this.assembly;
}
document.addEventListener('click', this.selectBlur, true)
},
beforeDestroy() {
document.removeEventListener('click', this.selectBlur, true)
// 移除滚动事件监听,防止内存泄漏
const scrollContainer = this.$refs.scrollContainer;
if (scrollContainer) {
scrollContainer.removeEventListener('scroll', this.handleScroll);
}
},
methods: {
handleScroll(event){
// console.log('------滚动了---------');
const { scrollTop, scrollHeight, clientHeight } = event.target;
//console.log('scrollTop + clientHeight=======',scrollTop + clientHeight)
// console.log('scrollHeight=======',scrollHeight)
// 判断是否滚动到底部
if (scrollTop + clientHeight >= scrollHeight-1 && this.hasMore ) {
this.paramsData.page++; // 增加页码
// this.fetchOptions(); // 请求下一页数据
// console.log('到底了,查询下一页------');
this.select();
}
},
// 输入框失去焦点
selectBlur(e) {
if (
this.visible === true
&& this.$refs.divRef
&& this.$refs.myInput
&& this.$refs.myInput.$el
&& !this.$refs.divRef.contains(e.target)
&& !this.$refs.myInput.$el.contains(e.target)
) {
this.visible = false
}
},
async handleAdded(data) {
this.parentids = data.id
// console.log('新增--handleAdded---查询----');
await this.select()
this.$emit('update:parentValue', data.id);
// 处理新增数据的回调方法
this.$emit('add-callback', { ...data })
},
handleClick() {
// if (this.showIcon === false) {
// this.focusSelect()
// }
},
// 输入框获得焦点
focusSelect() {
const inputElement = this.$refs.myInput.$el;
if (this.widths == undefined) {
this.width = inputElement.offsetWidth;
} else {
this.width = this.widths;
}
},
//失去焦点,或者按回车,返回
changes() {
if (this.urls == 'remark') {
this.inpIndex = false;
this.$emit('update:parentValue', this.name);
}
},
// 输入框数据改变
remoteName(e) {
this.paramsData.keyWord = e;
this.selectByKeyWord();
},
//点击选中,返回值,数据双向绑定
upData(it) {
this.name = it.name;
this.$emit('update:parentValue', it.id);
this.$emit('selectBackFun', it,this.rowIndex);//选中调用父类方法
this.visible = false;
},
// 新增
adds() {
this.$refs.assemblyAll.assembly();
this.visible = false;
},
// 重置
empty() {
this.name = "";
this.$emit('update:parentValue', "");
this.$emit('accountFn', {})
this.visible = false;
},
// 查询下拉框
async select() {
if (this.urls) {
await this.$myhttp.post("/main/dropDowm/" + this.urls, this.paramsData).then((res) => {
// console.log('select查询--res.data.code----',res.data.code);
if(res.data.code == 2000){
let datalist = res.data.data;
if(datalist != null && datalist.length > 0){
this.listArr = [...this.listArr,...datalist]
this.listCp = [...this.listCp,...datalist]
}
// console.log('select查询结果this.listArr---',this.listArr);
}
})
}
},
retParamsData(){
this.paramsData.page = 1;
this.paramsData.pageSize = 20;
},
// 查询下拉框
async selectByKeyWord(val) {
if (this.urls) {
if(this.parentValue && '' !== this.parentValue && val == 1){
this.paramsData.parentValue = this.parentValue;
}else {
this.paramsData.parentValue = null;
}
this.retParamsData();//还原查询分页数据
await this.$myhttp.post("/main/dropDowm/" + this.urls, this.paramsData).then((res) => {
if(res.data.code == 2000){
let datalist = res.data.data;
let count = res.data.count;
if(val == 1){
this.listArr = [...datalist];
this.listCp = [...datalist];
this.had_id_one = [...datalist];
if(this.had_id_one && this.had_id_one.length == 1){
this.name = this.had_id_one[0].name;
}
}else {
// 过滤出 this.had_id_one 中那些 id 不在 datalist 中的对象
let filteredDatalist = datalist.filter(item =>
!this.had_id_one.some(dataItem => dataItem.id === item.id)
);
// 合并过滤后的结果
this.listArr = [...filteredDatalist,...this.had_id_one];
this.listCp = [...datalist];
}
}
})
}else {
this.listArr = [];
}
},
}
};
</script>
<style>
.el-select-dropdown__wrap {
max-height: 200px !important;
}
.wb-padding {
padding: 0px;
min-width: 300px;
}
.addBorder {
text-align: center;
border-top: 1px solid rgb(221 221 221);
}
.delBorder {
text-align: center;
border-top: 1px solid rgb(221 221 221);
border-left: 1px solid rgb(221 221 221);
}
</style>
<style scoped>
.scroll-container {
height: 300px; /* 设置一个固定高度来模拟滚动 */
overflow-y: auto; /* 允许垂直滚动 */
}
</style>
在别的页面使用:
1、引入:
components: {
"wb-select-scroll": () => import('@/views/comm/wb-select-scroll.vue'),
},
2、使用:
<el-descriptions size="small" :column="1" border :labelStyle="{ width: '80px', color: '#000000' }"
:contentStyle="{ width: 'calc(100% - 80px)' }">
<el-descriptions-item label="货品名称">
<wb-select-scroll :parentValue.sync="goods.goodsid" urls="goodsList" @selectBackFun="dealSelectBackFun"
placeholders="请选择货品" clearable></wb-select-scroll >
</el-descriptions-item>
</el-descriptions>
重要说明:
parentValue.sync为绑定的变量,必填
:parentValue.sync="goods.goodsid"
urls为请求到后端接口的url,根据自己实际项目调整组合
urls="goodsList"
@selectBackFun点击下拉内容返回的回调函数,根据实际业务处理
@selectBackFun="dealSelectBackFun"