diff --git a/react-ui/config/defaultSettings.ts b/react-ui/config/defaultSettings.ts index 0d58faa1..91930d6e 100644 --- a/react-ui/config/defaultSettings.ts +++ b/react-ui/config/defaultSettings.ts @@ -18,7 +18,7 @@ const Settings: ProLayoutProps & { colorWeak: false, title: '复杂智能软件', pwa: true, - logo: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg', + logo: '/assets/images/left-top-logo.png', iconfontUrl: '', token: { // 参见ts声明,demo 见文档,通过token 修改样式 diff --git a/react-ui/public/assets/images/compoent-icon-6.png b/react-ui/public/assets/images/compoent-icon-6.png new file mode 100644 index 00000000..47e7fa9b Binary files /dev/null and b/react-ui/public/assets/images/compoent-icon-6.png differ diff --git a/react-ui/public/assets/images/component-icon-1.png b/react-ui/public/assets/images/component-icon-1.png index 5e5d7149..4568e3b2 100644 Binary files a/react-ui/public/assets/images/component-icon-1.png and b/react-ui/public/assets/images/component-icon-1.png differ diff --git a/react-ui/public/assets/images/component-icon-2.png b/react-ui/public/assets/images/component-icon-2.png index 2f4fba75..634a8762 100644 Binary files a/react-ui/public/assets/images/component-icon-2.png and b/react-ui/public/assets/images/component-icon-2.png differ diff --git a/react-ui/public/assets/images/component-icon-3.png b/react-ui/public/assets/images/component-icon-3.png index 941b323a..43d88c00 100644 Binary files a/react-ui/public/assets/images/component-icon-3.png and b/react-ui/public/assets/images/component-icon-3.png differ diff --git a/react-ui/public/assets/images/component-icon-4.png b/react-ui/public/assets/images/component-icon-4.png index fd240707..4c004faf 100644 Binary files a/react-ui/public/assets/images/component-icon-4.png and b/react-ui/public/assets/images/component-icon-4.png differ diff --git a/react-ui/public/assets/images/component-icon-5.png b/react-ui/public/assets/images/component-icon-5.png index b86bd925..e1c284e4 100644 Binary files a/react-ui/public/assets/images/component-icon-5.png and b/react-ui/public/assets/images/component-icon-5.png differ diff --git a/react-ui/public/assets/images/component-icon-7.png b/react-ui/public/assets/images/component-icon-7.png index 28c1803d..c0c1087c 100644 Binary files a/react-ui/public/assets/images/component-icon-7.png and b/react-ui/public/assets/images/component-icon-7.png differ diff --git a/react-ui/public/assets/images/mindspore模型转换.png b/react-ui/public/assets/images/mindspore模型转换.png new file mode 100644 index 00000000..73620da3 Binary files /dev/null and b/react-ui/public/assets/images/mindspore模型转换.png differ diff --git a/react-ui/public/assets/images/pipelieEditIcon.png b/react-ui/public/assets/images/pipelieEditIcon.png new file mode 100644 index 00000000..936bf97c Binary files /dev/null and b/react-ui/public/assets/images/pipelieEditIcon.png differ diff --git a/react-ui/public/assets/images/pipeline-canvas-back.png b/react-ui/public/assets/images/pipeline-canvas-back.png new file mode 100644 index 00000000..c7e9864e Binary files /dev/null and b/react-ui/public/assets/images/pipeline-canvas-back.png differ diff --git a/react-ui/public/assets/images/pipeline-edit-icon.png b/react-ui/public/assets/images/pipeline-edit-icon.png new file mode 100644 index 00000000..936bf97c Binary files /dev/null and b/react-ui/public/assets/images/pipeline-edit-icon.png differ diff --git a/react-ui/public/assets/images/pytorch推理.png b/react-ui/public/assets/images/pytorch推理.png new file mode 100644 index 00000000..11f2d37f Binary files /dev/null and b/react-ui/public/assets/images/pytorch推理.png differ diff --git a/react-ui/public/assets/images/pytorch训练.png b/react-ui/public/assets/images/pytorch训练.png new file mode 100644 index 00000000..81677987 Binary files /dev/null and b/react-ui/public/assets/images/pytorch训练.png differ diff --git a/react-ui/public/assets/images/tensorflow模型转换.png b/react-ui/public/assets/images/tensorflow模型转换.png new file mode 100644 index 00000000..4e58c5d7 Binary files /dev/null and b/react-ui/public/assets/images/tensorflow模型转换.png differ diff --git a/react-ui/public/assets/images/发送通知.png b/react-ui/public/assets/images/发送通知.png new file mode 100644 index 00000000..9e4d563a Binary files /dev/null and b/react-ui/public/assets/images/发送通知.png differ diff --git a/react-ui/src/global.less b/react-ui/src/global.less index 725af969..4e109ebf 100644 --- a/react-ui/src/global.less +++ b/react-ui/src/global.less @@ -31,7 +31,38 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } - +.ant-pro-layout .ant-pro-layout-content{ + padding: 10px; +} +.ant-pro-layout .ant-pro-layout-bg-list{ + background:#f9fafb; +} +.ant-table-wrapper .ant-table-thead >tr>th{ + background-color: #fff; +} +.ant-table-wrapper .ant-table-thead >tr>td{ + background-color: #fff; +} +.ant-menu-light .ant-menu-item-selected{ + background:rgba(197, 232, 255, 0.8)!important; +} +.ant-pro-base-menu-inline{ + // height: 87vh; + background:#f2f5f7; +border-radius:0px 20px 20px 0px; +} +.ant-pro-layout .ant-pro-layout-content{ + background-color: #fff; +} +.ant-pro-global-header-logo img{ + height: 21px; +} +.ant-pro-layout .ant-layout-sider.ant-pro-sider{ + height: 87vh; +} +.ant-pro-layout .ant-pro-layout-container{ + height: 98vh; +} ul, ol { list-style: none; @@ -53,3 +84,5 @@ ol { } } } + + diff --git a/react-ui/src/pages/Experiment/experimentText/index.jsx b/react-ui/src/pages/Experiment/experimentText/index.jsx index 8a961f04..354f349c 100644 --- a/react-ui/src/pages/Experiment/experimentText/index.jsx +++ b/react-ui/src/pages/Experiment/experimentText/index.jsx @@ -51,7 +51,7 @@ const ExperimentText = React.FC = () => { return { display: 'flex', backgroundColor:'#fff', - height:'81vh' + height:'98vh' }; }); const graphStyle = useEmotionCss(() => { @@ -158,98 +158,78 @@ const ExperimentText = React.FC = () => { },[]) 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', { + 'rect-node', + { + // draw anchor-point circles according to the anchorPoints in afterDraw + getAnchorPoints(cfg) { + return ( + cfg.anchorPoints || [ + // 上下各3,左右各1 + [0.5, 0], + [0.5, 1], + + ] + ); + }, + afterDraw(cfg, group) { + // console.log(group, cfg, 12312); + const image = group.addShape('image', { + attrs: { + x: -45, + y: -10, + width: 20, + height: 20, + 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: { - x: -25, - y: -13, - width: 23, - height: 21, - img: cfg.img, - cursor: 'pointer', + r: 3, + x: bbox.x + bbox.width * anchorPos[0], + y: bbox.y + bbox.height * anchorPos[1], + fill: '#fff', + stroke: '#a4a4a5', }, - 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 - }); + 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 }); - return image; - }, - - // response the state changes and show/hide the link-point circles - setState(name, value, item) { - // 默认显示全部锚点,防止过宽导致锚点无法被选中 - // if (name === 'showAnchors') { + }); + return image; + }, + + // response the state changes and show/hide the link-point circles + setState(name, value, item) { const anchorPoints = item.getContainer().findAll(ele => ele.get('name') === 'anchor-point'); anchorPoints.forEach(point => { - // if (value) point.show(); - // else point.hide(); - point.show(); - }); - // } - }, + if (value || point.get('links') > 0) point.show() + else point.hide() + }) + // } }, - 'rect' - ); + }, + 'rect' + ); console.log(graphRef,'graphRef'); graph = new G6.Graph({ container: graphRef.current, @@ -258,13 +238,27 @@ const ExperimentText = React.FC = () => { height: graphRef.current.clientHeight||760, animate: false, groupByTypes: false, - - plugins: [], + fitView:true, 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 'drag-canvas', 'zoom-canvas', + // 'brush-select', + 'drag-combo', ], altSelect: [ { @@ -277,49 +271,53 @@ const ExperimentText = React.FC = () => { defaultNode: { type: 'rect-node', - size: 70, + size: [110,36], labelCfg: { style: { fill: '#000', - fontSize: 12, + fontSize: 10, + cursor: 'pointer', - x: 0, + x: -20, y: 0, textAlign: 'left', textBaseline: 'middle', }, }, style: { - fill: 'transparent', - stroke: 'transparent', + fill: '#fff', + stroke: '#fff', + radius:10, + lineWidth:0.5 + }, + }, + nodeStateStyles: { + nodeSelected: { + fill: 'red', + shadowColor: 'red', + stroke: 'red', + 'text-shape': { + fill: 'red', + stroke: 'red', + }, }, }, - // nodeStateStyles: { - // nodeSelected: { - // fill: 'red', - // shadowColor: 'red', - // stroke: 'red', - // 'text-shape': { - // fill: 'red', - // stroke: 'red', - // }, - // }, - // }, defaultEdge: { // type: 'quadratic', - type: 'polyline', + type: 'cubic-vertical', style: { - endArrow: { - path: G6.Arrow.triangle(), + endArrow: { // 设置终点箭头 + path: G6.Arrow.triangle(3, 3, 3), // 使用内置箭头路径函数,参数为箭头的 宽度、长度、偏移量(默认为 0,与 d 对应) + d: 4.5, + fill:'#a2a6b5' }, cursor: 'pointer', - endArrow: true, lineWidth: 1, opacity: 1, stroke: '#a2a6b5', - radius: 10, + radius: 1, }, nodeStateStyle: { hover: { @@ -346,7 +344,7 @@ const ExperimentText = React.FC = () => { cursor: 'pointer', }, }, - linkCenter: true, + // linkCenter: true, fitView: true, fitViewPadding: [60, 60, 60, 80], }); diff --git a/react-ui/src/pages/Experiment/index.jsx b/react-ui/src/pages/Experiment/index.jsx index 2fd83114..79274b7f 100644 --- a/react-ui/src/pages/Experiment/index.jsx +++ b/react-ui/src/pages/Experiment/index.jsx @@ -1,6 +1,6 @@ import React ,{ useState,useEffect,useRef }from 'react'; import { Space, Table, Tag,Button,Modal, Form, Input ,message, Select,} from 'antd'; -import { PlusOutlined, EditOutlined ,PlayCircleOutlined,DeleteOutlined,FieldTimeOutlined} from '@ant-design/icons'; +import { PlusOutlined,PlusCircleOutlined, 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' @@ -49,6 +49,7 @@ const Experiment = React.FC = () => { size:10000, name:null }); + const [disableFlag,setDisableFlag]=useState(false) const timers=(time)=>{ let timer=new Date(time) let hours = timer.getHours(); //转换成时 @@ -96,6 +97,7 @@ const Experiment = React.FC = () => { } const showModal = () => { setDialogTitle('新建实验') + setDisableFlag(false) console.log(workflowList); setIsModalOpen(true); }; @@ -103,6 +105,7 @@ const Experiment = React.FC = () => { getExperimentById(id).then(ret=>{ if(ret.code==200){ form.setFieldsValue({...ret.data}) + setDisableFlag(true) setFormId(ret.data.id) setDialogTitle('编辑实验') getWorkflowList() @@ -262,6 +265,7 @@ const Experiment = React.FC = () => { { title: '操作', key: 'action', + width:300, render: (_, record) => ( + */} +
+
( @@ -340,7 +351,7 @@ const Experiment = React.FC = () => {
状态
运行时长
开始时间
-
操作
+
操作
:''} {experimentInList&&experimentInList.length>0?experimentInList.map((item,index)=>( @@ -349,7 +360,7 @@ const Experiment = React.FC = () => {
{statusObj[item.status]}
{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())}
{momnet(item.create_time).format('YYYY-MM-DD HH:mm:ss')}
-
+
} open={isModalOpen} okButtonProps={{ htmlType: 'submit', form: 'form', }} onCancel={handleCancel}> @@ -417,15 +431,6 @@ const Experiment = React.FC = () => { name="form" form={form} layout="vertical" - labelCol={{ - span: 8, - }} - wrapperCol={{ - span: 16, - }} - style={{ - maxWidth: 600, - }} initialValues={{ remember: true, }} @@ -460,6 +465,7 @@ const Experiment = React.FC = () => { { // }, ]} > - + {workflowList&&workflowList.length>0?workflowList.map(item=>{return {item.name}}):''} {/* Demo */} diff --git a/react-ui/src/pages/Experiment/index.less b/react-ui/src/pages/Experiment/index.less index b015521e..f1fc5a1f 100644 --- a/react-ui/src/pages/Experiment/index.less +++ b/react-ui/src/pages/Experiment/index.less @@ -7,22 +7,34 @@ height: 49px; background-size: 100% 100%; background-image: url(/assets/images/pipeline-back.png); + +} +.pipelineTopBox{ + 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); + margin-bottom: 10px; +} +.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:14px; + font-family: 'Alibaba'; +} +.plusButton:hover{ + background:rgba(22, 100, 255, 0.06)!important; + border:1px solid!important; + border-color:rgba(22, 100, 255, 0.11)!important; + color:#1d1d20!important; } -// .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; @@ -44,4 +56,45 @@ } .statusBox:hover .statusIcon{ visibility: visible; +} +.modal { + :global { + .ant-modal-content { + background:linear-gradient(180deg,#cfdfff 0%,#d4e2ff 9.77%,#ffffff 40%,#ffffff 100%); + border-radius:21px; + padding: 20px 67px; + width: 825px; + + } + .ant-modal-header{ + background-color: transparent; + margin: 20px 0; + } + .ant-input{ + border-color:#e6e6e6; + height: 40px; + + } + .ant-select-single{ + height: 40px; + } + .ant-form-item .ant-form-item-label >label{ + color:rgba(29, 29, 32, 0.8); + } + .ant-modal-footer{ + margin: 40px 0 30px 0; + display: flex; + justify-content: center; + } + .ant-btn{ + width:123px; + height:40px; + font-size:18px; + background:rgba(22, 100, 255, 0.06); + border-radius:10px; + } + .ant-btn-primary{ + background:#1664ff; + } + } } \ No newline at end of file diff --git a/react-ui/src/pages/Pipeline/editPipeline/editPipeline.less b/react-ui/src/pages/Pipeline/editPipeline/editPipeline.less index a809a14d..1fd2df8b 100644 --- a/react-ui/src/pages/Pipeline/editPipeline/editPipeline.less +++ b/react-ui/src/pages/Pipeline/editPipeline/editPipeline.less @@ -9,6 +9,7 @@ width: 100%; height:43px; background:#f8fbff; + color:#1d1d20; font-size:15px; font-family: 'Alibaba'; @@ -22,6 +23,7 @@ .buttonList{ display: flex; align-items: center; + justify-content: end; padding: 0 30px; width: 100%; height:45px; diff --git a/react-ui/src/pages/Pipeline/editPipeline/index.jsx b/react-ui/src/pages/Pipeline/editPipeline/index.jsx index a68f219a..a34d7bdf 100644 --- a/react-ui/src/pages/Pipeline/editPipeline/index.jsx +++ b/react-ui/src/pages/Pipeline/editPipeline/index.jsx @@ -17,11 +17,12 @@ const editPipeline = React.FC = () => { let contextMenu={} const locationParams =useParams () //新版本获取路由参数接口 let graph=null + let sourceAnchorIdx, targetAnchorIdx; const pipelineContainer = useEmotionCss(() => { return { display: 'flex', backgroundColor:'#fff', - height:'81vh' + height:'98vh' }; }); const rightmenu= useEmotionCss(() => { @@ -46,7 +47,8 @@ const editPipeline = React.FC = () => { const graphStyle = useEmotionCss(() => { return { width:'100%', - backgroundColor:'#f9fafb', + backgroundSize: '100% 100%', + backgroundImage: 'url(/assets/images/pipeline-canvas-back.png)', flex:1 }; }); @@ -81,7 +83,7 @@ const editPipeline = React.FC = () => { data.nodes[index] = val; graph.changeData(data) } - const savePipeline=()=>{ + const savePipeline=(val)=>{ const data = graph.save(); console.log(data); let params={ @@ -90,14 +92,22 @@ const editPipeline = React.FC = () => { } saveWorkflow(params).then(ret=>{ console.log(ret); - message.success('保存成功') + if(ret.code==200){ + message.success('保存成功') setTimeout(()=>{ - navgite({pathname:`/pipeline`,}); + if(val){ + navgite({pathname:`/pipeline`,}); + } },500) + } + else{ + message.error('保存失败') + } }) console.log(params); } const handlerClick=(e)=>{ + e.stopPropagation() console.log(propsRef,graph); // let cache = []; // let json_str = JSON.stringify(graph, function(key, value) { @@ -124,6 +134,106 @@ const editPipeline = React.FC = () => { },500) } } + const processParallelEdgesOnAnchorPoint = ( + edges, + offsetDiff = 15, + multiEdgeType = 'cubic-vertical', + singleEdgeType = undefined, + loopEdgeType = undefined + ) => { + const len = edges.length; + const cod = offsetDiff * 2; + const loopPosition = [ + 'top', + 'top-right', + 'right', + 'bottom-right', + 'bottom', + 'bottom-left', + 'left', + 'top-left', + ]; + const edgeMap = {}; + const tags = []; + const reverses = {}; + for (let i = 0; i < len; i++) { + const edge = edges[i]; + const { source, target, sourceAnchor, targetAnchor } = edge; + const sourceTarget = `${source}|${sourceAnchor}-${target}|${targetAnchor}`; + + if (tags[i]) continue; + if (!edgeMap[sourceTarget]) { + edgeMap[sourceTarget] = []; + } + tags[i] = true; + edgeMap[sourceTarget].push(edge); + for (let j = 0; j < len; j++) { + if (i === j) continue; + const sedge = edges[j]; + const { source: src, target: dst, sourceAnchor: srcAnchor, targetAnchor: dstAnchor } = sedge; + + // 两个节点之间共同的边 + // 第一条的source = 第二条的target + // 第一条的target = 第二条的source + if (!tags[j]) { + if (source === dst && sourceAnchor === dstAnchor + && target === src && targetAnchor === srcAnchor) { + edgeMap[sourceTarget].push(sedge); + tags[j] = true; + reverses[`${src}|${srcAnchor}|${dst}|${dstAnchor}|${edgeMap[sourceTarget].length - 1}`] = true; + } else if (source === src && sourceAnchor === srcAnchor + && target === dst && targetAnchor === dstAnchor) { + edgeMap[sourceTarget].push(sedge); + tags[j] = true; + } + } + } + } + + for (const key in edgeMap) { + const arcEdges = edgeMap[key]; + const { length } = arcEdges; + for (let k = 0; k < length; k++) { + const current = arcEdges[k]; + if (current.source === current.target) { + if (loopEdgeType) current.type = loopEdgeType; + // 超过8条自环边,则需要重新处理 + current.loopCfg = { + position: loopPosition[k % 8], + dist: Math.floor(k / 8) * 20 + 50, + }; + continue; + } + if (length === 1 && singleEdgeType && (current.source !== current.target || current.sourceAnchor !== current.targetAnchor)) { + current.type = singleEdgeType; + continue; + } + current.type = multiEdgeType; + const sign = + (k % 2 === 0 ? 1 : -1) * (reverses[`${current.source}|${current.sourceAnchor}|${current.target}|${current.targetAnchor}|${k}`] ? -1 : 1); + if (length % 2 === 1) { + current.curveOffset = sign * Math.ceil(k / 2) * cod; + } else { + current.curveOffset = sign * (Math.floor(k / 2) * cod + offsetDiff); + } + } + } + return edges; + }; + const cloneElement=(item)=>{ + console.log(item); + let data=graph.save() + const nodeId = s8(); + console.log(item.getModel()); + data.nodes.push({ + ...item.getModel(), + label: item.getModel().label+'-copy', + x: item.getModel().x + 150, + y: item.getModel().y, + id: item.getModel().component_name+'-'+nodeId, + }); + graph.changeData(data) + } const getFirstWorkflow=(val)=>{ getWorkflowById(val).then(ret=>{ console.log(ret); @@ -161,22 +271,17 @@ const editPipeline = React.FC = () => { color: #333333; overflow-y: auto;"> +
  • 复制
  • 删除
  • `; }, handleMenuClick: (target, item) => { switch (target.getAttribute('code')) { - //
  • 撤回
  • - //
  • 恢复
  • - // case 'undo': - // this.$emit('handleMenuCall', { code: 'undo' }); - // break; - // case 'redo': - // this.$emit('handleMenuCall', { code: 'redo' }); - // break; case 'delete': graph.removeItem(item); - + break; + case 'clone': + cloneElement(item) break; default: break; @@ -189,7 +294,7 @@ const editPipeline = React.FC = () => { offsetY: 0, // the types of items that allow the menu show up // 在哪些类型的元素上响应 - itemTypes: ['node', 'edge', 'canvas']}) + itemTypes: ['node', 'edge']}) initGraph() }; @@ -197,7 +302,17 @@ const editPipeline = React.FC = () => { getFirstWorkflow(locationParams.id) initMenu() - + return ()=>{ + graph.off('node:mouseenter', e => { + graph.setItemState(e.item, 'showAnchors', true); + graph.setItemState(e.item, 'nodeSelected', true); + }); + graph.off('node:mouseleave', e => { + // this.graph.setItemState(e.item, 'showAnchors', false); + graph.setItemState(e.item, 'nodeSelected', false); + }); + graph.off('dblclick', handlerClick); + } console.log(contextMenu); },[]) const initGraph=()=>{ @@ -209,25 +324,8 @@ const editPipeline = React.FC = () => { 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, 0], [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], ] ); }, @@ -235,10 +333,10 @@ const editPipeline = React.FC = () => { // console.log(group, cfg, 12312); const image = group.addShape('image', { attrs: { - x: -25, - y: -13, - width: 21, - height: 21, + x: -45, + y: -10, + width: 20, + height: 20, img: cfg.img, cursor: 'pointer', }, @@ -263,17 +361,16 @@ const editPipeline = React.FC = () => { anchorPoints.forEach((anchorPos, i) => { group.addShape('circle', { attrs: { - r: 5, + r: 3, x: bbox.x + bbox.width * anchorPos[0], y: bbox.y + bbox.height * anchorPos[1], - fill: '#000', - stroke: '#000', + fill: '#fff', + stroke: '#a4a4a5', }, 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; @@ -281,14 +378,11 @@ const editPipeline = React.FC = () => { // 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(); - }); + const anchorPoints = item.getContainer().findAll(ele => ele.get('name') === 'anchor-point'); + anchorPoints.forEach(point => { + if (value || point.get('links') > 0) point.show() + else point.hide() + }) // } }, }, @@ -299,7 +393,7 @@ const editPipeline = React.FC = () => { container: graphRef.current, grid: true, width: graphRef.current.clientWidth ||500, - height: graphRef.current.clientHeight||760, + height: graphRef.current.clientHeight||'100%', animate: false, groupByTypes: false, fitView:true, @@ -321,9 +415,28 @@ const editPipeline = React.FC = () => { }, // 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' + type: 'create-edge', + // trigger: 'drag', + shouldBegin: e => { + // avoid beginning at other shapes on the node + if (e.target && e.target.get('name') !== 'anchor-point') return false; + sourceAnchorIdx = e.target.get('anchorPointIdx'); + e.target.set('links', e.target.get('links') + 1); // cache the number of edge connected to this anchor-point circle + return true; }, + shouldEnd: e => { + // avoid ending at other shapes on the node + if (e.target && e.target.get('name') !== 'anchor-point') return false; + if (e.target) { + targetAnchorIdx = e.target.get('anchorPointIdx'); + e.target.set('links', e.target.get('links') + 1); // cache the number of edge connected to this anchor-point circle + return true; + } + targetAnchorIdx = undefined; + return true; + }, + + }, 'drag-canvas', 'zoom-canvas', // 'brush-select', @@ -340,49 +453,54 @@ const editPipeline = React.FC = () => { defaultNode: { type: 'rect-node', - size: 70, + size: [110,36], labelCfg: { style: { fill: '#000', - fontSize: 12, - cursor: 'pointer', - x: 0, + fontSize: 10, + boxShadow:'0px 0px 12px rgba(75, 84, 137, 0.05)', + + x: -20, y: 0, textAlign: 'left', textBaseline: 'middle', }, }, style: { - fill: 'transparent', - stroke: 'transparent', + fill: '#fff', + stroke: '#fff', + cursor: 'pointer', + radius:10, + lineWidth:0.5 + }, + }, + nodeStateStyles: { + nodeSelected: { + fill: 'red', + shadowColor: 'red', + stroke: 'red', + 'text-shape': { + fill: 'red', + stroke: 'red', + }, }, }, - // nodeStateStyles: { - // nodeSelected: { - // fill: 'red', - // shadowColor: 'red', - // stroke: 'red', - // 'text-shape': { - // fill: 'red', - // stroke: 'red', - // }, - // }, - // }, defaultEdge: { // type: 'quadratic', - type: 'polyline', + type: 'cubic-vertical', style: { - endArrow: { - path: G6.Arrow.triangle(), + endArrow: { // 设置终点箭头 + path: G6.Arrow.triangle(3, 3, 3), // 使用内置箭头路径函数,参数为箭头的 宽度、长度、偏移量(默认为 0,与 d 对应) + d: 4.5, + fill:'#CDD0DC' }, cursor: 'pointer', - endArrow: true, lineWidth: 1, opacity: 1, - stroke: '#a2a6b5', - radius: 10, + stroke: '#CDD0DC', + radius: 1, }, nodeStateStyle: { hover: { @@ -409,14 +527,29 @@ const editPipeline = React.FC = () => { cursor: 'pointer', }, }, - linkCenter: true, + // linkCenter: true, fitView: true, fitViewPadding: [60, 60, 60, 80], }); - graph.on('dblclick', handlerClick); + graph.on('dblclick', (e)=>{ + console.log(e.item); + graph.setItemState(e.item, 'nodeClicked', true); + handlerClick(e) + }); + graph.on('click', (e)=>{ + console.log(e.item); + + }); graph.on('aftercreateedge', (e) => { + // update the sourceAnchor and targetAnchor for the newly added edge + graph.updateItem(e.edge, { + sourceAnchor: sourceAnchorIdx, + targetAnchor: targetAnchorIdx + }) + + // update the curveOffset for parallel edges const edges = graph.save().edges; - G6.Util.processParallelEdges(edges); + processParallelEdgesOnAnchorPoint(edges); graph.getEdges().forEach((edge, i) => { graph.updateItem(edge, { curveOffset: edges[i].curveOffset, @@ -424,6 +557,50 @@ const editPipeline = React.FC = () => { }); }); }); + graph.on('node:mouseenter', e => { + // this.graph.setItemState(e.item, 'showAnchors', true); + graph.setItemState(e.item, 'nodeSelected', true); + graph.updateItem(e.item, { + // 节点的样式 + style: { + stroke: '#1664ff', + }, + }); + }); + graph.on('node:mouseleave', e => { + // this.graph.setItemState(e.item, 'showAnchors', false); + graph.setItemState(e.item, 'nodeSelected', false); + graph.updateItem(e.item, { + // 节点的样式 + style: { + stroke: 'transparent', + }, + }); + }); + graph.on('afterremoveitem', e => { + if (e.item && e.item.source && e.item.target) { + const sourceNode = graph.findById(e.item.source); + const targetNode = graph.findById(e.item.target); + const { sourceAnchor, targetAnchor } = e.item; + if (sourceNode && !isNaN(sourceAnchor)) { + const sourceAnchorShape = sourceNode.getContainer().find(ele => (ele.get('name') === 'anchor-point' && ele.get('anchorPointIdx') === sourceAnchor)); + sourceAnchorShape.set('links', sourceAnchorShape.get('links') - 1); + } + if (targetNode && !isNaN(targetAnchor)) { + const targetAnchorShape = targetNode.getContainer().find(ele => (ele.get('name') === 'anchor-point' && ele.get('anchorPointIdx') === targetAnchor)); + targetAnchorShape.set('links', targetAnchorShape.get('links') - 1); + } + } + }) + + // after clicking on the first node, the edge is created, update the sourceAnchor + graph.on('afteradditem', e => { + if (e.item && e.item.getType() === 'edge') { + graph.updateItem(e.item, { + sourceAnchor: sourceAnchorIdx + }); + } + }) window.onresize = () => { if (!graph || graph.get('destroyed')) return; if (!graphRef.current || !graphRef.current.scrollWidth || !graphRef.current.scrollHeight) return; @@ -434,9 +611,8 @@ const editPipeline = React.FC = () => {
    - {/* - */} - + +
    diff --git a/react-ui/src/pages/Pipeline/editPipeline/modelMenus.jsx b/react-ui/src/pages/Pipeline/editPipeline/modelMenus.jsx index 38cba27a..a72b7857 100644 --- a/react-ui/src/pages/Pipeline/editPipeline/modelMenus.jsx +++ b/react-ui/src/pages/Pipeline/editPipeline/modelMenus.jsx @@ -34,15 +34,17 @@ const modelMenus = ({onParDragEnd}) => { onParDragEnd({...data,x:e.clientX,y:e.clientY,label:data.component_label,img:`/assets/images/${data.icon_path}.png`}) } const { Panel } = Collapse; - return (
    + return (
    {modelMenusList && modelMenusList.length > 0 ? modelMenusList.map(item => ( {item.name}
    } key={item.key} > diff --git a/react-ui/src/pages/Pipeline/editPipeline/modelMenus.less b/react-ui/src/pages/Pipeline/editPipeline/modelMenus.less index 96cad5dc..3c730fd9 100644 --- a/react-ui/src/pages/Pipeline/editPipeline/modelMenus.less +++ b/react-ui/src/pages/Pipeline/editPipeline/modelMenus.less @@ -9,4 +9,35 @@ font-size:14px; height:40px; cursor: pointer; + border-radius:4px; + padding: 0 16px; +} +.collapseItem:hover{ + background:rgba(22, 100, 255, 0.08); +} +.collapse{ + :global { + .ant-collapse{ + border-color: transparent!important; + background-color: #fff; + } + .ant-collapse>.ant-collapse-item >.ant-collapse-header{ + background-color: #fff; + border-color: transparent; + margin-bottom: 5px; + } + .ant-collapse>.ant-collapse-item{ + border-radius: 0px; + border-color:rgba(20, 49, 179, 0.12); + margin: 0 10px; + } + .ant-collapse .ant-collapse-content{ + padding-bottom: 15px; + border-top: 1px solid transparent; + + } + .ant-collapse .ant-collapse-content>.ant-collapse-content-box{ + padding: 0; + } + } } \ No newline at end of file diff --git a/react-ui/src/pages/Pipeline/index.jsx b/react-ui/src/pages/Pipeline/index.jsx index 5c18fc2f..e11380de 100644 --- a/react-ui/src/pages/Pipeline/index.jsx +++ b/react-ui/src/pages/Pipeline/index.jsx @@ -1,6 +1,6 @@ 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 { PlusOutlined,PlusCircleOutlined, 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 momnet from 'moment' @@ -36,7 +36,7 @@ const Pipeline = React.FC = () => { } const showModal = () => { form.resetFields() - setDialogTitle('编辑流水线') + setDialogTitle('新建流水线') setIsModalOpen(true); }; const handleOk = () => { @@ -56,7 +56,10 @@ const Pipeline = React.FC = () => { } else{ addWorkflow(values).then(ret=>{ - navgite({pathname:`/pipeline/pytorchtext/${ret.id}/${ret.name}`,}); + console.log(ret); + if(ret.code==200){ + navgite({pathname:`/pipeline/pytorchtext/${ret.data.id}/${ret.data.name}`,}); + } } ) } @@ -107,7 +110,7 @@ const Pipeline = React.FC = () => { title: '序号', dataIndex: 'index', key: 'index', - width: 60, + width: 80, render(text, record, index) { return ( {(pageOption.current.page - 1) * 10 + index + 1} @@ -141,6 +144,7 @@ const Pipeline = React.FC = () => { { title: '操作', key: 'action', + render: (_, record) => (
    - + {dialogTitle} + } open={isModalOpen} className={Styles.modal} okButtonProps={{ htmlType: 'submit', form: 'form', }} onCancel={handleCancel}> @@ -242,15 +249,6 @@ const Pipeline = React.FC = () => { name="form" form={form} layout="vertical" - labelCol={{ - span: 8, - }} - wrapperCol={{ - span: 16, - }} - style={{ - maxWidth: 600, - }} initialValues={{ remember: true, }} @@ -282,18 +280,6 @@ const Pipeline = React.FC = () => { > - -