<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Org Chart Editor</title> <meta name="description" content="組織結構圖編輯器-編輯詳細信息並更改關係。" /> <meta name="viewport" content="width=device-width, initial-scale=1"> <script src="https://unpkg.com/gojs/release/go-debug.js"></script> <link rel="stylesheet" href="../extensions/dataInspector.css" /> <script id="code"> // 初始化JSON格式的數據 var modelObj = { "class": "go.TreeModel", "nodeDataArray": [{ "key": 1, "name": "Stella Payne Diaz", "title": "CEO" }, { "key": 2, "name": "Luke Warm", "title": "VP Marketing/Sales", "parent": 1 }, { "key": 3, "name": "Meg Meehan Hoffa", "title": "Sales", "parent": 2 }, { "key": 4, "name": "Peggy Flaming", "title": "VP Engineering", "parent": 1 }, { "key": 5, "name": "Saul Wellingood", "title": "Manufacturing", "parent": 4 }, { "key": 6, "name": "Al Ligori", "title": "Marketing", "parent": 2 }, { "key": 7, "name": "Dot Stubadd", "title": "Sales Rep", "parent": 3 }, { "key": 8, "name": "Les Ismore", "title": "Project Mgr", "parent": 5 }, { "key": 9, "name": "April Lynn Parris", "title": "Events Mgr", "parent": 6 }, { "key": 10, "name": "Xavier Breath", "title": "Engineering", "parent": 4 }, { "key": 11, "name": "Anita Hammer", "title": "Process", "parent": 5 }, { "key": 12, "name": "Billy Aiken", "title": "Software", "parent": 10 }, { "key": 13, "name": "Stan Wellback", "title": "Testing", "parent": 10 }, { "key": 14, "name": "Marge Innovera", "title": "Hardware", "parent": 10 }, { "key": 15, "name": "Evan Elpus", "title": "Quality", "parent": 5 }, { "key": 16, "name": "Lotta B. Essen", "title": "Sales Rep", "parent": 3 } ] }; function init() { if (window.goSamples) goSamples(); // 這些樣本的初始化-您無需調用 var $ = go.GraphObject.make; // 爲了簡潔定義模板 myDiagram = $(go.Diagram, "myDiagramDiv", // 必須是div的ID或引用。個DIV必須指定寬高,否者不會被渲染出來,咱們一般爲DIV設置一個背景顏色以便於咱們更便捷的觀察 { maxSelectionCount: 1, // 用戶一次只能選擇一個零件 "undoManager.isEnabled": false, // 支持 Ctrl-Z 和 Ctrl-Y 操做 //禁止橫豎拖動和鼠標滾動,流程圖總體的拖動 "allowHorizontalScroll": false, "allowVerticalScroll": false, "isReadOnly": true, // 只讀,能夠禁止拖動 validCycle: go.Diagram.CycleDestinationTree, // 確保用戶只能建立樹 "clickCreatingTool.archetypeNodeData": { // 容許在後臺雙擊以建立新節點 name: "(new person)", title: "", comments: "" }, "clickCreatingTool.insertPart": function (loc) { // 滾動到新節點 var 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.TreeLayout.StyleLastParents, arrangement: go.TreeLayout.ArrangementHorizontal, // 大多數樹的屬性: angle: 90, layerSpacing: 35, // 「最後父母」的屬性: alternateAngle: 90, alternateLayerSpacing: 35, alternateAlignment: go.TreeLayout.AlignmentBus, alternateNodeSpacing: 20 }), "undoManager.isEnabled": true // 啓用撤消和重作 }); // 修改文檔後,在標題上添加「*」並啓用「保存」按鈕 myDiagram.addDiagramListener("Modified", function (e) { var button = document.getElementById("SaveButton"); if (button) button.disabled = !myDiagram.isModified; var idx = document.title.indexOf("*"); if (myDiagram.isModified) { if (idx < 0) document.title += "*"; } else { if (idx >= 0) document.title = document.title.substr(0, idx); } }); // 從圖中刪除節點或連接時,手動管理老闆信息 myDiagram.addDiagramListener("SelectionDeleting", function (e) { var part = e.subject.first(); // e.subject是myDiagram.selection集合, // 因此咱們將得到第一個,由於咱們知道咱們只有一個選擇 myDiagram.startTransaction("clear boss"); if (part instanceof go.Node) { var it = part.findTreeChildrenNodes(); // 查找全部子節點 while (it.next()) { // 如今遍歷他們並清除老闆信息 var child = it.value; var bossText = child.findObject("boss"); // 因爲老闆TextBlock是命名的,咱們能夠按名稱訪問它 if (bossText === null) return; bossText.text = ""; } } else if (part instanceof go.Link) { var child = part.toNode; var bossText = child.findObject("boss"); // 因爲老闆TextBlock是命名的,咱們能夠按名稱訪問它 if (bossText === null) return; bossText.text = ""; } myDiagram.commitTransaction("clear boss"); }); var levelColors = ["#AC193D", "#2672EC", "#8C0095", "#5133AB", "#008299", "#D24726", "#008A00", "#094AB2" ]; // 重寫TreeLayout.commitNodes還可基於樹的深度級別修改背景畫筆 myDiagram.layout.commitNodes = function () { go.TreeLayout.prototype.commitNodes.call(myDiagram.layout); // 標準行爲 // 而後遍歷全部頂點並設置其對應的節點Shape.fill // 依賴於TreeVertex.level值的畫筆 myDiagram.layout.network.vertexes.each(function (v) { if (v.node) { var level = v.level % (levelColors.length); var color = levelColors[level]; var shape = v.node.findObject("SHAPE"); if (shape) shape.stroke = $(go.Brush, "Linear", { 0: color, 1: go.Brush.lightenBy(color, 0.05), start: go.Spot.Left, end: go.Spot.Right }); } }); }; // 雙擊節點時,向其添加一個子節點 function nodeDoubleClick(e, obj) { var clicked = obj.part; if (clicked !== null) { var thisemp = clicked.data; myDiagram.startTransaction("add employee"); var newemp = { name: "(new person)", title: "", comments: "", parent: thisemp.key }; myDiagram.model.addNodeData(newemp); myDiagram.commitTransaction("add employee"); } } // 單擊節點時,向其添加一個子節點 function nodeClick(e, obj) { var clicked = obj.part; if (clicked !== null) { var thisemp = clicked.data; myDiagram.startTransaction("add employee"); var newemp = { name: "(new person)", title: "", comments: "", parent: thisemp.key }; myDiagram.model.addNodeData(newemp); myDiagram.commitTransaction("add employee"); } } // 用於肯定拖動過程當中的反饋 function mayWorkFor(node1, node2) { if (!(node1 instanceof go.Node)) return false; // 必須是一個節點 if (node1 === node2) return false; // 不能爲本身工做 if (node2.isInTreeOf(node1)) return false; // 不能爲爲你工做的人工做 return true; } // 此功能爲大多數TextBlock提供了通用樣式。 // 其中某些值可能在特定的TextBlock中被覆蓋。 function textStyle() { return { font: "9pt Segoe UI,sans-serif", stroke: "white" }; } // 圖片使用了此轉換器。 function findHeadShot(key) { if (key < 0 || key > 16) return "D:/GoJS/site/samples/images/HS16.png"; // 服務器上只有16張圖像 return "D:/GoJS/site/samples/images/HS" + key + ".jpg" } // 定義節點模板 myDiagram.nodeTemplate = $(go.Node, "Auto", /*{ //doubleClick: nodeDoubleClick //單擊節點,通知外部打開編輯浮窗,外部點擊保存,調用內部的保存方法,外部關閉,內部不保存 click: nodeClick }, */ { // 處理將一個節點拖到一個節點上以(可能)更改報告關係 mouseDragEnter: function (e, node, prev) { var diagram = node.diagram; var selnode = diagram.selection.first(); if (!mayWorkFor(selnode, node)) return; var shape = node.findObject("SHAPE"); if (shape) { shape._prevFill = shape.fill; // 記得原來的刷子 shape.fill = "darkred"; } }, mouseDragLeave: function (e, node, next) { var shape = node.findObject("SHAPE"); if (shape && shape._prevFill) { shape.fill = shape._prevFill; // 恢復原始畫筆 } }, mouseDrop: function (e, node) { var diagram = node.diagram; var selnode = diagram.selection.first(); // 假設選擇中只有一個節點 if (mayWorkFor(selnode, node)) { // 查找到所選節點的任何現有連接 var link = selnode.findTreeParentLink(); if (link !== null) { // 從新鏈接任何現有連接 link.fromNode = node; } else { // 不然建立一個新連接 diagram.toolManager.linkingTool.insertLink(node, node.port, selnode, selnode.port); } } } }, // 爲了排序,讓Node.text爲data.name new go.Binding("text", "name"), // 綁定Part.layerName來控制節點的圖層,具體取決因而否被選中 new go.Binding("layerName", "isSelected", function (sel) { return sel ? "Foreground" : ""; }).ofObject(), // define the node's outer shape $(go.Shape, "Rectangle", { name: "SHAPE", fill: "#333333", stroke: 'white', strokeWidth: 3.5, // 設置端口屬性: portId: "", fromLinkable: true, toLinkable: true, cursor: "pointer" }), $(go.Panel, "Horizontal", $(go.Picture, { name: "Picture", desiredSize: new go.Size(70, 70), margin: 1.5, }, new go.Binding("source", "key", findHeadShot)), // 定義顯示文本的面板 $(go.Panel, "Table", { minSize: new go.Size(130, NaN), maxSize: new go.Size(150, NaN), margin: new go.Margin(6, 10, 0, 6), defaultAlignment: go.Spot.Left }, $(go.RowColumnDefinition, { column: 2, width: 4 }), $(go.TextBlock, textStyle(), // 名字 { row: 0, column: 0, columnSpan: 5, font: "12pt Segoe UI,sans-serif", editable: true, isMultiline: false, minSize: new go.Size(10, 16) }, new go.Binding("text", "name").makeTwoWay()), $(go.TextBlock, "Title: ", textStyle(), { row: 1, column: 0 }), $(go.TextBlock, textStyle(), { row: 1, column: 1, columnSpan: 4, editable: true, isMultiline: false, minSize: new go.Size(10, 14), margin: new go.Margin(0, 0, 0, 3) }, new go.Binding("text", "title").makeTwoWay()), $(go.TextBlock, textStyle(), { row: 2, column: 0 }, new go.Binding("text", "key", function (v) { return "ID: " + v; })), $(go.TextBlock, textStyle(), { name: "boss", row: 2, column: 3, }, // 咱們包含一個名稱,以便咱們在刪除節點/連接時能夠訪問此TextBlock new go.Binding("text", "parent", function (v) { return "Boss: " + v; })), $(go.TextBlock, textStyle(), // 評論 { row: 3, column: 0, columnSpan: 5, font: "italic 9pt sans-serif", wrap: go.TextBlock.WrapFit, editable: true, // 默認狀況下容許換行 minSize: new go.Size(10, 14) }, new go.Binding("text", "comments").makeTwoWay()) ) // end Table Panel ) // end Horizontal Panel ); // end Node // 選中的節點顯示用於添加子節點的按鈕 myDiagram.nodeTemplate.selectionAdornmentTemplate = $(go.Adornment, "Spot", $(go.Panel, "Auto", $(go.Placeholder, { margin: new go.Margin(4, -20, 50, 4) }) ), // 所選節點的刪除按鈕 $("Button", { alignment: go.Spot.Right, alignmentFocus: go.Spot.Left, click: function (e, obj) { // 刪除整個子樹,包括節點自己 var node = obj.part.adornedPart; if (node !== null) { myDiagram.startTransaction("remove dept"); myDiagram.removeParts(node.findTreeParts()); myDiagram.commitTransaction("remove dept"); } } // 定義裝飾中此按鈕的單擊行爲 }, $(go.TextBlock, "-", // 按鈕的內容 { font: "bold 8pt sans-serif" }) ), // 所選節點的新增按鈕 $("Button", { alignment: go.Spot.Right, alignmentFocus: go.Spot.Right, click: nodeClick }, $(go.TextBlock, "+", // 按鈕的內容 { font: "bold 8pt sans-serif" }) ) ); // 上下文菜單容許用戶騰空職位, // 刪除角色並從新分配子樹,或刪除部門 myDiagram.nodeTemplate.contextMenu = $("ContextMenu", $("ContextMenuButton", $(go.TextBlock, "Vacate Position"), { click: function (e, obj) { var node = obj.part.adornedPart; if (node !== null) { var thisemp = node.data; myDiagram.startTransaction("vacate"); // 更新密鑰,名稱和註釋 myDiagram.model.setDataProperty(thisemp, "name", "(Vacant)"); myDiagram.model.setDataProperty(thisemp, "comments", ""); myDiagram.commitTransaction("vacate"); } } } ), $("ContextMenuButton", $(go.TextBlock, "Remove Role"), { click: function (e, obj) { // 將子樹從新關聯到該節點的老闆,而後刪除該節點 var node = obj.part.adornedPart; if (node !== null) { myDiagram.startTransaction("reparent remove"); var chl = node.findTreeChildrenNodes(); // 遍歷子節點並將其父密鑰設置爲咱們所選節點的父密鑰 while (chl.next()) { var emp = chl.value; myDiagram.model.setParentKeyForNodeData(emp.data, node.findTreeParentNode().data.key); } // 如今刪除所選節點自己 myDiagram.model.removeNodeData(node.data); myDiagram.commitTransaction("reparent remove"); } } } ), $("ContextMenuButton", $(go.TextBlock, "Remove Department"), { click: function (e, obj) { // 刪除整個子樹,包括節點自己 var node = obj.part.adornedPart; if (node !== null) { myDiagram.startTransaction("remove dept"); myDiagram.removeParts(node.findTreeParts()); myDiagram.commitTransaction("remove dept"); } } } ) ); // 定義連接模板 myDiagram.linkTemplate = $(go.Link, go.Link.Orthogonal, { corner: 5, relinkableFrom: true, relinkableTo: true }, $(go.Shape, { strokeWidth: 1.5, stroke: "#F5F5F5" })); // the link shape //從modelObj讀取JSON格式的數據 load(); // 設置縮放以適合按鈕 document.getElementById('zoomToFit').addEventListener('click', function () { myDiagram.commandHandler.zoomToFit(); }); document.getElementById('centerRoot').addEventListener('click', function () { myDiagram.scale = 1; myDiagram.commandHandler.scrollToPart(myDiagram.findNodeForKey(1)); }); // 支持在HTML中編輯所選人員的屬性 if (window.Inspector) myInspector = new Inspector("myInspector", myDiagram, { properties: { "key": { readOnly: true }, "comments": {} } }); } // 初始化結束 // 以JSON格式顯示圖的模型 function save() { modelObj = myDiagram.model.toJson(); myDiagram.isModified = false; } function load() { myDiagram.model = go.Model.fromJson(modelObj); // 確保新數據鍵是惟一的正整數 var lastkey = 1; myDiagram.model.makeUniqueKeyFunction = function (model, data) { var k = data.key || lastkey; while (model.findNodeDataForKey(k)) k++; data.key = lastkey = k; return k; }; } //----------------------------../extensions/dataInspector.js-------- "use strict"; /** 此類爲GoJS模型數據對象實現檢查器。 構造函數採用三個參數: {string}分隔一個字符串,該字符串引用要檢查的div的HTML ID。 {Diagram}圖是對GoJS圖的引用。 {Object} options可選的JS Object,描述檢查器的選項。 選項: inspectSelection {boolean}默認爲true,是否自動顯示和填充Inspector 與當前選擇的圖零件。若是設置爲false,則檢查器將不顯示任何內容 直到您以Part或JavaScript對象做爲參數調用Inspector.inspectObject(object)。 includesOwnProperties {boolean}默認爲true,是否列出當前在檢查的數據對象上的全部屬性。 properties {Object}一個字符串對象:對象對,表明propertyName:propertyOptions。 可用於包括或排除其餘屬性。 propertyModified函數(propertyName,newValue)回調 multipleSelection {boolean}默認爲false,是否容許多重選擇並更改全部選定屬性而不是 單個第一個對象 showAllProperties {boolean}默認爲false,與multiSelection一塊兒顯示的屬性在false時使用屬性的相交仍是在true時使用並集 僅在multipleSelection爲true時影響 showSize {number}默認值0,顯示選擇多個節點時顯示多少個節點 當其小於1時,顯示全部節點 屬性選項: show:{boolean | function}一個布爾值,以向檢查器顯示或隱藏該屬性,或者一個謂詞函數以有條件地顯示。 readOnly:{boolean | function}該屬性是否爲只讀 類型:{string}描述數據類型的字符串。支持的值:「字符串|數字|布爾值|顏色|數字數組|點|矩形|大小|位置|邊距|選擇」 defaultValue:{*}屬性的默認值。默認爲空字符串。 選擇:{Array | function}當type ==「 select」時,要使用的選擇數組或返回選擇數組的函數。 Inspector的用法示例: var inspector = new Inspector("myInspector", myDiagram, { includesOwnProperties: false, properties: { "key": { show: Inspector.showIfPresent, readOnly: true }, "comments": { show: Inspector.showIfNode }, "LinkComments": { show: Inspector.showIfLink }, "chosen": { show: Inspector.showIfNode, type: "checkbox" }, "state": { show: Inspector.showIfNode, type: "select", choices: ["Stopped", "Parked", "Moving"] } } }); 這是檢查器在給定的DIV元素內建立的基本HTML結構: <div id="divid" class="inspector"> <tr> <td>propertyName</td> <td><input value=propertyValue /></td> </tr> ... </div> */ function Inspector(divid, diagram, options) { var mainDiv = document.getElementById(divid); mainDiv.className = "inspector"; mainDiv.innerHTML = ""; this._div = mainDiv; this._diagram = diagram; this._inspectedProperties = {}; this._multipleProperties = {}; // GoJS Part或簡單的數據對象,例如Model.modelData this.inspectedObject = null; // 督察選項默認值: this.includesOwnProperties = true; this.declaredProperties = {}; this.inspectsSelection = true; this.propertyModified = null; this.multipleSelection = false; this.showAllProperties = false; this.showSize = 0; if (options !== undefined) { if (options["includesOwnProperties"] !== undefined) this.includesOwnProperties = options[ "includesOwnProperties"]; if (options["properties"] !== undefined) this.declaredProperties = options["properties"]; if (options["inspectSelection"] !== undefined) this.inspectsSelection = options["inspectSelection"]; if (options["propertyModified"] !== undefined) this.propertyModified = options["propertyModified"]; if (options['multipleSelection'] !== undefined) this.multipleSelection = options['multipleSelection']; if (options['showAllProperties'] !== undefined) this.showAllProperties = options['showAllProperties']; if (options['showSize'] !== undefined) this.showSize = options['showSize']; } var self = this; diagram.addModelChangedListener(function (e) { if (e.isTransactionFinished) self.inspectObject(); }); if (this.inspectsSelection) { diagram.addDiagramListener("ChangedSelection", function (e) { self.inspectObject(); }); } } // 一些與「 show」屬性一塊兒使用的靜態謂詞。 Inspector.showIfNode = function (part) { return part instanceof go.Node }; Inspector.showIfLink = function (part) { return part instanceof go.Link }; Inspector.showIfGroup = function (part) { return part instanceof go.Group }; // 僅顯示該屬性(若是存在)。 對於「鍵」頗有用,它將顯示在節點和組上,但一般不會顯示在連接上 Inspector.showIfPresent = function (data, propname) { if (data instanceof go.Part) data = data.data; return typeof data === "object" && data[propname] !== undefined; }; /** * 給定{@link #inspectedObject}的屬性,請更新此檢查器的HTML狀態。 * @param {Object}對象是一個可選參數,當{@link #inspectSelection}爲false時, * 以使用它來設置{@link #inspectedObject}並顯示和編輯該對象的屬性。 */ Inspector.prototype.inspectObject = function (object) { var inspectedObject = null; var inspectedObjects = null; if (object === null) return; if (object === undefined) { if (this.inspectsSelection) { if (this.multipleSelection) { // 若是多項選擇爲true,則獲取選擇 inspectedObjects = this._diagram.selection; } else { // 不然搶第一個對象 inspectedObject = this._diagram.selection.first(); } } else { // 若是隻有一個檢查對象 inspectedObject = this.inspectedObject; } } else { // 若是對象做爲參數傳遞 inspectedObject = object; } if (inspectedObjects && inspectedObjects.count === 1) { inspectedObject = inspectedObjects.first(); } if (inspectedObjects && inspectedObjects.count <= 1) { inspectedObjects = null; } // 單個對象或沒有對象 if (!inspectedObjects || !this.multipleSelection) { if (inspectedObject === null) { this.inspectedObject = inspectedObject; this.updateAllHTML(); return; } this.inspectedObject = inspectedObject; if (this.inspectObject === null) return; var mainDiv = this._div; mainDiv.innerHTML = ''; // 使用Part.data或對象自己(對於model.modelData) var data = (inspectedObject instanceof go.Part) ? inspectedObject.data : inspectedObject; if (!data) return; // 構建表: var table = document.createElement('table'); var tbody = document.createElement('tbody'); this._inspectedProperties = {}; this.tabIndex = 0; var declaredProperties = this.declaredProperties; // 仔細檢查傳遞給檢查器的全部屬性,並顯示它們(若是適用): for (var name in declaredProperties) { var desc = declaredProperties[name]; if (!this.canShowProperty(name, desc, inspectedObject)) continue; var val = this.findValue(name, desc, data); tbody.appendChild(this.buildPropertyRow(name, val)); } // 瀏覽模型數據上的全部屬性,並顯示它們(若是適用): if (this.includesOwnProperties) { for (var k in data) { if (k === '__gohashid') continue; // 跳過內部GoJS哈希屬性 if (this._inspectedProperties[k]) continue; // 已經存在 if (declaredProperties[k] && !this.canShowProperty(k, declaredProperties[k], inspectedObject)) continue; tbody.appendChild(this.buildPropertyRow(k, data[k])); } } table.appendChild(tbody); mainDiv.appendChild(table); } else { // 選擇了多個對象 var mainDiv = this._div; mainDiv.innerHTML = ''; var shared = new go.Map(); // 用於節點共有的屬性 var properties = new go.Map(); // 用於添加屬性 var all = new go.Map(); // 之後使用以防止在不須要時更改屬性 var it = inspectedObjects.iterator; // 構建表: var table = document.createElement('table'); var tbody = document.createElement('tbody'); this._inspectedProperties = {}; this.tabIndex = 0; var declaredProperties = this.declaredProperties; it.next(); inspectedObject = it.value; this.inspectedObject = inspectedObject; var data = (inspectedObject instanceof go.Part) ? inspectedObject.data : inspectedObject; if (data) { // 初始通行證以設置共享和所有 // 仔細檢查傳遞給檢查器的全部屬性,並將它們添加到地圖(若是適用): for (var name in declaredProperties) { var desc = declaredProperties[name]; if (!this.canShowProperty(name, desc, inspectedObject)) continue; var val = this.findValue(name, desc, data); if (val === '' && desc && desc.type === 'checkbox') { shared.add(name, false); all.add(name, false); } else { shared.add(name, val); all.add(name, val); } } // 瀏覽模型數據上的全部屬性,並將它們添加到地圖(若是適用): if (this.includesOwnProperties) { for (var k in data) { if (k === '__gohashid') continue; // 跳過內部GoJS哈希屬性 if (this._inspectedProperties[k]) continue; // 已經存在 if (declaredProperties[k] && !this.canShowProperty(k, declaredProperties[k], inspectedObject)) continue; shared.add(k, data[k]); all.add(k, data[k]); } } } var nodecount = 2; while (it.next() && (this.showSize < 1 || nodecount <= this .showSize)) { // grabs all the properties from the other selected objects properties.clear(); inspectedObject = it.value; if (inspectedObject) { // 使用Part.data或對象自己(對於model.modelData) data = (inspectedObject instanceof go.Part) ? inspectedObject.data : inspectedObject; if (data) { // 仔細檢查傳遞給檢查器的全部屬性,並將它們添加到要添加的屬性中(若是適用): for (var name in declaredProperties) { var desc = declaredProperties[name]; if (!this.canShowProperty(name, desc, inspectedObject)) continue; var val = this.findValue(name, desc, data); if (val === '' && desc && desc.type === 'checkbox') { properties.add(name, false); } else { properties.add(name, val); } } // 遍歷模型數據上的全部屬性,並將它們添加到要添加的屬性中(若是適用): if (this.includesOwnProperties) { for (var k in data) { if (k === '__gohashid') continue; // 跳過內部GoJS哈希屬性 if (this._inspectedProperties[k]) continue; // 已經存在 if (declaredProperties[k] && !this.canShowProperty(k, declaredProperties[k], inspectedObject)) continue; properties.add(k, data[k]); } } } } if (!this.showAllProperties) { // 使用選定對象之間不共享的屬性清理共享地圖 // 若是適用,還將屬性添加到添加和共享地圖 var addIt = shared.iterator; var toRemove = []; while (addIt.next()) { if (properties.has(addIt.key)) { var newVal = all.get(addIt.key) + '|' + properties.get(addIt.key); all.set(addIt.key, newVal); if ((declaredProperties[addIt.key] && declaredProperties[addIt.key].type !== 'color' && declaredProperties[addIt.key].type !== 'checkbox' && declaredProperties[addIt.key].type !== 'select') || !declaredProperties[addIt.key]) { // 用於非字符串屬性,即顏色 newVal = shared.get(addIt.key) + '|' + properties.get(addIt.key); shared.set(addIt.key, newVal); } } else { // 因爲addIct仍在迭代中刪除數組 toRemove.push(addIt.key); } } for (var i = 0; i < toRemove.length; i++) { // 刪除任何不顯示allPropertys的東西 shared.remove(toRemove[i]); all.remove(toRemove[i]); } } else { // 使用正確數量的分隔符將缺失的屬性添加到全部屬性 var addIt = properties.iterator; while (addIt.next()) { if (all.has(addIt.key)) { if ((declaredProperties[addIt.key] && declaredProperties[addIt.key].type !== 'color' && declaredProperties[addIt.key].type !== 'checkbox' && declaredProperties[addIt.key].type !== 'select') || !declaredProperties[addIt.key]) { // 用於非字符串屬性,即顏色 var newVal = all.get(addIt.key) + '|' + properties.get(addIt.key); all.set(addIt.key, newVal); } } else { var newVal = ''; for (var i = 0; i < nodecount - 1; i++) newVal += '|'; newVal += properties.get(addIt.key); all.set(addIt.key, newVal); } } // 添加條以防萬一屬性不完整 addIt = all.iterator; while (addIt.next()) { if (!properties.has(addIt.key)) { if ((declaredProperties[addIt.key] && declaredProperties[addIt.key].type !== 'color' && declaredProperties[addIt.key].type !== 'checkbox' && declaredProperties[addIt.key].type !== 'select') || !declaredProperties[addIt.key]) { // 用於非字符串屬性,即顏色 var newVal = all.get(addIt.key) + '|'; all.set(addIt.key, newVal); } } } } nodecount++; } // 構建表屬性行並設置multipleProperties來幫助updateall var mapIt; if (!this.showAllProperties) mapIt = shared.iterator; else mapIt = all.iterator; while (mapIt.next()) { tbody.appendChild(this.buildPropertyRow(mapIt.key, mapIt.value)); // 顯示容許的屬性 } table.appendChild(tbody); mainDiv.appendChild(table); var allIt = all.iterator; while (allIt.next()) { this._multipleProperties[allIt.key] = allIt.value; // 用於updateall以瞭解要更改的屬性 } } }; /** * @ignore *若是不顯示給定的屬性,則該謂詞應爲false。 *一般,它僅檢查屬性描述符上的「顯示」值。 * 默認值是true。 * @param {string} propertyName屬性名稱 * @param {Object} propertyDesc屬性描述符 * @param {Object} inspectedObject數據對象 * @return {boolean}是否應在此檢查器中顯示特定屬性 */ Inspector.prototype.canShowProperty = function (propertyName, propertyDesc, inspectedObject) { if (propertyDesc.show === false) return false; // if "show" is a predicate, make sure it passes or do not show this property if (typeof propertyDesc.show === "function") return propertyDesc.show(inspectedObject, propertyName); return true; } /** * @ignore *若是給定屬性不該由用戶編輯,則此謂詞應爲false。 *一般,它僅檢查屬性描述符上的「 readOnly」值。 * 默認值是true。 * @param {string} propertyName屬性名稱 * @param {Object} propertyDesc屬性描述符 * @param {Object} inspectedObject數據對象 * @return {boolean}是否應在此檢查器中顯示特定屬性 */ Inspector.prototype.canEditProperty = function (propertyName, propertyDesc, inspectedObject) { if (this._diagram.isReadOnly || this._diagram.isModelReadOnly) return false; // assume property values that are functions of Objects cannot be edited var data = (inspectedObject instanceof go.Part) ? inspectedObject.data : inspectedObject; var valtype = typeof data[propertyName]; if (valtype === "function") return false; if (propertyDesc) { if (propertyDesc.readOnly === true) return false; // if "readOnly" is a predicate, make sure it passes or do not show this property if (typeof propertyDesc.readOnly === "function") return !propertyDesc.readOnly(inspectedObject, propertyName); } return true; } /** * @ignore * @param {any} propName * @param {any}屬性 * @param {any}數據 * @return {any} */ Inspector.prototype.findValue = function (propName, propDesc, data) { var val = ''; if (propDesc && propDesc.defaultValue !== undefined) val = propDesc.defaultValue; if (data[propName] !== undefined) val = data[propName]; if (val === undefined) return ''; return val; } /** * @ignore * 將設置this._inspectedProperties [propertyName]並建立HTML錶行: * <tr> * <td> propertyName </ td> * <td> <輸入值= propertyValue /> </ td> * </ tr> * @param {string} propertyName屬性名稱 * @param {*} propertyValue屬性值 * @返回表格行 */ Inspector.prototype.buildPropertyRow = function (propertyName, propertyValue) { var mainDiv = this._div; var tr = document.createElement("tr"); var td1 = document.createElement("td"); td1.textContent = propertyName; tr.appendChild(td1); var td2 = document.createElement("td"); var decProp = this.declaredProperties[propertyName]; var input = null; var self = this; function updateall() { self.updateAllProperties(); } if (decProp && decProp.type === "select") { input = document.createElement("select"); this.updateSelect(decProp, input, propertyName, propertyValue); input.addEventListener("change", updateall); } else { input = document.createElement("input"); input.value = this.convertToString(propertyValue); if (decProp) { var t = decProp.type; if (t !== 'string' && t !== 'number' && t !== 'boolean' && t !== 'arrayofnumber' && t !== 'point' && t !== 'size' && t !== 'rect' && t !== 'spot' && t !== 'margin') { input.setAttribute("type", decProp.type); } if (decProp.type === "color") { if (input.type === "color") { input.value = this.convertToColor(propertyValue); // input.addEventListener("input", updateall); input.addEventListener("change", updateall); } } if (decProp.type === "checkbox") { input.checked = !!propertyValue; input.addEventListener("change", updateall); } } if (input.type !== "color") input.addEventListener("blur", updateall); } if (input) { input.tabIndex = this.tabIndex++; input.disabled = !this.canEditProperty(propertyName, decProp, this.inspectedObject); td2.appendChild(input); } tr.appendChild(td2); this._inspectedProperties[propertyName] = input; return tr; }; /** * @ignore * HTML5顏色輸入僅採用十六進制, * 此var HTML5 canvas將顏色轉換爲十六進制格式。 * 會將「 rgb(255,0,0)」轉換爲「#FF0000」,依此類推。 * @param {string} propertyValue * @return {string} */ Inspector.prototype.convertToColor = function (propertyValue) { var ctx = document.createElement("canvas").getContext("2d"); ctx.fillStyle = propertyValue; return ctx.fillStyle; }; /** * @ignore * @param {string} * @return{Array。<number>} */ Inspector.prototype.convertToArrayOfNumber = function (propertyValue) { if (propertyValue === "null") return null; var split = propertyValue.split(' '); var arr = []; for (var i = 0; i < split.length; i++) { var str = split[i]; if (!str) continue; arr.push(parseFloat(str)); } return arr; }; /** * @ignore * @param {*} * @return {string} */ Inspector.prototype.convertToString = function (x) { if (x === undefined) return "undefined"; if (x === null) return "null"; if (x instanceof go.Point) return go.Point.stringify(x); if (x instanceof go.Size) return go.Size.stringify(x); if (x instanceof go.Rect) return go.Rect.stringify(x); if (x instanceof go.Spot) return go.Spot.stringify(x); if (x instanceof go.Margin) return go.Margin.stringify(x); if (x instanceof go.List) return this.convertToString(x.toArray()); if (Array.isArray(x)) { var str = ""; for (var i = 0; i < x.length; i++) { if (i > 0) str += " "; var v = x[i]; str += this.convertToString(v); } return str; } return x.toString(); }; /** * @ignore * 更新此檢查器中的全部HTML。 */ Inspector.prototype.updateAllHTML = function () { var inspectedProps = this._inspectedProperties; var diagram = this._diagram; var isPart = this.inspectedObject instanceof go.Part; var data = isPart ? this.inspectedObject.data : this.inspectedObject; if (!data) { // clear out all of the fields for (var name in inspectedProps) { var input = inspectedProps[name]; if (input instanceof HTMLSelectElement) { input.innerHTML = ""; } else if (input.type === "color") { input.value = "#000000"; } else if (input.type === "checkbox") { input.checked = false; } else { input.value = ""; } } } else { for (var name in inspectedProps) { var input = inspectedProps[name]; var propertyValue = data[name]; if (input instanceof HTMLSelectElement) { var decProp = this.declaredProperties[name]; this.updateSelect(decProp, input, name, propertyValue); } else if (input.type === "color") { input.value = this.convertToColor(propertyValue); } else if (input.type === "checkbox") { input.checked = !!propertyValue; } else { input.value = this.convertToString(propertyValue); } } } } /** * @ignore * 在給定propertyName的狀況下,使用適當的選項列表更新HTMLSelectElement */ Inspector.prototype.updateSelect = function (decProp, select, propertyName, propertyValue) { select.innerHTML = ""; // 清除那裏的任何東西 var choices = decProp.choices; if (typeof choices === "function") choices = choices(this.inspectedObject, propertyName); if (!Array.isArray(choices)) choices = []; decProp.choicesArray = choices; // 記住實際選擇值的列表(不是字符串) for (var i = 0; i < choices.length; i++) { var choice = choices[i]; var opt = document.createElement("option"); opt.text = this.convertToString(choice); select.add(opt, null); } select.value = this.convertToString(propertyValue); } /** * @ignore * 據如下內容更新{@link #inspectedObject}的全部數據屬性 * HTML輸入元素中保存的當前值。 */ Inspector.prototype.updateAllProperties = function () { var inspectedProps = this._inspectedProperties; var diagram = this._diagram; if (diagram.selection.count === 1 || !this.multipleSelection) { // 單對象更新 var isPart = this.inspectedObject instanceof go.Part; var data = isPart ? this.inspectedObject.data : this.inspectedObject; if (!data) return; // 沒有數據時切勿嘗試更新數據! diagram.startTransaction('set all properties'); for (var name in inspectedProps) { var input = inspectedProps[name]; var value = input.value; // 不要更新「 readOnly」數據屬性 var decProp = this.declaredProperties[name]; if (!this.canEditProperty(name, decProp, this.inspectedObject)) continue; // 若是是布爾值,或者之前的值爲布爾值, // 將值解析爲布爾值,而後更新input.value以使其匹配 var type = ''; if (decProp !== undefined && decProp.type !== undefined) { type = decProp.type; } if (type === '') { var oldval = data[name]; if (typeof oldval === 'boolean') type = 'boolean'; // infer boolean else if (typeof oldval === 'number') type = 'number'; else if (oldval instanceof go.Point) type = 'point'; else if (oldval instanceof go.Size) type = 'size'; else if (oldval instanceof go.Rect) type = 'rect'; else if (oldval instanceof go.Spot) type = 'spot'; else if (oldval instanceof go.Margin) type = 'margin'; } // 轉換爲特定類型(若是須要) switch (type) { case 'boolean': value = !(value === false || value === 'false' || value === '0'); break; case 'number': value = parseFloat(value); break; case 'arrayofnumber': value = this.convertToArrayOfNumber(value); break; case 'point': value = go.Point.parse(value); break; case 'size': value = go.Size.parse(value); break; case 'rect': value = go.Rect.parse(value); break; case 'spot': value = go.Spot.parse(value); break; case 'margin': value = go.Margin.parse(value); break; case 'checkbox': value = input.checked; break; case 'select': value = decProp.choicesArray[input.selectedIndex]; break; } // 在解析爲不一樣的狀況下(例如在布爾值的狀況下), // 顯示的值應與實際值匹配 input.value = value; // 以可撤消的方式修改數據對象 diagram.model.setDataProperty(data, name, value); // notify any listener if (this.propertyModified !== null) this.propertyModified(name, value, this); } diagram.commitTransaction('set all properties'); } else { // 以可撤消的方式修改數據對象 diagram.startTransaction('set all properties'); for (var name in inspectedProps) { var input = inspectedProps[name]; var value = input.value; var arr1 = value.split('|'); var arr2 = []; if (this._multipleProperties[name]) { // 若是它是聯合及其複選框類型,則不要拆分 if (this.declaredProperties[name] && this.declaredProperties[name].type === 'checkbox' && this .showAllProperties) { arr2.push(this._multipleProperties[name]); } else { arr2 = this._multipleProperties[name].toString().split('|'); } } var it = diagram.selection.iterator; var change = false; if (this.declaredProperties[name] && this.declaredProperties[name].type === 'checkbox') change = true; // always change checkbox if (arr1.length < arr2.length // 即Alpha | Beta-> Alpha進行更改 && (!this.declaredProperties[name] // from and to links || !(this.declaredProperties[name] // 不要更改顏色複選框和選項,由於它們老是少 && (this.declaredProperties[name].type === 'color' || this.declaredProperties[name].type === 'checkbox' || this.declaredProperties[name].type === 'choices')))) { change = true; } else { // 特性變化的標準檢測 for (var j = 0; j < arr1.length && j < arr2.length; j++) { if (!(arr1[j] === arr2[j]) && !(this.declaredProperties[name] && this.declaredProperties[name].type === 'color' && arr1[j] .toLowerCase() === arr2[j].toLowerCase())) { change = true; } } } if (change) { // 只更改須要更改的屬性,而不是所有更改 for (var i = 0; i < diagram.selection.count; i++) { it.next(); var isPart = it.value instanceof go.Part; var data = isPart ? it.value.data : it.value; if (data) { // 若是沒有數據,則忽略所選節點 if (i < arr1.length) value = arr1[i]; else value = arr1[0]; // 不要更新「 readOnly」數據屬性 var decProp = this.declaredProperties[name]; if (!this.canEditProperty(name, decProp, it.value)) continue; // 若是是布爾值,或者之前的值爲布爾值, // 將值解析爲布爾值,而後更新input.value以使其匹配 var type = ''; if (decProp !== undefined && decProp.type !== undefined) { type = decProp.type; } if (type === '') { var oldval = data[name]; if (typeof oldval === 'boolean') type = 'boolean'; // infer boolean else if (typeof oldval === 'number') type = 'number'; else if (oldval instanceof go.Point) type = 'point'; else if (oldval instanceof go.Size) type = 'size'; else if (oldval instanceof go.Rect) type = 'rect'; else if (oldval instanceof go.Spot) type = 'spot'; else if (oldval instanceof go.Margin) type = 'margin'; } // 轉換爲特定類型(若是須要) switch (type) { case 'boolean': value = !(value === false || value === 'false' || value === '0'); break; case 'number': value = parseFloat(value); break; case 'arrayofnumber': value = this.convertToArrayOfNumber(value); break; case 'point': value = go.Point.parse(value); break; case 'size': value = go.Size.parse(value); break; case 'rect': value = go.Rect.parse(value); break; case 'spot': value = go.Spot.parse(value); break; case 'margin': value = go.Margin.parse(value); break; case 'checkbox': value = input.checked; break; case 'select': value = decProp.choicesArray[input.selectedIndex]; break; } // 在解析爲不一樣的狀況下(例如在布爾值的狀況下), // 顯示的值應與實際值匹配 input.value = value; // 以可撤消的方式修改數據對象 diagram.model.setDataProperty(data, name, value); // 通知全部 if (this.propertyModified !== null) this.propertyModified(name, value, this); } } } } diagram.commitTransaction('set all properties'); } }; //-------------------------------------------------------------------- </script> </head> <body onload="init()"> <div id="sample"> <div id="myDiagramDiv" style="background-color: #34343C; border: solid 1px black; height: 570px;"></div> <p><button id="zoomToFit">縮放 <button id="centerRoot">居中</button></p> <div> <button id="SaveButton" onclick="save()">Save</button> <button onclick="load()">Load</button> </div> <div> <div id="myInspector"> </div> </div> <p> 此可編輯的組織結構圖示例根據層次結構中的樹級別對節點進行顏色編碼。 圖表模型以JSON格式保存 </p> <p> 雙擊節點以添加人或圖背景以添加新的老闆。 雙擊該圖使用ClickCreatingTool 使用自定義的ClickCreatingTool.insertPart滾動到新節點並開始編輯TextBlock的名稱。 </p> <p> 將一個節點拖到另外一個節點上以更改關係。 您還能夠繪製從節點背景到其餘沒有「老闆」的節點的連接。 連接也能夠從新連接以更改關係。 右鍵單擊或點住節點以顯示上下文菜單,該菜單使您能夠: <ul> <li>空缺職位-刪除特定於該職位的當前人員的信息</li> <li>刪除角色-徹底刪除該角色併爲全部子項添加父項</li> <li>刪除部門-刪除角色和整個子樹</li> </ul> 刪除節點或連接將孤立子節點並生成新樹。 自定義的SelectionDeleting DiagramEvent偵聽器將清除老闆信息 當父母被刪除時。 </p> <p> 選擇一個節點以編輯/更新節點數據值。 該示例使用Data Inspector擴展來顯示和修改零件數據。 </p> <div> </div> </div> </body> </html>
GoJS事件https://blog.csdn.net/pdw2009/article/details/82993971css