跳转到内容

Cascader 级联选择

用于位置或类别等层级数据的级联选择框。

import { Cascader } from 'asterui'

基本用法

简单的层级选择。

import { Cascader } from 'asterui'

function App() {
  const options = [
    {
      value: 'usa',
      label: 'United States',
      children: [
        {
          value: 'ca',
          label: 'California',
          children: [
            { value: 'sf', label: 'San Francisco' },
            { value: 'la', label: 'Los Angeles' },
          ],
        },
        {
          value: 'ny',
          label: 'New York',
          children: [
            { value: 'nyc', label: 'New York City' },
            { value: 'buf', label: 'Buffalo' },
          ],
        },
      ],
    },
  ]
  
  return (
      <Cascader options={options} placeholder="Select location" />
    )
}

export default App

悬停展开

悬停时展开子菜单。

import { Cascader } from 'asterui'

function App() {
  const options = [
    {
      value: 'electronics',
      label: 'Electronics',
      children: [
        {
          value: 'phones',
          label: 'Phones',
          children: [
            { value: 'iphone', label: 'iPhone' },
            { value: 'android', label: 'Android' },
          ],
        },
        {
          value: 'computers',
          label: 'Computers',
          children: [
            { value: 'laptop', label: 'Laptop' },
            { value: 'desktop', label: 'Desktop' },
          ],
        },
      ],
    },
  ]
  
  return (
      <Cascader options={options} expandTrigger="hover" placeholder="Select category" />
    )
}

export default App

尺寸

不同的级联选择器尺寸。

import { Cascader } from 'asterui'

function App() {
  const options = [
    { value: 'opt1', label: 'Option 1' },
    { value: 'opt2', label: 'Option 2' },
  ]
  
  return (
      <div className="flex flex-col gap-2">
        <Cascader size="xs" options={options} placeholder="Extra small" />
        <Cascader size="sm" options={options} placeholder="Small" />
        <Cascader size="md" options={options} placeholder="Medium" />
        <Cascader size="lg" options={options} placeholder="Large" />
      </div>
    )
}

export default App

禁用状态

禁用的级联选择器状态。

import { Cascader } from 'asterui'

function App() {
  const options = [
    { value: 'opt1', label: 'Option 1' },
    { value: 'opt2', label: 'Option 2' },
  ]
  
  return (
      <Cascader options={options} disabled value={['opt1']} />
    )
}

export default App

搜索

启用搜索/过滤功能。

import { Cascader } from 'asterui'

function App() {
  const options = [
    {
      value: 'usa',
      label: 'United States',
      children: [
        {
          value: 'ca',
          label: 'California',
          children: [
            { value: 'sf', label: 'San Francisco' },
            { value: 'la', label: 'Los Angeles' },
          ],
        },
      ],
    },
  ]
  
  return (
      <Cascader options={options} showSearch placeholder="Search locations" />
    )
}

export default App

选择即改变

允许选择任何级别,而不仅是叶子节点。

Selected: None

import { Cascader } from 'asterui'
import { useState } from 'react'

function App() {
  const [value, setValue] = useState<string[]>([])
  const options = [
    {
      value: 'usa',
      label: 'United States',
      children: [
        { value: 'ca', label: 'California' },
        { value: 'ny', label: 'New York' },
      ],
    },
  ]
  
  return (
      <div>
        <Cascader
          options={options}
          changeOnSelect
          value={value}
          onChange={setValue}
          placeholder="Select any level"
        />
        <p className="mt-2 text-sm">Selected: {value.join(' / ') || 'None'}</p>
      </div>
    )
}

export default App

异步加载

展开时动态加载选项。

import { Cascader } from 'asterui'
import { useState } from 'react'

function App() {
  const [options, setOptions] = useState<CascaderOption[]>([
    { value: 'region1', label: 'Region 1' },
    { value: 'region2', label: 'Region 2' },
  ])
  
  const loadData = async (selectedOptions: CascaderOption[]) => {
    const targetOption = selectedOptions[selectedOptions.length - 1]
    await new Promise(resolve => setTimeout(resolve, 1000))
  
    setOptions(prev => {
      const update = (opts: CascaderOption[]): CascaderOption[] =>
        opts.map(opt =>
          opt.value === targetOption.value
            ? {
                ...opt,
                children: [
                  { value: `${opt.value}-1`, label: 'Child 1', isLeaf: true },
                  { value: `${opt.value}-2`, label: 'Child 2', isLeaf: true },
                ],
              }
            : { ...opt, children: opt.children ? update(opt.children) : undefined }
        )
      return update(prev)
    })
  }
  
  return (
      <Cascader options={options} loadData={loadData} placeholder="Load on expand" />
    )
}

export default App

验证状态

显示错误或警告验证状态。

import { Cascader } from 'asterui'

function App() {
  const options = [
    { value: 'opt1', label: 'Option 1' },
    { value: 'opt2', label: 'Option 2' },
  ]
  
  return (
      <div className="flex flex-col gap-2">
        <Cascader options={options} status="error" placeholder="Error state" />
        <Cascader options={options} status="warning" placeholder="Warning state" />
      </div>
    )
}

export default App

多选

选择多个叶子值。

import { Cascader } from 'asterui'

function App() {
  const options = [
    {
      value: 'fruits',
      label: 'Fruits',
      children: [
        { value: 'apple', label: 'Apple' },
        { value: 'banana', label: 'Banana' },
      ],
    },
    {
      value: 'vegetables',
      label: 'Vegetables',
      children: [
        { value: 'carrot', label: 'Carrot' },
        { value: 'lettuce', label: 'Lettuce' },
      ],
    },
  ]
  
  return (
      <Cascader options={options} multiple placeholder="Select multiple" />
    )
}

export default App
属性描述类型默认值
options层级选项数据CascaderOption[][]
value受控的选择值路径string[]-
defaultValue默认选择值(非受控)string[][]
onChange选择改变时的回调函数(value: string[], selectedOptions: CascaderOption[]) => void-
placeholder输入占位符文本string'请选择'
disabled禁用级联选择器booleanfalse
allowClear显示清除按钮booleantrue
expandTrigger如何展开子菜单'click' | 'hover''click'
changeOnSelect允许选择任何级别,而不仅是叶子booleanfalse
displayRender自定义显示渲染函数(labels: ReactNode[], selectedOptions: CascaderOption[]) => ReactNode-
size输入框大小'xs' | 'sm' | 'md' | 'lg''md'
color焦点环颜色'primary' | 'secondary' | 'accent' | 'info' | 'success' | 'warning' | 'error'-
status验证状态'error' | 'warning'-
showSearch启用搜索功能boolean | { filter?, render?, matchInputWidth? }false
notFoundContent无搜索结果时的内容ReactNode'未找到结果'
loadData异步数据加载函数(selectedOptions: CascaderOption[]) => Promise<void>-
fieldNames数据映射的自定义字段名{ label?: string; value?: string; children?: string }-
open受控的下拉框打开状态boolean-
onDropdownVisibleChange下拉框可见性改变时的回调函数(open: boolean) => void-
popupClassName下拉框的类名string-
dropdownRender自定义下拉框包装渲染(menu: ReactNode) => ReactNode-
multiple启用多选模式booleanfalse
maxTagCount多选模式下显示的最大标签数number | 'responsive'-
className额外的 CSS 类名string-
属性描述类型默认值
value唯一标识符string-
label显示标签ReactNode-
children子选项CascaderOption[]-
disabled禁用此选项booleanfalse
isLeaf强制作为叶子节点(不展开,不异步加载)boolean-

Cascader 组件遵循 WAI-ARIA combobox 模式:

  • 在触发器上使用 role="combobox",带有 aria-expandedaria-haspopup
  • aria-activedescendant 为屏幕阅读器跟踪聚焦的选项
  • aria-selected 指示选项的选中状态
  • aria-disabled 指示禁用状态
  • 清除按钮有 aria-label="清除选择"
按键操作
打开下拉框或移动到下一个选项
移动到上一个选项
移动到下一列(展开)
移动到上一列
回车选择聚焦的选项或打开下拉框
空格切换下拉框(非搜索时)
Esc关闭下拉框
Home移动到第一个选项
End移动到最后一个选项

组件包含用于测试的数据属性:

  • 根容器上的 data-testid="cascader"
  • 下拉框上的 data-testid="cascader-dropdown"
  • 每个选项上的 data-testid="cascader-option-{value}"
  • 清除按钮上的 data-testid="cascader-clear"
  • 根上的 data-state="open|closed" 指示下拉框状态
  • 选项上的 data-state="selected|hovered" 指示状态
  • 选项上的 data-value="{value}" 提供值