You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
266 lines
11 KiB
266 lines
11 KiB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; |
|
import { forwardRef, useEffect, useImperativeHandle, useRef, useState, } from 'react'; |
|
import { compact, isArray, isEmpty } from 'lodash'; |
|
import { renderToString } from 'react-dom/server'; |
|
import { useReactive } from 'ahooks'; |
|
import { getStorage } from '@component/utils'; |
|
import { GDMap } from '../map'; |
|
import { HeatMapLayer } from '../layer'; |
|
import cluster_blue from './cluster_blue.png'; |
|
import marker_blue from './marker_blue.png'; |
|
import set_cluster from './cluster'; |
|
import style from './style.module.css'; |
|
export const MapMarker = forwardRef((props, ref) => { |
|
const { data = [], icon = marker_blue, clusterIcon = cluster_blue, options = {}, onMarker, onCluster, district = false, isHeatMap = false, isMapStyle = false, isZoomMap = false, circle = false, distance = 1000, polygonOptions = {}, adcode = null, isCluster = true, loadedCallBack, } = props; |
|
const position = !isEmpty(compact(getStorage('position') ? getStorage('position').split(',') : [])) |
|
? compact(getStorage('position').split(',')) |
|
: [116.398784, 39.910231]; |
|
const mapOptions = Object.assign({ center: position, zoom: 6 }, options); |
|
const infoWindowRef = useRef(null); |
|
const markersEle = useRef([]); |
|
const districtEle = useRef(null); |
|
const polygons = useRef([]); |
|
const mapEle = useRef(null); |
|
const circleRef = useRef(null); |
|
const [styleStatus, setStyleStatus] = useState((options === null || options === void 0 ? void 0 : options.mapStyle) === 'amap://styles/darkblue'); |
|
const showHeatMap = useReactive({ |
|
show: false, |
|
}); |
|
const clearMap = () => { |
|
mapEle.current.clearMap(); |
|
}; |
|
/** |
|
* marker点击事件 |
|
* @param event |
|
*/ |
|
const onMarkerClick = (event) => { |
|
const marker = event.target; |
|
const position = marker.getPosition(); |
|
const extData = marker.getExtData(); |
|
if (mapEle.current && onMarker) { |
|
onMarker(extData); |
|
return; |
|
} |
|
setInfoWindow({ |
|
position, |
|
extData, |
|
}); |
|
}; |
|
function setInfoWindow({ position, extData }) { |
|
var _a, _b, _c; |
|
if (infoWindowRef.current.getIsOpen()) { |
|
(_a = infoWindowRef.current) === null || _a === void 0 ? void 0 : _a.close(); |
|
} |
|
(_b = infoWindowRef.current) === null || _b === void 0 ? void 0 : _b.setContent(renderInfoContent(extData)); |
|
(_c = infoWindowRef.current) === null || _c === void 0 ? void 0 : _c.open(mapEle.current, position); |
|
mapEle.current.setCenter(position); |
|
} |
|
const createMaker = (item) => { |
|
const marker = new AMap.Marker({ |
|
position: [item.lng, item.lat], |
|
icon: item.icon, |
|
clickable: true, |
|
extData: item, |
|
map: !isCluster && mapEle.current, |
|
}); |
|
marker.on('click', onMarkerClick); |
|
return marker; |
|
}; |
|
const getMarkers = (data) => { |
|
if (!infoWindowRef.current) { |
|
infoWindowRef.current = new AMap.InfoWindow({ |
|
offset: new AMap.Pixel(10, -15), |
|
autoMove: true, |
|
}); |
|
} |
|
return data.map((item) => createMaker(Object.assign({ icon: icon }, item))); |
|
}; |
|
const setClusterEvent = () => { |
|
return [ |
|
{ |
|
type: 'click', |
|
eventHandle: (context, currentClusterObj) => { |
|
let map = context.getMap(); |
|
if (map.getZoom() >= 15) { |
|
let markers = currentClusterObj.markers, filterMarkers = []; |
|
for (let i = markers.length - 1; i >= 0; i--) { |
|
filterMarkers.unshift(markers[i].getExtData()); |
|
} |
|
if (onCluster) { |
|
onCluster(filterMarkers, (data) => { |
|
setInfoWindow({ |
|
position: currentClusterObj.lnglat, |
|
extData: data, |
|
}); |
|
}); |
|
return; |
|
} |
|
setInfoWindow({ |
|
position: currentClusterObj.lnglat, |
|
extData: filterMarkers, |
|
}); |
|
} |
|
}, |
|
}, |
|
]; |
|
}; |
|
const setMarkers = (data) => { |
|
markersEle.current = getMarkers(data); |
|
if (isCluster) { |
|
try { |
|
set_cluster(mapEle.current, markersEle.current, setClusterEvent(), clusterIcon, district, circle); |
|
} |
|
catch (e) { |
|
console.log('e', e); |
|
} |
|
} |
|
}; |
|
// 缩放地图 |
|
const setMapView = (type) => { |
|
if (!mapEle.current) { |
|
return; |
|
} |
|
let zoom = mapEle.current.getZoom(); |
|
if (type === 'add') { |
|
zoom++; |
|
zoom = zoom >= 18 ? 18 : zoom; |
|
} |
|
else { |
|
zoom--; |
|
zoom = zoom <= 1 ? 1 : zoom; |
|
} |
|
mapEle.current.setZoom(zoom); |
|
}; |
|
//热力图切换 |
|
const toggleHeatMap = () => { |
|
var _a, _b; |
|
if (!mapEle.current) { |
|
return; |
|
} |
|
showHeatMap.show = !showHeatMap.show; |
|
(_a = mapEle.current) === null || _a === void 0 ? void 0 : _a.clearInfoWindow(); |
|
(_b = mapEle.current) === null || _b === void 0 ? void 0 : _b.cluster.clearMarkers(); |
|
if (!showHeatMap.show) { |
|
setMarkers(data); |
|
} |
|
}; |
|
const setStyle = () => { |
|
if (!mapEle.current) { |
|
return; |
|
} |
|
const status = !styleStatus; |
|
mapEle.current.setMapStyle('amap://styles/' + (status ? 'darkblue' : 'fresh')); |
|
setStyleStatus(status); |
|
}; |
|
/** |
|
* Infowin窗口渲染内容 |
|
* @returns {string} |
|
*/ |
|
const renderInfoContent = (data) => { |
|
const { renderInfoContent } = props; |
|
const d = isArray(data) ? data : [data]; |
|
if (renderInfoContent) { |
|
return renderInfoContent(d); |
|
} |
|
return renderToString(_jsx("ul", Object.assign({ className: style['popup_list'] }, { children: d.map((item, key) => { |
|
return (_jsx("li", Object.assign({ title: item === null || item === void 0 ? void 0 : item.name }, { children: (item === null || item === void 0 ? void 0 : item.url) ? (_jsx("a", Object.assign({ target: "_blank", href: item === null || item === void 0 ? void 0 : item.url }, { children: item === null || item === void 0 ? void 0 : item.name }))) : (item === null || item === void 0 ? void 0 : item.name) }), key)); |
|
}) }))); |
|
}; |
|
/** |
|
* 地图加载完成后的回调 |
|
* @param map |
|
*/ |
|
const onMapLoaded = (map) => { |
|
init(map, data); |
|
}; |
|
const init = (map, data) => { |
|
mapEle.current = map; |
|
if (district) { |
|
drawBounds({ |
|
map, |
|
district: districtEle.current, |
|
polygons: polygons.current, |
|
adcode: adcode || getStorage('code'), |
|
polygonOptions: polygonOptions, |
|
}); |
|
} |
|
if (!showHeatMap.show) { |
|
setMarkers(data); |
|
} |
|
if (circle) { |
|
circleRef.current = new AMap.Circle({ |
|
center: mapOptions.center, |
|
strokeColor: '#00a5e6', |
|
strokeOpacity: 1, |
|
strokeWeight: 2, |
|
fillColor: '#bbd4e3', |
|
fillOpacity: 0.35, //填充透明度 |
|
}); |
|
setCircle(distance); |
|
} |
|
loadedCallBack && loadedCallBack(map); |
|
}; |
|
function setCircle(distance) { |
|
circleRef.current.setRadius(distance); |
|
mapEle.current.add(circleRef.current); |
|
// mapEle.current.setFitView([circleRef.current, ...markersEle.current]); |
|
mapEle.current.setFitView(); |
|
} |
|
useEffect(() => { |
|
if (mapEle.current) { |
|
clearMap(); |
|
init(mapEle.current, data); |
|
} |
|
}, [mapEle.current, data]); |
|
useImperativeHandle(ref, () => { |
|
return { |
|
setInfoWindow, |
|
clearMap, |
|
}; |
|
}); |
|
return (_jsxs("div", Object.assign({ className: style['map-container'] }, { children: [_jsx(GDMap, Object.assign({ aMapLoca: true, aMapUI: true, containerClass: style['map-container'], options: mapOptions, loadedCallBack: onMapLoaded }, { children: _jsx(HeatMapLayer, { data: data, open: showHeatMap.show, heatmapOptions: { |
|
zIndex: 11, |
|
radius: 50, |
|
opacity: [0, 0.8], |
|
gradient: { |
|
0.5: 'blue', |
|
0.65: 'rgb(117,211,248)', |
|
0.7: 'rgb(0, 255, 0)', |
|
0.9: '#ffea00', |
|
1.0: 'red', |
|
}, |
|
} }) })), _jsxs("ul", Object.assign({ className: style['map_action'] }, { children: [isHeatMap && (_jsx("li", Object.assign({ onClick: toggleHeatMap, className: style['switch_style'], style: { listStyle: 'none' } }, { children: showHeatMap.show ? '点聚合' : '热力图' }))), isMapStyle && (_jsx("li", Object.assign({ onClick: setStyle, className: style['hot_style'], style: { listStyle: 'none' } }, { children: styleStatus ? '标准' : '炫黑' }))), isZoomMap && (_jsxs("li", Object.assign({ className: style['too-bar'] }, { children: [_jsx("p", Object.assign({ onClick: () => setMapView('add') }, { children: "+" })), _jsx("p", Object.assign({ onClick: () => setMapView('sub') }, { children: "-" }))] })))] }))] }))); |
|
}); |
|
function drawBounds({ map, adcode, district, polygons, polygonOptions }) { |
|
//加载行政区划插件 |
|
AMap.plugin('AMap.DistrictSearch', () => { |
|
if (!district) { |
|
//实例化DistrictSearch |
|
district = new AMap.DistrictSearch({ |
|
zIndex: 10, |
|
subdistrict: 1, |
|
extensions: 'all', |
|
level: 'district', |
|
showbiz: false, // 最后一级返回街道信息 |
|
}); |
|
} |
|
//行政区查询 |
|
district.search(adcode, function (_status, result) { |
|
map.remove(polygons); //清除上次结果 |
|
polygons = []; |
|
if (isEmpty(result)) { |
|
return; |
|
} |
|
const bounds = result.districtList[0].boundaries; |
|
if (bounds) { |
|
for (let i = 0, l = bounds.length; i < l; i++) { |
|
//生成行政区划polygon |
|
const polygon = new AMap.Polygon(Object.assign({ strokeWeight: 1, path: bounds[i], fillOpacity: 0.4, fillColor: '#80d8ff', strokeColor: '#0091ea' }, polygonOptions)); |
|
polygons.push(polygon); |
|
} |
|
} |
|
map.add(polygons); |
|
map.setFitView(polygons); //视口自适应 |
|
}); |
|
}); |
|
}
|
|
|