視圖插件開發文檔
本文檔是供開發者閱讀的「視圖」插件開發文檔,需要開發者具備前端開發基礎,掌握Javascript、CSS和HTML等相關知識。
如果開發者熟悉 React JS,也可以通過查看HAP前端開源項目 https://github.com/mingdaocom/pd-openweb ,參考HAP係統視圖代碼進行插件的開發。
關於視圖插件
什麼是視圖插件?
視圖插件又叫「自定義視圖」,當HAP的錶格、看闆、層級、日曆、畫廊、詳情、甘特圖等係統視圖不能滿足用戶視圖展示需求的時候,開發者可以通過自己編寫代碼實現一個完全自定義的視圖頁麵,用於展示工作錶的記錄數據。自定義視圖支持搜索、篩選、統計、快速篩選和篩選列錶等操作,還可以通過HAP公共Javascript接口實現調用係統組件,比如展示記錄詳情彈窗、調用新建記錄窗口等等。
視圖插件和係統視圖有什麼區彆?
從使用者的角度看,視圖插件和普通視圖是沒有任何區彆的。當組織管理員通過發布開發者插件、安裝插件或者導入插件後,所有已啓用的插件即對組織下的所有用戶生效,用戶可以像使用錶格、看闆、日曆等係統視圖一樣使用這些視圖,也可以正常的為視圖分配權限和進行視圖分享等操作。
開發步驟
準備工作
- 安裝 Node.js(>=16.20) 和 npm
- 準備集成開發環境(IDE),推薦 VS Code
- 如果是團隊開發,請準備好代碼版本管理工具,推薦 Git
創建視圖插件
要創建一個視圖插件,有兩種方式。
1. 創建自定義視圖
通過在新建視圖時,創建一個「自定義視圖」,此時係統會自動創建一個視圖插件,並以當前工作錶為該視圖的開發調試環境。
2. 在插件中心製作插件
在係統首頁進入「插件中心」
在插件「我開發的」頁麵中點擊「製作插件」
通過此方式創建插件時,仍然需要選擇一張工作錶作為開發調試環境,選擇後會自動在該工作錶下創建一個新的視圖用於開發調試視圖插件。
創建好插件後,進入到工作錶下新創建的這個自定義視圖,可以進行下一步開發。
3. 插件需求分析
在製作視圖插件之前,一定要對要開發的視圖進行需求分析,明確視圖的適用範圍,並通過設計閤理的設置項來提高視圖插件的通用性。
比如,有兩張工作錶:訂單和訂單明細。
訂單錶 | 訂單明細錶 |
---|---|
現在開發者想自己開發一個視圖,將「訂單明細」的數據顯示到主「訂單」的錶格中,主訂單的數據將以閤並單元格的方式同時展示兩張錶的數據,大概類似這樣:
首先,在功能實現上,我們可以采取先加載主錶格,再通過異步的方式獲取子錶數據進行加載。
其次,經過分析,這個視圖插件如果要做到有一定的通用性,給任意一個工作錶都能使用,那就需要增加一些使用者可以自由配置的內容:
- 這個視圖錶格的顯示字段和順序是允許使用者自行調整配置的;
- 一個工作錶可能存在多張子錶,那麼就需要使用者配置要把哪張子錶展示到主錶格中;
通過對視圖需求的整理,可以讓用戶更加明確開發的目標和實現的邊界,也更容易將插件做到適應更多的通用場景,降低開發的成本。
4. 插件基礎設置
i. 圖標和名稱
插件的名稱建議能準確錶達視圖的作用,且不用帶“視圖”二字,比如:「地圖」、「思維導圖」、「樹型錶格」等等。
圖標可以使用自定義圖標,這個相當於插件的logo。
ii. 功能啓用
視圖插件允許用戶自由選擇是否啓用「快速篩選」和「篩選列錶」。當啓用後,視圖使用者在操作瞭快速篩選項和篩選列錶後,係統將嚮插件發送事件觸發消息,開發者需要在插件中添加事件處理句柄並通過傳入的篩選條件處理數據篩選邏輯。
可以參考本文檔附錄中的 mdye 消息係統 示例代碼。
iii. 定義視圖設置項參數
主錶顯示字段 | 明細錶字段 | 參數映射配置 |
在以上這個示例中,我們定義瞭兩個視圖設置項:「顯示字段(showFields
)」和「子錶明細字段(sub
)」,分彆對應兩個配置的需求。在「參數映射」中,開發者可以將實際的視圖配置映射到字段裏,並在代碼中通過 env
變量獲取到它的值。參考代碼:
import { env } from "mdye";
const { showFields, sub } = env;
// showFields, sub 即為使用者配置的值,變量名稱和配置中的變量ID一一對應
設置項參數有如下幾種類型:
設置項類型 | 子類型 | 值類型 | 備注 |
---|---|---|---|
字段選擇器 | 字段單選 字段多選 | array[string] | 字段多選時,可以限製選擇字段的數量 |
字符串 | string | ||
數值 | double | ||
枚舉值 | 單選框 下拉菜單 | array[string] | 選項格式為 key=value ,其中 vlaue 為呈現給使用者的文字,key 為代碼中獲取到的值; 樣式為單選框時,可以選擇橫嚮或竪嚮排列; |
布爾值 | 開關 勾選框 | boolean | |
分組標題 | null |
5. 創建本地項目
接下來,切換到「開發調試」麵闆,我們將根據嚮導創建一個本地項目,並將本地項目運行在調試工作錶中。
i. 選擇腳手架模闆
開始本地開發前,需要先選擇一個內置的腳手架模闆,在本地執行初始化命令時會創建對應的模闆文件。目前係統提供瞭以下模闆供選擇:
- React 基礎示例模闆
- JavaScript 基礎示例模闆
- React + Tailwind CSS 模闆
- Vue 3 模闆
- Vue 2 模闆
ii. 安裝 mdye cli 命令行工具
本地項目的初始化創建是通過HAP的命令行工具 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] 子命令幫助
iii. 初始化本地項目
在「建立本地項目」步驟中復製創建插件本項目的命令,在本地終端中執行。
你可以自定義本地項目文件夾名稱,直接迴車則使用係統給定的文件夾名稱。
接下來需要啓動本地項目,我們先進入插件本地項目文件夾,然後打開 VS Code,接下來的所有插件開發操作都在VS Code中完成:
$ cd mdye_view_6541abe07a43f661079c234f #進入項目文件夾
$ code . #在 VS code 中打開項目
在VS Code中打開項目後,從菜單「終端>新建終端」新建一個「終端」窗口,依次輸入以下命令:
$ npm i #安裝項目依賴
$ mdye start #啓動本地項目調試
執行之後,可以看到如下畫麵:
iv. 調試本地插件
項目運行成功後,會生成一個本地服務器js文件,把這個地址填入插件的「調試」頁麵,並點擊「加載」:
此時,視圖頁麵會動態渲染該視圖插件,我們在初始化腳本裏寫一瞭些簡單的和HAP工作錶交互的方法:
你可以嘗試修改 src/App.js
文件並保存,由於采用瞭熱更新技術,所以代碼修改保存後會在視圖上實時生效。
v. 配置代碼級環境參數
環境參數對於插件的作用,主要是存放一些在代碼級彆會用到的開發配置,用於將來插件上架後被安裝時或導齣到彆的環境中時可以更換配置值。例如開發者在開發視圖插件時使用瞭一個付費的第三方組件(前端),在開發時開發者填入的是自己的 license Key。開發者希望用戶安裝或導入插件後可以使用自己的 Key不要共享開發者的付費授權。此時就可以使用環境參數配置來處理。
但是這個配置不是視圖的使用者要去關心的,也不必在每次使用插件視圖時都配置這個 key。所以它是一個管理員級彆的隻需要配置一次的值。
這個參數是JSON格式,它會直接被注入在 mdye.env
的參數中,開發者在編寫環境參數時,注意不要和配置項參數的ID重復。
6. 編寫插件代碼
i. 文件結構
視圖插件是通過嵌入的 iframe 加載用戶本地提交的腳本渲染頁麵來實現。
📢 需要特彆注意的是,移動端的適配需要開發者自行實現。如果移動端有特殊的配置,也可以增加一些設置項參數來處理。開發者可以通過 UA 來判斷是否處於移動端設備中。
ii. 代碼調試
視圖插件代碼的開發和調試,和普通前端項目並無不同,開發者可以在瀏覽器使用 WebDevTools 進行代碼的跟蹤與調試。
iii. 與HAP數據的交互
我們提供瞭一個 mdye
的 npm 包來實現與HAP應用工作錶數據的交互。腳手架中已經默認安裝瞭此依賴,如果你要手動安裝,可以在項目中使用如下命令安裝:
$ npm i mdye --save
在項目代碼中,引入 mdye
:
import { env, config, api, utils } from "mdye";
mdye
提供瞭4個對象:
env
用於獲取視圖設置項參數config
用於獲取當前應用、工作錶、視圖相關的配置api
提供一係列的方法與HAP工作錶的數據進行交互utils
調用HAP公共組件
詳細用法可以查看本文檔附錄中的 JSSDK API 。
下麵是按上述示例需求開發完成的視圖插件的截圖:
7. 提交本地代碼
在視圖插件開發過程中,可以隨時將編譯後的插件代碼提交到服務器端保存。如果自定義視圖使用瞭某個已提交的版本作為當前代碼(需要清除本地加載的調試文件),則其他任何有該視圖訪問權限的人都將看到在服務端渲染的視圖。而在「調試」模式下的本地地址,是隻有開發者本人可以預覽到視圖的樣式的。
提交本地代碼使用如下命令:
$ mdye push -m "首次提交demo"
此時,如果開發者還沒有登錄授權到本地項目,則係統會彈齣授權頁麵進行自動授權。然後,會自動編譯和打包代碼,並以當前登錄的用戶身份進行代碼提交。
提交成功後,在插件的「提交」曆史中可以查看到提交的代碼。
8. 發布插件
視圖插件在發布之前,隻能在調試應用中被使用。如果想要全組織都可以使用開發好的插件,則需要將插件發布到組織。插件的發布是基於開發者提交的代碼的,開發者可以選擇將某次提交的代碼發布為一個正式的版本。發布時,需要定義版本號,且每次發布的版本號隻能大於當前的版本號。
如果管理員在「插件中心」中啓用瞭該插件,則該插件將對全組織下的成員生效,此時所有人都可以在搭建應用時使用該視圖。
此時為工作錶「添加視圖」時就會齣現視圖插件的可選項:
9. 插件管理
在「插件中心」,可以對開發中和已發布的插件進行管理。
「我開發的」列錶中的插件為開發者自己創建的插件。可以在此為插件添加調試應用、查看提交曆史與發布曆史、發布新的插件版本。
「組織」列錶中是組織下已經發布的所有插件。可以在此查看插件的發布曆史,對插件進行環境參數配置,以及查看插件的使用明細。管理員也可以在此發布新版本對插件升級和迴滾插件版本。
注:普通用戶(非組織應用管理員)對組織下的插件隻有列錶查看權限,沒有管理權限
10. 插件的導齣導入
開發者可以在「插件中心」裏將已發布的某個插件導齣為一個 .mdye
文件,然後分發給其他組織或私有部署用戶,後者在「插件中心」裏將其導入後便可以使用該插件。
i. 導齣插件
開發者在「插件中心」>「我開發的」插件列錶中,可以從插件詳情中的「發布曆史」裏點擊「導齣」按鈕將插件導齣。
開發者在導齣視圖插件時,通過設置授權密鑰,可以對插件導入者作齣如下限製:
- 設置導入密碼。當導入插件時輸入的密碼不正確,將無法導入該插件;
- 設置授權到期時間。當導入的插件到達授權期限時,全組織內該插件都將不可用;
- 設置授權組織。當導入者所在組織不在密鑰內時,將無法導入該插件;
- 設置私有部署授權服務器。當導入者所在服務器不在密鑰內時,將無法導入該插件。
以上設置組閤使用時,需要滿足所有條件,插件纔能被導入並正常使用。
導齣成功後,開發者可以在「導齣曆史」中下載導齣文件,並可以查看該文件的密鑰信息:
ii. 導入插件
要導入插件時,可以在「組織」頁麵點擊「+導入」按鈕,選擇一個 .mdye
文件進行導入。
如果插件設置瞭導齣密鑰,則需要輸齣正確的密碼後纔能被導入。如果插件設置瞭指定的組織、服務器,則在不滿足環境要求時會導入失敗。
導入成功後,可以在組織內啓用該插件,此時組織內均可使用該插件。導入的插件不可再次導齣。
在導入插件時,如果組織內已經有同源的插件,則可以選擇在原有的插件上進行升級或者是創建一個新的插件(通常用於測試插件新版本):
11. 上架到插件庫與安裝插件
暫未開放上架到插件庫功能,敬請期待。
附錄一:mdye
JSSDK API
mdye.env
{
"env": {
"fields": ["controlId"], // 字段選擇器
"string": "string", // 字符串
"numeric": 10, // 數值
"enum": ["key"], // 枚舉值
"boolean": true, // 布爾值
}
}
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": "", // 用戶語言
},
}
}
mdye.api
使用 VS Code 等支持代碼提示的編輯器時編輯器會自動顯示使用方法
getFilterRows(params)
獲取工作錶行記錄數據
參數:
params
:參數對象,包含以下屬性:params.worksheetId
:工作錶的ID,類型為字符串。params.viewId
:視圖的ID。params.pageSize
:每頁返迴的記錄數量。params.pageIndex
:要返迴的頁碼。params.sortId
:排序字段的ID。params.isAsc
:指示排序方式是否為升序。params.notGetTotal
:當設置為true時,接口將不返迴總記錄數,以提高接口速度。
getFilterRowsTotalNum(params)
獲取工作錶行記錄數
參數:
params
:參數對象,包含以下屬性:params.worksheetId
:工作錶的ID,類型為字符串。params.viewId
:視圖的ID。params.pageSize
:每頁返迴的記錄數量。params.pageIndex
:要返迴的頁碼。
getRowDetail(params)
獲取行記錄詳情
參數:
params
:參數對象,包含以下屬性:params.appId
:應用ID。params.worksheetId
:工作錶的ID。params.viewId
:視圖的ID。params.rowId
:記錄的ID。params.getTemplate
:返迴對應錶信息。
getRowRelationRows(params)
獲取記錄的關聯記錄 獲取記錄的子錶記錄
參數:
params
:參數對象,包含以下屬性:params.controlId
:關聯記錄字段或子錶字段的controlId。params.rowId
:記錄的ID。params.worksheetId
:當前錶(關聯記錄字段或子錶字段所在錶)的ID。params.keywords
:搜索記錄關鍵字。params.pageSize
:每頁數量。params.pageIndex
:頁碼。params.getWorksheet
:對應關聯錶對象。
addWorksheetRow(params)
創建記錄
參數:
params
:參數對象,包含以下屬性:params.appId
:應用的ID。params.worksheetId
:工作錶的ID。params.receiveControls
:字段數據,具體格式可以在瀏覽器DevTools裏看web端同名接口或更新字段數據示例 。
updateWorksheetRow(params)
更新行記錄
參數:
params
:參數對象,包含以下屬性:params.appId
:應用的ID。params.worksheetId
:工作錶的ID。params.rowId
:記錄的ID。params.newOldControl
:字段數據,具體格式可以在瀏覽器DevTools裏看web端同名接口或更新字段數據示例 。
deleteWorksheetRow(params)
刪除行記錄
參數:
params
:參數對象,包含以下屬性:params.appId
:應用的ID。params.worksheetId
:工作錶的ID。params.rowIds
:記錄ID列錶。
mdye.utils
openRecordInfo(params)
打開記錄詳情彈窗
參數:
params
:參數對象,包含以下屬性:params.appId
:應用idparams.worksheetId
:工作錶idparams.viewId
:視圖idparams.recordId
:記錄id
返迴:
返迴 Promise,Promise結果是更新後的記錄。
{
action: "update",
value: {
rowid: string,
[key: string]: any
}
}
openNewRecord(params)
打開創建記錄彈窗
參數:
params
:參數對象,包含以下屬性:params.appId
:應用idparams.worksheetId
:工作錶id
返迴:
返迴 Promise,Promise結果是新建的記錄。
{
rowid: string,
[key: string]: any
}
selectUsers(params)
選擇人員
參數:
params
:參數對象,包含以下屬性:params.projectId
:組織id[可選]默認當前應用所在組織params.unique
:隻能選一個
返迴:
返迴 Promise,Promise結果是選擇的人員。
[{
accountId: string,
avatar: string,
fullname: string
}]
selectDepartments(params)
選擇部門
參數:
params
:參數對象,包含以下屬性:params.projectId
:組織id[可選]默認當前應用所在組織params.unique
:隻能選一個
返迴:
返迴 Promise,Promise結果是選擇的部門。
[{
departmentId: string,
departmentName: string
}]
selectOrgRole(params)
選擇組織角色
參數:
params
:參數對象,包含以下屬性:params.projectId
:組織id[可選]默認當前應用所在組織params.unique
:隻能選一個
返迴:
返迴 Promise,Promise結果是選擇的組織。
[{
organizeId: string,
organizeName: string
}]
selectRecord(params)
選擇記錄
參數:
params
:參數對象,包含以下屬性:params.projectId
:組織id[可選]默認當前應用所在組織params.relateSheetId
:對應的工作錶 idparams.multiple
:選多個
返迴:
返迴 Promise,Promise結果是選擇的記錄。
[{
rowid: string,
[key: string]: any
}]
selectLocation(params)
選擇地圖定位
參數:
params
:參數對象,包含以下屬性:params.distance
:距離params.defaultPosition
:默認位置 {lat, lng}params.multiple
:選多個
返迴:
返迴 Promise,Promise結果是選擇的地點。
[{
address: string,
lat: string,
lng: string,
name: string
}]
更新字段數據示例
[
{
"controlId": "661514c080547873603db341",
"type": 2,
"value": "MINGDAO",
"controlName": "文本"
},
{
"controlId": "6615151480547873603db355",
"type": 6,
"value": "11",
"controlName": "數值"
},
{
"controlId": "6615151480547873603db356",
"type": 8,
"value": "12.00",
"controlName": "金額",
"dot": 2
},
{
"controlId": "6615151480547873603db357",
"type": 5,
"value": "hello@mingdao.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": "+8618777171711",
"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\":124.387523,\"y\":40.123234,\"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": "級聯選擇"
}
]
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
按鈕添加記錄