# MCityPicker 城市选择器

- 来源：[E10 UI 组件库](https://www.e-cology.com.cn/ui/mobile/city-picker/freepass)
- 平台：移动端
- 维护者：屈正巍
- Issue：http://gitlab.ecology10.cn/FRONTEND/weapp-ui/-/issues?state=all&search=[CityPicker]

## 代码演示

### 基础用法

在同一个选择器里便捷选择带有级联关系的城市区域

```jsx
import { Component } from "react";
import { constants, MCityPicker, CascaderValueType, CascaderOptionType } from "@weapp/ui";

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

type MCityPickerDemoState = {
  value: string;
}

export default class MCityPickerDemo extends Component<any, MCityPickerDemoState> {

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

  render() {
    return (
      <div className={prefixCls}>
        <h4>基础用法</h4>
        <MCityPicker
          level={'city'}
          onChange={this.onChange}
        />
        <br />
        <h4>关闭最近选项</h4>
        <MCityPicker
          level={'city'}
          onChange={this.onChange}
          useHistory={false}
        />
        <br />
        <h4>CityPicker城市选择器继承了Cascader级联选择器的特性</h4>
        <MCityPicker
          onChange={this.onChange}
          cascaderProps={{
            placeholder: '自定义placeholder',
            split: '#'
          }}
        />
        <h4>只读</h4>
        <MCityPicker
          cascaderProps={{
            defaultLabel: ['测试只读'],
            readOnly: true
          }}
        />
        <h4>只读数据为空</h4>
        <MCityPicker
          cascaderProps={{
            defaultLabel: [],
            readOnly: true
          }}
        />
      </div>
    )
  }
}
```

### 城市单选

可以通过type设置城市选择器的层级

```jsx
import { Component } from "react";
import { constants, MCityPicker, CascaderValueType, CascaderOptionType } from "@weapp/ui";

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

export default class MCityPickerDemo extends Component<any> {

  state = {
    value: []
  }

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

  render() {
    const { value } = this.state
    return (
      <div className={prefixCls}>
        <h4>国家级</h4>
        <MCityPicker
          onChange={this.onChange}
          cityPickerMode={'union'}
          value={value}
          level={'country'}
        />
        <br />
        <h4>省级</h4>
        <MCityPicker
          onChange={this.onChange}
          level={'province'}
        />
        <br />
        <h4>市级</h4>
        <MCityPicker
          onChange={this.onChange}
          level={'city'}
        />
        <h4>区县级</h4>
        <MCityPicker
          onChange={this.onChange}
        />
        <br />
      </div>
    )
  }
}
```

### 城市多选

在同一个选择器里便捷选择多个带有级联关系的城市区域

```jsx
import { Component } from "react";
import { constants, MCityPicker, CascaderValueType, CascaderOptionType } from "@weapp/ui";

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

type MCityPickerDemoState = {
  value1: any[];
}

export default class MCityPickerDemo extends Component<any, MCityPickerDemoState> {

  state = {
    value1: [],
  }

  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({ value1: value })
  }

  render() {
    const { value1 } = this.state
    return (
      <div className={prefixCls}>
        <h4>国家多选</h4>
        <MCityPicker
          level={'country'}
          onChange={this.onValueChange}
          multiple
          value={value1}
        />
        <br />
        <h4>省份多选</h4>
        <MCityPicker
          level={'province'}
          onChange={this.onChange}
          multiple
        />
        <br />
        <h4>市级多选</h4>
        <MCityPicker
          level={'city'}
          onChange={this.onChange}
          multiple
        />
        <br />
        <h4>区县多选</h4>
        <MCityPicker
          level={'district'}
          onChange={this.onChange}
          multiple
        />
        <br />
      </div>
    )
  }
}
```

### 数据回显

城市选择器的数据是异步请求的，在首次渲染时，需要采用defaultLabel来做假回显。逻辑说明：面板展开前，使用defaultLabel的数据进行展示，面板展开时，会根据异步请求到的options与value来进行数据匹配重新拼接出展示用的label，在使用时需要同时传入value与defaultLabel（逻辑继承级联选择器）

```jsx
import { Component } from "react";
import { constants, MCityPicker, CascaderValueType, CascaderOptionType, MButton } from "@weapp/ui";

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

type MCityPickerDemoState = {
  value: any[];
  multiValue: any[];
  defaultLabel: any[];
  multiDefaultLabel: any[];
  initValue: any[];
  initMultiValue: any[];
  initDefaultLabel: any[];
  initMultiDefaultLabel: any[]
}

export default class MCityPickerDemo extends Component<any, MCityPickerDemoState> {

  state = {
    value: ['691955639858126848', '691955644220203009', '691955648590667776'],
    defaultLabel: ['中国', '湖北省', '武汉市'],
    multiValue: [
      ["691955639858126848", "691955644220203009", "691955648590667776"],
      ["691955639858126848", "691955652751417366", "691955657256099840"]
    ],
    multiDefaultLabel: [
      ["中国", "湖北省", "武汉市"],
      ["中国", "湖北省", "宜昌市"],
      ["中国", "湖北省", "恩施土家族苗族自治州"]
    ],
    initValue: [],
    initDefaultLabel: ['中国', '湖北省', '武汉市'],
    initMultiValue: [],
    initMultiDefaultLabel: [
      ["中国", "湖北省", "武汉市"],
      ["中国", "湖北省", "宜昌市"],
      ["中国", "湖北省", "恩施土家族苗族自治州"]
    ]
  }

  onChange = (value: CascaderValueType, selectedOptions?: CascaderOptionType[]) => {
    console.info('value', value)
    console.info('selectedOptions', selectedOptions)
    const defaultLabel = selectedOptions?.map((item) => item.label) || []
    console.info('defaultLabel', defaultLabel)
    this.setState({
      value,
      defaultLabel
    })
  }
  onMultiChange = (multiValue: any, selectedOptions?: any[]) => {
    console.info('onMultiChange-multiValue', multiValue)
    console.info('onMultiChange-selectedOptions', selectedOptions)
    const multiDefaultLabel = selectedOptions?.map((item) => item.map((i: any) => i.label)) || []
    console.info('onMultiChange-multiDefaultLabel', multiDefaultLabel)
    this.setState({
      multiValue,
      multiDefaultLabel
    })
  }
  changeLabel = () => this.setState({ defaultLabel: ['中国', '上海市', '上海市'] })
  changeMultiLabel = () => this.setState({
    multiDefaultLabel: [
      ["中国", "上海市", "上海市"],
      ["中国", "天津市", "天津市"],
      ["中国", "北京市", "北京市"]
    ]
  })
  changeValue = () => this.setState({ initValue: ['691955639858126848', '691955644220203009', '691955648590667776'] })
  changeMultiValue = () => this.setState({
    initMultiValue: [
      ["691955639858126848", "691955644220203009", "691955648590667776"],
      ["691955639858126848", "691955652751417366", "691955657256099840"]
    ]
  })
  onChangeRender = (value: CascaderValueType, selectedOptions?: CascaderOptionType[]) => {
    console.info('value', value)
    console.info('selectedOptions', selectedOptions)
  }
  onInitValueChange = (value: CascaderValueType, selectedOptions?: CascaderOptionType[]) => {
    console.info('value', value)
    console.info('selectedOptions', selectedOptions)
    const defaultLabel = selectedOptions?.map((item) => item.label) || []
    this.setState({
      initValue: value,
      initDefaultLabel: defaultLabel
    })
  }
  onMultiInitValueChange = (multiValue: any, selectedOptions?: any[]) => {
    console.info('onMultiChange-multiValue', multiValue)
    console.info('onMultiChange-selectedOptions', selectedOptions)
    const multiDefaultLabel = selectedOptions?.map((item) => item.map((i: any) => i.label)) || []
    this.setState({
      initMultiValue: multiValue,
      initMultiDefaultLabel: multiDefaultLabel
    })
  }
  render() {
    const {
      value, defaultLabel,
      multiValue, multiDefaultLabel,
      initValue, initDefaultLabel,
      initMultiValue, initMultiDefaultLabel
    } = this.state
    return (
      <div className={prefixCls}>
        <h4>label数据回显</h4>
        <MButton
          type={'primary'}
          onClick={this.changeLabel}
        >点击改变defaultLabel</MButton>
        <br />
        <MCityPicker
          level={'city'}
          onChange={this.onChange}
          value={value}
          cascaderProps={{
            defaultLabel: defaultLabel
          }}
        />
        <br />
        <h4>仅value无label的情况下想要强制渲染label</h4>
        <MCityPicker
          level={'city'}
          value={['691955639858126848', '691955644220203009', '691955648590667776']}
          forceRenderLabel
          onChange={this.onChangeRender}
        />
        <MCityPicker
          level={'city'}
          multiple
          value={[["691955639858126848", "691955644220203009", "691955648590667776"],
          ["691955639858126848", "691955652751417366", "691955657256099840"]]}
          forceRenderLabel
          onChange={this.onChangeRender}
        />
        <br />
        <h4>value改变options为空时(点击下方数据不改变)</h4>
        <MButton
          type={'primary'}
          onClick={this.changeValue}
        >点击改变value</MButton>
        <br />
        <MCityPicker
          level={'city'}
          onChange={this.onInitValueChange}
          value={initValue}
          cascaderProps={{
            defaultLabel: initDefaultLabel
          }}
        />
        <br />
        <h4>数据回显只读</h4>
        <MCityPicker
          level={'city'}
          cascaderProps={{
            defaultLabel: defaultLabel,
            readOnly: true
          }}
        />
        <br />
        <h4>数据回显禁用</h4>
        <MCityPicker
          level={'city'}
          cascaderProps={{
            defaultLabel: defaultLabel,
            disabled: true
          }}
        />
        <br />
        <h4>多选数据回显</h4>
        <MButton
          type={'primary'}
          onClick={this.changeMultiLabel}
        >点击改变defaultLabel</MButton>
        <MCityPicker
          level={'city'}
          onChange={this.onMultiChange}
          value={multiValue}
          multiple
          cascaderProps={{
            defaultLabel: multiDefaultLabel
          }}
        />
        <br />
        <h4>value改变options为空时(点击下方数据不改变)</h4>
        <MButton
          type={'primary'}
          onClick={this.changeMultiValue}
        >点击改变multiValue</MButton>
        <MCityPicker
          level={'city'}
          onChange={this.onMultiInitValueChange}
          value={initMultiValue}
          multiple
          cascaderProps={{
            defaultLabel: initMultiDefaultLabel
          }}
        />
        <br />
        <h4>多选数据回显只读</h4>
        <MCityPicker
          level={'city'}
          multiple
          cascaderProps={{
            defaultLabel: multiDefaultLabel,
            readOnly: true
          }}
        />
        <br />
        <h4>多选数据回显禁用</h4>
        <MCityPicker
          level={'city'}
          multiple
          cascaderProps={{
            defaultLabel: multiDefaultLabel,
            disabled: true
          }}
        />
      </div>
    )
  }
}
```

### 输入框渲染层级

可以通过displayLevel或者后台的数据配置进行自定义展示

```jsx
import { Component } from "react";
import { constants, MCityPicker, CascaderValueType, CascaderOptionType } from "@weapp/ui";

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

type MCityPickerDemoState = {
  value: string;
}

export default class MCityPickerDemo extends Component<any, MCityPickerDemoState> {

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

  render() {
    return (
      <div className={prefixCls}>
        <p>展示层级优先读取props中的displayLevel，若无数据则可在后台管理中心中进行自定义配置</p>
        <h4>国家级</h4>
        <MCityPicker
          onChange={this.onChange}
          level={'country'}
        />
        <br />
        <h4>省级</h4>
        <MCityPicker
          onChange={this.onChange}
          level={'province'}
          displayLevel={'province'}
        />
        <br />
        <h4>市级</h4>
        <MCityPicker
          onChange={this.onChange}
          level={'city'}
          displayLevel={'city'}
        />
        <h4>区县级</h4>
        <MCityPicker
          onChange={this.onChange}
          displayLevel={'district'}
        />
        <br />
        <h4>国家级多选</h4>
        <MCityPicker
          onChange={this.onChange}
          level={'country'}
          multiple
        />
        <br />
        <h4>省级多选</h4>
        <MCityPicker
          onChange={this.onChange}
          level={'province'}
          displayLevel={'province'}
          multiple
        />
        <br />
        <h4>市级多选</h4>
        <MCityPicker
          onChange={this.onChange}
          level={'city'}
          displayLevel={'city'}
          multiple
        />
        <h4>区县级多选</h4>
        <MCityPicker
          onChange={this.onChange}
          displayLevel={'district'}
          multiple
        />
        <br />
      </div>
    )
  }
}
```

### 行政区域-等级-联合选择器

可以根据城市的等级关系，快捷选择所需城市

```jsx
import { Component } from "react";
import { constants, MCityPicker, CascaderValueType, CascaderOptionType } from "@weapp/ui";

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

type MCityPickerDemoState = {
  value: string;
}

export default class MCityPickerDemo extends Component<any, MCityPickerDemoState> {

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

  render() {
    return (
      <div className={prefixCls}>
        <h4>行政区域选择器</h4>
        <MCityPicker
          onChange={this.onChange}
          level={'city'}
          mode={'region'}
        />
        <br />
        <h4>等级选择器</h4>
        <MCityPicker
          onChange={this.onChange}
          level={'city'}
          mode={'rating'}
        />
        <br />
        <h4>行政区域-等级联合选择器-只有城市选择器生效</h4>
        <MCityPicker
          onChange={this.onChange}
          level={'city'}
          mode={'union'}
        />
        <br />
        <h4>表单中使用（根据接口返回数据判断mode模式）</h4>
        <MCityPicker
          onChange={this.onChange}
          level={'city'}
          mode={'rating'}
          requestData={{
            "businessId": "788345271817486336",
            "controlId": "788345507952279553",
          }}
        />
      </div>
    )
  }
}
```

### 行政区域-等级-联合多选

可以根据城市的等级关系，快捷选择多个城市

```jsx
import { Component } from "react";
import { constants, MCityPicker, CascaderValueType, CascaderOptionType } from "@weapp/ui";

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

type MCityPickerDemoState = {
  value: string;
}

export default class MCityPickerDemo extends Component<any, MCityPickerDemoState> {

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

  render() {
    return (
      <div className={prefixCls}>
        <h4>行政区域选择器</h4>
        <MCityPicker
          onChange={this.onChange}
          level={'city'}
          mode={'region'}
          multiple
        />
        <br />
        <h4>等级选择器</h4>
        <MCityPicker
          onChange={this.onChange}
          level={'city'}
          mode={'rating'}
          multiple
        />
        <br />
        <h4>行政区域-等级联合选择器-只有城市选择器生效</h4>
        <MCityPicker
          onChange={this.onChange}
          level={'city'}
          mode={'union'}
          multiple
        />
        <br />
        <h4>表单中使用（根据接口返回数据判断mode模式）</h4>
        <MCityPicker
          onChange={this.onChange}
          level={'city'}
          mode={'rating'}
          multiple
          requestData={{
            "businessId": "788345271817486336",
            "controlId": "788345507952279553",
          }}
        />
      </div>
    )
  }
}
```

### 默认数据选中

新增默认数据选中的功能

```jsx
import { Component } from "react";
import { constants, MCityPicker, CascaderValueType, CascaderOptionType, MButton } from "@weapp/ui";

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

export default class MCityPickerDemo extends Component<any> {

  state = {
    // useLetterIndex: false
    useLetterIndex: true
  }

  onChange = (value: CascaderValueType, selectedOptions?: CascaderOptionType[]) => {
    console.info('value', value)
    console.info('selectedOptions', selectedOptions)
    this.setState({ value })
  }
  onChangeIndex = () => {
    this.setState({ useLetterIndex: !this.state.useLetterIndex })
  }

  render() {
    return (
      <div className={prefixCls}>
        <MButton onClick={this.onChangeIndex}>{`点击${this.state.useLetterIndex ? '关闭' : '开启'}字母索引`}</MButton>
        <h4>省级</h4>
        <MCityPicker
          onChange={this.onChange}
          useLetterIndex={this.state.useLetterIndex}
          useLevelPanel
          level={'province'}
        />
        <MCityPicker
          onChange={this.onChange}
          useLetterIndex={this.state.useLetterIndex}
          useLevelPanel
          level={'province'}
          multiple
        />
        <br />
        <h4>市级</h4>
        <MCityPicker
          onChange={this.onChange}
          useLetterIndex={this.state.useLetterIndex}
          useLevelPanel
          level={'city'}
        />
        <MCityPicker
          onChange={this.onChange}
          useLetterIndex={this.state.useLetterIndex}
          useLevelPanel
          level={'city'}
          multiple
        />
        <br />
        <h4>区县级</h4>
        <MCityPicker
          onChange={this.onChange}
          useLetterIndex={this.state.useLetterIndex}
          useLevelPanel
        />
        <MCityPicker
          onChange={this.onChange}
          useLetterIndex={this.state.useLetterIndex}
          useLevelPanel
          multiple
        />
        <br />
      </div>
    )
  }
}
```

## API

### 注意：

```typescript
1.组件融合了城市配置接口，用于快捷的选中带有级联关系的城市
2.组件继承了Cascader级联选择器的特性
3.组件融合了后端的城市接口: '/api/bcw/administrativeArea/cascade'(维护人：师源)
4.具体的城市数据可以在 后台管理中心->通用设置->城市配置页面 进行自定义配置
5.在options的length不大于等一的情况下，无论value怎么变化，默认展示都会按照defaultLabel来进行渲染
```

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| disabled | 选择器是否禁用 | boolean | 否 |  |
| readOnly | 选择器是否完全只读 | boolean | 否 |  |
| onChange | 选择完成后的回调 | (value: CascaderValueType, selectedOptions: CascaderOptionType) => void | 否 |  |
| level | 城市选择器的层级 | 'countries' \| 'country' \| 'province' \| 'city' \| 'district' | 否 | 'district' |
| displayLevel | 展示层级(输入框内部展示到具体哪层数据) | 'countries' \| 'country' \| 'province' \| 'city' \| 'district' | 否 | 'district' |
| cascaderProps | 选择完成后的回调 | CascaderProps (参考级联选择器的 props) | 否 |  |
| mode | 城市选择器的模式 | 'region' \| 'rating' \| 'union' | 否 | 'region' |
| defaultModeValue | 城市选择器的模式默认值 | 'region' \| 'rating' \| 'union' | 否 |  |
| requestData | 行政区域接口的自定义 post 补充参数 | any | 否 |  |
| fetchRegionData | 自定义请求行政区域数据 | (options: RequestOptionConfig) => RequestPromise; | 否 |  |
| onRegionRequestError | 行政区域请求错误异常处理 | (e: Event) => void; | 否 |  |
| fetchLevelData | 自定义请求等级数据 | (options: RequestOptionConfig) => RequestPromise; | 否 |  |
| onLevelRequestError | 等级请求错误异常处理 | (e: Event) => void; | 否 |  |
| fetchTabData | 自定义请求 tab 数据 | (options: RequestOptionConfig) => RequestPromise; | 否 |  |
| onTabRequestError | tab 请求错误异常处理 | (e: Event) => void; | 否 |  |
| loginFree | 是否免登录 | boolean | 否 |  |
| useLetterIndex | 使用多级面板开启字母筛选 | boolean | 否 | false |
| useHistory | 是否开启最近选项 | boolean | 否 | true |
| forceRenderLabel | 仅value无label的情况下想要强制渲染label | boolean | 否 | false |
