• Vant React

Cascader 级联选择

介绍

级联选择框,用于多层级数据的选择,典型场景为省市区选择。

引入

import { Cascader } from "@taroify/core"

代码演示

基础用法

级联选择组件可以搭配 Field 和 Popup 组件使用,示例如下:

import { Cascader, Field, Popup } from "@taroify/core"
import { ArrowRight } from "@taroify/icons"
import { useState } from "react"

const dept = [
  {
    label: "研发中心",
    value: "1",
    children: [
      {
        label: "产线1",
        value: "1-1",
        children: [
          {
            label: "研发",
            value: "1-1-1",
          },
          {
            label: "测试",
            value: "1-1-2",
          },
          {
            label: "产品",
            value: "1-1-3",
          }
        ]
      },
      {
        label: "产线2",
        value: "1-2",
      }
    ]
  },
  {
    label: "客户中心",
    value: "2",
    children: [
      {
        label: "客服",
        value: "2-1",
      },
      {
        label: "销售",
        value: "2-2",
      }
    ]
  }
]

function BasicCascader() {
  const [open, setOpen] = useState(false)
  const [value, setValue] = useState<string[]>([])
  const [fieldValue, setFieldValue] = useState("")
  return (
    <>
      <Field label="选项值" isLink onClick={() => setOpen(true)}>
        <Input readonly placeholder="请选择部门" value={fieldValue} />
      </Field>
      <Popup open={open} rounded placement="bottom" onClose={setOpen}>
        <Popup.Close />
        <Cascader
          options={dept}
          value={value}
          title="请选择部门"
          placeholder="请选择"
          onSelect={setValue}
          onChange={(_values_, options) => {
            setOpen(false)
            setFieldValue(
              options.map(item => item.children).join("/")
            )
          }}
        />
      </Popup>
    </>
  )
}

自定义颜色

通过 CSS 变量来设置选中状态的高亮颜色。

<Cascader className="custom-color" />
.custom-color {
  --tabs-active-color: red;
  --cascader-active-color: red;
}

异步加载选项

通过loadData返回下一级的选项,返回空数组[]时结束触发onChange


export const dynamic = [
  {
    label: "产品",
    value: "1"
  },
  {
    label: "测试",
    value: "2"
  },
  {
    label: "研发",
    value: "3"
  }
]

function DynamicCascader() {
  const [open, setOpen] = useState(false)
  const [value, setValue] = useState<string[]>([])
  const [fieldValue, setFieldValue] = useState("")
  return (
    <>
      <Field label="选项值" isLink onClick={() => setOpen(true)}>
        <Input readonly placeholder="请选择" value={fieldValue} />
      </Field>
      <Popup open={open} rounded placement="bottom" onClose={setOpen}>
        <Popup.Close />
        <Cascader
          options={dynamic}
          loadData={(_values_) => {
            const len = _values_.length
            return new Promise((resolve) => {
              resolve(len > 3 ? [] : [
                { label: `动态${len}-1`, value: Math.random() },
                { label: `动态${len}-2`, value: Math.random() }
              ])
            })
          }}
          title="请选择"
          swipeable
          value={value}
          onSelect={setValue}
          onChange={(_values_, options) => {
            setOpen(false)
            setFieldValue(
              options.map(item => item.children).join("/")
            )
          }}
        />
      </Popup>
    </>
  )
}

Tips: 数据量大时,可关闭 animated,swipeable,避免卡顿

自定义字段名

const fieldNames = {
  label: "name",
  value: "code",
  children: "data"
}
function CustomFieldCascader() {
  const [open, setOpen] = useState(false)
  const [value, setValue] = useState<string[]>([])
  const [fieldValue, setFieldValue] = useState("")
  return (
    <>
      <Field label="选项值" isLink onClick={() => setOpen(true)}>
        <Input readonly placeholder="请选择地区" value={fieldValue} />
      </Field>
      <Popup open={open} rounded placement="bottom" onClose={setOpen}>
        <Popup.Close />
        <Cascader
          options={customArea}
          fieldNames={fieldNames}
          value={value}
          title="请选择地区"
          placeholder="请选择"
          onSelect={setValue}
          onChange={(_values_, options) => {
            setOpen(false)
            setFieldValue(
              options.map(item => item.children).join("/")
            )
          }}
        />
      </Popup>
    </>
  )
}

手动控制DOM

可以通过 Cascader.Header, Cascader.Tab, Cascader.Option,配合 useCascader 自己控制DOM

import { Cascader, Field, Popup } from "@taroify/core"
import { ArrowRight } from "@taroify/icons"
import { useState } from "react"
import { useCascader } from "@taroify/hooks"

function BasicCascader() {
  const [open, setOpen] = useState(false)
  const [value, setValue] = useState<string[]>([])
  const [fieldValue, setFieldValue] = useState("")
  const { columns } = useCascader({ value, depth: 3, options: area })
  return (
    <>
      <Field label="选项值" isLink onClick={() => setOpen(true)}>
        <Input readonly placeholder="请选择地区" value={fieldValue} />
      </Field>
      <Popup open={open} rounded placement="bottom" onClose={setOpen}>
        <Popup.Close />
        <Cascader
          value={value}
          onSelect={setValue}
          onChange={(_values_, options) => {
            setOpen(false)
            setFieldValue(
              options.map(item => item.children).join("/")
            )
          }}
        >
          <Cascader.Header>请选择所在地区</Cascader.Header>
          {
            columns.map((options, index) => (
              <Cascader.Tab key={index}>
                {
                  options.map(option => (
                    <Cascader.Option key={option.value} value={option.value}>
                      {option.label}
                    </Cascader.Option>
                  ))
                }
              </Cascader.Tab>
            ))
          }
        </Cascader>
      </Popup>
    </>
  )
}

通过改变selected.children,同样可以动态加载选项

const onSelect = (_value) => {
  const level = _value.length - 1
  const selected = columns[level].find(item => item.value === _value[level]);
  if (selected && level < depth - 1) {
    selected.children = [
      { label: `动态${level}-1`, value: Math.random() },
      { label: `动态${level}-2`, value: Math.random() }
    ]
  }

  setValue(_value)
}

API

Cascader Props

参数说明类型默认值
value选中项的值string[]-
placeholder未选中时的提示文案ReactNode请选择
title顶部标题ReactNode-
swipeable是否开启手势左右滑动切换booleanfalse
animated是否开启动过渡动画booleantrue
options可选项数据源CascaderDataOption[]
loadData动态加载数据(values: string[], options: CascaderEventOption[]) => Promise<any[]>_
fieldNames自定义 options 结构中的字段CascaderFieldNames{ label: 'label', value: 'value', children: 'children' }

Cascader Events

事件说明回调参数
onSelect选中项变化时触发values: any[], options: CascaderEventOption[]
onChange全部选项选择完成后触发values: any[], options: CascaderEventOption[]
onTabClick点击标签时触发event: Tabs.TabEvent

Cascader.Header Props

参数说明类型默认值
children头部内容ReactNoe-

Cascader.Tab Props

参数说明类型默认值
children子选项列表ReactNoe-

Cascader.Option Props

参数说明类型默认值
children选项内容(必填)ReactNoe-
value选项对应的值(必填)string | number-
disabled是否禁用选项booleanfalse

类型定义

通过@taroify/core/cascader导入类型

import type { 
  CascaderDataOption, CascaderEventOption, CascaderFieldNames 
} from '@taroify/core/cascader'

主题定制

样式变量

组件提供了下列 CSS 变量,可用于自定义样式,使用方法请参考 ConfigProvider 组件。

名称默认值描述
—cascader-active-colorvar(—blue, $blue)-
—cascader-header-height48px * $hd-
—cascader-header-padding0 var(—padding-md)-
—cascader-header-font-sizevar(—font-size-lg)-
—cascader-header-font-weightvar(—font-weight-bold)-
—cascader-header-line-height20px * $hd-
—cascader-tabs-height48px * $hd-
—cascader-tab-font-weightvar(—font-weight-bold)-
—cascader-tab-colorvar(—text-color)-
—cascader-inactive-tab-colorvar(—gray-6)-
—cascader-options-height384px * $hd-
—cascader-option-padding10px * $hd var(—padding-md)-
—cascader-option-font-sizevar(—font-size-md)-
—cascader-option-line-heightvar(—line-height-md)-
—cascader-option-active-background-colorvar(—active-color)-
—cascader-disabled-option-colorvar(—gray-5)-
—cascader-active-option-colorvar(—cascader-active-color)-
—cascader-active-option-font-weightvar(—font-weight-bold)-
—cascader-active-icon-font-size18px * $hd-