feat(ui): 添加删除操作延迟确认功能

增强用户体验,防止误删除操作:

1. MessageModal 组件增强
   - 添加 confirmDelay 属性(秒)
   - 确认按钮倒计时功能
   - 倒计时期间禁用确认按钮
   - 按钮显示剩余秒数 (例如: "删除 (4s)")

2. 删除操作应用延迟确认(4秒)
   -  文档类型删除 (document-types._index.tsx)
   -  文档删除和批量删除 (documents.list.tsx)
   -  入口模块删除 (entry-modules._index.tsx)
   -  提示词删除 (prompts._index.tsx)
   -  规则组删除 (rule-groups._index.tsx)

技术实现:
- 使用 useEffect + setInterval 实现倒计时
- 倒计时结束自动清理定时器
- 按钮禁用状态控制(disabled + opacity + cursor)

用户体验提升:
- 防止误操作:4秒思考时间
- 视觉反馈:倒计时提示
- 操作可逆:倒计时期间可取消

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-25 18:17:52 +08:00
parent be529d2f2a
commit 7c47b11ec7
7 changed files with 192 additions and 125 deletions
+35 -9
View File
@@ -40,6 +40,8 @@ interface MessageModalProps {
customIcon?: React.ReactNode;
// 自定义内容
children?: React.ReactNode;
// 确认按钮延迟时间(秒)- 用于危险操作(如删除)
confirmDelay?: number;
}
// 默认自动关闭延迟
@@ -63,10 +65,12 @@ export function MessageModal({
cancelText = '取消',
showCloseButton = true,
customIcon,
children
children,
confirmDelay = 0
}: MessageModalProps) {
const [isClosing, setIsClosing] = useState(false);
const [portalElement, setPortalElement] = useState<HTMLElement | null>(null);
const [remainingSeconds, setRemainingSeconds] = useState(confirmDelay);
// 在客户端渲染时获取 portal 容器
useEffect(() => {
@@ -108,6 +112,23 @@ export function MessageModal({
}
}, [isOpen, autoClose, autoCloseDelay, handleClose]);
// 确认延迟倒计时
useEffect(() => {
if (isOpen && confirmDelay > 0) {
setRemainingSeconds(confirmDelay);
const timer = setInterval(() => {
setRemainingSeconds((prev) => {
if (prev <= 1) {
clearInterval(timer);
return 0;
}
return prev - 1;
});
}, 1000);
return () => clearInterval(timer);
}
}, [isOpen, confirmDelay]);
// 关闭按钮键盘交互
const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
if (e.key === 'Escape') {
@@ -194,15 +215,20 @@ export function MessageModal({
<div className="message-modal-actions">
{onConfirm && (
<>
<button
className="message-modal-button primary"
<button
className="message-modal-button primary"
onClick={handleConfirm}
disabled={remainingSeconds > 0}
style={{
opacity: remainingSeconds > 0 ? 0.5 : 1,
cursor: remainingSeconds > 0 ? 'not-allowed' : 'pointer'
}}
>
{confirmText}
{remainingSeconds > 0 ? `${confirmText} (${remainingSeconds}s)` : confirmText}
</button>
{cancelText && (
<button
className="message-modal-button"
<button
className="message-modal-button"
onClick={handleClose}
>
{cancelText}
@@ -210,10 +236,10 @@ export function MessageModal({
)}
</>
)}
{!onConfirm && (
<button
className="message-modal-button primary"
<button
className="message-modal-button primary"
onClick={handleClose}
>
{confirmText}
+1 -1
View File
@@ -25,7 +25,7 @@ export { UploadArea } from './UploadArea';
// 反馈组件
export { Alert } from './Alert';
export { MessageModal } from './MessageModal';
export { MessageModal, messageService } from './MessageModal';
export { LoadingBar } from './LoadingBar';
export { RouteChangeLoader } from './RouteChangeLoader';
export { FileProgress } from './FileProgress';