做爲一名開發人員,每次接到開發任務,咱們首先應該先分析需求,而後再思考技術方案和解決方案。三思然後行,這是一個好的習慣。
css
需求:本項目是採用vue組件化開發的單頁應用項目,現須要在項目中引入百度的地圖展現功能,用於展現全部項目的分佈狀況。搜索功能(省略,不是這裏介紹的內容).......html
交互:選中左側的項目,選中項高亮,自動定位到右側地圖中項目所在位置,並彈出項目的基本信息。點擊右側的項目,自動高亮顯示左側的項目,並滾動到項目所在位置。地圖支持聚合和縮放。前端
項目運行效果以下圖所示:vue
接下來開始開發:jquery
百度開發者平臺已經封裝了基於vue的地圖組件,詳細使用,請參考官網:webpack
https://dafrok.github.io/vue-baidu-map/#/zh/start/installationgit
網上有一些是直接在index.html頁面所有引用的,本人強烈反對此種使用方式,由於咱們項目是組件化的單頁應用,強行引入多頁應用的開發方式,會破壞整個項目的框架,嚴重影響性能。有些甚至還在vue單頁應用中引入jquery,感受這都是一些反人類的騷操做,不到萬不得已,不建議使用。程序員
我這裏只演示單頁應用的開發方式。github
1.安裝組件web
$ npm install vue-baidu-map --save
2.註冊組件
組件的註冊能夠分爲全局註冊和局部註冊,我這裏採用的是局部註冊。由於整個項目中僅此一個界面使用。引入官方的說明:
若是有按需引入組件的須要,能夠選擇局部註冊百度地圖組件,這將減小工程打包後的容量尺寸。局部註冊的
BaiduMap
組件必須聲明ak
屬性。 全部的獨立組件均存放在vue-baidu-map/components
文件夾下,按需引用便可。 因爲未編譯的 ES 模塊不能在大多數瀏覽器中直接運行,若是引入組件時發生運行時錯誤,請檢查 webpack 的 loader 配置,確認include
和exclude
選項命中了組件庫。
引入組件代碼以下:
//百度地圖 import BaiduMap from 'vue-baidu-map/components/map/Map.vue' import BmScale from 'vue-baidu-map/components/controls/Scale' import BmNavigation from 'vue-baidu-map/components/controls/Navigation' import BmMarkerClusterer from 'vue-baidu-map/components/extra/MarkerClusterer' import BmMarker from 'vue-baidu-map/components/overlays/Marker' import BmInfoWindow from 'vue-baidu-map/components/overlays/InfoWindow'
組件註冊:
export default { name: "pm-distribution", components: { BaiduMap, BmScale, BmNavigation, BmMarkerClusterer, BmMarker, BmInfoWindow }, ......
3.HTML部分:
<baidu-map :style="{width:map.width,height:map.height}" class="map" ak="你的百度地圖祕鑰" :zoom="map.zoom" :center="{lng: map.center.lng, lat: map.center.lat}"
@ready="handler" :scroll-wheel-zoom="true"> <!--比例尺控件--> <bm-scale anchor="BMAP_ANCHOR_TOP_RIGHT"></bm-scale> <!--縮放控件--> <bm-navigation anchor="BMAP_ANCHOR_BOTTOM_RIGHT" ></bm-navigation> <!--聚合動態添加的點座標--> <bm-marker-clusterer :averageCenter="true"> <bm-marker v-for="marker of markers" :key="marker.code" :position="{lng: marker.lng, lat: marker.lat}" @click="lookDetail(marker)"></bm-marker> </bm-marker-clusterer> <!--信息窗體--> <bm-info-window :position="{lng: infoWindow.info.lng, lat: infoWindow.info.lat}" :title="infoWindow.info.name" :show="infoWindow.show" @close="infoWindowClose" @open="infoWindowOpen"> <p><span class="left">單位面積能耗:</span><span class="right">{{infoWindow.info.areaEnergy}}kWh/㎡</span></p> <p><span class="left">建築面積:</span><span class="right">{{infoWindow.info.area}}㎡</span></p> <p><span class="left">電耗:</span><span class="right">{{infoWindow.info.energy}}kWh</span></p> <p><span class="left">水耗:</span><span class="right">{{infoWindow.info.water}}m³</span></p> <p><span class="left">氣耗:</span><span class="right">{{infoWindow.info.air}}m³</span></p> </bm-info-window> </baidu-map>
尋找共性,提取公共部分,左側點擊項目和地圖中點擊項目,效果大致一致,都是彈出一個信息框,提取方法:
//查看詳情 lookDetail(data, target){ this.infoWindow.show =true; this.infoWindow.info=data; this.activeName = data.name; //爲彈窗口標題添加title this.$nextTick(()=>{ var win=document.querySelector(".BMap_bubble_title"); win.title = this.activeName; }) if(target=='left'){ //點擊的是左側列表項,則不須要滾動 this.map.center={lng: data.lng, lat: data.lat}; this.map.zoom = 15; return; } //滾動到指定元素位置 this.$nextTick(()=>{ var obj=document.querySelector(".active"); var scrollTop=obj.offsetTop; this.$refs.box.scrollTop=scrollTop-180; }) },
注意看上述代碼,用到了this.$nextTick,這是在vue中若是要對dom進行操做,在此方法中能夠保證dom節點已加載完成,vue中是以數據驅動的形式來渲染dom的,也就是說數據修改後,dom不會立刻改變,它會排隊等待修改。再演示某些程序員的騷操做:
setTimeout(function () { var win=document.querySelector(".BMap_bubble_title"); win.title = This.activeName; },300);
注意看:上述代碼使用了setTimeout進行延時,來避免數據改變了,可是獲取的dom中數據不是最新的狀況,雖然大部分狀況下能夠解決問題,可是存在缺陷。一、你如何肯定恰好300毫秒就能夠讀取到最新的dom數據了。(經驗值)2.萬一你設置的這個延時時間小了呢?或者數據變動得慢了一些呢?可能你設置了300ms,可實際100ms就已經變動了呢?是否是存在200ms的效率差?等等.....
左側項目名稱超出自動顯示省略號
單行文本的溢出顯示省略號同窗們應該都知道用text-overflow:ellipsis屬性來,固然還須要加寬度width屬來兼容部分瀏覽。
實現方法,直接經過css樣式控制:
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
左側項目列表選中項高亮,其它項正常顯示
經過定義一個變量activeName ,記錄當前選中的項目名稱(此處項目名稱不可能重複),若是當前項目的名稱和activeName 的值一致時,添加一個css樣式名稱active,而後設置這個樣式active高亮(設置背景色啊等等)
以下代碼所示:
<div class="row" v-for="marker of markers" :key="marker.code" @click="lookDetail(marker,'left')" :class="{active: activeName == marker.name}">
點擊地圖中項目,自動定位到左側項目選中位置
左側選中的dom,有一個激活樣式active,獲取到它的offsetTop屬性,而後設置列表容器的scrollTop屬性便可。
//滾動到指定元素位置 this.$nextTick(()=>{ var obj=document.querySelector(".active"); var scrollTop=obj.offsetTop; this.$refs.box.scrollTop=scrollTop-180; })
注意:點擊左側項目,不須要滾動,只有點擊地圖中的項目纔去滾動。
關於項目信息框中標題超出添加省略號,添加title完整提示
一般咱們添加了超出部分省略號,通常都會給其添加一個完整的title顯示。在現有的百度提供的InfoWindow組件中是沒有封裝這個屬性的,因此咱們一般有兩種辦法:1.擴展組件源碼(耗時)2.直接dom操做。
這裏我選擇第二種,由於快。瀏覽器中按F12,能夠發現這個標題的HTML代碼部分:
<div class="BMap_bubble_title" style="overflow: hidden; height: auto; line-height: 24px; white-space: nowrap; width: auto;" title="南京高新區管委會行政辦公大樓">南京高新區管委會行政辦公大樓</div>
咱們看到有一個BMap_bubble_title樣式,咱們能夠直接操做這個dom。
代碼以下:
//爲彈窗口標題添加title this.$nextTick(()=>{ var win=document.querySelector(".BMap_bubble_title"); win.title = this.activeName; })
從左側樹點擊項目要修改Zoom,直接定位到項目信息
地圖自動鋪滿右側,而且高度全屏且和左側高度基本一致
但凡這種狀況,首先就考慮要計算瀏覽器的寬高了,固然你也可使用一些自適應的UI庫,我這裏直接本身計算的。這個也很簡單,獲取瀏覽器但是部分高度,減去一些固定px的長度部分便可。
關於單頁應用中的樣式問題
我發現一些之前作慣了多頁應用開發的人,如今來作單頁應用,他會很迷糊,由於在多頁應用的世界,每一個界面是獨立的,每一個界面中的樣式是互不影響的。而單頁應用,一般是一個入口,其它組件(頁面)都是按需加載,樣式命名相同就衝突了,也就是合併覆蓋。避免的方式呢,組件中只本身用的,添加scoped,若是須要覆蓋其它的,就在組件容器的最外層添加一個class或者ID(這個在項目中惟一命名),而後覆蓋的樣式都寫在這個容器樣式之下。
如局部樣式:
<style lang="scss" scoped>
全局樣式:
<style lang="scss"> .pm-distribution{ .BMap_bubble_title { ......
完整代碼:
<template> <div class="pm-distribution"> <h3 class="title">項目分佈</h3> <div class="container" id="container" :style="{height:containerHeight}"> <div class="left"> <div class="top"> <!--行政區域--> <div class="item fl"> <el-select filterable clearable v-model="districtType" style="width: 140px;margin-left: 5px;"> <el-option v-for="item in districtTypeOptions" :key="item.value" :label="item.label" :value="item.value"> </el-option> </el-select> </div> <!--項目類型--> <div class="item fl"> <el-select filterable clearable v-model="buildType" style="width: 140px;"> <el-option v-for="item in buildTypeOptions" :key="item.id" :label="item.name" :value="item.id"> </el-option> </el-select> </div> <div class="item fl" style="margin-left: 10px"> <el-select filterable clearable v-model="buildId" style="width: 190px;"> <el-option v-for="item in buildOptions" :key="item.code" :label="item.name" :value="item.code"> </el-option> </el-select> </div> <!--查詢--> <div class="item query-submit fl" @click="search"> <i class="el-icon-search"></i> 查 詢 </div> </div> <div class="header">共{{markers.length}}個項目<span style="float: right;margin-right: 15px;">今日能耗</span></div> <div class="list" :style="{height:leftHeight}"> <div class="list-context"> <div ref="box" class="list-scroll bmr-y-scroll":style="{height:leftHeight}"> <!-- 項目列表--> <div class="listItemDIV"> <div class="row" v-for="marker of markers" :key="marker.code" @click="lookDetail(marker,'left')" :class="{active: activeName == marker.name}"> <div class="head-title" v-text="marker.name" :title="marker.name"></div> <div class="row-content"> <span class="item fl"><i class="iconfont nhjc-dianli electricity"></i>{{marker.energy}}kWh</span> <span class="item fl"><i class="iconfont nhjc-shui water"></i>{{marker.water}}m³</span> <span class="item fl"><i class="iconfont nhjc-qi air"></i>{{marker.air}}m³</span> </div> </div> <div style="clear:both;"></div> </div> </div> </div> </div> </div> <div class="right-context" id="right-context"> <baidu-map :style="{width:map.width,height:map.height}" class="map" ak="這裏填你的百度祕鑰" :zoom="map.zoom" :center="{lng: map.center.lng, lat: map.center.lat}" @ready="handler" :scroll-wheel-zoom="true"> <!--比例尺控件--> <bm-scale anchor="BMAP_ANCHOR_TOP_RIGHT"></bm-scale> <!--縮放控件--> <bm-navigation anchor="BMAP_ANCHOR_BOTTOM_RIGHT" ></bm-navigation> <!--聚合動態添加的點座標--> <bm-marker-clusterer :averageCenter="true"> <bm-marker v-for="marker of markers" :key="marker.code" :position="{lng: marker.lng, lat: marker.lat}" @click="lookDetail(marker)"></bm-marker> </bm-marker-clusterer> <!--信息窗體--> <bm-info-window :position="{lng: infoWindow.info.lng, lat: infoWindow.info.lat}" :title="infoWindow.info.name" :show="infoWindow.show" @close="infoWindowClose" @open="infoWindowOpen"> <p><span class="left">單位面積能耗:</span><span class="right">{{infoWindow.info.areaEnergy}}kWh/㎡</span></p> <p><span class="left">建築面積:</span><span class="right">{{infoWindow.info.area}}㎡</span></p> <p><span class="left">電耗:</span><span class="right">{{infoWindow.info.energy}}kWh</span></p> <p><span class="left">水耗:</span><span class="right">{{infoWindow.info.water}}m³</span></p> <p><span class="left">氣耗:</span><span class="right">{{infoWindow.info.air}}m³</span></p> </bm-info-window> </baidu-map> </div> </div> </div> </template> <script> import { buildTypeOptionsData, districtTypeOptionsData, buildOptionsData } from "../views/energyAnalysis/energyConsumptionRanking/energyConsumptionRanking" import { getProgramsType } from "../assets/js/bmr-request"; import GlobalUtil from "../utils/globalUtil"; import PmDistributionService from "../services/pmDistributionService" //百度地圖 import BaiduMap from 'vue-baidu-map/components/map/Map.vue' import BmScale from 'vue-baidu-map/components/controls/Scale' import BmNavigation from 'vue-baidu-map/components/controls/Navigation' import BmMarkerClusterer from 'vue-baidu-map/components/extra/MarkerClusterer' import BmMarker from 'vue-baidu-map/components/overlays/Marker' import BmInfoWindow from 'vue-baidu-map/components/overlays/InfoWindow' export default { name: "pm-distribution", components: { BaiduMap, BmScale, BmNavigation, BmMarkerClusterer, BmMarker, BmInfoWindow }, data() { return { districtType: 0,//行政區域 districtTypeOptions: GlobalUtil.deepCopy(districtTypeOptionsData),//行政區域選項 buildType: 0,//項目類型 buildTypeOptions: buildTypeOptionsData,//項目類型選項 buildId: '',//項目ID buildOptions: buildOptionsData, //項目列表 searchParams:{ regions:0,//區域編號 proType:0,//項目類型 proCode:'',//項目編號 }, map:{ center: {lng: 118.802422,lat:32.065631},//'南京市', zoom: 12, width:'1000px', height:'710px' }, markers:[], infoWindow: { lng: 0, lat: 0, show: false, info:{ air: 0, area: 12313, areaEnergy: 0.64, code: "440300A055", energy: 7922.66, lat: "32.043433", lng: "118.784107", name: "市人民檢察院", water: 0 }, }, activeName: '', leftHeight:'540px', containerHeight:'700px' } }, created() { this.districtTypeOptions[0].label = '請選擇行政區域'; this.getBuildTypes(); this.getSelectPro(); }, mounted() { this.leftHeight=document.body.clientHeight-300+'px'; this.containerHeight=document.body.clientHeight-150+'px'; }, methods: { //查詢 search() { this.searchParams.regions=this.districtType; this.searchParams.proType=this.buildType; this.searchParams.proCode=this.buildId; this.getProPositionMap(); }, infoWindowClose (e) { this.infoWindow.show = false }, infoWindowOpen (e) { this.infoWindow.show = true }, //查看詳情 lookDetail(data, target){ // console.log('data',data) this.infoWindow.show =true; this.infoWindow.info=data; this.activeName = data.name; // let This=this; //爲彈窗口標題添加title this.$nextTick(()=>{ var win=document.querySelector(".BMap_bubble_title"); win.title = this.activeName; }) if(target=='left'){ //點擊的是左側列表項,則不須要滾動 this.map.center={lng: data.lng, lat: data.lat}; this.map.zoom = 15; return; } //滾動到指定元素位置 this.$nextTick(()=>{ var obj=document.querySelector(".active"); var scrollTop=obj.offsetTop; this.$refs.box.scrollTop=scrollTop-180; }) }, //地圖初始化 handler ({BMap, map}) { console.log(BMap, map) // this.map.center.lng = 118.802422 // this.map.center.lat = 32.065631 // this.map.zoom = 12; this.map.width= document.getElementById("container").clientWidth-330+'px'; this.map.height=document.body.clientHeight -160+'px'; this.getProPositionMap(); }, //獲取項目列表 getSelectPro(){ PmDistributionService.instance().getSelectPro({}).then((res) => { if (res.code === 200) { console.log('res',res) res.data.list.unshift({code:'',name:'請輸入項目名稱'}) this.buildOptions=res.data.list; } else { this.$message({ message: "獲取數據失敗", type: "error" }); } }) }, //項目分佈地圖 getProPositionMap(){ PmDistributionService.instance().getProPositionMap(this.searchParams).then((res) => { if (res.code === 200) { // console.log('res',res) this.markers=res.data.list; } else { this.$message({ message: "獲取數據失敗", type: "error" }); } }) }, //獲取項目類型 getBuildTypes() { getProgramsType().then(res => { //獲取樓建築下拉框 let temArr = new Array(); temArr.push({name: '請選擇項目類型', id: 0, type: 10}) res.array.forEach(function (item, index) { temArr.push(item); }) console.log('res.array', res.array) this.buildTypeOptions = temArr; }); }, } } </script> <style lang="scss" scoped> $bgBlueColor: #1881bf; h3.title { box-sizing: border-box; height: 34px; line-height: 34px; font-size: 16px; font-weight: 600; padding: 0 0 0 30px; border: 1px #E5EEF3 solid; color: #2274A4; background: #F5F9F9; width: 100%; text-align: left; } .container { margin: 0 auto; min-width: 1366px; padding: 0px; min-height: 710px; .left { width: 320px; float: left; .header { width: 300px; clear: left; height: 34px; line-height: 34px; color: black; background: #F5F9F9; padding-left: 20px; } .top { height: 70px; padding: 8px 5px 12px 5px; } } .right-context { float: left; } .item { margin: 5px; height: 28px; line-height: 28px; .electricity{ color: #FFD670; } .water{ color:#4EB9DB; } .air{ color:#F39795; } .iconfont{ font-size: 22px; } } .query-submit { width: 90px; border-radius: 28px; background: $bgBlueColor; color: #fff; text-align: center; cursor: pointer; } .list{ .item{ width: 93px; height: 30px; line-height: 30px; display: block; } .item.fl{ float: left; } .list-context{ border-radius:5px;margin-top:10px;background:white; .list-scroll{ margin-top:10px;overflow-y:auto;min-height:537px;overflow-x: hidden; .row{ float:left;width:320px; cursor: pointer; .head-title{ overflow: hidden;text-overflow: ellipsis;white-space: nowrap;font-size:16px;color:#1781BF;font-weight:400; padding-left: 20px; height: 30px; line-height: 30px; } .row-content{ overflow: hidden;text-overflow: ellipsis;white-space: nowrap;padding-bottom:10px;font-size:12px;color:rgb(128, 128, 128); border-bottom: 1px solid #eee; padding-left: 10px; } } .row.active{ background-color: #CFDDF3; } } } } .map{ min-width: 800px; width:1000px;height:710px;float:left;background:white;border-radius:5px;margin-left:10px; .right{ text-align: left; } .left{ width: 100px; } } } </style> <style lang="scss"> .pm-distribution{ .BMap_bubble_title { color: #2DB7F5 !important; font-size: 16px; font-weight: 400; margin-bottom: 8px; overflow: hidden; text-overflow:ellipsis; white-space: nowrap; width: 220px !important; } } </style>
說明:本界面全部功能是一天以內趕出來的,因此代碼就湊合吧。其它的一些封裝的組件代碼沒有貼出來,由於本篇重點是介紹地圖的使用。我見過一些所謂前端工程師的代碼,也就那樣,有些還時不時的弄一些騷操做,請容許我自戀一下,O(∩_∩)O哈哈~。雖然我不是專業的前端,可是不論是寫後端代碼仍是前端代碼,多少要有點追求,有時候爲了趕進度,快得了一時,其實誤了後面更多時間,不少項目都是被怎麼快怎麼來玩死的,天天想着之後再重構,之後再重構,到後面就不了了之了..........
明天是三八節,今天給公司每一位女同胞寫賀卡,已經快忘記怎麼寫字了,中學時代,我但是專業情書槍手呢?O(∩_∩)O哈哈~