Files
leaudit-platform-frontend/docs/删除操作延迟确认功能实施文档.md
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

13 KiB

删除操作延迟确认功能实施文档

实施时间: 2025-11-25 功能描述: 为所有删除操作添加4秒延迟确认功能,防止误删除操作


📋 目录


功能概述

需求背景

为了防止用户误操作导致数据被删除,所有删除操作都需要:

  1. 显示确认弹窗提示
  2. 确认按钮在4秒倒计时结束后才能点击
  3. 倒计时期间按钮显示剩余秒数

核心功能

  • 延迟确认: 确认按钮在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 || '未知错误'}`);
  }
}

新代码 (使用 messageServicetoastService):

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. 批量删除操作测试

  • 批量删除文档
  • 批量删除评查点
  • 批量删除评查点分组

测试要点

  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()

🎯 后续维护

新增删除操作开发规范

当开发人员需要添加新的删除功能时,请遵循以下规范:

// ✅ 正确做法
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