fix: 全部页面 confirm() 替换为 ConfirmDialog 组件 — 9个页面统一回调模式

This commit is contained in:
logan committed 2026-05-27 21:25:14 +08:00
1 parent 41dab3deab
commit 7f6a4bb6df
9 files changed
+153 -62

No files matched your search

+12 -7
View File
@@ -1,6 +1,7 @@
import { useState, useEffect, useCallback } from 'react'; import { useState, useEffect, useCallback } from 'react';
import request from '@/api/request'; import request from '@/api/request';
import { Megaphone, Plus, Edit2, Trash2, Eye, Clock, CheckCircle, X, Save } from 'lucide-react'; import { Megaphone, Plus, Edit2, Trash2, Eye, Clock, CheckCircle, X, Save } from 'lucide-react';
import { ConfirmDialog } from '@/components/ConfirmDialog';
/* ── types ── */ /* ── types ── */
interface Announcement { interface Announcement {
@@ -56,6 +57,7 @@ export default function AnnouncementPage() {
/* view preview */ /* view preview */
const [viewAnn, setViewAnn] = useState<Announcement | null>(null); const [viewAnn, setViewAnn] = useState<Announcement | null>(null);
const [deleteConfirm, setDeleteConfirm] = useState<{msg: string; onConfirm: () => void} | null>(null);
const fetchList = useCallback(async () => { const fetchList = useCallback(async () => {
try { try {
@@ -118,13 +120,8 @@ export default function AnnouncementPage() {
}; };
const handleDelete = async (id: number) => { const handleDelete = async (id: number) => {
if (!confirm('确定删除此公告?')) return; setDeleteConfirm({msg: '确定删除此公告?', onConfirm: async () => { try { await request.delete(`/announcements/${id}`); fetchList(); } catch {} }});
try { return;
await request.delete(`/announcements/${id}`);
fetchList();
} catch {
/* ignore */
}
}; };
/* ── render ── */ /* ── render ── */
@@ -344,6 +341,14 @@ export default function AnnouncementPage() {
</div> </div>
</div> </div>
)} )}
<ConfirmDialog
open={!!deleteConfirm}
title={deleteConfirm?.msg || '确认操作'}
variant="danger"
confirmText="确定"
onConfirm={() => { deleteConfirm?.onConfirm(); }}
onCancel={() => setDeleteConfirm(null)}
/>
</div> </div>
); );
} }
+22 -6
View File
@@ -4,6 +4,7 @@ import { ArrowLeft, Database, Table2, Users, Cpu, Play, RefreshCw } from 'lucide
import { instanceDetailApi } from '@/api/instanceDetail'; import { instanceDetailApi } from '@/api/instanceDetail';
import { instanceApi } from '@/api/instance'; import { instanceApi } from '@/api/instance';
import { queryApi } from '@/api/query'; import { queryApi } from '@/api/query';
import { ConfirmDialog } from '@/components/ConfirmDialog';
interface InstanceInfo { interface InstanceInfo {
id: number; name: string; db_type: string; host: string; port: number; id: number; name: string; db_type: string; host: string; port: number;
@@ -27,6 +28,7 @@ export default function InstanceDetailPage() {
var [sqlText, setSqlText] = useState(''); var [sqlText, setSqlText] = useState('');
var [queryResult, setQueryResult] = useState<any>(null); var [queryResult, setQueryResult] = useState<any>(null);
var [loading, setLoading] = useState(false); var [loading, setLoading] = useState(false);
const [deleteConfirm, setDeleteConfirm] = useState<{msg: string; onConfirm: () => void} | null>(null);
var tabs: { key: TabKey; label: string; icon: React.ElementType }[] = [ var tabs: { key: TabKey; label: string; icon: React.ElementType }[] = [
{ key: 'overview', label: '概览', icon: Database }, { key: 'overview', label: '概览', icon: Database },
@@ -81,12 +83,17 @@ export default function InstanceDetailPage() {
}; };
var handleKill = function (pid: number) { var handleKill = function (pid: number) {
if (!confirm('确定 Kill 进程 ' + pid + '?')) return; setDeleteConfirm({
instanceDetailApi.killProcess(instanceId, pid).then(function () { msg: '确定 Kill 进程 ' + pid + '?',
instanceDetailApi.getProcesslist(instanceId).then(function (res: any) { onConfirm: () => {
setProcesses(res.processes || res || []); instanceDetailApi.killProcess(instanceId, pid).then(() => {
}); instanceDetailApi.getProcesslist(instanceId).then((res: any) => {
}).catch(function () {}); setProcesses(res.processes || res || []);
});
});
}
});
return;
}; };
if (!instance) return <div className="p-10 text-center text-[var(--dg-text-muted)]">加载中...</div>; if (!instance) return <div className="p-10 text-center text-[var(--dg-text-muted)]">加载中...</div>;
@@ -255,6 +262,15 @@ export default function InstanceDetailPage() {
{processes.length === 0 && <div className="text-center py-8 text-[var(--dg-text-muted)]">暂无进程</div>} {processes.length === 0 && <div className="text-center py-8 text-[var(--dg-text-muted)]">暂无进程</div>}
</div> </div>
)} )}
<ConfirmDialog
open={!!deleteConfirm}
title={deleteConfirm?.msg || '确认操作'}
variant="danger"
confirmText="确定"
onConfirm={() => { deleteConfirm?.onConfirm(); }}
onCancel={() => setDeleteConfirm(null)}
/>
</div> </div>
); );
} }
+16 -4
View File
@@ -8,6 +8,7 @@ import { instanceGroupApi, type InstanceGroup } from '@/api/instanceGroup';
import { instanceApi } from '@/api/instance'; import { instanceApi } from '@/api/instance';
import { userApi } from '@/api/user'; import { userApi } from '@/api/user';
import { useActionPermission } from '@/hooks/useAuth'; import { useActionPermission } from '@/hooks/useAuth';
import { ConfirmDialog } from '@/components/ConfirmDialog';
/* ───────── Constants ───────── */ /* ───────── Constants ───────── */
@@ -109,6 +110,7 @@ export default function InstanceGroupPage() {
const [showCreateModal, setShowCreateModal] = useState(false); const [showCreateModal, setShowCreateModal] = useState(false);
const [showEditModal, setShowEditModal] = useState(false); const [showEditModal, setShowEditModal] = useState(false);
const [showUserModal, setShowUserModal] = useState(false); const [showUserModal, setShowUserModal] = useState(false);
const [deleteConfirm, setDeleteConfirm] = useState<{msg: string; onConfirm: () => void} | null>(null);
const canManage = useActionPermission('instance-group.manage'); const canManage = useActionPermission('instance-group.manage');
// Form state — shared by create & edit // Form state — shared by create & edit
@@ -215,10 +217,11 @@ export default function InstanceGroupPage() {
/* ── Delete ── */ /* ── Delete ── */
const handleDelete = async (id: number) => { const handleDelete = async (id: number) => {
if (!confirm('确定删除该权限组?')) return; setDeleteConfirm({msg: '确定删除该权限组?', onConfirm: async () => {
await instanceGroupApi.delete(id); await instanceGroupApi.delete(id);
if (selectedId === id) setSelectedId(null); if (selectedId === id) setSelectedId(null);
fetchGroups(); fetchGroups();
}});
}; };
/* ── Assign Users ── */ /* ── Assign Users ── */
@@ -439,6 +442,15 @@ export default function InstanceGroupPage() {
<FormModal mode="create" /> <FormModal mode="create" />
<FormModal mode="edit" /> <FormModal mode="edit" />
<ConfirmDialog
open={!!deleteConfirm}
title={deleteConfirm?.msg || '确认操作'}
variant="danger"
confirmText="确定"
onConfirm={() => { deleteConfirm?.onConfirm(); }}
onCancel={() => setDeleteConfirm(null)}
/>
{/* User Modal */} {/* User Modal */}
{showUserModal && ( {showUserModal && (
<div className="fixed inset-0 z-[100] flex items-start justify-center pt-[10vh] bg-black/60 backdrop-blur-sm"> <div className="fixed inset-0 z-[100] flex items-start justify-center pt-[10vh] bg-black/60 backdrop-blur-sm">
+28 -19
View File
@@ -12,6 +12,7 @@ import PageHeader from '@/components/PageHeader';
import EmptyState from '@/components/EmptyState'; import EmptyState from '@/components/EmptyState';
import { instanceApi } from '@/api/instance'; import { instanceApi } from '@/api/instance';
import EditInstanceDialog from '@/components/EditInstanceDialog'; import EditInstanceDialog from '@/components/EditInstanceDialog';
import { ConfirmDialog } from '@/components/ConfirmDialog';
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */
/* Types */ /* Types */
@@ -189,29 +190,28 @@ export default function InstancePage() {
}, [filtered]); }, [filtered]);
const handleDelete = useCallback(async (id: number) => { const handleDelete = useCallback(async (id: number) => {
if (!confirm('确定删除该实例?')) return; setDeleteConfirm({
try { msg: '确定删除该实例?',
await instanceApi.delete(id); onConfirm: async () => {
fetchInstances(); try { await instanceApi.delete(id); fetchInstances(); } catch {}
} catch { },
/* ignore */ });
}
}, [fetchInstances]); }, [fetchInstances]);
const handleBatchDelete = useCallback(async () => { const handleBatchDelete = useCallback(async () => {
if (selectedIds.size === 0) return; if (selectedIds.size === 0) return;
if (!confirm(`确定删除选中的 ${selectedIds.size} 个实例?此操作不可恢复。`)) return; setDeleteConfirm({
setBatchDeleting(true); msg: `确定删除选中的 ${selectedIds.size} 个实例?此操作不可恢复。`,
try { onConfirm: async () => {
const ids = Array.from(selectedIds); setBatchDeleting(true);
await Promise.all(ids.map(id => instanceApi.delete(id))); try {
setSelectedIds(new Set()); const ids = Array.from(selectedIds);
fetchInstances(); await Promise.all(ids.map(i => instanceApi.delete(i)));
} catch { setSelectedIds(new Set());
/* ignore */ fetchInstances();
} finally { } finally { setBatchDeleting(false); }
setBatchDeleting(false); },
} });
}, [selectedIds, fetchInstances]); }, [selectedIds, fetchInstances]);
const toggleSelect = useCallback((id: number) => { const toggleSelect = useCallback((id: number) => {
@@ -994,6 +994,7 @@ function CreateInstanceForm({ defaultType, onCancel, onCreated }: {
const [error, setError] = useState(''); const [error, setError] = useState('');
const [testing, setTesting] = useState(false); const [testing, setTesting] = useState(false);
const [testResult, setTestResult] = useState<{ success: boolean; message: string; version?: string } | null>(null); const [testResult, setTestResult] = useState<{ success: boolean; message: string; version?: string } | null>(null);
const [deleteConfirm, setDeleteConfirm] = useState<{msg: string; onConfirm: () => void} | null>(null);
const isRedis = dbType === 'redis'; const isRedis = dbType === 'redis';
const needUsername = !isRedis; const needUsername = !isRedis;
@@ -1447,6 +1448,14 @@ function CreateInstanceForm({ defaultType, onCancel, onCreated }: {
</div> </div>
</div> </div>
</div> </div>
<ConfirmDialog
open={!!deleteConfirm}
title={deleteConfirm?.msg || '确认操作'}
variant="danger"
confirmText="确定"
onConfirm={() => { deleteConfirm?.onConfirm(); }}
onCancel={() => setDeleteConfirm(null)}
/>
</div> </div>
); );
} }
+12 -7
View File
@@ -1,6 +1,7 @@
import { useState, useEffect, useCallback } from 'react'; import { useState, useEffect, useCallback } from 'react';
import request from '@/api/request'; import request from '@/api/request';
import { Plus, Trash2, Send, Bell, Mail, Settings, X } from 'lucide-react'; import { Plus, Trash2, Send, Bell, Mail, Settings, X } from 'lucide-react';
import { ConfirmDialog } from '@/components/ConfirmDialog';
/* ── types ── */ /* ── types ── */
type ChannelType = 'dingtalk' | 'webhook_feishu' | 'email'; type ChannelType = 'dingtalk' | 'webhook_feishu' | 'email';
@@ -62,6 +63,7 @@ export default function NotificationPage() {
/* test */ /* test */
const [testingId, setTestingId] = useState<number | null>(null); const [testingId, setTestingId] = useState<number | null>(null);
const [testResult, setTestResult] = useState<Record<number, { success: boolean; message: string } | null>>({}); const [testResult, setTestResult] = useState<Record<number, { success: boolean; message: string } | null>>({});
const [deleteConfirm, setDeleteConfirm] = useState<{msg: string; onConfirm: () => void} | null>(null);
const fetchList = useCallback(async () => { const fetchList = useCallback(async () => {
try { try {
@@ -128,13 +130,8 @@ export default function NotificationPage() {
}; };
const handleDelete = async (id: number) => { const handleDelete = async (id: number) => {
if (!confirm('确定删除此通知渠道?')) return; setDeleteConfirm({msg: '确定删除此通知渠道?', onConfirm: async () => { try { await request.delete(`/notifications/${id}`); fetchList(); } catch {} }});
try { return;
await request.delete(`/notifications/${id}`);
fetchList();
} catch {
/* ignore */
}
}; };
const handleTest = async (id: number) => { const handleTest = async (id: number) => {
@@ -451,6 +448,14 @@ export default function NotificationPage() {
</div> </div>
</div> </div>
)} )}
<ConfirmDialog
open={!!deleteConfirm}
title={deleteConfirm?.msg || '确认操作'}
variant="danger"
confirmText="确定"
onConfirm={() => { deleteConfirm?.onConfirm(); }}
onCancel={() => setDeleteConfirm(null)}
/>
</div> </div>
); );
} }
+12 -5
View File
@@ -2,6 +2,7 @@ import { useState, useEffect, useCallback } from 'react';
import { Bell, Webhook, Check, Settings, TestTube, ToggleLeft, ToggleRight, Loader2, AlertCircle, Plus, Trash2 } from 'lucide-react'; import { Bell, Webhook, Check, Settings, TestTube, ToggleLeft, ToggleRight, Loader2, AlertCircle, Plus, Trash2 } from 'lucide-react';
import { Card, CardContent } from '@/components/ui/card'; import { Card, CardContent } from '@/components/ui/card';
import { notificationApi } from '@/api/notification'; import { notificationApi } from '@/api/notification';
import { ConfirmDialog } from '@/components/ConfirmDialog';
interface NotificationChannel { interface NotificationChannel {
id: number; id: number;
@@ -25,6 +26,7 @@ export default function NotificationsPage() {
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [testingId, setTestingId] = useState<number | null>(null); const [testingId, setTestingId] = useState<number | null>(null);
const [testResult, setTestResult] = useState<{ id: number; success: boolean; message: string } | null>(null); const [testResult, setTestResult] = useState<{ id: number; success: boolean; message: string } | null>(null);
const [deleteConfirm, setDeleteConfirm] = useState<{msg: string; onConfirm: () => void} | null>(null);
const fetchChannels = useCallback(async () => { const fetchChannels = useCallback(async () => {
try { try {
@@ -67,11 +69,8 @@ export default function NotificationsPage() {
}, []); }, []);
const handleDelete = useCallback(async (id: number) => { const handleDelete = useCallback(async (id: number) => {
if (!confirm('确认删除该通知渠道?')) return; setDeleteConfirm({msg: '确认删除该通知渠道?', onConfirm: async () => { try { await notificationApi.delete(id); setChannels(prev => prev.filter(c => c.id !== id)); } catch {} }});
try { return;
await notificationApi.delete(id);
setChannels(prev => prev.filter(c => c.id !== id));
} catch {}
}, []); }, []);
if (loading) { if (loading) {
@@ -175,6 +174,14 @@ export default function NotificationsPage() {
</div> </div>
</> </>
)} )}
<ConfirmDialog
open={!!deleteConfirm}
title={deleteConfirm?.msg || '确认操作'}
variant="danger"
confirmText="确定"
onConfirm={() => { deleteConfirm?.onConfirm(); }}
onCancel={() => setDeleteConfirm(null)}
/>
</div> </div>
); );
} }
+14 -2
View File
@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom'; import { useParams, useNavigate } from 'react-router-dom';
import { ArrowLeft, Key, Activity, Terminal, Gauge, Plus, RefreshCw, Search } from 'lucide-react'; import { ArrowLeft, Key, Activity, Terminal, Gauge, Plus, RefreshCw, Search } from 'lucide-react';
import { redisApi } from '@/api/redis'; import { redisApi } from '@/api/redis';
import { ConfirmDialog } from '@/components/ConfirmDialog';
type TabKey = 'keys' | 'monitor' | 'cli' | 'perf'; type TabKey = 'keys' | 'monitor' | 'cli' | 'perf';
@@ -24,6 +25,7 @@ export default function RedisDetailPage() {
var [slowlog, setSlowlog] = useState<any[]>([]); var [slowlog, setSlowlog] = useState<any[]>([]);
var [clients, setClients] = useState<any[]>([]); var [clients, setClients] = useState<any[]>([]);
var [loading, setLoading] = useState(false); var [loading, setLoading] = useState(false);
const [deleteConfirm, setDeleteConfirm] = useState<{msg: string; onConfirm: () => void} | null>(null);
var tabs: { key: TabKey; label: string; icon: React.ElementType }[] = [ var tabs: { key: TabKey; label: string; icon: React.ElementType }[] = [
{ key: 'keys', label: '键管理', icon: Key }, { key: 'keys', label: '键管理', icon: Key },
@@ -54,8 +56,10 @@ export default function RedisDetailPage() {
}; };
var handleDeleteKey = function (keyName: string) { var handleDeleteKey = function (keyName: string) {
if (!confirm('确定删除键 ' + keyName + '?')) return; setDeleteConfirm({msg: '确定删除该键?', onConfirm: async () => {
redisApi.deleteKey(instanceId, keyName, selectedDb).then(function () { fetchKeys(); }).catch(function () {}); redisApi.deleteKey(instanceId, keyName, selectedDb).then(function () { fetchKeys(); }).catch(function () {});
}});
return;
}; };
var handleCommand = function () { var handleCommand = function () {
@@ -304,6 +308,14 @@ export default function RedisDetailPage() {
</div> </div>
</div> </div>
)} )}
<ConfirmDialog
open={!!deleteConfirm}
title={deleteConfirm?.msg || '确认操作'}
variant="danger"
confirmText="确定"
onConfirm={() => { deleteConfirm?.onConfirm(); }}
onCancel={() => setDeleteConfirm(null)}
/>
</div> </div>
); );
} }
+20 -7
View File
@@ -10,6 +10,7 @@ import { userApi, ldapApi } from '@/api/user';
import { instanceGroupApi, type InstanceGroup } from '@/api/instanceGroup'; import { instanceGroupApi, type InstanceGroup } from '@/api/instanceGroup';
import { nameToUsername } from '@/utils/pinyin'; import { nameToUsername } from '@/utils/pinyin';
import { toast } from '@/hooks/useToast'; import { toast } from '@/hooks/useToast';
import { ConfirmDialog } from '@/components/ConfirmDialog';
/* ───────── Types ───────── */ /* ───────── Types ───────── */
@@ -134,6 +135,7 @@ export default function UsersPage() {
const [selectedIds, setSelectedIds] = useState<Set<number>>(new Set()); const [selectedIds, setSelectedIds] = useState<Set<number>>(new Set());
const [showBatchMenu, setShowBatchMenu] = useState(false); const [showBatchMenu, setShowBatchMenu] = useState(false);
const [showImport, setShowImport] = useState(false); const [showImport, setShowImport] = useState(false);
const [deleteConfirm, setDeleteConfirm] = useState<{msg: string; onConfirm: () => void} | null>(null);
const fetchUsers = useCallback(async () => { const fetchUsers = useCallback(async () => {
setLoading(true); setLoading(true);
@@ -188,13 +190,15 @@ export default function UsersPage() {
}; };
const handleDelete = async (id: number) => { const handleDelete = async (id: number) => {
if (!confirm('确定删除该用户?此操作不可恢复。')) return; setDeleteConfirm({msg: '确定删除该用户?此操作不可恢复。', onConfirm: async () => {
try { try {
await userApi.delete(id); await userApi.delete(id);
setSelectedIds(prev => { const n = new Set(prev); n.delete(id); return n; }); setSelectedIds(prev => { const n = new Set(prev); n.delete(id); return n; });
toast('success', '用户已删除'); toast('success', '用户已删除');
fetchUsers(); fetchUsers();
} catch { toast('error', '删除失败'); } } catch { toast('error', '删除失败'); }
}});
return;
}; };
const handleBatchAction = async (action: string) => { const handleBatchAction = async (action: string) => {
@@ -521,6 +525,14 @@ export default function UsersPage() {
{showImport && ( {showImport && (
<BatchImportModal onClose={() => setShowImport(false)} onImported={() => { setShowImport(false); fetchUsers(); }} /> <BatchImportModal onClose={() => setShowImport(false)} onImported={() => { setShowImport(false); fetchUsers(); }} />
)} )}
<ConfirmDialog
open={!!deleteConfirm}
title={deleteConfirm?.msg || '确认操作'}
variant="danger"
confirmText="确定"
onConfirm={() => { deleteConfirm?.onConfirm(); }}
onCancel={() => setDeleteConfirm(null)}
/>
</div> </div>
); );
} }
@@ -888,6 +900,7 @@ function BatchImportModal({ onClose, onImported }: { onClose: () => void; onImpo
const [newOuName, setNewOuName] = useState(''); const [newOuName, setNewOuName] = useState('');
const [newOuDesc, setNewOuDesc] = useState(''); const [newOuDesc, setNewOuDesc] = useState('');
const [creatingOu, setCreatingOu] = useState(false); const [creatingOu, setCreatingOu] = useState(false);
const [deleteConfirm, setDeleteConfirm] = useState<{msg: string; onConfirm: () => void} | null>(null);
// Check LDAP readiness // Check LDAP readiness
useEffect(() => { useEffect(() => {
+17 -5
View File
@@ -7,6 +7,7 @@ import {
import { Card, CardContent } from '@/components/ui/card'; import { Card, CardContent } from '@/components/ui/card';
import { workflowApi } from '@/api/workflow'; import { workflowApi } from '@/api/workflow';
import { userApi } from '@/api/user'; import { userApi } from '@/api/user';
import { ConfirmDialog } from '@/components/ConfirmDialog';
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */
/* Types */ /* Types */
@@ -470,6 +471,7 @@ export default function WorkflowPage() {
const [expandedId, setExpandedId] = useState<number | null>(null); const [expandedId, setExpandedId] = useState<number | null>(null);
const [previewWorkflow, setPreviewWorkflow] = useState<WorkflowDetail | null>(null); const [previewWorkflow, setPreviewWorkflow] = useState<WorkflowDetail | null>(null);
const [editorWorkflow, setEditorWorkflow] = useState<WorkflowDetail | null | undefined>(undefined); const [editorWorkflow, setEditorWorkflow] = useState<WorkflowDetail | null | undefined>(undefined);
const [deleteConfirm, setDeleteConfirm] = useState<{msg: string; onConfirm: () => void} | null>(null);
const fetchWorkflows = useCallback(async () => { const fetchWorkflows = useCallback(async () => {
try { try {
@@ -506,11 +508,13 @@ export default function WorkflowPage() {
}, []); }, []);
const handleDelete = useCallback(async (id: number) => { const handleDelete = useCallback(async (id: number) => {
if (!confirm('确认删除该审批流程?')) return; setDeleteConfirm({msg: '确认删除该审批流程?', onConfirm: async () => {
try { try {
await workflowApi.delete(id); await workflowApi.delete(id);
setItems((prev) => prev.filter((w) => w.id !== id)); setItems((prev) => prev.filter((w) => w.id !== id));
} catch {} } catch {}
}});
return;
}, []); }, []);
const setDefault = useCallback(async (id: number) => { const setDefault = useCallback(async (id: number) => {
@@ -807,6 +811,14 @@ export default function WorkflowPage() {
onSaved={() => { setEditorWorkflow(undefined); fetchWorkflows(); }} onSaved={() => { setEditorWorkflow(undefined); fetchWorkflows(); }}
/> />
)} )}
<ConfirmDialog
open={!!deleteConfirm}
title={deleteConfirm?.msg || '确认操作'}
variant="danger"
confirmText="确定"
onConfirm={() => { deleteConfirm?.onConfirm(); }}
onCancel={() => setDeleteConfirm(null)}
/>
</div> </div>
); );
} }