diff --git a/src/AppConfig.js b/src/AppConfig.js index 328579998..dc23caeff 100644 --- a/src/AppConfig.js +++ b/src/AppConfig.js @@ -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 => { diff --git a/src/forge/Component/EducoderAccount.jsx b/src/forge/Component/EducoderAccount.jsx index 59e2295f2..a20ae1311 100644 --- a/src/forge/Component/EducoderAccount.jsx +++ b/src/forge/Component/EducoderAccount.jsx @@ -25,6 +25,7 @@ function EducoderAccount({form , visible , onOk , email}){ return( { - 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( - - setVisible(false)} - > -
- - {getFieldDecorator("code",{ - rules:[ - {required:true,message:"请输入6位项目邀请码"}, - {validator:checkValue} - ] - })( - - )} - - - {getFieldDecorator("role",{ - rules:[{required:true,message:"请选择角色"}] - })( - - 管理员 - 开发者 - 报告者 - - )} - -
-
- setVisible(true)}>加入项目 -
- ) + const handleOk = () => { + validateFields((error, values) => { + if (!error) { + onOk(values); + } + }); + }; + + return ( + +
+ + {getFieldDecorator('code', { + rules: [ + { required: true, message: '请输入6位项目邀请码' }, + { validator: checkValue }, + ], + })( + + )} + + + {getFieldDecorator('role', { + rules: [{ required: true, message: '请选择角色' }], + })( + + 管理员 + 开发者 + 报告者 + + )} + +
+
+ ); }) -) \ No newline at end of file +); + +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 ( + +
+ + {getFieldDecorator('remarks', { + rules: [{ required: true, message: '请输入个人情况' }], + })( + + )} + + + {getFieldDecorator('attachment_id', { + rules: [], + })( + + + (单个文件最大20MB) + + )} + +
+
+ ); + }) +); + +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.data.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, { + applied_project: { + ...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 ( + + setVisible(false)} + /> + setInfoVis(false)} + contentFileList={contentFileList} + uploadProps={uploadProps} + /> + setVisible(true)}>加入项目 + + ); +}); \ No newline at end of file diff --git a/src/forge/Head/header.scss b/src/forge/Head/header.scss index 812280107..08b1cf29b 100644 --- a/src/forge/Head/header.scss +++ b/src/forge/Head/header.scss @@ -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; + } } \ No newline at end of file diff --git a/src/forge/Index.js b/src/forge/Index.js index e1402d227..5fd17749a 100644 --- a/src/forge/Index.js +++ b/src/forge/Index.js @@ -35,6 +35,7 @@ class Index extends Component { }) } + render() { return (
diff --git a/src/forge/Main/Index.js b/src/forge/Main/Index.js index 3c3ffe9a8..3a214919e 100644 --- a/src/forge/Main/Index.js +++ b/src/forge/Main/Index.js @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import { Link } from 'react-router-dom'; -import { Menu, Input , Spin, Pagination , Popover , Select , Button } from 'antd'; +import { Menu, Input , Spin, Pagination , Popover , Select , Button,Tabs } from 'antd'; import { getImageUrl } from 'educoder'; import '../css/index.scss' import './list.css'; @@ -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 &&
- { + {/* { recommendList.map((item,key)=>{ return(
this.getoDetail(item.author && item.author.login,item.identifier)}> @@ -298,7 +309,7 @@ class Index extends Component {
) }) - } + } */}
} @@ -315,7 +326,19 @@ class Index extends Component {
-
+
+ { + this.changeFilter(key) + }}> + + + + + {/* + + */} +
+
+ )} + + + {getFieldDecorator('attachment_id', { + rules: [], + })( + + + (单个文件最大20MB) + + )} + + + + ); +} +} + +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 ( -
- { projects && projects.length > 0 ? projects.map((item, key) => { +
+ {projects && projects.length > 0 ? projects.map((item, key) => { return (
{ - item.platform === "educoder" ? - - - - : - - - + item.platform === "educoder" ? ( + + + + ) : ( + + + + ) }
- + { + if (item.is_member || item.is_manager || !item.join_project_need_verify) { + 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} - { !item.is_public && 私有 } + {!item.is_public && 私有} { - item.forked_from_project_id ? + item.forked_from_project_id ? ( - : "" + ) : null } { - item.type && item.type !== 0 ? - item.type === 2 ? - - - : - - - :"" + item.type && item.type !== 0 ? + item.type === 2 ? + + + : + + + : null } + + { + (item.is_manager && item.join_project_need_verify && !!item.pending_verify_count) && + 待审核申请:{item.pending_verify_count} + } { - item.praises_count && item.praises_count>0 ? - - 赞 {item.praises_count} - :"" + item.praises_count && item.praises_count > 0 ? + + 赞 {item.praises_count} + : null } { - item.forked_count && item.forked_count>0 ? - - fork {item.forked_count} - :"" + item.forked_count && item.forked_count > 0 ? + + fork {item.forked_count} + : null } +
-

{item.description}

+

{item.description}

{/* {item.visits} */} {/* {item.category && item.category.id && {item.category.name}} */} - {item.last_update_time ? {item.time_ago} : ""} - {item.language && item.language.id ? {item.language.name} : ""} + {item.last_update_time ? {item.time_ago} : null} + {item.language && item.language.id ? {item.language.name} : null}
- ) - }) : } + ); + }) : } +
- ) + ); } } + export default IndexItem; \ No newline at end of file diff --git a/src/forge/Main/sub/DetailBanner.jsx b/src/forge/Main/sub/DetailBanner.jsx index 64db3dae2..270103b3a 100644 --- a/src/forge/Main/sub/DetailBanner.jsx +++ b/src/forge/Main/sub/DetailBanner.jsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { Skeleton , Tooltip } from 'antd'; +import { Skeleton , Tooltip,Badge } from 'antd'; import { Link } from 'react-router-dom'; function DetailBanner({ history,list , owner , projectsId , isManager , url , pathname , state , urlFlag , projectDetail , platform ,open_devops }){ @@ -109,7 +109,7 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa
  • 0 ? "iconfont icon-cangku color-grey-3 mr5 font-14":"iconfont icon-cangku color-grey-6 font-14 mr5"}> - 设置 + {projectDetail && projectDetail.pending_verify_count ? 设置 :'设置'}
  • } diff --git a/src/forge/New/Index.js b/src/forge/New/Index.js index b56b4d128..fdc5d1a33 100644 --- a/src/forge/New/Index.js +++ b/src/forge/New/Index.js @@ -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 { )} } + + {getFieldDecorator('join_project_need_verify')( + this.setState({join_project_need_verify:e.target.checked})}>用户加入项目时需要审核(使用邀请码加入也需要审核,由项目所有人或管理员审核) + )} +
    注: 为必填项,否则为选填
    diff --git a/src/forge/Settings/Audit.js b/src/forge/Settings/Audit.js new file mode 100644 index 000000000..53eabdfd9 --- /dev/null +++ b/src/forge/Settings/Audit.js @@ -0,0 +1,248 @@ +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%; +}` + + +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([]); + const [status, setStatus] = useState(1); + + useEffect(() => { + getData(); + }, []) + + function getData(status, search, page) { + const url = `/applied_projects.json`; + axios.get(url, { + params: { + page, limit, + keyword: search, + status, + project_id: (props.projectDetail) ? props.projectDetail.project_id :'' + } + }).then(result => { + if (result && result.data) { + setData(result.data.data.list); + setTotal(result.data.data.count); + } + }).catch(error => { }) + } + + // 切换分页 + function ChangePage(page) { + setPage(page); + getData(status, search, page); + } + + function verify(id, verifyStatus, reason) { + const url = `/applied_projects/${id}/verify.json?status=${status}`; + axios.post(url, { + verify_status: verifyStatus, + status, + reason + }).then(result => { + if (result && result.data) { + message.success("操作成功!"); + getData(status, search, page); + } + }).catch(error => { }) + } + + const columns = [ + { + title: '用户名', + dataIndex: 'user_name', + ellipsis: true, + width: "12%", + align:'left', + render: (item, record) => {item || '--'} + }, + { + title: '申请角色', + dataIndex: 'applied_project', + ellipsis: true, + width: "10%", + align:'left', + render: (value, item) => { + return value || "--" + } + }, + { + title: '学号', + dataIndex: 'student_id', + ellipsis: true, + width: "10%", + align: "left", + render: (value) => { + return value || "--" + } + }, + { + title: '邮箱', + dataIndex: 'mail', + ellipsis: true, + width: "10%", + align:'left', + render: (value) => value || "--" + }, + { + title: '个人情况', + dataIndex: 'remarks', + ellipsis: true, + width: "15%", + align:'left', + render: (value) => value || "--" + }, + { + title: '上传附件', + dataIndex: 'attachment_info', + width: "10%", + align:'left', + render: (value, record) => { + if (JSON.stringify(record.attachment_info)!="{}") { + return + } else { + return '--' + } + } + }, + { + title: '拒绝原因', + dataIndex: 'reason', + ellipsis: true, + width: "15%", + align:'left', + render: (value) => value || "--" + }, + { + title: '操作', + dataIndex: 'operation1', + width: "15%", + align:'right', + flex:'right', + render: (value, item) => { + return ( +
    + + +
    + ) + } + }, + { + title: '操作', + dataIndex: 'operation2', + width: "15%", + align:'right', + flex:'right', + render: (value, record) => {record.status == 'accepted'? '已同意':'已拒绝'} + } + ].filter(item => { + if (status == 1) { + return item.dataIndex != 'operation2' && item.dataIndex!= 'reason' + }else{ + return item.dataIndex != 'operation1' + } + }) + + return ( + + + <span>审核管理</span> + + +
    + + +
    +
    + { + setSearch(value) + getData(status, value) + }} /> +
    +
    +
    +
    + { + total > limit ? +
    + +
    + : "" + } +
    +
    + ) +}) \ No newline at end of file diff --git a/src/forge/Settings/Index.js b/src/forge/Settings/Index.js index 17ae87614..ab0334f8e 100644 --- a/src/forge/Settings/Index.js +++ b/src/forge/Settings/Index.js @@ -7,6 +7,7 @@ import "./setting.scss"; import Loadable from "react-loadable"; import Loading from "../../Loading"; import { Box, Long, Short, Gap } from '../Component/layout'; +import {Badge} from 'antd' const BranchNew = Loadable({ loader: () => import("./BranchRule"), @@ -16,6 +17,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 +70,18 @@ class Index extends Component {

    +
  • -1 ? "active" : "" + } + > +

    + + + {(this.props.projectDetail && this.props.projectDetail.pending_verify_count) ? 审核管理 : '审核管理'} + +

    +
  • -1 ? "active" : "" @@ -125,6 +142,12 @@ class Index extends Component { )} > + ( + + )} + > ( diff --git a/src/forge/Settings/Setting.js b/src/forge/Settings/Setting.js index 5b4f07c29..d775a27a2 100644 --- a/src/forge/Settings/Setting.js +++ b/src/forge/Settings/Setting.js @@ -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:false }; } @@ -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; @@ -216,8 +219,8 @@ class Setting extends Component { ], })()} -
    - 可见性 +
    + 可见性 {getFieldDecorator("private", { rules: [], @@ -227,8 +230,8 @@ class Setting extends Component { onChange={this.changePrivate} disabled={forked_from_project_id} > - 将仓库设为私有 - + 将仓库设为私有 + { forked_from_project_id ?`(Fork仓库的可见性实时同步自源仓库,不支持直接修改)`:`(修改仓库的可见性,将会影响到该仓库下所有Fork仓库的可见性)`} @@ -284,6 +287,24 @@ class Setting extends Component { )} +
    + 加入项目设置 + + {getFieldDecorator("join_project_need_verify", { + rules: [], + })( + this.setState({join_project_need_verify:e.target.checked})} + > + 用户加入项目时需要审核 + + (使用邀请码加入也需要审核,由项目所有人或管理员审核) + + + )} + +

    更新仓库设置