This commit is contained in:
ZhangRuining 2025-03-11 11:43:22 +08:00
parent 61eea3c3f7
commit 69bb64c4de
9 changed files with 818 additions and 137 deletions

View File

@ -32,7 +32,8 @@ export function initAxiosInterceptors(props) {
// 判断网络是否连接
initOnlineOfflineListener();
var proxy = "http://172.16.100.220:81";
// var proxy = "http://172.16.100.220:81";
var proxy = 'http://172.16.100.130:81'
//响应前的设置
axios.interceptors.request.use(
config => {

View File

@ -1,84 +1,270 @@
import React, { useState , forwardRef, useEffect } from 'react';
import { Form , Modal , Input , Radio } from 'antd';
import React, { useState, forwardRef, useEffect } from 'react';
import { Form, Modal, Input, Radio, Upload, Button, Icon, message } from 'antd';
import Axios from 'axios';
import { getUploadActionUrl, appendFileSizeToUploadFileAll } from 'educoder';
export default Form.create()(
forwardRef((props)=>{
const { getFieldDecorator, validateFields , setFieldsValue } = props && props.form;
const [ visible , setVisible ] = useState(false);
const JoinProjectForm = Form.create({
name: 'join_project_form',
})(
forwardRef((props) => {
const { getFieldDecorator, validateFields } = props.form;
const { onOk, onCancel } = props;
useEffect(()=>{
if(!visible){
setFieldsValue({
code:undefined,
role:"developer"
})
useEffect(() => {
if (props.visible) {
props.form.setFieldsValue({
code: '',
role: 'developer',
});
}
},[visible])
}, [props.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){
function checkValue(rule, value, callback) {
if (!value) {
callback();
}
if(value.length < 6 || value.length > 6){
callback("请输入6位数的邀请码");
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>
)
const handleOk = () => {
validateFields((error, values) => {
if (!error) {
onOk(values);
}
});
};
return (
<Modal
title="加入项目"
width="480px"
visible={props.visible}
centered={true}
onOk={handleOk}
onCancel={onCancel}
>
<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>
<Radio value="manager">管理员</Radio>
<Radio value="developer">开发者</Radio>
<Radio value="reporter">报告者</Radio>
</Radio.Group>
)}
</Form.Item>
</Form>
</Modal>
);
})
)
);
const PersonalInfoForm = Form.create({
name: 'personal_info_form',
})(
forwardRef((props) => {
const { getFieldDecorator, validateFields } = props.form;
const { onOk, onCancel, contentFileList, uploadProps } = props;
useEffect(() => {
if (props.visible) {
props.form.setFieldsValue({
remarks: '',
attachment_id: '',
});
}
}, [props.visible])
const handleOk = () => {
validateFields((error, values) => {
if (!error) {
const attachmentId = contentFileList.map((item) => {
return item.response ? item.response.id : item.id;
});
onOk({ ...values, attachment_id: attachmentId.join(',') });
}
});
};
return (
<Modal
title="填写个人信息"
width="480px"
visible={props.visible}
centered={true}
onOk={handleOk}
onCancel={onCancel}
>
<Form layout="inline" className="inviteForm2">
<Form.Item label="个人情况">
{getFieldDecorator('remarks', {
rules: [{ required: true, message: '请输入个人情况' }],
})(
<Input
placeholder="请输入个人情况"
// autoComplete="off"
maxLength={200}
style={{ width: '300px', pointerEvents: 'auto' }} // pointer-events auto
/>
)}
</Form.Item>
<Form.Item label="个人资料" style={{ flexWrap: 'nowrap' }}>
{getFieldDecorator('attachment_id', {
rules: [],
})(
<Upload {...uploadProps}>
<Button type="default" className='mr5'>
<Icon type="upload" /> 上传附件
</Button>
(单个文件最大20MB)
</Upload>
)}
</Form.Item>
</Form>
</Modal>
);
})
);
export default forwardRef((props) => {
const [visible, setVisible] = useState(false);
const [infoVis, setInfoVis] = useState(false);
const [contentFileList, setContentFileList] = useState([]);
//
const [joinProjectValues, setJoinProjectValues] = useState({});
const uploadProps = {
fileList: contentFileList,
multiple: false,
action: `${getUploadActionUrl()}`,
onChange: (info) => {
let content = info.fileList;
setContentFileList(appendFileSizeToUploadFileAll(content));
},
onRemove: (file) => {
if (file.response != undefined) {
Modal.confirm({
content: '是否确认删除?',
onOk: () => {
deleteAttachment(file);
},
onCancel() {
console.log('Cancel');
},
});
return false;
}
},
beforeUpload: (file) => {
console.log('beforeUpload', file.name);
const isLt150M = file.size / 1024 / 1024 <= 20;
if (!isLt150M) {
message.error('文件大小必须不超过20MB!');
}
return isLt150M;
},
};
const deleteAttachment = (file) => {
const url = `/attachments/${file.response ? file.response.id : file.uid}.json`;
Axios.delete(url, {})
.then((response) => {
if (response.data) {
const { status } = response.data;
if (status === 0) {
setContentFileList([]);
}
}
})
.catch((error) => {
console.log(error);
});
};
const handleJoinProjectOk = (values) => {
const url = `/applied_projects.json`;
setJoinProjectValues({ ...values })
Axios.post(url, {
applied_project: {
alert_remarks: false,
...values,
},
})
.then((result) => {
if (result) {
setVisible(false);
if (result.data.join_project_need_verify) {
setInfoVis(true);
} else {
props.showNotification('申请已提交,请等待项目管理者审核!');
}
}
})
.catch((error) => {
console.log(error);
});
};
const handlePersonalInfoOk = (values) => {
const url = `/applied_projects.json`;
let attachmentId = contentFileList.map(item => {
return item.response ? item.response.id : item.id
})
Axios.post(url, {
personal_info: {
...joinProjectValues,
alert_remarks: true,
...values,
attachment_id: attachmentId.join(','),
},
})
.then((result) => {
if (result && result.data) {
setInfoVis(false);
setContentFileList([]);
props.showNotification('申请已提交,请等待项目管理者审核!');
}
})
.catch((error) => {
console.log(error);
});
};
return (
<React.Fragment>
<JoinProjectForm
visible={visible}
onOk={handleJoinProjectOk}
onCancel={() => setVisible(false)}
/>
<PersonalInfoForm
visible={infoVis}
onOk={handlePersonalInfoOk}
onCancel={() => setInfoVis(false)}
contentFileList={contentFileList}
uploadProps={uploadProps}
/>
<a onClick={() => setVisible(true)}>加入项目</a>
</React.Fragment>
);
});

View File

@ -152,4 +152,17 @@
width: 110px;
text-align: right;
}
}
.inviteForm2{
.ant-form-item{
margin-right: 0px;
display:flex;
flex-wrap: nowrap;
}
.ant-form-item-label{
width: 92px;
text-align: right;
}
}

View File

@ -32,14 +32,15 @@ class Index extends Component {
recommendList:undefined,
languageList:undefined,
languageId:undefined
languageId:undefined,
filter:undefined,
}
}
componentDidMount = () => {
const { page, limit, search, sort, project_type, category_id , languageId } = this.state;
this.getListData(page, limit, search, sort, project_type, category_id , languageId);
const { page, limit, search, sort, project_type, category_id , languageId,filter } = this.state;
this.getListData(page, limit, search, sort, project_type, category_id , languageId,filter);
this.getType();
@ -74,7 +75,7 @@ class Index extends Component {
}
// 获取列表
getListData = (page, limit, search, sort, project_type, category_id,languageId) => {
getListData = (page, limit, search, sort, project_type, category_id,languageId,filter) => {
const { current_user } = this.props;
const url = `/projects.json`;
axios.get(url, {
@ -86,7 +87,8 @@ class Index extends Component {
sort_by: sort,
project_type,
category_id,
language_id:languageId
language_id:languageId,
filter
}
}).then((result) => {
if (result) {
@ -132,8 +134,8 @@ class Index extends Component {
search: undefined
})
this.setTypeList(list, type)
const { page, limit, sort, category_id , languageId } = this.state;
this.getListData(page, limit, undefined, sort, type, category_id , languageId);
const { page, limit, sort, category_id , languageId,filter } = this.state;
this.getListData(page, limit, undefined, sort, type, category_id , languageId,filter);
}
// 获取类型
@ -168,8 +170,8 @@ class Index extends Component {
page: 1
});
this.setCategoryList(list, id)
const { limit, sort, project_type , languageId } = this.state;
this.getListData(1, limit, undefined, sort, project_type, id , languageId);
const { limit, sort, project_type , languageId,filter } = this.state;
this.getListData(1, limit, undefined, sort, project_type, id , languageId,filter);
}
// 排序
@ -180,8 +182,8 @@ class Index extends Component {
search: undefined,
isSpin: true
})
const { limit, project_type, category_id , languageId } = this.state;
this.getListData(1, limit, undefined, e.key, project_type, category_id , languageId);
const { limit, project_type, category_id , languageId,filter } = this.state;
this.getListData(1, limit, undefined, e.key, project_type, category_id , languageId,filter);
}
// 搜索
@ -193,8 +195,8 @@ class Index extends Component {
project_type: undefined,
sort: "updated_on"
})
const { limit, sort, category_id , languageId } = this.state;
this.getListData(1, limit, value, sort, undefined, category_id , languageId);
const { limit, sort, category_id , languageId ,filter} = this.state;
this.getListData(1, limit, value, sort, undefined, category_id , languageId,filter);
}
changeSearchValue = (e) => {
this.setState({
@ -206,8 +208,8 @@ class Index extends Component {
this.setState({
page
})
const { limit, search, sort, project_type, category_id , languageId } = this.state;
this.getListData(page, limit, search, sort, project_type, category_id , languageId);
const { limit, search, sort, project_type, category_id , languageId,filter } = this.state;
this.getListData(page, limit, search, sort, project_type, category_id , languageId,filter);
}
getoDetail=(login,identifier)=>{
@ -220,8 +222,17 @@ class Index extends Component {
isSpin:true,
languageId:e === 0 ?undefined:e
})
const { page, limit, sort , project_type , category_id } = this.state;
this.getListData(page, limit, undefined, sort, project_type, category_id ,e === 0 ?undefined:e);
const { page, limit, sort , project_type , category_id,filter } = this.state;
this.getListData(page, limit, undefined, sort, project_type, category_id ,e === 0 ?undefined:e,filter);
}
changeFilter=(e)=>{
this.setState({
isSpin:true,
filter:e
})
const { page,limit, search, sort, project_type, category_id , languageId } = this.state;
this.getListData(page, limit, search, sort, project_type, category_id , languageId,e);
}
menu =()=> {
@ -282,7 +293,7 @@ class Index extends Component {
{
recommendList && recommendList.length>0 &&
<div className="recommandProjects">
{
{/* {
recommendList.map((item,key)=>{
return(
<div onClick={()=>this.getoDetail(item.author && item.author.login,item.identifier)}>
@ -298,7 +309,7 @@ class Index extends Component {
</div>
)
})
}
} */}
</div>
}
@ -315,6 +326,11 @@ class Index extends Component {
</div>
<div className="list-right boxShandow radius-2" style={{padding:0}}>
<Spin spinning={isSpin}>
<div style={{padding:'20px 30px 0px 30px'}}>
<Button size='large' onClick={()=>{this.changeFilter(undefined)}} type={this.state.filter == undefined ? 'primary':'default'}>全部</Button>
<Button size='large' onClick={()=>{this.changeFilter('manage')}} type={this.state.filter == 'manage' ? 'primary':'default'} className='ml10 mr10'>我管理的</Button>
<Button size='large' onClick={()=>{this.changeFilter('study')}} type={this.state.filter == 'study' ? 'primary':'default'}>我学习的</Button>
</div>
<div className="list-r-operation">
<div>
<Select

View File

@ -1,5 +1,5 @@
import React, { Component } from 'react';
import { Tooltip } from 'antd';
import { Tooltip, Badge, Modal, Form, Input, Upload, Button, Icon, Radio, message } from 'antd';
import { getImageUrl } from 'educoder';
import { AlignCenter } from '../Component/layout';
import { Link } from 'react-router-dom';
@ -7,87 +7,274 @@ import '../css/index.scss';
import Nodata from '../Nodata';
import './list.css';
import img_parise from '../Images/parise.png';
import Axios from 'axios'; // 引入 Axios
import { getUploadActionUrl,appendFileSizeToUploadFileAll } from 'educoder';
class PrivateAccessModal extends Component {
constructor(props) {
super(props);
this.state = {
contentFileList: []
};
}
handleOk = () => {
console.log('this.props.projecInfo',this.props.projectInfo)
this.props.form.validateFields((err, values) => {
if (!err) {
const url = `/applied_projects.json`;
let attachmentId = this.state.contentFileList.map(item => {
return item.response ? item.response.id : item.id
})
Axios.post(url, {
personal_info: {
alert_remarks: true,
...values,
attachment_id: attachmentId.join(','),
invite_code: this.props.projectInfo.invite_code
},
})
.then((result) => {
if (result && result.data) {
setInfoVis(false);
setContentFileList([]);
message.info('申请已提交,请等待项目管理者审核!');
}
})
.catch((error) => {
console.log(error);
});
this.props.onClose();
this.props.form.resetFields();
}
});
}
handleCancel = () => {
this.props.onClose();
this.props.form.resetFields();
}
uploadProps = {
fileList: [],
multiple: false,
action: `${getUploadActionUrl()}`,
onChange: (info) => {
let content = info.fileList;
this.setState({
contentFileList: appendFileSizeToUploadFileAll(content)
});
},
onRemove: (file) => {
if (file.response != undefined) {
Modal.confirm({
content: '是否确认删除?',
onOk: () => {
this.deleteAttachment(file);
},
onCancel() {
console.log('Cancel');
},
});
return false;
}
},
beforeUpload: (file) => {
console.log('beforeUpload', file.name);
const isLt150M = file.size / 1024 / 1024 <= 20;
if (!isLt150M) {
message.error('文件大小必须不超过20MB!');
}
return isLt150M;
},
};
deleteAttachment = (file) => {
const url = `/attachments/${file.response ? file.response.id : file.uid}.json`;
Axios.delete(url, {})
.then((response) => {
if (response.data) {
const { status } = response.data;
if (status === 0) {
this.setState({
contentFileList: []
});
}
}
})
.catch((error) => {
console.log(error);
});
};
render() {
const { getFieldDecorator } = this.props.form;
return (
<Modal
title="加入项目"
visible={this.props.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
centered={true}
width={500}
>
<Form layout="inline" className="inviteForm2">
<Form.Item label="选择角色">
{getFieldDecorator('role', {
rules: [{ required: true, message: '请选择角色' }],
initialValue: 'developer',
})(
<Radio.Group defaultValue={'developer'}>
<Radio value="manager">管理员</Radio>
<Radio value="developer">开发者</Radio>
<Radio value="reporter">报告者</Radio>
</Radio.Group>
)}
</Form.Item>
<Form.Item label="个人情况">
{getFieldDecorator('remarks', {
rules: [{ required: true, message: '请输入个人情况' }],
})(
<Input
placeholder="请输入个人情况"
// autoComplete="off"
maxLength={200}
style={{ width: '300px', pointerEvents: 'auto' }} // 确保 pointer-events 为 auto
/>
)}
</Form.Item>
<Form.Item label="个人资料" style={{ flexWrap: 'nowrap' }}>
{getFieldDecorator('attachment_id', {
rules: [],
})(
<Upload {...this.uploadProps}>
<Button type="default" className='mr5'>
<Icon type="upload" /> 上传附件
</Button>
(单个文件最大20MB)
</Upload>
)}
</Form.Item>
</Form>
</Modal>
);
}
}
const WrappedPrivateAccessModal = Form.create()(PrivateAccessModal);
class IndexItem extends Component {
TurnToDetail = (login, url) => {
this.props.history.push({
pathname: url,
state: login
})
});
}
state = {
showModal: false,
projectInfo: null
};
openModal = (projectInfo) => {
this.setState({ showModal: true, projectInfo });
};
closeModal = () => {
this.setState({ showModal: false, projectInfo: null });
};
render() {
const { projects } = this.props;
return (
<div className="project-list minH-670" style={{padding:"0px 20px"}}>
{ projects && projects.length > 0 ? projects.map((item, key) => {
<div className="project-list minH-670" style={{ padding: "0px 20px" }}>
{projects && projects.length > 0 ? projects.map((item, key) => {
return (
<div className="p-r-Item" key={key}>
{
item.platform === "educoder" ?
<a href="javascript:void(0)" style={{cursor:"default"}} className="show-user-link">
<img className="p-r-photo" alt="" src={item.author && item.author.image_url} ></img>
</a>
:
<Link to={item.author && (item.author.type === "Organization" ? `/organize/${item.author.login}`:`/users/${item.author.login}`)} className="show-user-link">
<img className="p-r-photo" alt="" src={getImageUrl(`/${item.author && item.author.image_url}`)} ></img>
</Link>
item.platform === "educoder" ? (
<a href="javascript:void(0)" style={{ cursor: "default" }} className="show-user-link">
<img className="p-r-photo" alt="" src={item.author && item.author.image_url} />
</a>
) : (
<Link to={item.author && (item.author.type === "Organization" ? `/organize/${item.author.login}` : `/users/${item.author.login}`)} className="show-user-link">
<img className="p-r-photo" alt="" src={getImageUrl(`/${item.author && item.author.image_url}`)} />
</Link>
)
}
<div className="p-r-Infos">
<div className="p-r-name">
<AlignCenter>
<Link to={`/projects/${item.author.login}/${item.identifier}`} title={`${item.author.name}/${item.name}`} className="color-grey-3 font-18 task-hide " style={{maxWidth: 470 }}>
<Link onClick={(e) => {
if (item.is_member || item.is_manager) {
return true;
} else {
e.preventDefault();
this.openModal(item);
return false;
}
}} to={`/projects/${item.author.login}/${item.identifier}`} title={`${item.author.name}/${item.name}`} className="color-grey-3 font-18 task-hide " style={{ maxWidth: 470 }}>
{item.author.name}/{item.name}
</Link>
{ !item.is_public && <span className="privateTag">私有</span> }
{!item.is_public && <span className="privateTag">私有</span>}
{
item.forked_from_project_id ?
item.forked_from_project_id ? (
<span className="ml5">
<i className="iconfont icon-fork font-18 color-orange" />
</span>
: ""
) : null
}
{
item.type && item.type !== 0 ?
item.type === 2 ?
<Tooltip title="该项目是一个镜像" className="ml5">
<i className="iconfont icon-banbenku font-18 color-green" />
</Tooltip>:
<span className="ml5">
<i className="iconfont icon-jingxiang font-18 color-green" />
</span>:""
item.type && item.type !== 0 ?
item.type === 2 ?
<Tooltip title="该项目是一个镜像" className="ml5">
<i className="iconfont icon-banbenku font-18 color-green" />
</Tooltip> :
<span className="ml5">
<i className="iconfont icon-jingxiang font-18 color-green" />
</span> : null
}
</AlignCenter>
{(item.is_manager && item.join_project_need_verify) && <span>
<Badge dot style={{ backgroundColor: '#FA6400' }}><span>待审核申请{item.pending_verify_count}</span></Badge>
</span>}
<span className="p-r-tags">
{
item.praises_count && item.praises_count>0 ?
<span className="pariseTag">
<img src={img_parise} alt="" className="pariseImg" /> {item.praises_count}
</span>:""
item.praises_count && item.praises_count > 0 ?
<span className="pariseTag">
<img src={img_parise} alt="" className="pariseImg" /> {item.praises_count}
</span> : null
}
{
item.forked_count && item.forked_count>0 ?
<span>
<i className="iconfont icon-fork mr3 font-16" style={{ color: "#1B8FFF" }} />fork {item.forked_count}
</span>:""
item.forked_count && item.forked_count > 0 ?
<span>
<i className="iconfont icon-fork mr3 font-16" style={{ color: "#1B8FFF" }} />fork {item.forked_count}
</span> : null
}
</span>
</div>
<p className="break_word task-hide-2 mt10" style={{ maxHeight: "44px",lineHeight:"22px" }}>{item.description}</p>
<p className="break_word task-hide-2 mt10" style={{ maxHeight: "44px", lineHeight: "22px" }}>{item.description}</p>
<div className="p-r-about">
<span className="p-r-detail">
{/* <span><label>浏览量:</label>{item.visits}</span> */}
{/* {item.category && item.category.id && <span>{item.category.name}</span>} */}
{item.last_update_time ? <span><label>更新于</label>{item.time_ago}</span> : ""}
{item.language && item.language.id ? <span className="color-grey-3">{item.language.name}</span> : ""}
{item.last_update_time ? <span><label>更新于</label>{item.time_ago}</span> : null}
{item.language && item.language.id ? <span className="color-grey-3">{item.language.name}</span> : null}
</span>
</div>
</div>
</div>
)
}) : <Nodata _html="暂无数据~"></Nodata>}
);
}) : <Nodata _html="暂无数据~" />}
<WrappedPrivateAccessModal
visible={this.state.showModal}
onClose={this.closeModal}
projectInfo={this.state.projectInfo}
/>
</div>
)
);
}
}
export default IndexItem;

View File

@ -44,7 +44,8 @@ class Index extends Component {
project_category_name: undefined,
license_name: undefined,
ignore_name: undefined,
descNum:0
descNum:0,
join_project_need_verify:true
}
}
componentDidMount = () => {
@ -169,7 +170,7 @@ class Index extends Component {
isSpin: true
})
const { projectsType } = this.props.match.params;
const { project_language_id, project_category_id, license_id, ignore_id , owners_id , owners_name } = this.state;
const { project_language_id, project_category_id, license_id, ignore_id , owners_id , owners_name,join_project_need_verify } = this.state;
const decoderPass = Base64.encode(values.password);
const url = (projectsType && projectsType === "mirror") ? "/projects/migrate.json" : "/projects.json";
axios.post(url, {
@ -179,7 +180,8 @@ class Index extends Component {
project_category_id,
license_id,
ignore_id,
user_id:owners_id
user_id:owners_id,
join_project_need_verify
}).then((result) => {
if (result) {
if (result.data.id) {
@ -288,6 +290,7 @@ class Index extends Component {
ignore_list,
mirrorCheck,
join_project_need_verify,
descNum
} = this.state;
@ -511,6 +514,14 @@ class Index extends Component {
)}
</Form.Item >
}
<Form.Item
label="加入项目设置"
colon={false}
>
{getFieldDecorator('join_project_need_verify')(
<Checkbox checked={join_project_need_verify} onChange={(e)=>this.setState({join_project_need_verify:e.target.checked})}>用户加入项目时需要审核<span className="font-13 color-grey-9">使用邀请码加入也需要审核由项目所有人或管理员审核</span></Checkbox>
)}
</Form.Item >
<div>
<span className="ant-form-item-required"></span>
</div>

224
src/forge/Settings/Audit.js Normal file
View File

@ -0,0 +1,224 @@
import React, { useEffect, useState } from 'react';
import { WhiteBack, FlexAJ } from '../../Component/layout';
import { Table, Pagination, Popconfirm, Button, Modal, message, Input } from 'antd';
import Title from '../../Component/Title';
import Search from '../../Component/Search';
import styled from 'styled-components';
import { getImageUrl } from 'educoder';
import axios from 'axios';
import { Link } from 'react-router-dom';
const Img = styled.img`{
width:30px;
height:30px;
border-radius:50%;
}`
const demoData = [
{
img: "images/avatars/User/g",
name: "蔡世",
email: "1149225589@qq.com",
team: "caicaizi",
role: "管理者",
operation: "移除成员",
}
]
export default ((props) => {
const [page, setPage] = useState(1);
const [limit, setLimit] = useState(15);
const [total, setTotal] = useState(0);
const [search, setSearch] = useState(undefined);
const [data, setData] = useState(demoData);
const [status, setStatus] = useState(1);
useEffect(() => {
getData();
}, [])
function getData(status, search, page) {
const url = `/api/applied_projects.json`;
axios.get(url, {
params: {
page, limit,
keyword: search,
status
}
}).then(result => {
if (result && result.data) {
setData(result.data.list);
setTotal(result.data.count);
}
}).catch(error => { })
}
// 切换分页
function ChangePage(page) {
setPage(page);
getData(status, search, page);
}
function verify(id, verifyStatus, reason) {
const url = `/api/applied_projects/${id}/verify.json?status=${status}`;
axios.post(url, {
body: {
verify_status: verifyStatus,
status,
reason
}
}).then(result => {
if (result && result.data) {
message.success("操作成功!");
}
}).catch(error => { })
}
const columns = [
{
title: '用户名',
dataIndex: 'user_name',
width: "15%",
render: (item, record) => <span>{item}</span>
},
{
title: '申请角色',
dataIndex: 'applied_project',
width: "10%",
render: (value, item) => {
return value
}
},
{
title: '学号',
dataIndex: 'student_id',
width: "10%",
align: "left",
render: (value) => {
return value
}
},
{
title: '邮箱',
dataIndex: 'mail',
width: "10%",
render: (value) => value
},
{
title: '个人情况',
dataIndex: 'remarks',
width: "15%",
render: (value) => value
},
{
title: '上传附件',
dataIndex: 'attachment_info',
width: "10%",
render: (value, record) => {
if (record.attachment_info.id) {
return <Button type='link' onClick={() => {
// 构建下载链接
const downloadUrl = `${record.attachment_info.url}`;
// 创建一个 a 元素
const a = document.createElement('a');
// 设置 a 元素的 href 属性为下载链接
a.href = downloadUrl;
// 设置 a 元素的 download 属性,可根据实际情况设置文件名
a.download = record.attachment_info.title || 'attachment';
// 模拟点击 a 元素
a.click();
// 移除 a 元素
document.body.removeChild(a);
}}>下载</Button>
} else {
return '--'
}
}
},
{
title: '拒绝原因',
dataIndex: 'reason',
width: "15%",
render: (value) => value
},
{
title: '操作',
dataIndex: 'operation',
width: "15%",
render: (value, item) => {
return (
<div>
<Button type='link' onClick={() => {
Modal.confirm({
title: '是否确认同意TA的加入',
okText: '确认',
cancelText: '取消',
onOk: () => {
verify(item.id, 'agree', '')
},
});
}}>同意</Button>
<Button type='link' className='ml10' onClick={() => {
let rejectReason = '';
Modal.confirm({
title: '拒绝申请',
icon: null,
okText: '确认',
width: 500,
content: <div>
<div><span style={{ color: "#ff4d4f" }}>*</span><span>:</span></div>
<div><Input.TextArea placeholder='请输入拒绝原因' rows={3} maxLength={200} onChange={(event) => {
rejectReason = event.target.value;
}} /></div>
</div>,
cancelText: '取消',
onOk: () => {
verify(item.id, 'reject', rejectReason)
},
})
}}>拒绝</Button>
</div>
)
}
}
]
return (
<WhiteBack>
<Title>
<span>审核管理</span>
</Title>
<FlexAJ className="padding20-30">
<Button size='large' type={status == 1 ? 'primary' : 'default'} onClick={() => {
setStatus(1);
getData(1, search)
}}>待审核</Button>
<Button size='large' type={status == 2 ? 'primary' : 'default'} className='ml10' onClick={() => {
setStatus(2);
getData(2, search)
}}>已审核</Button>
<div style={{ width: "580px" }}>
<Search placeholder="搜索用户信息" onSearch={(value) => {
setSearch(value)
getData(status, value)
}} />
</div>
</FlexAJ>
<div className="pl30 pr30 pb30" style={{ minHeight: "400px" }}>
<Table
size="small"
columns={columns}
dataSource={data}
pagination={false}
className="teamMemberTable"
></Table>
{
total > limit ?
<div className="edu-txt-center mt30 mb20">
<Pagination simple current={page} total={total} pageSize={limit} onChange={ChangePage}></Pagination>
</div>
: ""
}
</div>
</WhiteBack>
)
})

View File

@ -16,6 +16,10 @@ const Branch = Loadable({
loader: () => import("./Branch"),
loading: Loading,
});
const Audit = Loadable({
loader: () => import("./Audit"),
loading: Loading,
});
const Setting = Loadable({
loader: () => import("./Setting"),
loading: Loading,
@ -65,6 +69,18 @@ class Index extends Component {
</Link>
</p>
</li>
<li
className={
pathname.indexOf("settings/audit") > -1 ? "active" : ""
}
>
<p>
<Link to={`/${owner}/${projectsId}/settings/audit`} className="w-100">
<i className="iconfont icon-fenzhi font-20 mr10"></i>
审核管理
</Link>
</p>
</li>
<li
className={
pathname.indexOf("setting/branch") > -1 ? "active" : ""
@ -125,6 +141,12 @@ class Index extends Component {
<BranchNew {...this.props} {...props} {...this.state} />
)}
></Route>
<Route
path="/:owner/:projectsId/settings/audit"
render={(props) => (
<Audit {...this.props} {...props} {...this.state} />
)}
></Route>
<Route
path="/projects/:owner/:projectsId/setting/branch"
render={(props) => (

View File

@ -28,7 +28,8 @@ class Setting extends Component {
project_units:['home',"activity","code"],
divertVisible:false,
is_transfering:undefined,
projectName:undefined
projectName:undefined,
join_project_need_verify:true
};
}
@ -77,6 +78,7 @@ class Setting extends Component {
this.setState({
projectName:result.data.project_name,
private_check: result.data.private,
join_project_need_verify:result.data.join_project_need_verify,
loading:false,
project_units:units
});
@ -142,12 +144,13 @@ class Setting extends Component {
update=(values)=>{
const { projectsId , owner } = this.props.match.params;
const { private_check } = this.state;
const { private_check ,join_project_need_verify} = this.state;
const url = `/${owner}/${projectsId}.json`;
axios.put(url, {
name: values.project_name,
description: values.project_description,
private: private_check,
join_project_need_verify:join_project_need_verify,
...values,
}).then((result) => {
if (result) {
@ -196,7 +199,7 @@ class Setting extends Component {
const { getFieldDecorator } = this.props.form;
const { projectDetail } = this.props;
const { CategoryList, LanguageList, private_check ,loading } = this.state;
const { CategoryList, LanguageList, private_check ,loading,join_project_need_verify } = this.state;
let mirror = projectDetail && projectDetail.mirror;
let type = projectDetail && projectDetail.type;
const forked_from_project_id = this.props && this.props.projectDetail && this.props.projectDetail.forked_from_project_id;
@ -284,6 +287,24 @@ class Setting extends Component {
</Checkbox.Group>
)}
</Form.Item>
<div className="df" style={{ alignItems: "center" }}>
<span className="mr20 mb15 font-16">加入项目设置</span>
<Form.Item label="">
{getFieldDecorator("join_project_need_verify", {
rules: [],
})(
<Checkbox
checked={join_project_need_verify}
onChange={(e)=>this.setState({join_project_need_verify:e.target.checked})}
>
<span className="color-grey-9">用户加入项目时需要审核</span>
<span className="color-grey-6">
使用邀请码加入也需要审核由项目所有人或管理员审核
</span>
</Checkbox>
)}
</Form.Item>
</div>
<p className="clearfix">
<a className="submitBtn" onClick={this.resetSetting}>
更新仓库设置