使用场景
用于对输入框提供输入建议,进行自动补全的操作
与可搜索的 Select 组件的区别:
- AutoComplete 本质上是一个增强型的提供了输入建议的 Input 组件,而 Select 是一个选择器
- 点击展开时,Select 会将输入框的值全部清空,而 AutoComplete 会保留上次选中的值
- Select 的已选项渲染(
#selectedItem插槽)可以是任意节点,而 AutoComplete 的renderSelectedItem只允许返回字符串
代码演示
如何引入
<script setup lang="ts">
import { AutoComplete } from '@fibrous/semi-ui-vue';
</script>
基本用法
通过 @search 监听用户输入,将输入建议通过更新 data 传入。用 v-model 保持受控,当输入框变化/选中输入项时 v-model 会更新
代码
<script setup lang="ts">
import { ref } from 'vue';
import { IconSearch } from '@fibrous/semi-icons-vue';
import { AutoComplete } from '@fibrous/semi-ui-vue';
const stringData = ref<string[]>([]);
const value = ref<string | number>('');
const handleStringSearch = (input: string) => {
stringData.value = input ? ['gmail.com', '163.com', 'qq.com'].map((domain) => `${input}@${domain}`) : [];
};
const handleChange = (input: string | number) => {
console.log('onChange', input);
};
</script>
<template>
<AutoComplete v-model="value" :data="stringData" placeholder="搜索... " showClear style="width: 200px" @change="handleChange" @search="handleStringSearch">
<template #prefix>
<IconSearch />
</template>
</AutoComplete>
</template>自定义候选项渲染
需要自定义候选项渲染时,data 可以传入一个对象数组(每个 Object 含有 label、value 两个 key,value 为候选项选中的值,label 为候选项展示的文本,缺省时显示 value)
通过 #item="{ item, index }" 插槽可以自定义候选项的内容:item 是传入的数据项本身,数据项里只放数据,节点写在插槽里
代码
<script setup lang="ts">
import { ref } from 'vue';
import { IconSearch } from '@fibrous/semi-icons-vue';
import { AutoComplete, Avatar } from '@fibrous/semi-ui-vue';
interface ListItem {
name: string;
email: string;
abbr: string;
color: 'amber' | 'indigo' | 'blue' | 'cyan';
value?: string;
label?: string;
}
const list: ListItem[] = [
{ name: '夏可漫', email: 'xiakeman@example.com', abbr: 'XK', color: 'amber' },
{ name: '申悦', email: 'shenyue@example.com', abbr: 'SY', color: 'indigo' },
{ name: '曲晨一', email: 'quchenyi@example.com', abbr: 'CY', color: 'blue' },
{ name: '文嘉茂', email: 'wenjiamao@example.com', abbr: 'JM', color: 'cyan' },
];
const data = ref<ListItem[]>([]);
const handleStringSearch = (value: string) => {
data.value = value ? list.map((item) => ({ ...item, value: item.name, label: item.email })) : [];
};
const renderSelectedItem = (option: ListItem) => option.email;
</script>
<template>
<AutoComplete aria-label="搜索邮箱" :data="data" :renderSelectedItem="renderSelectedItem" showClear style="width: 280px" @search="handleStringSearch">
<template #prefix>
<IconSearch />
</template>
<template #item="{ item }">
<Avatar :color="item.color" size="small">{{ item.abbr }}</Avatar>
<div style="margin-left: 4px">
<div style="font-size: 14px; margin-left: 4px">{{ item.name }}</div>
<div style="margin-left: 4px">{{ item.email }}</div>
</div>
</template>
</AutoComplete>
</template>远程搜索
从 @search 中获取用户输入值,动态更新 data 值
代码
<script setup lang="ts">
import { ref } from 'vue';
import { debounce } from 'es-toolkit';
import { IconSearch } from '@fibrous/semi-icons-vue';
import { IconButton, IconForm, IconInput, IconSelect, IconTable } from '@fibrous/semi-icons-vue/lab';
import { AutoComplete, type AutoCompleteDataItem } from '@fibrous/semi-ui-vue';
interface ListItem {
value: 'select' | 'input' | 'form' | 'button' | 'table';
label: string;
}
const icons = {
select: IconSelect,
input: IconInput,
form: IconForm,
button: IconButton,
table: IconTable,
};
const initList: ListItem[] = [
{ value: 'select', label: '选择器' },
{ value: 'input', label: '输入框' },
{ value: 'form', label: '表单' },
{ value: 'button', label: '按钮' },
{ value: 'table', label: '表格' },
];
const loading = ref(false);
const list = ref(initList);
const handleSearch = (inputValue: string) => {
loading.value = true;
const newList = inputValue ? initList.filter((item) => item.value.includes(inputValue)) : initList;
setTimeout(() => {
list.value = newList;
loading.value = false;
}, 1000);
};
const search = debounce(handleSearch, 200);
const handleSelect = (value: string | number | AutoCompleteDataItem) => {
console.log(value);
};
// Unlike Select, this can return only a string, not a node
const renderSelectedItem = (item: ListItem) => item.value;
</script>
<template>
<AutoComplete aria-label="搜索" :data="list" :loading="loading" :renderSelectedItem="renderSelectedItem" style="width: 250px" @search="search" @select="handleSelect">
<template #prefix>
<IconSearch />
</template>
<template #item="{ item }">
<div style="display: flex; align-items: center">
<div style="font-size: 32px">
<component :is="icons[item.value]" />
</div>
<div style="margin-left: 12px">
<p>{{ item.value }}</p>
<p>{{ item.label }}</p>
</div>
</div>
</template>
</AutoComplete>
</template>尺寸
通过设置 size 可设置输入框尺寸,可选small,default(默认),large
代码
<script setup lang="ts">
import { AutoComplete } from '@fibrous/semi-ui-vue';
const data = [1, 2, 3, 4];
</script>
<template>
<div>
<AutoComplete :data="data" placeholder="small" size="small" style="width: 200px" />
<br />
<br />
<AutoComplete :data="data" placeholder="default" size="default" style="width: 200px" />
<br />
<br />
<AutoComplete :data="data" placeholder="large" size="large" style="width: 200px" />
</div>
</template>下拉菜单的位置
通过设置 position 可设置下拉菜单位置,可选值参考 Tooltip position
代码
<script setup lang="ts">
import { ref } from 'vue';
import { AutoComplete } from '@fibrous/semi-ui-vue';
const data = ref<string[]>([]);
const change = (input: string) => {
data.value = input ? ['gmail.com', '163.com', 'qq.com'].map((domain) => `${input}@${domain}`) : [];
};
</script>
<template>
<div>
<AutoComplete :data="data" placeholder="选项菜单在上方显示" position="top" style="width: 200px; margin: 10px" @search="change" />
<AutoComplete :data="data" placeholder="选项菜单在右侧显示" position="rightTop" style="width: 200px; margin: 10px" @search="change" />
</div>
</template>禁用
代码
<script setup lang="ts">
import { AutoComplete } from '@fibrous/semi-ui-vue';
const data = [1, 2, 3, 4];
</script>
<template>
<AutoComplete :data="data" disabled placeholder="禁用下拉菜单" style="width: 200px" />
</template>校验状态
可设置不同校验状态,展示不同样式
代码
<script setup lang="ts">
import { AutoComplete } from '@fibrous/semi-ui-vue';
</script>
<template>
<AutoComplete aria-label="Warning status" defaultValue="ies" validateStatus="warning" />
<br />
<br />
<AutoComplete aria-label="Error status" defaultValue="ies" validateStatus="error" />
<br />
<br />
<AutoComplete aria-label="Default status" defaultValue="ies" />
</template>自定义空内容
可设置自定义展示空内容
代码
<script setup lang="ts">
import { ref } from 'vue';
import { IllustrationNoContent } from '@fibrous/semi-illustrations-vue';
import { AutoComplete, Empty } from '@fibrous/semi-ui-vue';
const data = ref<number[]>([]);
const loading = ref(false);
const fetchData = (input: string) => {
loading.value = true;
setTimeout(() => {
data.value = input ? Array.from({ length: 5 }, () => Math.random()) : [];
loading.value = false;
}, 1000);
};
</script>
<template>
<AutoComplete aria-label="搜索" :data="data" :loading="loading" @search="fetchData">
<template #emptyContent>
<Empty style="padding: 12px; width: 300px">
<template #image>
<IllustrationNoContent style="width: 150px; height: 150px" />
</template>
<template #description>暂无内容</template>
</Empty>
</template>
</AutoComplete>
</template>API 参考
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| modelValue | 输入框的当前值,用 v-model 绑定。传入后为受控;传入 undefined 时按非受控处理。 | string | number | undefined |
| modelModifiers | v-model 修饰符 trim 与 number,由 Vue 在使用 v-model.trim、v-model.number 时传入。 | ModelModifiers | undefined |
| defaultValue | 非受控时输入框的初始值。 | string | number | semiGlobalDefault("AutoComplete", "defaultValue") |
| data | 候选项的数据源,可以是字符串、数字或对象的数组。对象项用 value 作选中值,label 作展示文本(缺省显示 value),disabled、class、style 与 data-* 字段落在对应的候选行上;节点内容用 item 插槽。数组原地修改也会更新列表。 | T[] | semiDefault("AutoComplete", "data", []) as T[] |
| autofocus | 是否在挂载后自动聚焦输入框。 | boolean | semiDefault("AutoComplete", "autofocus", false) |
| autoAdjustOverflow | 下拉面板被遮挡时是否自动调整方向。 | boolean | semiGlobalDefault("AutoComplete", "autoAdjustOverflow") |
| defaultActiveFirstOption | 面板打开后是否默认高亮第一个候选项,此时按 Enter 可直接选中。 | boolean | semiDefault("AutoComplete", "defaultActiveFirstOption", false) |
| defaultOpen | 挂载时是否默认展开下拉面板。 | boolean | semiGlobalDefault("AutoComplete", "defaultOpen") |
| disabled | 是否禁用。 | boolean | semiGlobalDefault("AutoComplete", "disabled") |
| dropdownClassName | 下拉列表的类名,落在 role="listbox" 元素上。 | string | semiGlobalDefault("AutoComplete", "dropdownClassName") |
| dropdownMatchSelectWidth | 下拉面板的最小宽度是否等于输入框宽度。 | boolean | semiDefault("AutoComplete", "dropdownMatchSelectWidth", true) |
| dropdownStyle | 下拉列表的内联样式,落在 role="listbox" 元素上。 | StyleValue | semiGlobalDefault("AutoComplete", "dropdownStyle") |
| insetLabelId | insetLabel 前缀元素的 id,combobox 与 listbox 的名称引用它;不传时组件在渲染 insetLabel 时生成。 | string | undefined |
| loading | 下拉列表是否显示加载动画;为 true 时代替候选项和空内容。 | boolean | semiDefault("AutoComplete", "loading", false) |
| maxHeight | 下拉列表的最大高度,数字按 px 处理。 | string | number | semiDefault("AutoComplete", "maxHeight", 300) |
| motion | 下拉面板显示、隐藏时是否有动画。 | boolean | semiDefault("AutoComplete", "motion", true) |
| selectWithObject | 布尔属性,不是事件:为 true 时,select 事件收到去掉内部字段的数据项对象,而不是选中值。 | boolean | semiDefault("AutoComplete", "selectWithObject", false) |
| placeholder | 输入框的提示文案。 | string | semiGlobalDefault("AutoComplete", "placeholder") |
| position | 下拉面板的位置,可选值同 Tooltip 的 position。未设置时为 bottomLeft,ConfigProvider 的 direction 为 rtl 时为 bottomRight。 | Type"top" | "bottom" | "left" | "right" | "topLeft" | "topRight" | "leftTop" | "leftBottom" | "rightTop" | "rightBottom" | "bottomLeft" | "bottomRight" | "leftTopOver" | "rightTopOver" | "leftBottomOver" | "rightBottomOver" | semiGlobalDefault("AutoComplete", "position") |
| renderSelectedItem | 选中候选项后输入框显示的文本,必须返回字符串;字符串或数字项收到 { value, label },对象项收到数据项本身。保留为函数 prop,因为 foundation 在渲染之外调用它。 | Type((option: AutoCompleteSelectedItem<T>) => string) | undefined |
| showClear | 有内容时是否显示清除按钮。 | boolean | semiDefault("AutoComplete", "showClear", false) |
| size | 输入框尺寸。 | Size | semiDefault("AutoComplete", "size", "default") |
| stopPropagation | 是否阻止下拉面板上的 click、mousedown 与焦点进出事件冒泡。 | string | boolean | semiDefault("AutoComplete", "stopPropagation", true) |
| validateStatus | 校验状态,只影响展示样式。 | "default" | "error" | "warning" | "success" | semiDefault("AutoComplete", "validateStatus", "default") |
Events
| 事件 | 说明 | 回调参数 |
|---|---|---|
| update:modelValue | 输入框内容变化或选中候选项时触发,供 v-model 回写;先于 change 触发。 | [value: string | number] |
| change | 输入框内容变化或选中候选项时触发。它是组件事件,会遮住输入框原生的 change 事件。 | [value: string | number] |
| search | 用户输入时触发,参数为输入的文本。 | [inputValue: string] |
| select | 选中候选项时触发,参数为选中值;设置 selectWithObject 时为去掉内部字段的数据项。 | [value: string | number | AutoCompleteDataItem] |
| clear | 用清除按钮清空内容后触发。 | [] |
| focus | 焦点进入组件时触发一次。它是组件事件,会遮住原生的 focus 事件。 | [event: FocusEvent] |
| blur | 焦点离开组件时触发一次。它是组件事件,会遮住原生的 blur 事件。 | [event: FocusEvent] |
| dropdownVisibleChange | 下拉面板显示或隐藏时触发,参数为是否可见;放在 KeepAlive 中失活时也会以 false 触发。 | [visible: boolean] |
| keydown | 组件处理完按键之后触发,参数为 KeyboardEvent。它会遮住原生的 keydown 事件。 | [event: KeyboardEvent] |
Slots
| 插槽 | 说明 | 作用域参数 |
|---|---|---|
| prefix | 输入框的前缀内容。 | any |
| suffix | 输入框的后缀内容。 | any |
| insetLabel | 输入框内嵌标签。设置 prefix 或 trigger 时不渲染,也不作为可访问名称。 | any |
| clearIcon | 自定义清除图标,showClear 为 true 时有效。 | any |
| emptyContent | data 为空时在下拉面板中显示的内容。 | any |
| item | 候选项行内的内容,参数 item 是未经加工的数据项,index 是它在已渲染候选项中的位置;不提供时显示 label。 | TypeAutoCompleteItemSlotProps<T> |
| trigger | 替换输入框。参数含 value、inputValue、placeholder、disabled、autofocus、size、validateStatus、showClear、insetLabelId、onChange(更新输入文本,触发 search 与 change 并展开列表)、onClear、onFocus、onBlur、controlAttrs(传给组件的 aria-* 属性,放到自定义触发器的可聚焦元素上)与 componentProps。使用时 id 落在根元素上。 | TypeAutoCompleteTriggerSlotProps<T> |
Exposes
| 名称 | 说明 | 类型 |
|---|---|---|
| 无 |
id、name 以及 aria-describedby、aria-errormessage、aria-invalid、aria-label、aria-labelledby、aria-required 落在输入框(role="combobox")上,<label for> 因此能关联到输入框;class、style、data-* 和其他未声明的属性与监听器(例如 @click)落在根 div 上,根 div 的 tabindex 固定为 -1。使用 #trigger 时没有输入框,id 落在根 div 上,name 不落到任何元素,aria-* 通过插槽参数 controlAttrs 交给自定义触发器的可聚焦元素。
data 的对象项:
| 字段 | 说明 | 类型 |
|---|---|---|
| value | 选中后的值 | string | number |
| label | 候选项展示的文本,缺省时显示 value;节点内容用 #item 插槽 | string | number |
| disabled | 是否禁用该候选项 | boolean |
| class | 候选行的类名 | string | object | array |
| style | 候选行的样式 | StyleValue |
其他字段原样保留,出现在 #item 插槽的 item 和 @select 的载荷里(设置 selectWithObject 时)。以 data- 开头的字段落在候选行上。字符串和数字项在 renderSelectedItem 中以 { value, label } 形式收到。
AutoComplete 通过模板 ref 暴露下列成员,类型为 AutoCompleteHandle:
| 成员 | 说明 | 类型 |
|---|---|---|
| nativeElement | 只读,包裹输入框或自定义触发器的根 div,未挂载时为 null | HTMLDivElement | null |
Accessibility
键盘和焦点
- AutoComplete 的 input 框带有
role="combobox",并通过aria-expanded、aria-controls、aria-autocomplete="list"和aria-activedescendant与选项面板关联 - combobox 的名称按优先级取:
aria-labelledby、aria-label、实际渲染出来的 insetLabel(combobox 的aria-labelledby指向它,未传insetLabelId时组件生成 id;有 prefix 时 insetLabel 不渲染,也不作名称)。listbox 使用同一个名称来源。AutoComplete 没有默认名称:以上都没有时,请用aria-label或aria-labelledby为输入框命名;<label for>只命名输入框,listbox 仍没有名称 - AutoComplete 的 input 框可被聚焦,聚焦后,键盘用户可以通过
上箭头或下箭头打开选项面板;data 为空时面板里显示 emptyContent(没有提供时为空),加载中显示加载动画 - AutoComplete 也支持通过
Enter键打开和收起面板 - 若用户将 defaultActiveFirstOption 属性设置为 true 时,选项面板打开后默认高亮第一个选项
- 若下拉菜单打开时:
- 使用
Esc可以关闭菜单 - 使用
Tab可以关闭菜单,焦点照常移到下一个可聚焦元素 - 使用
上箭头或下箭头可以切换选项,被高亮的选项(aria-activedescendant指向的项)aria-selected为true - 被聚焦的选项可以通过
Enter键选中,并收起面板 - 用鼠标选中选项后,焦点留在输入框中
- 使用
showClear开启且有内容时,清除按钮与 Input 相同:原生button,名称为语言包中的「清空输入」,从输入框按Tab移到它,Enter或空格键清空并把焦点放回输入框
文案规范
- 需要清晰地展示内容,让用户显而易见地感知到可用的各个选项
- 限制一次性展示的选项数量
设计变量
| Sass 变量 | 值 | 用法 |
|---|---|---|
| $transition_duration-autoComplete_option-bg | var(--semi-transition_duration-none) | 选择器-选项-动画持续时间 |
| $transition_function-autoComplete_option-bg | var(--semi-transition_function-easeIn) | 选择器-选项-过渡曲线 |
| $transition_delay-autoComplete_option-bg | var(--semi-transition_delay-none) | 选择器-选项-延迟时间 |
| $spacing-autoComplete_loading_wrapper-paddingTop | $spacing-tight | 加载搜索结果时的顶部内边距 |
| $spacing-autoComplete_loading_wrapper-paddingBottom | $spacing-tight | 加载搜索结果时的底部内边距 |
| $color-autoComplete_option-icon-default | transparent | 自动完成菜单选项图标颜色 - 默认态 |
| $color-autoComplete_option-bg-hover | var(--semi-color-fill-0) | 自动完成菜单选项背景颜色 - 悬停态 |
| $color-autoComplete_option-bg-active | var(--semi-color-fill-1) | 自动完成菜单选项背景颜色 - 按下态 |
| $color-autoComplete_option_disabled-text | var(--semi-color-disabled-text) | 禁用自动完成菜单选项文字颜色 |
| $width-autoComplete_option_tick | $width-icon-small | 自动完成菜单项选中对勾图标大小 |
| $spacing-autoComplete_option_tick-marginRight | $spacing-tight | 自动完成菜单选中对勾右侧外边距 |
| $spacing-autoComplete_option-paddingLeft | $spacing-base-tight | 自动完成菜单项左侧内边距 |
| $spacing-autoComplete_option-paddingRight | $spacing-base-tight | 自动完成菜单项右侧内边距 |
| $spacing-autoComplete_option-paddingTop | $spacing-tight | 自动完成菜单项顶部内边距 |
| $spacing-autoComplete_option-paddingBottom | $spacing-tight | 自动完成菜单项底部内边距 |
| $spacing-autoComplete_option_first-marginTop | $spacing-extra-tight | 自动完成第一个菜单项顶部外边距 |
| $spacing-autoComplete_option_last-marginBottom | $spacing-extra-tight | 自动完成最后一个菜单项顶部外边距 |
| $radius-autoComplete_option | 0px | 自动完成待选项圆角 |
| $color-autoComplete_option_main-text | var(--semi-color-text-0) | 自动完成菜单选项文本颜色 |