Skip to content

Commit

Permalink
补充地图选择器
Browse files Browse the repository at this point in the history
  • Loading branch information
2betop committed Mar 29, 2020
1 parent 2ad93f1 commit 4264513
Show file tree
Hide file tree
Showing 6 changed files with 337 additions and 6 deletions.
286 changes: 284 additions & 2 deletions src/components/BaiduMapPicker.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,289 @@
import React from 'react';
import {ClassNamesFn, themeable} from '../theme';
import {Icon} from '..';
import {loadScript, autobind, uuid} from '../utils/helper';
import { threadId } from 'worker_threads';
import debounce from 'lodash/debounce';

declare const BMap: any;

interface MapPickerProps {
ak: string;
classnames: ClassNamesFn;
classPrefix: string;
value?: {
address: string;
lat: number;
lng: number;
city?: string;
};
onChange: (value: any) => void;
}

interface LocationItem {
title?: string;
address: string;
lat: number;
lng: number;
city?: string;
}

interface MapPickerState {
inputValue: string;
locIndex?: number;
locs: Array<LocationItem>;
sugs: Array<string>;
}

export class BaiduMapPicker extends React.Component<
MapPickerProps,
MapPickerState
> {
state: MapPickerState = {
inputValue: '',
locs: [],
locIndex: -1,
sugs: [],
};

id = uuid();
mapRef: React.RefObject<HTMLDivElement> = React.createRef();
placeholderInput: HTMLInputElement;
map: any;
ac: any;
search = debounce(() => {
if (this.state.inputValue) {
this.ac?.search(this.state.inputValue);
} else {
this.setState({
sugs: []
})
}
}, 250, {
trailing: true,
leading: false
});

componentDidMount() {
if ((window as any).BMap) {
this.initMap();
} else {
loadScript(
`http://api.map.baidu.com/api?v=2.0&ak=${
this.props.ak
}&callback={{callback}}`
).then(this.initMap);
}
}

componentWillUnmount() {
this.ac?.dispose();
document.body.removeChild(this.placeholderInput);
delete this.placeholderInput;
delete this.map;
}

@autobind
async initMap() {
const map = new BMap.Map(this.mapRef.current, {
enableMapClick: false
});
this.map = map;

const value = this.props.value;
let point = value ? new BMap.Point(value.lng, value.lat) : new BMap.Point(116.404, 39.915);
map.centerAndZoom(point, 15);

const geolocationControl = new BMap.GeolocationControl();
geolocationControl.addEventListener('locationSuccess', (e: any) => {
this.getLocations(e.point);
});
map.addControl(geolocationControl);

map.addEventListener('click', (e: any) => {
this.getLocations(e.point, true);
});

const input = document.createElement('input');
input.className = 'invisible';
this.placeholderInput = input;
document.body.appendChild(input);

this.ac = new BMap.Autocomplete({
input,
location: map,
onSearchComplete: (e:any) => {
// 说明已经销毁了。
if (!this.map) {
return;
}

const sugs:Array<string> = [];
if (Array.isArray(e.Ir)) {
e.Ir.forEach((item:any) => {
sugs.push([item.province, item.city, item.district, item.street, item.business].filter(item => item).join(' '))
});

this.setState({
sugs
});
}
}
});

value ? this.getLocations(point) : geolocationControl.location();
}

getLocations(point: any, select?: boolean) {
const map = this.map;

map.clearOverlays();
const mk = new BMap.Marker(point);
map.addOverlay(mk);
map.panTo(point);

var geoc = new BMap.Geocoder();
geoc.getLocation(point, (rs: any) => {
// 说明已经销毁了。
if (!this.map) {
return;
}

const index = 0;
const locs: Array<LocationItem> = [];

locs.push({
title: '当前位置',
address: rs.address,
city: rs.addressComponents.city,
lat: rs.point.lat,
lng: rs.point.lng
});

if (Array.isArray(rs.surroundingPois)) {
rs.surroundingPois.forEach((item: any) => {
locs.push({
title: item.title,
address: item.address,
city: item.city,
lat: item.point.lat,
lng: item.point.lng
});
});
}

this.setState({
locIndex: index,
locs
}, () => {
if (!select) {
return;
}

this.props?.onChange({
address: locs[0].address,
lat: locs[0].lat,
lng: locs[0].lng,
city: locs[0].city
})
});
});
}

@autobind
handleChange(e: React.ChangeEvent<HTMLInputElement>) {
this.setState({
inputValue: e.currentTarget.value
}, this.search);
}

@autobind
handleSelect(e: React.MouseEvent<HTMLElement>) {
const index= parseInt(e.currentTarget.getAttribute('data-index')!, 10);
const loc = this.state.locs[index];

this.setState({
locIndex: index
}, () => {
const point = new BMap.Point(loc.lng, loc.lat);

this.map.clearOverlays();
const mk = new BMap.Marker(point);
this.map.addOverlay(mk);
this.map.panTo(point);

this.props?.onChange({
address: loc.address.trim() || loc.title,
lat: loc.lat,
lng: loc.lng,
city: loc.city
})
})
}

@autobind
handleSugSelect(e: React.MouseEvent<HTMLDivElement>) {
const value = e.currentTarget.innerText;
this.setState({
inputValue: value
});

var local = new BMap.LocalSearch(this.map, { //智能搜索
onSearchComplete: () => {
const results = local.getResults();
const poi = results.getPoi(0);
this.setState({
inputValue: poi.title,
sugs: []
});
this.getLocations(poi.point, true)
}
});
local.search(value);
}

export default class BaiduMapPicker extends React.Component<any> {
render() {
return <p>233</p>;
const {classnames: cx} = this.props;
const {locIndex, locs, inputValue, sugs} = this.state;
const hasSug = Array.isArray(sugs) && sugs.length;

return (
<div className={cx('MapPicker')}>
<div className={cx('MapPicker-search TextControl-control')}>
<div className={cx('TextControl-input')}>
<input onChange={this.handleChange} value={inputValue} placeholder="搜索地点" />
<span>
<Icon icon="search" />
</span>
</div>
</div>

<div ref={this.mapRef} className={cx('MapPicker-map', {
invisible: hasSug
})} />

<div className={cx('MapPicker-result', {
invisible: hasSug
})}>
{locs.map((item, index) => (
<div onClick={this.handleSelect} key={index} data-index={index} className={cx('MapPicker-item')}>
<div className={cx('MapPicker-itemTitle')}>{item.title}</div>
<div className={cx('MapPicker-itemDesc')}>{item.address}</div>
{locIndex === index ? <Icon icon="success" /> : null}
</div>
))}
</div>

{hasSug ? (
<div className={cx('MapPicker-sug')}>
{sugs.map(item =>
<div onClick={this.handleSugSelect} className={cx('MapPicker-sugItem')} key={item}>{item}</div>
)}
</div>
) : null}
</div>
);
}
}

export default themeable(BaiduMapPicker);
22 changes: 18 additions & 4 deletions src/components/LocationPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface LocationProps {
vendor: 'baidu' | 'gaode' | 'tenxun';
placeholder: string;
clearable: boolean;
ak: string;
value?: {
address: string;
lat: number;
Expand Down Expand Up @@ -113,6 +114,17 @@ export class LocationPicker extends React.Component<
e.preventDefault();
}

@autobind
handleChange(value: any) {
if (value) {
value = {
...value,
vendor: this.props.vendor
};
}
this.props.onChange(value);
}

render() {
const {
classnames: cx,
Expand All @@ -122,7 +134,8 @@ export class LocationPicker extends React.Component<
placeholder,
clearable,
popOverContainer,
vendor
vendor,
ak
} = this.props;
const {isFocused, isOpened} = this.state;

Expand All @@ -136,7 +149,8 @@ export class LocationPicker extends React.Component<
`LocationPicker`,
{
'is-disabled': disabled,
'is-focused': isFocused
'is-focused': isFocused,
'is-active': isOpened
},
className
)}
Expand All @@ -158,7 +172,7 @@ export class LocationPicker extends React.Component<
) : null}

<a className={cx('LocationPicker-toggler')}>
<Icon icon="search" />
<Icon icon="location" />
</a>

<Overlay
Expand All @@ -174,7 +188,7 @@ export class LocationPicker extends React.Component<
onClick={this.handlePopOverClick}
>
{vendor === 'baidu' ? (
<BaiduMapPicker />
<BaiduMapPicker ak={ak} value={value} onChange={this.handleChange} />
) : (<Alert2>{vendor} 地图控件不支持</Alert2>)}
</PopOver>
</Overlay>
Expand Down
4 changes: 4 additions & 0 deletions src/components/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ import MoveIcon from '../icons/move.svg';
// @ts-ignore
import InfoIcon from '../icons/info.svg';

// @ts-ignore
import LocationIcon from '../icons/location.svg';

// 兼容原来的用法,后续不直接试用。
// @ts-ignore
export const closeIcon = <CloseIcon />;
Expand Down Expand Up @@ -121,6 +124,7 @@ registerIcon('search', SearchIcon);
registerIcon('back', BackIcon);
registerIcon('move', MoveIcon);
registerIcon('info', InfoIcon);
registerIcon('location', LocationIcon);

export function Icon({
icon,
Expand Down
9 changes: 9 additions & 0 deletions src/icons/location.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/renderers/Form/Location.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import LocationPicker from '../../components/LocationPicker';
export interface LocationControlProps extends FormControlProps {
vendor: 'baidu' | 'gaode' | 'tenxun';
value: any;
ak: string;
onChange: (value: any) => void;
classnames: ClassNamesFn;
classPrefix: string;
Expand Down
Loading

0 comments on commit 4264513

Please sign in to comment.