diff --git a/react-ui/src/pages/AutoML/Create/index.tsx b/react-ui/src/pages/AutoML/Create/index.tsx index 743d8377..26e1866c 100644 --- a/react-ui/src/pages/AutoML/Create/index.tsx +++ b/react-ui/src/pages/AutoML/Create/index.tsx @@ -10,6 +10,7 @@ import { addAutoMLReq, getDatasetInfoReq, updateAutoMLReq } from '@/services/aut import { parseJsonText, trimCharacter } from '@/utils'; import { safeInvoke } from '@/utils/functional'; import { to } from '@/utils/promise'; +import SessionStorage from '@/utils/sessionStorage'; import { useNavigate, useParams } from '@umijs/max'; import { App, Button, Form } from 'antd'; import { omit } from 'lodash'; @@ -29,13 +30,25 @@ function CreateAutoML() { const id = safeInvoke(Number)(params.id); useEffect(() => { - if (id) { - getAutoMLInfo(); + // 复制和新建 + const recordId = SessionStorage.getItem(SessionStorage.autoMLRecordIDKey); + if (recordId && !Number.isNaN(Number(recordId))) { + getAutoMLInfo(Number(recordId), true); + } + return () => { + SessionStorage.removeItem(SessionStorage.autoMLRecordIDKey); + }; + }, []); + + useEffect(() => { + // 编辑 + if (id && !Number.isNaN(id)) { + getAutoMLInfo(id, false); } }, [id]); // 获取服务详情 - const getAutoMLInfo = async () => { + const getAutoMLInfo = async (id: number, isCopy = false) => { const [res] = await to(getDatasetInfoReq({ id })); if (res && res.data) { const autoMLInfo: AutoMLData = res.data; @@ -47,6 +60,8 @@ function CreateAutoML() { exclude_feature_preprocessor: exclude_feature_preprocessor_str, exclude_regressor: exclude_regressor_str, metrics: metrics_str, + ml_name: ml_name_str, + ...rest } = autoMLInfo; const include_classifier = include_classifier_str?.split(',').filter(Boolean); const include_feature_preprocessor = include_feature_preprocessor_str @@ -63,9 +78,10 @@ function CreateAutoML() { name: key, value, })); + const ml_name = isCopy ? `${ml_name_str}-copy` : ml_name_str; const formData = { - ...autoMLInfo, + ...rest, include_classifier, include_feature_preprocessor, include_regressor, @@ -73,6 +89,7 @@ function CreateAutoML() { exclude_feature_preprocessor, exclude_regressor, metrics, + ml_name, }; form.setFieldsValue(formData); @@ -135,7 +152,7 @@ function CreateAutoML() { let buttonText = '新建'; let title = '新增实验'; if (id) { - title = '更新实验'; + title = '编辑实验'; buttonText = '更新'; } diff --git a/react-ui/src/pages/AutoML/List/index.tsx b/react-ui/src/pages/AutoML/List/index.tsx index 9ef6eda6..be76e591 100644 --- a/react-ui/src/pages/AutoML/List/index.tsx +++ b/react-ui/src/pages/AutoML/List/index.tsx @@ -9,6 +9,7 @@ import { useCacheState } from '@/hooks/pageCacheState'; import { deleteAutoMLReq, getAutoMLListReq } from '@/services/autoML'; import themes from '@/styles/theme.less'; import { to } from '@/utils/promise'; +import SessionStorage from '@/utils/sessionStorage'; import tableCellRender, { TableCellValueType } from '@/utils/table'; import { modalConfirm } from '@/utils/ui'; import { useNavigate } from '@umijs/max'; @@ -90,7 +91,7 @@ function AutoMLList() { // 处理删除 const handleAutoMLDelete = (record: AutoMLData) => { modalConfirm({ - title: '删除后,该服务将不可恢复', + title: '删除后,该实验将不可恢复', content: '是否确认删除?', onOk: () => { deleteService(record); @@ -99,15 +100,21 @@ function AutoMLList() { }; // 创建、编辑 - const createService = (record?: AutoMLData) => { + const createService = (record?: AutoMLData, isCopy: boolean = false) => { setCacheState({ pagination, searchText, }); if (record) { - navigate(`/pipeline/autoML/edit/${record.id}`); + if (isCopy) { + SessionStorage.setItem(SessionStorage.autoMLRecordIDKey, record.id, false); + navigate(`/pipeline/autoML/create`); + } else { + navigate(`/pipeline/autoML/edit/${record.id}`); + } } else { + SessionStorage.setItem(SessionStorage.autoMLRecordIDKey, '', false); navigate(`/pipeline/autoML/create`); } }; @@ -139,7 +146,7 @@ function AutoMLList() { title: '序号', dataIndex: 'index', key: 'index', - width: '20%', + width: 80, render: tableCellRender(false, TableCellValueType.Index, { page: pagination.current! - 1, pageSize: pagination.pageSize!, @@ -166,7 +173,7 @@ function AutoMLList() { title: '状态', dataIndex: 'run_state', key: 'run_state', - width: '20%', + width: 100, render: RunStatusCell, }, { @@ -207,7 +214,7 @@ function AutoMLList() { size="small" key="edit" icon={} - onClick={() => createService(record)} + onClick={() => createService(record, false)} > 编辑 @@ -216,17 +223,11 @@ function AutoMLList() { size="small" key="copy" icon={} - onClick={() => toDetail(record)} + onClick={() => createService(record, true)} > 复制 - - + form.resetFields(['metrics'])}> 分类 回归 diff --git a/react-ui/src/pages/AutoML/components/CreateForm/TrialConfig.tsx b/react-ui/src/pages/AutoML/components/CreateForm/TrialConfig.tsx index d65b62e8..9201009a 100644 --- a/react-ui/src/pages/AutoML/components/CreateForm/TrialConfig.tsx +++ b/react-ui/src/pages/AutoML/components/CreateForm/TrialConfig.tsx @@ -8,6 +8,14 @@ import styles from './index.less'; function TrialConfig() { const form = Form.useFormInstance(); const task_type = Form.useWatch('task_type', form); + const metrics = Form.useWatch('metrics', form) || []; + const selectedMetrics = metrics + .map((item: { name: string; value: number }) => item?.name) + .filter(Boolean); + const allMetricsOptions = + task_type === AutoMLTaskType.Classification ? classificationMetrics : regressionMetrics; + const metricsOptions = allMetricsOptions.filter((item) => !selectedMetrics.includes(item.label)); + return ( <> - @@ -30,9 +37,9 @@ function TrialConfig() { {(fields, { add, remove }) => ( <> {fields.map(({ key, name, ...restField }, index) => ( - + - : + : -
+ )} -
+
))} {fields.length === 0 && ( diff --git a/react-ui/src/pages/AutoML/components/CreateForm/index.less b/react-ui/src/pages/AutoML/components/CreateForm/index.less index 8e8414a5..5ee9aacc 100644 --- a/react-ui/src/pages/AutoML/components/CreateForm/index.less +++ b/react-ui/src/pages/AutoML/components/CreateForm/index.less @@ -6,136 +6,136 @@ } } -.command { - width: 83.33%; - margin-bottom: 20px; - border: 1px solid rgba(234, 234, 234, 0.8); - border-radius: 4px; - &__header { - height: 50px; - padding-left: 8px; - color: @text-color; - font-size: @font-size; - background: #f8f8f9; - border-radius: 4px 4px 0px 0px; - - &__name { - flex: none; - width: 100px; - } - &__command { - flex: 1; - margin-right: 15px; - } - - &__operation { - flex: none; - width: 100px; - } - } - &__body { - padding: 8px; - border-bottom: 1px solid rgba(234, 234, 234, 0.8); - - &:last-child { - border-bottom: none; - } - - &__name { - flex: none; - width: 100px; - } - - &__command { - flex: 1; - margin-right: 15px; - margin-bottom: 0 !important; - } - - &__operation { - flex: none; - width: 100px; - } - } - - &__add { - display: flex; - align-items: center; - justify-content: center; - padding: 15px 0; - } -} - -.hyper-parameter { - width: 83.33%; - margin-bottom: 20px; - border: 1px solid rgba(234, 234, 234, 0.8); - border-radius: 4px; - &__header { - height: 50px; - padding-left: 8px; - color: @text-color; - font-size: @font-size; - background: #f8f8f9; - border-radius: 4px 4px 0px 0px; - - &__name, - &__type, - &__space { - flex: 1; - margin-right: 15px; - } - - &__operation { - flex: none; - width: 100px; - } - } - &__body { - padding: 8px; - border-bottom: 1px solid rgba(234, 234, 234, 0.8); - - &:last-child { - border-bottom: none; - } - - &__name, - &__type, - &__space { - flex: 1; - margin-right: 15px; - margin-bottom: 0 !important; - } - - &__operation { - flex: none; - width: 100px; - } - } - - &__add { - display: flex; - align-items: center; - justify-content: center; - padding: 15px 0; - } -} - -.trial-metrics { - width: calc(41.67% - 6px); - margin-bottom: 20px; - padding: 0 20px; - border: 1px dashed #e0e0e0; - border-radius: 8px; -} - -.upload-tip { - margin-top: 5px; - color: @text-color-secondary; - font-size: 14px; -} - -.upload-button { - height: 46px; - font-size: 15px; -} +// .command { +// width: 83.33%; +// margin-bottom: 20px; +// border: 1px solid rgba(234, 234, 234, 0.8); +// border-radius: 4px; +// &__header { +// height: 50px; +// padding-left: 8px; +// color: @text-color; +// font-size: @font-size; +// background: #f8f8f9; +// border-radius: 4px 4px 0px 0px; + +// &__name { +// flex: none; +// width: 100px; +// } +// &__command { +// flex: 1; +// margin-right: 15px; +// } + +// &__operation { +// flex: none; +// width: 100px; +// } +// } +// &__body { +// padding: 8px; +// border-bottom: 1px solid rgba(234, 234, 234, 0.8); + +// &:last-child { +// border-bottom: none; +// } + +// &__name { +// flex: none; +// width: 100px; +// } + +// &__command { +// flex: 1; +// margin-right: 15px; +// margin-bottom: 0 !important; +// } + +// &__operation { +// flex: none; +// width: 100px; +// } +// } + +// &__add { +// display: flex; +// align-items: center; +// justify-content: center; +// padding: 15px 0; +// } +// } + +// .hyper-parameter { +// width: 83.33%; +// margin-bottom: 20px; +// border: 1px solid rgba(234, 234, 234, 0.8); +// border-radius: 4px; +// &__header { +// height: 50px; +// padding-left: 8px; +// color: @text-color; +// font-size: @font-size; +// background: #f8f8f9; +// border-radius: 4px 4px 0px 0px; + +// &__name, +// &__type, +// &__space { +// flex: 1; +// margin-right: 15px; +// } + +// &__operation { +// flex: none; +// width: 100px; +// } +// } +// &__body { +// padding: 8px; +// border-bottom: 1px solid rgba(234, 234, 234, 0.8); + +// &:last-child { +// border-bottom: none; +// } + +// &__name, +// &__type, +// &__space { +// flex: 1; +// margin-right: 15px; +// margin-bottom: 0 !important; +// } + +// &__operation { +// flex: none; +// width: 100px; +// } +// } + +// &__add { +// display: flex; +// align-items: center; +// justify-content: center; +// padding: 15px 0; +// } +// } + +// .trial-metrics { +// width: calc(41.67% - 6px); +// margin-bottom: 20px; +// padding: 0 20px; +// border: 1px dashed #e0e0e0; +// border-radius: 8px; +// } + +// .upload-tip { +// margin-top: 5px; +// color: @text-color-secondary; +// font-size: 14px; +// } + +// .upload-button { +// height: 46px; +// font-size: 15px; +// } diff --git a/react-ui/src/pages/AutoML/components/ExecuteScheduleCell/index.tsx b/react-ui/src/pages/AutoML/components/ExecuteScheduleCell/index.tsx index b1b9ae29..c2af2074 100644 --- a/react-ui/src/pages/AutoML/components/ExecuteScheduleCell/index.tsx +++ b/react-ui/src/pages/AutoML/components/ExecuteScheduleCell/index.tsx @@ -6,18 +6,19 @@ import styles from './index.less'; -function ExecuteScheduleCell(status?: any) { +function ExecuteScheduleCell(progress?: number) { + const width = (progress || 0) * 100 + '%'; return (
- + {/* 1/2 - + */}
); } diff --git a/react-ui/src/pages/AutoML/types.ts b/react-ui/src/pages/AutoML/types.ts index 59496c64..bd61f941 100644 --- a/react-ui/src/pages/AutoML/types.ts +++ b/react-ui/src/pages/AutoML/types.ts @@ -51,4 +51,7 @@ export type AutoMLData = { exclude_feature_preprocessor?: string; exclude_regressor?: string; dataset?: string; -}; +} & Omit< + FormData, + 'metrics|dataset|include_classifier|include_feature_preprocessor|include_regressor|exclude_classifier|exclude_feature_preprocessor|exclude_regressor' +>; diff --git a/react-ui/src/utils/sessionStorage.ts b/react-ui/src/utils/sessionStorage.ts index 8ffa8836..b71a35fd 100644 --- a/react-ui/src/utils/sessionStorage.ts +++ b/react-ui/src/utils/sessionStorage.ts @@ -11,6 +11,8 @@ export default class SessionStorage { static readonly editorUrlKey = 'editor-url'; // 客户端信息 static readonly clientInfoKey = 'client-info'; + // 自动机器学习记录ID + static readonly autoMLRecordIDKey = 'auto-ml-record-id'; static getItem(key: string, isObject: boolean = false) { const jsonStr = sessionStorage.getItem(key);