| @@ -27,4 +27,18 @@ | |||
| height:45px; | |||
| background:#ffffff; | |||
| box-shadow:0px 3px 6px rgba(146, 146, 146, 0.09); | |||
| } | |||
| .detailBox{ | |||
| color:#1d1d20; | |||
| font-size:15px; | |||
| margin-bottom: 15px; | |||
| display: flex; | |||
| align-items: center; | |||
| } | |||
| .allMessageItem{ | |||
| display: flex; | |||
| align-items: center; | |||
| color:rgba(29, 29, 32, 0.8); | |||
| font-size:15px; | |||
| margin-right: 30px; | |||
| } | |||
| @@ -8,14 +8,45 @@ import { s8 } from '../../../utils'; | |||
| import { Button, message} from 'antd'; | |||
| import {SaveOutlined} from '@ant-design/icons'; | |||
| import {saveWorkflow,getWorkflowById,} from '@/services/pipeline/index.js' | |||
| import {getExperimentIns} from '@/services/experiment/index.js' | |||
| import {getExperimentIns,} from '@/services/experiment/index.js' | |||
| import { useNavigate} from 'react-router-dom'; | |||
| import momnet from 'moment' | |||
| const ExperimentText = React.FC = () => { | |||
| const propsRef=useRef() | |||
| const navgite=useNavigate(); | |||
| const locationParams =useParams () //新版本获取路由参数接口 | |||
| let graph=null | |||
| const [experimentStatusObj,setExperimentStatusObj]=useState({}) | |||
| const [experimentAllMessage,setExperimentAllMessage]=useState({}) | |||
| const statusObj={ | |||
| "Running":'运行中', | |||
| "Succeeded":'成功', | |||
| "Pending":'等待中', | |||
| "Failed":'失败', | |||
| "Error":'错误', | |||
| "Terminated":'终止', | |||
| "Skipped":'未执行', | |||
| "Omitted":'未执行', | |||
| } | |||
| const statusColorObj={ | |||
| "Running":'#165bff', | |||
| "Succeeded":'#63a728', | |||
| "Pending":'#f981eb', | |||
| "Failed":'#c73131', | |||
| "Error":'#c73131', | |||
| "Terminated":'#8a8a8a', | |||
| "Skipped":'#8a8a8a', | |||
| "Omitted":'#8a8a8ae', | |||
| } | |||
| const timers=(time)=>{ | |||
| let timer=new Date(time) | |||
| let hours = timer.getHours(); //转换成时 | |||
| let minutes = timer.getMinutes(); //转换成分 | |||
| let secend = timer.getSeconds(); //转换成秒 | |||
| let str = `${minutes}分${secend}秒`; | |||
| return str; | |||
| } | |||
| const pipelineContainer = useEmotionCss(() => { | |||
| return { | |||
| display: 'flex', | |||
| @@ -88,11 +119,13 @@ const ExperimentText = React.FC = () => { | |||
| console.log(JSON.parse(ret.data.dag)); | |||
| getExperimentIns(locationParams.id).then(res=>{ | |||
| if(res.code==200){ | |||
| console.log(ret.data,'data'); | |||
| const experimentStatusObjs=JSON.parse(res.data.nodes_status) | |||
| const newNodeList= JSON.parse(ret.data.dag).nodes.map(item=>{console.log(experimentStatusObjs); return {...item,component_id:experimentStatusObjs&&experimentStatusObjs[item.id]&&experimentStatusObjs[item.id].id,img:experimentStatusObjs&&experimentStatusObjs[item.id]&&experimentStatusObjs[item.id].phase?item.img.slice(0,item.img.length-4)+'-'+experimentStatusObjs[item.id].phase+'.png':item.img}}) | |||
| const newData={...JSON.parse(ret.data.dag),nodes:newNodeList} | |||
| console.log(newData); | |||
| getGraphData(newData) | |||
| const newNodeList= JSON.parse(ret.data.dag).nodes.map(item=>{console.log(experimentStatusObjs); return {...item,experimentEndTime:experimentStatusObjs&&experimentStatusObjs[item.id]&&experimentStatusObjs[item.id].finishedAt,experimentStartTime:experimentStatusObjs&&experimentStatusObjs[item.id]&&experimentStatusObjs[item.id].startedAt,experimentStatus:experimentStatusObjs&&experimentStatusObjs[item.id]&&experimentStatusObjs[item.id].phase,component_id:experimentStatusObjs&&experimentStatusObjs[item.id]&&experimentStatusObjs[item.id].id,img:experimentStatusObjs&&experimentStatusObjs[item.id]&&experimentStatusObjs[item.id].phase?item.img.slice(0,item.img.length-4)+'-'+experimentStatusObjs[item.id].phase+'.png':item.img}}) | |||
| const newData={...JSON.parse(ret.data.dag),nodes:newNodeList} | |||
| console.log(newData); | |||
| setExperimentAllMessage(res.data) | |||
| getGraphData(newData) | |||
| // setExperimentStatusObj(JSON.parse(ret.data.nodes_status)) | |||
| } | |||
| @@ -327,6 +360,16 @@ const ExperimentText = React.FC = () => { | |||
| return (<div className={pipelineContainer}> | |||
| <div className={Styles.centerContainer}> | |||
| <div className={Styles.buttonList}> | |||
| <div className={Styles.allMessageItem}>启动时间:{momnet(experimentAllMessage.create_time).format('YYYY-MM-DD HH:mm:ss')}</div> | |||
| <div className={Styles.allMessageItem}>执行时长:{experimentAllMessage.finish_time?timers(new Date(experimentAllMessage.finish_time).getTime()-new Date(experimentAllMessage.create_time).getTime()):timers(new Date().getTime()-new Date(experimentAllMessage.create_time).getTime())}</div> | |||
| <div className={Styles.allMessageItem}>状态: | |||
| <div style={{width:'8px', | |||
| height:'8px', | |||
| borderRadius:'50%', | |||
| marginRight:'6px', | |||
| backgroundColor:statusColorObj[experimentAllMessage.status] | |||
| }}></div> | |||
| <span style={{color:statusColorObj[experimentAllMessage.status]}}>{statusObj[experimentAllMessage.status]}</span></div> | |||
| </div> | |||
| <div className={graphStyle} ref={graphRef} id={Styles.graphStyle}></div> | |||
| </div> | |||
| @@ -3,19 +3,48 @@ import { Button, Drawer,Form, Input ,Tabs } from 'antd'; | |||
| import Styles from './editPipeline.less' | |||
| import{getQueryByExperimentLog}from '@/services/experiment/index.js' | |||
| import { ProfileOutlined, DatabaseOutlined} from '@ant-design/icons'; | |||
| import momnet from 'moment' | |||
| const { TextArea } = Input; | |||
| const Props = forwardRef(({onParentChange}, ref) =>{ | |||
| const [form] = Form.useForm(); | |||
| const [stagingItem,setStagingItem]=useState({}) | |||
| const [messageItem,setMessageItem]=useState('') | |||
| const statusObj={ | |||
| "Running":'运行中', | |||
| "Succeeded":'成功', | |||
| "Pending":'等待中', | |||
| "Failed":'失败', | |||
| "Error":'错误', | |||
| "Terminated":'终止', | |||
| "Skipped":'未执行', | |||
| "Omitted":'未执行', | |||
| } | |||
| const statusColorObj={ | |||
| "Running":'#165bff', | |||
| "Succeeded":'#63a728', | |||
| "Pending":'#f981eb', | |||
| "Failed":'#c73131', | |||
| "Error":'#c73131', | |||
| "Terminated":'#8a8a8a', | |||
| "Skipped":'#8a8a8a', | |||
| "Omitted":'#8a8a8ae', | |||
| } | |||
| const timers=(time)=>{ | |||
| let timer=new Date(time) | |||
| let hours = timer.getHours(); //转换成时 | |||
| let minutes = timer.getMinutes(); //转换成分 | |||
| let secend = timer.getSeconds(); //转换成秒 | |||
| let str = `${minutes}分${secend}秒`; | |||
| return str; | |||
| } | |||
| const items = [ | |||
| { | |||
| key: '1', | |||
| label: '日志详情', | |||
| children: <div style={{height:'740px', | |||
| background:'rgba(234, 234, 234, 0.5)', | |||
| color:'rgba(29, 29, 32, 0.8)', | |||
| background:'#19253b', | |||
| color:'#fff', | |||
| fontSize:'14px' | |||
| }} dangerouslySetInnerHTML={{ __html: messageItem }}></div>, | |||
| icon:<ProfileOutlined/> | |||
| @@ -226,11 +255,6 @@ const Props = forwardRef(({onParentChange}, ref) =>{ | |||
| form.resetFields(); | |||
| form.setFieldsValue({...e.item.getModel(),in_parameters:JSON.parse(e.item.getModel().in_parameters),out_parameters:JSON.parse(e.item.getModel().out_parameters),control_strategy:JSON.parse(e.item.getModel().control_strategy)}) | |||
| setStagingItem({...e.item.getModel(),in_parameters:JSON.parse(e.item.getModel().in_parameters),out_parameters:JSON.parse(e.item.getModel().out_parameters),control_strategy:JSON.parse(e.item.getModel().control_strategy)}) | |||
| // form.setFieldsValue({...e.item.getModel(),in_parameters:JSON.parse(e.item.getModel().in_parameters),out_parameters:JSON.parse(e.item.getModel().out_parameters)}) | |||
| // setStagingItem({...e.item.getModel(),in_parameters:JSON.parse(e.item.getModel().in_parameters),out_parameters:JSON.parse(e.item.getModel().out_parameters)}) | |||
| // setTimeout(() => { | |||
| // console.log(stagingItem); | |||
| // }, (500)); | |||
| setOpen(true); | |||
| }) | |||
| } | |||
| @@ -238,11 +262,6 @@ const Props = forwardRef(({onParentChange}, ref) =>{ | |||
| form.resetFields(); | |||
| form.setFieldsValue({...e.item.getModel(),in_parameters:JSON.parse(e.item.getModel().in_parameters),out_parameters:JSON.parse(e.item.getModel().out_parameters),control_strategy:JSON.parse(e.item.getModel().control_strategy)}) | |||
| setStagingItem({...e.item.getModel(),in_parameters:JSON.parse(e.item.getModel().in_parameters),out_parameters:JSON.parse(e.item.getModel().out_parameters),control_strategy:JSON.parse(e.item.getModel().control_strategy)}) | |||
| // form.setFieldsValue({...e.item.getModel(),in_parameters:JSON.parse(e.item.getModel().in_parameters),out_parameters:JSON.parse(e.item.getModel().out_parameters)}) | |||
| // setStagingItem({...e.item.getModel(),in_parameters:JSON.parse(e.item.getModel().in_parameters),out_parameters:JSON.parse(e.item.getModel().out_parameters)}) | |||
| // setTimeout(() => { | |||
| // console.log(stagingItem); | |||
| // }, (500)); | |||
| setOpen(true); | |||
| } | |||
| // console.log(e.item.getModel().in_parameters); | |||
| @@ -252,7 +271,18 @@ const Props = forwardRef(({onParentChange}, ref) =>{ | |||
| })); | |||
| return ( | |||
| <> | |||
| <Drawer title="编辑任务" placement="right" closeIcon={false} onClose={onClose} afterOpenChange={afterOpenChange} open={open}> | |||
| <Drawer title="任务执行详情" placement="right" closeIcon={false} onClose={onClose} afterOpenChange={afterOpenChange} open={open}> | |||
| <div className={Styles.detailBox}>任务名称:{stagingItem.label}</div> | |||
| <div className={Styles.detailBox} >执行状态: | |||
| <div style={{width:'8px', | |||
| height:'8px', | |||
| borderRadius:'50%', | |||
| marginRight:'6px', | |||
| backgroundColor:statusColorObj[stagingItem.experimentStatus] | |||
| }}></div> | |||
| <span style={{color:statusColorObj[stagingItem.experimentStatus]}}>{statusObj[stagingItem.experimentStatus]}</span></div> | |||
| <div className={Styles.detailBox}>启动时间:{momnet(stagingItem.experimentStartTime).format('YYYY-MM-DD HH:mm:ss')}</div> | |||
| <div className={Styles.detailBox}>耗时:{stagingItem.experimentEndTime?timers(new Date(stagingItem.experimentEndTime).getTime()-new Date(stagingItem.experimentStartTime).getTime()):timers(new Date().getTime()-new Date(stagingItem.experimentStartTime).getTime())}</div> | |||
| <Tabs | |||
| defaultActiveKey="1" | |||
| items={items}/> | |||
| @@ -1,8 +1,8 @@ | |||
| import React ,{ useState,useEffect,useRef }from 'react'; | |||
| import { Space, Table, Tag,Button,Modal, Form, Input ,message, Select,} from 'antd'; | |||
| import { PlusOutlined, EditOutlined ,PlayCircleOutlined} from '@ant-design/icons'; | |||
| import {getWorkflow,addWorkflow,removeWorkflow,cloneWorkflow} from '@/services/pipeline/index.js' | |||
| import {getExperiment,runExperiments,getExperimentById,postExperiment,putExperiment,getQueryByExperimentId} from '@/services/experiment/index.js' | |||
| import { PlusOutlined, EditOutlined ,PlayCircleOutlined,DeleteOutlined,FieldTimeOutlined} from '@ant-design/icons'; | |||
| import {getWorkflow,} from '@/services/pipeline/index.js' | |||
| import {getExperiment,runExperiments,getExperimentById,postExperiment,putExperiment,getQueryByExperimentId,deleteExperimentById,deleteQueryByExperimentInsId,putQueryByExperimentInsId} from '@/services/experiment/index.js' | |||
| import Styles from './index.less' | |||
| import { useNavigate} from 'react-router-dom'; | |||
| import momnet from 'moment' | |||
| @@ -14,9 +14,12 @@ const Experiment = React.FC = () => { | |||
| const statusObj={ | |||
| "Running":'运行中', | |||
| "Succeeded":'成功', | |||
| "Pending":'等待中', | |||
| "Failed":'失败', | |||
| "Error":'错误', | |||
| "Teminated":'终止' | |||
| "Terminated":'终止', | |||
| "Skipped":'未执行', | |||
| "Omitted":'未执行', | |||
| } | |||
| const [experimentList, setExperimentList] = useState([]); | |||
| @@ -258,7 +261,41 @@ const Experiment = React.FC = () => { | |||
| > | |||
| 编辑 | |||
| </Button> | |||
| <Button | |||
| type="link" | |||
| size="small" | |||
| danger | |||
| key="batchRemove" | |||
| 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> | |||
| ), | |||
| }, | |||
| @@ -278,14 +315,66 @@ const Experiment = React.FC = () => { | |||
| <div style={{width:'200px'}}>状态</div> | |||
| <div style={{width:'300px'}}>运行时长</div> | |||
| <div style={{width:'300px'}}>开始时间</div> | |||
| <div style={{width:'300px'}}>操作</div> | |||
| </div>:''} | |||
| {experimentInList&&experimentInList.length>0?experimentInList.map((item,index)=>( | |||
| <div 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 style={{width:'200px'}}>{statusObj[item.status]}</div> | |||
| <div style={{width:'300px'}}>{item.finish_time?timers(new Date(item.finish_time).getTime()-new Date(item.start_time).getTime()):timers(new Date().getTime()-new Date(item.start_time).getTime())}</div> | |||
| <div style={{width:'300px'}}>{momnet(item.start_time).format('YYYY-MM-DD HH:mm:ss')}</div> | |||
| <div style={{width:'300px'}}>{item.finish_time?timers(new Date(item.finish_time).getTime()-new Date(item.create_time).getTime()):timers(new Date().getTime()-new Date(item.create_time).getTime())}</div> | |||
| <div style={{width:'300px'}}>{momnet(item.create_time).format('YYYY-MM-DD HH:mm:ss')}</div> | |||
| <div style={{width:'300px'}}> | |||
| <Button | |||
| type="link" | |||
| size="small" | |||
| key="batchRemove" | |||
| 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" | |||
| 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> | |||
| @@ -21,12 +21,30 @@ export function getExperimentById(id) { | |||
| method: 'GET', | |||
| }); | |||
| } | |||
| // 根据id删除实验 | |||
| export function deleteExperimentById(id) { | |||
| return request(`/api/mmp/experiment/${id}`, { | |||
| method: 'DELETE', | |||
| }); | |||
| } | |||
| // 根据id查询实验实例 | |||
| export function getQueryByExperimentId(id) { | |||
| return request(`/api/mmp/experimentIns/queryByExperimentId/${id}`, { | |||
| method: 'GET', | |||
| }); | |||
| } | |||
| // 根据id删除实验实例 | |||
| export function deleteQueryByExperimentInsId(id) { | |||
| return request(`/api/mmp/experimentIns/${id}`, { | |||
| method: 'DELETE', | |||
| }); | |||
| } | |||
| // 根据id终止实验实例 | |||
| export function putQueryByExperimentInsId(id) { | |||
| return request(`/api/mmp/experimentIns/${id}`, { | |||
| method: 'PUT', | |||
| }); | |||
| } | |||
| // 根据id查询查询日志 | |||
| export function getQueryByExperimentLog(params) { | |||
| return request(`/api/mmp/experimentIns/log/`, { | |||