Merge pull request '新增导师审核页面' (#393) from tongChong/forgeplus-react:feature_GLCC into gitlink_server

This commit is contained in:
xxq250 2022-06-08 14:17:29 +08:00
commit e605d5e5b5
9 changed files with 765 additions and 70 deletions

View File

@ -96,4 +96,40 @@ export function getTaskById(id) {
url: `/api/applyInformation/task/${id}`,
method: 'get'
});
}
// 学生报名列表查询
export function getStudentList(params) {
return fetch({
url: '/api/studentApply/list',
method: 'get',
params,
});
}
// 是否有审核权限
export function hasAuditRole(params) {
return fetch({
url: '/api/applyInformation/hasAuditRole',
method: 'get',
params,
});
}
// 审核课题列表
export function getAuditList(params) {
return fetch({
url: '/api/applyInformation/auditList',
method: 'get',
params,
});
}
// 导师审核报名课题
export function auditPassTask(data) {
return fetch({
url: `/api/studentApply/auditPassTask`,
method: 'post',
data
});
}

330
src/glcc/check/index.jsx Normal file
View File

@ -0,0 +1,330 @@
import React, { Fragment, useEffect, useState } from 'react';
import { Checkbox, Input, message, Modal, Table, Tooltip, Breadcrumb, Tabs, Button } from 'antd';
import { Link } from 'react-router-dom';
import moment from 'moment';
import './index.scss';
import { auditPassTask, getStudentList, getAuditList, hasAuditRole } from '../api';
import {httpUrl} from '../fetch';
const { TabPane } = Tabs;
function Check({ current_user, showNotification, history }) {
//
const [data, setData] = useState([]);
const [visible, setVisible] = useState(false);
const [picture, setPicture] = useState('');
const [reload, setReload] = useState();
const [loading, setLoading] = useState(false);
const [expandedRowKeys, setExpandedRowKeys] = useState([]);
const [taskList, setTaskList] = useState([]);
const [taskId, setTaskId] = useState();
const [havePass, setHavePass] = useState(false);
const columns = [
{
title: '学生姓名',
className: "taskTableColumns",
dataIndex: 'studentName',
key: 'studentName',
width: '9%',
ellipsis: true,
render: (text, record) => {
return (
<Tooltip title={text}>
<span>{text}</span>
</Tooltip>
);
},
},
{
title: '报名时间',
key: 'createdOn',
dataIndex: 'createdOn',
className: "taskTableColumns",
width: '10%',
render: (text, record) => {
return (
text && moment(text).format('YYYY-MM-DD')
);
},
},
{
title: '所在高校',
key: 'school',
dataIndex: 'school',
className: "taskTableColumns",
width: '14%',
ellipsis: true,
render: (text, record) => {
return (
<Tooltip title={text}>
<span>{text}</span>
</Tooltip>
);
},
},
{
title: '联系电话',
key: 'phone',
dataIndex: 'phone',
className: "taskTableColumns",
width: '12%',
ellipsis: true,
render: (text, record) => {
return (
<Tooltip title={text}>
<span>{text}</span>
</Tooltip>
);
},
},
{
title: '邮箱地址',
key: 'mail',
dataIndex: 'mail',
className: "taskTableColumns",
width: '15%',
ellipsis: true,
render: (text, record) => {
return (
<Tooltip title={text}>
<span>{text}</span>
</Tooltip>
);
},
},
{
title: '申请自荐书',
key: 'memo',
dataIndex: 'memo',
className: 'taskTableColumns',
ellipsis: true,
width: '20%',
render: (text, record) => {
return (
<Tooltip title={text}>
<span>{text}</span>
</Tooltip>
);
},
},
{
title: '',
key: 'more',
dataIndex: 'more',
className: 'taskTableColumns moreColumns',
width: '11%',
},
{
title: '操作',
dataIndex: 'passStatus',
align: 'center',
width: '9%',
className: "actionColumns taskTableColumns",
render: ((text, record, index) => {
return (
<div className='actionBox'>
{
text ? <Button type="default" onClick={() => { checkStudent(record.id, 0) }}>撤销</Button> :
<Button type="primary" disabled={havePass} onClick={() => { checkStudent(record.id, 1) }}>通过</Button>
}
</div>
)
})
},
];
useEffect(() => {
if (!current_user.user_id) {
history.push('/403');
}
hasAuditRole({ userId: current_user.user_id }).then(res => {
if (!(res && res.message == 'success' && res.data.hasRole)) {
history.push('/403');
}
})
}, [])
useEffect(() => {
getAuditList({ userId: current_user.user_id }).then(res => {
if (res.message == 'success') {
setTaskList(res.data.rows);
res.data.rows.length && setTaskId(res.data.rows[0].id);
} else {
res && showNotification(res.message || '查询课题列表失败');
}
})
}, [])
function checkStudent(id, status) {
auditPassTask({
id,
status,
}).then(res => {
if (res && res.message === 'success') {
showNotification(status ? '已通过该学生课题申请' : '撤销成功')
setReload(Math.random());
} else {
showNotification(res ? res.message : '操作失败');
}
})
}
const customExpandIcon = (props) => {
if (props.expanded) {
return <a className='actionBox' style={{ marginRight: 8 }} onClick={e => {
props.onExpand(props.record, e);
}}>更多详情<i className="iconfont icon-changyongtubiao-xianxingdaochu-zhuanqu- font-12 ml5 down mr10"></i></a>
} else {
return <a className='actionBox' style={{ marginRight: 8 }} onClick={e => {
props.onExpand(props.record, e);
}}>更多详情<i className="iconfont icon-jiantou9 font-12 ml5 down mr10"></i></a>
}
}
const expandRow = (record) => {
return <div className="student-expand">
<div className="info-line">
{(record.grade || record.profession) && <div className="info-item">
<span className="info-tit">所学专业</span>
<span className="info-content">{record.grade} | {record.profession}</span>
</div>}
{record.location && <div className="info-item">
<span className="info-tit">所在地区</span>
<span className="info-content">{record.location}</span>
</div>}
</div>
{record.memoAttachment && <div className="info-line">
<div className="info-item">
<span className="info-tit">自荐书附件</span>
<i className='iconfont icon-lianjie3 font-14 color6 mr5'></i>
<a
className="link"
href={
`${httpUrl}/busiAttachments/download/${record.memoAttachment.id}`
}
>
{record.memoAttachment.fileName}
</a>
</div>
</div>}
{record.proveAttachmentId && <div className="info-line">
<div className="info-item">
<span className="info-tit">学生证明</span>
<img className="info-img"
onClick={() => { setVisible(true); setPicture(record.proveAttachmentId) }}
src={`${window.location.href.indexOf('test') > -1 ||
window.location.href.indexOf('localhost') > -1
? 'https://testforgeplus.trustie.net'
: ''
}/api/attachments/${record.proveAttachmentId}`}
></img>
</div>
</div>}
</div>
}
//
function onExpand(expanded, record) {
const keys = new Set(expandedRowKeys);
if (expanded) {
keys.add(record.id);
} else {
keys.delete(record.id);
}
setExpandedRowKeys(Array.from(keys));
}
useEffect(() => {
setExpandedRowKeys([]);
setLoading(true);
const params = {
curPage: 1,
pageSize: 10000,
keyword: '',
taskId,
}
taskId && getStudentList(params).then(res => {
if (res.data && Array.isArray(res.data.rows)) {
setData(res.data.rows);
let isPass = res.data.rows.some((item) => { return item.passStatus });
setHavePass(isPass);
}
setLoading(false);
})
}, [taskId, reload])
return (
<div className="glcc-container glcc-check">
<div className="glcc-main">
<Breadcrumb className='glcc_breadcrumb font-16'>
<Breadcrumb.Item><Link to="/glcc">开源夏令营</Link></Breadcrumb.Item>
<Breadcrumb.Item style={{ color: '#202D40' }}>导师审核</Breadcrumb.Item>
</Breadcrumb>
<div className="head_introduce">
<h4 className="head_tit">审核说明</h4>
<div className="head_content">1欢迎进入导师审核页各位导师可查看到您发布课题的全部学生报名信息</div>
<div className="head_content">2您可根据学生报名信息与学生进行邮箱或电话沟通了解学生详细情况选择满意的学生</div>
<div className="head_content">3每个课题仅允许审核通过一个学生在审核过程中您可以随时调整各学生的审核状态</div>
<div className="head_content">4请各位导师在7.1日前完成课题申请的审核7.1日将根据各导师审核信息公布各课题学生入选名单</div>
</div>
<Tabs className="task-tabs" onChange={(e) => { setTaskId(e) }} activeKey={taskId + ''}>
{
taskList.map((item, i) => {
return <TabPane tab={`课题${i + 1}`} key={item.id} >
<div className="task-title">{item.taskName}</div>
</TabPane>
})
}
</Tabs>
<div className="taskList listBox">
<div className='line'></div>
<Table
loading={loading}
columns={columns}
dataSource={data}
expandedRowRender={expandRow}
expandIconColumnIndex={6}
expandIconAsCell={false}
expandIcon={customExpandIcon}
rowKey={'id'}
expandedRowKeys={expandedRowKeys}
onExpand={onExpand}
pagination={false}
/>
</div>
<Modal
visible={visible && picture}
title="学生证明"
visible={visible}
onCancel={() => { setVisible(false) }}
onOk={() => { setVisible(false) }}
className="picture-modal"
width={900}
footer={null}
>
<img
src={`${window.location.href.indexOf('test') > -1 ||
window.location.href.indexOf('localhost') > -1
? 'https://testforgeplus.trustie.net'
: ''
}/api/attachments/${picture}`}
></img>
</Modal>
</div>
</div>
)
}
export default Check;

237
src/glcc/check/index.scss Normal file
View File

@ -0,0 +1,237 @@
.glcc-check {
.task-tabs {
border: 1px solid #fff;
border-bottom: 0;
margin-top: 30px;
background-color: #f1f6ff;
.ant-tabs-nav-container {
background-color: #3758de;
font-size: 16px;
}
}
.ant-tabs-nav .ant-tabs-tab {
padding: 14px 0px;
color: #fff;
margin-right: 64px;
}
.ant-tabs-nav-scroll {
margin-left: 64px;
}
.ant-tabs-nav .ant-tabs-tab-active {
color: #fff;
}
.ant-tabs-bar {
border: 0;
margin: 0;
}
.ant-tabs-ink-bar {
background: #fff;
}
.ant-tabs-tab-prev,
.ant-tabs-tab-next {
width: 163px;
background: url("../img/check_tabs.png") no-repeat;
background-size: 100% 100%;
opacity: 1;
& > span {
display: none;
}
}
.ant-tabs-tab-next {
transform: rotate(180deg);
right: 0;
}
.ant-tabs-tabpane {
padding: 0 25px;
}
.ant-tabs-content {
padding: 25px 0;
}
.task-title {
font-weight: 700;
color: #3753c5;
font-size: 16px;
line-height: 30px;
}
.taskList {
padding: 0px 25px 50px 25px;
border-left: 1px solid white;
border-right: 1px solid white;
border-bottom: 1px solid white;
background-color: #f1f6ff;
.line {
margin: 0 auto 25px;
height: 1px;
border-top: 1px dashed #bec5d5;
}
.ant-pagination-item-active,
.ant-pagination-item:hover,
.ant-pagination-next:not(.ant-pagination-disabled)
.ant-pagination-item-link:hover,
.ant-select-selection:hover,
.ant-pagination-options-quick-jumper:hover input {
border-color: #466aff;
a {
color: #466aff;
}
}
.ant-pagination-disabled .ant-pagination-item-link:hover .anticon {
color: rgba(0, 0, 0, 0.25);
}
}
.taskTableColumns span div span {
font-size: 16px;
font-weight: 700;
color: #273778;
}
.taskTableColumns span {
font-size: 15px;
color: #353f5e;
line-height: 1.4;
}
.ant-table-tbody .taskTableColumns.taskName span {
color: #2545c9;
cursor: pointer;
}
.ant-table-tbody {
.taskTableColumns {
line-height: 100%;
span {
display: inline-block;
width: 100%;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.ant-table-tbody > tr > .taskTableColumns {
background-color: #f1f6ff;
// background-color:#F1F6FF;
border-bottom: 1px dashed #bec5d5;
}
.ant-table-thead > tr > .taskTableColumns {
background: #e6ecff;
padding: 8px 16px;
span {
font-size: 15px;
}
}
.ant-table-tbody
> tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected)
> .taskTableColumns {
background-color: #fbfbfc;
}
.ant-table-tbody .moreColumns {
.ant-table-row-indent {
display: none;
}
}
.actionBox {
color: #466aff;
.ant-btn-primary{
span {
line-height: 29px;
}
}
.ant-btn-primary:not(:disabled) {
cursor: pointer;
background: #466aff;
border-color: #466aff;
&:hover {
background: #5d7cff !important;
}
&:active {
background: #1140ff !important;
}
span {
color: #fff;
}
}
.ant-btn-default {
border-color: #e44141;
span {
color: #e44141;
line-height: 29px;
}
&:hover {
border-color: #ff5a5a;
span {
color: #ff5a5a;
}
}
&:active {
border-color: #cb0101;
span {
color: #cb0101;
}
}
}
}
tr:hover .actionColumns .cancelApply {
visibility: visible;
color: #e31e1e;
}
.ant-table-expanded-row td {
padding: 0;
}
.student-expand {
padding: 23px;
// opacity: 80%;
background-color: #e9efff;
border: 1px solid #ffffff;
.info-line {
line-height: 44px;
display: flex;
}
.info-tit {
display: inline-block;
width: 6em;
margin-right: 18px;
text-align: right;
font-weight: 500;
color: #000000;
}
.info-content {
color: #465474;
}
.color6{
color: #666;
}
.info-img {
max-width: 300px;
}
.link {
color: #466aff;
&:hover {
opacity: 0.8;
}
}
}
}
.picture-modal {
width: 900px;
.ant-modal-body {
min-height: 300px;
display: flex;
justify-content: center;
align-items: center;
}
img {
max-width: 100%;
}
}

View File

@ -1,4 +1,4 @@
import React from "react";
import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
// import Banner from "./banner";
import Lightspot from './lightspot';
@ -11,30 +11,49 @@ 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 teacher from "../img/teacher.png";
import { hasAuditRole } from '../api';
import './index.scss';
export default (props) => {
const { current_user, isGlccApplyDate, showNotification, studentApplyStart,history } = props;
function goToApply() {
if (isGlccApplyDate) {
if (current_user && current_user.login) {
window.location.href = "https://wj.qq.com/s2/10175205/e8df/";
} else {
window.location.href = "/login?go_page=https://wj.qq.com/s2/10175205/e8df/";
const { current_user, isGlccApplyDate, showNotification, studentApplyStart, history } = props;
// function goToApply() {
// if (isGlccApplyDate) {
// if (current_user && current_user.login) {
// window.location.href = "https://wj.qq.com/s2/10175205/e8df/";
// } else {
// window.location.href = "/login?go_page=https://wj.qq.com/s2/10175205/e8df/";
// }
// } else {
// showNotification("415~520");
// window.location.href = "/glcc";
// }
// }
const [hasRole, setHhasRole] = useState(false);
useEffect(() => {
if (!current_user.user_id) {
history.push('/403');
}
hasAuditRole({ userId: current_user.user_id }).then(res => {
if (res && res.message == 'success' && res.data.hasRole) {
setHhasRole(true);
}
})
}, [])
function goToStudent() {
//
if (!studentApplyStart) {
showNotification("不在报名时间报名开始时间为5月26日");
} else {
showNotification("不在报名时间报名时间为4月15日~5月20日");
window.location.href = "/glcc";
history.push("/glcc/subjects");
}
}
function goToStudent(){
//
if(!studentApplyStart){
showNotification("不在报名时间报名开始时间为5月26日");
}else{
history.push("/glcc/subjects");
}
function goToCheck() {
history.push("/glcc/mentoradmin");
}
return (
@ -52,7 +71,7 @@ export default (props) => {
{/* 项目报名 */}
<Link to="/glcc/projects" className="apply project" >
<div>
<img src={apply1} alt="" className="applyIcon"/>
<img src={apply1} alt="" className="applyIcon" />
<span className="hover-none"><span className="til">查看项目</span> </span>
{/* <span className="hover-show">项目报名已截止</span> */}
</div>
@ -61,11 +80,19 @@ export default (props) => {
{/* 学生报名 */}
<div className="apply" onClick={goToStudent}>
<div>
<img src={apply2} alt="" className="applyIcon"/>
<span className="til">学生报名</span>
<img src={apply2} alt="" className="applyIcon" />
<span className="til">学生报名</span>
</div>
<div className="pt6">选择感兴趣的课题开启您的开源之旅</div>
</div>
{/* 导师审核 */}
{hasRole && <div className="apply" onClick={goToCheck}>
<div>
<img src={teacher} alt="" className="applyIcon" />
<span className="til">导师审核</span>
</div>
<div className="pt6">以赛代筛挖掘高潜力人才</div>
</div>}
</div>
{/* </div> */}
<div className="introduce">
@ -77,7 +104,7 @@ export default (props) => {
</div>
</div>
</div>
<Lightspot isGlccApplyDate={isGlccApplyDate} current_user={current_user} showNotification={showNotification} studentApplyStart={studentApplyStart}/>
<Lightspot isGlccApplyDate={isGlccApplyDate} current_user={current_user} showNotification={showNotification} studentApplyStart={studentApplyStart} />
<TimerShaft />
<Award />
<News />

View File

@ -17,6 +17,7 @@
.ant-btn-primary{
background-color: #466aff;
border-color: #466aff;
&:hover{
opacity: .8;
}
@ -41,6 +42,10 @@
flex-direction: column;
justify-content: center;
width: 355px;
&:not(:first-child){
margin-left: 35px;
}
.til{
margin-left: 10px;
font-family:PingFang SC;
@ -71,9 +76,7 @@
}
}
}
.apply:last-child{
margin-left: 35px;
}
.applyIcon{
width: 30px;
}

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -31,11 +31,16 @@ const Help = Loadable({
loader: () => import("./help"),
loading: Loading,
});
//
//
const Student = Loadable({
loader: () => import("./student"),
loading: Loading,
});
//
const Check = Loadable({
loader: () => import("./check"),
loading: Loading,
});
//
const Project = Loadable({
@ -96,6 +101,13 @@ const Glcc = (propsF) => {
<Apply {...propsF} {...props} isGlccApplyDate={isGlccApplyDate}/>
)}
></Route>
{/* 教师审核 */}
<Route
path="/glcc/mentoradmin"
render={(props) => (
<Check {...propsF} {...props} isGlccApplyDate={isGlccApplyDate}/>
)}
></Route>
{/* 帮助中心 */}
<Route
path="/glcc/help"

View File

@ -1,49 +1,99 @@
.-task-sidebar .glccHelp{
height: auto;
width: 48px;
padding: 6px 16px;
text-align: center;
background-color:#f6f9fe;
border:1px solid #ffffff;
border-radius:2px;
box-shadow:0px 0px 8px rgba(55, 148, 255, 0.16);
color:#1e1e1e;
.glcc-container {
background-image: linear-gradient(
180deg,
#ebf2ff 0%,
#ebf2ff 43.09%,
#f3f4f8 100%
);
padding-bottom: 120px;
.glcc_breadcrumb {
padding: 18px 0;
border-bottom: 1px dashed #bec5d5;
margin-bottom: 30px;
}
.head_introduce {
background: #e4edff;
padding: 20px;
}
.head_tit {
color: #000000;
font-size: 15px;
line-height: 2;
}
.head_content {
color: #6c7283;
font-size: 14px;
line-height: 35px;
word-break: break-word;
cursor: pointer;
.icon-bangzhuzhongxinicon, .icon-bangzhuzhongxinicon1{
color: #0654D6;
margin-left: -3px;}
&:hover{
color:#ffffff;
background-color:#466aff;
.icon-bangzhuzhongxinicon, .icon-bangzhuzhongxinicon1{color:#ffffff}
}
line-height: 38px;
}
.head_bold {
font-weight: 700;
color: #333;
}
}
.qqChatBox{
position: fixed;
.ant-popover-arrow{display: none;}
}
.qqChat{
margin: -12px -16px;
background-image:linear-gradient(180deg,#f8faff 0%,#dee7ff 100%);
border:1px solid #ffffff;
border-radius:4px;
box-shadow:0px 0px 6px rgba(121, 154, 245, 0.27);
padding: 25px 40px;
.glcc-main {
width: 1200px;
margin: 0 auto;
}
.-task-sidebar .glccHelp {
height: auto;
width: 48px;
padding: 6px 16px;
text-align: center;
background-color: #f6f9fe;
border: 1px solid #ffffff;
border-radius: 2px;
box-shadow: 0px 0px 8px rgba(55, 148, 255, 0.16);
color: #1e1e1e;
font-size: 14px;
line-height: 35px;
word-break: break-word;
cursor: pointer;
.icon-bangzhuzhongxinicon,
.icon-bangzhuzhongxinicon1 {
color: #0654d6;
margin-left: -3px;
}
&:hover {
color: #ffffff;
background-color: #466aff;
.icon-bangzhuzhongxinicon,
.icon-bangzhuzhongxinicon1 {
color: #ffffff;
}
}
}
.qqChatBox {
position: fixed;
.ant-popover-arrow {
display: none;
}
}
.qqChat {
margin: -12px -16px;
background-image: linear-gradient(180deg, #f8faff 0%, #dee7ff 100%);
border: 1px solid #ffffff;
border-radius: 4px;
box-shadow: 0px 0px 6px rgba(121, 154, 245, 0.27);
padding: 25px 40px;
text-align: center;
.qqMa {
padding: 8px;
margin-bottom: 15px;
text-align: center;
.qqMa{
padding: 8px;
margin-bottom: 15px;
text-align: center;
background-image:radial-gradient(ellipse 50% 50% at 50% 50% ,rgba(255, 255, 255, 0) 0%,rgba(239, 243, 251, 0) 100%);
box-shadow:0px 0px 6px #aecaff inset;
img{width: 120px;}
background-image: radial-gradient(
ellipse 50% 50% at 50% 50%,
rgba(255, 255, 255, 0) 0%,
rgba(239, 243, 251, 0) 100%
);
box-shadow: 0px 0px 6px #aecaff inset;
img {
width: 120px;
}
.qqTip{
color:#5769a5;
font-size:14px;
line-height:24px;
}
}
}
.qqTip {
color: #5769a5;
font-size: 14px;
line-height: 24px;
}
}