Merge branch 'gitlink_server' into pre_gitlink_server
|
@ -1,8 +1,8 @@
|
|||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 2340181 */
|
||||
src: url('iconfont.woff2?t=1651041108151') format('woff2'),
|
||||
url('iconfont.woff?t=1651041108151') format('woff'),
|
||||
url('iconfont.ttf?t=1651041108151') format('truetype');
|
||||
src: url('iconfont.woff2?t=1652927945088') format('woff2'),
|
||||
url('iconfont.woff?t=1652927945088') format('woff'),
|
||||
url('iconfont.ttf?t=1652927945088') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
|
@ -13,6 +13,62 @@
|
|||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-bangzhuzhongxinicon1:before {
|
||||
content: "\e935";
|
||||
}
|
||||
|
||||
.icon-ketixiangqingicon:before {
|
||||
content: "\e92b";
|
||||
}
|
||||
|
||||
.icon-bianjiicon:before {
|
||||
content: "\e92c";
|
||||
}
|
||||
|
||||
.icon-baomingxiangqingicon:before {
|
||||
content: "\e92d";
|
||||
}
|
||||
|
||||
.icon-sousuo4:before {
|
||||
content: "\e92e";
|
||||
}
|
||||
|
||||
.icon-quxiaoshenqingicon:before {
|
||||
content: "\e92f";
|
||||
}
|
||||
|
||||
.icon-xiala3:before {
|
||||
content: "\e930";
|
||||
}
|
||||
|
||||
.icon-tianjia_tianchong:before {
|
||||
content: "\e931";
|
||||
}
|
||||
|
||||
.icon-a-shanchu2:before {
|
||||
content: "\e932";
|
||||
}
|
||||
|
||||
.icon-shanchuicon3:before {
|
||||
content: "\e933";
|
||||
}
|
||||
|
||||
.icon-shenqingketiicon:before {
|
||||
content: "\e934";
|
||||
}
|
||||
|
||||
.icon-weixin:before {
|
||||
content: "\e92a";
|
||||
}
|
||||
|
||||
.icon-zanwushuju:before {
|
||||
content: "\e928";
|
||||
}
|
||||
|
||||
.icon-fuwuicon:before {
|
||||
content: "\e929";
|
||||
}
|
||||
|
||||
.icon-personal-center:before {
|
||||
content: "\e927";
|
||||
}
|
||||
|
|
|
@ -5,6 +5,104 @@
|
|||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "29811507",
|
||||
"name": "帮助中心icon",
|
||||
"font_class": "bangzhuzhongxinicon1",
|
||||
"unicode": "e935",
|
||||
"unicode_decimal": 59701
|
||||
},
|
||||
{
|
||||
"icon_id": "29701613",
|
||||
"name": "课题详情icon",
|
||||
"font_class": "ketixiangqingicon",
|
||||
"unicode": "e92b",
|
||||
"unicode_decimal": 59691
|
||||
},
|
||||
{
|
||||
"icon_id": "29701614",
|
||||
"name": "编辑icon",
|
||||
"font_class": "bianjiicon",
|
||||
"unicode": "e92c",
|
||||
"unicode_decimal": 59692
|
||||
},
|
||||
{
|
||||
"icon_id": "29701615",
|
||||
"name": "报名详情icon",
|
||||
"font_class": "baomingxiangqingicon",
|
||||
"unicode": "e92d",
|
||||
"unicode_decimal": 59693
|
||||
},
|
||||
{
|
||||
"icon_id": "29701616",
|
||||
"name": "搜索",
|
||||
"font_class": "sousuo4",
|
||||
"unicode": "e92e",
|
||||
"unicode_decimal": 59694
|
||||
},
|
||||
{
|
||||
"icon_id": "29701617",
|
||||
"name": "取消申请icon",
|
||||
"font_class": "quxiaoshenqingicon",
|
||||
"unicode": "e92f",
|
||||
"unicode_decimal": 59695
|
||||
},
|
||||
{
|
||||
"icon_id": "29701618",
|
||||
"name": "下拉",
|
||||
"font_class": "xiala3",
|
||||
"unicode": "e930",
|
||||
"unicode_decimal": 59696
|
||||
},
|
||||
{
|
||||
"icon_id": "29701619",
|
||||
"name": "添加_填充",
|
||||
"font_class": "tianjia_tianchong",
|
||||
"unicode": "e931",
|
||||
"unicode_decimal": 59697
|
||||
},
|
||||
{
|
||||
"icon_id": "29701620",
|
||||
"name": "删除 (2)",
|
||||
"font_class": "a-shanchu2",
|
||||
"unicode": "e932",
|
||||
"unicode_decimal": 59698
|
||||
},
|
||||
{
|
||||
"icon_id": "29701621",
|
||||
"name": "删除icon",
|
||||
"font_class": "shanchuicon3",
|
||||
"unicode": "e933",
|
||||
"unicode_decimal": 59699
|
||||
},
|
||||
{
|
||||
"icon_id": "29701622",
|
||||
"name": "申请课题icon",
|
||||
"font_class": "shenqingketiicon",
|
||||
"unicode": "e934",
|
||||
"unicode_decimal": 59700
|
||||
},
|
||||
{
|
||||
"icon_id": "29665762",
|
||||
"name": "微信",
|
||||
"font_class": "weixin",
|
||||
"unicode": "e92a",
|
||||
"unicode_decimal": 59690
|
||||
},
|
||||
{
|
||||
"icon_id": "29467269",
|
||||
"name": "暂无数据",
|
||||
"font_class": "zanwushuju",
|
||||
"unicode": "e928",
|
||||
"unicode_decimal": 59688
|
||||
},
|
||||
{
|
||||
"icon_id": "29467272",
|
||||
"name": "服务icon",
|
||||
"font_class": "fuwuicon",
|
||||
"unicode": "e929",
|
||||
"unicode_decimal": 59689
|
||||
},
|
||||
{
|
||||
"icon_id": "29206429",
|
||||
"name": "personal-center",
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
<meta name=”Keywords” Content=”issue,bug,tracker,软件工程,课程实践″>
|
||||
<meta name=”Description” Content=”持续构建协同、共享、可信的软件创建生态开源创作与软件生产相结合,支持大规模群体开展软件协同创新活动”>
|
||||
<meta name="theme-color" content="#000000">
|
||||
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests" />
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/iconfont.css">
|
||||
|
|
|
@ -125,7 +125,7 @@ const Glcc = Loadable({
|
|||
// })
|
||||
|
||||
// 此处仅维护前端可能的一级路由,不用进行项目或者组织判断的字段。
|
||||
const keyWord = ["explore", "settings", "setting", "mulan", "wiki", "issues", "setting", "trending", "code", "projects", "pulls", "mine", "login", "register", "email", "export", "nopage", "404", "403", "500", "501", "search", "organize", "login", "register", "resetPassword", "aboutus","educoder"];
|
||||
const keyWord = ["explore", "settings", "setting", "mulan", "wiki", "issues", "setting", "trending", "code", "projects", "pulls", "mine", "login", "register", "email", "export", "nopage", "404", "403", "500", "501", "search", "organize", "login", "register", "resetPassword", "aboutus","educoder", "glcc"];
|
||||
|
||||
class App extends Component {
|
||||
constructor(props) {
|
||||
|
@ -292,7 +292,7 @@ class App extends Component {
|
|||
<ConfigProvider locale={zhCN}>
|
||||
<MuiThemeProvider theme={theme}>
|
||||
<LoginDialog {...this.props} {...this.state} Modifyloginvalue={() => this.Modifyloginvalue()}></LoginDialog>
|
||||
{pathName && pathName.indexOf("glcc") === -1 ? <SiderBar /> : <SiderBarHelp/>}
|
||||
{!pathName || (pathName && pathName.indexOf("glcc") === -1) ? <SiderBar /> : <SiderBarHelp/>}
|
||||
{/* <Router> */}
|
||||
<Switch>
|
||||
{/* wiki预览 */}
|
||||
|
|
|
@ -11,7 +11,7 @@ function SystemNotice({showNotice,system_notification,history,login,hideSystemNo
|
|||
|
||||
useEffect(()=>{
|
||||
|
||||
if(system_notification && !system_notification.is_read && showNotice && login){
|
||||
if((system_notification && !system_notification.is_read) && showNotice && login){
|
||||
setVisible(true);
|
||||
}
|
||||
},[system_notification,login])
|
||||
|
@ -22,11 +22,12 @@ function SystemNotice({showNotice,system_notification,history,login,hideSystemNo
|
|||
axios.post(url,{
|
||||
system_notification_id:system_notification.id
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
setVisible(false);
|
||||
hideSystemNotice();
|
||||
}
|
||||
}).catch(error=>{})
|
||||
setVisible(false);
|
||||
hideSystemNotice();
|
||||
}).catch(error=>{
|
||||
setVisible(false);
|
||||
hideSystemNotice();
|
||||
})
|
||||
}else{
|
||||
setVisible(false);
|
||||
hideSystemNotice();
|
||||
|
|
|
@ -11,8 +11,8 @@ function CoderDepotReadme({ operate , history , readme , ChangeFile }){
|
|||
const [ content ,setContent ] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
if(readme && readme.content){
|
||||
setContent(readme.content);
|
||||
if(readme && readme.replace_content){
|
||||
setContent(readme.replace_content);
|
||||
}else{
|
||||
setContent(undefined);
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ class CoderRootFileDetail extends Component {
|
|||
value: undefined,
|
||||
language: undefined,
|
||||
languages: undefined,
|
||||
description: props.detail.content,
|
||||
description: props.detail.replace_content,
|
||||
menuList:undefined
|
||||
};
|
||||
}
|
||||
|
@ -36,12 +36,12 @@ class CoderRootFileDetail extends Component {
|
|||
};
|
||||
|
||||
componentDidUpdate=(prevProps)=>{
|
||||
const { content } = this.props && this.props.detail;
|
||||
const prevcontent = prevProps.detail && prevProps.detail.content;
|
||||
if (content && prevcontent) {
|
||||
if (prevcontent !== content){
|
||||
const { replace_content } = this.props && this.props.detail;
|
||||
const prevcontent = prevProps.detail && prevProps.detail.replace_content;
|
||||
if (replace_content && prevcontent) {
|
||||
if (prevcontent !== replace_content){
|
||||
this.setState({
|
||||
description: content
|
||||
description: replace_content
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -319,7 +319,7 @@ class CoderRootFileDetail extends Component {
|
|||
{...this.state}
|
||||
language={language ? language : "javascript"}
|
||||
filepath={`/${detail.path}`}
|
||||
content={description}
|
||||
content={detail.content}
|
||||
readOnly={readOnly}
|
||||
editorType="update"
|
||||
currentBranch={currentBranch}
|
||||
|
|
|
@ -129,6 +129,10 @@ const Source = Loadable({
|
|||
loader: () => import('../Source/Index'),
|
||||
loading: Loading,
|
||||
})
|
||||
const Server = Loadable({
|
||||
loader: () => import('../Server/Index'),
|
||||
loading: Loading,
|
||||
})
|
||||
const DevIndex = Loadable({
|
||||
loader: () => import('../DevOps/Index'),
|
||||
loading: Loading,
|
||||
|
@ -168,6 +172,8 @@ function checkPathname(projectsId, owner, pathname) {
|
|||
name = "source"
|
||||
} else if (url.indexOf(`/wiki`) > -1) {
|
||||
name = "wiki"
|
||||
} else if (url.indexOf(`/server`) > -1) {
|
||||
name = "server"
|
||||
}
|
||||
}
|
||||
return name;
|
||||
|
@ -651,6 +657,12 @@ class Detail extends Component {
|
|||
:
|
||||
<Spin spinning={secondSync} className="spinstyle" tip="正在同步镜像" size="large">
|
||||
<Switch {...this.props}>
|
||||
{/* 服务 */}
|
||||
<Route path="/:owner/:projectsId/server"
|
||||
render={
|
||||
() => (<Server {...this.props} {...this.state} {...common} />)
|
||||
}
|
||||
></Route>
|
||||
{/* 资源 */}
|
||||
<Route path="/:owner/:projectsId/source"
|
||||
render={
|
||||
|
|
|
@ -228,14 +228,14 @@
|
|||
li{
|
||||
text-align: center;
|
||||
padding:0px;
|
||||
margin-right: 40px;
|
||||
margin-right: 35px;
|
||||
display: flex;
|
||||
& > a{
|
||||
position: relative;
|
||||
font-size: 14px;
|
||||
height: 36px;
|
||||
line-height: 24px;
|
||||
display: block;
|
||||
display: flex;
|
||||
color: #000!important;
|
||||
&> span.num{
|
||||
line-height: 24px;
|
||||
|
|
|
@ -8,11 +8,13 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa
|
|||
useEffect(()=>{
|
||||
if(list){
|
||||
// 没有资源库banner但是通过连接进资源库页面时
|
||||
if(pathname && pathname==="source"){
|
||||
let a = list.filter(item=>item.menu_name === "resources");
|
||||
if(a && a.length === 0){
|
||||
history.push(`/${owner}/${projectsId}`);
|
||||
}
|
||||
let a = list.filter(item=>item.menu_name === "resources");
|
||||
if((pathname && pathname==="source") && (a && a.length === 0)){
|
||||
history.push(`/${owner}/${projectsId}`);
|
||||
}
|
||||
let b = list.filter(item=>item.menu_name === "server");
|
||||
if((pathname && pathname.indexOf("server") > 0) && (b && b.length === 0)){
|
||||
history.push(`/${owner}/${projectsId}`);
|
||||
}
|
||||
setMenuName(list);
|
||||
}
|
||||
|
@ -106,6 +108,15 @@ function DetailBanner({ history,list , owner , projectsId , isManager , url , pa
|
|||
</Link>
|
||||
</li>
|
||||
}
|
||||
{
|
||||
item.menu_name === "services" &&
|
||||
<li className={pathname === "server" ? "active" : ""}>
|
||||
<Link to={{ pathname: `/${owner}/${projectsId}/server`, state }}>
|
||||
<i className={"iconfont icon-fuwuicon color-grey-3 mr5 font-15"} style={{marginTop:"1px"}}></i>
|
||||
<span>服务</span>
|
||||
</Link>
|
||||
</li>
|
||||
}
|
||||
{
|
||||
item.menu_name === "activity" &&
|
||||
<li className={pathname==="activity" ? "active" : ""}>
|
||||
|
|
|
@ -33,7 +33,7 @@ export default Form.create()(
|
|||
const [fileList, setFileList] = useState(undefined);
|
||||
const [attachment, setAttachment] = useState(undefined);
|
||||
const [options , setOptions] = useState(undefined);
|
||||
const stable = history && history.location && history.location.state.stable;
|
||||
const stable = history && history.location && history.location.state&&history.location.state.stable;
|
||||
const { projectsId, versionId , owner } = match.params;
|
||||
|
||||
useEffect(()=>{
|
||||
|
|
|
@ -4,8 +4,9 @@ import User from '../Component/User';
|
|||
import { truncateCommitId } from '../common/util';
|
||||
import { getImageUrl } from 'educoder';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Pagination } from 'antd';
|
||||
|
||||
function Commits({ commits , projectsId , owner }){
|
||||
function Commits({ commits , projectsId , owner , total ,limit , changeCommitFunc }){
|
||||
return(
|
||||
<div className="pb20">
|
||||
{
|
||||
|
@ -29,6 +30,12 @@ function Commits({ commits , projectsId , owner }){
|
|||
)
|
||||
})
|
||||
}
|
||||
{
|
||||
total > limit &&
|
||||
<div style={{textAlign:"center",paddingTop:"20px"}}>
|
||||
<Pagination pageSize={limit} total={total} onChange={(page)=>changeCommitFunc(page)}/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ import './merge.css';
|
|||
* 3、合并请求列表页(新建、无数据时的提示),仓库相同,源、目标都为默认分支,owner/projectId/compare
|
||||
* 4、新建页面,切换分支、切换目标仓库、刷新页面等,存在所有可能情况
|
||||
*/
|
||||
const limit = 15;
|
||||
|
||||
function getBranchParams(pathname) {
|
||||
const result = {
|
||||
// 目标仓库所有者
|
||||
|
@ -152,7 +154,7 @@ class CreateMerge extends Component {
|
|||
let getbranch =await this.getBranchList(mergeOwner,projectId,pullBranch,mergeBranch,"merge");
|
||||
let checkpull =await this.getBranchList(pullOwner,projectId,pullBranch,mergeBranch,"pull");
|
||||
if(getbranch && checkpull){
|
||||
this.compareProject(mergeOwner === pullOwner, branchParams);
|
||||
this.compareProject(mergeOwner === pullOwner, branchParams,1);
|
||||
}else{
|
||||
this.setState({
|
||||
isSpin: false,isCompareSpin: false
|
||||
|
@ -161,7 +163,7 @@ class CreateMerge extends Component {
|
|||
}
|
||||
|
||||
// compare接口,获取分支对比信息
|
||||
compareProject = (sameProject, branchParams) => {
|
||||
compareProject = (sameProject, branchParams ,page) => {
|
||||
const { pullOwner, pullBranch, mergeOwner, mergeBranch, projectId } = branchParams;
|
||||
let url = `/${mergeOwner}/${projectId}/compare`;
|
||||
if (sameProject) {
|
||||
|
@ -170,7 +172,9 @@ class CreateMerge extends Component {
|
|||
url += `/${Base64.encode(returnbar(mergeBranch))}...${pullOwner}/${projectId}:${Base64.encode(returnbar(pullBranch))}.json`;
|
||||
}
|
||||
axios
|
||||
.get(url)
|
||||
.get(url,{
|
||||
params:{limit,page}
|
||||
})
|
||||
.then((result) => {
|
||||
if (result) {
|
||||
if (result.data.status === 0) {
|
||||
|
@ -185,7 +189,7 @@ class CreateMerge extends Component {
|
|||
}
|
||||
this.setState({
|
||||
comparesData: result.data,
|
||||
isSpin: false,isCompareSpin: false
|
||||
isSpin: false,isCompareSpin: false,isFirstLoading:false
|
||||
});
|
||||
}
|
||||
})
|
||||
|
@ -354,6 +358,15 @@ class CreateMerge extends Component {
|
|||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
changeCommitFunc=(page)=>{
|
||||
this.setState({
|
||||
isSpin:true
|
||||
})
|
||||
const branchParams = getBranchParams(this.props.location.pathname);
|
||||
const { mergeOwner , pullOwner} = branchParams;
|
||||
this.compareProject(mergeOwner === pullOwner, branchParams,page);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
data,
|
||||
|
@ -465,6 +478,8 @@ class CreateMerge extends Component {
|
|||
{...this.props}
|
||||
merge={merge}
|
||||
pull={pull}
|
||||
limit = {limit}
|
||||
changeCommitFunc={this.changeCommitFunc}
|
||||
comparesData={comparesData}
|
||||
></MergeFooter>
|
||||
)}
|
||||
|
|
|
@ -22,9 +22,14 @@ class MergeFooter extends Component {
|
|||
});
|
||||
};
|
||||
|
||||
changeCommitFunc=(page)=>{
|
||||
const { changeCommitFunc } = this.props;
|
||||
changeCommitFunc&& changeCommitFunc(page);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { projectsId, owner } = this.props.match.params;
|
||||
const { comparesData = {} } = this.props;
|
||||
const { comparesData = {} ,limit } = this.props;
|
||||
const { commits, diff, commits_count } = comparesData;
|
||||
const { activeKey } = this.state;
|
||||
|
||||
|
@ -53,6 +58,9 @@ class MergeFooter extends Component {
|
|||
<Commits
|
||||
{...this.props}
|
||||
commits={commits}
|
||||
total={commits_count}
|
||||
limit={limit}
|
||||
changeCommitFunc={this.changeCommitFunc}
|
||||
projectsId={projectsId}
|
||||
owner={owner}
|
||||
></Commits>
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
import React from 'react';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
import "./index.scss";
|
||||
import Loadable from 'react-loadable';
|
||||
import Loading from '../../Loading';
|
||||
|
||||
|
||||
const List = Loadable({
|
||||
loader: () => import('./List'),
|
||||
loading: Loading,
|
||||
})
|
||||
const Data = Loadable({
|
||||
loader: () => import('./data'),
|
||||
loading: Loading,
|
||||
})
|
||||
function ServerIndex(props){
|
||||
return(
|
||||
<div className="panels">
|
||||
<Switch {...props}>
|
||||
<Route path="/:owner/:projectsId/server/:id"
|
||||
render={
|
||||
() => (<Data {...props}/>)
|
||||
}
|
||||
></Route>
|
||||
<Route path="/:owner/:projectsId/server"
|
||||
render={
|
||||
() => (<List {...props}/>)
|
||||
}
|
||||
></Route>
|
||||
</Switch>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default ServerIndex;
|
|
@ -0,0 +1,48 @@
|
|||
import React,{useState , useEffect} from 'react';
|
||||
import AgreementModal from './agreementModal';
|
||||
|
||||
function Main(props){
|
||||
const [ visible , setVisible ] = useState(false);
|
||||
const { owner , projectsId } = props.match.params;
|
||||
const [ has_trace_user , setHas_trace_user ] = useState(false);
|
||||
|
||||
const { current_user , resetUserInfo } = props;
|
||||
|
||||
useEffect(()=>{
|
||||
if(current_user){
|
||||
setHas_trace_user(current_user.has_trace_user);
|
||||
}
|
||||
},[current_user])
|
||||
|
||||
function onOk(){
|
||||
setVisible(false);
|
||||
resetUserInfo && resetUserInfo();
|
||||
props.history.push(`/${owner}/${projectsId}/server/1`);
|
||||
}
|
||||
|
||||
function openDetail(){
|
||||
if(!has_trace_user){
|
||||
setVisible(true);
|
||||
}else{
|
||||
props.history.push(`/${owner}/${projectsId}/server/1`);
|
||||
}
|
||||
}
|
||||
return(
|
||||
<div>
|
||||
<AgreementModal visible={visible} onCancel={()=>setVisible(false)} onOk={onOk}/>
|
||||
<ul className="serverlist">
|
||||
<li>
|
||||
<span className="servername">
|
||||
<img src={require('./img/logo.png')} alt=""/>
|
||||
<a onClick={openDetail}>重晴鸟代码溯源系统</a>
|
||||
</span>
|
||||
<p className="task-hide-2 serverdesc">支持软件源代码的溯源分析、自主度评估、开源漏洞检测、开源许可证合规性分析等</p>
|
||||
<span className="serverbtn">
|
||||
<a onClick={openDetail} className="btnhover">查看详情</a>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Main;
|
|
@ -0,0 +1,65 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Modal , Checkbox } from 'antd';
|
||||
import axios from 'axios';
|
||||
|
||||
|
||||
function AgreementModal({ visible , onCancel , onOk}){
|
||||
const [ agree , setAgree ] = useState(false);
|
||||
function changeAgree(e){
|
||||
setAgree(e.target.checked);
|
||||
}
|
||||
// 不同意
|
||||
function unAgree(){
|
||||
setAgree(false);
|
||||
onCancel();
|
||||
}
|
||||
// 同意协议
|
||||
function agreeFunc(){
|
||||
const url = `/traces/trace_users.json`;
|
||||
axios.post(url).then(result=>{
|
||||
if(result){
|
||||
agree && onOk();
|
||||
setAgree(false);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
return(
|
||||
<Modal
|
||||
visible={visible}
|
||||
title="用户协议及声明条款"
|
||||
onCancel={onCancel}
|
||||
onOk={onOk}
|
||||
className="agreementModal"
|
||||
width="700px"
|
||||
footer={
|
||||
<div className="agreeBtn">
|
||||
<a onClick={unAgree}>不同意</a>
|
||||
<a className={agree ? "agree btnhover" : 'notagree'} onClick={ agreeFunc}>同意协议</a>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="agreementCon">
|
||||
<div className="agreementDesc">
|
||||
<p>在使用本系统前,请您务必仔细阅读下列条款: 本系统的资料、信息及其他内容均由国家超级计算无锡中心、中国科学院计算技术研究所、清华大学和北京大学提供,任何人进入本系统、阅读本系统所载任何内容、从本系统下载任何资料或使用本系统提供的任何资料,即表示同意遵守这些条款。如果您不同意遵守这些条款,请勿继续使用本系统。提供方有权对这些条款不时进行更新,前述更新之后的条款将在本系统进行公布,并自公布之日起生效。</p>
|
||||
<p className="blod">版权声明</p>
|
||||
<p>本系统所提供任何内容,包括但不限于数据、文字、图表或图象,其所有权、著作权及其他权利均由提供方享有。前述权利受中华人民共和国的法律、法规、规章、规范性法律文件及相关国际条约的保护;对前述权利的任何侵犯行为均有可能导致民事、行政或刑事责任,一切后果均由侵权行为人承担。未经提供方事先书面许可,您不得以任何方式复制、修改、传播、出版、转载或展示本系统的任何内容。</p>
|
||||
<p>本系统采用但非本系统原创的所有内容,其版权归版权所有人所有;转载的部分内容源自其他系统,因无法和版权权利人联系,如果您是前述内容的版权权利人,请与我们联系并提供证明材料和详实说明,我们将及时加注版权信息;如果您提出异议,我们将立即撤除任何涉及版权问题的内容。对于独家授权本系统提供的任何内容,如您需要转载,应当事先得到本系统和版权所有人的同意。</p>
|
||||
<p className="blod">引用本系统内容</p>
|
||||
<p>1. 在遵守国家法律以及本协议的情况下,您可以出于非商业目的浏览、下载本系统的内容。如出于商业目的使用(如:拷贝、下载、存储、通过硬盘拷贝或电子抓取系统发送、转换、演示、传播、出版本系统的任何内容,或创造与前述内容有关的演绎作品或衍生产品),则必须事先经过提供方的书面许可,并在使用时注明来源和版权标记。</p>
|
||||
<p>2. 如果您需要使用本系统包含的由任何其他主体提供的内容,请直接与版权权利人联系。</p>
|
||||
<p className="blod">免责条款</p>
|
||||
<p>对本系统的内容,我们已尽最大努力审核,但对于内容的正确性、完整性、及时性、有效性、稳定性、可用性、不侵犯他人权利等方面,不提供任何形式(无论明示或默示)的保证。我们不保证服务器的稳定性,任何由于黑客入侵或攻击、计算机病毒侵入或发作、因政府管制而造成的暂时性关闭等影响网络正常经营的不可抗力而造成的资料泄露、丢失、被盗用或被篡改等,本系统均得免责。我们不保证您在任何时候均可浏览、阅读、使用本系统;不保证本系统的内容不存在打印、复制及其他输入方面的错误。提供方有权随时更改本系统任何内容。 在任何情况下,对于无法进入本系统或无法使用本系统内容而导致的任何直接的、间接的、附带的、给第三人造成的损失(包括但不限于,利润损失、信息数据丢失、财产毁坏损失),提供方均不承担任何责任。 本系统提供的站外链接仅为用户提供方便,但链接的系统不属于本系统的控制范围或管理范畴,故提供方对链接系统所传送或登载的任何形式的内容不承担任何责任。 任何用户在本系统发表的商业信息、个人信息、留言以及其他信息,均不代表本系统以及提供方的立场,且与本系统与提供方无关,相关责任由作者完全承担;本系统有权对上述商业信息、个人信息、留言以及其他信息随时进行删除。</p>
|
||||
<p>本系统支持用户在使用本系统系统过程中的合法经营行为,但因用户的作为或不作为所造成的后果概由用户自行承担。</p>
|
||||
<p className="blod">隐私保护声明</p>
|
||||
<p>本系统承诺按照相关法律的规定、本系统的隐私政策,保护本系统收集的用户个人信息。我们建议您认真阅读本隐私保护声明。</p>
|
||||
<p className="blod">适用法律与争议解决</p>
|
||||
<p>因本公告或使用本系统所发生的争议适用中华人民共和国法律。因本公告或使用本系统发生争议,应当协商解决,协商不成的,由本公司所在地人民法院受理解决。</p>
|
||||
<p className="blod">终止协议或退出服务</p>
|
||||
<p>如果您希望不再使用本系统提供的服务,或者您不同意上述条款及更新内容,您可以选择终止协议或者退出服务具体请联系客服人员。</p>
|
||||
</div>
|
||||
<Checkbox checked={agree} onChange={changeAgree}>我已阅读并同意《用户协议及声明条款》</Checkbox>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
export default AgreementModal;
|
|
@ -0,0 +1,184 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import DataEmpty from './dataEmpty';
|
||||
import DetectionModal from './detectionModal';
|
||||
import { Table , Spin, message } from 'antd';
|
||||
import axios from 'axios';
|
||||
|
||||
|
||||
function Data(props){
|
||||
const [ detectionVisible , setDetectionVisible ] = useState(false);
|
||||
const [ dataSource , setDataSource ] = useState(undefined);
|
||||
const [ page , setPage ] = useState(1);
|
||||
const [ spining , setSpining ] = useState(true);
|
||||
const [ relayCount , setRelayCount ] = useState(5);
|
||||
const [ repeatId , setRepeatId ] = useState(undefined);
|
||||
const [ repeatBranch , setRepeatBranch ] = useState(undefined);
|
||||
|
||||
const { owner , projectsId } = props.match.params;
|
||||
const { current_user , isManager } = props;
|
||||
const limit = 15;
|
||||
|
||||
|
||||
function onDetectionOk(){
|
||||
props.showNotification("扫描成功!");
|
||||
setDetectionVisible(false);
|
||||
setSpining(true);
|
||||
Init();
|
||||
}
|
||||
|
||||
function Init(){
|
||||
const url = `/traces/${owner}/${projectsId}/task_results.json`;
|
||||
axios.get(url,{
|
||||
params:{
|
||||
page,limit
|
||||
}
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
setDataSource(result.data.data);
|
||||
setRelayCount(result.data.left_tasks_count);
|
||||
setSpining(false);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
setSpining(true);
|
||||
Init();
|
||||
},[])
|
||||
|
||||
|
||||
// 重新扫描
|
||||
function repeatCheck(project_id,branch){
|
||||
if(relayCount <= 0){
|
||||
message.error("无可用检测次数");
|
||||
return;
|
||||
}
|
||||
setRepeatId(project_id);
|
||||
setRepeatBranch(branch);
|
||||
setDetectionVisible(true);
|
||||
}
|
||||
|
||||
function createCheck(){
|
||||
if(relayCount <= 0){
|
||||
message.error("无可用检测次数");
|
||||
return;
|
||||
}
|
||||
setRepeatBranch(undefined);
|
||||
setRepeatId(undefined);
|
||||
setDetectionVisible(true);
|
||||
}
|
||||
|
||||
function handleDownExcel(task_id){
|
||||
setSpining(true);
|
||||
const url = `/api/traces/${owner}/${projectsId}/task_pdf.json?task_id=${task_id}`;
|
||||
// window.open(`/api/traces/${owner}/${projectsId}/task_pdf.json?task_id=${task_id}`);
|
||||
const x = new XMLHttpRequest();
|
||||
x.open('GET', url, true)
|
||||
x.responseType = 'blob';
|
||||
x.onload = (e) => {
|
||||
// 会创建一个 DOMString,其中包含一个表示参数中给出的对象的URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定。这个新的URL 对象表示指定的 File 对象或 Blob 对象。
|
||||
const url = window.URL.createObjectURL(x.response)
|
||||
const a = document.createElement('a')
|
||||
a.href = url;
|
||||
a.download = "report.pdf";
|
||||
a.click();
|
||||
setSpining(false);
|
||||
}
|
||||
x.send();
|
||||
console.log("spining");
|
||||
}
|
||||
|
||||
return(
|
||||
<div>
|
||||
<DetectionModal
|
||||
visible={detectionVisible}
|
||||
onCancel={()=>setDetectionVisible(false)}
|
||||
onOk={onDetectionOk}
|
||||
owner={owner}
|
||||
projectsId={projectsId}
|
||||
relayCount={relayCount}
|
||||
repeatId={repeatId}
|
||||
repeatBranch={repeatBranch}
|
||||
/>
|
||||
<div className="servertitle">
|
||||
<span className="systitle">重晴鸟代码溯源系统</span>
|
||||
{ isManager && <a className="btnhover" onClick={createCheck}>新建分析</a> }
|
||||
</div>
|
||||
<Spin spinning={spining}>
|
||||
<div style={{minHeight:"400px"}}>
|
||||
{
|
||||
dataSource && dataSource.length >0 &&
|
||||
<div>
|
||||
<ul className="dataUl">
|
||||
<li className="dataUlhead">
|
||||
<span>序号</span>
|
||||
<span>分支名称</span>
|
||||
<span>检测状态</span>
|
||||
<span>检测时间</span>
|
||||
{ isManager && <span>操作</span> }
|
||||
</li>
|
||||
{
|
||||
dataSource.map((e,key)=>{
|
||||
return(
|
||||
<div key={key}>
|
||||
<li className="dataUlbody">
|
||||
<span>{key + 1 }</span>
|
||||
<span title={e.branch_tag}>{e.branch_tag}</span>
|
||||
<span>
|
||||
{
|
||||
e.detect_status === "fail" ?
|
||||
<span className="failure">失败</span>
|
||||
: e.detect_status==="detecting" ?
|
||||
<div className="running">
|
||||
<span className="percentline" style={{width:`${e.detect_process}%`}}></span>
|
||||
<span className="percentNum" style={{left:`${e.detect_process+5}%`}}>{e.detect_process}%</span>
|
||||
</div>
|
||||
:
|
||||
<span className="success">成功</span>
|
||||
}
|
||||
</span>
|
||||
<span>{e.detect_startdate}</span>
|
||||
{
|
||||
isManager &&
|
||||
<span>
|
||||
<div className="operationBtns">
|
||||
{
|
||||
e.detect_status ==="detecting" ?
|
||||
<span>重新扫描</span>
|
||||
:
|
||||
<a onClick={()=>repeatCheck(e.project_id,e.branch_tag)}>重新扫描</a>
|
||||
}
|
||||
{/* {
|
||||
(e.detect_status ==="fail" || e.detect_status ==="detecting") ?
|
||||
<span>查看<i className="iconfont icon-sanjiaoxing-down"></i></span>
|
||||
:
|
||||
<a>查看<i className="iconfont icon-sanjiaoxing-down"></i></a>
|
||||
} */}
|
||||
{
|
||||
(e.detect_status ==="fail" || e.detect_status ==="detecting") ?
|
||||
<span>下载报告</span>
|
||||
:
|
||||
<a onClick={()=>handleDownExcel(e.task_id)}>下载报告</a>
|
||||
}
|
||||
</div>
|
||||
</span>
|
||||
}
|
||||
</li>
|
||||
{false && <iframe src="www.baidu.com" id={`htmlIframe_${key}`} frameBorder="0" name={`htmlIframe_${key}`} style={{ background: '#fff', height: '100%', width: '100%' }}></iframe>}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
{
|
||||
(dataSource === null || (dataSource && dataSource.length === 0)) &&
|
||||
<DataEmpty />
|
||||
}
|
||||
</div>
|
||||
</Spin>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Data;
|
|
@ -0,0 +1,18 @@
|
|||
import React from 'react';
|
||||
import { Divider} from 'antd';
|
||||
|
||||
|
||||
function DataEmpty(props){
|
||||
return(
|
||||
<div className="dataEmpty">
|
||||
<div className="dataemptyCon">
|
||||
<img src={require('./img/nullicon.png')} width="68px" alt=""/>
|
||||
<span className="nullTitle">暂无数据</span>
|
||||
<p className="nullDesc">重睛鸟代码溯源及安全审查系统是一款完全自主、面向主流语言的代码审查大数据平台。主要功能包含软件源代码的溯源分析、自主度评估、开源漏洞检测、开源许可证合规性分析,支持分析结果多层次可视化展示等,可用于提高软件代码的可控性与安全性。</p>
|
||||
<Divider />
|
||||
<p className="nullSubDesc">对每个仓库,可进行代码检测总次数仅为 <span style={{color:"rgba(70, 106, 255, 1)",fontSize:"20px"}}>5</span> 次</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default DataEmpty;
|
|
@ -0,0 +1,162 @@
|
|||
import React, { useState , forwardRef, useEffect } from 'react';
|
||||
import { Modal , Form , Select } from 'antd';
|
||||
import axios from 'axios';
|
||||
|
||||
function DetectionModal({form , visible , onCancel , onOk , projectsId, owner , relayCount , repeatId , repeatBranch }){
|
||||
const { getFieldDecorator, validateFields , setFieldsValue } = form;
|
||||
const [ check , setCheck ] = useState(false);
|
||||
const [ branch , setBranch ] = useState("");
|
||||
const [ branchList , setBranchList ] = useState([]);
|
||||
const source = axios.CancelToken.source();
|
||||
const layout = {
|
||||
labelCol: { span: 5 },
|
||||
wrapperCol: { span: 18 },
|
||||
};
|
||||
|
||||
useEffect(()=>{
|
||||
if(visible && owner && projectsId){
|
||||
const url = `/${owner}/${projectsId}/branches.json`;
|
||||
axios.get(url).then(result=>{
|
||||
if(result){
|
||||
setBranchList(result.data);
|
||||
}
|
||||
}).catch(error=>{})
|
||||
}
|
||||
},[owner,projectsId,visible])
|
||||
|
||||
// 取消检测
|
||||
function onCancelFunc(){
|
||||
source.cancel();
|
||||
if(check) {
|
||||
setCheck(false);
|
||||
} else{
|
||||
onCancel();
|
||||
}
|
||||
}
|
||||
// 开始检测
|
||||
function onOkFunc(){
|
||||
validateFields((error,values)=>{
|
||||
if(!error){
|
||||
setCheck(true);
|
||||
setBranch(values.branch);
|
||||
if(repeatId){
|
||||
// 重新扫描
|
||||
const url = `/traces/${owner}/${projectsId}/reload_task.json`;
|
||||
axios.get(url,{
|
||||
params:{
|
||||
project_id:repeatId,
|
||||
branch_name:repeatBranch
|
||||
}
|
||||
},{
|
||||
CancelToken:source.token
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
onOk();
|
||||
}
|
||||
setCheck(false);
|
||||
}).catch(error=>{})
|
||||
}else{
|
||||
// 新建分析
|
||||
const url = `/traces/${owner}/${projectsId}/tasks.json`;
|
||||
axios.post(url,{
|
||||
branch_name:values.branch
|
||||
},{
|
||||
CancelToken:source.token
|
||||
}).then(result=>{
|
||||
if(result){
|
||||
onOk();
|
||||
}
|
||||
setCheck(false);
|
||||
}).catch(error=>{})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
return(
|
||||
<Modal
|
||||
visible={visible}
|
||||
title=""
|
||||
onCancel={onCancel}
|
||||
className="agreementModal"
|
||||
width="635px"
|
||||
footer={
|
||||
<div className="agreeBtn">
|
||||
<a onClick={onCancelFunc}>取消</a>
|
||||
{ !check && relayCount !== 0 && <a className={"agree btnhover"} onClick={onOkFunc}>开始检测</a> }
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<div className="detectionAnimation">
|
||||
<ul className={check ? "animationUl action" : "animationUl" }>
|
||||
<li><img src={require('./img/1.png')} width="166px" alt=""/></li>
|
||||
<li><img src={require('./img/2.png')} width="136px" alt=""/></li>
|
||||
<li><img src={require('./img/3.png')} width="96px" alt=""/></li>
|
||||
<li><img src={require('./img/4.png')} width="96px" alt=""/></li>
|
||||
<li><img src={require('./img/5.png')} width="15px" alt=""/></li>
|
||||
<li><img src={require('./img/6.png')} width="25px" alt=""/></li>
|
||||
<li><img src={require('./img/7.png')} width="1px" alt=""/></li>
|
||||
</ul>
|
||||
{
|
||||
check ?
|
||||
<p className="lastTimes">正在开启检测,请等待...</p>
|
||||
:
|
||||
<p className="lastTimes">剩余扫描次数<span>{relayCount}</span></p>
|
||||
}
|
||||
</div>
|
||||
<div className="formDiv">
|
||||
<Form {...layout}>
|
||||
{
|
||||
!check && !repeatBranch &&
|
||||
<Form.Item label="检测分支" colon={false} labelAlign={"left"}>
|
||||
{getFieldDecorator("branch",{
|
||||
rules:[{required:true,message:"请选择要检测的分支"}]
|
||||
})(
|
||||
<Select placeholder="请选择仓库分支" showSearch style={{width:"360px"}}>
|
||||
{
|
||||
branchList && branchList.length >0 ?
|
||||
branchList.map((i,k)=>{
|
||||
return(
|
||||
<Select.Option value={i.name}>{i.name}</Select.Option>
|
||||
)
|
||||
})
|
||||
:
|
||||
<Select.Option value={""}>暂无分支</Select.Option>
|
||||
}
|
||||
</Select>
|
||||
)}
|
||||
</Form.Item>
|
||||
}
|
||||
<div className="checkInfos">
|
||||
{
|
||||
((check && branch) || repeatBranch) &&
|
||||
<div>
|
||||
<span>检测分支</span>
|
||||
<p className="task-hide" title={branch || repeatBranch}>{branch || repeatBranch}</p>
|
||||
</div>
|
||||
}
|
||||
<div>
|
||||
<span>检测类型</span>
|
||||
<p>快速-组件级</p>
|
||||
</div>
|
||||
<div>
|
||||
<span>检测参数</span>
|
||||
<div style={{flex:1}}>
|
||||
<p>解析层级:2</p>
|
||||
<p>最小解析文件行数:10</p>
|
||||
<p>许可证检测类型:开源软件</p>
|
||||
<span className="color-grey-6">文件相似阈值</span>
|
||||
<ul>
|
||||
<li>行数:20</li>
|
||||
<li>相似比例:50%</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
export default Form.create()(forwardRef(DetectionModal));
|
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 260 B |
After Width: | Height: | Size: 349 B |
After Width: | Height: | Size: 88 B |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 692 B |
|
@ -0,0 +1,454 @@
|
|||
.panels{
|
||||
width: 1200px;
|
||||
margin:20px auto;
|
||||
}
|
||||
.btnhover{
|
||||
background-color: #466aff;
|
||||
color: #fff!important;
|
||||
&:hover{
|
||||
background-color: rgba(26, 71, 255, 1)!important;
|
||||
color: #fff!important;
|
||||
}
|
||||
}
|
||||
.servertitle{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color:#fafcff;
|
||||
border:1px solid rgba(42, 97, 255, 0.23);
|
||||
border-radius:3px 3px 0px 0px;
|
||||
justify-content: space-between;
|
||||
padding:12px 16px;
|
||||
.systitle{
|
||||
font-weight:500;
|
||||
color:#333333;
|
||||
font-size:16px;
|
||||
}
|
||||
a{
|
||||
width:88px;
|
||||
height:36px;
|
||||
line-height: 36px;
|
||||
background-color:#466aff;
|
||||
border-radius:5px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
.dataEmpty{
|
||||
margin-top: 25px;
|
||||
background-color:#fafcff;
|
||||
border-radius:4px 4px 0px 0px;
|
||||
min-height: 418px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.dataemptyCon{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
max-width: 665px;
|
||||
text-align: center;
|
||||
.nullTitle{
|
||||
font-size: 22px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
margin:15px 0px 28px!important;
|
||||
color: #333;
|
||||
}
|
||||
.nullDesc{
|
||||
color:#666666;
|
||||
font-size:14px;
|
||||
line-height:28px;
|
||||
}
|
||||
.ant-divider{
|
||||
min-width: 500px;
|
||||
width: 500px;
|
||||
border-top:1px solid rgba(90, 117, 193, 0.23);
|
||||
margin:18px 0px!important;
|
||||
height: 0px;
|
||||
}
|
||||
.nullSubDesc{
|
||||
color:#333333;
|
||||
font-size:16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.agreementModal{
|
||||
.ant-modal-header{
|
||||
padding:0px ;
|
||||
height: 56px;
|
||||
background:url('./img/modaltitle.png') 100% 100% ;
|
||||
color: #fff;
|
||||
.ant-modal-title{
|
||||
color: #fff;
|
||||
height: 56px;
|
||||
line-height: 56px!important;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.ant-modal-close{
|
||||
top:0px!important;
|
||||
color: #fff;
|
||||
}
|
||||
.ant-modal-body{
|
||||
padding:0px;
|
||||
}
|
||||
.agreementCon{
|
||||
height: 400px;
|
||||
overflow-y: auto;
|
||||
padding:20px;
|
||||
p{
|
||||
line-height: 26px;
|
||||
font-size: 15px;
|
||||
color: #333;
|
||||
margin-bottom: 8px!important;
|
||||
&.blod{
|
||||
font-weight:500;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
.agreeBtn{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 67px;
|
||||
justify-content: center;
|
||||
a{
|
||||
display: block;
|
||||
height:36px;
|
||||
line-height: 34px;
|
||||
margin:0px 20px;
|
||||
background-color:#ffffff;
|
||||
border:1px solid;
|
||||
border-color:#d0d0d0;
|
||||
border-radius:4px;
|
||||
width: 120px;
|
||||
text-align: center;
|
||||
color:#666666;
|
||||
font-size:15px;
|
||||
&.notagree{
|
||||
background-color:rgba(177, 192, 255, 1);
|
||||
border-color:rgba(177, 192, 255, 1);
|
||||
color: #fff!important;
|
||||
cursor: default;
|
||||
}
|
||||
&.agree{
|
||||
background-color:#466aff;
|
||||
border-color:rgba(177, 192, 255, 1);
|
||||
color: #fff!important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.detectionAnimation{
|
||||
height: 240px;
|
||||
width: 100%;
|
||||
background-image: url('./img/detectionBG.png');
|
||||
background-size: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
.animationUl{
|
||||
width: 166px;
|
||||
height: 166px;
|
||||
position: relative;
|
||||
&.action li{
|
||||
-webkit-animation: App-logo-spin infinite 5s linear;
|
||||
animation: App-logo-spin infinite 5s linear;
|
||||
}
|
||||
li{
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
&:last-child{
|
||||
align-items: flex-end;
|
||||
height: 96px;
|
||||
bottom: 36px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@keyframes App-logo-spin {
|
||||
from{
|
||||
transform:rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform:rotate(360deg);
|
||||
}
|
||||
}
|
||||
.lastTimes{
|
||||
color: #fff;
|
||||
margin-top: 6px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
display: flex;
|
||||
span{
|
||||
font-size: 20px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.dataTable,.dataUl{
|
||||
.ant-table-thead > tr > th{
|
||||
background-color: #fff;
|
||||
div{
|
||||
color: rgba(51, 51, 51, 1);
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
.ant-table-tbody > tr:hover > td{
|
||||
background-color: rgba(248, 250, 255, 1)!important;
|
||||
}
|
||||
|
||||
.failure{
|
||||
display: block;
|
||||
height:32px;
|
||||
line-height:32px;
|
||||
background-color:rgba(230, 0, 6, 0.1);
|
||||
border:1px solid;
|
||||
border-color:#fcb6c2;
|
||||
border-radius:4px;
|
||||
color: rgba(255, 12, 12, 1);
|
||||
width:58px;
|
||||
text-align: center;
|
||||
}
|
||||
.success{
|
||||
display: block;
|
||||
height:32px;
|
||||
line-height:32px;
|
||||
background-color:rgba(83, 255, 163, 0.1);
|
||||
border:1px solid;
|
||||
border-color:rgba(0, 184, 67, 1);
|
||||
border-radius:4px;
|
||||
color: rgba(0, 156, 68, 1);
|
||||
width:58px;
|
||||
text-align: center;
|
||||
}
|
||||
.running{
|
||||
position: relative;
|
||||
width: 137px;
|
||||
height: 22px;
|
||||
background-color: rgba(75, 75, 75, 0.1);
|
||||
border-radius:26px;
|
||||
span.percentline{
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top:0px;
|
||||
height: 100%;
|
||||
background-color: rgba(80, 156, 255, 1);
|
||||
border-radius:26px;
|
||||
text-align: center;
|
||||
line-height: 24px;
|
||||
color: rgba(51, 51, 51, 1);
|
||||
}
|
||||
}
|
||||
.percentNum{
|
||||
position: absolute;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
}
|
||||
.operationBtns{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
a,span{
|
||||
margin:0px 14px;
|
||||
position: relative;
|
||||
color: rgba(70, 106, 255, 1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 26px;
|
||||
}
|
||||
span{
|
||||
color: rgba(197, 204, 225, 1)!important;
|
||||
cursor: default;
|
||||
}
|
||||
a > i,span>i{
|
||||
width: 13px;
|
||||
}
|
||||
a::before,span::before{
|
||||
position: absolute;
|
||||
content: "";
|
||||
width: 1px;
|
||||
height: 12px;
|
||||
top:7px;
|
||||
background-color: rgba(213, 213, 213, 1);
|
||||
left: -14px;
|
||||
}
|
||||
a:first-child::before,span:first-child::before{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.dataUl{
|
||||
&>div{
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
li{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
height: 66px;
|
||||
align-items: center;
|
||||
color:#333333;
|
||||
&>span{
|
||||
padding:0px 10px;
|
||||
text-align: left;
|
||||
}
|
||||
&>span:first-child{
|
||||
width: 7%;
|
||||
text-align: center;
|
||||
}
|
||||
&>span:nth-child(2){
|
||||
width: 30%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
&>span:nth-child(3){
|
||||
flex:1;
|
||||
}
|
||||
&>span:nth-child(4){
|
||||
width: 16%;
|
||||
}
|
||||
&>span:last-child{
|
||||
width: 30%;
|
||||
text-align: center;
|
||||
}
|
||||
&.dataUlhead{
|
||||
font-size:16px;
|
||||
}
|
||||
&.dataUlbody{
|
||||
font-size: 14px;
|
||||
&:hover{
|
||||
background-color:#f8faff
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.formDiv{
|
||||
width: 430px;
|
||||
margin:0px auto;
|
||||
padding:20px 0px;
|
||||
.has-error .ant-form-explain{
|
||||
position: absolute;
|
||||
bottom: -15px;
|
||||
}
|
||||
.ant-form-item-label{
|
||||
width: 70px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
margin-top: 4px;
|
||||
label{
|
||||
color:#20294a;
|
||||
font-size:15px;
|
||||
}
|
||||
}
|
||||
.ant-row.ant-form-item{
|
||||
margin-bottom: 12px!important;
|
||||
}
|
||||
.ant-form-item-required::before{
|
||||
display: none;
|
||||
}
|
||||
.checkInfos{
|
||||
&>div{
|
||||
display: flex;
|
||||
&>span{
|
||||
display: block;
|
||||
width: 70px;
|
||||
line-height: 32px;
|
||||
text-align: left;
|
||||
color:#20294a;
|
||||
font-size:15px;
|
||||
}
|
||||
ul{
|
||||
display: flex;
|
||||
margin-top: 15px;
|
||||
li:first-child{
|
||||
margin-right: 30px;
|
||||
}
|
||||
}
|
||||
p,ul>li{
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
padding:0px 12px;
|
||||
background-color:#f5f7ff;
|
||||
border-radius:4px;
|
||||
color:#666666;
|
||||
margin-bottom: 15px!important;
|
||||
flex:1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.serverlist{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
min-height: 500px;
|
||||
align-content: flex-start;
|
||||
li{
|
||||
width: 368px;
|
||||
margin-right: 36px;
|
||||
height: 198px;
|
||||
background-image:url('./img/bg.png');
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
margin-bottom: 20px!important;
|
||||
padding:30px 40px 0px 40px;
|
||||
position: relative;
|
||||
&:nth-child(3n+1){
|
||||
margin-left: -10px;
|
||||
}
|
||||
&:nth-child(3n){
|
||||
margin-right: -10px;
|
||||
}
|
||||
.servername{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
img{
|
||||
margin-right: 8px;
|
||||
width: 42px;
|
||||
}
|
||||
a{
|
||||
font-weight:500;
|
||||
color:#466aff;
|
||||
font-size:17px;
|
||||
}
|
||||
}
|
||||
&:hover{
|
||||
.serverbtn{
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.serverdesc{
|
||||
-webkit-line-clamp: 3;
|
||||
line-height: 25px;
|
||||
color:#525662;
|
||||
font-size:15px;
|
||||
}
|
||||
.serverbtn{
|
||||
position: absolute;
|
||||
height: 56px;
|
||||
background-color: transparent;
|
||||
bottom: 10px;
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 10px;
|
||||
opacity: 0;
|
||||
transition: 0.3s;
|
||||
a{
|
||||
display: block;
|
||||
width: 224px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
text-align: center;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ const menu = [
|
|||
// {name:"资源库",index:"resources"},
|
||||
{name:"里程碑",index:"versions"},
|
||||
{name:"维基 (Wiki)",index:"wiki"},
|
||||
// {name:"服务",index:"services"},
|
||||
{name:"动态",index:"activity"},
|
||||
]
|
||||
class Setting extends Component {
|
||||
|
|
|
@ -66,8 +66,8 @@ export default Form.create()(
|
|||
if(!value){
|
||||
callback();
|
||||
}
|
||||
if(value && !value.match(/^[a-zA-Z][a-zA-Z0-9_-]{3,19}$/)){
|
||||
callback("只能使用以字母开头,包含字母、数字、下划线、横杠等,长度4到20个字符");
|
||||
if(value && !value.match(/^[a-zA-Z0-9][a-zA-Z0-9_-]{3,19}$/)){
|
||||
callback("只能使用以字母、数字开头,包含字母、数字、下划线、横杠等,长度4到20个字符");
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ export default Form.create()(
|
|||
if(!value){
|
||||
callback();
|
||||
}
|
||||
if(value && !value.match(/^[a-zA-Z][a-zA-Z0-9_-]{3,19}$/)){
|
||||
if(value && !value.match(/^[a-zA-Z0-9][a-zA-Z0-9_-]{3,19}$/)){
|
||||
callback("只能使用以字母开头,包含字母、数字、下划线、横杠等,长度4到20个字符");
|
||||
}
|
||||
callback();
|
||||
|
|
|
@ -102,7 +102,7 @@ class Infos extends Component {
|
|||
}else if(pathname === `/${username}/followers`){
|
||||
this.setState({menuKey:undefined,route_type:"followers"});
|
||||
}else{
|
||||
this.setState({menuKey:undefined,route_type:undefined});
|
||||
this.setState({menuKey:"6",route_type:undefined});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -249,7 +249,6 @@ class Infos extends Component {
|
|||
const { current_user } = this.props;
|
||||
const { username } = this.props.match.params;
|
||||
const { user, isSpin, route_type , undo_events , menuKey , avatarVisible } = this.state;
|
||||
|
||||
return (
|
||||
<div className="newMain clearfix">
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import fetch from './fetch';
|
||||
|
||||
// 获取当前用户报名信息
|
||||
// 获取当前用户项目报名信息
|
||||
export function getUserApplyInfo(params) {
|
||||
return fetch({
|
||||
url: `/api/applyInformation/getUserApplyInfo`,
|
||||
|
@ -16,4 +16,76 @@ export function applyGlcc(data) {
|
|||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
//提交报名信息(学生)
|
||||
export function studentApply(data) {
|
||||
return fetch({
|
||||
url: '/api/studentApply/create',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
//修改报名信息(学生)
|
||||
export function studentApplyEdit(data) {
|
||||
return fetch({
|
||||
url: '/api/studentApply/update',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 获取当前学生用户报名信息
|
||||
// 课题列表
|
||||
export function projectList(params) {
|
||||
return fetch({
|
||||
url: `/api/applyInformation/list`,
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
// 课题列表
|
||||
export function taskList(params) {
|
||||
return fetch({
|
||||
url: `/api/applyInformation/taskList`,
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
// 项目id查询项目详情
|
||||
export function getProjectById(id) {
|
||||
return fetch({
|
||||
url: `/api/applyInformation/${id}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 获取用户(学生)课题报名信息
|
||||
export function getStudentApplyInfo(params) {
|
||||
return fetch({
|
||||
url: `/api/studentApply/getUserApplyInfo`,
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
// 用户取消课题报名
|
||||
export function cancelTaskApply(data) {
|
||||
return fetch({
|
||||
url: '/api/studentApply/delete',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
}
|
||||
|
||||
// 学生报名课题列表
|
||||
export function studentApplyList(params) {
|
||||
return fetch({
|
||||
url: `/api/studentApply/list`,
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
|
@ -46,7 +46,6 @@ function Apply(props) {
|
|||
}else{
|
||||
window.location.href="/login?go_page=/glcc/apply";
|
||||
}
|
||||
|
||||
}, [])
|
||||
|
||||
useEffect(()=>{
|
||||
|
@ -108,7 +107,6 @@ function Apply(props) {
|
|||
}
|
||||
return isLt100M && isType;
|
||||
}
|
||||
|
||||
function getBase64(img, callback) {
|
||||
const reader = new FileReader();
|
||||
reader.addEventListener('load', () => callback(reader.result));
|
||||
|
@ -128,7 +126,7 @@ function Apply(props) {
|
|||
}
|
||||
return(
|
||||
<div className="glcc_apply">
|
||||
<img className="glcc-banner" src={banner}></img>
|
||||
<img className="glcc-banner" src={banner} alt=''></img>
|
||||
<div className='apply'>
|
||||
<Breadcrumb className='pt20 font-16'>
|
||||
<Breadcrumb.Item><a href="/glcc">开源夏令营</a></Breadcrumb.Item>
|
||||
|
@ -177,7 +175,7 @@ function Apply(props) {
|
|||
'logo格式为png、jpg、jpeg,限制上传大小2m,建议上传尺寸为382*228',
|
||||
'logo',
|
||||
[{ required: true, message: "请正确上传项目logo" }],
|
||||
userApplyInfo && userApplyInfo.projectLogoId ? <div className='projectLogo'><img src={`${main_site_url}/api/attachments/${userApplyInfo.projectLogoId}`}/></div>
|
||||
userApplyInfo && userApplyInfo.projectLogoId ? <div className='projectLogo'><img src={`${main_site_url}/api/attachments/${userApplyInfo.projectLogoId}`} alt=''/></div>
|
||||
:<Upload
|
||||
listType="picture-card"
|
||||
className="avatar-uploader"
|
||||
|
@ -200,7 +198,7 @@ function Apply(props) {
|
|||
</Form>
|
||||
<div className={`explain ${userApplyInfo ? "mt70" : ""}`}>
|
||||
<p className='font-15 c000'>申请说明</p>
|
||||
<div>1、项目报名时间为<span className='c000'>4月15日—5月8日</span>,请在报名截止时间(北京时间<span className='c000'>2022年5月8日18点</span>)前提交报名信息。</div>
|
||||
<div>1、项目报名时间为<span className='c000'>4月15日—5月20日</span>,请在报名截止时间(北京时间<span className='c000'>2022年5月20日24点</span>)前提交报名信息。</div>
|
||||
<div>2、本次夏令营使用Gitlink为代码托管平台,学员基于Gitlink上项目数量完成课程任务。如果您的项目还未在Gitlink中,请现将项目迁移到Gitlink,迁移事项请查看<a href='https://forum.gitlink.org.cn/forums/7296/detail' target="_blank" className='link'>迁移说明文档</a>。如在迁移过程中遇到问题,请加qq群: 1071514693 联系qq群管理员。</div>
|
||||
<div>3、提交社区和项目信息后,欢迎与组委会联系沟通本次编程夏令营宣传推广和后续合作工作。联系人: _TigerWang(微信号,添加请备注Gitlink编程夏令营)</div>
|
||||
</div>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -8,26 +8,75 @@ import News from './news';
|
|||
import Partner from "./partner";
|
||||
import Contact from "./contact";
|
||||
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 './index.scss';
|
||||
|
||||
export default (props) => {
|
||||
const {current_user, isGlccApplyDate, showNotification} = props;
|
||||
const { current_user, isGlccApplyDate, showNotification } = 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("不在报名时间,报名时间为4月15日~5月20日");
|
||||
window.location.href = "/glcc";
|
||||
}
|
||||
}
|
||||
|
||||
function goToStudent(){
|
||||
// 学生报名时间
|
||||
const studentApply = Date.parse(new Date()) > 1653494400000;
|
||||
if(!studentApply){
|
||||
showNotification("不在报名时间,报名开始时间为5月26日");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="glcc">
|
||||
{/* <Banner /> */}
|
||||
<img className="glcc-banner" src={banner}></img>
|
||||
<img className="glcc-banner" src={banner} alt=""></img>
|
||||
{/* <div > */}
|
||||
<Link className="apply-btn" to={isGlccApplyDate ? current_user && current_user.login ? '/glcc/apply' : '/login?go_page=/glcc/apply' : '/glcc'} onClick={()=>{!isGlccApplyDate && showNotification("不在报名时间,报名时间为4月15日~5月8日")}}>
|
||||
{/* <Link className="apply-btn" to={isGlccApplyDate ? current_user && current_user.login ? 'https://wj.qq.com/s2/10175205/e8df/' : '/login?go_page=/glcc/apply' : '/glcc'} onClick={()=>{!isGlccApplyDate && showNotification("不在报名时间,报名时间为4月15日~5月20日")}}> */}
|
||||
{/* <div className="apply-btn" onClick={goToApply}>
|
||||
<div className="apply-text">立即报名</div>
|
||||
<div className="apply-icon"><i className="font-14 iconfont icon-xiangyoujiantou "></i></div>
|
||||
</Link>
|
||||
{/* </div> */}
|
||||
<div className="introduce glcc-content">
|
||||
<div className="glcc-tit">活动简介</div>
|
||||
<div className="introduce-content">GitLink编程夏令营(GLCC),是在CCF中国计算机学会指导下,由CCF开源发展委员会(CCF ODC)举办的面向全国高校学生的暑期编程活动。活动将覆盖近千所高校,并联合各大开源基金会、开源企业、开源社区、开源专家,旨在鼓励青年学生通过参加真实的开源软件开发,提升自身技术能力,为开源社区输送优秀人才。为青年学生提供开放友好的交流平台,希望进一步推动国内开源社区的繁荣发展。</div>
|
||||
</div> */}
|
||||
{/* 报名入口 */}
|
||||
<div className="applyBox">
|
||||
{/* 项目报名 */}
|
||||
<div className="apply project">
|
||||
<div>
|
||||
<img src={apply1} alt="" className="applyIcon"/>
|
||||
<span className="hover-none"><span className="til">项目报名</span> ></span>
|
||||
<span className="hover-show">项目报名已截止</span>
|
||||
</div>
|
||||
<div className="pt6">项目报名开启,导师席位虚位以待</div>
|
||||
</div>
|
||||
{/* 学生报名 */}
|
||||
<div className="apply" onClick={goToStudent}>
|
||||
<div>
|
||||
<img src={apply2} alt="" className="applyIcon"/>
|
||||
<span className="til">学生报名</span> >
|
||||
</div>
|
||||
<div className="pt6">选择感兴趣的课题,开启您的开源之旅</div>
|
||||
</div>
|
||||
</div>
|
||||
<Lightspot isGlccApplyDate={isGlccApplyDate} current_user={current_user} showNotification={showNotification}/>
|
||||
{/* </div> */}
|
||||
<div className="introduce">
|
||||
<div className="glcc-content">
|
||||
<div className="glcc-tit">活动简介</div>
|
||||
<div className="introduceBox">
|
||||
<div className="introduce-content">GitLink编程夏令营(GLCC),是在CCF中国计算机学会指导下,由CCF开源发展委员会(CCF ODC)举办的面向全国高校学生的暑期编程活动。活动将覆盖近千所高校,并联合各大开源基金会、开源企业、开源社区、开源专家,旨在鼓励青年学生通过参加真实的开源软件开发,提升自身技术能力,为开源社区输送优秀人才。为青年学生提供开放友好的交流平台,希望进一步推动国内开源社区的繁荣发展。</div>
|
||||
<img src={introduce} alt="" className="introduceImg" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Lightspot isGlccApplyDate={isGlccApplyDate} current_user={current_user} showNotification={showNotification} />
|
||||
<TimerShaft />
|
||||
<Award />
|
||||
<News />
|
||||
|
|
|
@ -17,22 +17,69 @@
|
|||
.ant-btn-primary{
|
||||
background-color: #466aff;
|
||||
border-color: #466aff;
|
||||
|
||||
&:hover{
|
||||
opacity: .8;
|
||||
}
|
||||
}
|
||||
|
||||
.apply-btn {
|
||||
.applyBox {
|
||||
position: absolute;
|
||||
top: 15vw;
|
||||
left: 18.8vw;
|
||||
width: 152px;
|
||||
height: 40px;
|
||||
background-color: #2e5bfe;
|
||||
border: 1px solid #466aff;
|
||||
color: #fff;
|
||||
top: 21vw;
|
||||
left: 17.5vw;
|
||||
padding-left: 18px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
.apply{
|
||||
color:#666666;
|
||||
font-size:15px;
|
||||
padding: 20px 0 20px 30px;
|
||||
background-image:linear-gradient(45deg,#C6D3F0 0%,#C9D8FF 50.63%,#E7ECFE 100%);
|
||||
border:1px solid #ffffff;
|
||||
border-radius:4px;
|
||||
box-shadow:0px 0px 6px rgba(255, 255, 255, 0.73);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
width: 355px;
|
||||
.til{
|
||||
margin-left: 10px;
|
||||
font-family:PingFang SC;
|
||||
font-weight:700;
|
||||
color:#333333;
|
||||
font-size:18px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
&:hover, &:hover .til{
|
||||
color:#466aff;
|
||||
}
|
||||
.hover-show{
|
||||
display: none;
|
||||
}
|
||||
&:hover .hover-none, &:hover .pt6{
|
||||
display: none;
|
||||
}
|
||||
&:hover.project{
|
||||
background-image:linear-gradient(45deg,#EBEFFF 0%, #E0E4F9 50.63%,#F1F1F1 100%);
|
||||
box-shadow:0px 0px 6px #466aff;
|
||||
cursor: default;
|
||||
.hover-show{
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
font-weight:700;
|
||||
color: #999999;
|
||||
font-size:18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.apply:last-child{
|
||||
margin-left: 35px;
|
||||
}
|
||||
.applyIcon{
|
||||
width: 30px;
|
||||
}
|
||||
.pt6{
|
||||
padding-top: 10px;
|
||||
}
|
||||
}
|
||||
.apply-text {
|
||||
font-family: Alibaba PuHuiTi;
|
||||
|
@ -83,7 +130,7 @@
|
|||
min-width: 62.5vw;
|
||||
max-width: 98vw;
|
||||
margin: 0 auto;
|
||||
padding-top: 45px;
|
||||
padding-top: 65px;
|
||||
}
|
||||
|
||||
// 1行省略号
|
||||
|
@ -108,25 +155,34 @@
|
|||
}
|
||||
|
||||
.introduce {
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
top: -45px;
|
||||
background-image: linear-gradient(
|
||||
163.51deg,
|
||||
#e5ecff 0%,
|
||||
#e0e9ff 32.91%,
|
||||
#fbfcff 100%
|
||||
);
|
||||
background: url("../img/introduce-bg.png");
|
||||
box-shadow: 0 0 12px rgba(161, 186, 255, 0.6);
|
||||
background-size: 100% 100%;
|
||||
padding: 48px;
|
||||
// z-index: 1;
|
||||
// position: relative;
|
||||
// top: -45px;
|
||||
// background-image: linear-gradient(
|
||||
// 163.51deg,
|
||||
// #e5ecff 0%,
|
||||
// #e0e9ff 32.91%,
|
||||
// #fbfcff 100%
|
||||
// );
|
||||
// background: url("../img/introduce-bg.png");
|
||||
// box-shadow: 0 0 12px rgba(161, 186, 255, 0.6);
|
||||
// background-size: 100% 100%;
|
||||
// padding: 48px;
|
||||
min-height: 500px;
|
||||
background-image:linear-gradient(132.67deg,#e5ecff 0%,#e0e9ff 53.13%,#fbfcff 100%);
|
||||
.glcc-tit {
|
||||
margin-bottom: 30px !important;
|
||||
margin-bottom: 60px !important;
|
||||
}
|
||||
.introduce-content {
|
||||
color: #202d40;
|
||||
font-size: 15px;
|
||||
line-height: 30px;
|
||||
}
|
||||
.introduceBox{
|
||||
display: flex;
|
||||
.introduceImg{
|
||||
width: 32vw;
|
||||
margin-left: 3vw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,27 @@ import { Link } from 'react-router-dom';
|
|||
function Lightspot(props) {
|
||||
const {current_user, isGlccApplyDate, showNotification} = 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("不在报名时间,报名时间为4月15日~5月20日");
|
||||
window.location.href="/glcc";
|
||||
}
|
||||
}
|
||||
|
||||
function goToStudent(){
|
||||
// 学生报名时间
|
||||
const studentApply = Date.parse(new Date()) > 1653494400000;
|
||||
if(!studentApply){
|
||||
showNotification("不在报名时间,报名开始时间为5月26日");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="lightspot">
|
||||
<div className="glcc-content">
|
||||
|
@ -23,32 +44,40 @@ function Lightspot(props) {
|
|||
<div className="teacher-text">
|
||||
<h3 className="teacher-invite">有兴趣成为GLCC的导师吗?</h3>
|
||||
<div className="teacher-content">想要扩大项目知名度和影响力,为开源项目吸引新鲜血液,培养长期开发者。通过GitLink平台,与高校建立连接,指导开源新人传授他们的开源文化,享受开源的乐趣</div>
|
||||
<Link className='applyBut' to={isGlccApplyDate ? current_user && current_user.login ? '/glcc/apply' : '/login?go_page=/glcc/apply' : '/glcc'} onClick={()=>{!isGlccApplyDate && showNotification("不在报名时间,报名时间为4月15日~5月8日")}}>立即报名</Link>
|
||||
{/* <Link className='applyBut' to={isGlccApplyDate ? current_user && current_user.login ? 'https://wj.qq.com/s2/10175205/e8df/' : '/login?go_page=/glcc/apply' : '/glcc'} onClick={()=>{!isGlccApplyDate && showNotification("不在报名时间,报名时间为4月15日~5月20日")}}>立即报名</Link> */}
|
||||
{/* onClick={goToApply} */}
|
||||
<div className='applyBut project'><span className='hover-none'>项目报名</span><span className='hover-show'>项目报名已截止</span></div>
|
||||
</div>
|
||||
<img className="teacher-img" src={teacherImg}></img>
|
||||
<div className="teacher-text">
|
||||
<h3 className="teacher-invite">你是开源新手,有兴趣参加GLCC吗?</h3>
|
||||
<div className="teacher-content">想要参与一线开源项目开发,熟悉开源社区运作流程,接受资深开源软件专家指导,获得丰厚奖金和实习机会。参加GLCC,开启全新的开源之旅。</div>
|
||||
{/* <Link className='applyBut' to={isGlccApplyDate ? current_user && current_user.login ? 'https://wj.qq.com/s2/10175205/e8df/' : '/login?go_page=/glcc/apply' : '/glcc'} onClick={()=>{!isGlccApplyDate && showNotification("不在报名时间,报名时间为4月15日~5月20日")}}>立即报名</Link> */}
|
||||
<div className='applyBut' onClick={goToStudent}>学生报名</div>
|
||||
</div>
|
||||
{/* <img className="teacher-img" src={teacherImg} alt=''></img> */}
|
||||
</div>
|
||||
|
||||
<div className="info-student">
|
||||
<div className="info-item">
|
||||
<img className="info-item-img" src={lightspot1}></img>
|
||||
<img className="info-item-img" src={lightspot1} alt=''></img>
|
||||
<h3 className="info-item-tit">汇聚行业顶尖开源项目</h3>
|
||||
<div className="info-item-content">平台汇聚华为、xiuos、鲲鹏、开源基金会、飞腾、浪潮等行业尖端开源项目,把握开源生态发展脉搏</div>
|
||||
</div>
|
||||
|
||||
<div className="info-item">
|
||||
<img className="info-item-img" src={lightspot2}></img>
|
||||
<img className="info-item-img" src={lightspot2} alt=''></img>
|
||||
<h3 className="info-item-tit">覆盖全国千所高校、万名会员</h3>
|
||||
<div className="info-item-content">活动邀请10+全球顶尖开源基金会,100+顶级开源项目社区,致力营造专业、开放的开源社区</div>
|
||||
</div>
|
||||
|
||||
<div className="info-item">
|
||||
<img className="info-item-img" src={lightspot3}></img>
|
||||
<img className="info-item-img" src={lightspot3} alt=''></img>
|
||||
<h3 className="info-item-tit">结识开源伙伴和技术大咖</h3>
|
||||
<div className="info-item-content">每个项目配备一名导师,参与开源项目开发可接受一线资深开源项目专家专业指导,结识志同道合的开发伙伴</div>
|
||||
</div>
|
||||
|
||||
<div className="info-item">
|
||||
<img className="info-item-img" src={lightspot4}></img>
|
||||
<img className="info-item-img" src={lightspot4} alt=''></img>
|
||||
<h3 className="info-item-tit">丰厚奖励&实习绿色通道</h3>
|
||||
<div className="info-item-content">入选同学可获得丰厚奖金,并有机会进入优秀开源项目企业实习或工作,受邀参与开源大会等机会</div>
|
||||
</div>
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
.info-teacher {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
.teacher-text {
|
||||
margin-right: 2.84%;
|
||||
|
@ -25,6 +26,20 @@
|
|||
&:hover{
|
||||
opacity: .8;
|
||||
color: white !important;
|
||||
cursor: pointer;
|
||||
.hover-show{
|
||||
display: inline-block;
|
||||
}
|
||||
.hover-none{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
&:hover.project{
|
||||
background-color:#abafbe;
|
||||
cursor: default;
|
||||
}
|
||||
.hover-show{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ import log65 from '../../img/65@2x.png';
|
|||
import log67 from '../../img/67@2x.png';
|
||||
import log69 from '../../img/69@2x.png';
|
||||
import log70 from '../../img/浙江大学.png';
|
||||
import logo1 from '../../img/logo1.png';
|
||||
import logo2 from '../../img/logo2.png';
|
||||
function Partner() {
|
||||
|
||||
return (
|
||||
|
@ -149,6 +151,8 @@ function Partner() {
|
|||
<div onClick={()=>{window.open("https://www.ecnu.edu.cn/")}}><img src={log65} className='gitlink' alt='华东师范大学'/></div>
|
||||
<div onClick={()=>{window.open("http://www.csust.edu.cn/")}}><img src={log67} className='gitlink' alt='长沙理工大学'/></div>
|
||||
<div onClick={()=>{window.open("https://www.jnu.edu.cn/")}}><img src={log50} className='gitlink' alt='暨南大学'/></div>
|
||||
<div onClick={()=>{window.open("https://www.shu.edu.cn/")}}><img src={logo2} className='gitlink' alt='上海大学'/></div>
|
||||
<div onClick={()=>{window.open("https://www.bnu.edu.cn/")}}><img src={logo1} className='gitlink' alt='北京师范大学'/></div>
|
||||
</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.8",
|
||||
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,
|
||||
}
|
||||
]
|
||||
|
|
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 479 KiB After Width: | Height: | Size: 1.1 MiB |
After Width: | Height: | Size: 384 KiB |
After Width: | Height: | Size: 129 KiB |
After Width: | Height: | Size: 64 KiB |
After Width: | Height: | Size: 121 KiB |
After Width: | Height: | Size: 83 KiB |
After Width: | Height: | Size: 67 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 2.0 MiB |
|
@ -11,6 +11,7 @@ import "./index.scss";
|
|||
import Loadable from "react-loadable";
|
||||
import Loading from "../Loading";
|
||||
import { ImageLayerOfCommentHOC } from "../modules/page/layers/ImageLayerOfCommentHOC";
|
||||
import { useState } from "react";
|
||||
|
||||
|
||||
// 任务管理审核
|
||||
|
@ -23,20 +24,37 @@ const Apply = Loadable({
|
|||
loader: () => import("./apply"),
|
||||
loading: Loading,
|
||||
});
|
||||
// 开源夏令营报名页面
|
||||
// 开源夏令营帮助中心页面
|
||||
const Help = Loadable({
|
||||
loader: () => import("./help"),
|
||||
loading: Loading,
|
||||
});
|
||||
// 任务管理审核
|
||||
const Student = Loadable({
|
||||
loader: () => import("./student"),
|
||||
loading: Loading,
|
||||
});
|
||||
|
||||
// 开源夏令营项目、课题列表页面
|
||||
const Project = Loadable({
|
||||
loader: () => import("./project"),
|
||||
loading: Loading,
|
||||
});
|
||||
|
||||
const Glcc = (propsF) => {
|
||||
// 判断时间是否在开源夏令营报名时间内(4月15日~5月8日)
|
||||
const isGlccApplyDate = Date.parse(new Date()) < 1652025600000;
|
||||
// 判断时间是否在开源夏令营报名时间内(4月15日~5月20日)
|
||||
const isGlccApplyDate = Date.parse(new Date()) < 1653062400000;
|
||||
|
||||
return (
|
||||
<div className="newMain clearfix">
|
||||
<Switch {...propsF}>
|
||||
{/* 学生报名 */}
|
||||
<Route
|
||||
path="/glcc/student/apply/:taskId"
|
||||
render={(props) => (
|
||||
<Student {...propsF} {...props} isGlccApplyDate={isGlccApplyDate}/>
|
||||
)}
|
||||
></Route>
|
||||
{/* 开源夏令营报名页面 */}
|
||||
<Route
|
||||
path="/glcc/apply"
|
||||
|
@ -51,6 +69,13 @@ const Glcc = (propsF) => {
|
|||
<Help {...propsF} {...props} />
|
||||
)}
|
||||
></Route>
|
||||
{/* 项目/课题列表 */}
|
||||
<Route
|
||||
path="/glcc/student"
|
||||
render={(props) => (
|
||||
<Project {...propsF} {...props}/>
|
||||
)}
|
||||
></Route>
|
||||
{/* 首页 */}
|
||||
<Route
|
||||
path="/glcc"
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
.glccHelp{
|
||||
.-task-sidebar .glccHelp{
|
||||
height: auto;
|
||||
width: 48px;
|
||||
padding: 6px 16px;
|
||||
text-align: center;
|
||||
|
@ -7,13 +8,39 @@
|
|||
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-bangzhuzhongxinicon, .icon-bangzhuzhongxinicon1{
|
||||
color: #0654D6;
|
||||
margin-left: -3px;}
|
||||
&:hover{
|
||||
color:#ffffff;
|
||||
background-color:#466aff;
|
||||
.icon-bangzhuzhongxinicon{color:#ffffff}
|
||||
.icon-bangzhuzhongxinicon, .icon-bangzhuzhongxinicon1{color:#ffffff}
|
||||
}
|
||||
}
|
||||
.qqChatBox .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;
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Button, message } from 'antd';
|
||||
import Nodata from '../../../forge/Nodata';
|
||||
import { useEffect } from 'react';
|
||||
import { getProjectById } from '../../api';
|
||||
|
||||
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查询项目详情
|
||||
getProjectById(projectId).then(response=>{
|
||||
if(response && response.message === 'success'){
|
||||
setInfo(response.data)
|
||||
}
|
||||
})
|
||||
}
|
||||
},[detail])
|
||||
|
||||
return (
|
||||
info ? <div className={`projectDetailBox ${detail ? '':'byTask'}`}>
|
||||
<div className="projectDetailHead">
|
||||
<span className='name'>{info.projectName}</span>
|
||||
{info.projectType && <span className='type'>{info.projectType}</span>}
|
||||
<p>GitLink项目地址: <a href={info.gitlinkUrl} className='linkUrl'>{info.gitlinkUrl}</a></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 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="right oneLine taskUrl mt5">奖金: <span className='taskReward'>{item.taskReward}</span></div>
|
||||
{item.taskUrl && <div className="taskUrl">课题链接: <span><a href={info.taskUrl}>{item.taskUrl}</a></span></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>
|
||||
}) : <Nodata _html="课题暂无数据" small={true}/>}
|
||||
</div>: <div className="projectDetailBox nodata"><Nodata _html="暂无数据" small={true}/></div>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
import React , { useEffect , useState } from 'react';
|
||||
import { Spin } from 'antd';
|
||||
import { Route, Switch } from "react-router-dom";
|
||||
import banner from "../img/studentProject.png";
|
||||
import './index.scss';
|
||||
import Loadable from "react-loadable";
|
||||
import Loading from "../../Loading";
|
||||
import { Link } from 'react-router-dom';
|
||||
import { getStudentApplyInfo } from '../api';
|
||||
|
||||
// 项目列表页面
|
||||
const ProjectList = Loadable({
|
||||
loader: () => import("./projectList"),
|
||||
loading: Loading,
|
||||
});
|
||||
// 课题列表页面
|
||||
const TaskList = Loadable({
|
||||
loader: () => import("./taskList"),
|
||||
loading: Loading,
|
||||
});
|
||||
|
||||
function Project(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({});
|
||||
// 刷新用户课题报名信息
|
||||
const [studentInfoReset, setStudentInfoReset] = useState(undefined);
|
||||
|
||||
useEffect(()=>{
|
||||
// 获取用户课题报名信息current_user user_id
|
||||
current_user && current_user.login && getStudentApplyInfo({userId: current_user.user_id}).then(response=>{
|
||||
if(response && response.message === "success"){
|
||||
// setData(response.data.rows);
|
||||
const data = {};
|
||||
response.data && response.data.registrationStudentTaskList.map(item=>{
|
||||
data[item.taskId] = item.id;
|
||||
})
|
||||
setApplyTaskId(data);
|
||||
}
|
||||
})
|
||||
},[studentInfoReset, current_user])
|
||||
return(
|
||||
<div className="glcc_project">
|
||||
<img className="glcc-banner" src={banner} alt=''></img>
|
||||
<div className='head'>
|
||||
<Link to="/glcc/student/2" className={!pathname.endsWith('/1') ? 'active' : ''}>课题列表</Link>
|
||||
<Link to="/glcc/student/1" className={pathname.endsWith('/1') ? 'active' : ''}>项目列表</Link>
|
||||
</div>
|
||||
<div className='gobackBox'>
|
||||
<a href='/glcc'>开源夏令营 / </a>
|
||||
课题及项目列表
|
||||
</div>
|
||||
<Spin spinning={false}>
|
||||
<Switch {...propsF}>
|
||||
<Route
|
||||
path="/glcc/student/1"
|
||||
render={(props) => (
|
||||
<ProjectList applyTaskId={applyTaskId} current_user={current_user} showLoginDialog={showLoginDialog} isStudentApplyDate={isStudentApplyDate} studentApplyEnd={studentApplyEnd}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/glcc/student/2"
|
||||
render={(props) => (
|
||||
<TaskList applyTaskId={applyTaskId} studentInfoReset={studentInfoReset} setStudentInfoReset={setStudentInfoReset} current_user={current_user} showLoginDialog={showLoginDialog} isStudentApplyDate={isStudentApplyDate} studentApplyEnd={studentApplyEnd}/>
|
||||
)}
|
||||
></Route>
|
||||
<Route
|
||||
path="/glcc/student"
|
||||
render={(props) => (
|
||||
<TaskList applyTaskId={applyTaskId} setStudentInfoReset={setStudentInfoReset} current_user={current_user} showLoginDialog={showLoginDialog} isStudentApplyDate={isStudentApplyDate} studentApplyEnd={studentApplyEnd}/>
|
||||
)}
|
||||
></Route>
|
||||
</Switch>
|
||||
</Spin>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Project;
|
|
@ -0,0 +1,200 @@
|
|||
.glcc_project{
|
||||
background-color: #ebf2ff;
|
||||
.head{
|
||||
background-color:rgba(188, 208, 255, 0.27);
|
||||
border:1px solid #ffffff;
|
||||
text-align: center;
|
||||
a{
|
||||
padding: 18px 20px 20px;
|
||||
display: inline-block;
|
||||
color: #273778;
|
||||
font-size: 18px;
|
||||
font-weight:700;
|
||||
line-height: 30px;
|
||||
&.active{
|
||||
border-bottom: 1px solid #5474df;;
|
||||
}
|
||||
&:last-child{
|
||||
margin-left: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.gobackBox{
|
||||
width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 25px 0 12px;
|
||||
color: #202d40;
|
||||
font-size: 16px;
|
||||
border-bottom: 1px dashed #bec5d5;
|
||||
a{
|
||||
color:#a4aabb;
|
||||
}
|
||||
}
|
||||
.listBox{
|
||||
background-image:linear-gradient(180deg,#ebf2ff 0%,#ebf2ff 43.09%,#f3f4f8 100%);
|
||||
.list{
|
||||
width: 1200px;
|
||||
margin: 0 auto;
|
||||
.search{
|
||||
width: 40%;
|
||||
margin: 0 auto;
|
||||
padding: 25px 0 40px;
|
||||
&.task{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
.ant-input-group-wrapper{width: 40%;}
|
||||
}
|
||||
}
|
||||
.search .ant-btn, .search .ant-input{
|
||||
height: 48px;
|
||||
}
|
||||
.search .ant-btn{font-size: 30px;}
|
||||
.search .ant-input{
|
||||
font-size: 16px;
|
||||
border-color: #ebf2ff;
|
||||
&:hover{
|
||||
border-color: #466aff;
|
||||
}
|
||||
}
|
||||
}
|
||||
.search{
|
||||
.ant-btn-primary{
|
||||
background-color:#466aff;
|
||||
border-color: #466aff;
|
||||
}
|
||||
// .ant-input:hover{
|
||||
// border-color: #466aff;
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
.glcc-banner {
|
||||
width: 100%;
|
||||
}
|
||||
// 项目详情框
|
||||
.projectDetailBox{
|
||||
&.byTask{
|
||||
background-image:linear-gradient(180deg,#f1f5ff 0%,#ffffff 100%);
|
||||
border:1px solid #ffffff;
|
||||
border-radius:4px;
|
||||
box-shadow:0px 0px 10px rgba(100, 141, 255, 0.2);
|
||||
padding: 30px 30px 50px;
|
||||
.taskItem{
|
||||
.center{
|
||||
// width: 60%;
|
||||
}
|
||||
.right{padding-right: 10px;}
|
||||
}
|
||||
}
|
||||
&.nodata{width: 200px;}
|
||||
font-size:15px;
|
||||
line-height:30px;
|
||||
.projectDetailHead{
|
||||
color:#465474;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px dashed #bec5d5;
|
||||
line-height: 36px;
|
||||
.name{
|
||||
font-weight:700;
|
||||
color:#3753c5;
|
||||
font-size:20px;
|
||||
}
|
||||
.type{
|
||||
display: inline-block;
|
||||
border:1px solid #6680bb;
|
||||
border-radius:4px;
|
||||
margin-left: 12px;
|
||||
padding: 4px 6px;
|
||||
line-height: 26px;
|
||||
}
|
||||
.linkUrl{
|
||||
color: #466aff;
|
||||
}
|
||||
}
|
||||
.taskItem {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
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: 316px;
|
||||
background-image: url('../img/projectDetailTaskBg.png');
|
||||
background-size: 100% 100%;
|
||||
padding: 22px 20px;
|
||||
color:#25304a;
|
||||
.taskTitle{
|
||||
color:#1834a7;
|
||||
font-size:18px;
|
||||
}
|
||||
.email span{color:#466aff;}
|
||||
.leftWidth{width: 270px;}
|
||||
}
|
||||
.center{
|
||||
// width: 650px;
|
||||
text-align: left;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
.taskDesc{
|
||||
color:#6b6b6b;
|
||||
}
|
||||
.taskUrl{
|
||||
color:#465474;
|
||||
font-size:16px;
|
||||
line-height: 28px;
|
||||
margin-bottom: 5px;
|
||||
a{color:#466aff;}
|
||||
}
|
||||
.applyBut{
|
||||
background-color:#466aff;
|
||||
border-color: #466aff;
|
||||
&:hover{background-color:#5d7cff;}
|
||||
&:focus{background-color:#1140ff;}
|
||||
}
|
||||
.taskReward{color:#ff8800;}
|
||||
}
|
||||
// .right{
|
||||
// min-width: 150px;
|
||||
// font-weight:700;
|
||||
// color:#ff8800;
|
||||
// font-size:24px;
|
||||
// padding: 30px 30px 0 0;
|
||||
// }
|
||||
}
|
||||
}
|
||||
.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;
|
||||
box-shadow:0px 0px 10px rgba(100, 141, 255, 0.2);
|
||||
.ant-popover-inner-content{
|
||||
padding: 30px 30px 50px;
|
||||
}
|
||||
}
|
||||
.lookDetail{
|
||||
border-color: #466aff;
|
||||
color: #466aff;
|
||||
&:hover{
|
||||
border-color: #5d7cff;
|
||||
color: #5d7cff;
|
||||
}
|
||||
&:focus{
|
||||
border-color: #1140ff;
|
||||
color: #1140ff;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
import React , { useEffect , useState } from 'react';
|
||||
import { Input, Popover, Spin, Table, Tooltip } from 'antd';
|
||||
import { main_site_url } from '../../fetch';
|
||||
import './index.scss';
|
||||
import { projectList } from '../../api';
|
||||
import ProjectDetail from '../component/projectDetail';
|
||||
const {Search} = Input;
|
||||
|
||||
// 项目列表
|
||||
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,
|
||||
pageSize: 20
|
||||
}
|
||||
projectList(params).then(response=>{
|
||||
if(response && response.message === "success"){
|
||||
setData(response.data.rows);
|
||||
}
|
||||
setLoading(false);
|
||||
})
|
||||
},[keyword])
|
||||
|
||||
return(
|
||||
<div className="taskList listBox">
|
||||
<div className="list">
|
||||
<div className='search'><Search placeholder='请输入项目名称进行搜索' allowClear enterButton onSearch={(value)=>{setKeyword(value)}}/></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} isStudentApplyDate={isStudentApplyDate} studentApplyEnd={studentApplyEnd}/>} trigger='click' overlayClassName='projectItemPopover' autoAdjustOverflow={false}>
|
||||
<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>
|
||||
)
|
||||
}
|
||||
export default ProjectList;
|
|
@ -0,0 +1,68 @@
|
|||
.projectListBox{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
// justify-content: space-between;
|
||||
.projectItem{
|
||||
width: 30%;
|
||||
background-image: linear-gradient(180deg,rgba(232, 237, 255, 0.85) 0%,rgba(255, 255, 255, 0.85) 100%);
|
||||
border: 1px solid;
|
||||
border-color: #ffffff;
|
||||
border-radius: 4px;
|
||||
box-shadow:0px 0px 8px rgba(93, 131, 255, 0.26);
|
||||
text-align: center;
|
||||
margin-bottom: 50px;
|
||||
background-image: url('../../img/projectBg.png');
|
||||
background-size: 100% 100%;
|
||||
margin-left: 5%;
|
||||
&.firstBox{
|
||||
margin-left: 0;
|
||||
}
|
||||
&:hover{
|
||||
cursor: pointer;
|
||||
background-image: none;
|
||||
// #E8EDFF—#FFFFFF(85%)
|
||||
background-image: linear-gradient(180deg,rgba(232, 237, 255, 0.85) 0%,rgba(255, 255, 255, 0.85) 100%);
|
||||
}
|
||||
.border{
|
||||
height: 5px;
|
||||
background-color:#657edf;
|
||||
border-radius:2px 2px 0px 0px;
|
||||
}
|
||||
.projectLogo{
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
margin: 25px auto 10px;
|
||||
img{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.title{
|
||||
color:#2a51bf;
|
||||
font-size:17px;
|
||||
line-height:30px;
|
||||
margin-bottom: 15px;
|
||||
padding: 0 50px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.intro{
|
||||
color:#4f5d84;
|
||||
font-size:15px;
|
||||
margin-bottom: 15px;
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
text-overflow: ellipsis;
|
||||
text-align: left;
|
||||
padding: 0 14px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,196 @@
|
|||
import React, { Fragment, useEffect, useState } from 'react';
|
||||
import { Checkbox, Input, message, Modal, Table, Tooltip } from 'antd';
|
||||
import './index.scss';
|
||||
import { cancelTaskApply, taskList, studentApplyList } from '../../api';
|
||||
import ProjectDetail from '../component/projectDetail';
|
||||
const { Search } = Input;
|
||||
|
||||
// 课题列表
|
||||
function TaskList({applyTaskId, setStudentInfoReset, current_user, showLoginDialog, isStudentApplyDate, studentApplyEnd, studentInfoReset}) {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [deleteTaskId, setDeleteTaskId] = useState(undefined);
|
||||
// 输入搜索框
|
||||
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([]);
|
||||
// 已报名
|
||||
const [apply, setApply] = useState(false);
|
||||
|
||||
const columns = [
|
||||
{ title: '序号', dataIndex: 'index', align: 'center', className:"taskTableColumns", width: '6%', render: (text, item, index) => <span>{(current-1)*pageSize+index + 1}</span> },
|
||||
{ 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", ellipsis: true, width: `${isStudentApplyDate || studentApplyEnd ? '15%' : ''}`, render: (text) => <Tooltip title={text} placement="topLeft"><span>{text}</span></Tooltip> },
|
||||
{
|
||||
title: '操作', dataIndex: 'action', align: 'center', className:"actionColumns taskTableColumns", render: ((text, item, index) => {
|
||||
return (
|
||||
<div className='actionBox'>
|
||||
{(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>:isStudentApplyDate && <Fragment>
|
||||
<span onClick={()=>{applyTask(item.id)}}><i className='iconfont icon-shenqingketiicon applyTask mr5'></i><span className='applyTask'>申请课题</span></span></Fragment>)}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
},
|
||||
]
|
||||
|
||||
const customExpandIcon = (props) => {
|
||||
if (props.expanded) {
|
||||
return <a className='actionBox' 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='actionBox' 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.regId} applyTaskId={applyTaskId} current_user={current_user} showLoginDialog={showLoginDialog} isStudentApplyDate={isStudentApplyDate} studentApplyEnd={studentApplyEnd}/>
|
||||
}
|
||||
|
||||
// 展开收起行回调
|
||||
function onExpand(expanded, record){
|
||||
const keys = new Set(expandedRowKeys);
|
||||
if(expanded){
|
||||
keys.add(record.id);
|
||||
}else{
|
||||
keys.delete(record.id);
|
||||
}
|
||||
setExpandedRowKeys(Array.from(keys));
|
||||
}
|
||||
|
||||
// 申请课题按钮点击函数
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
// 取消课题按钮点击函数
|
||||
function cancelApply(){
|
||||
const id = [];
|
||||
id.push(applyTaskId[deleteTaskId]);
|
||||
const params = {
|
||||
ids: id
|
||||
}
|
||||
cancelTaskApply(params).then(response=>{
|
||||
if(response && response.message === "success"){
|
||||
setVisible(false);
|
||||
setStudentInfoReset(Math.random());
|
||||
message.success('取消成功');
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 改变pagesize
|
||||
function onShowSizeChange(current, pageSize){
|
||||
setCurrent(1);
|
||||
setPageSize(pageSize);
|
||||
}
|
||||
|
||||
// 切换页数
|
||||
function changePage(page, pageSize){
|
||||
setCurrent(page);
|
||||
}
|
||||
|
||||
// 数组元素置顶方法
|
||||
function toFirst(data, index){
|
||||
if(index != 0){
|
||||
data.unshift(data.splice(index,1)[0]);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setExpandedRowKeys([]);
|
||||
setLoading(true);
|
||||
const params = {
|
||||
curPage: current,
|
||||
keyword,
|
||||
pageSize
|
||||
}
|
||||
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(data);
|
||||
setTotal(response.data.total);
|
||||
}
|
||||
setLoading(false);
|
||||
})
|
||||
}, [keyword, current, pageSize, applyTaskId])
|
||||
|
||||
useEffect(()=>{
|
||||
// if(apply){
|
||||
// // 判断用户是否已经登录
|
||||
// if(current_user && current_user.login){
|
||||
// apply && studentApplyList().then(response => {
|
||||
// if (response && response.message === "success") {
|
||||
// console.log(response);
|
||||
// }
|
||||
// })
|
||||
// }else{
|
||||
// showLoginDialog();
|
||||
// }
|
||||
// }
|
||||
},[apply])
|
||||
|
||||
return (
|
||||
<div className="taskList listBox">
|
||||
<div className="list">
|
||||
<div className='search task'>
|
||||
<Checkbox onChange={(e)=>{setApply(e.target.checked)}}>已报名</Checkbox>
|
||||
<Search className='taskSearch' placeholder='请输入课题名称进行搜索' allowClear enterButton onSearch={(value) => { setCurrent(1); setKeyword(value) }} />
|
||||
<div></div>
|
||||
</div>
|
||||
<Table
|
||||
loading={loading}
|
||||
columns={columns}
|
||||
dataSource={data}
|
||||
expandedRowRender={expandRow}
|
||||
expandIconColumnIndex={4}
|
||||
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}}
|
||||
/>
|
||||
</div>
|
||||
<Modal
|
||||
okText="确认删除"
|
||||
okType='default'
|
||||
title="删除"
|
||||
visible={visible}
|
||||
onCancel={()=>{setVisible(false)}}
|
||||
onOk={cancelApply}
|
||||
wrapClassName='cancelApplyTask'
|
||||
>
|
||||
<div className='tilTask mt20'><span className='carefulIcon'>!</span>取消申请即删除本次报名记录,确定取消?</div>
|
||||
<p className='tipTask'>此操作将永久删除该报名记录,请进行确认以防数据的丢失</p>
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default TaskList;
|
|
@ -0,0 +1,110 @@
|
|||
.taskList{
|
||||
padding-bottom: 50px;
|
||||
.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;
|
||||
font-weight:700;
|
||||
color:#273778;
|
||||
}
|
||||
.taskTableColumns span{
|
||||
font-size: 15px;
|
||||
color: #353f5e;
|
||||
}
|
||||
.ant-table-tbody .taskTableColumns.taskName span{
|
||||
color: #2545c9;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ant-table-thead > tr > .taskTableColumns, .ant-table-tbody > tr > .taskTableColumns{
|
||||
background-color:#F1F6FF;
|
||||
border-bottom: 1px dashed #bec5d5;
|
||||
}
|
||||
.ant-table-tbody > tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected) > .taskTableColumns{
|
||||
background-color:#fbfbfc;
|
||||
}
|
||||
.actionColumns{
|
||||
display: flex;
|
||||
.actionBox, .actionBox span{
|
||||
cursor: pointer;
|
||||
color: #466aff;
|
||||
&:hover{color:#5d7cff !important;}
|
||||
&:active{color:#1140ff !important;}
|
||||
}
|
||||
.applyTask, span.applyTask{
|
||||
color:#eb9350;
|
||||
&:hover{color: #FFB074 !important;}
|
||||
&:active{color:#D66A17 !important;}
|
||||
}
|
||||
.cancelApply{visibility: hidden;}
|
||||
}
|
||||
tr:hover .actionColumns .cancelApply{
|
||||
visibility: visible;
|
||||
color: #E31E1E;
|
||||
}
|
||||
.cancelApplyTask{
|
||||
.ant-modal-close{top: 0px !important;}
|
||||
.ant-modal-header{
|
||||
padding: 8px 20px;
|
||||
background-color: #e9edff;
|
||||
.ant-modal-title{text-align: left;}
|
||||
}
|
||||
.carefulIcon{
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
background: #ca0002;
|
||||
display: inline-block;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
line-height: 36px;
|
||||
font-size: 18px;
|
||||
color: white;
|
||||
margin-right: 15px;
|
||||
}
|
||||
.tilTask{
|
||||
color:#333333;
|
||||
font-size:16px;
|
||||
margin-left: 35px;
|
||||
}
|
||||
.tipTask{
|
||||
color:#666666;
|
||||
font-size:14px;
|
||||
margin: 30px 0 30px 86px !important;
|
||||
}
|
||||
.ant-modal-footer{
|
||||
border-top: none;
|
||||
text-align: center;
|
||||
padding-bottom: 50px;
|
||||
.ant-btn{
|
||||
&.ant-btn-default{
|
||||
color: #df0002;
|
||||
}
|
||||
&:hover{
|
||||
border-color: #466aff;
|
||||
color: #466aff;
|
||||
&.ant-btn-default{
|
||||
border-color: #df0002;
|
||||
color: #df0002;}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.task.search{
|
||||
.ant-checkbox-wrapper:hover .ant-checkbox-inner, .ant-checkbox:hover .ant-checkbox-inner, .ant-checkbox-input:focus + .ant-checkbox-inner{border-color: #466aff;}
|
||||
.ant-checkbox-checked{
|
||||
.ant-checkbox-inner {
|
||||
background-color: #466aff;
|
||||
}
|
||||
}
|
||||
.ant-checkbox::selection{border-color: #466aff;}
|
||||
.ant-checkbox-input::after{border:1px solid #466aff;}
|
||||
.ant-checkbox-checked::after{border:1px solid #466aff;}
|
||||
}
|
|
@ -1,12 +1,30 @@
|
|||
import React from 'react';
|
||||
import { Popover } from 'antd';
|
||||
import qqChat from "./img/qqChat.png";
|
||||
import './index.scss';
|
||||
|
||||
function SiderBar() {
|
||||
const content = (
|
||||
<div className='qqChat'>
|
||||
<div className='qqMa'><img src={qqChat} alt=''/></div>
|
||||
<p className='qqTip'>扫码进入QQ交流群</p>
|
||||
<p className='qqTip'>把握最新活动资讯</p>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
// 开源夏令营定制帮助中心调整按钮
|
||||
<div className={"glccHelp -task-sidebar"} onClick={()=>window.open("/glcc/help")}>
|
||||
<i className='iconfont icon-bangzhuzhongxinicon font-20'></i>
|
||||
帮助中心
|
||||
<div className={"-task-sidebar"}>
|
||||
<Popover placement='leftBottom' content={content} title={null} forceRender={true} overlayClassName='qqChatBox'>
|
||||
<div className={"glccHelp"}>
|
||||
<i className='iconfont icon-bangzhuzhongxinicon1 font-20'></i>
|
||||
QQ交流
|
||||
</div>
|
||||
</Popover>
|
||||
<div className={"glccHelp"} onClick={()=>window.open("/glcc/help")}>
|
||||
<i className='iconfont icon-bangzhuzhongxinicon font-20'></i>
|
||||
帮助中心
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,514 @@
|
|||
import React, { useCallback, useEffect, useState, Fragment } from 'react';
|
||||
import { Breadcrumb, Button, Form, Input, Select, Upload, Cascader, Spin } from 'antd';
|
||||
import { getUploadActionUrl, getUrl, appendFileSizeToUploadFileAll } from 'educoder';
|
||||
import { locData } from '../../forge/Utils/locData';
|
||||
import { getStudentApplyInfo, taskList, studentApply, studentApplyEdit } from '../api';
|
||||
import TextArea from 'antd/lib/input/TextArea';
|
||||
import { httpUrl } from '../fetch';
|
||||
import banner from "../img/banner.png";
|
||||
import studentSvg from "../img/student.png";
|
||||
import './index.scss';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const Option = Select.Option;
|
||||
const gradeList = [
|
||||
{ id: '大一', name: '大一' },
|
||||
{ id: '大二', name: '大二' },
|
||||
{ id: '大三', name: '大三' },
|
||||
{ id: '大四', name: '大四' },
|
||||
{ id: '大五', name: '大五' },
|
||||
{ id: '研一', name: '研一' },
|
||||
{ id: '研二', name: '研二' },
|
||||
{ id: '研三', name: '研三' }];
|
||||
function Apply(props) {
|
||||
const { form, current_user, showNotification, match, setStudentInfoReset } = props;
|
||||
const taskId = Number(match.params.taskId);
|
||||
// 可用于开发时不同账号报名
|
||||
// current_user && (current_user.user_id = 6)
|
||||
// current_user && (current_user.userName = "创新使者")
|
||||
const isStudentApplyDate = new Date().getTime() > new Date('2022-05-26').getTime() && new Date().getTime() < new Date('2022-06-25').getTime();
|
||||
|
||||
const { getFieldDecorator, validateFields, setFieldsValue, validateFieldsAndScroll } = form;
|
||||
const [imageUrl, setImageUrl] = useState(undefined);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [reload, setReload] = useState();
|
||||
const [userApplyInfo, setUserApplyInfo] = useState(undefined);
|
||||
const [editable, setEditable] = useState(isStudentApplyDate);
|
||||
const [files, setFiles] = useState([]);
|
||||
const [files1, setFiles1] = useState([]);
|
||||
const initTask = {
|
||||
taskId,
|
||||
memo: '',
|
||||
memoAttachmentId: ''
|
||||
};
|
||||
const [myTaskList, setMyTaskList] = useState([]);
|
||||
const [allTaskList, setAllTaskList] = useState([]);
|
||||
|
||||
// 学生报名时间范围内
|
||||
// 已过学生报名时间
|
||||
|
||||
useEffect(() => {
|
||||
// 进入此页面到填写页面
|
||||
setTimeout(()=>{
|
||||
let clientWidth = document.body.clientWidth;
|
||||
let scrollHeight = 500 * clientWidth / 1920;
|
||||
window.scrollTo(0, scrollHeight);
|
||||
});
|
||||
// window.scrollTo(0, 50);
|
||||
|
||||
if (new Date().getTime() < new Date('2022-05-26').getTime()) {
|
||||
// 不在开源夏令营报名时间之内
|
||||
window.location.href = "/glcc/student/2";
|
||||
} else if (current_user && current_user.login) {
|
||||
|
||||
} else {
|
||||
window.location.href = `/login?go_page=/glcc/student/apply/${taskId}`;
|
||||
}
|
||||
}, [])
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const params = {
|
||||
curPage: 1,
|
||||
pageSize: 10000
|
||||
}
|
||||
taskList(params).then(response => {
|
||||
if (response && response.message === "success") {
|
||||
setAllTaskList(response.data.rows);
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
// 获取当前用户报名信息
|
||||
current_user && getStudentApplyInfo({ userId: current_user.user_id }).then(response => {
|
||||
if (response && response.message === "success" && response.data) {
|
||||
let data = response.data;
|
||||
let applyInfo = {
|
||||
grade: data.grade,
|
||||
location: data.location && data.location.split(','),
|
||||
mail: data.mail,
|
||||
phone: data.phone,
|
||||
proveAttachmentId: data.proveAttachmentId,
|
||||
school: data.school,
|
||||
studentName: data.studentName,
|
||||
userId: current_user.user_id,
|
||||
profession: data.profession,
|
||||
};
|
||||
|
||||
if (Array.isArray(data.registrationStudentTaskList)) {
|
||||
if (data.registrationStudentTaskList.length) {
|
||||
let initTaskList = [];
|
||||
for (const [i, item] of data.registrationStudentTaskList.entries()) {
|
||||
applyInfo['memo' + i] = item.memo;
|
||||
applyInfo['taskId' + i] = item.taskId;
|
||||
applyInfo['memoAttachmentId' + i] = item.memoAttachmentId;
|
||||
|
||||
if (i == 0) {
|
||||
item.memoAttachment && setFiles([{
|
||||
name: item.memoAttachment && item.memoAttachment.fileName || '已上传文件',
|
||||
id: item.memoAttachmentId,
|
||||
uid: item.memoAttachmentId
|
||||
}]);
|
||||
} else {
|
||||
item.memoAttachment && setFiles1([{
|
||||
name: item.memoAttachment && item.memoAttachment.fileName || '已上传文件',
|
||||
id: item.memoAttachmentId,
|
||||
uid: item.memoAttachmentId,
|
||||
}]);
|
||||
}
|
||||
|
||||
initTaskList.push({
|
||||
id: item.id,
|
||||
memo: item.memo,
|
||||
memoAttachmentId: item.memoAttachmentId,
|
||||
taskId: item.taskId,
|
||||
});
|
||||
}
|
||||
setMyTaskList(initTaskList);
|
||||
|
||||
// 如果只有一条报名数据,且课题id与当前id不一致,那么新增一条默认数据
|
||||
if (data.registrationStudentTaskList.length === 1 && data.registrationStudentTaskList[0].taskId != taskId) {
|
||||
addTask(data.registrationStudentTaskList);
|
||||
}
|
||||
} else {
|
||||
// 先增加数据再给选择框赋默认值,否则不生效
|
||||
new Promise((resove) => {
|
||||
setMyTaskList(() => {
|
||||
resove();
|
||||
return [initTask]
|
||||
});
|
||||
}).then(res => setFieldsValue({ 'taskId0': taskId }))
|
||||
}
|
||||
}
|
||||
|
||||
setFieldsValue(applyInfo);
|
||||
setUserApplyInfo(response.data);
|
||||
} else {
|
||||
// 先增加数据再给选择框赋默认值,否则不生效
|
||||
new Promise((resove) => {
|
||||
setMyTaskList(() => {
|
||||
resove();
|
||||
return [initTask]
|
||||
});
|
||||
}).then(res => setFieldsValue({ 'taskId0': taskId }))
|
||||
}
|
||||
})
|
||||
}, [current_user, reload])
|
||||
|
||||
// 当用户输入结束时 检验用户输入是否符合规范
|
||||
function verify(dataIndex) {
|
||||
validateFields([dataIndex], (error, values) => {
|
||||
if (error && error[dataIndex]) {
|
||||
return;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 学生报名夏令营
|
||||
function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
validateFieldsAndScroll((err, values) => {
|
||||
if (!err) {
|
||||
setLoading(true);
|
||||
const params = {
|
||||
grade: values.grade,
|
||||
location: Array.isArray(values.location) && values.location.join(),
|
||||
mail: values.mail,
|
||||
phone: values.phone,
|
||||
proveAttachmentId: values.proveAttachmentId.file ? values.proveAttachmentId.file.response.id : userApplyInfo.proveAttachmentId,
|
||||
school: values.school,
|
||||
profession: values.profession,
|
||||
studentName: values.studentName,
|
||||
userId: current_user.user_id,
|
||||
registrationStudentTaskList: myTaskList
|
||||
}
|
||||
|
||||
if (userApplyInfo) {
|
||||
params.id = userApplyInfo.id;
|
||||
studentApplyEdit(params).then(response => {
|
||||
if (response && response.message === "success") {
|
||||
showNotification("修改信息成功");
|
||||
// setStudentInfoReset(Math.random());
|
||||
setReload(Math.random());
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
studentApply(params).then(response => {
|
||||
if (response && response.message === "success") {
|
||||
showNotification("报名成功");
|
||||
// setStudentInfoReset(Math.random());
|
||||
setReload(Math.random());
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const helper = useCallback(
|
||||
(label, extra, name, rules, widget) => (
|
||||
<Form.Item label={label} extra={extra}>
|
||||
{getFieldDecorator(name, { rules, validateFirst: true })(widget)}
|
||||
</Form.Item>
|
||||
),
|
||||
[]
|
||||
);
|
||||
|
||||
|
||||
// 检查图片文件上传是否符合规定
|
||||
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 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));
|
||||
reader.readAsDataURL(img);
|
||||
reader.onload = function (e) {
|
||||
setImageUrl(e.target.result); // 上传的图片的编码
|
||||
}
|
||||
}
|
||||
|
||||
// 图片上传完成后
|
||||
function handleChange(info) {
|
||||
if (info && info.file && info.file.status === "done") {
|
||||
getBase64(info.file.originFileObj, imageUrl =>
|
||||
setImageUrl(imageUrl)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 自荐书上传
|
||||
function bookChange(info, i) {
|
||||
if (info.file.status === 'uploading' || info.file.status === "done" || info.file.status === 'removed') {
|
||||
// setLoading(true);
|
||||
if (info.file.status === "done") {
|
||||
changeTaskItem('memoAttachmentId', info.fileList[0].response && info.fileList[0].response.data.id, i)
|
||||
}
|
||||
if (info.file.status === 'removed') {
|
||||
changeTaskItem('memoAttachmentId', '', i);
|
||||
}
|
||||
console.log(info.fileList);
|
||||
if (i) {
|
||||
setFiles1(appendFileSizeToUploadFileAll(info.fileList).slice(-1));
|
||||
} else {
|
||||
setFiles(appendFileSizeToUploadFileAll(info.fileList).slice(-1));
|
||||
}
|
||||
}
|
||||
|
||||
// if (info.file.status === "done" || info.file.status === 'removed') {
|
||||
// setLoading(false);
|
||||
// }
|
||||
}
|
||||
|
||||
// 支持文件下载
|
||||
function download(file) {
|
||||
const fileId = file.id || file.response.data.id;
|
||||
window.open(`${httpUrl}/busiAttachments/download/${fileId}`);
|
||||
}
|
||||
|
||||
function addTask(List) {
|
||||
let taskListNew = List ? [...List] : [...myTaskList];
|
||||
if (taskId != taskListNew[0].taskId) {
|
||||
taskListNew.push(initTask);
|
||||
// 先增加数据再给选择框赋默认值,否则不生效
|
||||
new Promise((resove) => {
|
||||
setMyTaskList(() => {
|
||||
resove();
|
||||
return taskListNew
|
||||
});
|
||||
}).then(res => setFieldsValue({ 'taskId1': taskId }))
|
||||
} else {
|
||||
taskListNew.push({ ...initTask, taskId: '' });
|
||||
setMyTaskList(taskListNew);
|
||||
}
|
||||
}
|
||||
|
||||
function deleteTask(i) {
|
||||
let taskListNew = myTaskList.slice();
|
||||
let reWriteInfo;
|
||||
if (i) {
|
||||
reWriteInfo = {
|
||||
taskId1: '',
|
||||
memo1: '',
|
||||
memoAttachmentId1: ''
|
||||
}
|
||||
setFiles1([]);
|
||||
} else {
|
||||
reWriteInfo = {
|
||||
taskId0: taskListNew[1].taskId,
|
||||
memo0: taskListNew[1].memo,
|
||||
memoAttachmentId0: taskListNew[1].memoAttachmentId,
|
||||
taskId1: '',
|
||||
memo1: '',
|
||||
memoAttachmentId1: ''
|
||||
}
|
||||
setFiles(files1);
|
||||
setFiles1([]);
|
||||
}
|
||||
|
||||
taskListNew.splice(i, 1);
|
||||
setMyTaskList(taskListNew);
|
||||
setFieldsValue(reWriteInfo);
|
||||
}
|
||||
|
||||
function changeTaskItem(field, val, i) {
|
||||
let taskListNew = [...myTaskList];
|
||||
taskListNew[i][field] = val;
|
||||
setMyTaskList(taskListNew);
|
||||
}
|
||||
|
||||
const chooseArr = myTaskList.map(i => { return i.taskId });
|
||||
|
||||
return (
|
||||
<div className="glcc_student_apply">
|
||||
<img className="glcc-banner" src={banner} alt=''></img>
|
||||
<Spin spinning={loading}>
|
||||
<div className='apply'>
|
||||
<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 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月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>
|
||||
<div className="head_content">5、提交申请后,请耐心等待,我们会在2022年7月1日公布入围结果。如有任何问题,请添加开源夏令营微信群进行咨询</div>
|
||||
</div>
|
||||
<div className="main-tit">
|
||||
<img src={studentSvg} /><span className="tit-text">学生报名</span>
|
||||
</div>
|
||||
<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 expandTrigger="hover" 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})$|([1][3,4,5,6,7,8,9][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} />
|
||||
)}
|
||||
|
||||
<div className='introArea update_item'>{helper('学生证明',
|
||||
'请以图片的形式上传学生证明,大小不超过5M,格式为png、jpg、jpeg',
|
||||
'proveAttachmentId',
|
||||
[{ required: true, message: "请正确上传学生证明" }],
|
||||
<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' }} /> : userApplyInfo && userApplyInfo.proveAttachmentId ?
|
||||
<div><img src={`${getUrl()}/api/attachments/${userApplyInfo.proveAttachmentId}`} alt='' /></div> : <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 && <span className="delete" onClick={() => { deleteTask(i) }}><i className="iconfont icon-shanchu mr5"></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">自荐书示例:<a href="https://forum.gitlink.org.cn/forums/7299/detail" target="_black">https://forum.gitlink.org.cn/forums/7299/detail</a></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('附件',
|
||||
'可以上传自荐书文件,大小不超过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>
|
||||
</div>
|
||||
</div>
|
||||
</Spin>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default Form.create()(Apply);
|
|
@ -0,0 +1,337 @@
|
|||
.glcc_student_apply {
|
||||
font-family: PingFang SC;
|
||||
background-image: linear-gradient(
|
||||
180deg,
|
||||
#ebf2ff 0%,
|
||||
#ebf2ff 43.09%,
|
||||
#f3f4f8 100%
|
||||
);
|
||||
padding-bottom: 120px;
|
||||
|
||||
.has-error .ant-form-explain,
|
||||
.has-error .ant-form-split {
|
||||
position: absolute;
|
||||
}
|
||||
.ant-btn-primary {
|
||||
background-color: #466aff;
|
||||
border-color: #466aff;
|
||||
&:hover {
|
||||
background-color: #5d7cff;
|
||||
border-color: #5d7cff;
|
||||
}
|
||||
&:active {
|
||||
background-color: #1140ff;
|
||||
border-color: #1140ff;
|
||||
}
|
||||
}
|
||||
.apply {
|
||||
width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.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: 38px;
|
||||
}
|
||||
.head_bold {
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
}
|
||||
.main-tit {
|
||||
margin: 25px 0;
|
||||
img {
|
||||
width: 25px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.tit-text {
|
||||
color: #333333;
|
||||
font-size: 18px;
|
||||
line-height: 30px;
|
||||
}
|
||||
}
|
||||
.form-tit {
|
||||
background-color: #e1e8ff;
|
||||
color: #333333;
|
||||
font-size: 18px;
|
||||
line-height: 56px;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
}
|
||||
.item-tit {
|
||||
width: 100%;
|
||||
line-height: 66px;
|
||||
color: #333333;
|
||||
font-size: 16px;
|
||||
border-bottom: 1px dashed #bec5d5;
|
||||
margin-bottom: 20px !important;
|
||||
|
||||
&::before {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 5px;
|
||||
height: 14px;
|
||||
margin-right: 10px;
|
||||
background-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;
|
||||
}
|
||||
.glcc_info_form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
.ant-row.ant-form-item {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
width: 50%;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.ant-form-item:nth-child(2n) {
|
||||
.ant-form-item-control-wrapper {
|
||||
padding-right: 3%;
|
||||
}
|
||||
}
|
||||
.ant-col.ant-form-item-control-wrapper {
|
||||
width: 85%;
|
||||
}
|
||||
.ant-form-item-label {
|
||||
margin-right: 15px;
|
||||
& > label::after {
|
||||
content: none;
|
||||
}
|
||||
}
|
||||
.ant-upload.ant-upload-select-picture-card {
|
||||
margin-bottom: 0;
|
||||
width: 118px;
|
||||
height: 118px;
|
||||
border: 1px solid;
|
||||
}
|
||||
.icon-tianjiadaohang:before {
|
||||
color: #b3c3db;
|
||||
}
|
||||
.ant-upload-text {
|
||||
color: #a4aabb;
|
||||
}
|
||||
.ant-input,
|
||||
.ant-select-selection,
|
||||
.ant-upload.ant-upload-select-picture-card {
|
||||
background: none !important;
|
||||
border-color: #b3c3db;
|
||||
color: #202d40;
|
||||
&:hover {
|
||||
border-color: #466aff;
|
||||
}
|
||||
}
|
||||
.has-error .ant-input:not([disabled]) {
|
||||
border-color: #ef0000;
|
||||
}
|
||||
.disabledInput,
|
||||
.disabledInput .ant-select-selection {
|
||||
background-color: #e5ebf9 !important;
|
||||
border: none;
|
||||
color: #202d40;
|
||||
}
|
||||
.projectLogo {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border: 1px dashed #b3c3db;
|
||||
border-radius: 4px;
|
||||
line-height: 120px;
|
||||
text-align: center;
|
||||
img {
|
||||
max-width: 100px;
|
||||
max-height: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-cascader-picker {
|
||||
background: none;
|
||||
&:hover {
|
||||
.ant-cascader-input {
|
||||
border: 1px solid #466aff;
|
||||
}
|
||||
}
|
||||
&:focus .ant-cascader-input {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
.introArea {
|
||||
width: 100%;
|
||||
.ant-row.ant-form-item {
|
||||
width: auto;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.ant-col.ant-form-item-control-wrapper {
|
||||
width: 90%;
|
||||
}
|
||||
.ant-form-item-label {
|
||||
width: 74px;
|
||||
}
|
||||
&.tips {
|
||||
margin-left: 40px;
|
||||
color: #e90000;
|
||||
}
|
||||
}
|
||||
.update_item {
|
||||
.ant-form-explain {
|
||||
bottom: 17px;
|
||||
}
|
||||
}
|
||||
.glcc_info_form .introArea.subInfo {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin: 40px 0 70px -60px;
|
||||
.sub {
|
||||
width: 200px;
|
||||
height: 36px;
|
||||
background-color: #466aff;
|
||||
border-color: #466aff;
|
||||
&:hover {
|
||||
background-color: #5d7cff;
|
||||
border-color: #5d7cff;
|
||||
}
|
||||
&:active {
|
||||
background-color: #1140ff;
|
||||
border-color: #1140ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
.explain {
|
||||
background-color: #f6f9fe;
|
||||
border: 2px solid;
|
||||
border-color: #ffffff;
|
||||
color: #6c7283;
|
||||
padding: 20px 30px 30px 20px;
|
||||
.c000 {
|
||||
color: #000000;
|
||||
}
|
||||
div > .c000 {
|
||||
font-weight: bold;
|
||||
}
|
||||
> div {
|
||||
margin: 10px 0;
|
||||
}
|
||||
.link {
|
||||
color: rgba(70, 106, 255, 1);
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
.applySuccess {
|
||||
height: 45px;
|
||||
border: 1px solid;
|
||||
border-color: #466aff;
|
||||
line-height: 45px;
|
||||
padding-left: 20px;
|
||||
color: #466aff;
|
||||
}
|
||||
|
||||
.glcc_btn {
|
||||
width: 90px;
|
||||
height: 32px;
|
||||
border: 1px solid;
|
||||
border-color: #b3c3db;
|
||||
border-radius: 4px;
|
||||
color: #466aff;
|
||||
font-size: 14px;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.add_task {
|
||||
margin-left: 90px;
|
||||
}
|
||||
|
||||
.delete {
|
||||
float: right;
|
||||
color: #466aff;
|
||||
cursor: pointer;
|
||||
: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;
|
||||
a {
|
||||
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);
|
||||
|
||||
.ant-select-dropdown-menu-item{
|
||||
&:hover{
|
||||
background-color: #e9efff;
|
||||
}
|
||||
}
|
||||
}
|
||||
.glcc_cascader{
|
||||
.ant-cascader-menu{
|
||||
width: 240px;
|
||||
|
||||
&::-webkit-scrollbar-track{
|
||||
background-color: #E2E9FF;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb{
|
||||
background-color: #BCCCFF;
|
||||
}
|
||||
}
|
||||
.ant-cascader-menu:first-child{
|
||||
width: 250px;
|
||||
}
|
||||
}
|
|
@ -226,6 +226,7 @@ export function TPMIndexHOC(WrappedComponent) {
|
|||
this.setState({
|
||||
showNotice:false
|
||||
})
|
||||
this.getAppdatausr();
|
||||
}
|
||||
|
||||
showCompeleteDialog=()=>{
|
||||
|
|