index.vue 12 KB

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