65.9K
CodeProject 正在变化。 阅读更多。
Home

使用Bootstrap Treeview的文件夹浏览器 - 第一部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.97/5 (11投票s)

2017 年 10 月 2 日

CPOL

5分钟阅读

viewsIcon

49849

downloadIcon

1

使用Bootstrap Treeview插件构建文件夹浏览器小部件。

文章提交向导弄乱了我的代码下载链接,所以代码在这里 GitHub 上

https://github.com/bradykelly/bootstrap-folder-browser.git

请忽略源代码中的 FancyTree 项目。那是一篇正在早期开发中的文章,很快就会在这里发布。

引言

不久前,我有一个编码需求,我常用的工具箱无法满足:一个用于 Web 应用程序的文件系统文件夹浏览器。用户需要选择一个文件夹以备份到另一个位置。
我查看了我以前使用过或至少看到过的一些文件/文件夹浏览器小部件。JQuery 及其家族有很多,其中一个值得特别推荐的是 jQuery FancyTree。可惜我暂时不会介绍它,但现在,我将重点介绍我的基于 Bootstrap 的文件夹浏览器。我选择 Bootstrap Treeview 产品是因为它简单易用,而且它已经像我的项目其他部分一样进行了样式设置,即“Twitter Bootstrap 默认外观”。当然,它也是免费和开源的。

我对这个小部件的文件夹浏览器改编远非功能完善,并且有一些缺点,但它实现起来非常快速和容易,它能正常工作,并且允许我选择一个文件夹,这是这里唯一真正的要求。本文的这一部分介绍了如何设置一个 treeview 来显示文件系统数据。在本文档的最后,您应该能够创建一个网页,供人们浏览文件夹。一旦重要的事情处理完毕,在第二部分中,我将向您展示如何将所有这些很棒的功能打包成一个新的文件夹浏览器小部件,提供更多功能和更好的可重用性。

图 1 - 文件夹浏览示例

设置 Bootstrap Treeview 的第一步

将 Treeview 包含到项目中

这很简单,只需下载或安装 Bootstrap Treeview 并引用视图(或布局页面)中的脚本和样式。该包可以通过 npmbower 获取,或者您可以从 github 克隆或下载整个仓库。我遇到的 npm 问题是,它只下载包并将其添加到解决方案的 node_modules 文件夹中,而不会将任何文件安装到项目本身。如果我必须去 node_modules 文件夹中挑选和复制文件,那么我宁愿下载整个仓库并从中挑选我想要的文件。我选择并复制的文件,我会将它们放在 wwwroot\lib\bootstrap-treeview 文件夹中,准备好提供服务。

样式和脚本引用

对于开发环境,我的项目中的样式引用如下。我总是尽量在开发环境中使用未压缩的文件,但在此情况下没有未压缩的文件。

<environment include="Development">
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" />
    <link href="~/lib/bootstrap-treeview/bootstrap-treeview.min.css" rel="stylesheet" />
</environment>

在非开发环境中,我使用所有样式表的压缩版本(如果可用)。

我的开发 脚本 引用如下

<environment include="Development">
    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
    <script src="~/lib/bootstrap-treeview/bootstrap-treeview.min.js"></script>
</environment>

我通常也只在开发环境中使用未压缩的脚本,除非不可用,例如在此情况。

容器视图和脚本

在您的视图中放置一个容器和脚本,最好是一个 divTreeview 将在此容器中显示。

<div id="tree"></div>
function getTree() {
  // Some logic to retrieve, or generate tree structure
  return data;
}

$('#tree').treeview({data: getTree()});

获取数据

Bootstrap Treeview 非常简单但也很棘手。它要求在初始化 Treeview 之前准备好树形图数据。单个节点最基本的数据结构只有两个属性:Text 是文件夹的名称,Nodes 是文件夹子节点结构的一个集合。

我们必须在初始化小部件之前拥有数据这一事实意味着我们可以在 done jQuery Ajax 方法(或等效方法)中执行 treeview 的初始化。

Ajax 回调

$.ajax("@Url.Action("TreeData","TreeView")")
    .done(function(resp) {
        $("#bsTree").treeview({
            data: resp
        });
    })
    .fail(function(error) {
        console.log(error);
    });

结构

我的简化节点类 TreeNode,旨在表示一个树节点,并且是小部件在主页上看到的 node 对象完整规范的一个非常小的子集,标题为数据结构。有用于图标的属性,但我未能使展开和折叠节点上的图标除了“+”和“-”之外还有其他显示。在线示例中也没有其他图标可见,因此我选择完全省略图标属性,以使内容尽可能小而整洁。

TreeNode 类

public class TreeNode
{
    [JsonProperty("text")]
    public string Text { get; set; }

    [JsonProperty("nodes")]
    public List<TreeNode> Nodes { get; set; } = new List<TreeNode>();
}

使用这种节点结构,一个 JSON 回复给 Treeview 可能看起来像这样。

JSON 示例

[
  {
    "text": "Folder1",
    "nodes": []
  },
  {
    "text": "Folder2",
    "nodes": [
      {
        "text": "FolderB",
        "nodes": [
          {
            "text": "FolderOne",
            "nodes": []
          }
        ]
      }
    ]
  },
  {
    "text": "Logs",
    "nodes": []
  }
]

递归

这个 treeview 需要一个递归数据结构 (JSON) 来表示整个文件夹树,因为该小部件不支持延迟加载,或除了初始加载之外的任何 Ajax。因此,它需要一次性获取所有数据。这让我感到不安,因为在 filesystem 中嵌套很普遍,深度未知,构建和传输巨大的 JSON 文档只会损害性能。

我使用以下控制器代码实现了所需的递归。

控制器

public class TreeViewController : Controller
{
    private FileTreeConfig _config;

    public TreeViewController(IOptions<FileTreeConfig> config)
    {
        _config = config.Value;
    }

    public IActionResult TreeData(string dir = "")
    {
        var browsingRoot = Path.Combine(_config.BaseDir, dir);
        var nodes = new List<TreeNode>();
        nodes.AddRange(RecurseDirectory(browsingRoot));
        return Json(nodes);
    }

    private List<TreeNode> RecurseDirectory(string directory)
    {
        var ret = new List<TreeNode>();
        var dirInfo = new DirectoryInfo(directory);

        try
        {
            var directories = dirInfo.GetDirectories("*", SearchOption.TopDirectoryOnly);
            foreach (var dir in directories)
            {
                if (dir.FullName.ToLower() == dirInfo.FullName)
                {
                    continue;
                }
                var thisNode = TreeNode.FromDirInfo(dir);
                thisNode.Nodes.AddRange(RecurseDirectory(dir.FullName));
                ret.Add(thisNode);
            }
        }
        catch (UnauthorizedAccessException ux)
        {
            // NB Log. 
        }
        return ret;
    }
} 

我认为上面的代码是可读的,考虑到我们知道它的确切目的,因此不再需要进一步解释。

浏览根目录

尤其是对于这个只支持递归的 treeview,我们不希望用户导航到文件系统的根目录,让我们的代码进行递归,这会很糟糕。理想的做法是设置一个浏览根目录,将其设置为一个文件系统文件夹,用户无法超出该范围。他们可以通过相对路径进行导航,但检查这些路径的复杂性超出了这个简单练习的范围。我们只能同情那些梦想出“..\..\..”类型路径的“可怜的灵魂”。

我有一个名为 BaseDir配置设置,这是 treeview 必须开始的根目录的路径。控制器代码中的 Path.Combine 有助于将其建立为 treeview 的根。

结论

如果您已经跟随本文与 Bootstrap Treeview 的官方文档一起学习,您应该能够轻松地构建一个(例如,图 1-文件夹浏览示例)页面,该页面可以浏览文件夹,即使不是一个真正的浏览器小部件。在本文的第二部分也是最后一部分,我将向您展示如何使用您的代码构建一个 proper、可重用的组件,该组件可以插入到任何需要选择或浏览文件夹的地方。

历史

这是第一部分(共两部分)的第一个公开草稿。

© . All rights reserved.