重写ssh前端组件,通用化

This commit is contained in:
dengqichen 2025-12-07 21:12:15 +08:00
parent 460f237211
commit e5bd51b4b5
5 changed files with 69 additions and 69 deletions

View File

@ -1 +1,3 @@
export { FileManager } from './FileManager'; export { FileManager } from './FileManager';
export { FileManagerWindowManager } from './FileManagerWindowManager';
export type { FileManagerProps } from './types';

View File

@ -7,7 +7,6 @@ import { Input } from '@/components/ui/input';
import 'xterm/css/xterm.css'; import 'xterm/css/xterm.css';
import styles from './index.module.less'; import styles from './index.module.less';
import { TerminalToolbar } from './TerminalToolbar'; import { TerminalToolbar } from './TerminalToolbar';
import { FileManager } from '@/components/FileManager';
import type { TerminalProps, TerminalToolbarConfig } from './types'; import type { TerminalProps, TerminalToolbarConfig } from './types';
import { TERMINAL_THEMES, getThemeByName } from './themes'; import { TERMINAL_THEMES, getThemeByName } from './themes';
import { Loader2, XCircle, ChevronUp, ChevronDown, X, WifiOff } from 'lucide-react'; import { Loader2, XCircle, ChevronUp, ChevronDown, X, WifiOff } from 'lucide-react';
@ -44,9 +43,6 @@ export const Terminal: React.FC<TerminalProps> = ({
const [connectionStatus, setConnectionStatus] = useState<ConnectionStatus>('disconnected'); // 初始状态,会被实例状态覆盖 const [connectionStatus, setConnectionStatus] = useState<ConnectionStatus>('disconnected'); // 初始状态,会被实例状态覆盖
const [errorMessage, setErrorMessage] = useState<string>(''); const [errorMessage, setErrorMessage] = useState<string>('');
const [connectedTime, setConnectedTime] = useState<Date | null>(null); const [connectedTime, setConnectedTime] = useState<Date | null>(null);
const [showFileManager, setShowFileManager] = useState(false);
const [fileManagerMinimized, setFileManagerMinimized] = useState(false);
const [fileManagerActive, setFileManagerActive] = useState(false);
// 默认工具栏配置 // 默认工具栏配置
const toolbarConfig: TerminalToolbarConfig = { const toolbarConfig: TerminalToolbarConfig = {
@ -278,40 +274,24 @@ export const Terminal: React.FC<TerminalProps> = ({
} }
}, []); }, []);
// 打开文件管理器 // 打开文件管理器(调用全局方法)
const handleFileManager = useCallback(() => { const handleFileManager = useCallback(() => {
if (connection.type !== 'ssh') { if (connection.type !== 'ssh') {
message.warning('文件管理仅支持SSH连接'); message.warning('文件管理仅支持SSH连接');
return; return;
} }
setShowFileManager(true); if (!connection.serverId || !connection.serverName) {
setFileManagerMinimized(false); message.error('缺少服务器信息');
setFileManagerActive(true); return;
}, [connection.type]); }
// 调用全局方法打开文件管理器
// 最小化文件管理器 const openFileManager = (window as any).openFileManager;
const handleFileManagerMinimize = useCallback(() => { if (openFileManager) {
setFileManagerMinimized(true); openFileManager(Number(connection.serverId), connection.serverName);
setFileManagerActive(false); } else {
}, []); message.error('文件管理器未初始化');
}
// 恢复文件管理器 }, [connection.type, connection.serverId, connection.serverName]);
const handleFileManagerRestore = useCallback(() => {
setFileManagerMinimized(false);
setFileManagerActive(true);
}, []);
// 激活文件管理器
const handleFileManagerFocus = useCallback(() => {
setFileManagerActive(true);
}, []);
// 关闭文件管理器
const handleFileManagerClose = useCallback(() => {
setShowFileManager(false);
setFileManagerMinimized(false);
setFileManagerActive(false);
}, []);
return ( return (
<div ref={wrapperRef} className={styles.terminalWrapper}> <div ref={wrapperRef} className={styles.terminalWrapper}>
@ -454,31 +434,7 @@ export const Terminal: React.FC<TerminalProps> = ({
)} )}
</div> </div>
{/* 最小化窗口状态栏 */} {/* 文件管理器已移至独立的窗口管理器 */}
{connection.type === 'ssh' && showFileManager && fileManagerMinimized && (
<div className="fixed bottom-4 left-4 z-[9999]">
<Button
variant="outline"
className="shadow-lg"
onClick={handleFileManagerRestore}
>
📁 - {`服务器 ${connection.serverId}`}
</Button>
</div>
)}
{/* 文件管理器 */}
{connection.type === 'ssh' && showFileManager && connection.serverId && !fileManagerMinimized && (
<FileManager
serverId={Number(connection.serverId)}
serverName={`服务器 ${connection.serverId}`}
open={showFileManager}
onClose={handleFileManagerClose}
onMinimize={handleFileManagerMinimize}
isActive={fileManagerActive}
onFocus={handleFileManagerFocus}
/>
)}
</div> </div>
); );
}; };

View File

@ -342,18 +342,42 @@ export const ServerCard: React.FC<ServerCardProps> = ({ server, onTest, onEdit,
{ skeleton: true, skeletonHeight: 'h-4' } { skeleton: true, skeletonHeight: 'h-4' }
)} )}
</div> </div>
<div className="flex flex-col items-center gap-1"> <Tooltip>
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-primary/10 text-primary"> <TooltipTrigger asChild>
<HardDrive className="h-5 w-5" /> <div className="flex flex-col items-center gap-1 cursor-help">
</div> <div className="flex h-10 w-10 items-center justify-center rounded-full bg-primary/10 text-primary">
{renderValue( <HardDrive className="h-5 w-5" />
server.diskSize ? ( </div>
<span className="text-xs font-medium text-foreground">{server.diskSize}GB</span> {renderValue(
) : null, server.diskSize ? (
'w-10', <span className="text-xs font-medium text-foreground">{server.diskSize}GB</span>
{ skeleton: true, skeletonHeight: 'h-4' } ) : null,
'w-10',
{ skeleton: true, skeletonHeight: 'h-4' }
)}
</div>
</TooltipTrigger>
{server.diskInfo && server.diskInfo.length > 0 && (
<TooltipContent className="max-w-xs">
<div className="space-y-2">
<div className="font-semibold text-xs border-b pb-1"></div>
{server.diskInfo.map((disk, index) => (
<div key={index} className="flex justify-between gap-4 text-xs">
<div className="flex items-center gap-2">
<span className="font-mono text-blue-400">{disk.mountPoint}</span>
<span className="text-muted-foreground">({disk.fileSystem})</span>
</div>
<span className="font-medium">{disk.totalSize}GB</span>
</div>
))}
<div className="flex justify-between gap-4 text-xs font-semibold border-t pt-1">
<span></span>
<span>{server.diskSize}GB</span>
</div>
</div>
</TooltipContent>
)} )}
</div> </Tooltip>
</div> </div>
</div> </div>
)} )}

View File

@ -41,6 +41,7 @@ import { ServerEditDialog } from './components/ServerEditDialog';
import { ServerCard } from './components/ServerCard'; import { ServerCard } from './components/ServerCard';
import { ServerTable } from './components/ServerTable'; import { ServerTable } from './components/ServerTable';
import { SSHWindowManager } from './components/SSHWindowManager'; import { SSHWindowManager } from './components/SSHWindowManager';
import { FileManagerWindowManager } from '@/components/FileManager';
const ServerList: React.FC = () => { const ServerList: React.FC = () => {
const { toast } = useToast(); const { toast } = useToast();
@ -611,6 +612,9 @@ const ServerList: React.FC = () => {
{/* SSH多窗口管理器 */} {/* SSH多窗口管理器 */}
<SSHWindowManager /> <SSHWindowManager />
{/* 文件管理器窗口管理器 */}
<FileManagerWindowManager />
</TooltipProvider> </TooltipProvider>
); );
}; };

View File

@ -92,6 +92,18 @@ export interface ServerCategoryRequest {
// ==================== 服务器 ==================== // ==================== 服务器 ====================
/**
*
*/
export interface DiskInfo {
/** 挂载点 */
mountPoint: string;
/** 文件系统类型 */
fileSystem: string;
/** 总大小(GB) */
totalSize: number;
}
/** /**
* *
*/ */
@ -130,6 +142,8 @@ export interface ServerResponse extends BaseResponse {
memorySize?: number; memorySize?: number;
/** 磁盘大小(GB) */ /** 磁盘大小(GB) */
diskSize?: number; diskSize?: number;
/** 磁盘详细信息 */
diskInfo?: DiskInfo[];
/** 标签JSON字符串 */ /** 标签JSON字符串 */
tags?: string; tags?: string;
/** 最后连接时间 */ /** 最后连接时间 */