You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET/.NET Core MVC可拖拽树形视图层级同步数据库方案咨询

可拖拽树形视图的.NET Core MVC实现方案

一、树形结构实体设计

首先需要在数据库中定义支持层级的实体,建议包含以下核心字段:

public class TreeItem
{
    public int Id { get; set; }
    public int? ParentId { get; set; } // 父节点ID,根节点为null
    public string Name { get; set; }
    public int SortOrder { get; set; } // 同层级排序索引,用于拖拽后调整顺序
    // 可添加其他业务字段
}

数据库表结构与实体对应,ParentId可设置为自关联外键(可选,根据业务需要)。

二、前端数据绑定与拖拽回调

假设你使用jstree作为jQuery拖拽插件,实现数据绑定和拖拽同步逻辑:

  1. 加载并渲染树形数据
// 从后端接口获取树形结构数据
$.get('/Tree/GetTreeData', function(data) {
    $('#tree-container').jstree({
        'core': {
            'data': data,
            'check_callback': true // 允许修改树形结构
        },
        'plugins': ['dnd'] // 启用拖拽功能
    });
});
  1. 拖拽完成后同步数据到后端
    监听拖拽完成事件,收集节点变更信息并发送请求:
$('#tree-container').on('move_node.jstree', function(e, data) {
    // 整理变更后的节点数据
    var updateData = {
        NodeId: data.node.id,
        NewParentId: data.parent === '#' ? null : data.parent,
        NewSortOrder: data.position // 新的同层级排序位置
    };

    // 发送更新请求
    $.post('/Tree/UpdateNodeHierarchy', updateData, function(res) {
        if (!res.success) {
            // 更新失败时回滚视图
            $('#tree-container').jstree('refresh');
            alert('层级更新失败:' + res.message);
        }
    });
});

三、后端接口实现

在MVC控制器中实现数据查询和层级更新逻辑:

  1. ViewModel接收前端数据
public class NodeUpdateRequest
{
    public int NodeId { get; set; }
    public int? NewParentId { get; set; }
    public int NewSortOrder { get; set; }
}
  1. 控制器核心方法
public class TreeController : Controller
{
    private readonly AppDbContext _dbContext;

    public TreeController(AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    // 获取树形结构数据
    public IActionResult GetTreeData()
    {
        var allItems = _dbContext.TreeItems.ToList();
        var treeData = BuildNestedTree(allItems, null);
        return Json(treeData);
    }

    // 递归构建嵌套树形结构
    private List<object> BuildNestedTree(List<TreeItem> items, int? parentId)
    {
        return items.Where(i => i.ParentId == parentId)
                    .OrderBy(i => i.SortOrder)
                    .Select(i => new {
                        id = i.Id,
                        text = i.Name,
                        children = BuildNestedTree(items, i.Id)
                    }).ToList<object>();
    }

    // 更新节点层级与排序
    [HttpPost]
    public IActionResult UpdateNodeHierarchy(NodeUpdateRequest request)
    {
        using var transaction = _dbContext.Database.BeginTransaction();
        try
        {
            var targetNode = _dbContext.TreeItems.Find(request.NodeId);
            if (targetNode == null)
            {
                return Json(new { success = false, message = "目标节点不存在" });
            }

            // 校验是否产生循环引用(如子节点拖到自己的子树中)
            if (IsCyclicReference(targetNode.Id, request.NewParentId))
            {
                return Json(new { success = false, message = "不能将节点拖入自身子层级" });
            }

            // 更新父节点ID
            targetNode.ParentId = request.NewParentId;
            // 调整同层级节点排序
            AdjustSiblingSortOrder(request.NewParentId, request.NodeId, request.NewSortOrder);

            _dbContext.SaveChanges();
            transaction.Commit();
            return Json(new { success = true });
        }
        catch (Exception ex)
        {
            transaction.Rollback();
            return Json(new { success = false, message = ex.Message });
        }
    }

    // 检查循环引用
    private bool IsCyclicReference(int nodeId, int? parentId)
    {
        if (parentId == null) return false;
        var parentNode = _dbContext.TreeItems.Find(parentId);
        while (parentNode != null)
        {
            if (parentNode.Id == nodeId) return true;
            parentNode = _dbContext.TreeItems.Find(parentNode.ParentId);
        }
        return false;
    }

    // 调整同层级节点的排序值
    private void AdjustSiblingSortOrder(int? parentId, int movedNodeId, int newPosition)
    {
        var siblings = _dbContext.TreeItems
                                .Where(i => i.ParentId == parentId && i.Id != movedNodeId)
                                .OrderBy(i => i.SortOrder)
                                .ToList();

        int currentOrder = 0;
        foreach (var sibling in siblings)
        {
            if (currentOrder == newPosition)
            {
                currentOrder++;
            }
            sibling.SortOrder = currentOrder;
            currentOrder++;
        }

        // 设置移动节点的新排序
        var movedNode = _dbContext.TreeItems.Find(movedNodeId);
        movedNode.SortOrder = newPosition;
    }
}

四、关键注意事项

  • 事务处理:更新层级和排序时使用事务,避免部分操作失败导致数据不一致。
  • 循环引用校验:防止节点被拖入自身的子层级,破坏树形结构。
  • 前端回滚:后端更新失败时,调用树形插件的refresh方法恢复视图状态。

内容的提问来源于stack exchange,提问作者Addy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 19:01:17