new
This commit is contained in:
205
src/pages/rules/components/CopyRule/comp/BatchCloneModal.tsx
Normal file
205
src/pages/rules/components/CopyRule/comp/BatchCloneModal.tsx
Normal file
@@ -0,0 +1,205 @@
|
||||
import {
|
||||
CheckCircleOutlined,
|
||||
CloseCircleOutlined,
|
||||
ExclamationCircleOutlined,
|
||||
SyncOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import {
|
||||
Alert,
|
||||
type AlertProps,
|
||||
Button,
|
||||
Divider,
|
||||
Flex,
|
||||
List,
|
||||
Modal,
|
||||
Progress,
|
||||
Space,
|
||||
Tag,
|
||||
Tooltip,
|
||||
} from 'antd';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { processSingleRule } from '@/pages/rules/utils';
|
||||
import { $t } from '@/utils/i18n';
|
||||
|
||||
type BatchCloneModalProps = {
|
||||
visible: boolean;
|
||||
selectedRows: Partial<API.RuleListItem>[];
|
||||
onClose?: (cb?: () => void) => void;
|
||||
onFinish?: (cb?: () => void) => void;
|
||||
};
|
||||
const BatchCloneModal = ({
|
||||
visible,
|
||||
selectedRows,
|
||||
onClose,
|
||||
onFinish,
|
||||
}: BatchCloneModalProps) => {
|
||||
const [tasks, setTasks] = useState<any[]>([]);
|
||||
const [isRunning, setIsRunning] = useState(false);
|
||||
const [currentRunning, setCurrentRunning] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
const initialTasks = selectedRows.map((row) => ({
|
||||
name: row.name,
|
||||
status: 'waiting', // waiting, processing, success, error
|
||||
errorMsg: '',
|
||||
}));
|
||||
setTasks(initialTasks);
|
||||
}, [visible, selectedRows]);
|
||||
|
||||
const finishedTasks = useMemo(
|
||||
() => tasks.filter((t) => ['success', 'warning'].includes(t.status)),
|
||||
[tasks],
|
||||
);
|
||||
|
||||
const finishedMessage = useMemo(() => {
|
||||
const hasWarning = finishedTasks.find((t) => t.status === 'warning');
|
||||
const type: AlertProps['type'] = hasWarning
|
||||
? 'warning'
|
||||
: finishedTasks.length === tasks.length
|
||||
? 'success'
|
||||
: 'error';
|
||||
return {
|
||||
type,
|
||||
message:
|
||||
type === 'success'
|
||||
? $t('pages.clones.step3.success')
|
||||
: type === 'warning'
|
||||
? $t('pages.clones.step3.warning')
|
||||
: $t('pages.clones.step3.error'),
|
||||
};
|
||||
}, [finishedTasks, tasks]);
|
||||
|
||||
const startBatch = async () => {
|
||||
setIsRunning(true);
|
||||
for (let i = 0; i < selectedRows.length; i++) {
|
||||
setCurrentRunning(i + 1);
|
||||
setTasks((prev) => {
|
||||
const next = [...prev];
|
||||
next[i].status = 'processing';
|
||||
return next;
|
||||
});
|
||||
try {
|
||||
await processSingleRule({ payload: selectedRows[i] });
|
||||
|
||||
setTasks((prev) => {
|
||||
const next = [...prev];
|
||||
next[i].status = 'success';
|
||||
return next;
|
||||
});
|
||||
} catch (e: any) {
|
||||
setTasks((prev) => {
|
||||
const next = [...prev];
|
||||
next[i].status = [1, 2, 3].includes(e.errType) ? 'warning' : 'error';
|
||||
next[i].errorMsg = e.errMsg ? $t(e.errMsg) : e.respMessage;
|
||||
return next;
|
||||
});
|
||||
}
|
||||
}
|
||||
setIsRunning(false);
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
setTasks([]);
|
||||
setIsRunning(false);
|
||||
setCurrentRunning(0);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={$t('pages.clones.step3.progress')}
|
||||
open={visible}
|
||||
closable={false}
|
||||
maskClosable={false}
|
||||
footer={null}
|
||||
centered
|
||||
destroyOnHidden
|
||||
>
|
||||
<Progress
|
||||
percent={Math.round((finishedTasks.length / tasks.length) * 100)}
|
||||
style={{ marginBottom: 20 }}
|
||||
/>
|
||||
<div style={{ maxHeight: '80vh', overflowY: 'auto' }}>
|
||||
<List
|
||||
size="small"
|
||||
dataSource={tasks}
|
||||
renderItem={(item) => (
|
||||
<List.Item style={{ display: 'flex', flexWrap: 'wrap' }}>
|
||||
<span style={{ whiteSpace: 'nowrap' }}>{item.name}</span>
|
||||
{item.status === 'processing' && (
|
||||
<Tag color="blue" icon={<SyncOutlined spin />}>
|
||||
{$t('pages.clones.step3.status1')}
|
||||
</Tag>
|
||||
)}
|
||||
{item.status === 'success' && (
|
||||
<Tag color="green" icon={<CheckCircleOutlined />}>
|
||||
{$t('pages.clones.step3.status2')}
|
||||
</Tag>
|
||||
)}
|
||||
{item.status === 'error' && (
|
||||
<Tooltip
|
||||
title={item.errorMsg || $t('pages.clones.step3.status3')}
|
||||
>
|
||||
<Tag color="red" icon={<CloseCircleOutlined />}>
|
||||
{$t('pages.clones.step3.status3')}
|
||||
</Tag>
|
||||
<ExclamationCircleOutlined />
|
||||
</Tooltip>
|
||||
)}
|
||||
{item.status === 'warning' && (
|
||||
<Tooltip
|
||||
title={item.errorMsg || $t('pages.clones.step3.status4')}
|
||||
>
|
||||
<Tag color="orange" icon={<CloseCircleOutlined />}>
|
||||
{$t('pages.clones.step3.status4')}
|
||||
</Tag>
|
||||
<ExclamationCircleOutlined />
|
||||
</Tooltip>
|
||||
)}
|
||||
{item.status === 'waiting' && (
|
||||
<Tag color="default">{$t('pages.clones.step3.status5')}</Tag>
|
||||
)}
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<Space direction="vertical" className="w-100">
|
||||
<Divider style={{ margin: '10px 0' }} />
|
||||
{!isRunning && currentRunning === tasks.length && (
|
||||
<>
|
||||
<Alert
|
||||
message={finishedMessage.message}
|
||||
type={finishedMessage.type}
|
||||
/>
|
||||
<Flex justify="flex-end">
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
onFinish?.(handleReset);
|
||||
}}
|
||||
>
|
||||
{$t('pages.clones.step3.understand')}
|
||||
</Button>
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!isRunning && currentRunning === 0 && (
|
||||
<Flex justify="flex-end" gap={10}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
onClose?.(handleReset);
|
||||
}}
|
||||
>
|
||||
{$t('pages.model.cancel')}
|
||||
</Button>
|
||||
<Button type="primary" onClick={startBatch}>
|
||||
{$t('pages.clones.step3.start')}
|
||||
</Button>
|
||||
</Flex>
|
||||
)}
|
||||
</Space>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default BatchCloneModal;
|
||||
Reference in New Issue
Block a user