Nuxt UI v3-alpha 已发布!

试试看
组件

命令面板

在你的应用中添加一个可自定义的命令面板。

用法

使用 v-model 来显示一个可搜索和可选择的命令列表。

Wade Cooper
Arlene Mccoy
Devon Webb
Tom Cook
Tanya Fox
Hellen Schmidt
<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,当命令被点击时,跳转到一个链接或执行其他操作。

最近搜索

benjamincanac
添加新文件
添加新文件夹
添加标签
添加标签
<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 属性自定义 iconlabel,或在 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>
在该文档的搜索中自己试试,按下 K.

你也可以传递一个 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 属性,以在搜索完成后过滤显示的命令。该函数将接收查询作为第一个参数,命令数组作为第二个参数,并应返回一个命令数组。

Arlene Mccoy
Tom Cook
Hellen Schmidt
Mason Heaney
Emil Schaefer
<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 插槽来覆盖左侧命令内容,默认情况下,它会显示 iconavatarchip

<group>-command

使用 #<group>-command 插槽来覆盖命令内容,默认情况下,它会显示 prefixsuffixlabel(可以通过 command-attribute 属性自定义)。

<group>-active

使用 #<group>-active 插槽来覆盖右侧命令内容(悬停时),默认情况下,它会显示组的 active 字段(如果提供)。

<group>-inactive

使用 #<group>-inactive 插槽来覆盖右侧命令内容(未悬停时),默认情况下,它会显示组的 inactive 字段(如果提供)或命令的 shortcuts

以上 4 个插槽将在插槽作用域中访问 groupcommandactiveselected 属性。

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>

属性

icon
string
config.default.icon
ui
{ wrapper?: string; container?: string; input?: DeepPartial<{ wrapper: string; base: string; padding: string; height: string; size: string; icon: { base: string; loading: string; size: string; padding: string; }; closeButton: { ...; }; }, any>; emptyState?: DeepPartial<...>; group?: DeepPartial<...>; default?: DeepP...
{}
modelValue
string | number | Record<string, any> | unknown[]
null
占位符
string
"Search..."
loadingIcon
string
config.default.loadingIcon
by
string
"id"
selectedIcon
string
config.default.selectedIcon
debounce
number
200
emptyState
{ icon: string; label: string; queryLabel: string; }
config.default.emptyState
groupAttribute
string
"label"
commandAttribute
string
"label"
groups
Group[]
[]
closeButton
按钮
config.default.closeButton as unknown as Button
fuse
UseFuseOptions<Command>
{}
nullable
boolean
false
加载
boolean
false
multiple
boolean
false
searchable
boolean
true
autoselect
boolean
true
autoclear
boolean
true

API

通过模板 ref 访问组件时,可以使用以下内容

query
string

当前查询。

updateQuery (query)

更新当前查询。

results
ComputedRef<FuseResult<Command>[]>

useFuse 公开的 results。

comboboxApi

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

没有最近搜索

看看这个组件!

Raycast

建议

线性
应用程序
Figma
应用程序
Slack
应用程序
YouTube
应用程序
GitHub
应用程序

命令

剪贴板历史记录
命令
导入扩展
命令
管理扩展
命令
看看这个组件!