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
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* ConfirmDialog — 基于 Promise 的确认对话框
|
||||
*
|
||||
* 用于替代 window.confirm(),支持异步等待结果
|
||||
*
|
||||
* @example
|
||||
* const { confirm, ConfirmDialogComponent } = useConfirmDialog()
|
||||
* const confirmed = await confirm({ title: '确认删除', message: '确定删除?', danger: true })
|
||||
*
|
||||
* // 在 JSX 中渲染:
|
||||
* <ConfirmDialogComponent />
|
||||
*/
|
||||
|
||||
import { useState, useCallback, useRef } from 'react'
|
||||
import Modal from './Modal'
|
||||
import { AlertTriangle } from 'lucide-react'
|
||||
|
||||
export interface ConfirmOptions {
|
||||
/** 对话框标题 */
|
||||
title: string
|
||||
/** 对话框内容 */
|
||||
message: string
|
||||
/** 是否为危险操作(红色确认按钮) */
|
||||
danger?: boolean
|
||||
/** 确认按钮文本 */
|
||||
confirmText?: string
|
||||
/** 取消按钮文本 */
|
||||
cancelText?: string
|
||||
}
|
||||
|
||||
interface ConfirmState extends ConfirmOptions {
|
||||
/** 是否显示 */
|
||||
isOpen: boolean
|
||||
/** resolve 回调 */
|
||||
resolver: ((value: boolean) => void) | null
|
||||
}
|
||||
|
||||
const initialState: ConfirmState = {
|
||||
isOpen: false,
|
||||
title: '',
|
||||
message: '',
|
||||
danger: false,
|
||||
confirmText: '确认',
|
||||
cancelText: '取消',
|
||||
resolver: null,
|
||||
}
|
||||
|
||||
/**
|
||||
* ConfirmDialog 组件 + Hook
|
||||
*
|
||||
* 返回 confirm 函数和需要渲染的组件
|
||||
*/
|
||||
export function useConfirmDialog() {
|
||||
const [state, setState] = useState<ConfirmState>(initialState)
|
||||
const resolverRef = useRef<((value: boolean) => void) | null>(null)
|
||||
|
||||
const confirm = useCallback((options: ConfirmOptions): Promise<boolean> => {
|
||||
return new Promise<boolean>((resolve) => {
|
||||
resolverRef.current = resolve
|
||||
setState({
|
||||
...options,
|
||||
isOpen: true,
|
||||
danger: options.danger ?? false,
|
||||
confirmText: options.confirmText ?? '确认',
|
||||
cancelText: options.cancelText ?? '取消',
|
||||
resolver: null,
|
||||
})
|
||||
})
|
||||
}, [])
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
setState((prev) => ({ ...prev, isOpen: false }))
|
||||
resolverRef.current?.(false)
|
||||
resolverRef.current = null
|
||||
}, [])
|
||||
|
||||
const handleConfirm = useCallback(() => {
|
||||
setState((prev) => ({ ...prev, isOpen: false }))
|
||||
resolverRef.current?.(true)
|
||||
resolverRef.current = null
|
||||
}, [])
|
||||
|
||||
const ConfirmDialogComponent = (
|
||||
<Modal
|
||||
isOpen={state.isOpen}
|
||||
title={state.title}
|
||||
onConfirm={handleConfirm}
|
||||
onClose={handleClose}
|
||||
confirmText={state.confirmText}
|
||||
cancelText={state.cancelText}
|
||||
isDanger={state.danger}
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
{state.danger && (
|
||||
<AlertTriangle className="w-5 h-5 text-red-400 mt-0.5 flex-shrink-0" />
|
||||
)}
|
||||
<p className="text-gray-300">{state.message}</p>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
|
||||
return { confirm, ConfirmDialogComponent }
|
||||
}
|
||||
|
||||
export default useConfirmDialog
|
||||
Reference in new issue
Block a user