Skip to content

自动完成 AutoComplete

输入框自动完成功能

何时使用

  • 需要一个输入框而不是选择器
  • 需要输入建议/辅助提示

Select 的区别是:

  • AutoComplete 是一个带提示的输入框,用户可以自由输入,关键词是辅助输入
  • Select 是在限定的可选项中进行选择,关键词是选择

基本使用

Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const value1 = ref('')
const options1 = ref<{ value: string }[]>([])
function mockVal(str: string, repeat = 1): { value: string } {
  return { value: str.repeat(repeat) }
}
function onSearch1(searchText: string) {
  options1.value = !searchText ? [] : [mockVal(searchText), mockVal(searchText, 2), mockVal(searchText, 3)]
}
</script>
<template>
  <AutoComplete
    v-model:value="value1"
    :options="options1"
    :width="200"
    placeholder="input here"
    @search="onSearch1"
  />
</template>

自定义选项

Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const value2 = ref('')
const options2 = ref<{ value: string }[]>([])
function onSearch2(val: string) {
  options2.value =
    !val || val.includes('@') ? [] : ['gmail.com', '163.com', 'qq.com'].map((domain) => ({ value: `${val}@${domain}` }))
}
</script>
<template>
  <AutoComplete
    v-model:value="value2"
    :options="options2"
    :width="200"
    placeholder="input here"
    @search="onSearch2"
  >
    <template #option="{ value: val }">
      {{ val.split('@')[0] }} @
      <span style="font-weight: bold">{{ val.split('@')[1] }}</span>
    </template>
  </AutoComplete>
</template>

自定义输入组件

Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const value3 = ref('')
const options3 = ref<{ value: string }[]>([])
function onSearch3(val: string) {
  options3.value = !val ? [] : [{ value: val }, { value: val + val }, { value: val + val + val }]
}
function onSelect3(value: string) {
  console.log('onSelect', value)
}
</script>
<template>
  <AutoComplete
    v-model:value="value3"
    :options="options3"
    :width="200"
    @search="onSearch3"
    @select="onSelect3"
  >
    <Textarea placeholder="input here" style="height: 50px" />
  </AutoComplete>
</template>

不区分大小写

Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const value4 = ref('')
const options4 = ref<{ value: string }[]>([
  { value: 'Burns Bay Road' },
  { value: 'Downing Street' },
  { value: 'Wall Street' }
])
function filterOption(input: string, option: unknown): boolean {
  const value = (option as { value?: string }).value ?? ''
  return value.toUpperCase().includes(input.toUpperCase())
}
</script>
<template>
  <AutoComplete
    v-model:value="value4"
    :options="options4"
    :width="200"
    placeholder="input here"
    :filter-option="filterOption"
  />
</template>

查询模式 - 确定类目

Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
import { UserOutlined } from '@ant-design/icons-vue'
const value5 = ref('')
const options5 = ref([
  {
    value: 'Libraries',
    options: [
      { value: 'VueAmazingUI', count: 10000 },
      { value: 'VueAmazingUI 组件', count: 10600 }
    ]
  },
  {
    value: 'Solutions',
    options: [
      { value: 'VueAmazingUI 组件 FAQ', count: 60100 },
      { value: 'VueAmazingUI FAQ', count: 30010 }
    ]
  },
  {
    value: 'Articles',
    options: [{ value: 'VueAmazingUI 设计语言', count: 100000 }]
  },
  {
    value: 'all'
  }
])
</script>
<template>
  <AutoComplete
    v-model:value="value5"
    :options="options5"
    :width="250"
    :dropdown-match-select-width="500"
  >
    <template #option="item">
      <template v-if="item.options">
        <div style="display: flex; justify-content: space-between; align-items: center">
          <span>{{ item.value }}</span>
          <a href="https://www.google.com/search?q=vue-amazing-ui" target="_blank" rel="noopener noreferrer">
            more
          </a>
        </div>
      </template>
      <template v-else-if="item.value === 'all'">
        <a href="https://www.google.com/search?q=vue-amazing-ui" target="_blank" rel="noopener noreferrer">
          View all results
        </a>
      </template>
      <template v-else>
        <div style="display: flex; justify-content: space-between; align-items: center">
          <span style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap">{{ item.value }}</span>
          <span style="flex-shrink: 0; padding-left: 8px">
            <UserOutlined />
            {{ item.count }}
          </span>
        </div>
      </template>
    </template>
    <InputSearch placeholder="input here" size="large" />
  </AutoComplete>
</template>

查询模式 - 不确定类目

Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
interface SearchOption {
  query: string
  category: string
  value: string
  count: number
}
const value6 = ref('')
const options6 = ref<SearchOption[]>([])
function onSelect6(value: string) {
  console.log('onSelect', value)
}
function getRandomInt(max: number, min = 0) {
  return Math.floor(Math.random() * (max - min + 1)) + min
}
function searchResult(query: string): SearchOption[] {
  return new Array(getRandomInt(5))
    .join('.')
    .split('.')
    .map((_item, idx) => ({
      query,
      category: `${query}${idx}`,
      value: `${query}${idx}`,
      count: getRandomInt(200, 100)
    }))
}
function onSearch6(val: string) {
  options6.value = val ? searchResult(val) : []
}
</script>
<template>
  <AutoComplete
    v-model:value="value6"
    :options="options6"
    :width="300"
    @select="onSelect6"
    @search="onSearch6"
  >
    <template #option="item">
      <div style="display: flex; justify-content: space-between">
        <span>
          Found {{ item.query }} on
          <a :href="`https://s.taobao.com/search?q=${item.query}`" target="_blank" rel="noopener noreferrer">
            {{ item.category }}
          </a>
        </span>
        <span>{{ item.count }} results</span>
      </div>
    </template>
    <InputSearch size="large" placeholder="input here" allow-clear :search-props="{ type: 'primary' }" />
  </AutoComplete>
</template>

自定义状态



Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const value7 = ref('')
const value8 = ref('')
const options7 = ref<{ value: string }[]>([])
function mockVal(str: string, repeat = 1): { value: string } {
  return { value: str.repeat(repeat) }
}
function onSearch7(searchText: string) {
  options7.value = !searchText ? [] : [mockVal(searchText), mockVal(searchText, 2), mockVal(searchText, 3)]
}
function onSelect7(value: string) {
  console.log('onSelect', value)
}
function onClear() {
  console.log('onClear')
}
</script>
<template>
  <AutoComplete
    v-model:value="value7"
    :options="options7"
    :width="200"
    placeholder="input here"
    status="error"
    @search="onSearch7"
    @select="onSelect7"
  />
  <br />
  <br />
  <AutoComplete
    v-model:value="value8"
    :options="options7"
    :width="200"
    placeholder="input here"
    status="warning"
    allow-clear
    @search="onSearch7"
    @select="onSelect7"
    @clear="onClear"
  />
</template>

无边框

Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const value9 = ref('')
const options7 = ref<{ value: string }[]>([])
function mockVal(str: string, repeat = 1): { value: string } {
  return { value: str.repeat(repeat) }
}
function onSearch7(searchText: string) {
  options7.value = !searchText ? [] : [mockVal(searchText), mockVal(searchText, 2), mockVal(searchText, 3)]
}
</script>
<template>
  <AutoComplete
    v-model:value="value9"
    :options="options7"
    :width="200"
    placeholder="border less"
    :bordered="false"
    @search="onSearch7"
  />
</template>

自定义清除按钮



Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const value10 = ref('')
const value11 = ref('')
const options7 = ref<{ value: string }[]>([])
function mockVal(str: string, repeat = 1): { value: string } {
  return { value: str.repeat(repeat) }
}
function onSearch7(searchText: string) {
  options7.value = !searchText ? [] : [mockVal(searchText), mockVal(searchText, 2), mockVal(searchText, 3)]
}
</script>
<template>
  <AutoComplete
    v-model:value="value10"
    :options="options7"
    :width="200"
    placeholder="Clearable"
    allow-clear
    @search="onSearch7"
  />
  <br />
  <br />
  <AutoComplete
    v-model:value="value11"
    :options="options7"
    :width="200"
    placeholder="Customized clear icon"
    allow-clear
    @search="onSearch7"
  >
    <template #clearIcon>
      <svg
        focusable="false"
        data-icon="close"
        width="1em"
        height="1em"
        fill="currentColor"
        aria-hidden="true"
        viewBox="64 64 896 896"
      >
        <path
          d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
        ></path>
      </svg>
    </template>
  </AutoComplete>
</template>

三种尺寸

small
middle
large


Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const size = ref('middle')
const sizeOptions = [
  {
    label: 'small',
    value: 'small'
  },
  {
    label: 'middle',
    value: 'middle'
  },
  {
    label: 'large',
    value: 'large'
  }
]
const valueS = ref('')
const optionsD = ref([
  {
    label: '北京市',
    value: '北京市'
  },
  {
    label: '上海市',
    value: '上海市',
    disabled: true
  },
  {
    label: '纽约市',
    value: '纽约市'
  }
])
</script>
<template>
  <Radio :options="sizeOptions" v-model:value="size" button button-style="solid" />
  <br />
  <br />
  <AutoComplete
    v-model:value="valueS"
    :options="optionsD"
    :width="200"
    placeholder="input here"
    :size="size"
  />
</template>

禁用

Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const valueD = ref('')
const optionsD = ref([
  {
    label: '北京市',
    value: '北京市'
  },
  {
    label: '上海市',
    value: '上海市',
    disabled: true
  },
  {
    label: '纽约市',
    value: '纽约市'
  }
])
</script>
<template>
  <AutoComplete v-model:value="valueD" :options="optionsD" :width="200" placeholder="input here" disabled />
</template>

禁用选项

Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const valueDD = ref('')
const optionsD = ref([
  {
    label: '北京市',
    value: '北京市'
  },
  {
    label: '上海市',
    value: '上海市',
    disabled: true
  },
  {
    label: '纽约市',
    value: '纽约市'
  }
])
</script>
<template>
  <AutoComplete v-model:value="valueDD" :options="optionsD" :width="200" placeholder="input here" />
</template>

字符串数组数据源

Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const valueArr = ref('')
const optionsArr = ['Apple', 'Banana', 'Cherry', 'Durian', 'Elderberry', 'Fig', 'Grape']
</script>
<template>
  <AutoComplete v-model:value="valueArr" :options="optionsArr" :width="200" placeholder="input here" />
</template>

分组数据源

Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const valueGroup = ref('')
const optionsGroup = ref([
  {
    label: '亚洲',
    options: [
      { label: '北京', value: 'beijing' },
      { label: '东京', value: 'tokyo' }
    ]
  },
  {
    label: '欧洲',
    options: [
      { label: '伦敦', value: 'london' },
      { label: '巴黎', value: 'paris' }
    ]
  },
  {
    label: '美洲',
    options: [
      { label: '纽约', value: 'newyork' },
      { label: '旧金山', value: 'sanfrancisco' }
    ]
  }
])
</script>
<template>
  <AutoComplete v-model:value="valueGroup" :options="optionsGroup" :width="200" placeholder="input here" />
</template>

键盘/悬浮回填

键盘 导航选项时会回填选中项到输入框中,按 Enter 确认选中,按 Esc 还原输入,悬浮选项同样会回填。


Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const valueBackfill = ref('')
const optionsBackfill = ['Apple', 'Banana', 'Cherry', 'Durian']
</script>
<template>
  <AutoComplete
    v-model:value="valueBackfill"
    :options="optionsBackfill"
    :width="200"
    placeholder="input here"
    backfill
  />
</template>

受控展开

通过 open 控制面板显隐,配合 dropdownVisibleChange 事件使用。


展开下拉
Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const valueOpen = ref('')
const openControlled = ref(false)
const optionsOpen = ['Option 1', 'Option 2', 'Option 3']
</script>
<template>
  <Space>
    <AutoComplete
      v-model:value="valueOpen"
      :options="optionsOpen"
      :open="openControlled"
      :width="200"
      placeholder="input here"
      @dropdown-visible-change="openControlled = $event"
    />
    <Button type="primary" @click="openControlled = !openControlled">{{ openControlled ? '收起' : '展开' }}下拉</Button>
  </Space>
</template>

默认展开

初始即展开面板,区别于受控的 open,初始值生效后不受外部状态控制。


Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const valueDefaultOpen = ref('')
const optionsOpen = ['Option 1', 'Option 2', 'Option 3']
</script>
<template>
  <AutoComplete
    v-model:value="valueDefaultOpen"
    :options="optionsOpen"
    :width="200"
    placeholder="input here"
    default-open
  />
</template>

关闭默认高亮首项

Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const valueActiveFirst = ref('')
const optionsOpen = ['Option 1', 'Option 2', 'Option 3']
</script>
<template>
  <AutoComplete
    v-model:value="valueActiveFirst"
    :options="optionsOpen"
    :width="200"
    placeholder="input here"
    :default-active-first-option="false"
  />
</template>

下拉面板宽度

通过 dropdownMatchSelectWidth 指定面板宽度,空间不足时自动调整对齐。


Show Code
vue
<script setup lang="ts">
import { ref } from 'vue'
const valueMatchWidth = ref('')
const optionsMatchWidth = ['一个较长的选项文本 A', '一个较长的选项文本 B', '一个较长的选项文本 C']
</script>
<template>
  <AutoComplete
    v-model:value="valueMatchWidth"
    :options="optionsMatchWidth"
    :width="200"
    placeholder="面板宽 300"
    :dropdown-match-select-width="300"
  />
</template>

APIs

AutoComplete

参数说明类型默认值
options自动完成的数据源(string | number | Option | GroupOption)[][]
value
v-model
当前输入的值stringundefined
placeholder默认占位文本stringundefined
disabled是否禁用booleanfalse
width自动完成宽度,单位 pxstring | number'100%'
size自动完成大小'small' | 'middle' | 'large''middle'
allowClear是否支持清除,有值时即显示清除图标booleanfalse
autofocus是否自动获取焦点booleanfalse
backfill使用键盘选择选项的时候把选中项回填到输入框中booleanfalse
bordered是否有边框booleantrue
defaultActiveFirstOption是否默认高亮第一个选项booleantrue
defaultOpen是否默认展开下拉菜单booleanfalse
open是否展开下拉菜单(受控)booleanundefined
status设置校验状态'error' | 'warning'undefined
dropdownMatchSelectWidth下拉菜单和选择器同宽,为数字时指定下拉菜单宽度(单位 px),当左右空间都不足以容纳时面板自动调整对齐boolean | numbertrue
dropdownMenuStyle下拉菜单自定义样式CSSPropertiesundefined
popupClassName下拉菜单的 className 属性stringundefined
to下拉面板挂载的容器节点,可选:元素标签名 (例如 'body') 或者元素本身,false 会待在原地string | HTMLElement | false'body'
filterOption根据输入项进行筛选:
  • 默认为 false 时不筛选,显示全部数据源,由用户在 search 事件中远程更新 options
  • 当其为 true 时,筛选每个选项的文本字段 label 是否包含输入项,包含返回 true,反之返回 false
  • 当其为函数 Function 时,接受 inputValue option 两个参数,当 option 符合筛选条件时,应返回 true,反之则返回 false
  • boolean | Functionfalse

    Option Type

    名称说明类型默认值
    value唯一的 value 值string | numberundefined
    label显示的 label 值stringundefined
    disabled?是否禁用选项booleanfalse

    GroupOption Type

    名称说明类型默认值
    options子选项,存在该字段即视为分组(string | number | Option)[]undefined
    label?分组名(显示为分组标题)stringundefined
    value?分组值string | numberundefined

    Events

    名称说明类型
    search输入内容变化时回调(value: string) => void
    select选中选项时回调(value: string | number, option: Option) => void
    change选项值改变后的回调(value: string) => void
    focus获得焦点时的回调() => void
    blur失去焦点时的回调() => void
    clear清除时的回调() => void
    openChange下拉菜单展开收起的回调(open: boolean) => void
    dropdownVisibleChange下拉菜单展开收起的回调(open: boolean) => void

    Slots

    名称说明参数
    option自定义选项内容Option
    clearIcon自定义清除图标-
    default自定义输入组件(替代原生 input 元素)-

    Methods

    名称说明类型
    focus获取输入框焦点() => void
    blur使输入框失去焦点() => void

    Released under the MIT License.