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 = '';
};
相关推荐
Redstone Monstrosity12 分钟前
字节二面
前端·面试
东方翱翔19 分钟前
CSS的三种基本选择器
前端·css
Fan_web42 分钟前
JavaScript高级——闭包应用-自定义js模块
开发语言·前端·javascript·css·html
yanglamei19621 小时前
基于GIKT深度知识追踪模型的习题推荐系统源代码+数据库+使用说明,后端采用flask,前端采用vue
前端·数据库·flask
千穹凌帝1 小时前
SpinalHDL之结构(二)
开发语言·前端·fpga开发
冯宝宝^1 小时前
基于mongodb+flask(Python)+vue的实验室器材管理系统
vue.js·python·flask
dot.Net安全矩阵1 小时前
.NET内网实战:通过命令行解密Web.config
前端·学习·安全·web安全·矩阵·.net
Hellc0071 小时前
MacOS升级ruby版本
前端·macos·ruby
前端西瓜哥1 小时前
贝塞尔曲线算法:求贝塞尔曲线和直线的交点
前端·算法
又写了一天BUG1 小时前
npm install安装缓慢及npm更换源
前端·npm·node.js