Files
alert/src/pages/rules/components/CopyRule/comp/Step1.tsx
Johnton Chen 3c636bcaa2 refactor(copy-rule): extract context and improve batch clone modal
- Extract MultiCopyPopupContext to standalone context.ts file
- Add Task type and improve type safety in BatchCloneModal
- Use useRef/useCallback to stabilize batch clone handlers
- Add cleanup effect for selectRules in RuleCheckCard on unmount

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-15 23:05:56 +08:00

132 lines
4.0 KiB
TypeScript

import { ArrowDownOutlined } from '@ant-design/icons';
import { ProFormDependency, ProFormSelect } from '@ant-design/pro-components';
import { Divider, Flex, Tag, Typography } from 'antd';
import { useContext, useEffect, useMemo } from 'react';
import { $t } from '@/utils/i18n';
import { MultiCopyPopupContext } from '../context';
import RuleCheckCard from './RuleCheckCard';
type Step1Props = {
formMapRef: any;
setTargetDataSourceId: (id: string | number | null) => void;
};
const Step1 = ({ formMapRef, setTargetDataSourceId }: Step1Props) => {
const { dataSourceList } = useContext(MultiCopyPopupContext);
const buildOptions = (sourceId?: string | number) => {
const sourceType = dataSourceList.find((item) => item.id === sourceId)
?.sourceType;
return dataSourceList.map((item) => ({
...item,
disabled: item.sourceType !== sourceType,
label: (
<Flex justify="space-between" align="center">
<span>{item.sourceName}</span>
<Typography.Text
type="secondary"
style={{ fontSize: 12, marginLeft: 8 }}
>
MT{item.sourceType}
</Typography.Text>
</Flex>
),
}));
};
const sourceOptions = useMemo(
() => dataSourceList.map((item) => ({
...item,
label: (
<Flex justify="space-between" align="center">
<span>{item.sourceName}</span>
<Typography.Text
type="secondary"
style={{ fontSize: 12, marginLeft: 8 }}
>
MT{item.sourceType}
</Typography.Text>
</Flex>
),
})),
[dataSourceList],
);
useEffect(() => {
if (dataSourceList?.length) {
const sourceItem = dataSourceList[0];
const sourceDataSourceId = sourceItem?.id || '';
const sameTypeItem = dataSourceList.find(
(item) => item.id !== sourceDataSourceId && item.sourceType === sourceItem?.sourceType,
);
const targetDataSourceId = sameTypeItem?.id || sourceDataSourceId;
const curFormRef = formMapRef?.current?.[0]?.current;
if (curFormRef) {
curFormRef?.setFieldsValue({
sourceDataSourceId,
targetDataSourceId,
});
setTargetDataSourceId(targetDataSourceId);
}
}
}, [dataSourceList]);
return (
<>
<ProFormSelect
name="sourceDataSourceId"
label={$t('pages.clones.step1.source')}
options={sourceOptions}
rules={[{ required: true, message: $t('form.required') }]}
fieldProps={{
allowClear: false,
fieldNames: {
label: 'label',
value: 'id',
},
optionLabelProp: 'label',
}}
/>
<ProFormDependency name={['sourceDataSourceId']}>
{({ sourceDataSourceId }) => {
if (!sourceDataSourceId) return null;
return <RuleCheckCard dataSourceId={sourceDataSourceId} />;
}}
</ProFormDependency>
<Divider>
<Tag style={{ borderRadius: 30, padding: '4px 16px' }}>
<ArrowDownOutlined /> {$t('pages.clones.step1.dividerText')}
</Tag>
</Divider>
<ProFormDependency name={['sourceDataSourceId']}>
{({ sourceDataSourceId }) => {
if (!sourceDataSourceId) return null;
const targetOptions = buildOptions(sourceDataSourceId);
return (
<ProFormSelect
name="targetDataSourceId"
label={$t('pages.clones.step1.target')}
options={targetOptions}
rules={[{ required: true, message: $t('form.required') }]}
fieldProps={{
allowClear: false,
fieldNames: {
label: 'label',
value: 'id',
},
optionLabelProp: 'label',
}}
onChange={(value: string | number | null) => {
setTargetDataSourceId(value || null);
}}
/>
);
}}
</ProFormDependency>
</>
);
};
export default Step1;