新增待办信息
This commit is contained in:
@@ -23,7 +23,18 @@
|
|||||||
:class="{ active: folder.expanded }"
|
:class="{ active: folder.expanded }"
|
||||||
:style="{ paddingLeft: '0px' }"
|
:style="{ paddingLeft: '0px' }"
|
||||||
>
|
>
|
||||||
|
<!-- 重新组织布局:省略号 → 文件夹图标 → 文件夹名称 → 删除按钮 → 折叠箭头 -->
|
||||||
<div class="folder-info">
|
<div class="folder-info">
|
||||||
|
<!-- 省略号菜单按钮 - 最左侧 -->
|
||||||
|
<span
|
||||||
|
class="folder-more-btn"
|
||||||
|
v-if="folder.id !== '0'"
|
||||||
|
@click.stop="toggleFolderMenu(folder.id)"
|
||||||
|
>
|
||||||
|
<Icon icon="ant-design:ellipsis-outlined" />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- 文件夹图标 -->
|
||||||
<span class="folder-icon">
|
<span class="folder-icon">
|
||||||
<Icon
|
<Icon
|
||||||
:icon="folder.expanded
|
:icon="folder.expanded
|
||||||
@@ -32,6 +43,8 @@
|
|||||||
:color="folder.expanded ? '#FF9800' : '#FFB74D'"
|
:color="folder.expanded ? '#FF9800' : '#FFB74D'"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<!-- 文件夹名称 -->
|
||||||
<template v-if="editingInfo.id === folder.id && editingInfo.type === 'parent' && folder.id !== '0'">
|
<template v-if="editingInfo.id === folder.id && editingInfo.type === 'parent' && folder.id !== '0'">
|
||||||
<a-input
|
<a-input
|
||||||
ref="editInputRef"
|
ref="editInputRef"
|
||||||
@@ -42,8 +55,24 @@
|
|||||||
@keyup.esc="cancelRename"
|
@keyup.esc="cancelRename"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<span v-else class="folder-name">{{ folder.id === '0' ? folder.name : (folder.folderName || folder.name) }}</span>
|
<span v-else class="folder-name">
|
||||||
|
{{ folder.id === '0' ? folder.name : (folder.folderName || folder.name) }}
|
||||||
|
<span class="file-count">({{ folder.totalCount || 0 }})</span>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧操作区域:删除按钮 → 折叠箭头 -->
|
||||||
|
<div class="folder-actions">
|
||||||
|
<!-- 删除按钮 -->
|
||||||
|
<span
|
||||||
|
class="folder-delete-btn"
|
||||||
|
v-if="showDeleteBtn === folder.id"
|
||||||
|
@click.stop="handleDeleteFolder(folder)"
|
||||||
|
>
|
||||||
|
<Icon icon="ant-design:delete-filled" color="#ff4d4f" />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- 折叠/展开箭头 - 最右侧 -->
|
||||||
<span class="tree-toggle" v-if="folder.children && folder.children.length">
|
<span class="tree-toggle" v-if="folder.children && folder.children.length">
|
||||||
<Icon
|
<Icon
|
||||||
:icon="folder.expanded ? 'simple-line-icons:arrow-down' : 'simple-line-icons:arrow-right'"
|
:icon="folder.expanded ? 'simple-line-icons:arrow-down' : 'simple-line-icons:arrow-right'"
|
||||||
@@ -51,6 +80,7 @@
|
|||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="tree-children" v-if="folder.expanded">
|
<div class="tree-children" v-if="folder.expanded">
|
||||||
<div
|
<div
|
||||||
@@ -66,6 +96,15 @@
|
|||||||
:style="{ paddingLeft: '16px' }"
|
:style="{ paddingLeft: '16px' }"
|
||||||
>
|
>
|
||||||
<div class="folder-info">
|
<div class="folder-info">
|
||||||
|
<!-- 省略号按钮 -->
|
||||||
|
<span
|
||||||
|
class="folder-more-btn"
|
||||||
|
@click.stop="toggleFolderMenu(second.id)"
|
||||||
|
>
|
||||||
|
<Icon icon="ant-design:ellipsis-outlined" />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- 文件夹图标 -->
|
||||||
<span class="folder-icon">
|
<span class="folder-icon">
|
||||||
<Icon
|
<Icon
|
||||||
:icon="second.expanded
|
:icon="second.expanded
|
||||||
@@ -74,6 +113,8 @@
|
|||||||
:color="second.expanded ? '#FF9800' : '#FFB74D'"
|
:color="second.expanded ? '#FF9800' : '#FFB74D'"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<!-- 文件夹名称 -->
|
||||||
<template v-if="editingInfo.id === second.id && editingInfo.type === 'child'">
|
<template v-if="editingInfo.id === second.id && editingInfo.type === 'child'">
|
||||||
<a-input
|
<a-input
|
||||||
:ref="el => childEditInputRef[`level1-${idx2}`] = el"
|
:ref="el => childEditInputRef[`level1-${idx2}`] = el"
|
||||||
@@ -84,8 +125,24 @@
|
|||||||
@keyup.esc="cancelRename"
|
@keyup.esc="cancelRename"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<span v-else class="folder-name">{{ second.folderName || second.name }}</span>
|
<span v-else class="folder-name">
|
||||||
|
{{ second.folderName || second.name }}
|
||||||
|
<span class="file-count">({{ second.totalCount || 0 }})</span>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧操作区域 -->
|
||||||
|
<div class="folder-actions">
|
||||||
|
<!-- 删除按钮 -->
|
||||||
|
<span
|
||||||
|
class="folder-delete-btn"
|
||||||
|
v-if="showDeleteBtn === second.id"
|
||||||
|
@click.stop="handleDeleteFolder(second)"
|
||||||
|
>
|
||||||
|
<Icon icon="ant-design:delete-filled" color="#ff4d4f" />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- 折叠箭头 -->
|
||||||
<span class="tree-toggle" v-if="second.children && second.children.length">
|
<span class="tree-toggle" v-if="second.children && second.children.length">
|
||||||
<Icon
|
<Icon
|
||||||
:icon="second.expanded ? 'simple-line-icons:arrow-down' : 'simple-line-icons:arrow-right'"
|
:icon="second.expanded ? 'simple-line-icons:arrow-down' : 'simple-line-icons:arrow-right'"
|
||||||
@@ -93,6 +150,7 @@
|
|||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="tree-children" v-if="second.expanded">
|
<div class="tree-children" v-if="second.expanded">
|
||||||
<div
|
<div
|
||||||
@@ -108,6 +166,15 @@
|
|||||||
:style="{ paddingLeft: '32px' }"
|
:style="{ paddingLeft: '32px' }"
|
||||||
>
|
>
|
||||||
<div class="folder-info">
|
<div class="folder-info">
|
||||||
|
<!-- 省略号按钮 -->
|
||||||
|
<span
|
||||||
|
class="folder-more-btn"
|
||||||
|
@click.stop="toggleFolderMenu(third.id)"
|
||||||
|
>
|
||||||
|
<Icon icon="ant-design:ellipsis-outlined" />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- 文件夹图标 -->
|
||||||
<span class="folder-icon">
|
<span class="folder-icon">
|
||||||
<Icon
|
<Icon
|
||||||
:icon="third.expanded
|
:icon="third.expanded
|
||||||
@@ -116,6 +183,8 @@
|
|||||||
:color="third.expanded ? '#FF9800' : '#FFB74D'"
|
:color="third.expanded ? '#FF9800' : '#FFB74D'"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<!-- 文件夹名称 -->
|
||||||
<template v-if="editingInfo.id === third.id && editingInfo.type === 'child'">
|
<template v-if="editingInfo.id === third.id && editingInfo.type === 'child'">
|
||||||
<a-input
|
<a-input
|
||||||
:ref="el => childEditInputRef[`level2-${idx2}-${idx3}`] = el"
|
:ref="el => childEditInputRef[`level2-${idx2}-${idx3}`] = el"
|
||||||
@@ -126,8 +195,24 @@
|
|||||||
@keyup.esc="cancelRename"
|
@keyup.esc="cancelRename"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<span v-else class="folder-name">{{ third.folderName || third.name }}</span>
|
<span v-else class="folder-name">
|
||||||
|
{{ third.folderName || third.name }}
|
||||||
|
<span class="file-count">({{ third.totalCount || 0 }})</span>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧操作区域 -->
|
||||||
|
<div class="folder-actions">
|
||||||
|
<!-- 删除按钮 -->
|
||||||
|
<span
|
||||||
|
class="folder-delete-btn"
|
||||||
|
v-if="showDeleteBtn === third.id"
|
||||||
|
@click.stop="handleDeleteFolder(third)"
|
||||||
|
>
|
||||||
|
<Icon icon="ant-design:delete-filled" color="#ff4d4f" />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- 折叠箭头 -->
|
||||||
<span class="tree-toggle" v-if="third.children && third.children.length">
|
<span class="tree-toggle" v-if="third.children && third.children.length">
|
||||||
<Icon
|
<Icon
|
||||||
:icon="third.expanded ? 'simple-line-icons:arrow-down' : 'simple-line-icons:arrow-right'"
|
:icon="third.expanded ? 'simple-line-icons:arrow-down' : 'simple-line-icons:arrow-right'"
|
||||||
@@ -135,6 +220,7 @@
|
|||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="tree-children" v-if="third.expanded">
|
<div class="tree-children" v-if="third.expanded">
|
||||||
<div
|
<div
|
||||||
@@ -150,6 +236,15 @@
|
|||||||
:style="{ paddingLeft: '48px' }"
|
:style="{ paddingLeft: '48px' }"
|
||||||
>
|
>
|
||||||
<div class="folder-info">
|
<div class="folder-info">
|
||||||
|
<!-- 省略号按钮 -->
|
||||||
|
<span
|
||||||
|
class="folder-more-btn"
|
||||||
|
@click.stop="toggleFolderMenu(fourth.id)"
|
||||||
|
>
|
||||||
|
<Icon icon="ant-design:ellipsis-outlined" />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- 文件夹图标 -->
|
||||||
<span class="folder-icon">
|
<span class="folder-icon">
|
||||||
<Icon
|
<Icon
|
||||||
:icon="fourth.expanded
|
:icon="fourth.expanded
|
||||||
@@ -158,6 +253,8 @@
|
|||||||
:color="fourth.expanded ? '#FF9800' : '#FFB74D'"
|
:color="fourth.expanded ? '#FF9800' : '#FFB74D'"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<!-- 文件夹名称 -->
|
||||||
<template v-if="editingInfo.id === fourth.id && editingInfo.type === 'child'">
|
<template v-if="editingInfo.id === fourth.id && editingInfo.type === 'child'">
|
||||||
<a-input
|
<a-input
|
||||||
:ref="el => childEditInputRef[`level3-${idx2}-${idx3}-${idx4}`] = el"
|
:ref="el => childEditInputRef[`level3-${idx2}-${idx3}-${idx4}`] = el"
|
||||||
@@ -168,8 +265,24 @@
|
|||||||
@keyup.esc="cancelRename"
|
@keyup.esc="cancelRename"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<span v-else class="folder-name">{{ fourth.folderName || fourth.name }}</span>
|
<span v-else class="folder-name">
|
||||||
|
{{ fourth.folderName || fourth.name }}
|
||||||
|
<span class="file-count">({{ fourth.totalCount || 0 }})</span>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧操作区域 -->
|
||||||
|
<div class="folder-actions">
|
||||||
|
<!-- 删除按钮 -->
|
||||||
|
<span
|
||||||
|
class="folder-delete-btn"
|
||||||
|
v-if="showDeleteBtn === fourth.id"
|
||||||
|
@click.stop="handleDeleteFolder(fourth)"
|
||||||
|
>
|
||||||
|
<Icon icon="ant-design:delete-filled" color="#ff4d4f" />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- 折叠箭头 -->
|
||||||
<span class="tree-toggle" v-if="fourth.children && fourth.children.length">
|
<span class="tree-toggle" v-if="fourth.children && fourth.children.length">
|
||||||
<Icon
|
<Icon
|
||||||
:icon="fourth.expanded ? 'simple-line-icons:arrow-down' : 'simple-line-icons:arrow-right'"
|
:icon="fourth.expanded ? 'simple-line-icons:arrow-down' : 'simple-line-icons:arrow-right'"
|
||||||
@@ -177,6 +290,7 @@
|
|||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="tree-children" v-if="fourth.expanded">
|
<div class="tree-children" v-if="fourth.expanded">
|
||||||
<div
|
<div
|
||||||
@@ -191,12 +305,23 @@
|
|||||||
:style="{ paddingLeft: '64px' }"
|
:style="{ paddingLeft: '64px' }"
|
||||||
>
|
>
|
||||||
<div class="folder-info">
|
<div class="folder-info">
|
||||||
|
<!-- 省略号按钮 -->
|
||||||
|
<span
|
||||||
|
class="folder-more-btn"
|
||||||
|
@click.stop="toggleFolderMenu(fifth.id)"
|
||||||
|
>
|
||||||
|
<Icon icon="ant-design:ellipsis-outlined" />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- 文件夹图标 -->
|
||||||
<span class="folder-icon">
|
<span class="folder-icon">
|
||||||
<Icon
|
<Icon
|
||||||
icon="ant-design:folder-outlined"
|
icon="ant-design:folder-outlined"
|
||||||
color="#FFB74D"
|
color="#FFB74D"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<!-- 文件夹名称 -->
|
||||||
<template v-if="editingInfo.id === fifth.id && editingInfo.type === 'child'">
|
<template v-if="editingInfo.id === fifth.id && editingInfo.type === 'child'">
|
||||||
<a-input
|
<a-input
|
||||||
:ref="el => childEditInputRef[`level4-${idx2}-${idx3}-${idx4}-${idx5}`] = el"
|
:ref="el => childEditInputRef[`level4-${idx2}-${idx3}-${idx4}-${idx5}`] = el"
|
||||||
@@ -207,7 +332,22 @@
|
|||||||
@keyup.esc="cancelRename"
|
@keyup.esc="cancelRename"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<span v-else class="folder-name">{{ fifth.folderName || fifth.name }}</span>
|
<span v-else class="folder-name">
|
||||||
|
{{ fifth.folderName || fifth.name }}
|
||||||
|
<span class="file-count">({{ fifth.totalCount || 0 }})</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧操作区域 -->
|
||||||
|
<div class="folder-actions">
|
||||||
|
<!-- 删除按钮 -->
|
||||||
|
<span
|
||||||
|
class="folder-delete-btn"
|
||||||
|
v-if="showDeleteBtn === fifth.id"
|
||||||
|
@click.stop="handleDeleteFolder(fifth)"
|
||||||
|
>
|
||||||
|
<Icon icon="ant-design:delete-filled" color="#ff4d4f" />
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -280,7 +420,7 @@ import { downloadByUrl } from '@jeesite/core/utils/file/download';
|
|||||||
import UploadModal from './upload.vue';
|
import UploadModal from './upload.vue';
|
||||||
import FolderModal from './folder.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 { BizMyfiles, bizMyfilesListAll, bizMyfilesDelete } from '@jeesite/biz/api/biz/myfiles';
|
||||||
|
|
||||||
import { useUserStore } from '@jeesite/core/store/modules/user';
|
import { useUserStore } from '@jeesite/core/store/modules/user';
|
||||||
@@ -305,6 +445,11 @@ interface FolderNode {
|
|||||||
parentName?: string;
|
parentName?: string;
|
||||||
userName?: string;
|
userName?: string;
|
||||||
loginCode?: string;
|
loginCode?: string;
|
||||||
|
fileCount?: number; // 当前文件夹直接文件数量
|
||||||
|
folderCount?: number; // 当前文件夹直接子文件夹数量
|
||||||
|
totalFileCount?: number; // 递归统计的所有下级文件总数
|
||||||
|
totalFolderCount?: number; // 递归统计的所有下级文件夹总数
|
||||||
|
totalCount?: number; // 总数量 = 所有下级文件数 + 所有下级文件夹数
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EditingInfo {
|
interface EditingInfo {
|
||||||
@@ -318,6 +463,7 @@ interface EditingInfo {
|
|||||||
|
|
||||||
const expandedState = ref<Record<string, boolean>>({});
|
const expandedState = ref<Record<string, boolean>>({});
|
||||||
const currentEditingNode = ref<FolderNode | null>(null);
|
const currentEditingNode = ref<FolderNode | null>(null);
|
||||||
|
const showDeleteBtn = ref<string | null>(null); // 新增:控制删除按钮显示
|
||||||
|
|
||||||
const editingInfo = ref<EditingInfo>({
|
const editingInfo = ref<EditingInfo>({
|
||||||
id: null,
|
id: null,
|
||||||
@@ -337,6 +483,103 @@ const folderSearchText = ref<string>('');
|
|||||||
const fileSearchText = ref<string>('');
|
const fileSearchText = ref<string>('');
|
||||||
const folderList = ref<FolderNode[]>([]);
|
const folderList = ref<FolderNode[]>([]);
|
||||||
const fileList = ref<BizMyfiles[]>([]);
|
const fileList = ref<BizMyfiles[]>([]);
|
||||||
|
const folderFileCountMap = ref<Record<string, number>>({}); // 文件夹直接文件数量映射
|
||||||
|
const folderChildCountMap = ref<Record<string, number>>({}); // 文件夹直接子文件夹数量映射
|
||||||
|
|
||||||
|
// 获取文件夹直接文件数量
|
||||||
|
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<string, number> = {};
|
||||||
|
|
||||||
|
// 递归遍历所有文件夹
|
||||||
|
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<string, number> = {};
|
||||||
|
|
||||||
|
// 递归遍历所有文件夹
|
||||||
|
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) => {
|
const bindParentReferences = (folders: FolderNode[], _parent: FolderNode | null = null) => {
|
||||||
folders.forEach(folder => {
|
folders.forEach(folder => {
|
||||||
@@ -344,9 +587,16 @@ const bindParentReferences = (folders: FolderNode[], _parent: FolderNode | null
|
|||||||
if (expandedState.value[folder.id]) {
|
if (expandedState.value[folder.id]) {
|
||||||
folder.expanded = true;
|
folder.expanded = true;
|
||||||
}
|
}
|
||||||
|
// 设置直接文件数量和直接子文件夹数量(仅用于单独展示,不参与递归统计)
|
||||||
|
folder.fileCount = folderFileCountMap.value[folder.id] || 0;
|
||||||
|
folder.folderCount = folderChildCountMap.value[folder.id] || 0;
|
||||||
|
|
||||||
if (folder.children && folder.children.length) {
|
if (folder.children && folder.children.length) {
|
||||||
bindParentReferences(folder.children, folder);
|
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);
|
bindParentReferences(folders);
|
||||||
folderList.value = folders;
|
folderList.value = folders;
|
||||||
getFileList({});
|
getFileList({});
|
||||||
|
|
||||||
if (currentEditingNode.value) {
|
if (currentEditingNode.value) {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
const nodeElement = document.querySelector(`[data-node-id="${currentEditingNode.value?.id}"]`);
|
const nodeElement = document.querySelector(`[data-node-id="${currentEditingNode.value?.id}"]`);
|
||||||
@@ -559,9 +816,14 @@ const toggleFolder = async (targetFolder: FolderNode) => {
|
|||||||
if (targetFolder.id !== '0'){
|
if (targetFolder.id !== '0'){
|
||||||
UploadFile = false;
|
UploadFile = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 点击文件夹时关闭删除按钮
|
||||||
|
showDeleteBtn.value = null;
|
||||||
|
|
||||||
if (editingInfo.value.id === targetFolder.id && editingInfo.value.type === 'parent' && targetFolder.id !== '0') return;
|
if (editingInfo.value.id === targetFolder.id && editingInfo.value.type === 'parent' && targetFolder.id !== '0') return;
|
||||||
expandedState.value[targetFolder.id] = !targetFolder.expanded;
|
expandedState.value[targetFolder.id] = !targetFolder.expanded;
|
||||||
targetFolder.expanded = !targetFolder.expanded;
|
targetFolder.expanded = !targetFolder.expanded;
|
||||||
|
|
||||||
if (targetFolder.expanded) {
|
if (targetFolder.expanded) {
|
||||||
try {
|
try {
|
||||||
if ('loading' in targetFolder) targetFolder.loading = true;
|
if ('loading' in targetFolder) targetFolder.loading = true;
|
||||||
@@ -601,6 +863,9 @@ const toggleFolder = async (targetFolder: FolderNode) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleChildClick = (child: FolderNode) => {
|
const handleChildClick = (child: FolderNode) => {
|
||||||
|
// 点击子文件夹时关闭删除按钮
|
||||||
|
showDeleteBtn.value = null;
|
||||||
|
|
||||||
if (editingInfo.value.id === child.id && editingInfo.value.type === 'child') return;
|
if (editingInfo.value.id === child.id && editingInfo.value.type === 'child') return;
|
||||||
ParamsFolders.value = {
|
ParamsFolders.value = {
|
||||||
folderId: child.id,
|
folderId: child.id,
|
||||||
@@ -611,6 +876,41 @@ const handleChildClick = (child: FolderNode) => {
|
|||||||
getFileList(ParamsFolders.value);
|
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 = (
|
const handleFolderDblClick = (
|
||||||
item: FolderNode,
|
item: FolderNode,
|
||||||
type: 'parent' | 'child',
|
type: 'parent' | 'child',
|
||||||
@@ -618,6 +918,9 @@ const handleFolderDblClick = (
|
|||||||
) => {
|
) => {
|
||||||
if (item.id === '0') return;
|
if (item.id === '0') return;
|
||||||
|
|
||||||
|
// 双击编辑时关闭删除按钮
|
||||||
|
showDeleteBtn.value = null;
|
||||||
|
|
||||||
currentEditingNode.value = item;
|
currentEditingNode.value = item;
|
||||||
const itemName = item.id === '0' ? item.name : item.folderName || item.name;
|
const itemName = item.id === '0' ? item.name : item.folderName || item.name;
|
||||||
|
|
||||||
@@ -845,7 +1148,7 @@ onMounted(() => {
|
|||||||
.folder-node {
|
.folder-node {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between; /* 左右分布:名称区域在左,操作区域在右 */
|
||||||
padding: 8px 6px;
|
padding: 8px 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
@@ -853,6 +1156,7 @@ onMounted(() => {
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
border-bottom: 1px solid rgba(207, 226, 239, 0.3);
|
border-bottom: 1px solid rgba(207, 226, 239, 0.3);
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.folder-node.active,
|
.folder-node.active,
|
||||||
@@ -879,27 +1183,32 @@ onMounted(() => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
gap: 6px; /* 元素之间的间距 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.folder-node:last-child {
|
/* 省略号按钮样式 */
|
||||||
border-bottom: none;
|
.folder-more-btn {
|
||||||
}
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
.tree-toggle {
|
justify-content: center;
|
||||||
display: inline-block;
|
width: 20px;
|
||||||
width: 18px;
|
height: 20px;
|
||||||
height: 18px;
|
border-radius: 50%;
|
||||||
text-align: center;
|
cursor: pointer;
|
||||||
|
color: #8c8c8c;
|
||||||
|
transition: all 0.2s ease;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #1890ff;
|
|
||||||
font-weight: bold;
|
|
||||||
user-select: none;
|
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
transition: transform 0.2s ease;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.folder-more-btn:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 文件夹图标样式 */
|
||||||
.folder-icon {
|
.folder-icon {
|
||||||
margin: 0 6px;
|
margin: 0;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
color: #1890ff;
|
color: #1890ff;
|
||||||
@@ -911,6 +1220,7 @@ onMounted(() => {
|
|||||||
color: #096dd9;
|
color: #096dd9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 文件夹名称样式 */
|
||||||
.folder-name {
|
.folder-name {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: #333;
|
color: #333;
|
||||||
@@ -920,6 +1230,15 @@ onMounted(() => {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 文件数量样式 */
|
||||||
|
.file-count {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #8c8c8c;
|
||||||
|
margin-left: 4px;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 编辑输入框样式 */
|
||||||
.folder-edit-input {
|
.folder-edit-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
@@ -927,6 +1246,54 @@ onMounted(() => {
|
|||||||
border: 1px solid #1890ff;
|
border: 1px solid #1890ff;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background: rgba(255, 255, 255, 0.95);
|
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 {
|
.tree-children {
|
||||||
@@ -1142,19 +1509,19 @@ onMounted(() => {
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 响应式样式 */
|
||||||
@media (max-width: 1400px) {
|
@media (max-width: 1400px) {
|
||||||
.file-list {
|
.file-list {
|
||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1024px) {
|
||||||
.file-list {
|
.file-list {
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
}
|
}
|
||||||
|
.sidebar {
|
||||||
.file-card {
|
width: 220px;
|
||||||
height: 90px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1162,16 +1529,34 @@ onMounted(() => {
|
|||||||
.file-list {
|
.file-list {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
.sidebar {
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
.header-bar {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.header-btn-group {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.file-card {
|
@media (max-width: 480px) {
|
||||||
height: 100px;
|
.file-manager-container {
|
||||||
|
flex-direction: column;
|
||||||
|
height: auto;
|
||||||
|
min-height: calc(100vh - 425px);
|
||||||
}
|
}
|
||||||
.sidebar {
|
.sidebar {
|
||||||
width: 220px;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user