|
- import {
- deleteExperimentById,
- deleteQueryByExperimentInsId,
- getExperiment,
- getExperimentById,
- getQueryByExperimentId,
- postExperiment,
- putExperiment,
- putQueryByExperimentInsId,
- runExperiments,
- } from '@/services/experiment/index.js';
- import { getWorkflow } from '@/services/pipeline/index.js';
- import { elapsedTime } from '@/utils/date';
- import { to } from '@/utils/promise';
- import {
- DeleteOutlined,
- EditOutlined,
- FieldTimeOutlined,
- PlayCircleOutlined,
- PlusCircleOutlined,
- } from '@ant-design/icons';
- import { Button, Modal, Space, Table, message } from 'antd';
- import momnet from 'moment';
- import { useEffect, useRef, useState } from 'react';
- import { useNavigate } from 'react-router-dom';
- import AddExperimentModal from './experimentText/addExperimentModal';
- import Styles from './index.less';
- import { experimentStatusInfo } from './status';
-
- function Experiment() {
- const navgite = useNavigate();
- const [experimentList, setExperimentList] = useState([]);
- const [workflowList, setWorkflowList] = useState([]);
- const [queryFlow, setQueryFlow] = useState({
- offset: 1,
- page: 0,
- size: 10000,
- name: null,
- });
- const [experimentId, setExperimentId] = useState(null);
- const [experimentInList, setExperimentInList] = useState([]);
- const [expandedRowKeys, setExpandedRowKeys] = useState(null);
- const [total, setTotal] = useState(0);
- const [isAdd, setIsAdd] = useState(true);
- const [isModalOpen, setIsModalOpen] = useState(false);
- const [addFormData, setAddFormData] = useState({});
- useEffect(() => {
- getList();
- getWorkflowList();
- }, []);
- // 获取实验列表
- const getList = async () => {
- const params = {
- offset: 0,
- page: pageOption.current.page - 1,
- size: pageOption.current.size,
- };
- const [res, _] = await to(getExperiment(params));
- if (res && res.data && Array.isArray(res.data.content)) {
- setExperimentList(
- res.data.content.map((item) => {
- return { ...item, key: item.id };
- }),
- );
- setTotal(res.data.totalElements);
- }
- };
- // 获取流水线列表
- const getWorkflowList = async () => {
- const [res, _] = await to(getWorkflow(queryFlow));
- if (res && res.data && res.data.content) {
- setWorkflowList(res.data.content);
- }
- };
- const getQueryByExperiment = (val) => {
- getQueryByExperimentId(val).then((ret) => {
- setExpandedRowKeys(val);
- if (ret.code === 200 && ret.data && ret.data.length > 0) {
- setExperimentInList(ret.data);
- getList();
- } else {
- setExperimentInList([]);
- getList();
- }
- });
- };
- const expandChange = (e, record) => {
- if (record.id === expandedRowKeys) {
- setExpandedRowKeys(null);
- } else {
- getQueryByExperiment(record.id);
- }
- };
- // 创建实验
- const createExperiment = () => {
- setIsAdd(true);
- setAddFormData({});
- setExperimentId(null);
- setIsModalOpen(true);
- };
- // 编辑实验
- const editExperiment = (id) => {
- getExperimentById(id).then((res) => {
- setAddFormData({
- ...res.data,
- });
- setExperimentId(res.data.id);
- setIsAdd(false);
- setIsModalOpen(true);
- });
- };
- // 创建或编辑实验取消
- const handleCancel = () => {
- setIsModalOpen(false);
- };
- const routeToEdit = (e, record) => {
- e.stopPropagation();
- navgite({ pathname: `/pipeline/pytorchtext/${record.workflow_id}/${record.workflow_name}` });
- };
- // 创建或者编辑实验接口请求
- const handleAddExperiment = async (values) => {
- const workflow_id = values['workflow_id'];
- let global_param = undefined;
- const pipeline = workflowList.find((v) => v.id === workflow_id);
- if (pipeline && pipeline.global_param) {
- const globalParamList = [...pipeline.global_param];
- for (const item of globalParamList) {
- item.param_value = values[item.param_name];
- values[item.param_name] = undefined;
- }
- global_param = JSON.stringify(globalParamList);
- }
- const params = {
- ...values,
- global_param,
- };
- if (!experimentId) {
- const [res, _] = await to(postExperiment(params));
- if (res) {
- message.success('新建实验成功');
- setIsModalOpen(false);
- getList();
- }
- } else {
- const params = { ...values, id: experimentId };
- const [res, _] = await to(putExperiment(params));
- if (res) {
- message.success('编辑实验成功');
- setIsModalOpen(false);
- getList();
- }
- }
- };
- const pageOption = useRef({ page: 1, size: 10 });
- const paginationProps = {
- showQuickJumper: true,
- showTotal: () => `共${total}条`,
- total: total,
- page: pageOption.current.page,
- size: pageOption.current.size,
- onChange: (current, size) => paginationChange(current, size),
- };
- // 当前页面切换
- const paginationChange = async (current, size) => {
- console.log('page', current, size);
- pageOption.current = {
- page: current,
- size: size,
- };
- getList();
- };
- const runExperiment = (id) => {
- runExperiments(id).then((ret) => {
- if (ret.code === 200) {
- message.success('运行成功');
- getQueryByExperiment(id);
- } else {
- message.error('运行失败');
- }
- });
- };
- const routerToText = (e, item, record) => {
- e.stopPropagation();
- navgite({ pathname: `/experiment/pytorchtext/${record.workflow_id}/${item.id}` });
- };
-
- const columns = [
- {
- title: '实验名称',
- dataIndex: 'name',
- key: 'name',
- render: (text) => <div>{text}</div>,
- },
- {
- title: '关联流水线名称',
- dataIndex: 'workflow_name',
- key: 'workflow_name',
- render: (text, record) => <a onClick={(e) => routeToEdit(e, record)}>{text}</a>,
- },
- {
- title: '实验描述',
- dataIndex: 'description',
- key: 'description',
- },
- {
- title: '最近五次运行状态',
- dataIndex: 'status_list',
- key: 'status_list',
- render: (text) => {
- let newText = text && text.replace(/\s+/g, '').split(',');
- console.log(newText);
- return (
- <>
- {newText && newText.length > 0
- ? newText.map((item, index) => {
- return (
- <img
- style={{ width: '17px', marginRight: '6px' }}
- key={index}
- src={experimentStatusInfo[item].icon}
- />
- );
- })
- : null}
- </>
- );
- },
- },
-
- {
- title: '操作',
- key: 'action',
- width: 300,
- render: (_, record) => (
- <Space size="small">
- <Button
- type="link"
- size="small"
- key="run"
- icon={<PlayCircleOutlined />}
- onClick={() => {
- runExperiment(record.id);
- }}
- >
- 运行
- </Button>
- <Button
- type="link"
- size="small"
- key="edit"
- icon={<EditOutlined />}
- onClick={() => {
- editExperiment(record.id);
- }}
- >
- 编辑
- </Button>
- <Button
- type="link"
- size="small"
- danger
- key="batchRemove"
- style={{ color: '#f98e1b' }}
- icon={<DeleteOutlined />}
- onClick={async () => {
- Modal.confirm({
- title: '删除',
- content: '确定删除该条实验吗?',
- okText: '确认',
- cancelText: '取消',
-
- onOk: () => {
- console.log(record);
- deleteExperimentById(record.id).then((ret) => {
- if (ret.code === 200) {
- message.success('删除成功');
- getList();
- } else {
- message.error(ret.msg);
- }
- });
-
- // if (success) {
- // if (actionRef.current) {
- // actionRef.current.reload();
- // }
- // }
- },
- });
- }}
- >
- 删除
- </Button>
- </Space>
- ),
- },
- ];
- return (
- <div>
- {/* <div >
- <Button type="primary" onClick={createExperiment} icon = {< PlusOutlined />}>
- 新建实验
- </Button>
- </div> */}
- <div className={Styles.pipelineTopBox}>
- <Button
- type="primary"
- className={Styles.plusButton}
- onClick={createExperiment}
- icon={<PlusCircleOutlined style={{ color: '#1664ff' }} />}
- >
- 新建实验
- </Button>
- </div>
- <Table
- columns={columns}
- dataSource={experimentList}
- pagination={paginationProps}
- expandable={{
- expandedRowRender: (record) => (
- <div>
- {experimentInList && experimentInList.length > 0 ? (
- <div className={Styles.tableExpandBox} style={{ paddingBottom: '16px' }}>
- <div style={{ width: '50px' }}>序号</div>
- <div style={{ width: '200px' }}>状态</div>
- <div style={{ width: '300px' }}>运行时长</div>
- <div style={{ width: '300px' }}>开始时间</div>
- <div style={{ width: '200px' }}>操作</div>
- </div>
- ) : (
- ''
- )}
-
- {experimentInList && experimentInList.length > 0
- ? experimentInList.map((item, index) => (
- <div
- key={item.id}
- className={Styles.tableExpandBox}
- style={{
- border: '1px solid #eaeaea',
- backgroundColor: '#fff',
- height: '45px',
- }}
- >
- <a style={{ width: '50px' }} onClick={(e) => routerToText(e, item, record)}>
- {index + 1}
- </a>
- <div className={Styles.statusBox} style={{ width: '200px' }}>
- <img
- style={{ width: '17px', marginRight: '7px' }}
- src={experimentStatusInfo[item.status]?.icon}
- />{' '}
- <span
- style={{ color: experimentStatusInfo[item.status]?.color }}
- className={Styles.statusIcon}
- >
- {experimentStatusInfo[item.status]?.label}
- </span>
- </div>
- <div style={{ width: '300px' }}>
- {item.finish_time
- ? elapsedTime(new Date(item.create_time), new Date(item.finish_time))
- : elapsedTime(new Date(item.create_time), new Date())}
- </div>
- <div style={{ width: '300px' }}>
- {momnet(item.create_time).format('YYYY-MM-DD HH:mm:ss')}
- </div>
- <div style={{ width: '200px' }}>
- <Button
- type="link"
- size="small"
- key="stop"
- disabled={
- item.status === 'Succeeded' ||
- item.status === 'Failed' ||
- item.status === 'Terminated'
- }
- icon={<FieldTimeOutlined />}
- onClick={async () => {
- putQueryByExperimentInsId(item.id).then((ret) => {
- if (ret.code === 200) {
- message.success('终止成功');
- getQueryByExperiment(record.id);
- } else {
- message.error(ret.msg);
- }
- });
- }}
- >
- 终止
- </Button>
- <Button
- type="link"
- size="small"
- danger
- key="batchRemove"
- style={{ color: '#f98e1b' }}
- disabled={item.status === 'Running' || item.status === 'Pending'}
- icon={<DeleteOutlined />}
- onClick={async () => {
- Modal.confirm({
- title: '删除',
- content: '确定删除该条实例吗?',
- okText: '确认',
- cancelText: '取消',
- onOk: () => {
- deleteQueryByExperimentInsId(item.id).then((ret) => {
- if (ret.code === 200) {
- message.success('删除成功');
- getQueryByExperiment(record.id);
- } else {
- message.error(ret.msg);
- }
- });
- },
- });
- }}
- >
- 删除
- </Button>
- </div>
- </div>
- ))
- : ''}
- </div>
- ),
- onExpand: (e, a) => {
- expandChange(e, a);
- },
- expandedRowKeys: [expandedRowKeys],
- rowExpandable: (record) => true,
- }}
- />
- <AddExperimentModal
- isAdd={isAdd}
- open={isModalOpen}
- initialValues={addFormData}
- onCancel={handleCancel}
- onFinish={handleAddExperiment}
- workflowList={workflowList}
- />
- </div>
- );
- }
- export default Experiment;
|