Browse Source

选中交互

master
Cmen 3 years ago
parent
commit
dbd0684bd1
  1. 17
      src/editor/Catalog/index.tsx
  2. 7
      src/editor/Catalog/style.less
  3. 4
      src/editor/Plot/Tools/index.tsx
  4. 2
      src/editor/Plot/index.tsx
  5. 4
      src/editor/Plot/mapHooks.ts
  6. 4
      src/map/constants.ts
  7. 34
      src/map/index.ts
  8. 23
      src/store/actions.ts
  9. 2
      src/store/index.ts
  10. 3
      src/store/initState.ts
  11. 3
      src/store/reducers/index.ts
  12. 17
      src/store/reducers/map/index.ts
  13. 7
      src/store/type.ts
  14. 13
      src/types/enum.ts

17
src/editor/Catalog/index.tsx

@ -1,10 +1,10 @@
import { Collapse } from "antd"; import { Collapse } from "antd";
import { useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { mapStateSelector } from "@store"; import { IOverlay, EditorAction, mapStateSelector } from "@store";
import { IOverlay } from "@store";
import "./style.less"; import "./style.less";
import classNames from "classnames";
const { Panel } = Collapse; const { Panel } = Collapse;
@ -14,12 +14,21 @@ export type OverlayListProps = {
const OverlayList = (props: OverlayListProps) => { const OverlayList = (props: OverlayListProps) => {
const { overlays } = props; const { overlays } = props;
const dispatch = useDispatch();
const onClick = (id: string) => () =>
dispatch(EditorAction.selectOverlay(id));
const { selectedIds } = useSelector(mapStateSelector);
return ( return (
<> <>
{overlays.map((overlay, index) => { {overlays.map((overlay, index) => {
const selected = selectedIds && selectedIds.indexOf(overlay.id) >= 0;
const className = classNames("overlay-item", {
"overlay-item-selected": selected,
});
return ( return (
<div key={index} className="overlay-item"> <div key={index} className={className} onClick={onClick(overlay.id)}>
{overlay.name} {overlay.name}
</div> </div>
); );

7
src/editor/Catalog/style.less

@ -10,7 +10,10 @@
.overlay-item{ .overlay-item{
margin-bottom: 4px; margin-bottom: 4px;
cursor: pointer; cursor: pointer;
&:hover, &[data-selected]{ &:hover{
background-color: #f7fafc; background-color: #e5e6e7;
}
} }
.overlay-item-selected{
background-color: #e5e6e7;
} }

4
src/editor/Plot/Tools/index.tsx

@ -1,7 +1,7 @@
import { Tooltip } from "antd"; import { Tooltip } from "antd";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import classnames from "classnames"; import classnames from "classnames";
import { editorAction, OverlayNamePrefixs, mapStateSelector } from "@store"; import { EditorAction, OverlayNamePrefixs, mapStateSelector } from "@store";
import { OverlayTypes } from "@types"; import { OverlayTypes } from "@types";
type IconWithTipProps = { type IconWithTipProps = {
@ -43,7 +43,7 @@ const OverlayTool = (props: OverlayToolProps) => {
const { overlayType } = useSelector(mapStateSelector); const { overlayType } = useSelector(mapStateSelector);
const selected = type === overlayType; const selected = type === overlayType;
const onClick = () => dispatch(editorAction.createOverlay(type)); const onClick = () => dispatch(EditorAction.createOverlay(type));
return <IconWithTip {...{ text, onClick, type, selected }} />; return <IconWithTip {...{ text, onClick, type, selected }} />;
}; };

2
src/editor/Plot/index.tsx

@ -2,7 +2,7 @@ import { Layout } from "antd";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { useSelector, useDispatch } from "react-redux"; import { useSelector, useDispatch } from "react-redux";
import { MapEditor } from "@map"; import { MapEditor } from "@map";
import { editorAction, IEditorState } from "@store"; import { IEditorState } from "@store";
import Tools from "./Tools"; import Tools from "./Tools";
import { registerMapEventHooks } from "./mapHooks"; import { registerMapEventHooks } from "./mapHooks";

4
src/editor/Plot/mapHooks.ts

@ -1,5 +1,5 @@
import { Dispatch } from "react"; import { Dispatch } from "react";
import { editorAction, IEditorState } from "@store"; import { EditorAction, IEditorState } from "@store";
import { MapEditor } from "@map"; import { MapEditor } from "@map";
import { EventTypes } from "../../types/enum"; import { EventTypes } from "../../types/enum";
@ -7,6 +7,6 @@ import { EventTypes } from "../../types/enum";
export function registerMapEventHooks(map: MapEditor, dispatch: Dispatch<any>) { export function registerMapEventHooks(map: MapEditor, dispatch: Dispatch<any>) {
// //
map.on(EventTypes.FinishCreateOverlay, (evt: any) => map.on(EventTypes.FinishCreateOverlay, (evt: any) =>
dispatch(editorAction.finishCreateOverlay(evt)) dispatch(EditorAction.finishCreateOverlay(evt))
); );
} }

4
src/map/constants.ts

@ -26,3 +26,7 @@ export const PolylineOptions: OverlayOptions = {
draggable: false, draggable: false,
bubble: true, bubble: true,
}; };
export const SelectedOptions: OverlayOptions = {
strokeColor: SelectedStroke,
};

34
src/map/index.ts

@ -13,9 +13,9 @@ import {
} from "./editors"; } from "./editors";
import { registerHotkey } from "../utils/hotkeys"; import { registerHotkey } from "../utils/hotkeys";
import { PolygonOptions, PolylineOptions } from "./constants"; import { PolygonOptions, PolylineOptions, SelectedOptions } from "./constants";
import { EventTypes, OverlayTypes } from "../types/enum"; import { Command, EventTypes, OverlayTypes } from "../types/enum";
let uuid = 0; let uuid = 0;
const getUuid = () => ++uuid; const getUuid = () => ++uuid;
@ -26,11 +26,16 @@ type AMapOverlayEditor =
| AMap.PolylineEditor | AMap.PolylineEditor
| AMap.CircleEditor; | AMap.CircleEditor;
type OverlayTemp = {
type: OverlayTypes;
target: AMap.MapOverlay;
};
export class MapEditor extends Emitter { export class MapEditor extends Emitter {
dom: HTMLDivElement; dom: HTMLDivElement;
private _map: AMap.Map | undefined; private _map: AMap.Map | undefined;
private overlayEditors: BaseOverlayEditor<AMapOverlayEditor>[] = []; private overlayEditors: BaseOverlayEditor<AMapOverlayEditor>[] = [];
private overlayMap: Record<number, AMap.MapOverlay> = {}; private overlayMap: Record<string, OverlayTemp> = {};
private _currentOverlayEditor: private _currentOverlayEditor:
| BaseOverlayEditor<AMapOverlayEditor> | BaseOverlayEditor<AMapOverlayEditor>
| undefined; | undefined;
@ -63,14 +68,31 @@ export class MapEditor extends Emitter {
} }
update(mapState: IMapState) { update(mapState: IMapState) {
const { status, overlayType } = mapState; const { command } = mapState;
if (status === "createOverlay") { switch (command) {
case Command.CreateOverlay:
this._createOverlay(mapState);
break;
case Command.SelectOverlay:
this._selectOverlays(mapState.selectedIds);
}
}
_createOverlay(mapState: IMapState) {
const { overlayType } = mapState;
this.finishCreateOverlay(); this.finishCreateOverlay();
this._currentOverlayEditor = this.overlayEditors.find( this._currentOverlayEditor = this.overlayEditors.find(
(editor) => editor.getType() === overlayType (editor) => editor.getType() === overlayType
); );
this._currentOverlayEditor?.create(); this._currentOverlayEditor?.create();
} }
_selectOverlays(ids?: string[]) {
if (!ids?.length) return;
ids.forEach((id) => {
const { target } = this.overlayMap[id];
target.setOptions(SelectedOptions);
});
} }
finishCreateOverlay() { finishCreateOverlay() {
@ -79,7 +101,7 @@ export class MapEditor extends Emitter {
if (target == null) return; if (target == null) return;
const id = getUuid(); const id = getUuid();
const type = this._currentOverlayEditor.getType(); const type = this._currentOverlayEditor.getType();
this.overlayMap[id] = target; this.overlayMap[id] = { type, target };
const evt: any = { id, type }; const evt: any = { id, type };
if (type === OverlayTypes.Circle) { if (type === OverlayTypes.Circle) {
const circle = target as AMap.Circle; const circle = target as AMap.Circle;

23
src/store/actions.ts

@ -4,28 +4,33 @@ export enum ActionTypes {
// AddRect = "addRect", // AddRect = "addRect",
CreateOverlay = "createOverlay", CreateOverlay = "createOverlay",
FinishCreateOverlay = "finishCreateOverlay", FinishCreateOverlay = "finishCreateOverlay",
SelectOverlay = "selectOverlay",
} }
export type CreatedOverlay = { export type CreatedOverlay = {
id: string; id: string;
type: OverlayTypes; type: OverlayTypes;
}; };
export class EditorAction {
// addRect() { type ActionCreator = (payload: any) => { type: ActionTypes; payload: any };
// return {
// type: ActionTypes.AddRect, export const EditorAction: Record<ActionTypes, ActionCreator> = {
// };
// }
createOverlay(type: OverlayTypes) { createOverlay(type: OverlayTypes) {
return { return {
type: ActionTypes.CreateOverlay, type: ActionTypes.CreateOverlay,
payload: type, payload: type,
}; };
} },
finishCreateOverlay(overlay: any) { finishCreateOverlay(overlay: any) {
return { return {
type: ActionTypes.FinishCreateOverlay, type: ActionTypes.FinishCreateOverlay,
payload: overlay as CreatedOverlay, payload: overlay as CreatedOverlay,
}; };
} },
} selectOverlay(id: string) {
return {
type: ActionTypes.SelectOverlay,
payload: id,
};
},
};

2
src/store/index.ts

@ -15,7 +15,7 @@ const store: IStore = createStore(reducer);
export { store }; export { store };
export const editorAction = new EditorAction(); export { EditorAction };
export const StoreContext = createContext<IStore | null>( export const StoreContext = createContext<IStore | null>(
null null

3
src/store/initState.ts

@ -2,7 +2,8 @@ import { IEditorState } from "./type";
export const initState: IEditorState = { export const initState: IEditorState = {
map: { map: {
status: "", status: null,
command: null,
overlayType: null, overlayType: null,
polygons: [], polygons: [],
polylines: [], polylines: [],

3
src/store/reducers/index.ts

@ -1,6 +1,6 @@
import { initState } from "../initState"; import { initState } from "../initState";
import { ActionTypes } from "../actions"; import { ActionTypes } from "../actions";
import { createOverlay, finishCreateOverlay } from "./map"; import { createOverlay, finishCreateOverlay, selectOverlay } from "./map";
import { IEditorState } from "@store"; import { IEditorState } from "@store";
export type Action = { export type Action = {
@ -13,6 +13,7 @@ type ActionReducer = (state: IEditorState, payload: any) => IEditorState;
const actionReducers: Record<ActionTypes, ActionReducer> = { const actionReducers: Record<ActionTypes, ActionReducer> = {
[ActionTypes.CreateOverlay]: createOverlay, [ActionTypes.CreateOverlay]: createOverlay,
[ActionTypes.FinishCreateOverlay]: finishCreateOverlay, [ActionTypes.FinishCreateOverlay]: finishCreateOverlay,
[ActionTypes.SelectOverlay]: selectOverlay,
// addRect: undefined // addRect: undefined
}; };

17
src/store/reducers/map/index.ts

@ -1,11 +1,12 @@
import produce from "immer"; import produce from "immer";
import { initState } from "../../initState"; import { initState } from "../../initState";
import { IOverlay, OverlayNamePrefixs } from "@store"; import { IOverlay, OverlayNamePrefixs } from "@store";
import { OverlayTypes } from "@types"; import { OverlayTypes, Status, Command } from "@types";
export function createOverlay(state = initState, payload: any) { export function createOverlay(state = initState, payload: any) {
return produce(state, (draft) => { return produce(state, (draft) => {
draft.map.status = "createOverlay"; draft.map.status = Status.CreateOverlay;
draft.map.command = Command.CreateOverlay;
draft.map.overlayType = payload as OverlayTypes; draft.map.overlayType = payload as OverlayTypes;
}); });
} }
@ -16,7 +17,8 @@ export function finishCreateOverlay(state = initState, payload: any) {
const { type } = overlay; const { type } = overlay;
// todo: uniqueName. // todo: uniqueName.
overlay.name = OverlayNamePrefixs[type] + overlay.id; overlay.name = OverlayNamePrefixs[type] + overlay.id;
draft.map.status = ""; draft.map.status = null;
draft.map.command = null;
draft.map.overlayType = null; draft.map.overlayType = null;
(type === OverlayTypes.Rectangle (type === OverlayTypes.Rectangle
? draft.map.rectangles ? draft.map.rectangles
@ -28,3 +30,12 @@ export function finishCreateOverlay(state = initState, payload: any) {
).push(overlay); ).push(overlay);
}); });
} }
export function selectOverlay(state = initState, payload: any) {
const id = payload as string;
return produce(state, (draft) => {
draft.map.command = Command.SelectOverlay;
draft.map.selectedIds = [id];
draft.map.status = null;
});
}

7
src/store/type.ts

@ -1,4 +1,4 @@
import { OverlayTypes } from "@types"; import { OverlayTypes, Status, Command } from "@types";
export interface IOverlay { export interface IOverlay {
id: string; id: string;
@ -8,14 +8,15 @@ export interface IOverlay {
paths?: number[][]; paths?: number[][];
radius?: number; radius?: number;
} }
export interface IMapState { export interface IMapState {
status: string; status: Status | null;
command: Command | null;
overlayType: OverlayTypes | null; overlayType: OverlayTypes | null;
polygons: IOverlay[]; polygons: IOverlay[];
polylines: IOverlay[]; polylines: IOverlay[];
circles: IOverlay[]; circles: IOverlay[];
rectangles: IOverlay[]; rectangles: IOverlay[];
selectedIds?: string[];
} }
export interface IEditorState { export interface IEditorState {

13
src/types/enum.ts

@ -8,3 +8,16 @@ export enum OverlayTypes {
Polyline = "polyline", Polyline = "polyline",
Circle = "circle", Circle = "circle",
} }
// 操作指令集
export enum Command {
CreateOverlay = "createOveraly",
SelectOverlay = "selectOverlay",
}
// 地图状态.
export enum Status {
CreateOverlay = "createOverlay",
Searching = "searching",
Selecting = "selecing",
}

Loading…
Cancel
Save