Merge branch 'develop'
This commit is contained in:
commit
c0f7c489fb
|
@ -3912,7 +3912,6 @@ html>body #ajax-indicator {
|
|||
text-align: center;
|
||||
height: 70px;
|
||||
box-sizing: border-box;
|
||||
min-width: 780px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
|
|
@ -4,7 +4,7 @@ import axios from 'axios';
|
|||
import { getImageUrl } from 'educoder';
|
||||
|
||||
const { Option } = AutoComplete;
|
||||
function AddMember({getID,login}){
|
||||
function AddMember({getID,login,showNotification}){
|
||||
const [ id , setID ] = useState(undefined);
|
||||
const [ source , setSource ] = useState(undefined);
|
||||
const [ searchKey , setSearchKey ] = useState(undefined);
|
||||
|
@ -45,7 +45,7 @@ function AddMember({getID,login}){
|
|||
src={getImageUrl(`/${item && item.image_url}`)}
|
||||
alt=""
|
||||
/>
|
||||
<span className="ml10" style={{ "vertical-align": "middle" }}>
|
||||
<span className="ml10" style={{ verticalAlign: "middle" }}>
|
||||
{item.username}
|
||||
<span className="color-grey ml10">({item.login})</span>
|
||||
</span>
|
||||
|
@ -66,7 +66,12 @@ function AddMember({getID,login}){
|
|||
};
|
||||
|
||||
function addCollaborator(){
|
||||
getID && getID(id);
|
||||
if(source && source.length>0){
|
||||
getID && getID(id);
|
||||
setSearchKey(undefined);
|
||||
}else{
|
||||
showNotification("请选择存在的用户!");
|
||||
}
|
||||
}
|
||||
|
||||
return(
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
import React, { useState , forwardRef, useEffect } from 'react';
|
||||
import { Form , Modal , Input , Radio } from 'antd';
|
||||
import Axios from 'axios';
|
||||
|
||||
export default Form.create()(
|
||||
forwardRef((props)=>{
|
||||
const { getFieldDecorator, validateFields , setFieldsValue } = props && props.form;
|
||||
const [ visible , setVisible ] = useState(false);
|
||||
|
||||
useEffect(()=>{
|
||||
if(!visible){
|
||||
setFieldsValue({
|
||||
code:undefined,
|
||||
role:"developer"
|
||||
})
|
||||
}
|
||||
},[visible])
|
||||
|
||||
function onOk() {
|
||||
validateFields((error,values)=>{
|
||||
if(!error){
|
||||
const url = `/applied_projects.json`;
|
||||
Axios.post(url,{
|
||||
applied_project:{
|
||||
...values
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result && result.data){
|
||||
setVisible(false);
|
||||
props.showNotification("申请加入项目成功,等待审核!");
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
})
|
||||
}
|
||||
function checkValue(rule, value, callback){
|
||||
if(!value){
|
||||
callback();
|
||||
}
|
||||
if(value.length < 6 || value.length > 6){
|
||||
callback("请输入6位数的邀请码");
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
||||
return(
|
||||
<React.Fragment>
|
||||
<Modal
|
||||
title="加入项目"
|
||||
width="480px"
|
||||
visible={visible}
|
||||
centered={true}
|
||||
onOk={onOk}
|
||||
onCancel={()=>setVisible(false)}
|
||||
>
|
||||
<Form layout={'inline'} className="inviteForm">
|
||||
<Form.Item label="项目邀请码">
|
||||
{getFieldDecorator("code",{
|
||||
rules:[
|
||||
{required:true,message:"请输入6位项目邀请码"},
|
||||
{validator:checkValue}
|
||||
]
|
||||
})(
|
||||
<Input placeholder="请输入6位项目邀请码" autoComplete={"off"} maxLength="6" style={{width:"300px"}}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="选择角色">
|
||||
{getFieldDecorator("role",{
|
||||
rules:[{required:true,message:"请选择角色"}]
|
||||
})(
|
||||
<Radio.Group defaultValue={"developer"}>
|
||||
<Radio value="manager">管理员</Radio>
|
||||
<Radio value="developer">开发者</Radio>
|
||||
<Radio value="reporter">报告者</Radio>
|
||||
</Radio.Group>
|
||||
)}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
<a onClick={()=>setVisible(true)}>加入项目</a>
|
||||
</React.Fragment>
|
||||
)
|
||||
})
|
||||
)
|
|
@ -2,13 +2,11 @@ import React, { Component } from 'react';
|
|||
import AccountProfile from "../../modules/user/AccountProfile";
|
||||
import { getImageUrl } from 'educoder'
|
||||
import axios from 'axios';
|
||||
import { Modal, Input, message, notification , Dropdown , Menu } from 'antd';
|
||||
import { Input , notification , Dropdown , Menu } from 'antd';
|
||||
|
||||
import LoginDialog from '../../modules/login/LoginDialog';
|
||||
import GotoQQgroup from '../../modal/GotoQQgroup'
|
||||
|
||||
import AddProjectModal from './AddProjectModal';
|
||||
import '../../modules/tpm/TPMIndex.css';
|
||||
import logo from '../../modules/tpm/images/logo.png';
|
||||
|
||||
import './header.scss';
|
||||
const $ = window.$
|
||||
|
@ -33,11 +31,9 @@ class NewHeader extends Component {
|
|||
Checkboxteachertype: false,
|
||||
Checkboxteachingtype: false,
|
||||
code_notice: false,
|
||||
checked_notice: false,
|
||||
RadioGroupvalue: undefined,
|
||||
submitapplications: false,
|
||||
isRender: false,
|
||||
showSearchOpentype: false,
|
||||
showTrial: false,
|
||||
setevaluatinghides: false,
|
||||
occupation: 0,
|
||||
|
@ -45,7 +41,6 @@ class NewHeader extends Component {
|
|||
headtypesonClickbool: false,
|
||||
headtypess: "/",
|
||||
settings: null,
|
||||
goshowqqgtounp: false,
|
||||
visiblemyss: false,
|
||||
openSearch:false,
|
||||
}
|
||||
|
@ -93,10 +88,11 @@ class NewHeader extends Component {
|
|||
}, 300)
|
||||
}}
|
||||
>
|
||||
<Search placeholder="实践课程/教学课堂/实践项目/交流问答"
|
||||
<Search placeholder="请输入搜索关键字"
|
||||
className={`search-input mr20`}
|
||||
onSearch={(value)=>this.onGlobalSearch(value,item)}
|
||||
autoFocus={true}
|
||||
style={{width:"260px"}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
@ -130,43 +126,7 @@ class NewHeader extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
submitsubmitapplications = () => {
|
||||
let {
|
||||
submitapplicationssum,
|
||||
submitapplicationsvaluedata
|
||||
} = this.state;
|
||||
this.setState({
|
||||
submitapplications: false,
|
||||
RadioGroupvalue: undefined
|
||||
})
|
||||
if (submitapplicationssum === 0) {
|
||||
if (submitapplicationsvaluedata !== undefined) {
|
||||
window.location.href = "/courses/" + submitapplicationsvaluedata;
|
||||
}
|
||||
} else if (submitapplicationssum === 1) {
|
||||
if (submitapplicationsvaluedata !== undefined) {
|
||||
window.location.href = "/projects/" + submitapplicationsvaluedata;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hidesubmitapplications = () => {
|
||||
this.setState({
|
||||
Addcoursestypes: false,
|
||||
tojoinitemtype: false,
|
||||
tojoinclasstitle: undefined,
|
||||
rolearr: ["", ""],
|
||||
Checkboxteacherchecked: false,
|
||||
Checkboxstudentchecked: false,
|
||||
Checkboxteachingchecked: false,
|
||||
Checkboxteachertype: false,
|
||||
Checkboxteachingtype: false,
|
||||
code_notice: false,
|
||||
checked_notice: false,
|
||||
submitapplications: false,
|
||||
RadioGroupvalue: undefined
|
||||
})
|
||||
}
|
||||
educoderlogin = () => {
|
||||
//登录账号
|
||||
this.setState({
|
||||
|
@ -205,23 +165,6 @@ class NewHeader extends Component {
|
|||
};
|
||||
|
||||
|
||||
hidetojoinclass = () => {
|
||||
this.setState({
|
||||
tojoinclasstype: false,
|
||||
tojoinitemtype: false,
|
||||
tojoinclasstitle: undefined,
|
||||
rolearr: ["", ""],
|
||||
Checkboxteacherchecked: false,
|
||||
Checkboxstudentchecked: false,
|
||||
Checkboxteachingchecked: false,
|
||||
Checkboxteachertype: false,
|
||||
Checkboxteachingtype: false,
|
||||
code_notice: false,
|
||||
checked_notice: false,
|
||||
RadioGroupvalue: undefined
|
||||
})
|
||||
}
|
||||
|
||||
// 关闭
|
||||
cancelModulationModels = () => {
|
||||
this.setState({ isRenders: false })
|
||||
|
@ -313,14 +256,6 @@ class NewHeader extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// 处理弹框
|
||||
setgoshowqqgtounp = (bool) => {
|
||||
this.setState({
|
||||
goshowqqgtounp: bool
|
||||
})
|
||||
}
|
||||
|
||||
addMenu=(list)=>{
|
||||
return(
|
||||
list && list.length >0 &&
|
||||
|
@ -333,10 +268,29 @@ class NewHeader extends Component {
|
|||
)
|
||||
})
|
||||
}
|
||||
<Menu.Item><AddProjectModal showNotification={this.props.showNotification}/></Menu.Item>
|
||||
</Menu>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
renderMenu=(personal)=>{
|
||||
const { current_user } = this.props;
|
||||
return(
|
||||
<Menu className="currentMenu">
|
||||
<Menu.Item>
|
||||
<span title={current_user && current_user.username}>{current_user && current_user.username}</span>
|
||||
</Menu.Item>
|
||||
{
|
||||
personal && personal.length > 0 && personal.map((item,key)=>{
|
||||
return(
|
||||
<li key={key}><a href={item.url} target="_blank">{item.name}</a></li>
|
||||
)
|
||||
})
|
||||
}
|
||||
<Menu.Item><a onClick={() => this.educoderloginysl()}>退出</a></Menu.Item>
|
||||
</Menu>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { match} = this.props;
|
||||
|
@ -345,17 +299,12 @@ class NewHeader extends Component {
|
|||
tojoinitemtype,
|
||||
tojoinclasstitle,
|
||||
code_notice,
|
||||
checked_notice,
|
||||
AccountProfiletype,
|
||||
submitapplications,
|
||||
submitapplicationsvalue,
|
||||
user,
|
||||
isRender,
|
||||
showSearchOpentype,
|
||||
headtypesonClickbool,
|
||||
headtypess,
|
||||
settings,
|
||||
goshowqqgtounp,
|
||||
openSearch,
|
||||
} = this.state;
|
||||
/*用户名称 用户头像url*/
|
||||
|
@ -452,11 +401,6 @@ class NewHeader extends Component {
|
|||
{...this.props}
|
||||
{...this.state}
|
||||
/> : ""}
|
||||
{
|
||||
goshowqqgtounp === true ?
|
||||
<GotoQQgroup {...this.state} {...this.props} setgoshowqqgtounp={(bool) => this.setgoshowqqgtounp(bool)}></GotoQQgroup>
|
||||
:""
|
||||
}
|
||||
{
|
||||
settings && settings.nav_logo_url ?
|
||||
<a href={settings && settings.new_course.default_url} className={"fl mr50"} style={{minWidth:"45px"}}>
|
||||
|
@ -522,32 +466,6 @@ class NewHeader extends Component {
|
|||
}
|
||||
</div>:""
|
||||
}
|
||||
<Modal
|
||||
keyboard={false}
|
||||
title="提示"
|
||||
visible={submitapplications}
|
||||
closable={false}
|
||||
footer={null}
|
||||
>
|
||||
<div className="task_popup_con ml30">
|
||||
<div className="mr15">
|
||||
<ul>
|
||||
<div className="task-popup-content">
|
||||
<p className="task-popup-text-center font-16">
|
||||
{submitapplicationsvalue}
|
||||
</p>
|
||||
</div>
|
||||
<li className="clearfix mt10 edu-txt-center">
|
||||
<a className="task-btn mr10"
|
||||
onClick={this.hidesubmitapplications}>取消</a>
|
||||
<a
|
||||
className="task-btn task-btn-orange ml20"
|
||||
onClick={this.submitsubmitapplications}>确定</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
{!user || (user && !user.login) ?
|
||||
<span className="font-15 ml30">
|
||||
|
@ -558,25 +476,11 @@ class NewHeader extends Component {
|
|||
}
|
||||
</span>
|
||||
:
|
||||
<div className="ml30 edu-menu-panel" style={{ height: "70px", lineHeight: "70px" }}>
|
||||
<a href={`/users/${this.props.current_user === undefined ? "" : this.props.current_user.login}`}>
|
||||
<img alt="头像" className="radius" height="34" id="nh_user_logo" name="avatar_image" src={getImageUrl(`/${user.image_url}`)} width="34">
|
||||
</img>
|
||||
<Dropdown placement={`bottomRight`} overlay={this.renderMenu(settings && settings.personal)}>
|
||||
<a href={`/users/${this.props.current_user && this.props.current_user.login}`}>
|
||||
<img alt="头像" src={getImageUrl(`/${user.image_url}`)} className="currentImg"></img>
|
||||
</a>
|
||||
<ul className="edu-menu-list" style={{ top: '60px', textAlign: 'center' }}>
|
||||
<li className="bor-bottom-greyE task-hide" title={this.props.current_user.username} style={{cursor:"default",background:"#fff"}}>{this.props.current_user.username}</li>
|
||||
{
|
||||
settings && settings.personal && settings.personal.length > 0 && settings.personal.map((item,key)=>{
|
||||
return(
|
||||
<li key={key}><a href={item.url} target="_blank">{item.name}</a></li>
|
||||
)
|
||||
})
|
||||
}
|
||||
<li className="bor-top-greyE">
|
||||
<a onClick={() => this.educoderloginysl()}>退出</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</Dropdown>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
.dropdownFlex{
|
||||
display:flex;
|
||||
padding:5px;
|
||||
background:#fff;
|
||||
border-radius: 3px;
|
||||
.ant-menu-vertical > .ant-menu-item{
|
||||
|
@ -9,11 +8,57 @@
|
|||
height: 35px;
|
||||
line-height: 35px;
|
||||
margin:0px;
|
||||
&.ant-menu-item-selected{
|
||||
background-color: #fff;
|
||||
a{color: rgba(0, 0, 0, 0.65)!important;}
|
||||
}
|
||||
&.ant-menu-item-active{
|
||||
a{color: #4cacff!important;}
|
||||
}
|
||||
}
|
||||
.ant-menu-vertical{
|
||||
border:none;
|
||||
}
|
||||
}
|
||||
.currentImg{
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 50%;
|
||||
margin-left: 30px;
|
||||
}
|
||||
.currentMenu{
|
||||
width: 120px;
|
||||
text-align: center;
|
||||
padding:0px;
|
||||
li{
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
padding:0px;
|
||||
cursor: default;
|
||||
&:hover{
|
||||
background-color: #fff;
|
||||
}
|
||||
&:first-child{
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
&:last-child{
|
||||
border-top: 1px solid #eee;
|
||||
a{
|
||||
border-radius: 0px 0px 4px 4px;
|
||||
}
|
||||
}
|
||||
a{
|
||||
padding:0px;
|
||||
margin:0px;
|
||||
display: block;
|
||||
color: #666;
|
||||
&:hover{
|
||||
color: #fff;
|
||||
background: #4CACFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.newFooter {
|
||||
position: absolute;
|
||||
|
@ -65,4 +110,13 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.inviteForm{
|
||||
.ant-form-item{
|
||||
margin-right: 0px;
|
||||
}
|
||||
.ant-form-item-label{
|
||||
width: 110px;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
|
@ -239,9 +239,11 @@ function CoderDepot(props){
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
let n = fileInfo && fileInfo.name;
|
||||
const mdFlag = n && n.substring(n.length-3,n.length) === ".md";
|
||||
|
||||
const { current_user } = props;
|
||||
const fileOperate = type === "dir" && projectDetail && projectDetail.type !== 2 && (projectDetail.permission !=="Reporter" || (current_user && current_user.admin));
|
||||
|
||||
return(
|
||||
<WhiteBack>
|
||||
|
@ -311,7 +313,7 @@ function CoderDepot(props){
|
|||
}
|
||||
<a onClick={()=>urlLink(`/projects/${owner}/${projectsId}/issues/new`)} >+ 任务</a>
|
||||
</div>
|
||||
{ type === "dir" && projectDetail.type !== 2 &&
|
||||
{ fileOperate &&
|
||||
<Dropdown overlay={fileMenu} className="mr20" trigger={['click']}>
|
||||
<Button type="default">文件 <i className="iconfont icon-sanjiaoxing-down ml3 font-14 color-grey-9"></i></Button>
|
||||
</Dropdown>
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import React, { Component } from 'react';
|
||||
import { Spin, Tooltip } from 'antd';
|
||||
import { Spin, Tooltip , message } from 'antd';
|
||||
import { Link, Route, Switch } from 'react-router-dom';
|
||||
import { Content } from '../Component/layout';
|
||||
import { Content , FlexAJ } from '../Component/layout';
|
||||
import DetailBanner from './sub/DetailBanner';
|
||||
import Invite from './sub/Invite';
|
||||
import '../css/index.scss'
|
||||
import './list.css';
|
||||
|
||||
|
@ -389,6 +390,15 @@ class Detail extends Component {
|
|||
})
|
||||
}
|
||||
|
||||
textFunc = (forked_from_project_id,fork_info)=>{
|
||||
return forked_from_project_id && fork_info ?
|
||||
<div className="color-grey-9">
|
||||
<span>forked from </span>
|
||||
<Link to={`/users/${fork_info.fork_project_user_login}`} className="show-user-link color-grey-6">{fork_info.fork_project_user_name}</Link>
|
||||
<span> / </span>
|
||||
<Link to={`/projects/${fork_info.fork_project_user_login}/${fork_info.fork_project_identifier}`} className="color-grey-6">{fork_info.fork_form_name}</Link>
|
||||
</div> : ""
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
|
@ -406,16 +416,6 @@ class Detail extends Component {
|
|||
|
||||
const { state } = this.props.history.location;
|
||||
|
||||
const text = (
|
||||
projectDetail && projectDetail.forked_from_project_id && projectDetail.fork_info ?
|
||||
<React.Fragment>
|
||||
<span>forked from </span>
|
||||
<Link to={`/users/${projectDetail.fork_info.fork_project_user_login}`} className="show-user-link color-grey-ccc">{projectDetail.fork_info.fork_project_user_name}</Link>
|
||||
<span> / </span>
|
||||
<Link to={`/projects/${projectDetail.fork_info.fork_project_user_login}/${projectDetail.fork_info.fork_project_identifier}`} className="color-grey-ccc">{projectDetail.fork_info.fork_form_name}</Link>
|
||||
</React.Fragment> : ""
|
||||
);
|
||||
|
||||
const common = {
|
||||
getDetail: this.getDetail,
|
||||
changeOpenDevops:this.changeOpenDevops,
|
||||
|
@ -425,7 +425,7 @@ class Detail extends Component {
|
|||
<div>
|
||||
<div className="detailHeader-wrapper">
|
||||
<div className="normal">
|
||||
<div className="f-wrap-between pb15" style={{ position: "relative" }}>
|
||||
<div className="f-wrap-between" style={{ position: "relative" }}>
|
||||
<p className="font-22 df flex-1 lineH2 mt15" style={{ alignItems: "center" }}>
|
||||
{project && project.author &&
|
||||
<Link to={`${project.author.type ==="Organization" ? "/organize":'/users'}/${project.author.login}`} className="show-user-link">
|
||||
|
@ -435,15 +435,7 @@ class Detail extends Component {
|
|||
<span className="ml5 mr5">/</span>
|
||||
<span className="hide-1 flex-1 df">
|
||||
<Link to={`/projects/${owner}/${projectsId}`} className="font-22">{project && project.name}</Link>
|
||||
{
|
||||
projectDetail && projectDetail.forked_from_project_id && projectDetail.fork_info ?
|
||||
<Tooltip placement={'right'} title={text}>
|
||||
<Link to={`/projects/${projectDetail.fork_info.fork_project_user_login}/${projectDetail.fork_info.fork_project_identifier}`}
|
||||
className="ml10" >
|
||||
<i className="iconfont icon-fork font-18 fl mt6" style={{ color: "#8D90E3" }}></i>
|
||||
</Link>
|
||||
</Tooltip> : ""
|
||||
}
|
||||
|
||||
{
|
||||
projectDetail && projectDetail.type && projectDetail.type !== 0 ?
|
||||
projectDetail.type === 2 ?
|
||||
|
@ -516,6 +508,19 @@ class Detail extends Component {
|
|||
</span>
|
||||
}
|
||||
</div>
|
||||
<FlexAJ>
|
||||
<div>
|
||||
{
|
||||
projectDetail && projectDetail.forked_from_project_id && projectDetail.fork_info ?
|
||||
this.textFunc(projectDetail.forked_from_project_id,projectDetail.fork_info)
|
||||
:""
|
||||
}
|
||||
</div>
|
||||
{
|
||||
projectDetail && projectDetail.invite_code &&
|
||||
<Invite code={projectDetail.invite_code} />
|
||||
}
|
||||
</FlexAJ>
|
||||
{
|
||||
firstSync ? "" :
|
||||
<DetailBanner
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import React from 'react';
|
||||
import { Tooltip , message } from 'antd';
|
||||
|
||||
function Invite({code}) {
|
||||
|
||||
function jsCopy(id) {
|
||||
const copyEle = document.querySelector(id); // 获取要复制的节点
|
||||
const range = document.createRange(); // 创造range
|
||||
window.getSelection().removeAllRanges(); //清除页面中已有的selection
|
||||
range.selectNode(copyEle); // 选中需要复制的节点
|
||||
window.getSelection().addRange(range); // 执行选中元素
|
||||
document.execCommand("Copy"); // 执行copy操作
|
||||
message.success('复制成功');
|
||||
}
|
||||
return(
|
||||
<div>
|
||||
<span>邀请码: <span id="devitecode">{code}</span></span>
|
||||
<Tooltip title={<p className="edu-txt-center">可以通过邀请码邀请成员加入项目<br/>点击复制邀请码。</p>} placement={"bottom"}>
|
||||
<i className="iconfont icon-fuzhi2 font-16 color-blue ml8" onClick={()=>jsCopy("#devitecode")}></i>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Invite;
|
|
@ -0,0 +1,114 @@
|
|||
import React, { useEffect , useState } from 'react';
|
||||
import Axios from 'axios';
|
||||
import { Pagination , Spin , Popconfirm }from 'antd';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { getImageUrl } from 'educoder';
|
||||
import Nodata from '../Nodata';
|
||||
import { FlexAJ } from '../Component/layout';
|
||||
|
||||
const limit = 15;
|
||||
function Apply(props) {
|
||||
const username = props.match.params.username;
|
||||
const [ list , setList ] = useState(undefined);
|
||||
const [ page , setPage ] = useState(1);
|
||||
const [ total , setTotal ] = useState(0);
|
||||
const [ isSpin , setIsSpin ] = useState(true);
|
||||
|
||||
useEffect(()=>{
|
||||
if(username){
|
||||
setIsSpin(true);
|
||||
getList();
|
||||
}
|
||||
},[username])
|
||||
|
||||
function getList() {
|
||||
const url = `/users/${username}/applied_projects.json`;
|
||||
Axios.get(url).then(result=>{
|
||||
if(result){
|
||||
setList(result.data.applied_projects);
|
||||
setTotal(result.data.total_count);
|
||||
setIsSpin(false);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
// 接受
|
||||
function acceptDivert(id){
|
||||
const url = `/users/${username}/applied_projects/${id}/accept.json`;
|
||||
Axios.post(url).then(result=>{
|
||||
if(result && result.data){
|
||||
getList();
|
||||
props && props.deleteEvent("apply",1);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
// 拒绝
|
||||
function revertDivert(id){
|
||||
const url = `/users/${username}/applied_projects/${id}/refuse.json`;
|
||||
Axios.post(url).then(result=>{
|
||||
if(result && result.data){
|
||||
getList();
|
||||
props && props.deleteEvent("apply",1);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
return(
|
||||
<div>
|
||||
<Spin spinning={isSpin}>
|
||||
<div style={{minHeight:"400px"}}>
|
||||
{
|
||||
list && list.length > 0 ?
|
||||
<ul className="notifyList">
|
||||
{
|
||||
list.map((i,k)=>{
|
||||
return(
|
||||
<li>
|
||||
<Link to={`/users/${i.user && i.user.login}`}><img src={getImageUrl(`/${i.user && i.user.image_url}`)} alt="" className="notifyImg"/></Link>
|
||||
<div className="notifyFlex">
|
||||
<p className="notifyInfos">
|
||||
<Link to={`/users/${i.user && i.user.login}`} className="font-15 mr20">{i.user && i.user.name}</Link>
|
||||
<span className="color-grey-9">{i.time_ago}</span>
|
||||
</p>
|
||||
<FlexAJ>
|
||||
<p>申请以【{i.role === "developer" ?"开发者":i.role === "manager" ? "管理者":"报告者"}】身份加入【{i.project && i.project.name}】项目。是否同意?</p>
|
||||
{
|
||||
i.status === "common" &&
|
||||
<span>
|
||||
<Popconfirm title={`确定同意${i.user && i.user.name}加入【${i.project && i.project.name}】项目?`} okText="确定" cancelText="取消" onConfirm={()=>acceptDivert(i.id)}>
|
||||
<a className="color-blue">同意</a>
|
||||
</Popconfirm>
|
||||
<Popconfirm title={`确定拒绝${i.user && i.user.name}加入【${i.project && i.project.name}】项目?`} okText="确定" cancelText="取消" onConfirm={()=>revertDivert(i.id)}>
|
||||
<a className="color-red ml20">拒绝</a>
|
||||
</Popconfirm>
|
||||
</span>
|
||||
}
|
||||
{
|
||||
i.status === "accepted" && <span className="color-grey-9">已接受</span>
|
||||
}
|
||||
{
|
||||
i.status === "refused" && <span className="color-grey-9">已拒绝</span>
|
||||
}
|
||||
</FlexAJ>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
:
|
||||
""
|
||||
}
|
||||
{list && list.length === 0 && <Nodata _html="暂无成员申请" />}
|
||||
{
|
||||
total > limit &&
|
||||
<div className="edu-txt-center pt20 pb20">
|
||||
<Pagination simple pageSize={limit} total={total} current={page} onChange={(p)=>{setPage(p)}}/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</Spin>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Apply;
|
|
@ -6,6 +6,10 @@ import Loadable from "react-loadable";
|
|||
import Loading from "../../Loading";
|
||||
import { Route, Switch } from "react-router-dom";
|
||||
|
||||
const Apply = Loadable({
|
||||
loader: () => import("./Apply"),
|
||||
loading: Loading,
|
||||
});
|
||||
const Notify = Loadable({
|
||||
loader: () => import("./Notify"),
|
||||
loading: Loading,
|
||||
|
@ -18,38 +22,62 @@ function Index(props){
|
|||
const username = props.match.params.username;
|
||||
const pathname = props.history.location.pathname;
|
||||
const user = props.user;
|
||||
const undo_messages = props.undo_messages;
|
||||
|
||||
const [ menu , setMenu ] = useState("notify");
|
||||
const [ messages , setMessages ] = useState(0);
|
||||
const [ transferProjects , setTransferProjects ] = useState(0);
|
||||
const [ messagesCount , setMessagesCount ] = useState(0);
|
||||
const [ transferCount , setTransferCount ] = useState(0);
|
||||
const [ applyCount , setApplyCount ] = useState(0);
|
||||
|
||||
const [ flag , setFlag ] = useState(true);
|
||||
const { current_user } = props;
|
||||
|
||||
useEffect(()=>{
|
||||
if((username && current_user && (current_user.login !== username))){
|
||||
props.history.push(`/users/${username}`);
|
||||
}
|
||||
},[current_user,username])
|
||||
|
||||
useEffect(()=>{
|
||||
if(user){
|
||||
setTransferProjects(user.undo_transfer_projects);
|
||||
setTransferCount(user.undo_transfer_projects);
|
||||
setApplyCount(user.undo_join_projects);
|
||||
setMessagesCount(user.undo_messages);
|
||||
}
|
||||
if(undo_messages){
|
||||
setMessages(undo_messages);
|
||||
}
|
||||
},[user,undo_messages])
|
||||
},[user])
|
||||
|
||||
useEffect(()=>{
|
||||
if(pathname && username){
|
||||
if(pathname === `/users/${username}/notice`){
|
||||
setMenu("notify");
|
||||
changeNum(user.undo_messages);
|
||||
}
|
||||
if(pathname === `/users/${username}/notice/undo`){
|
||||
setMenu("undo");
|
||||
}
|
||||
if(pathname === `/users/${username}/notice/apply`){
|
||||
setMenu("apply");
|
||||
}
|
||||
}
|
||||
},[pathname])
|
||||
|
||||
function fetchUser(){
|
||||
props && props.fetchUser();
|
||||
}
|
||||
},[pathname,user])
|
||||
|
||||
function changeNum(){
|
||||
fetchUser();
|
||||
if(flag){
|
||||
messagesCount && props.deleteUndoEvent(messagesCount);
|
||||
setFlag(false);
|
||||
}
|
||||
}
|
||||
|
||||
function deleteEvent(type,count) {
|
||||
let c = count;
|
||||
if(type==="apply"){
|
||||
setApplyCount(applyCount-count);
|
||||
}else if(type==="undo"){
|
||||
setTransferCount(applyCount-count);
|
||||
}else{
|
||||
setMessagesCount(0);
|
||||
c = messagesCount;
|
||||
}
|
||||
(c || c===0) && props.deleteUndoEvent(c);
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -58,27 +86,39 @@ function Index(props){
|
|||
<li className={menu === "notify" ? "active":""}>
|
||||
<Link to={`/users/${username}/notice`} onClick={changeNum}>
|
||||
<span>通知</span>
|
||||
{messages ? <span className="unNum">{messages}</span>:""}
|
||||
{messagesCount ? <span className="unNum">{messagesCount}</span>:""}
|
||||
</Link>
|
||||
</li>
|
||||
<li className={menu === "undo" ? "active":""}>
|
||||
<Link to={`/users/${username}/notice/undo`}>
|
||||
<span>接收仓库</span>
|
||||
{transferProjects ? <span className="unNum">{transferProjects}</span>:""}
|
||||
{transferCount ? <span className="unNum">{transferCount}</span>:""}
|
||||
</Link>
|
||||
</li>
|
||||
<li className={menu === "apply" ? "active":""}>
|
||||
<Link to={`/users/${username}/notice/apply`}>
|
||||
<span>成员申请</span>
|
||||
{applyCount ? <span className="unNum">{applyCount}</span>:""}
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
<Switch>
|
||||
<Route
|
||||
path="/users/:username/notice/apply"
|
||||
render={(p) => {
|
||||
return <Apply {...props} {...p} deleteEvent={deleteEvent}/>;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/users/:username/notice/undo"
|
||||
render={(p) => {
|
||||
return <UndoEvent {...props} {...p} fetchUser={fetchUser}/>;
|
||||
return <UndoEvent {...props} {...p} deleteEvent={deleteEvent}/>;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
path="/users/:username/notice"
|
||||
render={(p) => {
|
||||
return <Notify {...props} {...p} fetchUser={fetchUser}/>;
|
||||
return <Notify {...props} {...p} deleteEvent={deleteEvent}/>;
|
||||
}}
|
||||
></Route>
|
||||
</Switch>
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
min-width: 23px;
|
||||
text-align: center;
|
||||
background-color: #ffe4b3;
|
||||
margin-top: 19px;
|
||||
margin-top: 27px;
|
||||
margin-left: 10px;
|
||||
display: block;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,10 @@ function Notify(props){
|
|||
const [ total , setTotal ] = useState(0);
|
||||
const [ isSpin , setIsSpin ] = useState(true);
|
||||
|
||||
useEffect(()=>{
|
||||
props && props.deleteEvent("notify",0);
|
||||
},[])
|
||||
|
||||
useEffect(()=>{
|
||||
if(username){
|
||||
setIsSpin(true);
|
||||
|
@ -43,7 +47,7 @@ function Notify(props){
|
|||
return <p>取消转移【<Link to={`/projects/${project && project.owner && project.owner.login}/${project && project.identifier}`}>{project && project.name}</Link>】仓库</p>
|
||||
case 'common':
|
||||
return <p>正在将【<Link to={`/projects/${project && project.owner && project.owner.login}/${project && project.identifier}`}>{project && project.name}</Link>】仓库转移给【<Link to={`/users/${owner && owner.login}`}>{owner && owner.name}</Link>】</p>
|
||||
case 'successed':
|
||||
case 'successed':
|
||||
return <p>【<Link to={`/projects/${project && project.owner && project.owner.login}/${project && project.identifier}`}>{project && project.name}</Link>】仓库成功转移给【<Link to={`/users/${owner && owner.login}`}>{owner && owner.name}</Link>】</p>
|
||||
default:
|
||||
return <p>拒绝转移【<Link to={`/projects/${project && project.owner && project.owner.login}/${project && project.identifier}`}>{project && project.name}</Link>】仓库</p>
|
||||
|
@ -53,42 +57,56 @@ function Notify(props){
|
|||
}
|
||||
}
|
||||
|
||||
function renderApplyStatus(status,applied) {
|
||||
let { project } = applied;
|
||||
if(status){
|
||||
switch(status){
|
||||
case 'successed':
|
||||
return <p>已通过你加入【<Link to={`/projects/${project && project.owner && project.owner.login}/${project && project.identifier}`}>{project && project.name}</Link>】项目的申请</p>
|
||||
default:
|
||||
return <p>已拒绝你加入【<Link to={`/projects/${project && project.owner && project.owner.login}/${project && project.identifier}`}>{project && project.name}</Link>】项目的申请</p>
|
||||
}
|
||||
}else{
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
return(
|
||||
<div>
|
||||
<Spin spinning={isSpin}>
|
||||
<div style={{minHeight:"400px"}}>
|
||||
{
|
||||
list && list.length > 0 ?
|
||||
<ul className="notifyList">
|
||||
{
|
||||
list.map((i,k)=>{
|
||||
return(
|
||||
<li>
|
||||
<Link to={`/users/${i.login}`}><img src={getImageUrl(`/${i.applied_user && i.applied_user.image_url}`)} alt="" className="notifyImg"/></Link>
|
||||
<div className="notifyFlex">
|
||||
<p className="notifyInfos">
|
||||
<Link to={`/users/${i.applied_user && i.applied_user.login}`} className="font-15 mr20">{i.applied_user && i.applied_user.name}</Link>
|
||||
<span className="color-grey-9">{i.time_ago}</span>
|
||||
</p>
|
||||
{renderStatus(i.status,i.applied)}
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
:
|
||||
""
|
||||
}
|
||||
list && list.length > 0 ?
|
||||
<ul className="notifyList">
|
||||
{
|
||||
list.map((i,k)=>{
|
||||
return(
|
||||
<li>
|
||||
<Link to={`/users/${i.login}`}><img src={getImageUrl(`/${i.applied_user && i.applied_user.image_url}`)} alt="" className="notifyImg"/></Link>
|
||||
<div className="notifyFlex">
|
||||
<p className="notifyInfos">
|
||||
<Link to={`/users/${i.applied_user && i.applied_user.login}`} className="font-15 mr20">{i.applied_user && i.applied_user.name}</Link>
|
||||
<span className="color-grey-9">{i.time_ago}</span>
|
||||
</p>
|
||||
{ i.applied_type === "AppliedProject" ? renderApplyStatus(i.status,i.applied):renderStatus(i.status,i.applied)}
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
:
|
||||
""
|
||||
}
|
||||
{list && list.length === 0 && <Nodata _html="暂无通知" />}
|
||||
{
|
||||
total > limit &&
|
||||
<div className="edu-txt-center pt20 pb20">
|
||||
<Pagination simple pageSize={limit} total={total} current={page} onChange={(p)=>{setPage(p)}}/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</Spin>
|
||||
{list && list.length === 0 && <Nodata _html="暂无通知" />}
|
||||
{
|
||||
total > limit &&
|
||||
<div className="edu-txt-center pt20 pb20">
|
||||
<Pagination simple pageSize={limit} total={total} current={page} onChange={(p)=>{setPage(p)}}/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ function UndoEvent(props){
|
|||
Axios.post(url).then(result=>{
|
||||
if(result && result.data){
|
||||
getList();
|
||||
props && props.fetchUser();
|
||||
props && props.deleteEvent("undo",1);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ function UndoEvent(props){
|
|||
Axios.post(url).then(result=>{
|
||||
if(result && result.data){
|
||||
getList();
|
||||
props && props.fetchUser();
|
||||
props && props.deleteEvent("undo",1);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ function Collaborator(props){
|
|||
}
|
||||
{
|
||||
nav === "1" ?
|
||||
<AddMember getID={getID} login/>
|
||||
<AddMember getID={getID} login showNotification={props.showNotification}/>
|
||||
:
|
||||
<AddGroup getGroupID={getGroupID} organizeId={owner}/>
|
||||
}
|
||||
|
|
|
@ -146,7 +146,7 @@ export default ((props) => {
|
|||
<WhiteBack style={{minHeight:"400px"}}>
|
||||
<Title>
|
||||
<span>团队成员管理</span>
|
||||
<AddMember getID={getID}/>
|
||||
<AddMember getID={getID} showNotification={props.showNotification}/>
|
||||
</Title>
|
||||
<FlexAJ className="padding20-30">
|
||||
<div style={{ width: "580px" }}>
|
||||
|
|
|
@ -104,6 +104,7 @@ $flex:flex;
|
|||
margin:10px 0px;
|
||||
word-break: break-all;
|
||||
text-align: justify;
|
||||
font-size: 16px;
|
||||
}
|
||||
.focusBox,.infoBox{
|
||||
width: 100%!important;
|
||||
|
|
|
@ -66,7 +66,6 @@ class Infos extends Component {
|
|||
project_type: undefined,
|
||||
route_type: undefined,
|
||||
undo_events:0,
|
||||
undo_messages:0,
|
||||
menuKey:"0"
|
||||
};
|
||||
}
|
||||
|
@ -124,20 +123,14 @@ class Infos extends Component {
|
|||
});
|
||||
const { current_user } = this.props;
|
||||
const { username } = this.props.match.params;
|
||||
const { pathname } = this.props.location;
|
||||
const { notice } = this.state;
|
||||
|
||||
let url = `/users/${username || (current_user && current_user.login)}.json`;
|
||||
axios.get(url).then((result) => {
|
||||
let e = result.data && result.data.undo_events;
|
||||
let p = result.data && result.data.undo_messages;
|
||||
let n = notice || pathname === `/users/${username}/notice` ;
|
||||
this.setState({
|
||||
user: result.data,
|
||||
isSpin: false,
|
||||
undo_events:n ? (e-p) : e,
|
||||
undo_messages:0,
|
||||
notice:n
|
||||
undo_events:e
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
|
@ -161,7 +154,6 @@ class Infos extends Component {
|
|||
this.setState({
|
||||
route_type: undefined,
|
||||
project_type:"notice",
|
||||
notice:true
|
||||
},()=>{
|
||||
this.props.history.push(`/users/${user && user.login}/notice`);
|
||||
this.fetchUser();
|
||||
|
@ -190,10 +182,19 @@ class Infos extends Component {
|
|||
resetUserInfo && resetUserInfo();
|
||||
}
|
||||
|
||||
// 修改待办事项右侧的数量
|
||||
deleteUndoEvent=(count)=>{
|
||||
let { undo_events } = this.state;
|
||||
let undo = undo_events - count;
|
||||
this.setState({
|
||||
undo_events:undo
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const { current_user, mygetHelmetapi , resetUserInfo } = this.props;
|
||||
const { current_user } = this.props;
|
||||
const { username } = this.props.match.params;
|
||||
const { user, isSpin, project_type, route_type , undo_events , undo_messages , menuKey } = this.state;
|
||||
const { user, isSpin, route_type , undo_events , menuKey } = this.state;
|
||||
return (
|
||||
<div className="newMain clearfix">
|
||||
<Spin spinning={isSpin}>
|
||||
|
@ -213,7 +214,7 @@ class Infos extends Component {
|
|||
</span>
|
||||
</span>
|
||||
|
||||
<div className="text-center mt15 font-16 fwb task-hide" title={user && user.username}>
|
||||
<div className="text-center mt15 font-24 task-hide" title={user && user.username}>
|
||||
{user && user.username}
|
||||
</div>
|
||||
<div className="userDescription">
|
||||
|
@ -306,7 +307,7 @@ class Infos extends Component {
|
|||
<Route
|
||||
path="/users/:username/notice"
|
||||
render={() => {
|
||||
return <Notice {...this.props} {...this.state} fetchUser={this.fetchUser}/>;
|
||||
return <Notice {...this.props} {...this.state} deleteUndoEvent={this.deleteUndoEvent}/>;
|
||||
}}
|
||||
></Route>
|
||||
<Route
|
||||
|
|
|
@ -45,6 +45,7 @@ export default Form.create()(
|
|||
if(result && result.data){
|
||||
props.showNotification("资料修改成功!")
|
||||
resetUser && resetUser(result.data);
|
||||
props.history.push(`/users/${username}`)
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue