-
+
{project && project.author &&
@@ -435,15 +435,7 @@ class Detail extends Component {
/
{project && project.name}
- {
- projectDetail && projectDetail.forked_from_project_id && projectDetail.fork_info ?
-
-
-
-
- : ""
- }
+
{
projectDetail && projectDetail.type && projectDetail.type !== 0 ?
projectDetail.type === 2 ?
@@ -516,6 +508,19 @@ class Detail extends Component {
}
+
+
+ {
+ projectDetail && projectDetail.forked_from_project_id && projectDetail.fork_info ?
+ this.textFunc(projectDetail.forked_from_project_id,projectDetail.fork_info)
+ :""
+ }
+
+ {
+ projectDetail && projectDetail.invite_code &&
+
+ }
+
{
firstSync ? "" :
+ 邀请码: {code}
+ 可以通过邀请码邀请成员加入项目
点击复制邀请码。} placement={"bottom"}>
+ jsCopy("#devitecode")}>
+
+
+ )
+}
+export default Invite;
\ No newline at end of file
diff --git a/src/forge/users/Infos.js b/src/forge/users/Infos.js
index 77ab795a1..c19bce079 100644
--- a/src/forge/users/Infos.js
+++ b/src/forge/users/Infos.js
@@ -191,9 +191,9 @@ class Infos extends Component {
}
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 (
From 63e9cfe39464cff4ebf5f3e08945ab3c2dc82efd Mon Sep 17 00:00:00 2001
From: caishi <1149225589@qq.com>
Date: Fri, 11 Jun 2021 09:25:59 +0800
Subject: [PATCH 2/4] =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E9=82=80=E8=AF=B7?=
=?UTF-8?q?=E7=A0=81-=E7=AC=AC=E4=B8=80=E7=89=88=E6=B5=8B=E8=AF=95?=
=?UTF-8?q?=E7=89=88=E4=B8=8A=E7=BA=BF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/AppConfig.js | 2 +-
src/forge/Head/AddProjectModal.jsx | 84 +++++++++++++++++++++
src/forge/Head/Header.js | 5 +-
src/forge/Head/header.scss | 17 ++++-
src/forge/Notice/Apply.jsx | 114 +++++++++++++++++++++++++++++
src/forge/Notice/Index.jsx | 76 ++++++++++++++-----
src/forge/Notice/Index.scss | 2 +-
src/forge/Notice/Notify.jsx | 22 +++++-
src/forge/Notice/UndoEvent.jsx | 4 +-
src/forge/users/Infos.js | 21 +++---
10 files changed, 309 insertions(+), 38 deletions(-)
create mode 100644 src/forge/Head/AddProjectModal.jsx
create mode 100644 src/forge/Notice/Apply.jsx
diff --git a/src/AppConfig.js b/src/AppConfig.js
index 034612ece..a9853e2ac 100644
--- a/src/AppConfig.js
+++ b/src/AppConfig.js
@@ -25,7 +25,7 @@ if (isDev) {
}
debugType = window.location.search.indexOf('debug=t') !== -1 ? 'teacher' :
window.location.search.indexOf('debug=s') !== -1 ? 'student' :
- window.location.search.indexOf('debug=a') !== -1 ? 'admin' : parsed.debug || 'admin'
+ window.location.search.indexOf('debug=a') !== -1 ? 'admin' : parsed.debug || 'student'
}
window._debugType = debugType;
export function initAxiosInterceptors(props) {
diff --git a/src/forge/Head/AddProjectModal.jsx b/src/forge/Head/AddProjectModal.jsx
new file mode 100644
index 000000000..8e3073e30
--- /dev/null
+++ b/src/forge/Head/AddProjectModal.jsx
@@ -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(
+
+ setVisible(false)}
+ >
+
+ {getFieldDecorator("code",{
+ rules:[
+ {required:true,message:"请输入6位项目邀请码"},
+ {validator:checkValue}
+ ]
+ })(
+
+ )}
+
+
+ {getFieldDecorator("role",{
+ rules:[{required:true,message:"请选择角色"}]
+ })(
+
+ 管理员
+ 开发者
+ 报告者
+
+ )}
+
+
+
+ setVisible(true)}>加入项目
+
+ )
+ })
+)
\ No newline at end of file
diff --git a/src/forge/Head/Header.js b/src/forge/Head/Header.js
index 8c4cdd15a..6c3f96a62 100644
--- a/src/forge/Head/Header.js
+++ b/src/forge/Head/Header.js
@@ -5,8 +5,7 @@ import axios from 'axios';
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 './header.scss';
@@ -268,11 +267,11 @@ class NewHeader extends Component {
)
})
}
+
)
}
-
renderMenu=(personal)=>{
const { current_user } = this.props;
return(
diff --git a/src/forge/Head/header.scss b/src/forge/Head/header.scss
index 507262e37..a2c11b7a1 100644
--- a/src/forge/Head/header.scss
+++ b/src/forge/Head/header.scss
@@ -1,7 +1,6 @@
.dropdownFlex{
display:flex;
- padding:5px;
background:#fff;
border-radius: 3px;
.ant-menu-vertical > .ant-menu-item{
@@ -9,6 +8,13 @@
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;
@@ -104,4 +110,13 @@
}
}
}
+}
+.inviteForm{
+ .ant-form-item{
+ margin-right: 0px;
+ }
+ .ant-form-item-label{
+ width: 110px;
+ text-align: right;
+ }
}
\ No newline at end of file
diff --git a/src/forge/Notice/Apply.jsx b/src/forge/Notice/Apply.jsx
new file mode 100644
index 000000000..3c77edfaa
--- /dev/null
+++ b/src/forge/Notice/Apply.jsx
@@ -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(
+
+
+
+ {
+ list && list.length > 0 ?
+
+ {
+ list.map((i,k)=>{
+ return(
+ -
+
+
+
+ {i.user && i.user.name}
+ {i.time_ago}
+
+
+ 申请以【{i.role === "developer" ?"开发者":i.role === "manager" ? "管理者":"报告者"}】身份加入【{i.project && i.project.name}】项目。是否同意?
+ {
+ i.status === "common" &&
+
+ acceptDivert(i.id)}>
+ 同意
+
+ revertDivert(i.id)}>
+ 拒绝
+
+
+ }
+ {
+ i.status === "accepted" && 已接受
+ }
+ {
+ i.status === "refused" && 已拒绝
+ }
+
+
+
+ )
+ })
+ }
+
+ :
+ ""
+ }
+ {list && list.length === 0 &&
}
+ {
+ total > limit &&
+
+ }
+
+
+
+ )
+}
+export default Apply;
\ No newline at end of file
diff --git a/src/forge/Notice/Index.jsx b/src/forge/Notice/Index.jsx
index d9d04d59e..94c63804e 100644
--- a/src/forge/Notice/Index.jsx
+++ b/src/forge/Notice/Index.jsx
@@ -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){
通知
- {messages ? {messages}:""}
+ {messagesCount ? {messagesCount}:""}
接收仓库
- {transferProjects ? {transferProjects}:""}
+ {transferCount ? {transferCount}:""}
+
+
+
+
+ 成员申请
+ {applyCount ? {applyCount}:""}
+ {
+ return ;
+ }}
+ >
{
- return ;
+ return ;
}}
>
{
- return ;
+ return ;
}}
>
diff --git a/src/forge/Notice/Index.scss b/src/forge/Notice/Index.scss
index a50c34131..b00e18a5a 100644
--- a/src/forge/Notice/Index.scss
+++ b/src/forge/Notice/Index.scss
@@ -26,7 +26,7 @@
min-width: 23px;
text-align: center;
background-color: #ffe4b3;
- margin-top: 19px;
+ margin-top: 27px;
margin-left: 10px;
display: block;
}
diff --git a/src/forge/Notice/Notify.jsx b/src/forge/Notice/Notify.jsx
index 3aad55ae5..761366d3d 100644
--- a/src/forge/Notice/Notify.jsx
+++ b/src/forge/Notice/Notify.jsx
@@ -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
取消转移【{project && project.name}】仓库
case 'common':
return
正在将【{project && project.name}】仓库转移给【{owner && owner.name}】
- case 'successed':
+ case 'successed':
return
【{project && project.name}】仓库成功转移给【{owner && owner.name}】
default:
return
拒绝转移【{project && project.name}】仓库
@@ -53,6 +57,20 @@ function Notify(props){
}
}
+ function renderApplyStatus(status,applied) {
+ let { project } = applied;
+ if(status){
+ switch(status){
+ case 'successed':
+ return
已通过你加入【{project && project.name}】项目的申请
+ default:
+ return
已拒绝你加入【{project && project.name}】项目的申请
+ }
+ }else{
+ return ""
+ }
+ }
+
return(
@@ -70,7 +88,7 @@ function Notify(props){
{i.applied_user && i.applied_user.name}
{i.time_ago}
- {renderStatus(i.status,i.applied)}
+ { i.applied_type === "AppliedProject" ? renderApplyStatus(i.status,i.applied):renderStatus(i.status,i.applied)}
)
diff --git a/src/forge/Notice/UndoEvent.jsx b/src/forge/Notice/UndoEvent.jsx
index 68557a9e9..c8c579dc1 100644
--- a/src/forge/Notice/UndoEvent.jsx
+++ b/src/forge/Notice/UndoEvent.jsx
@@ -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=>{})
}
diff --git a/src/forge/users/Infos.js b/src/forge/users/Infos.js
index c19bce079..9b292d2eb 100644
--- a/src/forge/users/Infos.js
+++ b/src/forge/users/Infos.js
@@ -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,6 +182,15 @@ 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 } = this.props;
const { username } = this.props.match.params;
@@ -306,7 +307,7 @@ class Infos extends Component {
{
- return ;
+ return ;
}}
>
Date: Fri, 11 Jun 2021 11:01:46 +0800
Subject: [PATCH 3/4] =?UTF-8?q?=E5=A4=B4=E9=83=A8=E5=AF=BC=E8=88=AA?=
=?UTF-8?q?=E6=A0=8F-=E6=90=9C=E7=B4=A2=E5=B0=8F=E6=94=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
public/css/edu-purge.css | 1 -
src/AppConfig.js | 2 +-
src/forge/Head/Header.js | 3 ++-
src/forge/Main/CoderDepot.jsx | 6 ++++--
4 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/public/css/edu-purge.css b/public/css/edu-purge.css
index 4d59fc11f..c8f7c907f 100644
--- a/public/css/edu-purge.css
+++ b/public/css/edu-purge.css
@@ -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;
diff --git a/src/AppConfig.js b/src/AppConfig.js
index a9853e2ac..034612ece 100644
--- a/src/AppConfig.js
+++ b/src/AppConfig.js
@@ -25,7 +25,7 @@ if (isDev) {
}
debugType = window.location.search.indexOf('debug=t') !== -1 ? 'teacher' :
window.location.search.indexOf('debug=s') !== -1 ? 'student' :
- window.location.search.indexOf('debug=a') !== -1 ? 'admin' : parsed.debug || 'student'
+ window.location.search.indexOf('debug=a') !== -1 ? 'admin' : parsed.debug || 'admin'
}
window._debugType = debugType;
export function initAxiosInterceptors(props) {
diff --git a/src/forge/Head/Header.js b/src/forge/Head/Header.js
index 6c3f96a62..86bb52c54 100644
--- a/src/forge/Head/Header.js
+++ b/src/forge/Head/Header.js
@@ -88,10 +88,11 @@ class NewHeader extends Component {
}, 300)
}}
>
- this.onGlobalSearch(value,item)}
autoFocus={true}
+ style={{width:"190px"}}
/>
)
diff --git a/src/forge/Main/CoderDepot.jsx b/src/forge/Main/CoderDepot.jsx
index adda25c54..b1c384652 100644
--- a/src/forge/Main/CoderDepot.jsx
+++ b/src/forge/Main/CoderDepot.jsx
@@ -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(