mapPage.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. <!--
  2. 描述:拖放地图组件,默认尺寸是 500 * 300
  3. 接收属性参数:
  4. lat: 纬度
  5. lng: 经度
  6. 自定义事件:
  7. drag: 拖放完成事件
  8. 示例:
  9. <mapDrag @drag="dragMap" lat="22.574405" lng="114.095388"></mapDrag>
  10. -->
  11. <template>
  12. <div class="m-map" :style="{ height: curheight + 'px' }">
  13. <div id="js-containerPage" class="map">
  14. <h5 style="text-align: center">{{$t("Loading data")}} ...</h5>
  15. </div>
  16. <mu-bottom-sheet :open.sync="detailState">
  17. <div class="detailContianer">
  18. <h5>
  19. <span class="name">{{row.RegionName}}</span>
  20. </h5>
  21. <ul>
  22. <li class="lt">{{$t("state")}}:<em
  23. :class="{'red':row.Detstatus != '正常','green':row.Detstatus == '正常'}">{{row.Detstatus}}</em>
  24. </li>
  25. <li class="lt">{{$t("Detection equipment")}}:{{row.DectorNum}} {{$t("pcs")}}</li>
  26. <li class="lt">{{$t("phone signal")}}:{{row.DecList}}</li>
  27. <li class="lt">{{$t("Camera")}}:{{row.CamNum}} {{$t("pcs")}}</li>
  28. <li class="lt">{{$t("Suspicious camera")}}:{{row.DancapNum}} {{$t("pcs")}}</li>
  29. </ul>
  30. <div class="btnContainer">
  31. <button class="goPlane" @click="goPlane">{{$t("Plane details")}}</button>
  32. <button class="goProfile" @click="goProfile">{{$t("Company details")}}</button>
  33. </div>
  34. </div>
  35. </mu-bottom-sheet>
  36. </div>
  37. </template>
  38. <script>
  39. import remoteLoad from '../utils/remoteLoad.js'
  40. import {MapKey, MapCityName} from '../tools/map'
  41. let qs = require('qs');
  42. import axios from 'axios';
  43. import Global from '../../src/Global.js'
  44. export default {
  45. data() {
  46. return {
  47. searchKey: '',
  48. placeSearch: null,
  49. dragStatus: false,
  50. AMapUI: null,
  51. AMap: null,
  52. ComId: 0,
  53. detailState: false,
  54. row: {
  55. RegionName: '',
  56. Detstatus: '',
  57. DectorNum: '',
  58. CamNum: '',
  59. DecList: '',
  60. DancapNum: '',
  61. Regionid: '',
  62. },
  63. }
  64. },
  65. props: {
  66. curheight: {
  67. default: 310,
  68. type: Number
  69. }
  70. },
  71. watch: {
  72. searchKey() {
  73. if (this.searchKey === '') {
  74. this.placeSearch.clear()
  75. }
  76. },
  77. },
  78. beforeDestroy() {
  79. clearInterval(this.getPoint);
  80. },
  81. methods: {
  82. goPlane() {
  83. let that = this;
  84. that.$router.push({path: '/plane', query: {Regionid: that.row.Regionid, ComId: that.ComId}});
  85. },
  86. goProfile() {
  87. let that = this;
  88. that.$router.push({path: '/profile', query: {Regionid: that.row.Regionid, ComId: that.ComId}});
  89. },
  90. // 实例化地图
  91. initMap() {
  92. let AMapUI = this.AMapUI = window.AMapUI;
  93. let AMap = this.AMap = window.AMap;
  94. let timeInterval = 5000;
  95. let lang = localStorage.language == 'zh' ? 'zh' : 'en';
  96. AMapUI.loadUI(['misc/PositionPicker'], PositionPicker => {
  97. let mapConfig = {
  98. resizeEnable: true, //是否监控地图容器尺寸变化
  99. // mapStyle: mapStyle,//地图颜色风格
  100. zoom: 16,
  101. cityName: MapCityName,
  102. lang: lang
  103. };
  104. let map = new AMap.Map('js-containerPage', mapConfig);
  105. // 获取点位置
  106. this.getPoint(map);
  107. setInterval(this.getPoint(map), 5000);
  108. // 获取围栏信息
  109. this.getFenceInfo(map);
  110. // 启用工具条
  111. AMap.plugin(['AMap.ToolBar'], function () {
  112. map.addControl(new AMap.ToolBar({
  113. position: 'RB'
  114. }))
  115. });
  116. })
  117. },
  118. // 实例化点标记
  119. addMarker(map, lng, lat, content, imgState, row) {
  120. let that = this;
  121. let marker = new AMap.Marker({
  122. position: [lng, lat],
  123. offset: new AMap.Pixel(-13, -30),
  124. content: imgState,
  125. });
  126. marker.setMap(map);
  127. // 弹窗偏移度
  128. let infoWindow = new AMap.InfoWindow({offset: new AMap.Pixel(14, -20)});
  129. marker.on('click', function (e) {
  130. // 自定义的弹窗
  131. that.detailState = true;
  132. that.row.RegionName = row.RegionName;
  133. that.row.Detstatus = row.Detstatus == 1 ? that.$t('normal') : that.$t('abnormal');
  134. that.row.DecList = !row.DecList ? that.$t('Not found') : row.DecList;
  135. that.row.DectorNum = row.DectorNum;
  136. that.row.CamNum = row.CamNum;
  137. that.row.DancapNum = row.DancapNum;
  138. that.row.Regionid = row.Regionid;
  139. // 原生的弹窗
  140. // infoWindow.setContent(content);
  141. // infoWindow.open(map, e.target.getPosition());
  142. });
  143. },
  144. // 获取所有的点位置
  145. getPoint(map) {
  146. const that = this;
  147. let lnglats = [];
  148. let url = '';
  149. if (localStorage.userLevel == '单位管理员') {
  150. url = headapi + 'v1/Company/GetRegionMapinfo';//获取 单个企业获取
  151. } else {
  152. url = headapi + 'v1/Company/GetMapinfo';//获取
  153. }
  154. let param = {
  155. token: localStorage.token
  156. };
  157. let postdata = qs.stringify(param);
  158. axios.post(url, postdata).then(function (data) {
  159. let json = data.data;
  160. if (json.Rs) {
  161. map.setCenter([json.Rs[0].Lng, json.Rs[0].Lat]);
  162. // todo 使用数学中心计算显示点中心
  163. if (localStorage.lnglats != JSON.stringify(lnglats)) {
  164. that.wirtePoint(map, json);
  165. }
  166. localStorage.lnglats = JSON.stringify(json.Rs)
  167. }
  168. map.setFitView();
  169. }, function (response) {
  170. console.info(response);
  171. })
  172. },
  173. // 画点
  174. wirtePoint(map, json) {
  175. let that = this;
  176. let markers = [];
  177. let curPointName = '';
  178. let lnglats = json.Rs;
  179. map.remove(markers);
  180. // 根据状态判断图标显示颜色
  181. for (var i = 0; i < lnglats.length; i++) {
  182. let imgState = '';
  183. curPointName = !lnglats[i].RegionName ? lnglats[i].ComName : lnglats[i].RegionName;
  184. switch (parseInt(lnglats[i].Detstatus)) {
  185. // 0 safe 1 warning 2 danger
  186. case 0:
  187. imgState = '<i class="green_point"></i> <span class="point_name">' + curPointName + '</span>';
  188. break;
  189. case 1:
  190. imgState = '<i class="green_point"></i> <span class="point_name">' + curPointName + '</span>';
  191. break;
  192. case 2:
  193. imgState = '<i class="red_point"></i> <span class="point_name">' + curPointName + '</span>';
  194. break;
  195. }
  196. var position;
  197. position = [json.Rs[i].Lng, json.Rs[i].Lat];
  198. let content = '';
  199. if (localStorage.userLevel == '单位管理员') {
  200. let state = json.Rs[i].Detstatus == 1 ? this.$t("normal") : '<span style="color:#ff0000">this.$t("abnormal")</span>';
  201. let DecList = !json.Rs[i].DecList ? this.$t("Not found") : json.Rs[i].DecList;
  202. content =
  203. '<span class="map_a" href="profile.html?hotelid=' + localStorage.comId + '&Regionid=' + json.Rs[i].Regionid + '">' +
  204. +this.$t("region") + ':' + json.Rs[i].RegionName
  205. + '<br>' + this.$t("state") + ':' + state
  206. + '<br>' + this.$t("Detection equipment") + ':' + json.Rs[i].DectorNum
  207. + '<br>' + this.$t("phone signal") + ':' + DecList
  208. + '<br>' + json.Rs[i].Wifiname + ':' + json.Rs[i].CamNum
  209. + '<br>可疑' + json.Rs[i].Wifiname + ':' + json.Rs[i].DancapNum
  210. + '</span><em class="jumper" Regionid=' + json.Rs[i].Regionid + '>' +
  211. '<i class="icon iconProfile" href="profile.html?hotelid=' + localStorage.comId + '&Regionid=' + json.Rs[i].Regionid + '"></i>' +
  212. '<i class="icon iconPlane" href="plane.html?hotelid=' + localStorage.comId + '&Regionid=' + json.Rs[i].Regionid + '"></i></em>';
  213. } else {
  214. let state = json.Rs[i][3] == 1 ? '正常' : '<span style="color:#ff0000">异常</span>';
  215. let macAddr = !json.Rs[i][7] ? '无' : json.Rs[i][7];
  216. content =
  217. '<span class="map_a" href="profile.html?hotelid=' + json.Rs[i][8] + '">' +
  218. '企业:' + json.Rs[i][5]
  219. + '<br>' + this.$t("state") + ':' + state
  220. + '<br>' + this.$t("Detection equipment") + ':' + json.Rs[i][4]
  221. + '<br>' + this.$t("phone signal") + ':' + json.Rs[i][2]
  222. + '<br>可疑' + json.Rs[i][9] + ':' + json.Rs[i][6]
  223. + '<br>可疑MAC地址:' + macAddr
  224. + '</span><em class="jumper" Regionid=' + json.Rs[i][9] + '>' +
  225. '<i class="icon iconProfile" href="profile.html?hotelid=' + json.Rs[i][8] + '"></i>' +
  226. '<i class="icon iconPlane" href="plane.html?hotelid=' + json.Rs[i][8] + '"></i></em>';
  227. }
  228. that.addMarker(map, json.Rs[i].Lng, json.Rs[i].Lat, content, imgState, json.Rs[i]);
  229. }
  230. },
  231. // 给总管理员的画点
  232. wirtePointForAdmin(map, json) {
  233. let that = this;
  234. let markers = [];
  235. map.remove(markers);
  236. // 根据状态判断图标显示颜色
  237. for (var i = 0; i < json.length; i++) {
  238. let imgState = '';
  239. switch (parseInt(json[i][3])) {
  240. case 1:
  241. imgState = '<i class="green_point"></i> <span class="point_name">' + json[i][5] + '</span>';
  242. break;
  243. case 2:
  244. imgState = '<i class="yellow_point"></i> <span class="point_name">' + json[i][5] + '</span>';
  245. break;
  246. case 0:
  247. imgState = '<i class="red_point"></i> <span class="point_name">' + json[i][5] + '</span>';
  248. break
  249. }
  250. var position = json[i];
  251. let content = '';
  252. let state = json[i][3] == 1 ? this.$t('normal') : '<span style="color:#ff0000">' + this.$t('abnormal') + '</span>';
  253. let macAddr = !json[i][7] ? this.$t('nothing') : json[i][7];
  254. content = '<span class="map_a" href="profile.html?hotelid=' + json[i][8] + '">' +
  255. '企业:' + json[i][5]
  256. + '<br>状态:' + state
  257. + '<br>探测设备:' + json[i][4]
  258. + '<br>' + json[i][9] + ':' + json[i][2]
  259. + '<br>可疑' + json[i][9] + ':' + json[i][6]
  260. + '<br>可疑MAC地址:' + macAddr
  261. + '</span><em class="jumper" Regionid=' + json[i][9] + '>' +
  262. '<i class="icon iconProfile" href="profile.html?hotelid=' + json[i][8] + '"></i>' +
  263. '<i class="icon iconPlane" href="plane.html?hotelid=' + json[i][8] + '"></i></em>';
  264. that.addMarker(map, json[i][0], json[i][1], content, imgState,json[i]);
  265. }
  266. },
  267. // 获取围栏信息
  268. getFenceInfo(map) {
  269. const that = this;
  270. let url = headapi + 'v1/Company/GetComfencelist';
  271. let param = {
  272. token: localStorage.token,
  273. comid: localStorage.comId
  274. };
  275. let postdata = qs.stringify(param);
  276. axios.post(url, postdata).then(function (data) {
  277. let json = data.data;
  278. if (json.Code == 0) {
  279. that.ComId = json.Rs.ComId;
  280. let fence = json.Rs.Fence;
  281. let fenceArr = fence.split(",");
  282. let path = [];
  283. for (var i = 0; i < fenceArr.length; i++) {
  284. if (i % 2 == 0) {
  285. path.push([fenceArr[i], fenceArr[i + 1]]);
  286. }
  287. }
  288. let polygon = new AMap.Polygon({
  289. path: path,
  290. strokeColor: "#6496FF",
  291. strokeWeight: 6,
  292. strokeOpacity: 0.15,
  293. fillOpacity: 0.4,
  294. fillColor: '#6496FF',
  295. zIndex: 50,
  296. });
  297. map.add(polygon);
  298. // 缩放地图到合适的视野级别
  299. map.setFitView([polygon]);
  300. }
  301. }, function (response) {
  302. console.info(response);
  303. })
  304. },
  305. // 计算点中心
  306. calcCenterBySingle(data) {
  307. var total = data.length;
  308. var lat = 0, lon = 0;
  309. for (var i = 0; i < total; i++) {
  310. lat += data[i].Lat * Math.PI / 180;
  311. lon += data[i].Lng * Math.PI / 180;
  312. }
  313. lat /= total;
  314. lon /= total;
  315. return [lon * 180 / Math.PI, lat * 180 / Math.PI];
  316. },
  317. // 计算多点的数学圆心
  318. calcCenter(data) {
  319. var total = data.length;
  320. var lat = 0, lon = 0;
  321. for (var i = 0; i < total; i++) {
  322. lat += data[i][0] * Math.PI / 180;
  323. lon += data[i][1] * Math.PI / 180;
  324. }
  325. lat /= total;
  326. lon /= total;
  327. return [lat * 180 / Math.PI, lon * 180 / Math.PI];
  328. }
  329. },
  330. async created() {
  331. // 已载入高德地图API,则直接初始化地图
  332. if (window.AMap && window.AMapUI) {
  333. this.initMap()
  334. // 未载入高德地图API,则先载入API再初始化
  335. } else {
  336. await remoteLoad(`http://webapi.amap.com/maps?v=1.3&key=${MapKey}`);
  337. await remoteLoad('http://webapi.amap.com/ui/1.0/main.js');
  338. this.initMap()
  339. }
  340. }
  341. }
  342. </script>
  343. <style scoped>
  344. .m-map {
  345. width: 100%;
  346. /*max-height: 317px;*/
  347. /*height: 317px;*/
  348. position: relative;
  349. }
  350. .m-map .map {
  351. width: 100%;
  352. height: 100%;
  353. }
  354. .m-map .search {
  355. position: absolute;
  356. top: 10px;
  357. left: 10px;
  358. width: 285px;
  359. z-index: 1;
  360. }
  361. .m-map .search input {
  362. width: 180px;
  363. border: 1px solid #ccc;
  364. line-height: 20px;
  365. padding: 5px;
  366. outline: none;
  367. }
  368. .m-map .search button {
  369. line-height: 26px;
  370. background: #fff;
  371. border: 1px solid #ccc;
  372. width: 50px;
  373. text-align: center;
  374. }
  375. .m-map .result {
  376. max-height: 300px;
  377. overflow: auto;
  378. margin-top: 10px;
  379. }
  380. /deep/ .green_point {
  381. margin: 16px;
  382. height: 16px;
  383. width: 16px;
  384. border-radius: 50% !important;
  385. display: inline-block;
  386. background-color: #1eff30;
  387. }
  388. /deep/ .yellow_point {
  389. margin: 16px;
  390. height: 16px;
  391. width: 16px;
  392. border-radius: 50% !important;
  393. display: inline-block;
  394. background-color: #f2ff24;
  395. }
  396. /deep/ .red_point {
  397. margin: 16px;
  398. height: 16px;
  399. width: 16px;
  400. border-radius: 50% !important;
  401. display: inline-block;
  402. transform: scale(0.5);
  403. animation: bulge 2s infinite ease-in-out;
  404. background-color: #ff0000;
  405. animation-delay: 0s;
  406. }
  407. /deep/ .red_point::after {
  408. position: absolute;
  409. display: inline-block;
  410. content: '';
  411. height: 100%;
  412. width: 100%;
  413. border-radius: 50%;
  414. background-color: inherit;
  415. top: 0;
  416. left: 0;
  417. z-index: -1;
  418. transform: scale(1);
  419. animation: blow 2s infinite ease-in-out;
  420. }
  421. /deep/ .point_name {
  422. position: relative;
  423. left: -30px;
  424. top: -15px;
  425. width: 100%;
  426. min-width: 90px;
  427. padding: 1px;
  428. text-align: center;
  429. background-color: rgb(26, 34, 41);
  430. border: 1px solid #060D16;
  431. border-radius: 10px !important;
  432. color: #eeeeee;
  433. display: block;
  434. overflow: hidden;
  435. opacity: 0.9;
  436. font-size: 12px;
  437. }
  438. .red {
  439. color: red;
  440. }
  441. .green {
  442. color: greenyellow;
  443. }
  444. @keyframes bulge {
  445. 50% {
  446. transform: scale(1);
  447. }
  448. }
  449. @keyframes blow {
  450. 25% {
  451. opacity: 0.4;
  452. }
  453. 50% {
  454. opacity: 0.1;
  455. }
  456. 90% {
  457. opacity: 0;
  458. }
  459. 100% {
  460. transform: scale(9);
  461. opacity: 0;
  462. }
  463. }
  464. .detailContianer {
  465. position: absolute;
  466. bottom: 85px;
  467. left: 0;
  468. right: 0;
  469. width: 96%;
  470. height: 257px;
  471. overflow: hidden;
  472. display: block;
  473. margin: 0 auto;
  474. background: #fff;
  475. box-shadow: #333333 0 0 7px;
  476. z-index: 9999;
  477. border-radius: 8px;
  478. padding: 18px 10px;
  479. }
  480. .detailContianer h5 {
  481. width: 100%;
  482. overflow: hidden;
  483. display: block;
  484. margin: 0 auto;
  485. font-size: 16px;
  486. }
  487. .detailContianer h5 .name {
  488. width: 100%;
  489. overflow: hidden;
  490. display: block;
  491. margin: 0 auto;
  492. text-align: center;
  493. }
  494. .detailContianer h5 .state {
  495. float: right;
  496. }
  497. .detailContianer ul {
  498. width: 88%;
  499. overflow: hidden;
  500. display: block;
  501. margin: 0 auto;
  502. margin-top: 15px;
  503. margin-bottom: 10px;
  504. border-top: 1px solid #EBEBEB;
  505. border-bottom: 1px solid #EBEBEB;
  506. padding-top: 10px;
  507. padding-bottom: 10px;
  508. }
  509. .detailContianer li {
  510. width: 100%;
  511. float: left;
  512. margin-bottom: 3px;
  513. }
  514. .detailContianer .rt {
  515. text-align: right;
  516. }
  517. .detailContianer .btnContainer {
  518. width: 88%;
  519. overflow: hidden;
  520. display: block;
  521. margin: 0 auto;
  522. }
  523. .btnContainer button {
  524. width: 120px;
  525. height: 32px;
  526. line-height: 32px;
  527. text-align: center;
  528. border-radius: 250px;
  529. outline: none;
  530. border: none;
  531. }
  532. .btnContainer .goPlane {
  533. border: 1px solid #FFCC00;
  534. background: #fff;
  535. }
  536. .btnContainer .goProfile {
  537. border: 1px solid #FFCC00;
  538. background: #FFCC00;
  539. float: right;
  540. }
  541. /deep/ .amap-zoomcontrol {
  542. bottom: 10px !important;
  543. }
  544. </style>