Merge branch 'dev_aliyun' of http://bdgit.educoder.net/Hjqreturn/educoder into dev_aliyun
commit
0a46e83f89
@ -0,0 +1,46 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import './index.css';
|
||||
import './indexPlus.css';
|
||||
import App from './App';
|
||||
|
||||
// 加之前main.js 18.1MB
|
||||
// import { message } from 'antd';
|
||||
import message from 'antd/lib/message';
|
||||
import 'antd/lib/message/style/css';
|
||||
|
||||
import { AppContainer } from 'react-hot-loader';
|
||||
|
||||
import registerServiceWorker from './registerServiceWorker';
|
||||
|
||||
import { configureUrlQuery } from 'react-url-query';
|
||||
|
||||
import history from './history';
|
||||
|
||||
// link the history used in our app to url-query so it can update the URL with it.
|
||||
configureUrlQuery({ history });
|
||||
// ----------------------------------------------------------------------------------- 请求配置
|
||||
|
||||
window.__useKindEditor = false;
|
||||
|
||||
|
||||
const render = (Component) => {
|
||||
ReactDOM.render(
|
||||
<AppContainer {...this.props} {...this.state}>
|
||||
<Component {...this.props} {...this.state}/>
|
||||
</AppContainer>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// ReactDOM.render(
|
||||
// ,
|
||||
// document.getElementById('root'));
|
||||
// registerServiceWorker();
|
||||
|
||||
render(App);
|
||||
if (module.hot) {
|
||||
module.hot.accept('./App', () => { render(App) });
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* @Description:
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-20 23:10:48
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-28 14:41:42
|
||||
*/
|
||||
const CONST = {
|
||||
jcLabel: {
|
||||
name: '任务名称',
|
||||
language: '编程语言',
|
||||
description: '描述',
|
||||
difficult: '难易度',
|
||||
category: '分类',
|
||||
openOrNot: '公开程序',
|
||||
timeLimit: '时间限制'
|
||||
},
|
||||
fontSetting: {
|
||||
title: '代码格式',
|
||||
type: 'select',
|
||||
content: [
|
||||
{
|
||||
text: '字体大小',
|
||||
value: [
|
||||
{
|
||||
key: 1,
|
||||
text: '12px',
|
||||
value: 12
|
||||
},
|
||||
{
|
||||
key: 1,
|
||||
text: '14px',
|
||||
value: 14
|
||||
},
|
||||
{
|
||||
key: 1,
|
||||
text: '16px',
|
||||
value: 16
|
||||
},
|
||||
{
|
||||
key: 1,
|
||||
text: '18px',
|
||||
value: 18
|
||||
},
|
||||
{
|
||||
key: 1,
|
||||
text: '24px',
|
||||
value: 24
|
||||
},
|
||||
{
|
||||
key: 1,
|
||||
text: '30px',
|
||||
value: 30
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
opacitySetting: {
|
||||
title: '代码格式',
|
||||
type: 'label',
|
||||
content: [
|
||||
{
|
||||
text: '字体大小',
|
||||
value: 'CTRL + S'
|
||||
},
|
||||
{
|
||||
text: '唤出快捷键列表',
|
||||
value: 'F1/ALT + F1'
|
||||
},
|
||||
{
|
||||
text: '向左缩进',
|
||||
value: 'CTRL + ['
|
||||
},
|
||||
{
|
||||
text: '向右缩进',
|
||||
value: 'CTRL + ]'
|
||||
},
|
||||
{
|
||||
text: '跳到匹配的括号',
|
||||
value: 'CTRL + SHIFT + \\'
|
||||
},
|
||||
{
|
||||
text: '转到行首',
|
||||
value: 'HOME'
|
||||
},
|
||||
{
|
||||
text: '转到行尾',
|
||||
value: 'END'
|
||||
}
|
||||
]
|
||||
},
|
||||
tagBackground: {
|
||||
1: '#52c41a',
|
||||
2: '#faad14',
|
||||
3: '#f5222d'
|
||||
},
|
||||
diffText: {
|
||||
1: '简单',
|
||||
2: '中等',
|
||||
3: '困难'
|
||||
},
|
||||
reviewResult: {
|
||||
'-1': '测试用例结果不匹配',
|
||||
'0': '评测通过',
|
||||
'1': '',
|
||||
'2': '评测超时',
|
||||
'3': '评测pod失败',
|
||||
'4': '编译失败',
|
||||
'5': '执行失败'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default CONST;
|
||||
|
@ -0,0 +1,163 @@
|
||||
.teamsLayout{background: transparent !important;}
|
||||
|
||||
.competitionstitle{
|
||||
height:50px !important;
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.competitionstitle2{
|
||||
height:50px !important;
|
||||
margin-left: 30px !important;
|
||||
background: #fff;
|
||||
width: 1200px;
|
||||
}
|
||||
.CompetitionsList{
|
||||
position: relative;
|
||||
/*max-height: 210px;*/
|
||||
}
|
||||
.competitonimg{
|
||||
position: absolute;
|
||||
right: -5px;
|
||||
width: 80px;
|
||||
top: 20px;
|
||||
}
|
||||
|
||||
.ant-menu-horizontal {
|
||||
border-bottom:none !important;
|
||||
}
|
||||
|
||||
|
||||
.competitionsvalue{
|
||||
font-size: 16px;
|
||||
font-family: PingFangSC-Medium,PingFangSC;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.competitionmr50 {
|
||||
margin-right: 50px !important;
|
||||
}
|
||||
|
||||
.CompetitionsIndex .ant-list-item{
|
||||
background: #fff !important;
|
||||
margin-top: 20px;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.CompetitionsIndex .ant-list-item{
|
||||
padding:25px;
|
||||
}
|
||||
|
||||
.CompetitionsIndex .ant-list-item-meta-title{
|
||||
height:28px;
|
||||
font-size:28px;
|
||||
font-family:PingFangSC-Regular,PingFangSC;
|
||||
font-weight:400;
|
||||
color:rgba(5,16,26,1);
|
||||
line-height:28px;
|
||||
}
|
||||
|
||||
.CompetitionsIndex .ant-list-vertical .ant-list-item-meta{
|
||||
margin-bottom: 20px !important;
|
||||
}
|
||||
|
||||
.CompetitionsIndex .ant-list-vertical .ant-list-item-action {
|
||||
margin-top: 20px;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.CompetitionsIndex .ant-list-item-action-split{
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.CompetitionsIndexdadels{
|
||||
font-family: PingFangSC-Regular,PingFangSC;
|
||||
font-weight: 400;
|
||||
color: #777777;
|
||||
margin-bottom: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.CompetitionsIndexbottomvalue{
|
||||
font-size: 24px;
|
||||
font-family: ArialMT;
|
||||
color: rgba(5,16,26,1);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.CompetitionsIndex .gutter-row{
|
||||
/*margin-right:20px;*/
|
||||
width: 33%;
|
||||
}
|
||||
|
||||
.pt50{
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
.competitionstitles{
|
||||
max-width: 789px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
margin-right: 15px;
|
||||
color:#000 !important;
|
||||
}
|
||||
|
||||
.competitionsrelative{
|
||||
position: absolute;
|
||||
top: 28px;
|
||||
}
|
||||
|
||||
.CompetitionsList:hover{
|
||||
/*box-shadow: 0 2px 6px rgba(51,51,51,.09);*/
|
||||
box-shadow:3px 4px 10px 2px rgba(229,229,229,0.5);
|
||||
opacity: 1;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.endedfont{
|
||||
color:#000 !important;
|
||||
}
|
||||
.CompetitionsListzhezhao{
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 1206px;
|
||||
height: 100%;
|
||||
z-index: 10000;
|
||||
display: none;
|
||||
background: rgba(0,0,0,0.33);
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
font-size: 22px;
|
||||
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
flex-direction: column;
|
||||
}
|
||||
.CompetitionsList:hover .CompetitionsListzhezhao{
|
||||
display: block;
|
||||
display: flex;
|
||||
}
|
||||
.competitionstitlesshou:hover a{
|
||||
cursor: pointer;
|
||||
color: #1c91e8 !important;
|
||||
}
|
||||
|
||||
.competitionstitlesshou{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
.Competitionshead{
|
||||
background-color: #2d28ba !important;
|
||||
background-position: center !important;
|
||||
background-position: 50% !important;
|
||||
background-repeat: no-repeat !important;
|
||||
}
|
||||
|
||||
.span666{
|
||||
color:#666666 !important;
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
.teamsLayout{background: transparent !important;}
|
||||
.teamsLayout .teamsLayoutitle{
|
||||
font-size:18px;
|
||||
font-family:PingFangSC-Semibold,PingFang SC;
|
||||
font-weight:600;
|
||||
color:rgba(5,16,26,1);
|
||||
line-height:25px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.teamsLayoutTable .ant-table-bordered .ant-table-thead > tr > th, .ant-table-bordered .ant-table-tbody > tr > td {
|
||||
border-right: 1px solid transparent !important;
|
||||
}
|
||||
|
||||
.teamsLayoutTable .ant-table-body .ant-table-thead > tr> th:nth-last-child(1){
|
||||
border-right: 1px solid #e8e8e8 !important;
|
||||
}
|
||||
|
||||
.teamsLayoutTable .ant-table-body .ant-table-tbody > tr> td:nth-last-child(1){
|
||||
border-right: 1px solid #e8e8e8 !important;
|
||||
}
|
||||
|
||||
.teamsLayoutTable .ant-table-bordered .ant-table-thead > tr > th{
|
||||
background:#EEEEEE;
|
||||
font-size: 14px;
|
||||
font-family: PingFangSC-Regular,PingFang SC;
|
||||
font-weight: 400;
|
||||
color: rgba(102,102,102,1);
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.teamsLayoutTable .ant-table-bordered .ant-table-tbody > tr > th{
|
||||
background:#EEEEEE;
|
||||
font-size:14px;
|
||||
font-family:PingFangSC-Regular,PingFang SC;
|
||||
font-weight:400;
|
||||
color:rgba(5,16,26,1);
|
||||
line-height:20px;
|
||||
}
|
||||
|
||||
.teamsLayout .mt40{
|
||||
margin-top: 40px !important;
|
||||
}
|
||||
|
||||
.teamsLayoutheji{
|
||||
color: #878787;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.teamsLayoucolor-orange {
|
||||
color: #ff6800!important;
|
||||
font-size: 16px;
|
||||
}
|
@ -0,0 +1,438 @@
|
||||
.teamsLayout{background: transparent !important;}
|
||||
|
||||
.teamsLayout .ant-layout-sider{
|
||||
background: transparent !important;
|
||||
flex: 0 0 180px !important;
|
||||
max-width: 180px !important;
|
||||
min-width: 180px !important;
|
||||
width: 180px !important;
|
||||
}
|
||||
.teamsLayout .teamsLayoutitle{
|
||||
font-size:18px;
|
||||
font-family:PingFangSC-Semibold,PingFang SC;
|
||||
font-weight:600;
|
||||
color:rgba(5,16,26,1);
|
||||
line-height:25px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.teamsLayoutTable .ant-table-bordered .ant-table-thead > tr > th, .ant-table-bordered .ant-table-tbody > tr > td {
|
||||
border-right: 1px solid transparent !important;
|
||||
}
|
||||
|
||||
.teamsLayoutTable .ant-table-body .ant-table-thead > tr> th:nth-last-child(1){
|
||||
border-right: 1px solid #e8e8e8 !important;
|
||||
}
|
||||
|
||||
.teamsLayoutTable .ant-table-body .ant-table-tbody > tr> td:nth-last-child(1){
|
||||
border-right: 1px solid #e8e8e8 !important;
|
||||
}
|
||||
|
||||
.teamsLayoutTable .ant-table-bordered .ant-table-thead > tr > th{
|
||||
background:#EEEEEE;
|
||||
font-size: 14px;
|
||||
font-family: PingFangSC-Regular,PingFang SC;
|
||||
font-weight: 400;
|
||||
color: rgba(102,102,102,1);
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.teamsLayoutTable .ant-table-bordered .ant-table-tbody > tr > th{
|
||||
background:#EEEEEE;
|
||||
font-size:14px;
|
||||
font-family:PingFangSC-Regular,PingFang SC;
|
||||
font-weight:400;
|
||||
color:rgba(5,16,26,1);
|
||||
line-height:20px;
|
||||
}
|
||||
|
||||
.teamsLayout .mt40{
|
||||
margin-top: 40px !important;
|
||||
}
|
||||
|
||||
.teamsLayoutheji{
|
||||
color: #878787;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.teamsLayoucolor-orange {
|
||||
color: #ff6800 !important;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.CompetitionCommonbanner{
|
||||
padding: 20px;
|
||||
background:rgba(255,255,255,1);
|
||||
box-shadow:3px 2px 12px 2px rgba(0,0,0,0.05);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.CompetitionCommonbannerfont{
|
||||
height:100%;
|
||||
width: 365px !important;
|
||||
line-height: 34px;
|
||||
}
|
||||
|
||||
.CompetitionCommonbannerfont .competitionbannerdiv:nth-child(1){
|
||||
max-height:100px;
|
||||
font-size:25px;
|
||||
font-weight:400;
|
||||
color:rgba(5,16,26,1);
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.CompetitionCommonbannerfont .competitionbannerdiv:nth-child(2){
|
||||
max-height: 70px;
|
||||
font-size:16px;
|
||||
font-weight:400;
|
||||
/*color:rgba(155,155,155,1);*/
|
||||
color:#05101A;
|
||||
}
|
||||
|
||||
.CompetitionCommonbannerfont .competitionbannerdiv:nth-child(3){
|
||||
max-height: 70px;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
/*color: rgba(155,155,155,1);*/
|
||||
color:#05101A;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.Competitioncolor9b{
|
||||
color: #9B9B9B;
|
||||
}
|
||||
|
||||
.Competitioncolor77{
|
||||
color: #777777;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.Competitioncolor516{
|
||||
font-size:24px;
|
||||
color:rgba(5,16,26,1);
|
||||
}
|
||||
|
||||
.Competitionfontsize22{
|
||||
font-size:22px;
|
||||
font-weight:500;
|
||||
color:rgba(255,255,255,1);
|
||||
}
|
||||
|
||||
.Competitionfontsize16{
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
color: rgba(102,102,102,1);
|
||||
}
|
||||
|
||||
.ant-layout-sider {
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
background: #001529;
|
||||
-webkit-transition: all 0.2s;
|
||||
-o-transition: all 0.2s;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.CompetitionMenu .ant-menu-item::after {
|
||||
left: 0px !important;
|
||||
right: auto;
|
||||
border-right: 5px solid #4CACFF;
|
||||
}
|
||||
|
||||
.CompetitionMenu .ant-menu-item{
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
background:none;
|
||||
color:#666;
|
||||
}
|
||||
|
||||
.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.CompetitionMenu .ant-menu-item:not(:last-child){
|
||||
margin-bottom: 40px;
|
||||
background: transparent;
|
||||
color:#666;
|
||||
}
|
||||
|
||||
.CompetitionMenu .ant-menu-item{
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.CompetitionMenu .ant-menu-item-selected {
|
||||
color: rgba(76,172,255,1) !important;
|
||||
}
|
||||
|
||||
.CompetitionMenu{
|
||||
width: 145px;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(239,239,239,1);
|
||||
padding-top: 20px;
|
||||
padding-bottom: 40px !important;
|
||||
}
|
||||
|
||||
.teamsLayoutleft{
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.Competitioncharts{
|
||||
font-size: 24px;
|
||||
color: rgba(5,16,26,1);
|
||||
}
|
||||
.Competitionfirst{
|
||||
width:233px;
|
||||
height:298px;
|
||||
background:rgba(250,250,250,1);
|
||||
box-shadow:0px 2px 8px 2px rgba(255,134,34,0.5);
|
||||
border-radius:5px;
|
||||
}
|
||||
.Competitionsecondary{
|
||||
width:234px;
|
||||
height:298px;
|
||||
background:rgba(250,250,250,1);
|
||||
box-shadow:0px 3px 5px 0px rgba(254,190,154,1);
|
||||
border-radius:5px;
|
||||
}
|
||||
|
||||
.Competitionthird{
|
||||
width: 234px;
|
||||
height: 298px;
|
||||
background: rgba(250,250,250,1);
|
||||
box-shadow: 0px 4px 5px 0px rgba(200,200,202,1);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.Competition399{
|
||||
height:399px;
|
||||
}
|
||||
|
||||
.Competitiontransparent table{
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.Commonimg{
|
||||
position: absolute;
|
||||
right: -5px;
|
||||
width:93px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
.Competitionthirdbox{
|
||||
width:234px;
|
||||
height:167px;
|
||||
background:rgba(223,223,225,1);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.Competitionfirstbox{
|
||||
width:233px;
|
||||
height:167px;
|
||||
background:rgba(255,231,160,1);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.Competitionsecondarybox{
|
||||
width:234px;
|
||||
height:167px;
|
||||
background:rgba(253,230,217,1);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.rankingimg{
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50% !important;
|
||||
box-shadow: 0px 0px 12px rgba(0,0,0,0.2);
|
||||
border: 2px solid #459BE5;
|
||||
}
|
||||
|
||||
.Competitioncenter{
|
||||
text-align: center;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
|
||||
.jinshaifont{
|
||||
font-size: 16px;
|
||||
color: rgba(5,16,26,1);
|
||||
margin-top: 13px !important;
|
||||
}
|
||||
|
||||
.Competitionthird .ant-card-body {
|
||||
padding: 12px;
|
||||
zoom: 1;
|
||||
}
|
||||
|
||||
/*.Competitionthird .ant-card-meta-title{*/
|
||||
/*margin-bottom: 0px !important;*/
|
||||
/*}*/
|
||||
|
||||
.Competitionfirst .ant-card-body {
|
||||
padding: 12px;
|
||||
zoom: 1;
|
||||
}
|
||||
|
||||
.Competitionsecondary .ant-card-body {
|
||||
padding: 12px;
|
||||
zoom: 1;
|
||||
}
|
||||
|
||||
.center{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.rankfonttop{
|
||||
font-size:14px;
|
||||
color:rgba(102,102,102,1);
|
||||
}
|
||||
|
||||
.rankfontmid{
|
||||
font-size:18px;
|
||||
color:rgba(102,102,102,1);
|
||||
}
|
||||
|
||||
.rankfontbottom{
|
||||
font-size:26px;
|
||||
color:rgba(165,91,41,1);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.rankfontbottoms{
|
||||
font-size:28px;
|
||||
color:rgba(165,91,41,1);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.Competitionuserimg{
|
||||
width: 64px;
|
||||
height: 63px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #459BE5;
|
||||
}
|
||||
|
||||
.CompetitionsListzhezhao{
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 1206px;
|
||||
height: 100%;
|
||||
z-index: 10000;
|
||||
display: none;
|
||||
background: rgba(0,0,0,0.33);
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
font-size: 22px;
|
||||
|
||||
}
|
||||
|
||||
.relative{position: relative;}
|
||||
|
||||
.relative:hover .CompetitionsListzhezhao{
|
||||
display: block;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.image_urlbox{
|
||||
width: 790px;
|
||||
height: 340px;
|
||||
}
|
||||
|
||||
.CompetitionContents{
|
||||
background: #fff !important;
|
||||
padding: 40px;
|
||||
box-shadow: 3px 2px 12px 2px rgba(0,0,0,0.05);
|
||||
border: 1px solid rgba(239,239,239,1);
|
||||
}
|
||||
|
||||
.rankbeicenter{
|
||||
text-align: center;
|
||||
}
|
||||
.rankbei{
|
||||
font-size: 16px;
|
||||
color: rgba(119,119,119,1);
|
||||
|
||||
}
|
||||
|
||||
.youranklist{
|
||||
background: rgba(226,241,255,1);
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.ranknames{
|
||||
font-size: 16px;
|
||||
color: rgba(62,62,62,1);
|
||||
}
|
||||
|
||||
.ranknameslast{
|
||||
font-size:16px;
|
||||
color:rgba(12,158,254,1);
|
||||
}
|
||||
|
||||
.textleft{
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.textright{
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.userranksclass{
|
||||
text-align: left;
|
||||
width: 18%;
|
||||
padding-left: 12px;
|
||||
margin-right: 28px;
|
||||
}
|
||||
|
||||
.Commonimgbox{
|
||||
width: 800px !important;
|
||||
}
|
||||
|
||||
.CompetitionCommonbannerfont{
|
||||
width: 350px !important;
|
||||
margin-left:10px;
|
||||
}
|
||||
|
||||
.color000{
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.cursorpointer{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.rankfonttop{
|
||||
overflow:hidden;
|
||||
text-overflow:ellipsis;
|
||||
white-space:nowrap
|
||||
}
|
||||
|
||||
.usernamebox{
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
cursor: default;
|
||||
max-width: 100px;
|
||||
display: inherit;
|
||||
}
|
||||
|
||||
.competimgabsolute{
|
||||
position: absolute;
|
||||
left: 72px;
|
||||
bottom: -10px;
|
||||
}
|
||||
.competimgabsolute .ant-badge-count{
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.competimgabsoluteijmg{
|
||||
position: absolute;
|
||||
left: -11px;
|
||||
bottom: 0px;
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
import React, { Component } from 'react';
|
||||
import {Button,Layout} from 'antd';
|
||||
import axios from 'axios';
|
||||
import {markdownToHTML,getImageUrl,AttachmentList} from 'educoder';
|
||||
|
||||
|
||||
const { Header, Footer, Sider, Content } = Layout;
|
||||
class CompetitionContents extends Component{
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state={
|
||||
hash:undefined
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
window.document.title = '竞赛';
|
||||
this.props.MdifHasAnchorJustScorll();
|
||||
}
|
||||
|
||||
render() {
|
||||
let {mdContentdata, data} = this.props;
|
||||
//mdhash滚动
|
||||
this.props.MdifHasAnchorJustScorll();
|
||||
return (
|
||||
|
||||
<div className={"fr"}>
|
||||
{data && data.permission.editable === true ? this.props.Competitionedittype === false ? this.props.has_url === false ?
|
||||
<Button className={"fr"} type="primary" ghost onClick={() => this.props.Competitionedit()}>
|
||||
编辑
|
||||
</Button>:"":"":""}
|
||||
<div className={this.props.current_user&&this.props.current_user.admin===true||this.props.current_user&&this.props.current_user.business===true?"mt50 mb100 ":"mb100 "}>
|
||||
<Content className={"markdown-body"} dangerouslySetInnerHTML={{__html: markdownToHTML(mdContentdata===undefined?"":mdContentdata.md_content===undefined||mdContentdata.md_content===null?"":mdContentdata.md_content).replace(/▁/g, "▁▁▁")}}>
|
||||
</Content>
|
||||
|
||||
<div className={"mt30"}>
|
||||
<AttachmentList {...this.props} {...this.state} attachments={mdContentdata===undefined?[]:mdContentdata.attachments===undefined?[]:mdContentdata.attachments}></AttachmentList>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
export default CompetitionContents;
|
@ -0,0 +1,232 @@
|
||||
import React, { Component } from 'react';
|
||||
import {Button, Card, Row, Col ,Upload,Icon,message,Tabs} from 'antd';
|
||||
import axios from 'axios';
|
||||
import {getImageUrl,getUrl,appendFileSizeToUploadFileAll,appendFileSizeToUploadFile} from 'educoder';
|
||||
import TPMMDEditor from '../../../tpm/challengesnew/TPMMDEditor';
|
||||
const { TabPane } = Tabs;
|
||||
class CompetitionContentsMd extends Component{
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.contentMdRef = React.createRef();
|
||||
this.state={
|
||||
contentFileList:[],
|
||||
chartmodule_id:undefined
|
||||
}
|
||||
}
|
||||
componentDidUpdate =(prevState)=>{
|
||||
if(prevState!=this.props){
|
||||
this.getchartdata();
|
||||
}
|
||||
}
|
||||
componentDidMount(){
|
||||
window.document.title = '竞赛';
|
||||
|
||||
this.getchartdata()
|
||||
}
|
||||
|
||||
getchartdata=()=>{
|
||||
let {mdContentdata,chart_rules}=this.props;
|
||||
|
||||
// is_pdf: false
|
||||
if(this.props.module_type==="chart"){
|
||||
let type=true;
|
||||
if(chart_rules===undefined){
|
||||
|
||||
}else{
|
||||
chart_rules.rule_contents.map((items,keys)=>{
|
||||
debugger
|
||||
if(parseInt(this.props.tabkey)===items.competition_stage_id){
|
||||
console.log(items)
|
||||
this.contentMdRef.current.setValue(items.content);
|
||||
this.setState({
|
||||
contentFileList:undefined,
|
||||
chartmodule_id:items.id
|
||||
})
|
||||
type=false;
|
||||
}
|
||||
})
|
||||
|
||||
if(type===true){
|
||||
this.contentMdRef.current.setValue("");
|
||||
this.setState({
|
||||
contentFileList:undefined,
|
||||
chartmodule_id:undefined
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}else{
|
||||
let contentFileList = mdContentdata===undefined?[]:mdContentdata.attachments===undefined?[]:mdContentdata.attachments.map((item) => {
|
||||
return {
|
||||
id: item.id,
|
||||
uid: item.id,
|
||||
name: appendFileSizeToUploadFile(item),
|
||||
url: item.url,
|
||||
filesize: item.filesize,
|
||||
status: 'done',
|
||||
response:{id: item.id}
|
||||
}
|
||||
})
|
||||
this.setState({
|
||||
contentFileList:contentFileList
|
||||
})
|
||||
this.contentMdRef.current.setValue(mdContentdata===undefined?"":mdContentdata.md_content===undefined?"":mdContentdata.md_content || '')
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
handleContentUploadChange = (info) => {
|
||||
if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
|
||||
let contentFileList = info.fileList;
|
||||
this.setState({ contentFileList: appendFileSizeToUploadFileAll(contentFileList) });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onAttachmentRemove = (file, stateName) => {
|
||||
if(file.response!=undefined){
|
||||
this.props.confirm({
|
||||
content: '是否确认删除?',
|
||||
|
||||
onOk: () => {
|
||||
this.deleteAttachment(file, stateName)
|
||||
},
|
||||
onCancel() {
|
||||
console.log('Cancel');
|
||||
},
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
deleteAttachment = (file, stateName) => {
|
||||
// 初次上传不能直接取uid
|
||||
const url = `/attachments/${file.response ? file.response.id : file.uid}.json`
|
||||
axios.delete(url, {
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.data) {
|
||||
const { status } = response.data;
|
||||
if (status == 0) {
|
||||
console.log('--- success')
|
||||
this.props.showNotification(response.data.message);
|
||||
this.setState((state) => {
|
||||
const index = state[stateName].indexOf(file);
|
||||
const newFileList = state[stateName].slice();
|
||||
newFileList.splice(index, 1);
|
||||
return {
|
||||
[stateName]: newFileList,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
handleSubmit = () => {
|
||||
let {contentFileList}=this.state;
|
||||
const mdContnet = this.contentMdRef.current.getValue().trim();
|
||||
// if(mdContnet.length>10000){
|
||||
// this.props.showNotification("内容超过10000个字");
|
||||
// return
|
||||
// }
|
||||
let attachment_ids=undefined
|
||||
if(contentFileList!=undefined){
|
||||
attachment_ids= contentFileList.map(item => {
|
||||
return item.response ? item.response.id : item.id
|
||||
})
|
||||
}
|
||||
|
||||
let newstage_id=parseInt(this.props.tabkey)===0||null?undefined:parseInt(this.props.tabkey)
|
||||
let data={}
|
||||
if(this.props.module_type==="chart"){
|
||||
data={
|
||||
md_content_id:this.state.chartmodule_id,
|
||||
competition_module_id:this.props.module_id,
|
||||
stage_id:newstage_id,
|
||||
content:mdContnet,
|
||||
}
|
||||
}else{
|
||||
data={
|
||||
md_content_id:this.props.mdContentdata.md_id,
|
||||
competition_module_id:this.props.mdContentdata.id,
|
||||
content:mdContnet,
|
||||
attachment_ids:attachment_ids
|
||||
}
|
||||
}
|
||||
|
||||
let url=`/competitions/${this.props.match.params.identifier}/update_md_content.json`;
|
||||
axios.post(url,data
|
||||
).then((response) => {
|
||||
if(response.data.status===0){
|
||||
this.props.showNotification(response.data.message);
|
||||
this.props.getlistdata(this.props.thiskeys,this.props.tabkey);
|
||||
this.props.hideCompetitionedit();
|
||||
}else{
|
||||
this.props.showNotification(response.data.message);
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
|
||||
}
|
||||
render() {
|
||||
let {contentFileList}=this.state;
|
||||
let {chart_rules}=this.props;
|
||||
const uploadProps = {
|
||||
width: 600,
|
||||
fileList: contentFileList,
|
||||
multiple: true,
|
||||
// https://github.com/ant-design/ant-design/issues/15505
|
||||
// showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。
|
||||
// showUploadList: false,
|
||||
action: `${getUrl()}/api/attachments.json`,
|
||||
onChange: this.handleContentUploadChange,
|
||||
onRemove: (file) => this.onAttachmentRemove(file, 'contentFileList'),
|
||||
beforeUpload: (file) => {
|
||||
console.log('beforeUpload', file.name);
|
||||
const isLt150M = file.size / 1024 / 1024 < 150;
|
||||
if (!isLt150M) {
|
||||
this.props.showNotification("文件大小必须小于150MB");
|
||||
}
|
||||
return isLt150M;
|
||||
},
|
||||
};
|
||||
// console.log(this.props.tabkey)
|
||||
// console.log(chart_rules)
|
||||
console.log(this.props.mdContentdata)
|
||||
return (
|
||||
<div>
|
||||
{chart_rules===undefined?"":this.props.module_type==="chart"?<Tabs activeKey={this.props.tabkey} onChange={(e)=>this.props.Competitioncallback(e)}>
|
||||
|
||||
{chart_rules.stages.map((item,key)=>{
|
||||
return(
|
||||
<TabPane tab={item.name} key={item.id===null?0:item.id}></TabPane>
|
||||
)
|
||||
})}
|
||||
|
||||
</Tabs>:""}
|
||||
<TPMMDEditor ref={this.contentMdRef} placeholder="请输入内容说明" mdID={'courseContentMD'} refreshTimeout={1500}
|
||||
className="courseMessageMD" initValue={this.state.description}></TPMMDEditor>
|
||||
{this.props.module_type==="chart"?"":<Upload {...uploadProps} className="upload_1 newWorkUpload">
|
||||
<Button className="uploadBtn">
|
||||
<Icon type="upload" /> 上传附件
|
||||
</Button>
|
||||
(单个文件150M以内)
|
||||
</Upload>}
|
||||
|
||||
<div className="clearfix mt30 mb30">
|
||||
{/* htmlType="submit" */}
|
||||
<Button type="primary" onClick={this.handleSubmit} className="defalutSubmitbtn fl mr20">提交</Button>
|
||||
<a className="defalutCancelbtn fl" onClick={() => this.props.hideCompetitionedit()}>取消</ a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
export default CompetitionContentsMd;
|
@ -0,0 +1,64 @@
|
||||
import React, { Component } from 'react';
|
||||
import {Tabs} from 'antd';
|
||||
import axios from 'axios';
|
||||
import {markdownToHTML,getImageUrl,AttachmentList} from 'educoder';
|
||||
import CompetitionContentspdfdownload from './CompetitionContentspdfChild/CompetitionContentspdfdownload';
|
||||
import CompetitionContentspdfpeopledata from './CompetitionContentspdfChild/CompetitionContentspdfpeopledata';
|
||||
// import NoneData from "../../../courses/shixunHomework/shixunHomework";
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
class CompetitionContentspdf extends Component{
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state={
|
||||
Tabskey:"1"
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
window.document.title = '竞赛';
|
||||
let query=this.props.location&&this.props.location.search;
|
||||
const types = query.split('user_id=')
|
||||
if(types[1]===undefined){
|
||||
}else{
|
||||
this.setState({
|
||||
Tabskey:"2"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Competitioncallback=(key)=>{
|
||||
this.setState({
|
||||
Tabskey:key
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
|
||||
return (
|
||||
|
||||
<div className={"fr"}>
|
||||
<div className={"mb100 "}>
|
||||
<Tabs defaultActiveKey="1" onChange={(e) => this.Competitioncallback(e)} activeKey={this.state.Tabskey}>
|
||||
<TabPane tab="获奖证书下载" key="1" >
|
||||
{this.state.Tabskey==="1"?<CompetitionContentspdfdownload
|
||||
{...this.props}
|
||||
{...this.state}
|
||||
Competitioncallback={(e)=>this.Competitioncallback(e)}
|
||||
/>:""}
|
||||
</TabPane>
|
||||
<TabPane tab="完善个人信息" key="2">
|
||||
{this.state.Tabskey==="2"?<CompetitionContentspdfpeopledata
|
||||
{...this.props}
|
||||
{...this.state}
|
||||
/>:""}
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
export default CompetitionContentspdf;
|
@ -0,0 +1,24 @@
|
||||
.pdfdownload {
|
||||
max-width: 791px;
|
||||
height: 40px;
|
||||
background: rgba(249, 249, 249, 1);
|
||||
line-height: 40px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.pdfpicture {
|
||||
font-size: 16px;
|
||||
color: rgba(0, 0, 0, 1);
|
||||
}
|
||||
|
||||
.pdfdownloadfont4CACFF {
|
||||
color: #4CACFF !important;
|
||||
}
|
||||
|
||||
.pdfdownloadfont00CC5F {
|
||||
color: #00CC5F;
|
||||
}
|
||||
|
||||
.pdfdownloadfontFF6602 {
|
||||
color: #FF6602;
|
||||
}
|
@ -0,0 +1,270 @@
|
||||
import React, {Component} from 'react';
|
||||
import {Button, Layout, Input, Form} from 'antd';
|
||||
import axios from 'axios';
|
||||
import {getImageUrl} from 'educoder';
|
||||
import mycompetotionchild from './mycompetotionchild.css';
|
||||
import {getHiddenName} from "../../../../user/account/AccountBasicEdit";
|
||||
import '../../../../courses/css/Courses.css'
|
||||
|
||||
export const identityMap = {"teacher": "教师", "student": "学生", "professional": "专业人士"}
|
||||
|
||||
class Mailboxvalidation extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
basicInfo: {},
|
||||
updating: '',
|
||||
secondsFlag: false,
|
||||
seconds: 60,
|
||||
phonebool: false,
|
||||
emailbool: false,
|
||||
formationdata: [],
|
||||
bank_account_editable: false,
|
||||
leader: false,
|
||||
bank_account: undefined,
|
||||
certification: 1
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
window.document.title = '竞赛';
|
||||
// console.log("3获取用户信息");
|
||||
// console.log(this.props);
|
||||
}
|
||||
|
||||
|
||||
// 绑定邮箱
|
||||
onEmailSubmit = () => {
|
||||
this.props.form.validateFieldsAndScroll((err, values) => {
|
||||
if (!err) {
|
||||
let {id} = this.props.userdata;
|
||||
let reg = /^[a-zA-Z0-9]+([.\-_\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/;
|
||||
if (reg.test(values.email)) {
|
||||
let url = `/users/accounts/${id}/email_bind.json`
|
||||
axios.post((url), {
|
||||
email: values.email,
|
||||
code: values.emailValidateCode
|
||||
}).then((result) => {
|
||||
if (result) {
|
||||
this.props.showNotification("邮箱地址绑定成功!");
|
||||
this.hideUpdating(2);
|
||||
this.props.getdata(id);
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error);
|
||||
})
|
||||
} else {
|
||||
this.props.showNotification("请输入正确的邮箱地址");
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
//取消编辑
|
||||
hideUpdating = (i) => {
|
||||
if (i === 1) {
|
||||
this.props.hideUpdating(1);
|
||||
} else if (i === 2) {
|
||||
this.props.hideUpdating(2);
|
||||
|
||||
|
||||
} else if (i === 3) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 获取验证码
|
||||
getCode = (index) => {
|
||||
let url = `/accounts/get_verification_code.json`
|
||||
let login = '';
|
||||
let values = this.props.form.getFieldsValue();
|
||||
if (index == 3) {
|
||||
//绑定手机号码
|
||||
login = values.phone;
|
||||
let reg = /^1\d{10}$/;
|
||||
if (reg.test(login) == false) {
|
||||
this.props.showNotification(`请先输入正确的手机号码`);
|
||||
return;
|
||||
}
|
||||
} else if (index == 4) {
|
||||
// 绑定邮箱
|
||||
login = values.email;
|
||||
let reg = /^[a-zA-Z0-9]+([.\-_\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/;
|
||||
if (reg.test(login) == false) {
|
||||
this.props.showNotification(`请先输入正确的邮箱地址`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
let type = index;
|
||||
if (!login) {
|
||||
this.props.showNotification(`请先输入${index == 3 ? "手机号码" : "邮箱地址"}`);
|
||||
return;
|
||||
}
|
||||
axios.get((url), {
|
||||
params: {
|
||||
login, type
|
||||
}
|
||||
}).then((result) => {
|
||||
if (result) {
|
||||
// 倒计时
|
||||
this.setState({
|
||||
secondsFlag: true
|
||||
})
|
||||
this.remainTime();
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error);
|
||||
})
|
||||
}
|
||||
|
||||
// 获取验证码倒计时
|
||||
remainTime = () => {
|
||||
this.setState({
|
||||
seconds: 60
|
||||
})
|
||||
this.timer = setInterval(() => {
|
||||
let {seconds} = this.state;
|
||||
let s = parseInt(seconds) - 1;
|
||||
if (s > -1) {
|
||||
this.setState({
|
||||
seconds: s
|
||||
})
|
||||
} else {
|
||||
this.setState({
|
||||
secondsFlag: false
|
||||
})
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
phonebools = () => {
|
||||
this.setState({
|
||||
phonebool: true
|
||||
})
|
||||
}
|
||||
|
||||
emailbools = () => {
|
||||
console.log("点击了邮箱");
|
||||
this.setState({
|
||||
emailbool: true
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
const {getFieldDecorator} = this.props.form;
|
||||
const {updating, seconds, secondsFlag, basicInfo, phonebool, emailbool, certification, formationdata, bank_account_editable, leader, bank_account} = this.state
|
||||
console.log(emailbool);
|
||||
return (
|
||||
<div>
|
||||
<style>{`
|
||||
|
||||
.flexRow {
|
||||
padding: 20px 0;
|
||||
}
|
||||
.flexRow .name {
|
||||
margin-left: 12px;
|
||||
color: #666666;
|
||||
|
||||
text-align: center;
|
||||
flex: 0 0 100px;
|
||||
}
|
||||
.flexRow .description {
|
||||
margin-left: 10px;
|
||||
flex: 1;
|
||||
color: #CDCDCD;
|
||||
}
|
||||
.description span {
|
||||
margin-right: 20px;
|
||||
color: #05101A;
|
||||
}
|
||||
.flexRow .status {
|
||||
width: 100px;
|
||||
color: #28AC7F;
|
||||
text-align: right;
|
||||
}
|
||||
.flexTable .flexTable {
|
||||
border-bottom: 1px solid #EBEBEB;
|
||||
}
|
||||
|
||||
.settingForm label{
|
||||
color: #666666;
|
||||
font-size: 14px !important ;
|
||||
}
|
||||
.settingForm input {
|
||||
width: 340px;
|
||||
height: 40px;
|
||||
}
|
||||
.settingForm input.validateInput {
|
||||
width: 220px;
|
||||
}
|
||||
.settingForm .formItemInline button {
|
||||
width: 110px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.settingForm .ant-form-item-label {
|
||||
text-align: left;
|
||||
width: 84px;
|
||||
}
|
||||
|
||||
|
||||
.formItemInline .ant-form-explain{
|
||||
position:absolute;
|
||||
bottom:-22px;
|
||||
left:0px;
|
||||
width:100%;
|
||||
}
|
||||
`}</style>
|
||||
<div className="settingForm ml38">
|
||||
<React.Fragment>
|
||||
<Form>
|
||||
<Form.Item
|
||||
label="邮箱地址"
|
||||
className="formItemInline hideRequireTag mb20 mt20"
|
||||
>
|
||||
{getFieldDecorator('email', {
|
||||
rules: [{
|
||||
// initialValue: this.state.cityDefaultValue,
|
||||
required: true,
|
||||
message: basicInfo && basicInfo.mail ? '请输入要更换的新邮箱地址' : '请输入邮箱地址',
|
||||
}],
|
||||
})(
|
||||
<Input placeholder={`${basicInfo && basicInfo.mail ? '请输入要更换的新邮箱地址' : '请输入邮箱地址'}`}></Input>
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="邮箱验证码"
|
||||
className="mb20 formItemInline hideRequireTag"
|
||||
>
|
||||
{getFieldDecorator('emailValidateCode', {
|
||||
rules: [{
|
||||
// initialValue: this.state.cityDefaultValue,
|
||||
required: true,
|
||||
message: '请输入邮箱收到的验证码',
|
||||
}],
|
||||
})(
|
||||
<Input placeholder="请输入邮箱收到的验证码" className="validateInput"></Input>
|
||||
)}
|
||||
<Button type="primary" disabled={secondsFlag} onClick={() => this.getCode(4)}>
|
||||
{!secondsFlag ? "获取验证码" : `重新发送${seconds}s`}</Button>
|
||||
</Form.Item>
|
||||
|
||||
<div className="mb20" style={{marginLeft: '204px'}}>
|
||||
<Button type="primary" onClick={() => this.onEmailSubmit()}>确定</Button>
|
||||
<Button type="primary grayBtn" style={{marginLeft: '20px'}}
|
||||
onClick={() => this.hideUpdating(2)}>取消</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</React.Fragment>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const Mailboxvalidations = Form.create({name: 'Mailboxvalidation'})(Mailboxvalidation);
|
||||
|
||||
export default Mailboxvalidations;
|
||||
|
@ -0,0 +1,262 @@
|
||||
import React, {Component} from 'react';
|
||||
import {Button, Layout, Input, Form} from 'antd';
|
||||
import axios from 'axios';
|
||||
import {getImageUrl} from 'educoder';
|
||||
import mycompetotionchild from './mycompetotionchild.css';
|
||||
import {getHiddenName} from "../../../../user/account/AccountBasicEdit";
|
||||
import '../../../../courses/css/Courses.css'
|
||||
import RealNameCertificationModal from "../../../../user/modal/RealNameCertificationModal";
|
||||
|
||||
export const identityMap = {"teacher": "教师", "student": "学生", "professional": "专业人士"}
|
||||
|
||||
class Phonenumberverification extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
updating: '',
|
||||
secondsFlag: false,
|
||||
seconds: 60,
|
||||
phonebool: false,
|
||||
emailbool: false,
|
||||
formationdata: [],
|
||||
bank_account_editable: false,
|
||||
leader: false,
|
||||
bank_account: undefined,
|
||||
certification: 1
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
window.document.title = '竞赛';
|
||||
// console.log("获取用户信息");
|
||||
// console.log(this.props);
|
||||
}
|
||||
|
||||
|
||||
// 绑定手机
|
||||
onPhoneSubmit = () => {
|
||||
this.props.form.validateFieldsAndScroll((err, values) => {
|
||||
if (!err) {
|
||||
let {id} = this.props.userdata;
|
||||
let reg = /^1\d{10}$/;
|
||||
if (reg.test(values.phone)) {
|
||||
let url = `/users/accounts/${id}/phone_bind.json`
|
||||
axios.post((url), {
|
||||
phone: values.phone,
|
||||
code: values.phoneValidateCode
|
||||
}).then((result) => {
|
||||
if (result) {
|
||||
this.props.showNotification("手机号码绑定成功!");
|
||||
this.props.hideUpdating(1)
|
||||
this.props.getdata(id);
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error);
|
||||
})
|
||||
} else {
|
||||
this.props.showNotification("请输入有效的11位手机号码");
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
//取消编辑
|
||||
hideUpdating = (i) => {
|
||||
if (i === 1) {
|
||||
this.props.hideUpdating(1);
|
||||
} else if (i === 2) {
|
||||
this.props.hideUpdating(2);
|
||||
|
||||
} else if (i === 3) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 获取验证码
|
||||
getCode = (index) => {
|
||||
let url = `/accounts/get_verification_code.json`
|
||||
let login = '';
|
||||
let values = this.props.form.getFieldsValue();
|
||||
if (index == 3) {
|
||||
//绑定手机号码
|
||||
login = values.phone;
|
||||
let reg = /^1\d{10}$/;
|
||||
if (reg.test(login) == false) {
|
||||
this.props.showNotification(`请先输入正确的手机号码`);
|
||||
return;
|
||||
}
|
||||
} else if (index == 4) {
|
||||
// 绑定邮箱
|
||||
login = values.email;
|
||||
let reg = /^[a-zA-Z0-9]+([.\-_\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/;
|
||||
if (reg.test(login) == false) {
|
||||
this.props.showNotification(`请先输入正确的邮箱地址`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
let type = index;
|
||||
if (!login) {
|
||||
this.props.showNotification(`请先输入${index == 3 ? "手机号码" : "邮箱地址"}`);
|
||||
return;
|
||||
}
|
||||
axios.get((url), {
|
||||
params: {
|
||||
login, type
|
||||
}
|
||||
}).then((result) => {
|
||||
if (result) {
|
||||
// 倒计时
|
||||
this.setState({
|
||||
secondsFlag: true
|
||||
})
|
||||
this.remainTime();
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error);
|
||||
})
|
||||
}
|
||||
|
||||
// 获取验证码倒计时
|
||||
remainTime = () => {
|
||||
this.setState({
|
||||
seconds: 60
|
||||
})
|
||||
this.timer = setInterval(() => {
|
||||
let {seconds} = this.state;
|
||||
let s = parseInt(seconds) - 1;
|
||||
if (s > -1) {
|
||||
this.setState({
|
||||
seconds: s
|
||||
})
|
||||
} else {
|
||||
this.setState({
|
||||
secondsFlag: false
|
||||
})
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
phonebools = () => {
|
||||
this.setState({
|
||||
phonebool: true
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
const {getFieldDecorator} = this.props.form;
|
||||
const {updating, seconds, secondsFlag, phonebool, emailbool, certification, formationdata, bank_account_editable, leader, bank_account} = this.state
|
||||
const {basicInfo} = this.props
|
||||
console.log(emailbool);
|
||||
return (
|
||||
<div>
|
||||
<style>{`
|
||||
|
||||
.flexRow {
|
||||
padding: 20px 0;
|
||||
}
|
||||
.flexRow .name {
|
||||
margin-left: 12px;
|
||||
color: #666666;
|
||||
|
||||
text-align: center;
|
||||
flex: 0 0 100px;
|
||||
}
|
||||
.flexRow .description {
|
||||
margin-left: 10px;
|
||||
flex: 1;
|
||||
color: #CDCDCD;
|
||||
}
|
||||
.description span {
|
||||
margin-right: 20px;
|
||||
color: #05101A;
|
||||
}
|
||||
.flexRow .status {
|
||||
width: 100px;
|
||||
color: #28AC7F;
|
||||
text-align: right;
|
||||
}
|
||||
.flexTable .flexTable {
|
||||
border-bottom: 1px solid #EBEBEB;
|
||||
}
|
||||
|
||||
.settingForm label{
|
||||
color: #666666;
|
||||
font-size: 14px !important ;
|
||||
}
|
||||
.settingForm input {
|
||||
width: 340px;
|
||||
height: 40px;
|
||||
}
|
||||
.settingForm input.validateInput {
|
||||
width: 220px;
|
||||
}
|
||||
.settingForm .formItemInline button {
|
||||
width: 110px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.settingForm .ant-form-item-label {
|
||||
text-align: left;
|
||||
width: 84px;
|
||||
}
|
||||
.formItemInline .ant-form-explain{
|
||||
position:absolute;
|
||||
bottom:-22px;
|
||||
left:0px;
|
||||
width:100%;
|
||||
}
|
||||
`}</style>
|
||||
<div className="settingForm ml38">
|
||||
<React.Fragment>
|
||||
<Form>
|
||||
<Form.Item
|
||||
label="你的手机号"
|
||||
className="formItemInline hideRequireTag mb20 mt20"
|
||||
>
|
||||
{getFieldDecorator('phone', {
|
||||
rules: [{
|
||||
// initialValue: this.state.cityDefaultValue,
|
||||
required: true,
|
||||
message: `请输入要${basicInfo.phone ? '更换' : '绑定'}的手机号码`,
|
||||
}],
|
||||
})(
|
||||
<Input placeholder={`请输入要${basicInfo.phone ? '更换' : '绑定'}的手机号码`}></Input>
|
||||
)}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="手机验证码"
|
||||
className="mb20 formItemInline hideRequireTag"
|
||||
>
|
||||
{getFieldDecorator('phoneValidateCode', {
|
||||
rules: [{
|
||||
// initialValue: this.state.cityDefaultValue,
|
||||
required: true,
|
||||
message: '请输入手机获取的验证码',
|
||||
}],
|
||||
})(
|
||||
<Input placeholder="请输入手机获取的验证码" className="validateInput"></Input>
|
||||
)}
|
||||
<Button type="primary" disabled={secondsFlag} onClick={() => this.getCode(3)}>
|
||||
{!secondsFlag ? "获取验证码" : `重新发送${seconds}s`}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
|
||||
<div className="mb20" style={{marginLeft: '204px'}}>
|
||||
<Button type="primary" onClick={() => this.onPhoneSubmit()}>确定</Button>
|
||||
<Button type="primary grayBtn" style={{marginLeft: '20px'}}
|
||||
onClick={() => this.hideUpdating(1)}>取消</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</React.Fragment>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const Phonenumberverifications = Form.create({name: 'Phonenumberverification'})(Phonenumberverification);
|
||||
|
||||
export default Phonenumberverifications;
|
||||
|
@ -0,0 +1,314 @@
|
||||
/*垂直布局
|
||||
|
||||
一
|
||||
二
|
||||
三
|
||||
*/
|
||||
.flexdirectionjust {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
|
||||
.directstwebkitflex {
|
||||
display: flex;
|
||||
display: -webkit-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.diredisplayitflex {
|
||||
display: flex;
|
||||
display: -webkit-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/*垂直布局*/
|
||||
/*靠左侧
|
||||
一 二 三 四 五 六 七 八
|
||||
*/
|
||||
.flexdirection {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.flexdirections {
|
||||
display: flex;
|
||||
flex-direction: initial;
|
||||
}
|
||||
|
||||
/*靠左侧
|
||||
*/
|
||||
|
||||
|
||||
/*靠右侧八 七 六 五 四 三 二 一*/
|
||||
.flexdirectionss {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
|
||||
/*垂直布局
|
||||
一
|
||||
二
|
||||
三
|
||||
四
|
||||
*/
|
||||
.flexdidirectionss {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/*垂直布局
|
||||
四
|
||||
三
|
||||
二
|
||||
一
|
||||
*/
|
||||
.flexdidireverses {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
.fontcolorsysl {
|
||||
color: #FF0000
|
||||
}
|
||||
|
||||
.fontcolorsyslhei {
|
||||
color: #000000
|
||||
}
|
||||
|
||||
.fontcolorsyslhui {
|
||||
color: #888888
|
||||
}
|
||||
|
||||
.fontcolorsyslhui1 {
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.fontcolorsysllan {
|
||||
color: #4CACFF
|
||||
}
|
||||
|
||||
.fontcolorsysljin {
|
||||
color: #DD7600
|
||||
}
|
||||
|
||||
.w200 {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.w64 {
|
||||
width: 64px;
|
||||
}
|
||||
|
||||
.w60 {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.w98 {
|
||||
width: 98px;
|
||||
}
|
||||
|
||||
.myysllineheight {
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.myyslminwidth {
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
.myyslminwidth276 {
|
||||
width: 276px;
|
||||
}
|
||||
|
||||
.buttongo {
|
||||
background: #E7E7E7;
|
||||
border: 1px solid #E7E7E7;
|
||||
width: 60px;
|
||||
height: 30px;
|
||||
border-radius: 4px;
|
||||
color: #999999;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.buttongo2 {
|
||||
background: #4CACFF;
|
||||
border: 1px solid #4CACFF;
|
||||
width: 64px;
|
||||
height: 32px;
|
||||
border-radius: 4px;
|
||||
color: #FFFFFF;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.fontwenzi {
|
||||
text-align: center;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.mt17 {
|
||||
margin-top: 17px;
|
||||
}
|
||||
|
||||
.mt36 {
|
||||
margin-top: 36px;
|
||||
}
|
||||
|
||||
.mt23 {
|
||||
margin-top: 23px;
|
||||
}
|
||||
|
||||
.mt19 {
|
||||
margin-top: 19px;
|
||||
}
|
||||
|
||||
.mt23 {
|
||||
margin-top: 23px;
|
||||
}
|
||||
|
||||
.mt34 {
|
||||
margin-top: 34px;
|
||||
}
|
||||
|
||||
.ml11 {
|
||||
margin-left: 11px;
|
||||
}
|
||||
|
||||
.ml38 {
|
||||
margin-left: 38px;
|
||||
}
|
||||
|
||||
.ml7 {
|
||||
margin-left: 7px;
|
||||
}
|
||||
|
||||
.colorgreenlight {
|
||||
color: #6EC76E
|
||||
}
|
||||
|
||||
.colorgreenorg {
|
||||
color: #FF7300;
|
||||
}
|
||||
|
||||
.borcolors {
|
||||
border: 1px solid #4CACFF;
|
||||
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mycompitcursor {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.basicForm {
|
||||
background: #fff;
|
||||
padding: 30px;
|
||||
margin-bottom: 10px;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
min-height: 390px;
|
||||
}
|
||||
|
||||
.basicForm .title {
|
||||
font-size: 16px;
|
||||
padding-left: 30px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.flexTable {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flexRow {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.mb15 {
|
||||
margin-bottom: 15px !important;
|
||||
}
|
||||
|
||||
/* BUTTOn */
|
||||
.ant-btn {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
button.ant-btn.ant-btn-primary.grayBtn {
|
||||
background: #CBCBCB;
|
||||
border-color: #CBCBCB;
|
||||
}
|
||||
|
||||
.borderBottom {
|
||||
border-bottom: 1px solid #4CACFF;
|
||||
}
|
||||
|
||||
/* form ---------------- START */
|
||||
.formItemInline {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.formItemInline .ant-form-item-control-wrapper {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.hideRequireTag .ant-form-item-required:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
/* .basicForm .ant-form-item-label {
|
||||
width: 100px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.basicForm .ant-form-item-label label {
|
||||
color: #979797
|
||||
} */
|
||||
|
||||
|
||||
.courseNormalForm .ant-select-show-search {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.courseNormalForm .ant-select-auto-complete.ant-select .ant-input {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.courseNormalForm .ant-select-search__field__mirror {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.courseNormalForm .ant-input-lg {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.courseNormalForm .ant-select-selection--single {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.courseNormalForm .ant-select-auto-complete.ant-select .ant-select-selection--single {
|
||||
height: 40px
|
||||
}
|
||||
|
||||
.courseNormalForm .ant-input-affix-wrapper {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
/* 职业 */
|
||||
.courseNormalForm .ant-select-selection-selected-value {
|
||||
line-height: 38px
|
||||
}
|
||||
|
||||
.courseNormalForm input {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.w300 {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.w56 {
|
||||
width: 56px;
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { Redirect } from 'react-router';
|
||||
|
||||
import { Route, Link, Switch } from "react-router-dom";
|
||||
|
||||
import Loading from '../../Loading';
|
||||
|
||||
import Loadable from 'react-loadable';
|
||||
import { TPMIndexHOC } from '../tpm/TPMIndexHOC';
|
||||
import { CNotificationHOC } from '../courses/common/CNotificationHOC';
|
||||
|
||||
//新版竞赛首页
|
||||
const CompetitionsIndex = Loadable({
|
||||
loader: () => import('./Competitimain/CompetitionsIndex'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
//竞赛详情页
|
||||
const CompetitionCommon=Loadable({
|
||||
loader: () => import('./Competitioncommon/CompetitionCommon'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
|
||||
//战队详情
|
||||
const CompetitionTeams = Loadable({
|
||||
loader: () => import('./Competition_teams/Competitionteams'),
|
||||
loading: Loading,
|
||||
})
|
||||
|
||||
//团队竞赛报名
|
||||
const Registration = Loadable({
|
||||
loader: () => import('../competition/Registration'),
|
||||
loading: Loading,
|
||||
});
|
||||
|
||||
class Competitions extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
|
||||
window.document.title = '竞赛';
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
return (
|
||||
<div className="newMain clearfix">
|
||||
|
||||
<Switch>
|
||||
|
||||
{/*新版竞赛战队详情*/}
|
||||
<Route path="/competitions/:identifier/competition_teams/:competition_team_id"
|
||||
render={
|
||||
(props) => (<CompetitionTeams {...this.props} {...props} {...this.state} />)
|
||||
}
|
||||
></Route>
|
||||
|
||||
|
||||
{/*新版竞赛报名*/}
|
||||
<Route
|
||||
path="/competitions/:identifier/enroll"
|
||||
render={
|
||||
(props) => (<Registration {...this.props} {...props} {...this.state}/>)
|
||||
}
|
||||
/>
|
||||
|
||||
{/*新版竞赛详情页面*/}
|
||||
<Route path="/competitions/:identifier"
|
||||
render={
|
||||
(props) => (<CompetitionCommon {...this.props} {...props} {...this.state} />)
|
||||
}
|
||||
></Route>
|
||||
|
||||
{/*新版竞赛首页*/}
|
||||
<Route path="/competitions"
|
||||
render={
|
||||
(props) => (<CompetitionsIndex {...this.props} {...props} {...this.state} />)
|
||||
}
|
||||
></Route>
|
||||
|
||||
</Switch>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default CNotificationHOC() (TPMIndexHOC (Competitions)) ;
|
@ -0,0 +1,462 @@
|
||||
/*
|
||||
* @Description: undefined
|
||||
* @Author: tangjiang
|
||||
* @Date: 2019-11-15 11:02:49
|
||||
* @Last Modified by: tangjiang
|
||||
* @Last Modified time: 2019-11-18 16:52:38
|
||||
*/
|
||||
|
||||
import './index.scss';
|
||||
|
||||
import React from 'react';
|
||||
import { Table, Button, Dropdown, Icon, Menu, Card, Input, Select, Tag } from 'antd';
|
||||
import { connect } from 'react-redux';
|
||||
import actions from '../../redux/actions';
|
||||
import MultipTags from './components/multiptags';
|
||||
import { Link } from 'react-router-dom';
|
||||
import CONST from '../../constants';
|
||||
|
||||
const {tagBackground, diffText} = CONST;
|
||||
const { Search } = Input;
|
||||
const { Option } = Select;
|
||||
// import reqwest from 'reqwest';
|
||||
/**
|
||||
* 下拉菜单
|
||||
*/
|
||||
const maps = {
|
||||
'categoryMenu': [
|
||||
{
|
||||
'key': '0',
|
||||
'name': '全部',
|
||||
'value': '0'
|
||||
},
|
||||
{
|
||||
'key': '1',
|
||||
'name': '程序设计基础',
|
||||
'value': '1'
|
||||
},
|
||||
{
|
||||
'key': '2',
|
||||
'name': '数据结构与计算',
|
||||
'value': '2'
|
||||
}
|
||||
],
|
||||
'difficultMenu': [
|
||||
{
|
||||
'key': '1',
|
||||
'name': '简单',
|
||||
'value': '1'
|
||||
},
|
||||
{
|
||||
'key': '2',
|
||||
'name': '中等',
|
||||
'value': '2'
|
||||
},
|
||||
{
|
||||
'key': '3',
|
||||
'name': '困难',
|
||||
'value': '3'
|
||||
}
|
||||
],
|
||||
'statusMenu': [
|
||||
{
|
||||
'key': '-1',
|
||||
'name': '未做',
|
||||
'value': '-1'
|
||||
},
|
||||
{
|
||||
'key': '0',
|
||||
'name': '未通过',
|
||||
'value': '0'
|
||||
},
|
||||
{
|
||||
'key': '1',
|
||||
'name': '已通过',
|
||||
'value': '1'
|
||||
}
|
||||
],
|
||||
'come_fromMenu': [
|
||||
{
|
||||
'key': 'all',
|
||||
'name': '全部',
|
||||
'value': 'all'
|
||||
},
|
||||
{
|
||||
'key': 'mine',
|
||||
'name': '我创建的',
|
||||
'value': 'mine'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const testMaps = {
|
||||
category: {
|
||||
1: '程序设计基础',
|
||||
2: '数据结构与算法'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 表格列
|
||||
*/
|
||||
const options = {
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
fixed: 'right',
|
||||
width: 100,
|
||||
render: (text, record) => (
|
||||
<span>
|
||||
<Button type="primary">
|
||||
<Link to={`/problems/${record.identifier}/edit`}>编辑</Link>
|
||||
</Button>
|
||||
</span>
|
||||
),
|
||||
}
|
||||
const columns = [
|
||||
{
|
||||
title: '标题',
|
||||
dataIndex: 'name',
|
||||
render: (name, record) => <Link style={{ color: '#459be5' }} to={`/myproblems/${record.identifier}`}>{name}</Link>
|
||||
},
|
||||
{
|
||||
title: '分类',
|
||||
dataIndex: 'category',
|
||||
width: '20%',
|
||||
align: 'center',
|
||||
render: (category) => <span>{category ? testMaps['category'][+category] : '-'}</span>
|
||||
},
|
||||
{
|
||||
title: '难度',
|
||||
dataIndex: 'difficult',
|
||||
align: 'center',
|
||||
width: '15%',
|
||||
render: (difficult) => {
|
||||
if (difficult) {
|
||||
return <Tag color={tagBackground[+difficult]}>{diffText[+difficult]}</Tag>
|
||||
} else {
|
||||
return '-';
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '热度',
|
||||
dataIndex: 'hack_user_lastest_codes_count',
|
||||
sorter: true,
|
||||
align: 'center',
|
||||
width: '10%'
|
||||
},
|
||||
{
|
||||
title: '通过率',
|
||||
dataIndex: 'passed_rate',
|
||||
sorter: true,
|
||||
align:'right',
|
||||
width: '10%',
|
||||
render: val => <span>{`${val}%`}</span>
|
||||
},
|
||||
];
|
||||
|
||||
class DeveloperHome extends React.PureComponent {
|
||||
state = {
|
||||
data: [],
|
||||
loading: false,
|
||||
searchParams: {
|
||||
search: '', // 查询关键字
|
||||
'come_from': '', // 来源
|
||||
difficult: '', // 难易度
|
||||
status: '', // 未做
|
||||
category: '', // 分类
|
||||
'sort_by': '', // 排序
|
||||
'sort_direction': '', // 排序方向
|
||||
page: 1, // 当前页数
|
||||
limit: 10 // 每页显示条件
|
||||
},
|
||||
columns: columns,
|
||||
searchInfo: []
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
// 是否是我的,如果是我的 显示编辑按钮
|
||||
const { isMySource } = this.props;
|
||||
if (isMySource) {
|
||||
this.handleFilterSearch({come_from: 'mine'});
|
||||
let _columns = columns.concat([options]);
|
||||
this.setState({
|
||||
columns: _columns
|
||||
});
|
||||
} else {
|
||||
this.fetchData();
|
||||
}
|
||||
|
||||
const {hacks_count} = this.props.ojListReducer;
|
||||
this.setState({
|
||||
pagination: {
|
||||
total: hacks_count
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleTableChange = (pagination, filters, sorter) => {
|
||||
const {field, order} = sorter;
|
||||
const {current, pageSize} = pagination;
|
||||
this.handleFilterSearch({
|
||||
sort_by: field,
|
||||
sort_direction: order === 'descend' ? 'desc' : 'asc',
|
||||
page: current,
|
||||
limit: pageSize
|
||||
});
|
||||
this.props.changePaginationInfo(pagination);
|
||||
};
|
||||
|
||||
fetchData = () => {
|
||||
this.props.fetchOJList(this.state.searchParams);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据类型获取下拉菜单
|
||||
* @param type 类型
|
||||
* @param handleClick 处理函数
|
||||
*/
|
||||
getMenuItems = (type, handleClick) => {
|
||||
return (
|
||||
<Menu onClick={handleClick}>
|
||||
{
|
||||
maps[type].map((item) => {
|
||||
return (
|
||||
<Menu.Item key={item.key}>
|
||||
{item.name}
|
||||
</Menu.Item>
|
||||
)
|
||||
})
|
||||
}
|
||||
</Menu>
|
||||
)
|
||||
};
|
||||
|
||||
getOptionsItem = (type) => {
|
||||
return maps[type].map(item => {
|
||||
return <Option key={item.key} value={item.value}>{item.name}</Option>
|
||||
});
|
||||
}
|
||||
// 点击条件时加载数据
|
||||
handleFilterSearch = (obj) => {
|
||||
const searchParams = Object.assign({}, this.state.searchParams, obj);
|
||||
this.setState({
|
||||
searchParams: searchParams
|
||||
}, () => {
|
||||
this.fetchData();
|
||||
});
|
||||
}
|
||||
|
||||
// 添加显示搜索条件
|
||||
addShowFilterCtx = (obj) => {
|
||||
const {searchInfo} = this.state
|
||||
const index = searchInfo.findIndex(item => item.type === obj.type);
|
||||
let tempArr = [...searchInfo];
|
||||
if (index > -1) {
|
||||
tempArr[index] = obj;
|
||||
} else {
|
||||
tempArr.push(obj);
|
||||
}
|
||||
this.setState({
|
||||
searchInfo: tempArr
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 搜索输入框
|
||||
* @param value 输入框值
|
||||
*/
|
||||
handleInputSearch = (value) => {
|
||||
value = value.trim();
|
||||
// if (value.length === 0) return;
|
||||
this.handleFilterSearch({search: value});
|
||||
}
|
||||
// handleSearchChange = (e) => {
|
||||
// console.log(e.target.value);
|
||||
// const value = e.target.value.trim();
|
||||
// }
|
||||
// 下拉类别菜单
|
||||
handleCategoryMenuClick = (item) => {
|
||||
this.addShowFilterCtx({
|
||||
type: 'category',
|
||||
key: item.key
|
||||
});
|
||||
this.handleFilterSearch({category: +item.key === 0 ? '' : +item.key});
|
||||
}
|
||||
// 难度下拉
|
||||
handleHardMenuClick = (item) => {
|
||||
this.addShowFilterCtx({
|
||||
type: 'difficult',
|
||||
key: item.key
|
||||
});
|
||||
this.handleFilterSearch({difficult: +item.key});
|
||||
}
|
||||
// 状态下拉
|
||||
handleSatusMenuClick = (item) => {
|
||||
this.addShowFilterCtx({
|
||||
type: 'status',
|
||||
key: item.key
|
||||
});
|
||||
this.handleFilterSearch({status: +item.key});
|
||||
}
|
||||
// 来源下拉
|
||||
handleOriginMenuClick = (item) => {
|
||||
|
||||
this.addShowFilterCtx({
|
||||
type: 'come_from',
|
||||
key: item.key
|
||||
});
|
||||
|
||||
this.handleFilterSearch({come_from: item.key === 'all' ? '' : item.key});
|
||||
|
||||
if (item.key !== 'all') {
|
||||
let _columns = columns.concat([options]);
|
||||
this.setState({
|
||||
columns: _columns
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
columns: columns
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleTagClose = (info) => {
|
||||
|
||||
this.handleFilterSearch({[info.type]: ''});
|
||||
// 移除 searcInfo 中的数据
|
||||
const { type } = info;
|
||||
let tempArr = [...this.state.searchInfo];
|
||||
const index = tempArr.findIndex(item => item.type === type);
|
||||
if (index > -1) tempArr.splice(index, 1);
|
||||
this.setState({
|
||||
searchInfo: tempArr
|
||||
});
|
||||
if (info.type === 'come_from' && info.key === 'mine') {
|
||||
this.setState({
|
||||
columns: columns
|
||||
});
|
||||
}
|
||||
}
|
||||
render () {
|
||||
// const { testReducer, handleClick } = this.props;
|
||||
const {
|
||||
ojListReducer: {hacks_list, top_data, hacks_count},
|
||||
pagination
|
||||
} = this.props;
|
||||
const {passed_count = 0, simple_count = 0, medium_count = 0, diff_count = 0} = top_data;
|
||||
const { columns } = this.state;
|
||||
|
||||
// 渲染条件内容
|
||||
const renderSearch = () => {
|
||||
return this.state.searchInfo.map(info => {
|
||||
let ctx = '';
|
||||
const arrs = maps[`${info.type}Menu`];
|
||||
arrs.forEach(item => {
|
||||
if (item.key === info.key) ctx = item.name;
|
||||
});
|
||||
return (
|
||||
<Tag
|
||||
closable
|
||||
className={'search_tag_style'}
|
||||
key={info.type}
|
||||
onClose={() => this.handleTagClose(info)}
|
||||
>{ctx}</Tag>
|
||||
)});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="developer-list">
|
||||
<div className="ant-spin-container">
|
||||
<div className={'banner-wrap'}></div>
|
||||
<div className="educontent">
|
||||
<div className={'card-top'}>
|
||||
<div className="search-params">
|
||||
<p className={'save-question'}>已解决 <span className={''}>{passed_count}</span> / {hacks_count} 题</p>
|
||||
<div className={'question-level'}>
|
||||
<MultipTags type="success" text="简单" numb={simple_count} style={{ marginRight: '20px' }}/>
|
||||
<MultipTags type="warning" text="中等" numb={medium_count} style={{ marginRight: '20px' }}/>
|
||||
<MultipTags type="error" text="困难" numb={diff_count}/>
|
||||
</div>
|
||||
<Button type="primary">
|
||||
<Link to="/problems/new">新建</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className={'card-table'}>
|
||||
<div bordered={false} className={'filter_ctx_area'}>
|
||||
<div>
|
||||
<Dropdown className={'dropdonw-style'} placement="bottomLeft" overlay={this.getMenuItems('categoryMenu', this.handleCategoryMenuClick)}>
|
||||
<span className={'dropdown-span'}>分类 <Icon type="down"/></span>
|
||||
</Dropdown>
|
||||
<Dropdown className={'dropdonw-style'} placement="bottomLeft" overlay={this.getMenuItems('difficultMenu', this.handleHardMenuClick)}>
|
||||
<span className={'dropdown-span'}>难度 <Icon type="down"/></span>
|
||||
</Dropdown>
|
||||
<Dropdown className={'dropdonw-style'} placement="bottomLeft" overlay={this.getMenuItems('statusMenu', this.handleSatusMenuClick)}>
|
||||
<span className={'dropdown-span'}>状态 <Icon type="down"/></span>
|
||||
</Dropdown>
|
||||
<Dropdown className={'dropdonw-style'} placement="bottomLeft" overlay={this.getMenuItems('come_fromMenu', this.handleOriginMenuClick)}>
|
||||
<span className={'dropdown-span'}>来源 <Icon type="down"/></span>
|
||||
</Dropdown>
|
||||
</div>
|
||||
|
||||
<div className={'choice_ctx'}>
|
||||
{renderSearch()}
|
||||
</div>
|
||||
<Search
|
||||
placeholder="输入标题进行搜索"
|
||||
onChange={this.handleSearchChange}
|
||||
onSearch={value => this.handleInputSearch(value)}
|
||||
style={{ width: 320, float: 'right' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Card bordered={false} style={{ marginTop: '2px'}}>
|
||||
<Table
|
||||
columns={columns}
|
||||
rowKey={record => Math.random()}
|
||||
dataSource={hacks_list}
|
||||
pagination={pagination}
|
||||
onChange={this.handleTableChange}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {*} state store
|
||||
* @param {*} ownProps DeveloperHome 中的 props
|
||||
*/
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const {
|
||||
testReducer,
|
||||
ojListReducer,
|
||||
commonReducer
|
||||
} = state;
|
||||
|
||||
const { pagination } = ojListReducer;
|
||||
|
||||
return {
|
||||
testReducer,
|
||||
ojListReducer,
|
||||
isMySource: commonReducer.isMySource,
|
||||
pagination
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
handleClick: () => dispatch(actions.toggleTodo()),
|
||||
fetchOJList: (params) => dispatch(actions.getOJList(params)),
|
||||
changePaginationInfo: (obj) => dispatch(actions.changePaginationInfo(obj)),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(DeveloperHome);
|
||||
// export default DeveloperHome;
|
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* @Description: 右侧代码块控制台
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-27 16:02:36
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-28 16:48:50
|
||||
*/
|
||||
import './index.scss';
|
||||
import React, { useState, useRef } from 'react';
|
||||
import { Tabs, Button, Icon } from 'antd';
|
||||
import { connect } from 'react-redux';
|
||||
import InitTabCtx from '../initTabCtx';
|
||||
import ExecResult from '../execResult';
|
||||
import actions from '../../../../redux/actions';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
const ControlSetting = (props) => {
|
||||
|
||||
const {
|
||||
inputValue,
|
||||
loading,
|
||||
submitLoading,
|
||||
identifier,
|
||||
excuteState,
|
||||
commitRecordDetail,
|
||||
changeLoadingState,
|
||||
changeSubmitLoadingStatus,
|
||||
showOrHideControl,
|
||||
// debuggerCode
|
||||
updateCode,
|
||||
onSubmitForm
|
||||
} = props;
|
||||
const [defaultActiveKey, setDefaultActiveKey] = useState('1'); // 当前选中的tab
|
||||
const [showTextResult, setShowTextResult] = useState(false); // 是否点击控制台按钮
|
||||
const formRef = useRef(null);
|
||||
|
||||
const classNames = `control_tab ${showTextResult ? 'move_up move_up_final' : 'move_down_final'}`;
|
||||
|
||||
// 切换tab
|
||||
const handleTabChange = (key) => {
|
||||
setDefaultActiveKey(key);
|
||||
}
|
||||
|
||||
// 显示/隐藏tab
|
||||
const handleShowControl = () => {
|
||||
setShowTextResult(!showTextResult);
|
||||
showOrHideControl(!showTextResult);
|
||||
}
|
||||
|
||||
// 调试代码
|
||||
const handleTestCode = (e) => {
|
||||
// console.log(formRef.current.handleTestCodeFormSubmit);
|
||||
// 调出控制台界面
|
||||
setShowTextResult(true);
|
||||
showOrHideControl(true);
|
||||
formRef.current.handleTestCodeFormSubmit(() => {
|
||||
setDefaultActiveKey('2');
|
||||
});
|
||||
}
|
||||
|
||||
// 提交
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
changeSubmitLoadingStatus(true)
|
||||
onSubmitForm && onSubmitForm();
|
||||
}
|
||||
|
||||
// 处理调度代码
|
||||
const handleDebuggerCode = (values) => {
|
||||
// 改变状态值
|
||||
changeLoadingState(true);
|
||||
// 调用代码保存接口, 成功后再调用调试接口
|
||||
updateCode(identifier, values, 'debug');
|
||||
// 调用调试接口
|
||||
// debuggerCode(identifier, values);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="pane_control_area">
|
||||
<Tabs
|
||||
className={classNames}
|
||||
activeKey={defaultActiveKey}
|
||||
tabBarStyle={{ backgroundColor: '#000', color: '#fff' }}
|
||||
onChange={handleTabChange}
|
||||
>
|
||||
<TabPane tab={'自定义测试用例'} key={'1'} style={{ height: '280px', overflowY: 'auto' }}>
|
||||
<InitTabCtx
|
||||
inputValue={inputValue}
|
||||
wrappedComponentRef={(form) => formRef.current = form}
|
||||
onDebuggerCode={handleDebuggerCode}
|
||||
/>
|
||||
</TabPane>
|
||||
<TabPane tab={'代码执行结果'} key={'2'} style={{ height: '280px', overflowY: 'auto' }}>
|
||||
<ExecResult
|
||||
excuteState={excuteState}
|
||||
excuteDetail={commitRecordDetail}
|
||||
/>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
<div className="pane_control_opts">
|
||||
<Button
|
||||
type="link"
|
||||
style={{ color: '#fff' }}
|
||||
onClick={handleShowControl}>
|
||||
控制台 <Icon type={ showTextResult ? "down" : "up" } />
|
||||
</Button>
|
||||
<p>
|
||||
<Button ghost
|
||||
loading={loading}
|
||||
style={{ marginRight: '10px', color: '#28BD8B', borderColor: '#28BD8B' }}
|
||||
onClick={handleTestCode}
|
||||
disabled={!identifier}
|
||||
>调试代码</Button>
|
||||
<Button
|
||||
loading={submitLoading}
|
||||
type="primary"
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
{/* {props.identifier ? '更新' : '提交'} */}
|
||||
提交
|
||||
</Button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const {commonReducer, ojForUserReducer} = state;
|
||||
const { loading, excuteState, submitLoading } = commonReducer;
|
||||
const { user_program_identifier, commitRecordDetail } = ojForUserReducer;
|
||||
return {
|
||||
loading,
|
||||
submitLoading,
|
||||
excuteState,
|
||||
identifier: user_program_identifier,
|
||||
commitRecordDetail // 提交详情
|
||||
};
|
||||
};
|
||||
// changeSubmitLoadingStatus
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
showOrHideControl: (flag) => dispatch(actions.showOrHideControl(flag)),
|
||||
changeLoadingState: (flag) => dispatch(actions.changeLoadingState(flag)),
|
||||
changeSubmitLoadingStatus: (flag) => dispatch(actions.changeSubmitLoadingStatus(flag)),
|
||||
debuggerCode: (identifier, values) => dispatch(actions.debuggerCode(identifier, values)),
|
||||
// inputValue 输入值
|
||||
updateCode: (identifier, inputValue, type) => dispatch(actions.updateCode(identifier, inputValue, type))
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ControlSetting);
|
@ -0,0 +1,103 @@
|
||||
.pane_control_area{
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
// height: 56px;
|
||||
.control_tab{
|
||||
position: absolute;
|
||||
bottom: -325px;
|
||||
width: 100%;
|
||||
// transition: all .2s;
|
||||
opacity: 0;
|
||||
// animation: .3s ease-in-out move_up;
|
||||
// &.active{
|
||||
// bottom: 0;
|
||||
// opacity: 1;
|
||||
// }
|
||||
&.move_up{
|
||||
animation: move_up .3s ease-in;
|
||||
}
|
||||
&.move_up_final {
|
||||
bottom: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
&.move_down{
|
||||
animation: move_down .3s ease-in-out;
|
||||
}
|
||||
&.move_down_final{
|
||||
bottom: -325px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.ant-tabs-bar{
|
||||
padding: 0 10px;
|
||||
margin: 0px;
|
||||
border-bottom: transparent;
|
||||
}
|
||||
.ant-tabs-ink-bar{
|
||||
bottom: 1px;
|
||||
}
|
||||
// .tab_ctx_area.pos_center{
|
||||
// background: #222;
|
||||
// }
|
||||
.pane_control_opts{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
z-index: 20;
|
||||
height: 56px;
|
||||
padding-right: 30px;
|
||||
padding-left: 10px;
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.setting_drawer{
|
||||
.setting_h2{
|
||||
line-height: 50px;
|
||||
}
|
||||
.setting_desc{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
.flex_item{
|
||||
line-height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@keyframes move_up {
|
||||
0%{
|
||||
opacity: 0;
|
||||
// bottom: -325px;
|
||||
}
|
||||
90%{
|
||||
opacity: 0.5;
|
||||
// bottom: 0px;
|
||||
}
|
||||
100%{
|
||||
opacity: 1;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes move_down{
|
||||
0%{
|
||||
opacity: 1;
|
||||
bottom: 0
|
||||
}
|
||||
10%{
|
||||
opacity: .2;
|
||||
}
|
||||
20%{
|
||||
opacity: 0;
|
||||
}
|
||||
100%{
|
||||
opacity: 0;
|
||||
bottom: -325px;
|
||||
}
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* @Description: 执行结果
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-28 08:44:54
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-28 15:14:42
|
||||
*/
|
||||
import './index.scss';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Icon } from 'antd';
|
||||
import CONST from '../../../../constants';
|
||||
|
||||
const {reviewResult} = CONST;
|
||||
function ExecResult (props) {
|
||||
|
||||
const { excuteState, excuteDetail } = props;
|
||||
// 指定渲染初始, 加载中, 加载完成页面内容
|
||||
const renderInit = () => (
|
||||
<div className={'excute_result_area excute_flex_center'}>
|
||||
<span className={'init_ctx'}>请先点击“调试代码”运行您的代码</span>
|
||||
</div>
|
||||
);
|
||||
const renderLoading = () => (
|
||||
<div className={'excute_result_area excute_flex_center'}>
|
||||
<span className={'loading_ctx'}>
|
||||
<Icon className={'ctx_icon'} type="loading"/>
|
||||
<span>加载中...</span>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
const readerLoaded = () => (
|
||||
<div className={'excute_result_area excute_flex_center'}>
|
||||
<span className={'loaded_ctx'}>
|
||||
<Icon className={'ctx_icon'} type="loading"/>
|
||||
<span>加载完成</span>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
const renderFinish = () => {
|
||||
const {
|
||||
error_line,
|
||||
error_msg,
|
||||
execute_memory,
|
||||
execute_time,
|
||||
input,
|
||||
output,
|
||||
status,
|
||||
expected_output
|
||||
} = codeResult;
|
||||
|
||||
const excuteHeader = (state) => {
|
||||
const review_class = state === 0 ? `excute_suc` : `excute_err`;
|
||||
return (
|
||||
<p className={'excute_head_area'}>
|
||||
<span className={'excute_head_txt'}>执行结果: </span>
|
||||
<span className={review_class}>{reviewResult[`${state}`]}</span>
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
const excuteCtx = (state) => {
|
||||
if (state === 0) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<p className={'result_info_style'}>输入: {input}</p>
|
||||
<p className={'result_info_style'}>输出: {output}</p>
|
||||
</React.Fragment>
|
||||
);
|
||||
} else if (state === 4){
|
||||
return (
|
||||
<p className={'result_info_style'}>
|
||||
{error_msg}
|
||||
</p>
|
||||
)
|
||||
} else if (state === -1) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<p className={'result_info_style'}>输入: {input}</p>
|
||||
<p className={'result_info_style'}>输出: {output}</p>
|
||||
<p className={'result_info_style'}>预期输出: {expected_output}</p>
|
||||
</React.Fragment>
|
||||
)
|
||||
} else if (state === 5) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<p className={'result_info_style'}> 执行出错信息: {error_msg}</p>
|
||||
<p className={'result_info_style'}>最后执行的输入: {input}</p>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div className={'excute_result_info'}>
|
||||
{excuteHeader(status)}
|
||||
{excuteCtx(status)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// 渲染状态
|
||||
const [renderCtx, setRenderCtx] = useState(() => {
|
||||
return function () {
|
||||
return renderInit();
|
||||
}
|
||||
});
|
||||
// 提交记录详情
|
||||
const [codeResult, setCodeResult] = useState({})
|
||||
|
||||
// 渲染状态变化时渲染相应的内容
|
||||
useEffect(() => {
|
||||
if ('loading' === excuteState) {
|
||||
setRenderCtx(() => (renderLoading));
|
||||
} else if ('loaded' === excuteState) {
|
||||
setRenderCtx(() => (readerLoaded));
|
||||
} else if ('finish' === excuteState) {
|
||||
setRenderCtx(() => (renderFinish));
|
||||
}
|
||||
}, [excuteState]);
|
||||
|
||||
// 提交详情变化时
|
||||
useEffect(() => {
|
||||
console.log('提交记录详情=====>>>>>', excuteDetail);
|
||||
setCodeResult(excuteDetail);
|
||||
}, [excuteDetail]);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{renderCtx()}
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
export default ExecResult;
|
@ -0,0 +1,47 @@
|
||||
.excute_result_area{
|
||||
display: flex;
|
||||
height: 224px;
|
||||
width: 100%;
|
||||
|
||||
&.excute_flex_center{
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.init_ctx{
|
||||
color: #666666;
|
||||
}
|
||||
.loading_ctx,
|
||||
.loaded_ctx{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: #1890ff;
|
||||
.ctx_icon{
|
||||
font-size: 40px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.excute_result_info{
|
||||
padding: 20px 30px;
|
||||
color: #fff;
|
||||
height: 220px;
|
||||
/* overflow-y: auto; */
|
||||
overflow-y: auto;
|
||||
|
||||
.result_info_style{
|
||||
word-wrap: break-word;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.excute_head_area{
|
||||
line-height: 30px;
|
||||
.excute_suc{
|
||||
color: #28BD8B;
|
||||
}
|
||||
.excute_err{
|
||||
color: #E51C24;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
.tab_ctx_area{
|
||||
display: flex;
|
||||
height: 100%;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
&.pos_start{
|
||||
justify-content: flex-start;
|
||||
}
|
||||
&.pos_center{
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
&.pos_end{
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.ctx_default{
|
||||
margin: 10px 20px;
|
||||
}
|
||||
.ctx_loading,
|
||||
.ctx_loaded{
|
||||
display: flex;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
top: -20px;
|
||||
color: #1890ff;
|
||||
.ctx_icon{
|
||||
font-size: 40px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user_case_form{
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-top: 20px;
|
||||
.input_area{
|
||||
flex: 1;
|
||||
.ant-form-item-required{
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.flex_l{
|
||||
padding: 0 10px 0 20px;
|
||||
color: #fff;
|
||||
}
|
||||
.flex_r{
|
||||
padding: 0 20px 0 10px;
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* @Description: 编辑器侧边栏设置信息
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-25 17:50:33
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-27 14:40:36
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Select } from 'antd';
|
||||
|
||||
const { Option } = Select;
|
||||
const SettingDrawer = (props) => {
|
||||
/**
|
||||
* title: '', // 一级标题
|
||||
* type: '', // 类型: 目录 select 和 文本
|
||||
* content: [] // 显示的内容 { text: '' , value: string | [{ key: 1, value: '', text: '' }] }
|
||||
*/
|
||||
const {title, type = 'label', content = [] } = props;
|
||||
|
||||
const handleFontSize = (value) => {
|
||||
const {onChangeFontSize} = props;
|
||||
// console.log('fong size change: ', value);
|
||||
onChangeFontSize && onChangeFontSize(value);
|
||||
}
|
||||
|
||||
const renderCtx = (title, content = [], type = 'label') => {
|
||||
const result = content.map((ctx, index) => {
|
||||
const subText = ctx.text;
|
||||
const value = ctx.value;
|
||||
let renderResult = '';
|
||||
if (typeof value === 'string') {
|
||||
renderResult = (
|
||||
<div className={'setting_desc'} key={`lab_${index}`}>
|
||||
<span className={'flex_item'}>{subText}</span>
|
||||
<span className={'flex_item'}>{ctx.value}</span>
|
||||
</div>
|
||||
);
|
||||
} else if (Array.isArray(value)) {
|
||||
if (type === 'select') {
|
||||
const child = ctx.value.map((opt, i) => (
|
||||
<Option key={opt.key || `${opt.value}`} value={opt.value}>
|
||||
{opt.text}
|
||||
</Option>
|
||||
));
|
||||
renderResult = (
|
||||
<div className={'setting_desc'} key={`sel_${index}`}>
|
||||
<span className={'flex_item'}>{ctx.text}</span>
|
||||
<Select className={'flex_item'} style={{ width: '100px'}} onChange={handleFontSize}>
|
||||
{child}
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
return renderResult;
|
||||
});
|
||||
return (
|
||||
<React.Fragment>
|
||||
<h2 className={'setting_h2'}>{title}</h2>
|
||||
{ result }
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className={'setting_area'}>
|
||||
{renderCtx(title, content, type)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SettingDrawer;
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* @Description: 显示 文字 + number 标签类型
|
||||
* @Author: tangjiang
|
||||
* @Date: 2019-11-15 10:41:06
|
||||
* @Last Modified by: tangjiang
|
||||
* @Last Modified time: 2019-11-15 17:15:27
|
||||
*/
|
||||
import './index.scss';
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
const numberal = require('numeral');
|
||||
|
||||
export default class MultipTags extends PureComponent {
|
||||
|
||||
render () {
|
||||
const { type = 'primary', text, numb, ...props} = this.props;
|
||||
|
||||
if (typeof numb !== 'number' && typeof numb !== 'string') {
|
||||
throw new Error('输入的numb必须为数字或数字类型字符串.');
|
||||
}
|
||||
let result = Number(numb) >= 1000
|
||||
? numberal(Number(numb)).format('0.0a')
|
||||
: Number(numb);
|
||||
|
||||
return (
|
||||
<div className={'mul-tag-wrap'} {...props}>
|
||||
<span className={`tag-txt ${type}`}>
|
||||
{ text }
|
||||
</span>
|
||||
<span className={'tag-numb'}>
|
||||
{ result }
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
.mul-tag-wrap{
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
|
||||
.tag-txt, .tag-numb{
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
padding: 0 10px;
|
||||
// line-height: 20px;
|
||||
// height: 20px;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
.tag-txt{
|
||||
border: 1px solid transparent;
|
||||
border-top-left-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
color: #fff;
|
||||
|
||||
&.primary{
|
||||
background: #1890ff;
|
||||
}
|
||||
&.warning{
|
||||
background: #faad14;
|
||||
}
|
||||
&.success{
|
||||
background: #52c41a;
|
||||
}
|
||||
&.error{
|
||||
background: #f5222d;
|
||||
}
|
||||
}
|
||||
|
||||
.tag-numb{
|
||||
border: 1px solid rgba(238, 238, 238, 1);
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
border-left-color: transparent;
|
||||
margin-left: -1px;
|
||||
min-width: 40px;
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* @Description: 抽取代码编辑器
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-27 15:02:52
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-28 12:39:39
|
||||
*/
|
||||
import './index.scss';
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { Icon, Drawer } from 'antd';
|
||||
import { connect } from 'react-redux';
|
||||
import MonacoEditor from '@monaco-editor/react';
|
||||
import SettingDrawer from '../../components/monacoSetting';
|
||||
import CONST from '../../../../constants';
|
||||
import actions from '../../../../redux/actions';
|
||||
|
||||
const { fontSetting, opacitySetting } = CONST;
|
||||
|
||||
const MyMonacoEditor = (props) => {
|
||||
|
||||
const {
|
||||
language,
|
||||
code,
|
||||
showOrHideControl,
|
||||
saveUserInputCode
|
||||
} = props;
|
||||
const [showDrawer, setShowDrawer] = useState(false); // 控制配置滑框
|
||||
const [editCode, setEditCode] = useState('');
|
||||
// const [curLang, setCurLang] = useState('C');
|
||||
const [fontSize, setFontSize] = useState(12);
|
||||
const [ height, setHeight ] = useState('calc(100% - 112px)');
|
||||
const editorRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (code) {
|
||||
setEditCode(code);
|
||||
}
|
||||
}, [code]);
|
||||
|
||||
useEffect(() => {
|
||||
setHeight(showOrHideControl ? 'calc(100% - 382px)' : 'calc(100% - 112px)');
|
||||
}, [showOrHideControl]);
|
||||
// 控制侧边栏设置的显示
|
||||
const handleShowDrawer = () => {
|
||||
setShowDrawer(true);
|
||||
}
|
||||
// 关闭设置
|
||||
const handleDrawerClose = () => {
|
||||
setShowDrawer(false);
|
||||
}
|
||||
// 侧边栏改变字体大小
|
||||
const handleFontSizeChange = (value) => {
|
||||
setFontSize(value);
|
||||
}
|
||||
|
||||
// 文本框内容变化时,记录文本框内容
|
||||
const handleEditorChange = (origin, monaco) => {
|
||||
editorRef.current = monaco; // 获取当前monaco实例
|
||||
setEditCode(origin); // 保存编辑器初始值
|
||||
editorRef.current.onDidChangeModelContent(e => { // 监听编辑器内容的变化
|
||||
// TODO 需要优化 节流
|
||||
const val = editorRef.current.getValue();
|
||||
setEditCode(val);
|
||||
// 值一变化保存当前代码值
|
||||
saveUserInputCode(val);
|
||||
});
|
||||
}
|
||||
|
||||
// 配置编辑器属性
|
||||
const editorOptions = {
|
||||
selectOnLineNumbers: true,
|
||||
automaticLayout: true,
|
||||
fontSize: `${fontSize}px`
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className={"monaco_editor_area"}>
|
||||
<div className="code_title">
|
||||
<span>已保存</span>
|
||||
<Icon className={'code-icon'} type="setting" onClick={handleShowDrawer}/>
|
||||
</div>
|
||||
<MonacoEditor
|
||||
height={height}
|
||||
width="100%"
|
||||
language={language && language.toLowerCase()}
|
||||
value={editCode}
|
||||
options={editorOptions}
|
||||
theme="dark"
|
||||
editorDidMount={handleEditorChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Drawer
|
||||
className={'setting_drawer'}
|
||||
placement="right"
|
||||
closable={false}
|
||||
onClose={handleDrawerClose}
|
||||
visible={showDrawer}
|
||||
>
|
||||
<SettingDrawer {...fontSetting} onChangeFontSize={handleFontSizeChange}/>
|
||||
<SettingDrawer {...opacitySetting}/>
|
||||
</Drawer>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const { showOrHideControl } = state.commonReducer;
|
||||
return {
|
||||
showOrHideControl
|
||||
}
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
saveUserInputCode: (code) => dispatch(actions.saveUserInputCode(code)),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(MyMonacoEditor);
|
@ -0,0 +1,15 @@
|
||||
.monaco_editor_area{
|
||||
height: 100%;
|
||||
.code_title{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #000;
|
||||
color: #fff;
|
||||
height: 56px;
|
||||
padding: 0 30px;
|
||||
.code-icon{
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* @Description: 文字 | 图标 + 数字样式
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-27 10:58:37
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-27 14:22:38
|
||||
*/
|
||||
import './index.scss';
|
||||
import React from 'react';
|
||||
import { Icon } from 'antd';
|
||||
const numberal = require('numeral');
|
||||
|
||||
const TextNumber = (props) => {
|
||||
/**
|
||||
* text: 显示的文本信息
|
||||
* number: 显示的数字
|
||||
* position: 位置 vertical | horizontal (默认)
|
||||
* type: 内容 文字或图标
|
||||
* onIconClick: 点击图标时的回调函数
|
||||
*/
|
||||
const { text, number, position = 'horizontal', type = 'label', onIconClick} = props;
|
||||
|
||||
const handleIconClick = () => {
|
||||
onIconClick && onIconClick();
|
||||
}
|
||||
|
||||
const renderNumb = () => {
|
||||
let tempNumb = number;
|
||||
if ((tempNumb || tempNumb === 0) && (typeof Number(tempNumb) === 'number')) {
|
||||
tempNumb = numberal(tempNumb).format('0,0');
|
||||
return (
|
||||
<span className={'numb_value'}>{tempNumb}</span>
|
||||
)
|
||||
}
|
||||
return '';
|
||||
}
|
||||
const renderCtx = () => {
|
||||
if (type === 'icon') { // 图标加文字时
|
||||
return (
|
||||
<div className={`text_number_area text_icon_numb flex_${position}`}>
|
||||
<Icon onClick={handleIconClick} type={text} className={'numb_icon'}></Icon>
|
||||
{renderNumb()}
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<div className={`text_number_area text_label_numb flex_${position}`}>
|
||||
<span className={'text_label'}>{text}</span>
|
||||
{renderNumb()}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
return (
|
||||
<React.Fragment>
|
||||
{renderCtx()}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
export default TextNumber;
|
@ -0,0 +1,43 @@
|
||||
.text_number_area{
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex_vertical{
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flex_horizontal{
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.text_label_numb,
|
||||
.text_icon_numb{
|
||||
line-height: 18px;
|
||||
vertical-align: top;
|
||||
.numb_value{
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.text_label_numb{
|
||||
.numb_value{
|
||||
color: #333333;
|
||||
}
|
||||
.text_label{
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.text_icon_numb{
|
||||
.numb_icon{
|
||||
font-size: 16px;
|
||||
margin-right: 5px;
|
||||
color: #333333;
|
||||
cursor: pointer;
|
||||
}
|
||||
.numb_value{
|
||||
color: #999999;
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
.banner-wrap{
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
background-image: url(/static/media/path.e39ba7de.png);
|
||||
background-color: #000a4f;
|
||||
/* background-size: cover; */
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.developer-list{
|
||||
// overflow: hidden;
|
||||
.ant-spin-container{
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
.card-top {
|
||||
border-radius:4px;
|
||||
background:rgba(255,255,255,1);
|
||||
height:56px;
|
||||
padding: 0 30px;
|
||||
margin-top: 20px;
|
||||
.search-params{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.save-question{
|
||||
width: 200px;
|
||||
}
|
||||
// .flex-end{
|
||||
// // float: right;
|
||||
// }
|
||||
.question-level{
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.card-table{
|
||||
margin-top: 10px;
|
||||
.filter_ctx_area{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 10px 30px;
|
||||
background: #fff;
|
||||
align-items: center;
|
||||
}
|
||||
.choice_ctx{
|
||||
flex: 1;
|
||||
}
|
||||
.ant-card-body{
|
||||
padding: 10px 30px;
|
||||
// width: 100%;
|
||||
}
|
||||
.dropdown-span{
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
.dropdonw-style{
|
||||
margin-right: 50px;
|
||||
.dropdown-span{
|
||||
cursor: pointer;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search_tag_style{
|
||||
background: rgb(82, 196, 26);
|
||||
color: #fff;
|
||||
.anticon-close{
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
@import '../split_pane_resizer.scss';
|
||||
|
@ -0,0 +1,14 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import connect from 'react-redux';
|
||||
|
||||
class CommitTab extends PureComponent {
|
||||
|
||||
render () {
|
||||
return (
|
||||
<h2>提交页</h2>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// export default connect()(CommitTab);
|
||||
export default CommitTab;
|
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* @Description: 添加测试用例
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-21 09:19:38
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-26 15:47:06
|
||||
*/
|
||||
import './index.scss';
|
||||
import React, { useState } from 'react';
|
||||
import { Collapse, Icon, Input, Form, Button, Modal } from 'antd';
|
||||
import { connect } from 'react-redux';
|
||||
import actions from '../../../../../redux/actions';
|
||||
const { Panel } = Collapse;
|
||||
const { TextArea } = Input;
|
||||
const FormItem = Form.Item;
|
||||
const AddTestDemo = (props) => {
|
||||
const {
|
||||
onSubmitTest,
|
||||
onDeleteTest,
|
||||
testCase,
|
||||
key,
|
||||
ojTestCaseValidate,
|
||||
index
|
||||
} = props;
|
||||
|
||||
const [isEditor, setIsEditor] = useState(false); // 是否是编辑
|
||||
// console.log('测试用例属性: ====>>>>', props);
|
||||
// 删除操作
|
||||
const handleDeletePanel = (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
// console.log('点击的删除按钮')
|
||||
Modal.confirm({
|
||||
title: '删除',
|
||||
content: '确定要删除当前测试用例吗?',
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
onOk() {
|
||||
console.log('确定删除');
|
||||
onDeleteTest(testCase);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 输入框值改变时
|
||||
const handleInputChange = (e) => {
|
||||
const { index, testCaseInputChange } = props;
|
||||
const value = e.target.value;
|
||||
testCaseInputChange(value, index);
|
||||
}
|
||||
|
||||
// 输出值改变时
|
||||
const handleOutputChange = (e) => {
|
||||
const { index, testCaseOutputChange } = props;
|
||||
const value = e.target.value;
|
||||
testCaseOutputChange(value, index);
|
||||
}
|
||||
|
||||
// 右侧删除图标
|
||||
const genExtra = () => (
|
||||
<Icon
|
||||
type="close"
|
||||
onClick={handleDeletePanel}
|
||||
/>
|
||||
)
|
||||
|
||||
// 取消操作
|
||||
const handleReset = (e) => {
|
||||
e.preventDefault();
|
||||
props.form.resetFields();
|
||||
}
|
||||
|
||||
// 保存
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
props.form.validateFields((err, values) => {
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
console.log('提交表单: ', values);
|
||||
onSubmitTest(values);
|
||||
});
|
||||
}
|
||||
// 编辑后保存
|
||||
const handleEditorOrSave = (e) => {
|
||||
if (!isEditor) {
|
||||
setIsEditor(true);
|
||||
} else {
|
||||
// TODO 调用修改测试用例接口
|
||||
setIsEditor(false); // 保存后 设置 false
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染提交按钮
|
||||
const renderSubmitBtn = () => {
|
||||
const { identifier, testCase, loading } = props;
|
||||
// console.log('========', identifier);
|
||||
// 1. 新增时,不显示按钮
|
||||
if (identifier) {
|
||||
if (testCase.isAdd) {
|
||||
return (
|
||||
<FormItem style={{ textAlign: 'right' }}>
|
||||
<Button style={{ marginRight: '20px' }} onClick={handleReset}>取消</Button>
|
||||
<Button type="primary" onClick={handleSubmit}>保存</Button>
|
||||
</FormItem>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<FormItem style={{ textAlign: 'right' }}>
|
||||
<Button onClick={handleEditorOrSave} loading={loading}>{isEditor ? '保存' : (loading ? '保存' : '编辑')}</Button>
|
||||
</FormItem>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文本输入框可编辑的情况
|
||||
* 1. 新增时
|
||||
* 2. isAdd 为 false 且 isEditor 为true 时
|
||||
* @param {*} testCase
|
||||
*/
|
||||
const isDisabled = (testCase) => {
|
||||
return !testCase.isAdd && !isEditor;
|
||||
};
|
||||
const {input = {}, output = {}} = (ojTestCaseValidate[index] = {});
|
||||
return (
|
||||
<Collapse className={'collapse_area'}>
|
||||
<Panel header={`测试用例${testCase.position}`} extra={genExtra()} key={key}>
|
||||
<Form>
|
||||
<FormItem
|
||||
label={<span className={'label_text'}>输入</span>}
|
||||
validateStatus={input.validateStatus}
|
||||
help={input.errMsg}
|
||||
colon={ false }
|
||||
>
|
||||
<TextArea
|
||||
rows={5}
|
||||
value={testCase.input}
|
||||
onChange={handleInputChange}
|
||||
disabled={isDisabled(testCase)}/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label={<span className={'label_text'}>输出</span>}
|
||||
validateStatus={output.validateStatus}
|
||||
help={output.errMsg}
|
||||
colon={ false }
|
||||
>
|
||||
<Input
|
||||
value={testCase.output}
|
||||
onChange={handleOutputChange}
|
||||
disabled={isDisabled(testCase)}/>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Panel>
|
||||
</Collapse>
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const {identifier, loading, ojTestCaseValidate} = state.ojFormReducer;
|
||||
return {
|
||||
identifier,
|
||||
loading,
|
||||
ojTestCaseValidate
|
||||
}
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
testCaseOutputChange: (value, index) => dispatch(actions.testCaseOutputChange(value, index)),
|
||||
testCaseInputChange: (value, index) => dispatch(actions.testCaseInputChange(value, index))
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(AddTestDemo);
|
@ -0,0 +1,284 @@
|
||||
/*
|
||||
* @Description:
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-20 10:35:40
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-27 19:04:03
|
||||
*/
|
||||
import 'quill/dist/quill.core.css';
|
||||
import 'quill/dist/quill.bubble.css';
|
||||
import 'quill/dist/quill.snow.css';
|
||||
import './index.scss';
|
||||
// import 'katex/dist/katex.css';
|
||||
import React, { PureComponent } from 'react';
|
||||
import { Form, Input, Select, InputNumber, Button } from 'antd';
|
||||
import { connect } from 'react-redux';
|
||||
import AddTestDemo from './AddTestDemo';
|
||||
import QuillEditor from '../../../quillEditor';
|
||||
import actions from '../../../../../redux/actions';
|
||||
import CONST from '../../../../../constants';
|
||||
const {jcLabel} = CONST;
|
||||
const FormItem = Form.Item;
|
||||
const { Option } = Select;
|
||||
|
||||
const maps = {
|
||||
language: [
|
||||
{ title: 'C', key: 'C' },
|
||||
{ title: 'C++', key: 'C++' },
|
||||
{ title: 'Python', key: 'Python' },
|
||||
{ title: 'Java', key: 'Java' }
|
||||
],
|
||||
difficult: [
|
||||
{ title: '简单', key: '1' },
|
||||
{ title: '中等', key: '2'},
|
||||
{ title: '困难', key: '3' }
|
||||
],
|
||||
category: [
|
||||
{ title: '程序设计', key: '1' },
|
||||
{ title: '算法', key: '2'}
|
||||
],
|
||||
openOrNot: [
|
||||
{ title: '公开', key: '1' },
|
||||
{ title: '私有', key: '0' }
|
||||
]
|
||||
}
|
||||
class EditTab extends PureComponent {
|
||||
|
||||
constructor (props) {
|
||||
super(props);
|
||||
this.editorRef = React.createRef();
|
||||
}
|
||||
|
||||
// 改变任务名称
|
||||
handleNameChange = (e) => {
|
||||
const value = e.target.value;
|
||||
this.props.validateOJName(value);
|
||||
}
|
||||
// 改变语言
|
||||
handleLanguageChange = (value) => {
|
||||
this.props.validateOjLanguage(value);
|
||||
}
|
||||
// 改变描述信息
|
||||
handleChangeDescription = (value) => {
|
||||
// console.log('获取的编辑器内容为: ', value);
|
||||
this.props.validateOjDescription(value);
|
||||
}
|
||||
// 改变难易度
|
||||
handleChangeDifficult = (value) => {
|
||||
this.props.validateOjDifficult(value);
|
||||
}
|
||||
// 改变时间限制
|
||||
handleTimeLimitChange = (value) => {
|
||||
this.props.validateOjTimeLimit(value);
|
||||
}
|
||||
// 改变分类
|
||||
handleChangeCategory = (value) => {
|
||||
this.props.validateOjCategory(value);
|
||||
}
|
||||
// 改变公开程序
|
||||
handleChangeOpenOrNot = (value) => {
|
||||
this.props.validateOpenOrNot(value);
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
ojFormReducer: {ojForm, ojFormValidate},
|
||||
testCases = [], // 测试用例集合
|
||||
position, // 添加的测试用例位置
|
||||
addTestCase, // 添加测试用例
|
||||
deleteTestCase, // 删除测试用例
|
||||
} = this.props;
|
||||
// console.log('当前位置: ', position);
|
||||
// console.log('OJForm: ', ojForm);
|
||||
// console.log('当前位置: ', testCases);
|
||||
// 表单label
|
||||
const myLabel = (name, subTitle) => {
|
||||
if (subTitle) {
|
||||
return (
|
||||
<span className={'label_text'}>
|
||||
{name}
|
||||
<span className={'label_sub_text'}>
|
||||
({subTitle})
|
||||
</span>
|
||||
</span>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<span className={'label_text'}>{name}</span>
|
||||
)
|
||||
}
|
||||
};
|
||||
// 编程语言
|
||||
const getOptions = (key) => {
|
||||
return maps[key].map((opt, i) => {
|
||||
return (
|
||||
<Option value={opt.key} key={`opt_${i}`}>{opt.title}</Option>
|
||||
);
|
||||
});
|
||||
};
|
||||
// 提交测试用例
|
||||
const handleSubmitTest = (obj) => {
|
||||
console.log('提交的测试用例: ', obj);
|
||||
};
|
||||
// 删除测试用例
|
||||
const handleDeleteTest = (obj) => {
|
||||
console.log('删除的测试用例: ', obj);
|
||||
deleteTestCase(obj);
|
||||
};
|
||||
const renderTestCase = () => {
|
||||
return testCases.map((item, i) => (
|
||||
<AddTestDemo
|
||||
key={`key_${i}`}
|
||||
onSubmitTest={handleSubmitTest}
|
||||
onDeleteTest={handleDeleteTest}
|
||||
testCase={item}
|
||||
index={i}
|
||||
/>
|
||||
));
|
||||
};
|
||||
// 添加测试用例
|
||||
const handleAddTest = () => {
|
||||
console.log('添加测试用例');
|
||||
const obj = {
|
||||
input: '',
|
||||
output: '',
|
||||
position: position,
|
||||
isAdd: true // 新增的测试用例
|
||||
}
|
||||
addTestCase(obj);
|
||||
// TODO 点击新增时,需要滚到到最底部
|
||||
// this.editorRef.current.scrollTop
|
||||
// const oDiv = this.editorRef.current;
|
||||
// oDiv.scrollTo(oDiv.scrollLeft, 99999);
|
||||
// console.log(oDiv.scrollTop);
|
||||
// oDiv.scrollTop = 99999;
|
||||
}
|
||||
return (
|
||||
<div className={'editor_area'}>
|
||||
<Form className={'editor_form'}>
|
||||
<FormItem
|
||||
className={`input_area flex_60`}
|
||||
label={<span>{myLabel(jcLabel['name'])}</span>}
|
||||
validateStatus={ojFormValidate.name.validateStatus}
|
||||
help={ojFormValidate.name.errMsg}
|
||||
colon={ false }
|
||||
>
|
||||
<Input
|
||||
maxLength={60}
|
||||
placeholder="请输入任务名称"
|
||||
value={ojForm.name}
|
||||
suffix={<span style={{ fontSize: '12px', color: 'rgba(0, 0, 0, 0.45)' }}>{60 - ojForm.name.length}</span>}
|
||||
onChange={this.handleNameChange}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
className={`input_area flex_40`}
|
||||
label={<span>{myLabel(jcLabel['language'])}</span>}
|
||||
validateStatus={ojFormValidate.language.validateStatus}
|
||||
help={ojFormValidate.language.errMsg}
|
||||
colon={ false }
|
||||
>
|
||||
<Select onChange={this.handleLanguageChange} value={`${ojForm.language}`}>
|
||||
{getOptions('language')}
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
className={`input_area flex_100`}
|
||||
label={<span>{myLabel(jcLabel['description'])}</span>}
|
||||
validateStatus={ojFormValidate.description.validateStatus}
|
||||
help={ojFormValidate.description.errMsg}
|
||||
colon={ false }
|
||||
>
|
||||
<QuillEditor
|
||||
style={{ height: '300px' }}
|
||||
placeholder="init content"
|
||||
onEditorChange={this.handleChangeDescription}
|
||||
htmlCtx={ojForm.description}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
className={`input_area flex_50 flex_50_left`}
|
||||
label={<span>{myLabel(jcLabel['difficult'], '任务的难易程度')}</span>}
|
||||
validateStatus={ojFormValidate.difficult.validateStatus}
|
||||
help={ojFormValidate.difficult.errMsg}
|
||||
colon={ false }
|
||||
>
|
||||
<Select onChange={this.handleChangeDifficult} value={`${ojForm.difficult}`}>
|
||||
{getOptions('difficult')}
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
className={`input_area flex_50 flex_50_right`}
|
||||
label={<span>{myLabel(jcLabel['timeLimit'], '程序允许时间限制时长,单位:秒')}</span>}
|
||||
validateStatus={ojFormValidate.timeLimit.validateStatus}
|
||||
help={ojFormValidate.timeLimit.errMsg}
|
||||
colon={ false }
|
||||
>
|
||||
<InputNumber value={ojForm.timeLimit} min={0} style={{ width: '100%' }} onChange={this.handleTimeLimitChange}/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
className={`input_area flex_50 flex_50_left`}
|
||||
label={<span>{myLabel(jcLabel['category'], '任务所属分类')}</span>}
|
||||
validateStatus={ojFormValidate.category.validateStatus}
|
||||
help={ojFormValidate.category.errMsg}
|
||||
colon={ false }
|
||||
>
|
||||
<Select onChange={this.handleChangeCategory} value={`${ojForm.category}`}>
|
||||
{getOptions('category')}
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
className={`input_area flex_50 flex_50_right`}
|
||||
label={<span>{myLabel(jcLabel['openOrNot'], '社区:您的任务将向整个社会公开')}</span>}
|
||||
validateStatus={ojFormValidate.openOrNot.validateStatus}
|
||||
help={ojFormValidate.openOrNot.errMsg}
|
||||
colon={ false }
|
||||
>
|
||||
<Select onChange={this.handleChangeOpenOrNot} value={`${ojForm.openOrNot}`}>
|
||||
{getOptions('openOrNot')}
|
||||
</Select>
|
||||
</FormItem>
|
||||
</Form>
|
||||
|
||||
{/* 添加测试用例 */}
|
||||
<div className="test_demo_title">
|
||||
<h2>测试用例</h2>
|
||||
<Button type="primary" onClick={handleAddTest}>添加测试用例</Button>
|
||||
</div>
|
||||
<div className="test_demo_ctx">
|
||||
{ renderTestCase() }
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const ojFormReducer = state.ojFormReducer;
|
||||
return {
|
||||
ojFormReducer,
|
||||
testCases: ojFormReducer.testCases, // 测试用例
|
||||
position: ojFormReducer.position, // 测试用例位置
|
||||
}
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
// 任务名称校验
|
||||
validateOJName: (value) => dispatch(actions.validateOJName(value)),
|
||||
validateOjLanguage: (value) => dispatch(actions.validateOjLanguage(value)),
|
||||
validateOjDescription: (value) => dispatch(actions.validateOjDescription(value)),
|
||||
validateOjDifficult: (value) => dispatch(actions.validateOjDifficult(value)),
|
||||
validateOjTimeLimit: (value) => dispatch(actions.validateOjTimeLimit(value)),
|
||||
validateOjCategory: (value) => dispatch(actions.validateOjCategory(value)),
|
||||
validateOpenOrNot: (value) => dispatch(actions.validateOpenOrNot(value)),
|
||||
// 新增测试用例
|
||||
addTestCase: (value) => dispatch(actions.addTestCase(value)),
|
||||
// 删除测试用例
|
||||
deleteTestCase: (value) => dispatch(actions.deleteTestCase(value)),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(EditTab);
|
@ -0,0 +1,65 @@
|
||||
|
||||
.editor_area{
|
||||
padding: 20px 0;
|
||||
.editor_form{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.label_text{
|
||||
position: relative;
|
||||
font-size: 14px;
|
||||
&::before{
|
||||
display: inline-block;
|
||||
margin-right: 4px;
|
||||
color: #f5222d;
|
||||
font-size: 14px;
|
||||
font-family: SimSun,sans-serif;
|
||||
line-height: 1;
|
||||
content: '*';
|
||||
}
|
||||
}
|
||||
.input_area{
|
||||
display: inline-block;
|
||||
&.flex_60{
|
||||
padding-right: 20px;
|
||||
width: 60%;
|
||||
}
|
||||
&.flex_40{
|
||||
width: 40%;
|
||||
}
|
||||
&.flex_100{
|
||||
width: 100%;
|
||||
}
|
||||
&.flex_50{
|
||||
width: 50%;
|
||||
}
|
||||
&.flex_50_left{
|
||||
padding-right: 10px;
|
||||
}
|
||||
&.flex_50_right{
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
.label_sub_text{
|
||||
font-size: 12px;
|
||||
color: #999999;
|
||||
}
|
||||
.test_demo_title,
|
||||
.test_demo_ctx,
|
||||
.editor_form{
|
||||
margin: 0 30px;
|
||||
}
|
||||
.test_demo_title{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 60px;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.collapse_area{
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* @Description: 左侧编辑 / 评论 / 提交记录
|
||||
* @Author: tangjiang
|
||||
* @Date: 2019-11-19 11:35:30
|
||||
* @Last Modified by: tangjiang
|
||||
* @Last Modified time: 2019-11-19 19:07:02
|
||||
*/
|
||||
|
||||
import './index.scss';
|
||||
import React, { useState } from 'react';
|
||||
import { Tabs } from 'antd';
|
||||
import EditorTab from './editorTab';
|
||||
import PrevTab from './prevTab';
|
||||
import CommitTab from './commitTab';
|
||||
// import * from 'rc-form';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
const LeftPane = () => {
|
||||
|
||||
const [defaultActiveKey, setDefaultActiveKey] = useState('editor');
|
||||
|
||||
const tabArrs = [
|
||||
{ title: '编辑', key: 'editor', content: (<EditorTab />) },
|
||||
{ title: '预览', key: 'prev', content: (<PrevTab />) },
|
||||
// { title: '提交记录', key: 'commit', content: (<CommitTab />) },
|
||||
];
|
||||
|
||||
const tabs = tabArrs.map((tab) => {
|
||||
const Comp = tab.content;
|
||||
return (
|
||||
<TabPane tab={tab.title} key={tab.key}>
|
||||
{ Comp }
|
||||
</TabPane>
|
||||
)
|
||||
});
|
||||
|
||||
// tab切换时
|
||||
const handleTabChange = (key) => {
|
||||
setDefaultActiveKey(key);
|
||||
}
|
||||
|
||||
return (
|
||||
<Tabs activeKey={defaultActiveKey} onChange={handleTabChange}>
|
||||
{ tabs }
|
||||
</Tabs>
|
||||
)
|
||||
};
|
||||
|
||||
export default LeftPane;
|
@ -0,0 +1,25 @@
|
||||
// .split-pane-left{
|
||||
// .ant-tabs-nav-wrap{
|
||||
// padding: 0 30px;
|
||||
// }
|
||||
// .ant-tabs-bar{
|
||||
// margin: 0;
|
||||
// }
|
||||
// // .ant-tabs-tabpane{
|
||||
// // padding-top: 10px;
|
||||
// // height: calc(100vh - 110px);
|
||||
// // overflow: auto;
|
||||
// // }
|
||||
|
||||
// .ant-form-item-control{
|
||||
// line-height: 1;
|
||||
// }
|
||||
|
||||
// .editor_area,
|
||||
// .prev_area{
|
||||
// height: calc(100vh - 110px);
|
||||
// overflow-y: auto;
|
||||
// padding: 20px 0;
|
||||
// }
|
||||
// }
|
||||
@import '../../split_pane_resizer.scss';
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* @Description: 代码预览页面
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-24 10:09:55
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-27 19:30:51
|
||||
*/
|
||||
import './index.scss';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import {Empty} from 'antd';
|
||||
import QuillEditor from '../../../quillEditor';
|
||||
const PrevTab = (props) => {
|
||||
// const { } = props;
|
||||
const [desc, setDesc] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
setDesc(props.description);
|
||||
}, [props.description]);
|
||||
|
||||
const renderHtml = () => {
|
||||
if (!desc) {
|
||||
return (
|
||||
<div className={'no_result'}>
|
||||
<Empty />
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className={'render_html'} dangerouslySetInnerHTML={{ __html: desc }}></div>
|
||||
)
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div className={`prev_area`}>
|
||||
{/* {renderHtml()} */}
|
||||
{/* <div dangerouslySetInnerHTML={{ __html: desc }}></div> */}
|
||||
<QuillEditor
|
||||
style={{ height: 'calc(100% - 45px)', overflowY: 'auto' }}
|
||||
options={[]}
|
||||
readOnly={true}
|
||||
htmlCtx={props.description}/>
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
const mapStateToProps = (state) => {
|
||||
const { ojForm } = state.ojFormReducer;
|
||||
return {
|
||||
description: ojForm.description
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps
|
||||
)(PrevTab);
|
@ -0,0 +1,11 @@
|
||||
.no_result{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
|
||||
}
|
||||
|
||||
.render_html{
|
||||
padding: 20px 30px;
|
||||
}
|
@ -0,0 +1,215 @@
|
||||
/*
|
||||
* @Description: 右侧代码块
|
||||
* @Author: tangjiang
|
||||
* @Date: 2019-11-18 08:42:04
|
||||
* @Last Modified by: tangjiang
|
||||
* @Last Modified time: 2019-11-20 00:00:34
|
||||
*/
|
||||
|
||||
import './index.scss';
|
||||
|
||||
import React, { Fragment, useState, useRef, useEffect } from 'react';
|
||||
import { Icon, Drawer, Tabs, Button, notification } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import MonacoEditor from '@monaco-editor/react';
|
||||
import { connect } from 'react-redux';
|
||||
import InitTabCtx from './initTabCtx';
|
||||
import SettingDrawer from '../../components/monacoSetting';
|
||||
import CONST from '../../../../constants';
|
||||
import actions from '../../../../redux/actions';
|
||||
|
||||
const { fontSetting, opacitySetting } = CONST;
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
const RightPaneCode = (props) => {
|
||||
|
||||
const [showDrawer, setShowDrawer] = useState(false); // 控制配置滑框
|
||||
const [defaultActiveKey, setDefaultActiveKey] = useState('1'); // 当前选中的tab
|
||||
const [showTextResult, setShowTextResult] = useState(false); // 是否点击控制台按钮
|
||||
const [editCode, setEditCode] = useState(()=> {
|
||||
return '#include <stdio.h>';
|
||||
}); // monaco编辑器内容
|
||||
const [language, setLanguage] = useState('C')
|
||||
const [fontSize,setFontSize] = useState(12);
|
||||
const editorRef = useRef(null); // 编辑器ref
|
||||
|
||||
useEffect(() => {
|
||||
if (props.language) {
|
||||
// console.log('当前输入的代码:', editCode);
|
||||
// console.log('当前输入的语言:', props.language);
|
||||
setLanguage(props.language)
|
||||
}
|
||||
}, [props.language]);
|
||||
|
||||
useEffect(() => {
|
||||
}, [props.testCases]);
|
||||
|
||||
useEffect(() => {
|
||||
}, [editCode]);
|
||||
|
||||
// 监听store中编辑器内容变化
|
||||
useEffect(() => {
|
||||
setEditCode(props.code);
|
||||
}, [props.code]);
|
||||
|
||||
// 打开设置
|
||||
const handleShowDrawer = (e) => {
|
||||
e.preventDefault();
|
||||
setShowDrawer(true);
|
||||
}
|
||||
|
||||
// 关闭设置
|
||||
const handleDrawerClose = (e) => {
|
||||
e.preventDefault();
|
||||
setShowDrawer(false);
|
||||
}
|
||||
|
||||
// 切换tab
|
||||
const handleTabChange = (key) => {
|
||||
setDefaultActiveKey(key);
|
||||
}
|
||||
|
||||
// 显示/隐藏tab
|
||||
const handleShowControl = () => {
|
||||
setShowTextResult(!showTextResult);
|
||||
}
|
||||
|
||||
// 侧边栏改变字体大小
|
||||
const handleFontSizeChange = (value) => {
|
||||
setFontSize(value);
|
||||
}
|
||||
// 文本框内容变化时,记录文本框内容
|
||||
const handleEditorChange = (origin, monaco) => {
|
||||
editorRef.current = monaco; // 获取当前monaco实例
|
||||
setEditCode(origin); // 保存编辑器初始值
|
||||
editorRef.current.onDidChangeModelContent(e => { // 监听编辑器内容的变化
|
||||
// TODO 需要优化 节流
|
||||
const val = editorRef.current.getValue();
|
||||
setEditCode(val);
|
||||
// 保存当前代码
|
||||
props.saveOjFormCode(val);
|
||||
});
|
||||
}
|
||||
|
||||
// 提交按钮点击
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
if (!editCode) {
|
||||
notification['error']({
|
||||
message: '必填',
|
||||
description: '代码块内容必须输入!'
|
||||
});
|
||||
editorRef.current.focus();
|
||||
return;
|
||||
}
|
||||
props.changePublishLoadingStatus(true);
|
||||
const { onSubmitForm } = props;
|
||||
onSubmitForm(editCode);
|
||||
}
|
||||
|
||||
// 调试测试代码
|
||||
// const handleTestCode = () => {
|
||||
// // 打开控制台信息
|
||||
// setShowTextResult(true);
|
||||
// this.formRef.handleTestCodeFormSubmit(() => {
|
||||
// // 当验证通过后 切换tab 到代码执行结果
|
||||
// setDefaultActiveKey('2');
|
||||
// });
|
||||
// }
|
||||
|
||||
// 控制台点击时 添加active属性
|
||||
const classNames = `control_tab ${showTextResult ? 'move_up move_up_final' : 'move_down_final'}`;
|
||||
|
||||
// 配置编辑器属性
|
||||
const editorOptions = {
|
||||
selectOnLineNumbers: true,
|
||||
automaticLayout: true,
|
||||
fontSize: `${fontSize}px`
|
||||
}
|
||||
|
||||
// 返回渲染值
|
||||
return (
|
||||
<Fragment>
|
||||
<div className={'right_pane_code_wrap'}>
|
||||
<div className={'code-title'}>
|
||||
<span></span>
|
||||
<Icon className={'code-icon'} type="setting" onClick={handleShowDrawer}/>
|
||||
</div>
|
||||
{/** 代码编辑器 */}
|
||||
<MonacoEditor
|
||||
height={showTextResult ? 'calc(100% - 382px)' : 'calc(100% - 112px)'}
|
||||
width="100%"
|
||||
language={language.toLowerCase()}
|
||||
value={editCode}
|
||||
options={editorOptions}
|
||||
theme="dark"
|
||||
editorDidMount={handleEditorChange}
|
||||
/>
|
||||
{/* 控制台信息 */}
|
||||
<div className="pane_control_area">
|
||||
<Tabs
|
||||
className={classNames}
|
||||
activeKey={defaultActiveKey}
|
||||
tabBarStyle={{ backgroundColor: '#000', color: '#fff' }}
|
||||
onChange={handleTabChange}
|
||||
>
|
||||
<TabPane tab={'自定义测试用例'} key={'1'} style={{ height: '280px', overflowY: 'auto' }}>
|
||||
<InitTabCtx wrappedComponentRef={(form) => this.formRef = form }/>
|
||||
</TabPane>
|
||||
<TabPane tab={'代码执行结果'} key={'2'} style={{ height: '280px', overflowY: 'auto' }}>
|
||||
<h2>代码执行结果</h2>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
<div className="pane_control_opts">
|
||||
<Button type="link" style={{ color: '#fff' }} onClick={handleShowControl}>控制台 <Icon type={ showTextResult ? "down" : "up" } /></Button>
|
||||
<p>
|
||||
{/* <Button ghost
|
||||
style={{ marginRight: '10px', color: '#28BD8B', borderColor: '#28BD8B' }}
|
||||
onClick={handleTestCode}
|
||||
disabled={!props.identifier || props.testCases.length === 0}
|
||||
>调试代码</Button> */}
|
||||
<Button
|
||||
loading={props.submitLoading}
|
||||
type="primary"
|
||||
onClick={handleSubmit}
|
||||
>{props.identifier ? '更新' : '提交'}</Button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Drawer
|
||||
className={'setting_drawer'}
|
||||
placement="right"
|
||||
closable={false}
|
||||
onClose={handleDrawerClose}
|
||||
visible={showDrawer}
|
||||
>
|
||||
<SettingDrawer {...fontSetting} onChangeFontSize={handleFontSizeChange}/>
|
||||
<SettingDrawer {...opacitySetting}/>
|
||||
</Drawer>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const { ojForm, testCases, identifier, code } = state.ojFormReducer;
|
||||
const { submitLoading } = state.commonReducer;
|
||||
return {
|
||||
language: ojForm.language,
|
||||
testCases,
|
||||
identifier,
|
||||
code,
|
||||
submitLoading
|
||||
}
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
saveOjFormCode: (code) => dispatch(actions.saveOjFormCode(code)),
|
||||
changePublishLoadingStatus: (flag) => dispatch(actions.changeSubmitLoadingStatus(flag))
|
||||
});
|
||||
//
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(RightPaneCode);
|
@ -0,0 +1,139 @@
|
||||
.right_pane_code_wrap{
|
||||
position: relative;
|
||||
// justify-content: center;
|
||||
background-color: #222;
|
||||
height: 100%;
|
||||
// justify-content: ;
|
||||
.code-title,
|
||||
.controller-pane,
|
||||
.pane_control_opts{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
// padding: 0 30px;
|
||||
background: #000;
|
||||
color: #fff;
|
||||
}
|
||||
.code-title,
|
||||
.pane_control_opts{
|
||||
padding: 0 30px;
|
||||
}
|
||||
|
||||
.code-title{
|
||||
height: 56px;
|
||||
.code-icon{
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
// .controller-pane{
|
||||
// min-height: 56px;
|
||||
// background-color: #222;
|
||||
// }
|
||||
.code-pane-wrap{
|
||||
height: 800px;
|
||||
// position: absolute;
|
||||
// top: 56px;
|
||||
// bottom: 56px;
|
||||
// width: 100%;
|
||||
}
|
||||
|
||||
.pane_control_area{
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
// height: 56px;
|
||||
.control_tab{
|
||||
position: absolute;
|
||||
bottom: -325px;
|
||||
width: 100%;
|
||||
// transition: all .2s;
|
||||
opacity: 0;
|
||||
// animation: .3s ease-in-out move_up;
|
||||
// &.active{
|
||||
// bottom: 0;
|
||||
// opacity: 1;
|
||||
// }
|
||||
&.move_up{
|
||||
animation: move_up .3s ease-in;
|
||||
}
|
||||
&.move_up_final {
|
||||
bottom: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
&.move_down{
|
||||
animation: move_down .3s ease-in-out;
|
||||
}
|
||||
&.move_down_final{
|
||||
bottom: -325px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.pane_control_opts{
|
||||
height: 56px;
|
||||
}
|
||||
|
||||
.ant-tabs-bar{
|
||||
padding: 0 10px;
|
||||
margin: 0px;
|
||||
border-bottom: transparent;
|
||||
}
|
||||
.ant-tabs-ink-bar{
|
||||
bottom: 1px;
|
||||
}
|
||||
// .tab_ctx_area.pos_center{
|
||||
// background: #222;
|
||||
// }
|
||||
.pane_control_opts{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
z-index: 20;
|
||||
}
|
||||
}
|
||||
|
||||
.setting_drawer{
|
||||
.setting_h2{
|
||||
line-height: 50px;
|
||||
}
|
||||
.setting_desc{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
.flex_item{
|
||||
line-height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes move_up {
|
||||
0%{
|
||||
opacity: 0;
|
||||
// bottom: -325px;
|
||||
}
|
||||
90%{
|
||||
opacity: 0.5;
|
||||
// bottom: 0px;
|
||||
}
|
||||
100%{
|
||||
opacity: 1;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes move_down{
|
||||
0%{
|
||||
opacity: 1;
|
||||
bottom: 0
|
||||
}
|
||||
10%{
|
||||
opacity: .2;
|
||||
}
|
||||
20%{
|
||||
opacity: 0;
|
||||
}
|
||||
100%{
|
||||
opacity: 0;
|
||||
bottom: -325px;
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
.tab_ctx_area{
|
||||
display: flex;
|
||||
height: 100%;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
&.pos_start{
|
||||
justify-content: flex-start;
|
||||
}
|
||||
&.pos_center{
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
&.pos_end{
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.ctx_default{
|
||||
margin: 10px 20px;
|
||||
}
|
||||
.ctx_loading,
|
||||
.ctx_loaded{
|
||||
display: flex;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
top: -20px;
|
||||
color: #1890ff;
|
||||
.ctx_icon{
|
||||
font-size: 40px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user_case_form{
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-top: 20px;
|
||||
.input_area{
|
||||
flex: 1;
|
||||
.ant-form-item-required{
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.flex_l{
|
||||
padding: 0 10px 0 20px;
|
||||
color: #fff;
|
||||
}
|
||||
.flex_r{
|
||||
padding: 0 20px 0 10px;
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* @Description: 编辑器侧边栏设置信息
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-25 17:50:33
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-27 14:40:25
|
||||
*/
|
||||
import React from 'react';
|
||||
import { Select } from 'antd';
|
||||
|
||||
const { Option } = Select;
|
||||
const SettingDrawer = (props) => {
|
||||
/**
|
||||
* title: '', // 一级标题
|
||||
* type: '', // 类型: 目录 select 和 文本
|
||||
* content: [] // 显示的内容 { text: '' , value: string | [{ key: 1, value: '', text: '' }] }
|
||||
*/
|
||||
const {title, type = 'label', content = [] } = props;
|
||||
|
||||
const handleFontSize = (value) => {
|
||||
const {onChangeFontSize} = props;
|
||||
// console.log('fong size change: ', value);
|
||||
onChangeFontSize && onChangeFontSize(value);
|
||||
}
|
||||
|
||||
const renderCtx = (title, content = [], type = 'label') => {
|
||||
const result = content.map((ctx, index) => {
|
||||
const subText = ctx.text;
|
||||
const value = ctx.value;
|
||||
let renderResult = '';
|
||||
if (typeof value === 'string') {
|
||||
renderResult = (
|
||||
<div className={'setting_desc'} key={`lab_${index}`}>
|
||||
<span className={'flex_item'}>{subText}</span>
|
||||
<span className={'flex_item'}>{ctx.value}</span>
|
||||
</div>
|
||||
);
|
||||
} else if (Array.isArray(value)) {
|
||||
if (type === 'select') {
|
||||
const child = ctx.value.map((opt, i) => (
|
||||
<Option key={opt.key || `${opt.value}`} value={opt.value}>
|
||||
{opt.text}
|
||||
</Option>
|
||||
));
|
||||
renderResult = (
|
||||
<div className={'setting_desc'} key={`sel_${index}`}>
|
||||
<span className={'flex_item'}>{ctx.text}</span>
|
||||
<Select className={'flex_item'} style={{ width: '100px'}} onChange={handleFontSize}>
|
||||
{child}
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
return renderResult;
|
||||
});
|
||||
return (
|
||||
<React.Fragment>
|
||||
<h2 className={'setting_h2'}>{title}</h2>
|
||||
{ result }
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className={'setting_area'}>
|
||||
{renderCtx(title, content, type)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SettingDrawer;
|
@ -0,0 +1,3 @@
|
||||
.quill_editor_area{
|
||||
height: 300px;
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
|
||||
.new_add_task_wrap,
|
||||
.student_study_warp{
|
||||
height: 100vh;
|
||||
.task_header,
|
||||
.student_study_header{
|
||||
height: 65px;
|
||||
background:rgba(34,34,34,1);
|
||||
padding:0 30px;
|
||||
}
|
||||
|
||||
.task_header{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// justify-content: space-between;
|
||||
.header_btn,
|
||||
.header_title{
|
||||
color: #fff;
|
||||
}
|
||||
.header_btn{
|
||||
width: 88px;
|
||||
}
|
||||
.header_title{
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.split-pane-area,
|
||||
.pane_right_area{
|
||||
position: relative;
|
||||
height: calc(100% - 65px);
|
||||
.left_pane,
|
||||
.right_pane{
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.student_study_header{
|
||||
position: relative;
|
||||
.avator_nicker,
|
||||
.study_quit,
|
||||
.study_name{
|
||||
color: #fff;
|
||||
line-height: 65px;
|
||||
}
|
||||
|
||||
.avator_nicker,
|
||||
.study_quit{
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.student_nicker{
|
||||
margin-left: 20px;
|
||||
}
|
||||
.study_quit{
|
||||
float: right;
|
||||
}
|
||||
.study_name{
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.split-pane-area,
|
||||
.split-pane-left{
|
||||
.ant-tabs-nav-wrap{
|
||||
padding: 0 30px;
|
||||
}
|
||||
.ant-tabs-bar{
|
||||
margin: 0;
|
||||
}
|
||||
// .ant-tabs-tabpane{
|
||||
// padding-top: 10px;
|
||||
// height: calc(100vh - 110px);
|
||||
// overflow: auto;
|
||||
// }
|
||||
|
||||
.ant-form-item-control{
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.editor_area,
|
||||
.prev_area{
|
||||
height: calc(100vh - 110px);
|
||||
overflow-y: auto;
|
||||
// padding: 20px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.Resizer {
|
||||
background: #000;
|
||||
opacity: 0.2;
|
||||
z-index: 1;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-moz-background-clip: padding;
|
||||
-webkit-background-clip: padding;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
.Resizer:hover {
|
||||
-webkit-transition: all 2s ease;
|
||||
transition: all 2s ease;
|
||||
}
|
||||
|
||||
.Resizer.horizontal {
|
||||
height: 11px;
|
||||
margin: -5px 0;
|
||||
border-top: 5px solid rgba(255, 255, 255, 0);
|
||||
border-bottom: 5px solid rgba(255, 255, 255, 0);
|
||||
cursor: row-resize;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.Resizer.horizontal:hover {
|
||||
border-top: 5px solid rgba(0, 0, 0, 0.5);
|
||||
border-bottom: 5px solid rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.Resizer.vertical {
|
||||
width: 11px;
|
||||
margin: 0 -5px;
|
||||
border-left: 5px solid rgba(255, 255, 255, 0);
|
||||
border-right: 5px solid rgba(255, 255, 255, 0);
|
||||
cursor: col-resize;
|
||||
}
|
||||
|
||||
.Resizer.vertical:hover {
|
||||
border-left: 5px solid rgba(0, 0, 0, 0.5);
|
||||
border-right: 5px solid rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
.Resizer.disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.Resizer.disabled:hover {
|
||||
border-color: transparent;
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* @Description: 学员学习
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-23 10:53:19
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-29 08:56:18
|
||||
*/
|
||||
import './index.scss';
|
||||
import React, { useEffect } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import SplitPane from 'react-split-pane';
|
||||
import LeftPane from './leftpane';
|
||||
import RightPane from './rightpane';
|
||||
import { Link } from 'react-router-dom';
|
||||
// import RightPane from '../newOrEditTask/rightpane';
|
||||
import { Button, Avatar } from 'antd';
|
||||
import actions from '../../../redux/actions';
|
||||
|
||||
const StudentStudy = (props) => {
|
||||
|
||||
useEffect(() => {
|
||||
const { match: { params }, startProgramQuestion } = props;
|
||||
let { id } = params;
|
||||
startProgramQuestion(id);
|
||||
}, []);
|
||||
return (
|
||||
<div className={'student_study_warp'}>
|
||||
<div className={'student_study_header'}>
|
||||
<div className={'avator_nicker'}>
|
||||
<Avatar icon="user" />
|
||||
<span className={'student_nicker'}>
|
||||
我是昵称
|
||||
</span>
|
||||
</div>
|
||||
<div className={'study_name'}>
|
||||
<span>乘积最大序列</span>
|
||||
</div>
|
||||
<div className={'study_quit'}>
|
||||
<Button>
|
||||
<Link to="/problems">退出</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="split-pane-area">
|
||||
<SplitPane split="vertical" minSize={200} maxSize={-200} defaultSize="50%">
|
||||
<div className={'split-pane-left'}>
|
||||
<LeftPane />
|
||||
</div>
|
||||
<SplitPane split="vertical" defaultSize="100%" allowResize={false}>
|
||||
<RightPane />
|
||||
<div />
|
||||
</SplitPane>
|
||||
</SplitPane>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => ({});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
// 调用开启编辑
|
||||
startProgramQuestion: (id) => dispatch(actions.startProgramQuestion(id))
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(StudentStudy);
|
||||
|
||||
|
@ -0,0 +1,7 @@
|
||||
@import '../split_pane_resizer.scss';
|
||||
|
||||
.right_pane_code_wrap{
|
||||
position: relative;
|
||||
background-color: #222;
|
||||
height: 100%;
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* @Description:
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-27 09:49:35
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-27 09:52:53
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
const Comment = (props) => {
|
||||
|
||||
return (
|
||||
<h2> Comment </h2>
|
||||
)
|
||||
}
|
||||
|
||||
export default Comment;
|
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* @Description: 提交记录
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-27 09:49:33
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-28 19:54:56
|
||||
*/
|
||||
import './index.scss';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Table, Icon } from 'antd';
|
||||
import { connect } from 'react-redux';
|
||||
import actions from '../../../../../redux/actions';
|
||||
import CONST from '../../../../../constants';
|
||||
import moment from 'moment';
|
||||
const numberal = require('numeral');
|
||||
|
||||
const {reviewResult} = CONST;
|
||||
const columns = [
|
||||
{
|
||||
title: '提交时间',
|
||||
dataIndex: 'created_at',
|
||||
render: (created_at) => (
|
||||
<span>
|
||||
{moment(created_at, 'YYYYMMDD HHmmss').fromNow()}
|
||||
</span>)
|
||||
},
|
||||
{
|
||||
title: '提交结果',
|
||||
dataIndex: 'status',
|
||||
render: (value) => (<span style={{ color: value === 0 ? '#28BD8B' : '#E6262E'}}>{reviewResult[value]}</span>)
|
||||
},
|
||||
{
|
||||
title: '执行用时',
|
||||
dataIndex: 'execute_time',
|
||||
render: (value) => (<span>{`${value}s`}</span>)
|
||||
},
|
||||
{
|
||||
title: '内存消耗',
|
||||
dataIndex: 'execute_memory',
|
||||
render: (value) => {
|
||||
if (value) {
|
||||
return <span>{numberal(+value).format('0.00b')}</span>
|
||||
} else {
|
||||
return (<span>0MB</span>)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '语言',
|
||||
dataIndex: 'language'
|
||||
}
|
||||
]
|
||||
|
||||
const paginationConfig = {
|
||||
total: 1, // 总条数
|
||||
pageSize: 10, // 每页显示条数
|
||||
current: 1, // 当前页数
|
||||
showQuickJumper: true
|
||||
}
|
||||
const CommitRecord = (props) => {
|
||||
|
||||
const {
|
||||
identifier,
|
||||
commitRecord,
|
||||
commitRecordDetail,
|
||||
getUserCommitRecord
|
||||
} = props;
|
||||
|
||||
const [pagination, setPagination] = useState(paginationConfig);
|
||||
const [tableData, setTableData] = useState([]);
|
||||
// const [recordDetail, setRecordDetail] = useState({});
|
||||
const [renderCtx, setRenderCtx] = useState(() => {
|
||||
return function () {
|
||||
return '';
|
||||
}
|
||||
});
|
||||
// 渲染提交记录详情
|
||||
const renderRecordDetail = () => {
|
||||
const {
|
||||
error_line,
|
||||
error_msg,
|
||||
execute_memory,
|
||||
execute_time,
|
||||
input,
|
||||
output,
|
||||
status,
|
||||
expected_output
|
||||
} = commitRecordDetail;
|
||||
console.log('========', commitRecordDetail);
|
||||
if (Object.keys(commitRecordDetail).length > 0) {
|
||||
const classes = status === 0 ? 'record_result_suc' : 'record_result_err';
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className={'record_header'}>
|
||||
<span className={'record_result'}>
|
||||
执行结果: <span className={classes}>{reviewResult[status]}</span>
|
||||
</span>
|
||||
<span className={'copy_error'}>
|
||||
复制错误信息 <Icon type="copy" className={'icon_style'}/>
|
||||
</span>
|
||||
<span className={'show_detail'}>
|
||||
显示详情 <Icon type="right" className={'icon_style'}/>
|
||||
</span>
|
||||
</div>
|
||||
{/* <div className={'record_error_info'}>错误代码</div> */}
|
||||
</React.Fragment>
|
||||
);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('调用记录详情=====>>>');
|
||||
getUserCommitRecord(identifier);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('====>>>>+++++++++++++', commitRecord);
|
||||
setTableData(commitRecord);
|
||||
}, [commitRecord]);
|
||||
|
||||
useEffect(() => {
|
||||
// setRecordDetail(commitRecordDetail);
|
||||
setRenderCtx(() => (renderRecordDetail))
|
||||
}, [commitRecordDetail]);
|
||||
|
||||
console.log(commitRecord);
|
||||
return (
|
||||
<div className={'commit_record_area'}>
|
||||
{renderCtx()}
|
||||
<Table
|
||||
columns={columns}
|
||||
rowKey={record => Math.random()}
|
||||
dataSource={tableData}
|
||||
pagination={pagination}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const {
|
||||
ojForUserReducer
|
||||
} = state;
|
||||
const {
|
||||
user_program_identifier,
|
||||
commitRecordDetail,
|
||||
commitRecord
|
||||
} = ojForUserReducer;
|
||||
return {
|
||||
identifier: user_program_identifier,
|
||||
commitRecordDetail,
|
||||
commitRecord // 提交记录
|
||||
}
|
||||
}
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
getUserCommitRecord: (identifier) => dispatch(actions.getUserCommitRecord(identifier))
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(CommitRecord);
|
@ -0,0 +1,41 @@
|
||||
.commit_record_area{
|
||||
padding: 20px 30px;
|
||||
.record_header{
|
||||
display: flex;
|
||||
// justify-content: space-between;
|
||||
// background: gold;
|
||||
height: 66px;
|
||||
align-items: center;
|
||||
.record_result{
|
||||
color: #333333;
|
||||
font-size: 16px;
|
||||
// width:1px;
|
||||
}
|
||||
.copy_error{
|
||||
text-align: right;
|
||||
flex: 1;
|
||||
padding-right: 40px;
|
||||
}
|
||||
.show_detail{
|
||||
// width: 1px;
|
||||
}
|
||||
.copy_error,
|
||||
.show_detail{
|
||||
color: #5091FF;
|
||||
font-size: 14px;
|
||||
}
|
||||
.icon_style{
|
||||
font-size: 12px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
.record_result_suc{
|
||||
color: #28BD8B;
|
||||
}
|
||||
.record_result_err{
|
||||
color: #E51C24;
|
||||
}
|
||||
}
|
||||
.record_error_info{
|
||||
padding: 20px 30px;
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* @Description: 学员测评页面
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-23 11:33:41
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-29 11:10:57
|
||||
// */
|
||||
import './index.scss';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Tabs, Divider } from 'antd';
|
||||
import { connect } from 'react-redux';
|
||||
import Comment from './comment';
|
||||
import CommitRecord from './commitRecord';
|
||||
import TaskDescription from './taskDescription';
|
||||
import TextNumber from './../../components/textNumber';
|
||||
import actions from '../../../../redux/actions';
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
const LeftPane = (props) => {
|
||||
|
||||
const { hack, userCodeTab, changeUserCodeTab } = props;
|
||||
const { pass_count, submit_count } = hack;
|
||||
const [defaultActiveKey, setDefaultActiveKey] = useState('task');
|
||||
console.log(pass_count, submit_count);
|
||||
const tabArrs = [
|
||||
{ title: '任务描述', key: 'task', content: (<TaskDescription />) },
|
||||
{ title: '提交记录', key: 'record', content: (<CommitRecord />) },
|
||||
// { title: '评论', key: 'comment', content: (<Comment />) },
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
setDefaultActiveKey(userCodeTab);
|
||||
}, [userCodeTab])
|
||||
|
||||
const tabs = tabArrs.map((tab) => {
|
||||
const Comp = tab.content;
|
||||
return (
|
||||
<TabPane tab={tab.title} key={tab.key}>
|
||||
{ Comp }
|
||||
</TabPane>
|
||||
)
|
||||
});
|
||||
|
||||
// tab切换时
|
||||
const handleTabChange = (key) => {
|
||||
// setDefaultActiveKey(key);
|
||||
changeUserCodeTab(key);
|
||||
}
|
||||
|
||||
// 点击消息
|
||||
const handleClickMessage = () => {
|
||||
console.log('点击的消息图标---------');
|
||||
}
|
||||
|
||||
// 点击点赞
|
||||
const handleClickLike = () => {
|
||||
console.log('点击的Like---------');
|
||||
}
|
||||
|
||||
// 点击不喜欢
|
||||
const handleClickDisLike = () => {
|
||||
console.log('点击的DisLike---------');
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Tabs className={'user_code_tab_area'} activeKey={defaultActiveKey} onChange={handleTabChange}>
|
||||
{ tabs }
|
||||
</Tabs>
|
||||
<div className={'number_area'}>
|
||||
<div className="number_flex flex_count">
|
||||
<TextNumber text="通过次数" number={pass_count} position="vertical"/>
|
||||
<Divider type="vertical" style={{ height: '20px', margin: '10px 20px' }}/>
|
||||
<TextNumber text="提交次数" number={submit_count} position="vertical"/>
|
||||
</div>
|
||||
{/* <div className="number_flex flex_info">
|
||||
<TextNumber text="message" number={4235} type="icon" onIconClick={handleClickMessage}/>
|
||||
<TextNumber text="like" number={4235} type="icon" onIconClick={handleClickLike}/>
|
||||
<TextNumber text="dislike" type="icon" onIconClick={handleClickDisLike}/>
|
||||
</div> */}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const { hack, userCodeTab} = state.ojForUserReducer;
|
||||
return {
|
||||
hack,
|
||||
userCodeTab
|
||||
}
|
||||
}
|
||||
// changeUserCodeTab
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
changeUserCodeTab: (key) => dispatch(actions.changeUserCodeTab(key))
|
||||
});
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(LeftPane);
|
@ -0,0 +1,51 @@
|
||||
@import '../../split_pane_resizer.scss';
|
||||
|
||||
.user_code_tab_area{
|
||||
.ant-tabs-tabpane{
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
.number_area{
|
||||
display: flex;
|
||||
position: absolute;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
bottom: 0px;
|
||||
height: 56px;
|
||||
width: 100%;
|
||||
// background: pink;
|
||||
padding: 0 30px;
|
||||
// background-color: #fff;
|
||||
|
||||
.flex_count,
|
||||
.flex_info{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.flex_info{
|
||||
width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
.task_description_area{
|
||||
padding: 0 30px;
|
||||
height: calc(100vh - 166px);
|
||||
overflow-y: auto;
|
||||
.desc_area_header{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 64px;
|
||||
.header_flex{
|
||||
font-size: 14px;
|
||||
.flex_label{
|
||||
color: #999999;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.flex_value{
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* @Description:
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-27 09:49:30
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-27 19:36:41
|
||||
*/
|
||||
import '../index.scss';
|
||||
import React from 'react';
|
||||
import { Tag } from 'antd';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { connect } from 'react-redux';
|
||||
import QuillEditor from '../../../quillEditor';
|
||||
import CONST from '../../../../../constants';
|
||||
const {tagBackground, diffText} = CONST;
|
||||
|
||||
const TaskDescription = (props) => {
|
||||
|
||||
const { hack = {} } = props;
|
||||
const {language, difficult, time_limit, username, description} = hack;
|
||||
return (
|
||||
<div className={'task_description_area'}>
|
||||
<div className={'desc_area_header'}>
|
||||
<p className={'header_flex'}>
|
||||
<span className={'flex_label'}>编程语言:</span>
|
||||
<span className={'flex_value'}>{language}</span>
|
||||
</p>
|
||||
<p className={'header_flex'}>
|
||||
<span className={'flex_label'}>难度:</span>
|
||||
<Tag color={tagBackground[+difficult]}>{diffText[+difficult]}</Tag>
|
||||
</p>
|
||||
<p className={'header_flex'}>
|
||||
<span className={'flex_label'}>程序运行时间限制:</span>
|
||||
<span className={'flex_value'}>{time_limit}秒</span>
|
||||
</p>
|
||||
<p className={'header_flex'}>
|
||||
<span className={'flex_label'}>出题者:</span>
|
||||
<Link to="/" style={{ color: '#5091FF'}}>{username}</Link>
|
||||
</p>
|
||||
</div>
|
||||
<QuillEditor
|
||||
htmlCtx={description}
|
||||
readOnly={true}
|
||||
options={[]}
|
||||
style={{ height: "calc(100% - 109px)" }}
|
||||
/>
|
||||
{/* <div dangerouslySetInnerHTML={{__html: description}}></div> */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const { hack } = state.ojForUserReducer;
|
||||
return {
|
||||
hack
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps
|
||||
)(TaskDescription);
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* @Description:
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-27 14:59:51
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-28 17:17:25
|
||||
*/
|
||||
import React from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import MyMonacoEditor from '../../components/myMonacoEditor';
|
||||
import ControlSetting from '../../components/controlSetting';
|
||||
import actions from '../../../../redux/actions';
|
||||
|
||||
const RightPane = (props) => {
|
||||
|
||||
const {identifier, submitInput, submitUserCode} = props;
|
||||
const handleSubmitForm = () => {
|
||||
console.log('提交了表单内容');
|
||||
// 提交时, 先调用提交接口,提交成功后,循环调用测评接口
|
||||
submitUserCode(identifier, submitInput, 'submit');
|
||||
// // 提交时,先调用评测接口, 评测通过后才调用保存接口
|
||||
// updateCode(identifier, submitInput, 'submit');
|
||||
}
|
||||
return (
|
||||
<div className={'right_pane_code_wrap'}>
|
||||
<MyMonacoEditor language={props.language} code={props.code}/>
|
||||
<ControlSetting inputValue={props.input} onSubmitForm={handleSubmitForm}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
|
||||
const {user_program_identifier, hack, userTestInput} = state.ojForUserReducer;
|
||||
const { language, code } = hack;
|
||||
return {
|
||||
language,
|
||||
code,
|
||||
input: userTestInput,
|
||||
submitInput: hack.input,
|
||||
identifier: user_program_identifier
|
||||
};
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
// type: 提交类型 debug | submit
|
||||
// updateCode: (identifier, inputValue, type) => dispatch(actions.updateCode(identifier, inputValue, type))
|
||||
submitUserCode: (identifier, inputValue, type) => dispatch(actions.submitUserCode(identifier, inputValue, type))
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(RightPane);
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* @Description: action类型
|
||||
* @Author: tangjiang
|
||||
* @Date: 2019-11-13 20:05:39
|
||||
* @Last Modified by: tangjiang
|
||||
* @Last Modified time: 2019-11-14 09:29:45
|
||||
*/
|
||||
const types = {
|
||||
ADD_TODO: 'ADD_TODO',
|
||||
GET_OJ_LIST: 'GET_OJ_LIST', // JC 列表
|
||||
SAVE_OJ_FORM_ID: 'SAVE_OJ_FORM_ID', // 保存OJ form表单信息
|
||||
GET_OJ_BY_ID: 'GET_OJ_BY_ID', // 根据 id 号获取ojList中的数据
|
||||
SAVE_OJ_FORM_CODE: 'SAVE_OJ_FORM_CODE', // 代码
|
||||
VALIDATE_OJ_FORM: 'VALIDATE_OJ_FORM', // 验证表单
|
||||
VALIDATE_OJ_NAME: 'VALIDATE_OJ_NAME', // 任务名称
|
||||
VALIDATE_OJ_LANGUAGE: 'VALIDATE_OJ_LANGUAGE', // 编程语言
|
||||
VALIDATE_OJ_DESCRIPTION: 'VALIDATE_OJ_DESCRIPTION', // 描述
|
||||
VALIDATE_OJ_DIFFICULT: 'VALIDATE_OJ_DIFFICULT', // 难易度
|
||||
VALIDATE_OJ_TIMELIMIT: 'VALIDATE_OJ_TIMELIMIT', // 时间限制
|
||||
VALIDATE_OJ_CATEGORY: 'VALIDATE_OJ_CATEGORY', // 分类
|
||||
VALIDATE_OJ_OPENORNOT: 'VALIDATE_OJ_OPENORNOT', // 公开程序
|
||||
SAVE_OJ_FORM: 'SAVE_OJ_FORM', // 保存表单
|
||||
ADD_TEST_CASE: 'ADD_TEST_CASE', // 添加测试用例
|
||||
DELETE_TEST_CASE: 'DELETE_TEST_CASE', // 删除测试用例
|
||||
SAVE_TEST_CASE: '保存测试用例', // 保存测试用例
|
||||
CLEAR_JSFORM_STORE: 'CLEAR_JSFORM_STORE', // 清空测试用例
|
||||
SAVE_EDIT_OJ_FORM_AND_TEST_CASE: 'SAVE_EDIT_OJ_FORM_AND_TEST_CASE', // 保存根据id获取的表单及测试用例值
|
||||
TEST_CODE_STATUS: 'TEST_CODE_STATUS', // 代码调试状态
|
||||
VALIDATE_TEST_CODE_ARRS: 'VALIDATE_TEST_CODE_ARRS', // 更改测试用例验证结果
|
||||
TEST_CASE_INPUT_CHANGE: 'TEST_CASE_INPUT_CHANGE', // 测试用例输入值改变时
|
||||
TEST_CASE_OUTPUT_CHANGE: 'TEST_CASE_OUTPUT_CHANGE', // 测试用例输出值改变时
|
||||
DEBUGGER_CODE: 'DEBUGGER_CODE', // 调试代码
|
||||
SAVE_USER_PROGRAM_ID: 'SAVE_USER_PROGRAM_ID',// 保存用户编程题id值
|
||||
USER_PROGRAM_DETAIL: 'USER_PROGRAM_DETAIL', // 用户编程题详情
|
||||
SHOW_OR_HIDE_CONTROL: 'SHOW_OR_HIDE_CONTROL', // 显示或隐藏控制台
|
||||
LOADING_STATUS: 'LOADING_STATUS', // loading状态
|
||||
COMMIT_RECORD_DETAIL: 'COMMIT_RECORD_DETAIL', // 提交记录详情
|
||||
COMMIT_RECORD: 'COMMIT_RECORD', // 提交记录
|
||||
SAVE_USER_CODE: 'SAVE_USER_CODE', // 用户编辑的代码块
|
||||
IS_UPDATE_CODE: 'IS_UPDATE_CODE', // 是否更新代码块内容
|
||||
CHANGE_USER_CODE_TAB: 'CHANGE_USER_CODE_TAB', // 切换学员测评tab
|
||||
SUBMIT_LOADING_STATUS: 'SUBMIT_LOADING_STATUS', // 提交按钮状态值
|
||||
PUBLISH_LOADING_STATUS: 'PUBLISH_LOADING_STATUS', // 发布按钮
|
||||
IS_MY_SOURCE: 'IS_MY_SOURCE',
|
||||
CHANGE_PAGINATION_INFO: 'CHANGE_PAGINATION_INFO', // 改变分页数据
|
||||
}
|
||||
|
||||
export default types;
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* @Description: 控制全局
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-27 16:30:50
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-28 21:15:34
|
||||
*/
|
||||
import types from "./actionTypes";
|
||||
|
||||
// 切换控制台显示与隐藏
|
||||
export const showOrHideControl = (flag) => {
|
||||
return {
|
||||
type: types.SHOW_OR_HIDE_CONTROL,
|
||||
payload: flag
|
||||
}
|
||||
}
|
||||
|
||||
// 改变 loading 状态值
|
||||
export const changeLoadingState = (flag) => {
|
||||
return {
|
||||
type: types.LOADING_STATUS,
|
||||
payload: flag
|
||||
}
|
||||
}
|
||||
|
||||
// 改变提交按钮状态值
|
||||
export const changeSubmitLoadingStatus = (flag) => {
|
||||
return {
|
||||
type: types.SUBMIT_LOADING_STATUS,
|
||||
payload: flag
|
||||
}
|
||||
}
|
||||
|
||||
// 发布按钮状态
|
||||
export const changePublishLoadingStatus = (flag) => {
|
||||
return {
|
||||
type: types.PUBLISH_LOADING_STATUS,
|
||||
payload: flag
|
||||
}
|
||||
}
|
||||
|
||||
// 是否是我发布的
|
||||
export const isMyPublish = (flag) => {
|
||||
return {
|
||||
type: types.IS_MY_SOURCE,
|
||||
payload: flag
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* @Description: 全局导出 action 类型
|
||||
* @Author: tangjiang
|
||||
* @Date: 2019-11-13 20:12:23
|
||||
* @Last Modified by: tangjiang
|
||||
* @Last Modified time: 2019-11-14 09:55:47
|
||||
*/
|
||||
|
||||
import toggleTodo from './testAction.js';
|
||||
import {getOJList, changePaginationInfo} from './ojList';
|
||||
import {
|
||||
validateOjForm,
|
||||
saveOjFormCode,
|
||||
getOJFormById,
|
||||
saveOJFormId,
|
||||
clearOJFormStore,
|
||||
validateOJName,
|
||||
validateOjLanguage,
|
||||
validateOjDescription,
|
||||
validateOjDifficult,
|
||||
validateOjTimeLimit,
|
||||
validateOjCategory,
|
||||
validateOpenOrNot,
|
||||
addTestCase,
|
||||
deleteTestCase,
|
||||
testCaseInputChange,
|
||||
testCaseOutputChange,
|
||||
} from './ojForm';
|
||||
|
||||
import {
|
||||
startProgramQuestion,
|
||||
debuggerCode,
|
||||
getUserCommitRecord,
|
||||
getUserCommitRecordDetail,
|
||||
updateCode,
|
||||
saveUserInputCode,
|
||||
changeUserCodeTab,
|
||||
submitUserCode,
|
||||
// isUpdateCodeCtx
|
||||
} from './ojForUser';
|
||||
|
||||
import {
|
||||
showOrHideControl,
|
||||
changeLoadingState,
|
||||
changeSubmitLoadingStatus,
|
||||
changePublishLoadingStatus,
|
||||
isMyPublish,
|
||||
} from './common';
|
||||
|
||||
export default {
|
||||
toggleTodo,
|
||||
getOJList,
|
||||
changePaginationInfo,
|
||||
getOJFormById,
|
||||
saveOJFormId,
|
||||
clearOJFormStore,
|
||||
validateOjForm,
|
||||
saveOjFormCode,
|
||||
validateOJName,
|
||||
validateOjLanguage,
|
||||
validateOjDescription,
|
||||
validateOjDifficult,
|
||||
validateOjTimeLimit,
|
||||
validateOjCategory,
|
||||
validateOpenOrNot,
|
||||
addTestCase,
|
||||
deleteTestCase,
|
||||
testCaseInputChange,
|
||||
testCaseOutputChange,
|
||||
debuggerCode,
|
||||
startProgramQuestion,
|
||||
showOrHideControl,
|
||||
changeLoadingState,
|
||||
getUserCommitRecord,
|
||||
getUserCommitRecordDetail,
|
||||
updateCode,
|
||||
saveUserInputCode,
|
||||
changeUserCodeTab,
|
||||
changeSubmitLoadingStatus,
|
||||
submitUserCode,
|
||||
changePublishLoadingStatus,
|
||||
isMyPublish
|
||||
// isUpdateCodeCtx
|
||||
}
|
@ -0,0 +1,391 @@
|
||||
/*
|
||||
* @Description: 开发者社区编辑模块
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-20 16:35:46
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-28 21:21:56
|
||||
*/
|
||||
import types from './actionTypes';
|
||||
import CONST from '../../constants';
|
||||
import { fetchPostOjForm, fetchGetOjById, publishTask } from '../../services/ojService';
|
||||
import { Base64 } from 'js-base64';
|
||||
import { message, notification } from 'antd';
|
||||
const { jcLabel } = CONST;
|
||||
// 表单字段映射
|
||||
const maps = {
|
||||
name: {
|
||||
label: jcLabel['name'],
|
||||
type: types.VALIDATE_OJ_NAME
|
||||
},
|
||||
language: {
|
||||
label: jcLabel['language'],
|
||||
type: types.VALIDATE_OJ_LANGUAGE
|
||||
},
|
||||
description: {
|
||||
label: jcLabel['description'],
|
||||
type: types.VALIDATE_OJ_DESCRIPTION
|
||||
},
|
||||
difficult: {
|
||||
label: jcLabel['difficult'],
|
||||
type: types.VALIDATE_OJ_DIFFICULT
|
||||
},
|
||||
timeLimit: {
|
||||
label: jcLabel['timeLimit'],
|
||||
type: types.VALIDATE_OJ_TIMELIMIT
|
||||
},
|
||||
category: {
|
||||
label: jcLabel['category'],
|
||||
type: types.VALIDATE_OJ_CATEGORY
|
||||
},
|
||||
openOrNot: {
|
||||
label: jcLabel['openOrNot'],
|
||||
type: types.VALIDATE_OJ_OPENORNOT
|
||||
},
|
||||
input: {
|
||||
label: '输入'
|
||||
},
|
||||
output: {
|
||||
label: '输出'
|
||||
}
|
||||
};
|
||||
|
||||
// 非空校验
|
||||
const emptyValidate = (key, value) => {
|
||||
if ([undefined, '', null].includes(value)) {
|
||||
return {
|
||||
[key]: {
|
||||
validateStatus: 'error',
|
||||
errMsg: `${maps[key].label}不能为空`
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
[key]: {
|
||||
validateStatus: '',
|
||||
errMsg: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 组装字段值及校验信息
|
||||
const payloadInfo = (key, value, errMsg, validateInfo) => ({
|
||||
ojForm: {
|
||||
[key]: errMsg ? '' : value
|
||||
},
|
||||
ojFormValidate: {
|
||||
[key]: validateInfo
|
||||
}
|
||||
});
|
||||
|
||||
// 表单提交验证
|
||||
export const validateOjForm = (props, type) => {
|
||||
return (dispatch, getState) => {
|
||||
const {ojForm, testCases, identifier, code } = getState().ojFormReducer;
|
||||
let keys = Object.keys(ojForm);
|
||||
// 循环判断每个字段是否为空
|
||||
let hasSuccess = true;
|
||||
keys.forEach(key => {
|
||||
const value = ojForm[key];
|
||||
const validateResult = emptyValidate(key, value);
|
||||
const errMsg = validateResult[key].errMsg;
|
||||
if (errMsg) {
|
||||
hasSuccess = false;
|
||||
dispatch(
|
||||
{
|
||||
type: maps[key].type,
|
||||
payload: payloadInfo(key, value, errMsg, validateResult[key])
|
||||
}
|
||||
)
|
||||
}
|
||||
});
|
||||
// 验证测试用例中的数组是否都有对应的值
|
||||
const tcValidResult = [];
|
||||
testCases.forEach(obj => {
|
||||
// const tcKeys = Object.keys(obj); // 获取 obj 属性: input 与 output
|
||||
let tempObj = {};
|
||||
['input', 'output'].forEach(key => {
|
||||
const value = obj[key];
|
||||
const validateResult = emptyValidate(key, value);
|
||||
const errMsg = validateResult[key].errMsg;
|
||||
// const errMsg = validateResult[key].errMsg;
|
||||
if (errMsg) {
|
||||
hasSuccess = false;
|
||||
}
|
||||
Object.assign(tempObj, validateResult);
|
||||
});
|
||||
tcValidResult.push(tempObj);
|
||||
});
|
||||
|
||||
if (testCases.length === 0) {
|
||||
notification['error']({
|
||||
message: '必填',
|
||||
description: '测试用例必须输入!'
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!code) {
|
||||
notification['error']({
|
||||
message: '必填',
|
||||
description: '代码块内容必须输入!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 更改测试用例验证结果
|
||||
dispatch({
|
||||
type: types.VALIDATE_TEST_CODE_ARRS,
|
||||
payload: tcValidResult
|
||||
});
|
||||
// 验证成功后,调用提交接口
|
||||
if (hasSuccess) {
|
||||
// console.log('表单保存的数据为: ', getState());
|
||||
const {ojFormReducer} = getState();
|
||||
const {code, score, ojForm, testCases = []} = ojFormReducer;
|
||||
const {category, description, difficult, language, name, openOrNot, timeLimit} = ojForm;
|
||||
let paramsObj = {};
|
||||
const hack = { // 编程题干
|
||||
name,
|
||||
description,
|
||||
difficult,
|
||||
category,
|
||||
'open_or_not': openOrNot,
|
||||
'time_limit': timeLimit,
|
||||
score
|
||||
};
|
||||
|
||||
const hack_codes = { // 代码区域参数
|
||||
code: Base64.encode(code),
|
||||
language
|
||||
};
|
||||
|
||||
// const tempTc = testCases.map(tc => {
|
||||
// delete tc.isAdd
|
||||
// return tc;
|
||||
// });
|
||||
// console.log(tempTc);
|
||||
if (!identifier) { // 新增
|
||||
const tempTc = testCases.map(tc => {
|
||||
delete tc.isAdd
|
||||
return tc;
|
||||
});
|
||||
paramsObj['params'] = {
|
||||
hack,
|
||||
hack_sets: tempTc,
|
||||
hack_codes
|
||||
}
|
||||
paramsObj['submitType'] = 'add';
|
||||
} else { // 存在时调用更新接口
|
||||
const update_hack_sets = []; // 编辑的测试集
|
||||
const hack_sets = []; // 新增的测试集
|
||||
testCases.forEach(tc => {
|
||||
if (tc.isAdd) { // 新增
|
||||
delete tc.isAdd;
|
||||
hack_sets.push(tc);
|
||||
} else {
|
||||
delete tc.isAdd;
|
||||
update_hack_sets.push(tc);
|
||||
}
|
||||
});
|
||||
paramsObj['params'] = {
|
||||
hack,
|
||||
hack_sets,
|
||||
hack_codes,
|
||||
update_hack_sets
|
||||
}
|
||||
paramsObj['submitType'] = 'update';
|
||||
paramsObj['identifier'] = identifier;
|
||||
}
|
||||
|
||||
function linkToDev () {
|
||||
dispatch({
|
||||
type: types.IS_MY_SOURCE,
|
||||
payload: true
|
||||
});
|
||||
setTimeout(() => {
|
||||
props.history.push('/developer');
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
fetchPostOjForm(paramsObj).then(res => {
|
||||
// TODO
|
||||
if (res.status === 200) { // 保存成功后,重新跳转至列表页
|
||||
const {identifier} = res.data
|
||||
if (type === 'publish') { // 存在发布时,直接调用发布接口
|
||||
identifier && publishTask(identifier).then(res => {
|
||||
if (res.status === 200) {
|
||||
message.success('发布成功!');
|
||||
linkToDev();
|
||||
}
|
||||
dispatch({
|
||||
type: types.PUBLISH_LOADING_STATUS,
|
||||
payload: false
|
||||
});
|
||||
}).catch(() => {
|
||||
dispatch({
|
||||
type: types.PUBLISH_LOADING_STATUS,
|
||||
payload: false
|
||||
});
|
||||
});
|
||||
} else {
|
||||
message.success('保存成功!');
|
||||
linkToDev();
|
||||
dispatch({
|
||||
type: types.SUBMIT_LOADING_STATUS,
|
||||
payload: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}).catch(err => {
|
||||
dispatch({
|
||||
type: types.SUBMIT_LOADING_STATUS,
|
||||
payload: false
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
// 保存提交的代码
|
||||
export const saveOjFormCode = (value) => {
|
||||
return {
|
||||
type: types.SAVE_OJ_FORM_CODE,
|
||||
payload: value
|
||||
};
|
||||
}
|
||||
// 验证任务名称
|
||||
export const validateOJName = (value) => {
|
||||
const validate = emptyValidate('name', value)['name'];
|
||||
const errMsg = validate.errMsg;
|
||||
return {
|
||||
type: types.VALIDATE_OJ_NAME,
|
||||
payload: payloadInfo('name', value, errMsg, validate)
|
||||
}
|
||||
};
|
||||
// 验证编程语言
|
||||
export const validateOjLanguage = (value) => {
|
||||
const validate = emptyValidate('language', value)['language'];
|
||||
const errMsg = validate.errMsg;
|
||||
return {
|
||||
type: types.VALIDATE_OJ_LANGUAGE,
|
||||
payload: payloadInfo('language', value, errMsg, validate)
|
||||
}
|
||||
};
|
||||
// 验证描述
|
||||
export const validateOjDescription = (value) => {
|
||||
// createAction('description', value, types.VALIDATE_OJ_DESCRIPTION);
|
||||
const validate = emptyValidate('description', value)['description'];
|
||||
const errMsg = validate.errMsg;
|
||||
return {
|
||||
type: types.VALIDATE_OJ_DESCRIPTION,
|
||||
payload: payloadInfo('description', value, errMsg, validate)
|
||||
}
|
||||
};
|
||||
// 验证难易度
|
||||
export const validateOjDifficult = (value) => {
|
||||
// createAction('difficult', value, types.VALIDATE_OJ_DIFFICULT);
|
||||
const validate = emptyValidate('difficult', value)['difficult'];
|
||||
const errMsg = validate.errMsg;
|
||||
return {
|
||||
type: types.VALIDATE_OJ_DIFFICULT,
|
||||
payload: payloadInfo('difficult', value, errMsg, validate)
|
||||
}
|
||||
};
|
||||
// 验证时间限制
|
||||
export const validateOjTimeLimit = (value) => {
|
||||
// createAction('timeLimit', value, types.VALIDATE_OJ_TIMELIMIT);
|
||||
const validate = emptyValidate('timeLimit', value)['timeLimit'];
|
||||
const errMsg = validate.errMsg;
|
||||
return {
|
||||
type: types.VALIDATE_OJ_TIMELIMIT,
|
||||
payload: payloadInfo('timeLimit', value, errMsg, validate)
|
||||
}
|
||||
};
|
||||
// 验证分类
|
||||
export const validateOjCategory = (value) => {
|
||||
// createAction('category', value, types.VALIDATE_OJ_CATEGORY);
|
||||
const validate = emptyValidate('category', value)['category'];
|
||||
const errMsg = validate.errMsg;
|
||||
return {
|
||||
type: types.VALIDATE_OJ_CATEGORY,
|
||||
payload: payloadInfo('category', value, errMsg, validate)
|
||||
}
|
||||
};
|
||||
// 验证公开程序
|
||||
export const validateOpenOrNot = (value) => {
|
||||
const validate = emptyValidate('openOrNot', value)['openOrNot'];
|
||||
const errMsg = validate.errMsg;
|
||||
return {
|
||||
type: types.VALIDATE_OJ_OPENORNOT,
|
||||
payload: payloadInfo('openOrNot', value, errMsg, validate)
|
||||
}
|
||||
};
|
||||
// 新增测试用例
|
||||
export const addTestCase = (obj) => {
|
||||
return {
|
||||
type: types.ADD_TEST_CASE,
|
||||
payload: obj
|
||||
}
|
||||
}
|
||||
// 删除测试用例
|
||||
export const deleteTestCase = (obj) => {
|
||||
return {
|
||||
type: types.DELETE_TEST_CASE,
|
||||
payload: obj
|
||||
}
|
||||
}
|
||||
// 根据id号编辑OJ
|
||||
export const getOJFormById = (id) => {
|
||||
return (dispatch) => {
|
||||
fetchGetOjById(id).then(res => {
|
||||
console.log('获取OJ表单信息成功: ', res);
|
||||
dispatch({
|
||||
type: types.SAVE_EDIT_OJ_FORM_AND_TEST_CASE,
|
||||
payload: res.data
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
// 保存表单 id 信息
|
||||
export const saveOJFormId = (id) => {
|
||||
return {
|
||||
type: types.SAVE_OJ_FORM_ID,
|
||||
payload: id
|
||||
}
|
||||
}
|
||||
// 清空测试用例集合
|
||||
export const clearOJFormStore = () => {
|
||||
return {
|
||||
type: types.CLEAR_JSFORM_STORE
|
||||
}
|
||||
}
|
||||
|
||||
// 测试用例输入值改变时
|
||||
export const testCaseInputChange = (value, index) => {
|
||||
const validate = emptyValidate('input', value)['input'];
|
||||
return {
|
||||
type: types.TEST_CASE_INPUT_CHANGE,
|
||||
payload: {
|
||||
input: validate,
|
||||
value,
|
||||
index
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 测试用例输出值改变时
|
||||
export const testCaseOutputChange = (value, index) => {
|
||||
const validate = emptyValidate('output', value)['output'];
|
||||
return {
|
||||
type: types.TEST_CASE_OUTPUT_CHANGE,
|
||||
payload: {
|
||||
output: validate,
|
||||
value,
|
||||
index
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// // 调试代码时,更改对应的状态值
|
||||
// export const changeTestCodeStatus = () => {
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* @Description: 开发者社区action
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-20 10:48:24
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-29 11:09:54
|
||||
*/
|
||||
import types from './actionTypes';
|
||||
import { fetchOJList } from '../../services/ojService';
|
||||
|
||||
export const getOJList = (params) => {
|
||||
return (dispatch) => {
|
||||
fetchOJList(params).then((res) => {
|
||||
const { data } = res;
|
||||
dispatch({
|
||||
type: types.GET_OJ_LIST,
|
||||
payload: data
|
||||
});
|
||||
// 改变总页娄
|
||||
dispatch({
|
||||
type: types.CHANGE_PAGINATION_INFO,
|
||||
payload: {
|
||||
total: data.hacks_count
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 改变分页数据
|
||||
export const changePaginationInfo = (obj) => {
|
||||
return {
|
||||
type: types.CHANGE_PAGINATION_INFO,
|
||||
payload: obj
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
import types from './actionTypes';
|
||||
|
||||
export default function toggleTodo() {
|
||||
return {
|
||||
type: types.ADD_TODO
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* @Description: 全局控制 reducer
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-27 16:27:09
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-28 21:14:36
|
||||
*/
|
||||
import types from "../actions/actionTypes";
|
||||
|
||||
const initialState = {
|
||||
showOrHideControl: false,
|
||||
loading: false,
|
||||
excuteState: '', // 代码执行状态
|
||||
submitLoading: false, // 提交按钮状态
|
||||
publishLoading: false, // 发布
|
||||
isMySource: false
|
||||
}
|
||||
|
||||
const commonReducer = (state = initialState, action) => {
|
||||
|
||||
switch (action.type) {
|
||||
case types.SHOW_OR_HIDE_CONTROL:
|
||||
return {
|
||||
...state,
|
||||
showOrHideControl: action.payload
|
||||
}
|
||||
case types.LOADING_STATUS:
|
||||
return {
|
||||
...state,
|
||||
loading: action.payload
|
||||
}
|
||||
case types.TEST_CODE_STATUS: // 改变代码调试状态
|
||||
return {
|
||||
...state,
|
||||
excuteState: action.payload
|
||||
}
|
||||
case types.SUBMIT_LOADING_STATUS:
|
||||
return {
|
||||
...state,
|
||||
submitLoading: action.payload
|
||||
}
|
||||
case types.PUBLISH_LOADING_STATUS:
|
||||
return {
|
||||
...state,
|
||||
publishLoading: action.payload
|
||||
}
|
||||
case types.IS_MY_SOURCE:
|
||||
return {
|
||||
...state,
|
||||
isMySource: action.payload
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export default commonReducer;
|
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* @Description: 全局导出 reducers
|
||||
* @Author: tangjiang
|
||||
* @Date: 2019-11-13 20:12:54
|
||||
* @Last Modified by: tangjiang
|
||||
* @Last Modified time: 2019-11-14 09:55:10
|
||||
*/
|
||||
|
||||
import { combineReducers } from 'redux';
|
||||
import testReducer from './testReducer';
|
||||
import ojFormReducer from './ojFormReducer';
|
||||
import ojListReducer from './ojListReducer';
|
||||
import ojForUserReducer from './ojForUserReducer';
|
||||
import commonReducer from './commonReducer';
|
||||
export default combineReducers({
|
||||
testReducer,
|
||||
ojFormReducer,
|
||||
ojListReducer,
|
||||
ojForUserReducer,
|
||||
commonReducer
|
||||
});
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* @Description: 用户编程信息
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-27 13:41:48
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-28 17:34:13
|
||||
*/
|
||||
import types from "../actions/actionTypes";
|
||||
import { Base64 } from 'js-base64';
|
||||
|
||||
const initialState = {
|
||||
user_program_identifier: '', // 开启OJ题的唯一标题
|
||||
hack: {}, // 编程题主要内容
|
||||
test_case: {}, // 测试用例
|
||||
commitRecordDetail: {}, // 提交记录详情
|
||||
commitRecord: [], // 提交记录
|
||||
userCode: '', // 保存当前用户输入的代码
|
||||
isUpdateCode: false, // 是否更新了代码内容
|
||||
userCodeTab: 'task', // 学员测评tab位置: task | record | comment
|
||||
userTestInput: '', // 用户自定义输入值
|
||||
};
|
||||
|
||||
const ojForUserReducer = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case types.SAVE_USER_PROGRAM_ID:
|
||||
return {
|
||||
...state,
|
||||
user_program_identifier: action.payload
|
||||
}
|
||||
case types.USER_PROGRAM_DETAIL:
|
||||
const { hack, test_case } = action.payload;
|
||||
const { code }= hack;
|
||||
let tempCode = Base64.decode(code)
|
||||
Object.assign(hack, {code: tempCode});
|
||||
return {
|
||||
...state,
|
||||
hack: Object.assign({}, hack),
|
||||
test_case: Object.assign({}, test_case)
|
||||
}
|
||||
case types.COMMIT_RECORD_DETAIL:
|
||||
let result = action.payload;
|
||||
result['output'] = Base64.decode(result['output']);
|
||||
result['error_msg'] = Base64.decode(result['error_msg']);
|
||||
return {
|
||||
...state,
|
||||
commitRecordDetail: Object.assign({}, result)
|
||||
}
|
||||
case types.COMMIT_RECORD:
|
||||
return {
|
||||
...state,
|
||||
commitRecord: [...action.payload]
|
||||
}
|
||||
case types.SAVE_USER_CODE:
|
||||
let curCode = Base64.encode(action.payload);
|
||||
|
||||
return {
|
||||
...state,
|
||||
userCode: curCode,
|
||||
isUpdateCode: true
|
||||
}
|
||||
case types.IS_UPDATE_CODE:
|
||||
return {
|
||||
...state,
|
||||
isUpdateCode: action.payload
|
||||
}
|
||||
case types.CHANGE_USER_CODE_TAB:
|
||||
return {
|
||||
...state,
|
||||
userCodeTab: action.payload
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export default ojForUserReducer;
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* @Description:
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-21 22:17:03
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-29 09:31:04
|
||||
*/
|
||||
import types from '../actions/actionTypes';
|
||||
|
||||
const initialState = {
|
||||
hacks_list: [],
|
||||
top_data: {},
|
||||
hacks_count: 0, // 总条数
|
||||
pagination: {
|
||||
current: 1, // 当前页
|
||||
pageSize: 10, // 每页条数
|
||||
total: 1, // 总数
|
||||
showQuickJumper: true // 快速跳转
|
||||
}
|
||||
};
|
||||
|
||||
const ojListReducer = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case types.GET_OJ_LIST:
|
||||
return {
|
||||
...state,
|
||||
...action.payload
|
||||
}
|
||||
case types.CHANGE_PAGINATION_INFO:
|
||||
return {
|
||||
...state,
|
||||
pagination: Object.assign({}, state.pagination, action.payload)
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export default ojListReducer;
|
@ -0,0 +1,30 @@
|
||||
import types from '../actions/actionTypes';
|
||||
|
||||
const initialState = {
|
||||
count: 0
|
||||
};
|
||||
|
||||
// export default function (state = initialState, action) {
|
||||
// switch (action.type) {
|
||||
// case types.ADD_TODO:
|
||||
// return {
|
||||
// ...state,
|
||||
// count: state.count + 1
|
||||
// };
|
||||
// default:
|
||||
// return state;
|
||||
// }
|
||||
// }
|
||||
const testReducer = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case types.ADD_TODO:
|
||||
return {
|
||||
...state,
|
||||
count: state.count + 1
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export default testReducer;
|
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* @Description: 指定容器并绑定 reducers
|
||||
* @Author: tangjiang
|
||||
* @Date: 2019-11-13 20:13:21
|
||||
* @Last Modified by: tangjiang
|
||||
* @Last Modified time: 2019-11-14 19:20:44
|
||||
*/
|
||||
|
||||
import { createStore, applyMiddleware } from 'redux';
|
||||
import thunk from 'redux-thunk';
|
||||
import rootReducer from '../reducers';
|
||||
|
||||
const configureStore = () => createStore(
|
||||
rootReducer,
|
||||
applyMiddleware(thunk)
|
||||
);
|
||||
|
||||
export default configureStore;
|
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* @Description: 开发者社区接口
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-20 10:55:38
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-28 18:58:20
|
||||
*/
|
||||
|
||||
import axios from 'axios';
|
||||
|
||||
export async function fetchOJList (params) {
|
||||
console.log('传递的参数: ', params);
|
||||
const obj = {};
|
||||
Object.keys(params).forEach(key => {
|
||||
if (params[key]) {
|
||||
obj[key] = params[key];
|
||||
}
|
||||
});
|
||||
return axios.get('/problems.json', { params: obj });
|
||||
}
|
||||
|
||||
// 提交
|
||||
export async function fetchPostOjForm (paramsObj) {
|
||||
const { params, submitType, identifier } = paramsObj;
|
||||
const url = submitType === 'add' ? `/problems.json` : `/problems/${identifier}.json`;
|
||||
// return axios.post(url, params);
|
||||
// if (identifier) {
|
||||
// return axios.post(url, params);
|
||||
// } else {
|
||||
// return
|
||||
// }
|
||||
return identifier ? axios.put(url, params) : axios.post(url, params);
|
||||
}
|
||||
|
||||
// 根据id号获取OJ信息
|
||||
export async function fetchGetOjById (id) {
|
||||
const url = `/problems/${id}/edit.json`;
|
||||
return axios.get(url);
|
||||
}
|
||||
|
||||
// 调试代码
|
||||
export async function fetchDebuggerCode (identifier, params) {
|
||||
const url = `/myproblems/${identifier}/code_debug.json`;
|
||||
return axios.get(url, {params});
|
||||
}
|
||||
|
||||
// 调试代码成功后,循环调用提交接口
|
||||
export async function fetchCodeSubmit (identifier, params) {
|
||||
const url = `/myproblems/${identifier}/result.json`;
|
||||
return axios.get(url, {params});
|
||||
}
|
||||
|
||||
// 开启编程题接口
|
||||
export async function fetchStartProgram (identifier) {
|
||||
const url = `/problems/${identifier}/start.json`;
|
||||
return axios.get(url);
|
||||
}
|
||||
|
||||
// 用户编程题详情
|
||||
export async function fetchUserProgramDetail (identifier) {
|
||||
const url = `/myproblems/${identifier}.json`;
|
||||
return axios.get(url);
|
||||
}
|
||||
|
||||
// 获取提交记录
|
||||
export async function fetchUserCommitRecord (identifier) {
|
||||
const url = `/myproblems/${identifier}/submit_records.json`;
|
||||
return axios.get(url);
|
||||
}
|
||||
|
||||
// 获取提交记录详情
|
||||
export async function fetchUserCommitRecordDetail () {
|
||||
const url = `/myproblems/record_detail.json`;
|
||||
return axios.get(url);
|
||||
}
|
||||
|
||||
// 恢复初始代码
|
||||
export async function restoreInitialCode (identifier) {
|
||||
const url = `/myproblems/${identifier}/restore_initial_code.json`;
|
||||
return axios.get(url);
|
||||
}
|
||||
|
||||
// 发布任务
|
||||
export async function publishTask (identifier) {
|
||||
const url = `/problems/${identifier}/publish.json`;
|
||||
return axios.post(url);
|
||||
}
|
||||
|
||||
// 更新用户编辑代码
|
||||
export async function fetchUpdateCode (identifier, params) {
|
||||
const url = `/myproblems/${identifier}/update_code.json`;
|
||||
return axios.post(url, params);
|
||||
}
|
||||
|
||||
// 用户提交代码
|
||||
export async function fetchUserCodeSubmit (identifier) {
|
||||
const url = `/myproblems/${identifier}/code_submit.json`;
|
||||
return axios.get(url);
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import Comments from '../comment/Comments'
|
||||
|
||||
import { commentHOC } from '../comment/CommentsHOC'
|
||||
import { CircularProgress } from 'material-ui/Progress';
|
||||
|
||||
import './TPMShixunDiscuss.css'
|
||||
|
||||
import TPMRightSection from './component/TPMRightSection'
|
||||
import TPMNav from './component/TPMNav'
|
||||
|
||||
class TPMShixunDiscuss extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps, newContext) {
|
||||
if (newProps.shixun && newProps.shixun.id && (!this.props || !this.props.shixun || this.props.shixun.id != newProps.shixun.id) ) {
|
||||
window.document.title = newProps.shixun.name
|
||||
// this.props.fetchCommentIfNotFetched &&
|
||||
// this.props.fetchCommentIfNotFetched();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// TODO 加了HOC后 mount了两次
|
||||
this.props.fetchCommentIfNotFetched &&
|
||||
this.props.fetchCommentIfNotFetched();
|
||||
}
|
||||
//
|
||||
|
||||
onPaginationChange = (page) => {
|
||||
window.$("html,body").animate({"scrollTop":160})
|
||||
this.props.onPaginationChange(page)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { loadingComments, creator, shixun, myshixun, recommend_shixuns, current_user, watched,
|
||||
aboutFocus, user, match
|
||||
} = this.props;
|
||||
|
||||
let _user = user;
|
||||
if (user) {
|
||||
_user = Object.assign({}, user);
|
||||
_user.user_url = `/users/${user.login}`
|
||||
}
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="tpmComment educontent clearfix mt30 mb80">
|
||||
|
||||
<div className="with65 fl edu-back-white commentsDelegateParent" >
|
||||
<TPMNav
|
||||
match={match}
|
||||
user={user}
|
||||
shixun={shixun}
|
||||
{...this.props}
|
||||
></TPMNav>
|
||||
{ loadingComments ?
|
||||
<CircularProgress size={40} thickness={3} style={{ marginLeft: 'auto', marginRight: 'auto', marginTop: '200px', display: 'block' }}/> :
|
||||
<Comments
|
||||
{...this.props}
|
||||
user={_user}
|
||||
onPaginationChange={this.onPaginationChange}
|
||||
></Comments>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div className="with35 fr pl20">
|
||||
<TPMRightSection {...this.props}></TPMRightSection>
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default commentHOC( TPMShixunDiscuss );
|
@ -0,0 +1,260 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
import { List,Typography,Tag,Modal,Radio} from 'antd';
|
||||
|
||||
import TPMRightSection from './component/TPMRightSection';
|
||||
import TPMNav from './component/TPMNav';
|
||||
import axios from 'axios';
|
||||
|
||||
class Audit_situationComponent extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
datas:undefined,
|
||||
value:undefined,
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getdatas()
|
||||
|
||||
}
|
||||
|
||||
|
||||
getdatas=()=>{
|
||||
|
||||
let url=`/shixuns/${this.props.match.params.shixunId}/review_newest_record.json`;
|
||||
axios.get(url).then((response) => {
|
||||
|
||||
if(response.data===undefined||JSON.stringify(response.data) == "{}"||response.data===null){
|
||||
this.setState({
|
||||
datas:[
|
||||
{
|
||||
name: '内容审核情况',
|
||||
id:"Content",
|
||||
},
|
||||
{
|
||||
name: '性能审核情况',
|
||||
id:"Performance",
|
||||
},
|
||||
]
|
||||
})
|
||||
}else{
|
||||
let newlist=[]
|
||||
if(response.data.content_info!=undefined&&response.data.perference_info===undefined){
|
||||
let arr=[
|
||||
{
|
||||
name: '内容审核情况',
|
||||
id:"Content",
|
||||
status:response.data.content_info.status,
|
||||
username:response.data.content_info.username,
|
||||
time:response.data.content_info.time,
|
||||
},
|
||||
{
|
||||
name: '性能审核情况',
|
||||
id:"Performance",
|
||||
},
|
||||
]
|
||||
newlist=arr
|
||||
}
|
||||
|
||||
if(response.data.content_info===undefined&&response.data.perference_info!=undefined){
|
||||
let arr=[
|
||||
{
|
||||
name: '内容审核情况',
|
||||
id:"Content",
|
||||
},
|
||||
{
|
||||
name: '性能审核情况',
|
||||
id:"Performance",
|
||||
status:response.data.perference_info.status,
|
||||
username:response.data.perference_info.username,
|
||||
time:response.data.perference_info.time,
|
||||
},
|
||||
]
|
||||
newlist=arr
|
||||
}
|
||||
|
||||
if(response.data.content_info!=undefined&&response.data.perference_info!=undefined){
|
||||
let arr=[
|
||||
{
|
||||
name: '内容审核情况',
|
||||
id:"Content",
|
||||
status:response.data.content_info.status,
|
||||
username:response.data.content_info.username,
|
||||
time:response.data.content_info.time,
|
||||
},
|
||||
{
|
||||
name: '性能审核情况',
|
||||
id:"Performance",
|
||||
status:response.data.perference_info.status,
|
||||
username:response.data.perference_info.username,
|
||||
time:response.data.perference_info.time,
|
||||
},
|
||||
]
|
||||
newlist=arr
|
||||
}
|
||||
|
||||
this.setState({
|
||||
datas:newlist
|
||||
})
|
||||
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
});
|
||||
}
|
||||
|
||||
showModal = (id,status) => {
|
||||
debugger
|
||||
this.setState({
|
||||
visible: true,
|
||||
editid:id,
|
||||
value:status
|
||||
});
|
||||
};
|
||||
|
||||
handleOk=(id,editid)=>{
|
||||
let url = `/shixuns/${this.props.match.params.shixunId}/review_shixun.json`;
|
||||
axios.post(url, {
|
||||
status: id===undefined?1:id,
|
||||
review_type: editid,
|
||||
}).then((response) => {
|
||||
if(response.data.status===0){
|
||||
this.props.showNotification(response.data.message);
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
this.getdatas()
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
});
|
||||
};
|
||||
|
||||
handleCancel = e => {
|
||||
this.setState({
|
||||
visible: false,
|
||||
});
|
||||
};
|
||||
|
||||
onChange = e => {
|
||||
this.setState({
|
||||
value: e.target.value,
|
||||
});
|
||||
};
|
||||
render() {
|
||||
const { tpmLoading, shixun, user, match } = this.props;
|
||||
let {value,editid,datas}=this.state;
|
||||
|
||||
console.log(this.props)
|
||||
return (
|
||||
<React.Fragment>
|
||||
|
||||
{this.state.visible===true?<Modal
|
||||
title="审核情况更改"
|
||||
visible={this.state.visible}
|
||||
keyboard={false}
|
||||
closable={false}
|
||||
footer={null}
|
||||
destroyOnClose={true}
|
||||
centered={true}
|
||||
>
|
||||
<div>
|
||||
<style>
|
||||
{
|
||||
`
|
||||
body{
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.ant-modal-body{
|
||||
text-align: center;
|
||||
}
|
||||
`
|
||||
}
|
||||
</style>
|
||||
|
||||
<Radio.Group onChange={this.onChange} value={this.state.value===undefined?1:this.state.value}>
|
||||
<Radio value={1}>已完成</Radio>
|
||||
<Radio value={0}>未完成</Radio>
|
||||
</Radio.Group>
|
||||
|
||||
<div className={"mt30"}>
|
||||
<a className="pop_close task-btn mr20 margin-tp26" onClick={()=>this.handleCancel()}>取消</a>
|
||||
<a className="task-btn task-btn-orange margin-tp26" onClick={()=>this.handleOk(value,editid)}>确定</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</Modal>:""}
|
||||
|
||||
<style>
|
||||
{
|
||||
`
|
||||
.Itemtitle{
|
||||
float: left;
|
||||
padding-top: 2px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
`
|
||||
}
|
||||
</style>
|
||||
|
||||
{ tpmLoading ? <div style={{ minHeight: '886px'}}></div> :
|
||||
|
||||
<div className="tpmComment educontent clearfix mt30 mb80">
|
||||
|
||||
<div className="with65 fl edu-back-white commentsDelegateParent" >
|
||||
|
||||
<TPMNav
|
||||
match={match}
|
||||
user={user}
|
||||
shixun={shixun}
|
||||
{...this.props}
|
||||
></TPMNav>
|
||||
|
||||
<div className="padding20 edu-back-white mt20" style={{minHeight: '640px'}}>
|
||||
{this.props.identity >2||this.props.identity===undefined?"":<List
|
||||
dataSource={datas}
|
||||
bordered
|
||||
renderItem={(item,key) => (
|
||||
<List.Item
|
||||
key={item.id}
|
||||
actions={[
|
||||
<a onClick={()=>this.showModal(item.id,item.status)} key={key}>
|
||||
<i className="iconfont icon-bianjidaibeijing font-22 color-green"></i>
|
||||
</a>,
|
||||
]}
|
||||
>
|
||||
<List.Item.Meta
|
||||
title={<div className={"font-16"}>
|
||||
<div className={"Itemtitle"}>{item.name}</div>
|
||||
{item.status===undefined?"":item.status===1?<Tag color="#FF6800">已完成</Tag>:<Tag color="#bcbcbc">未完成</Tag>}
|
||||
</div>}
|
||||
description={
|
||||
<div>
|
||||
{item.time===undefined?"":<span>审核时间: {item.time}</span>}
|
||||
{item.username===undefined?"":<span className={"ml30"}>审核人: {item.username}</span>}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
/>}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div className="with35 fr pl20">
|
||||
<TPMRightSection {...this.props}></TPMRightSection>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Audit_situationComponent;
|
@ -0,0 +1,67 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { getImageUrl, toPath } from 'educoder'
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
class NewFooter extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps, newContext) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="newFooter edu-txt-center ">
|
||||
{/*newContainers*/}
|
||||
<div className="inner-footer_con">
|
||||
{this.props.user&&this.props.user.main_site===true?<div className="footercon">
|
||||
{/* <div className="inline mt40 mb5">
|
||||
<a href="/" className="fl" style={{height:'70px'}}>
|
||||
<img alt="高校智能化教学与实训平台" src={getImageUrl(`images/educoder/headNavLogo.png?1526520218`)} width="70px">
|
||||
</img>
|
||||
</a>
|
||||
<span className="fl color-grey-c cdefault font-28 ml22" style={{lineHeight:'74px'}}>EduCoder.net</span>
|
||||
</div> */}
|
||||
<ul className="clearfix inner-footernav">
|
||||
<li><a href="/" className="fl" target="_blank">网站首页</a></li>
|
||||
<li><Link to="/help/about_us" className="fl" target="_blank">关于我们</Link></li>
|
||||
<li><Link to="/help/contact_us" className="fl" target="_blank">联系我们</Link></li>
|
||||
<li><Link to="/help/cooperatives" className="fl" target="_blank">合作伙伴</Link></li>
|
||||
<li><Link to="/help/agreement" className="fl" target="_blank">服务协议</Link></li>
|
||||
<li><Link to="/help/help_center" className="fl" target="_blank">帮助中心</Link></li>
|
||||
<li><Link to="/help/feedback" className="fl" target="_blank">意见反馈</Link></li>
|
||||
</ul>
|
||||
</div>:""}
|
||||
<div>
|
||||
{this.props.mygetHelmetapi === null ? "" :
|
||||
this.props.mygetHelmetapi===undefined|| this.props.mygetHelmetapi.footer===null||this.props.mygetHelmetapi.footer===undefined?
|
||||
<p className="footer_con-p inline lineh-30 font-14">
|
||||
<span className="font-18 fl">©</span> 2019 EduCoder
|
||||
<a style={{"color":"#888"}} target="_blank" href="http://beian.miit.gov.cn/" className="ml15 mr15">湘ICP备17009477号</a>
|
||||
<a style={{"color":"#888"}} target="_blank" href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=43019002000962" className="mr15">
|
||||
<img className="vertical4" src={require('./beian.png')}/>湘公网安备43019002000962号
|
||||
</a>
|
||||
<a href="https://team.trustie.net" style={{"color":"#888"}}
|
||||
target="_blank">Trustie</a> & IntelliDE inside. <span
|
||||
className="mr15">版权所有 湖南智擎科技有限公司</span>
|
||||
</p>
|
||||
:
|
||||
<div dangerouslySetInnerHTML={{__html: this.props.mygetHelmetapi.footer}}></div>
|
||||
|
||||
}
|
||||
|
||||
</div>
|
||||
<div className="cl"></div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default NewFooter;
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,143 @@
|
||||
import React, { Component } from 'react';
|
||||
import { getImageUrl} from 'educoder';
|
||||
import './TPMIndex.css';
|
||||
|
||||
const $ = window.$;
|
||||
|
||||
$(window).resize(function(){
|
||||
rightSlider();
|
||||
});
|
||||
|
||||
$(window).scroll(function(){
|
||||
if($(".gotop").length>0){
|
||||
if($(document).scrollTop()>0){
|
||||
$(".-task-sidebar .gotop").show();
|
||||
$(".gotop").click(function(){
|
||||
$("html,body").scrollTop(0);
|
||||
});
|
||||
}
|
||||
if($(document).scrollTop()==0){
|
||||
$(".-task-sidebar .gotop").hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function rightSlider(){
|
||||
var poi=parseInt((parseInt($(window).width())- 1200 )/2)-81;
|
||||
// console.log(parseInt($(window).width())+" "+poi);
|
||||
if(poi>0){
|
||||
$(".-task-sidebar").css("right",poi);
|
||||
}else{
|
||||
$(".-task-sidebar").css("right","0px");
|
||||
}
|
||||
$(".-task-sidebar").show();
|
||||
}
|
||||
|
||||
|
||||
function _initSider() {
|
||||
var $descSide = $("<div class='-task-desc'></div>").appendTo("body");
|
||||
$(".-task-sidebar>div").hover(function(){
|
||||
//移入显示二维码
|
||||
if($(this).hasClass("scan")){
|
||||
$(".scan_ewm").show().css({right:"75px",opacity:0}).stop().animate({
|
||||
right:"45px",opacity:1
|
||||
})
|
||||
return;
|
||||
}
|
||||
var $tool = $(this).attr("tooltips");
|
||||
$descSide.html($tool+"<div><img src='/images/edu_user/jt.png'></div>");
|
||||
$descSide.data('_dom', this)
|
||||
$descSide.show().css({
|
||||
left:$(this).offset().left - $descSide.width()-30,
|
||||
opacity:0,
|
||||
top:$(this).offset().top
|
||||
}).stop().animate({
|
||||
left:$(this).offset().left - $descSide.width()-5,
|
||||
opacity:1
|
||||
},400);
|
||||
},function(){
|
||||
if($(this).hasClass("scan")){
|
||||
$(".scan_ewm").stop().animate({right:"75px",opacity:0},200).hide();
|
||||
}
|
||||
$descSide.stop().animate({
|
||||
left:$(this).offset().left - $descSide.width()-30,
|
||||
opacity:0
|
||||
},200).hide();
|
||||
});
|
||||
rightSlider();
|
||||
|
||||
$(window).scroll(function() {
|
||||
if ($descSide.height()) {
|
||||
var hoverIcon = $descSide.data('_dom')
|
||||
$descSide.css('top', $(hoverIcon).offset().top)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
class SiderBar extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
_initSider();
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
// console.log(this.props)
|
||||
return (
|
||||
|
||||
<div className="-task-sidebar" >
|
||||
{this.props.mygetHelmetapi&&this.props.mygetHelmetapi.main_site===true?<div>
|
||||
<div className="gotop" tooltips="返回顶部">
|
||||
<a>
|
||||
<i className="iconfont icon-shangjiantou color-white"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="feedback" tooltips="意见反馈">
|
||||
<a target="_blank" className="color_white" href="/help?index=6">
|
||||
<i className="iconfont icon-yijianfankui color-white font-22"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div className="scan pr">
|
||||
<span className="inline"><i className="iconfont icon-erweima color-white font-22 fl"></i></span>
|
||||
<p className="scan_ewm" style={{display: 'none', right:' 75px',opacity: '0'}}>
|
||||
<p className="pr padding10">
|
||||
<style>
|
||||
{
|
||||
`
|
||||
.WeChatstyle{
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
`
|
||||
}
|
||||
</style>
|
||||
<img src={getImageUrl("images/educoder/EWM.jpg")} width="158px" height="158px" />
|
||||
<p className={"WeChatstyle"}>微信扫一扫</p>
|
||||
<p className={"WeChatstyle"}>关注公众号</p>
|
||||
<span className="trangle_right"></span>
|
||||
</p>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="consult" tooltips="在线咨询">
|
||||
<a target="_blank" className="color_white" href="//shang.qq.com/wpa/qunwpa?idkey=2f2043d88c1bd61d182b98bf1e061c6185e23055bec832c07d8148fe11c5a6cd">
|
||||
<i className="iconfont icon-qqzaixianzixun color-white font-22"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>:""}
|
||||
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SiderBar;
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,54 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { CircularProgress } from 'material-ui/Progress';
|
||||
|
||||
import './TPMShixunDiscuss.css'
|
||||
|
||||
import Challenges from './shixunchild/Challenges/Challenges'
|
||||
|
||||
import TPMRightSection from './component/TPMRightSection'
|
||||
|
||||
import TPMNav from './component/TPMNav'
|
||||
|
||||
class TPMChallenge extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
const { loadingContent, shixun, user, match
|
||||
} = this.props;
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="educontent clearfix mt30 mb80">
|
||||
|
||||
<div className="with65 fl edu-back-white" >
|
||||
<TPMNav
|
||||
match={match}
|
||||
user={user}
|
||||
shixun={shixun}
|
||||
{...this.props}
|
||||
></TPMNav>
|
||||
<Challenges
|
||||
{...this.props}
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
<div className="with35 fr pl20">
|
||||
<TPMRightSection
|
||||
{...this.props}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TPMChallenge;
|
@ -0,0 +1,34 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
import TPMChallenge from './TPMChallenge';
|
||||
class TPMChallengeContainer extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
tpmLoading: true,
|
||||
creator: {
|
||||
owner_id: '',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { tpmLoading } = this.props;
|
||||
const user = this.props.current_user;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
|
||||
{ tpmLoading ? <div style={{ minHeight: '886px'}}></div> :
|
||||
<TPMChallenge
|
||||
{...this.props}
|
||||
>
|
||||
</TPMChallenge>
|
||||
}
|
||||
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TPMChallengeContainer;
|
@ -0,0 +1,53 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { CircularProgress } from 'material-ui/Progress';
|
||||
|
||||
import './TPMShixunDiscuss.css'
|
||||
|
||||
import Collaborators from './shixunchild/Collaborators/Collaborators'
|
||||
import TPMRightSection from './component/TPMRightSection'
|
||||
import TPMNav from './component/TPMNav'
|
||||
|
||||
class TPMCollaborators extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
const { loadingContent, creator, shixun, myshixun, recommend_shixuns, current_user, watched,
|
||||
aboutFocus, user, match
|
||||
} = this.props;
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="educontent clearfix mt30 mb80">
|
||||
|
||||
<div className="with65 fl edu-back-white" >
|
||||
<TPMNav
|
||||
match={match}
|
||||
user={user}
|
||||
shixun={shixun}
|
||||
{...this.props}
|
||||
></TPMNav>
|
||||
<Collaborators
|
||||
{...this.props}
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
<div className="with35 fr pl20">
|
||||
<TPMRightSection
|
||||
{...this.props}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TPMCollaborators;
|
@ -0,0 +1,47 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import TPMCollaborators from './TPMCollaborators'
|
||||
|
||||
import axios from 'axios';
|
||||
|
||||
class TPMChallengeContainer extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps, newContext) {
|
||||
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// this.props.showShixun();
|
||||
}
|
||||
|
||||
|
||||
|
||||
render() {
|
||||
const { tpmLoading } = this.props;
|
||||
const user = this.props.current_user;
|
||||
return (
|
||||
<React.Fragment>
|
||||
{ tpmLoading ? <div style={{ minHeight: '886px'}}></div> :
|
||||
<TPMCollaborators
|
||||
{...this.props}
|
||||
{...this.state}
|
||||
user={user}
|
||||
aboutFocus={this.props.aboutFocus}
|
||||
|
||||
>
|
||||
</TPMCollaborators>
|
||||
}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TPMChallengeContainer;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue