feat(@zhst/map): 添加popup功能
3
global.d.ts
vendored
@ -1 +1,4 @@
|
|||||||
declare module '*.less';
|
declare module '*.less';
|
||||||
|
declare module '*.png';
|
||||||
|
declare module '*.jpg';
|
||||||
|
declare module '*.jpeg';
|
||||||
|
@ -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>
|
||||||
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
@ -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>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -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>
|
||||||
)
|
)
|
||||||
|
11
packages/map/src/components/popup/index.less
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
.zhst-map-popup {
|
||||||
|
.mapboxgl-popup-content {
|
||||||
|
padding: 12px 6px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-container {
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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"]
|
||||||
}
|
}
|
||||||
|