优化创建同步任务交互,需求详见2966

This commit is contained in:
谢思 2024-05-14 17:02:29 +08:00
parent 18746dedb1
commit 390214e358
5 changed files with 1188 additions and 1188 deletions

2110
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
import React, {useEffect, useState} from 'react';
import { Button, Modal, Input, Select, message, Form } from 'antd';
import axios from 'axios';
import { confirmInfo1, confirmInfo2, synchronizeTips } from './editStore';
import { branchForm, confirmInfo1, confirmInfo2, synchronizeTips } from './editStore';
import gitHub from '../../img/github.png';
import gitee from '../../img/gitee.png';
import '../index.scss';
function DeleteBox({owner, projectsId, visible, setVisible, branchList, reload, form, sync_repositories_unique=[]}) {
const { getFieldDecorator, validateFields , resetFields, getFieldsValue } = form;
function DeleteBox({owner, projectsId, visible, setVisible, branchList, reload, form, sync_repositories_unique=[], repositoriesBranch}) {
const { getFieldDecorator, validateFields , resetFields, getFieldsValue, setFieldsValue } = form;
//
const [platform, setPlatform] = useState();
//
@ -72,45 +72,29 @@ function DeleteBox({owner, projectsId, visible, setVisible, branchList, reload,
rules:[{required:true,message:`请选择同步仓库平台`}],
})(
<Select placeholder="请选择同步仓库平台" onChange={(value)=>{
setPlatform(value)
setPlatform(value);
}}>
<Select.Option value="Github"><img src={gitHub} alt="" className="storeLogo mr5"/>Github</Select.Option>
<Select.Option value="Gitee"><img src={gitee} alt="" className="storeLogo mr5"/>Gitee</Select.Option>
<Select.Option value="Github" disabled={!repositoriesBranch["Github"]}><img src={gitHub} alt="" className="storeLogo mr5"/>Github{!repositoriesBranch["Github"] && "获取分支列表失败请检查token是否有效"}</Select.Option>
<Select.Option value="Gitee" disabled={!repositoriesBranch["Gitee"]}><img src={gitee} alt="" className="storeLogo mr5"/>Gitee{!repositoriesBranch["Gitee"] && "获取分支列表失败请检查token是否有效"}</Select.Option>
</Select>
)}
</Form.Item>}
{platform && <Form.Item label={`${platform}分支`}>
{getFieldDecorator("external_branch_name",{
rules:[{required:true,message:`请输入${platform}分支名称`}]
})(
<Input placeholder={`请输入${platform}分支名称`} className="storeInput" maxLength={50}/>
)}
</Form.Item>}
<Form.Item label="Gitlink分支">
{getFieldDecorator("gitlink_branch_name",{
rules:[{required:true,message:"请选择Gitlink仓库分支"}]
})(
<Select placeholder="请选择Gitlink仓库分支">
{branchList && branchList.map(item=>{
const {branch_id, name} = item;
return <Select.Option value={name} key={branch_id}>{name}</Select.Option>
})}
</Select>
)}
</Form.Item>
<div className='directionByAddTaskModal'>
{platform && <div className='directionByAddTaskModal'>
<Form.Item label="首次同步方向:">
{getFieldDecorator("first_sync_direction",{
rules:[{required:true,message:"请选择首次同步方向"}]
})(
<Select placeholder="请选择首次同步方向">
<Select placeholder="请选择首次同步方向" onChange={()=>{
resetFields(["external_branch_name", "gitlink_branch_name"]);
}}>
<Select.Option value="2">{platform}同步至GitLink</Select.Option>
<Select.Option value="1">GitLink同步至{platform}</Select.Option>
</Select>
)}
</Form.Item>
{synchronizeTips(platform)}
</div>
</div>}
{platform && first_sync_direction && branchForm(getFieldDecorator, setFieldsValue, {}, first_sync_direction, first_sync_direction === "2" ? repositoriesBranch[platform] : branchList, platform, {})}
</Form>
{showConfrimContent && <div className='pt15'>
{confirmInfo1()}

View File

@ -50,22 +50,60 @@ export const confirmInfo2 = (info) =>{
export const synchronizeTips = (platform="Github")=>{
return <Tooltip title={<div>
①首次同步方向指两个仓库首次进行同步时执行git pull指令的方向<br/>
②若同步分支为全部分支首次同步方向为<span style={{color: 'red'}}>GitLink同步至 {platform}</span>:系统将本仓库导入至已绑定的{platform}仓库覆善目标仓库源码后保持两个仓库的双向同步<br/>
③若同步分支为单个分支首次同步方向为<span style={{color: 'red'}}>GitLink同步至 {platform}</span>:系统将本仓库目标分支导入至已绑定的{platform}仓库覆盖目标分支名称的源码保持两个分支的双向同步
首次同步方向指两个仓库首次进行同步时执行git push指令的方向<br/>
若首次同步方向为GitLink同步至{platform}则本仓库代码将被强推至绑定的{platform}仓库一次后续根据webhook来监听仓库的双向同步
{/* <span style={{color: 'red'}}>GitLink {platform}</span>:{platform}<br/>
③若同步分支为单个分支首次同步方向为<span style={{color: 'red'}}>GitLink同步至 {platform}</span>:系统将本仓库目标分支导入至已绑定的{platform}仓库覆盖目标分支名称的源码保持两个分支的双向同步 */}
</div>}>
<Icon type="question-circle" theme="filled" style={{color: '#466aff'}} className="mb20 synchronizeTip"/>
</Tooltip>
}
// form item
export const branchForm = (getFieldDecorator, setFieldsValue, initialValues, first_sync_direction = "1", branchList, platform, formItemLayout)=>{
const branchBySelectLabel = first_sync_direction === "1" ? "Gitlink" : platform;
const branchBySelectKey = first_sync_direction === "1" ? "gitlink_branch_name" : "external_branch_name";
const branchByInputLabel = first_sync_direction === "1" ? platform : "Gitlink";
const branchByInputKey = first_sync_direction === "1" ? "external_branch_name" : "gitlink_branch_name";
return <Fragment>
<Form.Item label={`${branchBySelectLabel}分支`} {...formItemLayout}>
{getFieldDecorator(branchBySelectKey,{
rules:[{required:true,message:`请选择${branchBySelectLabel}仓库分支`}],
initialValue: initialValues[branchBySelectKey]
})(
<Select placeholder={`请选择${branchBySelectLabel}仓库分支`} onChange={(value)=>{
const formValue = {}
formValue[branchByInputKey] = value
setFieldsValue(formValue);
}}>
{branchList && branchList.map((item, index)=>{
const {name} = item;
return <Select.Option value={name} key={index}>{name}</Select.Option>
})}
</Select>
)}
</Form.Item>
<Form.Item label={`${branchByInputLabel}分支`} {...formItemLayout} extra="注:若仓库存在该分支,同步后将被源分支覆盖。若不存在该分支,则会新建一条同名分支并同步代码">
{getFieldDecorator(branchByInputKey,{
rules:[{required:true,message:`请输入${branchByInputLabel}分支名称`}],
initialValue: initialValues[branchByInputKey]
})(
<Input placeholder={`请输入${branchByInputLabel}分支名称`} className="storeInput" maxLength={50} disabled/>
)}
</Form.Item>
</Fragment>
}
function EditStore(props){
const { form, history, storeDetail} = props;
const {sync_repositories_unique=[]} = storeDetail || {};
const { owner , projectsId } = props.match.params;
const { getFieldDecorator, validateFields , resetFields, setFields, getFieldsValue } = form;
const { getFieldDecorator, validateFields , resetFields, setFieldsValue, getFieldsValue } = form;
const [step, setStep] = useState(0);
//
// Gitlink
const [branchList, setBranchList] = useState();
const [externalRepoBranchList, setExternalRepoBranchList] = useState();
// form
const [values, setValues] = useState({});
//
@ -95,6 +133,10 @@ function EditStore(props){
branchName: branch === "1" ? "全部分支" : gitlink_branch_name,
type: "gitlink"
}
//
const first_sync_direction = values.first_sync_direction || getFieldsValue(["first_sync_direction"]).first_sync_direction;
//
const synchronizedPlatformList = sync_repositories_unique.map(item=>{return item.type.split("::").pop()})
const platformList = (synchronizedPlatformList.length && ["Github", "Gitee"].filter(item=>synchronizedPlatformList.indexOf(item) === -1)) || ["Github", "Gitee"]
@ -175,8 +217,6 @@ function EditStore(props){
resetFields();
setPlatform(value);
}}>
{/* <Select.Option value="Github"><img src={github} alt="" className="storeLogo mr5"/>Github</Select.Option>
<Select.Option value="Gitee"><img src={gitee} alt="" className="storeLogo mr5"/>Gitee</Select.Option> */}
{platformList.map(item=>{
return <Select.Option key={item} value={item}>{platformLogoUnText(item)}{item}</Select.Option>
})}
@ -194,7 +234,14 @@ function EditStore(props){
<Input placeholder={`请输入${platform}同步仓库地址`} className="storeInput" maxLength={200}/>
)}
</Form.Item>
<Form.Item label={`${platform}同步仓库授权验证`} className="storeFormItem" hidden={step || !platform}>
<Form.Item label={`${platform}同步仓库授权验证`} className="storeFormItem" hidden={step || !platform} extra={<div className="pt5 pl10 font-13">
如何配置token<br/>
1登录{platform}确保拥有读写同步仓库的权限<br/>
{platform === "Github" && "2、找到已存在的token或新建一个token新建方式为点击个人头像→Settings→Developer Settings→Personal access tokens (classic)→Generate new token"}
{platform === "Gitee" && "2、找到已存在的token或新建一个token新建方式为点击个人头像→设置→私人令牌→生成新令牌"}
<br/>
3确保token拥有仓库读写权限且未过期将token复制到此处token填写框
</div>}>
{getFieldDecorator("external_token",{
rules:[{required:true,message: `请输入${platform || ""}授权token`}],
initialValue: values.external_token
@ -203,21 +250,26 @@ function EditStore(props){
)}
</Form.Item>
{!step && <div>
{/* {platform && <div>
<Form.Item label={`${platform}同步仓库授权`} className="storeFormItem" required style={{marginBottom: '0'}}></Form.Item>
<Button type="primary" ghost>点击授权</Button>
</div>} */}
<div className="mt30">
<Button style={{width: '100px'}} onClick={()=>{history.go(-1)}} className="mr30">返回</Button>
<Button type="primary" style={{width: '100px'}} onClick={()=>{
//
validateFields(['type', 'external_repo_address', 'external_token'], (error, formValues )=>{
if(!error){
setStep(1);
setValues({
...values,
...formValues
})
const address = formValues.external_repo_address.replace(".git", "").replace(`https://${platform.toLowerCase()}.com/`, "")
// github/gitee
platform && axios.get(`/v1/${address}/branches/${platform.toLowerCase()}`, {params: {page: 1, limit: 100, token: formValues.external_token}}).then(res=>{
if(res && res.data && Array.isArray(res.data)){
setExternalRepoBranchList(res.data)
setStep(1);
setValues({
...values,
...formValues
})
}else{
message.error(`获取${platform}平台仓库分支列表失败,请检查参数输入是否正确`)
}
}).catch((error)=>{})
}
})
}}>下一步</Button>
@ -226,19 +278,17 @@ function EditStore(props){
{/* 第二步 */}
{step === 1 && <div>
<Icon type="info-circle" theme="filled" style={{color: '#466aff'}}/>
<span className="color-grey-6 ml5">为了保证双向同步实时性请您完成以下操作以保证每个参与同步的仓库均已配置Webhook</span>
<p className="mt15 mb15">1登录{platform}账号</p>
2复制以下url配置为同步仓库Webhook接收请求的地址<br/>
<span className="color-grey-6 ml5 mb15">为了保证双向同步实时性请您完成以下操作以保证每个参与同步的仓库均已配置Webhook</span>
<p className="mt15">1复制Webhook接受请求地址</p>
<Input
id="webhook"
className="mt10 mb15 copyWebhookInput"
className="mt10 mb10 copyWebhookInput"
value={`${window.location.origin}/api/v1/${owner}/${projectsId}/sync_repositories/sync?sync_direction=2&repo_type=SyncRepositories::${platform}`}
readOnly
addonAfter={<a onClick={inviteClick}>复制链接</a>}
/>
3登录并前往{platform}仓库Webhook配置页激活此Webhook请务必勾选push触发事件此webhook用于监听{platform}仓库代码变更<br/>
<Button type="primary" ghost className="mt10 mb15" onClick={()=>{
2前往{platform}登录并进入同步仓库WebHook页面进行配置
<Button type="primary" ghost className="mt10 mb10 ml10" onClick={()=>{
const {type, external_repo_address} = values;
if(external_repo_address){
window.open(`${external_repo_address.replace(/.git$/, "")}${type === "Github" ? "/settings/hooks" : "/hooks"}`)
@ -246,7 +296,13 @@ function EditStore(props){
message.error("您暂未配置同步仓库地址")
}
}}>点击前往</Button><br/>
4完成配置
<div className="pl15 pb10">
添加webHook<br/>
将步骤1地址填入页面中的URL处<br/>
确保已勾选Push事件<br/>
激活
</div>
3完成配置
<div className="mt30">
<Button style={{width: '100px'}} onClick={()=>{setStep(0)}} className="mr30">上一步</Button>
<Button type="primary" style={{width: '100px'}} onClick={()=>{setStep(2)}}>下一步</Button>
@ -254,8 +310,8 @@ function EditStore(props){
</div>}
{/* 第三步 */}
{step === 2 && <div>
<div className="color-grey-6 mb15">请绑定同步分支</div>
<Form.Item>
<div className="color-grey-6 mb15">请绑定同步分支建立单个分支的实时双向同步</div>
<Form.Item hidden>
{getFieldDecorator("sync_granularity",{
rules:[],
// initialValue: values.sync_granularity
@ -274,34 +330,15 @@ function EditStore(props){
</Radio.Group>
)}
</Form.Item>
{branch === "2" && <Form.Item label={`${platform}分支`} {...formItemLayout}>
{getFieldDecorator("external_branch_name",{
rules:[{required:true,message:`请输入${platform}分支名称`}],
initialValue: values.external_branch_name
})(
<Input placeholder={`请输入${platform}分支名称`} className="storeInput" maxLength={50}/>
)}
</Form.Item>}
{branch === "2" && <Form.Item label="Gitlink分支" {...formItemLayout}>
{getFieldDecorator("gitlink_branch_name",{
rules:[{required:true,message:"请选择Gitlink仓库分支"}],
initialValue: values.gitlink_branch_name
})(
<Select placeholder="请选择Gitlink仓库分支">
{branchList && branchList.map(item=>{
const {branch_id, name} = item;
return <Select.Option value={name} key={branch_id}>{name}</Select.Option>
})}
</Select>
)}
</Form.Item>}
{branch && <div style={{display: 'flex', alignItems: "center"}}>
<Form.Item label="首次同步方向:" {...formItemLayout}>
{getFieldDecorator("first_sync_direction",{
rules:[{required:true,message:"请选择首次同步方向"}],
initialValue: values.first_sync_direction
})(
<Select placeholder="请选择首次同步方向">
<Select placeholder="请选择首次同步方向" onChange={()=>{
resetFields(["external_branch_name", "gitlink_branch_name"]);
}}>
<Select.Option value="2">{platform}同步至GitLink</Select.Option>
<Select.Option value="1">GitLink同步至{platform}</Select.Option>
</Select>
@ -309,6 +346,7 @@ function EditStore(props){
</Form.Item>
{synchronizeTips(platform)}
</div>}
{branch === "2" && first_sync_direction && branchForm(getFieldDecorator, setFieldsValue, values, first_sync_direction, first_sync_direction === "2" ? externalRepoBranchList : branchList, platform, formItemLayout)}
<div className="mt30">
<Button style={{width: '100px'}} className="mr30" onClick={()=>{
saveStep3Info();
@ -324,45 +362,6 @@ function EditStore(props){
}}>确认绑定</Button>}
</div>
</div>}
{/* <div className="storeTitle pb10 mb15"><img src={gitHub} alt="" className="storeLogo"/></div>
<Form.Item label="Github同步仓库地址" className="storeFormItem">
{getFieldDecorator("github_address",{
rules:[]
})(
<Input placeholder="请输入Github目标版本库地址" className="storeInput"/>
)}
</Form.Item>
<Form.Item label="Github同步仓库授权验证" className="storeFormItem">
{getFieldDecorator("github_token",{
rules:[]
})(
<Input addonBefore="token" placeholder="请输入Github的授权token" className="storeInput"/>
)}
</Form.Item>
<div className="storeTitle pb10 mb15 pt20"><img src={gitee} alt="" className="storeLogo"/></div>
<Form.Item label="Gitee同步仓库地址" className="storeFormItem">
{getFieldDecorator("gitee_address",{
rules:[]
})(
<Input placeholder="请输入Gitee目标版本库地址" className="storeInput"/>
)}
</Form.Item>
<Form.Item label="Gitee同步仓库授权验证" className="storeFormItem">
{getFieldDecorator("gitee_token",{
rules:[]
})(
<Input addonBefore="token" placeholder="请输入Gitee的授权token" className="storeInput"/>
)}
</Form.Item>
<div className="tipStoreBox">
1在开启仓库同步前您需要绑定并授权目标同步仓库<br/>
2完成仓库绑定后需对仓库内的分支进行二次绑定当已绑定的分支有代码提交代码推送变更将实时同步更新至其他仓库<br/>
3目前仅提供Github与Gitee平台的仓库同步功能每个平台支持同时同步一个仓库
</div>
<Form.Item>
<Button type="primary" style={{width: '112px', height: '36px'}} onClick={submit}>确认绑定</Button>
<Button style={{width: '112px', height: '36px'}} className="ml40" onClick={()=>{history.goBack(-1)}}>取消</Button>
</Form.Item> */}
</Form>
<Modal
title="绑定同步分支"

View File

@ -24,15 +24,13 @@ export const sync_status = (status) =>{
function StoreList(props){
const { storeDetail, form, reflushRepos} = props;
const { getFieldDecorator, validateFields , setFieldsValue, setFields, getFieldsValue } = form;
const {sync_repositories, sync_repositories_unique=[]} = storeDetail || {};
const {sync_repositories, sync_repositories_unique=[], repositoriesBranch={}} = storeDetail || {};
const { owner , projectsId } = props.match.params;
const [visible, setVisible] = useState(false);
//
const [visibleNew, setVisibleNew] = useState(false);
//
const [branchList, setBranchList] = useState();
//
const [platform, setPlatform] = useState("GitHub");
const [current, setCurrent] = useState(1);
const [total, setTotal] = useState(0);
const [pageSize, setPageSize] = useState(10);
@ -132,7 +130,6 @@ function StoreList(props){
{ title: '最后一次同步时间', dataIndex: 'sync_time', className:"primaryColor recordColumns"},
{ title: '最后一次同步状态', dataIndex: 'sync_status', className:"recordColumns", render: (text)=>sync_status(text)},
{ title: '操作', dataIndex: 'enable', align: 'center', className:"primaryColor recordColumns", render: (text, item)=>{
const branch = item[platform === 'GitHub' ? 'github_branch': 'gitee_branch']
return <div>
<Link to={`/${owner}/${projectsId}/service/reposyncer/record/${item.reposync_branch_ids}`} className="primaryColor">同步记录</Link>
<Divider type="vertical" />
@ -159,19 +156,13 @@ function StoreList(props){
<div className="nullStoreBox mt25">
<img src={logo} alt="" className="loBox mt50"/>
<p className="font-22 mt10">欢迎使用跨平台仓库代码同步服务</p>
<div className="introBox font-15">跨平台仓库代码同步服务提供跨托管平台的项目协同开发同步功能支持用户在任何一个托管平台上的代码提交代码推送合并请求等操作自动同步至其他托管平台不仅增加每个开源项目与开发者的流量也使不同平台的开源项目维护与更新变得方便与快捷</div>
<div className="introBox font-15">跨平台仓库代码同步服务用于不同代码托管平台之间的仓库代码双向同步用户将不同平台的仓库分支进行绑定当任意仓库分支有push事件被webhook监听时代码推送将实时自动同步至另一个仓库以实现不同平台之间分支提交信息的自动同步</div>
<div className="borBox"></div>
<Button type="primary" style={{width: '112px', height: '36px'}}><Link to={`/${owner}/${projectsId}/service/reposyncer/store/edit`}>开始体验</Link></Button>
</div>
</Fragment>}
{/* 已绑定仓库信息 */}
{sync_repositories && !!sync_repositories.length && <Fragment>
{/* <Radio.Group defaultValue="GitHub" buttonStyle="solid" onChange={(e)=>{
setPlatform(e.target.value);
}}>
<Radio.Button value="GitHub"><img src={platform === "GitHub" ? gitHub : gitHub1} alt="" style={{width: '70px'}}/></Radio.Button>
<Radio.Button value="Gitee"><img src={platform === "Gitee" ? gitee : gitee1} alt="" style={{width: '70px'}}/></Radio.Button>
</Radio.Group> */}
<div className="headBox flexBetween pl20 mt20">
<div>
{sync_repositories_unique.map((item, index)=>{
@ -188,7 +179,15 @@ function StoreList(props){
<Input.Search placeholder="搜索分支名称" allowClear onSearch={(value)=>{
setBranchName(value);
}}/>
<Button type="primary" className="ml20 mr20" onClick={()=>{setVisibleNew(true);}}>添加同步分支</Button>
<Button type="primary" className="ml20 mr20" onClick={()=>{
//
const platformByFirstRepo = sync_repositories_unique[0] && sync_repositories_unique[0].type.replace("SyncRepositories::", "")
if(sync_repositories_unique.length === 1 && !repositoriesBranch[platformByFirstRepo]){
message.error(`获取${platformByFirstRepo}平台仓库分支列表失败请检查token是否有效`);
return
}
setVisibleNew(true);
}}>添加同步分支</Button>
</div>
</div>
<Table
@ -300,7 +299,7 @@ function StoreList(props){
)}
</Form.Item>
</Modal>
<AddTaskModal owner={owner} projectsId={projectsId} visible={visibleNew} setVisible={setVisibleNew} branchList={branchList} sync_repositories_unique={sync_repositories_unique} reload={getRecordList}/>
<AddTaskModal owner={owner} projectsId={projectsId} visible={visibleNew} setVisible={setVisibleNew} branchList={branchList} sync_repositories_unique={sync_repositories_unique} reload={getRecordList} repositoriesBranch={repositoriesBranch}/>
</div>
}
export default Form.create()(StoreList);;

View File

@ -61,11 +61,29 @@ function Reposyncer(propsF){
//
axios.get(`/v1/${owner}/${projectsId}/sync_repositories.json`).then(res=>{
if(res && res.status === 200){
setStoreDetail({
...res.data,
//
sync_repositories_unique: unique(res.data.sync_repositories || [])
});
const repoList = unique(res.data.sync_repositories || []);
// 使
const repositoriesBranch = {}
const promises = repoList.map(item=>{
const {external_repo_address, external_token, type} = item;
const platform = type.replace("SyncRepositories::", "").toLowerCase()
const address = external_repo_address.replace(".git", "").replace(`https://${platform}.com/`, "")
// github/gitee
if(!external_token) return
axios.get(`/v1/${address}/branches/${platform}`, { params: { page: 1, limit: 100, token: external_token } }).then(response=>{
if (response && response.data && Array.isArray(response.data)) {
repositoriesBranch[type.replace("SyncRepositories::", "")] = response.data;
}
})
})
Promise.all(promises).then(() => {
setStoreDetail({
...res.data,
//
sync_repositories_unique: repoList,
repositoriesBranch
})
})
}
})
},[reload])