From 5181d636d9dd38e9b8034a57fab7f2882dac25f3 Mon Sep 17 00:00:00 2001 From: caishi <1149225589@qq.com> Date: Fri, 29 Jan 2021 14:17:49 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E6=B5=81=E9=85=8D=E7=BD=AE-?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/AppConfig.js | 2 +- src/forge/DevOps/Dispose.jsx | 49 +++++-- src/forge/DevOps/Dispose/Editors.jsx | 5 +- src/forge/DevOps/Dispose/List.jsx | 37 +++++- src/forge/DevOps/Dispose/PipelineName.jsx | 39 +++++- src/forge/DevOps/Dispose/head.jsx | 11 +- src/forge/DevOps/Index.jsx | 24 ++-- src/forge/DevOps/Mould.jsx | 155 ++++++++++++++++++++++ src/forge/DevOps/MouldNew.jsx | 139 +++++++++++++++++++ src/forge/DevOps/Structure.jsx | 3 +- src/forge/DevOps/disposePipeline.jsx | 40 ++---- src/forge/DevOps/ops.scss | 13 ++ 12 files changed, 447 insertions(+), 70 deletions(-) create mode 100644 src/forge/DevOps/Mould.jsx create mode 100644 src/forge/DevOps/MouldNew.jsx diff --git a/src/AppConfig.js b/src/AppConfig.js index a5a063b0..a36c05b5 100644 --- a/src/AppConfig.js +++ b/src/AppConfig.js @@ -27,7 +27,7 @@ if (isDev) { } debugType = window.location.search.indexOf('debug=t') !== -1 ? 'teacher' : window.location.search.indexOf('debug=s') !== -1 ? 'student' : - window.location.search.indexOf('debug=a') !== -1 ? 'admin' : parsed.debug || 'admin' + window.location.search.indexOf('debug=a') !== -1 ? 'admin' : parsed.debug || 'student' } function clearAllCookie() { cookie.remove('_educoder_session', { path: '/' }); diff --git a/src/forge/DevOps/Dispose.jsx b/src/forge/DevOps/Dispose.jsx index 8290190a..28a714f9 100644 --- a/src/forge/DevOps/Dispose.jsx +++ b/src/forge/DevOps/Dispose.jsx @@ -1,5 +1,5 @@ import React , { useState , useEffect } from 'react'; -import { Spin } from 'antd'; +import { Spin , Pagination } from 'antd'; import { Blueback } from '../Component/layout'; import List from './Dispose/List'; import Head from './Dispose/head'; @@ -11,13 +11,16 @@ import styled from 'styled-components'; const Div = styled.div`{ padding:24px 30px; }`; - +const limit = 15; function Dispose(props){ const [ spining , setSpining ] = useState(true); const [ updateInfo , setUpdateInfo ] = useState(undefined); const [ list , setList ] = useState(undefined); const [ permission , setPermission ] = useState(undefined); const [ visible , setVisible ] = useState(false); + const [ page , setPage ] = useState(1); + const [ totalCount , setTotalCount ] = useState(0); + const [ branchList , setBranchList ] =useState(undefined); const projectDetail = props.projectDetail; @@ -36,7 +39,8 @@ function Dispose(props){ const url = `/ci/pipelines/list.json`; axios.get(url,{ params:{ - identifier:projectsId + identifier:projectsId, + page,limit } }).then(result=>{ if(result && result.data){ @@ -48,20 +52,30 @@ function Dispose(props){ useEffect(()=>{ Init(); - },[]) + },[page]) + useEffect(()=>{ + if(owner && projectsId){ + const url = `/${owner}/${projectsId}/branches.json`; + axios.get(url).then(result=>{ + if(result && result.data){ + setBranchList(result.data); + } + }).catch(error=>{}) + } + },[owner,projectsId]) // 新增流水线 - function addNew(pipeline_name,id){ + function addNew(pipeline_name,id,branch,event){ setVisible(true); setUpdateInfo(undefined); if(pipeline_name){ - let l = {pipeline_name,id} + let l = {pipeline_name,id,branch,event} setUpdateInfo(l); } } - function onOk(pipeline_name,updateId){ + function onOk(pipeline_name,updateId,branch,event){ if(pipeline_name){ if(!updateId){ // 新增 @@ -69,7 +83,7 @@ function Dispose(props){ axios.post(url,{ pipeline_name, file_name:".trustie-pipeline.yml", - identifier:projectsId + repo:projectsId,branch,event,owner }).then(result=>{ setVisible(false); if(result && result.data){ @@ -83,7 +97,7 @@ function Dispose(props){ // 修改 const url = `/ci/pipelines/${updateId}.json`; axios.put(url,{ - pipeline_name + pipeline_name,repo:projectsId,branch,event,owner }).then(result=>{ if(result && result.data){ setVisible(false); @@ -110,17 +124,28 @@ function Dispose(props){ }).catch(error=>{}) } + // 模板管理 + function toModalManage(){ + props.history.push(`/projects/cxt/DevOps-mo/devops/mould`); + } + const operate = current_user && (permission && permission !== "Reporter"); return( - {setVisible(false);}} onOk={onOk}/> + setVisible(false)} onOk={onOk}/>
- +
- { operate && (!list ||(list && list.length === 0)) && addNew(undefined,undefined)}>新增流水线 } + { operate && addNew(undefined,undefined)}>新增流水线 }
+ { + totalCount > limit && +
+ setPage(page)}/> +
+ }
diff --git a/src/forge/DevOps/Dispose/Editors.jsx b/src/forge/DevOps/Dispose/Editors.jsx index 60c7b1c8..49c17446 100644 --- a/src/forge/DevOps/Dispose/Editors.jsx +++ b/src/forge/DevOps/Dispose/Editors.jsx @@ -1,9 +1,9 @@ import React from 'react'; import Editor from 'react-monaco-editor'; -function Editors({value,onChange,theme,height,visible}){ +function Editors({value,onChange,theme,height,visible,width="100%",Numbers="on"}){ const editor_options = { - lineNumbers: "on", + lineNumbers: Numbers, wordWrap: true, //强制换行 selectOnLineNumbers: true, lineHeight: 24, @@ -23,6 +23,7 @@ function Editors({value,onChange,theme,height,visible}){ return( { return( - showModal(txt,item.id)} style={{display:"block",cursor:"pointer"}}>{txt} + showModal(txt,item.id,item.branch,item.event)} style={{display:"block",cursor:"pointer"}}>{txt} ) } }, @@ -21,7 +29,7 @@ function List({ list, operate , projectsId , owner , showModal , deleteFunc }){ title:"文件名称", dataIndex:"file_name", key:1, - width:"17%", + width:"15%", className:"color-blue", ellipsis:true }, @@ -29,14 +37,31 @@ function List({ list, operate , projectsId , owner , showModal , deleteFunc }){ title:"创建时间", dataIndex:"created_at", key:1, - width:"18%", + width:"15%", ellipsis:true }, + { + title:"最近构建时间", + dataIndex:"last_build_time", + key:1, + width:"15%", + ellipsis:true + }, + { + title:"最近构建状态", + dataIndex:"pipeline_status", + key:1, + width:"15%", + ellipsis:true, + render:(txt)=>{ + return(STATUS[txt]) + } + }, { title:"操作", dataIndex:"operation", key:1, - width:"30%", + width:"21%", render:(txt,item)=>{ return( @@ -49,7 +74,7 @@ function List({ list, operate , projectsId , owner , showModal , deleteFunc }){ 删除 :"" } - 查看运行记录 + 查看运行记录 ) } diff --git a/src/forge/DevOps/Dispose/PipelineName.jsx b/src/forge/DevOps/Dispose/PipelineName.jsx index be840621..1a532fd4 100644 --- a/src/forge/DevOps/Dispose/PipelineName.jsx +++ b/src/forge/DevOps/Dispose/PipelineName.jsx @@ -1,17 +1,29 @@ import React , { useEffect , useState } from 'react'; -import { Modal , Input } from 'antd'; +import { Modal , Input , Select } from 'antd'; +const { Option }= Select; -function PipelineName({visible,onCancel,onOk,value}){ +const EVENT = ["push","pull_request","tag","cron","custom","promote","rollback"] +function PipelineName({visible,onCancel,onOk,value ,branchList}){ const [ name , setName ] = useState(undefined); + const [ branchValue , setBranchValue ] = useState(undefined); + const [ eventValue , setEventValue ] = useState(EVENT[0]); + + useEffect(()=>{ + if(branchList && branchList.length>0){ + setBranchValue(branchList[0].name); + } + },[branchList]) useEffect(()=>{ if(value){ setName(value.pipeline_name); + setBranchValue(value.branch); + setEventValue(value.event); } },[value]) function onSure(){ - onOk(name,value && value.id); + onOk(name,value && value.id,branchValue,eventValue); } return( 流水线名称: setName(e.target.value)} placeholder="请输入名称" style={{width:"340px",margin:"6px 0px"}}/> +
+ 触发条件: + + +
) } diff --git a/src/forge/DevOps/Dispose/head.jsx b/src/forge/DevOps/Dispose/head.jsx index 108719a1..c4daecde 100644 --- a/src/forge/DevOps/Dispose/head.jsx +++ b/src/forge/DevOps/Dispose/head.jsx @@ -1,12 +1,17 @@ import React from 'react'; -import { AlignCenterBetween } from '../../Component/layout'; +import { AlignCenterBetween , Blueline , FlexAJ } from '../../Component/layout'; -function head(){ +function head({manager}){ return( 工作流配置 - 模板使用说明 + + 模板使用说明 + { + manager && 模板管理 + } + ) } diff --git a/src/forge/DevOps/Index.jsx b/src/forge/DevOps/Index.jsx index 273d8d4d..193276cf 100644 --- a/src/forge/DevOps/Index.jsx +++ b/src/forge/DevOps/Index.jsx @@ -22,20 +22,11 @@ const Stucture = Loadable({ loader: () => import('./Structure'), loading: Loading, }) +const Mould = Loadable({ + loader: () => import('./Mould'), + loading: Loading, +}) export default ((props)=>{ - const { projectsId , owner } = props.match.params; - const open_devops = props.projectDetail && props.projectDetail.open_devops; - - // 工作流:两种状态进入的链接不同 - // useEffect(()=>{ - // if(open_devops !== undefined){ - // if(open_devops){ - // props.history.replace(`/projects/${owner}/${projectsId}/devops/dispose`); - // }else{ - // props.history.replace(`/projects/${owner}/${projectsId}/devops`); - // } - // } - // },[open_devops]) return( @@ -45,6 +36,11 @@ export default ((props)=>{ (p) => () } > + () + } + > () @@ -55,7 +51,7 @@ export default ((props)=>{ (p) => () } > - () } diff --git a/src/forge/DevOps/Mould.jsx b/src/forge/DevOps/Mould.jsx new file mode 100644 index 00000000..41b50eda --- /dev/null +++ b/src/forge/DevOps/Mould.jsx @@ -0,0 +1,155 @@ +import React , { useEffect , useState , useRef } from 'react'; +import { Banner , Blueback , FlexAJ , Spin } from '../Component/layout'; +import { Input , Table ,Pagination , Select , Popconfirm } from 'antd'; +import styled from 'styled-components'; +import axios from 'axios'; +import New from './MouldNew'; + +const { Option } = Select; + +const Div = styled.div`{ + padding:24px 30px; + min-height:420px; +}`; +const STAGE = [ + {stage_name:"所有",stage_type:"all"}, +  {stage_name:"初始化",stage_type:"init"}, +  {stage_name:"编译构建",stage_type:"build"}, +  {stage_name:"部署",stage_type:"deploy"}, +  {stage_name:"其他",stage_type:"customize"} +] +const limit = 15; +function Mould(props){ + const [ visible ,setVisible ] = useState(false); + const [ list ,setList ] = useState(undefined); + const [ page ,setPage ] = useState(1); + const [ totalCount ,setTotalCount ] = useState(0); + const [ stageType ,setStageType ] = useState("all"); + const [ search ,setSearch ] = useState(undefined); + const childRef = useRef(); + useEffect(()=>{ + Init(page,stageType); + },[page,stageType]) + + function Init(page,stageType,searchvalue){ + const url = `/ci/templates/list.json`; + axios.get(url,{ + params:{ + page,limit,stage_type:stageType,name:searchvalue + } + }).then(result=>{ + if(result && result.data){ + setList(result.data.templates); + setTotalCount(result.data.total_count); + } + }).catch(error=>{}) + } + + const columns=[ + { + title:"名称", + dataIndex:"template_name", + key:1, + ellipsis:true + }, + { + title:"所属阶段", + dataIndex:"stage_type", + key:2, + ellipsis:true, + render:(txt,item)=>{ + let i = STAGE.filter(item=>item.stage_type === txt); + return i && i.length>0 && i[0].stage_name + } + }, + { + title:"模板类型", + dataIndex:"category", + key:3, + ellipsis:true + }, + { + title:"操作", + dataIndex:"operation", + key:4, + ellipsis:true, + render:(txt,item)=>{ + return( + + editMouldFunc(item)}>编辑 + deleteMouldFunc(item.id)} okText="确定" cancelText={"取消"}> + 删除 + + + ) + } + } + ] + + // 编辑模板 + function editMouldFunc(item){ + if (childRef.current) { + childRef.current.setEditInfo(item); + } + setVisible(true); + } + + // 删除模板 + function deleteMouldFunc(id){ + const url = `/ci/templates/${id}.json`; + axios.delete(url).then(result=>{ + if(result && result.data){ + props.showNotification("模板删除成功!"); + Init(page,stageType,search); + } + }) + } + + function searchValue(){ + Init(page,stageType,search); + } + + function newMouldFunc(){ + if (childRef.current) { + childRef.current.setEditInfo(undefined); + } + setVisible(true); + } + + function onOk(){ + Init(page,"all"); + } + return( +
+ childRef.current = f} ref={childRef} visible={visible} onCancel={()=>setVisible(false)} onOk={onOk}> + 工作流 - 模板管理 +
+ + 新建模板 + + 阶段: + + setSearch(e.target.value)} allowClear style={{width:"160px",marginLeft:"15px"}}/> + 搜索 + + + row.id} pagination={false}>
+ { + totalCount > limit && +
+ setPage(page)}/> +
+ } +
+
+ ) +} +export default Mould; \ No newline at end of file diff --git a/src/forge/DevOps/MouldNew.jsx b/src/forge/DevOps/MouldNew.jsx new file mode 100644 index 00000000..cf6465bf --- /dev/null +++ b/src/forge/DevOps/MouldNew.jsx @@ -0,0 +1,139 @@ +import React , { useImperativeHandle , useState , forwardRef , useCallback } from 'react'; +import { Form , Modal , Input , Select , Spin } from 'antd'; +import Editor from './Dispose/Editors'; +import axios from 'axios'; + +const { Option } = Select; +const TYPE = ["Java","C","C++","Python","Go","Ruby","R","PHP", +     "Perl","Node","Docker","Rust","Swift","Erlang","Other"] + +function MouldNew({ form , visible , onCancel , onOk }, ref){ + const [value , setValue ] = useState(undefined); + const [isSpin , setIsSpin ] = useState(false); + const [valueFlag , setValueFlag ] = useState(false); + const [ id , setId ] = useState(false); + + const { getFieldDecorator, validateFields , setFieldsValue , getFieldsValue } = form; + + useImperativeHandle(ref, () => ({ + setEditInfo: (info) => { + if(info){ + setFieldsValue({ + ...info + }) + setValue(info.content); + setId(info.id); + }else{ + clear(); + setId(undefined); + } + } + })) + + const helper = useCallback( + (label, name, rules, widget, className, isRequired,flag) => ( + + {getFieldDecorator(name, { rules, validateFirst: true , valuePropName:flag ? "checked":"value" })(widget)} + + ), + [] + ); + function changeContent(v){ + if(v){ + setValue(v); + setValueFlag(false); + } + } + function cancel(){ + clear(); + onCancel(); + } + function sure(){ + if(!value){ + setValueFlag(true); + return; + } + validateFields((error,values)=>{ + if(!error){ + setIsSpin(true); + const url = `/ci/templates.json`; + axios.post(url,{ + ...values,id,content:value + }).then(result=>{ + if(result && result.data){ + setIsSpin(false); + cancel(); + onOk(); + } + }).catch(error=>{}) + } + }) + } + function clear(){ + setFieldsValue({ + stage_type:"all", + template_name:undefined, + category:"Java", + }) + setValue(""); + setValueFlag(false); + } + return( + + +
+ {helper( + "所属阶段", + "stage_type", + [{required:true,message:"请选择所属阶段"}], + ,true + )} + {helper( + "模板名称", + "template_name", + [{required:true,message:"请输入模板名称"}], + ,true + )} + {helper( + "模板分类", + "category", + [{required:true,message:"请选择模板分类"}], + ,true + )} +
+ * 模板内容: +
+
+ +
+ { valueFlag && 请输入模板内容} +
+
+
+
+
+ ) +} +export default Form.create()(forwardRef(MouldNew)); + diff --git a/src/forge/DevOps/Structure.jsx b/src/forge/DevOps/Structure.jsx index 8dde6e5a..5ad84aab 100644 --- a/src/forge/DevOps/Structure.jsx +++ b/src/forge/DevOps/Structure.jsx @@ -28,6 +28,7 @@ function Structure(props,ref){ let projectsId = props.match.params.projectsId; let owner = props.match.params.owner; + let branch = props.match.params.branch; const permission = props.projectDetail && props.projectDetail.permission; useImperativeHandle(ref, () => ({ @@ -49,7 +50,7 @@ function Structure(props,ref){ axios.get(url,{ params:{ search:status, - page,limit:LIMIT + page,limit:LIMIT,branch } }).then((result) => { if (result && result.data) { diff --git a/src/forge/DevOps/disposePipeline.jsx b/src/forge/DevOps/disposePipeline.jsx index 36fa0976..f39f7e6f 100644 --- a/src/forge/DevOps/disposePipeline.jsx +++ b/src/forge/DevOps/disposePipeline.jsx @@ -261,8 +261,6 @@ function disposePipeline(props){ // 确认提交 function sureSubmit(){ setLoading(true); - let sync = datas.sync || 0; - let url = ''; const { defaultBranch } = props; let params = { branch: defaultBranch, @@ -273,32 +271,18 @@ function disposePipeline(props){ owner:owner, repo:projectsId } - if(sync === 1){ - // 为true,则是编辑否则是新建 - url = `/${owner}/${projectsId}/update_trustie_pipeline.json`; - axios.put(url,{ - ...params - }).then(result=>{ - if(result){ - props.history.push(`/projects/${owner}/${projectsId}/devops/dispose`); - } - setLoading(false); - }).catch(error=>{ - console.log(error); - setLoading(false); - }) - }else{ - url = `/ci/pipelines/${disposeId}/create_trustie_pipeline.json`; - axios.post(url,params).then(result=>{ - if(result){ - props.history.push(`/projects/${owner}/${projectsId}/devops/dispose`); - } - setLoading(false); - }).catch(error=>{ - console.log(error); - setLoading(false); - }) - } + let url = `/${owner}/${projectsId}/update_trustie_pipeline.json`; + axios.put(url,{ + ...params + }).then(result=>{ + if(result){ + props.history.push(`/projects/${owner}/${projectsId}/devops/dispose`); + } + setLoading(false); + }).catch(error=>{ + console.log(error); + setLoading(false); + }) } return( diff --git a/src/forge/DevOps/ops.scss b/src/forge/DevOps/ops.scss index 49f431a0..17832e6a 100644 --- a/src/forge/DevOps/ops.scss +++ b/src/forge/DevOps/ops.scss @@ -487,4 +487,17 @@ .stepsBody.active{ display: block; } +} +.editorPanel{ + border:1px solid #eee; + .margin{ + width: 0px; + } + .monaco-scrollable-element.editor-scrollable{ + left: 0px!important; + width: 100%!important; + .view-lines{ + width: 336px!important; + } + } } \ No newline at end of file