iT邦幫忙

2021 iThome 鐵人賽

DAY 22
0
Modern Web

前端暴龍機,Vue2.x 進化 Vue3系列 第 22

[前端暴龍機,Vue2.x 進化 Vue3 ] Day22. Vue 旅遊小幫手(完成)

終於來到 Vue2.x 介紹的尾聲了,藉由完成今天自己的旅遊小幫手範例一起收尾吧,GO~

加入 YouBike 的訊息進去

1. 根據選擇地區篩選 YouBike

一樣的,先將取回來的 YouBike 資訊寫成計算屬性,留下該符合該地區的資料,方便之後資料的處理

// computed 

renderBikes(){
  if(this.selectArea === '全景點'){
    return this.youbikes;
  }else{
    return this.youbikes.filter(ele => ele.sarea.match(this.selectArea))
  }
}

2. 計算景點離選中的飯店距離

當選擇其一飯店時,顯示該地區各景點與該飯店的距離,不過因為資料只提供經緯度,所以我們必需運用經緯度來換算成公里數或其它我們比較熟悉的距離單位,提供我們參考

// methods

distance(lat1, lon1, lat2, lon2) {
    if ((lat1 == lat2) && (lon1 == lon2)) {
        return 0;
    }
    else {
        let radlat1 = Math.PI * lat1/180;
        let radlat2 = Math.PI * lat2/180;
        let theta = lon1-lon2;
        let radtheta = Math.PI * theta/180;
        let dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
        if (dist > 1) {
            dist = 1;
        }
        dist = Math.acos(dist);
        dist = dist * 180/Math.PI;
        dist = dist * 60 * 1.1515;

        // 轉為公里
        dist = dist * 1.609344;
        return dist;
    }
},

這部份可以搜尋一下,關於經緯度計算距離,畢竟很少會去用到~

// computed 再寫一個 景點的篩選,用來負責最終結果渲染使用

nearAttractions(){
      
      // 飯店位置
      let lon1 = this.selectHotel.經度Lng;
      let lat1 = this.selectHotel.緯度Lat;
      
      //計算景點離飯店的距離
      this.renderAttractions.forEach(ele => {
        let lon2 = ele.Px;
        let lat2 = ele.Py;
        
        let tmp  = this.distance(lat1, lon1, lat2, lon2);
        ele.betweens = Math.floor(tmp * 100) / 100;
      })
      
      return this.renderAttractions;
    },

把計算完的距離加入資料中,後面顯示會使用到

3. 處理在選擇完飯店後的畫面

<div id="app">
// ... 略

<div class="planningCard"  v-if="hasPlanning">
    <h3 class="markText">{{ selectHotel.旅宿名稱 }}</h3>
    <span>{{ selectHotel.電話 }}</span>
    <p>{{ selectHotel.地址 }}</p>
  
    <div class="planningAttractions">
      <div class="partCard" v-for="item in nearAttractions" :key="item.Id">
        <span class="markText">{{ item.Name }}</span><span>[ 距離 : {{ item.betweens }} 公里]</span>
        <p>{{ item.Add }}</p>
        <div>
          <p class="traffic">交通方式</p>
          <p >{{ item.Travellinginfo }}</p>
        </div>
      </div>
    </div>
  </div>
</div>


<script>

// computed
hasPlanning(){
  
  // 判斷是否為空物件
  if(JSON.stringify(this.selectHotel) === '{}'){
    return false;
  }else{
    return true;
  }
},
</script>

顯示該飯店訊息、該地區景點、我們計算出來的景點與飯店距離、如果有提供景點的交通方式也一併顯示。

因為該顯示區塊為選擇飯店後才顯示,所以使用 v-if 搭配計算屬性( hasPlanning )回傳的方式控制

4. 加入每個景點附近 1 公里內的 YouBike 站點


// computed

nearAttractions(){

  // 飯店位置
  let lon1 = this.selectHotel.經度Lng;
  let lat1 = this.selectHotel.緯度Lat;

  //計算景點離飯店的距離
  this.renderAttractions.forEach(ele => {
    let lon2 = ele.Px;
    let lat2 = ele.Py;

    let tmp  = this.distance(lat1, lon1, lat2, lon2);
    ele.betweens = Math.floor(tmp * 100) / 100;
  })

  // 處理景點附近的 youbikes
  this.renderAttractions.forEach(ele => {
    let lon2 = ele.Px;
    let lat2 = ele.Py;
    ele.bikes = [];

    this.renderBikes.forEach( bike => {
      let lon3 = bike.lng;
      let lat3 = bike.lat;
      let tmp2  = this.distance(lat3, lon3, lat2, lon2);
      tmp2 = Math.floor(tmp2 * 100) / 100;
      if(tmp2 < 1 ) ele.bikes.push(bike);
    })
  })
  return this.renderAttractions;
},
<div id="app">
// ... 略

<div class="planningCard"  v-if="hasPlanning">
    <h3 class="markText">{{ selectHotel.旅宿名稱 }}</h3>
    <span>{{ selectHotel.電話 }}</span>
    <p>{{ selectHotel.地址 }}</p>
  
    <div class="planningAttractions">
      <div class="partCard" v-for="item in nearAttractions" :key="item.Id">
        <span class="markText">{{ item.Name }}</span><span>[ 距離 : {{ item.betweens }} 公里]</span>
        <p>{{ item.Add }}</p>
        <div>
          <p class="traffic">交通方式</p>
          <p >{{ item.Travellinginfo }}</p>
          
          <template v-if="item.bikes.length > 0">
            <p class="YouBikeTitle">[ 1 公里內 YouBike2.0 站點 ]</p>
            <p v-for="bike in item.bikes" :key="bike.sno">
              <i class="fas fa-biking"></i>
              {{ bike.sna }} ➟ <span class="addr">{{ bike.ar }}</span>
            </p>
          </template>
        </div>
      </div>
    </div>
  </div>
</div>

目前到這邊,已經可以規劃完成前往各景點的方式或者是另外提供的 YouBike 選項,這麼一來,在行程上的安排,看似更容易達成了呢,畢竟我們的交通方式更加多元的選擇了

5. 提供旅館附近的 YouBike 站點

東跑跑、西跑跑,都已經玩得這麼累的一天了,只想好好的回旅館休息,如果我們沒有租車或是旅館無提供車位,這時如果旅館附近有 YouBike 站點,那麼可能會讓我們輕鬆一些,所以我們也將旅館附近 200 公尺內的 YouBike 站點也提供上去吧~


// methods

setHotel(item){
  // 選擇飯店
  this.selectHotel = item;
  this.selectHotel.bikes = [];

  // 飯店位置
  let lon1 = item.經度Lng;
  let lat1 = item.緯度Lat;

  // 提供附近 youbike 站點
  this.renderBikes.forEach( bike => {
      let lon3 = bike.lng;
      let lat3 = bike.lat;
      let tmp2  = this.distance(lat1, lon1, lat3, lon3);
      tmp2 = Math.floor(tmp2 * 100) / 100;
      if(tmp2 < 0.3 ) this.selectHotel.bikes.push(bike);
  })

},

在選擇旅館的時候,一併做 Youbike 的資料篩選,篩選離飯店較近的站點,最後儲存起來

<div id="app">
// ... 略

<div class="planningCard"  v-if="hasPlanning">
    <h3 class="markText">{{ selectHotel.旅宿名稱 }}</h3>
    <span>{{ selectHotel.電話 }}</span>
    <p>{{ selectHotel.地址 }}</p>
    
    <!-- 加入旅館附近的 YouBike -->
    <template v-if="selectHotel.bikes.length > 0">
      <p class="YouBikeTitle">[ 200 公尺內 YouBike2.0 站點 ]</p>
      <p v-for="bike in selectHotel.bikes" :key="bike.sno">
        <i class="fas fa-biking"></i>
        {{ bike.sna }} ➟ <span class="addr">{{ bike.ar }}</span>
      </p>
    </template>
  
    <div class="planningAttractions">
      <div class="partCard" v-for="item in nearAttractions" :key="item.Id">
        <span class="markText">{{ item.Name }}</span><span>[ 距離 : {{ item.betweens }} 公里]</span>
        <p>{{ item.Add }}</p>
        <div>
          <p class="traffic">交通方式</p>
          <p >{{ item.Travellinginfo }}</p>
          
          <template v-if="item.bikes.length > 0">
            <p class="YouBikeTitle">[ 1 公里內 YouBike2.0 站點 ]</p>
            <p v-for="bike in item.bikes" :key="bike.sno">
              <i class="fas fa-biking"></i>
              {{ bike.sna }} ➟ <span class="addr">{{ bike.ar }}</span>
            </p>
          </template>
        </div>
      </div>
    </div>
  </div>
</div>

6. 讓使用者可以重新選擇

因為我們畫面的安排,所以選擇完之後不允許使用者繼續切換地區,所以必需做一個按鈕讓使用者回到最初的選單畫面,原理很簡單,只要把我們當初做為控制的條件還原即可~

<div id="app">

// ... 略

<div class="planningCard"  v-if="hasPlanning">
    
    <!-- 加入關閉按鈕 -->
    <i class="far fa-window-close" @click="reset"></i>
    
// ... 略

// methods

reset(){
  this.selectHotel = {};
  this.selectArea = '全景點';
}

完成 Vue自己的旅遊小幫手

最後終於完成啦~ 不過因為 API 的關係,我們只有以高雄的資料做範例,所以如果想作其他地區的,可以找找 Open Data 有沒有提供,接著自己動手改改看囉~

成果展示

https://ithelp.ithome.com.tw/upload/images/20210819/20120722tnwC0RV5H1.jpg
https://ithelp.ithome.com.tw/upload/images/20210819/20120722JKBqQhwq5i.jpg
https://ithelp.ithome.com.tw/upload/images/20210819/20120722BMMrutWMDj.jpg

https://ithelp.ithome.com.tw/upload/images/20210819/20120722Pl5GunSDXS.jpg
(如果要安排某些景點,可能就真的需要租車比較方便 /images/emoticon/emoticon56.gif)

補上展示範例

小結

  1. 複習 以 <template> 作為 v-if 的渲染分組
  2. computed 的資料處理

參考資料

DeTools , 使用 javascript 計算兩個經緯度間的距離


上一篇
[前端暴龍機,Vue2.x 進化 Vue3 ] Day21. 『小專題◕ᴥ◕』 Vue 旅遊小幫手(二)
下一篇
[前端暴龍機,Vue2.x 進化 Vue3 ] Day23.正式進化-Vue3 起手式
系列文
前端暴龍機,Vue2.x 進化 Vue330
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言