forgeplus-react/src/forge/Main/tree/Index.jsx

400 lines
16 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React , { useEffect , useState } from 'react';
import CopyTool from '../../Component/CopyTool';
import { truncateCommitId } from '../../common/util';
import { Link } from 'react-router-dom';
import { getImageUrl , turnbar } from 'educoder';
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, getDetail } = props;
useEffect(()=>{
// 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)=> (
<Menu>
<Menu.Item key={'0'}><a href={zip_url}>ZIP</a></Menu.Item>
<Menu.Item key={'1'}><a href={tar_url}>TAR.GZ</a></Menu.Item>
</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={
// 如果项目是fork的项目pr自动指向源仓库否则指向本仓库默认分支
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=>{
if(result){
setList(result.data);
}
setIsSpin(false);
}).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>
<p className="branchSort">{item.branch_type === "default" ? "默认分支" : item.branch_type==="protected"?"保护分支":"其它分支"}</p>
{
item.list && item.list.length>0 &&
<ul className="treeUl">
{
item.list.map((i,k)=>{
let last_commit = i.last_commit;
return(
<li>
<div className="treeinfo">
<Link to={`/${owner}/${projectsId}/tree/${turnbar(i.name)}`} className="task-hide">{i.name}</Link>
<div>
{
last_commit && last_commit.committer && last_commit.committer.id?
<Link to={`/${ last_commit.committer.login}`}>
<img style={{borderRadius:"50%"}} src={getImageUrl(`/${ last_commit.committer.image_url}`)} alt=""/>
<span className="mr3 color-grey-3" style={{fontWeight:"500"}}>{last_commit && last_commit.committer && last_commit.committer.name}</span>
</Link>
:
<React.Fragment>
<img style={{borderRadius:"50%"}} src={getImageUrl(`/${ last_commit.committer.image_url}`)} alt=""/>
<span className="mr3 color-grey-3" style={{fontWeight:"500"}}>{last_commit && last_commit.committer && last_commit.committer.name}</span>
</React.Fragment>
}
<span className="color-grey-3">更新于{last_commit && last_commit.time_from_now}</span>
</div>
</div>
<div className="treecopy">
<div>
<span>
<img src={Tree} alt="sha" width={"16px"}/>
<Link to={`/${owner}/${projectsId}/commits/${truncateCommitId(last_commit && last_commit.sha)}`}>{truncateCommitId(last_commit && last_commit.sha)}</Link>
<input type="text" id={`value${key}${k}`} value={`${truncateCommitId(last_commit && last_commit.sha)}`}/>
</span>
<CopyTool beforeText="复制commit id" afterText="复制成功" inputId={`value${key}${k}`}/>
</div>
</div>
<div className="treeabout">
{
(isManager || isDeveloper) && (projectDetail && projectDetail.type!==2) &&
<Link to={
// 如果项目是fork的项目pr自动指向源仓库否则指向本仓库默认分支
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>
</Dropdown>
</div>
</li>
)
})
}
</ul>
}
</React.Fragment>
)
})
} */}
</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>
)
}
export default Index;