feat(clone): allow same-source cloning and disable cross-sourceType targets

- Remove the restriction that prevents cloning to the same data source
- Disable (instead of hide) target options with different sourceType
- Add sourceType label (MT4/MT5) to data source select options
- Preserve groupFilter/param1/param2 when cloning to same source
- Auto-reset target when source changes to incompatible sourceType
- Remove parent-level check requiring at least 2 data sources

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-14 21:39:01 +08:00
parent dfcba94cf5
commit 16999ca583
4 changed files with 174 additions and 97 deletions

View File

@@ -1,7 +1,7 @@
import { ArrowDownOutlined } from '@ant-design/icons';
import { ProFormDependency, ProFormSelect } from '@ant-design/pro-components';
import { Divider, Tag } from 'antd';
import { useContext, useEffect } from 'react';
import { Divider, Flex, Tag, Typography } from 'antd';
import { useContext, useEffect, useMemo } from 'react';
import { $t } from '@/utils/i18n';
import { MultiCopyPopupContext } from '../MultiCopyPopup';
import RuleCheckCard from './RuleCheckCard';
@@ -13,11 +13,52 @@ type Step1Props = {
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) {
// waitTime(1000).then(() => {
const sourceDataSourceId = dataSourceList[0]?.id || '';
const targetDataSourceId = dataSourceList[1]?.id || '';
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({
@@ -26,7 +67,6 @@ const Step1 = ({ formMapRef, setTargetDataSourceId }: Step1Props) => {
});
setTargetDataSourceId(targetDataSourceId);
}
// });
}
}, [dataSourceList]);
@@ -35,14 +75,15 @@ const Step1 = ({ formMapRef, setTargetDataSourceId }: Step1Props) => {
<ProFormSelect
name="sourceDataSourceId"
label={$t('pages.clones.step1.source')}
options={dataSourceList}
options={sourceOptions}
rules={[{ required: true, message: $t('form.required') }]}
fieldProps={{
allowClear: false,
fieldNames: {
label: 'sourceName',
label: 'label',
value: 'id',
},
optionLabelProp: 'label',
}}
/>
<ProFormDependency name={['sourceDataSourceId']}>
@@ -61,21 +102,20 @@ const Step1 = ({ formMapRef, setTargetDataSourceId }: Step1Props) => {
<ProFormDependency name={['sourceDataSourceId']}>
{({ sourceDataSourceId }) => {
if (!sourceDataSourceId) return null;
const dataSourceOptions = dataSourceList.filter(
(item) => item.id !== sourceDataSourceId,
);
const targetOptions = buildOptions(sourceDataSourceId);
return (
<ProFormSelect
name="targetDataSourceId"
label={$t('pages.clones.step1.target')}
options={dataSourceOptions}
options={targetOptions}
rules={[{ required: true, message: $t('form.required') }]}
fieldProps={{
allowClear: false,
fieldNames: {
label: 'sourceName',
label: 'label',
value: 'id',
},
optionLabelProp: 'label',
}}
onChange={(value: string | number | null) => {
setTargetDataSourceId(value || null);