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 = '';
};
相关推荐
GuWenyue1 小时前
放弃云端API!一套React+WebGPU本地LLM方案,零数据上传、离线可用
前端·人工智能·前端框架
原则猫2 小时前
函数/变量提升
前端
独泪了无痕3 小时前
Vue3 Hooks使用实战解析
前端·vue.js
shawxlee4 小时前
vue3在public下封装config.js自定义配置动态数据,可在打包后直接修改,方便后端部署及后续维护
前端·javascript·经验分享·vue·团队开发·js·项目优化
用户059540174465 小时前
把记忆存储的回归测试从手工换成 Playwright + GitHub Actions,线上缺陷降低 80%
前端·css
触底反弹5 小时前
🚀 从 DOM0 级到 React 合成事件:前端事件监听的 20 年演进史
前端·react.js·面试
kyriewen5 小时前
我给前端项目的接口请求套了6层保护——才发现以前一直在裸奔
前端·javascript·面试
颜酱5 小时前
04 | 召回前置准备:搭好召回所需的四个数据库
前端·人工智能·后端
郝亚军7 小时前
如何安装webstorm、Node.js和vue CLI
前端·javascript·vue.js
IT_陈寒7 小时前
React的useEffect依赖项把我坑惨了
前端·人工智能·后端