Merge branch 'gitlink_server' of http://106.75.45.236:3000/Gitlink/forgeplus-react into feature_devops

This commit is contained in:
谢思 2022-08-16 13:40:37 +08:00
commit ac224dc3d1
13 changed files with 835 additions and 16 deletions

View File

@ -6,7 +6,7 @@ function Main(props){
const { owner , projectsId } = props.match.params;
const [ has_trace_user , setHas_trace_user ] = useState(false);
const { current_user , resetUserInfo } = props;
const { current_user , resetUserInfo, projectDetail, showNotification } = props;
useEffect(()=>{
if(current_user){
@ -21,7 +21,9 @@ function Main(props){
}
function openDetail(){
if(!has_trace_user){
if(projectDetail && projectDetail.forked_from_project_id){
showNotification("fork仓库暂不支持代码溯源服务敬请谅解。");
}else if(!has_trace_user){
setVisible(true);
}else{
props.history.push(`/${owner}/${projectsId}/server/1`);

View File

@ -9,7 +9,7 @@ function Data(props) {
const [detectionVisible, setDetectionVisible] = useState(false);
const [dataSource, setDataSource] = useState(undefined);
const [page, setPage] = useState(1);
const [spining, setSpining] = useState(true);
const [spining, setSpining] = useState(false);
const [relayCount, setRelayCount] = useState(5);
const [repeatId, setRepeatId] = useState(undefined);
const [repeatBranch, setRepeatBranch] = useState(undefined);
@ -18,7 +18,7 @@ function Data(props) {
const [viewBase, setViewBase] = useState('https://cjntest.trustie.net/');
const { owner, projectsId } = props.match.params;
const { current_user, isManager } = props;
const { isManager, projectDetail, history } = props;
const limit = 15;
@ -37,7 +37,6 @@ function Data(props) {
}
}).then(result => {
if (result) {
if (Array.isArray(result.data.data)) {
setDataSource(result.data.data);
setRelayCount(result.data.left_tasks_count);
@ -47,21 +46,26 @@ function Data(props) {
if (item.detect_status === "detecting" || item.detect_status === "waiting" || (item.detect_status=="detected"&& item.is_reported=="0")) {
setTimeout(Init, 2000);
break;
}
}
}
} else {
result && result.data.message && props.showNotification(result.data.message);
}
setSpining(false);
}else{
setSpining(false);
}
}).catch(error => { })
}
useEffect(() => {
setSpining(true);
Init();
}, [])
if(projectDetail && projectDetail.forked_from_project_id){
history.go(-1);
}else{
setSpining(true);
Init();
}
}, [projectDetail])
useEffect(() => {
window.addEventListener("message", iframeHeight, false);

View File

@ -141,4 +141,56 @@ export function getPassList(params) {
method: 'get',
params,
});
}
// 中期考核-查看中期考核信息(返回导师评分)
export function getMediumTermExamineInfo(taskId) {
return fetch({
url: `/api/mediumTermExamineMaterial/getMediumTermExamineInfo/${taskId}`,
method: 'get'
});
}
// 中期考核-查看中期考核信息(不返回返回导师评分)
export function getBriefMediumTermExamineMaterial(taskId) {
return fetch({
url: `/api/mediumTermExamineMaterial/getBriefMediumTermExamineMaterial/${taskId}`,
method: 'get'
});
}
// 中期考核-提交中期考核信息
export function submitMedium(data) {
return fetch({
url: `/api/mediumTermExamineMaterial/create`,
method: 'post',
data
});
}
// 中期考核-导师提交中期考核评分
export function submitTutorEvaluation(data) {
return fetch({
url: `/api/tutorEvaluation/create`,
method: 'post',
data
});
}
// 中期考核-导师更新中期考核评分
export function updateTutorEvaluation(data) {
return fetch({
url: `/api/tutorEvaluation/update`,
method: 'post',
data
});
}
// 中期考核结果list
export function getMediumTermExamineInfoList(params) {
return fetch({
url: `/api/mediumTermExamineMaterial/getMediumTermExamineInfoList`,
method: 'get',
params,
});
}

View File

@ -11,13 +11,15 @@ import banner from "../img/banner.png";
import introduce from "../img/introduce.png";
import apply1 from "../img/apply1.png";
import apply2 from "../img/apply2.png";
import img1 from "../img/img1.png";
import img2 from "../img/img2.png";
import teacher from "../img/teacher.png";
import logo from "../img/openmmlab/logo1.png";
import { hasAuditRole } from '../api';
import './index.scss';
export default (props) => {
const { current_user, isGlccApplyDate, showNotification, studentApplyStart, history, secondStudentApplyDate, isStudentApplyDate } = props;
const { current_user, isGlccApplyDate, showNotification, studentApplyStart, history, secondStudentApplyDate, isStudentApplyDate, checkedTaskId, checkTime1, checkTime2, checkTime3 } = props;
// function goToApply() {
// if (isGlccApplyDate) {
// if (current_user && current_user.login) {
@ -31,9 +33,9 @@ export default (props) => {
// }
// }
const [hasRole, setHhasRole] = useState(false);
const [hasRole, setHasRole] = useState(false);
const resultTime1 = new Date().getTime() > new Date('2022-06-28 1:0').getTime() && new Date().getTime() < new Date('2022-07-01 0:0').getTime();
const resultTime2 = new Date().getTime() > new Date('2022-07-01 12:0').getTime();
const resultTime2 = new Date().getTime() > new Date('2022-07-01 12:0').getTime() && new Date().getTime() < new Date('2022-08-12 0:0').getTime();
useEffect(() => {
if (!current_user.user_id) {
@ -41,7 +43,7 @@ export default (props) => {
}
hasAuditRole({ userId: current_user.user_id }).then(res => {
if (res && res.message == 'success' && res.data.hasRole) {
setHhasRole(true);
setHasRole(true);
}
})
}, [])
@ -85,6 +87,32 @@ export default (props) => {
</div>
<div className="pt6">查看各课题入选学生名单</div>
</div>}
{/* 学生中期考核 */}
{!hasRole && checkTime1 && checkedTaskId && <Link className="apply" to={`/glcc/middle/submit`}>
<div>
<img src={img1} alt="" className="applyIcon" />
<span className="til">中期考核</span>
</div>
<div className="pt6">学生提交考核材料</div>
</Link>}
{/* 导师中期考核 */}
{hasRole && checkTime2 && <Link className="apply" to={`/glcc/middle/examination`}>
<div>
<img src={img1} alt="" className="applyIcon" />
<span className="til">中期考核</span>
</div>
<div className="pt6">导师拟定中期考核结果</div>
</Link>}
{/* 中期考核结果公示页 */}
{checkTime3 && <Link className="apply" to={`/glcc/middle/result`}>
<div>
<img src={img1} alt="" className="applyIcon" />
<span className="til">考核结果</span>
</div>
<div className="pt6">中期课题考核结果公示</div>
</Link>}
{/* 项目报名 */}
<Link to="/glcc/projects" className="apply project" >
<div>
@ -102,7 +130,7 @@ export default (props) => {
</div>
<div className="pt6">选择课题开启您的开源之旅</div>
</div> */}
{/* 导师审核 */}
{/* {hasRole && new Date().getTime() < new Date('2022-07-01 0:0').getTime() && <div className="apply" onClick={goToCheck}>
<div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

BIN
src/glcc/img/img1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
src/glcc/img/img2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

View File

@ -61,6 +61,21 @@ const Result = Loadable({
loader: () => import("./checkResult"),
loading: Loading,
});
// -
const StudentSubmit = Loadable({
loader: () => import("./interimReview/studentSubmit"),
loading: Loading,
});
// -
const TutorReview = Loadable({
loader: () => import("./interimReview/tutorReview"),
loading: Loading,
});
// -
const MiddleResult = Loadable({
loader: () => import("./interimReview/result"),
loading: Loading,
});
const Glcc = (propsF) => {
const {current_user, showLoginDialog} = propsF;
// 415~520
@ -70,6 +85,12 @@ const Glcc = (propsF) => {
const studentApplyStart = new Date().getTime() > new Date('2022-05-26').getTime();
const isStudentApplyDate = new Date().getTime() > new Date('2022-05-26').getTime() && new Date().getTime() < new Date('2022-06-25 0:0').getTime();
const secondStudentApplyDate = new Date().getTime() > new Date('2022-06-29 1:0').getTime() && new Date().getTime() < new Date('2022-06-30 0:0').getTime();
//
const checkTime1 = new Date().getTime() > new Date('2022-08-12 8:0').getTime() && new Date().getTime() < new Date('2022-08-25 24:0').getTime();
//
const checkTime2 = new Date().getTime() > new Date('2022-08-12 10:0').getTime() && new Date().getTime() < new Date('2022-08-25 24:0').getTime();
// 8260
const checkTime3 = new Date().getTime() > new Date('2022-08-26 0:0').getTime();
// id
const [applyTaskId, setApplyTaskId] = useState({});
@ -78,6 +99,9 @@ const Glcc = (propsF) => {
const [cancelCount,setCancelCount]=useState(0);
//
const [lockedTaskName, setLockedTaskName] = useState(undefined);
// id
const [checkedTaskId, setCheckedTaskId] = useState(undefined);
const [studentRegId, setStudentRegId] = useState(undefined);
useEffect(()=>{
// current_user user_id
@ -88,6 +112,8 @@ const Glcc = (propsF) => {
response.data && response.data.registrationStudentTaskList.map(item=>{
data[item.taskId] = item.id;
item.locked && setLockedTaskName(item.taskName);
item.passStatus && setCheckedTaskId(item.taskId);
item.passStatus && setStudentRegId(item.studentRegId);
})
setApplyTaskId(data);
response.data&&setCancelCount(Number(response.data.cancelCount));
@ -183,11 +209,35 @@ const Glcc = (propsF) => {
)}
></Route>
{/* 中期审核-结果公示 */}
<Route
path="/glcc/middle/result"
render={(props) => (
<MiddleResult current_user={current_user} history={props.history} checkTime3={checkTime3}/>
)}
></Route>
{/* 中期审核-学生 */}
<Route
path="/glcc/middle/submit"
render={(props) => (
<StudentSubmit current_user={current_user} history={props.history} checkedTaskId={checkedTaskId} checkTime1={checkTime1} studentRegId={studentRegId}/>
)}
></Route>
{/* 中期审核-导师 */}
<Route
path="/glcc/middle/examination"
render={(props) => (
<TutorReview current_user={current_user} history={props.history} checkTime2={checkTime2}/>
)}
></Route>
{/* 首页 */}
<Route
path="/glcc"
render={(props) => (
<Home {...propsF} {...props} studentApplyStart={studentApplyStart} isStudentApplyDate={isStudentApplyDate} secondStudentApplyDate={secondStudentApplyDate}/>
<Home {...propsF} {...props} studentApplyStart={studentApplyStart} isStudentApplyDate={isStudentApplyDate} secondStudentApplyDate={secondStudentApplyDate} checkedTaskId={checkedTaskId} checkTime1={checkTime1} checkTime2={checkTime2} checkTime3={checkTime3}/>
)}
></Route>

View File

@ -0,0 +1,232 @@
.interimBox{
background-image:linear-gradient(180deg,#ebf2ff 0%,#ebf2ff 43.09%,#f3f4f8 100%);
padding-bottom: 125px;
position: relative;
.bg1, .bg2{
width: 150px;
position: absolute;
top: 45%;
left: 6%;
}
.bg2{
top: auto;
left: auto;
right: 20%;
bottom: 0;
}
&.glcc-check .bg1{top: 36%;}
.bannerInterim{
width: 100%;
}
.navBox{
padding: 20px 0 10px;
color:#202d40;
border-bottom: 1px dashed #bec5d5;
.linkBox{
color:#a4aabb;
&:hover{color: $primary-color;}
}
}
.mainBox{
width: 1200px;
margin: 0 auto;
z-index: 1;
position: relative;
}
.tipBox{
padding: 12px 20px;
color:#6c7283;
line-height: 2.5;
background-color:#e4edff;
.spanBox{color: #000;}
div{line-height: 1.8;margin-bottom: 10px;}
.blueSpan{
color: $primary-color;
&:hover{color: $primary-color-hover;}
}
}
.titleBox{color: #333;}
.referBox{
margin-top: 25px;
background-color:rgba(255, 255, 255, 0.27);
border:1px solid #ffffff;
display: flex;
flex-wrap: wrap;
justify-content: space-around;
padding: 30px 0;
position: relative;
.referItem{
width: 47%;
display: flex;
.ant-form-item-control-wrapper{flex-grow: 1;}
&.oneCont{
width: 97%;
.ant-upload-list-item{width: 30%;}
.ant-upload-list-item:hover .ant-upload-list-item-info{background: none;}
}
.ant-input, .uploadBox{
border-color:#b3c3db;
background: none !important;
}
.contentBox{
padding: 10px 11px 20px
}
}
&.tutor{
border: none;
margin-top: -20px;
background: none;
.remarkBox{
position: relative;
flex: 0.97;
.has-error .ant-form-explain{
position: absolute;
}
}
.oneCont{display: block;}
.wordNum{
position: absolute;
right: 55px;
bottom: 10px;
}
.referItem{margin-bottom: 5px;}
}
}
.tutorContent{
border: 1px solid #fff;
margin-top: 40px;
background-color: #f1f6ff;
.task-tabs {
margin-top: 0px;
border: none;
background: none;
.task-title-stuName{
font-weight:700;
color:#333;
padding-bottom: 25px;
border-bottom:1px dashed #bec5d5;
}
}
}
.reviewBox{
.successReviewBox{
color:#2dab4d;
background-color:rgba(82, 206, 152, 0.11);
border:1px solid rgba(36, 167, 67, 0.61);
margin: -5px 20px 20px;
padding: 6px 15px;
}
.title{
color:#333333;
display: flex;
align-items: center;
.blueBox{
display: inline-block;
width:5px;
height:14px;
background-color:#466aff;
margin-right: 8px;
}
}
.flexBox{
color:#202d40;
border-bottom: 1px dashed #bec5d5;
div{
display: flex;
align-items: baseline;
margin-bottom: 10px;
.blueSpan{
color: $primary-color;
word-break: break-all;
display: block;
flex: 1;
}
.pptAttachment{
color: $primary-color;
max-width: 80%;
}
}
}
}
.resultBox{
padding: 30px 20px 50px 20px;
.nullDateTip{color: #ff3838;}
&.nullData .flexBox div{align-items: center;}
.blueBg{
width: 85%;
min-height: 36px;
display: inline-block;
padding: 4px 10px;
background-color:rgba(70, 106, 255, 0.05);
border-radius:4px;
word-break: break-all;
}
.mustSpan{
position: relative;
padding-left: 10px;
&::before{
content: '*';
color: #ff3838;
font-size: 18px;
position: absolute;
left: 8px;
top: 3px;
}
&.ppt::before{
top: -2px;
left: 12px;
}
}
.flexBox{
border: none;
width: 100%;
}
.tutorRes{
width: 100%;
background-color:rgba(107, 136, 255, 0.05);
border-radius:4px;
color:#6c7283;
padding: 16px 30px 30px 20px;
.smallTil{
color: #202d40;
}
.passStatusBox{
display: inline-flex;
justify-content: center;
align-items: center;
width:59px;
height:28px;
color:#d2001d;
background-color:rgba(251, 0, 34, 0.06);
border:1px solid #df001f;
border-radius:6px;
&.pass{
color:#2dab4d;
background-color:rgba(82, 206, 152, 0.11);
border-color:rgba(36, 167, 67, 0.61);
}
}
.blueSpan{color: $primary-color;}
}
.tutorRemark{
word-break: break-all;
flex: 1;
color:#6c7283;
}
}
}
.interimBox.resultListBox{
.searchBox{
display: flex;
align-items: center;
}
.resultListTable th.columnsResult.actionBox .ant-table-column-title{
visibility: visible;
}
.projectDetailBox.nodata{
margin: 0 auto;
}
.resultListTable .ant-table{
border: none;
}
}

View File

@ -0,0 +1,134 @@
import React, { useEffect, useState } from 'react';
import { Input, Table, Tooltip, Checkbox } from 'antd';
import { getMediumTermExamineInfoList } from '../api';
import ProjectDetail from '../project/component/projectDetail';
import resultBanner from "../img/resultBanner3.png";
import bgPng from "../img/bgPng.png";
import './index.scss';
import '../checkResult/index.scss';
import '../project/taskList/index.scss';
import '../project/index.scss';
const { Search } = Input;
//
function CheckResult({current_user, history, checkTime3}) {
//
const [keyword, setKeyword] = useState(undefined);
const [data, setData] = useState([]);
// table
const [current, setCurrent] = useState(1);
const [total, setTotal] = useState(0);
const [pageSize, setPageSize] = useState(20);
const [loading, setLoading] = useState(false);
const [expandedRowKeys, setExpandedRowKeys] = useState([]);
useEffect(()=>{
//
if(!checkTime3){
history.push('/glcc');
}
},[])
useEffect(() => {
setLoading(true);
setExpandedRowKeys([]);
const params = {
curPage: current,
keyword,
pageSize
}
getMediumTermExamineInfoList(params).then(response => {
if (response && response.message === "success") {
response.data.rows.map((item, index)=>{
item.id = (current-1)*pageSize+index + 1;
})
setData(response.data.rows);
setTotal(response.data.total);
}
setLoading(false);
})
}, [keyword, current, pageSize])
const columns = [
{ title: '序号', dataIndex: 'index', align: 'center', className:"columnsResult", width: '6%', render: (text, item, index) => <span>{(current-1)*pageSize+index + 1}</span> },
{ title: '入选学生', dataIndex: 'studentName', className:"columnsResult taskName", width: '10%', ellipsis: true},
{ title: '课题导师', dataIndex: 'tutorName', className:"columnsResult", width: '10%', ellipsis: true},
{ title: '课题名称', dataIndex: 'taskName', className:"columnsResult", width: '20%', ellipsis: true, render: (text, item) => <Tooltip title={text} placement="topLeft"><span className='toolTipSpan link' onClick={()=>{window.open(`/glcc/subjects/detail/${item.taskId}`)}}>{text}</span></Tooltip> },
{ title: '项目名称', dataIndex: 'projectName', className:"columnsResult", ellipsis: true, width: '14%', render: (text) => <Tooltip title={text} placement="topLeft"><span className='toolTipSpan'>{text}</span></Tooltip> },
{ title: '项目简介', dataIndex: 'introduce', className:"columnsResult", width: '13%', ellipsis: true},
{ title: '答辩视频', dataIndex: 'defenceVideoUrl', className:"columnsResult", width: '15%', ellipsis: true, render: (text, item) => <Tooltip title={text} placement="topLeft"><span className='toolTipSpan link' onClick={()=>{window.open(`/glcc/subjects/detail/${item.taskId}`)}}>{text}</span></Tooltip>},
{ title: '考核结果', dataIndex: 'totalityEvaluation', align: 'center', className:"columnsResult actionBox", render: (text, item, index) => <span>{text === 'D' || !text ? '未通过' : '通过'}</span>},
]
const customExpandIcon = (props) => {
if (props.expanded) {
return <a className='toolTipSpan link' style={{marginRight: 8 }} onClick={e => {
props.onExpand(props.record, e);
}}><i className='iconfont icon-ketixiangqingicon mr5'></i>项目简介<i className="iconfont icon-changyongtubiao-xianxingdaochu-zhuanqu- font-12 ml5 down mr10"></i></a>
} else {
return <a className='toolTipSpan link' style={{marginRight: 8 }} onClick={e => {
props.onExpand(props.record, e);
}}><i className='iconfont icon-ketixiangqingicon mr5'></i>项目简介<i className="iconfont icon-jiantou9 font-12 ml5 down mr10"></i></a>
}
}
const expandRow = (record) => {
return <ProjectDetail detail={null} projectId={record.projectIntro} showTask={false}/>
}
//
function onExpand(expanded, record){
const keys = new Set(expandedRowKeys);
if(expanded){
keys.add(record.id);
}else{
keys.delete(record.id);
}
setExpandedRowKeys(Array.from(keys));
}
// pagesize
function onShowSizeChange(current, pageSize){
window.scrollTo(0, 0);
setCurrent(1);
setPageSize(pageSize);
}
//
function changePage(page, pageSize){
window.scrollTo(0, 0);
setCurrent(page);
}
return (
<div className="interimBox taskList resultListBox">
<img className="bannerInterim" src={resultBanner} alt=""></img>
<div className='bgBox'>
<div className="resultList">
<div className='goBackBox'><a href='/glcc'>开源夏令营 / </a>中期课题考核结果公示</div>
<div className='searchBox'>
<Search className='search' placeholder='请输入学生姓名或课题名称进行搜索' allowClear enterButton onSearch={(value) => { setCurrent(1); setKeyword(value) }} />
<div style={{width: 100}}></div>
</div>
<Table
loading={loading}
columns={columns}
dataSource={data}
expandedRowRender={expandRow}
expandIconColumnIndex={5}
expandIconAsCell={false}
expandIcon={customExpandIcon}
rowKey={'id'}
expandedRowKeys={expandedRowKeys}
onExpand={onExpand}
pagination={{current: current, pageSize: pageSize, total: total, showSizeChanger: true, onShowSizeChange:onShowSizeChange, showQuickJumper: true, onChange: changePage}}
className='resultListTable pb30'
/>
</div>
<img src={bgPng} alt='' className='bgPng3'/>
<img src={bgPng} alt='' className='bgPng4'/>
</div>
</div>
)
}
export default CheckResult;

View File

@ -0,0 +1,134 @@
import React, { useEffect, useState } from "react";
import { Form, Upload, Input, Icon, Button } from "antd";
import { Link } from "react-router-dom";
import banner from '../img/banner-interim.png';
import img1 from '../img/img1.png';
import bg from '../img/bgPng.png';
import { httpUrl, main_site_url } from '../fetch';
import {getMediumTermExamineInfo, submitMedium, getBriefMediumTermExamineMaterial} from '../api';
import './index.scss';
function StudentSubmit(props){
const {form, checkedTaskId, checkTime1, studentRegId, history} = props;
const {getFieldDecorator, validateFieldsAndScroll } = form;
const [detail, setDetail] = useState(undefined);
const [reload, setReload] = useState(undefined);
const [fileList, setFileList] = useState(undefined);
//
const submitTime = new Date().getTime() < new Date('2022-08-21 24:0').getTime();
//
const lookTime = new Date().getTime() > new Date('2022-08-24 24:0').getTime() && new Date().getTime() < new Date('2022-08-25 24:0').getTime();
useEffect(()=>{
if(!checkTime1){
history.push("/glcc");
}
}, [])
useEffect(()=>{
//
if(lookTime){
// 8.25
checkedTaskId && getMediumTermExamineInfo(checkedTaskId).then(res=>{
if(res && res.message === "success"){
setDetail(res.data);
}
})
}else{
// 25
checkedTaskId && getBriefMediumTermExamineMaterial(checkedTaskId).then(res=>{
if(res && res.message === "success"){
setDetail(res.data);
}
})
}
},[checkedTaskId, reload])
//
function submit(e){
e.preventDefault();
validateFieldsAndScroll((err, values) => {
if(!err){
const {pptAttachment, defenceVideoUrl, codeOrPrUrl} = values;
const pptAttachmentId = pptAttachment.file.response.data.id;
const params = {
pptAttachmentId,
codeOrPrUrl,
defenceVideoUrl,
taskId: checkedTaskId,
studentRegId: studentRegId
}
submitMedium(params).then(res=>{
if(res && res.message === "success"){
setReload(Math.random());
}
})
}
})
}
//
function changeFileList(e) {
const {fileList} = e;
const lastFile = fileList.splice(fileList.length-1, fileList.length);
setFileList(lastFile);
}
return <div className="interimBox">
<img src={banner} alt="" className="bannerInterim"/>
<img src={bg} alt="" className="bg1"/>
<img src={bg} alt="" className="bg2"/>
<div className="mainBox">
<div className="navBox font-16"><Link to={`/glcc`} className="linkBox">开源夏令营 / </Link>提交中期考核材料</div>
<div className="tipBox mt30">
<div className="font-15 spanBox">材料提交说明:</div>
<div>1请各位学生<a href={`${main_site_url.indexOf("gitlink") !== -1 ? '/api/attachments/392565' : httpUrl+'/busiAttachments/download/169'}`} className="blueSpan">下载PPT模板</a> 根据课题开发进展按照PPT模板要求填写课题学习调研方案开发进度及开发成果下半阶段工作计划等考核材料</div>
<div>2欢迎各位学生录制本课题答辩视频将视频链接填写至下方视频介绍填写栏</div>
<div>3学生提交考核材料的时间为<span className="spanBox">2022年8月12日8点至2022年8月21日24点</span>请在截止时间前提交若在截止时间未提交中期考核表则默认自动放弃该课题将自动终止</div>
<div>4学生在<span className="spanBox">2022年8月25日</span>可在此页面查看自己考核成绩若对考核成绩有异议请及时联系导师进行更改</div>
</div>
<div className="titleBox mt25 font-18">
<img src={img1} alt="" width={24} className="mr5"/>
中期考核
</div>
{submitTime && !detail ? <Form className="referBox" onSubmit={submit} colon={false}>
<Form.Item label="答辩视频" className="referItem">
{getFieldDecorator('defenceVideoUrl', {
rules: [{ required: true, message: '请输入视频链接!'}, {pattern: /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/,message: "请正确输入链接"}],
})(<Input placeholder="请输入视频链接" maxLength={900}/>)}
</Form.Item>
<Form.Item label="代码/pr地址" className="referItem">
{getFieldDecorator('codeOrPrUrl', {
rules: [{pattern: /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/,message: "请正确输入链接"}],
})(<Input placeholder="请输入代码或pr链接" maxLength={900}/>)}
</Form.Item>
<Form.Item label="PPT附件" className="referItem oneCont">
{getFieldDecorator('pptAttachment', {
rules: [{ required: true, message: '请上传PPT附件!' }],
})(<Upload className="avatar-uploader" action={httpUrl + `/busiAttachments/upload`} onChange={changeFileList} fileList={fileList}>
<Button className="uploadBox"><Icon type="upload" /> 上传</Button></Upload>)}
</Form.Item>
<Form.Item className="referItem oneCont">
<Button style={{width: '100px', height: '36px'}} className="mt30 ml20" type="primary" htmlType="submit">提交</Button>
</Form.Item>
</Form> : <div className={`reviewBox referBox resultBox ${!detail ? 'nullData' : ''}`}>
<div className="flexBox">
<div className="mustSpan mb20">&nbsp;&nbsp;答辩视频<span className="blueBg ml10"><a href={detail && detail.defenceVideoUrl} target="_blank">{detail && detail.defenceVideoUrl}</a></span></div>
<div>代码/pr地址<span className="blueBg ml10"><a href={detail && detail.codeOrPrUrl} target="_blank">{detail && detail.codeOrPrUrl}</a></span></div>
<div className="mustSpan ppt mb20">&nbsp;&nbsp;&nbsp;PPT附件{detail && <i className="iconfont icon-lianjie3 font-13 mr5 ml10"></i>}{detail ? <a className="pptAttachment mr10" href={`${httpUrl}/busiAttachments/download/${detail.pptAttachment.id}`}>{`${detail.pptAttachment.fileName}`}</a> : <Button className="uploadBox ml10" disabled><Icon type="upload" /> 上传</Button>}{detail && detail.pptAttachment && detail.pptAttachment.fileSizeString}</div>
</div>
{!detail && <div className="nullDateTip font-15">很遗憾您在指定时间内未提交考核材料</div>}
{lookTime && detail && !detail.glccTutorEvaluation && <div className="font-15 nullDateTip">您的课题导师暂未评分请提醒导师尽快提交中期考核评分</div>}
{detail && detail.glccTutorEvaluation && <div className="tutorRes">
<div>您的课题导师对您的中期考核评价如下如有异议请及时联系导师进行更改</div>
<div className="mt10 mb10 smallTil">
工作态度: <span className="blueSpan ml5">{detail.glccTutorEvaluation.workAttitudeEvaluation}</span><br/>
开发进度: <span className="blueSpan ml5">{detail.glccTutorEvaluation.developProgressEvaluation}</span><br/>
项目完成质量: <span className="blueSpan ml5">{detail.glccTutorEvaluation.projectQualityEvaluation}</span><br/>
总体评分: <span className="blueSpan ml5">{detail.glccTutorEvaluation.totalityEvaluation}</span><span className={`passStatusBox ml20 font-15 ${detail.glccTutorEvaluation.totalityEvaluation === 'D' ? '' : 'pass'}`}>{detail.glccTutorEvaluation.totalityEvaluation === 'D' ? '未通过' : '通过'}</span>
</div>
<div className="flexBox"><div><span className="smallTil">导师评语: </span><span className="ml10 tutorRemark">{detail.glccTutorEvaluation.comment}</span></div></div>
</div>}
</div>}
</div>
</div>
}
export default Form.create()(StudentSubmit);

View File

@ -0,0 +1,183 @@
import React, {useState, useEffect, useCallback} from "react";
import { Form, Input, Icon, Button, Tabs, Radio, message } from "antd";
import { Link } from "react-router-dom";
import bg from '../img/bgPng.png';
import { getAuditList, getMediumTermExamineInfo, submitTutorEvaluation, updateTutorEvaluation, hasAuditRole } from '../api';
import './index.scss';
import '../check/index.scss'
import Nodata from "../../forge/Nodata";
import {httpUrl} from '../fetch';
const { TabPane } = Tabs;
const { TextArea } = Input;
function TutorReview(props){
const {form, current_user, showNotification, checkTime2, history} = props;
const {getFieldDecorator, setFieldsValue, validateFieldsAndScroll, resetFields } = form;
const [taskId, setTaskId] = useState();
const [taskList, setTaskList] = useState([]);
const [wordNum, setWordNum] = useState(0);
const [detail, setDetail] = useState(undefined);
const [reload, setReload] = useState(undefined);
//
const [isEdit, setIsEdit] = useState(false);
useEffect(() => {
if(!checkTime2){
history.push("/glcc");
}else if (!current_user.login) {
history.push('/login?go_page=/glcc/middle/examination');
}else{
hasAuditRole({ userId: current_user.user_id }).then(res => {
if (!(res && res.message == 'success' && res.data.hasRole)) {
history.push('/glcc');
}
})
}
getAuditList({ userId: current_user.user_id, pass: 1 }).then(res => {
if (res.message === 'success') {
const filterStuNull = res.data.rows.filter(item=>{return item.studentName !== null})
setTaskList(filterStuNull);
filterStuNull.length && setTaskId(filterStuNull[0].id);
} else {
res && showNotification(res.message || '查询课题列表失败');
}
})
}, [])
useEffect(()=>{
//
taskId && getMediumTermExamineInfo(taskId).then(res=>{
if(res && res.message === "success"){
setDetail(res.data);
if(res && res.data && res.data.glccTutorEvaluation){
setWordNum(res.data.glccTutorEvaluation.comment.length);
setFieldsValue({...res.data.glccTutorEvaluation});
}
}
})
},[taskId, reload])
//
function submit(e){
e.preventDefault();
validateFieldsAndScroll((err, values) => {
if(!err){
const params = {
...values,
mediumTermExamineMaterialId: detail.id,
tutorUserId: detail.studentRegId
}
if(detail.glccTutorEvaluation){
params['id'] = detail.glccTutorEvaluation.id;
updateTutorEvaluation(params).then(res=>{
if(res && res.message === "success"){
setIsEdit(false);
message.success("修改成功");
setReload(Math.random());
}
})
}else{
submitTutorEvaluation(params).then(res=>{
if(res && res.message === "success"){
message.success("您已成功评分");
setReload(Math.random());
}
})
}
}
})
}
const RadioGroup = <Radio.Group disabled={detail && detail.glccTutorEvaluation && !isEdit}>
<Radio value={'S'}>S</Radio>
<Radio value={'A'}>A</Radio>
<Radio value={'B'}>B</Radio>
<Radio value={'C'}>C</Radio>
<Radio value={'D'}>D</Radio>
</Radio.Group>;
const helper = useCallback(
(label, name, rules, widget, className) => (
<Form.Item label={label} className={`referItem ${className}`}>
{getFieldDecorator(name, { rules, validateFirst: true })(widget)}
</Form.Item>
),[taskId, reload]
);
return <div className="interimBox glcc-check">
<img src={bg} alt="" className="bg1"/>
<img src={bg} alt="" className="bg2"/>
<div className="mainBox">
<div className="navBox font-16"><Link to={`/glcc`} className="linkBox">开源夏令营 / </Link>导师拟定考核结果</div>
<div className="tipBox mt30">
<div className="font-15 spanBox">导师考核说明:</div>
<div>1请各位导师根据工作态度开发进度项目完成质量总体评分四个角度根据学生提交的考核材料与实际开发情况客观地进行打分打分标准分为S:特别优秀A:优秀B:良好C:合格D:不合格五个等级</div>
<div>2总体评分这一项将决定学生是否通过本次考核若总体评分为SABC则视为通过中期考核若该结果为D则该课题中期考核不通过课题将自动终止请各位导师谨慎做出评价</div>
<div>3导师提交打分结果后可对考核结果进行更改更改考核结果截止日期为<span className="spanBox">2022年8月25日24点</span></div>
<div>4北京时间<span className="spanBox">2022年8月26日20点</span>前GLCC官网将公布中期考核结果敬请留意</div>
</div>
<div className="tutorContent">
<Tabs className="task-tabs" onChange={(e) => { resetFields(); setTaskId(e); setIsEdit(false); setWordNum(0); }} activeKey={taskId + ''}>
{
taskList.map((item, i) => {
return <TabPane tab={`课题${i + 1}`} key={item.id} >
<div className="task-title-stuName font-16">{item.studentName} {item.taskName}</div>
</TabPane>
})
}
</Tabs>
{detail ? <div className="reviewBox">
{/* 已评分提示语句 */}
{detail.glccTutorEvaluation && !isEdit && <div className="successReviewBox font-15">您已评分成功! 在审核期间您可对评分结果进行更改</div>}
{/* 学生提交的资料 */}
<div className="stuCont pl20 pr20">
<div className="title font-16 mb15"><span className="blueBox"></span>基本信息</div>
<div className="flexBox">
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;答辩视频:<span className="blueSpan ml10"><a className="blueSpan" href={detail.defenceVideoUrl} target="_blank">{detail.defenceVideoUrl}</a></span></div>
<div>代码/pr地址:<span className="blueSpan ml10"><a className="blueSpan" href={detail.codeOrPrUrl} target="_blank">{detail.codeOrPrUrl}</a></span></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PPT附件:<i className="iconfont icon-lianjie3 font-13 mr5 ml10"></i><a className="mr10 pptAttachment" href={`${httpUrl}/busiAttachments/download/${detail.pptAttachment.id}`}>{detail.pptAttachment.fileName}</a>{detail.pptAttachment.fileSizeString}</div>
</div>
</div>
<div className="title font-16 pl20 mt25"><span className="blueBox"></span>导师评分</div>
{/* 导师打分 */}
<Form className="referBox tutor" onSubmit={submit} labelAlign="left" labelCol={{ span: 5 }} colon={false}>
{helper('工作态度',
'workAttitudeEvaluation',
[{ required: true, message: "请打分" }],
RadioGroup
)}
{helper('工作进度',
'developProgressEvaluation',
[{ required: true, message: "请打分" }],
RadioGroup
)}
{helper('项目完成质量',
'projectQualityEvaluation',
[{ required: true, message: "请打分" }],
RadioGroup
)}
{helper('总体评分',
'totalityEvaluation',
[{ required: true, message: "请打分" }],
RadioGroup
)}
<div className="remarkBox">
{helper('填写评语',
'comment',
[{ required: true, message: "请输入评语!" }],
<TextArea rows={8} placeholder="请对学生项目表现进行总体评价" className="contentBox" maxLength={500} onChange={(e)=>{setWordNum(e.target.value.length);}} disabled={detail.glccTutorEvaluation && !isEdit}/>,
'oneCont'
)}
<div className="wordNum">{wordNum} / 500</div>
</div>
<Form.Item className="referItem oneCont">
{(!detail.glccTutorEvaluation || (detail.glccTutorEvaluation && isEdit)) && <Button style={{width: '100px', height: '36px'}} className="mt20" type="primary" htmlType="submit">{detail.glccTutorEvaluation ? '保存' : '提交'}</Button>}
{detail.glccTutorEvaluation && !isEdit && <Button style={{width: '100px', height: '36px'}} className="mt20" type="primary" onClick={()=>{setIsEdit(true)}}>修改</Button>}
</Form.Item>
</Form>
</div>:<Nodata _html="该课题学生暂未提交中期考核材料,请提醒学生尽快提交"/>}
</div>
</div>
</div>
}
export default Form.create()(TutorReview);