😎优化文件树生成及页面调整
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="Magicodes.IE.Word" Version="2.7.5.2" />
|
||||||
<PackageReference Include="MailKit" Version="4.8.0" />
|
<PackageReference Include="MailKit" Version="4.8.0" />
|
||||||
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.4.6" />
|
<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="MiniWord" Version="0.8.0" />
|
||||||
<PackageReference Include="MQTTnet" Version="4.3.7.1207" />
|
<PackageReference Include="MQTTnet" Version="4.3.7.1207" />
|
||||||
<PackageReference Include="MySqlBackup.NET.MySqlConnector" Version="2.3.8" />
|
<PackageReference Include="MySqlBackup.NET.MySqlConnector" Version="2.3.8" />
|
||||||
@ -45,7 +45,7 @@
|
|||||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.170" />
|
<PackageReference Include="SqlSugarCore" Version="5.1.4.170" />
|
||||||
<PackageReference Include="SSH.NET" Version="2024.1.0" />
|
<PackageReference Include="SSH.NET" Version="2024.1.0" />
|
||||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.4.8" />
|
<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="UAParser" Version="3.1.47" />
|
||||||
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
|
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@ -286,26 +286,14 @@ public class SysFileService : IDynamicApiController, ITransient
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[DisplayName("获取文件路径")]
|
[DisplayName("获取文件路径")]
|
||||||
public async Task<dynamic> GetFolder()
|
public async Task<List<TreeNode>> GetFolder()
|
||||||
{
|
{
|
||||||
var files = await _sysFileRep.AsQueryable().ToListAsync();
|
var files = await _sysFileRep.AsQueryable().ToListAsync();
|
||||||
var folders = files.GroupBy(u => u.FilePath).Select(u => u.First().FilePath).ToList();
|
var folders = files.GroupBy(u => u.FilePath).Select(u => u.First().FilePath).ToList();
|
||||||
var result = folders
|
|
||||||
.GroupBy(u => u.Split('/').First())
|
var pathTreeBuilder = new PathTreeBuilder();
|
||||||
.Select((u, index) => new
|
var tree = pathTreeBuilder.BuildTree(folders);
|
||||||
{
|
return tree.Children;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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",
|
"name": "admin.net.pro",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "2.4.33",
|
"version": "2.4.33",
|
||||||
"lastBuildTime": "2024.11.08",
|
"lastBuildTime": "2024.11.10",
|
||||||
"description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架",
|
"description": "Admin.NET 站在巨人肩膀上的 .NET 通用权限开发框架",
|
||||||
"author": "zuohuaijun",
|
"author": "zuohuaijun",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -42,7 +42,7 @@
|
|||||||
"jsplumb": "^2.15.6",
|
"jsplumb": "^2.15.6",
|
||||||
"jwchat": "^2.0.3",
|
"jwchat": "^2.0.3",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"md-editor-v3": "^4.21.1",
|
"md-editor-v3": "^4.21.3",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"monaco-editor": "^0.52.0",
|
"monaco-editor": "^0.52.0",
|
||||||
"mqtt": "^5.10.1",
|
"mqtt": "^5.10.1",
|
||||||
@ -71,7 +71,7 @@
|
|||||||
"vue-router": "^4.4.5",
|
"vue-router": "^4.4.5",
|
||||||
"vue-signature-pad": "^3.0.2",
|
"vue-signature-pad": "^3.0.2",
|
||||||
"vue3-tree-org": "^4.2.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": "^4.7.59",
|
||||||
"vxe-table-plugin-element": "^4.0.4",
|
"vxe-table-plugin-element": "^4.0.4",
|
||||||
"vxe-table-plugin-export-xlsx": "^4.0.7",
|
"vxe-table-plugin-export-xlsx": "^4.0.7",
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
<template lang="">
|
<template>
|
||||||
<el-card class="box-card" shadow="hover" body-style="height:100%; overflow:auto;padding:5px;">
|
<el-card class="box-card" shadow="hover" body-style="height:100%;overflow:auto;padding:5px;width:100%;">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="tree-h-flex">
|
<div class="tree-h-flex">
|
||||||
<div class="tree-h-left">
|
<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>
|
||||||
<div class="tree-h-right">
|
<div class="tree-h-right">
|
||||||
<el-dropdown @command="handleCommand">
|
<el-dropdown @command="handleCommand">
|
||||||
<el-button style="margin-left: 8px; width: 34px">
|
<el-button style="margin-left: 8px; width: 34px">
|
||||||
<el-icon class="el-icon--center">
|
<el-icon class="el-icon--center">
|
||||||
<MoreFilled />
|
<more-filled />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</el-button>
|
</el-button>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
@ -23,10 +23,12 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
</div>
|
</div>
|
||||||
|
<el-checkbox v-if="!props.checkStrictly && state.isShowCheckbox" v-model="state.strictly" label="联动" style="margin-left: 8px" border />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div style="margin-bottom: 45px" v-loading="state.loading">
|
<div style="margin-bottom: 45px" v-loading="state.loading">
|
||||||
|
<el-scrollbar>
|
||||||
<el-tree
|
<el-tree
|
||||||
ref="treeRef"
|
ref="treeRef"
|
||||||
class="filter-tree"
|
class="filter-tree"
|
||||||
@ -35,12 +37,18 @@
|
|||||||
:props="{ children: 'children', label: 'name' }"
|
:props="{ children: 'children', label: 'name' }"
|
||||||
:filter-node-method="filterNode"
|
:filter-node-method="filterNode"
|
||||||
@node-click="nodeClick"
|
@node-click="nodeClick"
|
||||||
|
:show-checkbox="state.isShowCheckbox"
|
||||||
|
default-expand-all
|
||||||
highlight-current
|
highlight-current
|
||||||
check-strictly
|
:check-strictly="!state.strictly"
|
||||||
accordion
|
>
|
||||||
lazy
|
<template #default="{ node }">
|
||||||
:load="loadNode"
|
<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>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
@ -52,38 +60,38 @@ import { Search, MoreFilled } from '@element-plus/icons-vue';
|
|||||||
|
|
||||||
import { getAPI } from '/@/utils/axios-utils';
|
import { getAPI } from '/@/utils/axios-utils';
|
||||||
import { SysFileApi } from '/@/api-services/api';
|
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 filterText = ref('');
|
||||||
const treeRef = ref<InstanceType<typeof ElTree>>();
|
const treeRef = ref<InstanceType<typeof ElTree>>();
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
loading: false,
|
loading: false,
|
||||||
folderData: [] as any,
|
folderData: [] as any,
|
||||||
|
isShowCheckbox: false,
|
||||||
|
strictly: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
// 页面初始化
|
||||||
initTreeData();
|
onMounted(async () => {
|
||||||
|
await fetchTreeData();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 查询监听
|
||||||
watch(filterText, (val) => {
|
watch(filterText, (val) => {
|
||||||
treeRef.value!.filter(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();
|
var res = await getAPI(SysFileApi).apiSysFileFolderGet();
|
||||||
state.folderData = res.data.result ?? [];
|
state.folderData = res.data.result ?? [];
|
||||||
state.loading = false;
|
if (showLoading) state.loading = false;
|
||||||
};
|
return res.data.result ?? [];
|
||||||
|
|
||||||
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([]);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取已经选择
|
// 获取已经选择
|
||||||
@ -91,11 +99,13 @@ const getCheckedKeys = () => {
|
|||||||
return treeRef.value!.getCheckedKeys();
|
return treeRef.value!.getCheckedKeys();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 节点过滤
|
||||||
const filterNode = (value: string, data: any) => {
|
const filterNode = (value: string, data: any) => {
|
||||||
if (!value) return true;
|
if (!value) return true;
|
||||||
return data.name.includes(value);
|
return data.name.includes(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 节点操作
|
||||||
const handleCommand = async (command: string | number | object) => {
|
const handleCommand = async (command: string | number | object) => {
|
||||||
if ('expandAll' == command) {
|
if ('expandAll' == command) {
|
||||||
for (let i = 0; i < treeRef.value!.store._getAllNodes().length; i++) {
|
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;
|
treeRef.value!.store._getAllNodes()[i].expanded = false;
|
||||||
}
|
}
|
||||||
} else if ('refresh' == command) {
|
} else if ('refresh' == command) {
|
||||||
initTreeData();
|
fetchTreeData();
|
||||||
} else if ('rootNode' == command) {
|
} else if ('rootNode' == command) {
|
||||||
|
treeRef.value?.setCurrentKey();
|
||||||
emits('node-click', { id: 0, name: '' });
|
emits('node-click', { id: 0, name: '' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 与父组件的交互逻辑
|
// 与父组件的交互逻辑
|
||||||
const emits = defineEmits(['node-click']);
|
const emits = defineEmits(['node-click']);
|
||||||
|
|
||||||
const nodeClick = (node: any) => {
|
const nodeClick = (node: any) => {
|
||||||
emits('node-click', { id: node.id, name: node.name });
|
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>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="sys-file-container">
|
<div class="sys-file-container">
|
||||||
<splitpanes class="default-theme">
|
<splitpanes class="default-theme">
|
||||||
<pane size="20" style="display: flex">
|
<pane size="15" style="display: flex">
|
||||||
<FolderTree ref="folderTreeRef" @node-click="handleNodeChange" />
|
<FolderTree ref="folderTreeRef" @node-click="handleNodeChange" />
|
||||||
</pane>
|
</pane>
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user