<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>

<body>
  <div class="container my-5">
    <h2 class="text-center">全台各地空污指數</h2>
    <div class="row">     
        <div id="app">
          <!--select 選擇的城市會存到 data 的 filter 陣列-->
          <select name="" id="" class="form-control mb-3" v-model="filter">
            <option value="">--- 請選擇城市 ---</option>
            <!--AJAX 取出城市,儲存在 data 陣列-->
            <option v-for="county in location" :value="county">{{ county }}</option>
          </select>
          <div>
            <h4>關注測站</h4>
            <div class="card-columns">              
              <!--元件置入的地方-->
              <card-component v-for="(item, index) in staredData" :card-data="item" :star="stared" @change-stared="setStar" :key="item.SiteId"></card-component>
            </div>
          </div>

          <hr>
          <h4>所有測站</h4>
          <div class="card-columns">            
            <!--元件置入的地方-->
            <card-component v-for="(item, index) in filterData" :card-data="item" :star="stared" @change-stared="setStar" :key="item.SiteId"></card-component>
          </div>
        </div>        
   
   <!--元件模板-->     
   <script type="text/x-template" id="cardTemplate">
     <div class="card" :class="airColor"><!--airColor 是 computed-->
       <div class="card-header"> {{ cardData.County}} - {{ cardData.SiteName }}
         <a href="#" class="float-right" @click.prevent="clickStar"><i :class="starStyle"></i></a><!--clickStar 是元件的 method, 用 $emit 傳到外層-->
       </div>
       <div class="card-body">
         <ul>
           <li>AQI 指數: {{ cardData.AQI }}</li>
           <li>PM2.5: {{cardData['PM 2.5']}}</li>
           <li>說明: {{ cardData.Status }}</li>
         </ul>
         {{ cardData.PublishTime }}
       </div>
     </div>
   </script>
</body>

</html>
/* 狀態對應表
'良好',
'status-aqi2' '普通',
'status-aqi3' '對敏感族群不健康',
'status-aqi4' '對所有族群不健康',
'status-aqi5' '非常不健康',
'status-aqi6' '危害' */

.status-aqi2 {
  background-color: #ffff00;
}
.status-aqi3 {
  background-color: #ff7e00;
}
.status-aqi4 {
  background-color: #ff0000;
}
.status-aqi5 {
  background-color: #8f3f97;
}
.status-aqi6 {
  background-color: #7e0023;
}
// 元件
Vue.component('card-component', {
  props: ['cardData', 'star'], // 接收陣列資料, 關注城市
  template: '#cardTemplate',
  methods: {
    // 把點擊星星的城市送到根實例的 method
    clickStar: function(){
      this.$emit('change-stared', this.cardData.SiteName);
    }
  },
  computed: {
    // 判斷汙染等級顏色
    airColor: function(){
      let status = this.cardData.Status;
      switch (status) {
        case '普通':
          return 'status-aqi2';
          break;
        case '對敏感族群不健康':
          return 'status-aqi3';
          break;
        case '對所有族群不健康':
          return 'status-aqi4';
          break;
        case '非常不健康':
          return 'status-aqi5';
          break;
        case '危害':
          return 'status-aqi6';
          break;
      }
    },
    // 點選的城市不存在關注中時,星星為實心;已存在時,空心
    starStyle: function(){
      return (this.star.indexOf(this.cardData.SiteName) == -1) ? 'far fa-star' : 'fas fa-star';
    }
  },
});

// Vue 根實例
var app = new Vue({
  el: "#app",
  data: {
    data: [],
    location: [],
    stared: JSON.parse(localStorage.getItem('staredSites')) || [], //從 setStared 傳入,會再傳到元件去用
    filter: ""
  },
  created: function(){
    this.getData();
  },
  methods: {
    getData: function() {
      const vm = this;
      const api = "https://cors-anywhere.herokuapp.com/http://opendata2.epa.gov.tw/AQI.json";
      // 使用 jQuery ajax
      $.get(api).then(function(response) {
        vm.data = response;
        
        // 取出所有測站的所在縣市
        const siteCounty = [];
        vm.data.forEach(item => siteCounty.push(item.County));
        // 篩選不重複的縣市
        vm.location = siteCounty.filter((element, index, array) => array.indexOf(element) === index);
        // 陣列中第一個符合條件的索引等於本身索引的元素才會被回傳        
      });
    },
    
    // 接收 $emit 傳遞被點擊的城市名稱,判斷是否存在 stared 陣列中
    setStar: function(siteName) {
      if(this.stared.indexOf(siteName) == -1){
        // 不存在則將城市加入陣列
        this.stared.push(siteName);
      } else {
        // 存在則將城市移除
        this.stared.splice(this.stared.indexOf(siteName),1);
      }
      // 儲存至 localStorage
      localStorage.setItem('staredSites', JSON.stringify(this.stared));
    },
  },
  computed: {
    filterData: function(){
      if (this.filter == ''){
        return this.data;
      }
      let filtered = this.data.filter((item) => {
        return item.County == this.filter; // filter 的值是 select 選擇的城市
      });
      return filtered;
    },
    staredData: function(){
      const vm = this;
      let filtered = vm.data.filter((item) => {
        return vm.stared.indexOf(item.SiteName) != -1;
      });
      return filtered;
    }
  }
      
});

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css
  2. https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js