index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. <template>
  2. <div class="map-container">
  3. <nav>
  4. <map-selector :default-val="formData.mapId" @setMapInfo="handleSelectMap" />
  5. </nav>
  6. <div class="layout-map">
  7. <div id="vmap" />
  8. <div id="marker-tip" :style="marker_tip_style">
  9. <div class="head">基本信息</div>
  10. <div class="item">
  11. <p class="title">巡检任务统计({{ layerInfo.goafTaskTotalNum }})</p>
  12. <p><span>未完成:{{ layerInfo.goafTaskTotalNum-layerInfo.goafTaskCompletedNum }}</span><span>已完成:{{ layerInfo.goafTaskCompletedNum }}</span></p>
  13. </div>
  14. <div class="item">
  15. <p class="title">传感器统计({{ layerInfo.sensorTotalNum }})</p>
  16. <p><span>在线:{{ layerInfo.sensorOnlineTotalNum }}</span><span>离线:{{ layerInfo.sensorOfflineTotalNum }}</span></p>
  17. <p><span class="warn">预警:{{ layerInfo.sensorAlarmNum }}</span></p>
  18. </div>
  19. </div>
  20. </div>
  21. </div>
  22. </template>
  23. <script>
  24. import * as L from 'leaflet'
  25. import '@geoman-io/leaflet-geoman-free'
  26. import MapSelector from '@/components/MapSelector'
  27. import { getMapLayerById, getMapLayer,getMapLayerInfoById } from '@/api/goaf/layer'
  28. import { NumConvertLM } from '@/utils'
  29. const iconSize=[110,110]
  30. export default {
  31. name: 'GoafVisualEditor',
  32. components: {
  33. MapSelector
  34. },
  35. data() {
  36. return {
  37. viewData: {},
  38. formData: {
  39. mapId: undefined,
  40. layerId: undefined,
  41. layerVector: ''
  42. },
  43. map: undefined,
  44. geoLayer: undefined,
  45. conditions: {
  46. layerId: undefined
  47. },
  48. layer: {
  49. containerPoint:{
  50. x:"",
  51. y:""
  52. }
  53. },
  54. layerInfo:{
  55. //任务总数
  56. goafTaskTotalNum:"",
  57. //待处理个数
  58. goafTaskWaitingNum:"",
  59. //处理完成个数
  60. goafTaskCompletedNum:"",
  61. //逾期个数
  62. goafTaskExceedNum:"",
  63. //传感器总数
  64. sensorTotalNum:"",
  65. //传感器在线总数
  66. sensorOnlineTotalNum:"",
  67. //传感器离线总数
  68. sensorOfflineTotalNum:"",
  69. //传感器预警总数
  70. sensorAlarmNum:"",
  71. },
  72. marker_tip_style:"",
  73. layers: [],
  74. layerGroup: [],
  75. mapList:[],
  76. imageOverlay: undefined
  77. }
  78. },
  79. mounted() {
  80. this.init()
  81. },
  82. methods: {
  83. init() {
  84. this.initMap()
  85. },
  86. // 初始化
  87. getData() {
  88. getMapLayerById(this.conditions.layerId).then((resp) => {
  89. const { data } = resp
  90. this.viewData = data
  91. this.setRasterLayer()
  92. })
  93. },
  94. // 初始化地图
  95. initMap() {
  96. var yx = L.latLng
  97. var xy = function xy(x, y) {
  98. if (L.Util.isArray(x)) {
  99. return yx(x[1], x[0])
  100. }
  101. return yx(y, x)
  102. }
  103. var bounds = [xy(-600, -600), xy(300, 300)]
  104. this.map = L.map('vmap', {
  105. crs: L.CRS.Simple, // 一个简单的CRS,可将经度和纬度直接映射到x其中y。可用于平面地图(例如游戏地图)
  106. minZoom: -0.3,
  107. maxZoom: 5,
  108. zoom: 0,
  109. maxBounds: bounds, // 地图拖动到边界后自动弹回
  110. maxBoundsViscosity: 0.2,
  111. attributionControl: true,
  112. zoomControl: true // zoomControl(缩放控制):确定缩放控制是否默认加载在地图上。
  113. })
  114. this.layerGroup = L.layerGroup().addTo(this.map)
  115. this.geoLayer = L.geoJson(null, { pmIgnore: false })
  116. this.geoLayer.addTo(this.map)
  117. if (!this.viewData.layerVector) return
  118. this.geoLayer.addData(this.viewData.layerVector)
  119. },
  120. // 设置地图背景图
  121. setRasterLayer() {
  122. var yx = L.latLng
  123. var xy = function xy(x, y) {
  124. if (L.Util.isArray(x)) {
  125. return yx(x[1], x[0])
  126. }
  127. return yx(y, x)
  128. }
  129. var bounds = [xy(-600, -600), xy(300, 300)]
  130. if (this.imageOverlay) {
  131. this.imageOverlay.setUrl(this.viewData.mapRasterLayer)
  132. } else {
  133. this.imageOverlay = L.imageOverlay(this.viewData.mapRasterLayer, bounds).addTo(this.map)
  134. }
  135. this.map.fitBounds(bounds)
  136. // this.map.scrollWheelZoom.disable() //滚动缩放
  137. this.getMapLayers()
  138. },
  139. // 选择地图
  140. handleSelectMap(obj,mapList) {
  141. this.viewData.mapRasterLayer = obj.mapRasterLayer
  142. this.viewData.mapId = obj.mapId
  143. this.formData.mapId = obj.mapId
  144. if(mapList){
  145. this.mapList=mapList
  146. }
  147. this.setRasterLayer()
  148. this.clearLayers()
  149. },
  150. formSuccess() {
  151. this.$emit('formSuccess')
  152. },
  153. // 获取地图图层数据
  154. getMapLayers() {
  155. getMapLayer({
  156. mapId:this.formData.mapId
  157. }).then((resp) => {
  158. const { code, data, msg } = resp
  159. if (code === 0) {
  160. this.mapLayerList = data
  161. this.initMapLayers()
  162. } else {
  163. this.$message.error(msg)
  164. }
  165. })
  166. },
  167. updateMapLayers(mapId,type=1){
  168. if(type===1){
  169. this.setRasterLayer()
  170. this.clearLayers()
  171. }else{
  172. /** 其他图层改变需要跳转 */
  173. if(this.formData.mapId===mapId){
  174. this.setRasterLayer()
  175. this.clearLayers()
  176. }else{
  177. let obj=this.mapList.filter(item=>item.mapId===mapId)[0]
  178. this.viewData.mapRasterLayer = obj.mapRasterLayer
  179. this.viewData.mapId = obj.mapId
  180. this.formData.mapId = obj.mapId
  181. this.setRasterLayer()
  182. this.clearLayers()
  183. }
  184. }
  185. },
  186. // 初始化地图图层
  187. initMapLayers() {
  188. const self = this
  189. const layerList = this.mapLayerList
  190. const feature = []
  191. if (layerList != null && layerList.length > 0) {
  192. layerList.forEach((item) => {
  193. const layerMarker = item.layerMarker && JSON.parse(item.layerMarker)
  194. const layerVector = item.layerMarker && JSON.parse(item.layerVector)
  195. if (layerMarker && layerMarker.properties) {
  196. layerMarker.properties.layerId = item.layerId
  197. feature.push(layerMarker)
  198. }
  199. if (layerVector && layerVector.properties) {
  200. layerVector.properties.layerId = item.layerId
  201. feature.push(layerVector)
  202. }
  203. })
  204. L.geoJSON(feature, {
  205. pointToLayer: function(_feature, latlng) {
  206. const goafInfo = _feature.properties.goafInfo
  207. const layerId= _feature.properties.layerId
  208. var goafAlarmStatus=""
  209. let Volume = 0
  210. if (goafInfo) {
  211. Volume = (goafInfo.goafCanfillVolume - goafInfo.goafRemainVolume) / goafInfo.goafCanfillVolume
  212. Volume = (Volume * 100).toFixed(2)
  213. }
  214. if(_feature.properties.goafAlarmStatus==1){
  215. goafAlarmStatus='ico-warning'
  216. }
  217. const goafId = _feature.properties.goafId
  218. const goafOrebelt = _feature.properties.goafOrebelt
  219. const goafOrebody = _feature.properties.goafOrebody
  220. const goafOreheight = _feature.properties.goafOreheight
  221. const goafName = _feature.properties.goafName
  222. const goaf_icon_tag = `<div class="goaf-icon-tag" title="${NumConvertLM(goafOrebelt)}-${goafOrebody}-${goafOreheight}-${goafName}">${NumConvertLM(goafOrebelt)}-${goafOrebody}-${goafOreheight}-${goafName}</div>`
  223. const html = `<div data-layer="${layerId}" gid="${goafId}" class="goaf-icon-box ${goafAlarmStatus}" style="width:100%;height:100%;position: relative;">${goaf_icon_tag}<div class="goaf-fill-v" style="height:${Volume}%;"></div></div>`
  224. var icon = L.divIcon({
  225. html,
  226. iconSize
  227. })
  228. return L.marker(latlng, { icon })
  229. },
  230. onEachFeature: (_feature, layer) => {
  231. this.layerGroup.addLayer(layer)
  232. // layer.on('click', (ev) => {
  233. // self.layer=ev
  234. // self.marker_tip_style=`left:${ev.containerPoint.x+24}px;top:${ev.containerPoint.y-20}px`
  235. // console.log({
  236. // layer:ev
  237. // })
  238. // })
  239. layer.on('mouseover', (ev) => {
  240. self.layer=ev
  241. let goafId=ev.target.feature.properties?.goafId
  242. if(goafId){
  243. getMapLayerInfoById({
  244. goafId
  245. }).then((goaf_layer_res)=>{
  246. self.layerInfo=goaf_layer_res.data
  247. self.marker_tip_style=`left:${ev.containerPoint.x+60}px;top:${ev.containerPoint.y-240}px;display:block;`
  248. })
  249. }
  250. })
  251. layer.on('mouseout', (ev) => {
  252. self.layer=ev
  253. self.marker_tip_style="left:-1000px;top:0;display:none;"
  254. self.markerovertime=undefined
  255. })
  256. }
  257. }).addTo(this.layerGroup)
  258. }
  259. },
  260. clearLayers() {
  261. if (this.layerGroup && this.layerGroup.clearLayers) {
  262. this.layerGroup.clearLayers()
  263. }
  264. },
  265. createGeoJson() {
  266. var layerArray = []
  267. this.map.eachLayer(function(layer) {
  268. if (layer.pm !== 'undefined' && layer.pm != null && layer.pm !== '') {
  269. if (layer.pm._enabled === false && layer.pm.options.draggable === true) {
  270. layerArray.push(layer)
  271. }
  272. }
  273. })
  274. var geojson = L.layerGroup(layerArray).toGeoJSON()
  275. for (var n = 0; n < geojson.features.length; n++) {
  276. var nowJson = JSON.stringify(geojson.features[n])
  277. for (var m = n + 1; m < geojson.features.length; m++) {
  278. var nextJson = JSON.stringify(geojson.features[m])
  279. if (nowJson === nextJson) {
  280. geojson.features.splice(n, 1)
  281. }
  282. }
  283. }
  284. return geojson
  285. },
  286. updateLayerState(layerId=0,type=1){
  287. var marker = $(`.goaf-icon-box[data-layer='${layerId}']`);
  288. if(type===1){
  289. // 添加隐患预警
  290. marker.addClass("ico-warning" )
  291. }else if(type===3){
  292. //修改矿坑体积
  293. }else{
  294. marker.removeClass("ico-warning" )
  295. }
  296. },
  297. unique(arr = [], name = 'name') {
  298. const res = new Map()
  299. return arr.filter((item) => !res.has(item[name]) && res.set(item[name], 1))
  300. },
  301. deeepClone(params) {
  302. return JSON.parse(JSON.stringify(params))
  303. }
  304. }
  305. }
  306. </script>
  307. <style lang="scss" scoped>
  308. .map-container{
  309. nav{
  310. display: flex;
  311. justify-content: space-between;
  312. align-items: center;
  313. padding: 10px 16px;
  314. box-sizing: border-box;
  315. background-color: #193142;
  316. }
  317. .layout-map{
  318. display: flex;
  319. justify-content: space-between;
  320. height: calc(100vh - 190px);
  321. position: relative;
  322. #vmap {
  323. height: 100%;
  324. flex: 1;
  325. background: #8c939d;
  326. box-sizing: border-box;
  327. z-index: 99999;
  328. }
  329. #marker-tip{
  330. width: 200px;
  331. height: 200px;
  332. background-image: url(./tip.png);
  333. background-size: 100% 100%;
  334. background-repeat: no-repeat;
  335. position: absolute;
  336. z-index: 99999;
  337. padding: 15px 20px 0 20px;
  338. box-sizing: border-box;
  339. display: none;
  340. .head{
  341. font-size: 24px;
  342. color: #fff;
  343. }
  344. .item{
  345. padding-top: 15px;
  346. padding-bottom: 5px;
  347. &.item{
  348. padding-top: 8px;
  349. }
  350. .title{
  351. font-size: 16px;
  352. color: rgba(230, 247, 255, 0.6);
  353. padding-bottom: 5px;
  354. }
  355. span{
  356. font-size: 14px;
  357. padding-right: 15px;
  358. color: #fff;
  359. .warn{
  360. color: red;
  361. }
  362. }
  363. }
  364. }
  365. }
  366. }
  367. </style>
  368. <style lang="scss">
  369. .goaf-fill-v{
  370. width:100%;
  371. position: absolute;
  372. bottom: 0;
  373. left: 0;
  374. background-image: url("./goaf/stone.png");
  375. background-size: 7px;
  376. &.fill{
  377. background-image: url("./goaf/stone.png");
  378. }
  379. }
  380. .goaf-icon-tag{
  381. width: 100%;
  382. display: inline-block;
  383. background: linear-gradient(90deg, #21649c 0.21%, #060F1E 100%);
  384. overflow:hidden;
  385. text-overflow:ellipsis;
  386. white-space:nowrap;
  387. color: #fff;
  388. text-align: center;
  389. z-index: 99999;
  390. position: absolute;
  391. top: -18px;
  392. }
  393. /*动画*/
  394. .ico-warning {
  395. position: relative;
  396. &::after{
  397. display: block;
  398. content: "";
  399. width: 30px;
  400. height: 60px;
  401. // background-image: radial-gradient(yellow,red);
  402. background-color: transparent;
  403. background-image:url(@/assets/map/warn.png);
  404. background-repeat: no-repeat;
  405. background-position: center center;
  406. background-size: 30px 60px;
  407. // border-radius: 50% 50% 10% 10%;
  408. animation: twinkle 0.5s infinite alternate;
  409. position: absolute;
  410. transform: translate(-50%,-50%);
  411. left: 50%;
  412. top: 50%;
  413. // box-shadow: 0 0 20px red;
  414. z-index: 9999;
  415. animation: jump 0.5s ease-in-out infinite alternate;
  416. }
  417. }
  418. @keyframes jump {
  419. from {
  420. margin-top: 0;
  421. }
  422. to {
  423. margin-top: 10px;
  424. }
  425. }
  426. @keyframes twinkle {
  427. 0% {opacity: 0.9;}
  428. 50% {opacity: 0.5;}
  429. 100% {opacity: 0;}
  430. }
  431. </style>