分支设置

This commit is contained in:
caishi 2020-12-04 19:26:14 +08:00
parent 57363291f1
commit 0d1345f957
6 changed files with 384 additions and 79 deletions

View File

@ -63,7 +63,7 @@ class IndexItem extends Component {
<span className="p-r-detail">
{/* <span><label>浏览量:</label>{item.visits}</span> */}
{/* {item.category && item.category.id && <span>{item.category.name}</span>} */}
{item.last_update_time ? <span ><label>更新于</label>{item.time_ago}</span> : ""}
{item.last_update_time ? <span><label>更新于</label>{item.time_ago}</span> : ""}
{item.language && item.language.id ? <span className="color-grey-3">{item.language.name}</span> : ""}
</span>
</div>

View File

@ -2,8 +2,11 @@ import React , { useEffect, useState } from 'react';
import SelectBranch from '../Branch/Select';
import Title from '../Component/Title';
import styled from 'styled-components';
import { Blueline , FlexAJ , NumUl , GreenUnder , AlignCenter , WhiteBack } from '../Component/layout';
import { AlignCenter , WhiteBack , FlexAJ } from '../Component/layout';
import axios from 'axios';
import { Button, Select } from 'antd';
import { getBranch } from '../GetData/getData';
const Div = styled.div`{
padding:20px 30px;
min-height:500px;
@ -11,6 +14,13 @@ const Div = styled.div`{
export default ((props)=>{
const [ branch , setBranch ] = useState("master");
const [ branchList , setBranchList ] = useState(undefined);
const [ protectBranch , setProtectBranch ] = useState(undefined);
const [ protectBranchList , setProtectBranchList ] = useState(undefined);
const [ count , setCount ] = useState(0);
const [ page , setPage ] = useState(1);
const { projectsId , owner } = props.match.params;
const projectDetail = props.projectDetail;
@ -21,7 +31,36 @@ export default ((props)=>{
}
},[defaultBranch]);
useEffect(()=>{
if(owner){
getBranchs(projectsId,owner);
getProtectBranchList(owner,projectsId);
}
},[owner])
// 获取已经设置过分支保护的分支列表
function getProtectBranchList(owner,repo){
const url = `/${owner}/${repo}/protected_branches.json`;
axios.get(url,{
params:{
page,limit:15
}
}).then(result=>{
if(result){
setCount(result.data.total_count);
setProtectBranchList(result.data.protected_branches);
}
}).catch(error=>{})
}
// 获取分支列表(下拉、过滤掉已经设置过分支保护的分支)
async function getBranchs(id,owner){
let re = await getBranch(id,owner);
setBranchList(re);
}
// 设为默认分支
function resetSetting(){
const url = `/${owner}/${projectsId}.json`;
axios.put(url, {
@ -36,15 +75,18 @@ export default ((props)=>{
console.log(error);
});
}
function settingRule(){
props.history.push(`/projects/${owner}/${projectsId}/setting/branch/new`);
// 跳转
function settingRule(protectBranch){
props.history.push(`/projects/${owner}/${projectsId}/setting/branch/${protectBranch}`);
}
return(
<WhiteBack>
<Title>分支设置</Title>
<Div>
<div className="pb20" style={{borderBottom:"1px solid #eee"}}>
<p className="color-grey-3 mb10">默认分支</p>
<div className="pb20" style={{borderBottom:"1px dashed #eee"}}>
<p className="color-grey-3 mb10 font-18">默认分支</p>
<p className="mb10">默认分支被视作为代码库中的基本分支是所有克隆代码提交合并请求的目标分支</p>
<AlignCenter>
<SelectBranch
@ -58,16 +100,45 @@ export default ((props)=>{
<a className="color-blue ml20" onClick={()=>resetSetting()}>设为默认分支</a>
</AlignCenter>
</div>
<div>
<FlexAJ className="pt20">
<span className="color-grey-3">保护分支规则</span>
<Blueline onClick={()=>settingRule()}>+&nbsp;新建规则</Blueline>
</FlexAJ>
<NumUl>
<li>限制分支的推送合并强制推送相关请去<GreenUnder onClick={()=>{props.history.push(`/projects/${owner}/${projectsId}/setting`)}}>仓库设置</GreenUnder></li>
<li>一个分支同时只能有一个保护分支规则生效越早创建的规则优先级越高</li>
<li>保护分支规则只影响状态是保护分支的分支常规分支只读分支都不影响</li>
</NumUl>
<div className="mt10">
<p className="color-grey-3 mb10 font-18">分支保护</p>
<AlignCenter>
<Select
showSearch
className="setHeight"
style={{ width: 240, height: 40 }}
placeholder="选择分支"
optionFilterProp="children"
filterOption={(input, option) =>
option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
onChange={setProtectBranch}
>
{
branchList && branchList.length > 0 && branchList.map((item,key)=>{
return(
<Select.Option value={item.name}>{item.name}</Select.Option>
)
})
}
</Select>
<a className="color-blue ml20" onClick={()=>settingRule(protectBranch)}>设置分支保护</a>
</AlignCenter>
{
protectBranchList && protectBranchList.length > 0 &&
<div className="protectBranchList">
{
protectBranchList.map((item,key)=>{
return(
<FlexAJ>
<span>{item.branch_name}</span>
<Button onClick={()=>settingRule(item.branch_name)}>编辑</Button>
</FlexAJ>
)
})
}
</div>
}
</div>
</Div>
</WhiteBack>

View File

@ -1,21 +1,20 @@
import React, { forwardRef, useCallback, useState, useEffect } from "react";
import { Form, Input, Select, Button } from "antd";
import { Form, InputNumber , Select, Button , Checkbox , Radio } from "antd";
import Title from "../Component/Title";
import { WhiteBack, Cancel } from "../Component/layout";
import styled from "styled-components";
import { Cancel } from "../Component/layout";
import axios from "axios";
const { Option } = Select;
const Div = styled.div`
{
padding: 20px 30px;
}
`;
export default Form.create()(
forwardRef(({ form, match, history }) => {
forwardRef(({ form, match, history , showNotification }) => {
const [ protect , setProtect ] = useState(false);
const [ protects , setProtects ] = useState(false);
const [ mergeOptions , setMergeOptions ] = useState(undefined);
const [ approveOptions , setApproveOptions ] = useState(undefined);
const [ options , setOptions ] = useState(undefined);
const [ list, setList ] = useState(undefined);
const { projectsId, owner } = match.params;
const { getFieldDecorator, validateFields } = form;
const { projectsId, owner , branch } = match.params;
const { getFieldDecorator, validateFields , setFieldsValue } = form;
useEffect(() => {
const url = `/${owner}/${projectsId}/collaborators.json`;
@ -25,23 +24,65 @@ export default Form.create()(
.catch((error) => {});
}, []);
function getMember(list) {
return (
list &&
list.length > 0 &&
list.map((item, key) => {
return (
item.role !== "Manager" && (
<Option value={item.id}>{item.name}</Option>
)
);
})
);
useEffect(()=>{
if(branch){
//
getGuardDetail(owner,projectsId,branch);
}
},[branch]);
function getGuardDetail(owner,projectsId,branch){
const url = `/${owner}/${projectsId}/protected_branches/${branch}/edit.json`;
axios.get(url).then((result)=>{
if(result){
setProtect(result.data.protected);
setProtects(result.data.protected);
let protected_branch = result.data.protected_branch;
if(protected_branch){
let enable_push = protected_branch.enable_push_whitelist ? 2 : protected_branch.enable_push ? 1 : 0;
setOptions(enable_push);
setMergeOptions(protected_branch.enable_merge_whitelist);
setApproveOptions(protected_branch.enable_approvals_whitelist);
setFieldsValue({
enable_push,
...protected_branch
})
}
}
}).catch(error=>{});
}
function saveBranchRule() {
validateFields((error, values) => {
if (!error) {
let url = "";
if(protects){
// true
url = `/${owner}/${projectsId}/protected_branches/${branch}.json`;
}else{
//
url = `/${owner}/${projectsId}/protected_branches.json`;
}
axios({
method:!protects ? "post" : (protect ? "patch" : "delete"),
url,
params:
{
branch_name:branch,
enable_push:values.enable_push !== 0 ,
enable_push_whitelist:values.enable_push === 2 ? true : false,
...values
}
}).then(result=>{
if(result){
showNotification("保存成功!");
}
}).catch(error=>{})
}
});
}
@ -50,49 +91,202 @@ export default Form.create()(
(label, name, rules, widget, className, isRequired) => (
<div className={className}>
<span className={isRequired ? "required" : ""}>{label}</span>
<Form.Item>
{getFieldDecorator(name, { rules, validateFirst: true })(widget)}
<Form.Item >
{getFieldDecorator(name, { rules, validateFirst: true })(widget)}
</Form.Item>
</div>
),
[]
);
//
function changeProtect(e){
setProtect(e.target.checked);
}
// 线
function changeProtectOption(e){
setOptions(e.target.value);
}
//
function changeMergeOption(e){
setMergeOptions(e.target.checked);
}
//
function changeWhitelistUsernameOption(e){
setApproveOptions(e.target.checked);
}
return (
<WhiteBack>
<div style={{backgroundColor:"#fff"}}>
<Title>新建保护分支规则</Title>
<Div>
<Form>
<div style={{padding:"20px 30px"}}>
{helper(
"设置分支/通配符",
"sign",
[{ required: true, message: "请输入分支/通配符" }],
<Input placeholder="请输入分支名称或通配符规则" />,
"",
"branchProtect",
[],
<Checkbox checked={protect} onChange={changeProtect}>
启用分支保护<span className="color-grey-9 ml5 font-12">组织删除并限制Git推送和合并到分支</span>
</Checkbox>,
"setStyleRule"
)}
<p className="color-grey-8 mb20">
例如设置为master则对名称为master的分支生效设置为*-stable
release*则对名称符合此通配符的所有保护分支生效
</p>
{helper(
"可推送代码成员",
"psuhmember",
[{ required: true, message: "请选择可推送代码成员" }],
<Select placeholder="请选择仓库成员">
<Option value="0">请选择仓库成员</Option>
{getMember(list)}
</Select>,
"setSelectWidth"
)}
{helper(
"可合并Pull Request成员",
"pullmember",
[{ required: true, message: "请选择可合并Pull Request成员" }],
<Select placeholder="请选择仓库成员">
<Option value="0">请选择仓库成员</Option>
{getMember(list)}
</Select>,
"setSelectWidth"
)}
<div className="df pb30">
<div className="pl25 shortStyle">
{helper(
"",
"enable_push",
[],
<Radio.Group disabled={!protect} onChange={changeProtectOption}>
<Radio className="columsRadio" value={0}>
禁用推送<span className="color-grey-9 ml5 font-12">此分支不允许推送</span>
</Radio>
<Radio className="columsRadio" value={1}>
启用推送<span className="color-grey-9 ml5 font-12">任何拥有写访问权限的人将被允许推送到此分支(但不能强行推送)</span>
</Radio>
<Radio className="columsRadio" value={2}>
启用推送白名单<span className="color-grey-9 ml5 font-12">只有列入白名单的用户或团队才能被允许推送到此分支(但不能强行推送)</span>
</Radio>
</Radio.Group>,
""
)}
<div className="pl25 pt5 pb5 mb15">
{helper(
"",
"push_whitelist_usernames",
[],
<Select
mode="multiple"
placeholder="搜索用户"
style={{ width: '100%' }}
disabled={!protect || (options ===undefined || options !== 2)}
>
{list && list.map(item => (
<Select.Option key={item.id} value={item.login}>
{item.name}
</Select.Option>
))}
</Select>,
"setStyleRule"
)}
</div>
{helper(
"",
"enable_merge_whitelist",
[],
<Checkbox disabled={!protect} checked={mergeOptions} onChange={changeMergeOption}>
启用合并白名单<span className="color-grey-9 ml5 font-12">仅允许白名单用户或团队合并合并请求到此分支</span>
</Checkbox>,
"setStyleRule"
)}
<div className="pl25 pt5 pb5">
{helper(
"",
"merge_whitelist_usernames",
[],
<Select
mode="multiple"
placeholder="搜索用户"
style={{ width: '100%' }}
disabled={!protect || !mergeOptions}
>
{list && list.map(item => (
<Select.Option key={item.id} value={item.login}>
{item.name}
</Select.Option>
))}
</Select>,
"setStyleRule"
)}
</div>
{helper(
"",
"enable_status_check",
[],
<Checkbox disabled={!protect}>
启用状态检查
</Checkbox>,
"setStyleRule"
)}
<div style={{display:"flex",alignItems:"center"}}>
{helper(
"所需的批准数",
"required_approvals",
[],
<InputNumber min={0} style={{width:"140px"}}/>,
"inlineFlex"
)}
<span className="color-grey-9 ml5 font-12">只允许合并有足够审核人数的拉取请求</span>
</div>
{helper(
"",
"enable_approvals_whitelist",
[],
<Checkbox disabled={!protect} checked={approveOptions} onChange={changeWhitelistUsernameOption}>
批准仅限列入白名单的用户或团队<span className="color-grey-9 ml5 font-12">只有白名单用户或团队的审核才能计数 没有批准的白名单,任何有写访问权限的人的审核都将计数</span>
</Checkbox>,
"setStyleRule"
)}
<div className="pl25 pt5 pb5 mb15">
{helper(
"",
"approvals_whitelist_usernames",
[],
<Select
mode="multiple"
placeholder="搜索用户"
style={{ width: '100%' }}
disabled={!protect || !approveOptions}
>
{list && list.map(item => (
<Select.Option key={item.id} value={item.login}>
{item.name}
</Select.Option>
))}
</Select>,
"setStyleRule"
)}
</div>
{helper(
"",
"block_on_rejected_reviews",
[],
<Checkbox disabled={!protect}>
拒绝审核阻止了合并<span className="color-grey-9 ml5 font-12">如果官方审查人员要求作出改动即使有足够的批准合并也不允许</span>
</Checkbox>,
"setStyleRule"
)}
{helper(
"",
"dismiss_stale_approvals",
[],
<Checkbox disabled={!protect}>
取消过时的批准<span className="color-grey-9 ml5 font-12">当新的提交更改合并请求内容被推送到分支时旧的批准将被撤销</span>
</Checkbox>,
"setStyleRule"
)}
{helper(
"",
"require_signed_commits",
[],
<Checkbox disabled={!protect}>
需要签名提交
</Checkbox>,
"setStyleRule"
)}
{helper(
"",
"block_on_outdated_branch",
[],
<Checkbox disabled={!protect}>
如果拉取请求已经过时阻止合并<span className="color-grey-9 ml5 font-12">当头部分支落后基础分支时不能合并</span>
</Checkbox>,
"setStyleRule"
)}
</div>
<div className="df pb30 pt20">
<Button type="primary" onClick={saveBranchRule}>
保存
</Button>
@ -105,8 +299,9 @@ export default Form.create()(
取消
</Cancel>
</div>
</Div>
</WhiteBack>
</div>
</Form>
</div>
);
})
);

View File

@ -65,7 +65,7 @@ class Index extends Component {
</Link>
</p>
</li>
{/* <li
<li
className={
pathname.indexOf("setting/branch") > -1 ? "active" : ""
}
@ -76,7 +76,7 @@ class Index extends Component {
分支设置
</Link>
</p>
</li> */}
</li>
<li
className={pathname.indexOf("setting/tags") > -1 ? "active" : ""}
>
@ -120,7 +120,7 @@ class Index extends Component {
)}
></Route>
<Route
path="/projects/:owner/:projectsId/setting/branch/new"
path="/projects/:owner/:projectsId/setting/branch/:branch"
render={(props) => (
<BranchNew {...this.props} {...props} {...this.state} />
)}

View File

@ -187,7 +187,29 @@
width: 100%;
}
}
.shortStyle{
.setStyleRule{
height: 35px;
}
.columsRadio{
display: block;
height: 30px;
line-height: 30px;
}
.ant-row.ant-form-item{
margin-bottom: 0px;
}
}
.inlineFlex{
display: flex;
align-items: center;
& > span{
margin-right: 10px;
color: #666;
}
}
.setStyleRule{
height: 35px;
.ant-row.ant-form-item{
margin-bottom: 10px;
}
@ -196,4 +218,22 @@
.ant-select.ant-select-enabled{
width: 100%;
}
}
.setHeight{
.ant-select-selection,.ant-select-selection__rendered{
height: 40px;
line-height: 40px;
}
}
.protectBranchList{
border:1px solid #eee;
border-radius: 5px;
margin-top: 25px;
&>div{
padding:5px 15px;
border-bottom: 1px solid #eee;
}
&>div:last-child{
border-bottom: none;
}
}

View File

@ -4,7 +4,6 @@ import { AlignCenter } from '../Component/layout';
import axios from 'axios';
import Modals from '../Component/Modal';
import PasswordAuthority from '../Component/PasswordAuthority';
import { func } from 'prop-types';
const TIPS = "解除CI服务器绑定后您所有的项目构建数据将被清空。确定解除绑定";
function CIdispose(props){