layout.vue 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. <template>
  2. <div class="map-layout" v-loading="!loaded || captureing">
  3. <div class="custom_bar">
  4. <div class="back_container" v-if="!queryMode">
  5. <el-button :icon="Back" circle type="primary" @click="router.back()" />
  6. </div>
  7. <div class="nav_container">
  8. <div
  9. v-for="menu in menus"
  10. :key="menu.name"
  11. class="nav_item"
  12. :class="{
  13. active: menu.router.includes(router.currentRoute.value.name.toString()),
  14. }"
  15. @click="router.replace({ name: menu.router[Number(queryMode)] })"
  16. >
  17. <el-icon size="20">
  18. <component :is="menu.icon" />
  19. </el-icon>
  20. <span>{{ menu.name }}</span>
  21. </div>
  22. </div>
  23. </div>
  24. <div class="map-oper-layout">
  25. <div class="map-container" :ref="setMapContainer">
  26. <div class="board" :ref="setBoardContainer"></div>
  27. <div class="map-top-out-pano">
  28. <template v-if="!isCoordPage">
  29. <el-button @click="capture" v-if="loaded && !queryMode">
  30. 提取位置图
  31. </el-button>
  32. <el-button @click="showPoints = !showPoints">
  33. <el-checkbox :modelValue="showPoints" label="点位" size="large" />
  34. </el-button>
  35. </template>
  36. <div class="tile-select">
  37. <el-select
  38. v-model="tileType"
  39. placeholder="选择底图"
  40. style="width: 120px"
  41. class="tile-type-select"
  42. >
  43. <el-option
  44. v-for="item in tileOptions"
  45. :key="item"
  46. :label="item"
  47. :value="item"
  48. />
  49. </el-select>
  50. </div>
  51. </div>
  52. <div class="map-bottom-out-pano">
  53. <div class="point-info">
  54. <div v-for="type in typeOptions">
  55. <el-icon size="20" :color="pointColorMap[type]">
  56. <locationIcon />
  57. </el-icon>
  58. <p>{{ type }}</p>
  59. </div>
  60. </div>
  61. </div>
  62. </div>
  63. <div class="data-panel">
  64. <RouterView v-slot="{ Component }" v-if="loaded">
  65. <component :is="Component" />
  66. </RouterView>
  67. </div>
  68. </div>
  69. </div>
  70. </template>
  71. <script setup lang="ts">
  72. import { router } from "@/router";
  73. import { Back } from "@element-plus/icons-vue";
  74. import vectorIcon from "@/assets/vector.svg";
  75. import locationIcon from "@/assets/location.svg";
  76. import { COORD_NAME, POYS_NAME, QUERY_COORD_NAME, QUERY_POYS_NAME } from "@/router";
  77. import {
  78. mapManage,
  79. board,
  80. autoInitPos,
  81. tileOptions,
  82. defaultCenter,
  83. tileType,
  84. } from "./install";
  85. import { computed, ref, watch } from "vue";
  86. import { initSelfRelics, relics } from "@/store/relics";
  87. import { queryMode } from "./install";
  88. import { PointTypeEnum, pointColorMap, PoPoint } from "drawing-board";
  89. import { boardData, scenePoints } from "@/store/scene";
  90. import saveAs from "@/util/file-serve";
  91. const typeOptions = computed(() => {
  92. const options = [
  93. PointTypeEnum.border,
  94. PointTypeEnum.center,
  95. PointTypeEnum.marker,
  96. PointTypeEnum.other,
  97. ];
  98. if (!isCoordPage.value) {
  99. options.push(PointTypeEnum.mapSelect);
  100. }
  101. return options;
  102. });
  103. const menus = [
  104. {
  105. icon: locationIcon,
  106. name: "坐标",
  107. router: [COORD_NAME, QUERY_COORD_NAME],
  108. },
  109. {
  110. icon: vectorIcon,
  111. name: "矢量图",
  112. router: [POYS_NAME, QUERY_POYS_NAME],
  113. },
  114. ];
  115. const setMapContainer = (dom: HTMLDivElement) => setTimeout(() => mapManage.mount(dom));
  116. const setBoardContainer = (dom: HTMLDivElement) =>
  117. setTimeout(() => board.setProps({ dom }));
  118. const loaded = ref(false);
  119. const isCoordPage = computed(() => {
  120. const name = router.currentRoute.value.name;
  121. return name && [COORD_NAME, QUERY_COORD_NAME].includes(name.toString());
  122. });
  123. watch(
  124. () => router.currentRoute.value.params?.relicsId,
  125. (rid) => {
  126. if (!rid) return;
  127. loaded.value = false;
  128. const isEditmode = [COORD_NAME, POYS_NAME].includes(
  129. router.currentRoute.value.name.toString()
  130. );
  131. initSelfRelics(Number(rid), isEditmode).finally(() => {
  132. if (!relics.value) {
  133. return router.replace({ name: "relics" });
  134. }
  135. if (mapManage && !autoInitPos()) {
  136. mapManage.flyUserCenter(defaultCenter);
  137. }
  138. loaded.value = true;
  139. });
  140. },
  141. { immediate: true }
  142. );
  143. const showPoints = ref(true);
  144. watch(
  145. () =>
  146. [
  147. isCoordPage.value,
  148. boardData.value,
  149. showPoints.value,
  150. board.polygon.attrib.points.length,
  151. ] as const,
  152. ([isCoordPage, _, showPoints]) => {
  153. if (!board.polygon) return;
  154. const ids = scenePoints.value.map(({ id }) => id.toString());
  155. board.polygon.children.forEach((entity) => {
  156. if (entity instanceof PoPoint) {
  157. if (isCoordPage) {
  158. entity.visible(entity.attrib.rtk && ids.includes(entity.attrib.id));
  159. } else {
  160. entity.visible(showPoints);
  161. }
  162. } else {
  163. entity.visible(!isCoordPage);
  164. }
  165. });
  166. },
  167. { immediate: true, flush: "post" }
  168. );
  169. const captureing = ref(false);
  170. const capture = async () => {
  171. captureing.value = true;
  172. await new Promise((resolve) => setTimeout(resolve, 300));
  173. try {
  174. const dataURL = await board.toDataURL(2);
  175. await saveAs(dataURL, `${relics.value.name}-位置图.jpg`);
  176. } finally {
  177. captureing.value = false;
  178. }
  179. };
  180. </script>
  181. <style lang="scss" scoped>
  182. .map-layout {
  183. display: flex;
  184. flex-direction: row;
  185. height: 100%;
  186. }
  187. .custom_bar {
  188. width: 60px;
  189. height: 100%;
  190. background-color: white;
  191. // padding-top: 76px;
  192. .back_container {
  193. display: flex;
  194. justify-content: center;
  195. align-items: center;
  196. color: #606266;
  197. height: 76px;
  198. }
  199. .nav_container {
  200. display: flex;
  201. flex-direction: column;
  202. align-items: center;
  203. color: #606266;
  204. .nav_item {
  205. display: flex;
  206. flex-direction: column;
  207. align-items: center;
  208. justify-content: center;
  209. padding: 10px 0;
  210. cursor: pointer;
  211. user-select: none;
  212. width: 100%;
  213. span {
  214. line-height: 26px;
  215. font-size: var(--font14);
  216. }
  217. &.active {
  218. .icon {
  219. color: #409eff;
  220. }
  221. color: #409eff;
  222. background-color: #ecf5ff;
  223. position: relative;
  224. &::before {
  225. content: "";
  226. height: 100%;
  227. width: 4px;
  228. position: absolute;
  229. top: 0;
  230. left: 0;
  231. background-color: #409eff;
  232. }
  233. }
  234. }
  235. }
  236. }
  237. .map-oper-layout {
  238. display: flex;
  239. flex-direction: row;
  240. height: 100%;
  241. flex: 1;
  242. }
  243. .map-container {
  244. flex: 1;
  245. position: relative;
  246. .map-component {
  247. pointer-events: none;
  248. position: absolute;
  249. width: 100%;
  250. height: 100%;
  251. left: 0;
  252. top: 0;
  253. z-index: 9;
  254. }
  255. .board {
  256. position: absolute;
  257. left: 0;
  258. top: 0;
  259. bottom: 0;
  260. right: 0;
  261. z-index: 1;
  262. }
  263. }
  264. .data-panel {
  265. width: 320px;
  266. padding: 15px;
  267. border-left: 1px solid var(--border-color);
  268. position: relative;
  269. z-index: 3;
  270. }
  271. .map-top-out-pano {
  272. display: flex;
  273. position: absolute;
  274. right: 10px;
  275. top: 10px;
  276. z-index: 3;
  277. > * {
  278. margin-left: 10px;
  279. }
  280. }
  281. .map-bottom-out-pano {
  282. position: absolute;
  283. right: 10px;
  284. bottom: 10px;
  285. z-index: 3;
  286. }
  287. .point-info {
  288. background: #ffffff;
  289. border-radius: 4px 4px 4px 4px;
  290. padding: 10px;
  291. > div {
  292. display: flex;
  293. align-items: center;
  294. &:not(:last-child) {
  295. margin-bottom: 10px;
  296. }
  297. p {
  298. font-size: 16px;
  299. color: #606266;
  300. margin: 0;
  301. margin-left: 6px;
  302. }
  303. }
  304. }
  305. </style>