| @@ -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 修改样式 | |||
| @@ -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 { | |||
| } | |||
| } | |||
| } | |||
| @@ -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], | |||
| }); | |||
| @@ -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) => ( | |||
| <Space size="small"> | |||
| <Button | |||
| @@ -291,6 +295,7 @@ const Experiment = React.FC = () => { | |||
| size="small" | |||
| danger | |||
| key="batchRemove" | |||
| style={{color:'#f98e1b'}} | |||
| icon = {< DeleteOutlined />} | |||
| onClick={async () => { | |||
| Modal.confirm({ | |||
| @@ -298,6 +303,7 @@ const Experiment = React.FC = () => { | |||
| content: '确定删除该条实验吗?', | |||
| okText: '确认', | |||
| cancelText: '取消', | |||
| onOk: () => { | |||
| console.log(record); | |||
| deleteExperimentById(record.id).then(ret=>{ | |||
| @@ -326,10 +332,15 @@ const Experiment = React.FC = () => { | |||
| }, | |||
| ]; | |||
| return (<div> | |||
| <div > | |||
| {/* <div > | |||
| <Button type="primary" onClick={showModal} icon = {< PlusOutlined />}> | |||
| 新建实验 | |||
| </Button> | |||
| </div> */} | |||
| <div className={Styles.pipelineTopBox}> | |||
| <Button type="primary" className={Styles.plusButton} onClick={showModal} icon = {<PlusCircleOutlined style={{color:'#1664ff'}} />}> | |||
| 新建实验 | |||
| </Button> | |||
| </div> | |||
| <Table columns={columns} dataSource={experimentList} pagination={paginationProps} expandable={{ | |||
| expandedRowRender: (record) => ( | |||
| @@ -340,7 +351,7 @@ const Experiment = React.FC = () => { | |||
| <div style={{width:'200px'}}>状态</div> | |||
| <div style={{width:'300px'}}>运行时长</div> | |||
| <div style={{width:'300px'}}>开始时间</div> | |||
| <div style={{width:'300px'}}>操作</div> | |||
| <div style={{width:'200px'}}>操作</div> | |||
| </div>:''} | |||
| {experimentInList&&experimentInList.length>0?experimentInList.map((item,index)=>( | |||
| @@ -349,7 +360,7 @@ const Experiment = React.FC = () => { | |||
| <div className={Styles.statusBox} style={{width:'200px'}}><img style={{width:'17px',marginRight:'7px'}} src={statusImgObj[item.status]}/> <span style={{color:statusColorObj[item.status]}} className={Styles.statusIcon}>{statusObj[item.status]}</span></div> | |||
| <div style={{width:'300px'}}>{item.finish_time?timers(new Date(item.finish_time).getTime()-new Date(item.create_time).getTime()):timers(new Date().getTime()-new Date(item.create_time).getTime())}</div> | |||
| <div style={{width:'300px'}}>{momnet(item.create_time).format('YYYY-MM-DD HH:mm:ss')}</div> | |||
| <div style={{width:'300px'}}> | |||
| <div style={{width:'200px'}}> | |||
| <Button | |||
| type="link" | |||
| size="small" | |||
| @@ -375,6 +386,7 @@ const Experiment = React.FC = () => { | |||
| size="small" | |||
| danger | |||
| key="batchRemove" | |||
| style={{color:'#f98e1b'}} | |||
| disabled={item.status=='Running'||item.status=='Pending'} | |||
| icon = {< DeleteOutlined />} | |||
| onClick={async () => { | |||
| @@ -409,7 +421,9 @@ const Experiment = React.FC = () => { | |||
| expandedRowKeys:[expandedRowKeys], | |||
| rowExpandable: (record) =>true, | |||
| }}/> | |||
| <Modal title={dialogTitle} open={isModalOpen} okButtonProps={{ | |||
| <Modal className={Styles.modal} title={<div style={{display:'flex',alignItems:'center',fontWeight:500}}> | |||
| <img style={{width:'20px',marginRight:'10px'}} src={`/assets/images/pipeline-edit-icon.png`} alt="" />{dialogTitle} | |||
| </div>} 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 = () => { | |||
| <Form.Item | |||
| label="选择流水线" | |||
| name="workflow_id" | |||
| rules={[ | |||
| // { | |||
| // required: true, | |||
| @@ -467,8 +473,8 @@ const Experiment = React.FC = () => { | |||
| // }, | |||
| ]} | |||
| > | |||
| <Select> | |||
| {workflowList&&workflowList.length>0?workflowList.map(item=>{return <Select.Option value={item.id}>{item.name}</Select.Option>}):''} | |||
| <Select disabled={disableFlag}> | |||
| {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> | |||
| @@ -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; | |||
| } | |||
| } | |||
| } | |||
| @@ -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; | |||
| @@ -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;"> | |||
| <li style="padding: 10px 20px;cursor: pointer;" code="clone">复制</li> | |||
| <li style="padding: 10px 20px;cursor: pointer;" code="delete">删除</li> | |||
| </ul>`; | |||
| }, | |||
| handleMenuClick: (target, item) => { | |||
| switch (target.getAttribute('code')) { | |||
| // <li style="padding: 10px 20px;cursor: pointer;" code="undo">撤回</li> | |||
| // <li style="padding: 10px 20px;cursor: pointer;" code="redo">恢复</li> | |||
| // 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 = () => { | |||
| <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> | |||
| <Button type="primary" shape="round" icon={<SaveOutlined />} style={{marginRight:'20px'}} onClick={()=>{savePipeline(false)}}>保存</Button> | |||
| <Button type="primary" shape="round" icon={<SaveOutlined />} onClick={()=>{savePipeline(true)}}>保存并返回</Button> | |||
| </div> | |||
| <div className={graphStyle} ref={graphRef} id={Styles.graphStyle}></div> | |||
| </div> | |||
| @@ -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 (<div style={{width:'300px',height:'100%'}}> | |||
| return (<div style={{width:'250px',height:'100%'}} className={Styles.collapse}> | |||
| <Collapse | |||
| collapsible="header" | |||
| defaultActiveKey={['1']} | |||
| expandIconPosition="end" | |||
| > | |||
| {modelMenusList && modelMenusList.length > 0 | |||
| ? modelMenusList.map(item => ( | |||
| <Panel | |||
| header={<div>{item.name}</div>} | |||
| key={item.key} | |||
| > | |||
| @@ -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; | |||
| } | |||
| } | |||
| } | |||
| @@ -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 ( | |||
| <span>{(pageOption.current.page - 1) * 10 + index + 1}</span> | |||
| @@ -141,6 +144,7 @@ const Pipeline = React.FC = () => { | |||
| { | |||
| title: '操作', | |||
| key: 'action', | |||
| render: (_, record) => ( | |||
| <Space size="small"> | |||
| <Button | |||
| @@ -192,6 +196,7 @@ const Pipeline = React.FC = () => { | |||
| type="link" | |||
| size="small" | |||
| danger | |||
| style={{color:'#f98e1b'}} | |||
| key="batchRemove" | |||
| icon = {< DeleteOutlined />} | |||
| onClick={async () => { | |||
| @@ -229,12 +234,14 @@ const Pipeline = React.FC = () => { | |||
| ]; | |||
| return (<div> | |||
| <div className={Styles.pipelineTopBox}> | |||
| <Button type="primary" className={Styles.plusButton} onClick={showModal} icon = {< PlusOutlined />}> | |||
| <Button type="primary" className={Styles.plusButton} onClick={showModal} icon = {<PlusCircleOutlined style={{color:'#1664ff'}} />}> | |||
| 新建流水线 | |||
| </Button> | |||
| </div> | |||
| <Table columns={columns} dataSource={pipeList} pagination={paginationProps}/> | |||
| <Modal title={dialogTitle} open={isModalOpen} okButtonProps={{ | |||
| <Modal title={<div style={{display:'flex',alignItems:'center',fontWeight:500}}> | |||
| <img style={{width:'20px',marginRight:'10px'}} src={`/assets/images/pipeline-edit-icon.png`} alt="" />{dialogTitle} | |||
| </div>} 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 = () => { | |||
| > | |||
| <Input /> | |||
| </Form.Item> | |||
| <Form.Item | |||
| label="备注" | |||
| name="remake" | |||
| rules={[ | |||
| // { | |||
| // required: true, | |||
| // message: 'Please input your username!', | |||
| // }, | |||
| ]} | |||
| > | |||
| <TextArea /> | |||
| </Form.Item> | |||
| </Form> | |||
| </Modal> | |||
| </div>)}; | |||
| @@ -7,19 +7,58 @@ | |||
| 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; | |||
| } | |||
| .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-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; | |||
| } | |||
| } | |||
| } | |||
| // .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; | |||
| // } | |||