This commit is contained in:
caishi 2021-01-19 18:11:38 +08:00
parent f7fe479859
commit 0b007b35d9
17 changed files with 740 additions and 199 deletions

View File

@ -1,6 +1,6 @@
import React , { useState , useEffect } from 'react';
import { Spin , Pagination } from 'antd';
import { Blueback , Banner } from '../Component/layout';
import { Spin } from 'antd';
import { Blueback } from '../Component/layout';
import List from './Dispose/List';
import Head from './Dispose/head';
import axios from 'axios';
@ -11,13 +11,11 @@ import styled from 'styled-components';
const Div = styled.div`{
padding:24px 30px;
}`;
const LIMIT = 15;
function Dispose(props){
const [ spining , setSpining ] = useState(false);
const [ spining , setSpining ] = useState(true);
const [ updateInfo , setUpdateInfo ] = useState(undefined);
const [ list , setList ] = useState(undefined);
const [ page , setPage ] = useState(1);
const [ total , setTotal ] = useState(0);
const [ permission , setPermission ] = useState("");
const [ visible , setVisible ] = useState(false);
@ -33,69 +31,94 @@ function Dispose(props){
}
},[projectDetail])
useEffect(()=>{
function Init(){
const url = `/ci/pipelines/list.json`;
axios.get(url).then(result=>{
axios.get(url,{
params:{
identifier:projectsId
}
}).then(result=>{
if(result && result.data){
setList(result.data.pipelines);
setSpining(false);
}
}).catch(error=>{})
// let l = [
// {name:"线",fileName:"filename.jpg",time:"2021-01-06",status:""},
// {name:"线11",fileName:"filename111.jpg",time:"2021-01-06",status:""}
// ]
},[])
// -
function changePage(page){
setPage(page);
}
useEffect(()=>{
Init();
},[])
// 线
function addNew(){
function addNew(pipeline_name,id){
setVisible(true);
setUpdateInfo(undefined);
if(pipeline_name){
let l = {pipeline_name,id}
setUpdateInfo(l);
}
}
function onOk(pipeline_name){
function onOk(pipeline_name,updateId){
if(pipeline_name){
const url = `/ci/pipelines.json`;
axios.post(url,{
pipeline_name,
file_name:".trustie.pipeline.yml"
}).then(result=>{
if(result && result.data){
if(!updateId){
//
const url = `/ci/pipelines.json`;
axios.post(url,{
pipeline_name,
file_name:".trustie.pipeline.yml",
identifier:projectsId
}).then(result=>{
setVisible(false);
props.showNotification("流水线新增成果,请进行工作流配置!");
props.history.push(`/projects/${owner}/${projectsId}/devops/dispose/${result.data.id}`);
}else{
props.showNotification("流水线新增失败,请稍后再试!");
}
})
if(result && result.data){
props.showNotification("流水线新增成功,请进行工作流配置!");
props.history.push(`/projects/${owner}/${projectsId}/devops/dispose/${result.data.id}`);
}else{
props.showNotification("流水线新增失败,请稍后再试!");
}
}).catch(error=>{})
}else{
//
const url = `/ci/pipelines/${updateId}.json`;
axios.put(url,{
pipeline_name
}).then(result=>{
if(result && result.data){
setVisible(false);
Init();
props.showNotification("流水线名称更新成功!");
}else{
props.showNotification("流水线名称更新失败,请稍后再试!");
}
}).catch(error=>{})
}
}else{
props.showNotification("请输入流水线名称!");
}
}
//
function deleteFunc(id){
const url = `/ci/pipelines/${id}.json`;
axios.delete(url).then(result=>{
if(result && result.data){
props.showNotification("流水线删除成功!");
Init();
}
}).catch(error=>{})
}
return(
<Spin spinning={spining}>
<PipelineName visible={visible} onCancel={()=>setVisible(false)} onOk={onOk}/>
<PipelineName visible={visible} value={updateInfo} onCancel={()=>{setVisible(false);}} onOk={onOk}/>
<div className="disposePanel">
<Head />
<Div>
{ permission !=="Reporter" && (!list ||(list && list.length === 0)) && <Blueback onClick={addNew}>新增流水线</Blueback> }
{ permission !=="Reporter" && (!list ||(list && list.length === 0)) && <Blueback onClick={()=>addNew(undefined,undefined)}>新增流水线</Blueback> }
<div className="mt20 disposeList">
<List list={list} permission={permission} projectsId={projectsId} owner={owner}/>
{
total > LIMIT ?
<div className="txtright mt20 mb20">
<Pagination simple current={page} pageSize={LIMIT} onChange={changePage} total={total}/>
</div>
:""
}
<List list={list} permission={permission} projectsId={projectsId} owner={owner} showModal={addNew} deleteFunc={deleteFunc}/>
</div>
</Div>
</div>

View File

@ -1,36 +1,93 @@
import React , { useEffect , useState } from 'react';
function Choosen({name,chooseFunc}){
const [ index, setIndex ] = useState(undefined);
const [ models , setModels ] = useState(undefined);
function Choosen({ chooseFunc, temp , templateId , category }){
const [ tempId, setTemId ] = useState(undefined);
const [ cate , setCate ]= useState(undefined);
const [ templates , setTemplates ] = useState(undefined);
const [ categories , setCategories ] = useState(undefined);
useEffect(()=>{
let list = [
{id:1,name:"docker/arm64/linux"},
{id:2,name:"docker/x86/linux"}
]
setModels(list);
},[])
if(templateId){
setTemId(templateId);
}
},[templateId])
//
function chooseOption(id,k){
chooseFunc && chooseFunc(id);
setIndex(k);
useEffect(()=>{
if(category){
setCate(category);
}
},[category])
useEffect(()=>{
if(temp && temp.length > 0){
if(temp[0].category !== "初始化"){
setCategories(temp)
}else{
setCategories(undefined);
}
setTemplates(temp[0].templates);
setCate(temp[0].category);
if(category && temp[0].category !== "初始化" && category !== "初始化"){
let c = temp.filter(item=>item.category === category);
let t = c && c.length > 0 && c[0].templates;
setTemplates(t);
setCate(category);
}
}else{
setTemplates(undefined);
setCate(undefined);
setCategories(undefined);
}
},[temp,category])
//
function changeCate(cate){
setCate(cate);
let c = categories && categories.filter(item=>item.category === cate);
setTemplates(c && c[0].templates);
chooseFunc && chooseFunc(undefined,undefined,cate);
}
//
function chooseOption(id){
let item = templates.filter(item=>item.id === id);
let content = item && item.length >0 && item[0].content;
chooseFunc && chooseFunc(content,id,cate);
setTemId(id);
}
return(
<div className="choosenList">
<span>{name}</span>
<ul>
{
models && models.map((item,key)=>{
return(
<li className={index && index === key+1 ? "active":""} onClick={()=>chooseOption(item.id,key+1)}>{item.name}</li>
)
})
}
</ul>
</div>
<React.Fragment>
{
categories && categories.length > 0 &&
<div className="choosenList">
<span>模板类别</span>
<ul>
{
categories.map((item,key)=>{
return(
<li className={cate === item.category ?"active":""} onClick={()=>changeCate(item.category)}>{item.category}</li>)
})
}
</ul>
</div>
}
{
templates && templates.length> 0 &&
<div className="choosenList">
<span>模板选择</span>
<ul>
{
templates.map((item,key)=>{
return(
<li className={tempId === item.id ? "active":""} onClick={()=>chooseOption(item.id)}>{item.template_name}</li>
)
})
}
</ul>
</div>
}
</React.Fragment>
)
}
export default Choosen;

View File

@ -1,7 +1,25 @@
import React from 'react';
import Editor from 'react-monaco-editor';
function Editors({value,onChange,theme,height}){
function Editors({value,onChange,theme,height,visible}){
const editor_options = {
lineNumbers: "on",
wordWrap: true, //
selectOnLineNumbers: true,
lineHeight: 24,
renderLineHighlight: "line",
revealHorizontalRightPadding: 5,
placeholder:"请输入内容",
readOnly: visible,
cursorStyle: visible ? "underline-thin" : "line",
folding: true,
foldingStrategy: "indentation", //
automaticLayout: true, //
minimap: {
//
enabled: false,
},
}
return(
<Editor
height={height}
@ -9,8 +27,9 @@ function Editors({value,onChange,theme,height}){
theme={theme}
placeholder="请输入内容"
value={value}
options={"editor_options"}
options={editor_options}
onChange={(value)=>onChange(value)}
disabled={true}
></Editor>
)
}

View File

@ -1,24 +1,64 @@
import React , { useEffect , useState } from 'react';
import { Input , Button } from 'antd';
import { Button } from 'antd';
import Choosen from './Choosen';
import Editors from './Editors';
function Init(){
const [ modelsId , setModelsId ] = useState(undefined);
function Init({ datas , templates , saveFunc , saveDatas}){
const [ templateId , setTemplateId ] = useState(undefined);
const [ ymlValue , setYmlValue ] = useState(undefined);
const [ temp , setTemp ] = useState(undefined);
useEffect(()=>{
if(templates && templates.length > 0){
setTemp(templates)
}
},[templates])
function chooseFunc(id){
setModelsId(id);
useEffect(()=>{
if(datas && datas.length > 0){
setTemplateId(datas[0].template_id);
setYmlValue(datas[0].content);
}
},[datas])
//
function chooseFunc(content,id,cate){
setTemplateId(id);
setYmlValue(content);
recieveData(id,content);
}
function recieveData(id,content){
let steps = datas;
if(datas && datas.length>0){
steps[0].content = content || ymlValue;
steps[0].template_id = id || templateId ;
}else{
steps =
[{
step_name:"初始化",
show_index:1,
content:content || ymlValue,
template_id:id || templateId
}]
}
saveDatas(steps);
}
//
function nextStep(){
recieveData();
saveFunc(undefined,undefined,undefined,undefined,"next");
}
return(
<div>
<Choosen name={"模板选择:"} chooseFunc={chooseFunc}/>
<Choosen chooseFunc={chooseFunc} templateId={templateId} temp={temp}/>
<div className="mt15">
<Editors value={ymlValue} onChange={setYmlValue} theme={"vs-dark"} height={"400px"}/>
</div>
<div className="mt20">
<Button type={"primary"}>下一步</Button>
<Button type={"primary"} onClick={nextStep}>下一步</Button>
</div>
</div>
)

View File

@ -1,8 +1,8 @@
import React, { useState } from 'react';
import { Table } from 'antd';
import React from 'react';
import { Table , Popconfirm } from 'antd';
import { Link } from 'react-router-dom';
function List({ list, permission , projectsId , owner }){
function List({ list, permission , projectsId , owner , showModal , deleteFunc }){
const columns = [
{
@ -10,7 +10,12 @@ function List({ list, permission , projectsId , owner }){
dataIndex:"pipeline_name",
key:1,
width:"20%",
ellipsis:true
ellipsis:true,
render:(txt,item)=>{
return(
<span onDoubleClick={()=>showModal(txt,item.id)} style={{display:"block",cursor:"pointer"}}>{txt}</span>
)
}
},
{
title:"文件名称",
@ -27,13 +32,6 @@ function List({ list, permission , projectsId , owner }){
width:"18%",
ellipsis:true
},
// {
// title:"",
// dataIndex:"status",
// key:1,
// width:"15%",
// ellipsis:true
// },
{
title:"操作",
dataIndex:"operation",
@ -42,9 +40,12 @@ function List({ list, permission , projectsId , owner }){
render:(txt,item)=>{
return(
<span>
{ permission !=="Reporter" && <Link to={`/projects/${owner}/${projectsId}/devops/dispose/1`} className="color-grey-6"><i className="iconfont iconzaibianji font-13 mr3"></i>编辑</Link> }
{ permission !=="Reporter" && <a className="ml10 color-grey-6"><i className="iconfont iconlajitong font-13 mr3"></i>删除</a> }
{ permission !=="Reporter" && <a className="ml10 color-grey-6"><i className="iconfont iconyunhang font-13 mr3"></i>运行</a> }
{ permission !=="Reporter" && <Link to={`/projects/${owner}/${projectsId}/devops/dispose/${item.id}`} className="color-grey-6"><i className="iconfont iconzaibianji font-13 mr3"></i>编辑</Link> }
{ permission !=="Reporter" &&
<Popconfirm title={"确定要删除此流水线?"} onConfirm={()=>deleteFunc(item.id)} okText="确定" cancelText={"取消"}>
<a className="ml10 color-grey-6"><i className="iconfont iconlajitong font-13 mr3"></i>删除</a>
</Popconfirm>
}
<Link to={`/projects/${owner}/${projectsId}/devops/list`} className="ml10 color-grey-6">查看运行记录</Link>
</span>
)

View File

@ -1,11 +1,18 @@
import React , { useEffect , useState } from 'react';
import { Modal , Input } from 'antd';
function PipelineName({visible,onCancel,onOk}){
function PipelineName({visible,onCancel,onOk,value}){
const [ name , setName ] = useState(undefined);
console.log(value);
useEffect(()=>{
if(value){
setName(value.pipeline_name);
}
},[value])
function onSure(){
onOk(name);
onOk(name,value && value.id);
}
return(
<Modal

View File

@ -1,43 +1,95 @@
import React , { useEffect , useState } from 'react';
import { Button } from 'antd';
import { Button , Popconfirm } from 'antd';
import { Cancel } from '../../Component/layout';
import Item from './StageItem';
function Stage({}){
function Stage({ templates , datas , saveDatas , saveFunc , stepName , showNotification , deleteStep , deleteFunc }){
const [ stepList , setStepList ] = useState(undefined);
const [ temp , setTemp ] = useState(undefined);
const [ saveFlag , setSaveFlag ] = useState(true);
useEffect(()=>{
let l = [
{
"step_name": "编译构建-gradle",
"show_index": 1,
"content": "xxxxxxxxxxx",
"template_id":1
},
{
"step_name": "编译构建-maven",
"show_index": 1,
"content": "xxxxxxxxxxx",
"template_id":1
if(templates && templates.length > 0){
setTemp(templates);
}
},[templates])
useEffect(()=>{
if(datas){
if(datas.length > 0 && stepList !== datas){
setStepList(datas);
}else if(datas.length === 0){
let list = [];
setStepList(list);
}
]
setStepList(l);
},[])
setSaveFlag(true);
}
},[datas])
//
function addFunc(){
if(saveFlag){
let list = stepList;
let length = list ? list.length : 0;
let step =
{
"category":undefined,
"step_name": stepName+`${length + 1}`,
"show_index": length + 1,
"content":undefined,
"template_id":undefined,
"hide":false
}
list.push(step);
saveDatas(list);
setSaveFlag(false);
}else{
showNotification(`请先选择模板!`);
}
}
//
function saveItems(content,id,cate,key){
let item = stepList;
item[key].content = content;
item[key].template_id = id;
item[key].category = cate;
saveDatas([...item]);
setSaveFlag((id && content) ? true:false);
}
function slideItems(key,hide){
let item = stepList;
item[key].hide = !hide;
setStepList([...item]);
saveDatas(item);
}
function deleteItem(id){
setSaveFlag(true);
deleteStep(id);
}
//
function nextStep(btn){
saveFunc(undefined,undefined,undefined,undefined,btn);
}
return(
<div>
{
stepList && stepList.map((item,key)=>{
stepList && stepList.length > 0 && stepList.map((item,key)=>{
return(
<Item item={item}/>
<Item item={item} templates={temp} k={key} saveItems={saveItems} slideItems={slideItems} deleteStep={deleteItem}/>
)
})
}
<a className="addBtn">+ 添加步骤</a>
<a className="addBtn" onClick={addFunc}>+ 添加步骤</a>
<div className="mt20">
<Button type="primary">下一步</Button>
<Button className="ml20" type="primary">下一步</Button>
<Cancel className="ml20">删除</Cancel>
<Button type="primary" onClick={()=>nextStep("last")}>上一步</Button>
<Button className="ml20" type="primary" onClick={()=>nextStep("next")}>下一步</Button>
<Popconfirm title={'确定要删除当前阶段吗'} okText="是" cancelText="否" onConfirm={deleteFunc}>
<Cancel className="ml20">删除</Cancel>
</Popconfirm>
</div>
</div>
)

View File

@ -1,25 +1,39 @@
import React , { useEffect , useState } from 'react';
import React from 'react';
import { FlexAJ } from '../../Component/layout';
import Editors from './Editors';
import Choosen from './Choosen';
import { Popconfirm } from 'antd';
function StageItem({item}){
const [ value , setValue ] = useState(undefined);
function StageItem({item, templates,saveItems,k, slideItems , deleteStep}){
function onChangevalue(value){
console.log(value);
saveItems(value,item.template_id,item.category,k);
}
//
function chooseFunc(content,id,cate){
saveItems(content,id,cate,k);
}
return(
<div className="stepsItem">
<FlexAJ className="stepsHead">
<span>{item.step_name}</span>
<span np={k}>{item.step_name}</span>
<span className="color-grey-9">
<a><i className="iconfont iconlajitong1 font-14"></i></a>
<a><i className="iconfont iconsanjiaoxing-down font-14"></i></a>
<Popconfirm
title={"确定要删除这个步骤吗?"}
okText="是"
cancelText="否"
onConfirm={() => deleteStep(item.id)}
><a>
<i className="iconfont iconlajitong1 font-14"></i></a>
</Popconfirm>
<a onClick={()=>slideItems(k,item.hide)}><i className={ (!item.hide || item.hide === false) ? "iconfont iconsanjiaoxing-down font-14" :"iconfont icontriangle font-14"}></i></a>
</span>
</FlexAJ>
<div className="stepsBody">
<Editors value={value} onChange={onChangevalue} theme="vs-dark" height="120px" />
<div className={(!item.hide || item.hide === false) ? "stepsBody active" : "stepsBody"}>
<Choosen chooseFunc={chooseFunc} category={item.category} templateId={item.template_id} temp={templates}/>
<Editors value={item.content} onChange={onChangevalue} theme="vs-dark" height="120px" />
</div>
</div>
)

View File

@ -1,23 +1,26 @@
import React , { useEffect , useState } from 'react';
import React ,{useEffect , useState} from 'react';
import { Button } from 'antd';
import Editors from './Editors';
import { Cancel } from '../../Component/layout';
function Sure(){
const [ ymlValue , setYmlValue ] = useState(undefined);
function Sure({datas , name , saveFunc , sureSubmit}){
const [ value , setValue ] = useState(undefined);
useEffect(()=>{
if(datas && datas.content){
setValue(datas.content)
}
},[datas])
return(
<div>
<div style={{padding:"0px 15px 15px 15px"}}>
工作流名称流水线名称
工作流名称{name}
</div>
<div className="editorBody" style={{marginTop:"0px"}}>
<Editors value={ymlValue} onChange={setYmlValue} theme={"vs-grey"} height={"600px"}/>
<Editors value={value} theme={"vs-grey"} height={"600px"} visible/>
</div>
<div className="mt20">
<Button type={"primary"}>下一步</Button>
<Button type={"primary"} className="ml20">确定提交</Button>
<Cancel className="ml20">取消</Cancel>
<Button type={"primary"} onClick={()=>saveFunc(undefined,undefined,undefined,undefined,"last")}>上一步</Button>
{ value && <Button type={"primary"} className="ml20" onClick={sureSubmit}>确定提交</Button> }
</div>
</div>
)

View File

@ -1,4 +1,4 @@
import React , { useEffect , useState } from 'react';
import React from 'react';
import { AlignCenterBetween } from '../../Component/layout';
import { Link } from 'react-router-dom';
@ -8,7 +8,7 @@ function head(){
return(
<AlignCenterBetween>
<span className="font-20">工作流配置</span>
<Link to="" className="color-grey-6"><i className="iconfont icontishi1 font-14 mr3"></i>模板使用说明</Link>
<Link to={`https://forum.trustie.net/forums/3111/detail`} target="_blank" className="color-grey-6"><i className="iconfont icontishi1 font-14 mr3"></i>模板使用说明</Link>
</AlignCenterBetween>
)
}

View File

@ -1,16 +1,20 @@
import React , { useEffect , useState } from 'react';
import React from 'react';
import MenusRename from './menusRename';
import MenusAdd from './menusAdd';
const typeIcon = {
init:"iconinitialize",build:"iconstructure",deploy:"iconarrange",customize:"iconnewStage",confirm:'iconsure'
}
function Menus({step,changeStep,menuList}){
function InitActive(key){
changeStep(key)
function Menus({step,changeStep, menuList , renameFunc , checkDatas , addFunc }){
function InitActive(key,stage_type,stage_id,stage_name){
changeStep(key,stage_type,stage_id,stage_name);
}
function getName(name){
console.log(name);
//
function getName(name,index){
addFunc && addFunc(name,index);
}
return(
@ -19,12 +23,12 @@ function Menus({step,changeStep,menuList}){
menuList && menuList.length > 0 && menuList.map((item,key)=>{
return(
<React.Fragment key={item.id} >
<li onClick={()=>InitActive(key+1)} className={key+1 === step ?"active":""}>
<i className={`iconfont ${item.icon}`}></i>
<MenusRename name={item.stage_name} edit={item.stage_type !== "init" && item.stage_type !== "confirm"}/>
<li onClick={()=>InitActive(item.show_index,item.stage_type,item.id,item.stage_name)} className={item.show_index === step ?"active":""}>
<i className={`iconfont ${typeIcon[`${item.stage_type}`]}`}></i>
<MenusRename renameFunc={renameFunc} id={item.id} name={item.stage_name} edit={item.stage_type !== "init" && item.stage_type !== "confirm"}/>
</li>
{ key !== (menuList.length-1) && menuList.length <= 10 ?
<MenusAdd getName={getName}/>:""
{ key !== (menuList.length-1) && menuList.length <= 7 ?
<MenusAdd checkDatas={checkDatas} k={key+2} getName={getName}/>:""
}
</React.Fragment>
)

View File

@ -1,21 +1,53 @@
import React , { useState , useEffect } from 'react';
import { Input } from 'antd';
function menusAdd ({ getName }){
function menusAdd ({ getName , checkDatas , k }){
const [ show, setShow ] = useState(false);
const [ value , setValue ] = useState(undefined);
const [ index , setIndex ] = useState(undefined);
const [ref, setRef ] = useState(undefined);
const [put, setPut ] = useState(false);
useEffect(() => {
if (put && ref) {
ref.focus();
}
})
useEffect(() => {
if (k) {
setIndex(k);
}
},[k])
function blurInput(){
if(value){
getName(value);
getName(value , index);
}
setShow(false);
setPut(false);
}
function showInput(){
let c = checkDatas();
if(c){
setShow(true);
setPut(true);
}
}
return(
<li className="menuAdd">
{ !show && <i className="iconfont icontianjia" onClick={()=>setShow(true)}></i> }
{ show && <Input size={"small"} maxLength={8} style={{width:"75px"}} placeholder="新阶段名称" value={value} onChange={(e)=>setValue(e.target.value)} onBlur={blurInput} /> }
{ !show && <i className="iconfont icontianjia" onClick={showInput}></i> }
<Input
ref={(el) => setRef(el)}
size={"small"}
maxLength={8}
style={{width:"75px",display : `${show?"block":'none'}`}}
placeholder="新阶段名称"
value={value}
onChange={(e)=>setValue(e.target.value)} onBlur={blurInput}
/>
</li>
)
}

View File

@ -1,11 +1,17 @@
import React , { useEffect , useState } from 'react';
import { Input } from 'antd';
const regexp = /[^a-zA-Z0-9_]/g;
function menusRename({ name , edit }){
function menusRename({ name , edit , id , renameFunc }){
const [ n , setN ] = useState(undefined);
const [ show , setShow ] = useState(false);
const [ref, setRef ] = useState(undefined);
const [put, setPut ] = useState(false);
useEffect(() => {
if (edit && ref) {
ref.focus();
}
})
useEffect(()=>{
if(name){
@ -13,21 +19,32 @@ function menusRename({ name , edit }){
}
},[name])
// input
function changeShow(e){
e.stopPropagation();
setShow(true);
setPut(true);
}
function blurInput(){
function blurInput(e){
renameFunc(e.target.value,id);
setPut(false);
setShow(false);
}
return(
<div className="aboutEdit">
<span className="operateName">
{ !show && n }
{ show &&
<Input value={n} size="small" maxLength={8} onClick={(e)=>e.stopPropagation()} onBlur={blurInput} style={{width:"75px"}} onChange={(e)=>setN(e.target.value)}/>
}
<Input
ref={(el) => setRef(el)}
value={n}
size="small"
maxLength={8}
onClick={(e)=>e.stopPropagation()}
onBlur={blurInput}
style={{width:"75px",display : `${show?"block":'none'}`}}
onChange={(e)=>setN(e.target.value)}
/>
{ !show && edit && <i className="iconfont iconeditUnder font-16 color-grey-9" onClick={changeShow}></i> }
</span>
</div>

View File

@ -42,27 +42,27 @@ export default ((props)=>{
<Switch {...props}>
<Route path="/projects/:owner/:projectsId/devops/dispose/:disposeId"
render={
() => (<New {...props} />)
(p) => (<New {...props} {...p}/>)
}
></Route>
<Route path="/projects/:owner/:projectsId/devops/dispose/new"
render={
() => (<New {...props} />)
(p) => (<New {...props} {...p}/>)
}
></Route>
<Route path="/projects/:owner/:projectsId/devops/dispose"
render={
() => (<Dispose {...props} />)
(p) => (<Dispose {...props} {...p}/>)
}
></Route>
<Route path="/projects/:owner/:projectsId/devops/list"
render={
() => (<Stucture {...props} />)
(p) => (<Stucture {...props} {...p}/>)
}
></Route>
<Route path="/projects/:owner/:projectsId/devops"
render={
() => (<About {...props} />)
(p) => (<About {...props} {...p}/>)
}
></Route>
</Switch>

View File

@ -1,44 +1,310 @@
import React , { useEffect , useState } from 'react';
import { WhiteBack } from '../Component/layout';
import { Spin } from 'antd';
import Head from './Dispose/head';
import Menus from './Dispose/menus';
import Init from './Dispose/Init';
import Sure from './Dispose/Sure';
import Stage from './Dispose/Stage';
import axios from 'axios';
import { data } from 'autoprefixer';
function disposePipeline(){
const [ step , setStep ] = useState(1);
function disposePipeline(props){
const [ spining , setSpining ] = useState(true);
const [ stage , setStage ] = useState(1);
const [ pipeLineName , setPipeLineName ] = useState(undefined);
const [ stepName , setStepName ] = useState(undefined);
const [ stageId , setStageId ] =useState(undefined);
const [ menuList , setMenuList ] = useState(undefined);
const [ stageType , setStageType ] = useState("init");
const [ datas , setDatas ] = useState(undefined);
const [ templates , setTemplates] = useState(undefined);
const [ datasUpdataFlag , setDatasUpdataFlag ] = useState(false);
const { disposeId } = props.match.params;
let projectsId = props.match.params.projectsId;
let owner = props.match.params.owner;
useEffect(()=>{
if(stageType && stageType !=="confirm"){
InitTemplates();
}
},[stageType])
//
function InitTemplates(){
const url = `/ci/templates/templates_by_stage.json`;
axios.get(url,{
params:{ stage_type:stageType }
}).then(result=>{
if(result && result.data){
setTemplates(result.data);
}
}).catch(error=>{})
}
useEffect(()=>{
let list = [
{stage_name:"初始化",stage_type:"init",id:1,icon:"iconinitialize"},
{stage_name:"编辑构建",stage_type:"",id:2,icon:"iconstructure"},
{stage_name:"部署",stage_type:"",id:3,icon:"iconarrange"},
{stage_name:"确认",stage_type:"confirm",id:4,icon:"iconsure"}
];
setMenuList(list);
},[])
if(disposeId && (stageType && stageType !=="confirm")){
getData(0);
}
},[disposeId])
//
function getData(index){
const url = `/ci/pipelines/${disposeId}/stages.json`;
axios.get(url).then(result=>{
if(result && result.data){
setMenuList(result.data.stages);
if(index || index === 0){
let first = result.data.stages[index];
setStage(first.show_index);
setStageId(first.id);
setPipeLineName(`${first.pipeline_name}`);
}
}
})
}
function changeStep(s){
setStep(s);
useEffect(()=>{
if(stageId){
getStageStep(stageId);
}
},[stageId])
//
function getStageStep(stageId){
let url = "";
if(stageType && stageType === "confirm"){
url = `/ci/pipelines/${disposeId}/content.json`;
axios.get(url,{
params:{
owner,repo:projectsId
}
}).then(result=>{
if(result && result.data){
setDatas(result.data);
}
}).catch(error=>{})
}else{
url = `/ci/pipelines/${disposeId}/${stageId}/steps.json`;
axios.get(url).then(result=>{
if(result && result.data){
let step = result.data.steps;
setDatas(step);
let flag = !step || (step && step.length === 0);
setDatasUpdataFlag(flag);
}
}).catch(error=>{})
}
setSpining(false);
}
// sshow_index,stage_type:stage_type
function changestage(show_index,stage_type,stage_id,stage_name){
if(show_index !== stage){
saveFunc(show_index,stage_type,stage_id,stage_name);
}
}
//
function saveDatas(steps){
setDatas([...steps]);
setDatasUpdataFlag(true);
}
//
function checkDatas(){
if(datas && datas.length > 0){
for(let i= 0;i< datas.length;i++){
if(datas[i] && (!datas[i].content || !datas[i].template_id)){
props.showNotification("请先选择模板!");
return false;
}
}
}else{
if(stageType === "init" ){
props.showNotification("请先选择模板!");
return false;
}else if(stageType === "confirm" ){
return true;
}else{
return "";
}
}
return true;
}
//
function saveFunc(show_index,stage_type,stage_id,stageName,btn){
setSpining(true);
//
if(datasUpdataFlag && stageType !== "confirm"){
// undefined --datas
let f = checkDatas();
if(f && (datas && datas.length !== 0)){
//
saveDataFunc(btn,show_index,stage_type,stage_id,stageName);
}
else{
setSpining(false);
f === "" && elseFunc(btn,show_index,stage_type,stage_id,stageName);
}
}else{
elseFunc(btn,show_index,stage_type,stage_id,stageName);
}
}
function saveDataFunc(btn,show_index,stage_type,stage_id,stageName){
const url = `/ci/pipelines/${disposeId}/${stageId}/stage_step.json`;
axios.post(url,{
steps:datas
}).then(result=>{
if(result && result.data){
props.showNotification("阶段更新成功!");
setDatasUpdataFlag(false);
if(!btn){
setStage(show_index);
setStageType(stage_type);
setStageId(stage_id);
setStepName(pipeLineName+`-`+stageName);
}else{
enters(btn);
}
}
}).catch(error=>{})
}
function elseFunc(btn,show_index,stage_type,stage_id,stageName){
if(btn){
enters(btn);
}else{
setStage(show_index);
setStageType(stage_type);
setStageId(stage_id);
setStepName(pipeLineName+`-`+stageName);
}
}
//
function enters(btn){
let s;
if(btn === "next"){
//
s = stage+1;
}else{
//
s = stage-1;
}
let item = menuList && menuList.filter(i=>i.show_index === (s));
setStage(item[0].show_index);
setStageType(item[0].stage_type);
setStageId(item[0].id);
setStepName(pipeLineName+`-`+item[0].stage_name);
}
//
function deleteStep(id){
if(id){
const url = `/ci/pipelines/${disposeId}/${stageId}/${id}/delete_step.json`;
axios.delete(url).then(result=>{
if(result && result.data){
getStageStep(stageId);
props.showNotification("阶段步骤删除成功!");
}
}).catch(error=>{})
}else{
let d = datas.filter(s=>s.template_id !== undefined);
setDatas(d);
}
}
function renameFunc(value,id){
const url = `/ci/pipelines/${disposeId}/${id}/update_stage.json`;
axios.put(url,{
stage_name:value
}).then(result=>{
if(result && result.data){
getData();
}
}).catch(error=>{})
}
//
function addMenuFunc(name,index){
const url = `/ci/pipelines/${disposeId}/create_stage.json`;
axios.post(url,{
show_index:index,
stage_name:name
}).then(result=>{
if(result && result.data){
props.showNotification("阶段新增成功!");
getData(index-1);
}
})
}
//
function deleteStageFunc(){
const url = `/ci/pipelines/${disposeId}/${stageId}/delete_stage.json`;
axios.delete(url,{
show_index:stage
}).then(result=>{
if(result && result.data){
props.showNotification("阶段删除成功!");
getData(stage-1);
}
}).catch(error=>{})
}
//
function sureSubmit(){
let sync = datas.sync || 0;
let url = '';
const { defaultBranch } = props;
let params = {
branch: defaultBranch,
content:datas.content,
filepath:'.trustie-pipeline.yml',
message:'',
sha:datas.sha || undefined,
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}/${disposeId}/devops/dispose`);
}
}).catch(error=>{
console.log(error);
})
}else{
url = `/ci/pipelines/${disposeId}/create_trustie_pipeline.json`;
axios.post(url,params).then(result=>{
if(result){
props.history.push(`/projects/${owner}/${disposeId}/devops/dispose`);
}
}).catch(error=>{
console.log(error);
})
}
}
return(
<div className="disposePanel">
<Head />
<WhiteBack style={{padding:"24px 30px"}}>
<div style={{minHeight:"450px"}}>
<Menus step={step} changeStep={changeStep} menuList={menuList}/>
{
step === 1 ? <Init />
:
menuList && (step === menuList.length) ? <Sure />
:
<Stage />
}
</div>
<Spin spinning={spining}>
<div style={{minHeight:"450px"}}>
<Menus step={stage} checkDatas={checkDatas} changeStep={changestage} menuList={menuList} renameFunc={renameFunc} addFunc={addMenuFunc}/>
{
stage === 1 ? <Init stage_type={stageType} templates={templates} datas={datas} saveDatas={saveDatas} saveFunc={saveFunc}/>
:
menuList && (stage === menuList.length) ? <Sure sureSubmit={sureSubmit} name={pipeLineName} datas={datas} saveFunc={saveFunc}/>
:
<Stage {...props} stepName={stepName} deleteStep={deleteStep} stage_type={stageType} templates={templates} datas={datas} deleteFunc={deleteStageFunc} saveDatas={saveDatas} saveFunc={saveFunc}/>
}
</div>
</Spin>
</WhiteBack>
</div>
)

View File

@ -341,15 +341,16 @@
text-align: center;
color: #787878;
font-size: 16px;
min-width: 110px;
position: relative;
cursor: pointer;
width: 0;
width: 110px;
flex:2;
&>i{
font-size: 30px!important;
height: 30px;
width: 30px;
line-height: 30px;
margin-bottom: 10px;
}
&:hover,&.active{
& > i{
@ -368,12 +369,15 @@
}
.aboutEdit{
width: 100%;
padding:0px 15px;
.operateName{
position: relative;
line-height: 20px;
line-height: 22px;
height: 40px;
display: block;
& > i{
position: absolute;
right:-22px;
right:-15px;
top:50%;
margin-top: -11px;
display: none;
@ -467,7 +471,6 @@
border-radius: 4px;
margin-bottom: 15px;
.stepsHead{
border-bottom: 1px solid #e1e4e8;
padding:8px 15px;
span > a > i{
margin-left: 8px;
@ -476,6 +479,12 @@
}
.stepsBody{
padding:10px 15px;
border-top: 1px solid #e1e4e8;
background-color: rgb(251, 251, 251);
display: none;
transition: 0.3s;
}
.stepsBody.active{
display: block;
}
}

View File

@ -395,7 +395,6 @@ class NewHeader extends Component {
}
inputjoinclassvalue = (e) => {
console.log(e.target.value.length);
if (e.target.value.length >= 7) {
this.openNotification("请输入6位项目邀请码!");
return
@ -552,9 +551,7 @@ class NewHeader extends Component {
let url = "/setting.json";
axios.get(url).then((response) => {
if (response && response.data) {
this.setState({ mygetHelmetapi2: response.data.setting }, () => {
console.log('');
});
this.setState({ mygetHelmetapi2: response.data.setting });
// localStorage.setItem('chromesetting', JSON.stringify(response.data.setting));
// localStorage.setItem('chromesettingresponse', JSON.stringify(response));
try {