|
- /*
- * @Author: 赵伟
- * @Date: 2024-04-16 13:58:08
- * @Description: 自主机器学习列表
- */
- import KFIcon from '@/components/KFIcon';
- import PageTitle from '@/components/PageTitle';
- import { ExperimentStatus } from '@/enums';
- import { useCacheState } from '@/hooks/pageCacheState';
- import { experimentStatusInfo } from '@/pages/Experiment/status';
- import {
- deleteAutoMLReq,
- getAutoMLListReq,
- getExperimentInsListReq,
- runAutoMLReq,
- } from '@/services/autoML';
- import themes from '@/styles/theme.less';
- import { type ExperimentInstance as ExperimentInstanceData } from '@/types';
- 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';
- import {
- App,
- Button,
- ConfigProvider,
- Input,
- Table,
- type TablePaginationConfig,
- type TableProps,
- } from 'antd';
- import { type SearchProps } from 'antd/es/input';
- import classNames from 'classnames';
- import { useEffect, useState } from 'react';
- import ExperimentInstance from '../components/ExperimentInstance';
- import { AutoMLData } from '../types';
- import styles from './index.less';
-
- function AutoMLList() {
- const navigate = useNavigate();
- const { message } = App.useApp();
- const [cacheState, setCacheState] = useCacheState();
- const [searchText, setSearchText] = useState(cacheState?.searchText);
- const [inputText, setInputText] = useState(cacheState?.searchText);
- const [tableData, setTableData] = useState<AutoMLData[]>([]);
- 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>(
- cacheState?.pagination ?? {
- current: 1,
- pageSize: 10,
- },
- );
-
- useEffect(() => {
- getAutoMLList();
- }, [pagination, searchText]);
-
- // 获取自主机器学习列表
- const getAutoMLList = async () => {
- const params: Record<string, any> = {
- page: pagination.current! - 1,
- size: pagination.pageSize,
- ml_name: searchText,
- };
- const [res] = await to(getAutoMLListReq(params));
- if (res && res.data) {
- const { content = [], totalElements = 0 } = res.data;
- setTableData(content);
- setTotal(totalElements);
- }
- };
-
- // 搜索
- const onSearch: SearchProps['onSearch'] = (value) => {
- setSearchText(value);
- };
-
- // 删除模型部署
- const deleteAutoML = async (record: AutoMLData) => {
- const [res] = await to(deleteAutoMLReq(record.id));
- if (res) {
- message.success('删除成功');
- // 如果是一页的唯一数据,删除时,请求第一页的数据
- // 否则直接刷新这一页的数据
- // 避免回到第一页
- if (tableData.length > 1) {
- setPagination((prev) => ({
- ...prev,
- current: 1,
- }));
- } else {
- getAutoMLList();
- }
- }
- };
-
- // 处理删除
- const handleAutoMLDelete = (record: AutoMLData) => {
- modalConfirm({
- title: '删除后,该实验将不可恢复',
- content: '是否确认删除?',
- onOk: () => {
- deleteAutoML(record);
- },
- });
- };
-
- // 创建、编辑、复制自动机器学习
- const createAutoML = (record?: AutoMLData, isCopy: boolean = false) => {
- setCacheState({
- pagination,
- searchText,
- });
-
- if (record) {
- 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`);
- }
- };
-
- // 查看自动机器学习详情
- const gotoDetail = (record: AutoMLData) => {
- setCacheState({
- pagination,
- searchText,
- });
-
- navigate(`/pipeline/autoML/info/${record.id}`);
- };
-
- // 启动自动机器学习
- const startAutoML = async (record: AutoMLData) => {
- const [res] = await to(runAutoMLReq(record.id));
- if (res) {
- message.success('运行成功');
- setExpandedRowKeys([record.id]);
- refreshExperimentList();
- refreshExperimentIns(record.id);
- }
- };
-
- // --------------------------- 实验实例 ---------------------------
- // 获取实验实例列表
- 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 = (autoML: AutoMLData, record: ExperimentInstanceData) => {
- navigate({ pathname: `/pipeline/automl/instance/${autoML.id}/${record.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'] = (
- pagination,
- _filters,
- _sorter,
- { action },
- ) => {
- if (action === 'paginate') {
- setPagination(pagination);
- }
- };
-
- const columns: TableProps<AutoMLData>['columns'] = [
- {
- title: '实验名称',
- dataIndex: 'ml_name',
- key: 'ml_name',
- width: '16%',
- render: tableCellRender(false, TableCellValueType.Link, {
- onClick: gotoDetail,
- }),
- },
- {
- title: '实验描述',
- dataIndex: 'ml_description',
- key: 'ml_description',
- render: tableCellRender(true),
- 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: '操作',
- dataIndex: 'operation',
- width: 360,
- key: 'operation',
- render: (_: any, record: AutoMLData) => (
- <div>
- <Button
- type="link"
- size="small"
- key="start"
- icon={<KFIcon type="icon-yunhang" />}
- onClick={() => startAutoML(record)}
- >
- 运行
- </Button>
- <Button
- type="link"
- size="small"
- key="edit"
- icon={<KFIcon type="icon-bianji" />}
- onClick={() => createAutoML(record, false)}
- >
- 编辑
- </Button>
- <Button
- type="link"
- size="small"
- key="copy"
- icon={<KFIcon type="icon-fuzhi" />}
- onClick={() => createAutoML(record, true)}
- >
- 复制
- </Button>
-
- <ConfigProvider
- theme={{
- token: {
- colorLink: themes['warningColor'],
- },
- }}
- >
- <Button
- type="link"
- size="small"
- key="remove"
- icon={<KFIcon type="icon-shanchu" />}
- onClick={() => handleAutoMLDelete(record)}
- >
- 删除
- </Button>
- </ConfigProvider>
- </div>
- ),
- },
- ];
-
- return (
- <div className={styles['auto-ml-list']}>
- <PageTitle title="自动机器学习列表"></PageTitle>
- <div className={styles['auto-ml-list__content']}>
- <div className={styles['auto-ml-list__content__filter']}>
- <Input.Search
- placeholder="按实验名称筛选"
- onSearch={onSearch}
- onChange={(e) => setInputText(e.target.value)}
- style={{ width: 300 }}
- value={inputText}
- allowClear
- />
- <Button
- style={{ marginLeft: '20px' }}
- type="default"
- onClick={() => createAutoML()}
- icon={<KFIcon type="icon-xinjian2" />}
- >
- 新建实验
- </Button>
- </div>
- <div
- className={classNames('vertical-scroll-table', styles['auto-ml-list__content__table'])}
- >
- <Table
- dataSource={tableData}
- columns={columns}
- scroll={{ y: 'calc(100% - 55px)' }}
- pagination={{
- ...pagination,
- total: total,
- showSizeChanger: true,
- showQuickJumper: true,
- showTotal: () => `共${total}条`,
- }}
- onChange={handleTableChange}
- expandable={{
- expandedRowRender: (record) => (
- <ExperimentInstance
- experimentInsList={experimentInsList}
- experimentInsTotal={experimentInsTotal}
- onClickInstance={(item) => gotoInstanceInfo(record, item)}
- onRemove={() => {
- refreshExperimentIns(record.id);
- refreshExperimentList();
- }}
- onTerminate={handleInstanceTerminate}
- onLoadMore={() => loadMoreExperimentIns()}
- ></ExperimentInstance>
- ),
- onExpand: (e, a) => {
- handleExpandChange(e, a);
- },
- expandedRowKeys: expandedRowKeys,
- rowExpandable: () => true,
- }}
- rowKey="id"
- />
- </div>
- </div>
- </div>
- );
- }
-
- export default AutoMLList;
|