import React, { useState, useEffect, useCallback } from 'react' import { Folder, FileText, ChevronLeft, RefreshCw, HardDrive } from 'lucide-react' import { clsx } from 'clsx' import { fetchRemoteFiles, fetchRemoteDrives, type RemoteFileItem } from '../../api' interface RemoteFilePanelProps { serverHost: string port: number password?: string selectedFile: RemoteFileItem | null onSelect: (file: RemoteFileItem | null) => void onDownload: () => void onPathChange?: (path: string) => void disabled?: boolean } export const RemoteFilePanel: React.FC = ({ serverHost, port, password, selectedFile, onSelect, onDownload, onPathChange, disabled, }) => { const [currentPath, setCurrentPath] = useState('') const [files, setFiles] = useState([]) const [loading, setLoading] = useState(false) const [pathHistory, setPathHistory] = useState(['']) const [showDrives, setShowDrives] = useState(true) const loadDrives = useCallback(async () => { setLoading(true) try { const drives = await fetchRemoteDrives(serverHost, port, password) setFiles(drives) setShowDrives(true) } catch (err) { console.error('Failed to load remote drives:', err) setFiles([]) } finally { setLoading(false) } }, [serverHost, port, password]) const loadFiles = useCallback(async (path: string) => { setLoading(true) try { const items = await fetchRemoteFiles(serverHost, port, path, password, true) setFiles(items) setShowDrives(false) } catch (err) { console.error('Failed to load remote files:', err) setFiles([]) } finally { setLoading(false) } }, [serverHost, port, password]) useEffect(() => { loadDrives() }, [loadDrives]) const handleGoBack = () => { if (showDrives) { return } if (pathHistory.length > 1) { const newHistory = [...pathHistory] newHistory.pop() const prevPath = newHistory[newHistory.length - 1] setPathHistory(newHistory) setCurrentPath(prevPath) onSelect(null) onPathChange?.(prevPath) } else { loadDrives() } } const handleGoInto = (file: RemoteFileItem) => { if (showDrives) { const newPath = file.path + '\\' setPathHistory(['', newPath]) setCurrentPath(newPath) loadFiles(newPath) onPathChange?.(newPath) } else { const newPath = currentPath ? `${currentPath}\\${file.name}` : file.name setPathHistory([...pathHistory, newPath]) setCurrentPath(newPath) loadFiles(newPath) onPathChange?.(newPath) } onSelect(null) } const handleRefresh = () => { if (showDrives) { loadDrives() } else { loadFiles(currentPath) } } const handleGoToRoot = () => { setPathHistory(['']) setCurrentPath('') loadDrives() onSelect(null) onPathChange?.('') } const getDisplayPath = () => { if (showDrives) return '选择驱动器' if (!currentPath) return '远程文件' return currentPath } return (
{getDisplayPath()}
{ if ((e.target as HTMLElement).classList.contains('space-y-1') || (e.target as HTMLElement).classList.contains('p-2')) { onSelect(null) } }}> {loading ? (
加载中...
) : files.length === 0 ? (
空文件夹
) : (
{files.map((file) => { const isDrive = showDrives && file.name.length === 2 && file.name.endsWith(':') return (
onSelect(selectedFile?.path === file.path ? null : file)} onDoubleClick={() => (file.type === 'dir' || isDrive) && handleGoInto(file)} className={clsx( 'flex items-center gap-2 px-2 py-1.5 rounded-md cursor-pointer transition-colors text-sm', selectedFile?.path === file.path ? 'bg-gray-100 text-gray-800 dark:bg-gray-700/60 dark:text-gray-200' : 'hover:bg-gray-100 dark:hover:bg-gray-700/50 text-gray-700 dark:text-gray-200' )} > {isDrive ? ( ) : file.type === 'dir' ? ( ) : ( )} {file.name}
) })}
)}
) }