專題實作

計分板

<!DOCTYPE html>
<html>
<head>
  <!--1.調整手機瀏覽器的螢幕解析度 -->
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">

  <!-- 2.安裝字體及圖標 -->
  <script src="https://kit.fontawesome.com/1adb36782c.js" crossorigin="anonymous"></script>

  <!-- 3.安裝 Vue.js  -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>

  <style>
    .panel {
      width: 49%;
      height: 50vh;
      font-size: 25vh;
      float: left;
      border: 1px solid black;
      text-align: center;
      position: relative;
      user-select: none;
    }

    .fa-minus {
      font-size: 30px;
      position: absolute;     
      bottom: 10px;
      left: 10px;
    }

    .fa-redo {
      font-size: 30px;
      position: absolute;
      bottom: 10px;
      right: 10px;
    }
  </style>

</head>
<body>
  <div id="app">
    <div class="panel" @click="plus(0)">
      <div v-text="count0"></div>

      <i class="fas fa-minus" 
         @click.stop="minus(0)"></i>

      <i class="fas fa-redo" 
         @click.stop="reset(0)"></i>
    </div>

    <div class="panel" @click="plus(1)">
      <div v-text="count1"></div>

      <i class="fas fa-minus" 
         @click.stop="minus(1)"></i>

      <i class="fas fa-redo" 
         @click.stop="reset(1)"></i>
    </div>
  </div>

  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        count0: 0,
        count1: 0
      },
      methods: {
        reset: function (i) {
          i == 0 ? this.count0=0
                 :this.count1=0;
        },
        plus: function (i) {
          i == 0 ? this.count0++
                 : this.count1++;
        },
        minus: function (i) {
          i == 0 ? this.count0--
                 : this.count1--;

          if (this.count0 < 0)
            this.count0 = 0;

          if (this.count1 < 0)
            this.count1 = 0;
        }
      }
    })
  </script>
</body>
</html>

自訂元件 Components

自訂元件樣板

<!DOCTYPE html>
<html>
<head>
  <!-- 採用 Unicode utf-8 編碼 -->
  <meta charset="utf-8" />

  <!-- 設定寬度自動符合手機螢幕寬度 -->
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- VUE 開發版 -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>

  <!-- 安裝字體及圖標 -->
  <script src="https://kit.fontawesome.com/1adb36782c.js" crossorigin="anonymous"></script>
</head>
<body>
  <!--定義模板-->
  <template id="xxx">
  </template>

  <!--定義元件-->
  <script>
    const xxx = {
      template: '#xxx',
      data: function () {
        return { }
      },
      methods: { }
    };
  </script>

  <!-- VUE DOM 區塊,應用程式ID為 app -->
  <div id="app">
    <xxx></xxx>
  </div>

  <script>
    // 建立 Vue 物件
    var vm = new Vue({
      el: '#app',
      components: { xxx }
    });
  </script>
</body>
</html>

將計數器改爲自訂元件

<style>
  .panel {
    width: 49%;
    height: 50vh;
    font-size: 25vh;
    float: left;
    border: 1px solid black;
    text-align: center;
    position: relative;
    user-select: none;
  }

  .fa-minus {
    font-size: 30px;
    position: absolute;
    bottom: 10px;
    left: 10px;
  }

  .fa-redo {
    font-size: 30px;
    position: absolute;
    bottom: 10px;
    right: 10px;
  }
</style>

<!--定義模板-->
<template id="counter">
  <div class="panel" @click="plus()">
    <div v-text="count"></div>

    <i class="fas fa-minus"
        @click.stop="minus()"></i>

    <i class="fas fa-redo"
        @click.stop="reset()"></i>
  </div>
</template>

<script>
  // 定義區域性元件
  const counter = {
    props: ['count'],
    //data: function () {
    //  return {
    //    count: 0
    //  }
    //},
    methods: {
      reset: function () {
        this.count = 0;
      },
      plus: function () {
        this.count++;
      },
      minus: function () {
        this.count--;

        if (this.count < 0)
          this.count = 0;
      }
    },
    template: '#counter'
  };
</script>

<div id="app">
  <counter v-for="i in [0,1,2,3]" :count="i"></counter>
</div>

<script>
  var vm = new Vue({
    el: '#app',
    components: {
      counter
    }
  });
</script>

計數器共享資料 (Vuex)

  • 要在 Vue 元件間共享資料,可以透過 Vuex 的狀態管理機制來達成

Vuex 基本架構

  • val 為共享的狀態名稱
<script>
  // 定義區域性元件
  const xxx = {
    template: '#xxx',

    // 將 state map 到 Vue 的 computed
    computed: {
      ...Vuex.mapState(['val'])
    },

    // 將 mutations map 到 Vue 的 methods
    methods: {
      ...Vuex.mapMutations(['setVal']),
    }
  };
</script>

<script>
  // 定義 Vuex.Store
  const store = new Vuex.Store({
    state: {
      val: 0
    },
    mutations: {
      setVal(state, val) {
        state.val = val;
      }
    }
  });

  var vm = new Vue({
    store,
    el: '#app',
  });
</script>

在計數器間共享資料狀態

<!DOCTYPE html>
<html>
<head>
  <!-- 採用 Unicode utf-8 編碼 -->
  <meta charset="utf-8" />

  <!--調整手機瀏覽器的螢幕解析度 -->
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">

  <!-- 安裝字體及圖標 -->
  <script src="https://kit.fontawesome.com/1adb36782c.js" crossorigin="anonymous"></script>

  <!-- 裝 Vue.js  -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>

  <!-- 安裝 Vuex -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.0.1/vuex.js"></script>
</head>
<body>
<style>
  .panel {
    width: 49%;
    height: 50vh;
    font-size: 25vh;
    float: left;
    border: 1px solid black;
    text-align: center;
    position: relative;
    user-select: none;
  }

  .fas {
    font-size: 30px;
    position: absolute;
  }

  .fa-minus {
    bottom: 10px;
    left: 10px;
  }

  .fa-redo {
    bottom: 10px;
    right: 10px;
  }
</style>

<!--定義模板-->
<template id="counter">
  <div class="panel" @click="plus()">
    <div v-text="count"></div>

    <i class="fas fa-minus"
        @click.stop="minus()"></i>

    <i class="fas fa-redo"
        @click.stop="reset()"></i>
  </div>
</template>

<script>
  // 定義區域性元件
  const counter = {
    template: '#counter',

    // 將 state map 到 Vue 的 computed
    computed: {
      ...Vuex.mapState(['count'])
    },

    // 將 mutations map 到 Vue 的 methods
    methods: {
      ...Vuex.mapMutations(['reset','plus','minus']),
    }
  };
</script>

<div id="app">
  <counter v-for="i in [0,1,2,3]"></counter>
</div>

<script>
  const store = new Vuex.Store({
    state: {
      count: 0
    },
    // 同步方式更新
    mutations: {
      reset(state) {
        state.count = 0;
      },
      plus(state) {
        state.count++;
      },
      minus(state) {
        state.count--;

        if (state.count < 0)
          state.count = 0;
      }
    }
  });

  var vm = new Vue({
    el: '#app',
    store,
    components: {
      counter
    }
  });
</script>
</body>
</html>

計時器

<!DOCTYPE html>
<html>
<head>
  <!--1.調整手機瀏覽器的螢幕解析度 -->
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">

  <!-- 2.安裝字體及圖標 -->
  <script src="https://kit.fontawesome.com/1adb36782c.js" crossorigin="anonymous"></script>

  <!-- 3.安裝 Vue.js  -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>

  <style>
    .panel {
      width: 49%;
      height: 50vh;
      font-size: 25vh;
      float: left;
      border: 1px solid black;
      text-align: center;
      position: relative;
      user-select: none;
    }

    .fa-minus {
      font-size: 30px;
      position: absolute;
      bottom: 10px;
      left: 10px;
    }

    .fa-redo {
      font-size: 30px;
      position: absolute;
      bottom: 10px;
      right: 10px;
    }

    .fa-play, .fa-stop {
      font-size: 30px;
      position: absolute;
      top: 10px;
      right: 10px;
    }

  </style>

</head>
<body>
  <div id="app">
    <div class="panel" @click="plus(0)">
      <div v-text="count0"></div>
      <i class="fas fa-minus" 
         @click.stop="minus(0)"></i>
      <i class="fas fa-redo" 
         @click.stop="reset(0)"></i>
    </div>

    <div class="panel" @click="plus(1)">
      <!-- 開始計時 -->
      <i v-if="!playMode" 
         class="fas fa-play" 
         @click.stop="play()"></i>

      <!-- 暫停計時 -->
      <i v-else 
         class="fas fa-stop" 
         @click.stop="pause()"></i>

      <div v-text="count1"></div>

      <i class="fas fa-minus" 
         @click.stop="minus(1)"></i>

      <i class="fas fa-redo" 
         @click.stop="reset(1)"></i>
    </div>
  </div>

  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        count0: 1,
        count1: 0,
        playMode: false,
        time: null
      },
      methods: {
        reset: function (i) {
          i == 0 ? this.count0=0
                 : this.count1=0;
        },
        plus: function (i) {
          i == 0 ? this.count0++
                 : this.count1++;
        },
        minus: function (i) {
          i == 0 ? this.count0--
                 : this.count1--;

          if (this.count0 < 0)
            this.count0 = 0;

          if (this.count1 < 0)
            this.count1 = 0;
        },
        play() {
          this.playMode = true;
          let my = this
          this.time = setInterval(
            function () {
              my.count1--;

              if (my.count1 < 0) {
                my.count0--;
                my.count1 = 59;

                if (my.count0 < 0) {
                  my.count0 = 0;
                  my.count1 = 0;
                  my.playMode = false;
                  clearInterval(my.time);
                }
              }
            }, 100);
        },
        pause() {
          this.playMode = false;
          clearInterval(this.time)
        }
      }
    })
  </script>
</body>
</html>

問題與解答

問題,答案,顯示
球場兩端的短線稱為,Ans:端<底>線,false
甲運球,撞倒站著不動的乙,請問此犯規動作為,Ans:帶球撞人,false
運球上籃,走幾步為走步違例,Ans:3步,false
請問NBA籃球規則中規定個人犯規幾次即畢業離場,Ans:6次,false
籃球球員在擲界外球時,防守球員應距離,Ans:1公尺,false
下列何者非籃球規則之死球情形?,Ans:投球合法中籃後,false
籃球比賽球員技術犯規時,應記,Ans:T,false
籃球籃下3秒的違例,從何算起?,Ans:進攻隊伍任何一個站在籃架限,false
籃球的Timeout指,Ans:暫停,false
  • 將資料以items 變數表示,並儲存為 data.js 檔案
var items=[
  {
    "問題": "球場兩端的短線稱為",
    "答案": "<ul><li>Ans:端<底>線</li></ul>",
    "顯示": false
  },
  {
    "問題": "甲運球,撞倒站著不動的乙,請問此犯規動作為",
    "答案": "Ans:帶球撞人",
    "顯示": false
  },
  {
    "問題": "運球上籃,走幾步為走步違例",
    "答案": "Ans:3步",
    "顯示": false
  },
  {
    "問題": "請問NBA籃球規則中規定個人犯規幾次即畢業離場",
    "答案": "Ans:6次",
    "顯示": false
  },
  {
    "問題": "籃球球員在擲界外球時,防守球員應距離",
    "答案": "Ans:1公尺",
    "顯示": false
  }
]
<!DOCTYPE html>
<html>
<head>
    <!-- 採用 Unicode utf-8 編碼 -->
    <meta charset="utf-8" />

    <!-- 設定寬度自動符合手機螢幕寬度 -->
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- VUE 開發版 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
</head>
<body>
    <div id="app">
    </div>

    <script>
        var vm = new Vue({
            el: '#app',
            data: { }
        });
    </script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
  <!-- 採用 Unicode utf-8 編碼 -->
  <meta charset="utf-8" />

  <!--調整手機瀏覽器的螢幕解析度 -->
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- 安裝 Vue.js  -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
</head>
<body>
  <style>
    .item {
      border-bottom: solid 1px lightgray;
    }

      .item h3 {
        cursor: pointer;
      }
  </style>

  <div id="app">
    <div v-for="(item, i) in items" class="item"
         @click="itemOnClick(i)">
      <!-- 問題 -->
      <h3>{{item['問題']}}</h3>

      <!-- 解答 -->
      <p v-show="item['顯示']"
         v-html="item['答案']">
      </p>
    </div>
  </div>

  <script src="data.js"></script>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        preIndex: 0,
        items: null
      },
      mounted() {
        // 載入資料
        this.items = items;
      },
      methods: {
        // 展開單項
        itemOnClick: function (index) {
          var items = this.items;
          items[this.preIndex]['顯示'] = false;
          items[index]['顯示'] = !items[index]['顯示'];
          this.preIndex = index;
        }
      }
    })
  </script>
</body>
</html>

採用 Vuetify 美化

  • v-expansion-panels 擴展元件
<!DOCTYPE html>
<html>
<head>
  <!-- 採用 Unicode utf-8 編碼 -->
  <meta charset="utf-8" />

  <!--調整手機瀏覽器的螢幕解析度 -->
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- 安裝 Vue.js  -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>

  <!-- 安裝 Vuetify.js -->
  <link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
  <script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>
</head>
<body>
  <div id="app">
    <v-app id="inspire">
      <v-expansion-panels>
        <v-expansion-panel>

          <!-- header -->
          <v-expansion-panel-header>
            抬頭
          </v-expansion-panel-header>

          <!-- content -->
          <v-expansion-panel-content>
            内容
          </v-expansion-panel-content>

        </v-expansion-panel>
      </v-expansion-panels>
    </v-app>
  </div>

  <script>
    var vm = new Vue({
      el: '#app',
      vuetify: new Vuetify()
    })
  </script>
</body>
</html>
  • 將 JSON 格式資料導入
<!DOCTYPE html>
<html>
<head>
  <!-- 採用 Unicode utf-8 編碼 -->
  <meta charset="utf-8" />

  <!--調整手機瀏覽器的螢幕解析度 -->
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- 安裝 Vue.js  -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>

  <!-- 安裝 Vuetify.js -->
  <link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
  <script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>
</head>
<body>
  <div id="app">
    <v-app id="inspire">
      <v-expansion-panels>
        <v-expansion-panel v-for="(item, i) in items">

          <!-- header -->
          <v-expansion-panel-header>
            <h3>{{item['問題']}}</h3>
          </v-expansion-panel-header>

          <!-- content -->
          <v-expansion-panel-content>
            <div v-html="item['答案']"></div>
          </v-expansion-panel-content>

        </v-expansion-panel>
      </v-expansion-panels>
    </v-app>
  </div>

  <script src="data.js"></script>
  <script>
    var vm = new Vue({
      el: '#app',
      vuetify: new Vuetify(),
      data: {
        items: null
      },
      mounted () {
        this.items = items;
      }
    })
  </script>
</body>
</html>

測驗

  • 將資料以items 變數表示,並儲存為 data.js 檔案
var items=[
  {
    "題目": "在CSS樣式中,控制文字字體大小的屬性為?",
    "配分": 10,
    "解答": 1,
    "作答": "",
    "選項": [ "font-weight", "font-size", "font-color", "font-height" ]
  },
  {
    "題目": "請問網頁中圖片的標籤為?",
    "配分": 10,
    "解答": 1,
    "作答": "",
    "選項": [ "div", "img", "p", "br" ]
  },
  {
    "題目": "請問網頁中的段落標籤為?",
    "配分": 10,
    "解答": 0,
    "作答": "",
    "選項": [ "p", "br", "img", "head" ]
  },
  {
    "題目": "請問網頁表格的標籤為?",
    "配分": 10,
    "解答": 0,
    "作答": "",
    "選項": [ "<table>", "<div>", "<form>", "<input>" ]
  }
]
<!DOCTYPE html>
<html>
<head>
  <!-- 採用 Unicode utf-8 編碼 -->
  <meta charset="utf-8" />

  <!--調整手機瀏覽器的螢幕解析度 -->
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- 安裝 Vue.js  -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>

  <style>
    .anwser {
      background-color: lightblue;
    }

    .error {
      background-color: lightcoral;
    }
  </style>
</head>
<body>
<div id="app">
  <div v-if="!finish">
    <div v-for="item in items">
      <h4>{{item['題目']}}</h4>
      <div v-for="(option, i) in item['選項']">
        <label>
          <input type="radio" :value="i"
                  v-model="item['作答']">
          {{option}}
        </label>
      </div>
    </div>
  </div>
  <button v-if="!finish" @click="finish=true">
    交卷
  </button>

  <!-- 解答及計分 -->
  <div v-if="finish">
    <p>總分:{{computScore}}<p>
      <div v-for="item in items">
        <h4>{{item['題目']}}</h4>

        <div v-for="(option, i) in item['選項']">
          <!-- 作答的選項 -->
          <label v-if="item['作答']===i" 
               class="anwser"
               :class="{error: i !== item['解答']}">
            <input type="radio" :value="i" disabled
                    v-model="item['解答']">
            {{option}}
          </label>

          <!-- 其他選項 -->
          <label v-else>
            <input type="radio" :value="i" disabled
                    v-model="item['解答']">
            {{option}}
          </label>
        </div>
      </div>
  </div>
</div>

<script src="data.js"></script>
<script>  
  var vm = new Vue({
    el: '#app',
    data: {
      finish: false,
      items: null
    },
    mounted() {
      this.items = items;
    },
    computed: {
      computScore: function () {
        var score = 0;
        this.items.forEach(function (item){
          if (item['作答']===item['解答'])
            score += item['配分'];
        });
        return score;
      }
    }
  })
</script>
</body>
</html>

新北市 YouBike

<?php
header('Access-Control-Allow-Origin: *');
header('content-type:text/html;charset=utf-8');
?>

axios

  • Axios 是用於瀏覽器的 HTTP Client,可以方便開發者使用 AJAX 的套件。
<!DOCTYPE html>
<html>
<head>
  <!--調整手機瀏覽器的螢幕解析度 -->
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- 安裝 Vue.js  -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>

  <!-- axios 套件-->
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
  <div id="app">
    <!-- 顯示 Json 資料 -->
  </div>

  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        url: "http://ewin.tw/myApp/sample/UBike/",
        items: "資料讀取中..."
      },
      mounted() {
        //透過 axios 讀取網路上的json資料
        axios.get(this.url)
          .then(function (response) {
          })
          .catch(function (error) {
          })
      }
    });
  </script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
  <!--調整手機瀏覽器的螢幕解析度 -->
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- 安裝 Vue.js  -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>

  <!-- axios 套件-->
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>  
<body>

  <div id="app">
    <!-- 行政區篩選 -->
    <select v-model="area">
      <option v-for="sarea in sareas">
        {{sarea}}
      </option>
    </select>

    <ul>
      <!-- 顯示 YouBike 站名及經緯度 -->
      <li v-for="item in items"
          v-if="item.sarea==area">
        <div>{{item.sarea+'-'+item.sna}}</div>

        <!--地址-->
        <div>{{item.ar}}</div>
      </li>
    </ul>
  </div>

  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        url: "http://ewin.tw/myApp/sample/UBike/",
        items: {},
        area: '八里區',
        sareas: []
      },
      mounted() {
        var _this = this;

        //透過 axios 讀取網路上的json資料
        axios.get(this.url)
          .then(function (response) {
            _this.items=response.data.result.records;

            // 篩選出新北市的區名
            _this.sareas = _this.items.map(function (item) {
              return item.sarea;
            });

            // 去除重複
            // vm.sareas = [...new Set(vm.sareas)];
            _this.sareas = Array.from(new Set(_this.sareas))
          })
          .catch(function (error) {
            console.log(error);
          })
        }
      });
  </script>
</body>
</html>

採用 Vuetify 美化

  • v-card 元件
  • v-select 元件
  • v-list 元件
<!-- 安裝 Vuetify.js -->
<link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>

<div id="app">
<v-app>
  <v-card width="100%" height="98%">

    <!--下拉式清單-->
    <v-select :items="" label=""></v-select>
    <v-divider></v-divider>

    <!--清單-->
    <v-list>
      <div>
          <v-list-item>
            <v-list-item-content>
              <v-list-item-title>
              </v-list-item-title>

              <v-list-item-subtitle>
              </v-list-item-subtitle>
            </v-list-item-content>
          </v-list-item>
      </div>
    </v-list>
  </v-card>
</v-app>
</div>
<!DOCTYPE html>
<html>
<head>
  <!--1.調整手機瀏覽器的螢幕解析度 -->
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- 安裝 Vue.js  -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>

  <!-- axios 套件-->
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

  <!-- 安裝 Vuetify.js -->
  <link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
  <script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>
</head>
<body>
  <div id="app">
    <v-app>
      <v-card width="100%" height="98%">
        <!-- 行政區篩選 -->
        <v-select :items="sareas" label="行政區"
                  v-model="area">
        </v-select>
        <v-divider></v-divider>

        <!-- 清單 -->
        <v-list>
          <div v-for="item in items"
                    v-if="item.sarea==area">
              <v-list-item>
                <v-list-item-content>
                  <!--站名-->
                  <v-list-item-title>
                    {{item.sna}}
                  </v-list-item-title>

                  <!--地址-->
                  <v-list-item-subtitle>
                    {{item.ar}}
                  </v-list-item-subtitle>
                </v-list-item-content>
              </v-list-item>

              <!--分隔線-->
              <v-divider></v-divider>
          </div>
        </v-list>
      </v-card>
    </v-app>
  </div>

  <script>
    var vm = new Vue({
      el: '#app',
      vuetify: new Vuetify(),
      data: {
        url: "http://ewin.tw/myApp/sample/UBike/",
        items: {},
        area: '八里區',
        sareas: []
      },
      mounted() {
        var _this = this;

        //透過 axios 讀取網路上的json資料
        axios.get(this.url)
          .then(function (response) {
            _this.items=response.data.result.records;

            // 篩選出新北市的區名
            _this.sareas = _this.items.map(function (item) {
              return item.sarea;
            });

            // 去除重複
            // vm.sareas = [...new Set(vm.sareas)];
            _this.sareas = Array.from(new Set(_this.sareas))
          })
          .catch(function (error) {
            console.log(error);
          })
       }
    })
  </script>
</body>
</html>

採用 Vuetify Expansion-panel 元件美化

  • v-select 元件
  • v-expansion-panel 元件
<!DOCTYPE html>
<html>
<head>
  <!--調整手機瀏覽器的螢幕解析度 -->
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- 安裝 Vue.js  -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>

  <!-- axios 套件-->
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

  <!-- 安裝 Vuetify.js -->
  <link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
  <script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>
</head>
<body>
  <div id="app">
    <v-app id="inspire">
      <v-expansion-panels hover inset>
        <!-- 行政區篩選 -->
        <v-select :items="sareas" v-model="area"
                  label="行政區">
        </v-select>

        <v-expansion-panel v-for="item in items" v-if="item.sarea==area">
          <v-expansion-panel-header>
            <h3>{{item.sarea+'-'+item.sna}}</h3>
          </v-expansion-panel-header>
          <v-expansion-panel-content>
            站點代號: <span v-text="item.sno"></span><br>
            場站總停車格: <span v-text="item.tot"></span><br>
            可借車位數: <span v-text="item.sbi"></span><br>
            可還空位數: <span v-text="item.bemp"></span><br>
            中文地址: <span v-text="item.ar"></span><br>
            經緯度: <a :href="'https://www.google.com/search?q='+item.lat+'%2C'+item.lng+'&oq='+item.lat+'%2C'+item.lng"
               target="_blank" style="text-decoration:none">
               {{item.lat+','+item.lng}}
            </a>
          </v-expansion-panel-content>
        </v-expansion-panel>
      </v-expansion-panels>
    </v-app>
  </div>

  <script>
    var vm = new Vue({
      el: '#app',
      vuetify: new Vuetify(),
      data: {
        url: "http://ewin.tw/myApp/sample/UBike/",
        items: {},
        area: '八里區',
        sareas: []
      },
      mounted() {
        var _this = this;

        //透過 axios 讀取網路上的json資料
        axios.get(this.url)
          .then(function (response) {
            _this.items=response.data.result.records;

            // 篩選出新北市的區名
            _this.sareas = _this.items.map(function (item) {
              return item.sarea;
            });

            // 去除重複
            // vm.sareas = [...new Set(vm.sareas)];
            _this.sareas = Array.from(new Set(_this.sareas))
          })
          .catch(function (error) {
            console.log(error);
          })
       }
    })
  </script>
</body>
</html>

路由

基本路由框架

<!DOCTYPE html>
<html>
<head>
  <!--調整手機瀏覽器的螢幕解析度 -->
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- Vue.js  -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>

  <!-- vue-router 路由套件-->
  <script src="https://cdn.jsdelivr.net/npm/vue-router"></script>

  <!-- 安裝字體及圖標 -->
  <script src="https://kit.fontawesome.com/1adb36782c.js" crossorigin="anonymous"></script>
</head>
<body>
  <!--首頁 -->
  <style></style>

  <template id="home">
    <div>首頁内容</div>
  </template>

  <script>
    const home = {
      template: '#home',
      data() {
        return {
        }
      },
      mounted() {
      },
      methods: {
      }
    };
  </script>

  <!-- about -->
  <style></style>

  <template id="about">
    <div>關於</div>
  </template>

  <script>
    const about = {
      template: '#about',
      data: function () {
        return {
        }
      }
    };
  </script>

  <!-- 應用程式 -->
  <div id="app">
    <div>
      <!-- 路由鏈結 -->
      <router-link to="/">
        首頁
      </router-link>

      <router-link to="/about">
        關於
      </router-link>
    </div>

    <!-- 結果在此顯示 -->
    <router-view></router-view>
  </div>

  <script>
    const router = new VueRouter({
      routes: [
        {
          path: '/',
          component: home
        },
        {
          path: '/about',
          component: about
        }
      ]
    });

    var vm = new Vue({
      router,
      el: '#app',
      data: {
      },
      methods: {
      }
    })
  </script>
</body>
</html>

Vuetify 的主選單

<!DOCTYPE html>
<html>
<head>
  <!-- 採用 Unicode utf-8 編碼 -->
  <meta charset="utf-8" />

  <!-- 調整手機瀏覽器的螢幕解析度 -->
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- 安裝 Vue、vue-router -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
  <script src="https://cdn.jsdelivr.net/npm/vue-router"></script>

  <!-- 安裝 Vuetify -->
  <link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
  <script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>

  <!-- 安裝字體及圖標 -->
  <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
  <link href="https://cdn.jsdelivr.net/npm/@mdi/font@4.x/css/materialdesignicons.min.css" rel="stylesheet">
  <script src="https://use.fontawesome.com/releases/v5.0.6/js/all.js"></script>
</head>
<body>
<div id="app" v-cloak>
  <v-app>
    <v-card height="100vh" width="100%" 
          class="mx-auto overflow-hidden" 
          @click.stop="drawer=false">

      <!-- Draw Menu -->
      <v-navigation-drawer v-model="drawer" 
             @click.stop="drawer=false"                       
            absolute temporary dark>
        <!-- 選項 -->
        <v-list nav>
          <v-list-item>
            <v-list-item-icon>
              <v-icon>fas fa-home</v-icon>
            </v-list-item-icon>
            首頁
          </v-list-item>

          <!-- 請練習補上 info-circle 圖像 -->
          <v-list-item>
            關於
          </v-list-item>
        </v-list>
      </v-navigation-drawer>

      <!-- 工具列 -->
      <v-toolbar dark>
        <!--主選單按鈕-->
        <v-app-bar-nav-icon 
            @click.stop="drawer=!drawer">
        </v-app-bar-nav-icon>

        <!--工具列標題-->
        <v-toolbar-title>
          樂活學程式
        </v-toolbar-title>
      </v-toolbar>
    </v-card>
  </v-app>
</div>

<script>
  var vm = new Vue({
    el: '#app',
    vuetify: new Vuetify(),
    data: {
      drawer: false
    }
  });
</script>
</body>
</html>

整合 Vuetify 主選單與路由

<!DOCTYPE html>
<html>
<head>
  <!-- 採用 Unicode utf-8 編碼 -->
  <meta charset="utf-8" />

  <!-- 調整手機瀏覽器的螢幕解析度 -->
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- 安裝 Vue、vue-router -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
  <script src="https://cdn.jsdelivr.net/npm/vue-router"></script>

  <!-- 安裝 Vuetify -->
  <link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
  <script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>

  <!-- 安裝字體及圖標 -->
  <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
  <link href="https://cdn.jsdelivr.net/npm/@mdi/font@4.x/css/materialdesignicons.min.css" rel="stylesheet">
  <script src="https://use.fontawesome.com/releases/v5.0.6/js/all.js"></script>
</head>
<body>
  <!--首頁 -->
  <style></style>

  <template id="home">
    <div>首頁内容</div>
  </template>

  <script>
    const home = {
      template: '#home',
      data() {
        return {
        }
      },
      mounted() {
      },
      methods: {
      }
    };
  </script>

  <!-- about -->
  <style></style>

  <template id="about">
    <div>關於</div>
  </template>

  <script>
    const about = {
      template: '#about',
      data: function () {
        return {
        }
      }
    };
  </script>

  <div id="app" v-cloak>
    <v-app>
      <v-card height="100vh" width="100%"
              class="mx-auto overflow-hidden"
              @click.stop="drawer=false">
        <!-- Draw Menu -->
        <v-navigation-drawer v-model="drawer"
                     @click.stop="drawer=false"
                     absolute temporary dark>
          <v-list nav>
            <v-list-item to="/">
                <v-list-item-icon>
                  <v-icon>fas fa-home</v-icon>
                </v-list-item-icon>
                首頁
            </v-list-item>

            <v-list-item to="/about">
                <v-list-item-icon>
                  <v-icon>fas fa-info-circle</v-icon>
                </v-list-item-icon>
                關於
            </v-list-item>
          </v-list>
        </v-navigation-drawer>

        <!-- 工具列 -->
        <v-toolbar dark>
          <!--主選單按鈕-->
          <v-app-bar-nav-icon @click.stop="drawer=!drawer">
          </v-app-bar-nav-icon>

          <!--工具列標題-->
          <v-toolbar-title>
            樂活學程式
          </v-toolbar-title>
        </v-toolbar>

        <!-- 結果在此顯示 -->
        <router-view></router-view>
      </v-card>
    </v-app>
  </div>

  <script>
    const router = new VueRouter({
      routes: [
        {
          path: '/',
          component: home
        },
        {
          path: '/about',
          component: about
        }
      ]
    });

    var vm = new Vue({
      router,
      el: '#app',
      vuetify: new Vuetify(),
      data: {
        drawer: false
      }
    });
  </script>
</body>
</html>

導入計分板範例

<!DOCTYPE html>
<html>
<head>
  <!-- 採用 Unicode utf-8 編碼 -->
  <meta charset="utf-8" />

  <!-- 調整手機瀏覽器的螢幕解析度 -->
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- 安裝 Vue、vue-router -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
  <script src="https://cdn.jsdelivr.net/npm/vue-router"></script>

  <!-- 安裝 Vuetify -->
  <link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
  <script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>

  <!-- 安裝字體及圖標 -->
  <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
  <link href="https://cdn.jsdelivr.net/npm/@mdi/font@4.x/css/materialdesignicons.min.css" rel="stylesheet">
  <script src="https://use.fontawesome.com/releases/v5.0.6/js/all.js"></script>
</head>
<body>
  <!--首頁 -->
  <style>
    .panel {
      width: 49%;
      height: 50vh;
      font-size: 25vh;
      float: left;
      border: 1px solid black;
      text-align: center;
      position: relative;
      user-select: none;
    }

    .fa-minus {
      font-size: 30px !important;
      position: absolute !important;
      bottom: 10px;
      left: 10px;
    }

    .fa-redo {
      font-size: 30px !important;
      position: absolute !important;
      bottom: 10px;
      right: 10px;
    }
  </style>

  <template id="home">
    <div>
      <div class="panel" @click.stop="plus(0)">
        <div v-text="count0"></div>

        <v-icon class="fa-minus" @click.stop="minus(0)">
          mdi-minus
        </v-icon>

        <v-icon class="fa-redo" @click.stop="reset(0)">
          mdi-refresh
        </v-icon>
      </div>

      <div class="panel">
        <div v-text="count1" @click.stop="plus(1)"></div>

        <v-icon class="fa-minus" @click.stop="minus(1)">
          mdi-minus
        </v-icon>

        <v-icon class="fa-redo" @click.stop="reset(1)">
          mdi-refresh
        </v-icon>
      </div>
    </div>
  </template>

  <script>
    const home = {
      template: '#home',
      data() {
        return {
          count0: 0,
          count1: 0
        }
      },
      methods: {
        reset(i) {
          i == 0 ? this.count0 = 0
            : this.count1 = 0;
        },
        plus(i) {
          i == 0 ? this.count0++
            : this.count1++;
        },
        minus(i) {
          i == 0 ? this.count0--
            : this.count1--;

          if (this.count0 < 0)
            this.count0 = 0;

          if (this.count1 < 0)
            this.count1 = 0;
        }
      }
    };
  </script>

  <!-- about -->
  <style></style>

  <template id="about">
    <div>關於</div>
  </template>

  <script>
    const about = {
      template: '#about',
      data: function () {
        return {
        }
      }
    };
  </script>

  <div id="app" v-cloak>
    <v-app>
      <v-card height="100vh" width="100%"
              class="mx-auto overflow-hidden"
              @click.stop="drawer=false">
        <!-- Draw Menu -->
        <v-navigation-drawer v-model="drawer"
                     @click.stop="drawer=false"
                     absolute temporary dark>
          <v-list nav>
            <v-list-item to="/">
                <v-list-item-icon>
                  <v-icon>fas fa-home</v-icon>
                </v-list-item-icon>
                首頁
            </v-list-item>

            <v-list-item to="/about">
                <v-list-item-icon>
                  <v-icon>fas fa-info-circle</v-icon>
                </v-list-item-icon>
                關於
            </v-list-item>
          </v-list>
        </v-navigation-drawer>

        <!-- 工具列 -->
        <v-toolbar dark>
          <!--主選單按鈕-->
          <v-app-bar-nav-icon @click.stop="drawer=!drawer">
          </v-app-bar-nav-icon>

          <!--工具列標題-->
          <v-toolbar-title>
            樂活學程式
          </v-toolbar-title>
        </v-toolbar>

        <!-- 結果在此顯示 -->
        <router-view></router-view>
      </v-card>
    </v-app>
  </div>

  <script>
    const router = new VueRouter({
      routes: [
        {
          path: '/',
          component: home
        },
        {
          path: '/about',
          component: about
        }
      ]
    });

    var vm = new Vue({
      router,
      el: '#app',
      vuetify: new Vuetify(),
      data: {
        drawer: false
      }
    });
  </script>
</body>
</html>

完整範例

本範例整合以下的套件

  • vue:基本套件
  • vuetify:人機界面原件
  • VueRouter:路由,用來切換畫面
  • axios:AJAX 非同步資料存取套件
  • vuex:共享資料狀態
<!DOCTYPE html>
<html>
<head>
  <!-- 採用 Unicode utf-8 編碼 -->
  <meta charset="utf-8" />

  <!-- 調整手機瀏覽器的螢幕解析度 -->
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- 安裝 Vue、vue-router -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
  <script src="https://cdn.jsdelivr.net/npm/vue-router"></script>

  <!-- axios 套件-->
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

  <!-- 安裝 Vuex -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.0.1/vuex.js"></script>

  <!-- 安裝 Vuetify -->
  <link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
  <script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>

  <!-- 安裝字體及圖標 -->
  <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
  <link href="https://cdn.jsdelivr.net/npm/@mdi/font@4.x/css/materialdesignicons.min.css" rel="stylesheet">
  <script src="https://use.fontawesome.com/releases/v5.0.6/js/all.js"></script>

  <script src="https://cdn.jsdelivr.net/npm/vuex-map-fields@1.4.0/dist/index.min.js"></script>
</head>
<body>
<!--首頁 -->
<style></style>

<template id="home">
  <div>
    <v-app>
      <v-card width="100%" height="98%">
        <!-- 行政區篩選 -->
        <v-select :items="sareas" 
                  :value="area" @input="setArea"
                  label="行政區">
        </v-select>
        <v-divider></v-divider>

        <!-- 清單 -->
        <v-list>
          <div v-for="item in items"
                    v-if="item.sarea==area">
              <v-list-item>
                <v-list-item-content>
                  <!--站名-->
                  <v-list-item-title>
                    {{item.sna}}
                  </v-list-item-title>

                  <!--地址-->
                  <v-list-item-subtitle>
                    {{item.ar}}
                  </v-list-item-subtitle>
                </v-list-item-content>
              </v-list-item>

              <!--分隔線-->
              <v-divider></v-divider>
          </div>
        </v-list>
      </v-card>
    </v-app>
  </div>
</template>

<script>
  const home = {
    template: '#home',
    data() {
      return {
        url: "http://ewin.tw/myApp/sample/UBike/",
        items: {},
        sareas: []
      }
    },
    computed: {
      ...Vuex.mapState(['area'])
    },      
    // 將 mutations map 到 Vue 的 methods
    methods: {
      ...Vuex.mapMutations(['setArea']),
    },
    mounted() {
      var _this = this;

      //透過 axios 讀取網路上的json資料
      axios.get(this.url)
        .then(function (response) {
          _this.items=response.data.result.records;

          // 篩選出新北市的區名
          _this.sareas = _this.items.map(function (item) {
            return item.sarea;
          });

          // 去除重複
          // vm.sareas = [...new Set(vm.sareas)];
          _this.sareas = Array.from(new Set(_this.sareas))
        })
        .catch(function (error) {
          console.log(error);
        })
      }
  };
</script>

<!-- page2 -->
<style></style>

<template id="page2">
  <div>
    <v-app id="inspire">
      <v-expansion-panels hover inset>
        <!-- 行政區篩選 -->
        <v-select :items="sareas" 
                  :value="area" @input="setArea"
                  label="行政區">
        </v-select>

        <v-expansion-panel v-for="item in items" v-if="item.sarea==area">
          <v-expansion-panel-header>
            <h3>{{item.sarea+'-'+item.sna}}</h3>
          </v-expansion-panel-header>
          <v-expansion-panel-content>
            站點代號: <span v-text="item.sno"></span><br>
            場站總停車格: <span v-text="item.tot"></span><br>
            可借車位數: <span v-text="item.sbi"></span><br>
            可還空位數: <span v-text="item.bemp"></span><br>
            中文地址: <span v-text="item.ar"></span><br>
            經緯度: <a :href="'https://www.google.com/search?q='+item.lat+'%2C'+item.lng+'&oq='+item.lat+'%2C'+item.lng"
                target="_blank" style="text-decoration:none">
                {{item.lat+','+item.lng}}
            </a>
          </v-expansion-panel-content>
        </v-expansion-panel>
      </v-expansion-panels>
    </v-app>
  </div>  
</template>

<script>
  const page2 = {
    template: '#page2',
    data: function () {
      return {
        url: "http://ewin.tw/myApp/sample/UBike/",
        items: {},
        sareas: []          
      }
    },
    computed: {
      ...Vuex.mapState(['area']),
    }, 
    // 將 mutations map 到 Vue 的 methods
    methods: {
        ...Vuex.mapMutations(['setArea']),
    },
    mounted() {
      var _this = this;

      //透過 axios 讀取網路上的json資料
      axios.get(this.url)
        .then(function (response) {
          _this.items=response.data.result.records;

          // 篩選出新北市的區名
          _this.sareas = _this.items.map(function (item) {
            return item.sarea;
          });

          // 去除重複
          // vm.sareas = [...new Set(vm.sareas)];
          _this.sareas = Array.from(new Set(_this.sareas))
        })
        .catch(function (error) {
          console.log(error);
        })
      }
  };
</script>

<style>
  .v-ripple__container {
    display: none !important;
  }

  /* 避免程式啓動時閃爍 */
  [v-cloak] {
    display: none;
  }
</style>

<div id="app" v-cloak>
  <v-app>
    <v-card height="100vh" width="100%" 
          class="mx-auto overflow-hidden" 
          @click.stop="drawer=false">

      <!-- Draw Menu -->
      <v-navigation-drawer v-model="drawer" 
             @click.stop="drawer=false"                       
            absolute temporary dark>
        <!-- 選項 -->
        <v-list nav>
          <v-list-item  to="/">
            <v-list-item-icon>
              <v-icon>fas fa-home</v-icon>
            </v-list-item-icon>
            首頁
          </v-list-item>

          <!-- 請練習補上 info-circle 圖像 -->
          <v-list-item to="/page2">
            <v-list-item-icon>
                <v-icon>fas fa-info-circle</v-icon>
              </v-list-item-icon>  
            第二頁
          </v-list-item>
        </v-list>
      </v-navigation-drawer>

      <!-- 工具列 -->
      <v-toolbar dark>
        <!--主選單按鈕-->
        <v-app-bar-nav-icon 
            @click.stop="drawer=!drawer">
        </v-app-bar-nav-icon>

        <!--工具列標題-->
        <v-toolbar-title>
          樂活學程式
        </v-toolbar-title>

        <v-spacer></v-spacer>

        <!--其他工具按鈕-->
        <v-btn icon  @click.stop="dialog=true">
          <v-icon>
            mdi-dots-vertical
          </v-icon>
        </v-btn>        
      </v-toolbar>

      <!-- 底部導覽列 -->
      <v-bottom-navigation v-model="bottomNav" absolute>
        <v-btn value="recent" to="/">
          <span>首頁</span>
          <v-icon>mdi-home</v-icon>
        </v-btn>

        <v-btn value="favorites" to="/page2">
          <span>最愛</span>
          <v-icon>mdi-heart</v-icon>
        </v-btn>

        <v-btn value="nearby" @click.stop="dialog=true">
          <span>導航</span>
          <v-icon>
            mdi-map-marker
          </v-icon>
        </v-btn>
      </v-bottom-navigation>

      <!-- 結果在此顯示 -->
      <router-view></router-view>
    </v-card>
  </v-app>

  <!--加上 persistent會變 Do Model 對話框-->
  <v-dialog v-model="dialog" max-width="290">
    <v-card>
      <v-card-title class="headline">
        對話框抬頭
      </v-card-title>

      <v-card-text>
        對話框内容
      </v-card-text>

      <!-- 對話框按鈕 -->
      <v-card-actions>
        <v-spacer></v-spacer>

        <v-btn color="green darken-1"
                text @click="dialog = false">
          取消
        </v-btn>

        <v-btn color="green darken-1"
                text @click="dialog = false">
          確定
        </v-btn>
      </v-card-actions>
    </v-card>
  </v-dialog>  
</div>

<script>
    const router = new VueRouter({
      routes: [
        {
          path: '/',
          component: home
        },
        {
          path: '/page2',
          component: page2
        }
      ]
    });

    const store = new Vuex.Store({
      state: {
        area: '新莊區'
      },
      mutations: {
        setArea (state, area) {
          state.area= area
        }
      }
    });

  new Vue({
    router,
    store,
    el: '#app',
    vuetify: new Vuetify(),
    data: {
      drawer: false,
      bottomNav: 'recent',
      dialog: false
    }
  });
</script>
</body>
</html>
🏠