Autocomplete 自动补全输入框
根据当前输入给出推荐候选项。
基础用法
自动补全组件用于提供输入建议。
fetch-suggestions 为返回建议列表的方法。本例中 querySearch(queryString, cb) 在建议就绪后通过 cb(data) 将数据交给组件。
list suggestions when activated
list suggestions on input
vue
<template>
<div class="demo-autocomplete">
<div class="demo-block">
<div class="demo-title">list suggestions when activated</div>
<u-autocomplete
v-model="state1"
:fetch-suggestions="querySearch"
clearable
class="w-50"
placeholder="Please Input"
@select="handleSelect"
/>
</div>
<div class="demo-block">
<div class="demo-title">list suggestions on input</div>
<u-autocomplete
v-model="state2"
:fetch-suggestions="querySearch"
:trigger-on-focus="false"
clearable
class="w-50"
placeholder="Please Input"
@select="handleSelect"
/>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue'
interface RestaurantItem {
value: string
link: string
}
const state1 = ref('')
const state2 = ref('')
const restaurants = ref<RestaurantItem[]>([])
const querySearch = (queryString: string, cb: any) => {
const results = queryString
? restaurants.value.filter(createFilter(queryString))
: restaurants.value
// call callback function to return suggestions
cb(results)
}
const createFilter = (queryString: string) => {
return (restaurant: RestaurantItem) => {
return (
restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0
)
}
}
const loadAll = () => {
return [
{ value: 'vue', link: 'https://github.com/vuejs/vue' },
{ value: 'element', link: 'https://github.com/ElemeFE/element' },
{ value: 'cooking', link: 'https://github.com/ElemeFE/cooking' },
{ value: 'mint-ui', link: 'https://github.com/ElemeFE/mint-ui' },
{ value: 'vuex', link: 'https://github.com/vuejs/vuex' },
{ value: 'vue-router', link: 'https://github.com/vuejs/vue-router' },
{ value: 'babel', link: 'https://github.com/babel/babel' },
]
}
const handleSelect = (item: Record<string, any>) => {
console.log(item)
}
onMounted(() => {
restaurants.value = loadAll()
})
</script>
<style scoped>
.demo-autocomplete {
display: flex;
flex-wrap: wrap;
gap: 2rem;
}
.demo-block {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.demo-title {
font-size: 0.875rem;
color: var(--u-text-color-secondary);
min-height: 2.5em;
display: flex;
align-items: center;
}
@media screen and (max-width: 768px) {
.demo-autocomplete {
gap: 1rem;
}
.demo-block {
width: 100%;
}
}
</style>
隐藏源代码
自定义模板
自定义候选项的展示方式。
使用作用域插槽自定义候选项;作用域中可通过 item 访问候选项对象。
vue
<template>
<u-autocomplete
v-model="state"
:fetch-suggestions="querySearch"
popper-class="my-autocomplete"
placeholder="Please input"
@select="handleSelect"
>
<template #suffix>
<u-icon class="u-input__icon" @click="handleIconClick">
<edit />
</u-icon>
</template>
<template #default="{ item }">
<div class="value">{{ item.value }}</div>
<span class="link">{{ item.link }}</span>
</template>
</u-autocomplete>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue'
import { Edit } from '@uniboot/icons-vue'
interface LinkItem {
value: string
link: string
}
const state = ref('')
const links = ref<LinkItem[]>([])
const querySearch = (queryString: string, cb) => {
const results = queryString
? links.value.filter(createFilter(queryString))
: links.value
// call callback function to return suggestion objects
cb(results)
}
const createFilter = (queryString: string) => {
return (restaurant: LinkItem) => {
return (
restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0
)
}
}
const loadAll = () => {
return [
{ value: 'vue', link: 'https://github.com/vuejs/vue' },
{ value: 'element', link: 'https://github.com/ElemeFE/element' },
{ value: 'cooking', link: 'https://github.com/ElemeFE/cooking' },
{ value: 'mint-ui', link: 'https://github.com/ElemeFE/mint-ui' },
{ value: 'vuex', link: 'https://github.com/vuejs/vuex' },
{ value: 'vue-router', link: 'https://github.com/vuejs/vue-router' },
{ value: 'babel', link: 'https://github.com/babel/babel' },
]
}
const handleSelect = (item: Record<string, any>) => {
console.log(item)
}
const handleIconClick = (ev: Event) => {
console.log(ev)
}
onMounted(() => {
links.value = loadAll()
})
</script>
<style>
.my-autocomplete li {
line-height: normal;
padding: 7px;
}
.my-autocomplete li .name {
text-overflow: ellipsis;
overflow: hidden;
}
.my-autocomplete li .addr {
font-size: 12px;
color: #b4b4b4;
}
.my-autocomplete li .highlighted .addr {
color: #ddd;
}
</style>
隐藏源代码
远程搜索
从服务端获取数据。
vue
<template>
<u-autocomplete
v-model="state"
:fetch-suggestions="querySearchAsync"
placeholder="Please input"
@select="handleSelect"
/>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue'
const state = ref('')
interface LinkItem {
value: string
link: string
}
const links = ref<LinkItem[]>([])
const loadAll = () => {
return [
{ value: 'vue', link: 'https://github.com/vuejs/vue' },
{ value: 'element', link: 'https://github.com/ElemeFE/element' },
{ value: 'cooking', link: 'https://github.com/ElemeFE/cooking' },
{ value: 'mint-ui', link: 'https://github.com/ElemeFE/mint-ui' },
{ value: 'vuex', link: 'https://github.com/vuejs/vuex' },
{ value: 'vue-router', link: 'https://github.com/vuejs/vue-router' },
{ value: 'babel', link: 'https://github.com/babel/babel' },
]
}
let timeout: ReturnType<typeof setTimeout>
const querySearchAsync = (queryString: string, cb: (arg: any) => void) => {
const results = queryString
? links.value.filter(createFilter(queryString))
: links.value
clearTimeout(timeout)
timeout = setTimeout(() => {
cb(results)
}, 3000 * Math.random())
}
const createFilter = (queryString: string) => {
return (restaurant: LinkItem) => {
return (
restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0
)
}
}
const handleSelect = (item: Record<string, any>) => {
console.log(item)
}
onMounted(() => {
links.value = loadAll()
})
</script>
隐藏源代码
自定义加载
覆盖加载态展示内容。
loading icon1
loading icon2
vue
<template>
<div class="demo-autocomplete">
<div class="demo-block">
<div class="demo-title">loading icon1</div>
<u-autocomplete
v-model="state"
:fetch-suggestions="querySearchAsync"
class="w-50"
placeholder="Please input"
@select="handleSelect"
>
<template #loading>
<svg class="circular" viewBox="0 0 50 50">
<circle class="path" cx="25" cy="25" r="20" fill="none" />
</svg>
</template>
</u-autocomplete>
</div>
<div class="demo-block">
<div class="demo-title">loading icon2</div>
<u-autocomplete
v-model="state"
:fetch-suggestions="querySearchAsync"
class="w-50"
placeholder="Please input"
@select="handleSelect"
>
<template #loading>
<u-icon class="is-loading">
<svg class="circular" viewBox="0 0 20 20">
<g
class="path2 loading-path"
stroke-width="0"
style="animation: none; stroke: none"
>
<circle r="3.375" class="dot1" rx="0" ry="0" />
<circle r="3.375" class="dot2" rx="0" ry="0" />
<circle r="3.375" class="dot4" rx="0" ry="0" />
<circle r="3.375" class="dot3" rx="0" ry="0" />
</g>
</svg>
</u-icon>
</template>
</u-autocomplete>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue'
const state = ref('')
interface LinkItem {
value: string
link: string
}
const links = ref<LinkItem[]>([])
const loadAll = () => {
return [
{ value: 'vue', link: 'https://github.com/vuejs/vue' },
{ value: 'element', link: 'https://github.com/ElemeFE/element' },
{ value: 'cooking', link: 'https://github.com/ElemeFE/cooking' },
{ value: 'mint-ui', link: 'https://github.com/ElemeFE/mint-ui' },
{ value: 'vuex', link: 'https://github.com/vuejs/vuex' },
{ value: 'vue-router', link: 'https://github.com/vuejs/vue-router' },
{ value: 'babel', link: 'https://github.com/babel/babel' },
]
}
let timeout: ReturnType<typeof setTimeout>
const querySearchAsync = (queryString: string, cb: (arg: any) => void) => {
const results = queryString
? links.value.filter(createFilter(queryString))
: links.value
clearTimeout(timeout)
timeout = setTimeout(() => {
cb(results)
}, 5000 * Math.random())
}
const createFilter = (queryString: string) => {
return (restaurant: LinkItem) => {
return (
restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0
)
}
}
const handleSelect = (item: Record<string, any>) => {
console.log(item)
}
onMounted(() => {
links.value = loadAll()
})
</script>
<style scoped>
.demo-autocomplete {
display: flex;
flex-wrap: wrap;
gap: 2rem;
}
.demo-block {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.demo-title {
font-size: 0.875rem;
color: var(--u-text-color-secondary);
min-height: 2.5em;
display: flex;
align-items: center;
}
@media screen and (max-width: 768px) {
.demo-autocomplete {
gap: 1rem;
}
.demo-block {
width: 100%;
}
}
</style>
<style>
.circular {
display: inline;
height: 30px;
width: 30px;
animation: loading-rotate 2s linear infinite;
}
.path {
animation: loading-dash 1.5s ease-in-out infinite;
stroke-dasharray: 90, 150;
stroke-dashoffset: 0;
stroke-width: 2;
stroke: var(--u-color-primary);
stroke-linecap: round;
}
.loading-path .dot1 {
transform: translate(3.75px, 3.75px);
fill: var(--u-color-primary);
animation: custom-spin-move 1s infinite linear alternate;
opacity: 0.3;
}
.loading-path .dot2 {
transform: translate(calc(100% - 3.75px), 3.75px);
fill: var(--u-color-primary);
animation: custom-spin-move 1s infinite linear alternate;
opacity: 0.3;
animation-delay: 0.4s;
}
.loading-path .dot3 {
transform: translate(3.75px, calc(100% - 3.75px));
fill: var(--u-color-primary);
animation: custom-spin-move 1s infinite linear alternate;
opacity: 0.3;
animation-delay: 1.2s;
}
.loading-path .dot4 {
transform: translate(calc(100% - 3.75px), calc(100% - 3.75px));
fill: var(--u-color-primary);
animation: custom-spin-move 1s infinite linear alternate;
opacity: 0.3;
animation-delay: 0.8s;
}
@keyframes loading-rotate {
to {
transform: rotate(360deg);
}
}
@keyframes loading-dash {
0% {
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -40px;
}
100% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -120px;
}
}
@keyframes custom-spin-move {
to {
opacity: 1;
}
}
</style>
隐藏源代码
自定义头部与底部
可通过插槽自定义下拉面板的头部与底部。
使用插槽自定义内容。
Custom header content
Custom footer content
vue
<template>
<div class="autocomplete-custom-header-footer">
<div>
<p>Custom header content</p>
<u-autocomplete
v-model="headerSlotState"
:fetch-suggestions="querySearchAsync"
placeholder="Please input"
@select="handleSelect"
>
<template #header>header content</template>
</u-autocomplete>
</div>
<div>
<p>Custom footer content</p>
<u-autocomplete
ref="footerAutocompleteRef"
v-model="footerSlotstate"
:fetch-suggestions="querySearchAsync"
placeholder="Please input"
@select="handleSelect"
>
<template #footer>
<u-button link size="small" @click="handleClear"> Clear </u-button>
</template>
</u-autocomplete>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue'
const headerSlotState = ref('')
const footerSlotstate = ref('')
interface LinkItem {
value: string
link: string
}
const links = ref<LinkItem[]>([])
const loadAll = () => {
return [
{ value: 'vue', link: 'https://github.com/vuejs/vue' },
{ value: 'element', link: 'https://github.com/ElemeFE/element' },
{ value: 'cooking', link: 'https://github.com/ElemeFE/cooking' },
{ value: 'mint-ui', link: 'https://github.com/ElemeFE/mint-ui' },
{ value: 'vuex', link: 'https://github.com/vuejs/vuex' },
{ value: 'vue-router', link: 'https://github.com/vuejs/vue-router' },
{ value: 'babel', link: 'https://github.com/babel/babel' },
]
}
let timeout: ReturnType<typeof setTimeout>
const querySearchAsync = (queryString: string, cb: (arg: any) => void) => {
const results = queryString
? links.value.filter(createFilter(queryString))
: links.value
clearTimeout(timeout)
timeout = setTimeout(() => {
cb(results)
}, 3000 * Math.random())
}
const createFilter = (queryString: string) => {
return (restaurant: LinkItem) => {
return (
restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0
)
}
}
const handleSelect = (item: Record<string, any>) => {
console.log(item)
}
onMounted(() => {
links.value = loadAll()
})
const footerAutocompleteRef = ref()
const handleClear = () => {
footerSlotstate.value = ''
footerAutocompleteRef.value.getData()
}
</script>
<style scoped>
.autocomplete-custom-header-footer {
display: flex;
}
.autocomplete-custom-header-footer > div {
flex: 1;
text-align: center;
}
.autocomplete-custom-header-footer > div > :deep(.u-autocomplete) {
width: 50%;
}
.autocomplete-custom-header-footer > div:not(:last-child) {
border-right: 1px solid var(--u-border-color);
}
</style>
隐藏源代码
自动补全 API
自动补全 属性
| 名称 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| model-value / v-model | 绑定值 | string | — |
| placeholder | 占位符 | string | — |
| clearable | 是否显示清空按钮 | boolean | false |
| disabled | 是否禁用 | boolean | false |
| value-key | 候选项对象中用于展示的键名 | string | value |
| debounce | 输入防抖延迟(毫秒) | number | 300 |
| placement | 下拉弹出位置 | enum | bottom-start |
| fetch-suggestions | 获取建议的方法;数据就绪后调用 callback(data:[]) 回传 | Array / Function | — |
| trigger-on-focus | 是否在获得焦点时显示建议 | boolean | true |
| select-when-unmatched | 无匹配项时按回车是否仍触发 select | boolean | false |
| name | 同原生 name | string | — |
| aria-label a11y | 原生 aria-label | string | — |
| hide-loading | 远程搜索时是否隐藏加载图标 | boolean | false |
| popper-class | 下拉面板的自定义类名 | string / object | '' |
| popper-style | 下拉面板的自定义样式 | string / object | — |
| teleported | 是否将下拉传送到 body | boolean | true |
| append-to | 下拉挂载的目标节点 | CSSSelector / HTMLElement | — |
| highlight-first-item | 远程搜索时是否默认高亮第一项 | boolean | false |
| fit-input-width | 下拉宽度是否与输入框一致 | boolean | false |
| loop-navigation | 键盘导航是否在首尾循环 | boolean | true |
| input props | — | — | — |
自动补全 事件
| 事件名 | 说明 | 类型 |
|---|---|---|
| blur | 失焦时 | Function |
| focus | 聚焦时 | Function |
| input | 值变化时 | Function |
| clear | 点击清空按钮时 | Function |
| select | 选中建议项时 | Function |
| change | 输入框值变化时 | Function |
自动补全 插槽
| 名称 | 说明 |
|---|---|
| default | 自定义输入建议候选项;作用域类型 ^[object]` |
| header | 下拉面板顶部内容 |
| footer | 下拉面板底部内容 |
| prefix | 输入框前缀内容 |
| suffix | 输入框后缀内容 |
| prepend | 输入框前置区域内容 |
| append | 输入框后置区域内容 |
| loading | 自定义加载态内容 |
自动补全 暴露
| 名称 | 说明 | 类型 |
|---|---|---|
| activated | 是否已激活 | object |
| blur | 使输入框失焦 | Function |
| close | 收起建议列表 | Function |
| focus | 使输入框聚焦 | Function |
| handleSelect | 选中建议项时内部处理 | Function |
| handleKeyEnter | 处理回车键 | Function |
| highlightedIndex | 当前高亮项索引 | object |
| highlight | 高亮指定索引项 | Function |
| inputRef | 输入框组件实例 | object |
| loading | 远程加载状态 | object |
| popperRef | 文字提示组件实例 | object |
| suggestions | 建议列表数据 | object |
| getData | 加载建议列表 | Function |