diff --git a/package.json b/package.json index 52f504e..a68ff34 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "devDependencies": { "@amap/amap-jsapi-types": "^0.0.8", "@types/geojson": "^7946.0.8", + "@types/lodash": "^4.14.179", "@types/react": "^17.0.33", "@types/react-dom": "^17.0.10", "@vitejs/plugin-react": "^1.0.7", diff --git a/src/editor/Plot/Tools/SearchModal.tsx b/src/editor/Plot/Tools/SearchModal.tsx new file mode 100644 index 0000000..85a6e4c --- /dev/null +++ b/src/editor/Plot/Tools/SearchModal.tsx @@ -0,0 +1,41 @@ +import { Modal, Input } from "antd"; +import { debounce } from "lodash"; +import { useState } from "react"; +import { editorAction, searchResultsSelector } from "@store"; +import { useSelector } from "react-redux"; + +export type SearchModalProps = { + visible: boolean; + onCancel: () => void; +}; + +const delaySearch = debounce((keyword: string) => { + editorAction.searchPlace(keyword); +}, 500); + +export const SearchModal = (props: SearchModalProps) => { + const { visible, onCancel } = props; + const [keyword, setKeyword] = useState(""); + const searchResults = useSelector(searchResultsSelector); + const searchPlace = (keyword: string) => { + setKeyword(keyword); + delaySearch(keyword); + }; + + return ( + + searchPlace(e.target.value)} + /> +
+ {searchResults.map((item) => ( +
+ {item.name} +
+ ))} +
+
+ ); +}; diff --git a/src/editor/Plot/Tools/index.tsx b/src/editor/Plot/Tools/index.tsx index 76f5d72..7721ca2 100644 --- a/src/editor/Plot/Tools/index.tsx +++ b/src/editor/Plot/Tools/index.tsx @@ -8,6 +8,8 @@ import { overlayTypeSelector, } from "@store"; import { OverlayTypes, Status } from "@types"; +import { SearchModal } from "./SearchModal"; +import { useState } from "react"; type IconWithTipProps = { type: string; @@ -55,6 +57,8 @@ const OverlayTool = (props: OverlayToolProps) => { }; const Tools = () => { + const [searchModalVisible, setSearchModalVisible] = useState(false); + return ( <>
@@ -65,12 +69,21 @@ const Tools = () => {
- + setSearchModalVisible(true)} + />
{/*
*/}
+ setSearchModalVisible(false)} + /> ); }; diff --git a/src/map/MapEditor.ts b/src/map/MapEditor.ts index 3a52160..7aaa434 100644 --- a/src/map/MapEditor.ts +++ b/src/map/MapEditor.ts @@ -41,6 +41,8 @@ export class MapEditor extends Emitter implements IMapEditor { | undefined; private selectedIds: string[] = []; private editorStatus: "editing" | "creating" | null = null; + private autoComplete: AMap.AutoComplete | undefined; + private placeSearch: AMap.PlaceSearch | undefined; constructor(dom: HTMLDivElement) { super(); this.dom = dom; @@ -64,6 +66,8 @@ export class MapEditor extends Emitter implements IMapEditor { ], }); this._map = new AMap.Map(this.dom); + this.autoComplete = new AMap.AutoComplete({}); + this.placeSearch = new AMap.PlaceSearch(this._map); this.initEditors(); // Space的key是空字符串, 这就离谱. registerHotkey(" ", { callback: this.finishEditOverlay.bind(this) }); @@ -217,6 +221,18 @@ export class MapEditor extends Emitter implements IMapEditor { return id; } + searchPlace(keyword: string) { + return new Promise((resolve: (items: AMap.SearchResultItem[]) => void) => { + this.autoComplete?.search(keyword, (status, result) => { + if (status !== "complete") { + resolve([]); + } else { + resolve(result.tips); + } + }); + }); + } + _buildFromOverlay(overlay: IOverlay) { const { type, id } = overlay; const editor = this.overlayEditors.find( diff --git a/src/map/type.ts b/src/map/type.ts index 67ac1f5..37e970b 100644 --- a/src/map/type.ts +++ b/src/map/type.ts @@ -13,6 +13,7 @@ export interface IMapEditor { getCenter(): GeoJSON.Position; getZoom(): number; updateOverlayBackground(id: string, src?: string): void; + searchPlace(keyword: string): Promise; } export enum OverlayCategory { diff --git a/src/store/actions/StoreAction.ts b/src/store/actions/StoreAction.ts index ad5a6ed..7b12610 100644 --- a/src/store/actions/StoreAction.ts +++ b/src/store/actions/StoreAction.ts @@ -9,6 +9,7 @@ export enum ActionTypes { ReplaceState = "replaceState", ClearOverlays = "clearOverlays", UpdateOverlayProps = "updateOverlayProps", + SearchPlace = "searchPlace", } type ActionCreator = (payload?: any) => { @@ -58,4 +59,10 @@ export const StoreAction: Record = { payload, }; }, + searchPlace(payload: AMap.SearchResultItem[]) { + return { + type: ActionTypes.SearchPlace, + payload, + }; + }, }; diff --git a/src/store/actions/index.ts b/src/store/actions/index.ts index 53d9912..5b5790c 100644 --- a/src/store/actions/index.ts +++ b/src/store/actions/index.ts @@ -163,6 +163,11 @@ export class EditorAction { // } + async searchPlace(keyword: string) { + const results = await this.mapEditor!.searchPlace(keyword); + this.dispatch(StoreAction.searchPlace(results)); + } + private _openGeoJSON(geojson: GeoJSON.FeatureCollection) { console.log(geojson); } diff --git a/src/store/initState.ts b/src/store/initState.ts index f83cf34..c65b7eb 100644 --- a/src/store/initState.ts +++ b/src/store/initState.ts @@ -7,4 +7,5 @@ export const initState: IEditorState = { map: { overlays: [], }, + searchResults: [], }; diff --git a/src/store/reducers/index.ts b/src/store/reducers/index.ts index 091fdfc..5e6d2c7 100644 --- a/src/store/reducers/index.ts +++ b/src/store/reducers/index.ts @@ -26,8 +26,15 @@ const actionReducers: Record = { [ActionTypes.ReplaceState]: replaceState, [ActionTypes.ClearOverlays]: clearOverlays, [ActionTypes.UpdateOverlayProps]: updateOverlayProps, + [ActionTypes.SearchPlace]: searchPlace, }; +function searchPlace(state = initState, payload: AMap.SearchResultItem[]) { + return produce(state, (draft) => { + draft.searchResults = payload; + }); +} + function updateOverlayProps(state = initState, payload: Partial) { return produce(state, (draft) => { const { id, ...rest } = payload; diff --git a/src/store/selectors.ts b/src/store/selectors.ts index e764c82..64c5fad 100644 --- a/src/store/selectors.ts +++ b/src/store/selectors.ts @@ -4,3 +4,5 @@ export const mapOptionsSelector = (state: IEditorState) => state.map; export const overlayTypeSelector = (state: IEditorState) => state.overlayType; export const statusSelector = (state: IEditorState) => state.status; export const selectedIdsSelector = (state: IEditorState) => state.selectedIds; +export const searchResultsSelector = (state: IEditorState) => + state.searchResults; diff --git a/src/store/type.ts b/src/store/type.ts index 93e2360..d8d8dfb 100644 --- a/src/store/type.ts +++ b/src/store/type.ts @@ -10,4 +10,5 @@ export interface IEditorState { status: Status | null; selectedIds: string[]; overlayType: OverlayTypes | null; + searchResults: AMap.SearchResultItem[]; } diff --git a/src/types/amap.d.ts b/src/types/amap.d.ts index 8d9730e..79520aa 100644 --- a/src/types/amap.d.ts +++ b/src/types/amap.d.ts @@ -32,5 +32,24 @@ declare global { class CircleEditor extends BaseEditor { // } + class PlaceSearch { + constructor(map: AMap.Map); + setCity(code: string): void; + search(name: string): void; + } + + type SearchResultItem = { + name: string; + adcode: string; + }; + + type AutoCompleteCallback = ( + status: string, + result: { tips: SearchResultItem[] } + ) => void; + class AutoComplete { + constructor(options: any); + search(name: string, callback: AutoCompleteCallback): void; + } } } diff --git a/yarn.lock b/yarn.lock index 57e4fc5..dbd24db 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2526,6 +2526,11 @@ resolved "https://registry.nlark.com/@types/json5/download/@types/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/lodash@^4.14.179": + version "4.14.179" + resolved "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.179.tgz#490ec3288088c91295780237d2497a3aa9dfb5c5" + integrity sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w== + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.npmmirror.com/@types/parse-json/download/@types/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" @@ -4447,7 +4452,7 @@ lodash.merge@^4.6.2: lodash@^4.17.21: version "4.17.21" - resolved "https://registry.npmmirror.com/lodash/download/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-update@^4.0.0: