forked from Gitlink/forgeplus-react
Merge pull request 'glcc issue回归' (#377) from tongChong/forgeplus-react:feature_GLCC into gitlink_server
This commit is contained in:
commit
2089c7a28f
|
@ -10,7 +10,7 @@ function Award() {
|
|||
<div className="glcc-content">
|
||||
<div className="award-item">
|
||||
<div className="award-tit">丰厚现金奖励</div>
|
||||
<div className="award-content">根据项目难度设置高、中、低三个阶梯课题,对应奖金为<span className="number">12000</span> 元、<span className="number">9000</span> 元和 <span className="number">6000</span> 元</div>
|
||||
<div className="award-content">根据课题开发耗时,对应奖金从<span className="number">6000</span>元至<span className="number">12000</span>元不等。详细可以课题详情中具体奖金标准</div>
|
||||
<div className="award-line"></div>
|
||||
</div>
|
||||
|
||||
|
@ -28,7 +28,7 @@ function Award() {
|
|||
|
||||
<div className="award-item">
|
||||
<div className="award-tit">夏令营大礼包</div>
|
||||
<div className="award-content">夏令营纪念文化衫、帆布包、鼠标垫,开源系列图书,社区周边等。此外,还会评选出若干优秀社区、明星导师、最佳课题、开源之星等单项奖。</div>
|
||||
<div className="award-content">夏令营纪念文化衫、帆布包、鼠标垫,开源系列图书,社区周边等。此外,还会评选出若干优秀社区、明星导师、最佳课题、开源之星等单项奖</div>
|
||||
<div className="award-line"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -5,32 +5,32 @@ import './index.scss';
|
|||
const stepArr = [{
|
||||
title: "Step1",
|
||||
date: "4.15",
|
||||
content: "GLCC发布"
|
||||
content: "GLCC夏令营活动发布"
|
||||
},
|
||||
{
|
||||
title: "Step2",
|
||||
date: "4.15-5.20",
|
||||
content: "社区启动报名"
|
||||
date: "4.15-5.19",
|
||||
content: "社区项目报名,提交课题"
|
||||
},
|
||||
{
|
||||
title: "Step2",
|
||||
date: "5.20",
|
||||
content: "公示社区名单"
|
||||
date: "5.20-5.25",
|
||||
content: "公示社区项目名单"
|
||||
},
|
||||
{
|
||||
title: "Step2",
|
||||
date: "5.21-6.19",
|
||||
content: "学生提交proposal"
|
||||
date: "5.26-6.24",
|
||||
content: "学生报名,提交Proposal"
|
||||
},
|
||||
{
|
||||
title: "Step2",
|
||||
date: "7.1",
|
||||
content: "公示项目申请审核结果",
|
||||
content: "公示入选学生名单",
|
||||
noArrow: true,
|
||||
},
|
||||
{
|
||||
title: "Step2",
|
||||
date: "7.4-8.12",
|
||||
date: "7.1-8.12",
|
||||
content: "项目开发第一阶段",
|
||||
},
|
||||
{
|
||||
|
@ -40,13 +40,13 @@ const stepArr = [{
|
|||
},
|
||||
{
|
||||
title: "Step2",
|
||||
date: "10.1-10.14",
|
||||
content: "结项审核",
|
||||
date: "10.1-10.21",
|
||||
content: "结项审核,并公示考核结果",
|
||||
},
|
||||
{
|
||||
title: "Step2",
|
||||
date: "11.11",
|
||||
content: "公布优秀项目&学员",
|
||||
date: "10.21-11.11",
|
||||
content: "评选公布优秀项目和学员&学员",
|
||||
noArrow: true,
|
||||
}
|
||||
]
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 4.0 MiB After Width: | Height: | Size: 1.1 MiB |
Binary file not shown.
Before Width: | Height: | Size: 410 KiB After Width: | Height: | Size: 2.0 MiB |
|
@ -1,12 +1,28 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Button } from 'antd';
|
||||
import { Button, message } from 'antd';
|
||||
import Nodata from '../../../forge/Nodata';
|
||||
import { useEffect } from 'react';
|
||||
import { getProjectById } from '../../api';
|
||||
|
||||
export default ({ detail, projectId, applyTaskId }) => {
|
||||
export default ({ detail, projectId, applyTaskId, current_user, showLoginDialog, isStudentApplyDate, studentApplyEnd }) => {
|
||||
const [info, setInfo] = useState(detail);
|
||||
|
||||
// 申请课题按钮点击函数
|
||||
function applyTask(id){
|
||||
// 判断用户是否已经登录
|
||||
if(current_user && current_user.login){
|
||||
// 判断用户是否已经报名两个课题
|
||||
if(applyTaskId && Object.keys(applyTaskId).length >= 2){
|
||||
message.error('最多只能同时报名两个课题');
|
||||
}else{
|
||||
// 跳转到学生报名页
|
||||
window.location.href=`/glcc/student/apply/${id}`;
|
||||
}
|
||||
}else{
|
||||
showLoginDialog();
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
if(!detail && projectId){
|
||||
// 通过项目Id查询项目详情
|
||||
|
@ -40,22 +56,22 @@ export default ({ detail, projectId, applyTaskId }) => {
|
|||
<div className="projectDetailHead">
|
||||
<span className='name'>{info.projectName}</span>
|
||||
{info.projectType && <span className='type'>{info.projectType}</span>}
|
||||
<p>{info.gitlinkUrl}</p>
|
||||
<div>{info.officialWebsiteUrl}</div>
|
||||
<p>GitLink项目地址: {info.gitlinkUrl}</p>
|
||||
<div>项目简介: {info.projectIntro}</div>
|
||||
</div>
|
||||
{info.registrationTaskList && info.registrationTaskList.length > 0 ? info.registrationTaskList.map(item=>{
|
||||
return <div className='taskItem mt20'>
|
||||
<div className="left">
|
||||
<div className="taskTitle">{item.taskName}</div>
|
||||
<div className='mt20'>导师姓名: {item.tutorName}</div>
|
||||
<div className='mb20 email'>邮箱地址: <span>{item.tutorMail}</span></div>
|
||||
<div className='mt20 oneLine leftWidth'>导师姓名: {item.tutorName}</div>
|
||||
<div className='mb20 email oneLine leftWidth'>邮箱地址: <span>{item.tutorMail}</span></div>
|
||||
</div>
|
||||
<div className="center">
|
||||
<div className="taskDesc">{item.taskDesc}</div>
|
||||
<div className="taskUrl">课题链接: <span>{item.taskUrl}</span></div>
|
||||
<div>{applyTaskId && item.id && Object.keys(applyTaskId).includes(item.id.toString()) ? <Button onClick={()=>{window.location.href=`/glcc/student/apply/${item.id}`}}>报名详情</Button> : <Button type='primary' className='applyBut' onClick={()=>{window.location.href=`/glcc/student/apply/${item.id}`}}>申请课题</Button>}</div>
|
||||
<div>{(isStudentApplyDate || studentApplyEnd) && (applyTaskId && item.id && Object.keys(applyTaskId).includes(item.id.toString()) ? <Button onClick={()=>{window.location.href=`/glcc/student/apply/${item.id}`}} className='lookDetail'>报名详情</Button> : isStudentApplyDate && <Button type='primary' className='applyBut' onClick={()=>{applyTask(item.id)}}>申请课题</Button>)}</div>
|
||||
</div>
|
||||
<div className="right">¥ {item.taskReward}</div>
|
||||
<div className="right oneLine">¥ {item.taskReward}</div>
|
||||
</div>
|
||||
}) : <Nodata _html="课题暂无数据" small={true}/>}
|
||||
</div>: <div className="projectDetailBox nodata"><Nodata _html="暂无数据" small={true}/></div>
|
||||
|
|
|
@ -20,8 +20,12 @@ const TaskList = Loadable({
|
|||
});
|
||||
|
||||
function Project(propsF) {
|
||||
const {location, current_user} = propsF;
|
||||
const {location, current_user, showLoginDialog} = propsF;
|
||||
const {pathname} = location;
|
||||
const isStudentApplyDate = Date.parse(new Date()) > 1653494400000 && Date.parse(new Date()) < 1656086400000;
|
||||
const studentApplyEnd = Date.parse(new Date()) > 1656086400000;
|
||||
// const studentApplyEnd = false;
|
||||
// const isStudentApplyDate = true;
|
||||
|
||||
// 用户已报名课题id数组
|
||||
const [applyTaskId, setApplyTaskId] = useState({});
|
||||
|
@ -57,13 +61,13 @@ function Project(propsF) {
|
|||
<Route
|
||||
path="/glcc/student/1"
|
||||
render={(props) => (
|
||||
<ProjectList applyTaskId={applyTaskId}/>
|
||||
<ProjectList applyTaskId={applyTaskId} current_user={current_user} showLoginDialog={showLoginDialog} isStudentApplyDate={isStudentApplyDate} studentApplyEnd={studentApplyEnd}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/glcc/student/2"
|
||||
render={(props) => (
|
||||
<TaskList applyTaskId={applyTaskId} setStudentInfoReset={setStudentInfoReset}/>
|
||||
<TaskList applyTaskId={applyTaskId} setStudentInfoReset={setStudentInfoReset} current_user={current_user} showLoginDialog={showLoginDialog} isStudentApplyDate={isStudentApplyDate} studentApplyEnd={studentApplyEnd}/>
|
||||
)}
|
||||
></Route>
|
||||
</Switch>
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
padding: 30px 30px 50px;
|
||||
.taskItem{
|
||||
.center{
|
||||
width: 60%;
|
||||
// width: 60%;
|
||||
}
|
||||
.right{padding-right: 10px;}
|
||||
}
|
||||
|
@ -104,10 +104,15 @@
|
|||
.taskItem {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0px 0px 12px rgba(71, 105, 198, 0.4);
|
||||
border:1px solid #ffffff;
|
||||
&:hover{ box-shadow: 0px 0px 12px rgba(71, 105, 198, 0.4);}
|
||||
.oneLine{
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.left{
|
||||
width: 250px;
|
||||
width: 316px;
|
||||
background-image: url('../img/projectDetailTaskBg.png');
|
||||
background-size: 100% 100%;
|
||||
padding: 22px 20px;
|
||||
|
@ -117,9 +122,10 @@
|
|||
font-size:18px;
|
||||
}
|
||||
.email span{color:#466aff;}
|
||||
.leftWidth{width: 270px;}
|
||||
}
|
||||
.center{
|
||||
width: 66%;
|
||||
width: 650px;
|
||||
text-align: left;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
|
@ -141,6 +147,7 @@
|
|||
}
|
||||
}
|
||||
.right{
|
||||
min-width: 150px;
|
||||
font-weight:700;
|
||||
color:#ff8800;
|
||||
font-size:24px;
|
||||
|
@ -148,8 +155,14 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
.projectItemPopover{width: 1200px;}
|
||||
.projectItemPopover{
|
||||
width: 1200px;
|
||||
.ant-popover-content .ant-popover-arrow{
|
||||
border-width: 12px;
|
||||
}
|
||||
}
|
||||
.projectItemPopover .ant-popover-inner{
|
||||
transform: translateY(8px);
|
||||
background-image:linear-gradient(180deg,#f1f5ff 0%,#ffffff 100%);
|
||||
border:1px solid #ffffff;
|
||||
border-radius:4px;
|
||||
|
@ -157,4 +170,8 @@
|
|||
.ant-popover-inner-content{
|
||||
padding: 30px 30px 50px;
|
||||
}
|
||||
}
|
||||
.lookDetail:hover, .lookDetail:focus{
|
||||
border-color: #466aff;
|
||||
color: #466aff;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import React , { useEffect , useState } from 'react';
|
||||
import { Input, Popover, Table, Tooltip } from 'antd';
|
||||
import { Input, Popover, Spin, Table, Tooltip } from 'antd';
|
||||
import { main_site_url } from '../../fetch';
|
||||
import './index.scss';
|
||||
import { projectList } from '../../api';
|
||||
|
@ -7,12 +7,14 @@ import ProjectDetail from '../component/projectDetail';
|
|||
const {Search} = Input;
|
||||
|
||||
// 项目列表
|
||||
function ProjectList({applyTaskId}) {
|
||||
function ProjectList({applyTaskId, current_user, showLoginDialog, isStudentApplyDate, studentApplyEnd}) {
|
||||
// 输入搜索框
|
||||
const [keyword, setKeyword] = useState(undefined);
|
||||
const [data, setData] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(()=>{
|
||||
setLoading(true);
|
||||
const params = {
|
||||
curPage: 1,
|
||||
keyword,
|
||||
|
@ -22,6 +24,7 @@ function ProjectList({applyTaskId}) {
|
|||
if(response && response.message === "success"){
|
||||
setData(response.data.rows);
|
||||
}
|
||||
setLoading(false);
|
||||
})
|
||||
},[keyword])
|
||||
|
||||
|
@ -29,18 +32,20 @@ function ProjectList({applyTaskId}) {
|
|||
<div className="taskList listBox">
|
||||
<div className="list">
|
||||
<div className='search'><Search placeholder='请输入项目名称进行搜索' allowClear enterButton onSearch={(value)=>{setKeyword(value)}}/></div>
|
||||
<div className='projectListBox'>
|
||||
{data && data.map((item, index)=>{
|
||||
return <Popover placement= {(index+1)%3 === 0 ? 'bottomRight' : (index+1)%3%2 === 0 ? 'bottom' : 'bottomLeft'} content={<ProjectDetail detail={item} applyTaskId={applyTaskId}/>} trigger='click' overlayClassName='projectItemPopover'>
|
||||
<div className={`projectItem ${(index+1)%3 === 0 || (index+1)%3%2 === 0 ? '' : 'firstBox'}`}>
|
||||
<div className="border"></div>
|
||||
<div className="projectLogo"><img src={`${main_site_url}/api/attachments/${item.projectLogoId}`} alt=''/></div>
|
||||
<div className="title">{item.projectName}</div>
|
||||
<div className="intro">{item.projectIntro}</div>
|
||||
</div>
|
||||
</Popover>
|
||||
})}
|
||||
</div>
|
||||
<Spin spinning={loading}>
|
||||
<div className='projectListBox'>
|
||||
{data && data.map((item, index)=>{
|
||||
return <Popover placement= {(index+1)%3 === 0 ? 'bottomRight' : (index+1)%3%2 === 0 ? 'bottom' : 'bottomLeft'} content={<ProjectDetail detail={item} applyTaskId={applyTaskId} current_user={current_user} showLoginDialog={showLoginDialog}/>} trigger='click' overlayClassName='projectItemPopover' autoAdjustOverflow={false} isStudentApplyDate={isStudentApplyDate} studentApplyEnd={studentApplyEnd}>
|
||||
<div className={`projectItem ${(index+1)%3 === 0 || (index+1)%3%2 === 0 ? '' : 'firstBox'}`}>
|
||||
<div className="border"></div>
|
||||
<div className="projectLogo"><img src={`${main_site_url}/api/attachments/${item.projectLogoId}`} alt=''/></div>
|
||||
<div className="title">{item.projectName}</div>
|
||||
<div className="intro">{item.projectIntro}</div>
|
||||
</div>
|
||||
</Popover>
|
||||
})}
|
||||
</div>
|
||||
</Spin>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -1,15 +1,3 @@
|
|||
.actionBox{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #466aff;
|
||||
cursor: pointer;
|
||||
.iconfont{
|
||||
margin: 0 10px 0 14px;
|
||||
}
|
||||
.applyTask{
|
||||
color:#eb9350;
|
||||
}
|
||||
}
|
||||
.projectListBox{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
|
|
@ -6,7 +6,7 @@ import ProjectDetail from '../component/projectDetail';
|
|||
const { Search } = Input;
|
||||
|
||||
// 课题列表
|
||||
function TaskList({applyTaskId, setStudentInfoReset}) {
|
||||
function TaskList({applyTaskId, setStudentInfoReset, current_user, showLoginDialog, isStudentApplyDate, studentApplyEnd}) {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [deleteTaskId, setDeleteTaskId] = useState(undefined);
|
||||
// 输入搜索框
|
||||
|
@ -16,21 +16,22 @@ function TaskList({applyTaskId, setStudentInfoReset}) {
|
|||
const [current, setCurrent] = useState(1);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [pageSize, setPageSize] = useState(20);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const columns = [
|
||||
{ title: '序号', dataIndex: 'index', align: 'center', className:"taskTableColumns", render: (text, item, index) => index + 1 },
|
||||
{ title: '课题名称', dataIndex: 'taskName', className:"taskTableColumns taskName", width: '28%', render: (text, item) => <Tooltip title={text}><a onClick={()=>{window.location.href=item.taskUrl}}>{text}</a></Tooltip> },
|
||||
{ title: '项目名称', dataIndex: 'tutorName', className:"taskTableColumns", width: '28%', render: (text) => <Tooltip title={text}>{text}</Tooltip> },
|
||||
{ title: '课题奖金', dataIndex: 'taskReward', className:"taskTableColumns", render: (text) => <span>¥ {text}</span> },
|
||||
{ title: '序号', dataIndex: 'index', align: 'center', className:"taskTableColumns", width: '6%', render: (text, item, index) => index + 1 },
|
||||
{ title: '课题名称', dataIndex: 'taskName', className:"taskTableColumns taskName", width: '28%', ellipsis: true, render: (text, item) => <Tooltip title={text} placement="topLeft"><span onClick={()=>{window.location.href=item.taskUrl}}>{text}</span></Tooltip> },
|
||||
{ title: '项目名称', dataIndex: 'projectName', className:"taskTableColumns", width: '28%', ellipsis: true, render: (text) => <Tooltip title={text} placement="topLeft">{text}</Tooltip> },
|
||||
{ title: '课题奖金', dataIndex: 'taskReward', className:"taskTableColumns", width: `${isStudentApplyDate || studentApplyEnd ? '15%' : ''}`, render: (text) => <span>¥ {text}</span> },
|
||||
{
|
||||
title: '操作', dataIndex: 'action', align: 'center', className:"actionColumns taskTableColumns", render: ((text, item, index) => {
|
||||
return (
|
||||
<div className='actionBox'>
|
||||
{applyTaskId && Object.keys(applyTaskId).includes(item.id.toString()) ? <Fragment>
|
||||
{(isStudentApplyDate || studentApplyEnd) && (applyTaskId && Object.keys(applyTaskId).includes(item.id.toString()) ? <Fragment>
|
||||
<span onClick={()=>{window.location.href=`/glcc/student/apply/${item.id}`}}><i className='iconfont icon-baomingxiangqingicon mr5'></i>报名详情</span>
|
||||
<Tooltip title={"取消申请"}><i className='iconfont icon-shanchuicon3 ml20 cancelApply' onClick={()=>{setDeleteTaskId(item.id);setVisible(true)}}></i></Tooltip>
|
||||
</Fragment>:<Fragment>
|
||||
<span onClick={()=>{window.location.href=`/glcc/student/apply/${item.id}`}}><i className='iconfont icon-shenqingketiicon applyTask mr5'></i><span className='applyTask' onClick={()=>{applyTask(item.id)}}>申请课题</span></span></Fragment>}
|
||||
</Fragment>:isStudentApplyDate && <Fragment>
|
||||
<span onClick={()=>{applyTask(item.id)}}><i className='iconfont icon-shenqingketiicon applyTask mr5'></i><span className='applyTask'>申请课题</span></span></Fragment>)}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
@ -50,17 +51,22 @@ function TaskList({applyTaskId, setStudentInfoReset}) {
|
|||
}
|
||||
|
||||
const expandRow = (record) => {
|
||||
return <ProjectDetail detail={null} projectId={record.regId} applyTaskId={applyTaskId}/>
|
||||
return <ProjectDetail detail={null} projectId={record.regId} applyTaskId={applyTaskId} current_user={current_user} showLoginDialog={showLoginDialog} isStudentApplyDate={isStudentApplyDate} studentApplyEnd={studentApplyEnd}/>
|
||||
}
|
||||
|
||||
// 申请课题按钮点击函数
|
||||
function applyTask(id){
|
||||
// 判断用户是否已经报名两个课题
|
||||
if(applyTaskId.length >= 2){
|
||||
message.error('最多只能同时报名两个课题');
|
||||
// 判断用户是否已经登录
|
||||
if(current_user && current_user.login){
|
||||
// 判断用户是否已经报名两个课题
|
||||
if(applyTaskId && Object.keys(applyTaskId).length >= 2){
|
||||
message.error('最多只能同时报名两个课题');
|
||||
}else{
|
||||
// 跳转到学生报名页
|
||||
window.location.href=`/glcc/student/apply/${id}`;
|
||||
}
|
||||
}else{
|
||||
// 跳转到学生报名页
|
||||
window.location.href=`/glcc/student/apply/${id}`;
|
||||
showLoginDialog();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,7 +96,15 @@ function TaskList({applyTaskId, setStudentInfoReset}) {
|
|||
setCurrent(page);
|
||||
}
|
||||
|
||||
// 数组元素置顶方法
|
||||
function toFirst(data, index){
|
||||
if(index != 0){
|
||||
data.unshift(data.splice(index,1)[0]);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
const params = {
|
||||
curPage: current,
|
||||
keyword,
|
||||
|
@ -98,9 +112,16 @@ function TaskList({applyTaskId, setStudentInfoReset}) {
|
|||
}
|
||||
taskList(params).then(response => {
|
||||
if (response && response.message === "success") {
|
||||
// 将已报名task任务置顶
|
||||
const ids = Object.keys(applyTaskId);
|
||||
const data = response.data.rows;
|
||||
data.map((item, index)=>{
|
||||
ids.includes(item.id.toString()) && toFirst(data, index);
|
||||
})
|
||||
setData(response.data.rows);
|
||||
setTotal(response.data.total)
|
||||
}
|
||||
setLoading(false);
|
||||
})
|
||||
}, [keyword, current, pageSize])
|
||||
|
||||
|
@ -109,6 +130,7 @@ function TaskList({applyTaskId, setStudentInfoReset}) {
|
|||
<div className="list">
|
||||
<div className='search'><Search placeholder='请输入课题名称进行搜索' allowClear enterButton onSearch={(value) => { setKeyword(value) }} /></div>
|
||||
<Table
|
||||
loading={loading}
|
||||
columns={columns}
|
||||
dataSource={data}
|
||||
expandedRowRender={expandRow}
|
||||
|
|
|
@ -3,6 +3,13 @@
|
|||
.ant-table{
|
||||
border: 1px solid white;
|
||||
}
|
||||
.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;
|
||||
|
|
|
@ -9,6 +9,7 @@ import banner from "../img/banner.png";
|
|||
import studentSvg from "../img/student.png";
|
||||
import './index.scss';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { color } from 'echarts/lib/theme/light';
|
||||
|
||||
const Option = Select.Option;
|
||||
const gradeList = [
|
||||
|
@ -43,8 +44,12 @@ function Apply(props) {
|
|||
const [allTaskList, setAllTaskList] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
// 进入此页面置顶
|
||||
window.scrollTo(0, 0);
|
||||
// 进入此页面到填写页面
|
||||
// setTimeout(()=>{
|
||||
let clientWidth = document.body.clientWidth;
|
||||
let scrollHeight = 500 * clientWidth / 1920;
|
||||
window.scrollTo(0, scrollHeight);
|
||||
|
||||
if (!isGlccApplyDate) {
|
||||
// 不在开源夏令营报名时间之内
|
||||
window.location.href = "/glcc";
|
||||
|
@ -55,6 +60,7 @@ function Apply(props) {
|
|||
}
|
||||
}, [])
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const params = {
|
||||
curPage: 1,
|
||||
|
@ -94,13 +100,13 @@ function Apply(props) {
|
|||
applyInfo['memoAttachmentId' + i] = item.memoAttachmentId;
|
||||
|
||||
if (i == 0) {
|
||||
setFiles([{
|
||||
item.memoAttachment && setFiles([{
|
||||
name: item.memoAttachment && item.memoAttachment.fileName || '已上传文件',
|
||||
id: item.memoAttachmentId,
|
||||
uid: item.memoAttachmentId
|
||||
}]);
|
||||
} else {
|
||||
setFiles1([{
|
||||
item.memoAttachment && setFiles1([{
|
||||
name: item.memoAttachment && item.memoAttachment.fileName || '已上传文件',
|
||||
id: item.memoAttachmentId,
|
||||
uid: item.memoAttachmentId,
|
||||
|
@ -205,15 +211,29 @@ function Apply(props) {
|
|||
);
|
||||
|
||||
|
||||
// 检查文件上传是否符合规定
|
||||
// 检查图片文件上传是否符合规定
|
||||
function beforeUpload(file) {
|
||||
const isLittle = file.size / 1024 / 1024 < 5;
|
||||
if (!isLittle) {
|
||||
showNotification(`文件大小必须小于${5}MB!`);
|
||||
}
|
||||
const isType = file.type === "image/png" || file.type === "image/jpg" || file.type === "image/jpeg";
|
||||
if (!isType) {
|
||||
showNotification("只能上传png、jpg、jpeg格式文件");
|
||||
}
|
||||
return isType;
|
||||
return isLittle && isType;
|
||||
}
|
||||
|
||||
// 检查文件上传是否符合规定
|
||||
function beforeUpload2(file) {
|
||||
const isLittle = file.size / 1024 / 1024 < 5;
|
||||
if (!isLittle) {
|
||||
showNotification(`文件大小必须小于${5}MB!`);
|
||||
}
|
||||
return isLittle;
|
||||
}
|
||||
|
||||
|
||||
function getBase64(img, callback) {
|
||||
const reader = new FileReader();
|
||||
reader.addEventListener('load', () => callback(reader.result));
|
||||
|
@ -313,6 +333,8 @@ function Apply(props) {
|
|||
setMyTaskList(taskListNew);
|
||||
}
|
||||
|
||||
const chooseArr = myTaskList.map(i => { return i.taskId });
|
||||
|
||||
return (
|
||||
<div className="glcc_student_apply">
|
||||
<img className="glcc-banner" src={banner} alt=''></img>
|
||||
|
@ -321,12 +343,12 @@ function Apply(props) {
|
|||
<Breadcrumb className='glcc_breadcrumb font-16'>
|
||||
<Breadcrumb.Item><Link to="/glcc">开源夏令营</Link></Breadcrumb.Item>
|
||||
<Breadcrumb.Item><Link to="/glcc/student/2">查看项目</Link></Breadcrumb.Item>
|
||||
<Breadcrumb.Item>学生报名</Breadcrumb.Item>
|
||||
<Breadcrumb.Item style={{ color: '#202D40' }}>学生报名</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
|
||||
<div className="head_introduce">
|
||||
<h4 className="head_tit">申请说明:</h4>
|
||||
<div className="head_content">1、学生报名时间为<span className="head_bold">2022年5月21日-6月19日</span>,请在报名截止时间(北京时间<span className="head_bold">2022年6月19日18点</span>)前提交报名信息。</div>
|
||||
<div className="head_content">1、学生报名时间为<span className="head_bold">2022年5月26日-6月24日</span>,请在报名截止时间(北京时间<span className="head_bold">2022年6月24日24点</span>)前提交报名信息。</div>
|
||||
<div className="head_content">2、本次GLCC夏令营基于GitLink代码托管平台完成编程任务,若您没有平台账户,请先注册后完成课题申请</div>
|
||||
<div className="head_content">3、每位学员最多可申请2个课题,至多有且仅有1个课题可以入选</div>
|
||||
<div className="head_content">4、建议在提交课题申请,与课题导师邮件沟通,详细了解课题描述、编码任务、技能要求&编程语言、预期产出结果等信息,以便您能够选择最适合的课题</div>
|
||||
|
@ -335,140 +357,148 @@ function Apply(props) {
|
|||
<div className="main-tit">
|
||||
<img src={studentSvg} /><span className="tit-text">学生报名</span>
|
||||
</div>
|
||||
<div className="form-tit">申请课题</div>
|
||||
<Form className="glcc_info_form" onSubmit={handleSubmit}>
|
||||
<h4 className="item-tit">基本信息</h4>
|
||||
{helper('学生姓名',
|
||||
'',
|
||||
'studentName',
|
||||
[{ required: true, message: "请正确输入学生姓名" },
|
||||
{ max: 32, message: '超出限制长度32位字符,请重新编辑' }],
|
||||
<Input placeholder="请输入学生姓名" onBlur={() => { verify("studentName") }} className={editable ? "" : "disabledInput"} disabled={editable ? false : true} />
|
||||
)}
|
||||
{helper('所在高校',
|
||||
'',
|
||||
'school',
|
||||
[{ required: true, message: "请正确输入所在高校" },
|
||||
{ max: 32, message: '超出限制长度32位字符,请重新编辑' }],
|
||||
<Input placeholder="请输入所在高校" onBlur={() => { verify("school") }} className={editable ? "" : "disabledInput"} disabled={editable ? false : true} />
|
||||
)}
|
||||
{helper('所学专业',
|
||||
'',
|
||||
'profession',
|
||||
[{ max: 32, message: '超出限制长度32位字符,请重新编辑' }],
|
||||
<Input placeholder="请输入所学专业" onBlur={() => { verify("profession") }} className={editable ? "" : "disabledInput"} disabled={editable ? false : true} />
|
||||
)}
|
||||
<Form.Item label="地区">
|
||||
{getFieldDecorator("location", {
|
||||
rules: []
|
||||
})(
|
||||
<Cascader placeholder="请选择省份城市" options={locData} className={editable ? "" : "disabledInput"} disabled={editable ? false : true} />
|
||||
<div className="form-content">
|
||||
<div className="form-tit">申请课题</div>
|
||||
<Form className="glcc_info_form" onSubmit={handleSubmit}>
|
||||
<h4 className="item-tit">基本信息</h4>
|
||||
{helper('学生姓名',
|
||||
'',
|
||||
'studentName',
|
||||
[{ required: true, message: "请正确输入学生姓名" },
|
||||
{ max: 32, message: '超出限制长度32位字符,请重新编辑' }],
|
||||
<Input placeholder="请输入学生姓名" onBlur={() => { verify("studentName") }} className={editable ? "" : "disabledInput"} disabled={editable ? false : true} />
|
||||
)}
|
||||
{helper('所在高校',
|
||||
'',
|
||||
'school',
|
||||
[{ required: true, message: "请正确输入所在高校" },
|
||||
{ max: 32, message: '超出限制长度32位字符,请重新编辑' }],
|
||||
<Input placeholder="请输入所在高校" onBlur={() => { verify("school") }} className={editable ? "" : "disabledInput"} disabled={editable ? false : true} />
|
||||
)}
|
||||
{helper('所学专业',
|
||||
'',
|
||||
'profession',
|
||||
[{ max: 32, message: '超出限制长度32位字符,请重新编辑' }],
|
||||
<Input placeholder="请输入所学专业" onBlur={() => { verify("profession") }} className={editable ? "" : "disabledInput"} disabled={editable ? false : true} />
|
||||
)}
|
||||
<Form.Item label="地区">
|
||||
{getFieldDecorator("location", {
|
||||
rules: []
|
||||
})(
|
||||
<Cascader popupClassName="glcc_cascader" placeholder="请选择省份城市" options={locData} className={editable ? "" : "disabledInput"} disabled={editable ? false : true} />
|
||||
)}
|
||||
</Form.Item>
|
||||
{helper('所在年级',
|
||||
'',
|
||||
'grade',
|
||||
[],
|
||||
<Select placeholder="请选择所在年级" disabled={editable ? false : true} className={editable ? "" : "disabledInput"} dropdownClassName="glcc_select">
|
||||
{gradeList.map(item => { return <Option value={item.name} key={item.id}>{item.name}</Option> })}
|
||||
</Select>
|
||||
)}
|
||||
{helper('联系电话',
|
||||
'',
|
||||
'phone',
|
||||
[{ required: true, message: "请正确输入联系电话" },
|
||||
{ max: 14, message: '超出限制长度14位字符,请重新编辑' },
|
||||
{ pattern: /^(^(\d{3,4}-)?\d{7,8})$|(13[0-9]{9})$/, message: '请正确输入联系电话' }],
|
||||
<Input placeholder="请输入联系电话" onBlur={() => { verify("phone") }} className={editable ? "" : "disabledInput"} disabled={editable ? false : true} />
|
||||
)}
|
||||
{helper('邮箱地址',
|
||||
'',
|
||||
'mail',
|
||||
[
|
||||
{ type: 'email', message: '请输入正确的邮箱地址', },
|
||||
{ required: true, message: "请输入邮箱地址" },
|
||||
{ max: 50, message: '超出限制长度50位字符,请重新编辑' },
|
||||
],
|
||||
<Input placeholder="请输入邮箱地址" onBlur={() => { verify("mail") }} className={editable ? "" : "disabledInput"} disabled={editable ? false : true} />
|
||||
)}
|
||||
</Form.Item>
|
||||
{helper('所在年级',
|
||||
'',
|
||||
'grade',
|
||||
[],
|
||||
<Select placeholder="请选择所在年级" disabled={editable ? false : true} className={editable ? "" : "disabledInput"}>
|
||||
{gradeList.map(item => { return <Option value={item.name} key={item.id}>{item.name}</Option> })}
|
||||
</Select>
|
||||
)}
|
||||
{helper('联系电话',
|
||||
'',
|
||||
'phone',
|
||||
[{ required: true, message: "请正确输入联系电话" }],
|
||||
<Input placeholder="请输入联系电话" onBlur={() => { verify("phone") }} className={editable ? "" : "disabledInput"} disabled={editable ? false : true} />
|
||||
)}
|
||||
{helper('邮箱地址',
|
||||
'',
|
||||
'mail',
|
||||
[{ required: true, message: "请正确输入邮箱地址" }],
|
||||
<Input placeholder="请输入邮箱地址" onBlur={() => { verify("mail") }} className={editable ? "" : "disabledInput"} disabled={editable ? false : true} />
|
||||
)}
|
||||
|
||||
<div className='introArea update_item'>{helper('学生证明',
|
||||
'请以图片的形式上传学生证明,格式为png、jpg、jpeg',
|
||||
'proveAttachmentId',
|
||||
[{ required: true, message: "请正确上传学生证明" }],
|
||||
userApplyInfo && userApplyInfo.proveAttachmentId ? <div className='projectLogo'><img src={`${getUrl()}/api/attachments/${userApplyInfo.proveAttachmentId}`} alt='' /></div>
|
||||
: <Upload
|
||||
listType="picture-card"
|
||||
className="avatar-uploader"
|
||||
showUploadList={false}
|
||||
action={getUploadActionUrl()}
|
||||
accept=".png,.jpg,.jpeg"
|
||||
beforeUpload={beforeUpload}
|
||||
onChange={handleChange}
|
||||
>
|
||||
{imageUrl ? <img src={imageUrl} alt="avatar" style={{ maxWidth: '100px', maxHeight: '100px' }} /> : <div>
|
||||
<i className='iconfont icon-tianjiadaohang font-30'></i>
|
||||
<div className="ant-upload-text font-13">请上传学生证明</div>
|
||||
</div>}
|
||||
</Upload>
|
||||
)}</div>
|
||||
<div className='introArea update_item'>{helper('学生证明',
|
||||
'请以图片的形式上传学生证明,大小不超过5M,格式为png、jpg、jpeg',
|
||||
'proveAttachmentId',
|
||||
[{ required: true, message: "请正确上传学生证明" }],
|
||||
userApplyInfo && userApplyInfo.proveAttachmentId ? <div className='projectLogo'><img src={`${getUrl()}/api/attachments/${userApplyInfo.proveAttachmentId}`} alt='' /></div>
|
||||
: <Upload
|
||||
listType="picture-card"
|
||||
className="avatar-uploader"
|
||||
showUploadList={false}
|
||||
action={getUploadActionUrl()}
|
||||
accept=".png,.jpg,.jpeg"
|
||||
beforeUpload={beforeUpload}
|
||||
onChange={handleChange}
|
||||
>
|
||||
{imageUrl ? <img src={imageUrl} alt="avatar" style={{ maxWidth: '100px', maxHeight: '100px' }} /> : <div>
|
||||
<i className='iconfont icon-tianjiadaohang font-30'></i>
|
||||
<div className="ant-upload-text font-13">请上传学生证明</div>
|
||||
</div>}
|
||||
</Upload>
|
||||
)}</div>
|
||||
|
||||
{
|
||||
myTaskList.map((item, i) => {
|
||||
return (
|
||||
<Fragment key={item.taskId + '' + i}>
|
||||
<h4 className="item-tit">课题信息(<span className="item-tit-num">{i + 1}</span>/2) {myTaskList.length > 1 && <i className="iconfont icon-shanchu" onClick={() => { deleteTask(i) }}></i>}</h4>
|
||||
{helper('课题名称',
|
||||
'',
|
||||
'taskId' + i,
|
||||
[{ required: true, message: "请选择课题名称" }],
|
||||
<Select
|
||||
placeholder="请选择课题名称"
|
||||
showSearch
|
||||
disabled={editable ? false : true}
|
||||
className={editable ? "" : "disabledInput"}
|
||||
filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) > -1}
|
||||
onBlur={() => { verify("taskId" + i) }}
|
||||
onChange={(val) => { changeTaskItem("taskId", val, i) }}
|
||||
>
|
||||
{allTaskList.map(item => { return <Option value={item.id} key={item.id + ''}>{item.taskName}</Option> })}
|
||||
</Select>
|
||||
)}
|
||||
<div className='introArea'>{helper('自荐书',
|
||||
'',
|
||||
'memo' + i,
|
||||
[{ required: true, message: "请正确输入自荐书" },
|
||||
{ max: 500, message: '超出限制长度500位字符,请重新编辑' }],
|
||||
<TextArea placeholder="请填写你的优势和能力,不超过500字" onBlur={(e) => { verify("memo" + i); changeTaskItem("memo", e.currentTarget.value, i) }} rows={4} className={editable ? "" : "disabledInput"} />
|
||||
)}</div>
|
||||
{
|
||||
myTaskList.map((item, i) => {
|
||||
return (
|
||||
<Fragment key={item.taskId + '' + i}>
|
||||
<h4 className="item-tit">课题信息(<span className="item-tit-num">{i + 1}</span>/2) {myTaskList.length > 1 && <span className="delete"><i className="iconfont icon-shanchu mr5" onClick={() => { deleteTask(i) }}></i>删除</span>}</h4>
|
||||
{helper('课题名称',
|
||||
'',
|
||||
'taskId' + i,
|
||||
[{ required: true, message: "请选择课题名称" }],
|
||||
<Select
|
||||
placeholder="请选择课题名称"
|
||||
showSearch
|
||||
disabled={editable ? false : true}
|
||||
className={editable ? "" : "disabledInput"}
|
||||
filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) > -1}
|
||||
onBlur={() => { verify("taskId" + i) }}
|
||||
onChange={(val) => { changeTaskItem("taskId", val, i) }}
|
||||
>
|
||||
{allTaskList.map(item => { return <Option value={item.id} disabled={chooseArr.includes(item.id)} key={item.id + ''}>{item.taskName}</Option> })}
|
||||
</Select>
|
||||
)}
|
||||
<div className='introArea'>{helper('自荐书',
|
||||
<div className="memoExtra">自荐书示例:<span >https://forum.gitlink.org.cn/forums/7299/detail</span></div>,
|
||||
'memo' + i,
|
||||
[{ required: true, message: "请正确输入自荐书" },
|
||||
{ max: 500, message: '超出限制长度500位字符,请重新编辑' }],
|
||||
<TextArea placeholder="请填写你的优势和能力,不超过500字" onBlur={(e) => { verify("memo" + i); changeTaskItem("memo", e.currentTarget.value, i) }} rows={4} className={editable ? "memoText" : "memoText disabledInput"} />
|
||||
)}</div>
|
||||
|
||||
<div className='introArea'>{helper('附件',
|
||||
'',
|
||||
'memoAttachmentId' + i,
|
||||
[],
|
||||
<Upload
|
||||
// action={getUploadActionUrl()}
|
||||
action={httpUrl + `/busiAttachments/upload`}
|
||||
fileList={i ? files1 : files}
|
||||
onChange={(info) => { bookChange(info, i) }}
|
||||
onDownload={download}
|
||||
showUploadList={{
|
||||
showDownloadIcon: true,
|
||||
downloadIcon: <i className="iconfont icon-xiazai-icon"></i>,
|
||||
showRemoveIcon: true
|
||||
}}
|
||||
>
|
||||
<div className="glcc_btn">上传自荐书</div>
|
||||
</Upload>
|
||||
)}</div>
|
||||
</Fragment>
|
||||
)
|
||||
})
|
||||
}
|
||||
<div className='introArea'>{helper('附件',
|
||||
'可以上传自荐书文件,大小不超过5M',
|
||||
'memoAttachmentId' + i,
|
||||
[],
|
||||
<Upload
|
||||
// action={getUploadActionUrl()}
|
||||
action={httpUrl + `/busiAttachments/upload`}
|
||||
fileList={i ? files1 : files}
|
||||
onChange={(info) => { bookChange(info, i) }}
|
||||
onDownload={download}
|
||||
beforeUpload={beforeUpload2}
|
||||
showUploadList={{
|
||||
showDownloadIcon: true,
|
||||
downloadIcon: <i className="iconfont icon-xiazai-icon"></i>,
|
||||
showRemoveIcon: true
|
||||
}}
|
||||
>
|
||||
<div className="glcc_btn">上传自荐书</div>
|
||||
</Upload>
|
||||
)}</div>
|
||||
</Fragment>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
{
|
||||
myTaskList.length < 2 && <Button className="add_task" type="primary" onClick={() => { addTask() }}>添加课题</Button>
|
||||
}
|
||||
|
||||
<Form.Item className='subInfo introArea'>
|
||||
<Button type="primary" htmlType="submit" className='sub' disabled={!editable}>提交</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
{
|
||||
myTaskList.length < 2 && <Button className="add_task" type="primary" onClick={() => { addTask() }}>添加课题</Button>
|
||||
}
|
||||
|
||||
<Form.Item className='subInfo introArea'>
|
||||
<Button type="primary" htmlType="submit" className='sub' disabled={!editable}>提交</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</Spin>
|
||||
</div>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
.has-error .ant-form-split {
|
||||
position: absolute;
|
||||
}
|
||||
.ant-btn-primary{
|
||||
.ant-btn-primary {
|
||||
background-color: #466aff;
|
||||
border-color: #466aff;
|
||||
&:hover {
|
||||
|
@ -90,10 +90,14 @@
|
|||
background-color: #466aff;
|
||||
}
|
||||
|
||||
.item-tit-num{
|
||||
color:#466aff;
|
||||
.item-tit-num {
|
||||
color: #466aff;
|
||||
}
|
||||
}
|
||||
.form-content {
|
||||
background-color: rgba(255, 255, 255, 0.27);
|
||||
border: 1px solid #fff;
|
||||
}
|
||||
.glcc_info_form {
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
@ -137,6 +141,7 @@
|
|||
.ant-upload.ant-upload-select-picture-card {
|
||||
background: none !important;
|
||||
border-color: #b3c3db;
|
||||
color: #202d40;
|
||||
&:hover {
|
||||
border-color: #466aff;
|
||||
}
|
||||
|
@ -163,17 +168,16 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
.ant-cascader-picker{
|
||||
.ant-cascader-picker {
|
||||
background: none;
|
||||
&:hover{
|
||||
.ant-cascader-input{
|
||||
border:1px solid #466aff;
|
||||
&:hover {
|
||||
.ant-cascader-input {
|
||||
border: 1px solid #466aff;
|
||||
}
|
||||
}
|
||||
&:focus .ant-cascader-input {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
}
|
||||
.introArea {
|
||||
width: 100%;
|
||||
|
@ -192,8 +196,8 @@
|
|||
color: #e90000;
|
||||
}
|
||||
}
|
||||
.update_item{
|
||||
.ant-form-explain{
|
||||
.update_item {
|
||||
.ant-form-explain {
|
||||
bottom: 17px;
|
||||
}
|
||||
}
|
||||
|
@ -258,24 +262,54 @@
|
|||
line-height: 30px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
&:hover{
|
||||
opacity: .8;
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.add_task{
|
||||
margin-left:90px;
|
||||
.add_task {
|
||||
margin-left: 90px;
|
||||
}
|
||||
|
||||
.icon-shanchu{
|
||||
.delete {
|
||||
float: right;
|
||||
color: #466aff;
|
||||
cursor: pointer;
|
||||
:hover{
|
||||
:hover {
|
||||
color: #1140ff;
|
||||
}
|
||||
.icon-shanchu {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
}
|
||||
// .icon-shanchu{
|
||||
// color: #466aff;
|
||||
// cursor: pointer;
|
||||
// :hover{
|
||||
// color: #1140ff;
|
||||
// }
|
||||
// }
|
||||
.memoExtra {
|
||||
margin-top: 12px;
|
||||
margin-bottom: -10px;
|
||||
span {
|
||||
color: #466aff;
|
||||
}
|
||||
}
|
||||
|
||||
.memoText {
|
||||
padding: 9px 11px;
|
||||
line-height: 1.75;
|
||||
}
|
||||
|
||||
}
|
||||
.glcc-banner {
|
||||
width: 100%;
|
||||
}
|
||||
.glcc_select,.ant-cascader-menus.glcc_cascader{
|
||||
background-image: linear-gradient(180deg, #eef3ff 0%, #ffffff 100%);
|
||||
border: 1px solid #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0px 0px 12px rgba(203, 220, 255, 0.47);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue