forked from Gitlink/forgeplus-react
devops
This commit is contained in:
parent
cefd974498
commit
dfbcc09e5b
|
@ -3,15 +3,15 @@ import './Component.scss';
|
||||||
|
|
||||||
export const Tags = (status)=>{
|
export const Tags = (status)=>{
|
||||||
switch(status){
|
switch(status){
|
||||||
case 1:
|
case "running":
|
||||||
return(
|
return(
|
||||||
<span className="statusColor running">运行中</span>
|
<span className="statusColor running">运行中</span>
|
||||||
);
|
);
|
||||||
case 2:
|
case "failure":
|
||||||
return (
|
return (
|
||||||
<span className="statusColor failed">未通过</span>
|
<span className="statusColor failed">未通过</span>
|
||||||
);
|
);
|
||||||
case 3:
|
case "success":
|
||||||
return (
|
return (
|
||||||
<span className="statusColor pass">已通过</span>
|
<span className="statusColor pass">已通过</span>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import React , { forwardRef , useCallback } from 'react';
|
import React, { forwardRef, useCallback, useState , useEffect } from "react";
|
||||||
import activate from '../Images/activate.png';
|
import activate from "../Images/activate.png";
|
||||||
import { Blueback } from '../Component/layout';
|
import { Blueback } from "../Component/layout";
|
||||||
import styled from 'styled-components';
|
import styled from "styled-components";
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from "react-router-dom";
|
||||||
import { Form , Input } from 'antd';
|
import { Form, Input, Modal, Button } from "antd";
|
||||||
import axios from 'axios';
|
import axios from "axios";
|
||||||
|
|
||||||
const P = styled.p`{
|
const P = styled.p`
|
||||||
|
{
|
||||||
width: 200px;
|
width: 200px;
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
@ -14,9 +15,14 @@ const P = styled.p`{
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-top: 30px;
|
margin-top: 30px;
|
||||||
margin-bottom: 30px !important;
|
margin-bottom: 30px !important;
|
||||||
}`;
|
}
|
||||||
|
`;
|
||||||
function About(props, ref) {
|
function About(props, ref) {
|
||||||
const { form: { getFieldDecorator, validateFields } } = props;
|
const { form: { getFieldDecorator, validateFields } } = props;
|
||||||
|
const [step, setStep] = useState(1);
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [firstCompleted, setFirstCompleted] = useState(false);
|
||||||
|
|
||||||
|
|
||||||
const helper = useCallback(
|
const helper = useCallback(
|
||||||
(label, name, rules, widget, isRequired) => (
|
(label, name, rules, widget, isRequired) => (
|
||||||
|
@ -29,52 +35,140 @@ function About( props , ref){
|
||||||
),
|
),
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
// 下一步
|
||||||
function startActive(){
|
function goStep() {
|
||||||
|
if (!firstCompleted) {
|
||||||
let projectsId = props.match.params.projectsId;
|
let projectsId = props.match.params.projectsId;
|
||||||
validateFields((error, values) => {
|
validateFields((error, values) => {
|
||||||
if (!error) {
|
if (!error) {
|
||||||
const url = `/dev_ops/cloud_accounts.json`;
|
const url = `/dev_ops/cloud_accounts.json`;
|
||||||
axios.post(url, {
|
axios.post(url, {
|
||||||
...values,
|
...values,
|
||||||
project_id:projectsId
|
project_id: projectsId,
|
||||||
}).then(result=>{
|
|
||||||
if(result && result.data.redirect_url){
|
|
||||||
window.location.href = result.data.redirect_url;
|
|
||||||
}
|
|
||||||
}).catch(error=>{
|
|
||||||
console.log(error);
|
|
||||||
})
|
})
|
||||||
|
.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(){
|
||||||
|
validateFields((error, values) => {
|
||||||
|
if(!values){
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="activatePanel">
|
<div className="activatePanel">
|
||||||
|
<Modal
|
||||||
|
title="提示"
|
||||||
|
visible={visible}
|
||||||
|
closable={true}
|
||||||
|
onCancel={() => setVisible(false)}
|
||||||
|
footer={
|
||||||
|
<React.Fragment>
|
||||||
|
<Button onClick={() => setVisible(false)}>取消</Button>
|
||||||
|
<Button
|
||||||
|
onClick={sure}
|
||||||
|
type={"primary"}
|
||||||
|
className="ml20"
|
||||||
|
>
|
||||||
|
确定
|
||||||
|
</Button>
|
||||||
|
</React.Fragment>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div style={{ display: "flex", justifyContent: "center" }}>
|
||||||
|
<p style={{ maxWidth: "260px" }}>
|
||||||
|
初始化配置已完成,请前往:
|
||||||
|
<br />
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
href="http://ip:80/login/oauth/authorize"
|
||||||
|
className="color-blue"
|
||||||
|
>
|
||||||
|
http://ip:80/login/oauth/authorize
|
||||||
|
</a>
|
||||||
|
<br />
|
||||||
|
进入认证
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
<img src={activate} alt="" width="250px" />
|
<img src={activate} alt="" width="250px" />
|
||||||
<P>定义DevOps工作流,帮助您检测bug、发布代码…</P>
|
<P>定义DevOps工作流,帮助您检测bug、发布代码…</P>
|
||||||
<Link to={""} style={{color:"#5091FF",marginBottom:"20px"}}>了解什么是DevOps?</Link>
|
<Link to={""} style={{ color: "#5091FF", marginBottom: "20px" }}>
|
||||||
|
了解什么是DevOps?
|
||||||
|
</Link>
|
||||||
|
{step === 1 ? (
|
||||||
|
<React.Fragment>
|
||||||
<Form>
|
<Form>
|
||||||
|
<p className="mb20" style={{width:"370px"}}>请仔细核对您的服务器信息,一旦确认提交将无法修改</p>
|
||||||
{helper(
|
{helper(
|
||||||
"服务器IP地址:",
|
"服务器IP地址:",
|
||||||
"ip_num",
|
"ip_num",
|
||||||
[{ required: true, message: "请输入服务器IP地址" }],
|
[{ required: true, message: "请输入服务器IP地址" }],
|
||||||
<Input placeholder="请输入服务器IP地址" style={{width:"368px"}} size="large" />,true
|
<Input
|
||||||
|
placeholder="请输入服务器IP地址"
|
||||||
|
style={{ width: "368px" }}
|
||||||
|
size="large"
|
||||||
|
disabled={firstCompleted}
|
||||||
|
/>,
|
||||||
|
true
|
||||||
)}
|
)}
|
||||||
{helper(
|
{helper(
|
||||||
"服务器用户名:",
|
"服务器用户名:",
|
||||||
"account",
|
"account",
|
||||||
[{ required: true, message: "请输入服务器用户名" }],
|
[{ required: true, message: "请输入服务器用户名" }],
|
||||||
<Input placeholder="请输入服务器用户名" size="large" />,true
|
<Input placeholder="请输入服务器用户名" size="large" disabled={firstCompleted} />,
|
||||||
|
true
|
||||||
)}
|
)}
|
||||||
{helper(
|
{helper(
|
||||||
"服务器密码:",
|
"服务器密码:",
|
||||||
"secret",
|
"secret",
|
||||||
[{ required: true, message: "请输入服务器密码" }],
|
[{ required: true, message: "请输入服务器密码" }],
|
||||||
<Input.Password placeholder="请输入服务器密码" size="large" />,true
|
<Input.Password placeholder="请输入服务器密码" size="large" disabled={firstCompleted}/>,
|
||||||
|
true
|
||||||
)}
|
)}
|
||||||
</Form>
|
</Form>
|
||||||
<Blueback onClick={startActive}>开始激活</Blueback>
|
<Blueback onClick={goStep}>下一步</Blueback>
|
||||||
|
</React.Fragment>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<Form>
|
||||||
|
<p className="mb20" style={{width:"370px"}}>认证成功后,请前往<a target="_blank" href="http://ip:80/account" className="color-blue">http://ip:80/account</a>获取token值,并将获取的token值填入输入框</p>
|
||||||
|
{helper(
|
||||||
|
"token值:",
|
||||||
|
"token",
|
||||||
|
[{ required: true, message: "请输入token值" }],
|
||||||
|
<Input placeholder="请输入token值" size="large" />,
|
||||||
|
true
|
||||||
|
)}
|
||||||
|
<div style={{ display: "flex", justifyContent: "center" }}>
|
||||||
|
<Button onClick={()=>setStep(1)}>返回上一步</Button>
|
||||||
|
<Blueback onClick={startActive} className="ml20">开始激活</Blueback>
|
||||||
</div>
|
</div>
|
||||||
)
|
</Form>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
export default Form.create()(forwardRef(About));
|
export default Form.create()(forwardRef(About));
|
|
@ -1,42 +1,80 @@
|
||||||
import React from 'react';
|
import React, { useEffect ,useState } from "react";
|
||||||
import { FlexAJ , Blueline , AlignCenter } from '../Component/layout';
|
import { FlexAJ, Blueline, AlignCenter } from "../Component/layout";
|
||||||
import styled from 'styled-components';
|
import styled from "styled-components";
|
||||||
import { Menu } from 'antd';
|
import { Menu , Popconfirm } from "antd";
|
||||||
import { TagsLine } from '../Component/OpsStatus';
|
import { TagsLine } from "../Component/OpsStatus";
|
||||||
|
import { Time } from "../Utils/Time";
|
||||||
|
import { truncateCommitId } from "../common/util";
|
||||||
|
|
||||||
|
|
||||||
const SubMenu = Menu.SubMenu;
|
const SubMenu = Menu.SubMenu;
|
||||||
const Img = styled.img`{
|
const Img = styled.img`
|
||||||
|
{
|
||||||
width: 25px;
|
width: 25px;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}`
|
}
|
||||||
export default (()=>{
|
`;
|
||||||
|
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 (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<FlexAJ className="leftheader">
|
<FlexAJ className="leftheader">
|
||||||
<AlignCenter>
|
<AlignCenter>
|
||||||
<Img src="https://dss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2091711702,2468700162&fm=111&gp=0.jpg"/>
|
<Img src={data && data.author_avatar} />
|
||||||
<span className="nest">开始时间:<span>2020.07.10 15:30</span></span>
|
{data && data.started && (
|
||||||
<span className="nest">运行时间:<span>20s</span></span>
|
<span className="nest">
|
||||||
|
开始时间:<span> {tamp}</span>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{data && data.timestamp && (
|
||||||
|
<span className="nest">
|
||||||
|
运行时间:<span>{data && data.timestamp}s</span>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</AlignCenter>
|
</AlignCenter>
|
||||||
|
<Popconfirm title="是否重新创建?" okText="确定" cancelText="取消" onConfirm={repeatSet}>
|
||||||
<Blueline>重新创建</Blueline>
|
<Blueline>重新创建</Blueline>
|
||||||
|
</Popconfirm>
|
||||||
</FlexAJ>
|
</FlexAJ>
|
||||||
<div className="leftMainContent">
|
<div className="leftMainContent">
|
||||||
<AlignCenter className="contentBranch">
|
<AlignCenter className="contentBranch">
|
||||||
<i className="iconfont icon-fenzhi1"></i>
|
<i className="iconfont icon-fenzhi1"></i>
|
||||||
<span>分支:</span>
|
<span>分支:</span>
|
||||||
<span className="branchname">master</span>
|
<span className="branchname">{data && data.source}</span>
|
||||||
<span className="branchsha">8b3476f5</span>
|
<span className="branchsha">{sha}</span>
|
||||||
</AlignCenter>
|
</AlignCenter>
|
||||||
</div>
|
</div>
|
||||||
<Menu mode='inline' className="leftMenu">
|
<Menu mode="inline" className="leftMenu">
|
||||||
<SubMenu title={<div><i className="iconfont icon-gongzuoliu font-14 mr4"></i><span>CI</span></div>}>
|
<SubMenu
|
||||||
|
title={
|
||||||
|
<div>
|
||||||
|
<i className="iconfont icon-gongzuoliu font-14 mr4"></i>
|
||||||
|
<span>CI</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
<Menu.Item>
|
<Menu.Item>
|
||||||
<FlexAJ><span>Build setup 01 {TagsLine(1)}</span><span>20s</span></FlexAJ>
|
<FlexAJ>
|
||||||
|
<span>Build setup 01 {TagsLine(1)}</span>
|
||||||
|
<span>20s</span>
|
||||||
|
</FlexAJ>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</SubMenu>
|
</SubMenu>
|
||||||
</Menu>
|
</Menu>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
})
|
};
|
||||||
|
|
|
@ -1,68 +1,53 @@
|
||||||
import React , { useState , useEffect } from 'react';
|
import React, { useState, useEffect } from "react";
|
||||||
import { FlexAJ , AlignCenter , Blueback } from '../Component/layout';
|
import { FlexAJ, AlignCenter, Blueback } from "../Component/layout";
|
||||||
import { Table , Pagination } from 'antd';
|
import { Table, Pagination, Popconfirm } from "antd";
|
||||||
import { truncateCommitId } from '../common/util';
|
import { truncateCommitId } from "../common/util";
|
||||||
import styled from 'styled-components';
|
import styled from "styled-components";
|
||||||
import axios from 'axios';
|
import axios from "axios";
|
||||||
|
import { Time } from "../Utils/Time";
|
||||||
|
|
||||||
const STATUS = [
|
const STATUS = [
|
||||||
{ name: "所有", value: "1" },
|
{ name: "所有", value: "1" },
|
||||||
{ name: "准备中", value: "2" },
|
{ name: "准备中", value: "2" },
|
||||||
{ name: "运行中", value: "3" },
|
{ name: "运行中", value: "3" },
|
||||||
{name:"已完成",value:"4"}
|
{ 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:""
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
const LIMIT = 15;
|
||||||
const Img = styled.img`{
|
const Img = styled.img`
|
||||||
|
{
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
margin-rigth: 10px;
|
margin-rigth: 10px;
|
||||||
width: 25px;
|
width: 25px;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
}`
|
}
|
||||||
export default ((props)=>{
|
`;
|
||||||
|
export default (props) => {
|
||||||
const [status, setStatus] = useState("1");
|
const [status, setStatus] = useState("1");
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const [total, setTotal] = useState(10);
|
const [total, setTotal] = useState(10);
|
||||||
const [data, setData] = useState(undefined);
|
const [data, setData] = useState(undefined);
|
||||||
|
const [tableLoading, setTableLoading] = useState(true);
|
||||||
|
|
||||||
let projectsId = props.match.params.projectsId;
|
let projectsId = props.match.params.projectsId;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (projectsId) {
|
if (projectsId) {
|
||||||
const url ='/dev_ops/builds.json';
|
Init();
|
||||||
axios.get(url,{
|
|
||||||
params:{
|
|
||||||
project_id:projectsId
|
|
||||||
}
|
}
|
||||||
}).then(result=>{
|
}, []);
|
||||||
|
|
||||||
|
function Init() {
|
||||||
|
const url = "/dev_ops/builds.json";
|
||||||
|
axios
|
||||||
|
.get(url, {
|
||||||
|
params: {
|
||||||
|
project_id: projectsId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
if (result) {
|
if (result) {
|
||||||
let list = result.data && result.data.map((item,key)=>{
|
let list =
|
||||||
|
result.data &&
|
||||||
|
result.data.map((item, key) => {
|
||||||
return {
|
return {
|
||||||
status: item.status,
|
status: item.status,
|
||||||
author: item.sender,
|
author: item.sender,
|
||||||
|
@ -70,99 +55,171 @@ export default ((props)=>{
|
||||||
branch: item.source,
|
branch: item.source,
|
||||||
image: item.author_avatar,
|
image: item.author_avatar,
|
||||||
message: item.message,
|
message: item.message,
|
||||||
sha:truncateCommitId(item.after)
|
sha: truncateCommitId(item.after),
|
||||||
},
|
},
|
||||||
started:item.started,
|
started: item.started
|
||||||
timestamp:item.timestamp
|
? Time(parseInt(item.started) * 1000)
|
||||||
}
|
: "--",
|
||||||
})
|
timestamp: item.timestamp,
|
||||||
|
number: item.number,
|
||||||
|
id:item.id
|
||||||
|
};
|
||||||
|
});
|
||||||
setData(list);
|
setData(list);
|
||||||
|
setTableLoading(false);
|
||||||
}
|
}
|
||||||
}).catch(error=>{
|
|
||||||
console.log(error);
|
|
||||||
})
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},[])
|
|
||||||
|
|
||||||
function ChangeStatus(value) {
|
function ChangeStatus(value) {
|
||||||
setStatus(value)
|
setStatus(value);
|
||||||
}
|
}
|
||||||
// 切换分页
|
// 切换分页
|
||||||
function ChangePage(page) {
|
function ChangePage(page) {
|
||||||
setPage(page)
|
setPage(page);
|
||||||
}
|
}
|
||||||
function renderStatus() {
|
function renderStatus() {
|
||||||
return (
|
return (
|
||||||
<ul className="listNav">
|
<ul className="listNav">
|
||||||
{
|
{STATUS.map((item, key) => {
|
||||||
STATUS.map((item,key)=>{
|
return (
|
||||||
return <li onClick={()=>ChangeStatus(item.value)} className={ status === item.value ? "active":""}>{item.name}</li>
|
<li
|
||||||
})
|
onClick={() => ChangeStatus(item.value)}
|
||||||
}
|
className={status === item.value ? "active" : ""}
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
function renderStatusBtn(status){
|
function renderStatusBtn(status, number) {
|
||||||
if (status === "failure" || status === "success") {
|
if (status === "failure" || status === "success") {
|
||||||
return (
|
return (
|
||||||
<a className="color-blue">重新构建</a>
|
<Popconfirm
|
||||||
)
|
title="确认重新构建?"
|
||||||
|
onConfirm={(e) => repeatSet(e,number)}
|
||||||
|
onCancel={(e)=>{e.stopPropagation()}}
|
||||||
|
cancelText="取消"
|
||||||
|
okText="确定"
|
||||||
|
>
|
||||||
|
<a className="color-blue" onClick={(e)=>{e.stopPropagation()}}>重新构建</a>
|
||||||
|
</Popconfirm>
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<a className="color-red">撤销构建</a>
|
<Popconfirm
|
||||||
)
|
title="确认撤销构建?"
|
||||||
|
onConfirm={(e) => cancelSet(e,number)}
|
||||||
|
onCancel={(e)=>{e.stopPropagation()}}
|
||||||
|
cancelText="取消"
|
||||||
|
okText="确定"
|
||||||
|
>
|
||||||
|
<a className="color-red" onClick={(e)=>{e.stopPropagation()}}>撤销构建</a>
|
||||||
|
</Popconfirm>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 重新构建
|
||||||
|
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) {
|
function renderTableStatus(status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case "running":
|
case "running":
|
||||||
return (
|
return (
|
||||||
<span className="statusTag running"><i className="iconfont icon-yunhangzhong"></i>运行中</span>
|
<span className="statusTag running">
|
||||||
|
<i className="iconfont icon-yunhangzhong"></i>运行中
|
||||||
|
</span>
|
||||||
);
|
);
|
||||||
case "failure":
|
case "failure":
|
||||||
return (
|
return (
|
||||||
<span className="statusTag failed"><i className="iconfont icon-weitongguo"></i>未通过</span>
|
<span className="statusTag failed">
|
||||||
|
<i className="iconfont icon-weitongguo"></i>未通过
|
||||||
|
</span>
|
||||||
);
|
);
|
||||||
case "success":
|
case "success":
|
||||||
return (
|
return (
|
||||||
<span className="statusTag pass"><i className="iconfont icon-yitongguo"></i>已通过</span>
|
<span className="statusTag pass">
|
||||||
|
<i className="iconfont icon-yitongguo"></i>已通过
|
||||||
|
</span>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<span className="statusTag Preparing"><i className="iconfont icon-zhunbeizhong"></i>准备中</span>
|
<span className="statusTag Preparing">
|
||||||
|
<i className="iconfont icon-zhunbeizhong"></i>准备中
|
||||||
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clickRows(event,e){
|
||||||
|
props.history.push(`/projects/${projectsId}/ops/${e.number}/detail`);
|
||||||
|
}
|
||||||
const column = [
|
const column = [
|
||||||
{
|
{
|
||||||
title:'序号',
|
title: "序号",
|
||||||
dataIndex:"No",
|
dataIndex: "number",
|
||||||
key:"No",
|
key: "number",
|
||||||
width: "8%",
|
width: "8%",
|
||||||
render:(item,value,key)=>{
|
render: ( value, item, key) => {
|
||||||
return(
|
return <span>#{value}</span>;
|
||||||
<span>#{key+1}</span>
|
},
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title:'状态',
|
title: "状态",
|
||||||
dataIndex: "status",
|
dataIndex: "status",
|
||||||
key: "status",
|
key: "status",
|
||||||
width: "12%",
|
width: "12%",
|
||||||
render: (value, item, key) => {
|
render: (value, item, key) => {
|
||||||
return(renderTableStatus(value))
|
return renderTableStatus(value);
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title:'构建人',
|
title: "构建人",
|
||||||
dataIndex: "author",
|
dataIndex: "author",
|
||||||
key: "author",
|
key: "author",
|
||||||
width: "12%",
|
width: "12%",
|
||||||
align:"center"
|
align: "center",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title:'提交信息',
|
title: "提交信息",
|
||||||
dataIndex: "message",
|
dataIndex: "message",
|
||||||
key: "message",
|
key: "message",
|
||||||
width: "30%",
|
width: "30%",
|
||||||
|
@ -171,70 +228,91 @@ export default ((props)=>{
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div>
|
<div>
|
||||||
{ meg.branch && <span className="mr10 color-grey-8"><i className="iconfont icon-fenzhi1 font-16 mr5"></i>分支{meg.branch}</span>}
|
{meg.branch && (
|
||||||
|
<span className="mr10 color-grey-8">
|
||||||
|
<i className="iconfont icon-fenzhi1 font-16 mr5"></i>分支
|
||||||
|
{meg.branch}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
{meg.sha && <span className="color-orange">{meg.sha}</span>}
|
{meg.sha && <span className="color-orange">{meg.sha}</span>}
|
||||||
</div>
|
</div>
|
||||||
<AlignCenter>
|
<AlignCenter>
|
||||||
<Img src={meg.image} />
|
<Img src={meg.image} />
|
||||||
<div className="task-hide ml5" style={{maxWidth:"300px"}}>{meg.message}</div>
|
<div className="task-hide ml5" style={{ maxWidth: "300px" }}>
|
||||||
|
{meg.message}
|
||||||
|
</div>
|
||||||
</AlignCenter>
|
</AlignCenter>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)
|
);
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title:'开始时间',
|
title: "开始时间",
|
||||||
dataIndex: "started",
|
dataIndex: "started",
|
||||||
key: "started",
|
key: "started",
|
||||||
width: "15%",
|
width: "15%",
|
||||||
render: (value, item, key) => {
|
render: (value, item, key) => {
|
||||||
return (
|
return <span>{value || "--"}</span>;
|
||||||
<span>{value || "--"}</span>
|
},
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title:'运行时间',
|
title: "运行时间",
|
||||||
dataIndex: "timestamp",
|
dataIndex: "timestamp",
|
||||||
key: "timestamp",
|
key: "timestamp",
|
||||||
width: "15%",
|
width: "15%",
|
||||||
render: (value, item, key) => {
|
render: (value, item, key) => {
|
||||||
return (
|
return <span>{value || value === 0 ? `${value}s` : "--"}</span>;
|
||||||
<span>{value || value === 0 ? `${value}s` : "--"}</span>
|
},
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title:'操作',
|
title: "操作",
|
||||||
dataIndex: "operation",
|
dataIndex: "operation",
|
||||||
key: "operation",
|
key: "operation",
|
||||||
render: (value, item, key) => {
|
render: (value, item, key) => {
|
||||||
return(renderStatusBtn(item.status));
|
return renderStatusBtn(item.status, item.number);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
];
|
||||||
return (
|
return (
|
||||||
<div className="listPart">
|
<div className="listPart">
|
||||||
<FlexAJ>
|
<FlexAJ>
|
||||||
{renderStatus()}
|
{renderStatus()}
|
||||||
<span>
|
<span>
|
||||||
<Blueback className="mr30">手动创建</Blueback>
|
<Blueback className="mr30">手动创建</Blueback>
|
||||||
<span className="mr30"><i className="iconfont icon-fenzhi1 font-16 mr5 color-blue"></i>分支</span>
|
<span className="mr30">
|
||||||
<span><i className="iconfont icon-biaoqian3 font-16 mr5 color-blue"></i>标签</span>
|
<i className="iconfont icon-fenzhi1 font-16 mr5 color-blue"></i>分支
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<i className="iconfont icon-biaoqian3 font-16 mr5 color-blue"></i>
|
||||||
|
标签
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</FlexAJ>
|
</FlexAJ>
|
||||||
<Table
|
<Table
|
||||||
|
onRow={(record,index)=>{
|
||||||
|
return{
|
||||||
|
onClick:(event)=>clickRows(event,record)
|
||||||
|
}
|
||||||
|
}}
|
||||||
columns={column}
|
columns={column}
|
||||||
className="normalTable"
|
className="normalTable"
|
||||||
dataSource={data}
|
dataSource={data}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
|
loading={tableLoading}
|
||||||
></Table>
|
></Table>
|
||||||
{
|
{total > LIMIT ? (
|
||||||
total > LIMIT ?
|
<div style={{ textAlign: "center", margin: "30px 50px" }}>
|
||||||
<div style={{textAlign:'center',margin:"30px 50px"}}>
|
<Pagination
|
||||||
<Pagination showQuickJumper defaultCurrent={page} total={total} pageSize={LIMIT} onChange={ChangePage}></Pagination>
|
showQuickJumper
|
||||||
</div>:""
|
defaultCurrent={page}
|
||||||
}
|
total={total}
|
||||||
|
pageSize={LIMIT}
|
||||||
|
onChange={ChangePage}
|
||||||
|
></Pagination>
|
||||||
</div>
|
</div>
|
||||||
)
|
) : (
|
||||||
})
|
""
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
|
@ -70,6 +70,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.ant-modal-close{
|
||||||
|
top:7px;
|
||||||
|
}
|
||||||
// 列表
|
// 列表
|
||||||
.listPart{
|
.listPart{
|
||||||
.statusTag{
|
.statusTag{
|
||||||
|
|
|
@ -1,33 +1,90 @@
|
||||||
import React from 'react';
|
import React, { useEffect, useState , useRef , useContext } from "react";
|
||||||
import './ops.scss';
|
import "./ops.scss";
|
||||||
import { FlexAJ, AlignCenter } from '../Component/layout';
|
import { FlexAJ, AlignCenter } from "../Component/layout";
|
||||||
import { Tags } from '../Component/OpsStatus';
|
import { Tags } from "../Component/OpsStatus";
|
||||||
import SplitPane from 'react-split-pane';
|
import SplitPane from "react-split-pane";
|
||||||
import LeftPanel from './OpsDetailLeftpanel';
|
import LeftPanel from "./OpsDetailLeftpanel";
|
||||||
import RightPanel from './OpsDetailRightpanel';
|
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 (()=>{
|
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 (
|
return (
|
||||||
|
<Spin spinning={spinning}>
|
||||||
<div className="opsDetailPanel">
|
<div className="opsDetailPanel">
|
||||||
<FlexAJ className="opsInfos">
|
<FlexAJ className="opsInfos">
|
||||||
<AlignCenter>
|
<AlignCenter>
|
||||||
<span>#1</span>
|
<span>#{data && data.number}</span>
|
||||||
<span className="ml10">将分支“221063-improve-buy-ci-minutes-link”合并到“221063-improve-buy-ci-minutes-link"</span>
|
<span className="ml10">{data && data.message}</span>
|
||||||
{Tags(1)}
|
{Tags(`${data && data.status}`)}
|
||||||
</AlignCenter>
|
</AlignCenter>
|
||||||
<a style={{color:"#ddd"}}><i className="iconfont icon-yiguanbi font-15 mr5"></i>退出</a>
|
<Link style={{ color: "#ddd" }} to={`/projects/${projectId}/ops/list`}>
|
||||||
|
<i className="iconfont icon-yiguanbi font-15 mr5"></i>退出
|
||||||
|
</Link>
|
||||||
</FlexAJ>
|
</FlexAJ>
|
||||||
<div className="opsSection">
|
<div className="opsSection">
|
||||||
<SplitPane className="outer-split-pane" split="vertical" minSize={468} maxSize={-350} defaultSize="40%">
|
<SplitPane
|
||||||
|
className="outer-split-pane"
|
||||||
|
split="vertical"
|
||||||
|
minSize={468}
|
||||||
|
maxSize={-350}
|
||||||
|
defaultSize="40%"
|
||||||
|
>
|
||||||
<section className="leftSection">
|
<section className="leftSection">
|
||||||
<LeftPanel />
|
<LeftPanel data={data} repeatSet={repeatSet}/>
|
||||||
</section>
|
</section>
|
||||||
<section className="rightSection">
|
<section className="rightSection">
|
||||||
<RightPanel />
|
<RightPanel data={data} />
|
||||||
</section>
|
</section>
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
</Spin>
|
||||||
});
|
);
|
||||||
|
};
|
||||||
|
|
|
@ -438,12 +438,12 @@ class Detail extends Component {
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
{/* <li className={url.indexOf("/ops") > -1 ? "active" : ""}>
|
<li className={url.indexOf("/ops") > -1 ? "active" : ""}>
|
||||||
<Link to={{ pathname: `/projects/${projectsId}/ops`, state }}>
|
<Link to={{ pathname: `/projects/${projectsId}/ops`, state }}>
|
||||||
<i className="iconfont icon-gongzuoliu font-13 mr8"></i>工作流
|
<i className="iconfont icon-gongzuoliu font-13 mr8"></i>工作流
|
||||||
{projectDetail && projectDetail.ops_count ? <span>{projectDetail.ops_count}</span> : ""}
|
{projectDetail && projectDetail.ops_count ? <span>{projectDetail.ops_count}</span> : ""}
|
||||||
</Link>
|
</Link>
|
||||||
</li> */}
|
</li>
|
||||||
<li className={(url.indexOf("/Milepost") > -1 || url.indexOf("meilpost") > -1) ? "active" : ""}>
|
<li className={(url.indexOf("/Milepost") > -1 || url.indexOf("meilpost") > -1) ? "active" : ""}>
|
||||||
<Link to={{ pathname: `/projects/${projectsId}/orders/Milepost`, state }}>
|
<Link to={{ pathname: `/projects/${projectsId}/orders/Milepost`, state }}>
|
||||||
<img alt="" src={img_milepost} width="16" />里程碑
|
<img alt="" src={img_milepost} width="16" />里程碑
|
||||||
|
|
|
@ -26,7 +26,7 @@ export function getDateTime(value, dataformat) {
|
||||||
return date.format(dataformat);
|
return date.format(dataformat);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Time(UTCtiem) {
|
export function Time(UTCtiem) {
|
||||||
var dateTime = new Date(UTCtiem);
|
var dateTime = new Date(UTCtiem);
|
||||||
|
|
||||||
var year = dateTime.getFullYear();
|
var year = dateTime.getFullYear();
|
||||||
|
|
Loading…
Reference in New Issue