You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue+Quasar q-select多选组件选中后残留输入文本解决方法

问题描述

项目基于Vue3 + Quasar框架开发,需要实现带服务端自动补全能力的多选选择器:用户输入检索文本时,组件自动向服务端发送请求,拉取匹配输入内容的可选值供用户多选。
现有实现代码如下:

模板部分

<template>
<div class="autocomplete">
  <q-select
    filled
    label="Search"
    v-model="searchTerm"
    use-input
    use-chips
    multiple
    @filter="filterFn"
    @input-value="inputValue"
    @filter-abort="abortFilterFn"
    :options="searchResult"
    style="width: 395px"
    :option-label=""
    option-value="text"
   >
   <template v-slot:no-option>
    <q-item>
      <q-item-section class="text-grey"> No results </q-item-section>
    </q-item>
  </template>
</q-select>
<div>
  <back-button @click="go" data-test="btn-back" forwardicon="arrowright" />
</div>
</template>

脚本部分

<script>
import { defineComponent, ref, computed } from "vue";
import { apiProductService } from "../../models/";
import apiEndPoints from "../../models/api";
import BackButton from "../ui/";
import { useRouter } from "vue-router";
import { useSearchStore } from "../../store/";
import _ from "lodash";
import { debounce } from "quasar";

export default defineComponent({
  name: "search-bar", 
  components: { BackButton },
  setup(props, context) {
    const router = useRouter();
    let searchTerm = ref(null);
    let searchResult = ref([]);
    const search = useSearchStore();

    function filterFn(val, update, abort) {
      if (searchResult.value.length > 0) {
        update();
        return;
      }
      abortFilterFn();
      document.addEventListener('keypress', function(e) {
        true
      });
    }
    function abortFilterFn() {
      // console.log('delayed filter aborted')
    }

    const inputValue = computed(() => debounce(suggestions, 0).bind(this));

    function suggestions(val) {
      if (val) {
        getSearchData(val);
        document.addEventListener('keypress', function(e) {
          true
        });
      } else {
        searchResult.value = [];
      }
    }

    const getSearchData = async (val) => {
      await apiProductService(
        apiEndPoints.GetSearchSuggestions.method,
        apiEndPoints.GetSearchSuggestions.url,
        {
          q: val,
          top: 5,
          suggester: "sg",
        }
      ).then((response) => {
        searchResult.value = response?.data?.suggestions || [];
      });
    };

    return {
      searchTerm,
      searchResult,
      filterFn,
      inputValue
    };
  },
});
</script>

异常现象

输入检索词(例如tes)时,可以正常获取后端返回的匹配结果,但点击选中目标选项后,用户手动输入的检索文本会被错误保存为多选组件的已选值,异常效果如下:
选中后输入文本残留异常截图
需要实现选中选项后自动清除残留输入文本的效果。

问题根因
  • 未单独维护输入框的临时检索值,输入内容与已选值逻辑耦合,Quasar的q-select在use-input + multiple模式下不会主动清空输入框的临时输入内容,需要手动控制
  • @filter逻辑错误,本地过滤逻辑会将用户输入的临时文本加入可选列表,导致输入内容被识别为可选项
  • 防抖逻辑写法错误,将防抖函数包裹在computed中并使用bind(this)不符合Vue3组合式API的写法规范,事件触发时序异常
  • 未监听选项选中事件,没有在选中动作触发后执行清空输入的逻辑
  • 代码中冗余的全局键盘事件监听无实际作用,还可能引发意料之外的逻辑问题
  • option-label属性绑定为空字符串,会导致选项文本渲染逻辑异常
  • 多选模式下searchTerm初始值设为null不符合组件要求,易引发值绑定异常
修复方案
  1. 新增独立的响应式变量存储输入框临时文本,通过v-model:input-value双向绑定输入值,与存储已选结果的searchTerm完全隔离
  2. 修正filter逻辑,直接终止本地过滤,所有选项完全由服务端接口返回的searchResult提供,避免临时输入文本被加入选项列表
  3. 调整防抖函数写法,直接在setup中生成防抖检索方法,防抖时间设置为300ms避免频繁发请求
  4. 监听选项新增事件,选中选项后立刻清空临时输入值、重置搜索结果列表
  5. 修正option-label属性,与option-value对应绑定接口返回的text字段
  6. 删除所有冗余的无效键盘监听代码,补全缺失的页面跳转方法

修复后的完整代码如下:

<template>
<div class="autocomplete">
  <q-select
    filled
    label="Search"
    v-model="searchTerm"
    use-input
    use-chips
    multiple
    @filter="filterFn"
    @filter-abort="abortFilterFn"
    :options="searchResult"
    style="width: 395px"
    option-label="text"
    option-value="text"
    v-model:input-value="inputText"
    @add="onOptionSelected"
   >
   <template v-slot:no-option>
    <q-item>
      <q-item-section class="text-grey"> No results </q-item-section>
    </q-item>
  </template>
</q-select>
<div>
  <back-button @click="go" data-test="btn-back" forwardicon="arrowright" />
</div>
</template>

<script>
import { defineComponent, ref } from "vue";
import { apiProductService } from "../../models/";
import apiEndPoints from "../../models/api";
import BackButton from "../ui/";
import { useRouter } from "vue-router";
import { useSearchStore } from "../../store/";
import { debounce } from "quasar";

export default defineComponent({
  name: "search-bar", 
  components: { BackButton },
  setup(props, context) {
    const router = useRouter();
    // 已选结果,多选模式初始值为空数组
    const searchTerm = ref([]);
    // 服务端返回的搜索结果列表
    const searchResult = ref([]);
    // 输入框临时检索文本
    const inputText = ref('');
    const search = useSearchStore();

    // 终止本地过滤,完全使用服务端返回的选项
    function filterFn(val, update, abort) {
      abort();
    }
    function abortFilterFn() {
      // 过滤中止逻辑可按需扩展
    }

    // 选中选项后清空输入框和临时搜索结果
    function onOptionSelected() {
      inputText.value = '';
      searchResult.value = [];
    }

    // 防抖请求搜索建议,300ms间隔避免频繁触发接口
    const fetchSuggestions = debounce(async (val) => {
      if (!val.trim()) {
        searchResult.value = [];
        return;
      }
      const response = await apiProductService(
        apiEndPoints.GetSearchSuggestions.method,
        apiEndPoints.GetSearchSuggestions.url,
        {
          q: val,
          top: 5,
          suggester: "sg",
        }
      );
      searchResult.value = response?.data?.suggestions || [];
    }, 300);

    // 监听输入值变化触发检索
    function onInputChange(val) {
      inputText.value = val;
      fetchSuggestions(val);
    }

    // 返回按钮跳转逻辑
    function go() {
      router.back();
    }

    return {
      searchTerm,
      searchResult,
      inputText,
      filterFn,
      abortFilterFn,
      onOptionSelected,
      onInputChange,
      go
    };
  },
});
</script>

补充:如果需要支持自定义新标签(即用户输入的内容不在接口返回选项中时,也允许作为已选项添加),可以给q-select添加use-new-value属性;不需要该能力时保持上述代码即可,此时只有接口返回的匹配选项能被选中,不会出现输入文本残留的问题。

内容的提问来源于stack exchange,提问作者Javascript user

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 00:57:21