jsMind思维导图模式展示数据
效果图:
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> }