This is a minimalist HTML and JavaScript skeleton of the GoJS Sample orgChartEditor.html. It was automatically generated from a button on the sample page, and does not contain the full HTML. It is intended as a starting point to adapt for your own usage. For many samples, you may need to inspect the full source on Github and copy other files or scripts.
Este exemplo de organograma editável codifica os nós por cores de acordo com o nível da árvore na hierarquia.
Selecione um nó para editar seus valores de dados. Este exemplo usa a extensão Data Inspector para exibir e modificar dados de Part. Como este aplicativo simples faz parte de um site estático, não há como carregar uma fotografia de uma pessoa.
Clique duas vezes no fundo do diagrama para adicionar um novo chefe. Isso usa o ClickCreatingTool com um ClickCreatingTool.insertPart personalizado para rolar até o novo nó e começar a editar o TextBlock para seu nome.
Arraste um nó para outro para alterar relacionamentos, se permitido. Clique com o botão direito ou toque e segure um nó para abrir um menu de contexto que permite que você:
Para aprender como construir um organograma do zero com GoJS, consulte o tutorial de primeiros passos .
Este exemplo está definido para o tema escuro por padrão.
Se você quiser ter alguns nós "assistentes" na lateral, acima dos relatórios regulares, veja o exemplo de Assistentes de Organograma , que é uma versão com estilo mais simples deste exemplo que usa um TreeLayout personalizado para posicionar os "assistentes" dessa maneira.
GoJS versão 3.0.5. Copyright 1998-2024 da Northwoods Software.
Veja o código-fonte desta página de exemplo no GitHub
função init ( ) { const $ = go . GraphObject . make ; // para concisão na definição de modelos meuDiagrama = novo go . Diagrama ( "myDiagramDiv" , // deve ser o ID ou referência a div { allowCopy : false , allowDelete : false , initialAutoScale : go . AutoScale . UniformToFill , maxSelectionCount : 1 , // os usuários podem selecionar apenas uma parte por vez validCycle : go . CycleMode . DestinationTree , // certifique-se de que os usuários só podem criar árvores "clickCreatingTool.archetypeNodeData" : { // permitir clique duplo em segundo plano para criar um novo nó name : "(Nova pessoa)" , title : "(Título)" , dept : "(Dept)" } , "clickCreatingTool.insertPart" : function ( loc ) { // a substituição do método deve ser function, não => const node = go . ClickCreatingTool . prototype . insertPart . call ( this , loc ) ; if ( node !== null ) { this . diagram . select ( node ) ; this . diagram . commandHandler . scrollToPart ( node ) ; this . diagram . commandHandler . editTextBlock ( node . findObject ( "NAMETB" ) ) ; } return node ; } , layout : $ ( go . TreeLayout , { treeStyle : go . TreeStyle . LastParents , arrangement : go . TreeArrangement . Horizontal , // propriedades para a maior parte da árvore: angle : 90 , layerSpacing : 35 , // propriedades para os "últimos pais": alternativaAngle : 90 , alternativeLayerSpacing : 35 , alternativeAlignment : go . Alinhamento de árvore . Bus , alternativoNodeSpacing : 20 } ) , "undoManager.isEnabled" : true , // habilita desfazer e refazer "themeManager.changesDivBackground" : true , "themeManager.currentTheme" : document . getElementById ( "tema" ) . valor } ) ; // quando o documento for modificado, adicione um "*" ao título e habilite o botão "Salvar" myDiagram . addDiagramListener ( "Modified" , e => { const button = document . getElementById ( "SaveButton" ) ; if ( button ) button . disabled = ! myDiagram . isModified ; const idx = document . title . indexOf ( "*" ) ; if ( myDiagram . isModified ) { if ( idx < 0 ) document . title += "*" ; } else { if ( idx >= 0 ) document . title = document . title . slice ( 0 , idx ) ; } } ) ; // configura algumas cores/fontes para os temas padrão ('claro') e escuro myDiagram . themeManager . set ( "light" , { cores : { background : "#fff" , texto : "#111827" , textHighlight : '#11a8cd' , subtexto : "#6b7280" , emblema : "#f0fdf4" , badgeBorder : "#16a34a33 " , badgeText : "#15803d" , divisor : "#6b7280" , sombra : "#9ca3af" , dica de ferramenta : "#1f2937" , níveis : [ "#AC193D" , "#2672EC" , "#8C0095" , "# 5133AB" , "#008299" , "#D24726" , "#008A00" , "#094AB2" ] , dragOver : "#f0f9ff" , link : "#9ca3af" , div : "#f3f4f6" } , fontes : { nome : "500 0,875rem Inter, sans-serif" , normal : "0,875rem Inter, sans-serif" , crachá : "500 0,75rem Inter, sans-serif" , link : "600 0,875rem Inter, sans-serif" } } ) ; meuDiagrama . themeManager . set ( "dark" , { cores : { background : "#111827" , texto : "#fff" , subtexto : "#d1d5db" , emblema : "#22c55e19" , badgeBorder : "#22c55e21" , badgeText : "#4ade80 " , shadow : "#111827" , dragOver : "#082f49" , link : "#6b7280" , div : "#1f2937" } } ) ; // isso é usado para determinar o feedback durante o arrasto function mayWorkFor ( node1 , node2 ) { if ( ! ( node1 instanceof go . Node ) ) return false ; // deve ser um Nó if ( node1 === node2 ) return false ; // não pode funcionar sozinho if ( node2.isInTreeOf ( node1 ) ) return false ; // não pode trabalhar para alguém que trabalha para você return true ; } // Este conversor é usado pelo Picture. função findHeadShot ( pic ) { if ( ! pic ) return "../samples/images/user.svg" ; // Existem apenas 16 imagens no servidor return "../samples/images/HS" + pic ; } // Usado para converter o nível da árvore do nó em uma cor de tema function findLevelColor ( node ) { return node . findTreeLevel ( ) ; } // Obtém o texto para uma dica de ferramenta com base no nome do objeto adornado function toolTipTextConverter ( obj ) { if ( obj . name === "EMAIL" ) return obj . part . data . email ; if ( obj . name === "PHONE" ) return obj . part . data . phone ; } // Alinhar a dica de ferramenta com base nos limites da janela de visualização do objeto adornado function toolTipAlignConverter ( obj , tt ) { const d = obj . diagram ; const bot = obj . getDocumentPoint ( go . Spot . Bottom ) ; const viewPt = d . transformDocToView ( bot ) . offset ( 0 , 35 ) ; // se a dica de ferramenta estiver abaixo da janela de visualização, mostrar acima const align = d . viewportBounds . height >= viewPt . y / d . scale ? new go . Spot ( 0.5 , 1 , 0 , 6 ) : new go . Spot ( 0.5 , 0 , 0 , - 6 ) ; tt . alinhamento = alinhar ; tt . alinhamentoFoco = alinhar . y === 1 ? ir . Local . Topo : ir . Local . Fundo ; } // uma dica de ferramenta para os botões Email e Telefone const toolTip = new go . Adornment ( go . Panel . Spot , { isShadowed : true , shadowOffset : new go . Point ( 0 , 2 ) } ) . add ( new go . Placeholder ( ) , new go . Panel ( go . Panel . Auto ) . add ( new go . Shape ( "RoundedRectangle" , { strokeWidth : 0 , shadowVisible : true } ) . theme ( "fill" , "background" ) , new go . TextBlock ( { margin : 2 } ) . bindObject ( "texto" , "adornedObject" , toolTipTextConverter ) . theme ( "stroke" , "texto" ) . theme ( "fonte" , "normal" ) ) // define o alinhamento e o alignmentFocus com base na posição do objeto adornado na janela de visualização . bindObject ( "" , "adornedObject" , toolTipAlignConverter ) ) . theme ( "shadowColor" , "shadow" ) ; // define o modelo de nó myDiagram . nodeTemplate = new go . Node ( go . Panel . Spot , { isShadowed : true , shadowOffset : new go . Point ( 0 , 2 ) , selectionObjectName : "BODY" , // mostrar/ocultar botões quando o mouse entra/sai mouseEnter : ( e , node ) => node . findObject ( "BUTTON" ) . opacity = node . findObject ( "BUTTONX" ) . opacity = 1 , mouseLeave : ( e , node ) => node . findObject ( "BUTTON" ) . opacity = node . findObject ( "BUTTONX" ) . opacity = 0 , // manipular o arrasto de um nó para um nó para (talvez) alterar o relacionamento de relatórios mouseDragEnter : ( e , node , prev ) => { const diagram = node . diagram ; const selnode = diagram . seleção . primeiro ( ) ; se ( ! mayWorkFor ( selnode , nó ) ) retornar ; const forma = nó . findObject ( "FORMA" ) ; se ( forma ) { forma . _prevFill = forma . preencher ; // lembrar a forma original do pincel . preencher = diagrama . themeManager . encontrarValor( "dragOver" , "colors" ) ; // "#e0f2fe"; } } , mouseDragLeave : ( e , node , next ) => { const shape = node . findObject ( "SHAPE" ) ; if ( shape && shape . _prevFill ) { shape . fill = shape . _prevFill ; // restaura o pincel original } } , mouseDrop : ( e , node ) => { const diagram = node . diagram ; const selnode = diagram . selection . first ( ) ; // assume apenas um nó na seleção if ( mayWorkFor ( selnode , node ) ) { // encontra qualquer link existente no nó selecionado const link = selnode . findTreeParentLink ( ) ; if ( link !== null ) { // reconecta qualquer link existente link . fromNode = node ; } else { // senão cria um novo diagrama de links . toolManager . linkingTool . insertLink ( node , node . port , selnode , selnode . port ) ; } } } } ) . add ( new go . Panel ( go . Panel . Auto , { name : "BODY" } ) . add ( // define a forma externa do nó new go .Forma ( "RetânguloArredondado" , { nome : "FORMA" , strokeWidth : 0 , portId : "" , ponto1 : ir . Ponto . TopEsquerda , ponto2 : ir . Ponto . InferiorDireita } ) . tema ( "preenchimento" , "plano de fundo" ) , novo ir . Painel ( ir . Painel . Tabela , { margem : 0,5 , defaultRowSeparatorStrokeWidth : 0,5 } ) . tema ( "padrãoRowSeparatorStroke" , "divisor" ) . adicionar ( novo ir . Painel ( ir . Painel . Tabela , { preenchimento : novo ir . Margem ( 18 , 18 , 18 , 24 ) } ) . adicionarColumnDefinition ( 0 , { largura : 240 } ) . adicionar ( novo ir . Painel ( ir . Painel . Tabela , { coluna : 0 , alinhamento : ir . Ponto . Esquerda , esticar : ir . Esticar . Vertical , alinhamento padrão : ir . Ponto . Esquerda } ) . adicionar ( novo ir . Painel ( ir . Painel . Horizontal , { linha : 0 } ) . adicionar ( novo ir . Bloco de texto ( { editável: true , minSize : novo go . Tamanho ( 10 , 14 ) } ) . bindTwoWay ( "texto" , "nome" ) . tema ( "traço" , "texto" ) . tema ( "fonte" , "nome" ) , novo go . Painel ( go . Panel . Auto , { margin : new go . Margin ( 0 , 0 , 0 , 10 ) } ) . add ( novo go . Shape ( "Capsule" , { parâmetro1 : 6 , parâmetro2 : 6 } ) . theme ( "fill" , "badge" ) . theme ( "stroke" , "badgeBorder" ) , novo go . TextBlock ( { editável : true , minSize : novo go . Tamanho ( 10 , 12 ) , margem : novo go . Margin ( 2 , -1 ) } ) . " ) .tema ( "fonte" , " crachá" ) ) ) , novo go . TextBlock ( { row : 1 , editável : true , minSize : new go . Size ( 10 ,14 ) } ) . vincularTwoWay ( "texto" , "título" ) . tema ( "traço" , "subtexto" ) . tema ( "fonte" , "normal" ) ) , novo go . Painel ( go . Painel . Ponto , { isClipping : true , coluna : 1 } ) . adicionar ( novo go . Forma ( "Círculo" , { desiredSize : novo go . Tamanho ( 50 , 50 ) , strokeWidth : 0 } ) , novo go . Imagem ( { nome : "IMAGEM" , fonte : "../samples/images/user.svg" , desiredSize : novo go . Tamanho ( 50 , 50 ) } ) . vincular ( "fonte" , "imagem" , findHeadShot ) ) ) , novo go . Painel ( ir . Painel . Tabela , { linha : 1 , esticar : ir . Esticar . Horizontal , defaultColumnSeparatorStrokeWidth : 0.5 } ) . tema ( "defaultColumnSeparatorStroke" , "divisor" ) . adicionar ( makeBottomButton ( "EMAIL" ) , makeBottomButton ( "TELEFONE" ) ) ) ) , // fim do Painel Automático novo ir . Forma ( "RetânguloEsquerdoArredondado" , { alinhamento : ir . Ponto . Esquerda , alinhamentoFoco :ir . Ver . Esquerda , estique : vá . Esticar . Vertical , largura : 6 , acidente vascular cerebralWidth : 0 } ) . themeObject ( "preencher" , "" , "níveis" , findLevelColor ) , $ ( "Botão" , $ ( go . Shape , "PlusLine" , { largura : 8 , altura : 8 , traço : "#0a0a0a" , acidente vascular cerebralWidth : 2 } ) , { name : "BUTTON" , alinhamento : go . Spot , opacity : 0 , // inicialmente não visível clique : ( e , button ) => addEmployee ( button . part ) } , // o botão está visível quando o nó é selecionado ou ao passar o mouse new go .LineUp" , "_treeCollapsedFigure" : "LineDown" , name : "BUTTONX" , alinhamento : go . Spot . Bottom , opacity : 0 // inicialmente não visível } , // o botão fica visível quando o nó é selecionado ou ao passar o mouse novo go . Vinculação ( "opacidade" , "isSelected" ,s => s ? 1 : 0 ) . ofObject ( ) ) ) . theme ( "shadowColor" , "shadow" ) // para classificação, faça com que Node.text seja data.name . bind ( "text" , "name" ) // vincula o Part.layerName para controlar a camada do Node dependendo se ele éSelected . bindObject ( "layerName" , "isSelected" , sel => sel ? "Foreground" : "" ) . bindTwoWay ( "isTreeExpanded" ) ; função makeBottomButton ( nome ) { const phonePath = 'F M2 3.5A1.5 1.5 0 013.5 2h1.148a1.5 1.5 0 011.465 1.175l.716 3.223a1.5 1.5 0 01-1.052 1.767l-.933.267c-.41.117-.643.555-.48.95a11.542 11.542 0 006.254 6.254c.395.163.833-.07.95-.48l.267-.933a1.5 1.5 0 011.767-1.052l3.223.716A1.5 1.5 0 0118 15.352V16.5a1.5 1.5 0 01-1.5 1.5H15c-1.149 0-2.263-.15-3.326-.43A13.022 13.022 0 012.43 8.326 13.019 13.019 0 012 5V3.5z' ; const emailPath = 'F M3 4a2 2 0 00-2 2v1.161l8.441 4.221a1.25 1.25 0 001.118 0L19 7.162V6a2 2 0 00-2-2H3zM19 8.839l-7.77 3.885a2.75 2.75 0 01-2.46 0L1 8.839V14a2 2 0 002 2h14a2 2 0 002-2V8.839z' const convertSelectedToThemeProp = s => s ? 'textHighlight' : 'text' ; const isEmail = nome === "EMAIL" ; retornar novo go . Painel ( ir . Painel . Tabela , { mouseEnter : ( e , obj ) => myDiagram . model . set ( obj . part . data , nome , true ) , mouseLeave : ( e , obj ) => myDiagram . model . set ( obj . part . data , nome , false ) , nome , plano de fundo : "transparente" , cursor : 'Ponteiro' , coluna : éEmail ? 0 : 1 , largura : 140 , altura : 40 , dica de ferramenta : dica de ferramenta , clique : ( e , obj ) => { dialog . firstElementChild . firstElementChild . innerHTML = // o intervalo do modal `Você clicou em ${ isEmail ? 'enviar e-mail para' : 'ligar' } ${ obj . papel . dados . nome } em ${ obj . papel . dados [ nome . toLowerCase ( ) ] } ` ; diálogo . mostrarModal ( ) ; } } ) . add ( new go . Panel ( go . Panel . Horizontal ) .add ( new go . Shape ( { geometriaString : isEmail ? emailPath : phonePath , strokeWidth : 0 , desejadoSize : isEmail ? new go . Size ( 20 , 16 ) : novo go .Size ( 20 , 20 ) , margin : new go . Margin ( 0 , 12 , 0 , 0 ) , } ) ., novo go . TextBlock ( isEmail ? " Email " : " Phone " ) . tema ( " stroke " , " text " ) .) ) ) } function addEmployee ( node ) { if ( ! node ) return ; const thisemp = nó . dados ; deixe newnode ; meuDiagrama . modelo . commit ( m = > { const newemp = { nome : "(Nova pessoa)" , título : " ( Título)" , dept : thisemp .dept , pai : thisemp .key } ; m .addNodeData ( newemp ) ; newnode = myDiagram .findNodeForData ( newemp ) ; // define a localização para que o novo nó não seja animado no canto superior esquerdo if ( newnode ) newnode . meuDiagrama . comandoHandler . scrollToPart ( newnode ) ; } // o menu de contexto permite que os usuários tornem uma posição vaga, // removam uma função e reatribuam a subárvore ou removam um departamento myDiagram . nodeTemplate . contextMenu = $ ( "ContextMenu" , $ ( "ContextMenuButton" , $ ( go . TextBlock , "Adicionar Funcionário" ) , { click : ( e , button ) => addEmployee ( button . part . adornedPart ) } ) , $ ( "ContextMenuButton" , $ ( go . TextBlock , "Desocupar Cargo" ) , { click : ( e , button ) => { const node = button . part . adornedPart ; if ( node !== null ) { const thisemp = node . data ; myDiagram . model . commit ( m => { // atualiza o nome, a foto, o e-mail e o telefone, mas deixa o título/departamento m . set ( thisemp , "nome" , "(Vaga)" ) ; m . set ( thisemp , "pic" , "" ) ; m . set ( thisemp , "email" , "nenhum" ) ; m . set ( thisemp , "telefone" , "nenhum" ) ; } , "desocupar" ; } } } ) , $ ( " ContextMenuButton" , $ ( go .TextBlock , "Remover Função") , { click : ( e , button ) => { // reparente a subárvore para o chefe deste nó e, em seguida, remova o nó const node = button . part . adornedPart ; if ( node !== null ) { myDiagram . model . commit ( m => { const chl = node . findTreeChildrenNodes ( ) ; // itera pelos filhos e define sua chave pai para a chave pai do nó selecionado while ( chl . next ( ) ) { const emp = chl . value ; m . setParentKeyForNodeData ( emp . data , node . findTreeParentNode ( ) . data . key ) ; } // e agora remove o próprio nó selecionado m . removeNodeData ( node . data ) ; } , "reparent remove" ) } } } ) , $ ( "ContextMenuButton" , $ ( go . TextBlock , "Remover Departamento" ) , { click : ( e , button ) => { // remove a subárvore inteira, incluindo o próprio nó const node = button . part . adornedPart ; if ( node !== null ) { meuDiagrama . commit ( d => d . removeParts ( node . findTreeParts ( ) ) , "remover dept" ) ; } } } ) ) ; //define o modelo de link myDiagram . linkTemplate = $ ( go . Link , { roteamento : go . Routing . Orthogonal , layerName : "Background" , corner : 5 } , $ ( go . Shape , { strokeWidth : 2 } , novo go . ThemeBinding ( "stroke" , " link" ) ) ) ; //o formato do link // leia os dados no formato JSON do elemento "mySavedModel" load ( ) ; // suporta a edição das propriedades da pessoa selecionada em HTML myInspector = new Inspector ( "myInspector" , myDiagram , { properties : { "key" : { readOnly : true } , // Não mostra esses valores de dados temporários "EMAIL" : { mostrar : falso } , "TELEFONE" : { mostrar : falso } } } ) ; //Configura o zoom para caber no documento do botão . getElementById ( "zoomToFit" ) . addEventListener ( " clique " , ( ) = > myDiagram .commandHandler.zoomToFit ( ) ) ; documento . getElementById ( "centerRoot" ) . addEventListener ( " click " , ( ) = > { myDiagram .scale = 1 ; myDiagram .commandHandler .scrollToPart ( myDiagram . findNodeForKey ( 1 ) ) ; } ) ; } // fim da inicialização // Mostra o modelo do diagrama no formato JSON function save ( ) { document . getElementById ( "meuSavedModel" ) . valor = meuDiagrama . modelo . paraJson ( ) ; meuDiagrama . isModificado = falso ; } function load ( ) { meuDiagrama . modelo = ir . Modelo . fromJson ( document.getElementById ( " mySavedModel " ) . valor ) ; // certifique-se de que as novas chaves de dados sejam números inteiros positivos únicos let lastkey = 1 ; meuDiagrama . modelo . makeUniqueKeyFunction = ( modelo , dados ) => { let k = dados . chave || última chave ; while ( model.findNodeDataForKey ( k ) ) k ++ ; dados . chave = última chave = k ; retornar k ; } ; } function changeTheme ( ) { const meuDiagrama = go . Diagrama . fromDiv ( "meuDiagramaDiv" ) ; if ( meuDiagrama ) { meuDiagrama . themeManager . temaatual = documento . getElementById ( "tema" ) . valor ; } } janela . addEventListener ( "DOMContentLoaded" , ( ) => { dialog = document . querySelector ( 'dialog' ) ; dialog . addEventListener ( "click" , ( e ) => { dialog . close ( ) ; } ) ; // setTimeout apenas para certifique-se de que a fonte seja carregada antes de carregar o diagrama // você pode querer usar uma biblioteca de carregamento de ativos para isso // para manter este exemplo simples, ele não setTimeout ( ( ) => { init ( ) ; } , 300 ) } ) ;
De segunda a sexta, das 07h00 às 13h00
(28)3199-0530 ou 0800 150 1717
ouvidoria@rionovodosul.es.gov.br
Rua Fernando de Abreu, nº 18, - Centro, Rio Novo do Sul/ES - CEP: 29290-000