Skip to content

Commit 5acb190

Browse files
authored
Develop/collection status (#403)
* 💄 delete model * 💄 delete file model * 💄 add dataset status * feat: add DeleteConfirmModal component This component provides unified delete confirmation with customizable messages and is used by RatioTask and other modules.
1 parent 17e5dd1 commit 5acb190

File tree

11 files changed

+365
-158
lines changed

11 files changed

+365
-158
lines changed

frontend/src/components/ActionDropdown.tsx

Lines changed: 91 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
1-
import { Dropdown, Popconfirm, Button, Space } from "antd";
1+
import { Dropdown, Button, Space } from "antd";
22
import { EllipsisOutlined } from "@ant-design/icons";
33
import { useState } from "react";
44
import { useTranslation } from "react-i18next";
5+
import DeleteConfirmModal from "./DeleteConfirmModal";
56

67
interface ActionItem {
78
key: string;
89
label: string;
910
icon?: React.ReactNode;
1011
danger?: boolean;
12+
disabled?: boolean;
1113
confirm?: {
12-
title: string;
13-
description?: string;
14+
title?: string;
15+
message?: string;
16+
itemName?: string | ((item: any) => string);
1417
okText?: string;
1518
cancelText?: string;
19+
okType?: "default" | "primary" | "danger";
1620
};
21+
onClick?: (item?: any) => void | Promise<void>;
1722
}
1823

1924
interface ActionDropdownProps {
2025
actions?: ActionItem[];
21-
onAction?: (key: string, action: ActionItem) => void;
26+
onAction?: (key: string, action: ActionItem, item?: any) => void;
27+
item?: any;
2228
placement?:
2329
| "bottomRight"
2430
| "topLeft"
@@ -33,86 +39,106 @@ interface ActionDropdownProps {
3339
const ActionDropdown = ({
3440
actions = [],
3541
onAction,
42+
item,
3643
placement = "bottomRight",
3744
}: ActionDropdownProps) => {
3845
const { t } = useTranslation();
3946
const [open, setOpen] = useState(false);
40-
const handleActionClick = (action: ActionItem) => {
47+
const [deleteConfirm, setDeleteConfirm] = useState<{
48+
visible: boolean;
49+
action: ActionItem | null;
50+
}>({
51+
visible: false,
52+
action: null,
53+
});
54+
55+
const handleActionClick = (action: ActionItem, e: React.MouseEvent) => {
56+
e?.stopPropagation();
57+
4158
if (action.confirm) {
42-
// 如果有确认框,不立即执行,等待确认
59+
// 显示删除确认弹窗
60+
setDeleteConfirm({
61+
visible: true,
62+
action,
63+
});
64+
setOpen(false);
4365
return;
4466
}
67+
4568
// 执行操作
46-
onAction?.(action.key, action);
47-
// 如果没有确认框,则立即关闭 Dropdown
69+
if (action.onClick) {
70+
action.onClick(item);
71+
}
72+
onAction?.(action.key, action, item);
4873
setOpen(false);
4974
};
5075

76+
const handleConfirmDelete = async () => {
77+
if (deleteConfirm.action?.onClick) {
78+
await deleteConfirm.action.onClick(item);
79+
}
80+
if (deleteConfirm.action) {
81+
onAction?.(deleteConfirm.action.key, deleteConfirm.action, item);
82+
}
83+
setDeleteConfirm({ visible: false, action: null });
84+
};
85+
86+
const handleCancelDelete = () => {
87+
setDeleteConfirm({ visible: false, action: null });
88+
};
89+
5190
const dropdownContent = (
5291
<div className="bg-white p-2 rounded shadow-md">
5392
<Space direction="vertical" className="w-full">
54-
{actions.map((action) => {
55-
if (action.confirm) {
56-
return (
57-
<Popconfirm
58-
key={action.key}
59-
title={action.confirm.title}
60-
description={action.confirm.description}
61-
onConfirm={() => {
62-
onAction?.(action.key, action);
63-
setOpen(false);
64-
}}
65-
okText={action.confirm.okText || t('components.actionDropdown.confirm')}
66-
cancelText={action.confirm.cancelText || t('components.actionDropdown.cancel')}
67-
okType={action.danger ? "danger" : "primary"}
68-
styles={{ root: { zIndex: 9999 } }}
69-
>
70-
<Button
71-
type="text"
72-
size="small"
73-
disabled={action.disabled || false}
74-
className="w-full text-left"
75-
danger={action.danger}
76-
icon={action.icon}
77-
>
78-
{action.label}
79-
</Button>
80-
</Popconfirm>
81-
);
82-
}
83-
84-
return (
85-
<Button
86-
key={action.key}
87-
className="w-full"
88-
size="small"
89-
type="text"
90-
disabled={action.disabled || false}
91-
danger={action.danger}
92-
icon={action.icon}
93-
onClick={() => handleActionClick(action)}
94-
>
95-
{action.label}
96-
</Button>
97-
);
98-
})}
93+
{actions.map((action) => (
94+
<Button
95+
key={action.key}
96+
className="w-full"
97+
size="small"
98+
type="text"
99+
disabled={action.disabled || false}
100+
danger={action.danger}
101+
icon={action.icon}
102+
onClick={(e) => handleActionClick(action, e)}
103+
>
104+
{action.label}
105+
</Button>
106+
))}
99107
</Space>
100108
</div>
101109
);
102110

103111
return (
104-
<Dropdown
105-
overlay={dropdownContent}
106-
trigger={["click"]}
107-
placement={placement}
108-
open={open}
109-
onOpenChange={setOpen}
110-
>
111-
<Button
112-
type="text"
113-
icon={<EllipsisOutlined style={{ fontSize: 24 }} />}
114-
/>
115-
</Dropdown>
112+
<>
113+
<Dropdown
114+
menu={{ items: [] }}
115+
dropdownRender={() => dropdownContent}
116+
trigger={["click"]}
117+
placement={placement}
118+
open={open}
119+
onOpenChange={setOpen}
120+
>
121+
<Button
122+
type="text"
123+
icon={<EllipsisOutlined style={{ fontSize: 24 }} />}
124+
/>
125+
</Dropdown>
126+
127+
{deleteConfirm.action?.confirm && (
128+
<DeleteConfirmModal
129+
visible={deleteConfirm.visible}
130+
title={deleteConfirm.action.confirm.title}
131+
message={deleteConfirm.action.confirm.message}
132+
itemName={
133+
typeof deleteConfirm.action.confirm.itemName === 'function'
134+
? deleteConfirm.action.confirm.itemName(item)
135+
: deleteConfirm.action.confirm.itemName
136+
}
137+
onConfirm={handleConfirmDelete}
138+
onCancel={handleCancelDelete}
139+
/>
140+
)}
141+
</>
116142
);
117143
};
118144

frontend/src/components/CardView.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,15 @@ interface CardViewProps<T> {
4343
key: string;
4444
label: string;
4545
danger?: boolean;
46+
disabled?: boolean;
4647
icon?: React.JSX.Element;
48+
confirm?: {
49+
title: string;
50+
description?: string;
51+
okText?: string;
52+
cancelText?: string;
53+
okType?: "default" | "primary" | "danger";
54+
};
4755
onClick?: (item: T) => void;
4856
}[]
4957
| ((item: T) => ItemType[]);
@@ -327,11 +335,9 @@ function CardView<T extends BaseCardDataType>(props: CardViewProps<T>) {
327335
{operations && (
328336
<ActionDropdown
329337
actions={ops(item)}
338+
item={item}
330339
onAction={(key) => {
331-
const operation = ops(item).find((op) => op.key === key);
332-
if (operation?.onClick) {
333-
operation.onClick(item);
334-
}
340+
// ActionDropdown 已经处理了 onClick 调用
335341
}}
336342
/>
337343
)}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { Modal } from "antd";
2+
import { useTranslation } from "react-i18next";
3+
4+
export interface DeleteConfirmModalProps {
5+
visible: boolean;
6+
title?: string;
7+
message?: string;
8+
itemName?: string;
9+
onConfirm: () => void | Promise<void>;
10+
onCancel: () => void;
11+
}
12+
13+
/**
14+
* 公共删除确认弹窗组件
15+
* 样式参考任务管理的删除弹窗
16+
*
17+
* @example
18+
* ```tsx
19+
* const [deleteModal, setDeleteModal] = useState({
20+
* visible: false,
21+
* itemName: "",
22+
* });
23+
*
24+
* <DeleteConfirmModal
25+
* visible={deleteModal.visible}
26+
* itemName={deleteModal.itemName}
27+
* onConfirm={async () => {
28+
* await deleteItem(deleteModal.id);
29+
* setDeleteModal({ visible: false, itemName: "" });
30+
* }}
31+
* onCancel={() => setDeleteModal({ visible: false, itemName: "" })}
32+
* />
33+
* ```
34+
*/
35+
export default function DeleteConfirmModal({
36+
visible,
37+
title,
38+
message,
39+
itemName,
40+
onConfirm,
41+
onCancel,
42+
}: DeleteConfirmModalProps) {
43+
const { t } = useTranslation();
44+
45+
const defaultTitle = title || t("components.deleteConfirm.title");
46+
// 如果提供了自定义 message,直接使用;否则使用默认消息并替换占位符
47+
const defaultMessage = message
48+
? message.replace("{{itemName}}", itemName || "").replace("{{taskName}}", itemName || "")
49+
: t("components.deleteConfirm.message", { itemName: itemName || "" });
50+
51+
return (
52+
<Modal
53+
title={defaultTitle}
54+
open={visible}
55+
onOk={onConfirm}
56+
onCancel={onCancel}
57+
okType="danger"
58+
okText={t("components.deleteConfirm.confirm")}
59+
cancelText={t("components.deleteConfirm.cancel")}
60+
>
61+
<p>{defaultMessage}</p>
62+
</Modal>
63+
);
64+
}

frontend/src/i18n/locales/en/common.json

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@
9595
"updateFailed": "Failed to update task",
9696
"deleteSuccess": "Task deleted",
9797
"deleteConfirm": "Are you sure you want to delete this task? This action cannot be undone.",
98-
"deleteConfirmMessage": "Are you sure you want to delete task \"{{taskName}}\"? This action cannot be undone.",
98+
"deleteConfirmMessage": "Are you sure you want to delete \"{{itemName}}\"? This action cannot be undone.",
9999
"confirmDelete": "Delete",
100100
"cancel": "Cancel"
101101
},
@@ -373,7 +373,7 @@
373373
},
374374
"confirm": {
375375
"deleteDatasetTitle": "Confirm delete this dataset?",
376-
"deleteDatasetDesc": "This dataset cannot be recovered after deletion. Proceed with caution.",
376+
"deleteDatasetDesc": "Are you sure you want to delete \"{{itemName}}\"? This action cannot be undone.",
377377
"deleteFolderTitle": "Confirm delete folder?",
378378
"deleteFolderDesc": "Deleting folder \"{{name}}\" will remove all files and subfolders. This action cannot be undone.",
379379
"deleteConfirm": "Delete",
@@ -584,10 +584,12 @@
584584
"videoJsonl": "Dataset for large-scale video pretraining"
585585
},
586586
"datasetStatus": {
587+
"draft": "Draft",
587588
"active": "Active",
588589
"processing": "Processing",
589-
"inactive": "Inactive",
590-
"draft": "Draft"
590+
"archived": "Archived",
591+
"published": "Published",
592+
"deprecated": "Deprecated"
591593
},
592594
"dataSources": {
593595
"upload": "Local Upload",
@@ -649,7 +651,7 @@
649651
},
650652
"confirm": {
651653
"deleteTitle": "Confirm delete this task?",
652-
"deleteDesc": "This task cannot be recovered after deletion. Please proceed with caution.",
654+
"deleteDesc": "Are you sure you want to delete \"{{itemName}}\"? This action cannot be undone.",
653655
"okText": "Delete",
654656
"cancelText": "Cancel"
655657
},
@@ -2461,6 +2463,12 @@
24612463
"retry": "Retry",
24622464
"goHome": "Go Home"
24632465
},
2466+
"deleteConfirm": {
2467+
"title": "Confirm Delete",
2468+
"message": "This action cannot be undone. Are you sure you want to delete {{itemName}}?",
2469+
"confirm": "Delete",
2470+
"cancel": "Cancel"
2471+
},
24642472
"searchControls": {
24652473
"searchPlaceholder": "Search...",
24662474
"startTime": "Start Time",

0 commit comments

Comments
 (0)