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 '*.png';
declare module '*.jpg';
declare module '*.jpeg';

View File

@ -1,48 +1,47 @@
import 'mapbox-gl/dist/mapbox-gl.css';
import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css';
import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
import Map from 'react-map-gl';
import React, { forwardRef, useImperativeHandle, useMemo, useRef, useState } from 'react';
import Map, { PopupEvent } from 'react-map-gl';
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 Tools, { ToolsProps } from './components/tools'
import DrawControl, { DrawControlProps, DrawControlRefProps } from './components/drawControl';
import { MAP_CENTER, defaultMapConfig } from './utils/constants';
import './index.less';
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'
export interface IMarkerData {
key: string;
title: string;
population: string;
status: string;
latitude: number;
longitude: number;
}
export interface MapProps extends Partial<MapboxMap> {
mapboxAccessToken?: string; //token
markerData?: IMarkerData[]
minZoom?: number; //最小层级
maxZoom?: number; //最大层级
mapStyle?: MapStyle; //地图样式
height?: number | string;
width?: string | number;
mapRef?: MapRef;
style?: CSSProperties;
children?: JSX.Element | JSX.Element[] | Array<JSX.Element | undefined>;
export interface MapProps extends MapBoxProps {
mapboxAccessToken?: string //token
markerData?: MarkerProps[]
minZoom?: number //最小层级
maxZoom?: number //最大层级
mapStyle?: MapStyle //地图样式
height?: number | string
width?: string | number
mapRef?: MapRef
style?: CSSProperties
children?: JSX.Element | JSX.Element[] | Array<JSX.Element | undefined>
mapCenter?: {
longitude: number, latitude: number
}
draw?: boolean;
draw?: boolean
showMarker?: boolean // 显示标记点
buttonList?: ToolsProps['buttonList']
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;
popUpInfo?: PopUpProps
showPopUp?: boolean
customMarkerRender?: MarkerProps['customMarkerRender']
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 {
@ -57,6 +56,14 @@ const MapBox = forwardRef<MapRefProps, MapProps>((props, ref) => {
width = '100%',
draw,
markerData = [],
popUpInfo = {
...MAP_CENTER
},
showPopUp,
customMarkerRender,
showMarker,
onMarkerClick,
onPopUpClose,
buttonList = [
{
label: '圆形框选',
@ -102,12 +109,10 @@ const MapBox = forwardRef<MapRefProps, MapProps>((props, ref) => {
const [drawConfig] = useState<DrawControlProps>({
displayControlsDefault: false,
position: 'top-left',
styles: mapboxDrawStyle,
// Select which mapbox-gl-draw control buttons to add to the map.
controls: {
polygon: true,
trash: true
},
controls: false,
// The user does not have to click the polygon control button first.
defaultMode: 'draw_polygon',
})
@ -116,13 +121,14 @@ const MapBox = forwardRef<MapRefProps, MapProps>((props, ref) => {
() => {
return markerData.map((_item, index) => (
<Marker
key={_item.key || index}
key={_item.key || String(index)}
customMarkerRender={customMarkerRender}
onMarkerClick={onMarkerClick}
latitude={_item.latitude}
longitude={_item.longitude}
/>
))
},
[markerData]
}, [markerData]
);
useImperativeHandle(ref, () => ({
@ -145,7 +151,15 @@ const MapBox = forwardRef<MapRefProps, MapProps>((props, ref) => {
style={{ width: width, height: height, ...style }}
{...others}
>
{initMarker}
{/* 标记点位 */}
{showMarker && initMarker}
{/* 点位弹框 */}
{showPopUp && popUpInfo && (
<PopUp
onClose={onPopUpClose}
{...popUpInfo}
/>
)}
{/* <CustomOverlay
>
<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
*/
import React, { forwardRef, useContext, useImperativeHandle } from 'react'
import React, { forwardRef, ReactNode, useContext, useImperativeHandle } from 'react'
import {
Marker as MapboxMarker,
MarkerProps as MapboxMarkerProps
MarkerProps as MapboxMarkerProps,
MarkerEvent
} from 'react-map-gl'
import { ConfigProvider, Image } from '@zhst/meta'
import markerPic from '../../assets/icons/marker_monitor.png'
import { Checkbox, ConfigProvider, Image } from '@zhst/meta'
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 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 {
prefixCls?: 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 {
@ -22,27 +46,43 @@ export interface MarkerRefProps {
const Marker = forwardRef<MarkerRefProps, MarkerProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
key,
longitude,
latitude,
onClick
checked = false,
type = 'camera',
status = 'blue',
onClick,
onMarkerClick,
customMarkerRender
} = props
const { getPrefixCls } = useContext(ConfigContext)
const componentName = getPrefixCls('-map-marker', customizePrefixCls);
useImperativeHandle(ref, () => ({
}))
useImperativeHandle(ref, () => ({}))
return (
<MapboxMarker
key={key}
longitude={Number(longitude)}
latitude={Number(latitude)}
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>
)
})

View File

@ -3,16 +3,21 @@
*/
import React, { forwardRef, useContext, useImperativeHandle } from 'react'
import {
PopUp as MapboxPopUp,
Popup as MapboxPopUp,
PopupProps as MapboxPopupProps
} 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
export interface PopUpProps extends MapboxPopupProps {
prefixCls?: string;
size?: number;
title?: string;
url?: string;
content?: string;
}
export interface PopUpRefProps {
@ -23,24 +28,28 @@ const PopUp = forwardRef<PopUpRefProps, PopUpProps>((props, ref) => {
longitude,
latitude,
onClose,
title,
content,
url,
prefixCls: customizePrefixCls
} = props
const { getPrefixCls } = useContext(ConfigContext)
const componentName = getPrefixCls('map-popup', customizePrefixCls);
useImperativeHandle(ref, () => ({
}))
useImperativeHandle(ref, () => ({}))
return (
<MapboxPopUp
anchor="top"
className={componentName}
longitude={Number(longitude)}
latitude={Number(latitude)}
onClose={onClose}
>
<div className={componentName}>
popup
<div className={classNames(`${componentName}-container`)}>
{title && <h2>{title}</h2>}
{url && <Image src={url} />}
{content && <p>{content}</p>}
</div>
</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
*/
export { default as PopUp } from './PopUp'
import PopUp 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 { MapBox } from '@zhst/map';
import axios from 'axios';
import { FloatButton, Switch } from '@zhst/meta';
const demo = () => {
const [markerData, setMarkerData] = useState([])
const [showMarker, setShowMarker] = useState(true)
const [popupInfo, setPopupInfo] = useState()
const mapRef = useRef(null);
// 初始化
@ -44,16 +47,40 @@ const demo = () => {
}, [])
return (
<div>
<FloatButton>
<Switch value={true}/>
</FloatButton>
<MapBox
onLoad={handleMapLoad}
ref={mapRef}
// draw
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
]
},
"include": [".dumirc.ts", "src/**/*", "packages/**/*"],
"include": [".dumirc.ts", "src/**/*", "packages/**/*", "global.d.ts"],
"exclude": ["node_modules", "lib", "es", ".dumi"]
}