跳至主要内容

視圖插件開發文檔


本文檔是供開發者閱讀的「視圖」插件開發文檔,需要開發者具備前端開發基礎,掌握Javascript、CSS和HTML等相關知識。

一、關於視圖插件


1. 什麼是視圖插件?

視圖插件又叫「自定義視圖」,當niio的表格、看板、層級、日曆、畫廊、詳情、甘特圖等系統視圖不能滿足使用者視圖展示需求的時候,開發者可以通過自己編寫程式碼實現一個完全自定義的視圖頁面,用於展示工作表的資料資料。自定義視圖支持搜索、篩選、統計、快速篩選和篩選列表等操作,還可以通過niio公共Javascript接口實現調用系統組件,比如展示資料詳情彈窗、調用新建資料窗口等等。

2. 視圖插件和系統視圖有什麼區別?

從使用者的角度看,視圖插件和普通視圖是沒有任何區別的。當組織管理員通過發佈開發者插件、安裝插件或者匯入插件後,所有已啓用的插件即對組織下的所有使用者生效,使用者可以像使用表格、看板、日曆等系統視圖一樣使用這些視圖,也可以正常的為視圖分配權限和進行視圖分享等操作。

二、開發步驟


1. 準備工作

  • 安裝 Node.js(>=16.20) 和 npm
  • 準備集成開發環境(IDE),推薦 VS Code
  • 如果是團隊開發,請準備好程式碼版本管理工具,推薦 Git

2. 創建視圖插件

要創建一個視圖插件,有兩種方式。

2.1 創建自定義視圖

通過在新建視圖時,創建一個「自定義視圖」,此時系統會自動創建一個視圖插件,並以當前工作表為該視圖的開發調試環境。

2.2 在插件中心製作插件

在系統首頁進入「插件中心」

在插件「我開發的」頁面中點擊「製作插件」

通過此方式創建插件時,仍然需要選擇一張工作表作為開發調試環境,選擇後會自動在該工作表下創建一個新的視圖用於開發調試視圖插件。

創建好插件後,進入到工作表下新創建的這個自定義視圖,可以進行下一步開發。

2.3 插件需求分析

在製作視圖插件之前,一定要對要開發的視圖進行需求分析,明確視圖的適用範圍,並通過設計合理的設定項來提高視圖插件的通用性。

比如,有兩張工作表:訂單和訂單明細。

訂單表訂單明細表
訂單明細表

現在開發者想自己開發一個視圖,將「訂單明細」的資料顯示到主「訂單」的表格中,主訂單的資料將以合併儲存格的方式同時展示兩張表的資料,大概類似這樣:

首先,在功能實現上,我們可以采取先加載主表格,再通過異步的方式獲取子表資料進行加載。

其次,經過分析,這個視圖插件如果要做到有一定的通用性,給任意一個工作表都能使用,那就需要增加一些使用者可以自由設定的內容:

  • 這個視圖表格的顯示欄位和順序是允許使用者自行調整設定的;
  • 一個工作表可能存在多張子表,那麼就需要使用者設定要把哪張子表展示到主表格中;

通過對視圖需求的整理,可以讓使用者更加明確開發的目標和實現的邊界,也更容易將插件做到適應更多的通用場景,降低開發的成本。

2.4 插件基礎設定

2.4.1 圖標和名稱

插件的名稱建議能準確表達視圖的作用,且不用帶“視圖”二字,比如:「地圖」、「思維導圖」、「樹型表格」等等。

圖標可以使用自定義圖標,這個相當於插件的logo。

2.4.2 功能啓用

視圖插件允許使用者自由選擇是否啓用「快速篩選」和「篩選列表」。當啓用後,視圖使用者在操作了快速篩選項和篩選列表後,系統將嚮插件發送事件觸發消息,開發者需要在插件中增加事件處理,並通過傳入的篩選條件處理資料篩選邏輯。

可以參考本文檔附錄中的 mdye消息系統 示例程式碼。

2.4.3 定義視圖設定項參數
子表設定
主表顯示欄位明細表欄位參數映射設定

在以上這個示例中,我們定義了兩個視圖設定項:「顯示欄位(showFields)」和「子表明細欄位(sub)」,分別對應兩個設定的需求。在「參數映射」中,開發者可以將實際的視圖設定映射到欄位裡,並在程式碼中通過 env 變量獲取到它的值。參考程式碼:

import { env } from "mdye";
const { showFields, sub } = env;
// showFields, sub 即為使用者設定的值,變量名稱和設定中的變量ID一一對應

設定項參數有如下幾種類型:

設定項類型子類型值類型備注
欄位選擇器欄位單選
欄位多選
array[string]欄位多選時,可以限製選擇欄位的數量
字符串string
數值double
枚舉值(Enum)單選框
下拉菜單
array[string]選項格式為 key=value ,其中 vlaue 為呈現給使用者的文字,key 為程式碼中獲取到的值;
樣式為單選框時,可以選擇橫嚮或竪嚮排列;
布爾值開關
勾選框
boolean
分組標題null

2.5 創建本地項目

接下來,切換到「開發調試」面板,我們將根據嚮導創建一個本地項目,並將本地項目運行在調試工作表中。

2.5.1 選擇腳手架範本

開始本地開發前,需要先選擇一個內置的腳手架範本,在本地執行初始化命令時會創建對應的範本文件。目前系統提供了以下範本供選擇:

  • React 基礎示例範本
  • JavaScript 基礎示例範本
  • React + Tailwind CSS 範本
  • Vue 3 範本
  • Vue 2 範本
2.5.2 安裝 mdye cli 命令行工具

本地項目的初始化創建是通過niio的命令行工具 mdye 來實現的,所以需要事先全局安裝這個工具。mdye 是 MingDaoYun Extensions 的首字母縮寫。

請在計算機終端命令行用以下命令安裝:

$ npm install -g mdye-cli

如果報沒有權限的錯誤,請用 sudo 來安裝:

$ sudo npm install -g mdye-cli

安裝完成後,可以用下面的命令來驗證是否安裝成功:

$ mdye --version
beta-0.0.34

如果能正常輸出版本號,則表示安裝成功。這個工具的安裝通常來說是一次性的,即後續開發新插件時無需再次安裝該工具。如果該工具將來有新版本,則可以重新安裝該工具進行升級。

mdye 完整的命令如下:

Usage: mdye [options] [command]

Options:
-v, --version 查看 mdye 版本
-h, --help 幫助

Commands:
auth [options] mdye auth 授權登入
init [options] mdye init view --id <id> --template <template-name> 初始化項目,請從web端復製命令
start [options] mdye start 開始開發
build [options] mdye build
push [options] mdye push -m <message> 提交插件
whoami [options] mdye whoami
logout [options] mdye logout 注銷當前環境帳號
sync-params [options] mdye sync-params -f <file-path> 同步插件參數設定,-f 非必填 默認文件路徑為 ./.config/params-config.json
help [command] 子命令幫助
2.5.3 初始化本地項目

在「建立本地項目」步驟中復製創建插件本項目的命令,在本地終端中執行。

你可以自定義本地項目文件夾名稱,直接輸入Enter鍵則使用系統給定的文件夾名稱。

接下來需要啓動本地項目,我們先進入插件本地項目文件夾,然後打開 VS Code,接下來的所有插件開發操作都在VS Code中完成:

$ cd mdye_view_6541abe07a43f661079c234f  #進入項目文件夾
$ code . #在 VS code 中打開項目

在VS Code中打開項目後,從菜單「終端>新建終端」新建一個「終端」窗口,依次輸入以下命令:

$ npm i        #安裝項目依賴
$ mdye start #啓動本地項目調試

執行之後,可以看到如下畫面:

2.5.4 調試本地插件

項目運行成功後,會生成一個本地伺服器js文件,把這個地址填入插件的「調試」頁面,並點擊「加載」:

此時,視圖頁面會動態渲染該視圖插件,我們在初始化腳本裡寫一了些簡單的和niio工作表交互的方法:

你可以嘗試修改 src/App.js 文件並儲存,由於採用了熱更新技術,所以程式碼修改儲存後會在視圖上實時生效。

2.5.5 設定程式碼級環境參數

環境參數對於插件的作用,主要是存放一些在程式碼級別會用到的開發設定,用於將來插件上架後被安裝時或匯出到別的環境中時可以更換設定值。例如開發者在開發視圖插件時使用了一個付費的第三方組件(前端),在開發時開發者填入的是自己的 license Key。開發者希望使用者安裝或匯入插件後可以使用自己的 Key不要共享開發者的付費授權。此時就可以使用環境參數設定來處理。

但是這個設定不是視圖的使用者要去關心的,也不必在每次使用插件視圖時都設定這個 key。所以它是一個管理員級別的只需要設定一次的值。

這個參數是JSON格式,它會直接被注入在 mdye.env 的參數中,開發者在編寫環境參數時,注意不要和設定項參數的ID重復。

2.6 編寫插件程式碼

2.6.1 文件結構

視圖插件是通過嵌入的 iframe 加載使用者本地提交的腳本渲染頁面來實現。

📢 需要特別注意的是,行動端的適配需要開發者自行實現。如果行動端有特殊的設定,也可以增加一些設定項參數來處理。開發者可以通過 UA 來判斷是否處於行動端設備中。

2.6.2 程式碼調試

視圖插件程式碼的開發和調試,和普通前端項目並無不同,開發者可以在瀏覽器使用 WebDevTools 進行程式碼的跟蹤與調試。

2.6.3 與niio資料的交互

我們提供了一個 mdye 的 npm 包來實現與niio應用工作表資料的交互。腳手架中已經默認安裝了此依賴,如果你要手動安裝,可以在項目中使用如下命令安裝:

$ npm i mdye --save

在項目程式碼中,引入 mdye

import { env, config, api, utils } from "mdye";

mdye 提供了4個對象:

  • env 用於獲取視圖設定項參數
  • config 用於獲取當前應用、工作表、視圖相關的設定
  • api 提供一係列的方法與niio工作表的資料進行交互
  • utils 調用niio公共組件

詳細用法可以查看本文檔附錄中的 JSSDK API

2.7 提交本地程式碼

在視圖插件開發過程中,可以隨時將編譯後的插件程式碼提交到伺服器端儲存。如果自定義視圖使用了某個已提交的版本作為當前程式碼(需要清除本地加載的調試文件),則其他任何有該視圖訪問權限的人都將看到在伺服端渲染的視圖。而在「調試」模式下的本地地址,是只有開發者本人可以預覽到視圖的樣式的。

提交本地程式碼使用如下命令:

$ mdye push -m "首次提交demo"

此時,如果開發者還沒有登入授權到本地項目,則系統會彈出授權頁面進行自動授權。然後,會自動編譯和打包程式碼,並以當前登入的使用者身份進行程式碼提交。

提交成功後,在插件的「提交」歷史中可以查看到提交的程式碼。

2.8 發佈插件

視圖插件在發佈之前,只能在調試應用中被使用。如果想要全組織都可以使用開發好的插件,則需要將插件發佈到組織。插件的發佈是基於開發者提交的程式碼的,開發者可以選擇將某次提交的程式碼發佈為一個正式的版本。發佈時,需要定義版本號,且每次發佈的版本號只能大於當前的版本號。

如果管理員在「插件中心」中啓用了該插件,則該插件將對全組織下的成員生效,此時所有人都可以在創建應用時使用該視圖。

此時為工作表「增加視圖」時就會出現視圖插件的可選項:

2.9 插件管理

在「插件中心」,可以對開發中和已發佈的插件進行管理。

「我開發的」列表中的插件為開發者自己創建的插件。可以在此為插件增加調試應用、查看提交歷史與發佈歷史、發佈新的插件版本。

「組織」列表中是組織下已經發佈的所有插件。可以在此查看插件的發佈歷史,對插件進行環境參數設定,以及查看插件的使用明細。管理員也可以在此發佈新版本對插件升級和迴滾插件版本。

注:普通使用者(非組織應用管理員)對組織下的插件只有列表查看權限,沒有管理權限

2.10 插件的匯出匯入

開發者可以在「插件中心」裡將已發佈的某個插件匯出為一個 .mdye 文件,然後分發給其他組織或私有部署使用者,後者在「插件中心」裡將其匯入後便可以使用該插件。

2.10.1 匯出插件

開發者在「插件中心」>「我開發的」插件列表中,可以從插件詳情中的「發佈歷史」裡點擊「匯出」按鈕將插件匯出。

開發者在匯出視圖插件時,通過設定授權密鑰,可以對插件匯入者作出如下限製:

  1. 設定匯入密碼。當匯入插件時輸入的密碼不正確,將無法匯入該插件;
  2. 設定授權到期時間。當匯入的插件到達授權期限時,全組織內該插件都將不可用;
  3. 設定授權組織。當匯入者所在組織不在密鑰內時,將無法匯入該插件;
  4. 設定私有部署授權伺服器。當匯入者所在伺服器不在密鑰內時,將無法匯入該插件。

以上設定組合使用時,需要滿足所有條件,插件才能被匯入並正常使用。

匯出成功後,開發者可以在「匯出歷史」中下載匯出文件,並可以查看該文件的密鑰資訊:

2.10.2 匯入插件

要匯入插件時,可以在「組織」頁面點擊「+匯入」按鈕,選擇一個 .mdye 文件進行匯入。

如果插件設定了匯出密鑰,則需要輸出正確的密碼後才能被匯入。如果插件設定了指定的組織、伺服器,則在不滿足環境要求時會匯入失敗。

匯入成功後,可以在組織內啓用該插件,此時組織內均可使用該插件。匯入的插件不可再次匯出。

在匯入插件時,如果組織內已經有同源的插件,則可以選擇在原有的插件上進行升級或者是創建一個新的插件(通常用於測試插件新版本)

2.11 上架到插件庫與安裝插件

暫未開放上架到插件庫功能,敬請期待。


三、附錄一:mdye JSSDK API

1. mdye.env

{
"env": {
"fields": ["controlId"], // 欄位選擇器
"string": "string", // 字符串
"numeric": 10, // 數值
"enum": ["key"], // 枚舉值
"boolean": true, // 布爾值
}
}

2. mdye.config

{
"config": {
"appId": "string", // 當前應用ID
"worksheetId": "string", // 當前工作表ID
"projectId": "string", // 當前組織ID
"viewId": "string", // 當前視圖ID
"filters": [{}], // 當前視圖的篩選條件
"query": {}, // 當前頁面的 url query 參數
"controls": [{ // 當前視圖下的欄位設定資訊
"controlId": "string",
"controlName": "string",
......
}],
"worksheetInfo": {...} // 當前工作表設定資訊
"currentAccount": { // 當前使用者
"accountId": "", // 使用者id
"fullname": "", // 使用者名稱
"avatar": "", // 使用者頭像
"lang": "", // 使用者語言
},
}
}

3. mdye.api

使用 VS Code 等支持程式碼提示的編輯器時編輯器會自動顯示使用方法

3.1 getFilterRows(params )

獲取工作表列資料

參數

  • params:參數對象,包含以下屬性:
    • params.worksheetId:工作表的ID,類型為字符串。
    • params.viewId:視圖的ID。
    • params.pageSize:每頁返回的資料數量。
    • params.pageIndex:要返回的頁碼。
    • params.sortId:排序欄位的ID。
    • params.isAsc:指示排序方式是否為升序。
    • params.notGetTotal:當設定為true時,接口將不返回總資料數,以提高接口速度。

3.2 getFilterRowsTotalNum(params )

獲取工作表列資料數

參數

  • params:參數對象,包含以下屬性:
    • params.worksheetId:工作表的ID,類型為字符串。
    • params.viewId:視圖的ID。
    • params.pageSize:每頁返回的資料數量。
    • params.pageIndex:要返回的頁碼。

3.3 getRowDetail(params )

獲取列資料詳情

參數

  • params:參數對象,包含以下屬性:
    • params.appId:應用ID。
    • params.worksheetId:工作表的ID。
    • params.viewId:視圖的ID。
    • params.rowId:資料的ID。
    • params.getTemplate:返回對應表資訊。

3.4 getRowRelationRows(params )

獲取資料的關聯資料 獲取資料的子表資料

參數

  • params:參數對象,包含以下屬性:
    • params.controlId:關聯資料欄位或子表欄位的controlId。
    • params.rowId:資料的ID。
    • params.worksheetId:當前表(關聯資料欄位或子表欄位所在表)的ID。
    • params.keywords:搜索資料關鍵字。
    • params.pageSize:每頁數量。
    • params.pageIndex:頁碼。
    • params.getWorksheet:對應關聯表對象。

3.5 addWorksheetRow(params )

創建資料

參數

  • params:參數對象,包含以下屬性:
    • params.appId:應用的ID。
    • params.worksheetId:工作表的ID。
    • params.receiveControls:欄位資料,具體格式可以在瀏覽器DevTools裡看web端同名接口或更新欄位資料示例

3.6 updateWorksheetRow(params )

更新列資料

參數

  • params:參數對象,包含以下屬性:
    • params.appId:應用的ID。
    • params.worksheetId:工作表的ID。
    • params.rowId:資料的ID。
    • params.newOldControl:欄位資料,具體格式可以在瀏覽器DevTools裡看web端同名接口或更新欄位資料示例

3.7 deleteWorksheetRow(params )

刪除列資料

參數:

  • params:參數對象,包含以下屬性:
    • params.appId:應用的ID。
    • params.worksheetId:工作表的ID。
    • params.rowIds:資料ID列表。

4. mdye.utils[]

4.1 openRecordInfo(params )

打開資料詳情彈窗

參數

  • params:參數對象,包含以下屬性:
    • params.appId:應用id
    • params.worksheetId:工作表id
    • params.viewId:視圖id
    • params.recordId:資料id

返回

返回 Promise,Promise結果是更新後的資料。

{
action: "update",
value: {
rowid: string,
[key: string]: any
}
}

4.2 openNewRecord(params )

打開創建資料彈窗

參數

  • params:參數對象,包含以下屬性:
    • params.appId:應用id
    • params.worksheetId:工作表id

返回

返回 Promise,Promise結果是新建的資料。

{
rowid: string,
[key: string]: any
}

4.3 selectUsers(params )

選擇人員

參數

  • params:參數對象,包含以下屬性:
    • params.projectId:組織id[可選]默認當前應用所在組織
    • params.unique:只能選一個

返回

返回 Promise,Promise結果是選擇的人員。

[{
accountId: string,
avatar: string,
fullname: string
}]

4.4 selectDepartments(params )

選擇部門

參數

  • params:參數對象,包含以下屬性:
    • params.projectId:組織id【可選】默認當前應用所在組織
    • params.unique:只能選一個

返回

返回 Promise,Promise結果是選擇的部門。

[{
departmentId: string,
departmentName: string
}]

4.5 selectOrgRole(params )

選擇組織角色

參數:

  • params:參數對象,包含以下屬性:
    • params.projectId:組織id【可選】默認當前應用所在組織
    • params.unique:只能選一個

返回

返回 Promise,Promise結果是選擇的組織。

[{
organizeId: string,
organizeName: string
}]

4.6 selectRecord(params )

選擇資料

參數

  • params:參數對象,包含以下屬性:
    • params.projectId:組織id【可選】默認當前應用所在組織
    • params.relateSheetId:對應的工作表 id
    • params.multiple:選多個

返回

返回 Promise,Promise結果是選擇的資料。

[{
rowid: string,
[key: string]: any
}]

4.7 selectLocation(params )

選擇地圖定位

參數

* `params`:參數對象,包含以下屬性:
* `params.distance`:距離
* `params.defaultPosition`:默認位置 {lat, lng}
* `params.multiple`:選多個

返回

返回 Promise,Promise結果是選擇的地點。

[{
address: string,
lat: string,
lng: string,
name: string
}]

4.8 更新欄位資料示例


[
{
"controlId": "661514c080547873603db341",
"type": 2,
"value": "NIIO",
"controlName": "文字"
},
{
"controlId": "6615151480547873603db355",
"type": 6,
"value": "11",
"controlName": "數字"
},
{
"controlId": "6615151480547873603db356",
"type": 8,
"value": "12.00",
"controlName": "金額",
"dot": 2
},
{
"controlId": "6615151480547873603db357",
"type": 5,
"value": "hello@niio.com",
"controlName": "郵箱"
},
{
"controlId": "6615151480547873603db358",
"type": 15,
"value": "2024-04-10",
"controlName": "日期"
},
{
"controlId": "6615151480547873603db359",
"type": 46,
"value": "14:22:00",
"controlName": "時間"
},
{
"controlId": "6615151480547873603db35a",
"type": 3,
"value": "+886999123456",
"controlName": "手機"
},
{
"controlId": "6615151480547873603db35c",
"type": 11,
"value": "[\"a49c9652-5551-4c3d-8fca-cb70bb009b08\"]", // 這裡的key是選項欄位 options 裡 option 對象下的 key 屬性,欄位資料在 mdye.config.controls 裡
"controlName": "單選"
},
{
"controlId": "6615151480547873603db35d",
"type": 10,
"value": "[\"e978414d-ccee-4cde-8e45-780d27afa8e7\",\"9ace844e-e737-4a8f-9362-96e4c83ebe91\"]", // 這裡的key是選項欄位 options 裡 option 對象下的 key 屬性,欄位資料在 mdye.config.controls 裡
"controlName": "多選"
},
{
"controlId": "6615151480547873603db35e",
"type": 26,
"value": "[{\"accountId\":\"60149342-0453-4c8c-bae9-6120c62ac58d\"}]",
"controlName": "成員"
},
{
"controlId": "6615151480547873603db35f",
"type": 27,
"value": "[{\"departmentId\":\"0a192f2b-7ad0-40b4-b6f8-db3f502e00a5\"}]",
"controlName": "部門"
},
{
"controlId": "6615151480547873603db360",
"type": 48,
"value": "[{\"organizeId\":\"52b067a8-696e-4e68-9473-be415cef1cd1\"}]",
"controlName": "組織角色"
},
{
"controlId": "6615151480547873603db362",
"type": 36,
"value": "1", // 選中 '1' 未選中 '0'
"controlName": "檢查項"
},
{
"controlId": "6615151480547873603db363",
"type": 28,
"value": 3,
"controlName": "等級"
},
{
"controlId": "6615151480547873603db364",
"type": 41,
"value": "<h2>mingdao</h2>",
"controlName": "富文本"
},
{
"controlId": "6615151480547873603db365",
"type": 7,
"value": "321324200001010101",
"controlName": "證件"
},
{
"controlId": "6615151480547873603db366",
"type": 40,
"value": "{\"x\":121.519250,\"y\":25.047519,\"address\":\"台北火車站\",\"title\":\"\"}", // x 經度 y 緯度
"controlName": "定位"
},
{
"controlId": "6615151480547873603db367",
"type": 25,
"value": "壹拾貳元",
"controlName": "大寫金額"
},
{
"controlId": "6615151480547873603db368",
"type": 29,
"value": "[{\"sid\":\"df201312-5606-4d56-8342-ec2b00f9bf89\"}]", // 資料的 rowid
"controlName": "emitter"
},
{
"controlId": "6615151480547873603db36a",
"type": 35,
"value": "[{\"sid\":\"def7cd2a-d6c2-4524-bcd6-2b1df58ed6a9\"}]", // 對應層級資料的 rowid
"controlName": "層級選擇"
}
]

4.9 mdye 消息系統

mdye 支持以發佈訂閱的模式響應插件外部的操作,比如當視圖篩選發生變化時插件可以接收到變更後的篩選值去重新請求資料。

import React, { useEffect, useState, useCallback } from "react";
import { env, config, api, utils, md_emitter } from "mdye";
import { parseEnv } from "./utils";
const { getFilterRows } = api;

export default function App() {
const { appId, worksheetId, viewId, controls } = config;
const mapViewConfig = parseEnv(env);
const { loadNum } = mapViewConfig;
const [records, setRecords] = useState([]);
const [filters, setFilters] = useState({});
async function loadRecords() {
const res = await getFilterRows({
worksheetId,
viewId,
pageIndex: 1,
pageSize: loadNum,
...filters,
});
setRecords(res.data);
}
const handleFiltersUpdate = useCallback((newFilers) => {
setFilters(newFilers);
}, []);
useEffect(() => {
loadRecords();
}, [filters]);
useEffect(() => {
md_emitter.addListener("filters-update", handleFiltersUpdate);
return () => {
md_emitter.removeListener("filters-update", handleFiltersUpdate);
};
}, []);
return <div>{records.length}</div>;
}

當前支持的事件:

  • filters-update 篩選條件變更
  • new-record 按鈕增加資料