diff --git a/src/forge/Component/OpsStatus.jsx b/src/forge/Component/OpsStatus.jsx index 02883bd6..f5db8d72 100644 --- a/src/forge/Component/OpsStatus.jsx +++ b/src/forge/Component/OpsStatus.jsx @@ -3,15 +3,15 @@ import './Component.scss'; export const Tags = (status)=>{ switch(status){ - case 1: + case "running": return( 运行中 ); - case 2: + case "failure": return ( 未通过 ); - case 3: + case "success": return ( 已通过 ); diff --git a/src/forge/DevOps/About.jsx b/src/forge/DevOps/About.jsx index d606e71f..8148edc3 100644 --- a/src/forge/DevOps/About.jsx +++ b/src/forge/DevOps/About.jsx @@ -1,27 +1,33 @@ -import React , { forwardRef , useCallback } from 'react'; -import activate from '../Images/activate.png'; -import { Blueback } from '../Component/layout'; -import styled from 'styled-components'; -import { Link } from 'react-router-dom'; -import { Form , Input } from 'antd'; -import axios from 'axios'; +import React, { forwardRef, useCallback, useState , useEffect } from "react"; +import activate from "../Images/activate.png"; +import { Blueback } from "../Component/layout"; +import styled from "styled-components"; +import { Link } from "react-router-dom"; +import { Form, Input, Modal, Button } from "antd"; +import axios from "axios"; + +const P = styled.p` + { + width: 200px; + line-height: 30px; + font-size: 16px; + color: #333; + text-align: center; + margin-top: 30px; + margin-bottom: 30px !important; + } +`; +function About(props, ref) { + const { form: { getFieldDecorator, validateFields } } = props; + const [step, setStep] = useState(1); + const [visible, setVisible] = useState(false); + const [firstCompleted, setFirstCompleted] = useState(false); -const P = styled.p`{ - width:200px; - line-height:30px; - font-size:16px; - color:#333; - text-align:center; - margin-top:30px; - margin-bottom:30px!important; -}`; -function About( props , ref){ - const { form: { getFieldDecorator , validateFields } } = props; const helper = useCallback( (label, name, rules, widget, isRequired) => ( - {label} + {label} {getFieldDecorator(name, { rules, validateFirst: true })(widget)} @@ -29,52 +35,140 @@ function About( props , ref){ ), [] ); + // 下一步 + function goStep() { + if (!firstCompleted) { + let projectsId = props.match.params.projectsId; + validateFields((error, values) => { + if (!error) { + const url = `/dev_ops/cloud_accounts.json`; + axios.post(url, { + ...values, + project_id: projectsId, + }) + .then((result) => { + if (result && result.data.redirect_url) { + setVisible(true); + setFirstCompleted(true); + // window.location.href = result.data.redirect_url; + } + }) + .catch((error) => { + console.log(error); + }); + } + }); + } else { + setStep(2); + } + } + // 弹框确定 + function sure(){ + setVisible(false) + setStep(2); + } + // 开始激活 function startActive(){ - let projectsId = props.match.params.projectsId; - validateFields((error,values)=>{ - if(!error){ - const url = `/dev_ops/cloud_accounts.json`; - axios.post(url,{ - ...values, - project_id:projectsId - }).then(result=>{ - if(result && result.data.redirect_url){ - window.location.href = result.data.redirect_url; - } - }).catch(error=>{ - console.log(error); - }) + validateFields((error, values) => { + if(!values){ + } }) } - return( + return (
- + setVisible(false)} + footer={ + + + + + } + > +
+

+ 初始化配置已完成,请前往: +
+ + http://ip:80/login/oauth/authorize + +
+ 进入认证 +

+
+
+

定义DevOps工作流,帮助您检测bug、发布代码…

- 了解什么是DevOps? -
- {helper( - "服务器IP地址:", - "ip_num", - [{ required: true, message: "请输入服务器IP地址" }], - ,true - )} - {helper( - "服务器用户名:", - "account", - [{ required: true, message: "请输入服务器用户名" }], - ,true - )} - {helper( - "服务器密码:", - "secret", - [{ required: true, message: "请输入服务器密码" }], - ,true - )} - - 开始激活 + + 了解什么是DevOps? + + {step === 1 ? ( + +
+

请仔细核对您的服务器信息,一旦确认提交将无法修改

+ {helper( + "服务器IP地址:", + "ip_num", + [{ required: true, message: "请输入服务器IP地址" }], + , + true + )} + {helper( + "服务器用户名:", + "account", + [{ required: true, message: "请输入服务器用户名" }], + , + true + )} + {helper( + "服务器密码:", + "secret", + [{ required: true, message: "请输入服务器密码" }], + , + true + )} + + 下一步 +
+ ) : ( +
+
+

认证成功后,请前往http://ip:80/account获取token值,并将获取的token值填入输入框

+ {helper( + "token值:", + "token", + [{ required: true, message: "请输入token值" }], + , + true + )} +
+ + 开始激活 +
+
+
+ )}
- ) + ); } -export default Form.create()(forwardRef(About)); \ No newline at end of file +export default Form.create()(forwardRef(About)); diff --git a/src/forge/DevOps/OpsDetailLeftpanel.jsx b/src/forge/DevOps/OpsDetailLeftpanel.jsx index 427f0c99..c92cc096 100644 --- a/src/forge/DevOps/OpsDetailLeftpanel.jsx +++ b/src/forge/DevOps/OpsDetailLeftpanel.jsx @@ -1,42 +1,80 @@ -import React from 'react'; -import { FlexAJ , Blueline , AlignCenter } from '../Component/layout'; -import styled from 'styled-components'; -import { Menu } from 'antd'; -import { TagsLine } from '../Component/OpsStatus'; +import React, { useEffect ,useState } from "react"; +import { FlexAJ, Blueline, AlignCenter } from "../Component/layout"; +import styled from "styled-components"; +import { Menu , Popconfirm } from "antd"; +import { TagsLine } from "../Component/OpsStatus"; +import { Time } from "../Utils/Time"; +import { truncateCommitId } from "../common/util"; + const SubMenu = Menu.SubMenu; -const Img = styled.img`{ - width:25px; - height:25px; - border-radius:50%; - margin-right:10px; -}` -export default (()=>{ - return( +const Img = styled.img` + { + width: 25px; + height: 25px; + border-radius: 50%; + margin-right: 10px; + } +`; +export default ({ data , repeatSet }) => { + const [ tamp , setTamp ] = useState(undefined); + const [ sha , setSha ] = useState(undefined); + useEffect(()=>{ + if(data && data.started){ + let t = parseInt(data.started) * 1000; + let time = Time(t); + setTamp(time); + } + if(data && data.after){ + setSha(truncateCommitId(data.after)); + } + },[data]) + + return (
- - 开始时间:2020.07.10 15:30 - 运行时间:20s + + {data && data.started && ( + + 开始时间: {tamp} + + )} + {data && data.timestamp && ( + + 运行时间:{data && data.timestamp}s + + )} - 重新创建 + + 重新创建 +
分支: - master - 8b3476f5 + {data && data.source} + {sha}
- - CI
}> + + + + CI + + } + > - Build setup 01 {TagsLine(1)}20s + + Build setup 01 {TagsLine(1)} + 20s + - ) -}) \ No newline at end of file + ); +}; diff --git a/src/forge/DevOps/Structure.jsx b/src/forge/DevOps/Structure.jsx index d7bcde6a..6c78ca04 100644 --- a/src/forge/DevOps/Structure.jsx +++ b/src/forge/DevOps/Structure.jsx @@ -1,240 +1,318 @@ -import React , { useState , useEffect } from 'react'; -import { FlexAJ , AlignCenter , Blueback } from '../Component/layout'; -import { Table , Pagination } from 'antd'; -import { truncateCommitId } from '../common/util'; -import styled from 'styled-components'; -import axios from 'axios'; +import React, { useState, useEffect } from "react"; +import { FlexAJ, AlignCenter, Blueback } from "../Component/layout"; +import { Table, Pagination, Popconfirm } from "antd"; +import { truncateCommitId } from "../common/util"; +import styled from "styled-components"; +import axios from "axios"; +import { Time } from "../Utils/Time"; const STATUS = [ - {name:"所有",value:"1"}, - {name:"准备中",value:"2"}, - {name:"运行中",value:"3"}, - {name:"已完成",value:"4"} -] -const LIMIT = 15; -const datasource = [ - { - status:2, - author:"caishi", - message:{ - branch:"master", - sha:"8b3476f5", - image:"https://dss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2034740944,4251903193&fm=26&gp=0.jpg", - message:"将分支“ 221063-improve-buy-ci-minutes-link”" - }, - begin:"2020-07-08", - run:"20s" - }, - { - status:1, - author:"caishi", - message:{ - branch:"master", - sha:"8b3476f5", - image:"https://dss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2034740944,4251903193&fm=26&gp=0.jpg", - message:"将分支“ 221063-improve-buy-ci-minutes-link”" - }, - begin:"2020-07-08", - run:"" - } + { name: "所有", value: "1" }, + { name: "准备中", value: "2" }, + { name: "运行中", value: "3" }, + { name: "已完成", value: "4" }, ]; +const LIMIT = 15; +const Img = styled.img` + { + border-radius: 50%; + margin-rigth: 10px; + width: 25px; + height: 25px; + } +`; +export default (props) => { + const [status, setStatus] = useState("1"); + const [page, setPage] = useState(1); + const [total, setTotal] = useState(10); + const [data, setData] = useState(undefined); + const [tableLoading, setTableLoading] = useState(true); -const Img = styled.img`{ - border-radius:50%; - margin-rigth:10px; - width:25px; - height:25px; -}` -export default ((props)=>{ - const [ status ,setStatus ] = useState("1"); - const [ page ,setPage ] = useState(1); - const [ total ,setTotal ] = useState(10); - const [ data , setData ] = useState(undefined); - - let projectsId = props.match.params.projectsId; - - useEffect(()=>{ - if(projectsId){ - const url ='/dev_ops/builds.json'; - axios.get(url,{ - params:{ - project_id:projectsId - } - }).then(result=>{ - if(result){ - let list = result.data && result.data.map((item,key)=>{ - return { - status:item.status, - author:item.sender, - message:{ - branch:item.source, - image:item.author_avatar, - message:item.message, - sha:truncateCommitId(item.after) - }, - started:item.started, - timestamp:item.timestamp - } - }) - setData(list); - } - }).catch(error=>{ - console.log(error); - }) + let projectsId = props.match.params.projectsId; + useEffect(() => { + if (projectsId) { + Init(); } - },[]) + }, []); - function ChangeStatus(value){ - setStatus(value) + function Init() { + const url = "/dev_ops/builds.json"; + axios + .get(url, { + params: { + project_id: projectsId, + }, + }) + .then((result) => { + if (result) { + let list = + result.data && + result.data.map((item, key) => { + return { + status: item.status, + author: item.sender, + message: { + branch: item.source, + image: item.author_avatar, + message: item.message, + sha: truncateCommitId(item.after), + }, + started: item.started + ? Time(parseInt(item.started) * 1000) + : "--", + timestamp: item.timestamp, + number: item.number, + id:item.id + }; + }); + setData(list); + setTableLoading(false); + } + }) + .catch((error) => { + console.log(error); + }); + } + + function ChangeStatus(value) { + setStatus(value); } // 切换分页 - function ChangePage(page){ - setPage(page) + function ChangePage(page) { + setPage(page); } function renderStatus() { - return( + return ( - ) + ); } - function renderStatusBtn(status){ - if(status === "failure" || status ==="success"){ - return( - 重新构建 - ) - }else{ - return( - 撤销构建 - ) + function renderStatusBtn(status, number) { + if (status === "failure" || status === "success") { + return ( + repeatSet(e,number)} + onCancel={(e)=>{e.stopPropagation()}} + cancelText="取消" + okText="确定" + > + {e.stopPropagation()}}>重新构建 + + ); + } else { + return ( + cancelSet(e,number)} + onCancel={(e)=>{e.stopPropagation()}} + cancelText="取消" + okText="确定" + > + {e.stopPropagation()}}>撤销构建 + + ); } } - function renderTableStatus (status){ - switch (status){ + // 重新构建 + function repeatSet(e,number) { + e.stopPropagation(); + setTableLoading(true); + const url = `/dev_ops/builds/${number}.json`; + axios.post(url, { project_id: projectsId }) + .then((result) => { + if (result) { + props.showNotification("工作流正在重新构建!"); + Init(); + } + }) + .catch((error) => { + console.log(error); + }); + } + + // 撤销构建 + function cancelSet(e,number) { + e.stopPropagation(); + setTableLoading(true); + const url = `/dev_ops/builds/${number}.json`; + axios.delete(url, { + params:{project_id: projectsId} + }) + .then((result) => { + if (result) { + props.showNotification("撤销构建成功!"); + Init(projectsId); + } + }) + .catch((error) => { + console.log(error); + }); + } + + function renderTableStatus(status) { + switch (status) { case "running": - return( - 运行中 + return ( + + 运行中 + ); case "failure": return ( - 未通过 + + 未通过 + ); case "success": return ( - 已通过 + + 已通过 + ); default: return ( - 准备中 + + 准备中 + ); } } + + function clickRows(event,e){ + props.history.push(`/projects/${projectsId}/ops/${e.number}/detail`); + } const column = [ { - title:'序号', - dataIndex:"No", - key:"No", - width:"8%", - render:(item,value,key)=>{ - return( - #{key+1} - ) - } + title: "序号", + dataIndex: "number", + key: "number", + width: "8%", + render: ( value, item, key) => { + return #{value}; + }, }, { - title:'状态', - dataIndex:"status", - key:"status", - width:"12%", - render:(value,item,key)=>{ - return(renderTableStatus(value)) - } + title: "状态", + dataIndex: "status", + key: "status", + width: "12%", + render: (value, item, key) => { + return renderTableStatus(value); + }, }, { - title:'构建人', - dataIndex:"author", - key:"author", - width:"12%", - align:"center" + title: "构建人", + dataIndex: "author", + key: "author", + width: "12%", + align: "center", }, { - title:'提交信息', - dataIndex:"message", - key:"message", - width:"30%", - render:(value,item,key)=>{ + title: "提交信息", + dataIndex: "message", + key: "message", + width: "30%", + render: (value, item, key) => { let meg = item.message; - return ( + return (
- { meg.branch && 分支{meg.branch}} - { meg.sha && {meg.sha}} + {meg.branch && ( + + 分支 + {meg.branch} + + )} + {meg.sha && {meg.sha}}
-
{meg.message}
+
+ {meg.message} +
- ) - } + ); + }, }, { - title:'开始时间', - dataIndex:"started", - key:"started", - width:"15%", - render:(value,item,key)=>{ - return ( - {value || "--"} - ) - } + title: "开始时间", + dataIndex: "started", + key: "started", + width: "15%", + render: (value, item, key) => { + return {value || "--"}; + }, }, { - title:'运行时间', - dataIndex:"timestamp", - key:"timestamp", - width:"15%", - render:(value,item,key)=>{ - return ( - {value || value === 0 ? `${value}s` : "--"} - ) - } + title: "运行时间", + dataIndex: "timestamp", + key: "timestamp", + width: "15%", + render: (value, item, key) => { + return {value || value === 0 ? `${value}s` : "--"}; + }, }, { - title:'操作', - dataIndex:"operation", - key:"operation", - render:(value,item,key)=>{ - return(renderStatusBtn(item.status)); - } - } - ] - return( + title: "操作", + dataIndex: "operation", + key: "operation", + render: (value, item, key) => { + return renderStatusBtn(item.status, item.number); + }, + }, + ]; + return (
{renderStatus()} 手动创建 - 分支 - 标签 + + 分支 + + + + 标签 + { + return{ + onClick:(event)=>clickRows(event,record) + } + }} columns={column} className="normalTable" dataSource={data} pagination={false} + loading={tableLoading} >
- { - total > LIMIT ? -
- -
:"" - } + {total > LIMIT ? ( +
+ +
+ ) : ( + "" + )}
- ) -}) \ No newline at end of file + ); +}; diff --git a/src/forge/DevOps/ops.scss b/src/forge/DevOps/ops.scss index b9dcafcc..9aa790da 100644 --- a/src/forge/DevOps/ops.scss +++ b/src/forge/DevOps/ops.scss @@ -70,6 +70,9 @@ } } } + .ant-modal-close{ + top:7px; + } // 列表 .listPart{ .statusTag{ diff --git a/src/forge/DevOps/opsDetail.jsx b/src/forge/DevOps/opsDetail.jsx index efb6f846..34ae4e3e 100644 --- a/src/forge/DevOps/opsDetail.jsx +++ b/src/forge/DevOps/opsDetail.jsx @@ -1,33 +1,90 @@ -import React from 'react'; -import './ops.scss'; -import { FlexAJ, AlignCenter } from '../Component/layout'; -import { Tags } from '../Component/OpsStatus'; -import SplitPane from 'react-split-pane'; -import LeftPanel from './OpsDetailLeftpanel'; -import RightPanel from './OpsDetailRightpanel'; +import React, { useEffect, useState , useRef , useContext } from "react"; +import "./ops.scss"; +import { FlexAJ, AlignCenter } from "../Component/layout"; +import { Tags } from "../Component/OpsStatus"; +import SplitPane from "react-split-pane"; +import LeftPanel from "./OpsDetailLeftpanel"; +import RightPanel from "./OpsDetailRightpanel"; +import axios from "axios"; +import { Spin } from "antd"; +import { Link } from 'react-router-dom'; +export default (props) => { + const [data, setData] = useState(undefined); + const [stages, setStages] = useState(undefined); + const [spinning, setSpinning] = useState(true); -export default (()=>{ - return( -
- - - #1 - 将分支“221063-improve-buy-ci-minutes-link”合并到“221063-improve-buy-ci-minutes-link" - {Tags(1)} - - 退出 - -
- -
- -
-
- -
-
+ let projectId = props.match.params.projectId; + let opsId = props.match.params.opsId; + + useEffect(() => { + if (opsId && projectId) { + Init(); + } + }, [opsId]); + + function Init(){ + const url = `/dev_ops/builds/${opsId}.json`; + axios.get(url, { + params: { + project_id: projectId, + }, + }) + .then((result) => { + if (result && result.data) { + setSpinning(false); + setData(result.data); + setStages(result.data.stages); + } + }) + .catch((error) => { + console.log(error); + }); + } + + // 重新构建 + function repeatSet() { + const url = `/dev_ops/builds/${data && data.number}.json`; + axios.post(url, { project_id: projectId }) + .then((result) => { + if (result) { + Init(); + } + }) + .catch((error) => { + console.log(error); + }); + } + return ( + +
+ + + #{data && data.number} + {data && data.message} + {Tags(`${data && data.status}`)} + + + 退出 + + +
+ +
+ +
+
+ +
+
+
-
- ) -}); \ No newline at end of file + + ); +}; diff --git a/src/forge/Main/Detail.js b/src/forge/Main/Detail.js index 88f20638..8faaa61b 100644 --- a/src/forge/Main/Detail.js +++ b/src/forge/Main/Detail.js @@ -438,12 +438,12 @@ class Detail extends Component { } - {/*
  • -1 ? "active" : ""}> +
  • -1 ? "active" : ""}> 工作流 {projectDetail && projectDetail.ops_count ? {projectDetail.ops_count} : ""} -
  • */} +
  • -1 || url.indexOf("meilpost") > -1) ? "active" : ""}> 里程碑 diff --git a/src/forge/Utils/Time.js b/src/forge/Utils/Time.js index 55d73441..4de4b371 100644 --- a/src/forge/Utils/Time.js +++ b/src/forge/Utils/Time.js @@ -26,7 +26,7 @@ export function getDateTime(value, dataformat) { return date.format(dataformat); } -export default function Time(UTCtiem) { +export function Time(UTCtiem) { var dateTime = new Date(UTCtiem); var year = dateTime.getFullYear();