transportationSafetyCenter.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. <template>
  2. <div style="position: relative">
  3. <div id="bmap"></div>
  4. <ShipSearch
  5. class="ship-search z20"
  6. :searchapi="isSafety ? 'searchSafetyShipSelect' : 'searchShipSelect'"
  7. v-model="shipStr"
  8. @selectItem="selectShip($event, 1)"
  9. style="position: absolute; top: 40px; right: 524px"
  10. ></ShipSearch>
  11. <NumberVue
  12. class="z20"
  13. v-if="this.$store.state.currentTabText != '环境中心'"
  14. :data="this.$store.state.numbers"
  15. style="position: absolute; top: 0; left: 0"
  16. ></NumberVue>
  17. <SafetyModule
  18. class="z20"
  19. v-if="this.$store.state.currentTabText == '运输安全管理中心'"
  20. style="position: absolute; top: 24px; right: 34px"
  21. ></SafetyModule>
  22. <IntelligentModule
  23. class="z20"
  24. v-if="this.$store.state.currentTabText == '智能交易中心'"
  25. style="position: absolute; top: 24px; right: 34px"
  26. ></IntelligentModule>
  27. <EnvironmentalModule
  28. class="z20"
  29. v-if="this.$store.state.currentTabText == '环境中心'"
  30. style="position: absolute; top: 24px; right: 34px"
  31. ></EnvironmentalModule>
  32. <Warning
  33. class="z20"
  34. v-if="this.$store.state.currentTabText == '环境中心'"
  35. style="position: absolute; top: 40px; left: 20px; right: 744px"
  36. ></Warning>
  37. <div class="mask"></div>
  38. </div>
  39. </template>
  40. <script>
  41. import ShipSearch from "comps/ShipSearch.vue";
  42. import router from "router/index";
  43. import store from "store/index";
  44. import api from "apis/fetch";
  45. import { ref, onMounted, watch } from "vue";
  46. import { useRoute } from "vue-router";
  47. export default {
  48. components: {
  49. ShipSearch,
  50. },
  51. setup() {
  52. const bmap = ref({});
  53. let shipStr = ref("");
  54. const route = useRoute();
  55. // 初始化地图
  56. function initMap() {
  57. bmap.value = new BMapGL.Map("bmap", {
  58. minZoom: 5,
  59. maxZoom: 14,
  60. });
  61. bmap.value.setMapStyleV2({
  62. styleId: "fa3f2f79d64ac87e2683cfa762891cb5",
  63. });
  64. bmap.value.centerAndZoom(new BMapGL.Point(120.688612, 31.975529), 11);
  65. bmap.value.enableScrollWheelZoom(true);
  66. bmap.value.addEventListener("zoomend", changeZoomByLevel);
  67. }
  68. let cacheLevel = ref(0);
  69. let prevLevel = ref(0);
  70. // 根据缩放级别调整覆盖物大小及偏移 级别组
  71. function changeZoomByGroup() {
  72. let zoomLevel = bmap.value.getZoom().toFixed(0);
  73. if (cacheLevel.value == zoomLevel) return;
  74. cacheLevel.value = zoomLevel;
  75. switch (parseInt(zoomLevel)) {
  76. case 5: {
  77. prevLevel.value = 5;
  78. clearShipsOverlay();
  79. reDrewShips(0.2, 4);
  80. break;
  81. }
  82. case 6 || 7: {
  83. let arr = [6, 7];
  84. if (arr.includes(prevLevel.value)) return;
  85. prevLevel.value = 6;
  86. clearShipsOverlay();
  87. reDrewShips(0.4, 3);
  88. break;
  89. }
  90. case 8 || 9 || 10: {
  91. let arr = [8, 9, 10];
  92. if (arr.includes(prevLevel.value)) return;
  93. prevLevel.value = 8;
  94. clearShipsOverlay();
  95. reDrewShips(0.6, 2);
  96. break;
  97. }
  98. case 11 || 12 || 13 || 14: {
  99. let arr = [11, 12, 13, 14];
  100. if (arr.includes(prevLevel.value)) return;
  101. prevLevel.value = 11;
  102. clearShipsOverlay();
  103. reDrewShips(1);
  104. break;
  105. }
  106. }
  107. }
  108. // 根据缩放级别调整覆盖物大小及偏移 单级别
  109. function changeZoomByLevel() {
  110. let zoomLevel = bmap.value.getZoom().toFixed(0);
  111. if (cacheLevel.value == zoomLevel) return;
  112. cacheLevel.value = zoomLevel;
  113. switch (parseInt(zoomLevel)) {
  114. case 5: {
  115. clearShipsOverlay();
  116. reDrewShips(0.2, 134);
  117. break;
  118. }
  119. case 6: {
  120. clearShipsOverlay();
  121. reDrewShips(0.3, 116);
  122. break;
  123. }
  124. case 7: {
  125. clearShipsOverlay();
  126. reDrewShips(0.4, 99);
  127. break;
  128. }
  129. case 6: {
  130. clearShipsOverlay();
  131. reDrewShips(0.6, 66);
  132. break;
  133. }
  134. case 8: {
  135. clearShipsOverlay();
  136. reDrewShips(0.7, 50);
  137. break;
  138. }
  139. case 9: {
  140. clearShipsOverlay();
  141. reDrewShips(0.8, 34);
  142. break;
  143. }
  144. case 10: {
  145. clearShipsOverlay();
  146. reDrewShips(0.9, 18);
  147. break;
  148. }
  149. case 11 || 12 || 13 || 14: {
  150. let arr = [11, 12, 13, 14];
  151. if (arr.includes(prevLevel.value)) return;
  152. prevLevel.value = 11;
  153. clearShipsOverlay();
  154. reDrewShips(1);
  155. break;
  156. }
  157. }
  158. }
  159. // 重绘自定义船舶覆盖物
  160. function reDrewShips(scale, offset) {
  161. for (let i of shipsDataCache.value) {
  162. let overlay = cpxOverlay(i, scale, offset);
  163. shipsOverlayCache.value.push(overlay);
  164. bmap.value.addOverlay(overlay);
  165. }
  166. }
  167. watch(
  168. () => store.state.currentTabText,
  169. (a, b) => {
  170. if (a == "数字化赋能中心") return;
  171. store.dispatch("GetNumbers");
  172. isSafety.value = a == "运输安全管理中心";
  173. clearShipsOverlay();
  174. getAllShipLocation();
  175. }
  176. );
  177. // 轨迹缓存
  178. let polylineCache = ref({});
  179. // 轨迹方向箭头覆盖物缓存
  180. let arrowCache = ref([]);
  181. // 清除轨迹 及 轨迹箭头
  182. function clearPolyline() {
  183. bmap.value.removeOverlay(polylineCache.value);
  184. for (let i of arrowCache.value) {
  185. bmap.value.removeOverlay(i);
  186. }
  187. polylineCache.value = {};
  188. arrowCache.value = [];
  189. }
  190. // 查询选择船舶或地图点选船舶
  191. async function selectShip(item, cargo) {
  192. // if (!cargo) return;
  193. clearPolyline();
  194. let res;
  195. if (store.state.currentTabText == "运输安全管理中心") {
  196. res = await store.dispatch("GetManageShipDetail", item.key);
  197. }
  198. if (store.state.currentTabText == "智能交易中心") {
  199. res = await store.dispatch("GetTradeShipDetail", item.key);
  200. }
  201. return;
  202. if (res.length == 0) return;
  203. let points = [];
  204. for (let i of res) {
  205. points.push(new BMapGL.Point(i.lng, i.lat));
  206. }
  207. drawLine(points);
  208. }
  209. // 定义复杂覆盖物
  210. function cpxOverlay(
  211. item = {},
  212. scale = 1,
  213. offset = 0,
  214. offsetX = "0",
  215. offsetY = "0"
  216. ) {
  217. let { lng, lat, shipName, mmsi, cargo, tons, shipId } = item;
  218. // 复杂的自定义覆盖物
  219. function ComplexCustomOverlay(point) {
  220. this._point = point;
  221. }
  222. ComplexCustomOverlay.prototype = new BMapGL.Overlay();
  223. ComplexCustomOverlay.prototype.addEventListener = function (event, fun) {
  224. this._div["on" + event] = fun;
  225. };
  226. ComplexCustomOverlay.prototype.initialize = function (map) {
  227. this._map = map;
  228. let div = (this._div = document.createElement("div"));
  229. div.style.position = "absolute";
  230. div.style.margin = 0;
  231. div.style.padding = 0;
  232. div.style.zIndex = BMapGL.Overlay.getZIndex(this._point.lat);
  233. div.style.height = "0px";
  234. div.style.width = "0px";
  235. div.style.whiteSpace = "nowrap";
  236. div.style.MozUserSelect = "none";
  237. div.style.fontSize = "12px";
  238. div.style.cursor = "pointer";
  239. function parseDom(arg) {
  240. let objE = document.createElement("div");
  241. objE.innerHTML = arg;
  242. return objE;
  243. }
  244. let c2 = ` <div style="display: flex;
  245. align-items:center;position:relative;top:${offsetX}px;left:${offsetY}px">
  246. <div
  247. style="
  248. height: ${cargo ? "118px" : "60px"};
  249. width: 3px;
  250. box-sizing: border-box;
  251. background: rgb(16, 255, 185);
  252. border: 1px solid rgb(16, 255, 185);
  253. margin-right: 4px;
  254. "
  255. ></div>
  256. <div
  257. style="
  258. padding: 8px 12px;
  259. height: ${cargo ? "118px" : "60px"};
  260. width: 191px;
  261. background: rgba(0, 0, 0, 0.6);
  262. text-align: left;
  263. box-sizing: border-box;
  264. "
  265. >
  266. <div
  267. style="
  268. font-size: 18px;
  269. font-family: SourceHanSansSC-Bold, SourceHanSansSC;
  270. font-weight: bold;
  271. color: #10ffb9;
  272. margin-bottom: 1px;
  273. "
  274. >
  275. ${shipName}
  276. </div>
  277. <div
  278. style="
  279. font-size: 10px;
  280. font-family: SourceHanSansSC-Medium, SourceHanSansSC;
  281. font-weight: 500;
  282. color: #fafeff;
  283. margin-bottom: 10px;
  284. "
  285. >
  286. MMSI: ${mmsi}
  287. </div>
  288. <div
  289. style="
  290. display: ${cargo ? "flex" : "none"};
  291. font-size: 20px;
  292. font-family: DINAlternate-Bold, DINAlternate;
  293. font-weight: bold;
  294. color: #ffffff;
  295. "
  296. >
  297. <div style="margin-right: 35px">
  298. <div style="margin-bottom: 3px; height: 28px">${cargo}</div>
  299. <div
  300. style="
  301. font-size: 14px;
  302. font-family: SourceHanSansSC-Normal, SourceHanSansSC;
  303. font-weight: 400;
  304. color: #92afc7;
  305. "
  306. >
  307. 货种
  308. </div>
  309. </div>
  310. <div>
  311. <div
  312. style="
  313. margin-bottom: 3px;
  314. height: 28px;
  315. line-height:22px
  316. "
  317. >
  318. ${tons}
  319. <span style="display: inline-block; font-size: 16px"
  320. >吨</span
  321. >
  322. </div>
  323. <div
  324. style="
  325. font-size: 14px;
  326. font-family: SourceHanSansSC-Normal, SourceHanSansSC;
  327. font-weight: 400;
  328. color: #92afc7;
  329. "
  330. >
  331. 吨位
  332. </div>
  333. </div>
  334. </div>
  335. </div>
  336. </div>`;
  337. let content = `
  338. <div
  339. style="
  340. display: flex;
  341. align-items:center;
  342. text-shadow: 0px 0px 5px rgba(100, 185, 255, 0.6);
  343. position:relative;
  344. transform:scale(${scale});
  345. left:${137 * (scale - 1) + offset}px
  346. "
  347. >
  348. <img
  349. style="
  350. margin: 0px;
  351. padding: 0px;
  352. height: 55px;
  353. width: 55px;
  354. margin-right: 20px;
  355. "
  356. src="https://6875-huihenduo-2gx127w7f837b584-1255802371.tcb.qcloud.la/data-platform/map-ship-icon.png"
  357. alt=""
  358. />
  359. ${isSafety.value ? c2 : ""}
  360. </div>
  361. `;
  362. let shipBox = parseDom(content);
  363. div.addEventListener("click", function () {
  364. selectShip({ key: shipId }, cargo);
  365. });
  366. if (item != -1) {
  367. div.appendChild(shipBox);
  368. }
  369. bmap.value.getPanes().labelPane.appendChild(div);
  370. return div;
  371. };
  372. ComplexCustomOverlay.prototype.draw = function () {
  373. let map = this._map;
  374. let pixel = map.pointToOverlayPixel(this._point);
  375. this._div.style.left = pixel.x - 30 + "px";
  376. this._div.style.top = pixel.y - 65 + "px";
  377. };
  378. return new ComplexCustomOverlay(new BMapGL.Point(lng, lat));
  379. }
  380. // 轨迹绘制
  381. function drawLine(points) {
  382. // 定义覆盖物 线
  383. let polyline = new BMapGL.Polyline(points, {
  384. strokeWeight: "2", //折线的宽度,以像素为单位
  385. strokeColor: "#00ffb2", //折线颜色
  386. strokeStyle: "dashed", // 折线样式:虚线
  387. });
  388. polylineCache.value = polyline;
  389. bmap.value.addOverlay(polyline); //增加折线
  390. }
  391. // 船舶数据缓存
  392. let shipsDataCache = ref([]);
  393. // 船舶覆盖物缓存
  394. let shipsOverlayCache = ref([]);
  395. // 清除地图船舶覆盖物
  396. function clearShipsOverlay() {
  397. for (let i of shipsOverlayCache.value) {
  398. bmap.value.removeOverlay(i);
  399. }
  400. shipsOverlayCache.value = [];
  401. }
  402. // 获取所有船舶
  403. async function getAllShipLocation() {
  404. let res = await api[
  405. `${isSafety.value ? "getSafetyShipLocation" : "getAllShipLocation"}`
  406. ]();
  407. let data = res.data.result;
  408. // data.length = 30;
  409. shipsDataCache.value = data;
  410. for (let i of data) {
  411. let overlay = cpxOverlay(i);
  412. shipsOverlayCache.value.push(overlay);
  413. bmap.value.addOverlay(overlay);
  414. // let marker = new BMapGL.Marker( new BMapGL.Point(i.lng, i.lat));
  415. // marker.disableMassClear();
  416. // bmap.value.addOverlay(marker);
  417. }
  418. }
  419. let isSafety = ref(true);
  420. onMounted(() => {
  421. initMap();
  422. store.dispatch("GetNumbers");
  423. bmap.value.addOverlay(cpxOverlay(-1));
  424. isSafety.value = store.state.currentTabText == "运输安全管理中心";
  425. getAllShipLocation();
  426. });
  427. return {
  428. initMap,
  429. shipStr,
  430. selectShip,
  431. polylineCache,
  432. isSafety,
  433. };
  434. },
  435. };
  436. </script>
  437. <style lang="scss" scoped>
  438. #bmap {
  439. width: 100%;
  440. height: calc(100vh - 60px);
  441. box-sizing: border-box;
  442. background: #1d2c43;
  443. border-top: 1px solid grey;
  444. }
  445. :deep(.ship-search) {
  446. .el-input__inner {
  447. background: none;
  448. color: #fff;
  449. }
  450. }
  451. .mask {
  452. position: fixed;
  453. top: 60px;
  454. left: 0;
  455. right: 0;
  456. bottom: 0;
  457. z-index: 8;
  458. pointer-events: none;
  459. background-image: radial-gradient(ellipse, #213e5f, #0f1d2e);
  460. opacity: 0.35;
  461. }
  462. </style>