重写ssh前端组件,通用化
This commit is contained in:
parent
460f237211
commit
e5bd51b4b5
@ -1 +1,3 @@
|
|||||||
export { FileManager } from './FileManager';
|
export { FileManager } from './FileManager';
|
||||||
|
export { FileManagerWindowManager } from './FileManagerWindowManager';
|
||||||
|
export type { FileManagerProps } from './types';
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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;
|
||||||
/** 最后连接时间 */
|
/** 最后连接时间 */
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user