Merge pull request 'FIX 合并测试分支代码' (#182) from pre_develop_dev into featrue_system_popup_notification

This commit is contained in:
jasder 2021-10-18 11:49:55 +08:00
commit e744714da0
34 changed files with 934 additions and 278 deletions

View File

@ -2456,7 +2456,7 @@ a.hoverLine:hover{
.color-grey-9 { .color-grey-9 {
color: #333333 !important; color: #999 !important;
} }
a:hover{ a:hover{

View File

@ -97,10 +97,10 @@ const ProjectIndex = Loadable({
loading: Loading, loading: Loading,
}); });
const CreateMerge = Loadable({ // const CreateMerge = Loadable({
loader: () => import('./forge/Merge/NewMerge'), // loader: () => import('./forge/Merge/NewMerge'),
loading: Loading, // loading: Loading,
}) // })
// 此处仅维护前端可能的一级路由,不用进行项目或者组织判断的字段。 // 此处仅维护前端可能的一级路由,不用进行项目或者组织判断的字段。
const keyWord = ["explore", "settings", "setting", "mulan", "wiki", "issues", "setting", "trending", "code", "projects", "pulls", "mine", "login", "register", "email", "export", "nopage", "404", "403", "500", "501", "search", "organize"]; const keyWord = ["explore", "settings", "setting", "mulan", "wiki", "issues", "setting", "trending", "code", "projects", "pulls", "mine", "login", "register", "email", "export", "nopage", "404", "403", "500", "501", "search", "organize"];
@ -279,7 +279,7 @@ class App extends Component {
} /> } />
{/* 项目PR */} {/* 项目PR */}
<Route path="/:owner/:projectsId/pulls/new" <Route path="/:owner/:projectsId/compare"
render={ render={
(props) => (<ProjectDetail {...this.props} {...props} {...this.state} />) (props) => (<ProjectDetail {...this.props} {...props} {...this.state} />)
} }

View File

@ -10,6 +10,10 @@ import ActivityItem from './ActivityItem';
import axios from 'axios'; import axios from 'axios';
const LIMIT = 15; const LIMIT = 15;
const ARRAY = [ const ARRAY = [
{
id:"",
name:'全部'
},
{ {
id:1, id:1,
name:'1天' name:'1天'
@ -32,10 +36,15 @@ class Activity extends Component{
constructor(props){ constructor(props){
super(props); super(props);
this.state={ this.state={
time:'30', time:undefined,
type:undefined, type:undefined,
state:undefined, state:undefined,
page:1, page:1,
pr_count:undefined,
new_pr_count:undefined,
close_issues_count:undefined,
open_issues_count:undefined,
pr_all_count:undefined,issues_count:undefined,
data:undefined, data:undefined,
project_trends:undefined, project_trends:undefined,
@ -63,8 +72,15 @@ class Activity extends Component{
this.setState({ this.setState({
data:result.data, data:result.data,
project_trends:result.data.project_trends, project_trends:result.data.project_trends,
isSpin:false isSpin:false,
pr_count:result.data.pr_count,
new_pr_count:result.data.new_pr_count,
close_issues_count:result.data.close_issues_count,
open_issues_count:result.data.open_issues_count,
pr_all_count:result.data.pr_all_count,
issues_count:result.data.issues_count,
}) })
window.scrollTo(0,0);
} }
}).catch(error=>{ }).catch(error=>{
console.log(error); console.log(error);
@ -74,19 +90,19 @@ class Activity extends Component{
// 切换周期 // 切换周期
changeTime=(e)=>{ changeTime=(e)=>{
this.setState({ this.setState({
time:e.key, time:e.key ==="item_0"?undefined:e.key,
isSpin:true isSpin:true
}) })
const { type,status,page } = this.state; const { type,status,page } = this.state;
this.getInfo(e.key,type,status,page); this.getInfo(e.key ==="item_0"?undefined:e.key,type,status,page);
} }
//筛选 //筛选
changeTrends=(type,status)=>{ changeTrends=(type,status)=>{
this.setState({ this.setState({
type,status type,status,page:1
}) })
const {time,page}=this.state; const {time}=this.state;
this.getInfo(time,type,status,page); this.getInfo(time,type,status,1);
} }
// 分页 // 分页
ChangePage=(page)=>{ ChangePage=(page)=>{
@ -108,12 +124,14 @@ class Activity extends Component{
</Menu> </Menu>
) )
render(){ render(){
const { time , data , page , project_trends , isSpin } = this.state; const { time , data , page , project_trends , isSpin , pr_count , new_pr_count , close_issues_count , open_issues_count , pr_all_count ,issues_count } = this.state;
let name = time ? ARRAY.filter(item=>item.id === parseInt(time)) :[{name:"全部"}];
let name = time && ARRAY.filter(item=>item.id === parseInt(time)) ; const first_per = pr_all_count > 0 ? `${parseFloat(pr_count/pr_all_count).toFixed(2)*100}%` :"50%";
const second_per = (parseInt(data && data.close_issues_count)/parseInt(data && data.issues_count)*100)+'%'; const second_per =pr_all_count > 0 ? `${parseFloat(new_pr_count/pr_all_count).toFixed(2)*100}%` :"50%";
const third_per = (parseInt(data && data.close_issues_count)/parseInt(data && data.issues_count)*100)+'%'; const third_per =issues_count > 0 ?`${parseFloat(close_issues_count/issues_count).toFixed(2)*100}%` :"50%";
const fourth_per = (parseInt(data && data.open_issues_count)/parseInt(data && data.issues_count)*100)+'%'; const fourth_per =issues_count > 0 ?`${parseFloat(open_issues_count/issues_count).toFixed(2)*100}%` :"50%";
return( return(
<div className="main"> <div className="main">
@ -122,7 +140,7 @@ class Activity extends Component{
<div className="orderInfo"> <div className="orderInfo">
<div> <div>
<div className="percentLine prPercent"> <div className="percentLine prPercent">
<p className="percent_purple" style={{width:'100%'}}></p> <p className="percent_purple" style={{width:first_per}}></p>
<p className="percent_green resetStyle" style={{width:`${second_per}`}}></p> <p className="percent_green resetStyle" style={{width:`${second_per}`}}></p>
</div> </div>
<span>{data && data.pr_all_count}合并请求</span> <span>{data && data.pr_all_count}合并请求</span>
@ -132,25 +150,25 @@ class Activity extends Component{
<p className="percent_red" style={{width:`${third_per}`}}></p> <p className="percent_red" style={{width:`${third_per}`}}></p>
<p className="percent_green" style={{width:`${fourth_per}`}}></p> <p className="percent_green" style={{width:`${fourth_per}`}}></p>
</div> </div>
<span>{data && data.issues_count}任务</span> <span>{data && data.issues_count}易修</span>
</div> </div>
</div> </div>
<ul className="percentBox"> <ul className="percentBox">
<li> <li>
<span className="purple">{data && data.pr_count}</span> <span className="purple">{data && data.pr_count}</span>
<span className="change" onClick={()=>this.changeTrends("PullRequest","close")}>已处理的合并请求</span> <span className="change" onClick={()=>this.changeTrends("PullRequest","delay")}>已处理的合并请求</span>
</li> </li>
<li> <li>
<span className="green">{data && data.new_pr_count}</span> <span className="green">{data && data.new_pr_count}</span>
<span className="change" onClick={()=>this.changeTrends("PullRequest","create")}>未处理的合并请求</span> <span className="change" onClick={()=>this.changeTrends("PullRequest","not_delay")}>未处理的合并请求</span>
</li> </li>
<li> <li>
<span className="red">{data && data.close_issues_count}</span> <span className="red">{data && data.close_issues_count}</span>
<span className="change" onClick={()=>this.changeTrends("Issue","close")}>已关闭的任务</span> <span className="change" onClick={()=>this.changeTrends("Issue","delay")}>已关闭的易修</span>
</li> </li>
<li> <li>
<span className="green">{data && data.open_issues_count}</span> <span className="green">{data && data.open_issues_count}</span>
<span className="change" onClick={()=>this.changeTrends("Issue","create")}>未处理的任务</span> <span className="change" onClick={()=>this.changeTrends("Issue","not_delay")}>未处理的易修</span>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -27,7 +27,7 @@ class ActivityItem extends Component {
: :
// 如果是合并请求 // 如果是合并请求
<p className="itemLine"> <p className="itemLine">
<Link to={`/${owner}/${projectsId}/pulls/${item.trend_id}/Messagecount`} className="color-blue font-16">{item.name}</Link> <Link to={`/${owner}/${projectsId}/pulls/${item.trend_id}`} className="color-blue font-16">{item.name}</Link>
<span className="activity_type">{item.trend_type}</span> <span className="activity_type">{item.trend_type}</span>
</p > </p >
} }

View File

@ -367,7 +367,6 @@ class NewHeader extends Component {
} }
let search_url = settings && settings.common && settings.common.search; let search_url = settings && settings.common && settings.common.search;
let notice_url = settings && settings.common && settings.common.notice;
return ( return (
<div className="newHeaders" id="nHeader"> <div className="newHeaders" id="nHeader">
<div className="headerContent"> <div className="headerContent">
@ -438,7 +437,7 @@ class NewHeader extends Component {
</Dropdown>:"" </Dropdown>:""
} }
{current_user && current_user.login ? { (settings && settings.common && settings.common.notice) && (current_user && current_user.login)?
<Popover <Popover
overlayClassName="notice-popover" overlayClassName="notice-popover"
placement={`bottomRight`} placement={`bottomRight`}
@ -448,9 +447,9 @@ class NewHeader extends Component {
destroyTooltipOnHide destroyTooltipOnHide
> >
<Link to={"/settings/notice"} className="message-icon"> <Link to={"/settings/notice"} className="message-icon">
<Badge count={current_user.message_unread_total}> {current_user && <Badge count={current_user.message_unread_total}>
<i className="iconfont icon-xiaoxilingdang color-grey-6 ml15 mr15"></i> <i className="iconfont icon-xiaoxilingdang color-grey-6 ml15 mr15"></i>
</Badge> </Badge>}
</Link> </Link>
</Popover> </Popover>
: "" : ""

View File

@ -426,7 +426,7 @@ function CoderDepot(props){
<div className="addOptionBtn"> <div className="addOptionBtn">
{ {
baseOperate && baseOperate &&
<CheckProfile {...props} sureFunc={()=>urlLink(`/${owner}/${projectsId}/pulls/new/${branchName || defaultBranch}`)} >+ 合并请求</CheckProfile> <CheckProfile {...props} sureFunc={()=>urlLink(`/${owner}/${projectsId}/compare/master...${branchName || defaultBranch}`)} >+ 合并请求</CheckProfile>
} }
{ {
baseOper && baseOper &&

View File

@ -70,7 +70,7 @@ const MergeIndexDetail = Loadable({
}) })
const CreateMerge = Loadable({ const CreateMerge = Loadable({
loader: () => import('../Merge/NewMerge'), loader: () => import('../Merge/CreateMerge'),
loading: Loading, loading: Loading,
}) })
@ -150,7 +150,9 @@ function checkPathname(projectsId, owner, pathname) {
name = "about" name = "about"
} else if (url.indexOf("/issues") > -1 || url.indexOf("Milepost") > 0) { } else if (url.indexOf("/issues") > -1 || url.indexOf("Milepost") > 0) {
name = "issues"; name = "issues";
} else if (url.indexOf("/pulls") > -1) { } else if (url.indexOf("/pulls") > -1 || url.indexOf("/compare") > -1) {
// /pulls合并请求除新建合并请求外
// /compare新建合并请求
name = "pulls" name = "pulls"
} else if (url.indexOf("/milestones") > -1) { } else if (url.indexOf("/milestones") > -1) {
name = "milestones" name = "milestones"
@ -318,6 +320,9 @@ class Detail extends Component {
const url = `/${owner}/${projectsId}/detail.json`; const url = `/${owner}/${projectsId}/detail.json`;
axios.get(url).then((result) => { axios.get(url).then((result) => {
if (result && result.data) { if (result && result.data) {
if (result.data.status === 404) {
this.props.history.push('/nopage');
}
this.setState({ this.setState({
projectDetail: result.data, projectDetail: result.data,
project_id: result.data.project_id, project_id: result.data.project_id,
@ -552,7 +557,7 @@ class Detail extends Component {
<span className="detail_tag_btn" loading={forkSpin}> <span className="detail_tag_btn" loading={forkSpin}>
<Tooltip title="复刻是fork的中文名即复制代码仓库" placement="bottom"> <Tooltip title="复刻是fork的中文名即复制代码仓库" placement="bottom">
<a className="detail_tag_btn_name" style={{ cursor: platform ? "pointer" : "default" }} onClick={this.forkFunc}> <a className="detail_tag_btn_name" style={{ cursor: platform ? "pointer" : "default" }} onClick={this.forkFunc}>
<i className="iconfont icon-fork color-grey-9 mr3 font-16"></i><span></span> <i className="iconfont icon-fork color-grey-9 mr3 font-16"></i><span>(Fork)</span>
</a> </a>
</Tooltip> </Tooltip>
{ {
@ -713,22 +718,32 @@ class Detail extends Component {
} }
></Route> ></Route>
{/* 新建合并请求 */} {/* 新建合并请求 */}
<Route path="/:owner/:projectsId/pulls/new/:branch" {/* <Route path="/:owner/:projectsId/compare/:branch"
render={
(props) => (<CreateMerge {...this.props} {...props} {...this.state} {...common} is_fork={true} />)
}
></Route> */}
<Route path="/:owner/:projectsId/compare"
render={ render={
(props) => (<CreateMerge {...this.props} {...props} {...this.state} {...common} is_fork={true} />) (props) => (<CreateMerge {...this.props} {...props} {...this.state} {...common} is_fork={true} />)
} }
></Route> ></Route>
<Route path="/:owner/:projectsId/pulls/new" <Route path="/:owner/:projectsId/pulls/:mergeId/edit"
render={
(props) => (<CreateMerge {...this.props} {...props} {...this.state} {...common} is_fork={true} />)
}
></Route>
<Route path="/:owner/:projectsId/pulls/:mergeId/UpdateMerge"
render={ render={
(props) => (<UpdateMerge {...this.props} {...props} {...this.state} {...common} />) (props) => (<UpdateMerge {...this.props} {...props} {...this.state} {...common} />)
} }
></Route> ></Route>
<Route path="/:owner/:projectsId/pulls/:mergeId/Messagecount" <Route path="/:owner/:projectsId/pulls/:mergeId"
render={
(props) => (<MessageCount {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
<Route path="/:owner/:projectsId/pulls/:mergeId/commits"
render={
(props) => (<MessageCount {...this.props} {...props} {...this.state} {...common} />)
}
></Route>
<Route path="/:owner/:projectsId/pulls/:mergeId/files"
render={ render={
(props) => (<MessageCount {...this.props} {...props} {...this.state} {...common} />) (props) => (<MessageCount {...this.props} {...props} {...this.state} {...common} />)
} }

View File

@ -21,8 +21,8 @@ const Infos = styled.div`
& .markdown-body table{ & .markdown-body table{
background: #f1f8ff; background: #f1f8ff;
} }
& .f-wrap-between{ & .btnblue{
align-items: center; margin-top: 12px;
} }
& .task-hide{ & .task-hide{
width: 65rem; width: 65rem;

View File

@ -480,7 +480,7 @@
} }
} }
.ant-anchor-wrapper{ .ant-anchor-wrapper{
padding-left: 2px; padding-left: 2px!important;
.ant-anchor-ink::before{ .ant-anchor-ink::before{
background-color: #fff; background-color: #fff;
} }
@ -490,8 +490,8 @@
margin:0px auto; margin:0px auto;
} }
.griditemAnchor{ .griditemAnchor{
margin-left: 0px; margin-left: 0px!important;
padding: 0px; padding: 0px!important;
border-bottom: 1px solid #ddd; border-bottom: 1px solid #ddd;
.ant-anchor{ .ant-anchor{
display: flex; display: flex;

View File

@ -50,7 +50,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa
<Link to={{ pathname: `/${owner}/${projectsId}/issues`, state }}> <Link to={{ pathname: `/${owner}/${projectsId}/issues`, state }}>
<Tooltip title="易修是Issue的中文名即问题列表" placement="bottom"> <Tooltip title="易修是Issue的中文名即问题列表" placement="bottom">
<i className={"iconfont icon-yixiuicon1 color-grey-3 mr5 font-14"}></i> <i className={"iconfont icon-yixiuicon1 color-grey-3 mr5 font-14"}></i>
<span>易修</span> <span>易修(Issue)</span>
</Tooltip> </Tooltip>
{projectDetail && projectDetail.issues_count ? <span className="num">{numFormat(projectDetail.issues_count)}</span> : ""} {projectDetail && projectDetail.issues_count ? <span className="num">{numFormat(projectDetail.issues_count)}</span> : ""}
</Link> </Link>

View File

@ -121,15 +121,15 @@ function Tags(props) {
<SubMenu tab={"tags"} projectsId={projectsId} owner={owner}/> <SubMenu tab={"tags"} projectsId={projectsId} owner={owner}/>
<Spin spinning={isSpin}> <Spin spinning={isSpin}>
<div className="tagSpin"> <div className="tagSpin">
{ {
source && source.length > 0 && source && source.length > 0 &&
<Table <Table
className="tagTable" className="tagTable"
dataSource={source} columns={columns} pagination={false}></Table> dataSource={source} columns={columns} pagination={false}></Table>
} }
{ {
source && source.length === 0 && <Nonedata _html={'暂无数据~'}/> source && source.length === 0 && <Nonedata _html={'暂无数据~'}/>
} }
</div> </div>
</Spin> </Spin>
</div> </div>

View File

@ -91,7 +91,7 @@ function Index(props) {
<div className="treeabout"> <div className="treeabout">
{ {
(isManager || isDeveloper) && (projectDetail && projectDetail.type!==2) && (isManager || isDeveloper) && (projectDetail && projectDetail.type!==2) &&
<Link to={`/${owner}/${projectsId}/pulls/new/${i.name}`} className="btn-83">+ 合并请求</Link> <Link to={`/${owner}/${projectsId}/compare/master...${i.name}`} className="btn-83">+ 合并请求</Link>
} }
<Dropdown overlay={menu(i.zip_url,i.tar_url)} trigger={['click']} placement="bottomRight"> <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> <a className="btn-83 ml15">下载<i className="iconfont icon-sanjiaoxing-down font-14"></i></a>

View File

@ -0,0 +1,445 @@
import React, { Component } from 'react';
import { Input, Select, Spin, Alert } from 'antd';
import axios from 'axios';
import MergeForm from './merge_form';
import MergeFooter from './merge_footer';
import '../Order/order.css';
import './merge.css';
/**
* 根据url获取目标仓库目标分支源仓库源分支
* 路由规则owner/projectId/compare/merge...pullowner:pullBranch
* 可能存在的情况
* 1代码库首页跳转仓库相同目标分支为默认分支owner/projectId/compare/pullBranch
* 2代码库分支列表仓库相同目标分支为默认分支owner/projectId/compare/pullBranch
* 3合并请求列表页新建无数据时的提示仓库相同目标都为默认分支owner/projectId/compare
* 4新建页面切换分支切换目标仓库刷新页面等存在所有可能情况
*/
function getBranchParams(pathname) {
const result = {
// 目标仓库所有者
mergeOwner: undefined,
// 目标分支
mergeBranch: 'master',
// 源仓库所有者
pullOwner: undefined,
// 源分支
pullBranch: 'master',
// 仓库名称
projectId: undefined,
};
// 去掉第一个字符/
const _pathname = pathname.slice(1);
const [ownerProject, branchUrl] = _pathname.split('/compare');
const [mergeOwner, projectId] = ownerProject.split('/');
// 同仓库时
result.mergeOwner = mergeOwner;
result.pullOwner = mergeOwner;
result.projectId = projectId;
if (branchUrl) {
// 如果存在具体的分支
const _branchUrl = branchUrl.slice(1);
if (_branchUrl.indexOf('...') > -1) {
// 存在源分支与目标分支
const [mergeBranch, pullObj] = _branchUrl.split('...');
result.mergeBranch = mergeBranch;
if (pullObj.indexOf(':') > -1) {
// 存在源仓库
const [pullOwner, pullBranch] = pullObj.split(':');
result.pullOwner = pullOwner;
result.pullBranch = pullBranch;
} else {
result.pullBranch = pullObj;
}
} else {
result.pullBranch = _branchUrl;
}
}
return result;
}
const Option = Select.Option;
class CreateMerge extends Component {
constructor(props) {
super(props);
const { pullBranch, mergeBranch } = getBranchParams(
this.props.location.pathname
);
this.state = {
data: undefined,
pullBranches: undefined,
mergeBranches: undefined,
mergeProjects: undefined,
merge: mergeBranch || 'master',
pull: pullBranch || 'master',
id: undefined,
// isFork: false,
projects_names: undefined,
isSpin: true,
showMessage: false,
merge_head: false, // 是否向fork后的源项目发起合并请求
defaultMessage: '必须选择不同的分支',
project_id: undefined, // 当前项目的id也即开始发送合并请求的源项目id
merge_project_user: undefined,
comparesData: undefined, //提交和文件的内容保存compare接口返回的数据
// 比较分支时的加载效果
isCompareSpin: true,
// 是否是初次加载用这个字段来控制提示组件和文件组件的显示、隐藏比直接用isCompareSpin交互友好些
isFirstLoading: true,
};
}
componentDidMount = () => {
// 初始化时根据url获取目标仓库、分支源仓库、分支
// 再获取对应的仓库列表、分支列表
// 再调用比较接口
const branchParams = getBranchParams(this.props.location.pathname);
this.getMergeInfo(branchParams);
};
componentDidUpdate = (preProps) => {
// url变化触发时切换源分支、切换目标仓库、切换目标分支回退
const oldPathname = preProps.location.pathname;
const newPathname = this.props.location.pathname;
if (oldPathname !== newPathname) {
const branchParams = getBranchParams(newPathname);
this.getMergeInfo(branchParams);
}
};
//获取新建合并请求数据
getMergeInfo = (branchParams) => {
this.setState({ isSpin: true });
const { pullOwner, pullBranch, mergeOwner, mergeBranch, projectId } =
branchParams;
const url = `/${pullOwner}/${projectId}/pulls/new.json`;
axios
.get(url)
.then((result) => {
if (result) {
// 如果url上的分支不存在取默认值master
const noMergeBranch =
(result.data.branches || []).filter(
(branch) => branch.name === mergeBranch
).length === 0;
const noPullBranch =
(result.data.branches || []).filter(
(branch) => branch.name === pullBranch
).length === 0;
this.setState({
// isFork: result.data.is_fork,
projects_names: result.data.projects_names,
mergeProjects: result.data.merge_projects,
pullBranches: result.data.branches,
mergeBranches: result.data.branches,
project_id: result.data.project_id,
id: result.data.id,
merge: mergeBranch,
pull: pullBranch,
});
//判断源分支是否存在
if(noPullBranch){
this.setState({
showMessage: true,
defaultMessage:'源分支不存在',
isCompareSpin: false,
});
}else{
if(pullOwner === mergeOwner){
if (!noMergeBranch) {
this.compareProject(result.data.id, branchParams);
} else {
this.setState({
showMessage: true,
defaultMessage:'目标分支不存在',
isCompareSpin: false,
});
}
}else{
this.getBranchList(branchParams);
}
}
}
this.setState({ isSpin: false });
})
.catch((error) => {
this.setState({ isSpin: false });
console.log(error);
});
};
// compare接口获取分支对比信息
compareProject = (baseid, branchParams) => {
// const { project } = this.props;
// const { owner, projectsId } = this.props.match.params;
const projectObj = this.props.project;
const { pullOwner, pullBranch, mergeOwner, mergeBranch, projectId } =
branchParams;
let url = `/${mergeOwner}/${projectId}/compare`;
if (projectObj) {
if (baseid === projectObj.id) {
url += `/${pullBranch}...${mergeBranch}.json`;
} else {
url += `/${mergeBranch}...${pullOwner}/${projectId}:${pullBranch}.json`;
}
this.setState({ isSpin: false, isCompareSpin: true });
axios
.get(url)
.then((result) => {
if (result) {
if (result.data.status === 0) {
this.setState({
showMessage: false,
});
} else {
this.setState({
showMessage: true,
defaultMessage: result.data.message,
});
}
this.setState({
comparesData: result.data,
});
}
this.setState({
isFirstLoading: false,
isSpin: false,
isCompareSpin: false,
});
})
.catch((error) => {
this.setState({ isSpin: false, isCompareSpin: false });
});
}
};
// 根据所有者、仓库名,获取分支列表,目前仅涉及目标仓库分支查询
getBranchList = ({ mergeOwner, projectId, mergeBranch }) => {
this.setState({ isSpin: true });
const url = `/${mergeOwner}/${projectId}/pulls/get_branches.json`;
axios
.get(url)
.then((result) => {
if (result) {
const noMergeBranch =
(result.data || []).filter((branch) => branch.name === mergeBranch)
.length === 0;
this.setState({
mergeBranches: result.data,
showMessage: noMergeBranch,
defaultMessage: '目标分支不存在',
isCompareSpin: false,
});
}
this.setState({ isSpin: false });
})
.catch((error) => {
this.setState({ isSpin: false });
console.log(error);
});
};
// 切换分支事件
selectBrach = (type, value) => {
const { pullOwner, pullBranch, mergeOwner, mergeBranch, projectId } =
getBranchParams(this.props.location.pathname);
let _url = `/${mergeOwner}/${projectId}/compare/`;
// type为pull时pullBranch取value否则取原有值
// type为pull时mergeBranch取原有值否则取value
let _pullBranch = type === 'pull' ? value : pullBranch;
let _mergeBranch = type === 'pull' ? mergeBranch : value;
if (pullOwner === mergeOwner) {
// 如果仓库相同, compare/目标分支...源分支
_url += `${_mergeBranch}...${_pullBranch}`;
} else {
// 如果仓库不同, compare/目标分支...源分支
_url += `${_mergeBranch}...${pullOwner}:${_pullBranch}`;
}
this.props.history.push(_url);
};
// 切换仓库响应事件,目前仅目标分支可切换仓库
selectProjectName = (value) => {
const { projects_names, id } = this.state;
const { pullOwner, pullBranch } = getBranchParams(
this.props.location.pathname
);
let arr =
projects_names && projects_names.filter((item) => item.id === value);
let identifier = arr && arr[0].project_id;
let login = arr && arr[0].project_user_login;
// 目标仓库与源仓库不是一个仓库
let is_fork = parseInt(value, 10) !== parseInt(id, 10);
this.setState({
isSpin: true,
// merge_head: is_fork,
data: {
is_original: is_fork,
fork_project_id: is_fork ? id : '',
merge_user_login: is_fork
? projects_names[0].project_user_login
: undefined,
},
});
if (login === pullOwner) {
// 如果切换后, 仓库与源仓库一致了
this.props.history.push(
`/${login}/${identifier}/compare/master...${pullBranch}`
);
} else {
this.props.history.push(
`/${login}/${identifier}/compare/master...${pullOwner}:${pullBranch}`
);
}
// this.newMergelist(login, identifier);
};
// 渲染分支列表
renderBrances = (list) => {
if (list && list.length > 0) {
return list.map((item, key) => {
return (
<Option key={key + 1} value={item.name}>
{item.name}
</Option>
);
});
}
};
// 渲染项目列表
renderProjectNames = (list) => {
if (list && list.length > 0) {
return list.map((item, key) => {
return (
<Option key={key + 1} value={item.id}>
{item.project_name}
</Option>
);
});
}
};
// 渲染html内容
withHtml = (html) => {
return <div dangerouslySetInnerHTML={{ __html: html }}></div>;
};
render() {
const {
data,
pullBranches,
mergeBranches,
mergeProjects,
pull,
merge,
isSpin,
isCompareSpin,
isFirstLoading,
showMessage,
defaultMessage,
projects_names,
id,
comparesData,
} = this.state;
let { project } = this.props;
return (
<div>
<Spin spinning={isSpin || isCompareSpin}>
<div className="main">
<div className="merge-header width100 inline-block">
<div className="width40 pull-left">
<div className="color-grey-3 mb10 fwb">源分支:</div>
<Input.Group compact className="display-flex">
<Select
value={id}
className="hide-1 task-hide flex1"
disabled
>
{this.renderProjectNames(projects_names)}
</Select>
<Select
value={pull}
onSelect={(e) => this.selectBrach('pull', e)}
showSearch
className="merge-flex1 flex1"
>
{this.renderBrances(pullBranches)}
</Select>
</Input.Group>
</div>
<div className="width10 pull-left text-center mt25">
<i
className={'iconfont icon-youjiang color-grey-c font-32'}
></i>
</div>
<div className="width40 pull-left">
<div>
<div className="color-grey-3 mb10 fwb">目标分支:</div>
<Input.Group compact className="display-flex">
<Select
value={project && project.id}
className="hide-1 task-hide flex1"
onSelect={(e) => this.selectProjectName(e)}
>
{this.renderProjectNames(mergeProjects)}
</Select>
<Select
value={merge}
onSelect={(e) => this.selectBrach('merge', e)}
showSearch
className="merge-flex1 flex1"
>
{this.renderBrances(mergeBranches)}
</Select>
</Input.Group>
</div>
</div>
</div>
{/* 非加载状态且有提示 */}
{!isCompareSpin && showMessage && (
<div className="mb20">
<Alert
description={this.withHtml(defaultMessage)}
type="error"
/>
</div>
)}
{/* 非加载状态且可以提交 */}
{!isCompareSpin && !showMessage && (
<MergeForm
{...this.props}
merge_type="new"
data={data}
merge={merge}
pull={pull}
files_count={
comparesData &&
comparesData.diff &&
comparesData.diff.files_count
}
commits_count={comparesData && comparesData.commits_count}
></MergeForm>
)}
</div>
{!isFirstLoading && (
<MergeFooter
{...this.props}
merge={merge}
pull={pull}
comparesData={comparesData}
></MergeFooter>
)}
</Spin>
</div>
);
}
}
export default CreateMerge;

View File

@ -228,7 +228,7 @@ class MergeDetail extends Component {
<div> <div>
{ {
data && data.issue.user_permission ? data && data.issue.user_permission ?
<Link to={`/${owner}/${projectsId}/pulls/${mergeid}/updatemerge`} className="color-blue fr">编辑</Link> <Link to={`/${owner}/${projectsId}/pulls/${mergeid}/edit`} className="color-blue fr">编辑</Link>
: '' : ''
} }
</div> </div>

View File

@ -62,9 +62,9 @@ class MergeItem extends Component {
<p className="mb15 df" style={{ alignItems: "center" }}> <p className="mb15 df" style={{ alignItems: "center" }}>
<i className={`iconfont icon-hebingqingqiu1 font-14 mr3 i_${status}`}></i> <i className={`iconfont icon-hebingqingqiu1 font-14 mr3 i_${status}`}></i>
<Link <Link
to={`/${owner}/${projectsId}/pulls/${item.pull_request_id}/Messagecount`} to={`/${owner}/${projectsId}/pulls/${item.pull_request_id}`}
className="hide-1 font-15 color-grey-3 fwb lineh-30 mr10" className="hide-1 font-15 color-grey-3 fwb lineh-30 mr10"
style={{ maxWidth: "300px" }} style={{ maxWidth: "600px" }}
> >
{item.name} {item.name}
</Link> </Link>
@ -175,7 +175,7 @@ class MergeItem extends Component {
{item.journals_count ? ( {item.journals_count ? (
<Link <Link
className="mr5 color-grey-8" className="mr5 color-grey-8"
to={`/${owner}/${projectsId}/pulls/${item.pull_request_id}/Messagecount`} to={`/${owner}/${projectsId}/pulls/${item.pull_request_id}`}
> >
<i className="iconfont icon-huifu1 font-15 mr5 ver-middle"></i> <i className="iconfont icon-huifu1 font-15 mr5 ver-middle"></i>
{item.journals_count} {item.journals_count}
@ -196,7 +196,7 @@ class MergeItem extends Component {
> >
<div className="grid-item mr15 color-grey-9"> <div className="grid-item mr15 color-grey-9">
<Link <Link
to={`/${owner}/${projectsId}/pulls/${item.pull_request_id}/updatemerge`} to={`/${owner}/${projectsId}/pulls/${item.pull_request_id}/edit`}
className="color-grey-9" className="color-grey-9"
> >
<i className="iconfont icon-bianji3 font-14 mr5"></i> <i className="iconfont icon-bianji3 font-14 mr5"></i>

View File

@ -0,0 +1,210 @@
import React, { Component } from 'react';
import { Tabs, Spin } from 'antd';
import { Link } from 'react-router-dom';
import axios from 'axios';
import Commits from './Commits';
import Comments from '../comments/comments';
import Files from './Files';
import '../Order/order.css';
import './merge.css';
const { TabPane } = Tabs;
class MergeFooter extends Component {
constructor(props) {
super(props);
this.state = {
commitsData: [],
filesData: undefined,
isSpin: false,
activeKey: '1',
commitCount: 0,
filesCount: 0,
//
commentsTotalCount: 0,
};
}
componentDidMount() {
this.Init();
// 便
this.props.bindFootRef && this.props.bindFootRef(this);
}
componentDidUpdate(prevProps) {
// tab退taburltab
const newPathname = this.props.location.pathname;
const prevPathname = prevProps.location.pathname;
if (newPathname !== prevPathname) {
this.Init(true);
}
}
Init = (isTabChange) => {
const { data, location, match } = this.props;
const { pathname } = location;
const { projectsId, owner, mergeId } = match.params;
let activeKey = '1';
if (pathname.indexOf('commits') > -1) {
activeKey = '2';
this.getCommit(owner, projectsId, mergeId);
} else if (pathname.indexOf('files') > -1) {
activeKey = '3';
this.getFile(owner, projectsId, mergeId);
}
if (isTabChange && activeKey === '1') {
this.refreshComment();
}
this.setState({
activeKey: activeKey,
commitCount: data && data.commits_count,
filesCount: data && data.files_count,
});
};
bindCommentRef = (commentRef) => {
this.childComment = commentRef;
}
refreshComment = () => {
this.childComment && this.childComment.getjournalslist();
}
getCommit = (owner, projectsId, mergeId) => {
this.setState({ isSpin: true });
const url = `/${owner}/${projectsId}/pulls/${mergeId}/commits.json`;
axios
.get(url)
.then((result) => {
if (result) {
this.setState({
commitsData: result.data.commits,
commitCount: result.data.commits_count,
});
}
this.setState({ isSpin: false });
})
.catch((error) => {
this.setState({ isSpin: false });
});
};
getFile = (owner, projectsId, mergeId) => {
this.setState({ isSpin: true });
const url = `/${owner}/${projectsId}/pulls/${mergeId}/files.json`;
axios
.get(url)
.then((result) => {
if (result) {
this.setState({
filesData: result.data,
filesCount: result.data.files_count,
});
}
this.setState({ isSpin: false });
})
.catch((error) => {
this.setState({ isSpin: false });
});
};
render() {
const { projectsId, owner, mergeId } = this.props.match.params;
const { order_id, data = {} } = this.props;
const {
isSpin,
activeKey,
filesCount,
commitCount,
filesData,
commitsData = [],
} = this.state;
// Comment0
const commentsTotalCount = parseInt(
this.state.commentsTotalCount || data.comments_total_count || 0,
10
);
return (
<div className="main mergeRequest" style={{ paddingTop: '0px' }}>
<Spin spinning={isSpin}>
<Tabs
activeKey={activeKey}
className="custom-commit-tabs"
animated={false}
>
<TabPane
tab={
<Link to={`/${owner}/${projectsId}/pulls/${mergeId}`}>
<span className="font-16">评论</span>
{commentsTotalCount > 0 && (
<span className="tabNum">{commentsTotalCount}</span>
)}
</Link>
}
key="1"
>
<Comments
order_id={order_id}
showNotification={this.props.showNotification}
only_show_content={true}
updateCommentsNum={(commentsCount) => {
this.setState({ commentsTotalCount: commentsCount || 0 });
}}
{...this.props}
bindCommentRef={this.bindCommentRef}
/>
</TabPane>
{commitCount > 0 && (
<TabPane
tab={
<Link to={`/${owner}/${projectsId}/pulls/${mergeId}/commits`}>
<span className="font-16">提交</span>
{commitCount > 0 && (
<span className="tabNum">{commitCount}</span>
)}
</Link>
}
key="2"
>
{commitsData.length > 0 && (
<Commits
{...this.props}
commits={commitsData}
projectsId={projectsId}
owner={owner}
></Commits>
)}
</TabPane>
)}
{filesCount > 0 && (
<TabPane
tab={
<Link to={`/${owner}/${projectsId}/pulls/${mergeId}/files`}>
<span className="font-16">文件</span>
{filesCount > 0 && (
<span className="tabNum">{filesCount}</span>
)}
</Link>
}
key="3"
>
<Files
{...this.props}
data={filesData}
projectsId={projectsId}
owner={owner}
/>
</TabPane>
)}
</Tabs>
</Spin>
</div>
);
}
}
export default MergeFooter;

View File

@ -1,7 +1,6 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { Tabs } from 'antd'; import { Tabs } from 'antd';
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { AlignCenter } from '../Component/layout';
import axios from "axios"; import axios from "axios";
import { getImageUrl } from "educoder"; import { getImageUrl } from "educoder";
import { import {
@ -11,7 +10,6 @@ import {
Dropdown, Dropdown,
Icon, Icon,
Menu, Menu,
Select,
Tag, Tag,
Button, Button,
Alert, Alert,
@ -19,9 +17,8 @@ import {
import "./merge.css"; import "./merge.css";
import RenderHtml from "../../components/render-html"; import RenderHtml from "../../components/render-html";
import "../Order/order.css"; import "../Order/order.css";
import MergeFooter from "./merge_footer"; import MergeLinkFooter from "./MergeLinkFooter";
const Option = Select.Option;
const TextArea = Input.TextArea; const TextArea = Input.TextArea;
function turnbar(str){ function turnbar(str){
@ -61,6 +58,11 @@ class MessageCount extends Component {
// this.clickBody(); // this.clickBody();
}; };
bindFootRef = (footRef) => {
this.footRef = footRef;
}
clickBody=()=>{ clickBody=()=>{
document.body.addEventListener('click', e => { document.body.addEventListener('click', e => {
let name = e.target.className; let name = e.target.className;
@ -150,6 +152,8 @@ class MessageCount extends Component {
}); });
const { getDetail } = this.props; const { getDetail } = this.props;
getDetail && getDetail(); getDetail && getDetail();
// 调用子组件的方法刷新评论列表
this.footRef && this.footRef.refreshComment();
} else { } else {
this.setState({ SpinMerge: false }); this.setState({ SpinMerge: false });
} }
@ -283,13 +287,13 @@ class MessageCount extends Component {
conflict_files && conflict_files.length>0 && conflict_files && conflict_files.length>0 &&
<div> <div>
<p className="mt10 font-16 pt10" style={{borderTop:"1px solid #f9d7d5"}}>如下文件有代码冲突</p> <p className="mt10 font-16 pt10" style={{borderTop:"1px solid #f9d7d5"}}>如下文件有代码冲突</p>
<p> <div>
{ {
conflict_files.map((i,k)=>{ conflict_files.map((i,k)=>{
return <p>{i}</p> return <p key={k}>{i}</p>
}) })
} }
</p> </div>
</div> </div>
} }
</div> </div>
@ -336,11 +340,11 @@ class MessageCount extends Component {
<div> <div>
<div className="main"> <div className="main">
<div> <div>
<div className="grid-item-top pb20 border-1f"> <div className="pb20 border-1f df">
<div> <div className="flex1">
<div className="ver-middle"> <div className="ver-middle">
<span className="mr10 ver-middle"> <span className="mr10 ver-middle">
<span className="font-18 fwb"> <span className="font-18 fwb" style={{wordBreak:"break-all"}}>
{data.issue.subject} {data.issue.subject}
</span> </span>
</span> </span>
@ -441,14 +445,13 @@ class MessageCount extends Component {
</span> </span>
</div> </div>
</div> </div>
<div className="ml10"> <div className="ml10 text-right">
<div className="mt15 text-right" style={{display:"flex",justifyContent:"flex-end"}}>
{operate && ( {operate && (
<Button <Button
type="green" type="green"
ghost ghost
className="ml20" className="ml20"
onClick={()=>{this.props.history.push(`/${owner}/${projectsId}/pulls/${mergeId}/UpdateMerge`);}} onClick={()=>{this.props.history.push(`/${owner}/${projectsId}/pulls/${mergeId}/edit`);}}
> >
编辑 编辑
</Button> </Button>
@ -465,7 +468,6 @@ class MessageCount extends Component {
</Button> </Button>
)} )}
</div> </div>
</div>
</div> </div>
{ {
data.issue.description ? data.issue.description ?
@ -543,7 +545,7 @@ class MessageCount extends Component {
onChange={this.changbodypr} onChange={this.changbodypr}
/> />
</div> </div>
<p <div
className="clearfix mt15" className="clearfix mt15"
style={{ display: this.state.buttonshow }} style={{ display: this.state.buttonshow }}
> >
@ -558,19 +560,19 @@ class MessageCount extends Component {
取消 取消
</Button> </Button>
</Spin> </Spin>
</p> </div>
</div> </div>
</Spin> </Spin>
)} )}
</div> </div>
</div> </div>
<MergeFooter <MergeLinkFooter
footer_type={true}
order_id={data && data.issue.id} order_id={data && data.issue.id}
{...this.props} {...this.props}
{...this.state} {...this.state}
></MergeFooter> bindFootRef={this.bindFootRef}
></MergeLinkFooter>
</div> </div>
) : ( ) : (
"" ""

View File

@ -6,6 +6,11 @@ import "./merge.css";
import MergeForm from "./merge_form"; import MergeForm from "./merge_form";
import MergeFooter from "./merge_footer"; import MergeFooter from "./merge_footer";
const Option = Select.Option; const Option = Select.Option;
/**
* 此文件已废弃新文件为CreateMerge.js
* 2021.10.12
*/
class NewMerge extends Component { class NewMerge extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
@ -194,7 +199,7 @@ class NewMerge extends Component {
// this.ischeckmerge(); // this.ischeckmerge();
let { id ,merge , pull } = this.state; let { id ,merge , pull } = this.state;
if(type==="pull"){ if(type==="pull"){
this.props.history.push(`/${owner}/${projectsId}/pulls/new/${pull}`) this.props.history.push(`/${owner}/${projectsId}/compare/${pull}`)
this.compareProject(id,value,merge); this.compareProject(id,value,merge);
}else{ }else{
this.compareProject(id,pull,value); this.compareProject(id,pull,value);
@ -216,7 +221,7 @@ class NewMerge extends Component {
merge_user_login: is_fork_id ? projects_names[0].project_user_login : undefined merge_user_login: is_fork_id ? projects_names[0].project_user_login : undefined
} }
}) })
this.props.history.push(`/${login}/${identifier}/pulls/new`); this.props.history.push(`/${login}/${identifier}/compare`);
this.newMergelist(login,identifier); this.newMergelist(login,identifier);
}; };

View File

@ -69,7 +69,7 @@ class UpdateMerge extends Component {
</Button> </Button>
<Select <Select
defaultValue={data.is_original ? `${data.fork_project_user}:${pull}` : `${pull}`} defaultValue={data.is_original ? `${data.fork_project_user}:${pull}` : `${pull}`}
className="minW50 merge-flex1" className="minW50 merge-flex1 flex1"
disabled disabled
></Select>{" "} ></Select>{" "}
</Input.Group>{" "} </Input.Group>{" "}
@ -88,7 +88,7 @@ class UpdateMerge extends Component {
</Button> </Button>
<Select <Select
defaultValue={data.is_original ? `${data.project_login}:${merge}` : `${merge}`} defaultValue={data.is_original ? `${data.project_login}:${merge}` : `${merge}`}
className="minW50 merge-flex1" className="minW50 merge-flex1 flex1"
disabled disabled
></Select>{" "} ></Select>{" "}
</Input.Group>{" "} </Input.Group>{" "}

View File

@ -211,5 +211,6 @@ form .ant-cascader-picker, form .ant-select {
} }
.mergeRequest .folders{ .mergeRequest .folders{
width: 72rem; /* width: 72rem; */
width: 100%;
} }

View File

@ -213,7 +213,7 @@ class merge extends Component {
checkOperation() { checkOperation() {
const { projectsId,owner } = this.props.match.params; const { projectsId,owner } = this.props.match.params;
this.props.history.push(`/${owner}/${projectsId}/pulls/new`); this.props.history.push(`/${owner}/${projectsId}/compare/master...master`);
} }
render() { render() {
const { projectsId , owner } = this.props.match.params; const { projectsId , owner } = this.props.match.params;

View File

@ -1,169 +1,84 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import { Tabs, Spin } from "antd"; import { Tabs } from 'antd';
import "../Order/order.css"; import Commits from './Commits';
import "./merge.css"; import Files from './Files';
import Commits from "./Commits";
import Comments from "../comments/comments"; import '../Order/order.css';
import Files from "./Files"; import './merge.css';
import axios from 'axios';
const { TabPane } = Tabs; const { TabPane } = Tabs;
class MergeFooter extends Component { class MergeFooter extends Component {
constructor(props){ constructor(props) {
super(props); super(props);
this.state={ this.state = {
pageData:undefined, activeKey: '1',
commitsData:undefined, };
filesData:undefined,
isSpin:false,
activeKey:"1",
commitCount:0,
filesCount:0
}
}
componentDidMount=()=>{
const { footer_type ,data } = this.props;
if(footer_type){
const { projectsId , owner , mergeId } = this.props.match.params;
this.getCommit(owner,projectsId,mergeId);
this.getFile(owner,projectsId,mergeId);
}
this.setState({
activeKey:footer_type ? "1" : "2",
commitCount:data && data.commits_count,
filesCount:data && data.files_count
})
}
componentDidUpdate=(prevProps)=>{
const { comparesData } = this.props;
const { footer_type } = this.props;
if(footer_type){
const { data } = this.props;
if(data !== prevProps.data){
this.setState({
commitCount:data && data.commits_count,
filesCount:data && data.files_count
})
}
}
if(comparesData !== prevProps.comparesData){
this.setState({
activeKey:footer_type ? "1" : "2"
})
this.changeTab(footer_type ? "1" : "2");
}
} }
changeTab=(index)=>{ changeTab = (index) => {
this.setState({ this.setState({
isSpin:true activeKey: index,
}) });
this.setState({ };
activeKey:index
})
const { footer_type , comparesData } = this.props;
const { projectsId , owner , mergeId } = this.props.match.params;
if(footer_type){
if(index === "2"){
this.getCommit(owner,projectsId,mergeId);
}else if(index === "3"){
this.getFile(owner,projectsId,mergeId);
}else{
this.setState({
isSpin:false
})
}
}else{
this.setState({
commitsData:comparesData.commits,
filesData:comparesData.diff,
commitCount:comparesData.commits_count,
filesCount:comparesData.diff && comparesData.diff.files_count,
isSpin:false
})
}
}
getCommit =(owner,projectsId,mergeId)=>{
const url = `/${owner}/${projectsId}/pulls/${mergeId}/commits.json`;
axios.get(url).then(result=>{
if(result){
this.setState({
commitsData:result.data.commits,
isSpin:false,
commitCount:result.data.commits_count
})
}
}).catch(error=>{})
}
getFile =(owner,projectsId,mergeId)=>{
const url = `/${owner}/${projectsId}/pulls/${mergeId}/files.json`;
axios.get(url).then(result=>{
if(result){
this.setState({
filesData:result.data,
isSpin:false,
filesCount:result.data.files_count,
})
}
}).catch(error=>{})
}
render() { render() {
const { projectsId , owner } = this.props.match.params; const { projectsId, owner } = this.props.match.params;
const { comparesData = {} } = this.props;
const { commits, diff, commits_count } = comparesData;
const { activeKey } = this.state;
const { footer_type, order_id, data , comparesData } = this.props; return (commits && commits.length === 0) || !diff ? (
let { isSpin , activeKey , filesCount, commitCount , filesData , commitsData } = this.state; ''
) : (
return ( <div className="main mergeRequest" style={{ paddingTop: '0px' }}>
!footer_type && !comparesData || (comparesData && ((comparesData.commits && comparesData.commits.length===0)||(comparesData && !comparesData.diff)) )?"": <Tabs
<div className="main mergeRequest" style={{paddingTop:"0px"}}> activeKey={activeKey}
<Spin spinning={isSpin}> className="custom-commit-tabs"
<Tabs animated={false}
activeKey={activeKey} onChange={this.changeTab}
className="custom-commit-tabs" >
animated={false} {commits && commits.length > 0 && (
onChange={this.changeTab} <TabPane
> tab={
{ <span>
footer_type && <span className="font-16">提交</span>
<TabPane {commits_count > 0 && (
tab={ <span className="tabNum">{commits_count}</span>
<span><span className="font-16">评论</span> )}
{data && parseInt(data.comments_count) > 0 && <span className="tabNum">{data.comments_count}</span>} </span>
</span> }
} key="1"> key="1"
<Comments >
order_id={order_id} <Commits
showNotification={this.props.showNotification} {...this.props}
only_show_content={true} commits={commits}
{...this.props} projectsId={projectsId}
/> owner={owner}
</TabPane> ></Commits>
} </TabPane>
{ )}
commitsData && commitsData.length > 0 && {diff && diff.files && diff.files.length > 0 && (
<TabPane tab={<span><span className="font-16">提交</span> <TabPane
{commitCount > 0 && <span className="tabNum">{commitCount}</span>} tab={
</span>} key="2"> <span>
<Commits {...this.props} commits={commitsData} projectsId={projectsId} owner={owner}></Commits> <span className="font-16">文件</span>
</TabPane> {diff.files_count > 0 && (
} <span className="tabNum">{diff.files_count}</span>
{ )}
filesData && filesData.files && filesData.files.length>0 && </span>
<TabPane tab={ }
<span><span className="font-16">文件</span> key="3"
{filesCount > 0 && <span className="tabNum">{filesCount}</span>} >
</span> <Files
} key="3"> {...this.props}
<Files {...this.props} data={filesData} projectsId={projectsId} owner={owner}/> data={diff}
</TabPane> projectsId={projectsId}
} owner={owner}
/>
</Tabs> </TabPane>
</Spin> )}
</Tabs>
</div> </div>
); );
} }

View File

@ -165,7 +165,8 @@ class MergeForm extends Component {
this.setState({ this.setState({
isSpin: false, isSpin: false,
}); });
this.props.history.push(`/${owner}/${projectsId}/pulls`); const { pull_request_id } = result.data;
this.props.history.push(`/${owner}/${projectsId}/pulls/${pull_request_id}`);
const { getDetail } = this.props; const { getDetail } = this.props;
getDetail && getDetail(); getDetail && getDetail();
} else { } else {
@ -195,7 +196,7 @@ class MergeForm extends Component {
isSpin: false, isSpin: false,
}); });
this.props.history.push( this.props.history.push(
`/${owner}/${projectsId}/pulls/${mergeId}/Messagecount` `/${owner}/${projectsId}/pulls/${mergeId}`
); );
} else { } else {
this.setState({ this.setState({
@ -264,7 +265,7 @@ class MergeForm extends Component {
}, },
], ],
initialValue: title, initialValue: title,
})(<Input placeholder="标题" maxLength={50} />)} })(<Input placeholder="标题" maxLength={200} />)}
</Form.Item> </Form.Item>
<MDEditor <MDEditor
placeholder={"请输入合并请求的描述..."} placeholder={"请输入合并请求的描述..."}
@ -287,7 +288,7 @@ class MergeForm extends Component {
type="default" type="default"
className="ml30" className="ml30"
onClick={()=>{ onClick={()=>{
this.props.history.push(merge_type === "new" ? `/${owner}/${projectsId}/pulls` : `/${owner}/${projectsId}/pulls/${mergeId}/detail`) this.props.history.push(merge_type === "new" ? `/${owner}/${projectsId}/pulls` : `/${owner}/${projectsId}/pulls/${mergeId}`)
}} }}
> >
<span className="plr10">取消</span> <span className="plr10">取消</span>

View File

@ -12,7 +12,7 @@ class Nodata extends Component{
<h3>欢迎使用合并请求</h3> <h3>欢迎使用合并请求</h3>
<div className="color-grey-8"> <div className="color-grey-8">
合并请求可以帮助您与他人协作编写代码在使用之前请先创建一个 <Link className="color-blue" to={`/${owner}/${projectsId}/pulls/new`}>合并请求</Link> 合并请求可以帮助您与他人协作编写代码在使用之前请先创建一个 <Link className="color-blue" to={`/${owner}/${projectsId}/compare/master...master`}>合并请求</Link>
</div> </div>
</div> </div>
</div> </div>

View File

@ -285,12 +285,16 @@ class Index extends Component {
if(value.indexOf("/") > -1){ if(value.indexOf("/") > -1){
let arr = value.split("/"); let arr = value.split("/");
let first = arr[arr.length-1]; let first = arr[arr.length-1];
if(first.indexOf(".git") > -1){ if(first.indexOf(".") > -1){
let second = first.split('.')[0]; let second = first.split('.')[0];
if(!second)return; if(!second)return;
this.props.form.setFieldsValue({ this.props.form.setFieldsValue({
repository_name:second repository_name:second
}) })
}else{
this.props.form.setFieldsValue({
repository_name:first
})
} }
} }
} }
@ -351,7 +355,7 @@ class Index extends Component {
required: true, message: '请填写镜像版本库地址' required: true, message: '请填写镜像版本库地址'
}], }],
})( })(
<Input placeholder="请输入需要导入到本项目的仓库地址" onChange={this.ChangeAddr} /> <Input placeholder="请输入需要导入到本项目的仓库地址" onBlur={this.ChangeAddr} />
)} )}
</Form.Item> </Form.Item>
<p className="formTip color-orange">示例https://github.com/facebook/reack.git</p> <p className="formTip color-orange">示例https://github.com/facebook/reack.git</p>

View File

@ -1,5 +1,7 @@
import React, { Component } from "react"; import React, { Component } from "react";
import Editor from "react-monaco-editor"; import Editor from "react-monaco-editor";
// import {UnControlled as CodeMirror} from 'react-codemirror2'
import UserSubmitComponent from "./UserSubmitComponent"; import UserSubmitComponent from "./UserSubmitComponent";
import "./index.css"; import "./index.css";
@ -103,6 +105,17 @@ class m_editor extends Component {
editorWillMount={this.editorWillMount} editorWillMount={this.editorWillMount}
editorDidMount={handleEditorMount} editorDidMount={handleEditorMount}
/> />
{/* <CodeMirror
value={editorValue}
options={{
theme: 'monokai',
mode: 'JavaScript',
extraKeys: {"Ctrl": "autocomplete"},//ctrl可以弹出提示
styleActiveLine: true,
lineNumbers: true,
readOnly:true
}}
/> */}
</div> </div>
{!readOnly && ( {!readOnly && (
<div className="editorBorderSubmitBox" style={{marginTop:"20px",padding:"20px"}}> <div className="editorBorderSubmitBox" style={{marginTop:"20px",padding:"20px"}}>

View File

@ -233,7 +233,7 @@ class Detail extends Component {
: "合并请求"} : "合并请求"}
</span> </span>
<span className="font-16 fwb">{data && data.subject}</span> <span className="font-16 fwb" style={{wordBreak:"break-all"}}>{data && data.subject}</span>
</span> </span>
{data && data.priority && ( {data && data.priority && (

View File

@ -320,7 +320,7 @@ class order_form extends Component {
message: "请填写易修标题", message: "请填写易修标题",
}, },
] ]
})(<Input placeholder="标题" size="large" maxLength={80}/>)} })(<Input placeholder="标题" size="large" maxLength={200}/>)}
</Form.Item> </Form.Item>
<div className="quillContent"> <div className="quillContent">
<MDEditor <MDEditor

View File

@ -43,8 +43,9 @@ const PrivateLetter = Loadable({
}); });
function Index(props){ function Index(props){
const { current_user } = props; const { current_user,mygetHelmetapi } = props;
const { pathname } = props.location; const { pathname } = props.location;
const notice_url = mygetHelmetapi && mygetHelmetapi.common && mygetHelmetapi.common.notice;
return( return(
<div className="newMain clearfix whiteBack"> <div className="newMain clearfix whiteBack">
@ -59,11 +60,11 @@ function Index(props){
<li>个人信息</li> <li>个人信息</li>
<li className={pathname.indexOf("/settings/profile")>-1 ?"active":""}><Link to={`/settings/profile`}><i className="iconfont icon-gerenziliao mr5 font-14"></i><span className="text-shodow-bold">基本资料</span></Link></li> <li className={pathname.indexOf("/settings/profile")>-1 ?"active":""}><Link to={`/settings/profile`}><i className="iconfont icon-gerenziliao mr5 font-14"></i><span className="text-shodow-bold">基本资料</span></Link></li>
</ul> </ul>
<ul className="securityUl ul-border-buttom"> {notice_url && <ul className="securityUl ul-border-buttom">
<li>消息通知</li> <li>消息通知</li>
<li className={(pathname.indexOf("/settings/notice")>-1 && pathname.indexOf("/settings/notice/config") == -1) || pathname.indexOf("/settings/notice/privateLetter")>-1 ?"active":""}><Link to={"/settings/notice"}><i className="iconfont icon-wodetongzhi"></i><span className="text-shodow-bold">我的通知</span></Link></li> <li className={(pathname.indexOf("/settings/notice")>-1 && pathname.indexOf("/settings/notice/config") == -1) || pathname.indexOf("/settings/notice/privateLetter")>-1 ?"active":""}><Link to={"/settings/notice"}><i className="iconfont icon-wodetongzhi"></i><span className="text-shodow-bold">我的通知</span></Link></li>
{/* <li className={pathname.indexOf("/settings/notice/config")>-1 ?"active":""}><Link to={'/settings/notice/config'}><i className="iconfont icon-tongzhiguanli"></i><span className="text-shodow-bold">通知管理</span></Link></li> */} {/* <li className={pathname.indexOf("/settings/notice/config")>-1 ?"active":""}><Link to={'/settings/notice/config'}><i className="iconfont icon-tongzhiguanli"></i><span className="text-shodow-bold">通知管理</span></Link></li> */}
</ul> </ul>}
<ul className="securityUl"> <ul className="securityUl">
<li>安全设置</li> <li>安全设置</li>
<li className={pathname.indexOf("/settings/SSH")>-1 ?"active":""}><Link to={`/settings/SSH`}><i className="iconfont icon-xuanzhongssh_icon mr5 font-14"></i><span className="text-shodow-bold">SSH密钥</span></Link></li> <li className={pathname.indexOf("/settings/SSH")>-1 ?"active":""}><Link to={`/settings/SSH`}><i className="iconfont icon-xuanzhongssh_icon mr5 font-14"></i><span className="text-shodow-bold">SSH密钥</span></Link></li>

View File

@ -9,10 +9,9 @@ import './Index.scss';
import '../manager/Index.scss' import '../manager/Index.scss'
function MyNotice(props) { function MyNotice(props) {
let current_user = props.current_user; let { current_user, resetUserInfo, location, mygetHelmetapi, history}= props;
let resetUserInfo = props.resetUserInfo;
//tab //tab
let popover = props.location.query && props.location.query.noticeType; let popover = location && location.query && location.query.noticeType;
let pageSize = 15; let pageSize = 15;
const [noticeType, setNoticeType] = useState(popover==="atme"?"2":"0");//tab const [noticeType, setNoticeType] = useState(popover==="atme"?"2":"0");//tab
const [selectedNum, setSelectedNum] = useState(0);//@ const [selectedNum, setSelectedNum] = useState(0);//@
@ -27,6 +26,18 @@ function MyNotice(props) {
const [currentPage, setCurrentPage] = useState(1);// const [currentPage, setCurrentPage] = useState(1);//
const [onlyUnread, setOnlyUnread] = useState(); const [onlyUnread, setOnlyUnread] = useState();
//访/settings/profile
//访/explore
useEffect(()=>{
let notice = mygetHelmetapi && mygetHelmetapi.common && mygetHelmetapi.common.notice;
let login = current_user && current_user.login;
if(!login){
history.push(`/explore`);
}else if(!notice){
history.push(`/settings/profile`);
}
},[mygetHelmetapi])
useEffect(()=>{ useEffect(()=>{
popover==="atme" ? setNoticeType("2"):setNoticeType("0"); popover==="atme" ? setNoticeType("2"):setNoticeType("0");
},[popover]) },[popover])
@ -46,7 +57,7 @@ function MyNotice(props) {
limit: pageSize, limit: pageSize,
page: currentPage, page: currentPage,
}; };
axios.get(`/users/${current_user.login}/messages.json`, { current_user && axios.get(`/users/${current_user.login}/messages.json`, {
params: params, params: params,
}).then((response) => { }).then((response) => {
if(response && response.data){ if(response && response.data){
@ -59,7 +70,7 @@ function MyNotice(props) {
} }
function readNotice(id){ function readNotice(id){
if(id){ if(id && current_user){
const params = { const params = {
type: noticeType === "0" ? "notification" : noticeType === "2" ? "atme" : "", type: noticeType === "0" ? "notification" : noticeType === "2" ? "atme" : "",
ids:id, ids:id,

View File

@ -157,19 +157,18 @@ class Setting extends Component {
...values, ...values,
}).then((result) => { }).then((result) => {
if (result) { if (result) {
this.setState({
loading:false
})
this.props.showNotification(`仓库信息修改成功!`); this.props.showNotification(`仓库信息修改成功!`);
// if(values.project_identifier !== projectsId){ if(values.project_identifier !== projectsId){
// this.props.history.push(`/${owner}/${values.project_identifier}/settings`); this.props.history.push(`/${owner}/${values.project_identifier}/settings`);
// }else{ }else{
// }
const { getDetail } = this.props; const { getDetail } = this.props;
getDetail && getDetail(); getDetail && getDetail();
}
} }
this.setState({
loading:false
})
}).catch((error) => { }).catch((error) => {
console.log(error);
this.setState({ this.setState({
loading:false loading:false
}) })
@ -288,7 +287,9 @@ class Setting extends Component {
)} )}
</Form.Item> </Form.Item>
</div> </div>
{/* <Form.Item label=" (url)"> <Form.Item
label={<span>项目标识 <span className="color-grey-9">(项目url标识部分更改项目标识将导致原仓库地址失效)</span></span>}
>
{getFieldDecorator("project_identifier", { {getFieldDecorator("project_identifier", {
rules: [ rules: [
{ {
@ -299,7 +300,7 @@ class Setting extends Component {
})( })(
<Input placeholder="项目标识请使用与项目相关的英文关键字" maxLength="100" /> <Input placeholder="项目标识请使用与项目相关的英文关键字" maxLength="100" />
)} )}
</Form.Item> */} </Form.Item>
<Form.Item label="项目简介"> <Form.Item label="项目简介">
{getFieldDecorator("project_description", { {getFieldDecorator("project_description", {
rules: [], rules: [],

View File

@ -17,6 +17,7 @@ class children_comments extends Component {
page: 1, page: 1,
journal_spin: false, journal_spin: false,
search_count: 0, search_count: 0,
isSpin: false,
}; };
} }
@ -71,6 +72,9 @@ class children_comments extends Component {
.then((result) => { .then((result) => {
if (result) { if (result) {
this.getChildrenJournals(); this.getChildrenJournals();
// 删除回复后,如果需手动调用父组件查询评论列表的接口,以保持角标(评论数量)的一致(合并请求页面),
// 否则直接查顶级评论列表,否则只查询当前子评论列表(易修页面)
this.props.refreshCommentList && this.props.refreshCommentList();
} }
}) })
.catch((error) => { .catch((error) => {
@ -80,9 +84,15 @@ class children_comments extends Component {
// 翻页 // 翻页
ChangePage = (page) => { ChangePage = (page) => {
this.state.page = page; // this.state.page = page;
this.state.isSpin = true; // this.state.isSpin = true;
this.getChildrenJournals(); // 使用回调的写法这样在getChildrenJournals中使用的就是最新的state
this.setState({
page,
isSpin: true
}, () => {
this.getChildrenJournals();
});
}; };
commentCtx = (v) => { commentCtx = (v) => {
@ -183,7 +193,7 @@ class children_comments extends Component {
size="large" size="large"
loading={isSpin} loading={isSpin}
dataSource={journalsdata.issue_journals} dataSource={journalsdata.issue_journals}
renderItem={(item) => <List.Item>{this.renderList(item)}</List.Item>} renderItem={(item) => <List.Item key={item.id}>{this.renderList(item)}</List.Item>}
/> />
{this.Paginations()} {this.Paginations()}
</div> </div>

View File

@ -35,6 +35,8 @@ class comments extends Component {
componentDidMount = () => { componentDidMount = () => {
this.getjournalslist(); this.getjournalslist();
// 给父组件绑定以使父组件可以使用组件内方法用于切换tab时重新请求评论列表、合并请求完之后重新请求评论列表
this.props.bindCommentRef && this.props.bindCommentRef(this);
}; };
//添加评论 //添加评论
@ -151,6 +153,8 @@ class comments extends Component {
isSpin: false, isSpin: false,
fileList: undefined, fileList: undefined,
}); });
const { updateCommentsNum } = this.props;
updateCommentsNum && updateCommentsNum(result.data.journals_total_count);
} }
}) })
.catch((error) => { .catch((error) => {
@ -372,7 +376,7 @@ class comments extends Component {
const renderList = (item) => { const renderList = (item) => {
return ( return (
<div className="width100"> <div className="width100" key={item.id}>
<div className="pb5"> <div className="pb5">
<Link <Link
to={`/${item && item.user_login}`} to={`/${item && item.user_login}`}
@ -456,6 +460,7 @@ class comments extends Component {
parent_id={item.id} parent_id={item.id}
onRef={this.onRef} onRef={this.onRef}
children_comment_id={new_journal_id} children_comment_id={new_journal_id}
refreshCommentList={this.getjournalslist}
{...this.props} {...this.props}
></ChildrenComments> ></ChildrenComments>
</div> </div>
@ -500,7 +505,7 @@ class comments extends Component {
loading={isSpin} loading={isSpin}
header="" header=""
dataSource={journalsdata.issue_journals} dataSource={journalsdata.issue_journals}
renderItem={(item) => <List.Item>{renderList(item)}</List.Item>} renderItem={(item) => <List.Item key={item.id}>{renderList(item)}</List.Item>}
/> />
)} )}
{this.Paginations()} {this.Paginations()}
@ -558,7 +563,7 @@ class comments extends Component {
header="" header=""
dataSource={journalsdata.issue_journals} dataSource={journalsdata.issue_journals}
renderItem={(item) => ( renderItem={(item) => (
<List.Item>{renderList(item)}</List.Item> <List.Item key={item.id}>{renderList(item)}</List.Item>
)} )}
/> />
)} )}

View File

@ -298,7 +298,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {
.CodeMirror-scroll { .CodeMirror-scroll {
overflow: scroll !important; overflow: scroll !important;
margin-bottom: -30px; margin-bottom: -30px;
margin-right: -30px; margin-right: -30px!important;
padding-bottom: 30px; padding-bottom: 30px;
height: 100%; height: 100%;
outline: none; outline: none;