ElSelect 多选远程搜索选项丢失问题

发布于:2025-06-21 ⋅ 阅读:(16) ⋅ 点赞:(0)
1.  问题现象描述

我们在使用 Element Plus 中的 ElSelect 组件时,常会使用 remote + multiple 的组合来实现“远程搜索 + 多选下拉框”。然而:

选中第一个选项正常

搜索新关键词,选中第二个选项后,第一个选项会莫名其妙被清除(即选项列表和选中值都被重置)

2. 复现代码
<ElSelect
  v-model="selectedIds"
  remote
  filterable
  multiple
  remote-method="getCompanyList"
  placeholder="请选择公司"
>
  <ElOption
    v-for="item in companyList"
    :key="item.id"
    :label="item.name"
    :value="item.id"
  />
</ElSelect>
const selectedIds = ref([]);
const companyList = ref([]);

const getCompanyList = async (query) => {
  const res = await fetchCompanyList(query);
  companyList.value = res.items; // ❌ 直接覆盖
};
3. 问题产生的原因(底层机制)

Element Plus 中 remote 模式的机制,来自官方文档说明(见 ElSelect 文档 - 远程搜索)。

remote 模式下,组件只会根据传入的 ElOption 列表,来匹配 v-model 中的值用于展示。

若当前选中项不在 options(即下拉数据)中,组件无法渲染标签文字,甚至会清除该项。

⚠ 问题根本原因:

每次搜索时,覆盖了 companyList,这个列表是 ElOption 渲染数据源。

companyList.value = res.items // 会丢掉之前选中的项

于是:

  • 当前已选的第一个公司 ID 不再存在于新的 companyList 中;
  • ElSelect 组件找不到对应的 label,只能移除该 ID;
  • 结果:选中第二项后,第一个值直接“消失”了。

本质上,这是一个“视图与状态解耦”的问题:

  • v-model 存的是“选中值”;
  • 但 UI 渲染所依赖的 ElOption 是当前 companyList;
  • 如果 ElOption 没有包含 v-model 中的项,则 UI 会清除它。
4. 解决方法:合并搜索结果 + 已选项 + 去重

修改 getCompanyList

const getCompanyList = async (val) => {
  const res = await fetchCompanyList(val);
  const newList = res.items.map(item => ({ ...item, id: String(item.id) }));

  const selectedIds = selectedIds.value.map(String);
  const selectedOptions = companyList.value.filter(item => selectedIds.includes(String(item.id)));

  const merged = Array.from(
    new Map([...selectedOptions, ...newList].map(item => [item.id, item])).values()
  );

  companyList.value = merged;
};

关键点:

要点 描述
保留原选中项 防止它们在新 options 中丢失
合并数据 旧选项 + 新搜索结果
去重

使用 Map 以 id 为键去重

强制类型一致

id 全部转成 String,避免 '123' !== 123

5. 其他解决方法 

1、使用 value-key 并绑定对象数组

<ElSelect
  v-model="selectedCompanyObjects"
  value-key="id"
>
  <ElOption :value="company" v-for="company in companyList" :label="company.name" :key="company.id" />
</ElSelect>

但这种方式需要将 v-model 中的数据改为完整对象数组 [Company],并维护一致性,略显复杂。 

2、取消 remote 模式,前端本地过滤(适用于小数据量)

如果公司列表固定不大,可以一次性加载全部数据,使用 filterable 本地过滤:

<ElSelect filterable :filter-method="customFilter" />

网站公告

今日签到

点亮在社区的每一天
去签到