Browse Source

支持区域关键字搜索

master
Cmen 3 years ago
parent
commit
4a508cf85d
  1. 1
      package.json
  2. 41
      src/editor/Plot/Tools/SearchModal.tsx
  3. 15
      src/editor/Plot/Tools/index.tsx
  4. 16
      src/map/MapEditor.ts
  5. 1
      src/map/type.ts
  6. 7
      src/store/actions/StoreAction.ts
  7. 5
      src/store/actions/index.ts
  8. 1
      src/store/initState.ts
  9. 7
      src/store/reducers/index.ts
  10. 2
      src/store/selectors.ts
  11. 1
      src/store/type.ts
  12. 19
      src/types/amap.d.ts
  13. 7
      yarn.lock

1
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",

41
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 (
<Modal visible={visible} onCancel={onCancel} footer={[]} title="搜索定位">
<Input
placeholder="输入关键字"
value={keyword}
onChange={(e) => searchPlace(e.target.value)}
/>
<div className="search-result-container">
{searchResults.map((item) => (
<div key={item.name} className="search-result-item">
{item.name}
</div>
))}
</div>
</Modal>
);
};

15
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 (
<>
<div className="marker-tools">
@ -65,12 +69,21 @@ const Tools = () => {
</div>
<div className="common-tools">
<div className="tools-group">
<IconWithTip type="search" text="搜索定位" placement="right" />
<IconWithTip
type="search"
text="搜索定位"
placement="right"
onClick={() => setSearchModalVisible(true)}
/>
</div>
{/* <div className="tools-group">
<IconWithTip type="select" text="选择" placement="right" />
</div> */}
</div>
<SearchModal
visible={searchModalVisible}
onCancel={() => setSearchModalVisible(false)}
/>
</>
);
};

16
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(

1
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<AMap.SearchResultItem[]>;
}
export enum OverlayCategory {

7
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<ActionTypes, ActionCreator> = {
payload,
};
},
searchPlace(payload: AMap.SearchResultItem[]) {
return {
type: ActionTypes.SearchPlace,
payload,
};
},
};

5
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);
}

1
src/store/initState.ts

@ -7,4 +7,5 @@ export const initState: IEditorState = {
map: {
overlays: [],
},
searchResults: [],
};

7
src/store/reducers/index.ts

@ -26,8 +26,15 @@ const actionReducers: Record<ActionTypes, ActionReducer> = {
[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<IOverlay>) {
return produce(state, (draft) => {
const { id, ...rest } = payload;

2
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;

1
src/store/type.ts

@ -10,4 +10,5 @@ export interface IEditorState {
status: Status | null;
selectedIds: string[];
overlayType: OverlayTypes | null;
searchResults: AMap.SearchResultItem[];
}

19
src/types/amap.d.ts vendored

@ -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;
}
}
}

7
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:

Loading…
Cancel
Save