Files
leaudit-platform-frontend/docs/删除操作延迟确认功能实施文档.md
T
TanWenyan 3d6305376b docs: 添加 PostgREST 使用情况分析和删除确认功能文档
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>
2025-11-25 18:18:20 +08:00

558 lines
13 KiB
Markdown

# 删除操作延迟确认功能实施文档
> **实施时间**: 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
<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)
```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