feat: v0.3.0 版本 - SSH终端、SFTP、端口转发功能实现
核心功能: - SSH终端: 密码/密钥认证、XTerm.js终端、多主题配色、IP高亮 - SFTP管理: 双栏文件管理、上传下载、传输队列 - 端口转发: 本地/远程转发、SOCKS5动态代理、规则管理 架构改进: - 拆分大型组件 (SFTPView/TabBar/Sidebar) - 添加统一日志和错误处理 - 修复React严格模式下的终端黑屏问题 UI更新: - 新增端口转发视图 - 更新UI预览文档 - 左侧栏右键菜单支持SFTP快捷打开
This commit is contained in:
1 parent
cf1c054381
commit
484d850abd
105 files changed
+25330
-64
No files matched your search
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* 模态框组件
|
||||
*
|
||||
* 统一的对话框 UI
|
||||
*/
|
||||
|
||||
import { type ReactNode, useEffect } from 'react'
|
||||
import { X } from 'lucide-react'
|
||||
import Button from './Button'
|
||||
|
||||
interface ModalProps {
|
||||
/** 是否显示 */
|
||||
isOpen: boolean
|
||||
/** 标题 */
|
||||
title: string
|
||||
/** 内容 */
|
||||
children: ReactNode
|
||||
/** 确认按钮文本 */
|
||||
confirmText?: string
|
||||
/** 取消按钮文本 */
|
||||
cancelText?: string
|
||||
/** 确认回调 */
|
||||
onConfirm?: () => void
|
||||
/** 关闭回调 */
|
||||
onClose: () => void
|
||||
/** 是否显示确认按钮 */
|
||||
showConfirm?: boolean
|
||||
/** 是否危险操作 */
|
||||
isDanger?: boolean
|
||||
/** 加载状态 */
|
||||
loading?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* 模态框组件
|
||||
*
|
||||
* @example
|
||||
* <Modal
|
||||
* isOpen={isOpen}
|
||||
* title="确认删除"
|
||||
* onConfirm={handleDelete}
|
||||
* onClose={() => setIsOpen(false)}
|
||||
* isDanger
|
||||
* >
|
||||
* 确定要删除此会话吗?
|
||||
* </Modal>
|
||||
*/
|
||||
export default function Modal({
|
||||
isOpen,
|
||||
title,
|
||||
children,
|
||||
confirmText = '确认',
|
||||
cancelText = '取消',
|
||||
onConfirm,
|
||||
onClose,
|
||||
showConfirm = true,
|
||||
isDanger = false,
|
||||
loading = false,
|
||||
}: ModalProps) {
|
||||
// ESC 键关闭
|
||||
useEffect(() => {
|
||||
const handleEsc = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape' && isOpen) {
|
||||
onClose()
|
||||
}
|
||||
}
|
||||
window.addEventListener('keydown', handleEsc)
|
||||
return () => window.removeEventListener('keydown', handleEsc)
|
||||
}, [isOpen, onClose])
|
||||
|
||||
if (!isOpen) return null
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
|
||||
{/* 遮罩 */}
|
||||
<div
|
||||
className="absolute inset-0 bg-black/60 backdrop-blur-sm"
|
||||
onClick={onClose}
|
||||
/>
|
||||
|
||||
{/* 内容 */}
|
||||
<div className="relative bg-dark-800 rounded-xl border border-dark-700 shadow-2xl w-full max-w-md">
|
||||
{/* 头部 */}
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-dark-700">
|
||||
<h3 className="text-lg font-semibold text-gray-200">{title}</h3>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="p-1 hover:bg-dark-700 rounded-lg transition-colors"
|
||||
>
|
||||
<X className="w-5 h-5 text-gray-400" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 内容 */}
|
||||
<div className="px-6 py-4">
|
||||
{children}
|
||||
</div>
|
||||
|
||||
{/* 底部按钮 */}
|
||||
<div className="flex justify-end gap-3 px-6 py-4 border-t border-dark-700">
|
||||
<Button variant="ghost" onClick={onClose}>
|
||||
{cancelText}
|
||||
</Button>
|
||||
{showConfirm && (
|
||||
<Button
|
||||
variant={isDanger ? 'danger' : 'primary'}
|
||||
onClick={onConfirm}
|
||||
loading={loading}
|
||||
>
|
||||
{confirmText}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user