😎优化文件树生成及页面调整
This commit is contained in:
parent
8ccd73e268
commit
bf1e57bccd
@ -30,7 +30,7 @@
|
||||
<PackageReference Include="Magicodes.IE.Word" Version="2.7.5.2" />
|
||||
<PackageReference Include="MailKit" Version="4.8.0" />
|
||||
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.4.6" />
|
||||
<PackageReference Include="MiniExcel" Version="1.34.2" />
|
||||
<PackageReference Include="MiniExcel" Version="1.35.0" />
|
||||
<PackageReference Include="MiniWord" Version="0.8.0" />
|
||||
<PackageReference Include="MQTTnet" Version="4.3.7.1207" />
|
||||
<PackageReference Include="MySqlBackup.NET.MySqlConnector" Version="2.3.8" />
|
||||
@ -45,7 +45,7 @@
|
||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.170" />
|
||||
<PackageReference Include="SSH.NET" Version="2024.1.0" />
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.4.8" />
|
||||
<PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1119" />
|
||||
<PackageReference Include="TencentCloudSDK.Sms" Version="3.0.1120" />
|
||||
<PackageReference Include="UAParser" Version="3.1.47" />
|
||||
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -286,26 +286,14 @@ public class SysFileService : IDynamicApiController, ITransient
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[DisplayName("获取文件路径")]
|
||||
public async Task<dynamic> GetFolder()
|
||||
public async Task<List<TreeNode>> GetFolder()
|
||||
{
|
||||
var files = await _sysFileRep.AsQueryable().ToListAsync();
|
||||
var folders = files.GroupBy(u => u.FilePath).Select(u => u.First().FilePath).ToList();
|
||||
var result = folders
|
||||
.GroupBy(u => u.Split('/').First())
|
||||
.Select((u, index) => new
|
||||
{
|
||||
Id = index + 1, // 组的索引加1作为Id
|
||||
Pid = 0,
|
||||
Name = u.Key,
|
||||
Children = u.Select((item, subIndex) => new
|
||||
{
|
||||
Id = (index + 1 * 100) + subIndex + 1, // 子项的索引加1作为Id
|
||||
Pid = index + 1,
|
||||
Name = item.Split('/').Last(),
|
||||
Children = new List<string>()
|
||||
}).ToList()
|
||||
}).ToList();
|
||||
return result;
|
||||
|
||||
var pathTreeBuilder = new PathTreeBuilder();
|
||||
var tree = pathTreeBuilder.BuildTree(folders);
|
||||
return tree.Children;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
57
Admin.NET/Admin.NET.Core/Utils/PathTreeBuilder.cs
Normal file
57
Admin.NET/Admin.NET.Core/Utils/PathTreeBuilder.cs
Normal file
@ -0,0 +1,57 @@
|
||||
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
|
||||
//
|
||||
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
|
||||
//
|
||||
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
|
||||
|
||||
namespace Admin.NET.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 树形节点
|
||||
/// </summary>
|
||||
public class TreeNode
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int Pid { get; set; }
|
||||
public string Name { get; set; }
|
||||
public List<TreeNode> Children { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据路径数组生成树结构
|
||||
/// </summary>
|
||||
public class PathTreeBuilder
|
||||
{
|
||||
private int _nextId = 1;
|
||||
|
||||
public TreeNode BuildTree(List<string> paths)
|
||||
{
|
||||
var root = new TreeNode { Id = 1, Pid = 0, Name = "文件目录" }; // 根节点
|
||||
var dict = new Dictionary<string, TreeNode>();
|
||||
|
||||
foreach (var path in paths)
|
||||
{
|
||||
var parts = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
TreeNode currentNode = root;
|
||||
|
||||
foreach (var part in parts)
|
||||
{
|
||||
var key = currentNode.Id + "_" + part; // 生成唯一键
|
||||
if (!dict.ContainsKey(key))
|
||||
{
|
||||
var newNode = new TreeNode
|
||||
{
|
||||
Id = _nextId++,
|
||||
Pid = currentNode.Id,
|
||||
Name = part
|
||||
};
|
||||
currentNode.Children.Add(newNode);
|
||||
dict[key] = newNode;
|
||||
}
|
||||
currentNode = dict[key]; // 更新当前节点
|
||||
}
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,7 @@
|
||||
"name": "admin.net.pro",
|
||||
"type": "module",
|
||||
"version": "2.4.33",
|
||||
"lastBuildTime": "2024.11.08",
|
||||
"lastBuildTime": "2024.11.10",
|
||||
"description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架",
|
||||
"author": "zuohuaijun",
|
||||
"license": "MIT",
|
||||
@ -42,7 +42,7 @@
|
||||
"jsplumb": "^2.15.6",
|
||||
"jwchat": "^2.0.3",
|
||||
"lodash-es": "^4.17.21",
|
||||
"md-editor-v3": "^4.21.1",
|
||||
"md-editor-v3": "^4.21.3",
|
||||
"mitt": "^3.0.1",
|
||||
"monaco-editor": "^0.52.0",
|
||||
"mqtt": "^5.10.1",
|
||||
@ -71,7 +71,7 @@
|
||||
"vue-router": "^4.4.5",
|
||||
"vue-signature-pad": "^3.0.2",
|
||||
"vue3-tree-org": "^4.2.2",
|
||||
"vxe-pc-ui": "^4.2.46",
|
||||
"vxe-pc-ui": "^4.2.49",
|
||||
"vxe-table": "^4.7.59",
|
||||
"vxe-table-plugin-element": "^4.0.4",
|
||||
"vxe-table-plugin-export-xlsx": "^4.0.7",
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
<template lang="">
|
||||
<el-card class="box-card" shadow="hover" body-style="height:100%; overflow:auto;padding:5px;">
|
||||
<template>
|
||||
<el-card class="box-card" shadow="hover" body-style="height:100%;overflow:auto;padding:5px;width:100%;">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<div class="tree-h-flex">
|
||||
<div class="tree-h-left">
|
||||
<el-input :prefix-icon="Search" v-model="filterText" placeholder="文件夹名称" />
|
||||
<el-input :prefix-icon="Search" v-model.lazy="filterText" clearable placeholder="机构名称" />
|
||||
</div>
|
||||
<div class="tree-h-right">
|
||||
<el-dropdown @command="handleCommand">
|
||||
<el-button style="margin-left: 8px; width: 34px">
|
||||
<el-icon class="el-icon--center">
|
||||
<MoreFilled />
|
||||
<more-filled />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
@ -23,24 +23,32 @@
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<el-checkbox v-if="!props.checkStrictly && state.isShowCheckbox" v-model="state.strictly" label="联动" style="margin-left: 8px" border />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div style="margin-bottom: 45px" v-loading="state.loading">
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
class="filter-tree"
|
||||
:data="state.folderData"
|
||||
node-key="id"
|
||||
:props="{ children: 'children', label: 'name' }"
|
||||
:filter-node-method="filterNode"
|
||||
@node-click="nodeClick"
|
||||
highlight-current
|
||||
check-strictly
|
||||
accordion
|
||||
lazy
|
||||
:load="loadNode"
|
||||
/>
|
||||
<el-scrollbar>
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
class="filter-tree"
|
||||
:data="state.folderData"
|
||||
node-key="id"
|
||||
:props="{ children: 'children', label: 'name' }"
|
||||
:filter-node-method="filterNode"
|
||||
@node-click="nodeClick"
|
||||
:show-checkbox="state.isShowCheckbox"
|
||||
default-expand-all
|
||||
highlight-current
|
||||
:check-strictly="!state.strictly"
|
||||
>
|
||||
<template #default="{ node }">
|
||||
<el-icon v-if="node.level < 4" size="16" style="margin-right: 3px; display: inline; vertical-align: middle"><ele-FolderOpened /></el-icon>
|
||||
<el-icon v-else size="16" style="margin-right: 3px; display: inline; vertical-align: middle"><ele-Folder /></el-icon>
|
||||
{{ node.label }}
|
||||
</template>
|
||||
</el-tree>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
@ -52,38 +60,38 @@ import { Search, MoreFilled } from '@element-plus/icons-vue';
|
||||
|
||||
import { getAPI } from '/@/utils/axios-utils';
|
||||
import { SysFileApi } from '/@/api-services/api';
|
||||
import { TreeKey } from 'element-plus/es/components/tree/src/tree.type';
|
||||
|
||||
const props = defineProps({
|
||||
checkStrictly: { type: Boolean, defaul: true },
|
||||
});
|
||||
|
||||
const filterText = ref('');
|
||||
const treeRef = ref<InstanceType<typeof ElTree>>();
|
||||
const state = reactive({
|
||||
loading: false,
|
||||
folderData: [] as any,
|
||||
isShowCheckbox: false,
|
||||
strictly: false,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
initTreeData();
|
||||
// 页面初始化
|
||||
onMounted(async () => {
|
||||
await fetchTreeData();
|
||||
});
|
||||
|
||||
// 查询监听
|
||||
watch(filterText, (val) => {
|
||||
treeRef.value!.filter(val);
|
||||
});
|
||||
|
||||
const initTreeData = async () => {
|
||||
state.loading = true;
|
||||
// 获取树数据
|
||||
const fetchTreeData = async (showLoading: boolean = true) => {
|
||||
if (showLoading) state.loading = true;
|
||||
var res = await getAPI(SysFileApi).apiSysFileFolderGet();
|
||||
state.folderData = res.data.result ?? [];
|
||||
state.loading = false;
|
||||
};
|
||||
|
||||
const loadNode = async (node: any, resolve: any) => {
|
||||
console.log(node);
|
||||
if (node.data == undefined || Array.isArray(node.data)) return;
|
||||
state.loading = true;
|
||||
var data = state.folderData.find((u) => u.id == node.data.id);
|
||||
state.loading = false;
|
||||
console.log(data);
|
||||
if (data) resolve(data.children);
|
||||
else resolve([]);
|
||||
if (showLoading) state.loading = false;
|
||||
return res.data.result ?? [];
|
||||
};
|
||||
|
||||
// 获取已经选择
|
||||
@ -91,11 +99,13 @@ const getCheckedKeys = () => {
|
||||
return treeRef.value!.getCheckedKeys();
|
||||
};
|
||||
|
||||
// 节点过滤
|
||||
const filterNode = (value: string, data: any) => {
|
||||
if (!value) return true;
|
||||
return data.name.includes(value);
|
||||
};
|
||||
|
||||
// 节点操作
|
||||
const handleCommand = async (command: string | number | object) => {
|
||||
if ('expandAll' == command) {
|
||||
for (let i = 0; i < treeRef.value!.store._getAllNodes().length; i++) {
|
||||
@ -106,21 +116,26 @@ const handleCommand = async (command: string | number | object) => {
|
||||
treeRef.value!.store._getAllNodes()[i].expanded = false;
|
||||
}
|
||||
} else if ('refresh' == command) {
|
||||
initTreeData();
|
||||
fetchTreeData();
|
||||
} else if ('rootNode' == command) {
|
||||
treeRef.value?.setCurrentKey();
|
||||
emits('node-click', { id: 0, name: '' });
|
||||
}
|
||||
};
|
||||
|
||||
// 与父组件的交互逻辑
|
||||
const emits = defineEmits(['node-click']);
|
||||
|
||||
const nodeClick = (node: any) => {
|
||||
emits('node-click', { id: node.id, name: node.name });
|
||||
};
|
||||
|
||||
//设置当前选中节点
|
||||
const setCurrentKey = (key?: TreeKey | undefined, shouldAutoExpandParent?: boolean | undefined) => {
|
||||
treeRef.value?.setCurrentKey(key, shouldAutoExpandParent);
|
||||
};
|
||||
|
||||
// 导出对象
|
||||
defineExpose({ initTreeData, getCheckedKeys });
|
||||
defineExpose({ fetchTreeData, getCheckedKeys, setCurrentKey });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="sys-file-container">
|
||||
<splitpanes class="default-theme">
|
||||
<pane size="20" style="display: flex">
|
||||
<pane size="15" style="display: flex">
|
||||
<FolderTree ref="folderTreeRef" @node-click="handleNodeChange" />
|
||||
</pane>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user