Browse Source

merge

tags/v20240126
YaHoo94 2 years ago
parent
commit
07fd70c7c3
72 changed files with 2119 additions and 142 deletions
  1. +1
    -1
      react-ui/config/config.ts
  2. +2
    -2
      react-ui/config/defaultSettings.ts
  3. +3
    -2
      react-ui/config/proxy.ts
  4. +43
    -0
      react-ui/config/routes.ts
  5. +1
    -1
      react-ui/jest.config.ts
  6. +2
    -1
      react-ui/package.json
  7. BIN
      react-ui/public/assets/images/component-icon-1--Omitted.png
  8. BIN
      react-ui/public/assets/images/component-icon-1-Failed.png
  9. BIN
      react-ui/public/assets/images/component-icon-1-Omitted.png
  10. BIN
      react-ui/public/assets/images/component-icon-1-Pending.png
  11. BIN
      react-ui/public/assets/images/component-icon-1-Running.png
  12. BIN
      react-ui/public/assets/images/component-icon-1-Succeeded.png
  13. BIN
      react-ui/public/assets/images/component-icon-1.png
  14. BIN
      react-ui/public/assets/images/component-icon-2-Failed.png
  15. BIN
      react-ui/public/assets/images/component-icon-2-Omitted.png
  16. BIN
      react-ui/public/assets/images/component-icon-2-Pending.png
  17. BIN
      react-ui/public/assets/images/component-icon-2-Running.png
  18. BIN
      react-ui/public/assets/images/component-icon-2-Succeeded.png
  19. BIN
      react-ui/public/assets/images/component-icon-2.png
  20. BIN
      react-ui/public/assets/images/component-icon-3-Failed.png
  21. BIN
      react-ui/public/assets/images/component-icon-3-Omitted.png
  22. BIN
      react-ui/public/assets/images/component-icon-3-Pending.png
  23. BIN
      react-ui/public/assets/images/component-icon-3-Running.png
  24. BIN
      react-ui/public/assets/images/component-icon-3-Succeeded.png
  25. BIN
      react-ui/public/assets/images/component-icon-3.png
  26. BIN
      react-ui/public/assets/images/component-icon-4-Failed.png
  27. BIN
      react-ui/public/assets/images/component-icon-4-Omitted.png
  28. BIN
      react-ui/public/assets/images/component-icon-4-Pending.png
  29. BIN
      react-ui/public/assets/images/component-icon-4-Running.png
  30. BIN
      react-ui/public/assets/images/component-icon-4-Succeeded.png
  31. BIN
      react-ui/public/assets/images/component-icon-4.png
  32. BIN
      react-ui/public/assets/images/component-icon-5-Failed.png
  33. BIN
      react-ui/public/assets/images/component-icon-5-Omitted.png
  34. BIN
      react-ui/public/assets/images/component-icon-5-Pending.png
  35. BIN
      react-ui/public/assets/images/component-icon-5-Running.png
  36. BIN
      react-ui/public/assets/images/component-icon-5-Succeeded.png
  37. BIN
      react-ui/public/assets/images/component-icon-5.png
  38. BIN
      react-ui/public/assets/images/component-icon-6-Failed.png
  39. BIN
      react-ui/public/assets/images/component-icon-6-Omitted.png
  40. BIN
      react-ui/public/assets/images/component-icon-6-Pending.png
  41. BIN
      react-ui/public/assets/images/component-icon-6-Running.png
  42. BIN
      react-ui/public/assets/images/component-icon-6-Succeeded.png
  43. BIN
      react-ui/public/assets/images/component-icon-6.png
  44. BIN
      react-ui/public/assets/images/component-icon-7-Failed.png
  45. BIN
      react-ui/public/assets/images/component-icon-7-Omitted.png
  46. BIN
      react-ui/public/assets/images/component-icon-7-Pending.png
  47. BIN
      react-ui/public/assets/images/component-icon-7-Running.png
  48. BIN
      react-ui/public/assets/images/component-icon-7-Succeeded.png
  49. BIN
      react-ui/public/assets/images/component-icon-7.png
  50. BIN
      react-ui/public/assets/images/duty-message.png
  51. BIN
      react-ui/public/assets/images/static-message.png
  52. +3
    -3
      react-ui/public/fonts/font.css
  53. +14
    -0
      react-ui/src/pages/DevelopmentEnvironment/index.jsx
  54. +30
    -0
      react-ui/src/pages/Experiment/experimentText/editPipeline.less
  55. +335
    -0
      react-ui/src/pages/Experiment/experimentText/index.jsx
  56. +265
    -0
      react-ui/src/pages/Experiment/experimentText/props.jsx
  57. +364
    -0
      react-ui/src/pages/Experiment/index.jsx
  58. +35
    -0
      react-ui/src/pages/Experiment/index.less
  59. +30
    -0
      react-ui/src/pages/Pipeline/editPipeline/editPipeline.less
  60. +366
    -0
      react-ui/src/pages/Pipeline/editPipeline/index.jsx
  61. +60
    -0
      react-ui/src/pages/Pipeline/editPipeline/modelMenus.jsx
  62. +12
    -0
      react-ui/src/pages/Pipeline/editPipeline/modelMenus.less
  63. +214
    -0
      react-ui/src/pages/Pipeline/editPipeline/props.jsx
  64. +184
    -124
      react-ui/src/pages/Pipeline/index.jsx
  65. +1
    -1
      react-ui/src/pages/Pipeline/index.less
  66. +4
    -4
      react-ui/src/pages/User/Login/index.tsx
  67. +2
    -2
      react-ui/src/pages/User/Login/login.less
  68. +8
    -0
      react-ui/src/services/developmentEnvironment/index.js
  69. +62
    -0
      react-ui/src/services/experiment/index.js
  70. +66
    -0
      react-ui/src/services/pipeline/index.js
  71. +11
    -0
      react-ui/src/utils/index.js
  72. +1
    -1
      react-ui/tsconfig.json

+ 1
- 1
react-ui/config/config.ts View File

@@ -76,7 +76,7 @@ export default defineConfig({
* @name layout 插件 * @name layout 插件
* @doc https://umijs.org/docs/max/layout-menu * @doc https://umijs.org/docs/max/layout-menu
*/ */
title: 'Ant Design Pro',
title: '复杂智能软件',
layout: { layout: {
locale: true, locale: true,
...defaultSettings, ...defaultSettings,


+ 2
- 2
react-ui/config/defaultSettings.ts View File

@@ -14,9 +14,9 @@ const Settings: ProLayoutProps & {
contentWidth: 'Fluid', contentWidth: 'Fluid',
fixedHeader: false, fixedHeader: false,
fixSiderbar: false, fixSiderbar: false,
splitMenus: true,
splitMenus: false,
colorWeak: false, colorWeak: false,
title: 'Ant Design Pro',
title: '复杂智能软件',
pwa: true, pwa: true,
logo: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg', logo: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
iconfontUrl: '', iconfontUrl: '',


+ 3
- 2
react-ui/config/proxy.ts View File

@@ -15,14 +15,15 @@ export default {
// localhost:8000/api/** -> https://preview.pro.ant.design/api/** // localhost:8000/api/** -> https://preview.pro.ant.design/api/**
'/api/': { '/api/': {
// 要代理的地址 // 要代理的地址
target: 'http://localhost:8082',
// target: 'http://172.20.32.181:31205',
target: 'http://172.20.32.150:8082',
// 配置了这个可以从 http 代理到 https // 配置了这个可以从 http 代理到 https
// 依赖 origin 的功能可能需要这个,比如 cookie // 依赖 origin 的功能可能需要这个,比如 cookie
changeOrigin: true, changeOrigin: true,
pathRewrite: { '^/api': '' }, pathRewrite: { '^/api': '' },
}, },
'/profile/avatar/': { '/profile/avatar/': {
target: 'http://localhost:8082',
target: 'http://172.20.32.181:31205',
changeOrigin: true, changeOrigin: true,
} }
}, },


+ 43
- 0
react-ui/config/routes.ts View File

@@ -46,6 +46,49 @@ export default [
}, },
], ],
}, },
{
name: 'pipeline',
path: '/pipeline',
routes: [
{
name: '流水线',
path: '/pipeline',
component: './Pipeline/index',
},
{
name: '训练',
path: '/pipeline/pytorchtext/:id/:name',
component: './Pipeline/editPipeline/index',
},
],
},
{
name: 'experiment',
path: '/experiment',
routes: [
{
name: '实验',
path: '/experiment',
component: './Experiment/index',
},
{
name: '实验训练',
path: '/experiment/pytorchtext/:workflowId/:id',
component: './Experiment/experimentText/index',
},
],
},
{
name: 'developmentEnvironment',
path: '/developmentEnvironment',
routes: [
{
name: '开发环境',
path: '/developmentEnvironment',
component: './DevelopmentEnvironment/index',
},
],
},
{ {
name: 'system', name: 'system',
path: '/system', path: '/system',


+ 1
- 1
react-ui/jest.config.ts View File

@@ -12,7 +12,7 @@ export default async () => {
...config, ...config,
testEnvironmentOptions: { testEnvironmentOptions: {
...(config?.testEnvironmentOptions || {}), ...(config?.testEnvironmentOptions || {}),
url: 'http://localhost:8081',
url: 'http://localhost:8000',
}, },
setupFiles: [...(config.setupFiles || []), './tests/setupTests.jsx'], setupFiles: [...(config.setupFiles || []), './tests/setupTests.jsx'],
globals: { globals: {


+ 2
- 1
react-ui/package.json View File

@@ -56,13 +56,14 @@
"@ant-design/icons": "^5.0.0", "@ant-design/icons": "^5.0.0",
"@ant-design/pro-components": "^2.4.4", "@ant-design/pro-components": "^2.4.4",
"@ant-design/use-emotion-css": "1.0.4", "@ant-design/use-emotion-css": "1.0.4",
"@antv/g6": "^4.8.24",
"@umijs/route-utils": "^4.0.1", "@umijs/route-utils": "^4.0.1",
"antd": "^5.4.4", "antd": "^5.4.4",
"classnames": "^2.3.2", "classnames": "^2.3.2",
"fabric": "^5.3.0", "fabric": "^5.3.0",
"highlight.js": "^11.7.0", "highlight.js": "^11.7.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"moment": "^2.29.4",
"moment": "^2.30.1",
"omit.js": "^2.0.2", "omit.js": "^2.0.2",
"pnpm": "^8.9.0", "pnpm": "^8.9.0",
"query-string": "^8.1.0", "query-string": "^8.1.0",


BIN
react-ui/public/assets/images/component-icon-1--Omitted.png View File

Before After
Width: 108  |  Height: 108  |  Size: 3.1 kB

BIN
react-ui/public/assets/images/component-icon-1-Failed.png View File

Before After
Width: 108  |  Height: 108  |  Size: 3.2 kB

BIN
react-ui/public/assets/images/component-icon-1-Omitted.png View File

Before After
Width: 108  |  Height: 108  |  Size: 3.1 kB

BIN
react-ui/public/assets/images/component-icon-1-Pending.png View File

Before After
Width: 108  |  Height: 108  |  Size: 3.3 kB

BIN
react-ui/public/assets/images/component-icon-1-Running.png View File

Before After
Width: 108  |  Height: 108  |  Size: 3.1 kB

BIN
react-ui/public/assets/images/component-icon-1-Succeeded.png View File

Before After
Width: 108  |  Height: 108  |  Size: 3.4 kB

BIN
react-ui/public/assets/images/component-icon-1.png View File

Before After
Width: 29  |  Height: 29  |  Size: 688 B

BIN
react-ui/public/assets/images/component-icon-2-Failed.png View File

Before After
Width: 108  |  Height: 108  |  Size: 3.7 kB

BIN
react-ui/public/assets/images/component-icon-2-Omitted.png View File

Before After
Width: 108  |  Height: 108  |  Size: 3.6 kB

BIN
react-ui/public/assets/images/component-icon-2-Pending.png View File

Before After
Width: 108  |  Height: 108  |  Size: 3.7 kB

BIN
react-ui/public/assets/images/component-icon-2-Running.png View File

Before After
Width: 108  |  Height: 108  |  Size: 3.6 kB

BIN
react-ui/public/assets/images/component-icon-2-Succeeded.png View File

Before After
Width: 108  |  Height: 108  |  Size: 3.9 kB

BIN
react-ui/public/assets/images/component-icon-2.png View File

Before After
Width: 32  |  Height: 34  |  Size: 1.1 kB

BIN
react-ui/public/assets/images/component-icon-3-Failed.png View File

Before After
Width: 108  |  Height: 108  |  Size: 5.1 kB

BIN
react-ui/public/assets/images/component-icon-3-Omitted.png View File

Before After
Width: 108  |  Height: 108  |  Size: 5.1 kB

BIN
react-ui/public/assets/images/component-icon-3-Pending.png View File

Before After
Width: 108  |  Height: 108  |  Size: 5.1 kB

BIN
react-ui/public/assets/images/component-icon-3-Running.png View File

Before After
Width: 108  |  Height: 108  |  Size: 5.0 kB

BIN
react-ui/public/assets/images/component-icon-3-Succeeded.png View File

Before After
Width: 108  |  Height: 108  |  Size: 5.2 kB

BIN
react-ui/public/assets/images/component-icon-3.png View File

Before After
Width: 32  |  Height: 36  |  Size: 1.6 kB

BIN
react-ui/public/assets/images/component-icon-4-Failed.png View File

Before After
Width: 108  |  Height: 108  |  Size: 4.2 kB

BIN
react-ui/public/assets/images/component-icon-4-Omitted.png View File

Before After
Width: 108  |  Height: 108  |  Size: 4.1 kB

BIN
react-ui/public/assets/images/component-icon-4-Pending.png View File

Before After
Width: 108  |  Height: 108  |  Size: 4.2 kB

BIN
react-ui/public/assets/images/component-icon-4-Running.png View File

Before After
Width: 108  |  Height: 108  |  Size: 4.1 kB

BIN
react-ui/public/assets/images/component-icon-4-Succeeded.png View File

Before After
Width: 108  |  Height: 108  |  Size: 4.4 kB

BIN
react-ui/public/assets/images/component-icon-4.png View File

Before After
Width: 34  |  Height: 38  |  Size: 1.4 kB

BIN
react-ui/public/assets/images/component-icon-5-Failed.png View File

Before After
Width: 108  |  Height: 108  |  Size: 2.9 kB

BIN
react-ui/public/assets/images/component-icon-5-Omitted.png View File

Before After
Width: 108  |  Height: 108  |  Size: 2.8 kB

BIN
react-ui/public/assets/images/component-icon-5-Pending.png View File

Before After
Width: 108  |  Height: 108  |  Size: 3.0 kB

BIN
react-ui/public/assets/images/component-icon-5-Running.png View File

Before After
Width: 108  |  Height: 108  |  Size: 2.9 kB

BIN
react-ui/public/assets/images/component-icon-5-Succeeded.png View File

Before After
Width: 108  |  Height: 108  |  Size: 3.2 kB

BIN
react-ui/public/assets/images/component-icon-5.png View File

Before After
Width: 33  |  Height: 33  |  Size: 683 B

BIN
react-ui/public/assets/images/component-icon-6-Failed.png View File

Before After
Width: 108  |  Height: 108  |  Size: 3.2 kB

BIN
react-ui/public/assets/images/component-icon-6-Omitted.png View File

Before After
Width: 108  |  Height: 108  |  Size: 3.2 kB

BIN
react-ui/public/assets/images/component-icon-6-Pending.png View File

Before After
Width: 108  |  Height: 108  |  Size: 3.2 kB

BIN
react-ui/public/assets/images/component-icon-6-Running.png View File

Before After
Width: 108  |  Height: 108  |  Size: 3.1 kB

BIN
react-ui/public/assets/images/component-icon-6-Succeeded.png View File

Before After
Width: 108  |  Height: 108  |  Size: 3.3 kB

BIN
react-ui/public/assets/images/component-icon-6.png View File

Before After
Width: 31  |  Height: 31  |  Size: 716 B

BIN
react-ui/public/assets/images/component-icon-7-Failed.png View File

Before After
Width: 108  |  Height: 108  |  Size: 3.2 kB

BIN
react-ui/public/assets/images/component-icon-7-Omitted.png View File

Before After
Width: 108  |  Height: 108  |  Size: 3.1 kB

BIN
react-ui/public/assets/images/component-icon-7-Pending.png View File

Before After
Width: 108  |  Height: 108  |  Size: 3.2 kB

BIN
react-ui/public/assets/images/component-icon-7-Running.png View File

Before After
Width: 108  |  Height: 108  |  Size: 3.1 kB

BIN
react-ui/public/assets/images/component-icon-7-Succeeded.png View File

Before After
Width: 108  |  Height: 108  |  Size: 3.3 kB

BIN
react-ui/public/assets/images/component-icon-7.png View File

Before After
Width: 31  |  Height: 31  |  Size: 738 B

BIN
react-ui/public/assets/images/duty-message.png View File

Before After
Width: 27  |  Height: 27  |  Size: 483 B

BIN
react-ui/public/assets/images/static-message.png View File

Before After
Width: 26  |  Height: 28  |  Size: 620 B

+ 3
- 3
react-ui/public/fonts/font.css View File

@@ -1,4 +1,4 @@
@font-face {
font-family: Alibaba;
@font-face {
font-family: Alibaba;
src: url('./ALIBABA-PUHUITI-MEDIUM.TTF'); src: url('./ALIBABA-PUHUITI-MEDIUM.TTF');
}
}

+ 14
- 0
react-ui/src/pages/DevelopmentEnvironment/index.jsx View File

@@ -0,0 +1,14 @@
import React ,{ useState,useEffect,useRef }from 'react';
import {getJupyterUrl} from '@/services/developmentEnvironment/index.js'
const developmentEnvironment = React.FC = () => {
const [iframeUrl,setIframeUrl]=useState('')
useEffect(()=>{
getJupyterUrl().then(ret=>{
console.log(ret);
setIframeUrl(ret.msg)
})
},[])
return (
<iframe style={{width:'100%',height:'81vh'}} src={iframeUrl} frameborder="0"></iframe>
)};
export default developmentEnvironment;

+ 30
- 0
react-ui/src/pages/Experiment/experimentText/editPipeline.less View File

@@ -0,0 +1,30 @@
#graph {
height: 100%;
width: 100%;
position: relative;
}
.editPipelinePropsContent{
display: flex;
align-items: center;
width: 100%;
height:43px;
background:#f8fbff;
color:#1d1d20;
font-size:15px;
font-family: 'Alibaba';
padding: 0 20px;
}
.centerContainer{
flex: 1;
display: flex;
flex-direction: column;
}
.buttonList{
display: flex;
align-items: center;
padding: 0 30px;
width: 100%;
height:45px;
background:#ffffff;
box-shadow:0px 3px 6px rgba(146, 146, 146, 0.09);
}

+ 335
- 0
react-ui/src/pages/Experiment/experimentText/index.jsx View File

@@ -0,0 +1,335 @@
import React ,{ useState,useEffect,useRef }from 'react';
import { useParams } from 'react-router-dom'
import Props from './props';
import { useEmotionCss } from '@ant-design/use-emotion-css';
import G6 from '@antv/g6';
import Styles from './editPipeline.less'
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 { useNavigate} from 'react-router-dom';
const ExperimentText = React.FC = () => {
const propsRef=useRef()
const navgite=useNavigate();
const locationParams =useParams () //新版本获取路由参数接口
let graph=null
const [experimentStatusObj,setExperimentStatusObj]=useState({})
const pipelineContainer = useEmotionCss(() => {
return {
display: 'flex',
backgroundColor:'#fff',
height:'81vh'
};
});
const graphStyle = useEmotionCss(() => {
return {
width:'100%',
backgroundColor:'#f9fafb',
flex:1
};
});
const graphRef=useRef()
const onDragEnd=(val)=>{
console.log(val,'eee');
const _x = val.x
const _y = val.y
const point = graph.getPointByClient(_x, _y);
let model = {};
// 元模型
model = {
...val,
x: point.x,
y: point.y,
id: val.component_name+'-'+s8(),
isCluster: false,
};
console.log(graph, model);

graph.addItem('node', model, true);
console.log(graph);
}
const formChange=(val)=>{
}
const handlerClick=(e)=>{
console.log(propsRef,graph);
// let cache = [];
// let json_str = JSON.stringify(graph, function(key, value) {
// if (typeof value === 'object' && value !== null) {
// if (cache.indexOf(value) !== -1) {
// return;
// }
// cache.push(value);
// }
// return value;
// });
// console.log(json_str);
propsRef.current.showDrawer(e,locationParams.id)
}
const getGraphData=(data)=>{
if(graph){
console.log(graph);
graph.data(data)
graph.render()
}
else{
setTimeout(()=>{
getGraphData(data)
},500)
}
}
const getFirstWorkflow=(val)=>{
getWorkflowById(val).then(ret=>{
console.log(ret,'retttttttttt');
if(ret.code==200){
if(graph&&ret.data&&ret.data.dag){
console.log(JSON.parse(ret.data.dag));
getExperimentIns(locationParams.id).then(res=>{
if(res.code==200){
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)
// setExperimentStatusObj(JSON.parse(ret.data.nodes_status))
}
})

}
}
// graph&&graph.data(JSON.parse(ret.dag))
// graph.render()
})
}
// const getExperimentIn=(val)=>{
// getExperimentIns(val).then(ret=>{
// if(ret.code==200){
// console.log(JSON.parse(ret.data.nodes_status));
// setExperimentStatusObj(JSON.parse(ret.data.nodes_status))
// setTimeout(() => {
// console.log(experimentStatusObj);
// }, 1000);
// }
// })
// }
useEffect(()=>{
initGraph()
getFirstWorkflow(locationParams.workflowId)
},[])
const initGraph=()=>{
G6.registerNode(
'rect-node',
{
// draw anchor-point circles according to the anchorPoints in afterDraw
getAnchorPoints(cfg) {
return (
cfg.anchorPoints || [
// 上下各3,左右各1
[0.1, 0.05],
[0.5, 0.05],
[0.9, 0.05],
[0, 0.5],
[1, 0.5],
[0.1, 1],
[0.5, 1],
[0.9, 1],
// 四边中间
// [0.5, 0.05],
// [0, 0.5],
// [1, 0.5],
// [0.5, 1],
// 四个角落
// [0.05, 0.05],
// [0.9, 0.05],
// [0.05, 1],
// [0.9, 1],
]
);
},
afterDraw(cfg, group) {
// console.log(group, cfg, 12312);
const image = group.addShape('image', {
attrs: {
x: -25,
y: -13,
width: 23,
height: 21,
img: cfg.img,
cursor: 'pointer',
},
draggable: true,
});
// if (cfg.label) {
// group.addShape('text', {
// attrs: {
// x: 0,
// y: cfg.height / 2 - 5,
// textAlign: 'center',
// textBaseline: 'middle',
// text: cfg.label,
// fill: '#fff',
// },
// draggable: true,
// });
// }
const bbox = group.getBBox();
const anchorPoints = this.getAnchorPoints(cfg);
// console.log(anchorPoints);
anchorPoints.forEach((anchorPos, i) => {
group.addShape('circle', {
attrs: {
r: 5,
x: bbox.x + bbox.width * anchorPos[0],
y: bbox.y + bbox.height * anchorPos[1],
fill: '#000',
stroke: '#000',
},
name: `anchor-point`, // the name, for searching by group.find(ele => ele.get('name') === 'anchor-point')
anchorPointIdx: i, // flag the idx of the anchor-point circle
links: 0, // cache the number of edges connected to this shape
visible: false, // invisible by default, shows up when links > 1 or the node is in showAnchors state
draggable: true, // allow to catch the drag events on this shape
});
});
return image;
},
// response the state changes and show/hide the link-point circles
setState(name, value, item) {
// 默认显示全部锚点,防止过宽导致锚点无法被选中
// if (name === 'showAnchors') {
const anchorPoints = item.getContainer().findAll(ele => ele.get('name') === 'anchor-point');
anchorPoints.forEach(point => {
// if (value) point.show();
// else point.hide();
point.show();
});
// }
},
},
'rect'
);
console.log(graphRef,'graphRef');
graph = new G6.Graph({
container: graphRef.current,
grid: true,
width: graphRef.current.clientWidth ||500,
height: graphRef.current.clientHeight||760,
animate: false,
groupByTypes: false,
plugins: [],
enabledStack: true,
modes: {
default: [
'drag-canvas',
'zoom-canvas',
],
altSelect: [
{
type: 'brush-select',
trigger: 'drag',
},
'drag-node',
],
},

defaultNode: {
type: 'rect-node',
size: 70,
labelCfg: {
style: {
fill: '#000',
fontSize: 12,
cursor: 'pointer',
x: 0,
y: 0,
textAlign: 'left',
textBaseline: 'middle',
},
},
style: {
fill: 'transparent',
stroke: 'transparent',
},
},
// nodeStateStyles: {
// nodeSelected: {
// fill: 'red',
// shadowColor: 'red',
// stroke: 'red',
// 'text-shape': {
// fill: 'red',
// stroke: 'red',
// },
// },
// },
defaultEdge: {
// type: 'quadratic',
type: 'polyline',

style: {
endArrow: {
path: G6.Arrow.triangle(),
},
cursor: 'pointer',
endArrow: true,
lineWidth: 1,
opacity: 1,
stroke: '#a2a6b5',
radius: 10,
},
nodeStateStyle: {
hover: {
opacity: 1,
stroke: '#8fe8ff',
},
},
labelCfg: {
autoRotate: true,
// refY: 10,
style: {
fontSize: 10,
fill: '#FFF',
},
},
},
defaultCombo: {
type: 'rect',
fixCollapseSize: 70,
style: {
fill: '#00e0ff0d',
stroke: '#00e0ff',
lineDash: [5, 10],
cursor: 'pointer',
},
},
linkCenter: true,
fitView: true,
fitViewPadding: [60, 60, 60, 80],
});
graph.on('dblclick', handlerClick);
window.onresize = () => {
if (!graph || graph.get('destroyed')) return;
if (!graphRef.current || !graphRef.current.scrollWidth || !graphRef.current.scrollHeight) return;
graph.changeSize(graphRef.current.scrollWidth, graphRef.current.scrollHeight - 20);
};
}
return (<div className={pipelineContainer}>
<div className={Styles.centerContainer}>
<div className={Styles.buttonList}>
</div>
<div className={graphStyle} ref={graphRef} id={Styles.graphStyle}></div>
</div>
<Props ref={propsRef} onParentChange={formChange}></Props>
</div>)};
export default ExperimentText;

+ 265
- 0
react-ui/src/pages/Experiment/experimentText/props.jsx View File

@@ -0,0 +1,265 @@
import React, { useState,useImperativeHandle ,forwardRef } from 'react';
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';
const { TextArea } = Input;
const Props = forwardRef(({onParentChange}, ref) =>{
const [form] = Form.useForm();
const [stagingItem,setStagingItem]=useState({})
const [messageItem,setMessageItem]=useState('')

const items = [
{
key: '1',
label: '日志详情',
children: <div style={{height:'740px',
background:'rgba(234, 234, 234, 0.5)',
color:'rgba(29, 29, 32, 0.8)',
fontSize:'14px'
}} dangerouslySetInnerHTML={{ __html: messageItem }}></div>,
icon:<ProfileOutlined/>
},
{
key: '2',
label: '配置参数',
icon:<DatabaseOutlined />,
children: <Form
name="form"
form={form}
layout="vertical"
labelCol={{
span: 16,
}}
wrapperCol={{
span: 16,
}}
style={{
maxWidth: 600,
}}
initialValues={{
remember: true,
}}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
autoComplete="off"
>
<div className={Styles.editPipelinePropsContent}>
<img style={{width:'13px',marginRight:'10px'}} src={'/assets/images/static-message.png'} alt="" />
基本信息
</div>
<Form.Item
label="任务名称"
name="label"
rules={[
{
required: true,
message: '请输入任务名称',
},
]}
>
<Input disabled />
</Form.Item>
<Form.Item
label="任务ID"
name="id"
rules={[
{
required: true,
message: '请输入任务id',
},
]}
>
<Input disabled/>
</Form.Item>
<div className={Styles.editPipelinePropsContent}>
<img style={{width:'13px',marginRight:'10px'}} src={'/assets/images/duty-message.png'} alt="" />
任务信息
</div>
<Form.Item
label="镜像"
name="image"
rules={[
{
required: true,
message: '请输入镜像',
},
]}
>
<Input disabled/>
</Form.Item>
<Form.Item
label="工作目录"
name="working_directory"
>
<Input disabled/>
</Form.Item>
<Form.Item
label="启动命令"
name="command"
>
<Input disabled/>
</Form.Item>
<Form.Item
label="资源规格"
name="resources_standard"
rules={[
{
required: true,
message: '请输入资源规格',
},
]}
>
<Input disabled/>
</Form.Item>
<Form.Item
label="挂载路径"
name="mount_path"
>
<Input disabled/>
</Form.Item>
<Form.Item
label="环境变量"
name="env_variables"
>
<TextArea disabled/>
</Form.Item>
{stagingItem.control_strategy&&Object.keys(stagingItem.control_strategy)&&Object.keys(stagingItem.control_strategy).length>0?Object.keys(stagingItem.control_strategy).map(item=>
<Form.Item
label={stagingItem.control_strategy[item].label}
disabled
name={item}
>
<Input disabled/>
</Form.Item>
):''}
<div className={Styles.editPipelinePropsContent}>
<img style={{width:'13px',marginRight:'10px'}} src={'/assets/images/duty-message.png'} alt="" />
输入参数
</div>
{stagingItem.in_parameters&&Object.keys(stagingItem.in_parameters)&&Object.keys(stagingItem.in_parameters).length>0?Object.keys(stagingItem.in_parameters).map(item=>
<Form.Item
label={stagingItem.in_parameters[item].label+'('+item+')'}
name={item}
disabled
rules={[{ required: stagingItem.in_parameters[item].require?true:false}]}
>
<Input disabled/>
</Form.Item>
):''}
<div className={Styles.editPipelinePropsContent}>
<img style={{width:'13px',marginRight:'10px'}} src={'/assets/images/duty-message.png'} alt="" />
输出参数
</div>
{stagingItem.out_parameters&&Object.keys(stagingItem.out_parameters)&&Object.keys(stagingItem.out_parameters).length>0?Object.keys(stagingItem.out_parameters).map(item=>
<Form.Item
label={stagingItem.out_parameters[item].label+'('+item+')'}
disabled
rules={[{ required: stagingItem.out_parameters[item].require?true:false}]}
name={item}
>
<Input disabled/>
</Form.Item>
):''}
</Form>,
},
];
const [open, setOpen] = useState(false);
const afterOpenChange=()=>{
if(!open){
console.log(111,open);
console.log(stagingItem,form.getFieldsValue());
for(let i in form.getFieldsValue()){
for(let j in stagingItem.in_parameters){
if(i==j){
console.log(j,i);
stagingItem.in_parameters[j].value=form.getFieldsValue()[i]
}
}
for(let p in stagingItem.out_parameters){
if(i==p){
stagingItem.out_parameters[p].value=form.getFieldsValue()[i]
}
}
for(let k in stagingItem.control_strategy){
if(i==k){
stagingItem.control_strategy[k].value=form.getFieldsValue()[i]
}
}
}
// setStagingItem({...stagingItem,})
console.log((stagingItem.control_strategy));
onParentChange({...stagingItem,control_strategy:JSON.stringify(stagingItem.control_strategy),in_parameters:JSON.stringify(stagingItem.in_parameters),out_parameters:JSON.stringify(stagingItem.out_parameters),...form.getFieldsValue()})
// onParentChange({...stagingItem,...form.getFieldsValue()})
}
}
const onClose=()=> {
setOpen(false);
};
const onFinish = (values) => {
console.log('Success:', values);
};
const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo);
};
useImperativeHandle(ref, () => ({
showDrawer (e,id) {
setMessageItem('')
if(e.item&&e.item.getModel().component_id){
let params={
id:Number(id),
component_id:e.item&&e.item.getModel().component_id
}
getQueryByExperimentLog(params).then(ret=>{
console.log(ret);
let msg=ret.msg.replace(/\n/g,"</br>")
let newMsg=msg.replace(/\r/g,"</br>")
setMessageItem(newMsg)
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);
})
}
else{
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);
},
}));
return (
<>
<Drawer title="编辑任务" placement="right" closeIcon={false} onClose={onClose} afterOpenChange={afterOpenChange} open={open}>
<Tabs
defaultActiveKey="1"
items={items}/>
</Drawer>
</>
);
});

export default Props;

+ 364
- 0
react-ui/src/pages/Experiment/index.jsx View File

@@ -0,0 +1,364 @@
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 Styles from './index.less'
import { useNavigate} from 'react-router-dom';
import momnet from 'moment'
const { TextArea } = Input;

const Experiment = React.FC = () => {
const [form] = Form.useForm();
const navgite=useNavigate();
const statusObj={
"Running":'运行中',
"Succeeded":'成功',
"Failed":'失败',
"Error":'错误',
"Teminated":'终止'
}
const [experimentList, setExperimentList] = useState([]);
const [workflowList, setWorkflowList] = useState([]);
const [queryFlow,setQueryFlow]=useState({
offset:1,
page:0,
size:10000,
name:null
});
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 [formId,setFormId]=useState(null)
const [experimentInList,setExperimentInList]=useState([])
const [expandedRowKeys,setExpandedRowKeys]=useState(null)
const [total, setTotal] = useState(0);
const [dialogTitle, setDialogTitle] = useState('新建实验');
const [isModalOpen, setIsModalOpen] = useState(false);
const getWorkflowList=()=>{
getWorkflow(queryFlow).then(ret=>{
console.log(ret);
if(ret.code==200){
setWorkflowList(ret.data.content)
}
})
}
const getQueryByExperiment=(val)=>{
getQueryByExperimentId(val).then(ret=>{
setExpandedRowKeys(val)
if(ret.code==200&&ret.data&&ret.data.length>0){
setExperimentInList(ret.data)
}
else{
setExperimentInList([])
}
})
}
const expandChange=(e,record)=>{
if(record.id==expandedRowKeys){
setExpandedRowKeys(null)
}
else{
getQueryByExperiment(record.id)
}
}
const showModal = () => {
setDialogTitle('新建实验')
console.log(workflowList);
setIsModalOpen(true);
};
const editTable=(id)=>{
getExperimentById(id).then(ret=>{
if(ret.code==200){
form.setFieldsValue({...ret.data})
setFormId(ret.data.id)
setDialogTitle('编辑实验')
getWorkflowList()
setIsModalOpen(true)
}
})
// navgite({pathname:`/pipeline/pytorchtext/${record.id}/${record.name}` });
}
const handleOk = () => {
console.log(1111);
setIsModalOpen(false);
};
const handleCancel = () => {
setIsModalOpen(false);
};
const routeToEdit=(e,record)=>{
e.stopPropagation()
navgite({pathname:`/pipeline/pytorchtext/${record.workflow_id}/${record.workflow_name}` });
}
const onFinish = (values) => {
console.log(values,formId);
if(!formId){
postExperiment(values).then(ret=>{
if(ret.code==200){
message.success('新建实验成功')
setIsModalOpen(false)
getList()
}
else{
message.error('新建实验失败')
}
})
}
else{
putExperiment({...values,id:formId}).then(ret=>{
if(ret.code==200){
message.success('编辑实验成功')
setIsModalOpen(false)
getList()
}
else{
message.error('编辑实验失败')
}
})
}
setFormId(null)
};
const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo);
};
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)
getList()
}
else{
message.error('运行失败')
}
})
}
const routerToText=(e,item,record)=>{
e.stopPropagation()
navgite({pathname:`/experiment/pytorchtext/${record.workflow_id}/${item.id}` });
}
const getList=()=>{
let params={
offset:0,
page:pageOption.current.page-1,
size:pageOption.current.size
}
console.log(params,pageOption);
getExperiment(params).then(ret=>{
if(ret.code==200){
setExperimentList(ret.data.content.map(item=>{return{...item,key:item.id}}))
setTotal(ret.data.totalElements)
}
console.log(experimentList,total);
})
}
useEffect(()=>{
getList()
getWorkflowList()
// const timeOut= setInterval(() => {
// getList()
// }, 2000);
return () => {
// timeOut&&clearInterval(timeOut)
// console.log('will unmount');
}
},[])
const columns = [
// {
// title: '序号',
// dataIndex: 'index',
// key: 'index',
// width: 60,
// render(text, record, index) {
// return (
// <span>{(pageOption.current.page - 1) * 10 + index + 1}</span>
// )
// }
// // render: (text, record, index) => `${((curPage-1)*10)+(index+1)}`,
// },
{
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: 'state',
key: 'state',
},
{
title: '操作',
key: 'action',
render: (_, record) => (
<Space size="small">
<Button
type="link"
size="small"
key="edit"
icon = {<PlayCircleOutlined />}
onClick={() => {
runExperiment(record.id)
}}
>
运行
</Button>
<Button
type="link"
size="small"
key="edit"
icon = {< EditOutlined />}
onClick={() => {
editTable(record.id)
}}
>
编辑
</Button>

</Space>
),
},
];
return (<div>
<div >
<Button type="primary" onClick={showModal} icon = {< PlusOutlined />}>
新建实验
</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>:''}
{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>
)):''}
</div>

),
onExpand:(e,a)=>{expandChange(e,a)},
expandedRowKeys:[expandedRowKeys],
rowExpandable: (record) =>true,
}}/>
<Modal title={dialogTitle} open={isModalOpen} okButtonProps={{
htmlType: 'submit',
form: 'form',
}} onCancel={handleCancel}>
<Form
name="form"
form={form}
layout="vertical"
labelCol={{
span: 8,
}}
wrapperCol={{
span: 16,
}}
style={{
maxWidth: 600,
}}
initialValues={{
remember: true,
}}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
autoComplete="off"
>
<Form.Item
label="实验名称"
name="name"
rules={[
// {
// required: true,
// message: 'Please input your username!',
// },
]}
>
<Input />
</Form.Item>
<Form.Item
label="实验描述"
name="description"
rules={[
// {
// required: true,
// message: 'Please input your username!',
// },
]}
>
<Input />
</Form.Item>
<Form.Item
label="选择流水线"
name="workflow_id"
rules={[
// {
// required: true,
// message: 'Please input your username!',
// },
]}
>
<Select>
{workflowList&&workflowList.length>0?workflowList.map(item=>{return <Select.Option value={item.id}>{item.name}</Select.Option>}):''}
{/* <Select.Option value="demo">Demo</Select.Option> */}
</Select>
</Form.Item>
</Form>
</Modal>
</div>)};
export default Experiment;

+ 35
- 0
react-ui/src/pages/Experiment/index.less View File

@@ -0,0 +1,35 @@
.experimentTopBox{
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 30px;
width: 100%;
height: 49px;
background-size: 100% 100%;
background-image: url(/assets/images/pipeline-back.png);
}
// .plusButton{
// background:rgba(22, 100, 255, 0.06);
// border:1px solid;
// border-color:rgba(22, 100, 255, 0.11);
// border-radius:4px;
// color:#1d1d20;
// font-size:15px;
// font-family: 'Alibaba';
// }
// .plusButton:hover{
// background:rgba(22, 100, 255, 0.06);
// border:1px solid;
// border-color:rgba(22, 100, 255, 0.11);
// border-radius:4px;
// }
.tableExpandBox{
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
color:#1d1d20;
font-size:15px;
padding: 0 65px 0 40px;
}

+ 30
- 0
react-ui/src/pages/Pipeline/editPipeline/editPipeline.less View File

@@ -0,0 +1,30 @@
#graph {
height: 100%;
width: 100%;
position: relative;
}
.editPipelinePropsContent{
display: flex;
align-items: center;
width: 100%;
height:43px;
background:#f8fbff;
color:#1d1d20;
font-size:15px;
font-family: 'Alibaba';
padding: 0 20px;
}
.centerContainer{
flex: 1;
display: flex;
flex-direction: column;
}
.buttonList{
display: flex;
align-items: center;
padding: 0 30px;
width: 100%;
height:45px;
background:#ffffff;
box-shadow:0px 3px 6px rgba(146, 146, 146, 0.09);
}

+ 366
- 0
react-ui/src/pages/Pipeline/editPipeline/index.jsx View File

@@ -0,0 +1,366 @@
import React ,{ useState,useEffect,useRef }from 'react';
import { useParams } from 'react-router-dom'
import ModelMenus from './modelMenus';
import Props from './props';
import { useEmotionCss } from '@ant-design/use-emotion-css';
import G6 from '@antv/g6';
import Styles from './editPipeline.less'
import { s8 } from '../../../utils';
import { Button, message} from 'antd';
import {SaveOutlined} from '@ant-design/icons';
import {saveWorkflow,getWorkflowById} from '@/services/pipeline/index.js'
import { useNavigate} from 'react-router-dom';
const editPipeline = React.FC = () => {
const propsRef=useRef()
const navgite=useNavigate();
const locationParams =useParams () //新版本获取路由参数接口
let graph=null
const pipelineContainer = useEmotionCss(() => {
return {
display: 'flex',
backgroundColor:'#fff',
height:'81vh'
};
});
const graphStyle = useEmotionCss(() => {
return {
width:'100%',
backgroundColor:'#f9fafb',
flex:1
};
});
const graphRef=useRef()
const onDragEnd=(val)=>{
console.log(val,'eee');
const _x = val.x
const _y = val.y
const point = graph.getPointByClient(_x, _y);
let model = {};
// 元模型
model = {
...val,
x: point.x,
y: point.y,
id: val.component_name+'-'+s8(),
isCluster: false,
};
console.log(graph, model);

graph.addItem('node', model, true);
console.log(graph);
}
const formChange=(val)=>{
const data = graph.save();
const index = data.nodes.findIndex(item => {
return item.id ===val.id;
});
data.nodes[index] = val;
graph.changeData(data)
}
const savePipeline=()=>{
const data = graph.save();
console.log(data);
let params={
...locationParams,
dag:JSON.stringify(data)
}
saveWorkflow(params).then(ret=>{
console.log(ret);
message.success('保存成功')
setTimeout(()=>{
navgite({pathname:`/pipeline`,});
},500)
})
console.log(params);
}
const handlerClick=(e)=>{
console.log(propsRef,graph);
// let cache = [];
// let json_str = JSON.stringify(graph, function(key, value) {
// if (typeof value === 'object' && value !== null) {
// if (cache.indexOf(value) !== -1) {
// return;
// }
// cache.push(value);
// }
// return value;
// });
// console.log(json_str);
propsRef.current.showDrawer(e)
}
const getGraphData=(data)=>{
if(graph){
console.log(graph);
graph.data(data)
graph.render()
}
else{
setTimeout(()=>{
getGraphData(data)
},500)
}
}
const getFirstWorkflow=(val)=>{
getWorkflowById(val).then(ret=>{
console.log(ret);
if(ret.code==200){
if(graph&&ret.data&&ret.data.dag){

getGraphData(JSON.parse(ret.data.dag))

}
}
// graph&&graph.data(JSON.parse(ret.dag))
// graph.render()
})
}
useEffect(()=>{
getFirstWorkflow(locationParams.id)
initGraph()
},[])
const initGraph=()=>{
G6.registerNode(
'rect-node',
{
// draw anchor-point circles according to the anchorPoints in afterDraw
getAnchorPoints(cfg) {
return (
cfg.anchorPoints || [
// 上下各3,左右各1
[0.1, 0.05],
[0.5, 0.05],
[0.9, 0.05],
[0, 0.5],
[1, 0.5],
[0.1, 1],
[0.5, 1],
[0.9, 1],
// 四边中间
// [0.5, 0.05],
// [0, 0.5],
// [1, 0.5],
// [0.5, 1],
// 四个角落
// [0.05, 0.05],
// [0.9, 0.05],
// [0.05, 1],
// [0.9, 1],
]
);
},
afterDraw(cfg, group) {
// console.log(group, cfg, 12312);
const image = group.addShape('image', {
attrs: {
x: -25,
y: -13,
width: 21,
height: 21,
img: cfg.img,
cursor: 'pointer',
},
draggable: true,
});
// if (cfg.label) {
// group.addShape('text', {
// attrs: {
// x: 0,
// y: cfg.height / 2 - 5,
// textAlign: 'center',
// textBaseline: 'middle',
// text: cfg.label,
// fill: '#fff',
// },
// draggable: true,
// });
// }
const bbox = group.getBBox();
const anchorPoints = this.getAnchorPoints(cfg);
// console.log(anchorPoints);
anchorPoints.forEach((anchorPos, i) => {
group.addShape('circle', {
attrs: {
r: 5,
x: bbox.x + bbox.width * anchorPos[0],
y: bbox.y + bbox.height * anchorPos[1],
fill: '#000',
stroke: '#000',
},
name: `anchor-point`, // the name, for searching by group.find(ele => ele.get('name') === 'anchor-point')
anchorPointIdx: i, // flag the idx of the anchor-point circle
links: 0, // cache the number of edges connected to this shape
visible: false, // invisible by default, shows up when links > 1 or the node is in showAnchors state
draggable: true, // allow to catch the drag events on this shape
});
});
return image;
},
// response the state changes and show/hide the link-point circles
setState(name, value, item) {
// 默认显示全部锚点,防止过宽导致锚点无法被选中
// if (name === 'showAnchors') {
const anchorPoints = item.getContainer().findAll(ele => ele.get('name') === 'anchor-point');
anchorPoints.forEach(point => {
// if (value) point.show();
// else point.hide();
point.show();
});
// }
},
},
'rect'
);
console.log(graphRef,'graphRef');
graph = new G6.Graph({
container: graphRef.current,
grid: true,
width: graphRef.current.clientWidth ||500,
height: graphRef.current.clientHeight||760,
animate: false,
groupByTypes: false,
fitView:true,
plugins: [],
enabledStack: true,
modes: {
default: [
// config the shouldBegin for drag-node to avoid node moving while dragging on the anchor-point circles
{
type: 'drag-node',
shouldBegin: e => {
if (e.target.get('name') === 'anchor-point') return false;
return true;
},
// shouldEnd: e => {
// console.log(e);
// return false;
// },
},
// config the shouldBegin and shouldEnd to make sure the create-edge is began and ended at anchor-point circles
{
type: 'create-edge',
key: 'shift', // undefined by default, options: 'shift', 'control', 'ctrl', 'meta', 'alt'
},
'drag-canvas',
'zoom-canvas',
// 'brush-select',
'drag-combo',
],
altSelect: [
{
type: 'brush-select',
trigger: 'drag',
},
'drag-node',
],
},

defaultNode: {
type: 'rect-node',
size: 70,
labelCfg: {
style: {
fill: '#000',
fontSize: 12,
cursor: 'pointer',
x: 0,
y: 0,
textAlign: 'left',
textBaseline: 'middle',
},
},
style: {
fill: 'transparent',
stroke: 'transparent',
},
},
// nodeStateStyles: {
// nodeSelected: {
// fill: 'red',
// shadowColor: 'red',
// stroke: 'red',
// 'text-shape': {
// fill: 'red',
// stroke: 'red',
// },
// },
// },
defaultEdge: {
// type: 'quadratic',
type: 'polyline',

style: {
endArrow: {
path: G6.Arrow.triangle(),
},
cursor: 'pointer',
endArrow: true,
lineWidth: 1,
opacity: 1,
stroke: '#a2a6b5',
radius: 10,
},
nodeStateStyle: {
hover: {
opacity: 1,
stroke: '#8fe8ff',
},
},
labelCfg: {
autoRotate: true,
// refY: 10,
style: {
fontSize: 10,
fill: '#FFF',
},
},
},
defaultCombo: {
type: 'rect',
fixCollapseSize: 70,
style: {
fill: '#00e0ff0d',
stroke: '#00e0ff',
lineDash: [5, 10],
cursor: 'pointer',
},
},
linkCenter: true,
fitView: true,
fitViewPadding: [60, 60, 60, 80],
});
graph.on('dblclick', handlerClick);
graph.on('aftercreateedge', (e) => {
const edges = graph.save().edges;
G6.Util.processParallelEdges(edges);
graph.getEdges().forEach((edge, i) => {
graph.updateItem(edge, {
curveOffset: edges[i].curveOffset,
curvePosition: edges[i].curvePosition,
});
});
});
window.onresize = () => {
if (!graph || graph.get('destroyed')) return;
if (!graphRef.current || !graphRef.current.scrollWidth || !graphRef.current.scrollHeight) return;
graph.changeSize(graphRef.current.scrollWidth, graphRef.current.scrollHeight - 20);
};
}
return (<div className={pipelineContainer}>
<ModelMenus onParDragEnd={onDragEnd}></ModelMenus>
<div className={Styles.centerContainer}>
<div className={Styles.buttonList}>
<Button type="primary" shape="round" icon={<SaveOutlined />} onClick={savePipeline}>撤回</Button>
<Button type="primary" shape="round" icon={<SaveOutlined />} onClick={savePipeline}>恢复</Button>
<Button type="primary" shape="round" icon={<SaveOutlined />} onClick={savePipeline}>保存</Button>
</div>
<div className={graphStyle} ref={graphRef} id={Styles.graphStyle}></div>
</div>
<Props ref={propsRef} onParentChange={formChange}></Props>
</div>)};
export default editPipeline;

+ 60
- 0
react-ui/src/pages/Pipeline/editPipeline/modelMenus.jsx View File

@@ -0,0 +1,60 @@
import React ,{ useState,useEffect,useRef }from 'react';
import { Collapse, Divider } from 'antd';
import Styles from './modelMenus.less'
import {getComponentAll} from '@/services/pipeline/index.js'
const items = [
{
key: '1',
label: 'This is panel header 1',
children: [1,2,3,4,5],
},
{
key: '2',
label: 'This is panel header 2',
children: [1,2,3,4,5],
},
{
key: '3',
label: 'This is panel header 3',
children: [1,2,3,4,5],
},
];
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 style={{width:'300px',height:'100%'}}>
<Collapse
collapsible="header"
defaultActiveKey={['1']}
>
{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 draggable="true" onDragEnd={(e)=>{dragEnd(e,ele)}} className={Styles.collapseItem}>
<img style={{height:'16px',marginRight:'15px'}} src={`/assets/images/${ele.icon_path}.png`} alt="" />
{ele.component_label}
</div>)
):''}
</Panel>
))
: ""}
</Collapse>
</div>)};
export default modelMenus;

+ 12
- 0
react-ui/src/pages/Pipeline/editPipeline/modelMenus.less View File

@@ -0,0 +1,12 @@
.collapseList{
}
.collapseItem{
display: flex;
align-items: center;
color:#575757;
font-size:14px;
height:40px;
cursor: pointer;
}

+ 214
- 0
react-ui/src/pages/Pipeline/editPipeline/props.jsx View File

@@ -0,0 +1,214 @@
import React, { useState,useImperativeHandle ,forwardRef } from 'react';
import { Button, Drawer,Form, Input , } from 'antd';
import Styles from './editPipeline.less'
const { TextArea } = Input;
const Props = forwardRef(({onParentChange}, ref) =>{
const [form] = Form.useForm();
const [stagingItem,setStagingItem]=useState({})
const [open, setOpen] = useState(false);
const afterOpenChange=()=>{
if(!open){
console.log(111,open);
console.log(stagingItem,form.getFieldsValue());
for(let i in form.getFieldsValue()){
for(let j in stagingItem.in_parameters){
if(i==j){
console.log(j,i);
stagingItem.in_parameters[j].value=form.getFieldsValue()[i]
}
}
for(let p in stagingItem.out_parameters){
if(i==p){
stagingItem.out_parameters[p].value=form.getFieldsValue()[i]
}
}
for(let k in stagingItem.control_strategy){
if(i==k){
stagingItem.control_strategy[k].value=form.getFieldsValue()[i]
}
}
}
// setStagingItem({...stagingItem,})
console.log((stagingItem.control_strategy));
onParentChange({...stagingItem,control_strategy:JSON.stringify(stagingItem.control_strategy),in_parameters:JSON.stringify(stagingItem.in_parameters),out_parameters:JSON.stringify(stagingItem.out_parameters),...form.getFieldsValue()})
// onParentChange({...stagingItem,...form.getFieldsValue()})
}
}
const onClose=()=> {
setOpen(false);
};
const onFinish = (values) => {
console.log('Success:', values);
};
const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo);
};
useImperativeHandle(ref, () => ({
showDrawer (e) {
console.log(e.item.getModel());
// console.log(e.item.getModel().in_parameters);
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);
},
}));
return (
<>
<Drawer title="编辑任务" placement="right" closeIcon={false} onClose={onClose} afterOpenChange={afterOpenChange} open={open}>
<Form
name="form"
form={form}
layout="vertical"
labelCol={{
span: 16,
}}
wrapperCol={{
span: 16,
}}
style={{
maxWidth: 600,
}}
initialValues={{
remember: true,
}}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
autoComplete="off"
>
<div className={Styles.editPipelinePropsContent}>
<img style={{width:'13px',marginRight:'10px'}} src={'/assets/images/static-message.png'} alt="" />
基本信息
</div>
<Form.Item
label="任务名称"
name="label"
rules={[
{
required: true,
message: '请输入任务名称',
},
]}
>
<Input />
</Form.Item>
<Form.Item
label="任务ID"
name="id"
rules={[
{
required: true,
message: '请输入任务id',
},
]}
>
<Input disabled/>
</Form.Item>
<div className={Styles.editPipelinePropsContent}>
<img style={{width:'13px',marginRight:'10px'}} src={'/assets/images/duty-message.png'} alt="" />
任务信息
</div>
<Form.Item
label="镜像"
name="image"
rules={[
{
required: true,
message: '请输入镜像',
},
]}
>
<Input />
</Form.Item>
<Form.Item
label="工作目录"
name="working_directory"
>
<Input />
</Form.Item>
<Form.Item
label="启动命令"
name="command"
>
<Input />
</Form.Item>
<Form.Item
label="资源规格"
name="resources_standard"
rules={[
{
required: true,
message: '请输入资源规格',
},
]}
>
<Input />
</Form.Item>
<Form.Item
label="挂载路径"
name="mount_path"
>
<Input />
</Form.Item>
<Form.Item
label="环境变量"
name="env_variables"
>
<TextArea />
</Form.Item>
{stagingItem.control_strategy&&Object.keys(stagingItem.control_strategy)&&Object.keys(stagingItem.control_strategy).length>0?Object.keys(stagingItem.control_strategy).map(item=>
<Form.Item
label={stagingItem.control_strategy[item].label}
name={item}
>
<Input />
</Form.Item>
):''}
<div className={Styles.editPipelinePropsContent}>
<img style={{width:'13px',marginRight:'10px'}} src={'/assets/images/duty-message.png'} alt="" />
输入参数
</div>
{stagingItem.in_parameters&&Object.keys(stagingItem.in_parameters)&&Object.keys(stagingItem.in_parameters).length>0?Object.keys(stagingItem.in_parameters).map(item=>
<Form.Item
label={stagingItem.in_parameters[item].label+'('+item+')'}
name={item}
rules={[{ required: stagingItem.in_parameters[item].require?true:false}]}
>
<Input />
</Form.Item>
):''}
<div className={Styles.editPipelinePropsContent}>
<img style={{width:'13px',marginRight:'10px'}} src={'/assets/images/duty-message.png'} alt="" />
输出参数
</div>
{stagingItem.out_parameters&&Object.keys(stagingItem.out_parameters)&&Object.keys(stagingItem.out_parameters).length>0?Object.keys(stagingItem.out_parameters).map(item=>
<Form.Item
label={stagingItem.out_parameters[item].label+'('+item+')'}
rules={[{ required: stagingItem.out_parameters[item].require?true:false}]}
name={item}
>
<Input />
</Form.Item>
):''}
</Form>
</Drawer>
</>
);
});

export default Props;

+ 184
- 124
react-ui/src/pages/Pipeline/index.jsx View File

@@ -1,58 +1,142 @@
import React ,{ useState }from 'react';
import { Space, Table, Tag,Button,Modal, Form, Input } from 'antd';
import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined, DownOutlined, EditOutlined } from '@ant-design/icons';
import { ActionType, FooterToolbar, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
import React ,{ useState,useEffect,useRef }from 'react';
import { Space, Table, Tag,Button,Modal, Form, Input ,message} from 'antd';
import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined, DownOutlined, EditOutlined ,CopyOutlined} from '@ant-design/icons';
import {getWorkflow,addWorkflow,removeWorkflow,cloneWorkflow,getWorkflowById,editWorkflow} from '@/services/pipeline/index.js'
import Styles from './index.less' import Styles from './index.less'
import momnet from 'moment'
import { useNavigate} from 'react-router-dom';
const { TextArea } = Input;


const Pipeline = React.FC = () => { const Pipeline = React.FC = () => {
const editTable=()=>{
console.log(111)
}
const [form] = Form.useForm();
const navgite=useNavigate();
const [formId,setFormId]=useState(null)
const [dialogTitle,setDialogTitle]=useState('新建流水线')
const [pipeList, setPipeList] = useState([]);
const [total, setTotal] = useState(0);
const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false);
const editTable=(e,record)=>{
e.stopPropagation()
getWorkflowById(record.id).then(ret=>{
if(ret.code==200){
form.resetFields()
form.setFieldsValue({...ret.data})
setFormId(ret.data.id)
setDialogTitle('编辑流水线')
setIsModalOpen(true)
}
})
}
const routeToEdit=(e,record)=>{
e.stopPropagation()
navgite({pathname:`/pipeline/pytorchtext/${record.id}/${record.name}` });
}
const showModal = () => { const showModal = () => {
form.resetFields()
setDialogTitle('编辑流水线')
setIsModalOpen(true); setIsModalOpen(true);
}; };
const handleOk = () => { const handleOk = () => {
console.log(1111);
setIsModalOpen(false); setIsModalOpen(false);
}; };
const handleCancel = () => { const handleCancel = () => {
setIsModalOpen(false); setIsModalOpen(false);
}; };
const onFinish = (values) => { const onFinish = (values) => {
console.log('Success:', values);
};
const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo);
if(formId){
editWorkflow({...values,id:formId}).then(ret=>{
message.success('编辑成功')
getList()
setIsModalOpen(false)
})
}
else{
addWorkflow(values).then(ret=>{
navgite({pathname:`/pipeline/pytorchtext/${ret.id}/${ret.name}`,});
}
)
}
}; };
const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo);
};
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 getList=()=>{
let params={
offset:1,
page:pageOption.current.page-1,
size:pageOption.current.size
}
console.log(params,pageOption);
getWorkflow(params).then(ret=>{
if(ret.code==200){
setPipeList(ret.data.content)
setTotal(ret.data.totalElements)
}

})
}
useEffect(()=>{
getList()
},[])
const columns = [ const columns = [
{ {
title: '序号', title: '序号',
dataIndex: 'index', dataIndex: 'index',
key: 'index', key: 'index',
width: 60, width: 60,
render:(text,record,index)=> index + 1,
render(text, record, index) {
return (
<span>{(pageOption.current.page - 1) * 10 + index + 1}</span>
)
}
// render: (text, record, index) => `${((curPage-1)*10)+(index+1)}`, // render: (text, record, index) => `${((curPage-1)*10)+(index+1)}`,
}, },
{ {
title: '流水线名称', title: '流水线名称',
dataIndex: 'name', dataIndex: 'name',
key: 'name', key: 'name',
render: (text) => <a>{text}</a>,
render: (text,record) => <a onClick={(e)=>routeToEdit(e,record)}>{text}</a>,
}, },
{ {
title: '流水线描述', title: '流水线描述',
dataIndex: 'age',
key: 'age',
dataIndex: 'description',
key: 'description',
}, },
{ {
title: '创建时间', title: '创建时间',
dataIndex: 'address',
key: 'address',
dataIndex: 'create_time',
key: 'create_time',
render: (text) => <span>{momnet(text).format('YYYY-MM-DD HH:mm:ss')}</span>,
}, },
{ {
title: '修改时间', title: '修改时间',
dataIndex: 'address',
key: 'address',
dataIndex: 'update_time',
key: 'update_time',
render: (text) => <span>{momnet(text).format('YYYY-MM-DD HH:mm:ss')}</span>,
}, },
{ {
title: '操作', title: '操作',
@@ -64,8 +148,8 @@ const Pipeline = React.FC = () => {
size="small" size="small"
key="edit" key="edit"
icon = {< EditOutlined />} icon = {< EditOutlined />}
onClick={() => {
editTable()
onClick={(e) => {
editTable(e,record)
}} }}
> >
编辑 编辑
@@ -73,10 +157,33 @@ const Pipeline = React.FC = () => {
<Button <Button
type="link" type="link"
size="small" size="small"
key="edit"
icon = {< EditOutlined />}
onClick={() => {
editTable()
key="clone"
icon = {< CopyOutlined />}
onClick={async () => {
Modal.confirm({
title: '复制',
content: '确定复制该条流水线吗?',
okText: '确认',
cancelText: '取消',
onOk: () => {
console.log(record);
cloneWorkflow(record.id).then(ret=>{
if(ret.code==200){
message.success('复制成功')
getList()
}
else{
message.error('复制失败')
}
});
// if (success) {
// if (actionRef.current) {
// actionRef.current.reload();
// }
// }
},
});
}} }}
> >
复制 复制
@@ -90,11 +197,21 @@ const Pipeline = React.FC = () => {
onClick={async () => { onClick={async () => {
Modal.confirm({ Modal.confirm({
title: '删除', title: '删除',
content: '确定删除该吗?',
content: '确定删除该条流水线吗?',
okText: '确认', okText: '确认',
cancelText: '取消', cancelText: '取消',
onOk: async () => {
// const success = await handleRemoveOne(record);
onOk: () => {
console.log(record);
removeWorkflow(record.id).then(ret=>{
if(ret.code==200){
message.success('删除成功')
getList()
}
else{
message.error(ret.msg)
}
});
// if (success) { // if (success) {
// if (actionRef.current) { // if (actionRef.current) {
// actionRef.current.reload(); // actionRef.current.reload();
@@ -110,102 +227,21 @@ const Pipeline = React.FC = () => {
), ),
}, },
]; ];
const data = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
tags: ['nice', 'developer'],
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
tags: ['loser'],
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sydney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
{
key: '4',
name: 'Joe Black',
age: 32,
address: 'Sydney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
{
key: '5',
name: 'Joe Black',
age: 32,
address: 'Sydney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
{
key: '6',
name: 'Joe Black',
age: 32,
address: 'Sydney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
{
key: '7',
name: 'Joe Black',
age: 32,
address: 'Sydney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
{
key: '8',
name: 'Joe Black',
age: 32,
address: 'Sydney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
{
key: '9',
name: 'Joe Black',
age: 32,
address: 'Sydney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
{
key: '10',
name: 'Joe Black',
age: 32,
address: 'Sydney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
{
key: '11',
name: 'Joe Black',
age: 32,
address: 'Sydney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
{
key: '12',
name: 'Joe Black',
age: 32,
address: 'Sydney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
];
return (<div> return (<div>
<div className={Styles.pipelineTopBox}> <div className={Styles.pipelineTopBox}>
<Button type="primary" className={Styles.plusButton} onClick={showModal} icon = {< PlusOutlined />}> <Button type="primary" className={Styles.plusButton} onClick={showModal} icon = {< PlusOutlined />}>
新建流水线 新建流水线
</Button> </Button>
</div> </div>
<Table columns={columns} dataSource={data} />
<Modal title="新建流水线" open={isModalOpen} onOk={handleOk} onCancel={handleCancel}>
<Table columns={columns} dataSource={pipeList} pagination={paginationProps}/>
<Modal title={dialogTitle} open={isModalOpen} okButtonProps={{
htmlType: 'submit',
form: 'form',
}} onCancel={handleCancel}>
<Form <Form
name="basic"
name="form"
form={form}
layout="vertical"
labelCol={{ labelCol={{
span: 8, span: 8,
}} }}
@@ -223,18 +259,42 @@ const Pipeline = React.FC = () => {
autoComplete="off" autoComplete="off"
> >
<Form.Item <Form.Item
label="Username"
name="username"
label="流水线名称"
name="name"
rules={[ rules={[
{
required: true,
message: 'Please input your username!',
},
// {
// required: true,
// message: 'Please input your username!',
// },
]}
>
<Input />
</Form.Item>
<Form.Item
label="流水线描述"
name="description"
rules={[
// {
// required: true,
// message: 'Please input your username!',
// },
]} ]}
> >
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item
label="备注"
name="remake"
rules={[
// {
// required: true,
// message: 'Please input your username!',
// },
]}
>
<TextArea />
</Form.Item>
</Form> </Form>
</Modal> </Modal>
</div>)}; </div>)};
export default Pipeline;
export default Pipeline;

+ 1
- 1
react-ui/src/pages/Pipeline/index.less View File

@@ -22,4 +22,4 @@
// border:1px solid; // border:1px solid;
// border-color:rgba(22, 100, 255, 0.11); // border-color:rgba(22, 100, 255, 0.11);
// border-radius:4px; // border-radius:4px;
// }
// }

+ 4
- 4
react-ui/src/pages/User/Login/index.tsx View File

@@ -118,7 +118,7 @@ const Login: React.FC = () => {
color:'#1d1d20', color:'#1d1d20',
fontSize:'36px', fontSize:'36px',
fontFamily: 'Alibaba', fontFamily: 'Alibaba',
}; };
}); });
const centerTitleBoX= useEmotionCss(() => { const centerTitleBoX= useEmotionCss(() => {
@@ -249,12 +249,12 @@ const Login: React.FC = () => {
</div> </div>
<div className={centerTitleBoX}> <div className={centerTitleBoX}>
<span style={{whiteSpace:'nowrap'}}>复杂智能软件</span> <span style={{whiteSpace:'nowrap'}}>复杂智能软件</span>
<img src="/assets/images/ai-logo.png" style={{height:'47px',marginTop:'-10px'}} alt="" /> <img src="/assets/images/ai-logo.png" style={{height:'47px',marginTop:'-10px'}} alt="" />
</div> </div>
<div className={centerMessage}> <div className={centerMessage}>
<span style={{whiteSpace:'nowrap'}}>大语言模型运维 统一管理平台</span> <span style={{whiteSpace:'nowrap'}}>大语言模型运维 统一管理平台</span>
</div> </div>
<img src="/assets/images/left-back-logo.png" style={{width:'90%',position:'absolute',top:'326px',left: '50%', <img src="/assets/images/left-back-logo.png" style={{width:'90%',position:'absolute',top:'326px',left: '50%',
transform:'translateX(-50%)',}} alt="" /> transform:'translateX(-50%)',}} alt="" />
@@ -480,7 +480,7 @@ const Login: React.FC = () => {
</div> </div>
</LoginForm> </LoginForm>
</div> </div>
</div> </div>
{/* <Helmet> {/* <Helmet>
<title> <title>


+ 2
- 2
react-ui/src/pages/User/Login/login.less View File

@@ -1,6 +1,6 @@
.loginForm{ .loginForm{
width: 520px; width: 520px;
} }
:global .ant-pro-form-login-main{ :global .ant-pro-form-login-main{
margin: unset; margin: unset;
@@ -26,4 +26,4 @@
color:#ffffff; color:#ffffff;
font-size:20px; font-size:20px;
font-family: 'Alibaba'; font-family: 'Alibaba';
}
}

+ 8
- 0
react-ui/src/services/developmentEnvironment/index.js View File

@@ -0,0 +1,8 @@
import { request } from '@umijs/max';
// 查询流水线列表
export function getJupyterUrl(params) {
return request(`/api/mmp/jupyter/getURL`, {
method: 'GET',
params
});
}

+ 62
- 0
react-ui/src/services/experiment/index.js View File

@@ -0,0 +1,62 @@
import { request } from '@umijs/max';
// 查询实验列表
export function getExperiment(params) {
return request(`/api/mmp/experiment`, {
method: 'GET',
params
});
}
// 运行实验
export function runExperiments(id) {
return request('/api/mmp/experiment/experiments/'+id, {
method: 'PUT',
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
});
}
// 根据id查询实验
export function getExperimentById(id) {
return request(`/api/mmp/experiment/${id}`, {
method: 'GET',
});
}
// 根据id查询实验实例
export function getQueryByExperimentId(id) {
return request(`/api/mmp/experimentIns/queryByExperimentId/${id}`, {
method: 'GET',
});
}
// 根据id查询查询日志
export function getQueryByExperimentLog(params) {
return request(`/api/mmp/experimentIns/log/`, {
method: 'GET',
params
});
}
// 根据实例查询详情
export function getExperimentIns(id) {
return request(`/api/mmp/experimentIns/${id}`, {
method: 'GET',
});
}
// 新增实验
export function postExperiment(data) {
return request(`/api/mmp/experiment`, {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
data
});
}
// 编辑实验
export function putExperiment(data) {
return request(`/api/mmp/experiment`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
data
});
}

+ 66
- 0
react-ui/src/services/pipeline/index.js View File

@@ -0,0 +1,66 @@
import { request } from '@umijs/max';
import { ContentType } from '@/enums/httpEnum';
// 查询流水线列表
export function getWorkflow(params) {
return request(`/api/mmp/workflow`, {
method: 'GET',
params
});
}
// 新建流水线
export function addWorkflow(params) {
return request('/api/mmp/workflow', {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
data: params
});
}
// 编辑流水线
export function editWorkflow(params) {
return request('/api/mmp/workflow', {
method: 'PUT',
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
data: params
});
}
// 删除流水线
export async function removeWorkflow(id) {
return request(`/api/mmp/workflow/${id}`, {
method: 'DELETE',
});
}
// 复制流水线
export function cloneWorkflow(id) {
return request(`/api/mmp/workflow/duplicate/${id}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
});
}
// 查询组件模型列表
export function getComponentAll() {
return request(`/api/mmp/component/components/all`, {
method: 'GET',
});
}
// 保存流水线
export function saveWorkflow(params) {
return request('/api/mmp/workflow', {
method: 'PUT',
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
data: params
});
}
// 根据id查询流水线
export function getWorkflowById(id) {
return request(`/api/mmp/workflow/${id}`, {
method: 'GET',
});
}

+ 11
- 0
react-ui/src/utils/index.js View File

@@ -0,0 +1,11 @@
export function s8() {
return (((1 + Math.random()) * 0x100000000) | 0).toString(16).substring(1);
}
export function getNameByCode(list,code){
let name = ''
list.forEach(item=>{
if(item.dictValue === code) name = item.dictLabel
})
return name
}

+ 1
- 1
react-ui/tsconfig.json View File

@@ -19,5 +19,5 @@
"@@test/*": ["./src/.umi-test/*"] "@@test/*": ["./src/.umi-test/*"]
} }
}, },
"include": ["./**/*.d.ts", "./**/*.ts", "./**/*.tsx"]
"include": ["./**/*.d.ts", "./**/*.ts", "./**/*.tsx", "src/pages/Pipeline/index.jsx"]
} }

Loading…
Cancel
Save