map.vue 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  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, Manage } from "./openlayer";
  50. import { ScenePoint, Scene, scenePoints, scenes } from "@/store/scene";
  51. import { initRelics, initSelfRelics, relics } from "@/store/relics";
  52. import { computed, onMounted, ref, watchEffect, watch, onUnmounted } from "vue";
  53. import ScaleLine from "ol/control/ScaleLine";
  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. mapManage.setCenter([116.412611, 39.908866]);
  88. navigator.geolocation.getCurrentPosition(
  89. (pos) => {
  90. console.log("获取中心位置成功", pos);
  91. mapManage.setCenter([pos.coords.longitude, pos.coords.latitude]);
  92. },
  93. (e) => {
  94. console.error(e);
  95. console.error("获取中心位置失败");
  96. },
  97. {
  98. enableHighAccuracy: false,
  99. timeout: 50000,
  100. maximumAge: 0,
  101. }
  102. );
  103. };
  104. const center = [109.47293862712675, 30.26530938156551];
  105. const container = ref<HTMLDivElement>();
  106. let mapManage: Manage;
  107. onMounted(async () => {
  108. mapManage = createMap(container.value!);
  109. mapManage.setCenter(center);
  110. mapManage.hotsBus.on("active", (id) => {
  111. if (id) {
  112. activeId.value = id;
  113. active.value && activeScenePoint(active.value!);
  114. } else {
  115. activeId.value = null;
  116. activePixel.value = null;
  117. }
  118. });
  119. mapManage.hotsBus.on("click", (id) => {
  120. const point = id && scenePoints.value.find((point) => point.id === id);
  121. point && gotoPoint(point);
  122. });
  123. refreshHots();
  124. refreshTileType();
  125. const scaleLine = new ScaleLine({
  126. className: "scale-view",
  127. maxWidth: 150,
  128. minWidth: 100,
  129. units: "metric",
  130. });
  131. // 加载比例尺
  132. mapManage.map.addControl(scaleLine);
  133. watch(
  134. tileType,
  135. (type) => {
  136. const el = (scaleLine as any).element as HTMLDivElement;
  137. el.classList.add(type === "影像底图" ? "light" : "dark");
  138. el.classList.remove(type === "影像底图" ? "dark" : "light");
  139. console.log(el, type);
  140. },
  141. { flush: "post", immediate: true }
  142. );
  143. });
  144. const activeScenePoint = (point: ScenePoint) => {
  145. activePixel.value = mapManage.map.getPixelFromCoordinate(point.pos);
  146. activeId.value = point.id;
  147. };
  148. const flyPos = (pos: number[]) => mapManage.map.getView().setCenter(pos);
  149. const flyScenePoint = (point: ScenePoint) => {
  150. flyPos(point.pos);
  151. setTimeout(() => {
  152. activeScenePoint(point);
  153. }, 16);
  154. };
  155. const flyScene = (scene: Scene) => {
  156. const totalPos = [0, 0];
  157. let numCalc = 0;
  158. for (let i = 0; i < scene.scenePos.length; i++) {
  159. const coord = scene.scenePos[i].pos as number[];
  160. if (coord && coord.length > 0) {
  161. totalPos[0] += coord[0];
  162. totalPos[1] += coord[1];
  163. numCalc++;
  164. }
  165. }
  166. totalPos[0] /= numCalc;
  167. totalPos[1] /= numCalc;
  168. flyPos(totalPos);
  169. };
  170. const refreshHots = () => {
  171. if (!mapManage) return;
  172. mapManage.clearHots();
  173. mapManage.addHots(points.value);
  174. };
  175. const refreshTileType = () => {
  176. if (!mapManage) return;
  177. mapManage.setTileType(tileType.value);
  178. };
  179. watch(points, refreshHots, { immediate: true });
  180. watch(tileType, refreshTileType, { immediate: true });
  181. watch(
  182. () => [router.currentRoute.value.name, router.currentRoute.value.params?.relicsId],
  183. ([name, rid], old) => {
  184. if (["map", "query-map"].includes(name as string) && (!old || old[1] !== rid)) {
  185. relics.value = undefined;
  186. const fn = name === "map" ? initSelfRelics : initRelics;
  187. fn(Number(rid)).finally(() => {
  188. if (!relics.value) {
  189. router.replace({ name: "relics" });
  190. }
  191. if (!autoInitPos()) {
  192. flyUserCenter();
  193. }
  194. });
  195. }
  196. },
  197. { immediate: true }
  198. );
  199. const autoInitPos = () => {
  200. const scene = scenes.value.find(
  201. (scene) => !scene.scenePos.every((pos) => !pos.pos || pos.pos.length === 0)
  202. );
  203. if (scene) {
  204. flyScene(scene);
  205. return true;
  206. } else {
  207. return false;
  208. }
  209. };
  210. watch(
  211. () => {
  212. const scene = scenes.value.find(
  213. (scene) => !scene.scenePos.every((pos) => !pos.pos || pos.pos.length === 0)
  214. );
  215. return scene?.sceneCode;
  216. },
  217. (firstCode) => {
  218. if (firstCode) {
  219. autoInitPos();
  220. }
  221. }
  222. );
  223. watchEffect(() => {
  224. if (
  225. ["map", "query-map"].includes(router.currentRoute.value.name as string) &&
  226. relics.value
  227. ) {
  228. setDocTitle(relics.value.name);
  229. }
  230. });
  231. onUnmounted(() => mapManage.map.dispose());
  232. </script>
  233. <style lang="scss">
  234. .tooltip {
  235. pointer-events: none;
  236. }
  237. .map-layout {
  238. display: flex;
  239. flex-direction: row;
  240. height: 100%;
  241. }
  242. .map-container {
  243. flex: 1;
  244. position: relative;
  245. }
  246. .right-control {
  247. flex: none;
  248. width: 320px;
  249. padding: 15px;
  250. border-left: 1px solid var(--border-color);
  251. }
  252. .map-component {
  253. width: 100%;
  254. height: 100%;
  255. position: relative;
  256. }
  257. .active {
  258. cursor: pointer;
  259. }
  260. .active-point {
  261. position: absolute;
  262. pointer-events: none;
  263. }
  264. .map-component {
  265. pointer-events: none;
  266. position: absolute;
  267. width: 100%;
  268. height: 100%;
  269. left: 0;
  270. top: 0;
  271. z-index: 9;
  272. }
  273. .env {
  274. width: 100%;
  275. height: 100%;
  276. }
  277. .tile-type-select {
  278. pointer-events: all;
  279. position: absolute;
  280. right: 10px;
  281. top: 10px;
  282. }
  283. .scale-view {
  284. --color: #fff;
  285. position: absolute;
  286. left: 20px;
  287. bottom: 20px;
  288. height: 8px;
  289. color: var(--color);
  290. text-align: center;
  291. border: 1px solid var(--color);
  292. border-top: none;
  293. z-index: 1;
  294. font-size: 14px;
  295. display: flex;
  296. align-items: end;
  297. &.light {
  298. --color: #fff;
  299. > div {
  300. text-shadow: 0 0 2px #000;
  301. }
  302. }
  303. &.dark {
  304. --color: #000;
  305. > div {
  306. text-shadow: 0 0 2px #fff;
  307. }
  308. }
  309. }
  310. </style>
  311. ./openlayer./openlayer/manage