无限分级

很多时候我们不确定等级关系的层级,这个时候就需要用到无限分级了。

说到无限分级,又要扯到递归调用了。(据说频繁递归是很耗性能的),在此我们需要先设计好表机构,用来存储无限分级的数据。当然,以下都是自己捣鼓的结果,非标准。谁有更好的设计望不吝啬赐教。

说来其实也简单,就是一个ID和父ID的关系。

以此类推,Id需要是唯一的,ParenId需要是Id列里面存在即可。这样我们就实现无限分级了,如果再加一列Sort排序就更完美了。

jstree插件

官方地址:https://www.jstree.com/

为什么要用这个插件?因为有方便的api给我们做数据绑定,且支持节点拖动来实现增删改,个人觉得这个功能挺强大的。

Demo

下面我们来基于jstree插件来实现无限分级数据操作。以区域数据操作为例,用Code First的方式来编写demo代码。

创建Region实体

为了配合插件自动生成的节点id,我们这里使用的Node和ParentNode来存储上下级关系(而不是上面说的id和parentid,但是实际效果是一样的)。

/// <summary>
/// 区域
/// </summary>
public class Region
{/// <summary>/// 主键id/// </summary>public int Id { get; set; }/// <summary>/// 名称/// </summary>public string Name { get; set; }/// <summary>/// 节点/// </summary>public string Node { get; set; }/// <summary>/// 父节点/// </summary>public string ParentNode { get; set; }}

满足jstree插件的数据对象Dto

为了适应jstree插件的数据要求,我们需要把上面的数据转换成树状的数据对象。

/// <summary>
/// Dto
/// </summary>
public class RegionsTreeOutput
{/// <summary>/// Id/// </summary>public int RegionsId { get; set; }/// <summary>/// tree显示文本(对应region的name)/// </summary>public string text { get; set; }/// <summary>/// tree的id(对应Node)/// </summary>public string id { get; set; }      /// <summary>/// 子节点数据(此属性就体现的数据的层级关系)/// </summary>public List<RegionsTreeOutput> children { get; set; }
}

View Code

数据转换

  #region GetRegionTree 初始化数据获取 的辅助方法public RegionsTreeOutput LoadRegions(string id, List<Region> inRegions, RegionsTreeOutput outRegions){List<Region> regions = inRegions.Where(t => t.ParentNode == id).ToList();if (outRegions == null)//加载父节点
            {outRegions = ToTreeData(regions[0]);LoadRegions(outRegions.id, inRegions, outRegions);}else//加载子节点
            {outRegions.children = ToTreesData(regions);if (regions.Count > 0){for (int i = 0; i < regions.Count; i++){LoadRegions(regions[i].Node, inRegions, outRegions.children[i]);//递归调用
                    }}}return outRegions;}public RegionsTreeOutput ToTreeData(Region region){var treeData = new RegionsTreeOutput();treeData.id = region.Node;treeData.text = region.Name;treeData.RegionsId = region.Id;            return treeData;}public List<RegionsTreeOutput> ToTreesData(List<Region> listRegion){var regions = new List<RegionsTreeOutput>();for (int i = 0; i < listRegion.Count; i++){regions.Add(ToTreeData(listRegion[i]));}return regions;}#endregion

View Code

初始化获取转换后的数据

 /// <summary>/// 初始化数据获取/// </summary>/// <returns></returns>public JsonResult GetResultData(){TreeDbContext db = new TreeDbContext();var regions = db.Regions.Where(t => true).ToList();var regionObj = LoadRegions("-1", regions, null);return Json(regionObj);}

以上后台的数据差不多就完成了。

前台数据加载

 $(function () {$.post("/Home/GetResultData", null, function (sData) {treeObj = $('#jstree_demo').jstree({//, "checkbox"'plugins': ["contextmenu", "dnd", "search", "state", "types", "wholerow"],'core': {"animation": 0,"check_callback": true,'force_text': true,"themes": { "stripes": true },'data': sData},"types": {"default": {"icon": "fa fa-folder icon-state-warning icon-lg"},"file": {"icon": "fa fa-file icon-state-warning icon-lg"}},"contextmenu": {select_node: false,show_at_node: true,items: function (o, cb) {//因为这里我们之后需要定义多个项,所以通过对象的方式返回var actions = {};//添加一个"新增"右键菜单actions.create = {//这里的create其实阔以随意命名,关键是里面的 这里面的 action回调方法"separator_before": false,//Create这一项在分割线之前"separator_after": true,//Create这一项在分割线之后"_disabled": false, //false表示 create 这一项可以使用; true表示不能使用"label": "新增",  //Create这一项的名称 可自定义"action": function (data) {  //点击Create这一项触发该方法,这理还是蛮有用的var inst = $.jstree.reference(data.reference),obj = inst.get_node(data.reference);//获得当前节点,可以拿到当前节点所有属性//新加节点,以下三行代码注释掉就不会添加节点inst.create_node(obj, {}, "last", function (new_node) {setTimeout(function () { inst.edit(new_node); }, 0);//新加节点后触发 重命名方法,即 创建节点完成后可以立即重命名节点
                                    });}};if (o.id != "0001")//屏蔽对根节点的操作  “0001”改成根节点对应的真是id
                            {//添加一个"重命名"右键菜单actions.rename = {"separator_before": false,"separator_after": false,"_disabled": false, //(this.check("rename_node", data.reference, this.get_parent(data.reference), "")),"label": "重命名","action": function (data) {var inst = $.jstree.reference(data.reference),obj = inst.get_node(data.reference);inst.edit(obj);}}//添加一个"删除"右键菜单actions.delete = {"separator_before": false,"icon": false,"separator_after": false,"_disabled": false, //(this.check("delete_node", data.reference, this.get_parent(data.reference), "")),"label": "删除","action": function (data) {var inst = $.jstree.reference(data.reference),obj = inst.get_node(data.reference);if (inst.is_selected(obj)) {inst.delete_node(inst.get_selected());}else {inst.delete_node(obj);}}};}return actions;//返回右键菜单项
                        }},});});});

View Code

其他操作

//删除节点
$('#jstree_demo').on('delete_node.jstree', function (e, data) {            var id = data.node.original.RegionsId;          $.ajax({type: "get",url: "/Home/DeleteRegion?id=" + id,success: function (sData) {}}); });
//移动节点
$('#jstree_demo').on('move_node.jstree', function (e, data) {saveRegions(data);});
//修改名
$('#jstree_demo').on('rename_node.jstree', function (e, data) {            saveRegions(data);});
//保存
function saveRegions(data) {            var id = data.node.original.RegionsId;var name = data.node.text;//修改后的name//var oldName = data.old;//原name           //var pNode = $('#jstree_demo').jstree().get_node(data.node.parent).original.RegionsId;var josnData = { "Id": id, "Node": data.node.id, "ParentNode": data.node.parent, "Name": name };$.ajax({url: "/Home/SaveRegions",data: josnData,success: function (sData) {                   data.node.original.RegionsId = sData;data.node.state.opened = false;//是否展开
                }});}

当然,记得修改或是删除要取RegionsId这个对应后台实体的ID。

通过按钮来操作增删改

function createTree() {   var ref = $('#jstree_demo').jstree(true),sel = ref.get_selected();if (!sel.length) { return false; }sel = sel[0];sel = ref.create_node(sel, { "type": "file" });if (sel) {ref.edit(sel);}
};function renameTree() {var ref = $('#jstree_demo').jstree(true),sel = ref.get_selected();if (!sel.length) { return false; }sel = sel[0];ref.edit(sel, function () {});
};function deleteTree() {var ref = $('#jstree_demo').jstree(true),sel = ref.get_selected();if (!sel.length) { return false; }ref.delete_node(sel);
};

更加详细的细节请看demo。

链接:http://pan.baidu.com/s/1hrN5QvU 密码:c6b7


2016.08.26更新

以上方式有问题:如果多个用户同时新建节点,会有重复的。(因为节点data.node.id是前端页面自动生成的)

解决方法:(更改data.node.id的值从后台赋值)

前端:

$(function () {treeObj = $('#jstree_demo').jstree({//, "checkbox"'plugins': ["contextmenu", "dnd", "search", "state", "types", "wholerow"],'core': {"animation": 0,"check_callback": true,'force_text': true,"themes": { "stripes": true },//修改点(1)  【可以给插件刷新】'data': function (obj, callback) {$.ajax({"type": "post","url": "/Home/GetResultData","data": {},"success": function (sData) {callback(sData);//回调传请求得到的数据(这里可以把数据组装成插件需要的数据格式)
                            }});}},

//保存
function saveRegions(data) {var id = data.node.original.RegionsId;var name = data.node.text;//修改后的name//var oldName = data.old;//原name//var pNode = $('#jstree_demo').jstree().get_node(data.node.parent).original.RegionsId;//修改点(2)   【取自定义的父节点】var ParentNode = ($('#jstree_demo').jstree().get_node(data.node.parent).original.MyNode || data.node.parent);// data.node.parent;//修改点(3)   【取自定义的节点】var pNode = (data.node.original.MyNode || data.node.id);var josnData = { "Id": id, "Node": pNode, "ParentNode": ParentNode, "Name": name };$.ajax({url: "/Home/SaveRegions",data: josnData,type: "post",success: function (sData) { data.node.original.RegionsId = sData.Id;//data.node.state.opened = true;//是否展开data.node.original.MyNode = sData.Node;//修改点(4)$.jstree.reference("#jstree_demo").refresh();//刷新jstree控件,重新加载数据()
}});
} 

后台:

input.Node = Guid.NewGuid().ToString();

更新demo:http://pan.baidu.com/s/1gfdmQoN

【注意】

  • 不能使用select控件,可以使用div、input

转载于:https://www.cnblogs.com/zhaopei/p/5470672.html

无限分级和tree结构数据增删改【提供Demo下载】相关推荐

  1. 用sqlalchemy对mysql数据库增删改查demo

    首先要搭建mysql服务器.安装mysql python驱动.安装sqlalchemy包, 我用的是windows环境,安装参考:https://blog.csdn.net/jishuwenming/ ...

  2. JOOQ初学-简单的增删改查demo

    初学JOOQ,写个blog为了mark一下,也方便大家交流.直接上代码了.在网上搜不到太详细的demo和文档,都是英文的.哎,忧桑...在这里写几个demo,大家看看,有不足望指教. 初步的数据库连接 ...

  3. eggjs+vue+mysql增删改查demo

    序: 1.博主用eggjs+vue+mysql 的组合写了个简单的demo. 2.博主为了这个demo整了90天,这90可以接多少外包,有什么比花10米投资在自己身上更有价值的. 注: 点击下载dem ...

  4. jQuery LigerUI 初次发布一睹为快(提供Demo下载)

    一,简介 jQuery LigerUI 是基于jQuery的一系列UI控件组合,包括表单.表格.提示框.窗口.布局等等.可以快速地创建风格统一大方的界面.因为是前端控件,跟服务器无关,可以适合.net ...

  5. HtmlEditor在线编辑器V3.1提供Demo 下载,需要朋友们的热心反馈,才能提供src和Manage 的下载

    这个软件的许可协议为GPL. 详细请看GPL.txt或访问http://www.opensource.org/licenses/gpl-license.php 什么是 HtmlEditor[Versi ...

  6. HtmlEditor在线编辑器V3.0提供Demo 下载,需要朋友们的热心反馈,才能提供src和Manage 的下载

    这个软件的许可协议为GPL. 详细请看GPL.txt或访问http://www.opensource.org/licenses/gpl-license.php 什么是 HtmlEditor? Html ...

  7. 增删查改html模板,dataGrid增删改查(EasyUI)示例源码

    源码示例前台套用easyui,利用ajax调用sql数据库对学生信息表进行增删改查 资源下载此资源下载价格为2D币,请先登录 资源文件列表 GridDemos.sln , 907 JSonHelper ...

  8. springboot对于redis的增删改查

    springboot对于redis的增删改查 1.下载redis,安装之后,打开redis服务器.不过有绿色版的,直接打开redis服务器可执行文件,启动redis服务器. 2.在pom.xml配置文 ...

  9. iView(1) 增删改查 案例

    今天分享一波基于vue的一套关于iView的增删改查demo源码 温馨小提示: iView官网文档:https://www.iviewui.com/docs/guide/install Vue Rou ...

最新文章

  1. 10分钟教你用睡觉这件事玩转贝叶斯推断
  2. 如何用staruml画包图_StarUML的9种图
  3. 【深度学习】基于注意力机制的Transformer处理医疗影像
  4. Flask 模板 之 变量和过滤器
  5. mate7安装android o,华为Mate7升级安卓6.0详细教程
  6. std::nothrow
  7. php class variable,PHP中的變量類擴展 - 是否可能?
  8. Linux系统与网络服务管理技术
  9. 元素命名空间中的“MvcBuildViews”无效
  10. mapxtreme 更改图元的位置
  11. LoadRunner压力测试:详细操作流程
  12. 计算机上机考试自我检查800字,【考试太差 自我反省检讨书800字】_考试成绩差自我反省检讨书范文3篇...
  13. 15分钟快速搭建属于自己的网站
  14. Android Studio 模拟器重启(解决模拟器卡死问题)
  15. linux下面ps命令,Linux下修改后的ps命令,很好很强大!
  16. uploader.lib php,Lib/Upload.php · 跳跳虎1986/cwj - Gitee.com
  17. Razor 一知半解
  18. 为什么需要运营商级NAT设备?
  19. 卷积神经网络(CNN)系列介绍之一 (LeNet-5 / AlexNet / GoogLeNet / VGGNet / BNInception / Inceptionv3)
  20. 软件工程学科思维导图

热门文章

  1. python hash表_python数据结构与算法——哈希表
  2. 3层b+树索引访问磁盘次数_深入理解MySQL索引底层实现原理丨技术干货
  3. Java图形编程实验总结_实验二java图形界面编程2015级.doc
  4. Android Instant Apps教程
  5. kotlin 循环_Kotlin控制流–否则,用于循环,同时,范围
  6. jquery显示隐藏切换_jQuery显示,隐藏,切换
  7. python requests.exceptions.ConnectionError: ('Connection aborted.', BadStatusLine('HTTP/1.1 000\r\n'
  8. C++算法工程师需要具备开发能力
  9. Java基础篇:构造函数重载
  10. iOS开源项目周报1229