修复文件管理器侧边栏显示问题
- 修复目录树API数据格式不匹配:listSSHFiles返回{files, path}对象而非数组
- 修复侧边栏滚动问题:添加thin-scrollbar类保持样式一致性
- 修复Recent和Pin文件点击行为:区分文件和文件夹处理逻辑
- 增强侧边栏高度约束:确保滚动容器正确工作
- 优化TypeScript类型安全:更新listSSHFiles返回类型定义
主要改进:
1. 侧边栏目录树现在正确显示所有文件夹而不是只有根目录
2. Recent和Pinned文件点击时正确打开文件而不是当作文件夹处理
3. 侧边栏支持滚动查看所有内容,滚动条样式与主容器一致
4. API错误处理更加健壮,避免undefined导致的运行时错误
This commit is contained in:
@@ -45,6 +45,7 @@ import {
|
|||||||
addFolderShortcut,
|
addFolderShortcut,
|
||||||
getPinnedFiles
|
getPinnedFiles
|
||||||
} from "@/ui/main-axios.ts";
|
} from "@/ui/main-axios.ts";
|
||||||
|
import type { SidebarItem } from "./FileManagerSidebar";
|
||||||
|
|
||||||
|
|
||||||
interface FileManagerModernProps {
|
interface FileManagerModernProps {
|
||||||
@@ -1309,6 +1310,19 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理侧边栏文件打开
|
||||||
|
async function handleSidebarFileOpen(sidebarItem: SidebarItem) {
|
||||||
|
// 将SidebarItem转换为FileItem格式
|
||||||
|
const file: FileItem = {
|
||||||
|
name: sidebarItem.name,
|
||||||
|
path: sidebarItem.path,
|
||||||
|
type: 'file' // recent和pinned都是文件类型
|
||||||
|
};
|
||||||
|
|
||||||
|
// 调用常规的文件打开处理
|
||||||
|
await handleFileOpen(file);
|
||||||
|
}
|
||||||
|
|
||||||
// 处理文件打开
|
// 处理文件打开
|
||||||
async function handleFileOpen(file: FileItem) {
|
async function handleFileOpen(file: FileItem) {
|
||||||
if (file.type === 'directory') {
|
if (file.type === 'directory') {
|
||||||
@@ -1481,12 +1495,13 @@ function FileManagerContent({ initialHost, onClose }: FileManagerModernProps) {
|
|||||||
{/* 主内容区域 */}
|
{/* 主内容区域 */}
|
||||||
<div className="flex-1 flex" {...dragHandlers}>
|
<div className="flex-1 flex" {...dragHandlers}>
|
||||||
{/* 左侧边栏 */}
|
{/* 左侧边栏 */}
|
||||||
<div className="w-64 flex-shrink-0">
|
<div className="w-64 flex-shrink-0 h-full">
|
||||||
<FileManagerSidebar
|
<FileManagerSidebar
|
||||||
currentHost={currentHost}
|
currentHost={currentHost}
|
||||||
currentPath={currentPath}
|
currentPath={currentPath}
|
||||||
onPathChange={setCurrentPath}
|
onPathChange={setCurrentPath}
|
||||||
onLoadDirectory={loadDirectory}
|
onLoadDirectory={loadDirectory}
|
||||||
|
onFileOpen={handleSidebarFileOpen}
|
||||||
sshSessionId={sshSessionId}
|
sshSessionId={sshSessionId}
|
||||||
refreshTrigger={sidebarRefreshTrigger}
|
refreshTrigger={sidebarRefreshTrigger}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import {
|
|||||||
listSSHFiles
|
listSSHFiles
|
||||||
} from "@/ui/main-axios.ts";
|
} from "@/ui/main-axios.ts";
|
||||||
|
|
||||||
interface SidebarItem {
|
export interface SidebarItem {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
path: string;
|
path: string;
|
||||||
@@ -34,6 +34,7 @@ interface FileManagerSidebarProps {
|
|||||||
currentPath: string;
|
currentPath: string;
|
||||||
onPathChange: (path: string) => void;
|
onPathChange: (path: string) => void;
|
||||||
onLoadDirectory?: (path: string) => void;
|
onLoadDirectory?: (path: string) => void;
|
||||||
|
onFileOpen?: (file: SidebarItem) => void; // 新增:处理文件打开
|
||||||
sshSessionId?: string;
|
sshSessionId?: string;
|
||||||
refreshTrigger?: number; // 用于触发数据刷新
|
refreshTrigger?: number; // 用于触发数据刷新
|
||||||
}
|
}
|
||||||
@@ -43,6 +44,7 @@ export function FileManagerSidebar({
|
|||||||
currentPath,
|
currentPath,
|
||||||
onPathChange,
|
onPathChange,
|
||||||
onLoadDirectory,
|
onLoadDirectory,
|
||||||
|
onFileOpen,
|
||||||
sshSessionId,
|
sshSessionId,
|
||||||
refreshTrigger
|
refreshTrigger
|
||||||
}: FileManagerSidebarProps) {
|
}: FileManagerSidebarProps) {
|
||||||
@@ -51,7 +53,7 @@ export function FileManagerSidebar({
|
|||||||
const [pinnedItems, setPinnedItems] = useState<SidebarItem[]>([]);
|
const [pinnedItems, setPinnedItems] = useState<SidebarItem[]>([]);
|
||||||
const [shortcuts, setShortcuts] = useState<SidebarItem[]>([]);
|
const [shortcuts, setShortcuts] = useState<SidebarItem[]>([]);
|
||||||
const [directoryTree, setDirectoryTree] = useState<SidebarItem[]>([]);
|
const [directoryTree, setDirectoryTree] = useState<SidebarItem[]>([]);
|
||||||
const [expandedFolders, setExpandedFolders] = useState<Set<string>>(new Set(['/']));
|
const [expandedFolders, setExpandedFolders] = useState<Set<string>>(new Set(['root']));
|
||||||
|
|
||||||
// 加载快捷功能数据
|
// 加载快捷功能数据
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -113,7 +115,10 @@ export function FileManagerSidebar({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// 加载根目录
|
// 加载根目录
|
||||||
const rootFiles = await listSSHFiles(sshSessionId, '/');
|
const response = await listSSHFiles(sshSessionId, '/');
|
||||||
|
|
||||||
|
// listSSHFiles 现在总是返回 {files: Array, path: string} 格式
|
||||||
|
const rootFiles = response.files || [];
|
||||||
const rootFolders = rootFiles.filter((item: any) => item.type === 'directory');
|
const rootFolders = rootFiles.filter((item: any) => item.type === 'directory');
|
||||||
|
|
||||||
const rootTreeItems = rootFolders.map((folder: any) => ({
|
const rootTreeItems = rootFolders.map((folder: any) => ({
|
||||||
@@ -154,8 +159,20 @@ export function FileManagerSidebar({
|
|||||||
const handleItemClick = (item: SidebarItem) => {
|
const handleItemClick = (item: SidebarItem) => {
|
||||||
if (item.type === 'folder') {
|
if (item.type === 'folder') {
|
||||||
toggleFolder(item.id, item.path);
|
toggleFolder(item.id, item.path);
|
||||||
}
|
|
||||||
onPathChange(item.path);
|
onPathChange(item.path);
|
||||||
|
} else if (item.type === 'recent' || item.type === 'pinned') {
|
||||||
|
// 对于文件类型,调用文件打开回调
|
||||||
|
if (onFileOpen) {
|
||||||
|
onFileOpen(item);
|
||||||
|
} else {
|
||||||
|
// 如果没有文件打开回调,切换到文件所在目录
|
||||||
|
const directory = item.path.substring(0, item.path.lastIndexOf('/')) || '/';
|
||||||
|
onPathChange(directory);
|
||||||
|
}
|
||||||
|
} else if (item.type === 'shortcut') {
|
||||||
|
// 文件夹快捷方式直接切换到目录
|
||||||
|
onPathChange(item.path);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleFolder = async (folderId: string, folderPath?: string) => {
|
const toggleFolder = async (folderId: string, folderPath?: string) => {
|
||||||
@@ -169,7 +186,10 @@ export function FileManagerSidebar({
|
|||||||
// 按需加载子目录
|
// 按需加载子目录
|
||||||
if (sshSessionId && folderPath && folderPath !== '/') {
|
if (sshSessionId && folderPath && folderPath !== '/') {
|
||||||
try {
|
try {
|
||||||
const subFiles = await listSSHFiles(sshSessionId, folderPath);
|
const subResponse = await listSSHFiles(sshSessionId, folderPath);
|
||||||
|
|
||||||
|
// listSSHFiles 现在总是返回 {files: Array, path: string} 格式
|
||||||
|
const subFiles = subResponse.files || [];
|
||||||
const subFolders = subFiles.filter((item: any) => item.type === 'directory');
|
const subFolders = subFiles.filter((item: any) => item.type === 'directory');
|
||||||
|
|
||||||
const subTreeItems = subFolders.map((folder: any) => ({
|
const subTreeItems = subFolders.map((folder: any) => ({
|
||||||
@@ -271,7 +291,7 @@ export function FileManagerSidebar({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full flex flex-col bg-dark-bg border-r border-dark-border">
|
<div className="h-full flex flex-col bg-dark-bg border-r border-dark-border">
|
||||||
<div className="flex-1 overflow-y-auto p-2 space-y-4">
|
<div className="flex-1 overflow-y-auto thin-scrollbar p-2 space-y-4">
|
||||||
{/* 快捷功能区域 */}
|
{/* 快捷功能区域 */}
|
||||||
{renderSection(t("fileManager.recent"), <Clock className="w-3 h-3" />, recentItems)}
|
{renderSection(t("fileManager.recent"), <Clock className="w-3 h-3" />, recentItems)}
|
||||||
{renderSection(t("fileManager.pinned"), <Star className="w-3 h-3" />, pinnedItems)}
|
{renderSection(t("fileManager.pinned"), <Star className="w-3 h-3" />, pinnedItems)}
|
||||||
|
|||||||
@@ -958,14 +958,15 @@ export async function getSSHStatus(
|
|||||||
export async function listSSHFiles(
|
export async function listSSHFiles(
|
||||||
sessionId: string,
|
sessionId: string,
|
||||||
path: string,
|
path: string,
|
||||||
): Promise<any[]> {
|
): Promise<{files: any[], path: string}> {
|
||||||
try {
|
try {
|
||||||
const response = await fileManagerApi.get("/ssh/listFiles", {
|
const response = await fileManagerApi.get("/ssh/listFiles", {
|
||||||
params: { sessionId, path },
|
params: { sessionId, path },
|
||||||
});
|
});
|
||||||
return response.data || [];
|
return response.data || {files: [], path};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleApiError(error, "list SSH files");
|
handleApiError(error, "list SSH files");
|
||||||
|
return {files: [], path}; // 确保总是返回正确格式
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user