商务模块的文件夹结构优化成树形层级结构
This commit is contained in:
parent
0401a28037
commit
40ae745dfb
|
@ -37,7 +37,7 @@
|
|||
|
||||
<div class="folder-content">
|
||||
<!-- 加载状态 -->
|
||||
<a-skeleton :loading="loading && folderList.length === 0" :rows="5" v-if="loading" animation="pulse">
|
||||
<a-skeleton :loading="loading && folderList.length === 0" :rows="5" v-if="loading" :animation="true">
|
||||
<template #skeleton>
|
||||
<div class="skeleton-item flex items-center px-4 py-3" v-for="i in 5" :key="i">
|
||||
<div class="w-6 h-6 rounded bg-gray-200 mr-3"></div>
|
||||
|
@ -53,51 +53,53 @@
|
|||
</a-typography-text>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<a-empty v-if="!loading && folderList.length === 0" :description="searchKeyword ? '未找到匹配的文件夹' : '暂无文件夹'" />
|
||||
|
||||
<a-list v-if="!loading && folderList.length > 0" class="folder-list">
|
||||
<!-- 文件夹列表 -->
|
||||
<a-list-item
|
||||
v-for="folder in folderList"
|
||||
:key="folder.id"
|
||||
:class="['folder-list-item', { 'active': currentFolderId === folder.id }]"
|
||||
@click="handleFolderClick(folder.id)"
|
||||
:tooltip="sidebarCollapsed ? folder.name : ''"
|
||||
>
|
||||
<!-- 第一行:文件夹图标和名称 -->
|
||||
<div class="folder-main-info">
|
||||
<div class="folder-icon-wrapper">
|
||||
<IconFolder class="folder-icon" :style="{ color: folderColor }" />
|
||||
</div>
|
||||
<span v-if="!sidebarCollapsed" class="folder-name">{{ folder.name }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 第二行:文件夹操作按钮 -->
|
||||
<div v-if="!sidebarCollapsed" class="folder-actions-row">
|
||||
<!-- 树形文件夹结构 -->
|
||||
<div v-if="!loading && folderList.length > 0" class="folder-tree-container">
|
||||
|
||||
<a-tree
|
||||
:data="folderTreeData"
|
||||
:selected-keys="currentFolderId ? [currentFolderId] : []"
|
||||
:field-names="{ key: 'key', title: 'title', children: 'children' }"
|
||||
:show-line="!sidebarCollapsed"
|
||||
:block-node="true"
|
||||
:default-expand-all="true"
|
||||
@select="handleFolderSelect"
|
||||
@dblclick="handleFolderDoubleClick"
|
||||
class="folder-tree"
|
||||
:class="{ 'collapsed': sidebarCollapsed }"
|
||||
/>
|
||||
|
||||
<!-- 文件夹操作按钮 -->
|
||||
<div v-if="currentFolderId && currentFolderId !== '0'" class="folder-actions-bar" style="padding: 8px; border-top: 1px solid #e5e6eb; margin-top: 8px;">
|
||||
<a-space>
|
||||
<a-button
|
||||
type="text"
|
||||
shape="circle"
|
||||
size="small"
|
||||
@click.stop="handleRenameFolder(folder, folder.id, folder.name)"
|
||||
@click="handleRenameCurrentFolder"
|
||||
tooltip="重命名"
|
||||
class="action-btn"
|
||||
>
|
||||
<icon-edit />
|
||||
<template #icon><icon-edit /></template>
|
||||
重命名
|
||||
</a-button>
|
||||
<a-button
|
||||
type="text"
|
||||
shape="circle"
|
||||
size="small"
|
||||
@click.stop="handleDeleteFolder(folder)"
|
||||
@click="handleDeleteCurrentFolder"
|
||||
tooltip="删除"
|
||||
status="danger"
|
||||
class="action-btn"
|
||||
>
|
||||
<icon-delete />
|
||||
<template #icon><icon-delete /></template>
|
||||
删除
|
||||
</a-button>
|
||||
</div>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</a-space>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 侧边栏底部分页控件 -->
|
||||
|
@ -108,7 +110,8 @@
|
|||
</a-typography-text>
|
||||
</div>
|
||||
|
||||
<div class="pagination-controls">
|
||||
<!-- 隐藏分页控件,因为现在获取所有文件夹 -->
|
||||
<!-- <div class="pagination-controls">
|
||||
<a-pagination
|
||||
:current="currentPage"
|
||||
:page-size="pageSize"
|
||||
|
@ -120,7 +123,7 @@
|
|||
size="small"
|
||||
show-total
|
||||
/>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</a-layout-sider>
|
||||
|
||||
|
@ -128,8 +131,14 @@
|
|||
<a-layout-header class="file-header">
|
||||
<div class="breadcrumbs">
|
||||
<a-breadcrumb>
|
||||
<a-breadcrumb-item>知识库</a-breadcrumb-item>
|
||||
<a-breadcrumb-item>{{ currentFolderName }}</a-breadcrumb-item>
|
||||
<a-breadcrumb-item
|
||||
v-for="(item, index) in breadcrumbPath"
|
||||
:key="index"
|
||||
:class="{ 'clickable': index < breadcrumbPath.length - 1 }"
|
||||
@click="handleBreadcrumbClick(index)"
|
||||
>
|
||||
{{ item }}
|
||||
</a-breadcrumb-item>
|
||||
</a-breadcrumb>
|
||||
<a-button
|
||||
type="text"
|
||||
|
@ -369,20 +378,18 @@
|
|||
<a-input v-model="folderForm.name" placeholder="输入文件夹名称" max-length="50" />
|
||||
</a-form-item>
|
||||
<a-form-item label="父级目录" field="parentId" :rules="folderRules.parentId">
|
||||
<a-select
|
||||
<a-tree-select
|
||||
v-model="folderForm.parentId"
|
||||
placeholder="请选择父级目录"
|
||||
:data="folderTreeSelectData"
|
||||
:field-names="{ key: 'id', title: 'name', children: 'children' }"
|
||||
allow-clear
|
||||
:tree-props="{ showLine: true }"
|
||||
>
|
||||
<a-option value="0">根目录</a-option>
|
||||
<a-option
|
||||
v-for="folder in folderList"
|
||||
:key="folder.id"
|
||||
:value="folder.id"
|
||||
v-if="!folderForm.id || folder.id !== folderForm.id"
|
||||
>
|
||||
{{ folder.name }}
|
||||
</a-option>
|
||||
</a-select>
|
||||
<template #title="{ node }">
|
||||
<span>{{ node?.title || node?.name }}</span>
|
||||
</template>
|
||||
</a-tree-select>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
@ -592,7 +599,8 @@ import {
|
|||
const folderList = ref([]);
|
||||
const fileList = ref([]);
|
||||
const currentFolderId = ref('');
|
||||
const currentFolderName = ref('');
|
||||
// 移除currentFolderName,现在使用面包屑导航
|
||||
// const currentFolderName = ref('');
|
||||
const loading = ref(false);
|
||||
const folderDialogVisible = ref(false);
|
||||
const uploadDialogVisible = ref(false);
|
||||
|
@ -664,6 +672,111 @@ const canUpload = computed(() => {
|
|||
return hasFiles.value && !uploading.value && uploadForm.folderId;
|
||||
});
|
||||
|
||||
// 计算属性:将平铺的文件夹数据转换为树形结构
|
||||
const folderTreeData = computed(() => {
|
||||
console.log('=== folderTreeData计算属性执行 ===');
|
||||
console.log('folderList.value:', folderList.value);
|
||||
console.log('folderList.value.length:', folderList.value?.length);
|
||||
|
||||
if (!folderList.value || folderList.value.length === 0) {
|
||||
console.log('folderList为空,返回空数组');
|
||||
return [];
|
||||
}
|
||||
|
||||
// 创建文件夹映射表
|
||||
const folderMap = new Map();
|
||||
const rootFolders = [];
|
||||
|
||||
console.log('=== 开始创建文件夹映射 ===');
|
||||
// 首先创建所有文件夹的映射
|
||||
folderList.value.forEach((folder, index) => {
|
||||
console.log(`处理第${index + 1}个文件夹:`, folder);
|
||||
// 确保文件夹数据完整
|
||||
if (folder && folder.id && folder.name) {
|
||||
const node = {
|
||||
key: folder.id, // Tree组件需要的key字段
|
||||
title: folder.name, // Tree组件需要的title字段
|
||||
children: [], // Tree组件需要的children字段
|
||||
// 保留原始字段用于其他功能
|
||||
id: folder.id,
|
||||
name: folder.name,
|
||||
parentId: folder.parentId
|
||||
};
|
||||
folderMap.set(folder.id, node);
|
||||
console.log(`✅ 成功添加文件夹到映射: ${folder.name} (ID: ${folder.id})`);
|
||||
} else {
|
||||
console.warn('❌ 跳过不完整的文件夹数据:', folder);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('=== 开始构建树形结构 ===');
|
||||
console.log('文件夹映射表大小:', folderMap.size);
|
||||
|
||||
// 构建树形结构
|
||||
folderList.value.forEach((folder, index) => {
|
||||
console.log(`构建第${index + 1}个文件夹的树形结构:`, folder);
|
||||
// 确保文件夹数据完整
|
||||
if (!folder || !folder.id || !folder.name) {
|
||||
console.warn('❌ 跳过不完整的文件夹数据:', folder);
|
||||
return;
|
||||
}
|
||||
|
||||
const node = folderMap.get(folder.id);
|
||||
if (!node) {
|
||||
console.warn('❌ 找不到文件夹节点:', folder.id);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`处理文件夹: ${folder.name} (ID: ${folder.id}, ParentID: ${folder.parentId})`);
|
||||
|
||||
if (folder.parentId === '0' || folder.parentId === 0) {
|
||||
// 根文件夹
|
||||
rootFolders.push(node);
|
||||
console.log(`✅ 添加为根文件夹: ${folder.name}`);
|
||||
} else {
|
||||
// 子文件夹
|
||||
const parent = folderMap.get(folder.parentId);
|
||||
if (parent) {
|
||||
parent.children.push(node);
|
||||
console.log(`✅ 添加为子文件夹: ${folder.name} -> ${parent.name}`);
|
||||
} else {
|
||||
// 如果找不到父文件夹,当作根文件夹处理
|
||||
console.warn('⚠️ 找不到父文件夹,将文件夹作为根文件夹:', folder.name, folder.parentId);
|
||||
rootFolders.push(node);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log('=== 树形结构构建完成 ===');
|
||||
console.log('根文件夹数量:', rootFolders.length);
|
||||
console.log('构建的树形结构:', rootFolders);
|
||||
|
||||
// 验证树形结构中的节点数据
|
||||
rootFolders.forEach((root, index) => {
|
||||
console.log(`根文件夹${index + 1}:`, {
|
||||
id: root.id,
|
||||
name: root.name,
|
||||
childrenCount: root.children?.length || 0
|
||||
});
|
||||
});
|
||||
|
||||
return rootFolders;
|
||||
});
|
||||
|
||||
// 计算属性:树形选择器数据(包含根目录选项)
|
||||
const folderTreeSelectData = computed(() => {
|
||||
const rootOption = {
|
||||
key: '0', // Tree组件需要的key字段
|
||||
title: '根目录', // Tree组件需要的title字段
|
||||
children: [], // Tree组件需要的children字段
|
||||
// 保留原始字段用于其他功能
|
||||
id: '0',
|
||||
name: '根目录'
|
||||
};
|
||||
|
||||
return [rootOption, ...folderTreeData.value];
|
||||
});
|
||||
|
||||
// 搜索相关
|
||||
const searchKeyword = ref(''); // 文件夹搜索关键词
|
||||
const fileSearchKeyword = ref(''); // 文件搜索关键词
|
||||
|
@ -682,7 +795,7 @@ const initData = async () => {
|
|||
|
||||
const apiParams = {
|
||||
page: currentPage.value,
|
||||
pageSize: pageSize.value,
|
||||
pageSize: 1000, // 修改为足够大的值,确保获取所有文件夹
|
||||
folderName: searchKeyword.value.trim() || undefined
|
||||
};
|
||||
|
||||
|
@ -701,11 +814,43 @@ const initData = async () => {
|
|||
|
||||
// 根据后端返回的数据结构处理
|
||||
if (folderRes.code === 200 && folderRes.data) {
|
||||
const processedFolders = folderRes.data.rows.map(folder => ({
|
||||
id: String(folder.folderId),
|
||||
name: folder.folderName,
|
||||
parentId: String(folder.parentId || 0)
|
||||
}));
|
||||
console.log('=== 开始处理数据 ===');
|
||||
console.log('folderRes.data:', folderRes.data);
|
||||
console.log('folderRes.data.rows:', folderRes.data.rows);
|
||||
console.log('folderRes.data.rows类型:', typeof folderRes.data.rows);
|
||||
console.log('folderRes.data.rows长度:', folderRes.data.rows?.length);
|
||||
|
||||
// 检查数据结构
|
||||
if (!folderRes.data.rows || !Array.isArray(folderRes.data.rows)) {
|
||||
console.error('API返回的数据结构不正确,rows字段不存在或不是数组');
|
||||
console.log('可用的字段:', Object.keys(folderRes.data));
|
||||
folderList.value = [];
|
||||
totalFolders.value = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const processedFolders = folderRes.data.rows.map((folder, index) => {
|
||||
console.log(`处理第${index + 1}个原始文件夹数据:`, folder);
|
||||
console.log(`原始数据字段:`, Object.keys(folder));
|
||||
console.log(`folderId:`, folder.folderId);
|
||||
console.log(`folderName:`, folder.folderName);
|
||||
console.log(`parentId:`, folder.parentId);
|
||||
|
||||
// 确保所有必需字段都存在
|
||||
if (!folder.folderId || !folder.folderName) {
|
||||
console.warn('❌ 跳过不完整的文件夹数据:', folder);
|
||||
return null;
|
||||
}
|
||||
|
||||
const processedFolder = {
|
||||
id: String(folder.folderId),
|
||||
name: String(folder.folderName),
|
||||
parentId: String(folder.parentId || 0)
|
||||
};
|
||||
|
||||
console.log(`✅ 处理后的文件夹数据:`, processedFolder);
|
||||
return processedFolder;
|
||||
}).filter(Boolean); // 过滤掉null值
|
||||
|
||||
folderList.value = processedFolders;
|
||||
totalFolders.value = Number(folderRes.data.total) || 0;
|
||||
|
@ -720,6 +865,8 @@ const initData = async () => {
|
|||
totalFolders.value = 0;
|
||||
console.log('API响应异常,清空列表');
|
||||
console.log('响应码不是200或数据为空');
|
||||
console.log('folderRes.code:', folderRes.code);
|
||||
console.log('folderRes.data:', folderRes.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('初始化文件夹数据失败:', error);
|
||||
|
@ -784,6 +931,7 @@ const handleSearchClear = () => {
|
|||
}
|
||||
currentPage.value = 1;
|
||||
console.log('重置页码为:', currentPage.value);
|
||||
// 清除搜索后立即刷新数据,显示所有文件夹
|
||||
initData();
|
||||
};
|
||||
|
||||
|
@ -854,13 +1002,6 @@ const loadFiles = async (folderId) => {
|
|||
}
|
||||
|
||||
currentFolderId.value = folderId;
|
||||
|
||||
if (folderId === '0') {
|
||||
currentFolderName.value = '根目录';
|
||||
} else {
|
||||
const folder = folderList.value.find(f => f.id === folderId);
|
||||
currentFolderName.value = folder ? folder.name : '未知文件夹';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载文件失败:', error);
|
||||
Message.error('服务开小差,请稍后再试');
|
||||
|
@ -873,14 +1014,141 @@ const loadFiles = async (folderId) => {
|
|||
|
||||
|
||||
// 文件夹点击事件
|
||||
const handleFolderClick = (folderId) => {
|
||||
const id = String(folderId);
|
||||
if (currentFolderId.value !== id) {
|
||||
fileCurrentPage.value = 1;
|
||||
// 切换文件夹时清空文件搜索关键词
|
||||
fileSearchKeyword.value = '';
|
||||
// const handleFolderClick = (folderId) => {
|
||||
// const id = String(folderId);
|
||||
// if (currentFolderId.value !== id) {
|
||||
// fileCurrentPage.value = 1;
|
||||
// // 切换文件夹时清空文件搜索关键词
|
||||
// fileSearchKeyword.value = '';
|
||||
// }
|
||||
// currentFolderId.value = id;
|
||||
// };
|
||||
|
||||
// 树形文件夹选择事件
|
||||
const handleFolderSelect = (selectedKeys, info) => {
|
||||
if (selectedKeys.length > 0) {
|
||||
const folderId = selectedKeys[0];
|
||||
if (currentFolderId.value !== folderId) {
|
||||
fileCurrentPage.value = 1;
|
||||
// 切换文件夹时清空文件搜索关键词
|
||||
fileSearchKeyword.value = '';
|
||||
}
|
||||
currentFolderId.value = folderId;
|
||||
loadFiles(folderId);
|
||||
}
|
||||
};
|
||||
|
||||
// 文件夹双击处理
|
||||
const handleFolderDoubleClick = (info) => {
|
||||
console.log('文件夹双击:', info);
|
||||
|
||||
const { node } = info;
|
||||
if (!node) return;
|
||||
|
||||
// 显示操作选项
|
||||
Modal.confirm({
|
||||
title: '文件夹操作',
|
||||
content: `请选择对文件夹"${node.title}"的操作`,
|
||||
okText: '重命名',
|
||||
cancelText: '删除',
|
||||
onOk: () => handleRenameFolder(node),
|
||||
onCancel: () => handleDeleteFolder(node)
|
||||
});
|
||||
};
|
||||
|
||||
// 重命名当前选中的文件夹
|
||||
const handleRenameCurrentFolder = () => {
|
||||
if (!currentFolderId.value || currentFolderId.value === '0') {
|
||||
Message.warning('请先选择一个文件夹');
|
||||
return;
|
||||
}
|
||||
|
||||
// 从folderList中找到当前选中的文件夹
|
||||
const currentFolder = folderList.value.find(folder => folder.id === currentFolderId.value);
|
||||
if (!currentFolder) {
|
||||
Message.error('找不到当前文件夹信息');
|
||||
return;
|
||||
}
|
||||
|
||||
// 构造node对象
|
||||
const node = {
|
||||
key: currentFolder.id,
|
||||
title: currentFolder.name,
|
||||
id: currentFolder.id,
|
||||
name: currentFolder.name
|
||||
};
|
||||
|
||||
handleRenameFolder(node);
|
||||
};
|
||||
|
||||
// 删除当前选中的文件夹
|
||||
const handleDeleteCurrentFolder = () => {
|
||||
if (!currentFolderId.value || currentFolderId.value === '0') {
|
||||
Message.warning('请先选择一个文件夹');
|
||||
return;
|
||||
}
|
||||
|
||||
// 从folderList中找到当前选中的文件夹
|
||||
const currentFolder = folderList.value.find(folder => folder.id === currentFolderId.value);
|
||||
if (!currentFolder) {
|
||||
Message.error('找不到当前文件夹信息');
|
||||
return;
|
||||
}
|
||||
|
||||
// 构造node对象
|
||||
const node = {
|
||||
key: currentFolder.id,
|
||||
title: currentFolder.name,
|
||||
id: currentFolder.id,
|
||||
name: currentFolder.name
|
||||
};
|
||||
|
||||
handleDeleteFolder(node);
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 计算属性:面包屑导航路径
|
||||
const breadcrumbPath = computed(() => {
|
||||
if (!currentFolderId.value || currentFolderId.value === '0') {
|
||||
return ['知识库', '根目录'];
|
||||
}
|
||||
|
||||
const path = ['知识库'];
|
||||
let currentId = currentFolderId.value;
|
||||
|
||||
// 从当前文件夹向上查找父级路径
|
||||
while (currentId && currentId !== '0') {
|
||||
const folder = folderList.value.find(f => f.id === currentId);
|
||||
if (folder) {
|
||||
path.unshift(folder.name);
|
||||
currentId = folder.parentId;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
});
|
||||
|
||||
// 面包屑点击事件处理
|
||||
const handleBreadcrumbClick = (index) => {
|
||||
if (index === 0) {
|
||||
// 点击"知识库",回到根目录
|
||||
currentFolderId.value = '0';
|
||||
loadFiles('0');
|
||||
} else {
|
||||
// 点击其他路径项,需要找到对应的文件夹ID
|
||||
const targetPath = breadcrumbPath.value.slice(0, index + 1);
|
||||
const targetFolderName = targetPath[targetPath.length - 1];
|
||||
|
||||
// 查找对应的文件夹
|
||||
const targetFolder = folderList.value.find(folder => folder.name === targetFolderName);
|
||||
if (targetFolder) {
|
||||
currentFolderId.value = targetFolder.id;
|
||||
loadFiles(targetFolder.id);
|
||||
}
|
||||
}
|
||||
currentFolderId.value = id;
|
||||
};
|
||||
|
||||
// 重命名对话框状态
|
||||
|
@ -901,22 +1169,42 @@ const renameFileForm = reactive({
|
|||
});
|
||||
|
||||
// 文件夹重命名处理函数
|
||||
const handleRenameFolder = (folder, folderId, currentName) => {
|
||||
console.log('handleRenameFolder 被调用:', { folder, folderId, currentName });
|
||||
const handleRenameFolder = (folder) => {
|
||||
console.log('重命名文件夹:', folder);
|
||||
|
||||
if (!folder) {
|
||||
Message.error('文件夹信息不能为空');
|
||||
return;
|
||||
}
|
||||
|
||||
const folderId = folder.key || folder.id;
|
||||
const currentName = folder.title || folder.name;
|
||||
|
||||
// 验证参数
|
||||
if (!folderId) {
|
||||
console.error('folderId 为空');
|
||||
Message.error('文件夹ID不能为空');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!currentName) {
|
||||
console.error('currentName 为空');
|
||||
Message.error('当前文件夹名称不能为空');
|
||||
Message.error('文件夹名称不能为空');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!currentName) {
|
||||
console.error('❌ currentName 为空');
|
||||
console.error('尝试从folder对象获取名称...');
|
||||
const fallbackName = folder?.title || folder?.name;
|
||||
console.error('fallbackName:', fallbackName);
|
||||
|
||||
if (!fallbackName) {
|
||||
Message.error('当前文件夹名称不能为空');
|
||||
return;
|
||||
} else {
|
||||
console.log('✅ 使用fallbackName:', fallbackName);
|
||||
currentName = fallbackName;
|
||||
}
|
||||
}
|
||||
|
||||
// 先显示一个简单的提示,确认函数被调用
|
||||
Message.info('重命名功能被触发');
|
||||
|
||||
|
@ -961,13 +1249,14 @@ const confirmRename = async () => {
|
|||
if (result && result.code === 200) {
|
||||
if (isRoot) {
|
||||
Message.success('根目录重命名成功');
|
||||
currentFolderName.value = newName.trim();
|
||||
// 移除对currentFolderName的设置,现在使用面包屑导航
|
||||
// currentFolderName.value = newName.trim();
|
||||
} else {
|
||||
Message.success('文件夹重命名成功');
|
||||
// 如果重命名的是当前选中的文件夹,更新显示名称
|
||||
if (currentFolderId.value === folderId) {
|
||||
currentFolderName.value = newName.trim();
|
||||
}
|
||||
// 移除对currentFolderName的设置,现在使用面包屑导航
|
||||
// if (currentFolderId.value === folderId) {
|
||||
// currentFolderName.value = newName.trim();
|
||||
// }
|
||||
}
|
||||
|
||||
initData(); // 刷新文件夹列表
|
||||
|
@ -1015,6 +1304,10 @@ const handleFilePageSizeChange = (current, size) => {
|
|||
const refreshData = async () => {
|
||||
refreshing.value = true;
|
||||
try {
|
||||
// 强制清空搜索关键词,确保显示所有文件夹
|
||||
searchKeyword.value = '';
|
||||
currentPage.value = 1;
|
||||
|
||||
await initData();
|
||||
if (currentFolderId.value) {
|
||||
await loadFiles(currentFolderId.value);
|
||||
|
@ -1035,14 +1328,22 @@ const submitFolderForm = async () => {
|
|||
await updateFolderApi(folderForm.id, folderForm.name);
|
||||
Message.success('文件夹重命名成功');
|
||||
} else {
|
||||
await createFolderApi({
|
||||
const result = await createFolderApi({
|
||||
name: folderForm.name,
|
||||
parentId: folderForm.parentId
|
||||
});
|
||||
Message.success('文件夹创建成功');
|
||||
|
||||
// 新建文件夹后,刷新数据并自动选中新建的文件夹
|
||||
await initData();
|
||||
|
||||
// 如果有返回新建文件夹的ID,自动选中它
|
||||
if (result.data && result.data.folderId) {
|
||||
currentFolderId.value = String(result.data.folderId);
|
||||
loadFiles(currentFolderId.value);
|
||||
}
|
||||
}
|
||||
folderDialogVisible.value = false;
|
||||
initData();
|
||||
} catch (error) {
|
||||
console.error('文件夹操作失败:', error);
|
||||
Message.error(folderForm.id ? '重命名失败' : '创建失败');
|
||||
|
@ -1669,7 +1970,7 @@ const showAudioPreview = (url, fileName) => {
|
|||
marginBottom: '20px',
|
||||
color: '#165DFF'
|
||||
}
|
||||
}, '🎵'),
|
||||
}, '<EFBFBD><EFBFBD>'),
|
||||
|
||||
// 音频播放器
|
||||
h('audio', {
|
||||
|
@ -1883,16 +2184,17 @@ const confirmRenameFile = async () => {
|
|||
const handleDeleteFolder = (folder) => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: `确定要删除文件夹「${folder.name}」吗?删除后无法恢复,文件夹内的所有文件也将被删除。`,
|
||||
content: `确定要删除文件夹「${folder.title || folder.name}」吗?删除后无法恢复,文件夹内的所有文件也将被删除。`,
|
||||
onOk: async () => {
|
||||
try {
|
||||
const result = await deleteFolderApi(folder.id);
|
||||
const result = await deleteFolderApi(folder.key || folder.id);
|
||||
if (result.code === 200) {
|
||||
Message.success('文件夹删除成功');
|
||||
// 如果删除的是当前选中的文件夹,切换到根目录
|
||||
if (currentFolderId.value === folder.id) {
|
||||
if (currentFolderId.value === (folder.key || folder.id)) {
|
||||
currentFolderId.value = '0';
|
||||
currentFolderName.value = '根目录';
|
||||
// 移除对currentFolderName的设置,现在使用面包屑导航
|
||||
// currentFolderName.value = '根目录';
|
||||
fileList.value = [];
|
||||
totalFiles.value = 0;
|
||||
}
|
||||
|
@ -2067,6 +2369,8 @@ onMounted(() => {
|
|||
background-color: var(--color-bg-2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 侧边栏样式 */
|
||||
.folder-sidebar {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
|
@ -3208,4 +3512,110 @@ onMounted(() => {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 树形文件夹结构 */
|
||||
.folder-tree-container {
|
||||
padding: 8px;
|
||||
background: var(--color-bg-2);
|
||||
border-radius: 6px;
|
||||
margin: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tree-node-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.folder-name {
|
||||
flex: 1;
|
||||
margin-left: 4px;
|
||||
font-size: 14px;
|
||||
color: var(--color-text-1);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.folder-actions {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.tree-node-content:hover .folder-actions {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.folder-actions .action-btn {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: var(--color-text-3);
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
color: var(--color-primary);
|
||||
background: var(--color-fill-3);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
.folder-tree {
|
||||
:deep(.arco-tree-node-content) {
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-fill-2);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.arco-tree-node-selected .arco-tree-node-content) {
|
||||
background-color: var(--color-primary-light-1);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
:deep(.arco-tree-node-indent) {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
&.collapsed {
|
||||
:deep(.arco-tree-node-content) {
|
||||
padding: 8px 4px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
:deep(.arco-tree-node-title) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:deep(.arco-tree-node-switcher) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:deep(.arco-tree-node-indent) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 面包屑导航样式 */
|
||||
.breadcrumbs {
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
color: var(--color-primary);
|
||||
transition: color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
color: var(--color-primary-light-1);
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Reference in New Issue