merge 分支

This commit is contained in:
caishi 2023-09-01 15:01:19 +08:00
parent dccc4e118a
commit 6aaa67f240
2 changed files with 401 additions and 15 deletions

View File

@ -3,23 +3,60 @@ import CopyTool from '../../Component/CopyTool';
import { truncateCommitId } from '../../common/util';
import { Link } from 'react-router-dom';
import { getImageUrl , turnbar } from 'educoder';
import { Dropdown , Menu , Spin } from 'antd';
import Nodata from '../../Nodata';
import { Button, Dropdown , Input, Menu , message, Modal, Select, Spin, Table, Tooltip } from 'antd';
import Modals from '../../Component/PublicModal/Index';
import './Index.scss';
import Tree from '../img/tree.png';
import Axios from 'axios';
function Index(props) {
const [ reload, setReload] = useState(undefined);
const [ list , setList ] = useState([]);
const [ isSpin , setIsSpin ] = useState(true);
//
const [visible, setVisible] = useState(false);
//
const [addBranch, setAddBranch] = useState(false);
const [newBranchName, setNewBranchName] = useState(undefined);
const [newFormBranchName, setNewFormBranchName] = useState(undefined);
const [newBranchError, setNewBranchError] = useState(undefined);
const [branchList, setBranchList] = useState([]);//
//
const [name, setName] = useState(undefined);
//
const [current, setCurrent] = useState(1);
const [total, setTotal] = useState(0);
const [pageSize, setPageSize] = useState(10);
const { projectsId , owner } = props.match.params;
const { isManager , isDeveloper , projectDetail , defaultBranch } = props;
const { isManager , isDeveloper , projectDetail , defaultBranch, getDetail } = props;
useEffect(()=>{
getList();
},[])
// getList();
const url = `/v1/${owner}/${projectsId}/branches.json`;
Axios.get(url,{
params:{
keyword: name,
page: current,
limit: pageSize
}
}).then(result=>{
if(result){
const {branches, total_count} = result.data;
setTotal(total_count)
setList(branches);
setNewFormBranchName(branches[0].name);
}
setIsSpin(false);
}).catch(error=>{setIsSpin(false);})
},[name, reload, pageSize, current])
useEffect(()=>{
if(projectDetail && document.title.indexOf('所有分支-') === -1){
const { author, name} = projectDetail;
document.title = `所有分支-${author.name}/${name}`;
}
}, [projectDetail])
const menu =(zip_url,tar_url)=> (
@ -29,6 +66,105 @@ function Index(props) {
</Menu>
)
const columns = [
{
title: "分支名称",
dataIndex: "name",
key: 1,
ellipsis: true,
className: "branchNameColumn",
render: (txt, item) => {
return (
<Link to={`/${owner}/${projectsId}/tree/${turnbar(txt)}`} onClick={()=>{window.scrollTo(0,0)}} className="task-hide">{txt}</Link>
)
}
},
{
title: "更新信息",
dataIndex: "commit",
key: 3,
ellipsis: true,
width: "240px",
render: (txt, item) => {
return (
<div className='treeinfo'>
{
txt && txt.committer && txt.committer.id?
<Link to={`/${ txt.committer.login}`}>
<img style={{borderRadius:"50%"}} src={getImageUrl(`/${ txt.committer.image_url}`)} alt="" className='branchLastUpdateUserImage'/>
<span style={{fontWeight:"500"}}>{txt && txt.committer && txt.committer.name}</span>
</Link>
:
<React.Fragment>
<img style={{borderRadius:"50%"}} src={getImageUrl(`/${ txt.committer.image_url}`)} alt="" className='branchLastUpdateUserImage'/>
<span style={{fontWeight:"500"}}>{txt && txt.committer && txt.committer.name}</span>
</React.Fragment>
}
<span className="color-grey-3">更新于{txt && txt.time_ago}</span>
</div>
)
}
},
{
title: "commit信息",
dataIndex: "commit_id",
key: 4,
ellipsis: true,
width: "250px",
render: (txt, item) => {
return <div className="treecopy">
<div className='treeCopyBox'>
<span>
<img src={Tree} alt="sha" width={"16px"}/>
<Link to={`/${owner}/${projectsId}/commits/${truncateCommitId(txt)}`}>{truncateCommitId(txt)}</Link>
<input type="text" id={`value${truncateCommitId(txt)}`} value={`${truncateCommitId(txt)}`}/>
</span>
<CopyTool beforeText="复制commit id" afterText="复制成功" inputId={`value${truncateCommitId(txt)}`}/>
</div>
</div>
}
},
{
title: "分支类型",
dataIndex: "default_branch",
key: 5,
width: "150px",
render: (txt, item) => {
return isManager ? txt === item.name ? <a className='btn-83' style={{width: '93px'}}>默认分支</a> : <Select value={1} onChange={(value)=>{updateDefaultBranch(value, item)}}>
<Select.Option value={0}>默认分支</Select.Option>
<Select.Option value={1}>普通分支</Select.Option>
</Select> : txt === item.name ? '默认分支' : '普通分支'
}
},
{
title: "操作",
dataIndex: "name",
key: 6,
align: "center",
width: "320px",
className: "branchActionColumn",
render: (txt, item) => {
return (
<div className="treeabout">
{
(isManager || isDeveloper) && (projectDetail && projectDetail.type!==2) &&
<Tooltip title={`${txt}为源分支创建一个合并请求`}><Link to={
// forkpr
projectDetail && projectDetail.fork_info?
`/${projectDetail.fork_info.fork_project_user_login}/${projectDetail.fork_info.fork_project_identifier}/compare/${turnbar(txt || defaultBranch)}...${owner + ':' + turnbar(txt)}`
:`/${owner}/${projectsId}/compare/${turnbar(defaultBranch)}...${turnbar(txt)}`
} className="btn-83">+ 合并请求</Link></Tooltip>
}
<Dropdown overlay={menu(item.zip_url,item.tar_url)} trigger={['click']} placement="bottomRight">
<a className="btn-83" style={{width: '76px'}}>下载<i className="iconfont icon-sanjiaoxing-down font-14"></i></a>
</Dropdown>
{isManager && (txt === item.default_branch ? <Tooltip title={`该分支为默认分支,不支持删除操作`}><Button style={{width: '76px'}} disabled>删除</Button></Tooltip> : <Button className='f6_bor_but' style={{width: '76px'}} onClick={()=>{setVisible(item)}}>删除</Button>)}
</div>
)
}
}
];
function getList() {
const url = `/${owner}/${projectsId}/branches_slice.json`;
Axios.get(url).then(result=>{
@ -39,10 +175,121 @@ function Index(props) {
}).catch(error=>{setIsSpin(false);})
}
//
function deleteLabel(){
Axios.delete(`/v1/${owner}/${projectsId}/branches/${visible.name}.json`).then(res=>{
if(res && res.data){
if(current>1 && (list && list.length===1)){
setCurrent(current-1);
}else{
setReload(Math.random());
}
setVisible(false);
!res.data.status && message.success('删除成功');
}
})
}
//
function createBranch(){
setNewBranchError(undefined);
if(!newBranchName){
setNewBranchError("请输入新建分支名称");
}else{
Axios.post(`/v1/${owner}/${projectsId}/branches.json`, {
new_branch_name: newBranchName,
old_branch_name: newFormBranchName
}).then(res=>{
if(res && res.data){
//
getDetail();
setReload(Math.random());
setAddBranch(false);
message.success('新建分支成功');
}
})
}
}
//
function checkNewBranchName(e) {
setNewBranchError(undefined);
const value = e.target.value;
if (value) {
const str = '^*';
let reg = /\\/g;
if(reg.test(value)){
setNewBranchError('分支名不能包含下列任何字符:^ * \\ ');
}else{
for(var i=0;i<str.length;i++){
let s = str[i];
if(value.indexOf(s) > -1){
setNewBranchError('分支名不能包含下列任何字符:^ * \\ ');
return;
}else{
setNewBranchName(e.target.value);
}
}
}
}
}
//
function updateDefaultBranch(value, info){
Axios.patch(`/v1/${owner}/${projectsId}/branches/update_default_branch.json`,{
name: info.name
}).then(res=>{
if(res && res.data){
getDetail();
setReload(Math.random());
!res.data.status && message.success('操作成功');
}
})
}
// pagesize
function onShowSizeChange(current, pageSize){
window.scrollTo(0, 0);
setCurrent(1);
setPageSize(pageSize);
}
//
function changePage(page, pageSize){
window.scrollTo(0, 0);
setCurrent(page);
}
//
function addBranchBtn(){
setAddBranch(true);
const url = `/v1/${owner}/${projectsId}/branches/all.json`;
Axios.get(url).then(result=>{
if(result){
setBranchList(result.data);
}
}).catch(error=>{console.log('error', error);})
}
return(
<Spin spinning={isSpin}>
<div style={{paddingTop:"10px",minHeight:"400px",paddingBottom:"30px"}}>
{
<div className='branchListActionBox'>
<Input.Search placeholder='请输入分支名称' style={{width: '354px'}} className='searchBranch' onSearch={(value)=>{setCurrent(1);setName(value)}} allowClear/>
<div>
{isManager && <Link to={`/${owner}/${projectsId}/settings/branches`} className="btn-83" style={{width: '102px'}}><i className="iconfont icon-xuanzhongjibenshezhiicon font-14 mr5"></i>分支设置</Link>}
{(isManager || isDeveloper) && (projectDetail && projectDetail.type!==2) && <Button type='primary' onClick={addBranchBtn}>+ 新建分支</Button>}
</div>
</div>
<Table
rowKey={'name'}
className='branchListTable'
dataSource={list}
columns={columns}
pagination={{current: current, pageSize: pageSize, total: total, showSizeChanger: true, onShowSizeChange:onShowSizeChange, showQuickJumper: true, onChange: changePage}}
>
</Table>
{/* {
list && list.length>0 && list.map((item,key)=>{
return(
<React.Fragment>
@ -86,7 +333,12 @@ function Index(props) {
<div className="treeabout">
{
(isManager || isDeveloper) && (projectDetail && projectDetail.type!==2) &&
<Link to={`/${owner}/${projectsId}/compare/${turnbar(defaultBranch)}...${turnbar(i.name)}`} className="btn-83">+ 合并请求</Link>
<Link to={
// forkpr
projectDetail && projectDetail.fork_info?
`/${projectDetail.fork_info.fork_project_user_login}/${projectDetail.fork_info.fork_project_identifier}/compare/${turnbar(i.name || defaultBranch)}...${owner + ':' + turnbar(i.name)}`
:`/${owner}/${projectsId}/compare/${turnbar(defaultBranch)}...${turnbar(i.name)}`
} className="btn-83">+ 合并请求</Link>
}
<Dropdown overlay={menu(i.zip_url,i.tar_url)} trigger={['click']} placement="bottomRight">
<a className="btn-83 ml15">下载<i className="iconfont icon-sanjiaoxing-down font-14"></i></a>
@ -101,11 +353,47 @@ function Index(props) {
</React.Fragment>
)
})
}
{
list && list.length === 0 && <Nodata _html="暂无数据"/>
}
} */}
</div>
<Modals
title="删除分支"
onCancel={()=>{setVisible(false)}}
visible={Boolean(visible)}
width="456px"
btn={
<div>
<Button size={"large"} onClick={()=>{setVisible(false)}}>取消</Button>
<Button type={'danger'} ghost size={"large"} onClick={deleteLabel}>确认删除</Button>
</div>
}
>
<div className="contents mb30" style={{justifyContent: 'flex-start'}}>
<i className='iconfont icon-shanchu_tc_icon1 font-36 mr18' style={{color: '#ca0002'}}></i>
<p style={{color: '#333'}}>确定删除{visible.name}分支?</p>
</div>
</Modals>
<Modal
title="新建分支"
onCancel={()=>{setAddBranch(false)}}
visible={addBranch}
width="388px"
footer={
<Button type={'primary'} style={{width:'334px', height: '42px'}} onClick={createBranch}>确认新建</Button>
}
className="createNewBranchModal"
>
<div className="mb30" style={{justifyContent: 'flex-start'}}>
<div className='font-15 createBranchLabel'>分支名称</div>
<Input placeholder='请输入分支名称' className='createBranchInput' onChange={checkNewBranchName} maxLength={100}/>
<div className='newBranchError mt5'>{newBranchError}</div>
<div className='font-15 createBranchLabel mt20'>分支起点</div>
<Select showSearch style={{width: '340px'}} className='createBranchInput' placeholder="请选择分支起点" onChange={(value)=>{setNewFormBranchName(value)}} getPopupContainer={triggerNode => triggerNode.parentNode}>
{branchList && branchList.map((item, index)=>{
return <Select.Option value={item.name} key={index}>{item.name}</Select.Option>
})}
</Select>
</div>
</Modal>
</Spin>
)
}

View File

@ -1,3 +1,98 @@
.treeinfo{
.branchLastUpdateUserImage{
height: 20px;
width: 20px;
margin-right: 5px;
}
}
.f6_bor_but, .f6_bor_but:focus{
color:#f60011;
background-color:rgba(196, 0, 14, 0.09);
border:1px solid #f60011;
border-radius:5px;
&:hover{
color:#f60011;
background-color:rgba(196, 0, 14, 0.18);
border-color:#ff727c;
}
&:active{
color:#f60011;
background-color:rgba(196, 0, 14, 0.22);
border-color:#f60011;
}
}
.branchListTable{
.ant-table-thead > tr > th{
border-top: 1px solid rgba(42, 97, 255, 0.23);
border-bottom: 1px solid rgba(42, 97, 255, 0.23) !important;
background-color:#fafcff;
}
.ant-table-column-title{
color:#40424a;
}
.ant-table-thead > tr:first-child > th:last-child{
border-right: 1px solid rgba(42, 97, 255, 0.23);
}
.ant-table-thead > tr:first-child > th:first-child{
border-left: 1px solid rgba(42, 97, 255, 0.23);
}
.ant-table-tbody > tr > td{
border-color: #eeeeee;
padding: 18.5px 16px;
}
.branchNameColumn{
border-left: 1px solid #d0d0d0;
}
.branchActionColumn{
border-right: 1px solid #d0d0d0;
}
.task-hide{
color: $primary-color;
}
.ant-table-empty .ant-table-placeholder{
border-left: 1px solid #d0d0d0;
border-right: 1px solid #d0d0d0;
}
}
.branchListActionBox{
display: flex;
justify-content: space-between;
align-items: center;
margin: 20px 0 30px;
}
.createNewBranchModal{
.ant-modal-title{
text-align: left;
color:#333333;
font-size:17px;
font-weight: normal !important;
}
.ant-modal-header{
border-bottom: none;
background-color:rgba(70, 106, 255, 0.05);
}
.ant-modal-close{right: 5px;}
.ant-modal-close-x{font-size: 26px;}
.ant-modal-footer{
border-top: none;
text-align: center;
padding-bottom: 53px;
}
.ant-modal-content{
background-image:linear-gradient(359.37deg,#ebf3ff 0%,#f8fbff 55.01%,#f1f5ff 100%);
}
.createBranchLabel{
color:#27282d;
margin-bottom: 12px;
}
.createBranchInput, .createBranchInput .ant-select-selection{
border-color:#9eaacb;
background-color: transparent !important;
}
.newBranchError{
color:#df0002;
}
}
.branchSort{
font-weight: 500;
color: #333333;
@ -49,14 +144,16 @@
}
.treecopy{
flex:1;
display: flex;
justify-content: center;
display: flex;
// justify-content: center;
&>div:not(.treeCopyBox){
position: relative;
}
&>div{
height: 32px;
background: #FAFBFC;
border-radius: 4px;
border: 1px solid #D0D0D0;
position: relative;
z-index: 1;
display: flex;
align-items: center;
@ -64,6 +161,7 @@
padding:0px 15px;
border-right: 1px solid rgba(153, 153, 153, 0.4);
height: 100%;
width: 140px;
img{
margin-right: 4px;
}