[前端暴龙机,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 计算两个经纬度间的距离


<<:  Domain layer testing

>>:  No Time To Die在线看

Day-27 : Model 一对多

续上一篇,今天要来讲的是一对多 还记得吗?我们昨天说的 我们希望每一间商店可以贩售许多种商品,这就是...

D5 - 如何用 Google Apps Script 搭配 HTML 客制 Google 表单的回应信件?

来到了第五天,关於寄信可以进入比较进阶的操作。但一样先讲结论,如果你很急着用,可以直接使用这份 Ad...

Day 10 - 转换人生跑道

简介 casting 就是资料型态之间的转换。 例如把 A type 转换成 B type。 但是这...

Day26 玩家技巧、阶级与配对关系

我们在设计我们的竞技类配对系统时,有几个主要的中心想法分别是,想让玩家可以与,自己技巧相近的玩家游玩...

Day3-安装JDK

前言 提到JDK就不得不提到JRE了,先来介绍他们之间的差别吧。 JRE:Java Runtime ...