select组件 消费 模糊搜索/分页接口 编码实践

需求场景

有些后端接口是分页并支持模糊搜索的,我们需要编写代码用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 = '';
};
相关推荐
canonical_entropy5 分钟前
Nop Chaos Flux:百度AMIS之后的下一代低代码渲染引擎
前端·低代码·ai编程
时光足迹24 分钟前
Tiptap 简单编辑器模版
前端·javascript·react.js
JSLove37 分钟前
nginx入门
前端·nginx
时光足迹38 分钟前
ThreeJS之GUI控制器
前端·javascript·three.js
时光足迹39 分钟前
Tiptap编辑器
前端·javascript·react.js
时光足迹43 分钟前
电子书阅读器之笔记高亮(跨段处理)
前端·javascript·react.js
Dabei1 小时前
Android 副屏(Virtual Display)创建与悬浮窗画中画显示实战
前端·架构
RONIN1 小时前
mock模拟后端,生成伪数据接口
vue.js
Hello-Mr.Wang1 小时前
【保姆级教程】MasterGo MCP + Cursor 一键实现 UI 设计稿还原
前端·javascript·vue.js·ai编程