# MCascader 级联选择器

- 来源：[E10 UI 组件库](https://www.e-cology.com.cn/ui/mobile/cascader/freepass)
- 平台：移动端
- 维护者：屈正巍
- 设计稿：http://pd.e-cology.com.cn/pd/E10/interface/e10xiaoguotu/H5/E10zjys/#s15
- Issue：http://gitlab.ecology10.cn/FRONTEND/weapp-ui/-/issues?state=all&search=[MCascader]

## 代码演示

### 级联单选

当一个数据集合有清晰的层级结构时，可通过级联选择器逐级查看并选择

```jsx
import { Component } from "react";
import { constants, MCascader, Button, CascaderOptionType, CascaderValueType } from "@weapp/ui";
import { options } from '../mock';

const prefixCls = `${constants.uiPcClsPrefix}-demo-cascader`;

function filter(inputValue: string, path: CascaderOptionType[]) {
  //@ts-ignore
  return path.some((option: CascaderOptionType) => option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1);
}

type CascaderProps = {}
type CascaderState = {}

export default class CascaderDemo extends Component<CascaderProps, CascaderState> {

  onChange = (value: CascaderValueType, selectedOptions?: CascaderOptionType[]) => {
    console.info('value', value)
    console.info('selectedOptions', selectedOptions)
  }

  render() {
    return (
      <div className={prefixCls}>
        <p>基础用法</p>
        <MCascader
          showSearch={{ filter }}
          defaultValue={['hubei', 'wuhan', 'guanggu']}
          options={options}
          onChange={this.onChange}
        />
        <p>只读</p>
        <MCascader
          defaultValue={['hubei', 'wuhan', 'guanggu']}
          options={options}
          onChange={this.onChange}
          readOnly
        />
        <p>只读数据为空</p>
        <MCascader
          options={[]}
          readOnly
        />
        <p>自定义分隔符</p>
        <MCascader
          options={options}
          onChange={this.onChange}
          split={'#'}
        />
      </div>
    )
  }
}
```

### 级联单选受控

外部控制 value 与 visible

```jsx
import { Component } from "react";
import { constants, MCascader, MDialog, Button, CascaderOptionType, CascaderValueType } from "@weapp/ui";
import { options } from '../mock';

const { toast } = MDialog

const prefixCls = `${constants.uiPcClsPrefix}-demo-cascader`;

type CascaderProps = {}
type CascaderState = {
  value: CascaderValueType,
  visible: boolean,
}
export default class CascaderDemo extends Component<CascaderProps, CascaderState> {

  constructor(props: CascaderDemo) {
    super(props)
    this.state = {
      value: ['hubei', 'wuhan', 'guanggu'],
      // value: [],
      visible: false,
    }
  }

  onChange = (value: CascaderValueType, selectedOptions?: CascaderOptionType[]) => {
    console.info('value', value)
    console.info('selectedOptions', selectedOptions)
    this.setState({ value })
  }
  reset = () => this.setState({ value: [] })
  onOpen = () => this.setState({ visible: true })
  onClose = () => this.setState({ visible: false })
  onCancel = () => {
    toast({
      content: '取消选择',
      onClose: () => { console.info('onClose') }
    });
  }

  render() {
    const { value, visible } = this.state
    return (
      <div className={prefixCls}>
        <p>受控</p>
        <Button onClick={this.onOpen}>弹出MCascader</Button>
        <MCascader
          visible={visible}
          value={value}
          options={options}
          onChange={this.onChange}
          onDialogOpen={this.onOpen}
          onDialogClose={this.onClose}
          onCancel={this.onCancel}
        />
        &nbsp;
        <Button type={'primary'} size={'small'} onClick={this.reset}>重置</Button>
      </div>
    )
  }
}
```

### 级联单选视图

MCascaderView 是 MCascader 的内容区域

```jsx
import { Component } from "react";
import { constants, MCascader, Button, CascaderOptionType, CascaderValueType } from "@weapp/ui";
import { options } from '../mock';

const { MCascaderView } = MCascader

const prefixCls = `${constants.uiPcClsPrefix}-demo-cascader`;

type CascaderProps = {}
type CascaderState = {
  value: CascaderValueType,
}

export default class CascaderDemo extends Component<CascaderProps, CascaderState> {

  constructor(props: CascaderDemo) {
    super(props)
    this.state = {
      value: ['hubei', 'wuhan', 'guanggu'],
    }
  }

  onChange = (value: CascaderValueType) => console.info('value', value)
  onValueChange = (value: CascaderValueType) => {
    console.info('value', value)
    this.setState({ value })
  }
  reset = () => this.setState({ value: [] })

  render() {
    return (
      <div className={prefixCls}>
        <p>级联选择视图</p>
        <MCascaderView
          options={options}
          value={this.state.value}
          onChange={this.onValueChange}
        />
      </div>
    )
  }
}
```

### 级联单选视图融合城市接口

通过融合城市接口，实现城市选择器

```jsx
import { Component } from "react";
import { constants, MCascader, CascaderOptionType, CascaderValueType } from "@weapp/ui";
import { request, RequestResult } from '@weapp/utils'
import { options } from '../mock';

const { MCascaderView } = MCascader

const prefixCls = `${constants.uiPcClsPrefix}-demo-cascader`;

type CascaderProps = {}

export default class CascaderDemo extends Component<CascaderProps> {

  state = {
    value: ['hubei', 'wuhan', 'guanggu'],
    options: options
  }

  componentDidMount() {
    request({
      url: `/api/bcw/administrativeArea/cascade`,
      method: 'POST',
      data: { level: 'district' }
    }).then((result: RequestResult) => {
      if (result.status) {
        console.info('result', result)
        this.setState({ options: result.data.nodes })
      }
    })
  }

  onChange = (value: CascaderValueType) => console.info('value', value)
  onValueChange = (value: CascaderValueType) => {
    console.info('value', value)
    this.setState({ value })
  }
  reset = () => this.setState({ value: [] })

  render() {
    return (
      <div className={prefixCls}>
        <p>使用 城市配置接口 /api/bcw/administrativeArea/cascade 来自定义配置options内容</p>
        <MCascaderView
          options={this.state.options}
          onChange={this.onValueChange}
        />
      </div>
    )
  }
}
```

### 级联多选

当一个数据集合有清晰的层级结构时，可通过级联选择器逐级查看并选择多个数据

```jsx
import { Component } from "react";
import { constants, MCascader, Button, CascaderOptionType } from "@weapp/ui";
import { options } from '../mock';
const { MMultiCascader } = MCascader

const prefixCls = `${constants.uiPcClsPrefix}-demo-cascader`;

function filter(inputValue: string, path: CascaderOptionType[]) {
  //@ts-ignore
  return path.some((option: CascaderOptionType) => option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1);
}

type CascaderProps = {}
type CascaderState = {}

export default class CascaderDemo extends Component<CascaderProps, CascaderState> {

  onChange = (value: any, selectedOptions?: any[]) => {
    console.info('onChange-value', value)
    console.info('onChange-selectedOptions', selectedOptions)
  }
  reset = () => this.setState({ value: [] })

  render() {
    return (
      <div className={prefixCls}>
        <p>基础用法</p>
        <MMultiCascader
          defaultValue={[['hubei', 'wuhan', 'guanggu'], ['shanghai', 'minhang', 'fanwei']]}
          options={options}
          onChange={this.onChange}
          showSearch={{ filter }}
        />
        <p>只读</p>
        <MMultiCascader
          defaultValue={[['hubei', 'wuhan', 'guanggu'], ['shanghai', 'minhang', 'fanwei']]}
          options={options}
          onChange={this.onChange}
          readOnly
        />
        <p>只读数据为空</p>
        <MMultiCascader
          options={[]}
          readOnly
        />
        <p>自定义分隔符</p>
        <MMultiCascader
          options={options}
          onChange={this.onChange}
          split={'#'}
        />
      </div>
    )
  }
}
```

### 级联多选受控

外部控制 value 与 visible

```jsx
import { Component } from "react";
import { constants, MCascader, Button, MDialog } from "@weapp/ui";
import { options } from '../mock';

const { MMultiCascader } = MCascader
const { toast } = MDialog

const prefixCls = `${constants.uiPcClsPrefix}-demo-cascader`;

type CascaderProps = {}
type CascaderState = {
  value: any[],
  visible: boolean
}

export default class CascaderDemo extends Component<CascaderProps, CascaderState> {

  constructor(props: CascaderDemo) {
    super(props)
    this.state = {
      value: [
        [
          "hubei",
          "wuhan",
          "guanggu"
        ],
        [
          "hubei",
          "wuhan",
          "hankouhuochezhan"
        ],
        [
          "hubei",
          "wuhan",
          "wandaguangchang"
        ],
        [
          "shanghai",
          "minhang",
          "fanwei"
        ]
      ],
      visible: false,
    }
  }

  onChange = (value: any, selectedOptions?: any) => {
    console.info('onValueChange-value', value)
    console.info('onValueChange-selectedOptions', selectedOptions)
    this.setState({ value })
  }
  reset = () => this.setState({ value: [] })
  onOpen = () => this.setState({ visible: true })
  onClose = () => this.setState({ visible: false })
  onCancel = () => {
    toast({
      content: '取消选择',
      onClose: () => { console.info('onClose') }
    });
  }

  render() {
    const { value } = this.state
    return (
      <div className={prefixCls}>
        <Button onClick={this.onOpen}>弹出MMultiCascader</Button>
        <p>受控</p>
        <MMultiCascader
          value={value}
          options={options}
          onChange={this.onChange}
          onDialogOpen={this.onOpen}
          onDialogClose={this.onClose}
          onCancel={this.onCancel}
        />
        &nbsp;
        <Button type={'primary'} size={'small'} onClick={this.reset}>重置</Button>
      </div>
    )
  }
}
```

### 异步数据处理

当options为异步时数据回显的处理

```jsx
import { Component } from "react";
import { constants, MCascader, MButton, CascaderOptionType, CascaderValueType } from "@weapp/ui";
import { options2 } from '../mock';
const { MMultiCascader } = MCascader

const prefixCls = `${constants.uiPcClsPrefix}-demo-cascader`;

type CascaderProps = {}
type CascaderState = {
  options: CascaderOptionType[],
  value1: CascaderValueType,
  value2: CascaderValueType[],
  defaultLabel1?: any[],
  defaultLabel2?: any[],
}

export default class CascaderDemo extends Component<CascaderProps, CascaderState> {

  constructor(props: CascaderDemo) {
    super(props)
    this.state = {
      options: [],
      value1: ['yzy1111', 'yzy11111'],
      value2: [["yzy1111", "yzy11111"], ["yzy3333", "yzy33333"], ["yzy3333", "yzy33332"]],
      defaultLabel1: ['金蝶', 'K33'],
      defaultLabel2: [['金蝶', 'K33'], ['泛微', 'e10'], ['泛微', 'e9']],
    }
  }
  componentDidMount() {
    setTimeout(() => {
      this.setState({
        options: options2,
        // defaultLabel1: void 0,
        // defaultLabel2: void 0,
      })
    }, 1000);
  }
  onChange = (value: any, selectedOptions?: any[]) => {
    console.info('value', value)
    console.info('selectedOptions', selectedOptions)
  }
  onValue1Change = (value: any, selectedOptions?: any) => {
    console.info('value', value)
    console.info('selectedOptions', selectedOptions)
    this.setState({ value1: value })
  }
  onValue2Change = (value: any, selectedOptions?: any) => {
    console.info('value', value)
    console.info('selectedOptions', selectedOptions)
    this.setState({ value2: value })
  }
  reset1 = () => this.setState({ value1: [] })
  reset2 = () => this.setState({ value2: [] })

  render() {
    const { options, value1, value2, defaultLabel1, defaultLabel2 } = this.state
    return (
      <div className={prefixCls}>
        <h3>单选</h3>
        <p>options异步与defaultLabel处理</p>
        <p>受控</p>
        <MCascader
          options={options}
          defaultLabel={defaultLabel1}
          value={value1}
          onChange={this.onValue1Change}
        />
        <MButton type={'primary'} onClick={this.reset1}>重置</MButton>
        <p>非受控</p>
        <MCascader
          options={options}
          defaultLabel={defaultLabel1}
          onChange={this.onChange}
        />
        <h3>多选</h3>
        <p>options异步与defaultLabel处理</p>
        <p>受控</p>
        <MMultiCascader
          options={options}
          defaultLabel={defaultLabel2}
          value={value2}
          onChange={this.onValue2Change}
        />
        <MButton type={'primary'} onClick={this.reset2}>重置</MButton>
        <p>非受控</p>
        <MMultiCascader
          options={options}
          defaultLabel={defaultLabel2}
          onChange={this.onChange}
        />
      </div>
    )
  }
}
```

### 空数据

当options为空时的展示

```jsx
import { Component } from "react";
import { constants, MCascader, Button, CascaderOptionType, CascaderValueType } from "@weapp/ui";
import { options } from '../mock';

const { MMultiCascader } = MCascader

const prefixCls = `${constants.uiPcClsPrefix}-demo-cascader`;

type CascaderProps = {}
type CascaderState = {}

export default class CascaderDemo extends Component<CascaderProps, CascaderState> {

  render() {
    return (
      <div className={prefixCls}>
        <p>单选空数据</p>
        <MCascader
          options={[]}
        />
        <p>多选空数据</p>
        <MMultiCascader
          options={[]}
        />
      </div>
    )
  }
}
```

### 默认数据选中

新增在没有数据的时候增加默认选中的功能

```jsx
import { Component } from "react";
import { constants, MCascader, MDialog, Button, CascaderOptionType, CascaderValueType } from "@weapp/ui";
import { options } from '../mock';
const { toast } = MDialog

const initialActiveValue = ['shanghai', 'minhang', 'fanwei'];

const prefixCls = `${constants.uiPcClsPrefix}-demo-cascader`;

function filter(inputValue: string, path: CascaderOptionType[]) {
  //@ts-ignore
  return path.some((option: CascaderOptionType) => option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1);
}

type CascaderProps = {}
type CascaderState = {
  value: CascaderValueType,
  visible: boolean,
}

export default class CascaderDemo extends Component<CascaderProps, CascaderState> {

  constructor(props: CascaderDemo) {
    super(props)
    this.state = {
      // value: ['hubei', 'wuhan', 'guanggu'],
      value: [],
      visible: false,
    }
  }

  onChange = (value: CascaderValueType, selectedOptions?: CascaderOptionType[]) => {
    console.info('value', value)
    console.info('selectedOptions', selectedOptions)
  }

  onValueChange = (value: CascaderValueType, selectedOptions?: CascaderOptionType[]) => {
    console.info('value', value)
    console.info('selectedOptions', selectedOptions)
    this.setState({ value })
  }
  reset = () => this.setState({ value: [] })
  onOpen = () => this.setState({ visible: true })
  onClose = () => this.setState({ visible: false })
  onCancel = () => {
    toast({
      content: '取消选择',
      onClose: () => { console.info('onClose') }
    });
  }

  render() {
    const { value, visible } = this.state
    return (
      <div className={prefixCls}>
        <p>基础用法</p>
        <MCascader
          showSearch={{ filter }}
          options={options}
          onChange={this.onChange}
          // value={initialActiveValue}
          initialActiveValue={initialActiveValue}
        />
        <p>受控</p>
        <Button onClick={this.onOpen}>弹出MCascader</Button>
        <MCascader
          visible={visible}
          // value={value}
          options={options}
          onChange={this.onValueChange}
          onDialogOpen={this.onOpen}
          onDialogClose={this.onClose}
          onCancel={this.onCancel}
          // value={initialActiveValue}
          initialActiveValue={initialActiveValue}
        />
        &nbsp;
        <Button type={'primary'} size={'small'} onClick={this.reset}>重置</Button>
      </div>
    )
  }
}
```

## API

### 注意：

```typescript
1.在options的length不大于等一的情况下，无论value怎么变化，默认展示都会按照defaultLabel来进行渲染
```

### 类型说明：（请从@weapp/ui 中导出）

```typescript
1.option类型
type CascaderOptionType =  {
  value?: string | number;
  label?: React.ReactNode;
  disabled?: boolean;
  isLeaf?: boolean;
  loading?: boolean;
  children?: Array<CascaderOptionType>;
  [key: string]: any;
};

2.onChange抛出的value类型
type CascaderValueType = (string | number)[];
```

### 参数说明如下：

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| disabled | 选择器是否禁用 | boolean | 否 | false |
| readOnly | 选择器是否完全只读 | boolean |  |  |
| className | 自定义类名 | string |  |  |
| dialogProps | 弹出层的 props | 参考移动端 dialog 组件的 props | 否 |  |
| style | 自定义样式 | CSSProperties |  |  |
| options | 可选项数据源 | Option[] | 是 |  |
| defaultValue | 默认的选中项 | string[] \| number[] |  |  |
| value | 指定选中项 | string[] \| number[] |  |  |
| loadData | 用于动态加载选项，无法与 `showSearch` 一起使用 | (selectedOptions) => {} |  |  |
| notFoundContent | 当下拉列表为空时显示的内容 | string |  |  |
| placeholder | 输入框占位文本 | string |  |  |
| menuPlaceholder | 未选中时的提示文案 | string |  |  |
| visible | 控制浮层显隐 | boolean |  |  |
| showSearch | 是否为搜索模式 | boolean \| 搜索模式说明 |  | true |
| iconName | 后缀 icon | icon 组件的 iconNames / ''(不想展示 icon 请传'') | 否 | false |
| onChange | 选择完成后的回调 | (value: CascaderValueType, selectedOptions?: CascaderOptionType[]) => {} |  |  |
| onCancel | 点击取消的回调 | () => {} |  |  |
| onDialogOpen | 弹层展开时的回调 | (value: CascaderValueType, selectedOptions?: CascaderOptionType[]) => {} | 否 |  |
| onDialogClose | 弹层关闭时的回调 | (value: CascaderValueType, selectedOptions?: CascaderOptionType[]) => {} | 否 |  |
| isFilterIDMatchNoLabelValue | 是否过滤id匹配不到value的数据 | boolean |  | true |
| isFilterOnChange | 数据过滤后是否触发onchange抛出过滤后的value以及selectedOptions | boolean |  | true |
### showSearch

`` 为对象时，其中的字段：

| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| filter | 接收 `inputValue` `path` 两个参数，当 `path` 符合筛选条件时，应返回 true，反之则返回 false | function(inputValue, path): boolean | - |
| limit | 搜索结果展示数量 | number \| false | 99999 |
| matchInputWidth | 搜索结果列表是否与输入框同宽（效果） | boolean | true |
| render | 用于渲染 filter 后的选项 | function(inputValue, path): ReactNode | - |
| sort | 用于排序 filter 后的选项 | function(a, b, inputValue) | - |
