forked from p96170835/amis
添加城市选择器
This commit is contained in:
parent
ca42d937ca
commit
a0a4c7198a
|
@ -122,14 +122,14 @@ export function normalizeOptions(options: string | {[propName: string]: string}
|
||||||
interface SelectProps {
|
interface SelectProps {
|
||||||
classPrefix: string;
|
classPrefix: string;
|
||||||
classnames: ClassNamesFn;
|
classnames: ClassNamesFn;
|
||||||
className: string;
|
className?: string;
|
||||||
creatable: boolean;
|
creatable: boolean;
|
||||||
multiple: boolean;
|
multiple: boolean;
|
||||||
valueField: string;
|
valueField: string;
|
||||||
labelField: string;
|
labelField: string;
|
||||||
searchable: boolean;
|
searchable?: boolean;
|
||||||
options: Array<Option>;
|
options: Array<Option>;
|
||||||
value: Option | Array<Option>;
|
value: any;
|
||||||
loadOptions?: Function;
|
loadOptions?: Function;
|
||||||
searchPromptText: string;
|
searchPromptText: string;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
|
@ -175,6 +175,7 @@ export class Select extends React.Component<SelectProps, SelectState> {
|
||||||
promptTextCreator: (label: string) => `新增:${label}`,
|
promptTextCreator: (label: string) => `新增:${label}`,
|
||||||
onNewOptionClick: noop,
|
onNewOptionClick: noop,
|
||||||
inline: false,
|
inline: false,
|
||||||
|
disabled: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
input: HTMLInputElement;
|
input: HTMLInputElement;
|
||||||
|
|
|
@ -76,6 +76,7 @@ import './renderers/Form/Number';
|
||||||
import './renderers/Form/Textarea';
|
import './renderers/Form/Textarea';
|
||||||
import './renderers/Form/Checkboxes';
|
import './renderers/Form/Checkboxes';
|
||||||
import './renderers/Form/Checkbox';
|
import './renderers/Form/Checkbox';
|
||||||
|
import './renderers/Form/City';
|
||||||
import './renderers/Form/Rating';
|
import './renderers/Form/Rating';
|
||||||
import './renderers/Form/Switch';
|
import './renderers/Form/Switch';
|
||||||
import './renderers/Form/Button';
|
import './renderers/Form/Button';
|
||||||
|
|
|
@ -0,0 +1,327 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import {
|
||||||
|
FormItem,
|
||||||
|
FormControlProps
|
||||||
|
} from './Item';
|
||||||
|
import db, {
|
||||||
|
province,
|
||||||
|
city,
|
||||||
|
district
|
||||||
|
} from './CityDB';
|
||||||
|
import { ClassNamesFn, themeable } from '../../theme';
|
||||||
|
import { Select } from '../../components';
|
||||||
|
import { autobind } from '../../utils/helper';
|
||||||
|
import { Option } from './Options';
|
||||||
|
|
||||||
|
|
||||||
|
export interface CityPickerProps {
|
||||||
|
value:any;
|
||||||
|
onChange: (value:any) => void;
|
||||||
|
joinValues: boolean;
|
||||||
|
delimiter: string;
|
||||||
|
classnames: ClassNamesFn;
|
||||||
|
classPrefix: string;
|
||||||
|
className?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
allowCity: boolean;
|
||||||
|
allowDistrict: boolean;
|
||||||
|
allowStreet: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface CityPickerState {
|
||||||
|
code: number;
|
||||||
|
province: string;
|
||||||
|
provinceCode: number;
|
||||||
|
city: string;
|
||||||
|
cityCode: number;
|
||||||
|
district: string;
|
||||||
|
districtCode: number;
|
||||||
|
street: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class CityPicker extends React.Component<CityPickerProps, CityPickerState> {
|
||||||
|
static defaultProps = {
|
||||||
|
joinValues: true,
|
||||||
|
delimiter: ',',
|
||||||
|
allowCity: true,
|
||||||
|
allowDistrict: true,
|
||||||
|
allowStreet: false
|
||||||
|
};
|
||||||
|
|
||||||
|
state = {
|
||||||
|
code: 0,
|
||||||
|
province: '',
|
||||||
|
provinceCode: 0,
|
||||||
|
city: '',
|
||||||
|
cityCode: 0,
|
||||||
|
district: '',
|
||||||
|
districtCode: 0,
|
||||||
|
street: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
this.syncIn();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps:CityPickerProps) {
|
||||||
|
const props = this.props;
|
||||||
|
|
||||||
|
if (props.value !== nextProps.value) {
|
||||||
|
this.syncIn(nextProps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleProvinceChange(option:Option) {
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
province: option.label as string,
|
||||||
|
provinceCode: option.value as number,
|
||||||
|
city: '',
|
||||||
|
cityCode: 0,
|
||||||
|
district: '',
|
||||||
|
districtCode: 0,
|
||||||
|
street: '',
|
||||||
|
code: option.value
|
||||||
|
}, this.syncOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleCityChange(option:Option) {
|
||||||
|
if (option.value % 100) {
|
||||||
|
return this.handleDistrictChange(option, {
|
||||||
|
cityCode: option.value as number
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
city: option.label as string,
|
||||||
|
cityCode: option.value as number,
|
||||||
|
district: '',
|
||||||
|
districtCode: 0,
|
||||||
|
street: '',
|
||||||
|
code: option.value
|
||||||
|
}, this.syncOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleDistrictChange(option:Option, otherStates:Partial<CityPickerState> = {}) {
|
||||||
|
this.setState({
|
||||||
|
...otherStates as any,
|
||||||
|
district: option.label as string,
|
||||||
|
districtCode: option.value as number,
|
||||||
|
street: '',
|
||||||
|
code: option.value as number
|
||||||
|
}, this.syncOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleStreetChange(e:React.ChangeEvent<HTMLInputElement>) {
|
||||||
|
this.setState({
|
||||||
|
street: e.currentTarget.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleStreetEnd() {
|
||||||
|
this.syncOut();
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
syncIn(props = this.props) {
|
||||||
|
const {
|
||||||
|
value,
|
||||||
|
delimiter
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
code: 0,
|
||||||
|
province: '',
|
||||||
|
provinceCode: 0,
|
||||||
|
city: '',
|
||||||
|
cityCode: 0,
|
||||||
|
district: '',
|
||||||
|
districtCode: 0,
|
||||||
|
street: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
let code = value && value.code
|
||||||
|
|| typeof value === "number" && value
|
||||||
|
|| typeof value === "string" && /(\d{6})/.test(value) && RegExp.$1;
|
||||||
|
|
||||||
|
if (code && db[code]) {
|
||||||
|
code = parseInt(code, 10);
|
||||||
|
state.code = code;
|
||||||
|
|
||||||
|
const provinceCode = code - (code % 10000);
|
||||||
|
if (db[provinceCode]) {
|
||||||
|
state.provinceCode = provinceCode;
|
||||||
|
state.province = db[provinceCode];
|
||||||
|
}
|
||||||
|
|
||||||
|
const cityCode = code - (code % 100);
|
||||||
|
if (db[cityCode]) {
|
||||||
|
state.cityCode = cityCode;
|
||||||
|
state.city = db[cityCode];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code % 100) {
|
||||||
|
state.district = db[code];
|
||||||
|
state.districtCode = code;
|
||||||
|
}
|
||||||
|
} else if (value) {
|
||||||
|
// todo 模糊查找
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value && value.street) {
|
||||||
|
state.street = value.street;
|
||||||
|
} else if (typeof value === "string" && ~value.indexOf(delimiter)) {
|
||||||
|
state.street = value.slice(value.indexOf(delimiter) + delimiter.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
syncOut() {
|
||||||
|
const {
|
||||||
|
onChange,
|
||||||
|
allowStreet,
|
||||||
|
joinValues,
|
||||||
|
delimiter
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const {
|
||||||
|
code,
|
||||||
|
province,
|
||||||
|
city,
|
||||||
|
district,
|
||||||
|
street
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
|
if (joinValues) {
|
||||||
|
code ? onChange(allowStreet && street ? [code, street].join(delimiter) : String(code)) : onChange('');
|
||||||
|
} else {
|
||||||
|
onChange({
|
||||||
|
code,
|
||||||
|
province,
|
||||||
|
city,
|
||||||
|
district,
|
||||||
|
street
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
classnames: cx,
|
||||||
|
className,
|
||||||
|
disabled,
|
||||||
|
allowCity,
|
||||||
|
allowDistrict,
|
||||||
|
allowStreet
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const {
|
||||||
|
provinceCode,
|
||||||
|
cityCode,
|
||||||
|
districtCode,
|
||||||
|
street
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cx('CityPicker', className)}>
|
||||||
|
<Select
|
||||||
|
disabled={disabled}
|
||||||
|
options={province.map(item => ({
|
||||||
|
label: db[item],
|
||||||
|
value: item
|
||||||
|
}))}
|
||||||
|
value={provinceCode}
|
||||||
|
onChange={this.handleProvinceChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{provinceCode && allowDistrict && Array.isArray(district[provinceCode]) ? (
|
||||||
|
<Select
|
||||||
|
disabled={disabled}
|
||||||
|
options={(district[provinceCode] as Array<number>).map(item => ({
|
||||||
|
label: db[item],
|
||||||
|
value: item
|
||||||
|
}))}
|
||||||
|
value={districtCode}
|
||||||
|
onChange={this.handleDistrictChange}
|
||||||
|
/>
|
||||||
|
) : allowCity && city[provinceCode] ? (
|
||||||
|
<Select
|
||||||
|
disabled={disabled}
|
||||||
|
options={city[provinceCode].map(item => ({
|
||||||
|
label: db[item],
|
||||||
|
value: item
|
||||||
|
}))}
|
||||||
|
value={cityCode}
|
||||||
|
onChange={this.handleCityChange}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{cityCode && allowDistrict && district[provinceCode] && district[provinceCode][cityCode] ? (
|
||||||
|
<Select
|
||||||
|
disabled={disabled}
|
||||||
|
options={(district[provinceCode][cityCode] as Array<number>).map(item => ({
|
||||||
|
label: db[item],
|
||||||
|
value: item
|
||||||
|
}))}
|
||||||
|
value={districtCode}
|
||||||
|
onChange={this.handleDistrictChange}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{allowStreet && districtCode ? (
|
||||||
|
<input
|
||||||
|
className={cx('CityPicker-input')}
|
||||||
|
value={street}
|
||||||
|
onChange={this.handleStreetChange}
|
||||||
|
onBlur={this.handleStreetEnd}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ThemedCity = themeable(CityPicker);
|
||||||
|
export default ThemedCity;
|
||||||
|
|
||||||
|
|
||||||
|
export interface LocationControlProps extends FormControlProps {
|
||||||
|
allowCity?: boolean;
|
||||||
|
allowDistrict?: boolean;
|
||||||
|
allowStreet?: boolean;
|
||||||
|
};
|
||||||
|
export class LocationControl extends React.Component<LocationControlProps> {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
allowCity,
|
||||||
|
allowDistrict,
|
||||||
|
allowStreet
|
||||||
|
} = this.props;
|
||||||
|
return (
|
||||||
|
<ThemedCity
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
allowCity={allowCity}
|
||||||
|
allowDistrict={allowDistrict}
|
||||||
|
allowStreet={allowStreet}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FormItem({
|
||||||
|
type: 'city',
|
||||||
|
sizeMutable: false
|
||||||
|
})
|
||||||
|
export class CheckboxControlRenderer extends LocationControl {};
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue