跳至主要内容

Tree 控制項

Tree 適合呈現商品分類、組織架構、資料夾、地區等階層資料。資料量少且穩定時,可直接在頁面結構中維護;資料由業務系統管理時,則可透過 API 載入根節點與子節點。Tree 也支援搜尋、節點操作與拖放排序。

1. 選擇實作方式​

情境建議設定
由頁面作者維護的少量固定項目靜態 Tree Nodes
由業務系統管理的階層資料Tree Data Source
新增、編輯、刪除或節點快捷操作動態資料來源 + Node Actions
調整同層順序或跨層移動動態資料來源 + onDrop

1.1 Tree:靜態商品分類​

靜態範例會先將 [[treeNode.name]] 寫入 Page Context 的 selectedCategory,再呼叫右側 ProTable 的 reloadTable。ProTable Query Context 則把 selectedCategory 對應到請求參數 category。

1.2 Tree02:動態載入與階層編輯​

Tree02 在頁面開啟時載入根節點,展開目錄時再以 parentId 請求直接子節點。它也示範伺服器搜尋、節點詳情、編輯、新增子節點、刪除及拖放儲存。建議依序完成根節點、延遲載入、搜尋、點擊聯動與節點操作,最後才啟用拖放。

2. 新增 Tree​

  1. 在 Page Designer 開啟目標頁面。
  2. 選取頁面結構中的 Screen。
  3. 在 Navigation Tree 子元素區域新增 Tree。
  4. 靜態資料新增 Tree Nodes;動態資料則在 Data Source 新增 Tree Data Source。
  5. 在 Panels 放置 ProTable、Form 或其他內容控制項。

Navigation Tree 會形成左側導覽區;Panels 與 Grids 會形成內容區,不需要另以 Grid 重建相同版面。

3. 設定 Tree​

屬性用途
Title樹狀導覽上方的選用標題
Width桌面版寬度;窄畫面會自動改為整列
Show Search顯示搜尋列
Checkable顯示核取方塊
Default Expand All初次顯示時展開已載入節點
Node Draggable允許拖放並觸發 onDrop

4. 靜態節點​

在 Tree Nodes 新增 TreeNode,設定 Title、Value、Disabled、Selectable、Checkable、Is Leaf、Icon 與 Extra Info。Value 應在整棵樹中保持唯一且穩定;後續互動會以它辨識節點。

靜態節點適合小型、固定的分類。若節點由後端維護,請改用動態資料來源,避免頁面設定與業務資料重複。

5. 動態資料來源​

在 Data Source 新增 TreeDataSource,先完成根節點請求:

設定Tree02 範例
Root Data URL/spark/api/sample/navigation/tree/nodes
Request MethodGET
Response List Pathdata.nodes
Title Fieldlabel
Value Fieldid
Children Fieldchildren
Leaf Fieldleaf
Extra Fieldcount

5.1 使用 Test 驗證請求​

按一下 Test。Test、設計畫布與執行頁面共用同一套 Tree 請求建構與回應解析邏輯,因此結果可直接協助判斷問題位於 URL、後端資料、Response List Path 或欄位對應。平台會自動攜帶:

X-Spark-Page-Environment: DEV
X-Spark-Page-Render-Mode: DESIGN_PREVIEW

Data Source 不需要手動加入這兩個 Header。完整規範請參閱頁面請求內容與後端偵錯。

後端應使用共用常數解析 Header,再依列舉值決定是否回傳穩定的設計預覽資料:

SparkPageRequestContext context = SparkPageRequestHeaders.resolve(
environment, renderMode);

boolean developmentPreview = context != null
&& context.getEnvironment() == SparkPageEnvironment.DEV
&& context.getRenderMode() == SparkPageRenderMode.DESIGN_PREVIEW;

不要以手寫字串、Map 或自訂查詢參數重複判斷模式。公開 Test 資料也不得包含正式環境密鑰、個資或敏感業務資料。

5.2 完整載入與延遲載入​

小型資料可在 children 中一次回傳完整階層。大型資料則設定 Child Nodes URL 與 Parent Parameter;展開非葉節點時,Tree 會把目前節點 Value 帶到例如 parentId 的參數。非葉節點必須回傳 leaf: false,即使當下的 children 是空陣列。

6. 搜尋​

靜態或已完整載入的資料可使用本機搜尋。延遲載入時建議設定 Search URL 與查詢參數,由後端搜尋未載入分支,並同時回傳命中節點的祖先路徑,避免結果成為沒有上下文的孤立葉節點。

7. 目前節點與事件內容​

事件常用資料
onClicktreeNode、selectedTreeNode
onChecktreeCheck.checkedKeys、treeCheck.checkedNodes
onExpandtreeExpand.expandedKeys、treeExpand.node
onLoadtreeLoad.node、treeLoad.children
onDroptreeDrop.dragNode、treeDrop.dropNode、treeDrop.position

常用路徑包含 treeNode.value、treeNode.title、treeNode.isLeaf 與 treeNode.data.category。Node Action 會更新目前節點,但不會同時觸發 Tree 的 onClick。

8. 設定節點操作​

在 Node Actions 新增 TreeNodeAction,設定標題、統一 Icon 選擇器、觸發方式、語意色彩、顯示條件與停用條件。More 按鈕與桌面右鍵選單共用同一份操作清單;觸控裝置使用 More,不依賴長按。

Edit、Add Child 與 Delete 可分別呼叫:

PUT /spark/api/sample/navigation/tree/nodes/[[treeNode.value]]
POST /spark/api/sample/navigation/tree/nodes
DELETE /spark/api/sample/navigation/tree/nodes/[[treeNode.value]]

請把互動設定在 Tree Node Action 本身,並只在請求成功後呼叫 reloadTree。隱藏操作不是安全邊界;後端仍須驗證權限與刪除規則。

9. 拖放與儲存​

啟用 Node Draggable 後,Tree 先完成畫面中的移動,再觸發 onDrop。treeDrop.position 可能為 BEFORE、INSIDE 或 AFTER。若要保留新順序,請在 onDrop 新增 Send Request:

POST /spark/api/sample/navigation/tree/reorder

參數分別為 dragId = [[treeDrop.dragNode.key]]、dropId = [[treeDrop.dropNode.key]]、position = [[treeDrop.position]]。請求失敗或中斷時,Tree 會回復到拖放前的結構。後端仍須檢查循環引用、父節點、權限與並行版本。

10. 點擊節點後重新整理內容​

靜態 Tree 的 onClick 應先執行 Set Page Context,將 [[treeNode.name]] 寫入 selectedCategory,再以 Component API 呼叫 ProTable 的 reloadTable。ProTable Query Context 將 category 對應到 selectedCategory。

動態 Tree02 則先寫入 selectedNodeId = [[treeNode.value]] 與 selectedCategory = [[treeNode.data.category]],再重新載入 ProTable。順序不可顛倒,否則列表會使用上一次的選取值。

11. 元件函式​

Function作用
reloadTree重新請求根節點,並保留仍有效的選取
expandAll展開目前已載入的所有目錄
collapseAll收合所有目錄

expandAll 不會為了展開大型樹而自動載入所有尚未請求的分支。

12. Java 後端範例​

Tree02 對應 Java 17 與 Spring Boot 4。節點 DTO 至少應包含穩定的 id、label、parentId、leaf 與 children;寫入請求包含 parentId、label、category,拖放請求包含 dragId、dropId、position。

用途方法與路徑
根節點、子節點與搜尋GET /spark/api/sample/navigation/tree/nodes
新增子節點POST /spark/api/sample/navigation/tree/nodes
編輯節點PUT /spark/api/sample/navigation/tree/nodes/{nodeId}
刪除節點DELETE /spark/api/sample/navigation/tree/nodes/{nodeId}
儲存拖放POST /spark/api/sample/navigation/tree/reorder

查詢服務可依標準內容選擇可重複的預覽資料或各環境 Repository:

Map<String, NodeRecord> source = isDevelopmentPreview(context)
? designPreviewNodes : nodes;
String keyword = StringUtils.trimToNull(query);
if (keyword != null) {
return children(source, null).stream()
.map(root -> filterTree(source, root, keyword))
.filter(Objects::nonNull)
.toList();
}
return children(source, StringUtils.trimToNull(parentId)).stream()
.map(node -> toDto(source, node))
.toList();

新增、編輯、刪除與拖放都必須在後端重新驗證節點、父節點、循環引用與權限。使用者可見錯誤應透過 LatticeRuntimeException(Message.code(...)) 與服務端 i18n 資源回傳,不要在 Service 拼接字串。拖放更新父節點和同層排序時應位於同一交易中;失敗則回傳非成功結果,讓 Tree 回復畫面。

右側 ProTable 可在一般商品查詢中加入 nodeId。分類節點沿用 category;Operations、Support 等業務節點則由後端依 nodeId 轉換為對應條件。

13. 驗證與問題排查​

建議依資料載入、選取聯動、節點操作、拖放儲存的順序驗證:先以 Test 確認根節點與欄位對應,再測試展開和搜尋;接著點選多個節點確認右側列表改變;最後驗證新增、編輯、刪除與拖放失敗時的回復。

現象優先檢查
Test 請求失敗URL、方法、Connection、驗證狀態與 CORS
HTTP 200 但節點數為 0Response List Path 是否指向陣列
有數量但沒有標題Title Field、Value Field 與實際欄位名
目錄無法展開Leaf Field 是否錯誤回傳 true
不同節點顯示相同列表Page Context 執行順序與 ProTable Query Context 路徑
拖放後重新整理恢復舊順序onDrop 參數與後端是否持久化