iT邦幫忙

0

kintone 外掛開發 ② 簡單實作範例 part 1 - 動手打包第一個外掛

  • 分享至 

  • xImage
  •  

前言

在上一篇文章 kintone 外掛開發 ① kintone 外掛的基本架構 中,我們介紹了 kintone 外掛的基本結構與打包方式,並簡單說明了外掛的組成與開發時的注意事項。

這一篇文章,我們將透過一個簡單的實作範例,實際動手打包一個 kintone 外掛,帶大家從寫好一段基本的 JavaScript 客製化邏輯開始,逐步轉換成可安裝的外掛檔案。

範例介紹:根據條件加總表格中的值

在 kintone 中,我們可以透過「表格外」的計算欄位搭配公式 SUM(表格中的數值欄位代碼) 來加總表格中特定的數值欄位。例如下圖中應用程式的「總計」欄位,就是使用 SUM(金額) 公式,自動加總明細表格中的「金額」欄位:

然而,如果我們希望計算出「表格中分類為交通費的金額總和」,就無法單純透過自動計算欄位來達成。

常見的誤解是,嘗試在「表格外」的計算欄位中使用 IF(條件, 條件為真的值, 條件為假的值),像是:

IF(分類="交通費", SUM(金額), 0)

但目前 kintone 的自動計算公式不支援這種跨表格欄位條件判斷的寫法。詳情可參考官方說明文件:kintone 說明 - 計算公式的錯誤訊息

替代方案:在表格內使用條件欄位

比較常見的替代方案,是在「表格內」增加一個新的計算欄位,來進行條件判斷。例如新增一個「交通費金額」欄位,公式如下:

IF(分類="交通費", 金額, 0)

接著再於「表格外」的計算欄位使用 SUM(交通費金額) 來加總這個欄位的值。
雖然這樣做可以達成功能,但也會讓表格變得較長、欄位數量變多,在視覺上較為混亂。

讓畫面簡潔的作法:使用 JavaScript 客製化

若希望畫面更簡潔、避免出現過多中間欄位,就可以透過 JavaScript 客製化,透過程式碼運算來實現條件加總。或者也採用 顯示/隱藏欄位的 API 將不必要的欄位隱藏,避免干擾使用者操作。

在本篇範例中,我們將採用 JavaScript 客製化的方式,來實作一個簡單的條件分類加總功能,並進一步將它打包成可重複使用的 kintone 外掛。

範例應用程式

以下是一個簡單的範例應用程式,我們希望達到的效果是:

在明細表格中輸入分類(交通費、住宿費、餐飲費)與金額後,系統能夠自動計算出每個分類的小計,並顯示在表格外的「交通費小計」、「住宿費小計」、「餐飲費小計」三個欄位中。

應用程式欄位

欄位名稱 欄位代碼 欄位類型 備註
申請日 申請日 日期
標題 標題 單行文字方塊
申請人 申請人 選擇使用者
明細 明細 表格
分類 分類 下拉式選單 表格「明細」中的欄位。選項內容:交通費、住宿費、餐飲費。
項目 項目 單行文字方塊 表格「明細」中的欄位。
金額 金額 數值
總計 總計 計算 計算公式:SUM(金額)
交通費小計 交通費小計 數值
住宿費小計 住宿費小計 數值
餐飲費小計 餐飲費小計 數值

範例程式碼

以下是一段簡單的客製化程式碼,透過觸發表格「明細」以及表格中欄位「分類」、「金額」改變時的 kintone event,在輸入分類與金額時自動加總數值,並輸出到對應的小計欄位中:

(() => {
  'use strict'

  const tableField = '明細'
  const inputFields = ['分類', '金額']
  const outputFields = ['交通費小計', '住宿費小計', '餐飲費小計']

  kintone.events.on([
    'app.record.create.show', 'app.record.edit.show',
    'mobile.app.record.create.show', 'mobile.app.record.edit.show',
    createChangeEvents([...inputFields, tableField]).flat()
  ], event => {
    const { record } = event
    const table = record[tableField].value

    // 禁止編輯小計欄位
    outputFields.forEach(field => {
      if (record[field]) record[field].disabled = true
    })

    // 根據條件分類加總計算表格中的金額
    const subtotal = Object.fromEntries(outputFields.map(field => [field, 0]))

    table.forEach(row => {
      const category = row.value['分類'].value
      const price = Number(row.value['金額'].value) || 0

      if (category === '交通費') {
        subtotal['交通費小計'] += price
      } else if (category === '住宿費') {
        subtotal['住宿費小計'] += price
      } else if (category === '餐飲費') {
        subtotal['餐飲費小計'] +=  price
      }
    })

    // 將小計值輸出到指定的欄位
    outputFields.forEach(field => {
      record[field].value = subtotal[field]
    })

    return event
  })

  // 建立 change events 用
  function createChangeEvents(fieldCodes) {
    if (!Array.isArray(fieldCodes)) return
    
    return fieldCodes.map(fieldCode => [
      `app.record.create.change.${fieldCode}`,
      `app.record.edit.change.${fieldCode}`,
      `mobile.app.record.create.change.${fieldCode}`,
      `mobile.app.record.edit.change.${fieldCode}`,
      `app.record.index.edit.change.${fieldCode}`
    ]).flat()
  }
})()

這段程式碼的邏輯並不複雜,主要是根據使用者輸入的分類與金額,分別加總出三個分類的總金額,並自動填入至表格外的對應欄位。同時也透過 disabled 屬性,讓使用者無法編輯這些欄位,使其更像是自動計算欄位的行為。

雖然這樣的程式碼很實用,但若未來要更改欄位代碼、分類項目,或是希望將功能套用到不同的應用程式,就必須每次都手動修改原始碼,稍嫌不便。

這時候,我們就可以考慮把這段程式碼封裝成一個可重複使用的 kintone 外掛,讓設定項目可以由非工程師透過圖形介面調整。

下一段,我們就會開始動手,嘗試把這段功能封裝成可安裝的外掛檔案。

範例實作

Step 1: 建立專案架構

首先,建立專案資料夾以及外掛所需的基本檔案:

demo-plugin/                 // 專案根目錄
├── src/                      // 將需打包的檔案集中在此資料夾下
│   ├── js/                   
│   │   ├── config.js         // 外掛設定畫面的程式碼
│   │   └── customize.js      // 反映於應用程式中的程式碼
│   ├── html/                 
│   │   └── config.html       // 外掛設定畫面的 HTML 結構
│   ├── image/                
│   │   └── icon.png          // 外掛的圖示
│   └── manifest.json         // 外掛設定的中樞,定義所有檔案與基本資訊
└── package.json

請先建立專案資料夾「demo-plugin」,並於其中執行 npm init -y 產生 package.json。接著,依照上方架構建立相對應的資料夾與檔案。

config.html

這個檔案是外掛設定畫面的 HTML 結構,這裡我們先簡單放上一個作為 container 的元素:

<div id="plugin-setting-container"></div>

config.js

此檔案負責設定畫面的程式邏輯。為了演示效果,這裡先不處理實際設定功能,僅顯示目前的外掛 ID:

((PLUGIN_ID) => {
  'use strict'

  const container = document.querySelector('#plugin-setting-container')
  
  const pluginIdText = document.createElement('p')
  pluginIdText.textContent = `Plugin ID: ${PLUGIN_ID}`

  container.appendChild(pluginIdText)
  
})(kintone.$PLUGIN_ID)

同上篇文章中「外掛ID的參照範例」的部分所述,當一個應用程式中使用了多個外掛時,kintone.$PLUGIN_ID 可能會被多次賦值,因此建議使用立即執行函式(IIFE)來補捉並使用當下的外掛 ID,並封裝邏輯。

customize.js

這是實際作用於應用程式的客製化程式碼,可將先前撰寫的範例程式碼原封不動搬過來。

icon.png

圖示為打包時必備的檔案,請選擇一張合適的圖片(建議為正方形 PNG 格式)。

manifest.json

這是整個外掛的設定檔,定義了基本資訊與載入的檔案:

{
  "manifest_version": 1,
  "version": 1,
  "type": "APP",
  "name": {
    "ja": "サンプルプラグイン",
    "en": "sample plugin",
    "zh-TW": "範例外掛"
  },
  "description": {
    "ja": "これはサンプルプラグインです。",
    "en": "This is sample plugin.",
    "zh-TW": "這是一個範例外掛"
  },
  "icon": "image/icon.png",
  "desktop": {
    "js": [
      "js/customize.js"
    ]
  },
  "mobile": {
    "js": [
      "js/customize.js"
    ]
  },
  "config": {
    "html": "html/config.html",
    "js": [
      "js/config.js"
    ]
  }
}

Step 2:安裝 plugin-packer

在專案根目錄下執行以下指令,安裝 kintone 官方提供的打包工具:

npm install -D @kintone/plugin-packer

Step 3:設定打包指令並執行

為了簡化後續打包流程,我們可以將指令寫入 package.jsonscripts 區段:

"scripts": {
    "pack:new": "kintone-plugin-packer --out dist/plugin.zip src",
    "pack": "kintone-plugin-packer --out dist/plugin.zip --ppk dist/private.ppk src"
  }

初次打包時,執行:

npm run pack:new

上述指令會將打包後的外掛壓縮檔 plugin.zip 輸出至 dist 資料夾,並同時產生一個密鑰檔(預設為 PLUGIN_ID.ppk)。請將該密鑰檔重新命名為 private.ppk,日後即可使用下列指令重新打包:

npm run pack

至此,我們已成功將原本的客製化功能封裝為一個 kintone 外掛。接下來,你可以將 plugin.zip 上傳至 kintone 並安裝使用。安裝後,請記得將應用程式中原本的客製化程式碼移除,再啟用此外掛。

打開外掛設定畫面,應該可以看到顯示外掛 ID 的字樣:

更新應用程式後,回到記錄編輯畫面進行測試,應可如預期執行分類加總的功能。

目前外掛中的欄位代碼仍為寫死的狀態,因此若應用程式的欄位有所變動,仍需修改程式碼。為了讓外掛更具彈性與可調整性,下一篇文章將介紹如何透過外掛設定頁面及相關 API,讓使用者可以自行設定欄位代碼與分類項目。


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言