|
|
@@ -5,9 +5,17 @@ |
|
|
*/ |
|
|
*/ |
|
|
import KFIcon from '@/components/KFIcon'; |
|
|
import KFIcon from '@/components/KFIcon'; |
|
|
import PageTitle from '@/components/PageTitle'; |
|
|
import PageTitle from '@/components/PageTitle'; |
|
|
|
|
|
import { ExperimentStatus } from '@/enums'; |
|
|
import { useCacheState } from '@/hooks/pageCacheState'; |
|
|
import { useCacheState } from '@/hooks/pageCacheState'; |
|
|
import { deleteAutoMLReq, getAutoMLListReq, runAutoMLReq } from '@/services/autoML'; |
|
|
|
|
|
|
|
|
import { experimentStatusInfo } from '@/pages/Experiment/status'; |
|
|
|
|
|
import { |
|
|
|
|
|
deleteAutoMLReq, |
|
|
|
|
|
getAutoMLListReq, |
|
|
|
|
|
getExperimentInsListReq, |
|
|
|
|
|
runAutoMLReq, |
|
|
|
|
|
} from '@/services/autoML'; |
|
|
import themes from '@/styles/theme.less'; |
|
|
import themes from '@/styles/theme.less'; |
|
|
|
|
|
import { type ExperimentInstance as ExperimentInstanceData } from '@/types'; |
|
|
import { to } from '@/utils/promise'; |
|
|
import { to } from '@/utils/promise'; |
|
|
import SessionStorage from '@/utils/sessionStorage'; |
|
|
import SessionStorage from '@/utils/sessionStorage'; |
|
|
import tableCellRender, { TableCellValueType } from '@/utils/table'; |
|
|
import tableCellRender, { TableCellValueType } from '@/utils/table'; |
|
|
@@ -25,8 +33,7 @@ import { |
|
|
import { type SearchProps } from 'antd/es/input'; |
|
|
import { type SearchProps } from 'antd/es/input'; |
|
|
import classNames from 'classnames'; |
|
|
import classNames from 'classnames'; |
|
|
import { useEffect, useState } from 'react'; |
|
|
import { useEffect, useState } from 'react'; |
|
|
import ExecuteScheduleCell from '../components/ExecuteScheduleCell'; |
|
|
|
|
|
import RunStatusCell from '../components/RunStatusCell'; |
|
|
|
|
|
|
|
|
import ExperimentInstance from '../components/ExperimentInstance'; |
|
|
import { AutoMLData } from '../types'; |
|
|
import { AutoMLData } from '../types'; |
|
|
import styles from './index.less'; |
|
|
import styles from './index.less'; |
|
|
|
|
|
|
|
|
@@ -38,6 +45,9 @@ function AutoMLList() { |
|
|
const [inputText, setInputText] = useState(cacheState?.searchText); |
|
|
const [inputText, setInputText] = useState(cacheState?.searchText); |
|
|
const [tableData, setTableData] = useState<AutoMLData[]>([]); |
|
|
const [tableData, setTableData] = useState<AutoMLData[]>([]); |
|
|
const [total, setTotal] = useState(0); |
|
|
const [total, setTotal] = useState(0); |
|
|
|
|
|
const [experimentInsList, setExperimentInsList] = useState<ExperimentInstanceData[]>([]); |
|
|
|
|
|
const [expandedRowKeys, setExpandedRowKeys] = useState<number[]>([]); |
|
|
|
|
|
const [experimentInsTotal, setExperimentInsTotal] = useState(0); |
|
|
const [pagination, setPagination] = useState<TablePaginationConfig>( |
|
|
const [pagination, setPagination] = useState<TablePaginationConfig>( |
|
|
cacheState?.pagination ?? { |
|
|
cacheState?.pagination ?? { |
|
|
current: 1, |
|
|
current: 1, |
|
|
@@ -46,11 +56,11 @@ function AutoMLList() { |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
useEffect(() => { |
|
|
getServiceList(); |
|
|
|
|
|
|
|
|
getAutoMLList(); |
|
|
}, [pagination, searchText]); |
|
|
}, [pagination, searchText]); |
|
|
|
|
|
|
|
|
// 获取模型部署服务列表 |
|
|
|
|
|
const getServiceList = async () => { |
|
|
|
|
|
|
|
|
// 获取自主机器学习列表 |
|
|
|
|
|
const getAutoMLList = async () => { |
|
|
const params: Record<string, any> = { |
|
|
const params: Record<string, any> = { |
|
|
page: pagination.current! - 1, |
|
|
page: pagination.current! - 1, |
|
|
size: pagination.pageSize, |
|
|
size: pagination.pageSize, |
|
|
@@ -64,8 +74,13 @@ function AutoMLList() { |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 搜索 |
|
|
|
|
|
const onSearch: SearchProps['onSearch'] = (value) => { |
|
|
|
|
|
setSearchText(value); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
// 删除模型部署 |
|
|
// 删除模型部署 |
|
|
const deleteService = async (record: AutoMLData) => { |
|
|
|
|
|
|
|
|
const deleteAutoML = async (record: AutoMLData) => { |
|
|
const [res] = await to(deleteAutoMLReq(record.id)); |
|
|
const [res] = await to(deleteAutoMLReq(record.id)); |
|
|
if (res) { |
|
|
if (res) { |
|
|
message.success('删除成功'); |
|
|
message.success('删除成功'); |
|
|
@@ -78,29 +93,24 @@ function AutoMLList() { |
|
|
current: 1, |
|
|
current: 1, |
|
|
})); |
|
|
})); |
|
|
} else { |
|
|
} else { |
|
|
getServiceList(); |
|
|
|
|
|
|
|
|
getAutoMLList(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
// 搜索 |
|
|
|
|
|
const onSearch: SearchProps['onSearch'] = (value) => { |
|
|
|
|
|
setSearchText(value); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 处理删除 |
|
|
// 处理删除 |
|
|
const handleAutoMLDelete = (record: AutoMLData) => { |
|
|
const handleAutoMLDelete = (record: AutoMLData) => { |
|
|
modalConfirm({ |
|
|
modalConfirm({ |
|
|
title: '删除后,该实验将不可恢复', |
|
|
title: '删除后,该实验将不可恢复', |
|
|
content: '是否确认删除?', |
|
|
content: '是否确认删除?', |
|
|
onOk: () => { |
|
|
onOk: () => { |
|
|
deleteService(record); |
|
|
|
|
|
|
|
|
deleteAutoML(record); |
|
|
}, |
|
|
}, |
|
|
}); |
|
|
}); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
// 创建、编辑 |
|
|
|
|
|
const createService = (record?: AutoMLData, isCopy: boolean = false) => { |
|
|
|
|
|
|
|
|
// 创建、编辑、复制自动机器学习 |
|
|
|
|
|
const createAutoML = (record?: AutoMLData, isCopy: boolean = false) => { |
|
|
setCacheState({ |
|
|
setCacheState({ |
|
|
pagination, |
|
|
pagination, |
|
|
searchText, |
|
|
searchText, |
|
|
@@ -119,8 +129,8 @@ function AutoMLList() { |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
// 查看详情 |
|
|
|
|
|
const toDetail = (record: AutoMLData) => { |
|
|
|
|
|
|
|
|
// 查看自动机器学习详情 |
|
|
|
|
|
const gotoDetail = (record: AutoMLData) => { |
|
|
setCacheState({ |
|
|
setCacheState({ |
|
|
pagination, |
|
|
pagination, |
|
|
searchText, |
|
|
searchText, |
|
|
@@ -129,24 +139,90 @@ function AutoMLList() { |
|
|
navigate(`/pipeline/autoML/info/${record.id}`); |
|
|
navigate(`/pipeline/autoML/info/${record.id}`); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
// 启动 |
|
|
|
|
|
|
|
|
// 启动自动机器学习 |
|
|
const startAutoML = async (record: AutoMLData) => { |
|
|
const startAutoML = async (record: AutoMLData) => { |
|
|
const [res] = await to(runAutoMLReq(record.id)); |
|
|
const [res] = await to(runAutoMLReq(record.id)); |
|
|
if (res) { |
|
|
if (res) { |
|
|
message.success('操作成功'); |
|
|
message.success('操作成功'); |
|
|
getServiceList(); |
|
|
|
|
|
|
|
|
getAutoMLList(); |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
// 停止 |
|
|
|
|
|
const stopAutoML = async (record: AutoMLData) => { |
|
|
|
|
|
const [res] = await to(runAutoMLReq(record.id)); |
|
|
|
|
|
if (res) { |
|
|
|
|
|
message.success('操作成功'); |
|
|
|
|
|
getServiceList(); |
|
|
|
|
|
|
|
|
// --------------------------- 实验实例 --------------------------- |
|
|
|
|
|
// 获取实验实例列表 |
|
|
|
|
|
const getExperimentInsList = async (autoMLId: number, page: number) => { |
|
|
|
|
|
const params = { |
|
|
|
|
|
autoMlId: autoMLId, |
|
|
|
|
|
page: page, |
|
|
|
|
|
size: 5, |
|
|
|
|
|
}; |
|
|
|
|
|
const [res] = await to(getExperimentInsListReq(params)); |
|
|
|
|
|
if (res && res.data) { |
|
|
|
|
|
const { content = [], totalElements = 0 } = res.data; |
|
|
|
|
|
try { |
|
|
|
|
|
if (page === 0) { |
|
|
|
|
|
setExperimentInsList(content); |
|
|
|
|
|
} else { |
|
|
|
|
|
setExperimentInsList((prev) => [...prev, ...content]); |
|
|
|
|
|
} |
|
|
|
|
|
setExperimentInsTotal(totalElements); |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
console.error('JSON parse error: ', error); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
// 展开实例 |
|
|
|
|
|
const handleExpandChange = (expanded: boolean, record: AutoMLData) => { |
|
|
|
|
|
setExperimentInsList([]); |
|
|
|
|
|
if (expanded) { |
|
|
|
|
|
setExpandedRowKeys([record.id]); |
|
|
|
|
|
getExperimentInsList(record.id, 0); |
|
|
|
|
|
} else { |
|
|
|
|
|
setExpandedRowKeys([]); |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 跳转到实验实例详情 |
|
|
|
|
|
const gotoInstanceInfo = (item, record) => { |
|
|
|
|
|
navigate({ pathname: `/pipeline/experiment/instance/${record.workflow_id}/${item.id}` }); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 刷新实验实例列表 |
|
|
|
|
|
const refreshExperimentIns = (experimentId: number) => { |
|
|
|
|
|
getExperimentInsList(experimentId, 0); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 加载更多实验实例 |
|
|
|
|
|
const loadMoreExperimentIns = () => { |
|
|
|
|
|
const page = Math.round(experimentInsList.length / 5); |
|
|
|
|
|
const autoMLId = expandedRowKeys[0]; |
|
|
|
|
|
getExperimentInsList(autoMLId, page); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 实验实例终止 |
|
|
|
|
|
const handleInstanceTerminate = async (experimentIns: ExperimentInstanceData) => { |
|
|
|
|
|
// 刷新实验列表 |
|
|
|
|
|
refreshExperimentList(); |
|
|
|
|
|
setExperimentInsList((prevList) => { |
|
|
|
|
|
return prevList.map((item) => { |
|
|
|
|
|
if (item.id === experimentIns.id) { |
|
|
|
|
|
return { |
|
|
|
|
|
...item, |
|
|
|
|
|
status: ExperimentStatus.Terminated, |
|
|
|
|
|
}; |
|
|
|
|
|
} |
|
|
|
|
|
return item; |
|
|
|
|
|
}); |
|
|
|
|
|
}); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 刷新实验列表状态, |
|
|
|
|
|
// 目前是直接刷新实验列表,后续需要优化,只刷新状态 |
|
|
|
|
|
const refreshExperimentList = () => { |
|
|
|
|
|
getAutoMLList(); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// --------------------------- Table --------------------------- |
|
|
// 分页切换 |
|
|
// 分页切换 |
|
|
const handleTableChange: TableProps<AutoMLData>['onChange'] = ( |
|
|
const handleTableChange: TableProps<AutoMLData>['onChange'] = ( |
|
|
pagination, |
|
|
pagination, |
|
|
@@ -160,47 +236,23 @@ function AutoMLList() { |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
const columns: TableProps<AutoMLData>['columns'] = [ |
|
|
const columns: TableProps<AutoMLData>['columns'] = [ |
|
|
{ |
|
|
|
|
|
title: '序号', |
|
|
|
|
|
dataIndex: 'index', |
|
|
|
|
|
key: 'index', |
|
|
|
|
|
width: 80, |
|
|
|
|
|
render: tableCellRender(false, TableCellValueType.Index, { |
|
|
|
|
|
page: pagination.current! - 1, |
|
|
|
|
|
pageSize: pagination.pageSize!, |
|
|
|
|
|
}), |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
{ |
|
|
title: '实验名称', |
|
|
title: '实验名称', |
|
|
dataIndex: 'ml_name', |
|
|
dataIndex: 'ml_name', |
|
|
key: 'ml_name', |
|
|
key: 'ml_name', |
|
|
width: '20%', |
|
|
|
|
|
|
|
|
width: '16%', |
|
|
render: tableCellRender(false, TableCellValueType.Link, { |
|
|
render: tableCellRender(false, TableCellValueType.Link, { |
|
|
onClick: toDetail, |
|
|
|
|
|
|
|
|
onClick: gotoDetail, |
|
|
}), |
|
|
}), |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
title: '实验描述', |
|
|
title: '实验描述', |
|
|
dataIndex: 'ml_description', |
|
|
dataIndex: 'ml_description', |
|
|
key: 'ml_description', |
|
|
key: 'ml_description', |
|
|
width: '20%', |
|
|
|
|
|
render: tableCellRender(true), |
|
|
render: tableCellRender(true), |
|
|
ellipsis: { showTitle: false }, |
|
|
ellipsis: { showTitle: false }, |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
|
|
|
title: '状态', |
|
|
|
|
|
dataIndex: 'run_state', |
|
|
|
|
|
key: 'run_state', |
|
|
|
|
|
width: 100, |
|
|
|
|
|
render: RunStatusCell, |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
title: '实验实例执行进度', |
|
|
|
|
|
dataIndex: 'progress', |
|
|
|
|
|
key: 'progress', |
|
|
|
|
|
render: ExecuteScheduleCell, |
|
|
|
|
|
width: 180, |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
{ |
|
|
{ |
|
|
title: '创建时间', |
|
|
title: '创建时间', |
|
|
dataIndex: 'update_time', |
|
|
dataIndex: 'update_time', |
|
|
@@ -210,26 +262,53 @@ function AutoMLList() { |
|
|
ellipsis: { showTitle: false }, |
|
|
ellipsis: { showTitle: false }, |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
title: '修改时间', |
|
|
|
|
|
dataIndex: 'update_time', |
|
|
|
|
|
key: 'update_time', |
|
|
|
|
|
width: '20%', |
|
|
|
|
|
render: tableCellRender(true, TableCellValueType.Date), |
|
|
|
|
|
ellipsis: { showTitle: false }, |
|
|
|
|
|
|
|
|
title: '最近五次运行状态', |
|
|
|
|
|
dataIndex: 'status_list', |
|
|
|
|
|
key: 'status_list', |
|
|
|
|
|
width: 200, |
|
|
|
|
|
render: (text) => { |
|
|
|
|
|
const newText: string[] = text && text.replace(/\s+/g, '').split(','); |
|
|
|
|
|
return ( |
|
|
|
|
|
<> |
|
|
|
|
|
{newText && newText.length > 0 |
|
|
|
|
|
? newText.map((item, index) => { |
|
|
|
|
|
return ( |
|
|
|
|
|
<img |
|
|
|
|
|
style={{ width: '17px', marginRight: '6px' }} |
|
|
|
|
|
key={index} |
|
|
|
|
|
src={experimentStatusInfo[item as ExperimentStatus].icon} |
|
|
|
|
|
draggable={false} |
|
|
|
|
|
alt="" |
|
|
|
|
|
/> |
|
|
|
|
|
); |
|
|
|
|
|
}) |
|
|
|
|
|
: null} |
|
|
|
|
|
</> |
|
|
|
|
|
); |
|
|
|
|
|
}, |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
title: '操作', |
|
|
title: '操作', |
|
|
dataIndex: 'operation', |
|
|
dataIndex: 'operation', |
|
|
width: 320, |
|
|
|
|
|
|
|
|
width: 360, |
|
|
key: 'operation', |
|
|
key: 'operation', |
|
|
render: (_: any, record: AutoMLData) => ( |
|
|
render: (_: any, record: AutoMLData) => ( |
|
|
<div> |
|
|
<div> |
|
|
|
|
|
<Button |
|
|
|
|
|
type="link" |
|
|
|
|
|
size="small" |
|
|
|
|
|
key="start" |
|
|
|
|
|
icon={<KFIcon type="icon-yunhang" />} |
|
|
|
|
|
onClick={() => startAutoML(record)} |
|
|
|
|
|
> |
|
|
|
|
|
运行 |
|
|
|
|
|
</Button> |
|
|
<Button |
|
|
<Button |
|
|
type="link" |
|
|
type="link" |
|
|
size="small" |
|
|
size="small" |
|
|
key="edit" |
|
|
key="edit" |
|
|
icon={<KFIcon type="icon-bianji" />} |
|
|
icon={<KFIcon type="icon-bianji" />} |
|
|
onClick={() => createService(record, false)} |
|
|
|
|
|
|
|
|
onClick={() => createAutoML(record, false)} |
|
|
> |
|
|
> |
|
|
编辑 |
|
|
编辑 |
|
|
</Button> |
|
|
</Button> |
|
|
@@ -238,31 +317,11 @@ function AutoMLList() { |
|
|
size="small" |
|
|
size="small" |
|
|
key="copy" |
|
|
key="copy" |
|
|
icon={<KFIcon type="icon-fuzhi" />} |
|
|
icon={<KFIcon type="icon-fuzhi" />} |
|
|
onClick={() => createService(record, true)} |
|
|
|
|
|
|
|
|
onClick={() => createAutoML(record, true)} |
|
|
> |
|
|
> |
|
|
复制 |
|
|
复制 |
|
|
</Button> |
|
|
</Button> |
|
|
{record.run_state === 'Running' ? ( |
|
|
|
|
|
<Button |
|
|
|
|
|
type="link" |
|
|
|
|
|
size="small" |
|
|
|
|
|
key="stop" |
|
|
|
|
|
icon={<KFIcon type="icon-tingzhi" />} |
|
|
|
|
|
onClick={() => startAutoML(record)} |
|
|
|
|
|
> |
|
|
|
|
|
停止 |
|
|
|
|
|
</Button> |
|
|
|
|
|
) : ( |
|
|
|
|
|
<Button |
|
|
|
|
|
type="link" |
|
|
|
|
|
size="small" |
|
|
|
|
|
key="start" |
|
|
|
|
|
icon={<KFIcon type="icon-yunhang" />} |
|
|
|
|
|
onClick={() => stopAutoML(record)} |
|
|
|
|
|
> |
|
|
|
|
|
运行 |
|
|
|
|
|
</Button> |
|
|
|
|
|
)} |
|
|
|
|
|
|
|
|
|
|
|
<ConfigProvider |
|
|
<ConfigProvider |
|
|
theme={{ |
|
|
theme={{ |
|
|
token: { |
|
|
token: { |
|
|
@@ -301,7 +360,7 @@ function AutoMLList() { |
|
|
<Button |
|
|
<Button |
|
|
style={{ marginLeft: '20px' }} |
|
|
style={{ marginLeft: '20px' }} |
|
|
type="default" |
|
|
type="default" |
|
|
onClick={() => createService()} |
|
|
|
|
|
|
|
|
onClick={() => createAutoML()} |
|
|
icon={<KFIcon type="icon-xinjian2" />} |
|
|
icon={<KFIcon type="icon-xinjian2" />} |
|
|
> |
|
|
> |
|
|
新建实验 |
|
|
新建实验 |
|
|
@@ -322,6 +381,26 @@ function AutoMLList() { |
|
|
showTotal: () => `共${total}条`, |
|
|
showTotal: () => `共${total}条`, |
|
|
}} |
|
|
}} |
|
|
onChange={handleTableChange} |
|
|
onChange={handleTableChange} |
|
|
|
|
|
expandable={{ |
|
|
|
|
|
expandedRowRender: (record) => ( |
|
|
|
|
|
<ExperimentInstance |
|
|
|
|
|
experimentInsList={experimentInsList} |
|
|
|
|
|
experimentInsTotal={experimentInsTotal} |
|
|
|
|
|
onClickInstance={(item) => gotoInstanceInfo(item, record)} |
|
|
|
|
|
onRemove={() => { |
|
|
|
|
|
refreshExperimentIns(record.id); |
|
|
|
|
|
refreshExperimentList(); |
|
|
|
|
|
}} |
|
|
|
|
|
onTerminate={handleInstanceTerminate} |
|
|
|
|
|
onLoadMore={() => loadMoreExperimentIns()} |
|
|
|
|
|
></ExperimentInstance> |
|
|
|
|
|
), |
|
|
|
|
|
onExpand: (e, a) => { |
|
|
|
|
|
handleExpandChange(e, a); |
|
|
|
|
|
}, |
|
|
|
|
|
expandedRowKeys: expandedRowKeys, |
|
|
|
|
|
rowExpandable: () => true, |
|
|
|
|
|
}} |
|
|
rowKey="id" |
|
|
rowKey="id" |
|
|
/> |
|
|
/> |
|
|
</div> |
|
|
</div> |
|
|
|