shinyTree包拖拽时复制指定项而非移动、锁定源菜单方法
shinyTree 拖拽复制+固定菜单区实现方案
核心通过shinyTree底层jsTree的原生配置实现需求,不需要额外引入其他功能包,修改后可同时满足两个核心要求:拖拽菜单区节点执行复制而非移动、菜单区完全锁定不可修改。
核心修改点
- 开启dnd插件全局复制配置,所有拖拽操作默认执行复制逻辑,不删除原节点
- 配置节点操作校验规则,拦截所有针对Menu区域的修改操作:禁止区域内节点重排、禁止删除区域内节点、禁止向区域内拖入/新增节点,仅允许从区域内向外复制节点
- 屏蔽Menu区域节点的右键菜单,避免通过右键入口修改菜单内容
完整可运行代码
library(shiny) library(shinyTree) library(htmlwidgets) ui <- fluidPage( pageWithSidebar( headerPanel("shinyTree 拖拽复制演示"), sidebarPanel(helpText("基于shinyTree包开发")), mainPanel( shinyTree( "tree", stripes = TRUE, multiple = TRUE, animation = FALSE, dragAndDrop = TRUE, contextmenu = TRUE, options = list( core = list( check_callback = JS(' function(operation, node, node_parent, node_position, more) { const isInMenu = (targetNode) => { return targetNode.text === "Menu" || (targetNode.parents && targetNode.parents.some(p => p === "Menu")); }; if (operation !== "copy_node") { if (isInMenu(node) || node_parent.text === "Menu") return false; } else { if (node_parent.text === "Menu") return false; } return true; } ') ), dnd = list( always_copy = TRUE, copy_selection_only = TRUE ), contextmenu = list( items = JS(' function(node) { const isInMenu = (targetNode) => { return targetNode.text === "Menu" || (targetNode.parents && targetNode.parents.some(p => p === "Menu")); }; if (isInMenu(node)) return {}; return $.jstree.defaults.contextmenu.items(); } ') ) ) ) ) ) ) server <- function(input, output, session) { output$tree <- renderTree({ list( 'Menu' = list(A = "", B = "", C = "", D = ""), 'Drag here:' = list("") ) }) } shinyApp(ui, server)
实现效果
- 拖拽Menu区域下任意节点到“Drag here”区域,自动创建副本,Menu区域原节点保留不消失
- Menu区域完全固定:无法调整内部节点顺序、无法删除节点、无法新增节点、无法拖入外部内容,右键点击区域内节点不会弹出操作菜单
- “Drag here”区域保留完整编辑能力,可自由调整节点顺序、增删节点,不影响正常交互
内容的提问来源于stack exchange,提问作者Curious Jorge - user9788072
相关产品推荐
相关产品推荐

