要對個別的元件做更多的指令之前,需要先把他們個別的拆出來,第一個拆解的是列表的部分,先在components/下建立一個asideMenu.vue檔案,把剛剛App.vue中的<div class="aside-menu">...</div>
搬過去
//asideMenu.vue
<template>
<div class="aside-menu">
...
</div>
</template>
<script>
export default {
name: "asideMenu",
};
</script>
在App.vue被搬走的位置改由取代,並import新建立的
//App.vue
<template>
<div id="app">
<!-- aside-menu 左側欄 -->
<asideMenu />
<!-- 地圖區塊 -->
<div class="mask-map" id="mask-map"></div>
</div>
</template>
<script>
import asideMenu from "./components/asideMenu.vue";
export default {
name: "App",
components: {
asideMenu,
},
};
</script>
<style lang="scss" src="./style.scss"></style>
在store/index.js中先定義好幾個狀態
state: {
//目前所選縣市,預設城市
currCity: '臺北市',
//目前所選行政區,預設北投區
currDistrict: '北投區',
//存放API回傳的縣市/行政區列表資訊
location: [],
//存放API回傳的所有藥局
stores: []
}
先建立對應的mutations,透過他操作state的資料
mutations: {
setcurrCity(state, payload) {
state.currCity = payload
},
setcurrDistrict(state, payload) {
state.currDistrict = payload
},
setAreaLocation(state, payload) {
state.location = payload
},
setStores(state, payload) {
state.stores= payload
}
}
接著透過actions從json檔案抓取資料,存到state裡面
actions: {
// 取得行政區資料
async fetchLocations({ commit }) {
const json = await fetch('https://raw.githubusercontent.com/kurotanshi/mask-map/master/raw/area-location.json')
.then((res) => res.json())
//commit操作mutaions
commit('setAreaLocation', json)
},
// 取得藥局資料
async fetchPharmacies({ commit }) {
const json = await fetch('https://raw.githubusercontent.com/kiang/pharmacies/master/json/points.json')
.then((res) => res.json())
//整理資料格式,拆出經緯度
const data = json.features.map((d) => ({
...d.properties,
latitude: d.geometry.coordinates[0],
longitude: d.geometry.coordinates[1]
}))
//commit操作mutaions
commit('setStores', data)
}
}
最後回到App.vue新增mountedhook
import { mapActions } from "vuex";
import asideMenu from "./components/asideMenu.vue";
export default {
name: "App",
components: {
asideMenu,
},
methods: {
...mapActions(["fetchLocations", "fetchPharmacies"]),
},
mounted() {
this.fetchLocations();
this.fetchPharmacies();
},
};
可以從開發者工具看看是否有發送遠端請求
得到資料後就要把它放到左側兩個下拉選單之中,在asideMenu.vue下新增computed來存取state的資料,將currCity和currDistrict透過v-model雙向綁定。
computed: {
currCity: {
get() {
return this.$store.state.currCity;
},
set(value) {
this.$store.commit("setcurrCity", value);
},
},
currDistrict: {
get() {
return this.$store.state.currDistrict;
},
set(value) {
this.$store.commit("setcurrDistrict", value);
},
},
},
並改寫模板
<div class="aside-menu">
<div class="wraps">
<label>
縣市:<select v-model="currCity">
<option>臺北市</option>
</select>
</label>
<label>
行政區:<select v-model="currDistrict">
<option>北投區</option>
</select>
</label>
</div>
接下來要處理option中的資料,從state回傳的資料如下,並不適合用v-for渲染,所以需要回到getters處理
▼ location:Array[21]
▼ 0:
▶ districts: Array[12]
id: "0000"
name: "臺北市"
sort: 1
▼ 1:
▶ districts: Array[29]
id: "0001"
name: "新北市"
sort: 2
行政區的部分有使用到optional chaining處理預設問題,關於此用法可以參考此連結
getters: {
cityList(state) {
//城市
return state.location.map((d) => d.name)
},
districtList(state) {
//行政區
return state.location.find((d) => d.name === state.currCity)?.districts || []
}
}
處理好資料後回到asideMenu.vue在computed新增mapGetters
computed: {
currCity: {...},
currDistrict: {...},
...mapGetters(["cityList", "districtList"]),
},
並再次改寫option模板的部分
<div class="aside-menu">
<div class="wraps">
<label>
縣市:<select v-model="currCity">
<option v-for="c in cityList" :key="c">{{ c }}</option>
</select>
</label>
<label>
行政區:<select v-model="currDistrict">
<option v-for="d in districtList" :key="d.id">{{ d.name }}</option>
</select>
</label>
</div>
這樣大致就完成囉!!! 最後,可以加入一個watch功能,讓使用者在選擇新的城市時,行政區會自動切換第一個行政區。districtList 一更新,this.currDistrict也會馬上切換成第一筆資料。
watch: {
districtList(v) {
const [arr] = v;
this.currDistrict = arr.name;
},
},
明天繼續完成剩下的部分...