Dva 使用antd的Cascader動態獲取樹形省市區聯級選擇框的完整元件
效果圖:
就是實現這樣的,選擇框的時候,將向後臺請求省的資料,當點選省的時候,就會請求當前點選市的資料 。。。。,請求的介面只有一個,傳值為code值。樹形的資料。
完整的元件程式碼:
import React, { Component } from 'react';
import { Cascader, message } from 'antd';
import _ from 'lodash';
import { isSuccess } from 'utils';
import styles from './area.less'
import { queryArea } from './service';
let options = [];
const areaChildrenMap = {};
let functions = [];
const municipality = ['北京市', '上海市', '天津市', '重慶市', '香港特別行政區', '澳門特別行政區', ];
async function getData(value) {
let target = null;
let val = [];
let code = 0;
if (value && value.length) {
val = value.concat();
target = val.pop();
code = target.code;
}
const isLeaf = !!target && (municipality.includes(target.label) || value.length > 1);
const tem = val.length ? val.pop().value : 0;
await getDataByCode(code, isLeaf, areaChildrenMap[tem], target);
}
// 訂閱資料更改
function subscribe(callback) {
functions.push(callback);
}
// 取消訂閱
function unsubscribe(callback) {
functions = functions.filter(fun => fun !== callback);
}
async function getCodes(labels, list) {
const label = labels.shift();
const target = list.find(l => l.label === label);
if (!target) return [];
const code = target.code;
let arr = [code];
if (!labels.length) return arr;
if (!areaChildrenMap[code]) {
if (target.code.toString().slice(2, 6) == '0000' && !municipality.includes(target.name)) {
await getDataByCode(code, true, list);
} else {
await getDataByCode(code, true, list);
}
}
arr = arr.concat(await getCodes(labels, areaChildrenMap[code]));
return arr;
}
async function getDataByCode(code, isLeaf, list, target) {
const res = await queryArea(code);
target ? target.loading = false : null;
if (isSuccess(res)) {
res.data.forEach(d => {
d.value = d.code;
d.label = d.name;
d.isLeaf = isLeaf;
});
areaChildrenMap[code] = res.data;
if (!code) { // 第一層
options = res.data;
} else {
res.data.length && list.forEach(d => d.code === code ? d.children = res.data : null);
}
functions.forEach(fun => fun());
} else {
message.error(res.message);
}
}
getData();// 獲取第一層
class AreaSelect extends Component {
constructor(props, context) {
super(props, context);
this.state = {
value: [],
labels: [],
options,
};
}
componentWillMount() {
subscribe(this.update);
}
componentWillReceiveProps(nextProps) {
if (!nextProps.visible) {
this.setState({ value: [] });
}
if (nextProps.value && nextProps.value !== this.state.value) {
this.setValue(nextProps.value);
}
}
componentWillUnmount() {
unsubscribe(this.update);
}
onChange = (value, selectedOptions) => {
const { onChange } = this.props;
let labels = selectedOptions.map(o => o.label);
value = value.concat(new Array(3 - value.length).fill(''));
labels = labels.concat(new Array(3 - labels.length).fill(''));
this.setState({ value, labels });
onChange ? onChange(labels) : null;
}
async setValue(value, key, callback) {
if (!value.length) {
this.setState({ value: [], labels: [] }, callback);
return;
}
if (value.length < 3) value = value.slice(0, 2);
if (key === 'code') this.setState({ value }, callback);
else {
this.setState({ labels: value });
const val = await getCodes(value.concat(), options);
this.setState({ value: val }, callback);
}
}
getValue(key) {
if (key === 'code') return this.state.value.concat();
else return this.state.labels.concat();
}
update = () => {
this.setState({ options: options.concat() });
}
loadData = async (selectedOptions) => {
const targetOption = selectedOptions[selectedOptions.length - 1];
targetOption.loading = true;
await getData(selectedOptions);
}
render() {
const { ...props } = this.props;
return (
<div className={styles.wrapper}>
<Cascader
{...props}
options={this.state.options}
onChange={this.onChange}
loadData={this.loadData}
value={this.state.value}
placeholder="請選擇省市"
changeOnSelect
/>
</div>
);
}
}
export default AreaSelect;
isSuccess 的方法程式碼:
const error = {
success: {
code: '200',
message: '請求成功',
},
};
export function isSuccess(res) {
if (!res) return false;
const tem = res.code === error.success.code;
return tem;
}
queryArea:這是省市區的資料介面
使用方法:在需要的地方引入元件:<AreaSelect onChange={handleChangeAddress}/>