3d6305376b
1. PostgREST 使用情况分析文档 - PostgREST使用情况-后端API替代建议.md: 完整的迁移建议和优先级分析 - PostgREST实际使用清单.md: 当前使用的 PostgREST 接口清单 - PostgREST未使用函数清单.md: 已封装但未使用的函数列表 - PostgREST请求模块清单.md: 所有请求模块的使用情况 2. 删除操作延迟确认功能实施文档 - 功能设计和实现细节 - 使用示例和最佳实践 - 技术实现说明 这些文档用于: - 追踪 PostgREST 到 FastAPI 的迁移进度 - 指导后续的接口迁移工作 - 记录 UI 改进的实施细节 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
13 KiB
13 KiB
删除操作延迟确认功能实施文档
实施时间: 2025-11-25 功能描述: 为所有删除操作添加4秒延迟确认功能,防止误删除操作
📋 目录
功能概述
需求背景
为了防止用户误操作导致数据被删除,所有删除操作都需要:
- 显示确认弹窗提示
- 确认按钮在4秒倒计时结束后才能点击
- 倒计时期间按钮显示剩余秒数
核心功能
- 延迟确认: 确认按钮在4秒倒计时后才可点击
- 倒计时显示: 按钮文本显示 "删除 (4s)" → "删除 (3s)" → ... → "删除"
- 视觉反馈: 倒计时期间按钮呈半透明状态且鼠标样式为禁用
- 统一体验: 所有删除操作使用相同的确认流程
技术实现
1. MessageModal 组件增强
文件: app/components/ui/MessageModal.tsx
新增 Props
interface MessageModalProps {
// ... 现有属性
// 确认按钮延迟时间(秒)- 用于危险操作(如删除)
confirmDelay?: number;
}
状态管理
const [remainingSeconds, setRemainingSeconds] = useState(confirmDelay);
倒计时逻辑
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]);
按钮渲染
<button
className="message-modal-button primary"
onClick={handleConfirm}
disabled={remainingSeconds > 0}
style={{
opacity: remainingSeconds > 0 ? 0.5 : 1,
cursor: remainingSeconds > 0 ? 'not-allowed' : 'pointer'
}}
>
{remainingSeconds > 0 ? `${confirmText} (${remainingSeconds}s)` : confirmText}
</button>
已更新的文件
1. 文档管理模块 (documents.list.tsx)
文件路径: app/routes/documents.list.tsx
更新位置 1: 单个文档删除 (Line 580-596)
messageService.show({
title: "确认删除",
message: `确定要删除文档"${name}"吗?`,
type: "warning",
confirmText: "删除",
cancelText: "取消",
confirmDelay: 4, // ✅ 新增
onConfirm: () => {
// 删除逻辑
}
});
更新位置 2: 批量删除文档 (Line 617-642)
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)
messageService.show({
title: "确认删除",
message: `确认删除评查点【${rule.name}】吗?`,
type: "warning",
confirmText: "删除",
cancelText: "取消",
confirmDelay: 4, // ✅ 新增
onConfirm: () => {
// 删除逻辑
}
});
更新位置 2: 批量删除评查点 (Line 603-634)
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
导入更新
// 原代码
import { toastService } from "~/components/ui";
// 新代码
import { toastService, messageService } from "~/components/ui";
更新位置 1: 单个分组删除 (Line 233-275)
原代码 (使用 confirm()):
if (confirm("确定要删除该分组吗?此操作将同时删除该分组下的所有评查点,且不可恢复。")) {
// 删除逻辑
}
新代码 (使用 messageService):
messageService.show({
title: "确认删除",
message: "确定要删除该分组吗?此操作将同时删除该分组下的所有评查点,且不可恢复。",
type: "warning",
confirmText: "删除",
cancelText: "取消",
confirmDelay: 4, // ✅ 新增
onConfirm: async () => {
// 删除逻辑
}
});
更新位置 2: 批量删除分组 (Line 307-328)
原代码 (使用 confirm()):
if (!confirm(`确定要删除选中的 ${selectedIds.length} 个分组吗?此操作不可恢复。`)) {
return;
}
新代码 (使用 messageService):
messageService.show({
title: "确认批量删除",
message: `确定要删除选中的 ${selectedIds.length} 个分组吗?此操作不可恢复。`,
type: "warning",
confirmText: "删除",
cancelText: "取消",
confirmDelay: 4, // ✅ 新增
onConfirm: async () => {
// 删除逻辑
}
});
4. 提示词模板管理模块 (prompts._index.tsx)
文件路径: app/routes/prompts._index.tsx
导入更新
// 原代码
import { toastService } from "~/components/ui";
// 新代码
import { toastService, messageService } from "~/components/ui";
更新位置: 删除模板 (Line 220-234)
原代码 (使用 confirm()):
if (confirm('确定要删除该模板吗?删除后无法恢复。')) {
const formData = new FormData();
formData.append('id', id);
formData.append('intent', 'delete');
fetcher.submit(formData, { method: 'post' });
}
新代码 (使用 messageService):
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
导入更新
// 原代码
import { toastService } from "~/components/ui/Toast";
// 新代码
import { toastService } from "~/components/ui/Toast";
import { messageService } from "~/components/ui/MessageModal";
更新位置: 删除入口模块 (Line 215-250)
原代码 (使用 confirm()):
if (confirm('确定要删除该入口模块吗?此操作不可撤销。')) {
setIsDeleting(true);
// 删除逻辑
}
新代码 (使用 messageService):
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
导入更新
// 原代码
import { toastService } from "~/components/ui/Toast";
// 新代码
import { toastService } from "~/components/ui/Toast";
import { messageService } from "~/components/ui/MessageModal";
更新位置: 删除文档类型 (Line 204-239)
原代码 (使用 confirm() 和 alert()):
if (confirm('确定要删除该文档类型吗?此操作不会影响关联的评查点分组,但可能会影响使用该类型的文档评查。')) {
setIsDeleting(true);
// 删除逻辑
if (result.success) {
alert('删除成功!');
} else {
alert(`删除失败: ${result.error || '未知错误'}`);
}
}
新代码 (使用 messageService 和 toastService):
messageService.show({
title: "确认删除",
message: "确定要删除该文档类型吗?此操作不会影响关联的评查点分组,但可能会影响使用该类型的文档评查。",
type: "warning",
confirmText: "删除",
cancelText: "取消",
confirmDelay: 4, // ✅ 新增
onConfirm: async () => {
setIsDeleting(true);
// 删除逻辑
if (result.success) {
toastService.success('删除成功!');
} else {
toastService.error(`删除失败: ${result.error || '未知错误'}`);
}
}
});
使用示例
基本用法
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);
}
});
};
批量删除示例
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. 批量删除操作测试
- 批量删除文档
- 批量删除评查点
- 批量删除评查点分组
测试要点
-
倒计时功能:
- ✅ 弹窗打开后,确认按钮显示 "删除 (4s)"
- ✅ 每秒递减: "删除 (3s)" → "删除 (2s)" → "删除 (1s)" → "删除"
- ✅ 倒计时期间按钮不可点击
-
视觉反馈:
- ✅ 倒计时期间按钮半透明 (opacity: 0.5)
- ✅ 鼠标悬停显示禁用光标 (cursor: not-allowed)
- ✅ 倒计时结束后按钮恢复正常样式
-
交互行为:
- ✅ 点击取消按钮可以立即关闭弹窗
- ✅ 点击遮罩层可以立即关闭弹窗
- ✅ 按 ESC 键可以立即关闭弹窗
- ✅ 倒计时结束后点击确认执行删除操作
-
边界情况:
- ✅ 快速打开/关闭弹窗不会导致计时器泄漏
- ✅ 多次打开弹窗,倒计时每次都重新开始
预期行为
正常流程:
用户点击删除 → 弹窗显示 → 确认按钮显示 "删除 (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()
🎯 后续维护
新增删除操作开发规范
当开发人员需要添加新的删除功能时,请遵循以下规范:
// ✅ 正确做法
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 检查点
在代码审查时,请确认:
- ✅ 所有删除操作都使用
messageService.show() - ✅ 所有删除确认都包含
confirmDelay: 4 - ✅ 没有使用原生
confirm()或alert() - ✅ 导入了正确的
messageService
📞 联系支持
如遇到问题,请联系开发团队。
文档维护人: Claude Code 最后更新: 2025-11-25