Newer
Older
dcms_front / src / views / baseSource / compEdit.vue
StephanieGitHub on 18 Mar 2021 20 KB MOD:优化地图查询
<!--
 * @Description: 部件编辑
 * @Author: 王晓颖
 * @Date: 2021-03-08 09:46:39
 -->
<template>
  <app-container>
    <div id="map" class="leaflet_container"/>
    <!--查询显示组件-->
    <search-list
      ref="searchList"
      :total-data="searchList"
      :loading="searchLoading"
      @search="search"
      @clear="clearSearch"
      @change="changeSearchPage"
      @item-click="clickSearchItem"/>
    <!--工具箱-->
    <tools-container>
      <!-- 绘制工具-->
      <draw-tool @click="drawBox"/>
      <!--删除工具-->
      <delete-tool :disabled="batchDeleteDisabled" @click="batchDelete"/>
      <!--添加工具-->
      <add-tool @click="openAddMode"/>
      <!--图层选择工具-->
      <layer-choose :layers="layers" :select-layers="selectLayers" @choose="layerChange"/>
    </tools-container>
    <!--图层选择组件-->
    <!--编辑部件弹窗-->
    <edit-parts-dialog ref="editPartsDialog" @add="onAddFeature" @update="onUpdateFeature" @abort="onAbort"/>
  </app-container>
</template>
<script>
import L from 'leaflet'
import 'leaflet/dist/leaflet.css'
import AppContainer from '@/components/layout/AppContainer'
import LayerChoose from './components/layerChoose'
import SearchList from './components/searchList'
import { mapGetters } from 'vuex'
import EditPartsDialog from './components/editPartsDialog'
import ToolsContainer from './components/toolsContainer'
import DrawTool from './components/drawTool'
import DeleteTool from './components/deleteTool'
import AddTool from './components/addTool'
var esri = require('esri-leaflet')
export default {
  name: 'ComponentEdit',
  components: { AddTool, DeleteTool, DrawTool, ToolsContainer, EditPartsDialog, LayerChoose, AppContainer, SearchList },
  data() {
    return {
      searchLoading: false, // 加载动画
      addMode: false, // 添加元素状态
      map: null, // 地图对象
      drawLayer: null, // 绘制图层(框选)
      layers: this.baseConfig.layers,
      selectLayers: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
      maps: [], // 地图图层
      parts: [], // 部件图层
      featureCollection: [], // 选中的图层和Feature
      featureCollectionLength: 0, // 选中的feature个数
      batchDeleteDisabled: true, // 批量删除是否不可点
      curretLayer: null, // 当前选中featureLayer
      currentItem: null, // 当前选中部件
      searchList: [], // 全部查询结果
      currentSearchMarkers: [], // 当前查询页面的markers
      // searchLength: 0, // 全部查询结果长度
      searchIconList: [] // 查询结果的Icon列表
    }
  },
  computed: {
    ...mapGetters([
      'baseUrl',
      'partsUrl',
      'partsAllUrl',
      'mapUrl',
      'partsEditUrl'
    ])
  },
  watch: {
    addMode(val) {
      if (!val) { // 关闭新增模式是要关闭点击事件
        this.map.off('click')
      }
    }
  },
  // 销毁时移除监听
  destroyed() {
    window.removeEventListener('click', this.handleKeyup, true)
    this.socket.close()
  },
  // 加载完添加点击事件的监听
  mounted() {
    this.init()
    this.initIcons()
    window.addEventListener('click', this.handleKeyup, true)
  },
  methods: {
    // 地图上点击之后,搜索列表隐藏
    handleKeyup(val) {
      if (this.$refs.searchList && val.target.id === 'map') {
        this.$refs.searchList.hideSearch()
      }
    },
    // 移除绘制图层
    drawDelete() {
      if (this.drawLayer) {
        this.drawLayer.layer.remove()
      }
    },
    // 绘制选框
    drawBox(type) {
      this.drawDelete()
      this.$nextTick(() => {
        this.map.pm.enableDraw(type, {
          snappable: false
        })
        // 绘制完成
        this.map.on('pm:create', e => {
          // 获取 选区的geojson
          const geojson = e.layer.toGeoJSON()
          // 执行查询,遍历所有部件图层
          const results = [] // 存放查询结果
          let partsCount = 0 // 部件数量
          const partsLength = this.parts.length // 全部部件图层数量
          for (let i = 0; i < partsLength; i++) {
            const part = this.parts[i]
            const query = esri.query({ url: part.options.url })
            query.where('1=1')
            query.within(geojson)
            query.run((error, featureCollection, response) => {
              if (error) {
                console.log(error)
                return
              }
              // 查询成功
              if (featureCollection.features.length > 0) {
                results.push({
                  layer: part,
                  url: part.options.url,
                  features: featureCollection.features
                })
                partsCount += featureCollection.features.length
              }
              // 最后一个图层完毕, 查询结果放入featureCollection中
              if (i === (partsLength - 1)) {
                this.featureCollection = results
                this.featureCollectionLength = partsCount
                this.$message.info(`已选中部件: ${partsCount} 个`)
                this.batchDeleteDisabled = false
              }
            })
          }
        })
      })
    },
    // 部件图层切换
    layerChange(node, checked) {
      const selectItem = this.layers.filter(item => item.name === node.name)[0]
      // 如果选中底图,选中添加,没选中移除
      if (selectItem.type === 'map') {
        if (!checked) {
          for (const item of this.maps) {
            this.map.removeLayer(item)
          }
        } else {
          for (const item of this.maps) {
            this.map.addLayer(item)
          }
        }
      } else if (node.type === 'layer') { // 其他图层
        if (!checked) {
          for (let i = 0; i < selectItem.domain.length; i++) {
            this.map.removeLayer(this.parts[selectItem.domain[i] - 1])
          }
        } else {
          for (let i = 0; i < selectItem.domain.length; i++) {
            this.map.addLayer(this.parts[selectItem.domain[i] - 1])
          }
        }
      }
    },
    // 初始化地图
    init() {
      this.loading = false
      const map = L.map('map', {
        minZoom: 13,
        // maxZoom: 21,
        center: this.baseConfig.center,
        zoom: this.baseConfig.zoom,
        zoomControl: false,
        attributionControl: false,
        crs: L.CRS.EPSG3857
      })
      map.doubleClickZoom.disable() // 禁止双击
      this.map = map // data挂载map
      window.map = map
      const that = this
      // 打开绘制工具
      this.map.on('pm:create', e => {
        that.drawDelete()
        that.drawLayer = e
      })
      // 打开弹窗添加的事件
      this.map.on('popupopen', e => {
        // 给弹窗的编辑、删除按钮添加点击事件
        document.getElementById('btnEdit').onclick = that.editInfo
        document.getElementById('btnDelete').onclick = that.delete
      })
      // 添加底图
      for (let i = 0; i <= 21; i++) {
        const item = { url: `${this.baseUrl}${this.mapUrl}/${i}` }
        this.maps.push(esri.featureLayer(item).addTo(map))
      }
      // TODO: 添加部件:可优化为读取配置文件里的部件图层信息
      // 添加部件,
      // const partsLayer = this.baseConfig.partsLayer
      for (let i = 1; i <= 40; i++) {
        let item = { url: `${this.baseUrl}${this.partsEditUrl}/${i}`, minZoom: 18 }
        console.log(item)
        if (i === 15) {
          item = {
            url: `${this.baseUrl}${this.partsEditUrl}/${i}`,
            minZoom: 18,
            style: function(feature) {
              return { color: '#ff0000', opacity: 0.75, weight: 5 }
            }
          }
        }
        var layer = esri.featureLayer(item).addTo(map)
        // 点击部件事件
        layer.on('click', function(e) {
          // 获取要素的属性
          const properties = e.layer.feature.properties
          that.currentItem = e.layer.feature
          that.searchLayerByUrl(e.layer.options.url)
          // 弹窗样式
          var str = `<div class="pop-window">
                        <div class="pop-title">
                          ${properties['小类名称']}
                        </div>
                        <div class="pop-line"><col>大类:</col>${properties['大类名称']}</div>
                        <div class="pop-line"><col>小类:</col>${properties['小类名称']}</div>
                        <div class="pop-line"><col>部件编码:</col>${properties['编码']}</div>
                        <div class="pop-line"><col>权属单位:</col>${properties['权属单位'] ? properties.权属单位 : '未知'}</div>
                        <div class="pop-line"><col>详细地址:</col>${properties['详细地址'] ? properties.详细地址 : '未知'}</div>
                        <div class="pop-btns">
                          <button id="btnEdit" class="mini-btn">编辑</button>
                          <button id="btnDelete" class="mini-btn">删除</button>
                        </div>
                      <div>`
          var popup = L.popup().setContent(str)
          e.layer.dragging._marker.bindPopup(popup, { minWidth: 200 }).openPopup()
        })
        this.parts.push(layer)
      }
      // this.map.setZoom(this.baseConfig.zoom)
    },
    // 初始化图标
    initIcons() {
      // 加载所有图标,1到5
      for (let i = 1; i < 6; i++) {
        const icon = L.icon({
          iconUrl: require(`@/assets/global_images/point${i}.png`),
          iconSize: [25, 25],
          iconAnchor: [12.5, 25]
        })
        this.searchIconList.push(icon)
      }
    },
    // 点击新增部件按钮: 添加按钮并打开弹窗
    openAddMode() {
      if (this.addMode) { // 如果已经处于添加模式,则关闭,并清空页面上的点
        this.addMode = false
        this.onAbort('create')
      } else {
        // 开始添加点
        this.addMode = true
        const icon = L.icon({
          iconUrl: require('../../assets/icons/icon-position.png'),
          iconSize: [35, 35],
          iconAnchor: [17.5, 35]
        })
        this.map.on('click', e => {
          // 转换为GeoJSON
          const feat = L.marker(e.latlng, { icon: icon })
          feat.addTo(this.map)
          this.currentItem = feat
          console.log(feat)
          this.$refs.editPartsDialog.initDialog(feat, 'create')
          // TODO: 测试新增代码
          // const feat = L.marker(e.latlng).toGeoJSON()
          // feat.properties['OBJECTID'] = 13234
          // feat.properties['POINT_X'] = 408913.539
          // feat.properties['POINT_Y'] = 3071272.381
          // feat.properties['大类编码'] = 1
          // feat.properties['大类名称'] = 'test'
          // feat.properties['小类编码'] = 1
          // feat.properties['小类名称'] = 'test'
          // console.log(feat)
          // const layerUrl = `${this.baseUrl}${this.partsEditUrl}/35/`
          // this.searchLayerByUrl(layerUrl)
          // this.currentLayer.addFeature(feat, (err, response) => {
          //   console.log(err, response)
          //   debugger
          // })
        })
      }
    },
    // 点击编辑: 打开编辑弹窗
    editInfo(ev) {
      // TODO: 临时测试,原样上传更新
      this.currentLayer.updateFeature(this.currentItem, (err, response) => {
        if (err) {
        } else {
          this.$message.success('编辑成功')
        }
      })
      // 打开弹窗
      // console.log(this.currentItem)
      // this.$refs.editPartsDialog.initDialog(this.currentItem, 'update')
    },
    // 点击单个元素的删除,ok
    delete(code) {
      // 先确认操作,然后执行删除工作
      if (this.currentLayer && this.currentItem) {
        this.$confirm('确定删除该部件吗?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          // 确定
          this.currentLayer.deleteFeature(this.currentItem.id, (err, response) => {
            if (err) {
              this.$message.error('删除失败')
            } else {
              this.$message.success('删除成功')
              this.currentItem = null
            }
          })
        })
      }
    },
    // 框选区域批量删除,ok
    batchDelete() {
      if (this.featureCollectionLength > 0) {
        this.$confirm(`确定要删除所框选的${this.featureCollectionLength}个部件吗?`, '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          // 确定则遍历图层,删除features
          for (const layer of this.featureCollection) {
            const ids = layer.features.map(item => item.id)
            if (layer.layer && layer.features) {
              layer.layer.deleteFeatures(ids, (err, response) => {
                if (err) {
                  this.$message.error('批量删除部件失败')
                } else {
                  this.$message.success('批量删除部件成功')
                  this.drawDelete()
                  this.featureCollection = []
                  this.featureCollectionLength = 0
                }
              })
            }
          }
        }).catch(() => {
          // 放弃则清空选框和查询结果
          this.drawDelete()
          this.featureCollection = []
          this.featureCollectionLength = 0
        })
      }
    },
    // 添加到feature图层
    onAddFeature(feature) {
      const feat = L.marker(feature.latlng).toGeoJSON()
      // 读取属性
      feat.properties['大类编码'] = feature.classCode
      feat.properties['大类'] = feature.class
      feat.properties['小类编码'] = feature.subClassCode
      feat.properties['小类'] = feature.subClass
      feat.properties['权属单位'] = feature.dept
      feat.properties['地址'] = feature.address
      console.log(feat)
      // 图层地址拼接
      const layerUrl = `${this.baseUrl}${this.partsEditUrl}/${feature.layer}/`
      // 遍历找到当前图层
      this.searchLayerByUrl(layerUrl)
      // 添加部件
      this.currentLayer.addFeature(feat, (err, response) => {
        console.log(err, response)
        debugger
        if (err) {
          console.log('添加失败')
        } else {
          // 关闭添加模式, 并关闭弹窗
          this.addMode = true
          this.$refs.editPartsDialog.closeDialog()
        }
      })
    },
    // 新增部件的,放弃操作,dialogStatus弹窗类型
    onAbort(dialogStatus) {
      if (dialogStatus === 'create') {
        // 清除地图上的锚点
        if (this.currentItem) {
          this.map.removeLayer(this.currentItem)
        }
      }
    },
    // 编辑保存操作
    onUpdateFeature(feature) {
      if (this.currentLayer) {
        console.log('=========')
        console.log(feature)
        console.log('=========')
        this.currentLayer.updateFeature(feature, (err, response) => {
          if (err) {
            this.$message.error('部件信息更新失败')
          } else {
            this.$message.success('部件信息更新成功')
          }
        })
      }
    },
    // 地图查询功能
    search(keyword) {
      if (keyword === '') { // 如果是空字符串,表示未输入任何查询条件,根据内容提示用户
        this.$message.warning('请输入查询关键字')
        return
      }
      this.searchLoading = true
      var that = this
      const results = [] // 存放查询结果列表
      // 遍历所有图层进行查询
      const partsLength = this.parts.length // 全部部件图层数量
      for (let i = 0; i < partsLength; i++) {
        const part = this.parts[i]
        var str = ['编码', '小类名称', '大类名称']
        var query = esri.query({ url: part.options.url })
        let queryString = '1=1' // 默认查询语句
        if (keyword) {
          // 拼接查询语句
          queryString = str.map(item => `(${item} like '%${keyword}%')`).join(' OR ')
          console.log(queryString)
        } else {
          this.$message.warning('请输入查询关键字')
          return
        }
        query.where(queryString)
        // query.within(this.bounds)
        // 执行查询
        query.run((error, featureCollection, response) => {
          if (error) {
            console.log(error)
            return
          }
          const featuresLength = featureCollection.features.length
          if (featuresLength > 0) {
            // 遍历所有查询结果,放入results
            for (const feature of featureCollection.features) {
              results.push({
                layer: part,
                feature: feature
              })
            }
          }
          // 如果是最后一个图层
          if (i === (partsLength - 1)) {
            that.searchList = results
            this.searchLoading = false
            // that.drawIcon()
          }
        })
      }
    },
    // 清空查询
    clearSearch() {
      this.searchList = []
      if (this.currentSearchMarkers.length > 0) {
        for (const marker of this.currentSearchMarkers) {
          this.map.removeLayer(marker)
        }
        this.currentSearchMarkers = []
      }
    },
    // 查询结果页面变化,重绘icon
    changeSearchPage(data) {
      console.log('changeSearchPage')
      // 清除原来绘制的点
      if (this.currentSearchMarkers.length > 0) {
        for (const marker of this.currentSearchMarkers) {
          this.map.removeLayer(marker)
        }
      }
      // 赋予新的查询结果
      const currentMarkesLength = data.length
      for (let i = 0; i < currentMarkesLength; i++) {
        const feature = data[i].feature
        var icon = this.searchIconList[i]
        const latlng = L.latLng([feature.geometry.coordinates[1], feature.geometry.coordinates[0]])
        var item = L.marker(latlng, { icon: icon, layer: data[i].layer, properties: feature.properties }).addTo(this.map)
        this.currentSearchMarkers.push(item)
      }
      // 中心置为第一个点的中心
      if (this.currentSearchMarkers[0]) {
        this.clickSearchItem(data[0])
      }
    },
    // 点击查询列表项,将位置放在中间
    clickSearchItem(item) {
      console.log('clickItem')
      if (item) {
        const feature = item.feature
        const latlng = L.latLng([feature.geometry.coordinates[1], feature.geometry.coordinates[0]])
        let zoom = this.map.getZoom()
        if (zoom < 19) {
          zoom = 19
        }
        this.map.flyTo(latlng, zoom)
      }
    },
    // 绘制查询结果
    drawIcon() {
      var that = this
      for (var i = 0; i < that.markerlist.length; i++) {
        that.map.removeLayer(that.markerlist[i])
      }

      that.markerlist = []
      for (let num = 0; num < that.showlist.length; num++) {

      }
      this.cansearch = true
    },
    // 根据url找到layer
    searchLayerByUrl(url) {
      const layer = this.parts.filter(item => item.options.url === url)[0]
      if (layer) {
        this.currentLayer = layer
      }
    }
  }
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
  $tableTitleHeight:35px;
  .leaflet_container{
    width: 100%;
    height: 85vh;
    min-height: 596px;
    /*max-height: 90vh;*/
    background-color: white;
  }
</style>
<style rel="stylesheet/scss" lang="scss">
  .pop-window{
    font-size: 14px;width:200px;
    padding-right:5px;
    .pop-title{
      font-size: 16px;
      margin-bottom: 8px;
    }
    .pop-line{
      line-height:20px;
    }
    .pop-btns{
      margin:10px 0px;
      .mini-btn{
        display: inline-block;
        line-height: 1;
        white-space: nowrap;
        cursor: pointer;
        background: #fff;
        border: 1px solid #dcdfe6;
        color: #606266;
        -webkit-appearance: none;
        text-align: center;
        box-sizing: border-box;
        outline: none;
        margin: 0;
        transition: .1s;
        font-weight: 500;
        padding: 7px 15px;
        font-size: 12px;
        border-radius: 3px;
      }
      .mini-btn+.mini-btn{
        margin-left:10px;
      }
      #btnEdit{
        color: #409eff;
        background: #ecf5ff;
        border-color: #b3d8ff;
      }
      #btnEdit:hover{
        background: #409eff;
        border-color: #409eff;
        color: #fff;
      }
      #btnDelete{
        color: #f56c6c;
        background: #fef0f0;
        border-color: #fbc4c4;
      }
      #btnDelete:hover{
        background: #f56c6c;
        border-color: #f56c6c;
        color: #fff;
      }
    }
  }
</style>