<template> <div> <div id="map" class="leaflet_container" style="background-color: white"> <!--查询显示组件--> <search-list ref="searchList" :total-data="searchList" :loading="searchLoading" @clear="clearSearch" @search="search" @change="changeSearchPage" @item-click="clickSearchItem"/> <!--工具箱--> <tools-container> <!-- 绘制工具--> <draw-tool @click="drawBox"/> <!--重置工具--> <clear-tool :disabled="clearDisabled" @click="drawDelete"/> <!--图层选择工具--> <layer-choose ref="layerRadio" :layers="layers" :select-layers="selectLayers" @check="layerChange"/> </tools-container> <!--图层选择组件--> </div> </div> </template> <script> import L from 'leaflet' import ClearTool from './components/clearTool' import AppContainer from '@/components/layout/AppContainer' import LayerChoose from './components/layerChoose' import SearchList from './components/searchList' import { mapGetters } from 'vuex' import ToolsContainer from './components/toolsContainer' import DrawTool from './components/drawTool' import AddTool from './components/addTool' var esri = require('esri-leaflet') export default { name: 'MapSearch', components: { AddTool, ClearTool, DrawTool, ToolsContainer, LayerChoose, AppContainer, SearchList }, data() { return { keyword: '', // 查询关键字 searchLoading: false, // 查询状态 map: null, // 地图对象 drawLayer: null, // 绘制图层(框选) bounds: null, // 查询区域 clearDisabled: true, // 禁用重置按钮 baseLayer: [], layers: [ { 'id': 0, 'name': '商户', 'type': 'layer', 'domain': [35] }, { 'id': 1, 'name': '部件', 'type': 'layer', 'domain': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45] } ], selectLayers: [0], // 选中图层 maps: [], // 地图图层 parts: [], // 部件图层 layerGroups: [], // 图层组,用于控制一组内容显示或者隐藏 searchList: [], // 全部查询结果 featureCollection: [], // 选中的图层和Feature featureCollectionLength: 0, // 选中的feature个数 currentSearchMarkers: [], // 当前查询页面的markers searchIconList: [] // 查询结果的Icon列表 } }, computed: { ...mapGetters([ 'baseUrl', 'partsUrl', 'shopUrl', 'mapUrl' ]) }, destroyed() { window.removeEventListener('click', this.handleKeyup, true) }, 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() } }, // 地图查询功能,关键字keyword,是否需要提示不能为空tips search(keyword, tips = true) { if (keyword) { // 如果传入了非空keyword表示点击搜索按钮,则将keyword置为keyword this.keyword = keyword } else if (keyword === '') { // 如果是空字符串,表示未输入任何查询条件,根据内容提示用户 if (tips) this.$message.warning('请输入查询关键字') return } // 其他不予处理 this.searchLoading = true const results = [] // 存放查询结果列表 // 获取是查询部件还是查询商户 if (this.selectLayers.toString() === '0') { // 查询商户 const str = ['objid', 'dutyname', 'position_'] // 查询条件属性 const query = esri.query({ url: `${this.baseUrl}${this.shopUrl}` }) let queryString = '1=1' // 默认查询语句 // 如果有关键字 if (this.keyword) { // 拼接查询语句 queryString = str.map(item => `(${item} like '%${this.keyword}%')`).join(' OR ') } else { return } query.where(queryString) // 如果有查询边界 if (this.bounds) { 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) { const popupStr = '<div class="popup-div">' + '<div class="popup-title">商户信息</div>' + '<div class="dashed-line"></div>' + '<div class="popup-item"><label>商户名称</label>' + feature.properties.dutyname + '</div>' + '<div class="popup-item"><label>商户编号</label>' + feature.properties.objid + '</div>' + // '<div class="popup-item"><label>所在社区</label>' + feature.properties.communame + '</div>' + // '<div class="popup-item"><label>所在网格编号</label>' + feature.properties.bgid + '</div>' + '<div class="popup-item"><label>三包责任单位</label>' + feature.properties.deptname + '</div>' + '<div class="popup-item"><label>详细地址</label>' + feature.properties.position_ + '</div>' + '</div>' results.push({ name: feature.properties.dutyname, code: feature.properties.objid, content: { title: '地址', text: feature.properties.position_ }, lng: feature.properties.x, lat: feature.properties.y, popupStr: popupStr }) } } this.searchList = results this.searchLoading = false }) } else if (this.selectLayers.toString() === '1') { // 查询部件 // 遍历所有图层进行查询 const partsLength = this.parts.length // 全部部件图层数量 for (let i = 0; i < partsLength; i++) { const part = this.parts[i] const str = ['objid', 'xl', 'dl'] const query = esri.query({ url: part.url }) let queryString = '1=1' // 默认查询语句 // 如果有关键字 if (this.keyword) { // 拼接查询语句 queryString = str.map(item => `(${item} like '%${this.keyword}%')`).join(' OR ') } else { return } query.where(queryString) // 如果有查询边界 if (this.bounds) { 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) { const popupStr = '<div class="popup-div">' + '<div class="popup-title">部件信息</div>' + '<div class="dashed-line"></div>' + '<div class="popup-item"><label>部件类型</label>' + feature.properties.dl + ' / ' + feature.properties.xl + '</div>' + '<div class="popup-item"><label>部件编号</label>' + feature.properties.objid + '</div>' + '<div class="popup-item"><label>所在网格编号</label>' + feature.properties.bgid + '</div>' + '<div class="popup-item"><label>权属部门</label>' + feature.properties.deptname2 + '</div>' + '<div class="popup-item"><label>责任部门</label>' + feature.properties.deptname1 + '</div>' + '<div class="popup-item"><label>养护部门</label>' + feature.properties.deptname3 + '</div>' + '</div>' results.push({ name: feature.properties.dl + ' / ' + feature.properties.xl, code: feature.properties.objid, content: { title: '权属部门', text: feature.properties.deptname2 }, lng: feature.geometry.coordinates[0], lat: feature.geometry.coordinates[1], popupStr: popupStr }) } } // 如果是最后一个图层 if (i === (partsLength - 1)) { this.searchList = results this.searchLoading = false } }) } } else { this.searchLoading = false } }, // 清空查询 clearSearch() { this.searchList = [] if (this.currentSearchMarkers.length > 0) { for (const marker of this.currentSearchMarkers) { this.map.removeLayer(marker) } this.currentSearchMarkers = [] } }, // 查询结果页面变化,重绘icon changeSearchPage(data) { // 清除原来绘制的点 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 icon = this.searchIconList[i] const latlng = L.latLng([data[i].lat, data[i].lng]) const item = L.marker(latlng, { icon: icon }).addTo(this.map) item.on('click', e => { if (item.getPopup()) { item.unbindPopup() } // 绑定弹窗 const popup = L.popup().setContent(data[i].popupStr) item.bindPopup(popup).openPopup([data[i].lat, data[i].lng]) }) this.currentSearchMarkers.push(item) } // 中心置为第一个点的中心 if (this.currentSearchMarkers[0]) { this.clickSearchItem(data[0], 0) } }, // 点击查询列表项,将位置放在中间 clickSearchItem(item, index) { if (item) { const latlng = L.latLng([item.lat, item.lng]) let zoom = this.map.getZoom() if (zoom < 19) { zoom = 19 } this.map.flyTo(latlng, zoom) // this.currentSearchMarkers[index].openPopup() } }, // 移除框选区域 drawDelete() { if (this.drawLayer) { this.drawLayer.layer.remove() } this.bounds = null this.search() }, // 绘制选框 drawBox(type) { if (this.drawLayer) { this.drawLayer.layer.remove() } this.bounds = null this.$nextTick(() => { this.map.pm.enableDraw(type, { snappable: false }) // 绘制完成, 选区内重新查询 this.map.on('pm:create', e => { this.clearDisabled = false if (this.drawLayer) { this.drawLayer.layer.remove() } this.drawLayer = e this.bounds = e.layer._bounds this.search() }) }) }, // 部件图层切换 layerChange(node) { this.$refs.layerRadio.$refs.layerTree.setCheckedKeys([node.id]) this.selectLayers = [node.id] // 清除查询 this.clearSearch() }, // 地图初始化 init() { const map = L.map('map', { minZoom: 14, maxZoom: 25, 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 // 加载天地图底图和标注 this.baseLayer.push(L.tileLayer(this.baseConfig.mapUrl).addTo(map)) this.baseLayer.push(L.tileLayer(this.baseConfig.labelUrl).addTo(map)) // 构建并加载layerGroup /* eslint-disable new-cap */ this.addMapDtLyaer() // 底图 // 部件图层组 for (let i = 3; i < 10; i++) { this.addPartsLayer(i) } }, // 添加底图图层 addMapDtLyaer: function() { const mapDtGroup = new L.layerGroup() // 创建layerGroup // 创建图层并加载到layerGroup中 const item = { url: `${this.baseUrl}${this.mapUrl}` } esri.dynamicMapLayer(item).addTo(mapDtGroup) this.layerGroups.push(mapDtGroup) this.map.addLayer(mapDtGroup) }, addPartsLayer: function(index) { const that = this const group = new L.layerGroup() // 创建layerGroup const domains = this.baseConfig.layers[index].domain domains.forEach(dom => { // 创建图层并加载到layerGroup中 const item = { url: `${this.baseUrl}${this.partsUrl}/${dom}`, minZoom: 18 } const layer = esri.featureLayer(item).addTo(group) this.parts.push(item) layer.on('click', function(e) { // 获取要素的属性 const properties = e.layer.feature.properties // 弹窗样式 const popupStr = '<div class="popup-div">' + '<div class="popup-title">部件信息</div>' + '<div class="dashed-line"></div>' + '<div class="popup-item"><label>部件类型</label>' + properties.dl + ' / ' + properties.xl + '</div>' + '<div class="popup-item"><label>部件编号</label>' + properties.objid + '</div>' + '<div class="popup-item"><label>所在网格编号</label>' + properties.bgid + '</div>' + '<div class="popup-item"><label>权属部门</label>' + properties.deptname2 + '</div>' + '<div class="popup-item"><label>责任部门</label>' + properties.deptname1 + '</div>' + '<div class="popup-item"><label>养护部门</label>' + properties.deptname3 + '</div>' + '</div>' // 弹出窗口 L.popup().setContent(popupStr).setLatLng(e.latlng).openOn(that.map) }) that.layerGroups.push(group) }) this.map.addLayer(group) }, // 初始化图标 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, 25], popupAnchor: [0, -25] }) this.searchIconList.push(icon) } }, handleCurrentChange(val) { this.offset = val const num = 5 * val >= this.searchList.length ? this.searchList.length : 5 * val this.showlist = this.searchList.slice(5 * (val - 1), num) this.drawIcon() } } } </script> <style rel="stylesheet/scss" lang="scss" scoped> /deep/ .el-input-group__append, .el-input-group__prepend { background-color: #409eff !important; border: 1px solid #409eff !important; } /*/deep/ .leaflet-touch .leaflet-control-layers, /deep/ .leaflet-touch .leaflet-bar {*/ /*!*border: 2px solid rgba(0,0,0,0.2);*!*/ /*!*background-clip: padding-box;*!*/ /*position: absolute !important;*/ /*right: 300px !important;*/ /*top: 10px;*/ /*}*/ $tableTitleHeight:35px; .leaflet_container{ width: calc( 100vw - 208px ); height: calc( 100vh - 158px ); background-color: white; } .search-total{ background-color: white; width: 300px;height: 35px; margin-top: 6px;border: 1px solid #DCDFE6; padding-left: 15px;color: #409eff; padding-top: 8px; font-size: 13px; border-radius:3px; } .draw-icon{ background-color: white; width: 100px;height: 40px; border: 1px solid #DCDFE6; border-radius:4px; } .table{ width: 300px;margin-top: 3px; border: 1px solid #DCDFE6; border-radius:3px; /deep/ .el-table th.is-leaf, /deep/ .el-table td { border-bottom: 1px solid white !important; } /deep/ .el-table--group::after, .el-table--border::after, .el-table::before { background-color: white !important; } .table-item{ padding: 5px; margin-top: 2px; margin-bottom: 1px; -moz-box-shadow: 0px 1px 3px #d9d9d9; /* 老的 Firefox */ box-shadow: 0px 1px 3px #d9d9d9; .item-icon{ width: 30px; height: 30px; background-size: contain; background-image: url("../../assets/global_images/point.png"); color: white; text-align: center; margin-top: 10px; margin-left: 5px; padding-top: 2px; } } } .top_title{ height: 40px; font-size: 18px; border: 1px solid #b5b5b5; padding-top: 10px; } .title{ height: 40px; width: 320px; padding-top: 10px; padding-left: 5px; z-index: 100000; background-color: rgba(255, 255, 255, 0.91); } .titletext{ text-align: center; font-size: 15px; } .titlenumtext{ text-align: center; font-size: 19px; } .list{ text-align:center; width: 500px; height: 40px; z-index: 10000; position: absolute; right:20px; top:20px; } .btn_bottom{ text-align:center; width: 100%; height:70px; z-index: 1000; position: fixed; bottom: 0; left: 0;right:0; } .table-name{ position:relative; width: 100px; top:20px; margin:-10px auto; //外面的div高度的一半 z-index: 100001; } .right-card{ margin-top: 20px;position: absolute;right:50px;z-index: 100000;width: 130px; } .table-title{ background-color:rgba(243, 243, 243, 1); height: $tableTitleHeight; .title-header{ line-height:$tableTitleHeight; color: #606266; font-size: 15px; i{ margin-left: 5px; margin-right: 5px; } } } </style> <style> .leaflet-container a.leaflet-popup-close-button { top: 5px; /* 修改弹窗关闭按钮样式 */ right: 10px; /* 修改弹窗关闭按钮样式 */ } .leaflet-popup-content .popup-div { font-size: 15px; padding: 6px; } .leaflet-popup-content .popup-title { font-weight: bold; } .leaflet-popup-content .dashed-line { border-bottom: 1px dashed #dddddd; margin: 10px -10px; } .leaflet-popup-content .popup-item { padding-bottom: 6px; } .leaflet-popup-content .popup-item label { font-weight: bold; padding-right: 15px; } </style>