---
# System prepended metadata

title: 玉山暑期實習學習紀錄
tags: [' docker', spring boot, ' vue.js']

---

---
title: '玉山暑期實習學習紀錄'
tags: spring boot, vue.js, docker
---

玉山暑期實習紀錄


> [Github](https://github.com/mrwenwei/ESUN-Summer-Project)

## 目錄

[TOC]

## 需求分析

## 系統設計
### 開發時程


```mermaid
gantt
    title 甘特圖
    section 需求訪談
    5days:2019-07-15, 5d
    section 資料庫設計
    12 days:2019-07-15  , 12d
    section 後端程式設計
    20 days:2019-07-22, 20d
    section 前端程式設計
    20 days:2019-07-29, 20d
    section 文件撰寫
    7 days:2019-08-18, 7d
    section 測試
    14 days:2019-08-18, 14d
```

## 資料庫
了解需求之後我們需要對每張單據設計一個資料庫，需要有存匯取款的所有資料，包含金額日期等等。

### ERD

初步設計出來的 ER Diagram
![](https://i.imgur.com/Nx1WOKA.png)

不過如果以這種方式建資料庫，在未來若有單據要擴充的話會很麻煩，因此學長有與我們建議使用 [CLOB](https://en.wikipedia.org/wiki/Character_large_object)，後來 ERD 也簡化成如下：
![](https://i.imgur.com/RrOoq10.png)
￼￼

如此一來就能在其中的一個欄位存一個 JSON，要使用時只需要 parse 過即可。


### Docker
Docker 的好處是可以將自己的 container 存成新的 image，上傳到 Docker Hub 供別人使用，所以你也可以直接使用建好資料庫的 image，不過缺點是若利用 commit 的方式更新 image ，這種方式建立的 Image 是難以重現的，比較好的方式是編寫 dockerfile，不過這個部分還在研究中因此待補上。 

MSSQL 在 Docker 上的 image 安裝 (以下為 bash 指令):
1. Pull 官方的 SQL server image
```
$ sudo docker pull mcr.microsoft.com/mssql/server:2017-latest
```
    
2. run image 檔 
<!-- 2. run image 檔，這裡要注意的點是 SQL Server 設定變更和資料庫檔案都會保存於容器中，即使使用 docker stop 和 docker start 來將容器重新啟動也一樣。不過，如果使用 docker rm 來移除容器，則會刪除容器中的所有項目，因此建議使用 [Volumn](https://docs.docker.com/storage/volumes/)（指令中 -v 的部分） -->

```
$ sudo docker run -e 'ACCEPT_EULA=Y' \
    -e 'MSSQL_SA_PASSWORD=<YourStrong!Passw0rd>' \
    -p 1433:1433 --name sql1
    -d mcr.microsoft.com/mssql/server:2017-latest
```
ACCEPT_EULA 為授權合約需同意，密碼的長度至少必須是 8 個字元，包含下列四種集合的其中三種字元：大寫字母、小寫字母、以 10 為底數的數字，以及符號，1433 為 port，name 為容器名稱可任意定義，-d 為在背景執行。

3. 查看建立的 docker 容器
```
$ sudo docker ps -a
```
4. 執行容器（也可以下載 [Kitematic](https://kitematic.com/) 用圖形化介面操作）
```
$ sudo docker exec -it sql1 "bash"
```
5. 連線到 SQL server

```
root@xxxxxxxxx:/# /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P "<YourNewStrong!Passw0rd>"
```

如果成功，畫面會長這個樣子
```
1>
```
再來就能開始操作 DB 了

6. 建立 Database
```
1> CREATE DATABASE your_db_name
1> COLLATE Chinese_PRC_CI_AS; 
1> GO
```
**Docker 在使用資料庫時會發現無法插入中文資料，因此在建構資料庫時必須輸入以上指令：**

7. 而建立好 Database 的部分後可以直接從 SQLQuery 內的[資料夾](https://github.com/mrwenwei/ESUN-Summer-Project/tree/master/SQLQuery)執行 SQL 碼就行了（四個 table）。

做完以上步驟後即可在終端機中使用 SQL 指令操控資料庫，但如果你用不習慣 command line ，想要使用圖形介面這裡也介紹一個 Microsoft 推出的 [Azure Data Studio](https://docs.microsoft.com/zh-tw/sql/azure-data-studio/download?view=sql-server-2017)，下載安裝好後只需要連線本地端的 SQL Server 帳密，並選擇資料庫，就可以使用像 MS SQL 那樣的圖形化介面，還有提供 notebook 的方式可以進行 Query，非常方便。
![](https://i.imgur.com/PDBmD8g.png)

## Spring Boot
### 建立專案
1. 點擊 command(ctrl)+N 找到 spring starter project，或者是使用官方網站提供的 [spring initializr](https://start.spring.io/) 下載並匯入。
![](https://i.imgur.com/Cgd4V0K.png)

2. 命名設定
![](https://i.imgur.com/YxVucnZ.png)

3. 將需要的套件加入專案，點擊 finish 完成專案建置，會自動生成 XML（專案需求需用到 web、JPA、MS SQL driver）。
![](https://i.imgur.com/m84mLWX.png)
pom.xml 內已配置好 dependency，也可以手動在這裡刪減。
![](https://i.imgur.com/2Urt7A4.png)

4. 將新增的專案新增到 server 上：右鍵點擊 wildfly server 選擇 add and remove，將專案加進 Configured，點擊 finish 即可。
![](https://i.imgur.com/iZS3T42.png)


### 專案架構
建立不同 package 來分別處理不同事情
![](https://i.imgur.com/edAMCU9.png =250x)

新增方式： 在 src/main/java 資料夾內點擊右鍵>new>package

共分為 Controller、Entity、Repository、Service 四類，以下我們以實作使用者帳戶的 table 為範例來詳細討論。

* Entity

    新增方式： 在 entity 的 package 右鍵>new>class
    ![](https://i.imgur.com/QsiKNtK.png)

    Entity 用來對應到資料庫中的 table，下圖是可看出在資料庫中有個叫 "AccountInfo" 的 table，在後端會將一個 row 當作一個這樣的物件來存取，定義好每個欄位的特性(ID、not null 等等)，並定義 get/set 函式來取用或更改物件內容。
    ![](https://i.imgur.com/LY23mwU.png)

    "@Entity" 的用途是告知 spring 這是一個 entity 的 class
    這些 entity 的內容都是由 javax.persistence 提供，也就是 JPA，用來後續操控資料庫用。

* Repository
    
    新增方式： 在 Repository 的 package 右鍵>new>class
    
    與 DB 連接的橋樑。 DAO（Data Access Object）連接到數據庫，以在數據庫和服務層之間來回讀取和寫入數據。
    JpaRepository 是 Spring 提供的 JPA library ([Doc](https://docs.spring.io/spring-data/jpa/docs/current/reference/html/))，用法是在 Repository 裡繼承 JpaRepository 的 interface，底下也可以自己定義 function，不過 Spring 內建就有很多很好用的 function 了，如此一來不僅不需要打到 SQL 語法，也造成程式碼的整潔。
    
    ![](https://i.imgur.com/x3nuqa5.png)
    
* Service
    
    新增方式：在 Service 的 package 右鍵>new>class

    定義所有操作的詳細內容，像是新增、查詢、刪除、更新等等，寫出一套屬於自己的服務。
    
    ![](https://i.imgur.com/OLF2MJ4.png)
    
    圖中的 @Autowired 是用來自動對應到別的 class 的內容，建立連結的概念，讓程式知道你是在呼叫前面定義好的 class，而這裡宣告出來的 userRepo 就可以使用 Spring data JPA 提供的 function 了，如圖中 22 行的 findAll()，其意思等同於「SELECT * from AccountInfo」。
    其中 save 也是一個很好用的 function，當一個 entity 的 id 不存在 table 裡頭時，呼叫此 function 則會直接新增一個欄位，若 id 存在則是更新該欄位。
    
    ![](https://i.imgur.com/L2z8yjA.png)

* Controller

    新增方式：在 Controller 的 package 右鍵>new>class
    
    Controller 顧名思義是控制整個流程的地方
    @RestController 是 @Controller 和 @ResponseBody 註釋的組合。@Controller 包含處理用戶請求的路由邏輯方法。 @ResponseBody 註釋用於提及必須通過具有 @Controller 註釋的類中定義的方法返回 http response。通過將這兩個功能組合到單個註釋中，Spring Boot 可以輕鬆實現，因此我們不會必須使用 @ResponseBody 註釋來註釋 @Controller 類的每個方法。此外，response 的返回類型默認配置為 JSON。
    
    @RequestMapping 可以對應到前端傳送來的要求，例如圖中的 getAll() 是用 "GET/users" 標注起來(method 預設為 GET)，當前端傳來 GET 要求並且網址符合就會呼叫此 function，並回傳 list of entity 到前端。
    
    ![](https://i.imgur.com/mUxZmXq.png)
    
    到目前為止就寫出了一個符合 REST API 形式的 API，我們用 Postman 來測試看看。
    ![](https://i.imgur.com/mdlVWsw.png)
    
    成功回傳 table 內的所有內容。

## Vue.js
### Vue.js 開發環境建置
#### 一、vue-cli 安裝 
在開發Vue.js專案時，往往會搭配各種套件來取代重複性的人工作業，而 Vue-cli 是由 Vue 官方提供的專案樣板工具，可以快速透過指令建立出一個立即可用的開發環境。

```
$ npm install -g @vue/cli
```
#### 二、建立 Vue 專案
```
$ vue init <樣板名稱> <專案名稱>
```
(可用 $ vue list 查看官方預設樣板清單)
我們是以 webpack 樣板建立專案，如下：

```
$ vue init webpack EsunSummerProject
```
- 專案建立過程可依需求調整相關選項。
- 建議安裝 vue-router (作為路由)。

![](https://i.imgur.com/x7dUMM9.png)

- 建立完畢後，按照指示移動到專案路徑，並安裝所需模組
```
$ cd <專案資料夾>
$ npm install
```
- 啟動開發環境
```
$ npm run dev
```

- 打開瀏覽器就可以看到專案預設的畫面了
(預設是 localhost:8080)

#### 三、Visual Studio Code 安裝
https://code.visualstudio.com
VS Code是一款很好用的編輯器，除了介面好看易用之外，裡面也有許多好用的套件，這邊就不多作介紹，有興趣的人可以自行google。

#### 四、Vue.js 各種套件的安裝
如果有需要使用到套件，通常有兩種使用方式，一種是透過 npm 下載套件，另一種是直接以 CDN 網址來使用。

以Vuex套件來說，以 npm 安裝：
```
npm install vuex --save
```

以 CDN 使用直接在 script 中加入：
```javascript=
<script src="/path/to/vuex.js"></script>
```
### Vue.js 專案介紹
#### 一、專案架構
```
.
├── build          // 與 webpack 相關
├── config         // 與 webpack 相關
├── node_modules   // node npm 模組
├── src            // 主要開發環境 source code
|   ├── assets     // 圖片等靜態檔
|   ├── router     // vue 路由器
|   ├── app.vue    // 主要的樣板檔
|   ├── main.js    // 主要的 js 檔，套件 import 的入口文件
|   └── components // vue 元件檔
├── static         // 放置第三方 plugin 位置
├── index.html     // 靜態 html檔
├── package.json
└── package-lock.json
```
#### 二、專案的起點
Vue.js 是個專注在視圖層 (View) 的框架，它不僅易於上手，還便於與第三方套件或既有專案整合。

- 一個頁面中的各個部分可以自行決定各自獨立運作或是相連。
![](https://i.imgur.com/GkGSoZz.png)

- 我們先來看 main.js 這個檔案，它是整個 Vue 專案的最源頭，裡面引入了 Vue 並創建了 Vue 實例，這個 Vue 實例有一個引入的 components: App。 這個App就是 components資料夾中的 App.vue 檔案，會有 .vue 這個副檔名是因為專案中有使用 webpack， webpack有一個套件 vue-loader 能夠將 .vue 檔解析成 js。
```javascript=
new Vue({
  el: '#app',
  router,
  store,
  components: { App },
  template: '<App/>',
  data: {
    bgimg1: "../static/bgimg1.jpg"
  }
})
```

- 以我們的這個專案來說，在 index.html 檔案的內容如下，可以看到上面Vue實例中註冊的 App.vue ，因此網頁一打開就會載入 App.vue 的內容。
```htmlmixed=
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>esp-front-end</title>
  </head>
  <body>
    <!--  App.vue  -->
    <App></App>
  </body>
</html>
```

### Vue.js 基本應用
Vue.js的官方文件有中文版，內容也很豐富，可以先從官方文件開始學習。
https://cn.vuejs.org/v2/guide/index.html
#### 一、利用 data 與 v-model 達到資料的雙向綁定
- data 用來儲存 vue 元件內部狀態或資料，並與 v-model 合作實現資料的雙向綁定。以下以本專案的匯款表單來說明：
￼￼￼![](https://i.imgur.com/uQAUcFg.png)
- 由於合計的部分顯示出來的資料是依據使用者輸入的資料來改變，因此我們以 mastache 語法 (雙大括號)來與 data 中的值綁定，當 data 中的 totalAmount 一改變，網頁顯示出來的合計值也會馬上更新。


```htmlmixed=
<!-- template -->
<div class="col-1">
    <label>合計</label>
</div>
<div class="col-2">
    <!-- 以 mastache 與 data中的 totalAmount 綁定 -->
    <label>{{transferForm.totalAmount}}</label>
</div>
```

- data 中的 totalAmount 預設為0，當他被計算出來後，數值會同步更新到畫面上。

```javascript=
// script
export default {
  data() {
    return {
      transferForm: {
        //與 totalAmount 綁定
        totalAmount: "0"
      }
    };
  },
}
```
- 上述的 mastache 用法通常用來實現資料的單向流動，而 v-model 則是實現雙向綁定用的，常用在表單元件上，像是 input 、 select 和 textarea 等。以下以本專案的匯款表單來做說明：
![](https://i.imgur.com/dVTUPfY.png)
- 我們希望在使用者填寫表單後，輸入的內容可以同步更新在 data 中，因此使用了 v-model ，這樣的用法是雙向的，也就是不管是 html 中的表單元素，還是 javascript 中的 data，只要任一邊改變，另一邊都會同步改變。

```htmlmixed=
<!-- template -->
<div class="col-2">
  <label for="transactAmount">匯款金額（新台幣）</label>
</div>
<div class="col-4">
  <input
    type="text"
    class="form-control"
    id="transactAmount"
    placeholder="請輸入金額"
    @input="transferFeeCounter()"
    v-model="transferForm.transactAmount"
    required
  />
</div>
```
```javascript=
//script
export default {
  data() {
    return {
      transferForm: {
        //預設值是0，當表單被使用者輸入，此處的內容會同步改變
        transactAmount: "0",
      }
    };
  },
}  
```
#### 二、事件處理 (Event Handling) 與 Methods
- 為了能讓使用者與畫面互動，我們可以使用 v-on:method_name (簡寫@method_name) 綁定事件監聽器，藉此使用我們宣告的 method。以下以本專案的匯款表單來說明：
![](https://i.imgur.com/pGoavZh.png)
- 當使用者輸入匯款金額、匯款方式之後，會觸發 method 計算出匯費和合計金額。

```htmlmixed=
<!-- template -->
<div class="col-2">
  <label for="transactAmount">匯款金額（新台幣）</label>
</div>
<div class="col-4">
  <input
    type="text"
    class="form-control"
    id="transactAmount"
    placeholder="請輸入金額"
    @input="transferFeeCounter()"
    v-model="transferForm.transactAmount"
    required
  />
</div>
```
- 在上面的 input 輸入欄位中， @input="transferFeeCounter()" 等同於 v-on:input="transferFeeCounter()" ，也就是當輸入欄位有更動時，就會去呼叫 "transferFeeCounter()" 方法。

```javascript=
export default {
    methods:{
        transferFeeCounter() {
          //費用的計算方式
        }
    }
}
```
#### 三、v-for 迴圈
- v-for 跟一般常見的 for 迴圈差不多，常用來在 template中顯示陣列或是物件的內容。以下以 select 選單中的 v-for 應用來說明：
![](https://i.imgur.com/ktFM98D.png)
- 我們希望能將從後端抓來的銀行列表內的資料，當成下拉列表中的選項，因此使用了 v-for 來實作。
![](https://i.imgur.com/WvZwsdd.png =200x) 

- 透過我們的 api 傳回來的銀行資料格式大概是這個樣子。

```json=
[
    {
        "name": "台灣銀行",
        "swiftcode": "004"
    },
    {
        "name": "土地銀行",
        "swiftcode": "005"
    },
    {
        "name": "合作金庫",
        "swiftcode": "006"
    }
]    
```
- option 指的是 select 選單中的選項，我們以 v-for 遍歷陣列 banks ， 把銀行的代碼和名稱顯示在選單中，並以前面提到的 v-model 把使用者選擇的值存入 data 中。

```htmlmixed=

<div class="col-2">
    <label for="transferBank">收款銀行</label>
</div>
<div class="col-2">
    <select
    class="custom-select"
    id="transferBank"
    v-model="transferForm.transferBank"
    required
    >
    <option v-for=" bank in banks">{{bank.swiftcode}} {{bank.name}}</option>
    </select>
</div>
```
#### 四、v-if 條件渲染
- 如果希望某些畫面上的元素在特定條件下才出現，可以使用 v-if、 v-if-else 和 v-else，以條件式來決定畫面出現的內容。以下以交易單據中，銀行作業欄位的表單來說明：

![](https://i.imgur.com/Q1Px3Dg.png)

- 當交易是存款、匯款、提款時，這些行員必需詢問顧客的問題會不一樣，這時候為了避免同一份表單要設計很多種，可以使用 v-if 將不同的內容依條件顯示在畫面上，在這個例子中，紅圈圈起來的這一個問題在存款、匯款時的內容和提款時的不一樣，因此我們以 v-if 來判斷交易的 type，並顯示相對應的內容。
```htmlmixed=
<!-- 當交易是存款或匯款時顯示 -->
<label
for="question2"
class="col-md-4 col-form-label col-form-label-sm"
v-if="this.transact_data.type=='存款' || this.transact_data.type=='匯款'"
style="border:1px solid;"
>2.申請人是否認識存入帳戶的受款人
</label>

<!-- 當交易是提款時顯示 -->
<label
for="question2"
class="col-md-4 col-form-label col-form-label-sm"
v-if="this.transact_data.type=='取款'"
style="border:1px solid;"
>2.申請人是否認識陪同提款者(有陪同提款者時詢問)
</label>
```
### VueRouter 網站路由管理
[VueRouter官方文件](https://router.vuejs.org/zh/)
- vue 專案可以透過 vue-router 的設定檔來定義整體網站的路由規則，再利用 router-view 來定位子路由組件渲染的出口。

- 以下是本專案部分的路由檔案，vue router 的設定檔案會放在 components 資料夾中的 router資料夾內。

```javascript=
let router = new VueRouter({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'login',
      component: login,
      beforeEnter: (to, from, next) => {
        if(store.getters.isLoggedIn){
          next('home_'+store.getters.authToken)
        }else{
          next()
        }
      }
    },
    {
      path: '/home_customer',
      name: 'home_customer',
      component: home_customer,
      meta: {
        requiresAuth: true,
        allowAuth: 'customer'
      },
    },
  ]
})
export default router
```
#### 一、router-view 
- router-view 簡單的作用就是在符合路由規則時，把指定路由組件取代父層組件中的視圖；而整個應用程式的第一個視圖就是在 App.vue 檔案中，也就表示在 vue-router 設定中的路由組件在匹配情境下，會直接取代 App.vue 中 router-view 元素。


![](https://i.imgur.com/a8uQfpv.jpg)


- App.vue 
```htmlmixed=
<template>
    <div>
        NavBar
    </div>
    <router-view />
</template>
```
在上面的程式中，可以看到在NavBar的下面我們引入了router-view，因此顯示出來的是路由表中路徑為 '/' 的 login.vue。

#### 二、頁面的跳轉
- router.push 可以跳轉頁面，例如登入成功後，判斷使用者身份，如果是顧客的話，就將它轉移到顧客的首頁 home_customer。
```javascript=
this.$router.push("/home_customer");
```
### Axios 發送 http 請求
基本用法可以先參考官方文件，挺詳細的。
https://github.com/axios/axios
vue更新到2.0之後，作者就宣告不再對vue-resource更新，而是推薦使用 axios 來發送 http 請求。

以 npm 安裝：
```
$ npm install axios
```
以 CDN 使用：
```javascript=
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
```

一般常見的 Axios 寫法如下：
```javascript=
axios.get('/user?ID=12345')
  .then(function (response) {
    // 請求成功後執行
    console.log(response);
  })
  .catch(function (error) {
    // 錯誤處理
    console.log(error);
  })
  .finally(function () {
    // 不管請求成功或失敗都會執行
  });

```

#### 一、API 呼叫
以下以我們匯款表單發送後的請求來示範 API 呼叫：
```javascript=
submit_form() { 
  //if user 點選確定
  if (confirm("您確定要送出申請嗎？")) {
    let uri = "api/POST/transaction";
    this.axios
      //發送 post 請求，將本次交易內容傳到後端
      .post(uri, {
        type: this.transactType,
        receiptsData: JSON.stringify(this.transferForm),
        branchCode: this.$store.getters.getBranchCode
      })
      //post 請求成功後，再以 get 請求後端返還本次交易ID 
      .then(response => {
        this.axios
          .get("api/GET/transaction/" + response.data.id)
          .then(res => {
            alert(
              "已送出申請，即將返回首頁，您的交易編號為：" + res.data.tnum
            );
          });
      });
    //以 VueRouter 返回首頁
    this.$router.push("home_customer");
  }
}
```

#### 二、CORS 處理
在 config 資料夾下的 index.js 中， proxyTable 可以設定跨域請求的位置，之後只要以 '/api' 就能取代很長的網址。
```javascript=
proxyTable: {
  '/api': {
    target: 'http://172.20.10.2:8080/esp-back-end-0.0.1-SNAPSHOT/',
    changeOrigin: true,
    pathRewrite: {
      '^/api': ''
    }
  }
},
```
### Vuex 與登入管理
基本用法可以先參考官方文件，挺詳細的。
https://vuex.vuejs.org/installation.html
從前幾個範例中，可以發現資料都是存放在各組件 script 的 data 裡面，但是一個大型系統中，重複使用到的資料會有很多（e.g. token, user info, shopping cart list），這樣各個頁面切換之間如何拿到同一筆資料呢？

因此我們需要一個集中管理資料的地方，這樣的需求可以用 Vuex 來完成， 透過 vuex 的 store 可以儲存多頁面共用的資料，像是登入資訊等等。

透過 npm 或 YARN 安裝
```
npm install vuex --save
```

打開 main.js 宣告使用 vuex
```javascript=
// 引入 vuex
import Vuex from 'vuex'

// 告訴 Vue 使用 vuex
Vue.use(Vuex)
```
以下以登入管理來示範 vuex 的 store 使用方法：

#### 一、建立 token
在 ./src/main.js 檔案加入以下：
```javascript=
import store from './store'
import Axios from 'axios'

Vue.prototype.$http = Axios;
const token = localStorage.getItem('token')
if (token) {
  Vue.prototype.$http.defaults.headers.common['Authorization'] = token
}
[...]
```
#### 二、 store 的寫法
這些內容我們放在 src/store 資料夾下的 index.js 檔案

1. state: 負責記錄應用程式所有的 State。

2. mutations: 負責接收 action(commit) 資料並計算邏輯後改變 State，只有 mutation 可以改變 state。

3. action: 定義了整個應用程式的行為如：按下 login 這顆按鈕，它是一個 action！現在我們要開始登入囉！這樣的行為通常需要與 server 溝通，因此 call API 的行為（非同步）也會在 action 中執行！ 它使用 commit 向 Mutations 溝通（傳遞資料）。

4. getters: 讓我們能在各個組件中存取 state 中的資料。例如：this.$store.getters.isLoggedIn 可以得知是否以登入。 
```javascript=
export default new Vuex.Store({
  //state，存放想要整個網站共享的資料能放在這裡
  state: {
    status: '',
    token: localStorage.getItem('token') || '',
  },
  //mutation，只能透過 mutation 改變 state 內容
  mutations: {
    auth_request(state) {
      state.status = 'loading'
    },
    auth_success(state, payload) {
      state.status = 'success'
      state.token = payload.token
    },
    auth_error(state) {
      state.status = 'error'
    },
  },
  //action，負責觸發 mutation
  actions: {
    login({ commit }, user) {
      return new Promise((resolve, reject) => {
        commit('auth_request')
        //把使用者輸入的帳號密碼傳送到後端做比對
        axios({ url: '/api/POST/user/auth/' + user.id, data: user, method: 'POST' })
          //將回傳的 token 存到 localstorage中
          .then(resp => {
            const token = resp.data.role
            localStorage.setItem('token', token)
            axios.defaults.headers.common['Authorization'] = token
            //透過 commit 呼叫 mutation，藉此改變 state 中的資料
            commit('auth_success', {'token':token})
            resolve(resp)
          })
          .catch(err => {
            commit('auth_error')
            localStorage.removeItem('token')
            reject(err)
          })
      })
    },
  },
  //getters
  getters: {
    isLoggedIn: state => !!state.token,
    authStatus: state => state.status,
    authToken: state => state.token,
  }
})
```

#### 三、 Login 頁面中，按下 Login 按鈕後所觸發的事件
```javascript=
methods: {
    login: function() {
      //user輸入的id和password
      let id = this.user.id;
      let password = this.user.password;
      //使用 vuex 的 store
      this.$store
        //呼叫 store 中的 action 中的 login method
        .dispatch("login", { id, password })
        //如果後端處理的結果判斷帳號錯誤
        .then(() => {
          if (this.$store.getters.authToken == "Wrong account") {
            alert("帳號錯誤");
            this.$store.dispatch("logout");
            this.$router.push("/");
          //如果後端處理的結果判斷密碼錯誤
          } else if (this.$store.getters.authToken == "Wrong password") {
            alert("密碼錯誤");
            this.$store.dispatch("logout");
            this.$router.push("/");
          //如果帳密都正確，依照 token 被 push 到相對應的頁面
          } else {
            this.$router.push("/home_" + this.$store.getters.authToken);
            this.$router.go()
          }
        })
        .catch(err => console.log(err));
    },
  }
```

### Vue.js 元件式設計作法
在vue.js中，各個元件獨立運作，每個元件都有各自用途，然後再互相組合搭配，讓系統開發上比較結構化。

參考資料：
https://ithelp.ithome.com.tw/articles/10196032
https://ithelp.ithome.com.tw/articles/10196169



### Vue-tables-2 列表設計
vue 之套件，只要把資料傳進去就能輕易顯示出表格，並且做許多操作，像是篩選、搜尋等等，非常方便。
- 安裝
```
$ npm install vue-tables-2
```
- Import and register (main.js)
```htmlmixed=
import {ServerTable, ClientTable, Event} from 'vue-tables-2';
Vue.use(ClientTable);
Vue.use(ServerTable);
window.Event = Event;
Vue.use(Event);
window.moment = require('moment');
```

- 變數宣告
```htmlmixed=
data() {
    return {
      tableData: [],
      columns: ["type", "tnum", "顧客姓名", "交易金額", "dateTime", "broker", "finishedTime", "finished"],
    }
}
```
tableData 用陣列存所有列資料(Json)，columns 可以選擇你想放的欄位，例如：tableData 內的資料含有：id, name, age，此時 columns: ["id", "name"] 就不會把 age 的資料顯示出來。

- 跟後端索取資料
```htmlmixed=
this.axios.get("api/GET/transactions/"+this.$store.getters.getBranchCode).then(res => {
      this.tableData = res.data;
      this.tableData.map(x => {
        x.dateTime = moment(x.dateTime + " GMT+0000");
      });
});
```
拿到資料後放進 tableData 內，若資料內有 datetime 之類的時間資料必須先用轉成 moment 物件，後面有 GMT+0000 是因為當時件資料庫時沒有設定成台灣時區，時區是在0的位置，而 moment 會預設傳進去的時區是當地的，因此要標示傳進來的時區，moment 才能辨認轉成正確時區。

- 顯示 table
```htmlmixed=
<v-client-table ref="myTable" :data="tableData" :columns="columns" :options="options">
</v-client-table>
```
如此就能顯示出資料表如下 (除 filter)
![](https://i.imgur.com/fnjXmM3.png)

- Filter 功能
vue-table-2 其中一項強大的功能就是簡易實現 filter，若我們要針對個別欄位搜尋，只要在 filterable 內加入想要 filter 的欄位即可。
```htmlmixed=
options: {
    filterable: ["id", "type", "dateTime", "tnum"],
}
```
只不過預設是對字串的搜尋，如果想要客製化 filter 的功能像是 list filter，在 options 裡再加入 filterByColumn 及 listColumns：
```htmlmixed=
options: {
    filterable: ["id", "type", "dateTime", "tnum"],
    filterByColumn: true,
    listColumns: {
      type: [
        { id: "存款", text: "存款" },
        { id: "取款", text: "取款" },
        { id: "匯款", text: "匯款" }
      ]
    },
}
```

vue-table-2 有提供許多 [options](https://www.npmjs.com/package/vue-tables-2#options) ，可以實現許多 incredible 的功能。
另外這個 [github](https://github.com/KarateJB/eBooks/tree/master/Vue.js) 的教學寫得相當詳盡也可以參考。
