Lzh on GitHub
一组可折叠面板的堆叠。

用法

accordion

accordion prop 属性:

  • items 属性: 这是定义手风琴内容的数组。数组中的每个对象代表一个可折叠项,你可以为每个项设置 label (标题)、content (内容)、icontrailingIcondisabled 等。
  • type 属性:
    • 'single' (默认): 一次只能展开一个项。
    • 'multiple': 可以同时展开多个项。
  • default-valuev-model: 控制手风琴项的默认展开状态或通过响应式数据进行双向绑定。
  • ui 属性: 这是 Nuxt UI 组件的一个强大特性,允许你传递一个对象,其中包含 Tailwind CSS 类,用于深度定制手风琴的各个子元素(如 rootitemheadertriggercontentbody 等)。这使得组件的样式高度可定制,无需修改其内部结构。
  • 插槽 (Slots):
    • 默认插槽 (#default): 如果你需要完全自定义手风琴项的标题部分,可以使用这个作用域插槽。它会暴露 item, index, open 数据。
    • leading 插槽 (#leading): 自定义标题左侧的内容,通常用于图标。
    • trailing 插槽 (#trailing): 自定义标题右侧的内容,通常用于箭头或切换图标。
    • content 插槽 (#content): 自定义手风琴项的内容区域。
    • body 内容主体插槽 (#body): 类似 content,但通常用于包裹默认内容,并保留 body 样式。
    • 自定义具名插槽 (#{{ item.slot }}): 如果你在 items 数组的某个项中设置了 slot: 'myCustomSlotName',你可以使用 #myCustomSlotName#myCustomSlotName-body 来为该特定项提供完全自定义的内容。

Items

使用 items prop 属性,它是一个对象数组,具有以下属性:

  • label?: string
  • icon?: string
  • trailingIcon?: string
  • content?: string
  • value?: string
  • disabled?: boolean
  • slot?: string
  • class?: any
  • ui?: { item?: ClassNameValue, header?: ClassNameValue, trigger?: ClassNameValue, leadingIcon?: ClassNameValue, label?: ClassNameValue, trailingIcon?: ClassNameValue, content?: ClassNameValue, body?: ClassNameValue }
<script setup lang="ts">
import type { AccordionItem } from '@nuxt/ui'

const items = ref<AccordionItem[]>([
  {
    label: 'Icons',
    icon: 'i-lucide-smile',
    content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
  },
  {
    label: 'Colors',
    icon: 'i-lucide-swatch-book',
    content: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
  },
  {
    label: 'Components',
    icon: 'i-lucide-box',
    content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
  }
])
</script>

<template>
  <UAccordion :items="items" />
</template>

多选 (Multiple)

type 属性设置为 multiple 以允许同时激活多个项目。默认为 single

<script setup lang="ts">
import type { AccordionItem } from '@nuxt/ui'

const items = ref<AccordionItem[]>([
  {
    label: 'Icons',
    icon: 'i-lucide-smile',
    content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
  },
  {
    label: 'Colors',
    icon: 'i-lucide-swatch-book',
    content: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
  },
  {
    label: 'Components',
    icon: 'i-lucide-box',
    content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
  }
])
</script>

<template>
  <UAccordion type="multiple" :items="items" />
</template>

可折叠 (Collapsible)

typesingle 时,您可以将 collapsible 属性设置为 false 以防止活动项目折叠。

<script setup lang="ts">
import type { AccordionItem } from '@nuxt/ui'

const items = ref<AccordionItem[]>([
  {
    label: 'Icons',
    icon: 'i-lucide-smile',
    content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
  },
  {
    label: 'Colors',
    icon: 'i-lucide-swatch-book',
    content: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
  },
  {
    label: 'Components',
    icon: 'i-lucide-box',
    content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
  }
])
</script>

<template>
  <UAccordion :collapsible="false" :items="items" />
</template>

卸载 (Unmount)

使用 unmount-on-hide 属性可以防止手风琴折叠时内容被卸载。默认为 true

<script setup lang="ts">
import type { AccordionItem } from '@nuxt/ui'

const items = ref<AccordionItem[]>([
  {
    label: 'Icons',
    icon: 'i-lucide-smile',
    content: '您无需做任何事,@nuxt/icon 会自动处理。'
  },
  {
    label: 'Colors',
    icon: 'i-lucide-swatch-book',
    content: '从您的 Tailwind CSS 主题中选择一个主色(primary)和一个中性色(neutral)。'
  },
  {
    label: 'Components',
    icon: 'i-lucide-box',
    content: '您可以通过使用 `class` / `ui` 属性或在 app.config.ts 中自定义组件。'
  }
])
</script>

<template>
  <UAccordion :unmount-on-hide="false" :items="items" />
</template>
您可以检查 DOM 以查看每个项目的渲染内容。

禁用 (Disabled)

使用 disabled 属性禁用整个手风琴。

您还可以通过在项目对象中使用 disabled 属性来禁用特定项目。

<script setup lang="ts">
import type { AccordionItem } from '@nuxt/ui'

const items = ref<AccordionItem[]>([
  {
    label: 'Icons',
    icon: 'i-lucide-smile',
    content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
  },
  {
    label: 'Colors',
    icon: 'i-lucide-swatch-book',
    content: 'Choose a primary and a neutral color from your Tailwind CSS theme.',
    disabled: true
  },
  {
    label: 'Components',
    icon: 'i-lucide-box',
    content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
  }
])
</script>

<template>
  <UAccordion disabled :items="items" />
</template>

尾部图标 (Trailing Icon)

使用 trailing-icon 属性自定义每个项目的尾部 Icon。默认为 i-lucide-chevron-down

您还可以通过在项目对象中使用 trailingIcon 属性为特定项目设置图标。
<script setup lang="ts">
import type { AccordionItem } from '@nuxt/ui'

const items = ref<AccordionItem[]>([
  {
    label: 'Icons',
    icon: 'i-lucide-smile',
    content: '您无需做任何事,@nuxt/icon 会自动处理。',
    trailingIcon: 'i-lucide-plus'
  },
  {
    label: 'Colors',
    icon: 'i-lucide-swatch-book',
    content: '从您的 Tailwind CSS 主题中选择一个主色(primary)和一个中性色(neutral)。'
  },
  {
    label: 'Components',
    icon: 'i-lucide-box',
    content: '您可以通过使用 `class` / `ui` 属性或在 `app.config.ts` 中自定义组件。'
  }
])
</script>

<template>
  <UAccordion trailing-icon="i-lucide-arrow-down" :items="items" />
</template>
您可以在 app.config.ts 中的 ui.icons.chevronDown 键下全局自定义此图标。
您可以在 vite.config.ts 中的 ui.icons.chevronDown 键下全局自定义此图标。

示例

控制活动项目

您可以通过使用 default-value 属性或使用 v-model 指令结合数据项的索引来控制活动项目。

<script setup lang="ts">
import type { AccordionItem } from '@nuxt/ui'

const items: AccordionItem[] = [
  {
    label: 'Icons',
    icon: 'i-lucide-smile',
    content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
  },
  {
    label: 'Colors',
    icon: 'i-lucide-swatch-book',
    content: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
  },
  {
    label: 'Components',
    icon: 'i-lucide-box',
    content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
  }
]

const active = ref('0')

// Note: This is for demonstration purposes only. Don't do this at home.
onMounted(() => {
  setInterval(() => {
    active.value = String((Number(active.value) + 1) % items.length)
  }, 2000)
})
</script>

<template>
  <UAccordion v-model="active" :items="items" />
</template>
您也可以传递所提供项目中的 value
type="multiple" 时,请确保向 default-value 属性或 v-model 指令传递一个数组。

拖放 (Drag and Drop)

使用来自 @vueuse/integrationsuseSortable 可组合项,以在手风琴上启用拖放功能。此集成封装了 Sortable.js,以提供无缝的拖放体验。

<script setup lang="ts">
import type { AccordionItem } from '@nuxt/ui'
import { useSortable } from '@vueuse/integrations/useSortable'

const items = shallowRef<AccordionItem[]>([
  {
    label: 'Icons',
    icon: 'i-lucide-smile',
    content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
  },
  {
    label: 'Colors',
    icon: 'i-lucide-swatch-book',
    content: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
  },
  {
    label: 'Components',
    icon: 'i-lucide-box',
    content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
  }
])

const accordion = useTemplateRef<HTMLElement>('accordion')

useSortable(accordion, items, {
  animation: 150
})
</script>

<template>
  <UAccordion ref="accordion" :items="items" />
</template>

使用 body 插槽

使用 #body 插槽自定义每个项目的主体。

<script setup lang="ts">
import type { AccordionItem } from '@nuxt/ui'

const items: AccordionItem[] = [
  {
    label: 'Icons',
    icon: 'i-lucide-smile'
  },
  {
    label: 'Colors',
    icon: 'i-lucide-swatch-book'
  },
  {
    label: 'Components',
    icon: 'i-lucide-box'
  }
]
</script>

<template>
  <UAccordion :items="items">
    <template #body="{ item }">
      This is the {{ item.label }} panel.
    </template>
  </UAccordion>
</template>
#body 插槽包含一些预定义样式,如果您想从头开始,请使用 #content slot

使用 content 插槽

使用 #content 插槽自定义每个项目的内容。

<script setup lang="ts">
import type { AccordionItem } from '@nuxt/ui'

const items: AccordionItem[] = [
  {
    label: 'Icons',
    icon: 'i-lucide-smile'
  },
  {
    label: 'Colors',
    icon: 'i-lucide-swatch-book'
  },
  {
    label: 'Components',
    icon: 'i-lucide-box'
  }
]
</script>

<template>
  <UAccordion :items="items">
    <template #content="{ item }">
      <p class="pb-3.5 text-sm text-muted">
        This is the {{ item.label }} panel.
      </p>
    </template>
  </UAccordion>
</template>

使用自定义插槽

使用 slot 属性自定义特定数据项使用的插槽。

您将可以访问以下插槽:

  • #{{ item.slot }}
  • #{{ item.slot }}-body
#{{ item.slot }}-body#{{ item.slot }} 插槽多包含一些预定义样式。如果想从头开始,请使用 #{{ item.slot }}
<script setup lang="ts">
import type { AccordionItem } from '@nuxt/ui'

const items = [
  {
    label: 'Icons',
    icon: 'i-lucide-smile',
    content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
  },
  {
    label: 'Colors',
    icon: 'i-lucide-swatch-book',
    slot: 'colors' as const,
    content: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
  },
  {
    label: 'Components',
    icon: 'i-lucide-box',
    content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
  }
] satisfies AccordionItem[]
</script>

<template>
  <UAccordion :items="items">
    <template #colors="{ item }">
      <p class="text-sm pb-3.5 text-primary">
        {{ item.content }}
      </p>
    </template>
  </UAccordion>
</template>

内容渲染的优先级流程

以下是内容渲染的优先级顺序,从高到低:

  1. 数据项特定的 item.slot(不带 -body 后缀):如果 item.slot 已定义,并且父组件提供了与该确切名称匹配的具名插槽。
  2. 通用 content 插槽:如果没有使用 item.slot(或没有为其提供匹配的插槽),但父组件提供了通用的 content 具名插槽。
  3. 数据项特定的 item.slot-body(带 -body 后缀):如果上述两者都不适用,但 item.slot 已定义,并且父组件提供了与 item.slot + '-body' 匹配的具名插槽。
  4. 通用 body 插槽:如果上述三者都不适用,但父组件提供了通用的 body 插槽。
  5. item.content 字符串:如果父组件绝对没有提供任何插槽,则显示 item.content 中的原始字符串。
包含 content 类型插槽比包含 body 插槽可定制性更多。

使用自定义 ui

<script setup lang="ts">
import type { AccordionItem } from '@nuxt/ui'

const items1: AccordionItem[] = [
  {
    label: '什么是 Nuxt UI?',
    icon: 'iconamoon:question-mark-circle-light',
    content: 'Nuxt UI 是一个基于 Vue 3 和 Tailwind CSS 的开源 UI 库,旨在为 Nuxt 应用程序提供可组合、无障碍和高度可定制的 UI 组件。'
  },
  {
    label: '如何开始使用 Nuxt UI?',
    icon: 'lets-icons:arrow-right-light',
    content: '您可以通过在 Nuxt 项目中安装 `@nuxt/ui` 模块并将其添加到 `nuxt.config.ts` 来开始使用。'
  },
  {
    label: 'Nuxt UI 支持哪些主题?',
    icon: 'ph:paint-brush-light',
    content: 'Nuxt UI 提供开箱即用的深色和浅色主题,并且所有组件都可通过 `ui` 属性或 `app.config.ts` 进行高度定制。'
  }
];

const items2: AccordionItem[] = [
  {
    label: '核心原则',
    icon: 'stash:light-bulb-light',
    content: '我们的设计理念是简洁、高效和可扩展性。'
  },
  {
    label: '创新技术',
    icon: 'heroicons-outline:sparkles',
    content: '我们积极探索并应用最新的前端技术。'
  },
  {
    label: '用户体验',
    icon: 'fa6-regular:face-smile',
    content: '始终将用户体验放在首位,致力于提供流畅和直观的界面。'
  }
];

const items3: AccordionItem[] = [
  {
    label: '默认样式项',
    content: '这个项会继承父级 UAccordion 的 UI 样式。'
  },
  {
    label: '定制背景色和文本色',
    content: '这个项有其独特的背景和文本颜色。',
    ui: { // 这个 ui 属性只应用于当前这个 AccordionItem
      item: 'bg-green-100 dark:bg-green-900 border-green-300 dark:border-green-700',
      header: 'bg-green-200 dark:bg-green-800 text-green-900 dark:text-green-100 hover:!bg-green-300 dark:hover:!bg-green-700', // 注意 ! 用于提高优先级
      content: 'bg-green-50 dark:bg-green-950 text-green-800 dark:text-green-200'
    }
  },
  {
    label: '定制图标和触发器',
    icon: 'iconamoon:star-light',
    content: '这个项有定制的图标和触发器样式。',
    ui: {
      leadingIcon: 'text-yellow-500', // 仅此项的左侧图标变为黄色
      trigger: 'p-6 font-mono text-purple-700 dark:text-purple-300' // 仅此项的触发器字体和颜色
    }
  }
];
</script>

<template>
  <div class="p-4">
    <h2 class="text-2xl font-bold mb-6">UAccordion UI 属性示例</h2>

    <div class="space-y-8">
      <div>
        <h3 class="text-xl font-semibold mb-4">示例 1: 基本样式定制</h3>
        <UAccordion
          :items="items1"
          :ui="{
            // root: 'bg-white dark:bg-gray-900 shadow-lg rounded-xl', // 整个手风琴容器的样式
            item: 'border-b border-gray-200 dark:border-gray-700 last:border-b-0', // 每个手风琴项的底部边框
            header: 'hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors duration-200', // 头部悬停效果
            trigger: 'p-4 flex items-center justify-between w-full text-left', // 触发器内边距和对齐
            content: 'px-4 pb-4 text-gray-700 dark:text-gray-300' // 内容区域的内边距和文本颜色
          }"
        />
      </div>

      <div>
        <h3 class="text-xl font-semibold mb-4">示例 2: 深度定制与特定项样式</h3>
        <UAccordion
          :items="items2"
          type="multiple"
          :ui="{
            // 全局样式
            root: 'bg-white dark:bg-gray-900 rounded-lg overflow-hidden border border-purple-300 dark:border-purple-700',
            item: 'border-b border-purple-200 dark:border-purple-800 last:border-b-0',
            header: 'bg-purple-50 dark:bg-purple-950 text-purple-800 dark:text-purple-200',
            trigger: 'p-5 font-bold text-lg',
            leadingIcon: 'text-purple-600 dark:text-purple-400',
            trailingIcon: 'text-purple-600 dark:text-purple-400 transition-transform duration-200 ui-expanded:rotate-180',
            label: 'tracking-wide', // 字间距
            content: 'px-5 py-3 bg-purple-100 dark:bg-purple-900 text-purple-900 dark:text-purple-100'
          }"
        />
      </div>

      <div>
        <h3 class="text-xl font-semibold mb-4">示例 3: 结合 `item.ui` 定制单个项</h3>
        <UAccordion
          :items="items3"
          :ui="{
            item: 'border-b border-gray-200 dark:border-gray-700 last:border-b-0'
          }"
        />
      </div>
    </div>
  </div>
</template>
  1. 可以通过 props.class 定义根组件的样式。
  2. 可以通过 props.ui 覆盖组件的默认样式。
  3. 可以通过 item.class 定义当前手风琴项目的根样式。
  4. 可以通过 item.ui 覆盖当前手风琴项目默认样式。

API

Props

Prop Default Type
as

'div'

any

The element or component this component should render as.

items

AccordionItem[]

trailingIcon

appConfig.ui.icons.chevronDown

string

The icon displayed on the right side of the trigger.

labelKey

'label'

string

The key used to get the label from the item.

collapsible

true

boolean

When type is "single", allows closing content when clicking trigger for an open item. When type is "multiple", this prop has no effect.

defaultValue

string | string[]

The default active value of the item(s).

Use when you do not need to control the state of the item(s).

modelValue

string | string[]

The controlled value of the active item(s).

Use this when you need to control the state of the items. Can be binded with v-model

type

'single'

"single" | "multiple"

Determines whether a "single" or "multiple" items can be selected at a time.

This prop will overwrite the inferred type from modelValue and defaultValue.

disabled

false

boolean

When true, prevents the user from interacting with the accordion and all its items

unmountOnHide

true

boolean

When true, the element will be unmounted on closed state.

ui

{ root?: ClassNameValue; item?: ClassNameValue; header?: ClassNameValue; trigger?: ClassNameValue; content?: ClassNameValue; body?: ClassNameValue; leadingIcon?: ClassNameValue; trailingIcon?: ClassNameValue; label?: ClassNameValue; }

Slots

Slot Type
leading

{ item: AccordionItem; index: number; open: boolean; }

default

{ item: AccordionItem; index: number; open: boolean; }

trailing

{ item: AccordionItem; index: number; open: boolean; }

content

{ item: AccordionItem; index: number; open: boolean; }

body

{ item: AccordionItem; index: number; open: boolean; }

Emits

Event Type
update:modelValue

string | string[]

当一个项目的展开状态改变时调用的事件处理程序。update:modelValue 事件是 UAccordion 组件向父组件 “报告” 其内部展开状态变化的方式,使得父组件可以响应并控制手风琴的行为。
UAccordion 组件自身并不直接在其内部逻辑中显式地调用 emits('update:modelValue', ...),而是依赖于其内部使用的 reka-ui 库的 AccordionRoot 组件来发出这个事件。AccordionRoot 是一个 Radix Vue 或类似无头 UI 库的组件,它会管理手风琴的展开/折叠状态,并在状态改变时发出 update:modelValue 事件。

update:modelValue 事件的使用场景

update:modelValue 事件主要用于:

  • 双向绑定v-model):这是最常见和推荐的用法。
  • 单向数据流控制:model-value@update:model-value):当你需要更精细地控制手风琴的展开状态时。

modelValue 的值会根据 type 属性的不同而有所区别:

  • type="single" (单选模式) 时,modelValue 会是一个 字符串 (对应当前展开项的 value 属性) 或 null。
  • type="multiple" (多选模式) 时,modelValue 会是一个 字符串数组 (对应所有展开项的 value 属性)。

示例用法

假设你有一个手风琴,你想在父组件中控制哪个项是展开的,并在展开状态改变时执行一些逻辑。

  1. 使用 v-model (推荐)

这是最简洁的用法。

<template>
  <div>
    <h2>使用 v-model 控制手风琴</h2>
    <p>当前展开项的值: {{ activeAccordionItem }}</p>

    <UAccordion
      v-model="activeAccordionItem"
      :items="accordionItems"
      type="single"
    />

    <h2>使用 v-model 控制多个手风琴</h2>
    <p>当前展开项的值 (多选): {{ activeMultipleAccordionItems }}</p>

    <UAccordion
      v-model="activeMultipleAccordionItems"
      :items="accordionItems"
      type="multiple"
    />
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import type { AccordionItem } from '@nuxt/ui'

const accordionItems: AccordionItem[] = [
  { label: '服务条款', content: '这里是服务条款的详细内容。', value: 'terms' },
  { label: '隐私政策', content: '这里是隐私政策的详细内容。', value: 'privacy' },
  { label: '联系方式', content: '您可以通过邮箱或电话联系我们。', value: 'contact' }
];

// 单选模式的 v-model
const activeAccordionItem = ref<string | null>('privacy'); // 初始展开 'privacy'

// 多选模式的 v-model
const activeMultipleAccordionItems = ref<string[]>(['terms']); // 初始展开 'terms'
</script>

解释:

  • v-model="activeAccordionItem" 等同于 :model-value="activeAccordionItem" @update:modelValue="activeAccordionItem = $event"
  • 当用户点击手风琴项使其展开或折叠时,底层的 AccordionRoot 会发出 update:modelValue 事件,事件的载荷就是新的 modelValue (当前展开项的 valuevalue 数组)。
  • Vue 的 v-model 语法会自动捕获这个事件并更新 activeAccordionItem 这个 ref
  1. 使用 :model-value@update:modelValue (更精细控制)

当你需要截获 update:modelValue 事件并执行额外逻辑时,可以使用这种方式。

<template>
  <div>
    <h2>使用 @update:modelValue 监听手风琴状态</h2>
    <p>当前展开项: {{ currentActiveItem }}</p>
    <p>上一个操作: {{ lastAction }}</p>

    <UAccordion
      :model-value="currentActiveItem"
      @update:model-value="handleAccordionChange"
      :items="accordionItems"
      type="single"
    />
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import type { AccordionItem } from '@nuxt/ui'

const accordionItems: AccordionItem[] = [
  { label: '产品介绍', content: '我们的产品功能强大,操作简便。', value: 'product' },
  { label: '技术支持', content: '遇到任何问题,请联系我们的技术支持团队。', value: 'support' },
  { label: '合作伙伴', content: '期待与您建立长期的合作关系。', value: 'partners' }
];

const currentActiveItem = ref<string | null>('product');
const lastAction = ref<string>('');

const handleAccordionChange = (newValue: string | string[]) => {
  console.log('手风琴状态改变了!新值:', newValue);

  // 根据新旧值判断是展开还是折叠
  if (Array.isArray(newValue)) { // 多选模式
    if (currentActiveItem.value && !newValue.includes(currentActiveItem.value[0])) { // 假设只关心第一个元素
      lastAction.value = `折叠了: ${currentActiveItem.value[0]}`;
    } else if (newValue.length > (currentActiveItem.value as string[]).length) {
      const newOpened = newValue.filter(val => !(currentActiveItem.value as string[]).includes(val));
      lastAction.value = `展开了: ${newOpened.join(', ')}`;
    } else {
      lastAction.value = '状态更新';
    }
  } else { // 单选模式
    if (newValue === null) {
      lastAction.value = `折叠了: ${currentActiveItem.value}`;
    } else if (currentActiveItem.value === null) {
      lastAction.value = `展开了: ${newValue}`;
    } else {
      lastAction.value = `${currentActiveItem.value} 切换到 ${newValue}`;
    }
  }

  // 更新组件的 active 状态,这是必须的,因为我们没有使用 v-model
  currentActiveItem.value = newValue as string | null;

  // 你可以在这里执行其他逻辑,例如发送分析事件,加载数据等
  if (newValue === 'support') {
    console.log('用户展开了技术支持,准备加载相关文档...');
    // loadSupportDocs();
  }
};
</script>

解释:

  • 我们使用 :model-value="currentActiveItem" 来向 UAccordion 传递当前展开的状态。
  • 我们通过 @update:model-value="handleAccordionChange" 监听事件。handleAccordionChange 函数会接收到最新的展开状态值。
  • 重要:在 handleAccordionChange 中,你必须手动更新 currentActiveItem.value = newValue,否则手风琴的展开状态将不会在 UI 上反映出来,因为它不再是自动双向绑定。
  • 你可以在 handleAccordionChange 函数中加入任何你需要的自定义逻辑。

Theme

app.config.ts
export default defineAppConfig({
  ui: {
    accordion: {
      slots: {
        root: 'w-full',
        item: 'border-b border-default last:border-b-0',
        header: 'flex',
        trigger: 'group flex-1 flex items-center gap-1.5 font-medium text-sm py-3.5 focus-visible:outline-primary min-w-0',
        content: 'data-[state=open]:animate-[accordion-down_200ms_ease-out] data-[state=closed]:animate-[accordion-up_200ms_ease-out] overflow-hidden focus:outline-none',
        body: 'text-sm pb-3.5',
        leadingIcon: 'shrink-0 size-5',
        trailingIcon: 'shrink-0 size-5 ms-auto group-data-[state=open]:rotate-180 transition-transform duration-200',
        label: 'text-start break-words'
      },
      variants: {
        disabled: {
          true: {
            trigger: 'cursor-not-allowed opacity-75'
          }
        }
      }
    }
  }
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'

export default defineConfig({
  plugins: [
    vue(),
    ui({
      ui: {
        accordion: {
          slots: {
            root: 'w-full',
            item: 'border-b border-default last:border-b-0',
            header: 'flex',
            trigger: 'group flex-1 flex items-center gap-1.5 font-medium text-sm py-3.5 focus-visible:outline-primary min-w-0',
            content: 'data-[state=open]:animate-[accordion-down_200ms_ease-out] data-[state=closed]:animate-[accordion-up_200ms_ease-out] overflow-hidden focus:outline-none',
            body: 'text-sm pb-3.5',
            leadingIcon: 'shrink-0 size-5',
            trailingIcon: 'shrink-0 size-5 ms-auto group-data-[state=open]:rotate-180 transition-transform duration-200',
            label: 'text-start break-words'
          },
          variants: {
            disabled: {
              true: {
                trigger: 'cursor-not-allowed opacity-75'
              }
            }
          }
        }
      }
    })
  ]
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import uiPro from '@nuxt/ui-pro/vite'

export default defineConfig({
  plugins: [
    vue(),
    uiPro({
      ui: {
        accordion: {
          slots: {
            root: 'w-full',
            item: 'border-b border-default last:border-b-0',
            header: 'flex',
            trigger: 'group flex-1 flex items-center gap-1.5 font-medium text-sm py-3.5 focus-visible:outline-primary min-w-0',
            content: 'data-[state=open]:animate-[accordion-down_200ms_ease-out] data-[state=closed]:animate-[accordion-up_200ms_ease-out] overflow-hidden focus:outline-none',
            body: 'text-sm pb-3.5',
            leadingIcon: 'shrink-0 size-5',
            trailingIcon: 'shrink-0 size-5 ms-auto group-data-[state=open]:rotate-180 transition-transform duration-200',
            label: 'text-start break-words'
          },
          variants: {
            disabled: {
              true: {
                trigger: 'cursor-not-allowed opacity-75'
              }
            }
          }
        }
      }
    })
  ]
})
在你的整个 Nuxt UI 应用程序中,每当你在 UAccordion 组件(或者 items 数组中的某个手风琴项)上设置了 disabled="true" 属性时,该手风琴项的 “标题/点击区域”(即 trigger)就会自动拥有一个 “禁止点击” 的鼠标游标和 75% 的不透明度。
variants 的作用

在 Nuxt UI 中,app.config.ts 文件允许你对所有组件进行全局默认样式的定制。而 variants 字段是其中非常重要的一个部分,它专门用来定义组件在不同状态或不同属性值下所展现的样式 “变体”。

你可以把它想象成:当组件的某个属性发生变化时,它的外观也会随之改变,而 variants 就是预先定义这些变化规则的地方
variants 的配置方式
variants 字段在 app.config.ts 中用于定义基于组件 props 值的条件样式。它的结构通常是:
variants: {
  // `propName` 是组件的 prop 名称
  propName: {
    // `propValue` 是 prop 的一个可能值
    propValue: {
      // `componentPart` 是组件内部的一个部分(比如 `root`, `title`, `description`, `icon` 等)
      componentPart: 'tailwind-css-classes'
    },
    // ... 其他 propValue
  },
  // ... 其他 propName
}
compoundVariants 的配置方式,就是:当一个或多个特定的 props 组合出现时,才应用某些额外的样式。compoundVariants 的语法结构它通常是 variants 同级的一个数组,每个数组项是一个对象:
// app.config.ts 或组件的 theme 定义中
export default defineAppConfig({
  ui: {
    ComponentName: {
      // ... 其他 slots 和 variants 配置

      compoundVariants: [
        {
          // 第一个对象定义了触发这个复合变体的 prop 组合
          // 这些键/值对必须与组件的 props 名称及其值匹配
          propName1: 'propValueA',
          propName2: 'propValueB',
          // ... 更多 prop 组合条件
          class: {
            // `componentPart` 是组件内部的一个部分(比如 `root`, `title`, `description`, `icon` 等)
            componentPart: 'tailwind-css-classes'
          } // 当所有条件都满足时应用的类
        },
        {
          // 第二个复合变体规则
          propNameX: 'propValueY',
          propNameZ: true,
          class: {
            // `componentPart` 是组件内部的一个部分(比如 `root`, `title`, `description`, `icon` 等)
            componentPart: 'tailwind-css-classes'
          }
        }
        // ... 更多复合变体
      ]
    }
  }
})
defaultVariants 的作用 defaultVariants 主要用于:
  • 设置组件的默认样式变体:它指定了在没有显式传递某些 prop 时,组件应该使用哪种颜色、变体、尺寸等。
  • 减少重复代码:避免在每次使用组件时都手动设置那些你希望大多数情况下都是默认的 prop。
  • 提供一致的基准外观:确保组件在被使用时,即使没有传递任何 prop,也能有一个合理的、符合设计规范的初始状态。
defaultVariants 的语法结构它通常是 variants 同级的一个对象,键是 prop 名称,值是该 prop 的默认值。
// app.config.ts 或组件的 theme 定义中
export default defineAppConfig({
  ui: {
    ComponentName: {
      // ... slots 和 variants 配置

      defaultVariants: {
        propName1: 'defaultValue1',
        propName2: 'defaultValue2',
        // ... 更多 prop 的默认值
      }
    }
  }
})
defaultVariants 如何与 propsvariants 协作defaultVariants 的优先级通常是这样的(从低到高):
  1. 组件内置主题的 defaultVariants: 组件本身定义的最基础的默认值。
  2. app.config.ts 中的 defaultVariants: 你在全局配置中覆盖或扩展的默认值。
  3. 组件实例上直接传递的 prop: 这是最高优先级,会覆盖所有默认值。