index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  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 = [65, 65]
  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 = ""
  223. //`<div class="goaf-icon-tag" title="${NumConvertLM(goafOrebelt)}-${goafOrebody}-${goafOreheight}-${goafName}">${NumConvertLM(goafOrebelt)}-${goafOrebody}-${goafOreheight}-${goafName}</div>`
  224. 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>`
  225. var icon = L.divIcon({
  226. html,
  227. iconSize
  228. })
  229. return L.marker(latlng, { icon })
  230. },
  231. onEachFeature: (_feature, layer) => {
  232. this.layerGroup.addLayer(layer)
  233. // layer.on('click', (ev) => {
  234. // self.layer=ev
  235. // self.marker_tip_style=`left:${ev.containerPoint.x+24}px;top:${ev.containerPoint.y-20}px`
  236. // console.log({
  237. // layer:ev
  238. // })
  239. // })
  240. layer.on('mouseover', (ev) => {
  241. self.layer=ev
  242. let goafId=ev.target.feature.properties?.goafId
  243. if(goafId){
  244. getMapLayerInfoById({
  245. goafId
  246. }).then((goaf_layer_res)=>{
  247. self.layerInfo=goaf_layer_res.data
  248. self.marker_tip_style=`left:${ev.containerPoint.x+60}px;top:${ev.containerPoint.y-240}px;display:block;`
  249. })
  250. }
  251. })
  252. layer.on('mouseout', (ev) => {
  253. self.layer=ev
  254. self.marker_tip_style="left:-1000px;top:0;display:none;"
  255. self.markerovertime=undefined
  256. })
  257. }
  258. }).addTo(this.layerGroup)
  259. }
  260. },
  261. clearLayers() {
  262. if (this.layerGroup && this.layerGroup.clearLayers) {
  263. this.layerGroup.clearLayers()
  264. }
  265. },
  266. createGeoJson() {
  267. var layerArray = []
  268. this.map.eachLayer(function(layer) {
  269. if (layer.pm !== 'undefined' && layer.pm != null && layer.pm !== '') {
  270. if (layer.pm._enabled === false && layer.pm.options.draggable === true) {
  271. layerArray.push(layer)
  272. }
  273. }
  274. })
  275. var geojson = L.layerGroup(layerArray).toGeoJSON()
  276. for (var n = 0; n < geojson.features.length; n++) {
  277. var nowJson = JSON.stringify(geojson.features[n])
  278. for (var m = n + 1; m < geojson.features.length; m++) {
  279. var nextJson = JSON.stringify(geojson.features[m])
  280. if (nowJson === nextJson) {
  281. geojson.features.splice(n, 1)
  282. }
  283. }
  284. }
  285. return geojson
  286. },
  287. updateLayerState(layerId=0,type=1){
  288. var marker = $(`.goaf-icon-box[data-layer='${layerId}']`);
  289. if(type===1){
  290. // 添加隐患预警
  291. marker.addClass("ico-warning" )
  292. }else if(type===3){
  293. //修改矿坑体积
  294. }else{
  295. marker.removeClass("ico-warning" )
  296. }
  297. },
  298. unique(arr = [], name = 'name') {
  299. const res = new Map()
  300. return arr.filter((item) => !res.has(item[name]) && res.set(item[name], 1))
  301. },
  302. deeepClone(params) {
  303. return JSON.parse(JSON.stringify(params))
  304. }
  305. }
  306. }
  307. </script>
  308. <style lang="scss" scoped>
  309. .map-container{
  310. nav{
  311. display: flex;
  312. justify-content: space-between;
  313. align-items: center;
  314. padding: 10px 16px;
  315. box-sizing: border-box;
  316. background-color: #193142;
  317. }
  318. .layout-map{
  319. display: flex;
  320. justify-content: space-between;
  321. height: calc(100vh - 190px);
  322. position: relative;
  323. #vmap {
  324. width: 100%;
  325. height: 100%;
  326. // flex: 1;
  327. background: #8c939d;
  328. box-sizing: border-box;
  329. z-index: 999;
  330. }
  331. #marker-tip{
  332. width: 200px;
  333. height: 200px;
  334. background-image: url(./tip.png);
  335. background-size: 100% 100%;
  336. background-repeat: no-repeat;
  337. position: absolute;
  338. z-index: 99999;
  339. padding: 15px 20px 0 20px;
  340. box-sizing: border-box;
  341. display: none;
  342. .head{
  343. font-size: 24px;
  344. color: #fff;
  345. }
  346. .item{
  347. padding-top: 15px;
  348. padding-bottom: 5px;
  349. &.item{
  350. padding-top: 8px;
  351. }
  352. .title{
  353. font-size: 16px;
  354. color: rgba(230, 247, 255, 0.6);
  355. padding-bottom: 5px;
  356. }
  357. span{
  358. font-size: 14px;
  359. padding-right: 15px;
  360. color: #fff;
  361. .warn{
  362. color: red;
  363. }
  364. }
  365. }
  366. }
  367. }
  368. }
  369. </style>
  370. <style lang="scss">
  371. .goaf-fill-v{
  372. width:100%;
  373. position: absolute;
  374. bottom: 0;
  375. left: 0;
  376. background-image: url("./goaf/stone.png");
  377. background-size: 7px;
  378. &.fill{
  379. background-image: url("./goaf/stone.png");
  380. }
  381. }
  382. .goaf-icon-tag{
  383. width: 100%;
  384. display: inline-block;
  385. background: linear-gradient(90deg, #21649c 0.21%, #060F1E 100%);
  386. overflow:hidden;
  387. text-overflow:ellipsis;
  388. white-space:nowrap;
  389. color: #fff;
  390. text-align: center;
  391. z-index: 99999;
  392. position: absolute;
  393. top: -18px;
  394. }
  395. /*动画*/
  396. .ico-warning {
  397. position: relative;
  398. &::after{
  399. display: block;
  400. content: "";
  401. width: 30px;
  402. height: 60px;
  403. // background-image: radial-gradient(yellow,red);
  404. background-color: transparent;
  405. background-image:url(@/assets/map/warn.png);
  406. background-repeat: no-repeat;
  407. background-position: center center;
  408. background-size: 24px 48px;
  409. // border-radius: 50% 50% 10% 10%;
  410. animation: twinkle 0.5s infinite alternate;
  411. position: absolute;
  412. transform: translate(-50%,-50%);
  413. left: 50%;
  414. top: 50%;
  415. // box-shadow: 0 0 20px red;
  416. z-index: 9999;
  417. animation: jump 0.5s ease-in-out infinite alternate;
  418. }
  419. }
  420. @keyframes jump {
  421. from {
  422. margin-top: 0;
  423. }
  424. to {
  425. margin-top: 8px;
  426. }
  427. }
  428. @keyframes twinkle {
  429. 0% {opacity: 0.9;}
  430. 50% {opacity: 0.5;}
  431. 100% {opacity: 0;}
  432. }
  433. </style>