# uni-collapse

折叠面板组件

本 Component 是 uni ext component,需下载插件:uni-collapse

折叠面板组件,包含:

  • uni-collapse:父容器
  • uni-collapse-item:子面板

一个折叠面板组件可以包含若干uni-collapse-item。每个uni-collapse-item包括顶部标题栏和通过slot传入的内容区。

# 基本用法

<uni-collapse :accordion="true">
  <uni-collapse-item title="标题 1" :open="true">
    <view><text>内容 1</text></view>
  </uni-collapse-item>
  <uni-collapse-item title="标题 2">
    <view><text>内容 2</text></view>
  </uni-collapse-item>
</uni-collapse>

uni-collapse-item的标题栏的自定义:

  • 标题栏容器可通过 title-wrap-class 自定义背景等容器样式。
  • 标题栏支持title属性,可通过title-class自定义文字样式。
  • 标题展开态可通过 title-open-class 自定义激活文字样式。组件默认使用透明度区分状态,便于外部继续覆盖文字颜色。
  • 标题禁用态可通过 title-disabled-class 自定义禁用文字样式。若同时存在展开态和禁用态,以禁用态为最终状态。
  • 内容外层容器可通过 content-wrap-class 自定义背景等容器样式。
  • 标题栏右边默认带有箭头,可通过arrow-class自定义箭头样式。该箭头为一个view包括2个直角边的边框,然后旋转而成。
  • 箭头展开态和禁用态可分别通过 arrow-open-classarrow-disabled-class 覆盖。组件默认也使用透明度区分状态,便于外部继续覆盖箭头颜色。
  • 如果完全不想要默认包含的title和arrow,也可以传入一个名为title的具名插槽来替换。

# 具名插槽标题

<uni-collapse-item>
  <template #title="{ open, disabled }">
    <view class="custom-title">
      <view class="custom-title-icon"></view>
      <text class="custom-title-text">{{ open ? '已展开' : '未展开' }}</text>
    </view>
  </template>
  <view><text>自定义标题内容</text></view>
</uni-collapse-item>

# externalClass

<uni-collapse-item
  title="自定义样式"
  title-wrap-class="my-title-wrap-class"
  title-class="my-title-class"
  title-open-class="my-title-open-class"
  content-wrap-class="my-content-wrap-class"
  arrow-class="my-arrow-class"
  arrow-open-class="my-arrow-open-class"
>
  <view><text>内容区域</text></view>
</uni-collapse-item>

# Slots

# uni-collapse-item

插槽名 说明 插槽参数
default 面板内容 -
title 自定义标题区域 open: booleandisabled: boolean

# Events

# uni-collapse-item

事件名 说明 回调参数
change 面板展开状态变化时触发 open: boolean

# Expose

# uni-collapse-item

方法名 说明
openCollapse(open: boolean) 执行展开/收起,会遵循 disabled 和手风琴逻辑
openOrClose(open: boolean) 直接切换内容显示状态

# 暗黑模式适配

建议通过组件 class 和 externalClass 组合传入暗色样式:

  • 用组件 class 统一控制根节点和内容区等外层视觉。
  • title-wrap-classtitle-classtitle-open-classcontent-wrap-classarrow-classarrow-open-class 等 externalClass 覆盖标题区、内容外层和箭头样式。
  • 默认展开态和禁用态主要通过透明度表达,减少颜色覆盖时的交叉冲突。

# 注意

  • 本组件在Android上需HBuilderX 5.09+。在低版本上箭头变颜色+旋转时,在Android上会消失

# 兼容性

Web 微信小程序 Android Android(Vapor) iOS iOS(Vapor) HarmonyOS HarmonyOS(Vapor)
5.07 5.07 5.07 x 5.07 x 5.07 5.07

# 属性

名称 类型 默认值 兼容性 描述
accordion boolean true - 是否开启手风琴模式,开启后同一时间仅允许一个子项展开

# 示例

示例为hello uni-app x alpha分支,与最新HBuilderX Alpha版同步。与最新正式版同步的master分支示例另见

扫码体验(手机浏览器跳转到App直达页)

示例

<template>
  <scroll-view class="page">
    <view class="section">
      <text class="section-title">手风琴模式</text>
      <uni-collapse :accordion="true">
        <uni-collapse-item title="面板 A(默认展开)" :open="data.enableOpen" @change="onPanelAChange">
          <view class="content-box" @click="onPanelAClick">
            <text class="content-text">手风琴模式下,同时只会保持一个面板展开。点击当前标题会自动收起其他面板。</text>
          </view>
        </uni-collapse-item>
        <uni-collapse-item title="面板 B" @change="onPanelBChange">
          <view id="panel-b" class="content-box">
            <text class="content-text">当前项展开时,其他已展开的项会自动收起。这里用于验证切换动画和事件回调。</text>
          </view>
        </uni-collapse-item>
        <uni-collapse-item title="禁用项" :disabled="true">
          <view class="content-box">
            <text class="content-text">禁用状态下不会响应点击,也不会触发展开或收起。</text>
          </view>
        </uni-collapse-item>
      </uni-collapse>
    </view>

    <view class="section">
      <text class="section-title">普通模式-可同时展开的分组折叠列表</text>
      <uni-collapse :accordion="false">
        <uni-collapse-item v-for="(group, index) in categoryGroups" :key="group.title" :title="group.title" @change="onCategoryChange(group.title, $event as boolean)">
          <view class="category-list">
            <view v-for="(item, itemIndex) in group.items" :key="item.title" class="category-list-item"
              :class="{'category-list-item--last': itemIndex == group.items.length - 1}">
              <text class="category-list-text">{{ item.title }}</text>
              <view class="category-list-arrow"></view>
            </view>
          </view>
        </uni-collapse-item>
      </uni-collapse>
    </view>

    <view class="section">
      <text class="section-title">externalClass 示例</text>
      <uni-collapse :accordion="false">
        <uni-collapse-item title="自定义标题文字" title-class="demo-title-emphasis">
          <view class="content-box">
            <text class="content-text">通过 title-class 可以覆盖 title 属性对应 text 组件的样式。</text>
          </view>
        </uni-collapse-item>
        <uni-collapse-item title="自定义箭头样式" arrow-class="demo-arrow-large">
          <view class="content-box">
            <text class="content-text">通过 arrow-class 可以覆盖默认箭头 view 的尺寸、边框粗细和颜色。</text>
          </view>
        </uni-collapse-item>
        <uni-collapse-item title="同时自定义标题和箭头" title-class="demo-title-accent" arrow-class="demo-arrow-accent">
          <view class="content-box">
            <text class="content-text">title-class 和 arrow-class 可以组合使用。</text>
          </view>
        </uni-collapse-item>
      </uni-collapse>
    </view>

    <view class="section">
      <text class="section-title">具名插槽标题</text>
      <uni-collapse :accordion="false">
        <uni-collapse-item>
          <template #title="{ open, disabled }">
            <view class="slot-title-wrap">
              <view class="slot-title-icon" :class="{'slot-title-icon--open': open, 'slot-title-icon--disabled': disabled}"></view>
              <text class="slot-title-text" :class="{'slot-title-text--open': open, 'slot-title-text--disabled': disabled}">消息中心</text>
              <text class="slot-title-badge" :class="{'slot-title-badge--open': open}">{{ open ? 'OPEN' : 'CLOSE' }}</text>
            </view>
          </template>
          <view class="content-box">
            <text class="content-text">通过 #title 可以自定义标题区域结构,例如在文字前增加图标、状态徽标和辅助说明。</text>
          </view>
        </uni-collapse-item>
        <uni-collapse-item>
          <template #title="{ open }">
            <view class="slot-title-wrap">
              <view class="slot-title-icon slot-title-icon--secondary" :class="{'slot-title-icon--open': open}"></view>
              <text class="slot-title-text" :class="{'slot-title-text--open': open}">设置中心</text>
              <text class="slot-title-note">{{ open ? '当前已展开' : '点击展开配置项' }}</text>
            </view>
          </template>
          <view class="content-box">
            <text class="content-text">具名插槽会透出 open 和 disabled 状态,方便你在标题区域里联动样式。</text>
          </view>
        </uni-collapse-item>
      </uni-collapse>
    </view>

    <view class="section section-dark">
      <text class="section-title section-title-dark">暗黑模式 externalClass 示例</text>
      <view class="dark-tip">
        <text class="dark-tip-text">这个示例通过给 uni-collapse-item 传入 class 和 externalClass,分别定制内容区和标题区样式。</text>
      </view>
      <uni-collapse :accordion="false">
        <uni-collapse-item class="dark-collapse-item" title="暗色标题 A" :open="true" title-wrap-class="dark-title-wrap" title-class="dark-title-text" title-open-class="dark-title-text-open" content-wrap-class="dark-content-wrap" arrow-class="dark-arrow" arrow-open-class="dark-arrow-open">
          <view class="content-box dark-content-box">
            <text class="content-text dark-content-text">组件根节点通过 class 统一接入暗色卡片样式,标题背景、默认文字、展开态文字和箭头颜色通过 externalClass 传入。</text>
          </view>
        </uni-collapse-item>
        <uni-collapse-item class="dark-collapse-item dark-collapse-item-strong" title="暗色标题 B" title-wrap-class="dark-title-wrap-strong" title-class="dark-title-text-strong" title-open-class="dark-title-text-open" content-wrap-class="dark-content-wrap" arrow-class="dark-arrow-strong" arrow-open-class="dark-arrow-open">
          <view class="content-box dark-content-box">
            <text class="content-text dark-content-text">你也可以继续组合不同的根容器 class、标题字重、箭头粗细和激活色样式。</text>
          </view>
        </uni-collapse-item>
        <uni-collapse-item class="dark-collapse-item dark-collapse-item-muted" title="暗色禁用项" :disabled="true" title-wrap-class="dark-title-wrap-muted" title-class="dark-title-text" title-disabled-class="dark-title-text-disabled" content-wrap-class="dark-content-wrap-muted" arrow-class="dark-arrow" arrow-disabled-class="dark-arrow-disabled">
          <view class="content-box dark-content-box dark-content-box-muted">
            <text class="content-text dark-content-text">禁用态默认通过透明度表达状态,外部依然可以继续指定禁用态文字和箭头颜色。</text>
          </view>
        </uni-collapse-item>
      </uni-collapse>
    </view>

    <view class="log-section">
      <text class="log-title">最近事件</text>
      <text class="log-text">{{ data.lastEvent }}</text>
    </view>
  </scroll-view>
</template>

<script setup lang="uts">
  type CategoryItem = {
    title : string
  }

  type CategoryGroup = {
    title : string,
    items : CategoryItem[]
  }

  const categoryGroups : CategoryGroup[] = [
    {
      title: '分类1',
      items: [
        { title: '分类1-item1' },
        { title: '分类1-item2' },
        { title: '分类1-item3' }
      ]
    },
    {
      title: '分类2',
      items: [
        { title: '分类2-item1' },
        { title: '分类2-item2' },
        { title: '分类2-item3' }
      ]
    },
    {
      title: '分类3',
      items: [
        { title: '分类3-item1' },
        { title: '分类3-item2' },
        { title: '分类3-item3' }
      ]
    }
  ]

  type Data = {
    lastEvent: string,
    enableOpen: boolean
  }

  const data = reactive<Data>({
    lastEvent: '暂无交互',
    enableOpen: true
  })

  function updateEvent(panel : string, open : boolean) {
    const stateText = open ? '展开' : '收起'
    const message = `${panel}${stateText}`
    data.lastEvent = message
    console.log(`[collapse] ${message}`)
  }

  function onPanelAChange(open : boolean) {
    updateEvent('面板 A ', open)
  }

  function onPanelBChange(open : boolean) {
    updateEvent('面板 B ', open)
  }

  function onCategoryChange(title : string, open : boolean) {
    updateEvent(`${title} `, open)
  }

  const onPanelAClick = () => {
    console.log('onPanelAClick')
  }

  defineExpose({
    data,
    onPanelAClick
  })
</script>

<style>
  .page {
    flex: 1;
    padding: 12px;
  }

  .section {
    margin-bottom: 12px;
    border-radius: 8px;
    overflow: hidden;
    background-color: var(--list-background-color, #ffffff);
  }

  .section-title {
    padding: 12px;
    font-size: 14px;
    color: var(--text-color, #333333);
    font-weight: 500;
  }

  .content-box {
    padding: 12px;
    background-color: var(--active-background-color, #f8fafc);
  }

  .content-text {
    font-size: 13px;
    color: var(--text-color, #475569);
    opacity: 0.78;
    line-height: 20px;
  }

  .category-list {
    background-color: var(--active-background-color, #f8fafc);
  }

  .category-list-item {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    padding: 12px 18px;
    border-bottom-width: 1px;
    border-bottom-style: solid;
    border-bottom-color: var(--border-color, #e2e8f0);
  }

  .category-list-item--last {
    border-bottom-width: 0px;
  }

  .category-list-text {
    flex: 1;
    font-size: 13px;
    color: var(--text-color, #334155);
    line-height: 20px;
  }

  .category-list-arrow {
    width: 8px;
    height: 8px;
    margin-left: 12px;
    transform: rotate(-45deg);
    border-right-width: 1px;
    border-right-style: solid;
    border-right-color: var(--arrow-color, #94a3b8);
    border-bottom-width: 1px;
    border-bottom-style: solid;
    border-bottom-color: var(--arrow-color, #94a3b8);
  }

  .demo-title-emphasis {
    font-size: 16px;
    font-weight: 700;
  }

  .demo-title-accent {
    font-size: 15px;
    font-weight: 700;
    color: #0f766e;
  }

  .demo-arrow-large {
    width: 12px;
    height: 12px;
    border-right-width: 2px;
    border-bottom-width: 2px;
  }

  .demo-arrow-accent {
    border-right-color: #0f766e;
    border-bottom-color: #0f766e;
    border-right-width: 2px;
    border-bottom-width: 2px;
  }

  .slot-title-wrap {
    flex: 1;
    flex-direction: row;
    align-items: center;
  }

  .slot-title-icon {
    width: 10px;
    height: 10px;
    border-radius: 5px;
    margin-right: 10px;
    background-color: #94a3b8;
  }

  .slot-title-icon--secondary {
    background-color: #c084fc;
  }

  .slot-title-icon--open {
    background-color: #2563eb;
  }

  .slot-title-icon--disabled {
    background-color: #cbd5e1;
  }

  .slot-title-text {
    flex: 1;
    font-size: 14px;
    color: var(--text-color, #1f2937);
    font-weight: 600;
  }

  .slot-title-text--open {
    color: #2563eb;
  }

  .slot-title-text--disabled {
    color: #94a3b8;
  }

  .slot-title-badge {
    padding: 4px 8px;
    border-radius: 10px;
    background-color: #e2e8f0;
    font-size: 11px;
    color: #475569;
  }

  .slot-title-badge--open {
    background-color: #dbeafe;
    color: #1d4ed8;
  }

  .slot-title-note {
    font-size: 12px;
    color: #64748b;
  }

  .section-dark {
    background-color: #111827;
  }

  .section-title-dark {
    color: #e5e7eb;
  }

  .dark-collapse-item {
    background-color: #111827;
  }

  .dark-collapse-item-strong {
    background-color: #0f172a;
  }

  .dark-collapse-item-muted {
    background-color: #101826;
  }

  .dark-tip {
    padding: 0px 12px 12px 12px;
  }

  .dark-tip-text {
    font-size: 12px;
    color: #9ca3af;
    line-height: 18px;
  }

  .dark-content-box {
    background-color: #1f2937;
  }

  .dark-content-wrap {
    background-color: #1f2937;
  }

  .dark-content-text {
    color: #cbd5e1;
  }

  .dark-content-box-muted {
    background-color: #182333;
  }

  .dark-content-wrap-muted {
    background-color: #182333;
  }

  .dark-title-wrap {
    background-color: #111827;
  }

  .dark-title-wrap-strong {
    background-color: #0f172a;
  }

  .dark-title-wrap-muted {
    background-color: #101826;
  }

  .dark-title-text {
    color: #f8fafc;
  }

  .dark-title-text-strong {
    font-size: 15px;
    font-weight: 700;
    color: #e2e8f0;
  }

  .dark-title-text-open {
    color: #60a5fa;
  }

  .dark-title-text-disabled {
    color: #94a3b8;
  }

  .dark-arrow {
    border-right-color: #cbd5e1;
    border-bottom-color: #cbd5e1;
  }

  .dark-arrow-strong {
    border-right-color: #cbd5e1;
    border-bottom-color: #cbd5e1;
    border-right-width: 2px;
    border-bottom-width: 2px;
  }

  .dark-arrow-open {
    border-right-color: #60a5fa;
    border-bottom-color: #60a5fa;
  }

  .dark-arrow-disabled {
    border-right-color: #94a3b8;
    border-bottom-color: #94a3b8;
  }

  .log-section {
    padding: 12px;
    border-radius: 8px;
    background-color: var(--list-background-color, #ffffff);
  }

  .log-title {
    font-size: 13px;
    color: var(--text-color, #333333);
    font-weight: 500;
    margin-bottom: 8px;
  }

  .log-text {
    font-size: 13px;
    color: #2563eb;
  }
</style>

# 参见

# uni-collapse-item

折叠面板子项组件

本 Component 是 uni ext component,需下载插件:uni-collapse-item

折叠面板组件,包含:

  • uni-collapse:父容器
  • uni-collapse-item:子面板

一个折叠面板组件可以包含若干uni-collapse-item。每个uni-collapse-item包括顶部标题栏和通过slot传入的内容区。

# 基本用法

<uni-collapse :accordion="true">
  <uni-collapse-item title="标题 1" :open="true">
    <view><text>内容 1</text></view>
  </uni-collapse-item>
  <uni-collapse-item title="标题 2">
    <view><text>内容 2</text></view>
  </uni-collapse-item>
</uni-collapse>

uni-collapse-item的标题栏的自定义:

  • 标题栏容器可通过 title-wrap-class 自定义背景等容器样式。
  • 标题栏支持title属性,可通过title-class自定义文字样式。
  • 标题展开态可通过 title-open-class 自定义激活文字样式。组件默认使用透明度区分状态,便于外部继续覆盖文字颜色。
  • 标题禁用态可通过 title-disabled-class 自定义禁用文字样式。若同时存在展开态和禁用态,以禁用态为最终状态。
  • 内容外层容器可通过 content-wrap-class 自定义背景等容器样式。
  • 标题栏右边默认带有箭头,可通过arrow-class自定义箭头样式。该箭头为一个view包括2个直角边的边框,然后旋转而成。
  • 箭头展开态和禁用态可分别通过 arrow-open-classarrow-disabled-class 覆盖。组件默认也使用透明度区分状态,便于外部继续覆盖箭头颜色。
  • 如果完全不想要默认包含的title和arrow,也可以传入一个名为title的具名插槽来替换。

# 具名插槽标题

<uni-collapse-item>
  <template #title="{ open, disabled }">
    <view class="custom-title">
      <view class="custom-title-icon"></view>
      <text class="custom-title-text">{{ open ? '已展开' : '未展开' }}</text>
    </view>
  </template>
  <view><text>自定义标题内容</text></view>
</uni-collapse-item>

# externalClass

<uni-collapse-item
  title="自定义样式"
  title-wrap-class="my-title-wrap-class"
  title-class="my-title-class"
  title-open-class="my-title-open-class"
  content-wrap-class="my-content-wrap-class"
  arrow-class="my-arrow-class"
  arrow-open-class="my-arrow-open-class"
>
  <view><text>内容区域</text></view>
</uni-collapse-item>

# Slots

# uni-collapse-item

插槽名 说明 插槽参数
default 面板内容 -
title 自定义标题区域 open: booleandisabled: boolean

# Events

# uni-collapse-item

事件名 说明 回调参数
change 面板展开状态变化时触发 open: boolean

# Expose

# uni-collapse-item

方法名 说明
openCollapse(open: boolean) 执行展开/收起,会遵循 disabled 和手风琴逻辑
openOrClose(open: boolean) 直接切换内容显示状态

# 暗黑模式适配

建议通过组件 class 和 externalClass 组合传入暗色样式:

  • 用组件 class 统一控制根节点和内容区等外层视觉。
  • title-wrap-classtitle-classtitle-open-classcontent-wrap-classarrow-classarrow-open-class 等 externalClass 覆盖标题区、内容外层和箭头样式。
  • 默认展开态和禁用态主要通过透明度表达,减少颜色覆盖时的交叉冲突。

# 注意

  • 本组件在Android上需HBuilderX 5.09+。在低版本上箭头变颜色+旋转时,在Android上会消失

# 兼容性

Web 微信小程序 Android Android(Vapor) iOS iOS(Vapor) HarmonyOS HarmonyOS(Vapor)
5.07 5.07 5.07 x 5.07 x 5.07 5.07

# 属性

名称 类型 默认值 兼容性 描述
title string "" - 折叠项标题文字
open boolean false - 是否默认展开
disabled boolean false - 是否禁用,禁用后点击标题不会展开或收起
titleWrapClass string(string.ClassString) "" - 标题行(整行容器)的样式类
titleClass string(string.ClassString) "" - 标题文字的基础样式类
titleOpenClass string(string.ClassString) "" - 展开状态下叠加到标题文字的样式类
titleDisabledClass string(string.ClassString) "" - 禁用状态下叠加到标题文字的样式类
contentWrapClass string(string.ClassString) "" - 内容外层容器的样式类
arrowClass string(string.ClassString) "" - 箭头图标的基础样式类
arrowOpenClass string(string.ClassString) "" - 展开状态下叠加到箭头图标的样式类
arrowDisabledClass string(string.ClassString) "" - 禁用状态下叠加到箭头图标的样式类
@change Event - - -

# 参见