需求场景
有些后端接口是分页并支持模糊搜索的,我们需要编写代码用select组件去展示列表数据,并且支持搜索能力(用select组件消费这个接口)
源码
html:
html
<某某组件库-select
v-model="formParams.model.sceneModelList"
placeholder="请选择界面"
:filterable="true"
:remote="true"
:debounce="500"
multiple
:remote-method="getSceneModelListByKeyword"
:loading="isGettingSceneModelList"
value-key="id"
:formatter="formatterDataModel"
@blur="onBlur"
@scroll-bottom="selectScrollBottom"
>
<某某组件库-option
v-for="item in sceneModelList"
:key="item.id"
:label="item.name"
:value="item"
/>
<某某组件库-loading v-if="isScrollLoading" size="small" />
</某某组件库-select>
js:
typescript
// 数据模型select分页相关
// 数据模型select滚动触底时触发分页接口访问
const isGettingSceneModelList = ref(true);
const sceneModelList = ref<TDataModelList>([]);
const isScrollLoading = ref(false);
const noMoreData = ref(false);
let page = 1;
const size = 20;
let keyword = '';
// 滚动触底回调
const selectScrollBottom = async () => {
if (noMoreData.value) return;
isScrollLoading.value = true;
page += 1;
const params = {
page,
size,
keyword,
};
const [error, res] = await to(getSceneModelList(params));
if (error) {
return Message({ message: error.message, type: 'error' });
}
if (res.data.items === null || res.data.items.length === 0) {
isScrollLoading.value = false;
noMoreData.value = true;
return;
}
sceneModelList.value = [...sceneModelList.value, ...(res.data.items || [])];
isScrollLoading.value = false;
};
// 远程搜索回调
const getSceneModelListByKeyword = async (input = '') => {
page = 1;
keyword = input;
isGettingSceneModelList.value = true;
const params = {
page,
size,
keyword,
};
const [error, res] = await to(getSceneModelList(params));
if (error) {
return Message({ message: error.message, type: 'error' });
}
sceneModelList.value = res.data.items || [];
isGettingSceneModelList.value = false;
};
getSceneModelListByKeyword();
// 失去焦点时清空搜索关键词
const onBlur = () => {
keyword = '';
};
// 数据模型格式化
const formatterDataModel = ({ value }: { value: IDataModelItem }) => {
return value?.name;
};
实现select适配分页
比如我们假定一页的size
为20,我们的目标就是最初select请求第一页(page = 1
)的20条数据,后面每次滚动触底时再请求第二页、第三页...的数据,直至后端数据全部被请求完毕,也就是page
过大导致返回数据为空时,我们不再触发请求的逻辑。
我们暂时不考虑搜索能力的实现,所以暂时把搜索参数keyword
置空,编写如下代码:
typescript
// 数据模型select滚动触底时触发分页接口访问
const isGettingSceneModelList = ref(true); // 整个select的loading控制变量
const sceneModelList = ref<TDataModelList>([]); // 存放下拉列表
const isScrollLoading = ref(false); // loading控制变量(在select-option最下面放了一个loading)
const noMoreData = ref(false); // 标识后端是否已经没有数据了
let page = 1;
const size = 20;
let keyword = '';
// 滚动触底回调, 对应select的scroll-bottom事件
const selectScrollBottom = async () => {
if (noMoreData.value) return; // 如果noMoreData为true, 则没必要触发函数体执行return即可
isScrollLoading.value = true; // 展示最下面的loading
page += 1; // 请求下一页的数据(追加至List列表)
const params = {
page,
size,
keyword,
};
const [error, res] = await to(getSceneModelList(params));
if (error) {
return Message({ message: error.message, type: 'error' });
}
// 如果后端无数据返回, noMoreData置为true
if (res.data.items === null || res.data.items.length === 0) {
isScrollLoading.value = false; // 下拉列表最下面的loading结束
noMoreData.value = true;
return;
}
// 追加后端返回的数据至list
sceneModelList.value = [...sceneModelList.value, ...(res.data.items || [])];
isScrollLoading.value = false; // 下拉列表最下面的loading结束
};
实现select支持远程搜索
说白了我们需要维护用户的输入,即keyword
变量,当搜索时更新keyword
这个请求参数,失去焦点时清空即可。
搭配select
组件的filterable = "true"
即开启输入搜索, remote = "true"
(默认为false
,即针对列表内的数据进行搜索)即调用remote-method
指定的方法进行搜索(或者说用户输入时具体执行的逻辑全由remote-method
决定了),remote-method = "getSceneModelListByKeyword"
即用户输入时执行的回调函数。
编写getSceneModelListByKeyword
函数如下:
typescript
// 远程搜索回调
const getSceneModelListByKeyword = async (input = '') => {
page = 1;
keyword = input; // 更新外层的keyword, 因为此时的分页请求也是基于当前用户输入的keyword进行搜索的
isGettingSceneModelList.value = true; // 搜索时整个select置为loading态
const params = {
page,
size,
keyword,
};
const [error, res] = await to(getSceneModelList(params));
if (error) {
return Message({ message: error.message, type: 'error' });
}
// 更新list为搜索到的数据
sceneModelList.value = res.data.items || [];
isGettingSceneModelList.value = false;
};
// 组件挂载之前调用一次getSceneModelListByKeyword, 获取初始数据, 其实也可以借助触底方法来获取初始数据, 但是触底方法控制的loading是下拉列表最底部的, 而非整个select的loading, 所以调用远程搜索方法获取初始数据最为合适
getSceneModelListByKeyword();
// 失去焦点时清空搜索关键词keyword
const onBlur = () => {
keyword = '';
};