449 lines
14 KiB
JavaScript
449 lines
14 KiB
JavaScript
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(true, 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 = (sameProject, branchParams) => {
|
||
// const { project } = this.props;
|
||
// const { owner, projectsId } = this.props.match.params;
|
||
const { pullOwner, pullBranch, mergeOwner, mergeBranch, projectId } =
|
||
branchParams;
|
||
|
||
let url = `/${mergeOwner}/${projectId}/compare`;
|
||
if (sameProject) {
|
||
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 = (branchParams) => {
|
||
const { mergeOwner, projectId, mergeBranch } = branchParams;
|
||
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,
|
||
});
|
||
!noMergeBranch && this.compareProject(false, branchParams);
|
||
}
|
||
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 matchwidth"
|
||
dropdownMatchSelectWidth={false}
|
||
dropdownClassName="overlihide"
|
||
>
|
||
{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 matchwidth"
|
||
dropdownMatchSelectWidth={false}
|
||
dropdownClassName="overlihide"
|
||
>
|
||
{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;
|