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 = '';
};
相关推荐
嘗_4 分钟前
暑期前端训练day5
前端
uncleTom66610 分钟前
前端布局利器:rem 适配全面解析
前端
谦哥13 分钟前
Claude4免费Vibe Coding!目前比较好的Cursor替代方案
前端·javascript·claude
LEAFF24 分钟前
如何 测试Labview是否返回数据 ?
前端
Spider_Man26 分钟前
🚀 从阻塞到丝滑:React中DeepSeek LLM流式输出的实现秘籍
前端·react.js·llm
心在飞扬27 分钟前
理解JS事件环(Event Loop)
前端·javascript
盏茶作酒2944 分钟前
打造自己的组件库(一)宏函数解析
前端·vue.js
山有木兮木有枝_1 小时前
JavaScript 设计模式--单例模式
前端·javascript·代码规范
一大树1 小时前
Vue3 开发必备:20 个实用技巧
前端·vue.js
颜渊呐1 小时前
uniapp中APPwebview与网页的双向通信
前端·uni-app