---
# System prepended metadata

title: 後台重構 - 以設定檔自動化生成第三方遊戲欄位
tags: [JavaScript, Vue Router, Refactor, Vue, element-ui]

---

# 後台重構 - 以設定檔自動化生成第三方遊戲欄位

最近公司的需求變得沒有這麼急迫，可以花點時間還債了 QQ

我們的產品主要有彩票和第三方遊戲－
彩票泛指像是**六合彩、七星彩、飛艇**等常見的博弈遊戲；
第三方遊戲則是介接第三方廠商的 API 服務在前台用 iframe 打請求拿取 URL 遊玩的遊戲，像是**棋牌、視訊、電子、捕魚、體育**都是我們有提供的遊戲類別。

~~沒錯，我都跟朋友戲稱我是負責串接接性感荷官在線發牌的前端工程師。~~

![AE視訊](https://i.imgur.com/5OxP2Va.png =400x)

我認真思考了一下後台在快速迭代下設計上的缺失，最主要會遇到的麻煩是**每次添加新的第三方遊戲類別都要浪費一堆時間找到組件補上欄位、新增路由**，還常常會東漏西漏，讓我改到快發瘋。

### 自動化

想到了一個非常簡單暴力的解法：
何不嘗試建立一個**設定檔**直接讓所有組件、路由、選項都依賴此設定檔生成就好？

![genius](https://i.imgur.com/E1ayrAM.png =300x)

我一直有在思考是不是可以這樣做，但是苦於新創迭代需求太頻繁害我來不及擦屁股 QQ，既然今天終於抽出時間讓我重構了，那就開始著手嘗試吧！

```javascript
// @/config/external-groups.js
export default [
  { id: 1, name: '棋牌', code: 'poker' },
  { id: 2, name: '視訊', code: 'live' },
  { id: 3, name: '電子', code: 'slot' },
  { id: 4, name: '捕魚', code: 'fish' },
  { id: 5, name: '體育', code: 'sport' },
]
```

對，就是這麼樸實無華， KISS 對每個人都好，把要用到的 attr 用陣列包一包即可。

以下是各 attr 的實際用途：

- id - 生成各遊戲類別之注單的預設 query (ex: ?group=1)
- name - 生成各遊戲類別的欄位 label 跟選項的 label
- code - 生成各遊戲類別的欄位 attr 和選項的 value

分析一下以這個方式重構帶來的利弊：

- 好處－生成遊戲類別一律只要改動設定檔就好，**節省大量時間**，且類別排序能完全一致。
- 壞處－程式碼**可讀性變差了一點**，新人可能需要追 code 搞懂我搞了什麼花樣。

覺得能省掉至少半天到一天的時程這點還蠻划算的，且後端的欄位 key 都有一致讓我很溫馨，於是開始動工！

後台專案目前是以 Vue2, Vue Router 和 element-ui 為技術棧，下面的例子會以這些技術為基礎做講解。

### Router

接下來就是一路無聊的 for-loop 到底。
感謝 ES6 的 **展開運算符**，我們可以依賴設定檔自動生成注單路由。

```javascript
// @/router.js
import Router from 'vue-router'
import EXTERNAL_GROUPS from '@/config/external-groups'
export default new Router({
  routes: [
    // 其他路由
    ...EXTERNAL_GROUPS.map((group) => ({
      path: `/report/${group.code}-bet-record`,
      name: `report-${group.code}-bet-record`,
      component: () => import('./views/Report/ExternalBetRecord'),
      meta: {
        title: `${group.name}注单`, // 頁面標題
        providerCategory: group.id, // 注單頁 API 的 default query
        // 其他 meta key 如權限、頁面類別...等ㄎ
      },
    })),
    // 其他路由
  ],
})
```

### Options

要讓 html template 渲染記得把設定檔放進 data 裡。

```htmlembedded
<html>
  <el-select v-model="query.type" placeholder="全部">
    <el-option label="全部" value="" />
    <el-option label="彩票佣金收入" value="lottery_commission" />
    <el-option
      v-for="type in EXTERNAL_GROUPS"
      :key="`external-${ type.code }-commission`"
      :label="`${ type.name }佣金收入`"
      :value="`${ type.code }_commission`"
    />
    <el-option label="佣金取款" value="withdraw" />
    <el-option label="佣金提取" value="transfer" />
  </el-select>
</html>
```

### Table

```htmlembedded
<html>
  <el-table-column
    v-for="type in EXTERNAL_GROUPS"
    :key="`external-${ type.code }-profit-and-amount`"
    :label="type.name"
    align="right"
    v-slot="{ row }"
  >
    <p
      :class="profitColor(row[`${ type.code }_profit`])"
      @click="routerGo(row.time, `${ type.code }_record`)"
    >
      {{ row[`${ type.code }_profit`] | currency('￥') }}
    </p>
    <p>{{ row[`${ type.code }_amount`] | currency('￥') }}</p>
  </el-table-column>
</html>
```

### Filters

之前用 filters 寫 switch-case 的部分打掉改用 methods 由 map 去找對應值。

```javascript
// @/views/Agent/CommissionRecord.vue
import EXTERNAL_GROUPS from '@/config/external-groups'
export default {
  name: 'CommissionRecord',
  data() {
    return {
      typeMap: {
        withdraw: '佣金取款',
        transfer: '佣金提取',
        profitspace_commission: '彩票佣金收入',
        revenue_commission: '彩票佣金收入',
        ...EXTERNAL_GROUPS.reduce((map, type) => {
          map[`${type.code}_commission`] = `${type.name}佣金收入`
          return map
        }, {}),
      },
    }
  },
  methods: {
    formatType(value) {
      return this.typeMap[value] || ''
    },
  },
}
```

大功告成，同事以後加第三方的遊戲類別時應該會很舒爽。
不過因為這包專案沒 i18n 所以才這麼輕鬆，之後還要煩惱怎麼自動加 i18n 跟客製化注單的欄位 (不是每個欄位都這麼簡單跑個 for-loop 就能輕鬆搞定的 QQ)

###### tags: `Refactor` `JavaScript` `Vue` `Vue Router` `element-ui`
