| @@ -18,7 +18,7 @@ import themes from '@/styles/theme.less'; | |||||
| import { elapsedTime, formatDate } from '@/utils/date'; | import { elapsedTime, formatDate } from '@/utils/date'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { modalConfirm } from '@/utils/ui'; | import { modalConfirm } from '@/utils/ui'; | ||||
| import { App, Button, ConfigProvider, Space, Table } from 'antd'; | |||||
| import { App, Button, ConfigProvider, Space, Table, Tooltip } from 'antd'; | |||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
| import { useEffect, useRef, useState } from 'react'; | import { useEffect, useRef, useState } from 'react'; | ||||
| import { useNavigate } from 'react-router-dom'; | import { useNavigate } from 'react-router-dom'; | ||||
| @@ -279,14 +279,14 @@ function Experiment() { | |||||
| dataIndex: 'name', | dataIndex: 'name', | ||||
| key: 'name', | key: 'name', | ||||
| render: (text) => <div>{text}</div>, | render: (text) => <div>{text}</div>, | ||||
| width: '20%', | |||||
| width: '16%', | |||||
| }, | }, | ||||
| { | { | ||||
| title: '关联流水线名称', | title: '关联流水线名称', | ||||
| dataIndex: 'workflow_name', | dataIndex: 'workflow_name', | ||||
| key: 'workflow_name', | key: 'workflow_name', | ||||
| render: (text, record) => <a onClick={(e) => routeToEdit(e, record)}>{text}</a>, | render: (text, record) => <a onClick={(e) => routeToEdit(e, record)}>{text}</a>, | ||||
| width: '20%', | |||||
| width: '16%', | |||||
| }, | }, | ||||
| { | { | ||||
| title: '实验描述', | title: '实验描述', | ||||
| @@ -443,13 +443,17 @@ function Experiment() { | |||||
| <div style={{ width: '50%' }}> | <div style={{ width: '50%' }}> | ||||
| {elapsedTime(item.create_time, item.finish_time)} | {elapsedTime(item.create_time, item.finish_time)} | ||||
| </div> | </div> | ||||
| <div style={{ width: '50%' }}>{formatDate(item.create_time)}</div> | |||||
| <div style={{ width: '50%' }} className={Styles.startTime}> | |||||
| <Tooltip title={formatDate(item.create_time)}> | |||||
| <span>{formatDate(item.create_time)}</span> | |||||
| </Tooltip> | |||||
| </div> | |||||
| </div> | </div> | ||||
| <div className={Styles.statusBox}> | <div className={Styles.statusBox}> | ||||
| <img | <img | ||||
| style={{ width: '17px', marginRight: '7px' }} | style={{ width: '17px', marginRight: '7px' }} | ||||
| src={experimentStatusInfo[item.status]?.icon} | src={experimentStatusInfo[item.status]?.icon} | ||||
| />{' '} | |||||
| /> | |||||
| <span | <span | ||||
| style={{ color: experimentStatusInfo[item.status]?.color }} | style={{ color: experimentStatusInfo[item.status]?.color }} | ||||
| className={Styles.statusIcon} | className={Styles.statusIcon} | ||||
| @@ -36,17 +36,21 @@ | |||||
| } | } | ||||
| .index { | .index { | ||||
| width: calc((100% + 32px + 33px) / 5); | |||||
| width: calc((100% + 32px + 33px) / 6.25); | |||||
| } | } | ||||
| .tensorBoard { | .tensorBoard { | ||||
| width: calc((100% + 32px + 33px) / 5); | |||||
| width: calc((100% + 32px + 33px) / 6.25); | |||||
| } | } | ||||
| .description { | .description { | ||||
| display: flex; | display: flex; | ||||
| flex: 1; | flex: 1; | ||||
| align-items: center; | align-items: center; | ||||
| .startTime { | |||||
| .singleLine(); | |||||
| } | |||||
| } | } | ||||
| .status { | .status { | ||||
| @@ -80,6 +84,7 @@ | |||||
| .statusBox:hover .statusIcon { | .statusBox:hover .statusIcon { | ||||
| visibility: visible; | visibility: visible; | ||||
| } | } | ||||
| .experimentBox { | .experimentBox { | ||||
| height: calc(100% - 20px); | height: calc(100% - 20px); | ||||
| .experimentTable { | .experimentTable { | ||||
| @@ -0,0 +1,90 @@ | |||||
| import { getComponentAll } from '@/services/pipeline/index.js'; | |||||
| import { PipelineNodeModel } from '@/types'; | |||||
| import { to } from '@/utils/promise'; | |||||
| import { Collapse, type CollapseProps } from 'antd'; | |||||
| import { useEffect, useState } from 'react'; | |||||
| import Styles from './index.less'; | |||||
| type ModelMenuData = { | |||||
| key: string; | |||||
| name: string; | |||||
| value: PipelineNodeModel[]; | |||||
| }; | |||||
| type ModelMenuProps = { | |||||
| onComponentDragEnd: ( | |||||
| data: PipelineNodeModel & { x: number; y: number; label: string; img: string }, | |||||
| ) => void; | |||||
| }; | |||||
| const ModelMenu = ({ onComponentDragEnd }: ModelMenuProps) => { | |||||
| const [modelMenusList, setModelMenusList] = useState<ModelMenuData[]>([]); | |||||
| const [collapseItems, setCollapseItems] = useState<CollapseProps['items']>([]); | |||||
| useEffect(() => { | |||||
| getAllComponents(); | |||||
| }, []); | |||||
| // 获取所有组件 | |||||
| const getAllComponents = async () => { | |||||
| const [res] = await to(getComponentAll()); | |||||
| if (res && res.data) { | |||||
| const menus = res.data as ModelMenuData[]; | |||||
| setModelMenusList(menus); | |||||
| const items = menus.map((item) => { | |||||
| return { | |||||
| key: item.key, | |||||
| label: item.name, | |||||
| children: item.value.map((ele) => { | |||||
| return ( | |||||
| <div | |||||
| key={ele.id} | |||||
| draggable="true" | |||||
| onDragEnd={(e) => { | |||||
| dragEnd(e, ele); | |||||
| }} | |||||
| className={Styles.collapseItem} | |||||
| > | |||||
| {ele.icon_path && ( | |||||
| <img | |||||
| style={{ height: '16px', marginRight: '15px' }} | |||||
| src={`/assets/images/${ele.icon_path}.png`} | |||||
| alt="" | |||||
| /> | |||||
| )} | |||||
| {ele.component_label} | |||||
| </div> | |||||
| ); | |||||
| }), | |||||
| }; | |||||
| }); | |||||
| setCollapseItems(items); | |||||
| } | |||||
| }; | |||||
| const dragEnd = (e: React.DragEvent<HTMLDivElement>, data: PipelineNodeModel) => { | |||||
| onComponentDragEnd({ | |||||
| ...data, | |||||
| x: e.clientX, | |||||
| y: e.clientY, | |||||
| label: data.component_label, | |||||
| img: `/assets/images/${data.icon_path}.png`, | |||||
| }); | |||||
| }; | |||||
| const defaultActiveKey = modelMenusList.map((item) => item.key + ''); | |||||
| return ( | |||||
| <div className={Styles.collapse}> | |||||
| <div className={Styles.modelMenusTitle}>组件库</div> | |||||
| {modelMenusList.length > 0 ? ( | |||||
| <Collapse | |||||
| collapsible="header" | |||||
| expandIconPosition="end" | |||||
| defaultActiveKey={defaultActiveKey} | |||||
| items={collapseItems} | |||||
| ></Collapse> | |||||
| ) : null} | |||||
| </div> | |||||
| ); | |||||
| }; | |||||
| export default ModelMenu; | |||||
| @@ -8,8 +8,8 @@ import { useEffect, useRef } from 'react'; | |||||
| import { useNavigate, useParams } from 'react-router-dom'; | import { useNavigate, useParams } from 'react-router-dom'; | ||||
| import { s8 } from '../../../utils'; | import { s8 } from '../../../utils'; | ||||
| import GlobalParamsDrawer from '../components/GlobalParamsDrawer'; | import GlobalParamsDrawer from '../components/GlobalParamsDrawer'; | ||||
| import ModelMenu from '../components/ModelMenu'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import ModelMenus from './modelMenus'; | |||||
| import Props from './props'; | import Props from './props'; | ||||
| import { findAllParentNodes, findFirstDuplicate } from './utils'; | import { findAllParentNodes, findFirstDuplicate } from './utils'; | ||||
| @@ -97,7 +97,7 @@ const EditPipeline = () => { | |||||
| closeParamsDrawer(); | closeParamsDrawer(); | ||||
| setTimeout(() => { | setTimeout(() => { | ||||
| if (val) { | if (val) { | ||||
| navgite({ pathname: `/pipeline` }); | |||||
| navgite({ pathname: `/pipeline/template` }); | |||||
| } | } | ||||
| }, 500); | }, 500); | ||||
| }); | }); | ||||
| @@ -699,7 +699,7 @@ const EditPipeline = () => { | |||||
| }; | }; | ||||
| return ( | return ( | ||||
| <div className={styles['pipeline-container']}> | <div className={styles['pipeline-container']}> | ||||
| <ModelMenus onParDragEnd={onDragEnd}></ModelMenus> | |||||
| <ModelMenu onComponentDragEnd={onDragEnd}></ModelMenu> | |||||
| <div className={styles['pipeline-container__workflow']}> | <div className={styles['pipeline-container__workflow']}> | ||||
| <div className={styles['pipeline-container__workflow__top']}> | <div className={styles['pipeline-container__workflow__top']}> | ||||
| <Button | <Button | ||||
| @@ -1,67 +0,0 @@ | |||||
| import { getComponentAll } from '@/services/pipeline/index.js'; | |||||
| import { Collapse } from 'antd'; | |||||
| import { useEffect, useState } from 'react'; | |||||
| import Styles from './modelMenus.less'; | |||||
| const ModelMenus = ({ onParDragEnd }) => { | |||||
| const [modelMenusList, setModelMenusList] = useState([]); | |||||
| useEffect(() => { | |||||
| getComponentAll().then((ret) => { | |||||
| console.log(ret); | |||||
| if (ret.code === 200) { | |||||
| setModelMenusList(ret.data); | |||||
| } | |||||
| }); | |||||
| }, []); | |||||
| const dragEnd = (e, data) => { | |||||
| console.log(e, data); | |||||
| onParDragEnd({ | |||||
| ...data, | |||||
| x: e.clientX, | |||||
| y: e.clientY, | |||||
| label: data.component_label, | |||||
| img: `/assets/images/${data.icon_path}.png`, | |||||
| }); | |||||
| }; | |||||
| const { Panel } = Collapse; | |||||
| return ( | |||||
| <div className={Styles.collapse}> | |||||
| <div className={Styles.modelMenusTitle}>组件库</div> | |||||
| {modelMenusList && modelMenusList.length > 0 ? ( | |||||
| <Collapse | |||||
| collapsible="header" | |||||
| defaultActiveKey={modelMenusList.map((item) => item.key + '')} | |||||
| expandIconPosition="end" | |||||
| > | |||||
| {modelMenusList && modelMenusList.length > 0 | |||||
| ? modelMenusList.map((item) => ( | |||||
| <Panel header={<div>{item.name}</div>} key={item.key}> | |||||
| {item.value && item.value.length > 0 | |||||
| ? item.value.map((ele) => ( | |||||
| <div | |||||
| key={ele.id} | |||||
| draggable="true" | |||||
| onDragEnd={(e) => { | |||||
| dragEnd(e, ele); | |||||
| }} | |||||
| className={Styles.collapseItem} | |||||
| > | |||||
| {ele.icon_path && ( | |||||
| <img | |||||
| style={{ height: '16px', marginRight: '15px' }} | |||||
| src={`/assets/images/${ele.icon_path}.png`} | |||||
| alt="" | |||||
| /> | |||||
| )} | |||||
| {ele.component_label} | |||||
| </div> | |||||
| )) | |||||
| : ''} | |||||
| </Panel> | |||||
| )) | |||||
| : ''} | |||||
| </Collapse> | |||||
| ) : null} | |||||
| </div> | |||||
| ); | |||||
| }; | |||||
| export default ModelMenus; | |||||
| @@ -41,9 +41,11 @@ export type PipelineNodeModel = { | |||||
| control_strategy: string; | control_strategy: string; | ||||
| in_parameters: string; | in_parameters: string; | ||||
| out_parameters: string; | out_parameters: string; | ||||
| component_label: string; | |||||
| icon_path: string; | |||||
| }; | }; | ||||
| // 流水线 | |||||
| // 流水线节点模型数据 | |||||
| export type PipelineNodeModelParameter = { | export type PipelineNodeModelParameter = { | ||||
| label: string; | label: string; | ||||
| value: any; | value: any; | ||||