feat(v1.1): Toast通知系统 + 自定义对话框 + 终端搜索 + SFTP真实实现 + 标签持久化
任务7: Toast通知系统 — ToastProvider+4类型(success/error/warning/info)+右上角堆叠动画 任务8: 替换alert/confirm/prompt — ConfirmDialog+PromptDialog+useToast替换7处原生对话框 任务9: 终端搜索 — xterm SearchAddon Ctrl+F搜索栏(上一个/下一个/关闭) 任务10: SFTP上传/删除 — useSFTP.ts upload/delete方法实现 任务11: 会话恢复 — tabStore持久化到localStorage,启动恢复tabs 14 files changed, 1218 insertions(+), 241 deletions(-)
This commit is contained in:
1 parent
53274e2291
commit
d344900118
14 files changed
+1224
-247
No files matched your search
@@ -8,6 +8,7 @@ import { useState, useEffect } from 'react'
|
||||
import { useTabStore } from '../stores/tabStore'
|
||||
import { useConnectionPool } from '../stores/connection'
|
||||
import { ArrowRightLeft, Globe, Server, Plus, Trash2, Play, Square } from 'lucide-react'
|
||||
import { useConfirmDialog } from './common/ConfirmDialog'
|
||||
|
||||
interface PortForwardViewProps {
|
||||
tabId: string
|
||||
@@ -25,10 +26,12 @@ interface ForwardRule {
|
||||
}
|
||||
|
||||
export default function PortForwardView({ tabId, isActive: _isActive = true }: PortForwardViewProps) {
|
||||
const { tabs } = useTabStore()
|
||||
const { tabs, updateTab } = useTabStore()
|
||||
const pool = useConnectionPool()
|
||||
const { confirm, ConfirmDialogComponent } = useConfirmDialog()
|
||||
|
||||
const currentTab = tabs.find(t => t.id === tabId)
|
||||
const needsReconnect = currentTab?.needsReconnect
|
||||
const connectionId = currentTab?.connectionInstance?.connectionId
|
||||
const connection = connectionId ? pool.getConnection(connectionId) : null
|
||||
|
||||
@@ -146,14 +149,46 @@ export default function PortForwardView({ tabId, isActive: _isActive = true }: P
|
||||
}
|
||||
|
||||
// Delete rule
|
||||
const deleteRule = (ruleId: string) => {
|
||||
if (confirm('确定删除此转发规则?')) {
|
||||
const deleteRule = async (ruleId: string) => {
|
||||
const confirmed = await confirm({
|
||||
title: '确认删除',
|
||||
message: '确定删除此转发规则?',
|
||||
danger: true,
|
||||
confirmText: '删除',
|
||||
})
|
||||
if (confirmed) {
|
||||
saveRules(rules.filter(r => r.id !== ruleId))
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="h-full flex flex-col bg-dark-900">
|
||||
{/* 会话恢复 - 需要重新连接覆盖层 */}
|
||||
{needsReconnect && currentTab?.sessionId && (
|
||||
<div className="absolute inset-0 bg-dark-900/90 flex items-center justify-center z-50">
|
||||
<div className="text-center">
|
||||
<div className="w-16 h-16 bg-accent-blue/20 rounded-2xl flex items-center justify-center mx-auto mb-4">
|
||||
<ArrowRightLeft className="w-8 h-8 text-accent-blue" />
|
||||
</div>
|
||||
<div className="text-gray-200 mb-2 text-lg">会话已恢复</div>
|
||||
<div className="text-gray-400 text-sm mb-6">
|
||||
上次关闭时的端口转发标签已恢复<br />
|
||||
需要重新建立 SSH 连接才能使用
|
||||
</div>
|
||||
<button
|
||||
onClick={() => {
|
||||
updateTab(tabId, { needsReconnect: false })
|
||||
// 刷新页面或触发连接逻辑
|
||||
}}
|
||||
className="px-6 py-2.5 bg-accent-blue hover:bg-blue-500 text-white rounded-lg transition-colors"
|
||||
>
|
||||
重新连接
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-dark-700">
|
||||
<div className="flex items-center gap-3">
|
||||
@@ -383,5 +418,7 @@ export default function PortForwardView({ tabId, isActive: _isActive = true }: P
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{ConfirmDialogComponent}
|
||||
</>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user