SelectSearch 组件
<template>
<a-select
v-model:value="valueId"
v-bind="getBindValues"
show-search
:filter-option="false"
:options="options"
@search="handleSearch"
@popupScroll="popupScroll"
@change="handelChange"
style="min-width: 200px"
>
<template #notFoundContent>
<a-spin size="small" v-if="fetching" />
<a-empty v-else />
</template>
<template #dropdownRender="{ menuNode: menu }" v-if="isSelectAll">
<v-nodes :vnodes="menu" />
<a-divider style="margin: 4px 0" />
<div
style="
padding: 4px 8px;
cursor: pointer;
display: flex;
justify-content: space-between;
width: 100%;
"
@mousedown="(e) => e.preventDefault()"
>
<a-button type="link" @click="selectAll">全选</a-button>
<a-button type="link" @click="clearAll">清空</a-button>
</div>
</template>
<template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
<slot :name="item" v-bind="data || {}"></slot>
</template>
</a-select>
</template>
<script>
import { defineComponent, reactive, toRefs } from 'vue';
import { Select } from 'ant-design-vue';
import { debounce } from 'lodash-es';
export default defineComponent({
name: 'SelectSearch',
components: {
VNodes: (_, { attrs }) => {
return attrs.vnodes;
},
},
props: Object.assign(Select.props, {
data: {
type: Function,
default: () => {},
},
fieldNames: {
type: Object,
default: () => {
return { label: 'label', value: 'value', options: 'options' };
},
},
allowClear: {
type: Boolean,
default: true,
},
pager: {
type: Object,
default: {},
},
searchKey: {
type: String,
default: '',
},
isSelectAll: {
type: Boolean,
default: false,
},
}),
setup(props, { emit }) {
const getBindValues = computed(() => {
let propsData = {
class: 's-select-warp',
...props,
};
delete propsData['onUpdate:value'];
return propsData;
});
const valueId = computed({
get: () => props.value,
set: (val) => {
emit('update:value', val);
},
});
const state = reactive({
options: [],
fetching: false,
total: 0,
fetchId: 0,
parmas: {},
lastFetchId: 0,
pager: props.pager || {},
});
// 重置
const init = () => {
if (props.searchKey) state.parmas[props.searchKey] = '';
if (state.pager.pageNum) state.pager.pageNum = 1;
state.options = [];
getList();
};
// 接口获取数据方法
const getList = () => {
state.fetching = true;
if (props.data) {
const result = props.data(Object.assign(state.pager, state.parmas));
result.then((res) => {
if (res.code === 0) {
state.total = res.total;
state.options = state.options.concat(res.data);
state.fetching = false;
}
});
}
};
// 有分页时下拉加载数据
const popupScroll = (e) => {
if (state.pager.pageNum) {
const { target } = e;
const scrllHeight = target.scrollHeight - target.scrollTop;
const clientHeight = target.clientHeight;
// 下拉框不下拉的时候
if (scrllHeight === 0 && clientHeight === 0) {
state.pager.pageNum = 1;
} else if (scrllHeight - clientHeight == 0) {
// 下拉到底部时
if (state.options.length < state.total) {
// 如果滑到底部,则加载下一页
state.pager.pageNum++;
getList();
}
}
}
};
// 清空时重新获取数据
const handelChange = (value) => {
if (!value) {
init();
}
};
// 节流查询
const handleSearch = debounce((value) => {
state.lastFetchId += 1;
if (state.pager.pageNum) state.pager.pageNum = 1;
if (props.searchKey) state.parmas[props.searchKey] = value; // 查询Key
state.fetchId = state.lastFetchId;
state.options = [];
if (state.fetchId !== state.lastFetchId) {
return;
}
getList();
}, 800);
// 全选
const selectAll = () => {
let vals = state.options.map((item) => item[props.fieldNames.value]);
emit('update:value', vals);
};
// 清空
const clearAll = () => {
emit('update:value', []);
};
getList();
return {
...toRefs(state),
handleSearch,
handelChange,
getList,
popupScroll,
getBindValues,
valueId,
selectAll,
clearAll,
};
},
});
/**
* selectDictTypeList : 调用后台接口方法 Promise
* 无分页
* 有分页
* 指定label/key
* 全选/清空
*/
</script>
<a-form-item label="权限字典名" name="dictHeader">
<SelectSearch
:data="selectDictTypeList"
v-model:value="item.dictHeader"
searchKey="dictName"
:fieldNames="{
label: 'dictName',
value: 'dictType',
options: 'options',
}"
@change="dictSelect(index)"
>
<template #option="row">
{{ row.dictName }} + '自定义'
</template>
</SelectSearch>
</a-form-item>