使用 Vue.js 實作郵局三倍券存量地圖紀錄
繼前面兩篇 使用 Vue.js + Element UI 製作 Todo List 全紀錄及 使用 Vue.js 串接 Twitch API 顯示熱門遊戲及直撥頻道,今天再來繼續 Vue.js 的修行
為甚麽想做這個呢?因爲昨天半夜睡不著覺,無聊的時候滑著手機看新聞才發現,哦~原來今天 7/15 是開始領三倍券的日子,所以上到政府資料開放平臺找看看有沒有相關的 API 可以接,結果真的有找到全國郵局即時振興三倍券販賣存量,還有之前沒有跟到寫口罩地圖,所以更加想練習看看,如果文章內有錯誤的地方,請多多包涵 :)
1. Demo & Source Code
2. 縣市地區選單
為了在前端渲染出各縣市地區的下拉選項,當然不可能每次都發請求去要回 JSON 來渲染,所以我們先下載已經整理好的縣市地區資料檔並存到 src/assets 底下
接著將縣市地區的 JSON 檔 Import 進來,並綁定到 Vue Data 中,這邊也建立 select 空物件,以便將下拉選單的值綁定進來
<script>
import cityName from "@/assets/CityCountyData.json";
export default {
name: "Home",
data() {
return {
cityName,
select: {},
};
},
}
</script>
接著在前端將選項渲染出來,這邊搭配使用的 Element UI 的框架,縣市地區很簡單的搭配 v-for 迴圈把縣市地區渲染出來就可以,那至於地區該怎麼辦?因爲必須判斷縣市再來選擇出相依的地區,所以這邊利用了 Array.prototype.find(),這邊建立一個 city 的 callback,當 city.CityName 等於所選的縣市 select.city 時會回傳陣列的索引值,所以接著在回傳該索引值中的 AreaList 就可以渲染出相依的縣市地區
<el-select
v-model="select.city"
placeholder="請選擇縣市"
size="mini"
>
<el-option
v-for="item in cityName"
:key="item.CityName"
:label="item.CityName"
:value="item.CityName"
></el-option>
</el-select>
<el-select
v-model="select.area"
placeholder="請選擇地區"
v-if="select.city"
size="mini"
>
<el-option
v-for="item in cityName.find(
(city) => city.CityName === select.city
).AreaList"
:key="item.AreaName"
:label="item.AreaName"
:value="item.AreaName"
></el-option>
</el-select>
對了,在地區的下拉選項中,我加入了一個 v-if,當 select.city 有值的時候才會渲染地區的下拉選項,不這麼做的話會因爲渲染時沒有值導致錯誤
3. Import Axios & Test API
完成下拉選單後就可以來串接 API,首先先安裝 Axios 並註冊到 Vue 中
npm install --save axios vue-axios
// src/main.js
import axios from "axios";
import VueAxios from "vue-axios";
Vue.use(VueAxios, axios);
在需要引入的組件中引入,在 Data 中先建立空的陣列以便儲存 API 資料,並在 mounted() 鉤子函數中呼叫 getPostData(),以便在元素載入後自動呼叫 getPostData() 拿資料回來
import axios from "axios";
export default {
name: "Home",
data() {
return {
postData: [],
};
},
mounted() {
this.getPostData();
},
methods: {
async getPostData() {
await axios
.get("https://3000.gov.tw/hpgapi-openmap/api/getPostData")
.then((response) => {
this.postData = response.data;
console.log(this.postData);
})
.catch((error) => {
console.log(error);
});
},
}
}
4. Import OpenStreetMap & Leaflet
完成下拉選單及串接 API 後就可以來渲染地圖的部份,這邊使用的是 OpenStreetMap,這是一套開源的地圖工具,並且是免費的,這邊需要搭配 Leaflet 一起使用
首先先裝 Leaflet 到我們的專案中並在組件引入
npm install leaflet
import L from "leaflet";
並加入需要的 CSS,加入到 public/index.html 中
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css"
integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ=="
crossorigin=""/>
5. 渲染地圖
在完成 Leaflet 的前置作業後,我們就可以先來測試渲染地圖出來看看,我們要先在網頁模版中加入容器,並記得加上容器的高度,否則是看不到的
<div id="map"></div>
<style>
#id {
height: 500px;
}
</style>
接著先建立一個變數 openStreetMap
let openStreetMap = {};
export default {
name: "Home",
data() {
return {
cityName,
postData: [],
select: {},
userData: {},
};
},
建立一個初始化 Function 在 mounted() 中
mounted() {
openStreetMap = L.map("map", {
center: [25.0401, 121.5120],
zoom: 13,
});
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution:
'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
maxZoom: 20,
}).addTo(openStreetMap);
}
這時重新整理就可以看到地圖正確渲染出來,並定位到指定經緯度上
6. 建立地標到地圖中
首先先在 computed 計算屬性中,加入一個方法來篩選資料,這邊使用到 Array.prototype.filter(),在 post 這個 callback 加入判斷如果前端有沒有選擇地區的話,就回傳 post.hsnNm 等於 select.city 的元素並建構成新的陣列,反之回傳地區的元素並建構新的陣列
computed: {
postOffice() {
return this.postData.filter((post) => {
if (!this.select.area) {
return post.hsnNm === this.select.city;
}
return post.townNm === this.select.area;
});
},
},
watch: {
postOffice(val) {
this.updateMap();
},
},
methods: {
updateMap() {
// clear markers
openStreetMap.eachLayer((layer) => {
if (layer instanceof L.Marker) {
openStreetMap.removeLayer(layer);
}
});
// add markers
this.postOffice.forEach((post) => {
L.marker([post.latitude, post.longitude]).addTo(openStreetMap)
.bindPopup(`<p><strong style="font-size: 20px;">${
post.storeNm
}</strong></p>
<strong style="font-size: 16px; color: #d45345;">三倍券剩餘:${
post.total ? `${post.total} 張` : "未取得資料"
}</strong><br>
地址: ${post.addr}<br>
電話: ${post.tel}<br>
<small>最後更新時間: ${post.updateTime}</small>`);
});
},
}
接著配合 watch 來追蹤 postOffice() 的值是否有變化,有變化的話就依據新的資料建立地標到地圖中
7. 取得使用者的座標
在上面我們的寫法是將預設座標經緯度固定了,現在我們希望使用者在進入網頁時會跳出詢問視窗,是否授權給予座標資訊
我們在 methods 中加入一個 getCurrent 方法,當使用者同意給予座標資訊後會將經緯度綁定到 userData.longitude 及 userData.latitude 並 呼叫 createMap() 定位到使用者的經緯度上,反之使用者拒絕或獲取位置失敗會自訂一個位置並呼叫 createMap() 來定位
mounted() {
this.getPostData();
this.getCurrent();
},
methods: {
getCurrent() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
//locationSuccess
(position) => {
this.userData.longitude = position.coords.longitude;
this.userData.latitude = position.coords.latitude;
this.createMap();
},
//locationError
(error) => {
this.userData.latitude = 25.042474;
this.userData.longitude = 121.513729;
this.createMap();
}
);
}
},
createMap() {
openStreetMap = L.map("map", {
center: [this.userData.latitude, this.userData.longitude],
zoom: 13,
});
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution:
'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
maxZoom: 20,
}).addTo(openStreetMap);
},
}
一開始我們是將渲染地圖的方法直接寫在 mounted() 中,現在記得要搬回 methods 中並獨立成一個方法
8. 總結
雖然我已經把三倍券綁定在信用卡上了 XD 無法參與到領實體券,但至少有參與製作三倍券地圖,稍稍彌補了之前沒有練習到口罩地圖的遺憾,雖然這個程式很陽春,但也可以熟悉 Array 的常用方法及使用 Leaflet 的方式,這在未來實作時也會時常用到,也是很不錯的練習 :)
Leave a comment