feat(@zhst/map): 添加popup功能

This commit is contained in:
NICE CODE BY DEV 2024-05-24 18:03:43 +08:00
parent 23706fabf9
commit dd6ff1d2be
27 changed files with 173 additions and 67 deletions

3
global.d.ts vendored
View File

@ -1 +1,4 @@
declare module '*.less'; declare module '*.less';
declare module '*.png';
declare module '*.jpg';
declare module '*.jpeg';

View File

@ -1,48 +1,47 @@
import 'mapbox-gl/dist/mapbox-gl.css'; import 'mapbox-gl/dist/mapbox-gl.css';
import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css'; import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css';
import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'; import React, { forwardRef, useImperativeHandle, useMemo, useRef, useState } from 'react';
import Map from 'react-map-gl'; import Map, { PopupEvent } from 'react-map-gl';
import { CSSProperties } from "react"; import { CSSProperties } from "react";
import { MapboxMap, MapRef, MapStyle } from "react-map-gl"; import { MapRef, MapStyle, MapProps as MapBoxProps } from "react-map-gl";
import classnames from 'classnames' import classnames from 'classnames'
import Tools, { ToolsProps } from './components/tools' import Tools, { ToolsProps } from './components/tools'
import DrawControl, { DrawControlProps, DrawControlRefProps } from './components/drawControl'; import DrawControl, { DrawControlProps, DrawControlRefProps } from './components/drawControl';
import { MAP_CENTER, defaultMapConfig } from './utils/constants'; import { MAP_CENTER, defaultMapConfig } from './utils/constants';
import './index.less'; import './index.less';
import mapboxDrawStyle from './utils/drawStyle'; import mapboxDrawStyle from './utils/drawStyle';
import Marker from './components/marker'; import Marker, { MarkerProps } from './components/marker';
import PopUp, { PopUpProps } from './components/popup';
const componentName = 'zhst-map' const componentName = 'zhst-map'
export interface IMarkerData {
key: string;
title: string;
population: string;
status: string;
latitude: number;
longitude: number;
}
export interface MapProps extends Partial<MapboxMap> { export interface MapProps extends MapBoxProps {
mapboxAccessToken?: string; //token mapboxAccessToken?: string //token
markerData?: IMarkerData[] markerData?: MarkerProps[]
minZoom?: number; //最小层级 minZoom?: number //最小层级
maxZoom?: number; //最大层级 maxZoom?: number //最大层级
mapStyle?: MapStyle; //地图样式 mapStyle?: MapStyle //地图样式
height?: number | string; height?: number | string
width?: string | number; width?: string | number
mapRef?: MapRef; mapRef?: MapRef
style?: CSSProperties; style?: CSSProperties
children?: JSX.Element | JSX.Element[] | Array<JSX.Element | undefined>; children?: JSX.Element | JSX.Element[] | Array<JSX.Element | undefined>
mapCenter?: { mapCenter?: {
longitude: number, latitude: number longitude: number, latitude: number
} }
draw?: boolean; draw?: boolean
showMarker?: boolean // 显示标记点
buttonList?: ToolsProps['buttonList'] buttonList?: ToolsProps['buttonList']
onLoad?: (e: mapboxgl.MapboxEvent<undefined>) => void; popUpInfo?: PopUpProps
onDrawCreate?: (e: { features: object[], [key: string]: any }) => void; showPopUp?: boolean
onDrawUpdate?: (e: { features: object[], [key: string]: any }) => void; customMarkerRender?: MarkerProps['customMarkerRender']
onDrawDelete?: (e: { features: object[], [key: string]: any }) => void; onLoad?: (e: mapboxgl.MapboxEvent<undefined>) => void
onDrawCreate?: (e: { features: object[], [key: string]: any }) => void
onDrawUpdate?: (e: { features: object[], [key: string]: any }) => void
onDrawDelete?: (e: { features: object[], [key: string]: any }) => void
onMarkerClick?: MarkerProps['onMarkerClick']
onPopUpClose?: (e: PopupEvent) => void;
} }
export interface MapRefProps { export interface MapRefProps {
@ -57,6 +56,14 @@ const MapBox = forwardRef<MapRefProps, MapProps>((props, ref) => {
width = '100%', width = '100%',
draw, draw,
markerData = [], markerData = [],
popUpInfo = {
...MAP_CENTER
},
showPopUp,
customMarkerRender,
showMarker,
onMarkerClick,
onPopUpClose,
buttonList = [ buttonList = [
{ {
label: '圆形框选', label: '圆形框选',
@ -102,12 +109,10 @@ const MapBox = forwardRef<MapRefProps, MapProps>((props, ref) => {
const [drawConfig] = useState<DrawControlProps>({ const [drawConfig] = useState<DrawControlProps>({
displayControlsDefault: false, displayControlsDefault: false,
position: 'top-left', position: 'top-left',
styles: mapboxDrawStyle, styles: mapboxDrawStyle,
// Select which mapbox-gl-draw control buttons to add to the map. // Select which mapbox-gl-draw control buttons to add to the map.
controls: { controls: false,
polygon: true,
trash: true
},
// The user does not have to click the polygon control button first. // The user does not have to click the polygon control button first.
defaultMode: 'draw_polygon', defaultMode: 'draw_polygon',
}) })
@ -116,13 +121,14 @@ const MapBox = forwardRef<MapRefProps, MapProps>((props, ref) => {
() => { () => {
return markerData.map((_item, index) => ( return markerData.map((_item, index) => (
<Marker <Marker
key={_item.key || index} key={_item.key || String(index)}
customMarkerRender={customMarkerRender}
onMarkerClick={onMarkerClick}
latitude={_item.latitude} latitude={_item.latitude}
longitude={_item.longitude} longitude={_item.longitude}
/> />
)) ))
}, }, [markerData]
[markerData]
); );
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
@ -145,7 +151,15 @@ const MapBox = forwardRef<MapRefProps, MapProps>((props, ref) => {
style={{ width: width, height: height, ...style }} style={{ width: width, height: height, ...style }}
{...others} {...others}
> >
{initMarker} {/* 标记点位 */}
{showMarker && initMarker}
{/* 点位弹框 */}
{showPopUp && popUpInfo && (
<PopUp
onClose={onPopUpClose}
{...popUpInfo}
/>
)}
{/* <CustomOverlay {/* <CustomOverlay
> >
<Button></Button> <Button></Button>

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,19 +1,43 @@
/** /**
* Created by jiangzhixiong on 2024/05/23 * Created by jiangzhixiong on 2024/05/23
*/ */
import React, { forwardRef, useContext, useImperativeHandle } from 'react' import React, { forwardRef, ReactNode, useContext, useImperativeHandle } from 'react'
import { import {
Marker as MapboxMarker, Marker as MapboxMarker,
MarkerProps as MapboxMarkerProps MarkerProps as MapboxMarkerProps,
MarkerEvent
} from 'react-map-gl' } from 'react-map-gl'
import { ConfigProvider, Image } from '@zhst/meta' import { Checkbox, ConfigProvider, Image } from '@zhst/meta'
import markerPic from '../../assets/icons/marker_monitor.png' import cameraBlue from '../../assets/icons/camera_blue.png'
import cameraGreen from '../../assets/icons/camera_green.png'
import cameraGrey from '../../assets/icons/camera_grey.png'
import cameraRed from '../../assets/icons/camera_red.png'
import cameraYellow from '../../assets/icons/camera_yellow.png'
const { ConfigContext } = ConfigProvider const { ConfigContext } = ConfigProvider
const PIC_MAP = new Map([
['camera_blue', cameraBlue],
['camera_green', cameraGreen],
['camera_grey', cameraGrey],
['camera_red', cameraRed],
['camera_yellow', cameraYellow],
])
// @ts-ignore
export interface MarkerProps extends MapboxMarkerProps { export interface MarkerProps extends MapboxMarkerProps {
prefixCls?: string; prefixCls?: string;
key: string; key: string;
title?: string;
checked?: boolean;
type?: 'camera' | 'cluster' | 'marker';
population?: string;
status?: 'blue' | 'green' | 'yellow' | 'grey' | 'red_border' | 'escape' | 'escape_border' | 'red_track'; // 摄像头状态
latitude: number;
longitude: number;
onClick?: (e?: MarkerEvent, data?: MarkerProps) => void;
onMarkerClick?: (e?: MouseEvent, checked?: boolean, data?: MarkerProps) => void;
customMarkerRender?: (data: MarkerProps) => ReactNode
} }
export interface MarkerRefProps { export interface MarkerRefProps {
@ -22,27 +46,43 @@ export interface MarkerRefProps {
const Marker = forwardRef<MarkerRefProps, MarkerProps>((props, ref) => { const Marker = forwardRef<MarkerRefProps, MarkerProps>((props, ref) => {
const { const {
prefixCls: customizePrefixCls, prefixCls: customizePrefixCls,
key,
longitude, longitude,
latitude, latitude,
onClick checked = false,
type = 'camera',
status = 'blue',
onClick,
onMarkerClick,
customMarkerRender
} = props } = props
const { getPrefixCls } = useContext(ConfigContext) const { getPrefixCls } = useContext(ConfigContext)
const componentName = getPrefixCls('-map-marker', customizePrefixCls); const componentName = getPrefixCls('-map-marker', customizePrefixCls);
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({}))
}))
return ( return (
<MapboxMarker <MapboxMarker
key={key}
longitude={Number(longitude)} longitude={Number(longitude)}
latitude={Number(latitude)} latitude={Number(latitude)}
anchor="bottom" anchor="bottom"
onClick={onClick} // @ts-ignore
onClick={e => {
e.originalEvent.stopPropagation();
onClick?.(e, props)
}}
> >
<Image src={markerPic} preview={false} /> {/* 自定义marker */}
{customMarkerRender?.(props) || (
<div className={componentName}>
<Checkbox value={checked} onClick={(e) => {
e.stopPropagation()
// @ts-ignore
onMarkerClick?.(e, checked, props)
}}>
<Image src={PIC_MAP.get(`${type}_${status}`)} preview={false} />
</Checkbox>
</div>
)}
</MapboxMarker> </MapboxMarker>
) )
}) })

View File

@ -3,16 +3,21 @@
*/ */
import React, { forwardRef, useContext, useImperativeHandle } from 'react' import React, { forwardRef, useContext, useImperativeHandle } from 'react'
import { import {
PopUp as MapboxPopUp, Popup as MapboxPopUp,
PopupProps as MapboxPopupProps PopupProps as MapboxPopupProps
} from 'react-map-gl' } from 'react-map-gl'
import { ConfigProvider } from '@zhst/meta' import { ConfigProvider, Image } from '@zhst/meta'
import './index.less'
import classNames from 'classnames'
const { ConfigContext } = ConfigProvider const { ConfigContext } = ConfigProvider
export interface PopUpProps extends MapboxPopupProps { export interface PopUpProps extends MapboxPopupProps {
prefixCls?: string; prefixCls?: string;
size?: number; size?: number;
title?: string;
url?: string;
content?: string;
} }
export interface PopUpRefProps { export interface PopUpRefProps {
@ -23,24 +28,28 @@ const PopUp = forwardRef<PopUpRefProps, PopUpProps>((props, ref) => {
longitude, longitude,
latitude, latitude,
onClose, onClose,
title,
content,
url,
prefixCls: customizePrefixCls prefixCls: customizePrefixCls
} = props } = props
const { getPrefixCls } = useContext(ConfigContext) const { getPrefixCls } = useContext(ConfigContext)
const componentName = getPrefixCls('map-popup', customizePrefixCls); const componentName = getPrefixCls('map-popup', customizePrefixCls);
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({}))
}))
return ( return (
<MapboxPopUp <MapboxPopUp
anchor="top" anchor="top"
className={componentName}
longitude={Number(longitude)} longitude={Number(longitude)}
latitude={Number(latitude)} latitude={Number(latitude)}
onClose={onClose} onClose={onClose}
> >
<div className={componentName}> <div className={classNames(`${componentName}-container`)}>
popup {title && <h2>{title}</h2>}
{url && <Image src={url} />}
{content && <p>{content}</p>}
</div> </div>
</MapboxPopUp> </MapboxPopUp>
) )

View File

@ -0,0 +1,11 @@
.zhst-map-popup {
.mapboxgl-popup-content {
padding: 12px 6px 6px;
}
&-container {
p {
margin: 0;
}
}
}

View File

@ -1,5 +1,7 @@
/** /**
* Created by jiangzhixiong on 2024/05/23 * Created by jiangzhixiong on 2024/05/23
*/ */
export { default as PopUp } from './PopUp' import PopUp from './PopUp'
export type { PopUpProps, PopUpRefProps } from './PopUp' export type { PopUpProps, PopUpRefProps } from './PopUp'
export default PopUp

View File

@ -1,9 +1,12 @@
import React, { useRef, useEffect, useState } from 'react'; import React, { useRef, useEffect, useState } from 'react';
import { MapBox } from '@zhst/map'; import { MapBox } from '@zhst/map';
import axios from 'axios'; import axios from 'axios';
import { FloatButton, Switch } from '@zhst/meta';
const demo = () => { const demo = () => {
const [markerData, setMarkerData] = useState([]) const [markerData, setMarkerData] = useState([])
const [showMarker, setShowMarker] = useState(true)
const [popupInfo, setPopupInfo] = useState()
const mapRef = useRef(null); const mapRef = useRef(null);
// 初始化 // 初始化
@ -44,16 +47,40 @@ const demo = () => {
}, []) }, [])
return ( return (
<MapBox <div>
onLoad={handleMapLoad} <FloatButton>
ref={mapRef} <Switch value={true}/>
onDrawCreate={e => console.log('Create', e)} </FloatButton>
onDrawDelete={e => console.log('Delete', e)} <MapBox
onDrawUpdate={e => console.log('Update', e)} onLoad={handleMapLoad}
width='100%' ref={mapRef}
height='100vh' // draw
markerData={markerData} onDrawCreate={e => console.log('Create', e)}
/> onDrawDelete={e => console.log('Delete', e)}
onDrawUpdate={e => console.log('Update', e)}
width='100%'
height='100vh'
markerData={markerData}
showMarker={showMarker}
showPopUp
popUpInfo={popupInfo}
onPopUpClose={e => setPopupInfo(null)}
onMarkerClick={(e, status, data) => {
setPopupInfo({
longitude: data?.longitude,
latitude: data?.latitude,
content: '测试'
})
}}
onZoomEnd={map => {
let zoom = map.target.getZoom() // 画面层级,用来判断标签是否显示隐藏
console.log('zoom', zoom)
// setShowMarker(pre => !pre)
}}
// customMarkerRender={(_data) => <div>自定义标记</div>}
/>
</div>
); );
}; };

View File

@ -32,6 +32,6 @@
// "@zhst/meta" 使 npm // "@zhst/meta" 使 npm
] ]
}, },
"include": [".dumirc.ts", "src/**/*", "packages/**/*"], "include": [".dumirc.ts", "src/**/*", "packages/**/*", "global.d.ts"],
"exclude": ["node_modules", "lib", "es", ".dumi"] "exclude": ["node_modules", "lib", "es", ".dumi"]
} }