mirror of
https://gitee.com/baidu/amis.git
synced 2024-12-02 12:08:13 +08:00
补充地图选择器
This commit is contained in:
parent
2ad93f1c10
commit
42645138a6
@ -1,7 +1,289 @@
|
|||||||
import React from 'react';
|
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() {
|
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';
|
vendor: 'baidu' | 'gaode' | 'tenxun';
|
||||||
placeholder: string;
|
placeholder: string;
|
||||||
clearable: boolean;
|
clearable: boolean;
|
||||||
|
ak: string;
|
||||||
value?: {
|
value?: {
|
||||||
address: string;
|
address: string;
|
||||||
lat: number;
|
lat: number;
|
||||||
@ -113,6 +114,17 @@ export class LocationPicker extends React.Component<
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleChange(value: any) {
|
||||||
|
if (value) {
|
||||||
|
value = {
|
||||||
|
...value,
|
||||||
|
vendor: this.props.vendor
|
||||||
|
};
|
||||||
|
}
|
||||||
|
this.props.onChange(value);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
classnames: cx,
|
classnames: cx,
|
||||||
@ -122,7 +134,8 @@ export class LocationPicker extends React.Component<
|
|||||||
placeholder,
|
placeholder,
|
||||||
clearable,
|
clearable,
|
||||||
popOverContainer,
|
popOverContainer,
|
||||||
vendor
|
vendor,
|
||||||
|
ak
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const {isFocused, isOpened} = this.state;
|
const {isFocused, isOpened} = this.state;
|
||||||
|
|
||||||
@ -136,7 +149,8 @@ export class LocationPicker extends React.Component<
|
|||||||
`LocationPicker`,
|
`LocationPicker`,
|
||||||
{
|
{
|
||||||
'is-disabled': disabled,
|
'is-disabled': disabled,
|
||||||
'is-focused': isFocused
|
'is-focused': isFocused,
|
||||||
|
'is-active': isOpened
|
||||||
},
|
},
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
@ -158,7 +172,7 @@ export class LocationPicker extends React.Component<
|
|||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<a className={cx('LocationPicker-toggler')}>
|
<a className={cx('LocationPicker-toggler')}>
|
||||||
<Icon icon="search" />
|
<Icon icon="location" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<Overlay
|
<Overlay
|
||||||
@ -174,7 +188,7 @@ export class LocationPicker extends React.Component<
|
|||||||
onClick={this.handlePopOverClick}
|
onClick={this.handlePopOverClick}
|
||||||
>
|
>
|
||||||
{vendor === 'baidu' ? (
|
{vendor === 'baidu' ? (
|
||||||
<BaiduMapPicker />
|
<BaiduMapPicker ak={ak} value={value} onChange={this.handleChange} />
|
||||||
) : (<Alert2>{vendor} 地图控件不支持</Alert2>)}
|
) : (<Alert2>{vendor} 地图控件不支持</Alert2>)}
|
||||||
</PopOver>
|
</PopOver>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
@ -60,6 +60,9 @@ import MoveIcon from '../icons/move.svg';
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import InfoIcon from '../icons/info.svg';
|
import InfoIcon from '../icons/info.svg';
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
import LocationIcon from '../icons/location.svg';
|
||||||
|
|
||||||
// 兼容原来的用法,后续不直接试用。
|
// 兼容原来的用法,后续不直接试用。
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
export const closeIcon = <CloseIcon />;
|
export const closeIcon = <CloseIcon />;
|
||||||
@ -121,6 +124,7 @@ registerIcon('search', SearchIcon);
|
|||||||
registerIcon('back', BackIcon);
|
registerIcon('back', BackIcon);
|
||||||
registerIcon('move', MoveIcon);
|
registerIcon('move', MoveIcon);
|
||||||
registerIcon('info', InfoIcon);
|
registerIcon('info', InfoIcon);
|
||||||
|
registerIcon('location', LocationIcon);
|
||||||
|
|
||||||
export function Icon({
|
export function Icon({
|
||||||
icon,
|
icon,
|
||||||
|
9
src/icons/location.svg
Normal file
9
src/icons/location.svg
Normal file
@ -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 {
|
export interface LocationControlProps extends FormControlProps {
|
||||||
vendor: 'baidu' | 'gaode' | 'tenxun';
|
vendor: 'baidu' | 'gaode' | 'tenxun';
|
||||||
value: any;
|
value: any;
|
||||||
|
ak: string;
|
||||||
onChange: (value: any) => void;
|
onChange: (value: any) => void;
|
||||||
classnames: ClassNamesFn;
|
classnames: ClassNamesFn;
|
||||||
classPrefix: string;
|
classPrefix: string;
|
||||||
|
@ -1140,3 +1140,24 @@ export function mapObject(value: any, fn: Function): any {
|
|||||||
}
|
}
|
||||||
return fn(value);
|
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
Block a user