forked from p96170835/amis
补充地图选择器
This commit is contained in:
parent
2ad93f1c10
commit
42645138a6
|
@ -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);
|
||||
|
|
|
@ -11,6 +11,7 @@ export interface LocationProps {
|
|||
vendor: 'baidu' | 'gaode' | 'tenxun';
|
||||
placeholder: string;
|
||||
clearable: boolean;
|
||||
ak: string;
|
||||
value?: {
|
||||
address: string;
|
||||
lat: number;
|
||||
|
@ -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,
|
||||
|
@ -122,7 +134,8 @@ export class LocationPicker extends React.Component<
|
|||
placeholder,
|
||||
clearable,
|
||||
popOverContainer,
|
||||
vendor
|
||||
vendor,
|
||||
ak
|
||||
} = this.props;
|
||||
const {isFocused, isOpened} = this.state;
|
||||
|
||||
|
@ -136,7 +149,8 @@ export class LocationPicker extends React.Component<
|
|||
`LocationPicker`,
|
||||
{
|
||||
'is-disabled': disabled,
|
||||
'is-focused': isFocused
|
||||
'is-focused': isFocused,
|
||||
'is-active': isOpened
|
||||
},
|
||||
className
|
||||
)}
|
||||
|
@ -158,7 +172,7 @@ export class LocationPicker extends React.Component<
|
|||
) : null}
|
||||
|
||||
<a className={cx('LocationPicker-toggler')}>
|
||||
<Icon icon="search" />
|
||||
<Icon icon="location" />
|
||||
</a>
|
||||
|
||||
<Overlay
|
||||
|
@ -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>
|
||||
|
|
|
@ -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 />;
|
||||
|
@ -121,6 +124,7 @@ registerIcon('search', SearchIcon);
|
|||
registerIcon('back', BackIcon);
|
||||
registerIcon('move', MoveIcon);
|
||||
registerIcon('info', InfoIcon);
|
||||
registerIcon('location', LocationIcon);
|
||||
|
||||
export function Icon({
|
||||
icon,
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg t="1585402743083" class="icon" viewBox="0 0 1024 1024" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg" p-id="2408"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
|
||||
<defs>
|
||||
<style type="text/css"></style>
|
||||
</defs>
|
||||
<path d="M511.968 0c-207.84 0-376.96 169.12-376.96 376.992 0 54.208 11.104 105.984 32.96 153.888 94.24 206.24 274.976 424 328.128 485.824 3.968 4.608 9.792 7.296 15.904 7.296s11.904-2.656 15.904-7.296c53.12-61.824 233.856-279.552 328.128-485.824 21.888-47.904 32.96-99.648 32.96-153.888-0.032-207.872-169.152-376.992-376.992-376.992zM511.968 572.8c-107.968 0-195.808-87.84-195.808-195.808s87.84-195.84 195.808-195.84 195.808 87.84 195.808 195.84c0 107.968-87.84 195.808-195.808 195.808z" p-id="2409"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 917 B |
|
@ -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;
|
||||
|
|
|
@ -1140,3 +1140,24 @@ export function mapObject(value: any, fn: Function): any {
|
|||
}
|
||||
return fn(value);
|
||||
}
|
||||
|
||||
export function loadScript(src: string) {
|
||||
return new Promise((ok, fail) => {
|
||||
const script = document.createElement('script');
|
||||
script.onerror = reason => fail(reason);
|
||||
|
||||
if (~src.indexOf('{{callback}}')) {
|
||||
const callbackFn = `loadscriptcallback_${uuid()}`;
|
||||
(window as any)[callbackFn] = () => {
|
||||
ok();
|
||||
delete (window as any)[callbackFn];
|
||||
};
|
||||
src = src.replace('{{callback}}', callbackFn);
|
||||
} else {
|
||||
script.onload = () => ok();
|
||||
}
|
||||
|
||||
script.src = src;
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue