diff --git a/web-vue/packages/core/layouts/views/desktop/workbench/components/myfiles/index.vue b/web-vue/packages/core/layouts/views/desktop/workbench/components/myfiles/index.vue
index e7af802e..5335abe7 100644
--- a/web-vue/packages/core/layouts/views/desktop/workbench/components/myfiles/index.vue
+++ b/web-vue/packages/core/layouts/views/desktop/workbench/components/myfiles/index.vue
@@ -23,7 +23,18 @@
:class="{ active: folder.expanded }"
:style="{ paddingLeft: '0px' }"
>
+
+
+
+
+
+
+
+
+
-
{{ folder.id === '0' ? folder.name : (folder.folderName || folder.name) }}
+
+ {{ folder.id === '0' ? folder.name : (folder.folderName || folder.name) }}
+ ({{ folder.totalCount || 0 }})
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
@@ -66,6 +96,15 @@
:style="{ paddingLeft: '16px' }"
>
+
+
+
+
+
+
+
+
-
{{ second.folderName || second.name }}
+
+ {{ second.folderName || second.name }}
+ ({{ second.totalCount || 0 }})
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
@@ -108,6 +166,15 @@
:style="{ paddingLeft: '32px' }"
>
+
+
+
+
+
+
+
+
-
{{ third.folderName || third.name }}
+
+ {{ third.folderName || third.name }}
+ ({{ third.totalCount || 0 }})
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
@@ -150,6 +236,15 @@
:style="{ paddingLeft: '48px' }"
>
+
+
+
+
+
+
+
+
-
{{ fourth.folderName || fourth.name }}
+
+ {{ fourth.folderName || fourth.name }}
+ ({{ fourth.totalCount || 0 }})
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
@@ -191,12 +305,23 @@
:style="{ paddingLeft: '64px' }"
>
+
+
+
+
+
+
+
+
-
{{ fifth.folderName || fifth.name }}
+
+ {{ fifth.folderName || fifth.name }}
+ ({{ fifth.totalCount || 0 }})
+
+
+
+
+
+
+
+
+
@@ -280,7 +420,7 @@ import { downloadByUrl } from '@jeesite/core/utils/file/download';
import UploadModal from './upload.vue';
import FolderModal from './folder.vue';
-import { bizFoldersSave, bizFoldersAllData, bizFolderItemAll } from '@jeesite/biz/api/biz/folders';
+import { bizFoldersSave, bizFoldersAllData, bizFolderItemAll, bizFoldersDelete } from '@jeesite/biz/api/biz/folders';
import { BizMyfiles, bizMyfilesListAll, bizMyfilesDelete } from '@jeesite/biz/api/biz/myfiles';
import { useUserStore } from '@jeesite/core/store/modules/user';
@@ -305,6 +445,11 @@ interface FolderNode {
parentName?: string;
userName?: string;
loginCode?: string;
+ fileCount?: number; // 当前文件夹直接文件数量
+ folderCount?: number; // 当前文件夹直接子文件夹数量
+ totalFileCount?: number; // 递归统计的所有下级文件总数
+ totalFolderCount?: number; // 递归统计的所有下级文件夹总数
+ totalCount?: number; // 总数量 = 所有下级文件数 + 所有下级文件夹数
}
interface EditingInfo {
@@ -318,6 +463,7 @@ interface EditingInfo {
const expandedState = ref>({});
const currentEditingNode = ref(null);
+const showDeleteBtn = ref(null); // 新增:控制删除按钮显示
const editingInfo = ref({
id: null,
@@ -337,6 +483,103 @@ const folderSearchText = ref('');
const fileSearchText = ref('');
const folderList = ref([]);
const fileList = ref([]);
+const folderFileCountMap = ref>({}); // 文件夹直接文件数量映射
+const folderChildCountMap = ref>({}); // 文件夹直接子文件夹数量映射
+
+// 获取文件夹直接文件数量
+const getFolderFileCount = async (folderId: string) => {
+ try {
+ const reqParams = {
+ folderId,
+ loginCode: userinfo.value.loginCode,
+ };
+ const result = await bizMyfilesListAll(reqParams);
+ return result ? result.length : 0;
+ } catch (error) {
+ console.error(`获取文件夹 ${folderId} 直接文件数量失败:`, error);
+ return 0;
+ }
+};
+
+// 批量获取所有文件夹的直接文件数量
+const getAllFolderFileCounts = async (folders: FolderNode[]) => {
+ const countMap: Record = {};
+
+ // 递归遍历所有文件夹
+ const traverseFolders = async (folderNodes: FolderNode[]) => {
+ for (const folder of folderNodes) {
+ if (folder.id !== '0') { // 根目录跳过
+ countMap[folder.id] = await getFolderFileCount(folder.id);
+ }
+ if (folder.children && folder.children.length) {
+ await traverseFolders(folder.children);
+ }
+ }
+ };
+
+ await traverseFolders(folders);
+ return countMap;
+};
+
+// 获取文件夹直接子文件夹数量
+const getFolderChildCount = async (folderId: string) => {
+ try {
+ const reqParams = {
+ parentId: folderId,
+ loginCode: userinfo.value.loginCode,
+ };
+ const result = await bizFoldersAllData(reqParams);
+ return Array.isArray(result) ? result.length : 0;
+ } catch (error) {
+ console.error(`获取文件夹 ${folderId} 直接子文件夹数量失败:`, error);
+ return 0;
+ }
+};
+
+// 批量获取所有文件夹的直接子文件夹数量
+const getAllFolderChildCounts = async (folders: FolderNode[]) => {
+ const countMap: Record = {};
+
+ // 递归遍历所有文件夹
+ const traverseFolders = async (folderNodes: FolderNode[]) => {
+ for (const folder of folderNodes) {
+ if (folder.id !== '0') { // 根目录也统计
+ countMap[folder.id] = await getFolderChildCount(folder.id);
+ }
+ if (folder.children && folder.children.length) {
+ await traverseFolders(folder.children);
+ }
+ }
+ };
+
+ await traverseFolders(folders);
+ return countMap;
+};
+
+// 递归计算文件夹的所有下级文件和文件夹总数 - 修复核心逻辑
+const calculateRecursiveCounts = (folder: FolderNode): { totalFiles: number, totalFolders: number } => {
+ // 初始值:仅当前文件夹的直接文件数(文件夹数初始为0,因为子文件夹数要从子节点累加)
+ let totalFiles = folder.fileCount || 0;
+ let totalFolders = 0; // 修复:初始化为0,不再包含当前文件夹的直接子文件夹数
+
+ // 递归遍历所有子文件夹
+ if (folder.children && folder.children.length) {
+ folder.children.forEach(child => {
+ const childCounts = calculateRecursiveCounts(child);
+ // 累加子文件夹的所有文件数
+ totalFiles += childCounts.totalFiles;
+ // 累加子文件夹的数量(子文件夹自身 + 其子文件夹数)
+ totalFolders += 1 + childCounts.totalFolders; // 修复:+1 表示当前子文件夹本身,再累加其子文件夹数
+ });
+ }
+
+ // 保存到当前文件夹节点
+ folder.totalFileCount = totalFiles;
+ folder.totalFolderCount = totalFolders;
+ folder.totalCount = totalFiles + totalFolders;
+
+ return { totalFiles, totalFolders };
+};
const bindParentReferences = (folders: FolderNode[], _parent: FolderNode | null = null) => {
folders.forEach(folder => {
@@ -344,9 +587,16 @@ const bindParentReferences = (folders: FolderNode[], _parent: FolderNode | null
if (expandedState.value[folder.id]) {
folder.expanded = true;
}
+ // 设置直接文件数量和直接子文件夹数量(仅用于单独展示,不参与递归统计)
+ folder.fileCount = folderFileCountMap.value[folder.id] || 0;
+ folder.folderCount = folderChildCountMap.value[folder.id] || 0;
+
if (folder.children && folder.children.length) {
bindParentReferences(folder.children, folder);
}
+
+ // 递归计算所有下级的总数
+ calculateRecursiveCounts(folder);
});
};
@@ -512,9 +762,16 @@ const getDataList = async (params: {}, keepExpanded = true) => {
});
}
+ // 获取所有文件夹的直接文件数量
+ folderFileCountMap.value = await getAllFolderFileCounts(folders);
+ // 获取所有文件夹的直接子文件夹数量
+ folderChildCountMap.value = await getAllFolderChildCounts(folders);
+
+ // 绑定父引用并递归计算总数
bindParentReferences(folders);
folderList.value = folders;
getFileList({});
+
if (currentEditingNode.value) {
nextTick(() => {
const nodeElement = document.querySelector(`[data-node-id="${currentEditingNode.value?.id}"]`);
@@ -559,9 +816,14 @@ const toggleFolder = async (targetFolder: FolderNode) => {
if (targetFolder.id !== '0'){
UploadFile = false;
}
+
+ // 点击文件夹时关闭删除按钮
+ showDeleteBtn.value = null;
+
if (editingInfo.value.id === targetFolder.id && editingInfo.value.type === 'parent' && targetFolder.id !== '0') return;
expandedState.value[targetFolder.id] = !targetFolder.expanded;
targetFolder.expanded = !targetFolder.expanded;
+
if (targetFolder.expanded) {
try {
if ('loading' in targetFolder) targetFolder.loading = true;
@@ -601,6 +863,9 @@ const toggleFolder = async (targetFolder: FolderNode) => {
};
const handleChildClick = (child: FolderNode) => {
+ // 点击子文件夹时关闭删除按钮
+ showDeleteBtn.value = null;
+
if (editingInfo.value.id === child.id && editingInfo.value.type === 'child') return;
ParamsFolders.value = {
folderId: child.id,
@@ -611,6 +876,41 @@ const handleChildClick = (child: FolderNode) => {
getFileList(ParamsFolders.value);
};
+// 切换文件夹菜单显示/隐藏
+const toggleFolderMenu = (folderId: string) => {
+ showDeleteBtn.value = showDeleteBtn.value === folderId ? null : folderId;
+};
+
+const handleDeleteFolder = async (folder: FolderNode) => {
+ if(folder.totalCount != 0 ){
+ message.error("该文件夹非空,无法删除!");
+ showDeleteBtn.value = null;
+ return;
+ }
+
+ Modal.confirm({
+ title: '删除确认',
+ content: '您确定要删除当前文件夹吗?',
+ okText: '确认删除',
+ cancelText: '取消',
+ okType: 'danger',
+ onOk: async () => {
+ try {
+ const res = await bizFoldersDelete({ folderId: folder.id });
+ message.success(res.message);
+ showDeleteBtn.value = null;
+ await getDataList({}, true);
+ if (ParamsFolders.value.folderId === folder.id) {
+ ParamsFolders.value = {};
+ fileList.value = [];
+ }
+ } catch (error) {
+ console.error('删除文件夹失败:', error);
+ }
+ }
+ });
+};
+
const handleFolderDblClick = (
item: FolderNode,
type: 'parent' | 'child',
@@ -618,6 +918,9 @@ const handleFolderDblClick = (
) => {
if (item.id === '0') return;
+ // 双击编辑时关闭删除按钮
+ showDeleteBtn.value = null;
+
currentEditingNode.value = item;
const itemName = item.id === '0' ? item.name : item.folderName || item.name;
@@ -845,7 +1148,7 @@ onMounted(() => {
.folder-node {
display: flex;
align-items: center;
- justify-content: space-between;
+ justify-content: space-between; /* 左右分布:名称区域在左,操作区域在右 */
padding: 8px 6px;
cursor: pointer;
border-radius: 6px;
@@ -853,6 +1156,7 @@ onMounted(() => {
flex-wrap: wrap;
border-bottom: 1px solid rgba(207, 226, 239, 0.3);
transition: all 0.2s ease;
+ position: relative;
}
.folder-node.active,
@@ -879,27 +1183,32 @@ onMounted(() => {
align-items: center;
flex: 1;
overflow: hidden;
+ gap: 6px; /* 元素之间的间距 */
}
-.folder-node:last-child {
- border-bottom: none;
-}
-
-.tree-toggle {
- display: inline-block;
- width: 18px;
- height: 18px;
- text-align: center;
+/* 省略号按钮样式 */
+.folder-more-btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ cursor: pointer;
+ color: #8c8c8c;
+ transition: all 0.2s ease;
font-size: 12px;
- color: #1890ff;
- font-weight: bold;
- user-select: none;
flex-shrink: 0;
- transition: transform 0.2s ease;
}
+.folder-more-btn:hover {
+ background-color: rgba(0, 0, 0, 0.05);
+ color: #1890ff;
+}
+
+/* 文件夹图标样式 */
.folder-icon {
- margin: 0 6px;
+ margin: 0;
font-size: 14px;
flex-shrink: 0;
color: #1890ff;
@@ -911,6 +1220,7 @@ onMounted(() => {
color: #096dd9;
}
+/* 文件夹名称样式 */
.folder-name {
font-size: 13px;
color: #333;
@@ -920,6 +1230,15 @@ onMounted(() => {
flex: 1;
}
+/* 文件数量样式 */
+.file-count {
+ font-size: 11px;
+ color: #8c8c8c;
+ margin-left: 4px;
+ font-weight: normal;
+}
+
+/* 编辑输入框样式 */
.folder-edit-input {
width: 100%;
font-size: 13px;
@@ -927,6 +1246,54 @@ onMounted(() => {
border: 1px solid #1890ff;
border-radius: 4px;
background: rgba(255, 255, 255, 0.95);
+ flex: 1;
+}
+
+/* 右侧操作区域样式 */
+.folder-actions {
+ display: flex;
+ align-items: center;
+ gap: 2px;
+ flex-shrink: 0;
+ margin-left: 8px;
+}
+
+/* 删除按钮样式 */
+.folder-delete-btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ font-size: 12px;
+ flex-shrink: 0;
+}
+
+.folder-delete-btn:hover {
+ background-color: rgba(255, 77, 79, 0.1);
+}
+
+/* 折叠/展开箭头样式 */
+.tree-toggle {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 20px;
+ height: 20px;
+ text-align: center;
+ font-size: 10px;
+ color: #1890ff;
+ font-weight: bold;
+ user-select: none;
+ flex-shrink: 0;
+ transition: transform 0.2s ease;
+}
+
+.folder-node:last-child {
+ border-bottom: none;
}
.tree-children {
@@ -1142,19 +1509,19 @@ onMounted(() => {
border-radius: 4px;
}
+/* 响应式样式 */
@media (max-width: 1400px) {
.file-list {
grid-template-columns: repeat(3, 1fr);
}
}
-@media (max-width: 1200px) {
+@media (max-width: 1024px) {
.file-list {
grid-template-columns: repeat(2, 1fr);
}
-
- .file-card {
- height: 90px;
+ .sidebar {
+ width: 220px;
}
}
@@ -1162,16 +1529,34 @@ onMounted(() => {
.file-list {
grid-template-columns: 1fr;
}
-
- .file-card {
- height: 100px;
- }
.sidebar {
- width: 220px;
+ width: 200px;
+ }
+ .header-bar {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 8px;
+ }
+ .header-btn-group {
+ justify-content: flex-end;
}
}
-* {
- box-sizing: border-box;
+@media (max-width: 480px) {
+ .file-manager-container {
+ flex-direction: column;
+ height: auto;
+ min-height: calc(100vh - 425px);
+ }
+ .sidebar {
+ width: 100%;
+ height: 200px;
+ border-radius: 8px 8px 0 0;
+ border-right: none;
+ border-bottom: 1px solid rgba(207, 226, 239, 0.6);
+ }
+ .main-content {
+ border-radius: 0 0 8px 8px;
+ }
}
\ No newline at end of file