# 删除操作延迟确认功能实施文档 > **实施时间**: 2025-11-25 > **功能描述**: 为所有删除操作添加4秒延迟确认功能,防止误删除操作 --- ## 📋 目录 - [功能概述](#功能概述) - [技术实现](#技术实现) - [已更新的文件](#已更新的文件) - [使用示例](#使用示例) - [测试验证](#测试验证) --- ## 功能概述 ### 需求背景 为了防止用户误操作导致数据被删除,所有删除操作都需要: 1. 显示确认弹窗提示 2. 确认按钮在4秒倒计时结束后才能点击 3. 倒计时期间按钮显示剩余秒数 ### 核心功能 - **延迟确认**: 确认按钮在4秒倒计时后才可点击 - **倒计时显示**: 按钮文本显示 "删除 (4s)" → "删除 (3s)" → ... → "删除" - **视觉反馈**: 倒计时期间按钮呈半透明状态且鼠标样式为禁用 - **统一体验**: 所有删除操作使用相同的确认流程 --- ## 技术实现 ### 1. MessageModal 组件增强 **文件**: `app/components/ui/MessageModal.tsx` #### 新增 Props ```typescript interface MessageModalProps { // ... 现有属性 // 确认按钮延迟时间(秒)- 用于危险操作(如删除) confirmDelay?: number; } ``` #### 状态管理 ```typescript const [remainingSeconds, setRemainingSeconds] = useState(confirmDelay); ``` #### 倒计时逻辑 ```typescript 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]); ``` #### 按钮渲染 ```typescript ``` --- ## 已更新的文件 ### 1. 文档管理模块 (documents.list.tsx) **文件路径**: `app/routes/documents.list.tsx` #### 更新位置 1: 单个文档删除 (Line 580-596) ```typescript messageService.show({ title: "确认删除", message: `确定要删除文档"${name}"吗?`, type: "warning", confirmText: "删除", cancelText: "取消", confirmDelay: 4, // ✅ 新增 onConfirm: () => { // 删除逻辑 } }); ``` #### 更新位置 2: 批量删除文档 (Line 617-642) ```typescript messageService.show({ title: "确认批量删除", message: `确认删除选中的 ${selectedRowKeys.length} 个文档?`, type: "warning", confirmText: "删除", cancelText: "取消", confirmDelay: 4, // ✅ 新增 onConfirm: () => { // 批量删除逻辑 } }); ``` --- ### 2. 评查点管理模块 (rules.list.tsx) **文件路径**: `app/routes/rules.list.tsx` #### 更新位置 1: 单个评查点删除 (Line 526-542) ```typescript messageService.show({ title: "确认删除", message: `确认删除评查点【${rule.name}】吗?`, type: "warning", confirmText: "删除", cancelText: "取消", confirmDelay: 4, // ✅ 新增 onConfirm: () => { // 删除逻辑 } }); ``` #### 更新位置 2: 批量删除评查点 (Line 603-634) ```typescript messageService.show({ title: "确认批量删除", message: `确定要删除选中的 ${selectedIds.length} 个评查点吗?此操作不可恢复。`, type: "warning", confirmText: "删除", cancelText: "取消", confirmDelay: 4, // ✅ 新增 onConfirm: async () => { // 批量删除逻辑 } }); ``` --- ### 3. 评查点分组管理模块 (rule-groups._index.tsx) **文件路径**: `app/routes/rule-groups._index.tsx` #### 导入更新 ```typescript // 原代码 import { toastService } from "~/components/ui"; // 新代码 import { toastService, messageService } from "~/components/ui"; ``` #### 更新位置 1: 单个分组删除 (Line 233-275) **原代码** (使用 `confirm()`): ```typescript if (confirm("确定要删除该分组吗?此操作将同时删除该分组下的所有评查点,且不可恢复。")) { // 删除逻辑 } ``` **新代码** (使用 `messageService`): ```typescript messageService.show({ title: "确认删除", message: "确定要删除该分组吗?此操作将同时删除该分组下的所有评查点,且不可恢复。", type: "warning", confirmText: "删除", cancelText: "取消", confirmDelay: 4, // ✅ 新增 onConfirm: async () => { // 删除逻辑 } }); ``` #### 更新位置 2: 批量删除分组 (Line 307-328) **原代码** (使用 `confirm()`): ```typescript if (!confirm(`确定要删除选中的 ${selectedIds.length} 个分组吗?此操作不可恢复。`)) { return; } ``` **新代码** (使用 `messageService`): ```typescript messageService.show({ title: "确认批量删除", message: `确定要删除选中的 ${selectedIds.length} 个分组吗?此操作不可恢复。`, type: "warning", confirmText: "删除", cancelText: "取消", confirmDelay: 4, // ✅ 新增 onConfirm: async () => { // 删除逻辑 } }); ``` --- ### 4. 提示词模板管理模块 (prompts._index.tsx) **文件路径**: `app/routes/prompts._index.tsx` #### 导入更新 ```typescript // 原代码 import { toastService } from "~/components/ui"; // 新代码 import { toastService, messageService } from "~/components/ui"; ``` #### 更新位置: 删除模板 (Line 220-234) **原代码** (使用 `confirm()`): ```typescript if (confirm('确定要删除该模板吗?删除后无法恢复。')) { const formData = new FormData(); formData.append('id', id); formData.append('intent', 'delete'); fetcher.submit(formData, { method: 'post' }); } ``` **新代码** (使用 `messageService`): ```typescript messageService.show({ title: "确认删除", message: "确定要删除该模板吗?删除后无法恢复。", type: "warning", confirmText: "删除", cancelText: "取消", confirmDelay: 4, // ✅ 新增 onConfirm: () => { const formData = new FormData(); formData.append('id', id); formData.append('intent', 'delete'); fetcher.submit(formData, { method: 'post' }); } }); ``` --- ### 5. 入口模块管理模块 (entry-modules._index.tsx) **文件路径**: `app/routes/entry-modules._index.tsx` #### 导入更新 ```typescript // 原代码 import { toastService } from "~/components/ui/Toast"; // 新代码 import { toastService } from "~/components/ui/Toast"; import { messageService } from "~/components/ui/MessageModal"; ``` #### 更新位置: 删除入口模块 (Line 215-250) **原代码** (使用 `confirm()`): ```typescript if (confirm('确定要删除该入口模块吗?此操作不可撤销。')) { setIsDeleting(true); // 删除逻辑 } ``` **新代码** (使用 `messageService`): ```typescript messageService.show({ title: "确认删除", message: "确定要删除该入口模块吗?此操作不可撤销。", type: "warning", confirmText: "删除", cancelText: "取消", confirmDelay: 4, // ✅ 新增 onConfirm: async () => { setIsDeleting(true); // 删除逻辑 } }); ``` --- ### 6. 文档类型管理模块 (document-types._index.tsx) **文件路径**: `app/routes/document-types._index.tsx` #### 导入更新 ```typescript // 原代码 import { toastService } from "~/components/ui/Toast"; // 新代码 import { toastService } from "~/components/ui/Toast"; import { messageService } from "~/components/ui/MessageModal"; ``` #### 更新位置: 删除文档类型 (Line 204-239) **原代码** (使用 `confirm()` 和 `alert()`): ```typescript if (confirm('确定要删除该文档类型吗?此操作不会影响关联的评查点分组,但可能会影响使用该类型的文档评查。')) { setIsDeleting(true); // 删除逻辑 if (result.success) { alert('删除成功!'); } else { alert(`删除失败: ${result.error || '未知错误'}`); } } ``` **新代码** (使用 `messageService` 和 `toastService`): ```typescript messageService.show({ title: "确认删除", message: "确定要删除该文档类型吗?此操作不会影响关联的评查点分组,但可能会影响使用该类型的文档评查。", type: "warning", confirmText: "删除", cancelText: "取消", confirmDelay: 4, // ✅ 新增 onConfirm: async () => { setIsDeleting(true); // 删除逻辑 if (result.success) { toastService.success('删除成功!'); } else { toastService.error(`删除失败: ${result.error || '未知错误'}`); } } }); ``` --- ## 使用示例 ### 基本用法 ```typescript import { messageService } from "~/components/ui/MessageModal"; const handleDelete = (id: string, name: string) => { messageService.show({ title: "确认删除", message: `确定要删除"${name}"吗?`, type: "warning", confirmText: "删除", cancelText: "取消", confirmDelay: 4, // 4秒延迟 onConfirm: () => { // 执行删除操作 deleteItem(id); } }); }; ``` ### 批量删除示例 ```typescript const handleBatchDelete = () => { if (selectedIds.length === 0) { toastService.error('请至少选择一项'); return; } messageService.show({ title: "确认批量删除", message: `确定要删除选中的 ${selectedIds.length} 项吗?此操作不可恢复。`, type: "warning", confirmText: "删除", cancelText: "取消", confirmDelay: 4, onConfirm: async () => { // 执行批量删除 await batchDeleteItems(selectedIds); } }); }; ``` --- ## 测试验证 ### 测试清单 #### 1. 单个删除操作测试 - [ ] 文档删除 (`/documents`) - [ ] 评查点删除 (`/rules`) - [ ] 评查点分组删除 (`/rule-groups`) - [ ] 提示词模板删除 (`/prompts`) - [ ] 入口模块删除 (`/entry-modules`) - [ ] 文档类型删除 (`/document-types`) #### 2. 批量删除操作测试 - [ ] 批量删除文档 - [ ] 批量删除评查点 - [ ] 批量删除评查点分组 ### 测试要点 1. **倒计时功能**: - ✅ 弹窗打开后,确认按钮显示 "删除 (4s)" - ✅ 每秒递减: "删除 (3s)" → "删除 (2s)" → "删除 (1s)" → "删除" - ✅ 倒计时期间按钮不可点击 2. **视觉反馈**: - ✅ 倒计时期间按钮半透明 (opacity: 0.5) - ✅ 鼠标悬停显示禁用光标 (cursor: not-allowed) - ✅ 倒计时结束后按钮恢复正常样式 3. **交互行为**: - ✅ 点击取消按钮可以立即关闭弹窗 - ✅ 点击遮罩层可以立即关闭弹窗 - ✅ 按 ESC 键可以立即关闭弹窗 - ✅ 倒计时结束后点击确认执行删除操作 4. **边界情况**: - ✅ 快速打开/关闭弹窗不会导致计时器泄漏 - ✅ 多次打开弹窗,倒计时每次都重新开始 ### 预期行为 **正常流程**: ``` 用户点击删除 → 弹窗显示 → 确认按钮显示 "删除 (4s)" → 倒计时 3秒 → 倒计时 2秒 → 倒计时 1秒 → "删除" 可点击 → 用户点击确认 → 执行删除 → 显示成功提示 ``` **取消流程**: ``` 用户点击删除 → 弹窗显示 → 倒计时进行中 → 用户点击取消/遮罩层/ESC键 → 弹窗关闭 → 不执行删除 ``` --- ## 📊 统计总结 ### 更新统计 | 模块 | 文件名 | 删除操作数 | 导入变更 | 替换 confirm() | |------|--------|------------|----------|----------------| | 文档管理 | documents.list.tsx | 2 | - | - | | 评查点管理 | rules.list.tsx | 2 | - | - | | 评查点分组 | rule-groups._index.tsx | 2 | ✅ | ✅ | | 提示词模板 | prompts._index.tsx | 1 | ✅ | ✅ | | 入口模块 | entry-modules._index.tsx | 1 | ✅ | ✅ | | 文档类型 | document-types._index.tsx | 1 | ✅ | ✅ | | **总计** | **6 个文件** | **9 个操作** | **4 个文件** | **4 个文件** | ### 代码改动 - **新增代码**: MessageModal 组件增强 (~50 行) - **修改代码**: 9 个删除操作函数 (~200 行) - **导入变更**: 4 个文件添加 messageService 导入 - **替换操作**: 4 个文件从 `confirm()` 迁移到 `messageService.show()` --- ## 🎯 后续维护 ### 新增删除操作开发规范 当开发人员需要添加新的删除功能时,请遵循以下规范: ```typescript // ✅ 正确做法 import { messageService } from "~/components/ui/MessageModal"; const handleDelete = (id: string) => { messageService.show({ title: "确认删除", message: "确定要删除该项吗?", type: "warning", confirmText: "删除", cancelText: "取消", confirmDelay: 4, // 必须添加 onConfirm: () => { // 删除逻辑 } }); }; // ❌ 错误做法 const handleDelete = (id: string) => { if (confirm("确定要删除吗?")) { // 不要使用原生 confirm // 删除逻辑 } }; ``` ### Code Review 检查点 在代码审查时,请确认: 1. ✅ 所有删除操作都使用 `messageService.show()` 2. ✅ 所有删除确认都包含 `confirmDelay: 4` 3. ✅ 没有使用原生 `confirm()` 或 `alert()` 4. ✅ 导入了正确的 `messageService` --- ## 📞 联系支持 如遇到问题,请联系开发团队。 **文档维护人**: Claude Code **最后更新**: 2025-11-25