Merge branch 'develop'

This commit is contained in:
caishi 2021-06-11 18:11:43 +08:00
commit c0f7c489fb
18 changed files with 470 additions and 218 deletions

View File

@ -3912,7 +3912,6 @@ html>body #ajax-indicator {
text-align: center; text-align: center;
height: 70px; height: 70px;
box-sizing: border-box; box-sizing: border-box;
min-width: 780px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;

View File

@ -4,7 +4,7 @@ import axios from 'axios';
import { getImageUrl } from 'educoder'; import { getImageUrl } from 'educoder';
const { Option } = AutoComplete; const { Option } = AutoComplete;
function AddMember({getID,login}){ function AddMember({getID,login,showNotification}){
const [ id , setID ] = useState(undefined); const [ id , setID ] = useState(undefined);
const [ source , setSource ] = useState(undefined); const [ source , setSource ] = useState(undefined);
const [ searchKey , setSearchKey ] = useState(undefined); const [ searchKey , setSearchKey ] = useState(undefined);
@ -45,7 +45,7 @@ function AddMember({getID,login}){
src={getImageUrl(`/${item && item.image_url}`)} src={getImageUrl(`/${item && item.image_url}`)}
alt="" alt=""
/> />
<span className="ml10" style={{ "vertical-align": "middle" }}> <span className="ml10" style={{ verticalAlign: "middle" }}>
{item.username} {item.username}
<span className="color-grey ml10">({item.login})</span> <span className="color-grey ml10">({item.login})</span>
</span> </span>
@ -66,7 +66,12 @@ function AddMember({getID,login}){
}; };
function addCollaborator(){ function addCollaborator(){
if(source && source.length>0){
getID && getID(id); getID && getID(id);
setSearchKey(undefined);
}else{
showNotification("请选择存在的用户!");
}
} }
return( return(

View File

@ -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>
)
})
)

View File

@ -2,13 +2,11 @@ import React, { Component } from 'react';
import AccountProfile from "../../modules/user/AccountProfile"; import AccountProfile from "../../modules/user/AccountProfile";
import { getImageUrl } from 'educoder' import { getImageUrl } from 'educoder'
import axios from 'axios'; 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 LoginDialog from '../../modules/login/LoginDialog';
import GotoQQgroup from '../../modal/GotoQQgroup' import AddProjectModal from './AddProjectModal';
import '../../modules/tpm/TPMIndex.css'; import '../../modules/tpm/TPMIndex.css';
import logo from '../../modules/tpm/images/logo.png';
import './header.scss'; import './header.scss';
const $ = window.$ const $ = window.$
@ -33,11 +31,9 @@ class NewHeader extends Component {
Checkboxteachertype: false, Checkboxteachertype: false,
Checkboxteachingtype: false, Checkboxteachingtype: false,
code_notice: false, code_notice: false,
checked_notice: false,
RadioGroupvalue: undefined, RadioGroupvalue: undefined,
submitapplications: false, submitapplications: false,
isRender: false, isRender: false,
showSearchOpentype: false,
showTrial: false, showTrial: false,
setevaluatinghides: false, setevaluatinghides: false,
occupation: 0, occupation: 0,
@ -45,7 +41,6 @@ class NewHeader extends Component {
headtypesonClickbool: false, headtypesonClickbool: false,
headtypess: "/", headtypess: "/",
settings: null, settings: null,
goshowqqgtounp: false,
visiblemyss: false, visiblemyss: false,
openSearch:false, openSearch:false,
} }
@ -93,10 +88,11 @@ class NewHeader extends Component {
}, 300) }, 300)
}} }}
> >
<Search placeholder="实践课程/教学课堂/实践项目/交流问答" <Search placeholder="请输入搜索关键字"
className={`search-input mr20`} className={`search-input mr20`}
onSearch={(value)=>this.onGlobalSearch(value,item)} onSearch={(value)=>this.onGlobalSearch(value,item)}
autoFocus={true} autoFocus={true}
style={{width:"260px"}}
/> />
</div> </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 = () => { educoderlogin = () => {
//登录账号 //登录账号
this.setState({ 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 = () => { cancelModulationModels = () => {
this.setState({ isRenders: false }) this.setState({ isRenders: false })
@ -313,14 +256,6 @@ class NewHeader extends Component {
} }
} }
// 处理弹框
setgoshowqqgtounp = (bool) => {
this.setState({
goshowqqgtounp: bool
})
}
addMenu=(list)=>{ addMenu=(list)=>{
return( return(
list && list.length >0 && list && list.length >0 &&
@ -333,10 +268,29 @@ class NewHeader extends Component {
) )
}) })
} }
<Menu.Item><AddProjectModal showNotification={this.props.showNotification}/></Menu.Item>
</Menu> </Menu>
</div> </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() { render() {
const { match} = this.props; const { match} = this.props;
@ -345,17 +299,12 @@ class NewHeader extends Component {
tojoinitemtype, tojoinitemtype,
tojoinclasstitle, tojoinclasstitle,
code_notice, code_notice,
checked_notice,
AccountProfiletype, AccountProfiletype,
submitapplications,
submitapplicationsvalue,
user, user,
isRender, isRender,
showSearchOpentype,
headtypesonClickbool, headtypesonClickbool,
headtypess, headtypess,
settings, settings,
goshowqqgtounp,
openSearch, openSearch,
} = this.state; } = this.state;
/*用户名称 用户头像url*/ /*用户名称 用户头像url*/
@ -452,11 +401,6 @@ class NewHeader extends Component {
{...this.props} {...this.props}
{...this.state} {...this.state}
/> : ""} /> : ""}
{
goshowqqgtounp === true ?
<GotoQQgroup {...this.state} {...this.props} setgoshowqqgtounp={(bool) => this.setgoshowqqgtounp(bool)}></GotoQQgroup>
:""
}
{ {
settings && settings.nav_logo_url ? settings && settings.nav_logo_url ?
<a href={settings && settings.new_course.default_url} className={"fl mr50"} style={{minWidth:"45px"}}> <a href={settings && settings.new_course.default_url} className={"fl mr50"} style={{minWidth:"45px"}}>
@ -522,32 +466,6 @@ class NewHeader extends Component {
} }
</div>:"" </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> </div>
{!user || (user && !user.login) ? {!user || (user && !user.login) ?
<span className="font-15 ml30"> <span className="font-15 ml30">
@ -558,25 +476,11 @@ class NewHeader extends Component {
} }
</span> </span>
: :
<div className="ml30 edu-menu-panel" style={{ height: "70px", lineHeight: "70px" }}> <Dropdown placement={`bottomRight`} overlay={this.renderMenu(settings && settings.personal)}>
<a href={`/users/${this.props.current_user === undefined ? "" : this.props.current_user.login}`}> <a href={`/users/${this.props.current_user && 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 alt="头像" src={getImageUrl(`/${user.image_url}`)} className="currentImg"></img>
</img>
</a> </a>
<ul className="edu-menu-list" style={{ top: '60px', textAlign: 'center' }}> </Dropdown>
<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>
} }
</div> </div>
</div> </div>

View File

@ -1,7 +1,6 @@
.dropdownFlex{ .dropdownFlex{
display:flex; display:flex;
padding:5px;
background:#fff; background:#fff;
border-radius: 3px; border-radius: 3px;
.ant-menu-vertical > .ant-menu-item{ .ant-menu-vertical > .ant-menu-item{
@ -9,11 +8,57 @@
height: 35px; height: 35px;
line-height: 35px; line-height: 35px;
margin:0px; 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{ .ant-menu-vertical{
border:none; 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 { .newFooter {
position: absolute; position: absolute;
@ -66,3 +111,12 @@
} }
} }
} }
.inviteForm{
.ant-form-item{
margin-right: 0px;
}
.ant-form-item-label{
width: 110px;
text-align: right;
}
}

View File

@ -239,10 +239,12 @@ function CoderDepot(props){
} }
}) })
} }
let n = fileInfo && fileInfo.name; let n = fileInfo && fileInfo.name;
const mdFlag = n && n.substring(n.length-3,n.length) === ".md"; 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( return(
<WhiteBack> <WhiteBack>
<UpdateDescModal desc={desc} website={website} lesson_url={lesson_url} visible={openModal} onCancel={()=>setOpenModal(false)} onOk={okUpdate}/> <UpdateDescModal desc={desc} website={website} lesson_url={lesson_url} visible={openModal} onCancel={()=>setOpenModal(false)} onOk={okUpdate}/>
@ -311,7 +313,7 @@ function CoderDepot(props){
} }
<a onClick={()=>urlLink(`/projects/${owner}/${projectsId}/issues/new`)} >+ 任务</a> <a onClick={()=>urlLink(`/projects/${owner}/${projectsId}/issues/new`)} >+ 任务</a>
</div> </div>
{ type === "dir" && projectDetail.type !== 2 && { fileOperate &&
<Dropdown overlay={fileMenu} className="mr20" trigger={['click']}> <Dropdown overlay={fileMenu} className="mr20" trigger={['click']}>
<Button type="default">文件 <i className="iconfont icon-sanjiaoxing-down ml3 font-14 color-grey-9"></i></Button> <Button type="default">文件 <i className="iconfont icon-sanjiaoxing-down ml3 font-14 color-grey-9"></i></Button>
</Dropdown> </Dropdown>

View File

@ -1,8 +1,9 @@
import React, { Component } from 'react'; 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 { Link, Route, Switch } from 'react-router-dom';
import { Content } from '../Component/layout'; import { Content , FlexAJ } from '../Component/layout';
import DetailBanner from './sub/DetailBanner'; import DetailBanner from './sub/DetailBanner';
import Invite from './sub/Invite';
import '../css/index.scss' import '../css/index.scss'
import './list.css'; 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() { render() {
@ -406,16 +416,6 @@ class Detail extends Component {
const { state } = this.props.history.location; 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 = { const common = {
getDetail: this.getDetail, getDetail: this.getDetail,
changeOpenDevops:this.changeOpenDevops, changeOpenDevops:this.changeOpenDevops,
@ -425,7 +425,7 @@ class Detail extends Component {
<div> <div>
<div className="detailHeader-wrapper"> <div className="detailHeader-wrapper">
<div className="normal"> <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" }}> <p className="font-22 df flex-1 lineH2 mt15" style={{ alignItems: "center" }}>
{project && project.author && {project && project.author &&
<Link to={`${project.author.type ==="Organization" ? "/organize":'/users'}/${project.author.login}`} className="show-user-link"> <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="ml5 mr5">/</span>
<span className="hide-1 flex-1 df"> <span className="hide-1 flex-1 df">
<Link to={`/projects/${owner}/${projectsId}`} className="font-22">{project && project.name}</Link> <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 && projectDetail.type && projectDetail.type !== 0 ?
projectDetail.type === 2 ? projectDetail.type === 2 ?
@ -516,6 +508,19 @@ class Detail extends Component {
</span> </span>
} }
</div> </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 ? "" : firstSync ? "" :
<DetailBanner <DetailBanner

View File

@ -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;

114
src/forge/Notice/Apply.jsx Normal file
View File

@ -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;

View File

@ -6,6 +6,10 @@ import Loadable from "react-loadable";
import Loading from "../../Loading"; import Loading from "../../Loading";
import { Route, Switch } from "react-router-dom"; import { Route, Switch } from "react-router-dom";
const Apply = Loadable({
loader: () => import("./Apply"),
loading: Loading,
});
const Notify = Loadable({ const Notify = Loadable({
loader: () => import("./Notify"), loader: () => import("./Notify"),
loading: Loading, loading: Loading,
@ -18,38 +22,62 @@ function Index(props){
const username = props.match.params.username; const username = props.match.params.username;
const pathname = props.history.location.pathname; const pathname = props.history.location.pathname;
const user = props.user; const user = props.user;
const undo_messages = props.undo_messages;
const [ menu , setMenu ] = useState("notify"); const [ menu , setMenu ] = useState("notify");
const [ messages , setMessages ] = useState(0); const [ messagesCount , setMessagesCount ] = useState(0);
const [ transferProjects , setTransferProjects ] = 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(()=>{ useEffect(()=>{
if(user){ if(user){
setTransferProjects(user.undo_transfer_projects); setTransferCount(user.undo_transfer_projects);
setApplyCount(user.undo_join_projects);
setMessagesCount(user.undo_messages);
} }
if(undo_messages){ },[user])
setMessages(undo_messages);
}
},[user,undo_messages])
useEffect(()=>{ useEffect(()=>{
if(pathname && username){ if(pathname && username){
if(pathname === `/users/${username}/notice`){ if(pathname === `/users/${username}/notice`){
setMenu("notify"); setMenu("notify");
changeNum(user.undo_messages);
} }
if(pathname === `/users/${username}/notice/undo`){ if(pathname === `/users/${username}/notice/undo`){
setMenu("undo"); setMenu("undo");
} }
if(pathname === `/users/${username}/notice/apply`){
setMenu("apply");
} }
},[pathname])
function fetchUser(){
props && props.fetchUser();
} }
},[pathname,user])
function changeNum(){ 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 ( return (
@ -58,27 +86,39 @@ function Index(props){
<li className={menu === "notify" ? "active":""}> <li className={menu === "notify" ? "active":""}>
<Link to={`/users/${username}/notice`} onClick={changeNum}> <Link to={`/users/${username}/notice`} onClick={changeNum}>
<span>通知</span> <span>通知</span>
{messages ? <span className="unNum">{messages}</span>:""} {messagesCount ? <span className="unNum">{messagesCount}</span>:""}
</Link> </Link>
</li> </li>
<li className={menu === "undo" ? "active":""}> <li className={menu === "undo" ? "active":""}>
<Link to={`/users/${username}/notice/undo`}> <Link to={`/users/${username}/notice/undo`}>
<span>接收仓库</span> <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> </Link>
</li> </li>
</ul> </ul>
<Switch> <Switch>
<Route
path="/users/:username/notice/apply"
render={(p) => {
return <Apply {...props} {...p} deleteEvent={deleteEvent}/>;
}}
></Route>
<Route <Route
path="/users/:username/notice/undo" path="/users/:username/notice/undo"
render={(p) => { render={(p) => {
return <UndoEvent {...props} {...p} fetchUser={fetchUser}/>; return <UndoEvent {...props} {...p} deleteEvent={deleteEvent}/>;
}} }}
></Route> ></Route>
<Route <Route
path="/users/:username/notice" path="/users/:username/notice"
render={(p) => { render={(p) => {
return <Notify {...props} {...p} fetchUser={fetchUser}/>; return <Notify {...props} {...p} deleteEvent={deleteEvent}/>;
}} }}
></Route> ></Route>
</Switch> </Switch>

View File

@ -26,7 +26,7 @@
min-width: 23px; min-width: 23px;
text-align: center; text-align: center;
background-color: #ffe4b3; background-color: #ffe4b3;
margin-top: 19px; margin-top: 27px;
margin-left: 10px; margin-left: 10px;
display: block; display: block;
} }

View File

@ -13,6 +13,10 @@ function Notify(props){
const [ total , setTotal ] = useState(0); const [ total , setTotal ] = useState(0);
const [ isSpin , setIsSpin ] = useState(true); const [ isSpin , setIsSpin ] = useState(true);
useEffect(()=>{
props && props.deleteEvent("notify",0);
},[])
useEffect(()=>{ useEffect(()=>{
if(username){ if(username){
setIsSpin(true); setIsSpin(true);
@ -53,6 +57,20 @@ 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( return(
<div> <div>
<Spin spinning={isSpin}> <Spin spinning={isSpin}>
@ -70,7 +88,7 @@ function Notify(props){
<Link to={`/users/${i.applied_user && i.applied_user.login}`} className="font-15 mr20">{i.applied_user && i.applied_user.name}</Link> <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> <span className="color-grey-9">{i.time_ago}</span>
</p> </p>
{renderStatus(i.status,i.applied)} { i.applied_type === "AppliedProject" ? renderApplyStatus(i.status,i.applied):renderStatus(i.status,i.applied)}
</div> </div>
</li> </li>
) )
@ -80,8 +98,6 @@ function Notify(props){
: :
"" ""
} }
</div>
</Spin>
{list && list.length === 0 && <Nodata _html="暂无通知" />} {list && list.length === 0 && <Nodata _html="暂无通知" />}
{ {
total > limit && total > limit &&
@ -90,6 +106,8 @@ function Notify(props){
</div> </div>
} }
</div> </div>
</Spin>
</div>
) )
} }
export default Notify; export default Notify;

View File

@ -42,7 +42,7 @@ function UndoEvent(props){
Axios.post(url).then(result=>{ Axios.post(url).then(result=>{
if(result && result.data){ if(result && result.data){
getList(); getList();
props && props.fetchUser(); props && props.deleteEvent("undo",1);
} }
}).catch(error=>{}) }).catch(error=>{})
} }
@ -53,7 +53,7 @@ function UndoEvent(props){
Axios.post(url).then(result=>{ Axios.post(url).then(result=>{
if(result && result.data){ if(result && result.data){
getList(); getList();
props && props.fetchUser(); props && props.deleteEvent("undo",1);
} }
}).catch(error=>{}) }).catch(error=>{})
} }

View File

@ -34,7 +34,7 @@ function Collaborator(props){
} }
{ {
nav === "1" ? nav === "1" ?
<AddMember getID={getID} login/> <AddMember getID={getID} login showNotification={props.showNotification}/>
: :
<AddGroup getGroupID={getGroupID} organizeId={owner}/> <AddGroup getGroupID={getGroupID} organizeId={owner}/>
} }

View File

@ -146,7 +146,7 @@ export default ((props) => {
<WhiteBack style={{minHeight:"400px"}}> <WhiteBack style={{minHeight:"400px"}}>
<Title> <Title>
<span>团队成员管理</span> <span>团队成员管理</span>
<AddMember getID={getID}/> <AddMember getID={getID} showNotification={props.showNotification}/>
</Title> </Title>
<FlexAJ className="padding20-30"> <FlexAJ className="padding20-30">
<div style={{ width: "580px" }}> <div style={{ width: "580px" }}>

View File

@ -104,6 +104,7 @@ $flex:flex;
margin:10px 0px; margin:10px 0px;
word-break: break-all; word-break: break-all;
text-align: justify; text-align: justify;
font-size: 16px;
} }
.focusBox,.infoBox{ .focusBox,.infoBox{
width: 100%!important; width: 100%!important;

View File

@ -66,7 +66,6 @@ class Infos extends Component {
project_type: undefined, project_type: undefined,
route_type: undefined, route_type: undefined,
undo_events:0, undo_events:0,
undo_messages:0,
menuKey:"0" menuKey:"0"
}; };
} }
@ -124,20 +123,14 @@ class Infos extends Component {
}); });
const { current_user } = this.props; const { current_user } = this.props;
const { username } = this.props.match.params; 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`; let url = `/users/${username || (current_user && current_user.login)}.json`;
axios.get(url).then((result) => { axios.get(url).then((result) => {
let e = result.data && result.data.undo_events; 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({ this.setState({
user: result.data, user: result.data,
isSpin: false, isSpin: false,
undo_events:n ? (e-p) : e, undo_events:e
undo_messages:0,
notice:n
}); });
}) })
.catch((error) => { .catch((error) => {
@ -161,7 +154,6 @@ class Infos extends Component {
this.setState({ this.setState({
route_type: undefined, route_type: undefined,
project_type:"notice", project_type:"notice",
notice:true
},()=>{ },()=>{
this.props.history.push(`/users/${user && user.login}/notice`); this.props.history.push(`/users/${user && user.login}/notice`);
this.fetchUser(); this.fetchUser();
@ -190,10 +182,19 @@ class Infos extends Component {
resetUserInfo && resetUserInfo(); resetUserInfo && resetUserInfo();
} }
// 修改待办事项右侧的数量
deleteUndoEvent=(count)=>{
let { undo_events } = this.state;
let undo = undo_events - count;
this.setState({
undo_events:undo
})
}
render() { render() {
const { current_user, mygetHelmetapi , resetUserInfo } = this.props; const { current_user } = this.props;
const { username } = this.props.match.params; 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 ( return (
<div className="newMain clearfix"> <div className="newMain clearfix">
<Spin spinning={isSpin}> <Spin spinning={isSpin}>
@ -213,7 +214,7 @@ class Infos extends Component {
</span> </span>
</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} {user && user.username}
</div> </div>
<div className="userDescription"> <div className="userDescription">
@ -306,7 +307,7 @@ class Infos extends Component {
<Route <Route
path="/users/:username/notice" path="/users/:username/notice"
render={() => { render={() => {
return <Notice {...this.props} {...this.state} fetchUser={this.fetchUser}/>; return <Notice {...this.props} {...this.state} deleteUndoEvent={this.deleteUndoEvent}/>;
}} }}
></Route> ></Route>
<Route <Route

View File

@ -45,6 +45,7 @@ export default Form.create()(
if(result && result.data){ if(result && result.data){
props.showNotification("资料修改成功!") props.showNotification("资料修改成功!")
resetUser && resetUser(result.data); resetUser && resetUser(result.data);
props.history.push(`/users/${username}`)
} }
}).catch(error=>{}) }).catch(error=>{})
} }