资讯动态

Qt C++文件浏览器开发:QFileSystemModel与QTreeView实战指南

发布时间:2026/8/19 12:08:03 来源:尧图企业网站定制
这次我们来看一个 Qt C 开发中非常实用的组合QFileSystemModel与QTreeView。对于需要实现文件浏览器、目录树、资源管理器功能的桌面应用来说这个组合是绕不开的核心技术。它最大的优点就是“开箱即用”Qt 框架已经帮你处理了文件系统的监听、数据索引和视图渲染你只需要几行代码就能搭建一个功能完整的文件树。这篇文章不讲复杂的 MVC 理论直接聚焦于实战。我们会拆解QFileSystemModel的核心能力演示如何用QTreeView展示它并一步步实现过滤、排序、自定义显示、右键菜单等高级功能。无论你是想做一个简单的项目文件浏览器还是一个需要深度定制的资源管理工具这里的代码和思路都能直接复用。1. 核心能力速览在深入代码之前我们先快速了解QFileSystemModel和QTreeView搭配能做什么以及它的基本门槛。能力项说明核心功能提供本地文件系统的数据模型并与树形视图组件绑定实现目录树的浏览、展开、折叠。数据实时性自动监听文件系统的增删改操作视图会自动更新无需手动刷新。信息维度可显示文件名、大小、类型、修改日期等文件属性需要设置QDir::Filters和QFileSystemModel::Roles。过滤与排序支持通过名称过滤器、类型过滤器隐藏文件支持按名称、大小、日期等多列排序。自定义显示可通过委托Delegate或模型角色Role自定义单元格的图标、文本颜色、背景等。交互功能支持通过信号槽捕获双击、单击、右键菜单、拖拽等用户交互事件。平台兼容性跨平台Windows, Linux, macOS路径分隔符等细节由 Qt 自动处理。启动与集成作为 Qt Widgets 模块的一部分无需额外安装在项目中包含对应头文件并链接 Qt 库即可。硬件门槛无特殊要求普通开发机即可运行。性能取决于遍历目录的文件数量对于超大型目录如数十万文件首次加载可能有延迟。适合场景项目资源管理器、日志文件查看器、本地文档浏览器、安装程序目录选择框、任何需要展示层级文件结构的桌面应用。简单来说如果你需要在 Qt C 应用里做一个类似 Windows 资源管理器左侧的导航树QFileSystemModelQTreeView就是最高效、最稳定的选择。2. 适用场景与使用边界适合谁用Qt C 桌面应用开发者需要内置文件浏览功能。工具软件开发者开发 IDE、代码编辑器、图像管理器、音频处理软件等需要让用户方便地选择工作目录或文件。初学者学习 Qt Model/View 框架这是一个理解 Qt 模型-视图架构的绝佳实例比自定义模型更直观。能解决什么问题快速构建文件树无需自己递归遍历目录、维护树形数据结构。保持视图与文件系统同步当用户在外部如系统资源管理器增删文件时视图能自动更新。提供丰富的文件信息轻松获取并展示文件属性无需调用系统 API。实现复杂的交互逻辑基于标准的 Model/View 信号可以方便地添加自定义操作。不适合什么场景远程文件系统FTP, WebDAVQFileSystemModel只针对本地文件系统。远程文件需要自定义模型或使用QNetworkAccessManager。虚拟文件系统或归档文件ZIP, 7z需要自己解析归档结构并实现对应的QAbstractItemModel。需要极高性能的百万级文件列表虽然可用但首次加载和过滤可能较慢。对于纯列表展示QFileSystemModel与QListView或QTableView搭配可能更合适。仅需要简单的文件选择对话框直接使用 Qt 内置的QFileDialog更省事。安全与合规边界路径访问该模型会访问并展示应用具有读取权限的目录。注意不要将包含敏感信息的路径如系统目录、其他用户目录默认展示给所有用户。符号链接与快捷方式默认会跟随符号链接在 Unix 系统或解析快捷方式在 Windows这可能导致循环引用或访问意外路径。可通过QDir::Filters进行控制。文件操作模型本身主要提供“读”能力。通过它实现的“删除”、“重命名”等写操作务必在代码中添加用户确认提示防止误操作。3. 环境准备与前置条件在开始编码前请确保你的开发环境已就绪。Qt 开发环境Qt 版本推荐 Qt 5.12 及以上或 Qt 6.2 及以上。本文示例代码在 Qt 5.15 和 Qt 6.5 上均测试通过。安装方式可以通过 Qt 官网 下载在线安装器或使用系统包管理器如 apt, yum, brew安装。确保安装时勾选了对应版本的Qt Creator和Desktop gcc套件。验证安装打开 Qt Creator能成功创建并编译一个默认的 Widgets 应用项目。C 编译器Windows: MinGW 或 MSVC (随 Visual Studio 安装)。Linux: g 或 clang。macOS: Xcode Command Line Tools (包含 clang)。项目配置.pro 文件在你的 Qt 项目文件.pro中必须包含widgets模块。QT core gui widgets # 如果使用 Qt 6通常 widgets 已包含在 core gui 中但显式声明更安全 # Qt 6 也可以写为: QT core5compat widgets # 如果需要兼容 Qt5 的一些 API如果你的项目是 CMake 构建则在CMakeLists.txt中find_package(Qt6 COMPONENTS Widgets REQUIRED) target_link_libraries(your_target PRIVATE Qt6::Widgets)基础代码框架我们将在一个主窗口MainWindow中实现。你可以通过 Qt Creator 的“新建项目”向导创建一个“Qt Widgets Application”这将自动生成包含MainWindow类的基础代码。4. 基础搭建显示文件树我们从最简单的例子开始在窗口里显示一个指定目录的树形结构。4.1 创建模型并设置根路径首先在MainWindow类的头文件如mainwindow.h中声明成员变量。// mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include QMainWindow #include QFileSystemModel #include QTreeView class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent nullptr); ~MainWindow(); private: QFileSystemModel *m_fileSystemModel; // 文件系统模型 QTreeView *m_treeView; // 树形视图 }; #endif // MAINWINDOW_H接下来在MainWindow的构造函数中初始化它们。// mainwindow.cpp #include mainwindow.h #include QHBoxLayout #include QWidget MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , m_fileSystemModel(new QFileSystemModel(this)) , m_treeView(new QTreeView(this)) { // 1. 设置模型的根路径 // 这里以用户家目录为例跨平台兼容。Windows下通常是 C:/Users/用户名 QString rootPath QDir::homePath(); m_fileSystemModel-setRootPath(rootPath); // 2. 设置模型过滤选项可选 // QDir::AllDirs: 列出所有目录即使为空 // QDir::Files: 列出文件 // QDir::NoDotAndDotDot: 不显示 . 和 .. // QDir::AllEntries: AllDirs | Files | Drives (在Windows上) m_fileSystemModel-setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot); // 3. 设置名称过滤器可选例如只显示 .txt 和 .cpp 文件 // QStringList filters; // filters *.txt *.cpp; // m_fileSystemModel-setNameFilters(filters); // m_fileSystemModel-setNameFilterDisables(false); // 不匹配的项直接隐藏 // 4. 将模型设置给视图 m_treeView-setModel(m_fileSystemModel); // 5. 设置视图的根索引只显示指定路径下的内容 m_treeView-setRootIndex(m_fileSystemModel-index(rootPath)); // 6. 设置一些视图属性以改善体验 m_treeView-setAnimated(true); // 展开折叠时有动画 m_treeView-setSortingEnabled(true); // 启用排序 m_treeView-sortByColumn(0, Qt::AscendingOrder); // 默认按第一列名称升序排序 m_treeView-setColumnWidth(0, 250); // 设置第一列宽度 // 7. 将视图放入中心窗口 QWidget *centralWidget new QWidget(this); QHBoxLayout *layout new QHBoxLayout(centralWidget); layout-addWidget(m_treeView); setCentralWidget(centralWidget); // 设置窗口标题和大小 setWindowTitle(tr(Qt 文件浏览器 - 基础版)); resize(800, 600); } MainWindow::~MainWindow() { // 由于指定了parentQObject的树形内存管理会自动释放这里无需手动delete }关键点解析setRootPath告诉模型从哪个目录开始建立索引。这并不意味着视图只显示这个目录而是模型的数据源从这里开始。setRootIndex这才是控制视图显示范围的关键。它让视图只显示指定索引对应某个目录下的子项。我们通常把它设置为模型根路径对应的索引。setFilter控制哪些类型的条目会被模型包含。QDir::NoDotAndDotDot非常重要它隐藏了当前目录.和父目录..让视图更整洁。setSortingEnabled启用后点击表头即可排序。模型会自动处理排序逻辑。编译并运行这个程序你将看到一个显示你用户主目录的树形文件浏览器可以展开、折叠、点击列排序。4.2 显示更多文件属性细节视图默认情况下QFileSystemModel提供了多列数据。我们可以让QTreeView显示出来。 在构造函数中设置模型后可以配置视图显示哪些列。// 在 m_treeView-setModel(m_fileSystemModel); 之后添加 // 设置列头 m_treeView-setHeaderHidden(false); // 确保表头显示 // 默认列顺序0-名称1-大小2-类型3-修改日期 // 你可以隐藏不需要的列 // m_treeView-hideColumn(1); // 隐藏大小列 // m_treeView-hideColumn(2); // 隐藏类型列 // 或者调整列宽 m_treeView-setColumnWidth(0, 300); // 名称列宽 m_treeView-setColumnWidth(1, 80); // 大小列宽 m_treeView-setColumnWidth(2, 100); // 类型列宽 m_treeView-setColumnWidth(3, 150); // 日期列宽现在运行程序你应该能看到一个包含文件大小、类型和修改日期的多列表格树。5. 功能进阶过滤、自定义与交互基础功能有了接下来实现更实用的功能。5.1 动态过滤文件我们添加一个输入框让用户可以实时过滤只显示特定类型的文件。在mainwindow.h中添加一个QLineEdit成员和槽函数声明。// mainwindow.h #include QLineEdit // ... 其他包含 class MainWindow : public QMainWindow { Q_OBJECT // ... 原有成员 private slots: void onFilterTextChanged(const QString text); // 过滤文本变化的槽 private: // ... 原有成员变量 QLineEdit *m_filterLineEdit; // 过滤输入框 };在mainwindow.cpp的构造函数中创建输入框并连接信号槽。// mainwindow.cpp #include QVBoxLayout #include QLabel // ... 其他包含 MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , m_fileSystemModel(new QFileSystemModel(this)) , m_treeView(new QTreeView(this)) , m_filterLineEdit(new QLineEdit(this)) { // ... 之前的模型和视图设置代码保持不变 ... // 创建过滤区域 QLabel *filterLabel new QLabel(tr(名称过滤 (如 *.cpp):), this); // 连接输入框的文字变化信号到我们的槽函数 connect(m_filterLineEdit, QLineEdit::textChanged, this, MainWindow::onFilterTextChanged); // 重新组织布局 QWidget *centralWidget new QWidget(this); QVBoxLayout *mainLayout new QVBoxLayout(centralWidget); QHBoxLayout *filterLayout new QHBoxLayout(); filterLayout-addWidget(filterLabel); filterLayout-addWidget(m_filterLineEdit); filterLayout-addStretch(); // 添加伸缩空间 mainLayout-addLayout(filterLayout); mainLayout-addWidget(m_treeView); setCentralWidget(centralWidget); // ... 设置窗口标题和大小 ... }实现过滤槽函数。// mainwindow.cpp void MainWindow::onFilterTextChanged(const QString text) { QStringList filters; if (!text.isEmpty()) { // 支持空格分隔多个模式如 *.cpp *.h filters text.split( , Qt::SkipEmptyParts); } else { // 如果输入框为空则清空过滤器显示所有文件 filters *; } m_fileSystemModel-setNameFilters(filters); // setNameFilterDisables(false) 表示不匹配的项直接隐藏 // 如果设置为 true不匹配的项会变灰禁用 m_fileSystemModel-setNameFilterDisables(false); }现在运行程序在顶部的输入框里输入*.txt树形视图将只显示.txt文件。输入*.cpp *.h则显示 C 头文件和源文件。5.2 自定义显示例如高亮特定文件我们想将所有.exe文件用红色文字显示。这需要通过**委托Delegate或模型数据角色Role**来实现。这里使用更简单的角色方式在模型的data()函数返回前进行判断通过子类化或使用代理模型。为了演示我们创建一个简单的代理模型。首先创建一个继承自QSortFilterProxyModel的类重写data函数。// myproxymodel.h #ifndef MYPROXYMODEL_H #define MYPROXYMODEL_H #include QSortFilterProxyModel #include QFileInfo class MyProxyModel : public QSortFilterProxyModel { Q_OBJECT public: explicit MyProxyModel(QObject *parent nullptr) : QSortFilterProxyModel(parent) {} QVariant data(const QModelIndex index, int role Qt::DisplayRole) const override { if (role Qt::ForegroundRole) { // 获取源模型的索引 QModelIndex sourceIndex mapToSource(index); // 获取文件路径 QString filePath sourceModel()-data(sourceIndex, QFileSystemModel::FilePathRole).toString(); QFileInfo fileInfo(filePath); // 如果是 .exe 文件返回红色画笔 if (fileInfo.suffix().toLower() exe) { return QBrush(Qt::red); } // 如果是目录返回蓝色画笔可选 if (sourceModel()-data(sourceIndex, QFileSystemModel::FileIconRole).isValid()) { // 简单判断模型返回有效图标的很可能是文件目录也有图标但我们可以用其他方式判断 // 更准确的判断是使用 QFileSystemModel::isDir 角色但这里为演示简单处理 // 实际中应通过 QFileSystemModel::FileInfoRole 获取 QFileInfo 判断 } } // 对于其他角色或情况返回父类的默认数据 return QSortFilterProxyModel::data(index, role); } }; #endif // MYPROXYMODEL_H然后在MainWindow中使用这个代理模型。// mainwindow.cpp #include myproxymodel.h // ... 其他包含 MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , m_fileSystemModel(new QFileSystemModel(this)) , m_treeView(new QTreeView(this)) , m_filterLineEdit(new QLineEdit(this)) { // ... 之前的模型设置代码 ... // 创建代理模型并设置源模型 MyProxyModel *proxyModel new MyProxyModel(this); proxyModel-setSourceModel(m_fileSystemModel); // 将代理模型设置给视图而不是原始模型 m_treeView-setModel(proxyModel); // 注意设置根索引时需要将路径索引映射到代理模型 QModelIndex sourceRootIndex m_fileSystemModel-index(rootPath); QModelIndex proxyRootIndex proxyModel-mapFromSource(sourceRootIndex); m_treeView-setRootIndex(proxyRootIndex); // ... 后续的布局等代码 ... }现在树形视图中的所有.exe文件都会显示为红色文字。你可以扩展MyProxyModel::data函数根据文件类型、大小、修改时间等条件返回不同的颜色、字体或图标。5.3 添加右键菜单为树形视图添加一个右键菜单实现“打开文件所在位置”和“复制文件路径”功能。在mainwindow.h中添加槽函数声明。// mainwindow.h private slots: // ... 已有的 onFilterTextChanged void onCustomContextMenuRequested(const QPoint pos); // 右键菜单请求 void onActionOpenContainingFolder(); // 打开所在文件夹 void onActionCopyFilePath(); // 复制文件路径 private: // ... 原有成员变量 QString m_contextMenuFilePath; // 临时存储右键点击的文件路径在mainwindow.cpp的构造函数中启用视图的上下文菜单策略并连接信号。// mainwindow.cpp #include QMenu #include QClipboard #include QDesktopServices #include QUrl #include QDir #include QMessageBox // ... 其他包含 MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , m_fileSystemModel(new QFileSystemModel(this)) , m_treeView(new QTreeView(this)) , m_filterLineEdit(new QLineEdit(this)) , m_contextMenuFilePath() { // ... 之前的模型、代理模型、视图设置代码 ... // 启用自定义上下文菜单 m_treeView-setContextMenuPolicy(Qt::CustomContextMenu); connect(m_treeView, QTreeView::customContextMenuRequested, this, MainWindow::onCustomContextMenuRequested); // ... 布局代码 ... }实现右键菜单请求的槽函数。void MainWindow::onCustomContextMenuRequested(const QPoint pos) { // 获取点击处的索引代理模型的索引 QModelIndex index m_treeView-indexAt(pos); if (!index.isValid()) { return; // 点击在空白处不显示菜单 } // 通过代理模型获取源模型的索引 QSortFilterProxyModel *proxyModel qobject_castQSortFilterProxyModel*(m_treeView-model()); if (!proxyModel) { // 如果没有使用代理模型则直接用视图的模型 proxyModel nullptr; } QModelIndex sourceIndex; if (proxyModel) { sourceIndex proxyModel-mapToSource(index); } else { sourceIndex index; } // 从源模型获取文件路径 m_contextMenuFilePath m_fileSystemModel-filePath(sourceIndex); // 创建菜单 QMenu contextMenu(this); QAction *openFolderAction contextMenu.addAction(tr(打开文件所在位置)); QAction *copyPathAction contextMenu.addAction(tr(复制文件路径)); contextMenu.addSeparator(); QAction *refreshAction contextMenu.addAction(tr(刷新)); // 连接菜单动作到槽函数 connect(openFolderAction, QAction::triggered, this, MainWindow::onActionOpenContainingFolder); connect(copyPathAction, QAction::triggered, this, MainWindow::onActionCopyFilePath); connect(refreshAction, QAction::triggered, [this]() { // 刷新模型重新读取当前目录 QModelIndex rootIdx m_treeView-rootIndex(); if (proxyModel) { rootIdx proxyModel-mapToSource(rootIdx); } m_fileSystemModel-fetchMore(rootIdx); // 对于文件系统模型简单的刷新可以这样做 // 更彻底的刷新是重置根路径但可能会丢失展开状态 // m_fileSystemModel-setRootPath(m_fileSystemModel-rootPath()); }); // 在鼠标位置显示菜单 contextMenu.exec(m_treeView-viewport()-mapToGlobal(pos)); }实现打开文件夹和复制路径的槽函数。void MainWindow::onActionOpenContainingFolder() { if (m_contextMenuFilePath.isEmpty()) return; QFileInfo fileInfo(m_contextMenuFilePath); QString folderPath; if (fileInfo.isDir()) { folderPath m_contextMenuFilePath; } else { folderPath fileInfo.absolutePath(); } // 使用 QDesktopServices 打开文件夹 bool success QDesktopServices::openUrl(QUrl::fromLocalFile(folderPath)); if (!success) { QMessageBox::warning(this, tr(打开失败), tr(无法打开文件夹: %1).arg(folderPath)); } } void MainWindow::onActionCopyFilePath() { if (m_contextMenuFilePath.isEmpty()) return; QClipboard *clipboard QApplication::clipboard(); clipboard-setText(m_contextMenuFilePath); // 可以给一个简单的提示例如状态栏消息 statusBar()-showMessage(tr(路径已复制到剪贴板: %1).arg(m_contextMenuFilePath), 3000); }现在在树形视图的任意文件或目录上点击右键会出现一个菜单可以选择打开其所在文件夹或将完整路径复制到剪贴板。6. 性能调优与注意事项对于包含大量文件的目录首次加载可能会感觉卡顿。以下是一些优化建议延迟加载QFileSystemModel默认是延迟加载的QFileSystemModel::setResolveSymlinks和QFileSystemWatcher会带来一些开销。对于初始根目录可以手动调用fetchMore来预加载。限制监听范围文件系统监视器QFileSystemWatcher会监听模型已索引目录的变更。如果目录树非常庞大这可能会消耗较多系统资源。可以考虑对非关键目录禁用监视或者使用自定义模型来管理监听范围。使用代理模型进行过滤如我们之前所做复杂的过滤和排序操作放在代理模型中比直接操作QFileSystemModel更高效且不会影响源模型的数据。分页或虚拟化对于极端情况数十万文件QTreeView可能仍会力不从心。考虑使用QListView的图标模式或实现自定义的视图只渲染可视区域内的项目。在后台线程中操作如果需要在加载后执行耗时的文件操作如计算哈希、读取元数据务必在后台线程中进行避免阻塞 UI。7. 常见问题与排查方法问题现象可能原因排查方式解决方案树形视图一片空白不显示任何内容。1. 模型根路径设置错误。2. 视图的根索引未设置或设置错误。3. 过滤器设置过于严格过滤掉了所有项。1. 检查setRootPath的参数路径是否存在且有读取权限。2. 检查setRootIndex的参数是否是通过model-index(rootPath)正确获取的。3. 暂时注释掉setFilter和setNameFilters的代码。1. 使用QDir::homePath()或QDir::currentPath()等绝对路径进行测试。2. 确保在setModel之后调用setRootIndex。3. 逐步添加过滤条件定位问题。点击列标题无法排序。1. 视图的排序功能未启用。2. 代理模型可能重写了排序逻辑。1. 检查是否调用了setSortingEnabled(true)。2. 检查代理模型的sort函数是否被正确重写或调用。1. 确保调用setSortingEnabled(true)。2. 在代理模型中确保调用了QSortFilterProxyModel::sort的基类实现。文件在系统资源管理器中增删后视图没有自动更新。1. 文件系统监视器未正常工作某些网络驱动器或虚拟文件系统可能不支持。2. 模型被设置为不监听变化。1. 检查路径是否在可监听的本地文件系统上。2. 查看QFileSystemModel的文档确认默认行为。1. 对于不支持监听的路径可以手动添加一个“刷新”按钮调用model-setRootPath(model-rootPath())强制刷新。2. 确保没有禁用相关的模型标志。自定义颜色/图标不生效。1. 代理模型的data()函数重写有误未返回正确的QVariant。2. 角色role判断错误。3. 视图未请求该角色。1. 在代理模型的data()函数中设置断点或打印日志检查是否被调用以及参数值。2. 确认使用的角色如Qt::ForegroundRole,Qt::DecorationRole是否正确。1. 确保在data()函数中对于未处理的情况返回父类的data(index, role)。2. 参考 Qt 文档中关于 Item Data Roles 的说明。程序崩溃特别是在操作模型索引时。1. 索引失效例如模型重置后使用了旧的索引。2. 在多线程中错误地访问了 UI 组件或模型。1. 检查所有存储的QModelIndex是否在模型布局变化后及时更新或作废。2. 确保所有对模型和视图的调用都在主线程UI线程中进行。1. 使用QPersistentModelIndex来保存需要长期使用的索引它会自动更新。2. 使用信号槽机制将后台线程的数据传递到主线程再更新模型。中文文件名显示乱码。文件系统编码与程序编码不一致主要出现在 Windows 上使用本地编码如 GBK而 Qt 默认使用 UTF-8。检查文件名的字节序列。在main函数中设置编码转换器Qt5或确保源代码文件保存为 UTF-8 with BOM (Windows)。Qt5 示例:#include QTextCodecint main(...) {QApplication a(...);QTextCodec::setCodecForLocale(QTextCodec::codecForName(UTF-8));...}8. 最佳实践与使用建议模型生命周期将QFileSystemModel的父对象设置为窗口或长期存在的对象利用 Qt 的对象树自动管理内存。避免在局部作用域内创建模型然后将其设置给视图。路径使用始终使用QDir::separator()或/Qt 会内部转换来构建路径以保证跨平台兼容性。使用QFileInfo来获取路径的各个部分目录、文件名、后缀等。性能敏感操作对于可能遍历大量文件的操作如全盘搜索不要在 UI 线程中直接使用QFileSystemModel进行递归。考虑使用QDirIterator在后台线程中收集信息然后批量更新到模型或自定义数据结构中。自定义数据如果需要为文件附加额外的元数据如标签、评分不要尝试直接修改QFileSystemModel。最佳实践是创建一个单独的数据库或数据结构来管理元数据并通过文件路径进行关联。或者在代理模型中将这些元数据作为额外的角色Qt::UserRole及以上提供。视图状态保持如果希望用户关闭再打开程序时能恢复之前展开的目录树状态需要手动保存和加载视图的展开状态QTreeView::isExpanded以及滚动条位置。测试不同平台在 Windows、Linux、macOS 上分别测试你的文件浏览器。特别注意权限问题、符号链接、文件系统大小写敏感度以及特殊路径如 Windows 的“我的文档”、Linux 的/proc的处理。QFileSystemModel与QTreeView的组合是 Qt 框架强大生产力的一个缩影。它用极少的代码为你搭建了一个功能坚实、交互流畅的文件系统浏览核心。掌握了它们的基础用法和进阶定制技巧你就能应对大多数需要展示文件结构的桌面应用场景。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价