diff --git a/src/editor/Property/OverlayBackground.tsx b/src/editor/Property/OverlayBackground.tsx new file mode 100644 index 0000000..1769023 --- /dev/null +++ b/src/editor/Property/OverlayBackground.tsx @@ -0,0 +1,43 @@ +import { CSSProperties } from "react"; +import { Upload } from "antd"; +import { IOverlay } from "@map"; +import { editorAction } from "@store"; + +export type OverlayBackgroundProps = { + overlay: IOverlay; +}; + +export const OverlayBackground = (props: OverlayBackgroundProps) => { + const { overlay } = props; + const { id, backgroundImage } = overlay; + + const beforeUpload = (file: File) => { + const reader = new FileReader(); + reader.onload = () => { + editorAction.updateOverlay({ + id, + backgroundImage: reader.result as string, + }); + }; + reader.readAsDataURL(file); + return false; + }; + + const style: CSSProperties = {}; + if (backgroundImage) { + style.backgroundImage = `url(${backgroundImage})`; + } + + return ( +
+ + 上传 + +
+ ); +}; diff --git a/src/editor/Property/index.less b/src/editor/Property/index.less index 0789f7b..f72d49d 100644 --- a/src/editor/Property/index.less +++ b/src/editor/Property/index.less @@ -1,3 +1,22 @@ .property-area{ padding: 8px; +} +.overlay-background{ + background-size: cover; +} +.overlay-background-upload{ + display: inline-block; + width: 100%; + height: 120px; + line-height: 120px; + text-align: center; + vertical-align: middle; + background-color: rgba(0, 0, 0, .2); + border: 1px dashed #d9d9d9; + border-radius: 2px; + cursor: pointer; + transition: border-color .3s; +} +.ant-form-item .ant-upload{ + color: #fff; } \ No newline at end of file diff --git a/src/editor/Property/index.tsx b/src/editor/Property/index.tsx index 7540a4f..f7ae30a 100644 --- a/src/editor/Property/index.tsx +++ b/src/editor/Property/index.tsx @@ -1,11 +1,12 @@ -import { useDispatch, useSelector } from "react-redux"; -import { Form, Input, Select } from "antd"; -import { selectedIdsSelector, mapOptionsSelector, StoreAction } from "@store"; +import { useSelector } from "react-redux"; +import { Form, Select } from "antd"; +import { selectedIdsSelector, mapOptionsSelector, editorAction } from "@store"; import { OverlayCategory, IOverlay } from "@map"; import { OverlayTypes } from "@types"; import { FineInput } from "./FineInput"; +import { OverlayBackground } from "./OverlayBackground"; import "./index.less"; @@ -19,14 +20,12 @@ const { Option } = Select; const Property = () => { const selectedIds = useSelector(selectedIdsSelector); const { overlays } = useSelector(mapOptionsSelector); - const dispatch = useDispatch(); if (!selectedIds?.length) return <>; const [id] = selectedIds; const overlay = overlays.find((overlay) => overlay.id === id)!; - const updateOverlayProps = (props: Partial) => { - dispatch(StoreAction.updateOverlayProps(props)); - }; + const updateOverlayProps = (props: Partial) => + editorAction.updateOverlay(props); const categorys: CategoryItem[] = []; if (overlay.type === OverlayTypes.Polyline) { @@ -83,6 +82,11 @@ const Property = () => { /> ) : null} + {overlay.type === OverlayTypes.Rectangle ? ( + + + + ) : null} ); diff --git a/src/map/MapEditor.ts b/src/map/MapEditor.ts index 5bd40e6..22be735 100644 --- a/src/map/MapEditor.ts +++ b/src/map/MapEditor.ts @@ -11,7 +11,7 @@ import { CircleEditor, } from "./editors"; -import { IMapOptions, IMapEditor } from "./type"; +import { IMapOptions, IMapEditor, IOverlay } from "./type"; import { PolygonOptions, PolylineOptions, SelectedOptions } from "./constants"; @@ -35,6 +35,7 @@ export class MapEditor extends Emitter implements IMapEditor { private _map: AMap.Map | undefined; private overlayEditors: BaseOverlayEditor[] = []; private overlayMap: Record = {}; + private imageLayerMap: Record = {}; private currentOverlayEditor: | BaseOverlayEditor | undefined; @@ -122,14 +123,14 @@ export class MapEditor extends Emitter implements IMapEditor { overlays.forEach((overlay) => { const { type, id } = overlay; const target = overlayEditors[type].build(overlay); - this.overlayMap[id] = { - type, - target, - }; + this._addOverlay(id, { type, target }); target.setOptions( type === OverlayTypes.Polyline ? PolylineOptions : PolygonOptions ); this.map.add(target); + if (overlay.backgroundImage) { + this.updateOverlayBackground(id, overlay.backgroundImage); + } }); } @@ -146,6 +147,28 @@ export class MapEditor extends Emitter implements IMapEditor { this.selectedIds = []; } + updateOverlayBackground(id: string, src?: string) { + if (this.overlayMap[id] == null) return; + const overlay = this.overlayMap[id]; + // 只支持矩形 + if (overlay.type !== OverlayTypes.Rectangle) return; + const rect = overlay.target as AMap.Rectangle; + const rectBounds = rect.getBounds()!; + + if (this.imageLayerMap[id] == null && src) { + const { southWest, northEast } = rectBounds; + this.imageLayerMap[id] = new AMap.ImageLayer({ + url: src, + // 居然不一样..... + bounds: [southWest.lng, southWest.lat, northEast.lng, northEast.lat], + zIndex: 0, + }); + this.map.add(this.imageLayerMap[id]); + } + this.imageLayerMap[id]?.setBounds(rectBounds); + src && this.imageLayerMap[id]?.setImageUrl(src); + } + createOverlay(type: OverlayTypes) { this.currentOverlayEditor = this.getEditorByType(type!); this.currentOverlayEditor?.create(); @@ -171,6 +194,10 @@ export class MapEditor extends Emitter implements IMapEditor { const { target } = this.overlayMap[id]; this.map.remove(target); delete this.overlayMap[id]; + if (this.imageLayerMap[id]) { + this.map.remove(this.imageLayerMap[id]); + delete this.imageLayerMap[id]; + } }); this.selectedIds = []; } @@ -183,10 +210,20 @@ export class MapEditor extends Emitter implements IMapEditor { const isCreatingOverlay = this.editorStatus === "creating"; let id = getUID(); if (isCreatingOverlay) { - this.overlayMap[id] = { type, target }; + this._addOverlay(id, { type, target }); } else { id = this.selectedIds[0]; } + this._updateOverlay(id); + target.setOptions( + type === OverlayTypes.Polyline ? PolylineOptions : PolygonOptions + ); + this.editorStatus = null; + } + + _updateOverlay(id: string) { + if (this.overlayMap[id] == null) return; + const { type, target } = this.overlayMap[id]; const evt: any = { id, type }; if (type === OverlayTypes.Circle) { const circle = target as AMap.Circle; @@ -196,11 +233,8 @@ export class MapEditor extends Emitter implements IMapEditor { } else { evt.path = getOverlayPaths(target, type); } - target.setOptions( - type === OverlayTypes.Polyline ? PolylineOptions : PolygonOptions - ); this.emit(EventTypes.FinishEditOverlay, evt); - this.editorStatus = null; + this.updateOverlayBackground(id); } editSelectedOverlay() { @@ -211,4 +245,13 @@ export class MapEditor extends Emitter implements IMapEditor { this.currentOverlayEditor?.edit(target); this.editorStatus = "editing"; } + + onOverlayDragEnd(id: string) { + this._updateOverlay(id); + } + + _addOverlay(id: string, overlay: OverlayTemp) { + this.overlayMap[id] = overlay; + overlay.target.on("dragend", () => this.onOverlayDragEnd(id)); + } } diff --git a/src/map/type.ts b/src/map/type.ts index 7d55525..0e85f9c 100644 --- a/src/map/type.ts +++ b/src/map/type.ts @@ -11,6 +11,7 @@ export interface IMapEditor { clearOverlays(): void; getCenter(): GeoJSON.Position; getZoom(): number; + updateOverlayBackground(id: string, src?: string): void; } export enum OverlayCategory { diff --git a/src/store/actions/index.ts b/src/store/actions/index.ts index f2f8cc4..18d4e4f 100644 --- a/src/store/actions/index.ts +++ b/src/store/actions/index.ts @@ -94,6 +94,13 @@ export class EditorAction { } } + updateOverlay(props: Partial) { + this.dispatch(StoreAction.updateOverlayProps(props)); + if (props.backgroundImage && props.id) { + this.mapEditor?.updateOverlayBackground(props.id, props.backgroundImage); + } + } + saveGeoJSON() { const geojson = getGeoJSON(this.mapOptions); downloadJSON(geojson, "fine.geojson"); diff --git a/src/store/reducers/map/index.ts b/src/store/reducers/map/index.ts index ee00246..2b08e67 100644 --- a/src/store/reducers/map/index.ts +++ b/src/store/reducers/map/index.ts @@ -35,7 +35,10 @@ export function finishEditOverlay(state = initState, payload: any) { const { overlays } = draft.map; const existed = overlays.find((overlay) => overlay.id === id); if (existed) { - overlays[overlays.indexOf(existed)] = overlay; + overlays[overlays.indexOf(existed)] = { + ...existed, + ...overlay, + }; } else { overlays.push(overlay); }