用法
使用 v-model
来显示一个可搜索和可选择的命令列表。
<script setup lang="ts">
const people = [
{ id: 1, label: 'Wade Cooper' },
{ id: 2, label: 'Arlene Mccoy' },
{ id: 3, label: 'Devon Webb' },
{ id: 4, label: 'Tom Cook' },
{ id: 5, label: 'Tanya Fox' },
{ id: 6, label: 'Hellen Schmidt' },
{ id: 7, label: 'Caroline Schultz' },
{ id: 8, label: 'Mason Heaney' },
{ id: 9, label: 'Claudie Smitham' },
{ id: 10, label: 'Emil Schaefer' }
]
const selected = ref([people[3]])
</script>
<template>
<UCommandPalette
v-model="selected"
multiple
nullable
:autoselect="false"
:groups="[{ key: 'people', commands: people }]"
:fuse="{ resultLimit: 6, fuseOptions: { threshold: 0.1 } }"
/>
</template>
你可以在任何你想要的地方放一个 CommandPalette
,但它最常用于模态框中。
<script setup lang="ts">
const isOpen = ref(false)
const people = [
{ id: 1, label: 'Wade Cooper' },
{ id: 2, label: 'Arlene Mccoy' },
{ id: 3, label: 'Devon Webb' },
{ id: 4, label: 'Tom Cook' },
{ id: 5, label: 'Tanya Fox' },
{ id: 6, label: 'Hellen Schmidt' },
{ id: 7, label: 'Caroline Schultz' },
{ id: 8, label: 'Mason Heaney' },
{ id: 9, label: 'Claudie Smitham' },
{ id: 10, label: 'Emil Schaefer' }
]
const selected = ref([])
</script>
<template>
<div>
<UButton label="Open" @click="isOpen = true" />
<UModal v-model="isOpen">
<UCommandPalette
v-model="selected"
multiple
nullable
:groups="[{ key: 'people', commands: people }]"
/>
</UModal>
</div>
</template>
你可以传递多个命令组到组件中。每个组将通过一个分隔符分隔,并显示一个标签。
如果没有使用 v-model
,你也可以监听 @update:model-value
,当命令被点击时,跳转到一个链接或执行其他操作。
最近搜索
<script setup lang="ts">
const router = useRouter()
const toast = useToast()
const commandPaletteRef = ref()
const users = [
{ id: 'benjamincanac', label: 'benjamincanac', href: 'https://github.com/benjamincanac', target: '_blank', avatar: { src: 'https://ipx.nuxt.com/s_16x16/gh_avatar/benjamincanac', srcset: 'https://ipx.nuxt.com/s_32x32/gh_avatar/benjamincanac 2x', loading: 'lazy' } },
{ id: 'Atinux', label: 'Atinux', href: 'https://github.com/Atinux', target: '_blank', avatar: { src: 'https://ipx.nuxt.com/s_16x16/gh_avatar/Atinux', srcset: 'https://ipx.nuxt.com/s_32x32/gh_avatar/Atinux 2x', loading: 'lazy' } },
{ id: 'smarroufin', label: 'smarroufin', href: 'https://github.com/smarroufin', target: '_blank', avatar: { src: 'https://ipx.nuxt.com/s_16x16/gh_avatar/smarroufin', srcset: 'https://ipx.nuxt.com/s_32x32/gh_avatar/smarroufin 2x', loading: 'lazy' } }
]
const actions = [
{ id: 'new-file', label: 'Add new file', icon: 'i-heroicons-document-plus', click: () => toast.add({ title: 'New file added!' }), shortcuts: ['⌘', 'N'] },
{ id: 'new-folder', label: 'Add new folder', icon: 'i-heroicons-folder-plus', click: () => toast.add({ title: 'New folder added!' }), shortcuts: ['⌘', 'F'] },
{ id: 'hashtag', label: 'Add hashtag', icon: 'i-heroicons-hashtag', click: () => toast.add({ title: 'Hashtag added!' }), shortcuts: ['⌘', 'H'] },
{ id: 'label', label: 'Add label', icon: 'i-heroicons-tag', click: () => toast.add({ title: 'Label added!' }), shortcuts: ['⌘', 'L'] }
]
const groups = computed(() =>
[commandPaletteRef.value?.query
? {
key: 'users',
commands: users
}
: {
key: 'recent',
label: 'Recent searches',
commands: users.slice(0, 1)
}, {
key: 'actions',
commands: actions
}].filter(Boolean))
function onSelect(option) {
if (option.click) {
option.click()
} else if (option.to) {
router.push(option.to)
} else if (option.href) {
window.open(option.href, '_blank')
}
}
</script>
<template>
<UCommandPalette ref="commandPaletteRef" :groups="groups" :autoselect="false" @update:model-value="onSelect" />
</template>
图标
通过使用以下模式设置 icon
属性,使用来自 Iconify 的任何图标:i-{collection_name}-{icon_name}
。
使用 selected-icon
属性设置不同的图标,或在 ui.commandPalette.default.selectedIcon
中全局更改它。默认值为 i-heroicons-check-20-solid
。
<template>
<UCommandPalette icon="i-heroicons-command-line" />
</template>
加载
使用 loading
属性显示加载图标。
使用 loading-icon
属性设置不同的图标,或在 ui.commandPalette.default.loadingIcon
中全局更改它。默认值为 i-heroicons-arrow-path-20-solid
。
<template>
<UCommandPalette loading />
</template>
占位符
使用 placeholder
属性更改输入框的占位符。
<template>
<UCommandPalette placeholder="Type a command..." />
</template>
关闭
使用 close-button
属性在输入框的右侧显示一个关闭按钮。
你可以将 按钮 组件的所有属性传递给 close-button
属性,或通过 ui.commandPalette.default.closeButton
全局更改它。
<template>
<UCommandPalette
:close-button="{ icon: 'i-heroicons-x-mark-20-solid', color: 'gray', variant: 'link', padded: false }"
/>
</template>
空状态
当没有结果时,将显示一个空状态。
使用 empty-state
属性自定义 icon
和 label
,或在 ui.commandPalette.default.emptyState
中全局更改它们。
你也可以将它设置为 null
来隐藏空状态。
我们找不到任何项目。
<template>
<UCommandPalette
:empty-state="{ icon: 'i-heroicons-magnifying-glass-20-solid', label: 'We couldn't find any items.', queryLabel: 'We couldn't find any items with that term. Please try again.' }"
placeholder="Type something to see the empty label change"
/>
</template>
全文搜索
CommandPalette 组件使用 Fuse.js 为你处理全文搜索。你可以通过 fuse
属性传递 Fuse.js 的所有选项。
搜索命令时,组件默认会查找命令的 label
属性。你可以通过覆盖 command-attribute
属性来自定义此行为。这也将影响命令的显示。
你也可以通过将 fuse.fuseOptions.includeMatches
设置为 true
来突出显示命令中的匹配项。CommandPalette 组件会自动为你处理突出显示。
<template>
<UCommandPalette
command-attribute="title"
:fuse="{
fuseOptions: {
ignoreLocation: true,
includeMatches: true,
threshold: 0,
keys: ['title', 'description', 'children.children.value', 'children.children.children.value']
},
resultLimit: 10
}"
/>
</template>
异步搜索
你也可以传递一个 async
函数到组的 search
属性,以执行异步搜索。该函数将接收查询作为第一个参数,并应返回一个命令数组。
我们找不到任何项目。
<script setup lang="ts">
const groups = [{
key: 'users',
label: q => q && `Users matching “${q}”...`,
search: async (q) => {
if (!q) {
return []
}
// @ts-ignore
const users: any[] = await $fetch('https://jsonplaceholder.typicode.com/users', { params: { q } })
return users.map(user => ({ id: user.id, label: user.name, suffix: user.email }))
}
}]
</script>
<template>
<UCommandPalette :groups="groups" :autoselect="false" />
</template>
search
函数正在加载时,loading
状态将自动启用。你可以通过将 loading-icon
属性设置为 null
或在 ui.commandPalette.default.loadingIcon
中全局更改它来禁用此行为。过滤搜索
你也可以传递一个函数到组的 filter
属性,以在搜索完成后过滤显示的命令。该函数将接收查询作为第一个参数,命令数组作为第二个参数,并应返回一个命令数组。
<script setup lang="ts">
const people = [
{ id: 1, label: 'Wade Cooper', child: true },
{ id: 2, label: 'Arlene Mccoy' },
{ id: 3, label: 'Devon Webb', child: true },
{ id: 4, label: 'Tom Cook' },
{ id: 5, label: 'Tanya Fox', child: true },
{ id: 6, label: 'Hellen Schmidt' },
{ id: 7, label: 'Caroline Schultz', child: true },
{ id: 8, label: 'Mason Heaney' },
{ id: 9, label: 'Claudie Smitham', child: true },
{ id: 10, label: 'Emil Schaefer' }
]
const groups = [{
key: 'users',
commands: people,
filter: (q, commands) => {
if (!q) {
return commands?.filter(command => !command.child)
}
return commands
}
}]
</script>
<template>
<UCommandPalette :groups="groups" :autoselect="false" />
</template>
插槽
<group>-icon
使用 #<group>-icon
插槽来覆盖左侧命令内容,默认情况下,它会显示 icon
、avatar
和 chip
。
<group>-command
使用 #<group>-command
插槽来覆盖命令内容,默认情况下,它会显示 prefix
、suffix
和 label
(可以通过 command-attribute
属性自定义)。
<group>-active
使用 #<group>-active
插槽来覆盖右侧命令内容(悬停时),默认情况下,它会显示组的 active
字段(如果提供)。
<group>-inactive
使用 #<group>-inactive
插槽来覆盖右侧命令内容(未悬停时),默认情况下,它会显示组的 inactive
字段(如果提供)或命令的 shortcuts
。
group
、command
、active
和 selected
属性。empty-state
使用 #empty-state
插槽自定义空状态。
<template>
<UCommandPalette>
<template #empty-state>
<div class="flex flex-col items-center justify-center py-6 gap-3">
<span class="italic text-sm">Nothing here!</span>
<UButton label="Add item" />
</div>
</template>
</UCommandPalette>
</template>
属性
config.default.icon
{}
null
"Search..."
config.default.loadingIcon
"id"
config.default.selectedIcon
200
config.default.emptyState
"label"
"label"
[]
config.default.closeButton as unknown as Button
{}
false
false
false
true
true
true
API
通过模板 ref 访问组件时,可以使用以下内容
当前查询。
更新当前查询。
由 useFuse 公开的 results。
Headless UI 公开的 Combobox API。
配置
{
wrapper: 'flex flex-col flex-1 min-h-0 divide-y divide-gray-100 dark:divide-gray-800',
container: 'relative flex-1 overflow-y-auto divide-y divide-gray-100 dark:divide-gray-800 scroll-py-2',
input: {
wrapper: 'relative flex items-center',
base: 'w-full placeholder-gray-400 dark:placeholder-gray-500 bg-transparent border-0 text-gray-900 dark:text-white focus:ring-0 focus:outline-none',
padding: 'px-4',
height: 'h-12',
size: 'sm:text-sm',
icon: {
base: 'pointer-events-none absolute start-4 text-gray-400 dark:text-gray-500',
loading: 'animate-spin',
size: 'h-5 w-5',
padding: 'ps-11'
},
closeButton: {
base: 'absolute end-4',
padding: 'pe-10'
}
},
emptyState: {
wrapper: 'flex flex-col items-center justify-center flex-1 px-6 py-14 sm:px-14',
label: 'text-sm text-center text-gray-900 dark:text-white',
queryLabel: 'text-sm text-center text-gray-900 dark:text-white',
icon: 'w-6 h-6 mx-auto text-gray-400 dark:text-gray-500 mb-4'
},
group: {
wrapper: 'p-2',
label: 'px-2.5 my-2 text-xs font-semibold text-gray-900 dark:text-white',
container: 'text-sm text-gray-700 dark:text-gray-200',
command: {
base: 'flex justify-between select-none items-center rounded-md px-2.5 py-1.5 gap-2 relative',
active: 'bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-white',
inactive: '',
label: 'flex items-center gap-1.5 min-w-0',
prefix: 'text-gray-400 dark:text-gray-500',
suffix: 'text-gray-400 dark:text-gray-500',
container: 'flex items-center gap-1.5 min-w-0',
icon: {
base: 'flex-shrink-0 w-5 h-5',
active: 'text-gray-900 dark:text-white',
inactive: 'text-gray-400 dark:text-gray-500'
},
selectedIcon: {
base: 'h-5 w-5 text-gray-900 dark:text-white flex-shrink-0'
},
avatar: {
base: 'flex-shrink-0',
size: '2xs'
},
chip: {
base: 'flex-shrink-0 w-2 h-2 mx-1 rounded-full'
},
disabled: 'opacity-50',
shortcuts: 'hidden md:inline-flex flex-shrink-0 gap-0.5'
},
active: 'flex-shrink-0 text-gray-500 dark:text-gray-400',
inactive: 'flex-shrink-0 text-gray-500 dark:text-gray-400'
},
default: {
icon: 'i-heroicons-magnifying-glass-20-solid',
loadingIcon: 'i-heroicons-arrow-path-20-solid',
emptyState: {
icon: 'i-heroicons-magnifying-glass-20-solid',
label: "We couldn't find any items.",
queryLabel: "We couldn't find any items with that term. Please try again."
},
closeButton: null,
selectedIcon: 'i-heroicons-check-20-solid'
}
}
示例
以下是一些示例,说明你可以如何使用 ui
属性来自定义 CommandPalette
组件的外观。
Algolia
没有最近搜索