map.vue 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. <template>
  2. <div class="map-layout" @click="activeId = null">
  3. <div
  4. id="map"
  5. class="map-container"
  6. ref="container"
  7. :class="{ active: !!activeId }"
  8. @click.stop
  9. @mousedown="activeId = null"
  10. @wheel="activeId = null"
  11. >
  12. <div class="map-component">
  13. <el-tooltip
  14. class="tooltip"
  15. :visible="!!activeId"
  16. :content="active?.name"
  17. effect="light"
  18. placement="top"
  19. virtual-triggering
  20. :virtual-ref="triggerRef"
  21. />
  22. <el-select
  23. v-model="tileType"
  24. placeholder="选择底图"
  25. style="width: 120px"
  26. class="tile-type-select"
  27. >
  28. <el-option
  29. v-for="item in tileOptions"
  30. :key="item"
  31. :label="item"
  32. :value="item"
  33. />
  34. </el-select>
  35. </div>
  36. </div>
  37. <div class="right-control">
  38. <MapRight
  39. @fly-point="flyScenePoint"
  40. @fly-scene="flyScene"
  41. @goto-point="gotoPoint"
  42. />
  43. </div>
  44. </div>
  45. </template>
  46. <script setup lang="ts">
  47. import MapRight from "./map-right.vue";
  48. import { router, setDocTitle } from "@/router";
  49. import { TileType, createMap } from "./";
  50. import { ScenePoint, Scene, scenePoints, scenes } from "@/store/scene";
  51. import { initRelics, initSelfRelics, relics } from "@/store/relics";
  52. import { computed, onMounted, ref, watchEffect, watch } from "vue";
  53. import { Manage } from "./manage";
  54. const activeId = ref<ScenePoint["id"] | null>();
  55. const active = computed(() =>
  56. scenePoints.value.find((point) => point.id === activeId.value)
  57. );
  58. const activePixel = ref<number[] | null>();
  59. const triggerRef = ref({
  60. getBoundingClientRect() {
  61. return DOMRect.fromRect({
  62. x: activePixel.value![0],
  63. y: activePixel.value![1],
  64. width: 0,
  65. height: 0,
  66. });
  67. },
  68. });
  69. const tileOptions: TileType[] = ["影像底图", "矢量底图"];
  70. const tileType = ref<TileType>(tileOptions[0]);
  71. const points = computed(() =>
  72. scenePoints.value
  73. .filter((point) => point.pos)
  74. .map((point) => ({
  75. data: point.pos,
  76. id: point.id,
  77. label: point.name,
  78. }))
  79. );
  80. const gotoPoint = (point: ScenePoint) => {
  81. router.push({
  82. name: router.currentRoute.value.name === "map" ? "pano" : "query-pano",
  83. params: { pid: point.id },
  84. });
  85. };
  86. const flyUserCenter = () => {
  87. navigator.geolocation.getCurrentPosition(
  88. (pos) => {
  89. mapManage.setCenter([pos.coords.longitude, pos.coords.latitude]);
  90. },
  91. () => console.error("获取中心位置失败"),
  92. {
  93. enableHighAccuracy: true,
  94. timeout: 5000,
  95. maximumAge: 0,
  96. }
  97. );
  98. };
  99. const center = [109.47293862712675, 30.26530938156551];
  100. const container = ref<HTMLDivElement>();
  101. let mapManage: Manage;
  102. onMounted(async () => {
  103. mapManage = createMap(container.value!);
  104. mapManage.setCenter(center);
  105. mapManage.hotsBus.on("active", (id) => {
  106. if (id) {
  107. activeId.value = id;
  108. active.value && activeScenePoint(active.value!);
  109. } else {
  110. activeId.value = null;
  111. activePixel.value = null;
  112. }
  113. });
  114. mapManage.hotsBus.on("click", (id) => {
  115. const point = id && scenePoints.value.find((point) => point.id === id);
  116. point && gotoPoint(point);
  117. });
  118. refreshHots();
  119. refreshTileType();
  120. });
  121. const activeScenePoint = (point: ScenePoint) => {
  122. activePixel.value = mapManage.map.getPixelFromCoordinate(point.pos);
  123. activeId.value = point.id;
  124. };
  125. const flyPos = (pos: number[]) => mapManage.map.getView().setCenter(pos);
  126. const flyScenePoint = (point: ScenePoint) => {
  127. flyPos(point.pos);
  128. setTimeout(() => {
  129. activeScenePoint(point);
  130. }, 16);
  131. };
  132. const flyScene = (scene: Scene) => {
  133. const totalPos = [0, 0];
  134. let numCalc = 0;
  135. for (let i = 0; i < scene.scenePos.length; i++) {
  136. totalPos[0] += scene.scenePos[i].pos[0];
  137. totalPos[1] += scene.scenePos[i].pos[1];
  138. numCalc++;
  139. }
  140. totalPos[0] /= numCalc;
  141. totalPos[1] /= numCalc;
  142. flyPos(totalPos);
  143. };
  144. const refreshHots = () => {
  145. if (!mapManage) return;
  146. mapManage.clearHots();
  147. mapManage.addHots(points.value);
  148. };
  149. const refreshTileType = () => {
  150. if (!mapManage) return;
  151. mapManage.setTileType(tileType.value);
  152. };
  153. watch(points, refreshHots, { immediate: true });
  154. watch(tileType, refreshTileType, { immediate: true });
  155. watch(
  156. () => [router.currentRoute.value.name, router.currentRoute.value.params?.relicsId],
  157. ([name, rid]) => {
  158. if (["map", "query-map"].includes(name as string)) {
  159. relics.value = undefined;
  160. const fn = name === "map" ? initSelfRelics : initRelics;
  161. fn(Number(rid)).finally(() => {
  162. if (!relics.value) {
  163. router.replace({ name: "relics" });
  164. }
  165. const scene = scenes.value.find(
  166. (scene) => !scene.scenePos.every((pos) => !pos.pos || pos.pos.length === 0)
  167. );
  168. if (scene) {
  169. flyScene(scene);
  170. } else {
  171. flyUserCenter();
  172. }
  173. });
  174. }
  175. },
  176. { immediate: true }
  177. );
  178. watchEffect(() => {
  179. if (
  180. ["map", "query-map"].includes(router.currentRoute.value.name as string) &&
  181. relics.value
  182. ) {
  183. setDocTitle(relics.value.name);
  184. }
  185. });
  186. </script>
  187. <style lang="scss">
  188. .tooltip {
  189. pointer-events: none;
  190. }
  191. .map-layout {
  192. display: flex;
  193. flex-direction: row;
  194. height: 100%;
  195. }
  196. .map-container {
  197. flex: 1;
  198. position: relative;
  199. }
  200. .right-control {
  201. flex: none;
  202. width: 300px;
  203. padding: 15px;
  204. border-left: 1px solid var(--border-color);
  205. }
  206. .map-component {
  207. width: 100%;
  208. height: 100%;
  209. position: relative;
  210. }
  211. .active {
  212. cursor: pointer;
  213. }
  214. .active-point {
  215. position: absolute;
  216. pointer-events: none;
  217. }
  218. .map-component {
  219. pointer-events: none;
  220. position: absolute;
  221. width: 100%;
  222. height: 100%;
  223. left: 0;
  224. top: 0;
  225. z-index: 9;
  226. }
  227. .env {
  228. width: 100%;
  229. height: 100%;
  230. }
  231. .tile-type-select {
  232. pointer-events: all;
  233. position: absolute;
  234. right: 10px;
  235. top: 10px;
  236. }
  237. </style>