This commit is contained in:
caishi 2020-05-28 11:35:57 +08:00
parent 9f012dff55
commit 28f92a5f82
16 changed files with 1251 additions and 710 deletions

BIN
src/forge/Images/upload.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,9 +1,8 @@
import React , { Component } from 'react'; import React , { Component } from 'react';
import { Table , Spin } from 'antd'; import { Spin } from 'antd';
import { getImageUrl } from 'educoder'; import { getImageUrl } from 'educoder';
import { truncateCommitId } from '../common/util' import { truncateCommitId } from '../common/util'
import SelectBranch from '../Branch/SelectBranch'; import SelectBranch from '../Branch/SelectBranch';
import Top from './DetailTop';
import axios from 'axios'; import axios from 'axios';
import {Link} from "react-router-dom"; import {Link} from "react-router-dom";

View File

@ -27,11 +27,11 @@ const CoderRootVersion = Loadable({
loading: Loading, loading: Loading,
}) })
const CoderRootVersionNew = Loadable({ const CoderRootVersionNew = Loadable({
loader: () => import('../Version/NewVersion'), loader: () => import('../Version/New'),
loading: Loading, loading: Loading,
}) })
const CoderRootVersionUpdate = Loadable({ const CoderRootVersionUpdate = Loadable({
loader: () => import('../Version/UpdateVersion'), loader: () => import('../Version/New'),
loading: Loading, loading: Loading,
}) })

View File

@ -303,6 +303,9 @@ class Detail extends Component {
<Link to={`/projects/${projectDetail.forked_from_project_id}/coders`} className="color-grey-ccc">{projectDetail.fork_info.fork_form_name}</Link> <Link to={`/projects/${projectDetail.forked_from_project_id}/coders`} className="color-grey-ccc">{projectDetail.fork_info.fork_form_name}</Link>
</React.Fragment> : "" </React.Fragment> : ""
); );
const common ={
getDetail:this.getDetail
}
return ( return (
<div> <div>
<Spin spinning={isSpin}> <Spin spinning={isSpin}>
@ -412,55 +415,55 @@ class Detail extends Component {
{/* 新建文件 */} {/* 新建文件 */}
<Route path="/projects/:projectsId/coders/:branch/newfile/:path" <Route path="/projects/:projectsId/coders/:branch/newfile/:path"
render={ render={
(props) => (<FileNew {...this.props} {...props} {...this.state} />) (props) => (<FileNew {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
<Route path="/projects/:projectsId/coders/:branch/newfile" <Route path="/projects/:projectsId/coders/:branch/newfile"
render={ render={
(props) => (<FileNew {...this.props} {...props} {...this.state} />) (props) => (<FileNew {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
{/* 标签列表 */} {/* 标签列表 */}
<Route path="/projects/:projectsId/orders/tags" <Route path="/projects/:projectsId/orders/tags"
render={ render={
(props) => (<TagList {...this.props} {...props} {...this.state} />) (props) => (<TagList {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
{/* 仓库设置 */} {/* 仓库设置 */}
<Route path="/projects/:projectsId/setting" <Route path="/projects/:projectsId/setting"
render={ render={
(props) => (<Setting {...this.props} {...props} {...this.state} getDetail={this.getDetail} />) (props) => (<Setting {...this.props} {...props} {...this.state} {...common} />)
} }
></Route> ></Route>
{/* 任务详情 */} {/* 任务详情 */}
<Route path="/projects/:projectsId/orders/:orderId/detail" <Route path="/projects/:projectsId/orders/:orderId/detail"
render={ render={
(props) => (<OrderDetail {...this.props} {...props} {...this.state} />) (props) => (<OrderDetail {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
{/* 里程碑 */} {/* 里程碑 */}
<Route path="/projects/:projectsId/orders/Milepost" <Route path="/projects/:projectsId/orders/Milepost"
render={ render={
(props) => (<OrderMilepost {...this.props} {...props} {...this.state} />) (props) => (<OrderMilepost {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
{/* 新建里程碑 */} {/* 新建里程碑 */}
<Route path="/projects/:projectsId/orders/meilpost" <Route path="/projects/:projectsId/orders/meilpost"
render={ render={
(props) => (<OrdernewMilepost {...this.props} {...props} {...this.state} />) (props) => (<OrdernewMilepost {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
{/*里程碑详情*/} {/*里程碑详情*/}
<Route path="/projects/:projectsId/orders/:meilid/MilepostDetail" <Route path="/projects/:projectsId/orders/:meilid/MilepostDetail"
render={ render={
(props) => (<MilepostDetail {...this.props} {...props} {...this.state} />) (props) => (<MilepostDetail {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
{/*修改里程碑*/} {/*修改里程碑*/}
<Route path="/projects/:projectsId/orders/:meilid/meilpost" <Route path="/projects/:projectsId/orders/:meilid/meilpost"
render={ render={
(props) => (<OrderupdateMilepost {...this.props} {...props} {...this.state} />) (props) => (<OrderupdateMilepost {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
@ -468,34 +471,34 @@ class Detail extends Component {
{/* 里程碑页面新建任务 */} {/* 里程碑页面新建任务 */}
<Route path="/projects/:projectsId/orders/:milepostId/new" <Route path="/projects/:projectsId/orders/:milepostId/new"
render={ render={
(props) => (<OrderNew {...this.props} {...props} {...this.state} />) (props) => (<OrderNew {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
{/* 新建任务 */} {/* 新建任务 */}
<Route path="/projects/:projectsId/orders/new" <Route path="/projects/:projectsId/orders/new"
render={ render={
(props) => (<OrderNew {...this.props} {...props} {...this.state} />) (props) => (<OrderNew {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
{/* 修改详情 */} {/* 修改详情 */}
<Route path="/projects/:projectsId/orders/:orderId/updatedetail" <Route path="/projects/:projectsId/orders/:orderId/updatedetail"
render={ render={
(props) => (<OrderupdateDetail {...this.props} {...props} {...this.state} />) (props) => (<OrderupdateDetail {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
{/* 复制详情 */} {/* 复制详情 */}
<Route path="/projects/:projectsId/orders/:orderId/copyetail" <Route path="/projects/:projectsId/orders/:orderId/copyetail"
render={ render={
(props) => (<OrdercopyDetail {...this.props} {...props} {...this.state} />) (props) => (<OrdercopyDetail {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
{/* 动态 */} {/* 动态 */}
<Route path="/projects/:projectsId/trends" <Route path="/projects/:projectsId/trends"
render={ render={
(props) => (<TrendsIndex {...this.props} {...props} {...this.state} />) (props) => (<TrendsIndex {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
@ -503,68 +506,68 @@ class Detail extends Component {
{/* 代码Index */} {/* 代码Index */}
<Route path="/projects/:projectsId/orders" <Route path="/projects/:projectsId/orders"
render={ render={
(props) => (<OrderIndex {...this.props} {...props} {...this.state} />) (props) => (<OrderIndex {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
<Route path="/projects/:projectsId/merge/new" <Route path="/projects/:projectsId/merge/new"
render={ render={
(props) => (<CreateMerge {...this.props} {...props} {...this.state} />) (props) => (<CreateMerge {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
<Route path="/projects/:projectsId/merge/:mergeId/UpdateMerge" <Route path="/projects/:projectsId/merge/:mergeId/UpdateMerge"
render={ render={
(props) => (<MessageCount {...this.props} {...props} {...this.state} />) (props) => (<MessageCount {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
<Route path="/projects/:projectsId/merge/:mergeId/Messagecount" <Route path="/projects/:projectsId/merge/:mergeId/Messagecount"
render={ render={
(props) => (<MessageCount {...this.props} {...props} {...this.state} />) (props) => (<MessageCount {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
<Route path="/projects/:projectsId/merge/:mergeId/MergeSubmit" <Route path="/projects/:projectsId/merge/:mergeId/MergeSubmit"
render={ render={
(props) => (<MessageCount {...this.props} {...props} {...this.state} />) (props) => (<MessageCount {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
<Route path="/projects/:projectsId/merge" <Route path="/projects/:projectsId/merge"
render={ render={
(props) => (<MergeIndexDetail {...this.props} {...props} {...this.state} />) (props) => (<MergeIndexDetail {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
<Route path="/projects/:projectsId/coders/filesurl" <Route path="/projects/:projectsId/coders/filesurl"
render={ render={
(props) => (<CoderRootIndex {...this.props} {...props} {...this.state} />) (props) => (<CoderRootIndex {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
<Route path="/projects/:projectsId/coders" <Route path="/projects/:projectsId/coders"
render={ render={
(props) => (<CoderRootIndex {...this.props} {...props} {...this.state} />) (props) => (<CoderRootIndex {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
<Route path="/projects/:projectsId/watch_users" <Route path="/projects/:projectsId/watch_users"
render={ render={
(props) => (<WatchUsers {...this.props} {...props} {...this.state}/>) (props) => (<WatchUsers {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
<Route path="/projects/:projectsId/praise_users" <Route path="/projects/:projectsId/praise_users"
render={ render={
(props) => (<PraiseUsers {...this.props} {...props} {...this.state}/>) (props) => (<PraiseUsers {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
<Route path="/projects/:projectsId/fork_users" <Route path="/projects/:projectsId/fork_users"
render={ render={
(props) => (<ForkUsers {...this.props} {...props} {...this.state}/>) (props) => (<ForkUsers {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
<Route path="/projects/:projectsId" <Route path="/projects/:projectsId"
render={ render={
(props) => (<CoderRootIndex {...this.props} {...props} {...this.state} />) (props) => (<CoderRootIndex {...this.props} {...props} {...this.state} {...common}/>)
} }
></Route> ></Route>
</Switch> </Switch>

View File

@ -1,6 +1,6 @@
import React , {Component} from 'react'; import React, { Component } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import {Radio, Form,Menu,Dropdown,Input,Select,Table, Spin} from 'antd'; import { Radio, Form, Menu, Dropdown, Input, Select, Table, Spin } from 'antd';
import axios from 'axios'; import axios from 'axios';
import UploadComponent from '../Upload/Index'; import UploadComponent from '../Upload/Index';
import { getImageUrl } from 'educoder'; import { getImageUrl } from 'educoder';
@ -13,115 +13,115 @@ const Option = Select.Option;
const options = [ const options = [
['bold', 'italic', 'underline'], ['bold', 'italic', 'underline'],
[{header: [1,2,3,false]}], [{ header: [1, 2, 3, false] }],
['blockquote', 'code-block'], ['blockquote', 'code-block'],
['link', 'image'], ['link', 'image'],
['formula'] ['formula']
]; ];
class NewMerge extends Component{ class NewMerge extends Component {
constructor(props){ constructor(props) {
super(props); super(props);
this.state={ this.state = {
data:undefined, data: undefined,
//合并 拉取 //合并 拉取
merge:undefined, merge: undefined,
pull:undefined, pull: undefined,
//判断 是否显示创建合并请求的页面 //判断 是否显示创建合并请求的页面
desc:undefined, desc: undefined,
iscreatemerge:'none', iscreatemerge: 'none',
issue_tag_ids:"", issue_tag_ids: "",
fixed_version_id:"", fixed_version_id: "",
assigned_to_id:"", assigned_to_id: "",
titledata:undefined, titledata: undefined,
dataCount:undefined, dataCount: undefined,
limit:50, limit: 50,
page:1, page: 1,
isSpin:false, isSpin: false,
mergedata:undefined, mergedata: undefined,
} }
} }
componentDidMount=()=>{ componentDidMount = () => {
this.getmergelist(); this.getmergelist();
this.InitData(); this.InitData();
} }
InitData=()=>{ InitData = () => {
this.props.form.setFieldsValue({ this.props.form.setFieldsValue({
...this.state ...this.state
}); });
} }
onPanelChange=(time, mode)=>{ onPanelChange = (time, mode) => {
this.setState({ this.setState({
value:time value: time
}); });
} }
onSelect=(time)=>{ onSelect = (time) => {
this.setState({ this.setState({
value:time, value: time,
selectedValue: time, selectedValue: time,
}); });
} }
getOption=(name,id)=>{ getOption = (name, id) => {
if(id==='branches'){ if (id === 'branches') {
this.ischeckmerge(name,this.state.pull) this.ischeckmerge(name, this.state.pull)
this.setState({ this.setState({
merge:name merge: name
}) })
}else{ } else {
if(this.state.iscreatemerge==='block'){ if (this.state.iscreatemerge === 'block') {
if(this.state.merge===name){ if (this.state.merge === name) {
}else{ } else {
const { page , limit } = this.state; const { page, limit } = this.state;
this.getCommitList( name , page , limit ); this.getCommitList(name, page, limit);
} }
}else{ } else {
} }
this.ischeckmerge(this.state.merge,name) this.ischeckmerge(this.state.merge, name)
this.setState({ this.setState({
pull:name pull: name
}) })
} }
} }
ismerge=()=>{ ismerge = () => {
this.setState({ this.setState({
iscreatemerge:'block' iscreatemerge: 'block'
}) })
const { page , limit } = this.state; const { page, limit } = this.state;
this.getCommitList( this.state.pull , page , limit ); this.getCommitList(this.state.pull, page, limit);
} }
//获取新建分枝数据 //获取新建分枝数据
getmergelist=()=>{ getmergelist = () => {
const { projectsId } = this.props.match.params; const { projectsId } = this.props.match.params;
const url = `/projects/${projectsId}/pull_requests/new.json`; const url = `/projects/${projectsId}/pull_requests/new.json`;
axios.get(url).then((result)=>{ axios.get(url).then((result) => {
if(result){ if (result) {
this.setState({ this.setState({
data:result.data, data: result.data,
merge:result.data.branches[0], merge: result.data.branches[0],
pull:result.data.branches[0], pull: result.data.branches[0],
}) })
} }
}).catch((error)=>{ }).catch((error) => {
console.log(error); console.log(error);
}) })
} }
renderMenu =(array,id)=>{ renderMenu = (array, id) => {
return( return (
<Menu> <Menu>
{ {
array && array.length > 0 && array.map((item,key)=>{ array && array.length > 0 && array.map((item, key) => {
return( return (
<Menu.Item key={item} onClick={()=>this.getOption(item,id)}>{item}</Menu.Item> <Menu.Item key={item} onClick={() => this.getOption(item, id)}>{item}</Menu.Item>
) )
}) })
} }
@ -129,12 +129,12 @@ class NewMerge extends Component{
) )
} }
renderSelect=(list)=>{ renderSelect = (list) => {
if(list && list.length >0){ if (list && list.length > 0) {
return( return (
list.map((item,key)=>{ list.map((item, key) => {
return( return (
<Option key={key+1} value={item.id+''}>{item.name}</Option> <Option key={key + 1} value={item.id + ''}>{item.name}</Option>
) )
}) })
) )
@ -143,43 +143,44 @@ class NewMerge extends Component{
//创建合并请求 //创建合并请求
submit=()=>{ submit = () => {
this.setState({ this.setState({
isSpin: true isSpin: true
}) })
this.props.form.validateFieldsAndScroll((err, values) => { this.props.form.validateFieldsAndScroll((err, values) => {
if(!err){ if (!err) {
const { projectsId } = this.props.match.params; const { projectsId } = this.props.match.params;
const url = `/projects/${projectsId}/pull_requests.json`; const url = `/projects/${projectsId}/pull_requests.json`;
if(values.issue_tag_ids.length > 0){ if (values.issue_tag_ids.length > 0) {
values.issue_tag_ids = [parseInt(values.issue_tag_ids)] values.issue_tag_ids = [parseInt(values.issue_tag_ids)]
}else{ } else {
values.issue_tag_ids = [] values.issue_tag_ids = []
} }
const { desc } = this.state; const { desc } = this.state;
axios.post(url,{ axios.post(url, {
...values, ...values,
body:JSON.stringify(desc), body: JSON.stringify(desc),
project_id:projectsId, project_id: projectsId,
head:this.state.merge, head: this.state.merge,
base:this.state.pull, base: this.state.pull,
}).then(result=>{ }).then(result => {
if(result){ if (result) {
this.setState({ this.setState({
isSpin: false isSpin: false
}) })
this.props.history.push(`/projects/${projectsId}/merge`); this.props.history.push(`/projects/${projectsId}/merge`);
const { getDetail } = this.props;
getDetail && getDetail();
} }
}).catch(error => {
}).catch(error=>{
this.setState({ this.setState({
isSpin: false isSpin: false
}) })
console.log(error); console.log(error);
}) })
}else{ } else {
this.setState({ this.setState({
isSpin: false isSpin: false
}) })
@ -188,133 +189,126 @@ class NewMerge extends Component{
} }
//获取提交列表 //获取提交列表
getCommitList=(branch , page , limit)=>{ getCommitList = (branch, page, limit) => {
const { projectsId } = this.props.match.params; const { projectsId } = this.props.match.params;
const url = `/repositories/${projectsId}/commits.json`; const url = `/repositories/${projectsId}/commits.json`;
axios.get(url,{ axios.get(url, {
params:{ params: {
sha:branch, sha: branch,
page, page,
limit limit
} }
}).then((result)=>{ }).then((result) => {
if(result){ if (result) {
const array = []; const array = [];
result.data && result.data.commits.length > 0 && result.data.commits.map((item,key)=>{ result.data && result.data.commits.length > 0 && result.data.commits.map((item, key) => {
array.push({ array.push({
name:item.author && item.author.name, name: item.author && item.author.name,
login: item.author && item.author.login, login: item.author && item.author.login,
image_url:item.author && item.author.image_url, image_url: item.author && item.author.image_url,
sha:item.sha, sha: item.sha,
time_from_now:item.time_from_now, time_from_now: item.time_from_now,
message:item.message message: item.message
}) })
}) })
this.setState({ this.setState({
titledata:array, titledata: array,
dataCount:result.data.total_count, dataCount: result.data.total_count,
isSpin:false isSpin: false
}) })
} }
}).catch((error)=>{console.log(error)}) }).catch((error) => { console.log(error) })
} }
//判断2分支是否可以合并 //判断2分支是否可以合并
ischeckmerge=(head,base)=>{ ischeckmerge = (head, base) => {
const { projectsId } = this.props.match.params; const { projectsId } = this.props.match.params;
const url = `/projects/${projectsId}/pull_requests/check_can_merge.json`; const url = `/projects/${projectsId}/pull_requests/check_can_merge.json`;
axios.post(url,{ axios.post(url, {
project_id:projectsId, project_id: projectsId,
head:head, head: head,
base:base, base: base,
}).then(result=>{ }).then(result => {
if(result){ if (result) {
this.setState({ this.setState({
mergedata:result.data mergedata: result.data
}) })
} }
}).catch(error=>{ }).catch(error => {
console.log(error); console.log(error);
}) })
} }
onContentChange=(value)=>{ onContentChange = (value) => {
this.setState({ this.setState({
desc:value desc: value
}) })
} }
render(){ render() {
const { getFieldDecorator } = this.props.form; const { getFieldDecorator } = this.props.form;
const { projectsId } = this.props.match.params; const { projectsId } = this.props.match.params;
const { current_user } = this.props; const { current_user } = this.props;
const { issue_tag_ids , fixed_version_id ,assigned_to_id , data,titledata , desc, isSpin } = this.state; const { issue_tag_ids, fixed_version_id, assigned_to_id, data, titledata, desc, isSpin } = this.state;
const columns=[{ const columns = [{
title:"作者", title: "作者",
dataIndex: 'name', dataIndex: 'name',
width:"10%", width: "10%",
render: (text,item) => ( render: (text, item) => (
<span className="f-wrap-alignCenter"> <span className="f-wrap-alignCenter">
<Link to={`/users/${item.login}/projects`} className="show-user-link"> <Link to={`/users/${item.login}/projects`} className="show-user-link">
<img src={getImageUrl(`images/${item.image_url}`)} alt="" width="28px" height="28px" className="mr3 radius"/> <img src={getImageUrl(`images/${item.image_url}`)} alt="" width="28px" height="28px" className="mr3 radius" />
<label className="hide-1" style={{maxWidth:"75px",'vertical-align':'middle'}}>{text}</label> <label className="hide-1" style={{ maxWidth: "75px", 'vertical-align': 'middle' }}>{text}</label>
</Link> </Link>
{/*<img src={getImageUrl(`images/${item.image_url}`)} alt="" width="28px" height="28px" className="mr3 radius"/>*/}
{/*<label className="hide-1" style={{maxWidth:"75px"}}>{text}</label>*/}
</span> </span>
), ),
},{ }, {
title:"SHA", title: "SHA",
dataIndex: 'sha', dataIndex: 'sha',
render: (text) => ( render: (text) => (
<span className="commitKey">{text}</span> <span className="commitKey">{text}</span>
) )
},{ }, {
title:"备注", title: "备注",
dataIndex: 'message', dataIndex: 'message',
render: (text) => ( render: (text) => (
<span>{text}</span> <span>{text}</span>
) )
},{ }, {
title:"提交时间", title: "提交时间",
className:"edu-txt-right", className: "edu-txt-right",
dataIndex: 'time_from_now', dataIndex: 'time_from_now',
render: (text) => ( render: (text) => (
<span>{text}</span> <span>{text}</span>
) )
}] }]
const title =()=>{ const title = () => {
return( return (
<div className="f-wrap-between" style={{alignItems:"center"}}> <div className="f-wrap-between" style={{ alignItems: "center" }}>
<span className="font-16">提交列表</span> <span className="font-16">提交列表</span>
{/* <div className="f-wrap-alignCenter">
<Input placeholder="搜索提交历史" style={{width:"300px"}}/>
<Checkbox className="ml15">所有分支</Checkbox>
<a className="btn_32 ml15">搜索</a>
</div> */}
</div> </div>
) )
} }
const pull =()=>{ const pull = () => {
if(this.state.mergedata&&this.state.mergedata.status===-2){ if (this.state.mergedata && this.state.mergedata.status === -2) {
return( return (
<div> <div>
在这些分支直接合并请求已经存在<Link to={`/projects/${projectsId}/merge/${this.state.mergedata&&this.state.mergedata.pull_request_id}/Messagecount`} style={{color:'blue'}}>{this.state.mergedata&&this.state.mergedata.pull_request_name}</Link> 在这些分支直接合并请求已经存在<Link to={`/projects/${projectsId}/merge/${this.state.mergedata && this.state.mergedata.pull_request_id}/Messagecount`} style={{ color: 'blue' }}>{this.state.mergedata && this.state.mergedata.pull_request_name}</Link>
</div> </div>
) )
}else{ } else {
return( return (
<div> <div>
{this.state.mergedata&&this.state.mergedata.status===0?<Button className="topWrapper_btn" onClick={()=>this.ismerge()}>创建合并请求</Button>: ""} {this.state.mergedata && this.state.mergedata.status === 0 ? <Button className="topWrapper_btn" onClick={() => this.ismerge()}>创建合并请求</Button> : ""}
</div> </div>
) )
} }
@ -322,35 +316,35 @@ class NewMerge extends Component{
return( return (
<div className="main"> <div className="main">
<h1 className="mb10">创建合并请求</h1> <h1 className="mb10">创建合并请求</h1>
<h5 className="mb10">选择合并的目标分支和源分支</h5> <h5 className="mb10">选择合并的目标分支和源分支</h5>
<div style={{display:'flex'}}> <div style={{ display: 'flex' }}>
<div className="mergediv"> <div className="mergediv">
<div> <div>
<Dropdown className="topWrapperSelect" overlay={this.renderMenu(this.state.data &&this.state.data.branches,'branches')} trigger={['click']} placement="bottomCenter"> <Dropdown className="topWrapperSelect" overlay={this.renderMenu(this.state.data && this.state.data.branches, 'branches')} trigger={['click']} placement="bottomCenter">
<Button>合并到{this.state.merge}</Button> <Button>合并到{this.state.merge}</Button>
</Dropdown> </Dropdown>
... ...
<Dropdown overlay={this.renderMenu(this.state.data &&this.state.data.branches,'pull')} trigger={['click']} placement="bottomCenter"> <Dropdown overlay={this.renderMenu(this.state.data && this.state.data.branches, 'pull')} trigger={['click']} placement="bottomCenter">
<Button>拉取从{this.state.pull}</Button> <Button>拉取从{this.state.pull}</Button>
</Dropdown> </Dropdown>
</div> </div>
</div> </div>
</div> </div>
<div style={{display:this.state.iscreatemerge==='none'?'block':'none'}}> <div style={{ display: this.state.iscreatemerge === 'none' ? 'block' : 'none' }}>
<div className="mergediv" style={{marginTop:15}} > <div className="mergediv" style={{ marginTop: 15 }} >
{pull()} {pull()}
</div> </div>
</div> </div>
<div style={{display:this.state.iscreatemerge==='none'?'none':'block'}}> <div style={{ display: this.state.iscreatemerge === 'none' ? 'none' : 'block' }}>
<Form> <Form>
<div className="f-wrap-between mt20" style={{alignItems:"flex-start"}}> <div className="f-wrap-between mt20" style={{ alignItems: "flex-start" }}>
<div className="list-right df" style={{padding:"0px",paddingTop:"10px"}}> <div className="list-right df" style={{ padding: "0px", paddingTop: "10px" }}>
<Link to={`/users/${current_user && current_user.login}/projects`} className="show-user-link"> <Link to={`/users/${current_user && current_user.login}/projects`} className="show-user-link">
<img className="user_img" src={getImageUrl(`images/${current_user && current_user.image_url}`)} alt=""/> <img className="user_img" src={getImageUrl(`images/${current_user && current_user.image_url}`)} alt="" />
</Link> </Link>
<div className="new_context"> <div className="new_context">
@ -360,7 +354,7 @@ class NewMerge extends Component{
required: true, message: '请填写请求标题' required: true, message: '请填写请求标题'
}], }],
})( })(
<Input placeholder="标题"/> <Input placeholder="标题" />
)} )}
</Form.Item> </Form.Item>
<MDEditor placeholder={'请输入合并请求的描述...'} height={350} <MDEditor placeholder={'请输入合并请求的描述...'} height={350}
@ -374,7 +368,7 @@ class NewMerge extends Component{
</p> </p>
</div> </div>
</div> </div>
<div className="list-left" style={{paddingRight:"0px",paddingLeft:"15px",paddingTop:"10px"}}> <div className="list-left" style={{ paddingRight: "0px", paddingLeft: "15px", paddingTop: "10px" }}>
<div className="list-l-panel"> <div className="list-l-panel">
<Form.Item <Form.Item
label="标签" label="标签"
@ -384,8 +378,8 @@ class NewMerge extends Component{
rules: [], rules: [],
})( })(
<Select value={issue_tag_ids}> <Select value={issue_tag_ids}>
<Option value="">{data && data.issue_tags.length > 0 ? '未选择标签':'请在仓库设置里添加标签'}</Option> <Option value="">{data && data.issue_tags.length > 0 ? '未选择标签' : '请在仓库设置里添加标签'}</Option>
{ this.renderSelect(data && data.issue_tags) } {this.renderSelect(data && data.issue_tags)}
</Select> </Select>
)} )}
</Form.Item> </Form.Item>
@ -397,8 +391,8 @@ class NewMerge extends Component{
rules: [], rules: [],
})( })(
<Select value={fixed_version_id}> <Select value={fixed_version_id}>
<Option value="">{data && data.issue_versions.length > 0 ? '未选择里程碑': '请添加里程碑'}</Option> <Option value="">{data && data.issue_versions.length > 0 ? '未选择里程碑' : '请添加里程碑'}</Option>
{ this.renderSelect(data && data.issue_versions) } {this.renderSelect(data && data.issue_versions)}
</Select> </Select>
)} )}
</Form.Item> </Form.Item>
@ -409,7 +403,7 @@ class NewMerge extends Component{
})( })(
<Select value={assigned_to_id}> <Select value={assigned_to_id}>
<Option value="">未指派成员</Option> <Option value="">未指派成员</Option>
{ this.renderSelect(data && data.members) } {this.renderSelect(data && data.members)}
</Select> </Select>
)} )}
</Form.Item> </Form.Item>
@ -418,7 +412,7 @@ class NewMerge extends Component{
</div> </div>
</Form> </Form>
</div> </div>
<div style={{display:this.state.iscreatemerge==='none'?'none':'block'}}> <div style={{ display: this.state.iscreatemerge === 'none' ? 'none' : 'block' }}>
<Table <Table
className="mt20 wrap-commit-table" className="mt20 wrap-commit-table"
columns={columns} columns={columns}

View File

@ -290,7 +290,7 @@ class merge extends Component {
<a className="topWrapper_btn ml10" onClick={() => this.islogin()}>创建合并请求</a> <a className="topWrapper_btn ml10" onClick={() => this.islogin()}>创建合并请求</a>
</div> </div>
</div> </div>
<div className="f-wrap-between mb20"> <div className="f-wrap-between pb20" style={{borderBottom:"1px dashed #ddd"}}>
<div></div> <div></div>
<ul className="topWrapper_select"> <ul className="topWrapper_select">
<li> <li>

View File

@ -125,6 +125,8 @@ class New extends Component {
description: '', description: '',
isSpin: false isSpin: false
}) })
const { getDetail } = this.props;
getDetail && getDetail();
} }
}).catch(error => { }).catch(error => {
this.setState({ this.setState({

View File

@ -1,7 +1,7 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { Popconfirm } from 'antd' import { Popconfirm } from 'antd'
import { TagInfo } from '../Utils/TagColor';
class OrderItem extends Component { class OrderItem extends Component {
@ -18,7 +18,7 @@ class OrderItem extends Component {
return ( return (
issue_tags.map((item, key) => { issue_tags.map((item, key) => {
return ( return (
<span className="issue-tag-show" style={{ background: item.color }}>{item.name}</span> <span>{item.name}</span>
) )
}) })
) )
@ -43,36 +43,31 @@ class OrderItem extends Component {
isdisplay: false isdisplay: false
}) })
} }
render() { render() {
const { issues, search_count, page, limit } = this.props; const { item, key, checkbox } = this.props;
const { projectsId } = this.props.match.params; const { projectsId } = this.props.match.params;
const { current_user } = this.props const { current_user } = this.props
const renderList = () => { const renderList = () => {
if (issues && issues.length > 0) { if (item) {
return (
issues.map((item, key) => {
return ( return (
<div className="issueItem" key={key}> <div className="issueItem" key={key}>
{current_user && current_user.login && checkbox}
<div className="flex-1"> <div className="flex-1">
<p className="mb15 df"> <p className="mb15 df" style={{alignItems:"center"}}>
<span className={item.issue_status === "关闭" ? "issueNo" : "issueNo issueOpen"}># {search_count - (key + (page - 1) * limit)}</span> <Link to={`/projects/${projectsId}/orders/${item.id}/detail`} className="hide-1 font-16 color-grey-3 lineh-30" style={{maxWidth:"300px"}}>{item.name}</Link>
<Link to={`/projects/${projectsId}/orders/${item.id}/detail`} className="flex-1 hide-1 font-16 color-grey-3 lineh-30">{item.name}</Link> {TagInfo(item.priority,"ml10")}
</p> </p>
<p className="color-grey-6 font-12"> <p className="color-grey-6 font-12">
<span>{item.format_time}</span><span className="ml5"></span> <span>{item.format_time}</span><span className="ml5"></span>
{ {
item.updated_at === item.format_time ? item.updated_at === item.format_time ? "" :
""
:
<span className="ml20"><span>{item.updated_at}</span><span className="ml5"></span></span> <span className="ml20"><span>{item.updated_at}</span><span className="ml5"></span></span>
} }
</p> </p>
</div> </div>
<ul className="topWrapper_select no-cursor" onMouseMove={() => this.onMouseMove(item.id)} onMouseOut={() => this.onMouseOut()} > <ul className="topWrapper_select no-cursor" onMouseMove={() => this.onMouseMove(item.id)} onMouseOut={() => this.onMouseOut()} >
<li>{this.set_issue_tags(item.issue_tags)}</li> <li>{this.set_issue_tags(item.issue_tags)}</li>
<li>{item.tracker || "--"}</li>
<li> <li>
{ {
item.author_name ? item.author_name ?
@ -91,20 +86,20 @@ class OrderItem extends Component {
: "--" : "--"
} }
</li> </li>
<li><p style={{ width: 60, height: 25, margin: 'auto', paddingTop: 2, background: item.priority === '高' ? '#e67e22' : item.priority === '正常' ? '#28be6c' : item.priority === '低' ? '#1abc9c' : '#e74c3c', color: '#ffffff', borderRadius: 4 }}>{item.priority || "--"}</p></li> <li>{item.tracker || "--"}</li>
<li>{item.done_ratio || "--"}</li> <li>{item.version || "--"}</li>
<li>{item.issue_status || "--"}</li>
<li style={{color:`${item.done_ratio === "100%"?"#28BD6C":"#F73030"}`}}>{item.done_ratio || "--"}</li>
<li> <li>
<div className="flex-1"> <div className="milepostleft">
<p> <Link to={`/projects/${projectsId}/orders/${item.id}/detail`}><i className="iconfont icon-pinglun1 mr3 font-16"></i>{item.journals_count}</Link>
{item.journals_count ? <Link to={`/projects/${projectsId}/orders/${item.id}/detail`}><i className="iconfont icon-pinglun1 mr3 font-16"></i>{item.journals_count}</Link> : ""}
</p>
{ {
current_user && current_user.login ? current_user && current_user.login ?
<div className="milepostleft" style={{ display: this.state.orderid === item.id && this.state.isdisplay ? 'flex' : 'none' }}> <div style={{ display: this.state.orderid === item.id && this.state.isdisplay ? 'flex' : 'none' }}>
<div className="grid-item mr15 color-grey-9"> <div className="mr8 ml8 color-grey-9">
<Link to={`/projects/${projectsId}/orders/${item.id}/updatedetail`} className="color-grey-9"><i className="iconfont icon-bianji3 font-14 mr5"></i></Link> <Link to={`/projects/${projectsId}/orders/${item.id}/updatedetail`} className="color-grey-9"><i className="iconfont icon-bianji3 font-14 mr5"></i></Link>
</div> </div>
<div className="grid-item color-grey-9"> <div className="color-grey-9">
<Popconfirm placement="bottom" title={'您确定要删除吗'} okText="是" cancelText="否" onConfirm={() => this.deletedetail(item.id)}> <Popconfirm placement="bottom" title={'您确定要删除吗'} okText="是" cancelText="否" onConfirm={() => this.deletedetail(item.id)}>
<i className="iconfont icon-yiguanbi1 font-14"></i> <i className="iconfont icon-yiguanbi1 font-14"></i>
</Popconfirm> </Popconfirm>
@ -117,14 +112,10 @@ class OrderItem extends Component {
</ul> </ul>
</div> </div>
) )
})
)
} }
} }
return ( return (
<div> renderList()
{renderList()}
</div>
) )
} }
} }

View File

@ -0,0 +1,72 @@
.screenWrap{
background:rgba(250,250,250,1);
border:1px solid rgba(221,221,221,1);
}
.searchBanner{
display: flex;
height: 50px;
line-height: 50px;
li{
margin-right: 30px;
display: flex;
align-items: center;
}
li > label{
position: relative;
cursor: pointer;
}
li > span{
display: block;
padding:0px 8px;
border-radius: 10px;
background-color: #eee;
margin-left: 5px;
cursor: pointer;
height: 20px;
line-height: 20px;
}
li.active > label::after{
position: absolute;
width: 100%;
height: 2px;
content: '';
left: 0px;
bottom:0px;
background-color:#5091FF;
}
}
.milepostleft{
&>div{
display: flex;
align-items: center;
justify-content: center
}
}
.statusTag{
display: block;
height: 24px;
line-height: 24px;
border-radius: 4px;
padding:0px 12px;
color: #fff;
margin:3px 0px 0px 10px;
}
.updateBtn{
display: block;
width:60px;
text-align: center;
height: 26px;
line-height: 26px;
background-color: #fff;
border-radius: 5px;
}
.updateBtn.blue{
border:1px solid #5091FF;
color: #5091FF;
}
.updateBtn.red{
border:1px solid #F73030;
color: #F73030;
}

View File

@ -1,9 +1,8 @@
.topWrapper { .topWrapper {
padding: 20px 0; padding-bottom:20px;
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
border-bottom: 1px solid #EEEEEE;
flex-wrap: wrap; flex-wrap: wrap;
} }
.quillContent{ .quillContent{
@ -129,8 +128,9 @@
border-radius: 0px 5px 5px 0px; border-radius: 0px 5px 5px 0px;
} }
.topWrapper_btn { .topWrapper_btn {
background: #21ba45; background: #fff;
color: #FFFFFF!important; border:1px solid #5091FF;
color: #5091FF!important;
padding:0px 12px; padding:0px 12px;
text-align: center; text-align: center;
height: 32px; height: 32px;
@ -138,23 +138,29 @@
border-radius: 4px; border-radius: 4px;
} }
.topWrapper_type{ .topWrapper_type{
border:1px solid #f4f4f4;
border-radius: 6px;
display: flex; display: flex;
} }
.topWrapper_type li{ .topWrapper_type li{
display: flex;
align-items: center;
margin-right: 30px;
}
.topWrapper_type li >span{
display: block;
height: 30px; height: 30px;
line-height: 30px; line-height: 30px;
border-left:1px solid #f4f4f4; padding: 0px 13px;
padding:0px 8px; border-radius: 5px;
min-width: 60px;
box-sizing: border-box;
text-align: center;
background:rgba(250,250,250,1);
border:1px solid rgba(221,221,221,1);
cursor: pointer; cursor: pointer;
color: #666
} }
.topWrapper_type li.active{ .topWrapper_type li > span.active{
color: #4CACFF; color: #4CACFF;
} border:1px solid rgba(80,145,255,1);
.topWrapper_type li:first-child{
border-left: none;
} }
.topWrapper_select{ .topWrapper_select{
display: flex; display: flex;
@ -202,9 +208,6 @@
border-bottom:1px dashed #cecdcd; border-bottom:1px dashed #cecdcd;
padding:16px 0px; padding:16px 0px;
} }
.issueItem:first-child{
border-top:1px dashed #cecdcd;
}
.issueNo{ .issueNo{
padding:0px 5px; padding:0px 5px;
border-radius: 4px; border-radius: 4px;
@ -421,10 +424,8 @@
width: 80%; width: 80%;
} }
.milepostleft{ .milepostleft{
text-align: right;
display: flex; display: flex;
justify-content: right; justify-content: center;
width: 20%;
} }
.textwidth{ .textwidth{
display: flex; display: flex;
@ -531,7 +532,6 @@ a.issue-type-button.active:hover{background: #f4f4f4; color: #4CACFF;}
.attachment-list-div:hover .attachment-list-delete{display: block !important;} .attachment-list-div:hover .attachment-list-delete{display: block !important;}
.attachment-list-a{color: rgba(0, 0, 0, 0.65) !important;} .attachment-list-a{color: rgba(0, 0, 0, 0.65) !important;}
.btp1{border-top: 1px solid #f4f4f4;} .btp1{border-top: 1px solid #f4f4f4;}
.grid-item{display: grid; align-items: center; grid-template-columns: max-content 1fr;}
.fwb{font-weight: bold;} .fwb{font-weight: bold;}
/* 发布人、指派人数量过多时要出现滚动条 */ /* 发布人、指派人数量过多时要出现滚动条 */

View File

@ -1,6 +1,8 @@
import React, { Component } from "react"; import React, { Component } from "react";
import { Input, Dropdown, Menu, Icon, Pagination, Spin } from "antd"; import { Input, Dropdown, Menu, Icon, Pagination, Spin, DatePicker, Checkbox } from "antd";
import "./order.css"; import "./order.css";
import './index.scss';
import moment from 'moment';
import NoneData from "../Nodata"; import NoneData from "../Nodata";
import OrderItem from "./OrderItem"; import OrderItem from "./OrderItem";
@ -33,32 +35,40 @@ class order extends Component {
search: undefined, search: undefined,
author_id: undefined, author_id: undefined,
assigned_to_id: undefined, assigned_to_id: undefined,
// limit: 15,
// page: 1,
search_count: undefined, search_count: undefined,
issue_type: undefined, issue_type: undefined,
status_type: "1", status_type: undefined,
//设置选择高亮
// openselect: 1,
// closeselect: undefined,
issue_tag_ids: "标签", issue_tag_ids: "标签",
tracker_ids: "所有分类", tracker_ids: "类型",
author_ids: "发布人", author_ids: "发布人",
assigned_to_ids: "指派人", assigned_to_ids: "负责人",
priority_ids: "优先度", fixed_version_ids: "里程碑",
status_ids: "状态",
done_ratios: "完成度", done_ratios: "完成度",
paix: "排序", paix: "排序",
update_author_ids:"更换负责人",
update_fixed_version_ids:'更换里程碑',
update_status_ids:"修改状态",
begin: '',
end: '',
checkedValue: [],
allValue: [],
all: false,
select_params: { select_params: {
status_type: "1", //开启中和关闭中,默认为开启中的 status_type: undefined, //开启中和关闭中,全部,默认为全部
assigned_to_id: undefined, // 指派人 assigned_to_id: undefined, // 负责
author_id: undefined, // 发布人 author_id: undefined, // 发布人
issue_tag_id: undefined, // 标签 issue_tag_id: undefined, // 标签
tracker_id: undefined, //所有分类 tracker_id: undefined, //
done_ratio: undefined, // 完成度 done_ratio: undefined, // 完成度
priority_id: undefined, // 优先级 status_id: undefined, // 优先级
fixed_version_id: undefined,//里程碑
order_name: undefined, order_name: undefined,
order_type: undefined, order_type: undefined,
search: undefined, search: undefined,
update_author_id:undefined,
update_fixed_version_id:undefined,
update_status_id:undefined,
page: 1, page: 1,
limit: 15, limit: 15,
}, },
@ -71,15 +81,17 @@ class order extends Component {
}; };
getSelectList = () => { getSelectList = () => {
this.setState({
isSpin: true
})
const { projectsId } = this.props.match.params; const { projectsId } = this.props.match.params;
const url = `/projects/${projectsId}/issues/index_chosen.json`; const url = `/projects/${projectsId}/issues/index_chosen.json`;
axios axios.get(url).then((result) => {
.get(url)
.then((result) => {
if (result) { if (result) {
this.setState({ this.setState({
issue_chosen: result.data.issue_chosen, issue_chosen: result.data.issue_chosen,
isSpin: false
}); });
} }
}) })
@ -89,21 +101,32 @@ class order extends Component {
}; };
// 获取列表数据 // 获取列表数据
getIssueList = () => { getIssueList = (begin, end) => {
this.setState({
isSpin: true
})
const { select_params } = this.state; const { select_params } = this.state;
const { projectsId } = this.props.match.params; const { projectsId } = this.props.match.params;
const url = `/projects/${projectsId}/issues.json`; const url = `/projects/${projectsId}/issues.json`;
axios axios
.get(url, { .get(url, {
params: select_params, params: {
...select_params,
start_date: begin,
due_date: end
}
}) })
.then((result) => { .then((result) => {
if (result) { if (result) {
const issues = result.data.issues
this.setState({ this.setState({
data: result.data, data: result.data,
issues: result.data.issues, issues: issues,
search_count: result.data.search_count, search_count: result.data.search_count,
isSpin: false, isSpin: false,
allValue: issues && issues.length > 0 && issues.map((item) => {
return (item.id)
})
}); });
} }
}) })
@ -145,35 +168,38 @@ class order extends Component {
this.getIssueList(); this.getIssueList();
}; };
getOption = (e, id, name) => { getOption = (e, id, name,toGet) => {
const { current_user } = this.props; const { current_user } = this.props;
this.setState({
isSpin: true,
});
let option_id = e.key === "all" ? undefined : e.key; let option_id = e.key === "all" ? undefined : e.key;
this.state.select_params[`${id}`] = option_id;
this.state.select_params.page = 1;
this.state[`${id}s`] = name;
if (current_user) {
if ( this.state.select_params.author_id && parseInt(this.state.select_params.author_id) === current_user.user_id) {
this.state.author_id = current_user.user_id;
} else {
this.state.author_id = undefined;
}
if (this.state.select_params.assigned_to_id && parseInt(this.state.select_params.assigned_to_id) === current_user.user_id ) { let select_params = this.state.select_params;
this.state.assigned_to_id = current_user.user_id; select_params[`${id}`] = option_id;
select_params.page = 1;
if (current_user) {
if (this.state.select_params.author_id && parseInt(this.state.select_params.author_id) === current_user.user_id) {
select_params.author_id = current_user.user_id;
} else { } else {
this.state.assigned_to_id = undefined; select_params.author_id = undefined;
}
if (this.state.select_params.assigned_to_id && parseInt(this.state.select_params.assigned_to_id) === current_user.user_id) {
select_params.assigned_to_id = current_user.user_id;
} else {
select_params.assigned_to_id = undefined;
} }
} }
this.setState({
[`${id}s`] : name,
select_params
});
if(!toGet){
this.getIssueList(); this.getIssueList();
}
}; };
renderMenu = (array, name, id) => { renderMenu = (array, name, id,toGet) => {
return ( return (
<Menu> <Menu>
<Menu.Item key={"all"} onClick={(e) => this.getOption(e, id, name)}> <Menu.Item key={"all"} onClick={(e) => this.getOption(e, id, name,toGet)}>
{name} {name}
</Menu.Item> </Menu.Item>
{array && {array &&
@ -182,7 +208,7 @@ class order extends Component {
return ( return (
<Menu.Item <Menu.Item
key={item.id} key={item.id}
onClick={(e) => this.getOption(e, id, item.name)} onClick={(e) => this.getOption(e, id, item.name,toGet)}
> >
{item.name} {item.name}
</Menu.Item> </Menu.Item>
@ -196,6 +222,8 @@ class order extends Component {
ChangePage = (page) => { ChangePage = (page) => {
this.setState({ this.setState({
isSpin: true, isSpin: true,
checkedValue: [],
all: false
}); });
this.state.select_params.page = page; this.state.select_params.page = page;
this.getIssueList(); this.getIssueList();
@ -213,19 +241,15 @@ class order extends Component {
}; };
openorder = (type) => { openorder = (type) => {
this.setState({
isSpin: true,
});
if (type) {
this.setState({ this.setState({
author_id: undefined, author_id: undefined,
assigned_to_id: undefined, assigned_to_id: undefined,
status_type: type, status_type: type,
issue_tag_ids: "标签", issue_tag_ids: "标签",
tracker_ids: "所有分类", tracker_ids: "",
author_ids: "发布人", author_ids: "发布人",
assigned_to_ids: "指派人", assigned_to_ids: "负责人",
priority_ids: "优先度", status_ids: "状态",
done_ratios: "完成度", done_ratios: "完成度",
paix: "排序", paix: "排序",
}); });
@ -236,7 +260,6 @@ class order extends Component {
limit: 15, limit: 15,
}; };
this.getIssueList(); this.getIssueList();
}
}; };
// 筛选:全部、指派给我、由我创建 // 筛选:全部、指派给我、由我创建
@ -266,7 +289,7 @@ class order extends Component {
author_ids: current_user.username, author_ids: current_user.username,
author_id: current_user.user_id, author_id: current_user.user_id,
assigned_to_id: undefined, assigned_to_id: undefined,
assigned_to_ids: "指派人", assigned_to_ids: "负责人",
}); });
this.state.select_params.assigned_to_id = undefined; this.state.select_params.assigned_to_id = undefined;
this.state.select_params.author_id = current_user.user_id; this.state.select_params.author_id = current_user.user_id;
@ -275,7 +298,7 @@ class order extends Component {
this.setState({ this.setState({
author_ids: "发布人", author_ids: "发布人",
author_id: undefined, author_id: undefined,
assigned_to_ids: "指派人", assigned_to_ids: "负责人",
assigned_to_id: undefined, assigned_to_id: undefined,
}); });
this.state.select_params.assigned_to_id = undefined; this.state.select_params.assigned_to_id = undefined;
@ -288,8 +311,7 @@ class order extends Component {
deletedetail = (id) => { deletedetail = (id) => {
const { projectsId } = this.props.match.params; const { projectsId } = this.props.match.params;
const url = `/projects/${projectsId}/issues/${id}.json`; const url = `/projects/${projectsId}/issues/${id}.json`;
axios axios.delete(url, {
.delete(url, {
data: { data: {
project_id: projectsId, project_id: projectsId,
id: id, id: id,
@ -303,7 +325,6 @@ class order extends Component {
.catch((error) => { .catch((error) => {
console.log(error); console.log(error);
}); });
console.log(id);
}; };
islogin() { islogin() {
@ -316,9 +337,127 @@ class order extends Component {
} }
} }
// 修改开始时间
changeBeginTime = (data, value) => {
this.setState({
begin: value
})
this.getIssueList(value, this.state.end);
}
changeEndTime = (data, value) => {
this.setState({
end: value
})
this.getIssueList(this.state.begin, value);
}
// 选择列表里面的checkbox
checkIssues = (value) => {
this.setState({
checkedValue: value
})
const { allValue } = this.state;
this.setState({
all: allValue && value && value.length === allValue.length,
})
// 不勾选数据时清除右上角选择的项
if(value.length === 0){
this.setState({
update_author_ids:"更换负责人",
update_fixed_version_ids:"更换里程碑",
update_status_ids:"修改状态",
select_params:{
update_author_id:undefined,
update_fixed_version_id:undefined,
update_status_id:undefined
}
})
}
}
// 全选和反选
changeAll = (e) => {
if (e.target.checked) {
const { allValue } = this.state;
this.setState({
checkedValue: allValue
})
} else {
this.setState({
checkedValue: []
})
}
this.setState({
all: e.target.checked
})
}
// 批量修改
updateIssues=()=>{
const {checkedValue , select_params} = this.state;
const { projectsId } = this.props.match.params;
this.setState({
isSpin:true
})
const url = `/projects/${projectsId}/issues/series_update.json`;
axios.post(url,{
ids:checkedValue,
assigned_to_id:select_params.update_author_id,
fixed_version_id:select_params.update_fixed_version_id,
status_id:select_params.update_status_id
}).then(result=>{
if(result){
this.props.showNotification("修改成功!");
this.successFunc();
}
}).catch(error=>{
console.log(error);
})
}
successFunc=()=>{
let select_params = this.state.select_params;
select_params.update_author_id = undefined;
select_params.update_fixed_version_id = undefined;
select_params.update_status_id = undefined;
this.setState({
all:false,
checkedValue:[],
update_author_ids:"更换负责人",
update_fixed_version_ids:"更换里程碑",
update_status_ids:"修改状态",
select_params
})
this.getIssueList();
}
// 批量删除
deleteIssues=()=>{
this.props.confirm({
content: "是否确认删除所有选中的任务?",
onOk:()=>{
this.setState({
isSpin:true
})
const { checkedValue } = this.state;
const { projectsId } = this.props.match.params;
const url = `/projects/${projectsId}/issues/clean.json`;
axios.post(url,{
ids:checkedValue
}).then(result=>{
if(result){
this.props.showNotification("删除成功!");
this.successFunc();
}
}).catch(error=>{
console.log(error);
})
}
})
}
render() { render() {
const { current_user } = this.props; const { current_user } = this.props;
const { const {
issue_chosen, issue_chosen,
issues, issues,
@ -329,8 +468,8 @@ class order extends Component {
isSpin, isSpin,
status_type, status_type,
select_params, select_params,
begin, end, checkedValue, all
} = this.state; } = this.state;
const menu = ( const menu = (
<Menu onClick={(e) => this.getMenu(e)}> <Menu onClick={(e) => this.getMenu(e)}>
<Menu.Item key={"created_on-desc"} value="desc"> <Menu.Item key={"created_on-desc"} value="desc">
@ -367,44 +506,65 @@ class order extends Component {
); );
return ( return (
<div className="main"> <div className="main">
<div className="topWrapper" style={{ borderBottom: "none" }}> <div className="topWrapper" style={{ paddingTop: "10px" }}>
<p className="topWrapper_type"> <ul className="topWrapper_type">
<li <li>
<label>所有</label>
<span
className={status_type ? "" : "active"}
onClick={() => this.openorder()}
>{data && data.all_count}</span>
</li>
<li>
<label>开启中</label>
<span
className={status_type === "1" ? "active" : ""} className={status_type === "1" ? "active" : ""}
onClick={() => this.openorder("1")} onClick={() => this.openorder("1")}
> >{data && data.open_count}</span>
{data && data.open_count}个开启中
</li> </li>
<li <li>
className={status_type === "2" ? "active" : ""} <label>已关闭</label>
onClick={() => this.openorder("2")} <span className={status_type === "2" ? "active" : ""}
> onClick={() => this.openorder("2")}>{data && data.close_count}</span>
{data && data.close_count}个已关闭
</li> </li>
</p> </ul>
<a className="topWrapper_btn ml10" onClick={() => this.islogin()}>
<div className="topWrapper_select"> +&nbsp;创建任务
</a>
</div>
<div className="topWrapper">
<div className="target-detail-search"> <div className="target-detail-search">
<Search <Search
placeholder="搜索" placeholder="输入issue名称进行搜索"
enterButton enterButton
onSearch={this.searchFunc} onSearch={this.searchFunc}
style={{ width: 300 }} style={{ width: 300 }}
/> />
</div> </div>
<a className="topWrapper_btn ml10" onClick={() => this.islogin()}> <div>
创建任务 <DatePicker
</a> value={begin ? moment(begin, 'YYYY-MM-DD') : ""}
style={{ marginRight: "20px" }}
placeholder="请选择开始时间"
onChange={this.changeBeginTime}
/>
<DatePicker value={end ? moment(end, 'YYYY-MM-DD') : ""} placeholder="请选择结束时间" onChange={this.changeEndTime} />
</div> </div>
</div> </div>
<Spin spinning={isSpin}> <Spin spinning={isSpin}>
<div className="f-wrap-between mb20"> <div className="f-wrap-between screenWrap">
<ul className="topWrapper_type"> <div className="df">
<Checkbox value="0" style={{ lineHeight: "50px", margin: "0px 15px 0px 20px" }} checked={all} onChange={this.changeAll}></Checkbox>
{checkedValue && checkedValue.length > 0 ?
<span style={{ lineHeight: "50px" }}>选中{checkedValue.length}个issue</span>
:
<ul className="searchBanner">
<li <li
className={!author_id && !assigned_to_id ? "active" : ""} className={!author_id && !assigned_to_id ? "active" : ""}
onClick={() => this.ChangeAssign()} onClick={() => this.ChangeAssign()}
> >
全部 <label>搜索结果</label>
<span>{data && data.search_count}</span>
</li> </li>
<li <li
style={{ style={{
@ -414,7 +574,8 @@ class order extends Component {
className={assigned_to_id ? "active" : ""} className={assigned_to_id ? "active" : ""}
onClick={() => this.ChangeAssign(1)} onClick={() => this.ChangeAssign(1)}
> >
指派给我 <label>指派给我</label>
<span>{data && data.assign_me_count}</span>
</li> </li>
<li <li
style={{ style={{
@ -424,9 +585,70 @@ class order extends Component {
className={author_id ? "active" : ""} className={author_id ? "active" : ""}
onClick={() => this.ChangeAssign(2)} onClick={() => this.ChangeAssign(2)}
> >
由我创建 <label>我的发布</label>
<span>{data && data.my_published_count}</span>
</li> </li>
</ul> </ul>
}
</div>
{
checkedValue && checkedValue.length>0 ?
<ul className="topWrapper_select">
<li className="mr20">
<Dropdown
className="topWrapperSelect"
overlay={this.renderMenu(
issue_chosen && issue_chosen.assign_user,
"更换负责人",
"update_author_id",true
)}
trigger={["click"]}
placement="bottomCenter"
>
<span>
{this.state.update_author_ids}
<Icon type="caret-down" className="ml5" />
</span>
</Dropdown>
</li>
<li className="mr20">
<Dropdown
className="topWrapperSelect"
overlay={this.renderMenu(
issue_chosen && issue_chosen.issue_version,
"更换里程碑",
"update_fixed_version_id",true
)}
trigger={["click"]}
placement="bottomCenter"
>
<span>
{this.state.update_fixed_version_ids}
<Icon type="caret-down" className="ml5" />
</span>
</Dropdown>
</li>
<li className="mr20">
<Dropdown
className="topWrapperSelect"
overlay={this.renderMenu(
issue_chosen && issue_chosen.issue_status,
"修改状态",
"update_status_id",true
)}
trigger={["click"]}
placement="bottomCenter"
>
<span>
{this.state.update_status_ids}
<Icon type="caret-down" className="ml5" />
</span>
</Dropdown>
</li>
<a onClick={this.updateIssues} className="updateBtn blue mr20">确定</a>
<a onClick={this.deleteIssues} className="updateBtn red mr20">删除</a>
</ul>
:
<ul className="topWrapper_select"> <ul className="topWrapper_select">
<li> <li>
<Dropdown <Dropdown
@ -445,23 +667,6 @@ class order extends Component {
</span> </span>
</Dropdown> </Dropdown>
</li> </li>
<li>
<Dropdown
className="topWrapperSelect"
overlay={this.renderMenu(
issue_chosen && issue_chosen.tracker,
"所有分类",
"tracker_id"
)}
trigger={["click"]}
placement="bottomCenter"
>
<span>
{this.state.tracker_ids}
<Icon type="caret-down" className="ml5" />
</span>
</Dropdown>
</li>
<li> <li>
<Dropdown <Dropdown
className="topWrapperSelect" className="topWrapperSelect"
@ -484,7 +689,7 @@ class order extends Component {
className="topWrapperSelect" className="topWrapperSelect"
overlay={this.renderMenu( overlay={this.renderMenu(
issue_chosen && issue_chosen.assign_user, issue_chosen && issue_chosen.assign_user,
"指派人", "负责人",
"assigned_to_id" "assigned_to_id"
)} )}
trigger={["click"]} trigger={["click"]}
@ -500,15 +705,49 @@ class order extends Component {
<Dropdown <Dropdown
className="topWrapperSelect" className="topWrapperSelect"
overlay={this.renderMenu( overlay={this.renderMenu(
issue_chosen && issue_chosen.priority, issue_chosen && issue_chosen.tracker,
"优先度", "类型",
"priority_id" "tracker_id"
)} )}
trigger={["click"]} trigger={["click"]}
placement="bottomCenter" placement="bottomCenter"
> >
<span> <span>
{this.state.priority_ids} {this.state.tracker_ids}
<Icon type="caret-down" className="ml5" />
</span>
</Dropdown>
</li>
<li>
<Dropdown
className="topWrapperSelect"
overlay={this.renderMenu(
issue_chosen && issue_chosen.issue_version,
"里程碑",
"fixed_version_id"
)}
trigger={["click"]}
placement="bottomCenter"
>
<span>
{this.state.fixed_version_ids}
<Icon type="caret-down" className="ml5" />
</span>
</Dropdown>
</li>
<li>
<Dropdown
className="topWrapperSelect"
overlay={this.renderMenu(
issue_chosen && issue_chosen.issue_status,
"状态",
"status_id"
)}
trigger={["click"]}
placement="bottomCenter"
>
<span>
{this.state.status_ids}
<Icon type="caret-down" className="ml5" /> <Icon type="caret-down" className="ml5" />
</span> </span>
</Dropdown> </Dropdown>
@ -544,12 +783,20 @@ class order extends Component {
</Dropdown> </Dropdown>
</li> </li>
</ul> </ul>
}
</div> </div>
{search_count === 0 ? ( {search_count === 0 ? (
<NoneData _html="暂时还没有相关数据哦!" /> <NoneData _html="暂时还没有相关数据哦!" />
) : ( ) : (
<div style={{ minHeight: "500px" }}>
<Checkbox.Group name="issues" onChange={this.checkIssues} value={checkedValue} style={{ width: "100%" }}>
{issues && issues.length > 0 && issues.map((item, key) => {
return (
<OrderItem <OrderItem
issues={issues} key={key}
item={item}
checkbox={<Checkbox value={item.id} key={item.id} style={{ margin: '4px 15px 0px 20px' }}></Checkbox>}
search_count={search_count} search_count={search_count}
page={select_params.page} page={select_params.page}
limit={select_params.limit} limit={select_params.limit}
@ -557,6 +804,10 @@ class order extends Component {
{...this.state} {...this.state}
deletedetail={this.deletedetail} deletedetail={this.deletedetail}
></OrderItem> ></OrderItem>
)
})}
</Checkbox.Group>
</div>
)} )}
{Paginations} {Paginations}
</Spin> </Spin>

View File

@ -73,9 +73,18 @@ class Index extends Component {
array && this.props.load && this.props.load(array); array && this.props.load && this.props.load(array);
} }
beforeUpload = (file)=>{
const { size } = this.props;
const isLt100M = file.size / 1024 / 1024 < size;
if (!isLt100M) {
this.props.showNotification(`文件大小必须小于${size}MB!`);
}
return isLt100M;
}
render() { render() {
//判断是否已经提交,如已提交评论则上一条评论数据清除 //判断是否已经提交,如已提交评论则上一条评论数据清除
const { isComplete } = this.props; const { isComplete , icon } = this.props;
const { fileList } = this.state; const { fileList } = this.state;
let list = isComplete === true ? fileList : undefined; let list = isComplete === true ? fileList : undefined;
@ -85,13 +94,14 @@ class Index extends Component {
action: `${getUploadActionUrl()}`, action: `${getUploadActionUrl()}`,
onChange: this.handleChange, onChange: this.handleChange,
onRemove: this.onAttachmentRemove, onRemove: this.onAttachmentRemove,
beforeUpload:this.beforeUpload
}; };
return ( return (
<div> <div>
<Dragger {...upload} > <Dragger {...upload} className={this.props.className}>
<Icon type="inbox" /> { icon || <Icon type="inbox" />}
<p className="ant-upload-text">拖动文件或者点击此处上传</p> <p className="ant-upload-text">拖动文件或<span className="color-blue">点击此处上传</span></p>
</Dragger> </Dragger>
</div> </div>
) )

View File

@ -0,0 +1,20 @@
import React from 'react';
import { Tag } from 'antd';
/**
* tagname:标签名字
* className:额外需要添加的样式
*/
export function TagInfo (tagname,className){
let color = '#e74c3c';
if(tagname === "高"){
color = '#e67e22';
}else if(tagname === "正常"){
color = '#28be6c';
}else if(tagname === "低"){
color = '#1abc9c';
}else{
color = '#e74c3c';
}
return (<Tag color={color} className={className} style={{height:'25px',lineHeight:"23px"}}>{tagname}</Tag>);
}

View File

@ -1,5 +1,5 @@
function getDateTime(value, dataformat) { export function getDateTime(value, dataformat) {
Date.prototype.format = function (format) { Date.prototype.format = function (format) {
var date = { var date = {
"M+": this.getMonth() + 1, "M+": this.getMonth() + 1,

View File

@ -1,70 +1,275 @@
import React , { useState, useEffect } from 'react'; import React, { useState, useEffect, useCallback, forwardRef } from "react";
import styled from 'styled-components'; import styled from "styled-components";
import { AutoComplete } from 'antd'; import { AutoComplete, Select, Input, Checkbox, Button, Form } from "antd";
import axios from 'axios';
import './version.css';
export default (projectDetail)=>{ import Editor from "../../modules/tpm/challengesnew/tpm-md-editor";
const [ tagList , setTagList ] = useState(undefined); import Upload from "../Upload/Index";
import Attachments from "../Upload/attachment";
import axios from "axios";
import "./version.css";
import UploadImg from "../Images/upload.png";
const { Option } = AutoComplete;
export default Form.create()(
forwardRef(
(
{ form, projectDetail, branchs, match, showNotification, history },
ref
) => {
const { getFieldDecorator, validateFields, setFieldsValue } = form;
const [tagList, setTagList] = useState(undefined);
const [branchList, setBranchList] = useState(undefined);
const [desc, setDesc] = useState(null);
const [fileList, setFileList] = useState(undefined);
const [attachment, setAttachment] = useState(undefined);
const SelectDiv = styled.div` const SelectDiv = styled.div`
display:flex; display: flex;
align-item:center; align-item: center;
margin-bottom:5px; margin-bottom: 5px;
& span{ & .ant-row {
margin:0px 15px; margin-bottom: 0px;
color:#BBB;
line-height:35px;
} }
`; `;
const Span = styled.span`
margin: 0px 15px;
color: #bbb;
line-height: 35px;
`;
const repo_id = projectDetail && projectDetail.repo_id; const repo_id = projectDetail && projectDetail.repo_id;
const { projectsId, versionId } = match.params;
useEffect(() => {
if (branchs) {
setBranchList(branchs);
}
}, [branchs]);
useEffect(() => {
if (versionId) {
const url = `/projects/${projectsId}/version_releases/${versionId}/edit.json`;
axios.get(url).then(result => {
if (result) {
setFieldsValue(result.data);
setDesc(result.data.body);
setAttachment(result.data.attachments);
}
});
}
}, [versionId]);
useEffect(() => { useEffect(() => {
if (repo_id) { if (repo_id) {
const url = `/repositories/${repo_id}/tags.json`; const url = `/repositories/${repo_id}/tags.json`;
axios.get(url).then((result) => { axios
.get(url)
.then(result => {
if (result) { if (result) {
setTagList(result.data); setTagList(result.data);
} }
}).catch(error => {
console.log(error);
}) })
.catch(error => {
console.log(error);
});
} }
}, [repo_id]); }, [repo_id]);
return(
function renderTagList(list) {
if (list) {
let array = list.map((item, key) => {
return (
<Option key={key} value={item.name}>
{item.name}
</Option>
);
});
return array || undefined;
}
}
function changeT(value) {
// let l = tagList.filter(item => item.name.indexOf(value) > -1);
// setTagList(l);
}
function submit() {
validateFields((err, value) => {
if (versionId) {
let url = `/projects/${projectsId}/version_releases/${versionId}.json`;
axios
.put(url, {
...value,
body: desc,
attachment_ids: fileList
})
.then(result => {
if (result) {
showNotification("版本修改成功!");
history.push(`/projects/${projectsId}/coders/version`);
}
});
} else {
let url = `/projects/${projectsId}/version_releases.json`;
axios
.post(url, {
...value,
body: desc,
attachment_ids: fileList
})
.then(result => {
if (result) {
showNotification("版本发布成功!");
history.push(`/projects/${projectsId}/coders/version`);
}
});
}
});
}
const helper = useCallback(
(label, name, rules, widget, isRequired = true) => (
<React.Fragment>
<span required={isRequired}>{label}</span>
<Form.Item>
{getFieldDecorator(name, { rules, validateFirst: true })(widget)}
</Form.Item>
</React.Fragment>
),
[]
);
return (
<div className="main df"> <div className="main df">
<div className="versionForm"> <Form className="versionForm">
<div>
<p className="font-16 color-grey-3 mb15">创建发行版</p> <p className="font-16 color-grey-3 mb15">创建发行版</p>
<div> <div>
<SelectDiv> <SelectDiv>
<div className="tagComplete"> {helper(
<i className="iconfont icon-biaoqian3 font-14 color-grey-8"></i> "",
"tag_name",
[{ required: true, message: "请输入获取或选择一个标签" }],
<AutoComplete <AutoComplete
placeholder="标记一个版本" placeholder="标记一个版本"
dataSource={tagList} dataSource={renderTagList(tagList)}
onChange={changeT}
style={{ width: "200px" }}
></AutoComplete> ></AutoComplete>
<i className="iconfont icon-jiantou9 color-grey-8 font-14 ml5"></i> )}
</div> <Span>@</Span>
<span>@</span> {helper(
"",
"target_commitish",
[{ required: true, message: "请选择一个分支" }],
<Select
placeholder="请选择一个分支"
style={{ width: "200px" }}
showArrow={false}
>
{renderTagList(branchList)}
</Select>
)}
</SelectDiv> </SelectDiv>
<p className="font-13 color-grey-8">选择一个已经存在的标签或者在发布时新建一个标签</p> <p className="font-13 color-grey-8">
选择一个已经存在的标签或者在发布时新建一个标签
</p>
</div> </div>
<div className="pt20">
{helper(
"",
"name",
[{ required: true, message: "请输入发行版的标题" }],
<Input placeholder="发行版的标题" />
)}
</div> </div>
<div>
<Editor
placeholder={"描述此发行版"}
height={200}
mdID={`version-comments-description`}
initValue={desc}
onChange={setDesc}
/>
</div>
<div className="set-ant-row">
{helper(
"",
"prerelease",
[],
<Checkbox>这是一个预览版本</Checkbox>
)}
</div>
<div>
<Upload
className="versionStyle"
isComplete={true}
load={setFileList}
icon={
<img
src={UploadImg}
width="58"
alt=""
style={{ marginBottom: 15 }}
/>
}
size={100}
showNotification={showNotification}
/>
{versionId && attachment && attachment.length > 0 ? (
<Attachments
attachments={attachment}
showNotification={showNotification}
canDelete={true}
/>
) : (
""
)}
</div>
<p className="pt20">
<Button onClick={submit} type="primary" className="mr30">
{versionId ? "保存" : "创建"}发行版
</Button>
<Button
onClick={() =>
history.push(`/projects/${projectsId}/coders/version`)
}
style={{
backgroundColor: "rgba(187,187,187,1)",
color: "#fff"
}}
>
取消
</Button>
</p>
</div>
</Form>
<div className="versionTips"> <div className="versionTips">
<div className="infosTip"> <div className="infosTip">
<p className="font-16 mb15">标签命名建议</p> <p className="font-16 mb15">标签命名建议</p>
<p className="mb15">通常的做法是在版本名称前加上字母 v 前缀 v1.0 或者 v2.3.4</p> <p className="mb15">
<p>如果标签不适合在生产环境下使用请在版本名称后添加预发行版本例如v0.2-alpha 或者 v5.9-beta.3</p> 通常的做法是在版本名称前加上字母 v 前缀 v1.0 或者 v2.3.4
</p>
<p>
如果标签不适合在生产环境下使用请在版本名称后添加预发行版本例如v0.2-alpha
或者 v5.9-beta.3
</p>
</div> </div>
<div className="infosTip"> <div className="infosTip">
<p className="font-16 mb15">语义化版本</p> <p className="font-16 mb15">语义化版本</p>
<p className="mb15">如果你是第一次发布版本我们强烈建议你阅读语义化版本</p> <p className="mb15">
如果你是第一次发布版本我们强烈建议你阅读语义化版本
</p>
</div> </div>
<div className="infosTip"> <div className="infosTip">
<p className="font-16 mb15">附件大小说明</p> <p className="font-16 mb15">附件大小说明</p>
<p className="mb15">单个附件不能超过 100MGVP 项目200M每个仓库总附件不可超过 1G推荐项目不可超过 5GGVP 项目不可超过 20G附件总容量统计包括仓库附件和发行版附件</p> <p className="mb15">
单个附件不能超过 100MGVP 项目200M每个仓库总附件不可超过
1G推荐项目不可超过 5GGVP 项目不可超过
20G附件总容量统计包括仓库附件和发行版附件
</p>
</div> </div>
</div> </div>
</div> </div>
);
}
) )
} );

View File

@ -186,18 +186,12 @@
margin-bottom: 22px; margin-bottom: 22px;
color: #333; color: #333;
} }
.tagComplete{ .versionStyle{
border:1px solid #d9d9d9; height: 200px!important;
padding:0px 10px; border: 1px dashed rgba(80,145,255,1)!important;
border-radius: 4px; }
.set-ant-row .ant-row{
display: flex; display: flex;
height: 20px;
align-items: center; align-items: center;
width: 240px;
}
.tagComplete .ant-select{
flex: 1;
}
.tagComplete .ant-select-auto-complete.ant-select .ant-input{
border:none!important;
background-color: #fff!important;
} }