效果圖:
jsmind組件下載地址:https://files.cnblogs.com/files/fengyeqingxiang/jsmind.zip
后端代碼,此處以C#編寫的后台,Java或其他語言同理
using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Web; using System.Web.Mvc; namespace Web.Controllers { public class TreeDataController : BaseController { BLL.TreeData bll = new BLL.TreeData(); #region 以樹形式展示圖紙目錄 /// <summary> /// 視圖 /// </summary> /// <returns></returns> public ActionResult DrawingTree() { if (CurrentUser == null)//驗證用戶是否登錄 return new HttpUnauthorizedResult(); return View(); } #endregion /// <summary> /// 文件樹視圖,頁面初始化獲取樹數據,以json形式返回 /// </summary> /// <returns></returns> public ActionResult GetTreeData() { List<FileNode> listTree = InitTree(); return Json(listTree, JsonRequestBehavior.AllowGet); } /// <summary> /// 初始化加載樹 /// </summary> /// <returns></returns> private List<FileNode> InitTree() { List<FileNode> listNodes = new List<FileNode>(); var newTree = bll.QueryList(); //數據庫查找數據源,此處也可以定義虛擬數據 #region 首次加載檢測不到數據時默認插入項目節點 if (newTree.Count == 0) { bll.Add(new Model.TreeData() { BgColor = "#eee";//節點背景顏色 FgColor="#eee";//節點字體顏色 Level = 0, Order = 0, TreeName = "項目名稱", TreeCode = "節點編碼", ParentId = 0, UpdateTime = DateTime.Now, FilePath=null }); } #endregion #region 一次性存儲數據源,后面后面遞歸子集時多次使用 List<FileNode> nodeList = new List<FileNode>(); foreach (var item in newTree) { FileNode node2 = new FileNode(); node2.id = item.Id;//要顯示的id,此id一般為表的主鍵,具有唯一性 node2.topic = item.TreeName;//要顯示的名稱 node2.direction = "right";//思維導圖伸向,目前只支持left/right node2.parentId = item.ParentId; node2.expanded = true;//該節點是否展開 nodeList.Add(node2); } #endregion #region 裝載數據源,此數據結果返回的是最終的所有結點樹集合 List<FileNode> rootNode = new List<FileNode>(); foreach (var plist in newTree.Where(t => t.ParentId== 0)) { FileNode node = new FileNode(); node.id = plist.Id; node.topic = plist.Code; node.direction = plist.Note;//思維導圖伸向,目前只支持left/right node.parentId = plist.ParentId; node.background = "#eee";//節點背景顏色 node.foreground = "blue";//節點字體顏色 node.expanded = true; node.children = CreateChildTree(nodeList, node); rootNode.Add(node); } return rootNode; #endregion } /// <summary> /// 獲取子集樹 /// </summary> /// <param name="TreeList"></param> /// <param name="jt"></param> /// <returns></returns> private List<FileNode> CreateChildTree(List<FileNode> TreeList, FileNode filenode) { List<FileNode> nodeList = new List<FileNode>(); var children = TreeList.Where(t => t.parentId == filenode.Id); foreach (var chl in children) { FileNode node = new FileNode(); node.id = chl.Id; node.topic = chl.topic; node.direction = chl.direction;//思維導圖伸向,目前只支持left/right node.parentId = chl.parentId; node.background = chl.background;//節點背景顏色 node.foreground = chl.foreground;//節點字體顏色 node.expanded = true; node.children = CreateChildTree(TreeList, node); nodeList.Add(node); } return nodeList; } /// <summary> /// 根據選擇的節點ID和方向參數,獲取同級的上一個節點ID或下一個節點ID /// </summary> /// <returns>上一個或下一個節點排序號</returns> [HttpPost] public JsonResult GetMoveOrder() { var id = GetQueryString("id"); var parentId = GetQueryInt("parent", 0); var direction = GetQueryString("direction"); var model = bll.GetModel(Convert.ToInt32(id)); int upId = -1; int targetId = -1;//最終返回的相鄰的上/下的節點ID if (direction == "up") //向上移動 { upId = Convert.ToInt32(model.order) - 1; if (upId >= 0) { //執行修改本身 model.order= upId; bll.Update(model); //執行修改相鄰的上一個 var list = bll.GetAllList("parentId='" + parentId+ "' and order='" + upId + "' and id<>'"+Id+"'"); if (list.Count > 0) { var upModel = list[0]; upModel.order= upId + 1; bll.Update(upModel); targetId = upModel.id; } } } else { upId = Convert.ToInt32(model.order) + 1; var list = bll.GetAllList("ParentDrawingId='" + parentId+ "'"); if (upId < list.Count) { //執行修改本身 model.order= upId; bll.Update(model); //執行修改相鄰的上一個 var newList = list.Where(c => c.order== upId && c.id!= model.id); if (newList.Count() > 0) { var upModel = newList.FirstOrDefault(); upModel.order= upId - 1; bll.Update(upModel); targetId = upModel.DrawingId; } } } return Json(new { result = targetId.ToString() }, JsonRequestBehavior.AllowGet); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace Web.Model.Design { ///<summary> ///節點實體類 /// </summary> [Serializable] public class FileNode { public int id { get; set; }//對應jsmind唯一id public string topic { get; set; }//對應jsmind顯示的名稱 public string direction { get; set; }//對應jsmind思維導圖的朝向 left/right public bool expanded { get; set; } //對應jsmind該節點是否展開true/false public string background { get; set; } //jsmind只識別background-color屬性,此處定義“-”會編譯不通過,待前台js批量替換處理 public string foreground { get; set; } //jsmind只識別foreground-color屬性,此處定義“-”會編譯不通過,待前台js批量替換處理 public int parentId { get; set; } //jsmind沒有此屬性,此處定義為了與數據庫所屬父節點字段對應,遞歸關聯查詢時會用到 public List<FileNode> children { get; set; }//對應jsmind當前節點的子節點集合 } }
前端頁面代碼,此處以asp.net mvc頁面視圖編寫,都是插件獲取后台返回的json,其他語言同理
@model List<Model.Admin.TreeData> @{ ViewBag.Title = "上傳文件"; } <div class="bim-cont"> <div class="bim-forms bg-none"> <form class="form-inline"> <div class="form-group"> <input type="hidden" id="moveDirection" value="up" /> <input type="text" class="form-control" id="keywords" placeholder="請輸入節點名稱"> </div> <button type="button" class="btn js-btn-class margin" οnclick="search()">檢索</button> </form> </div> <div class="box table-responsive border-top-none" id="contentbody" style="overflow: hidden;"> <div id="layout"> <div id="jsmind_container"></div> <div style="display: none"> <input class="file" type="file" id="image-chooser" accept="image/*" /> </div> </div> </div> </div> <!--右側菜單--> <div id="divmenu" class="menu"> <ul> <li οnclick="expand_all()" class="pub">展開所有</li> <li οnclick="collapse_all()" class="pub">合並所有</li> <li οnclick="zoomIn()" class="pub">畫布放大</li> <li οnclick="zoomOut()" class="pub">畫布縮小</li> <li οnclick="add_node();" class="add">新增節點</li> <li οnclick="add_upfile();" class="upload">上傳文件</li> <li οnclick="show_selected();" class="sel">查看節點</li> <li οnclick="remove_node()" class="delete">刪除</li> <li οnclick="move_node('up')" class="move">上移</li> <li οnclick="move_node('down')" class="move">下移</li> </ul> </div> @section Styles{ <link type="text/css" rel="stylesheet" href="~/Content/plugins/jsmind/style/jsmind.css" /> <style> .bim-forms { border-bottom: solid 1px #f5f1f1; } .bim-forms .btn { padding: 6px 12px; } .menu { width: 100px; font-size: 14px; font-family: "微軟雅黑"; border: 1px solid #ccc; z-index: 9999; position: absolute; display: none; background: #f2f2f2; } .menu ul { margin: 0px; padding: 0px; text-align: center; list-style-type: none; } .menu ul li { padding: 3px 0px; font-size: 12px; } .menu ul li:hover { background: #e1dddd; } .menu ul li a:link { color: #000; text-decoration: none; } </style> } @section Scripts{ <script type="text/javascript" src="~/Content/plugins/jsmind/js/jsmind.js"></script> <script type="text/javascript" src="~/Content/plugins/jsmind/js/jsmind.draggable.js"></script> <script type="text/javascript" src="~/Content/plugins/jsmind/js/jsmind.screenshot.js"></script> <script type="text/javascript"> var _jm = null; function open_empty() { var options = { container: 'jsmind_container', theme: 'greensea', editable: false } _jm = jsMind.show(options); } var jm = 0; function auto_height() { if (jm == 0) jm = $(".root").offset().top - ($("#jsmind_container").parent().height() / 2) - ($("body").height() / 2) -140; //獲取中心點位置 var cavHeight = $("#jsmind_container").find("canvas").height();//-3703 $("#jsmind_container").height(cavHeight); //將畫布高度設置與拖動層 $("#jsmind_container").offset({ top: ((0 - jm)), left: ($("body").width() / 10) }); //將中心點調至屏幕中心 } //預覽文件 function showFile(filepath) { layer.photos({ photos: { "data": [{ "src": filepath }] }, anim: 5 }); } $(function () { //初始化裝載數據 open_empty(); InitJsMind(); dragFunc("jsmind_container"); //監聽右側菜單點擊事件,發生點擊則隱藏菜單層 $("#divmenu").click(function (event) { var $this = $(event.target); $("#divmenu").hide(); }); //畫布添加鼠標點擊事件 $('#jsmind_container').mousedown(function (e) { if (e.which == 1) { // 1 = 鼠標左鍵 left; 2 = 鼠標中鍵; 3 = 鼠標右鍵 $("#divmenu").hide(); } }); $("#contentbody").bind("contextmenu", function () { var div = $("#divmenu"); if (showmenu()) div.css({ "left": document.body.scrollLeft + event.clientX - 125, "top": document.body.scrollTop + event.clientY - 60 }).show(); return false; }); }); //頁面初始化獲取樹數據 function InitJsMind() { $.get("/Admin/GetTreeData", function (data) { var str = JSON.stringify(data); str = str.slice(1); //刪除第一個字符[ str = str.substring(0, str.length - 1);//刪除 最后一個字符] re = new RegExp("background", "g"); //定義正則表達式,g標識全部替換 var newstr = str.replace(re, "background-color"); re = new RegExp("foreground", "g"); //定義正則表達式,g標識全部替換 newstr = newstr.replace(re, "foreground-color"); var jsonData = $.parseJSON(newstr); console.log(jsonData); //加載模型樹 var mind = { "meta": { "name": "", "author": "", "version": "0" }, "format": "node_tree",//node_array "data": jsonData } _jm.show(mind); auto_height(); }) } //新增節點 function add_node() { var selected_node = _jm.get_selected_node(); // as parent of new node if (!selected_node) { layer.msg('請選擇一個節點!'); return; } var fHeight = 280; if (selected_node.data.leave == 0) { //根節點新增時因為字段多,高度單獨做調整 fHeight = 420; } layer_show('新增節點', '/Admin/Add?Id=' + selected_node.id, 600, fHeight); } //新增、變更節點完成后子頁面調此方法 function append_node(newNode) { _jm.enable_edit();//新增前置為可編輯狀態 var selected_node = _jm.get_selected_node(); // as parent of new node if (!selected_node) { prompt_info('請先選擇一個節點.'); return; } //處理json數據 var str = JSON.stringify(newNode); re = new RegExp("background", "g"); //定義正則表達式,g標識全部替換 var newstr = str.replace(re, "background-color"); re = new RegExp("foreground", "g"); //定義正則表達式,g標識全部替換 newstr = newstr.replace(re, "foreground-color"); var jsonData = $.parseJSON(newstr); //delete jsonData["direction"]; console.log(jsonData); //開始新增 var nodeid = newNode.id; var topic = newNode.topic; var node = _jm.add_node(selected_node, nodeid, topic, jsonData); _jm.disable_edit();//新增前置為不可編輯狀態 } //文件變更 function update_nodes() { var selected_node = _jm.get_selected_node(); // as parent of new node if (!selected_node) { layer.msg('請選擇一個節點!'); return; } var isLastNode = Object.keys(selected_node.children).length; if (isLastNode > 0) { layer.msg('請選擇文件節點進行變更!'); } else { if (selected_node.id == 0) { //中心根節點不能直接上傳文件 layer.msg("根節點不可上傳!", { icon: 0 }); } else { layer_show('文件變更', '/Admin/Update?Id=' + selected_node.id, 700, 540); } } } //文件變更后子頁面調父頁面方法 function update_node(nodeid, topic) { _jm.enable_edit();//置為可編輯狀態 _jm.update_node(nodeid, topic); _jm.disable_edit();//置為不可編輯狀態 } //上移、下移節點,direction(top,bottom) function move_node(direction) { var selected_node = _jm.get_selected_node(); $("#moveDirection").val(direction); $.post("/Admin/GetMoveOrder", { Id: selected_node.id, parentId: selected_node.data.parentId, direction: direction }, function (d) { if (d.result != -1) { _jm.enable_edit();//置為可編輯狀態 _jm.move_node(selected_node.id, d.result); _jm.disable_edit();//置為不可編輯狀態 } else { if (direction=="up") { layer.msg("已經是最頂級!", { icon: 0 }); } else { layer.msg("已經是最下級!", { icon: 0 }); } } }); } //上傳文件 function add_upfile() { var selected_node = _jm.get_selected_node(); if (!selected_node) { layer.msg('請選擇一個節點!'); return; } var isLastNode = Object.keys(selected_node.children).length; if (isLastNode > 0) { layer_show('文件上傳', '/Admin/upload?Id=' + selected_node.id, 700, 540); } else { if (selected_node.id == 0) { //中心根節點不能直接上傳文件 layer.msg("根節點不可上傳!", { icon: 0 }); } else { layer_show('文件上傳', '/Admin/upload?Id=' + selected_node.id, 700, 540); } } } //刪除節點 function remove_node() { var selected_node = _jm.get_selected_node(); // as parent of new node if (!selected_node) { layer.msg('請選擇一個節點!'); return; } var isLastNode = selected_node.children.length; if (isLastNode > 0) { layer.msg('存在子集,不能刪除!'); } else { layer.confirm('確認要刪除嗎?', function (index) { jQuery.post("/Admin/Delete?Id=" + selected_node.id, function (msg) { if (msg == 1) { //InitJsMind(); _jm.enable_edit();//新增前置為可編輯狀態 _jm.remove_node(selected_node.id); _jm.disable_edit();//新增前置為不可編輯狀態 layer.msg('已刪除!', { icon: 1, time: 1000 }); } else { layer.msg('操作失敗!', { time: 1000 }); } }); }); } } //節點查看 function show_selected() { var selected_node = _jm.get_selected_node(); // as parent of new node if (!selected_node) { layer.msg('請選擇一個節點!'); return; } if (selected_node.id == 0) { layer.msg('根節點不支持查看!'); return; } var layer_height = 420; if (selected_node.data.leave > 0) layer_height = 280; if (selected_node.data.leave == 1) layer_height = 420; layer_show('節點查看', '/Admin/Show?Id=' + selected_node.id, 600, layer_height); } //展開選擇的節點 function expand() { var selected_id = get_selected_nodeid(); if (!selected_id) { layer.msg('please select a node first.'); return; } _jm.expand_node(selected_id); } //合並選擇的節點 function collapse() { var selected_id = get_selected_nodeid(); if (!selected_id) { layer.msg('please select a node first.'); return; } _jm.collapse_node(selected_id); } //展開所有節點 function expand_all() { _jm.expand_all(); $("#jsmind_container").offset({ top: ((0 - jm)), left: 100 }); //將中心點調至屏幕中心 } //合並所有節點 function collapse_all() { _jm.collapse_all(); $("#jsmind_container").offset({ top: ((0 - jm)), left: 100 }); //將中心點調至屏幕中心 } //畫布縮小 function zoomIn() { if (_jm.view.zoomIn()) { zoomOutButton.disabled = false; } else { zoomInButton.disabled = true; }; }; //畫布放大 function zoomOut() { if (_jm.view.zoomOut()) { zoomInButton.disabled = false; } else { zoomOutButton.disabled = true; }; }; function dragFunc(id) { var Drag = document.getElementById(id); Drag.onmousedown = function (event) { var ev = event || window.event; event.stopPropagation(); var disX = ev.clientX - Drag.offsetLeft; var disY = ev.clientY - Drag.offsetTop; document.onmousemove = function (event) { var ev = event || window.event; Drag.style.left = ev.clientX - disX + "px"; Drag.style.top = ev.clientY - disY + "px"; Drag.style.cursor = "move"; }; }; Drag.onmouseup = function () { document.onmousemove = null; this.style.cursor = "default"; }; }; //通過JS屏蔽自帶右鍵菜單 document.oncontextmenu = function (e) { return false; } //動態化展示右鍵菜單 function showmenu(selected_node) { var selected_node = _jm.get_selected_node(); // as parent of new node if (!selected_node) { $("#divmenu ul li:not(.pub)").hide(); $("#divmenu ul li[class='pub']").show(); } else { if ((selected_node.isroot || false)) { $("#divmenu ul li:not(.add)").hide(); $("#divmenu ul li[class='add']").show(); } else if (selected_node.data.leave == 1) { if (selected_node.children.length > 0) { $(".pub,.update,.view,.history,.delete").hide(); $(".sel,.upload,.add,.move").show(); } else { $(".pub,.update,.view,.history").hide(); $(".sel,.upload,.add,.move,.delete").show(); } } else { if (selected_node.children.length > 0) { $(".pub,.view,.history,.update,.delete").hide(); $(".upload,.sel,.add,.move").show(); } else { if (selected_node.data.filepath == "null" || selected_node.data.filepath == undefined) { $(".upload,.sel,.delete,.add,.move").show(); $(".pub,.view,.history,.update").hide(); } else { $(".view,.delete,.update,.history,.move").show(); $(".pub,.sel,.upload,.add").hide(); } } } } return true; } //搜索 function search() { var count = 0; if($.trim($("#keywords").val()).length == 0) { InitJsMind(); } else { //InitJsMind(); $('jmnode').each(function (i, e) { var code = $(this).attr("code"); if (code != undefined) { code = $(this).attr("code").toLowerCase(); var filenode = "<span>" + $("#keywords").val().toLowerCase() + "</span>"; if ($(this).html().toLowerCase().indexOf($("#keywords").val().toLowerCase()) != -1 || $(this).html().toLowerCase().indexOf(filenode) != -1 || code.indexOf($("#keywords").val().toLowerCase()) != -1) { $(this).addClass("root selected"); $(this).css("opacity","0.9"); count = parseInt(count) + 1; if (count == 1) { auto_height();//恢復位置 console.log(code); var jm2 = $(this).css("top").replace("px", ""); //獲取當前點位置 $("#jsmind_container").offset({ top: ((0 - parseInt(jm2))+($("body").height()/2)), left: ($("body").width() / 10) }); //將中心點調至屏幕中心 } } else { $(this).removeClass("root selected"); $(this).css("opacity", "0.1"); //沒找到的元素透明的設高 } } }); if (count == 0) { layer.msg('未檢索到任何數據!'); } } } //鼠標滾輪縮放 window.onmousewheel = document.onmousewheel = function (e) { e = e || window.event; if (e.wheelDelta) { //判斷瀏覽器IE,谷歌滑輪事件 if (e.wheelDelta > 0) { //當滑輪向上滾動時 _jm.view.zoomIn() } if (e.wheelDelta < 0) { //當滑輪向下滾動時 _jm.view.zoomOut(); } } else if (e.detail) { //Firefox滑輪事件 if (e.detail > 0) { //當滑輪向下滾動時 _jm.view.zoomOut(); } if (e.detail < 0) { //當滑輪向上滾動時 _jm.view.zoomIn() } } } </script> }