如何用jsTreeR创建不可编辑的叶子节点子集
使用jsTreeR实现不可编辑叶子节点及关联分支保护
需求说明
需要创建jsTreeR树结构,满足以下要求:
- 部分叶子节点不可操作:禁止重命名、删除、添加子节点
- 若分支节点包含上述不可操作叶子,则该分支节点也不能被删除
- 不可操作叶子节点允许在不同分支/父节点间拖拽
尝试使用state = list(disabled = TRUE)参数,但该设置对右键菜单无效果,测试代码如下:
library(jsTreeR) library(shiny) nodes <- list( list( text = "分支1", state = list(opened = TRUE, disabled = FALSE), type = "parent", children = list( list(text = "叶子A", type = "child", state = list(disabled = TRUE), data = list(customdata = 1)), list(text = "叶子B", type = "child", state = list(disabled = FALSE), data = list(customdata = 2)), list(text = "叶子C", type = "child", state = list(disabled = FALSE), data = list(customdata = 3)), list(text = "叶子D", type = "child", state = list(disabled = FALSE), data = list(customdata = 4)) ) ), list(text = "分支2", type = "parent", state = list(opened = TRUE)) ) ui <- fluidPage( jstreeOutput("jstree") ) server <- function(input, output, session){ output[["jstree"]] <- renderJstree({ suppressMessages(jstree( nodes, search = list( show_only_matches = TRUE, case_sensitive = FALSE, search_leaves_only = FALSE ), dragAndDrop = TRUE, multiple = TRUE, contextMenu = TRUE, types = list(default = list(icon = "fa fa-caret-right"), child = list(icon = "fa-solid fa-leaf"), parent = list(icon = "fa-brands fa-pagelines")), theme = "proton" )) }) } shinyApp(ui, server)
解决方案
通过自定义右键菜单结合节点类型标记实现需求,同时添加逻辑检查分支节点是否包含不可删除的子节点。以下是封装后的完整Shiny应用代码:
library(jsTreeR) library(shiny) nodes <- list( list( text = "分支1", state = list(opened = TRUE, disabled = FALSE), type = "parent", children = list( list(text = "叶子A", type = "undeletable", state = list(disabled = FALSE), data = list(customdata = 1)), list(text = "叶子B", type = "undeletable", state = list(disabled = FALSE), data = list(customdata = 2)), list(text = "叶子C", type = "undeletable", state = list(disabled = FALSE), data = list(customdata = 3)), list(text = "叶子D", type = "undeletable", state = list(disabled = FALSE), data = list(customdata = 4)) ) ), list(text = "分支2", type = "parent", state = list(opened = TRUE)) ) ui <- fluidPage( jstreeOutput("mytree") ) customMenu <- JS( "function customMenu(node) {", " var tree = $('#mytree').jstree(true);", // 'mytree'对应Shiny中的树ID " var hasUndeletableChild = false;", " // 检查当前节点是否包含不可删除的子节点", " if(node.children.length > 0){", " node.children.forEach(function(childId){", " var childNode = tree.get_node(childId);", " if(childNode.type === 'undeletable'){", " hasUndeletableChild = true;", " }", " });", " }", " var items = {", " 'delete' : {", " 'label' : '删除',", " 'action' : function (obj) {", " // 仅当节点不是不可删除类型,且不包含不可删除子节点时允许删除", " if(node.type !== 'undeletable' && !hasUndeletableChild){", " tree.delete_node(node);", " }", " },", " 'icon' : 'glyphicon glyphicon-trash'", " },", " 'rename' : {", " 'label' : '重命名',", " 'action' : function (obj) {", " // 仅允许重命名非不可删除类型的节点", " if(node.type !== 'undeletable'){", " tree.rename_node(node);", " }", " },", " 'icon' : 'glyphicon glyphicon-pencil'", " },", " 'create' : {", " 'label' : '添加子节点',", " 'action' : function (obj) {", " // 仅允许给非不可删除类型的节点添加子节点", " if(node.type !== 'undeletable'){", " tree.create_node(node, {}, 'last', function(new_node){", " setTimeout(function(){ tree.edit(new_node); }, 0);", " });", " }", " },", " 'icon' : 'glyphicon glyphicon-plus'", " }", " };", " // 隐藏不可删除节点的所有编辑操作", " if(node.type === 'undeletable'){", " delete items.rename;", " delete items.create;", " }", " return items;", "}") server <- function(input, output, session){ output[["mytree"]] <- renderJstree({ suppressMessages(jstree( nodes, search = list( show_only_matches = TRUE, case_sensitive = FALSE, search_leaves_only = FALSE ), dragAndDrop = TRUE, multiple = TRUE, contextMenu = list(items = customMenu), types = list(default = list(icon = "fa fa-caret-right"), undeletable = list(icon = "fa-solid fa-leaf"), parent = list(icon = "fa-brands fa-pagelines")), theme = "proton" )) }) } shinyApp(ui, server)
核心逻辑说明
- 节点类型标记:将需要保护的叶子节点标记为
undeletable类型,分支节点使用parent类型区分。 - 自定义右键菜单:
- 删除操作:检查节点类型是否为
undeletable,同时检查该节点是否包含undeletable子节点,两者都不满足才允许删除。 - 重命名/添加子节点操作:仅对非
undeletable节点开放。
- 删除操作:检查节点类型是否为
- 拖拽保留:保持
dragAndDrop = TRUE,未对拖拽逻辑做限制,满足叶子节点可跨分支拖拽的需求。
内容的提问来源于stack exchange,提问作者ahnungslos
相关产品推荐
相关产品推荐

