<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, watch, useAttrs } 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 attrs = useAttrs();
const getBindValues = computed(() => {
let propsData = {
class: 's-select-warp',
...props,
};
return propsData;
});
const valueId = computed({
get: () => {
handelMoreVmodel(props.value) // 处理多个v-model
return props.value;
},
set: (val) => {
emit('update:value', val);
},
});
const state = reactive({
options: [],
fetching: false,
total: 0,
fetchId: 0,
parmas: {},
lastFetchId: 0,
pager: props.pager || {},
});
// 多个vmodel处理方法
const handelMoreVmodel = (val) => {
let arr = [];
for (let i in attrs) {
if (i.toString().indexOf('onUpdate') > -1) {
arr.push(i);
}
}
// 找到 val 对应的item
let item = state.options.find((item) => {
return item[props.fieldNames.value] === val;
});
arr.forEach((s) => {
const key = s.split(':')[1];
if (item) {
emit(`update:${key}`, item[key] || '');
} else {
emit(`update:${key}`, '');
}
});
};
// 重置
const reset = () => {
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) {
reset();
}
};
// 节流查询
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
* 无分页 <SelectSearch:data="selectDictTypeList"v-model:value="testValue1"></SelectSearch>
* 有分页 <SelectSearch :data="selectDictTypeList" v-model:value="testValue1" :pager="{ pageSize:1,pageNum:10 }"></SelectSearch>
* 指定label/key <SelectSearch :data="selectDictTypeList" v-model:value="testValue1" :fieldNames="{label: 'dictName', value: 'dictType', options: 'options'}"></SelectSearch>
* 全选/清空 <SelectSearch :data="selectDictTypeList" v-model:value="testValue1" mode="multiple" :isSelectAll="true"></SelectSearch>
* slot <SelectSearch :data="selectDictTypeList" v-model:value="item.dictHeader">
<template #option="row">
{{ row.dictName }}
</template>
</SelectSearch>
* 多个v-model(单选)
<SelectSearch
:data="selectDictTypeList"
v-model:value="item.dictHeader"
v-model:dictName="item.dictName"
v-model:status="item.status"
searchKey="dictName"
:fieldNames="{
label: 'dictName',
value: 'dictType',
options: 'options',
}"
@change="dictSelect(index)"
>
</SelectSearch>
*/
</script>
antdesign-vue 3.0 二次封装 a-select 支持多个v-model
最新推荐文章于 2024-06-20 09:46:43 发布