Merge branch 'dev_aliyun' of http://bdgit.educoder.net/Hjqreturn/educoder into dev_aliyun

dev_forge
cxt 5 years ago
commit 0a46e83f89

@ -267,7 +267,7 @@ module.exports = {
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
// You can remove this if you don't use Moment.js:
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
new MonacoWebpackPlugin(),
new MonacoWebpackPlugin(),
],
// Some libraries import Node modules but don't use them in the browser.
// Tell Webpack to provide empty mocks for them so importing them works.

@ -11,6 +11,7 @@ const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
const eslintFormatter = require('react-dev-utils/eslintFormatter');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
const paths = require('./paths');
const getClientEnvironment = require('./env');
@ -371,6 +372,7 @@ module.exports = {
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
// You can remove this if you don't use Moment.js:
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
new MonacoWebpackPlugin(),
],
// Some libraries import Node modules but don't use them in the browser.
// Tell Webpack to provide empty mocks for them so importing them works.

@ -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) });
}

@ -84,7 +84,6 @@
"@icedesign/base": {
"version": "0.2.8",
"resolved": "https://registry.npm.taobao.org/@icedesign/base/download/@icedesign/base-0.2.8.tgz",
"integrity": "sha1-hmlSY+17gnKJB3sbgoy446sqzAk=",
"requires": {
"async-validator": "^1.6.7",
"classnames": "^2.2.3",
@ -138,8 +137,7 @@
},
"@types/tapable": {
"version": "1.0.4",
"resolved": "https://registry.npm.taobao.org/@types/tapable/download/@types/tapable-1.0.4.tgz",
"integrity": "sha1-tP/H3Je0mMlps2CkHu4kf4JhY3A="
"resolved": "https://registry.npm.taobao.org/@types/tapable/download/@types/tapable-1.0.4.tgz"
},
"@types/uglify-js": {
"version": "3.0.4",
@ -2779,8 +2777,7 @@
},
"cropperjs": {
"version": "0.7.2",
"resolved": "https://registry.npm.taobao.org/cropperjs/download/cropperjs-0.7.2.tgz",
"integrity": "sha1-atinHbAGKbqULZzt5lKyeXXp50o="
"resolved": "https://registry.npm.taobao.org/cropperjs/download/cropperjs-0.7.2.tgz"
},
"cross-spawn": {
"version": "5.1.0",
@ -10456,6 +10453,11 @@
"resolved": "http://registry.npm.taobao.org/requires-port/download/requires-port-1.0.0.tgz",
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
},
"reqwest": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/reqwest/-/reqwest-2.0.5.tgz",
"integrity": "sha1-APsVrEkYxBnKgrQ/JMeIguZgOaE="
},
"resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "http://registry.npm.taobao.org/resize-observer-polyfill/download/resize-observer-polyfill-1.5.1.tgz",
@ -10834,86 +10836,6 @@
"resolved": "http://registry.npm.taobao.org/shellwords/download/shellwords-0.1.1.tgz",
"integrity": "sha1-1rkYHBpI05cyTISHHvvPxz/AZUs="
},
"showdown": {
"version": "1.9.0",
"resolved": "http://registry.npm.taobao.org/showdown/download/showdown-1.9.0.tgz",
"integrity": "sha1-1J0qC22yG3wulu+FX3s7KijvRvQ=",
"requires": {
"yargs": "^10.0.3"
},
"dependencies": {
"ansi-regex": {
"version": "3.0.0",
"resolved": "http://registry.npm.taobao.org/ansi-regex/download/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
},
"camelcase": {
"version": "4.1.0",
"resolved": "http://registry.npm.taobao.org/camelcase/download/camelcase-4.1.0.tgz",
"integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
},
"cliui": {
"version": "4.1.0",
"resolved": "http://registry.npm.taobao.org/cliui/download/cliui-4.1.0.tgz",
"integrity": "sha1-NIQi2+gtgAswIu709qwQvy5NG0k=",
"requires": {
"string-width": "^2.1.1",
"strip-ansi": "^4.0.0",
"wrap-ansi": "^2.0.0"
}
},
"os-locale": {
"version": "2.1.0",
"resolved": "http://registry.npm.taobao.org/os-locale/download/os-locale-2.1.0.tgz",
"integrity": "sha1-QrwpAKa1uL0XN2yOiCtlr8zyS/I=",
"requires": {
"execa": "^0.7.0",
"lcid": "^1.0.0",
"mem": "^1.1.0"
}
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "http://registry.npm.taobao.org/strip-ansi/download/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"requires": {
"ansi-regex": "^3.0.0"
}
},
"which-module": {
"version": "2.0.0",
"resolved": "http://registry.npm.taobao.org/which-module/download/which-module-2.0.0.tgz",
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
},
"yargs": {
"version": "10.1.2",
"resolved": "http://registry.npm.taobao.org/yargs/download/yargs-10.1.2.tgz",
"integrity": "sha1-RU0HTCsWpRpD4vt4B+T53mnMtcU=",
"requires": {
"cliui": "^4.0.0",
"decamelize": "^1.1.1",
"find-up": "^2.1.0",
"get-caller-file": "^1.0.1",
"os-locale": "^2.0.0",
"require-directory": "^2.1.1",
"require-main-filename": "^1.0.1",
"set-blocking": "^2.0.0",
"string-width": "^2.0.0",
"which-module": "^2.0.0",
"y18n": "^3.2.1",
"yargs-parser": "^8.1.0"
}
},
"yargs-parser": {
"version": "8.1.0",
"resolved": "http://registry.npm.taobao.org/yargs-parser/download/yargs-parser-8.1.0.tgz",
"integrity": "sha1-8TdqM7Ziml0GN4KUTacyYx6WaVA=",
"requires": {
"camelcase": "^4.1.0"
}
}
}
},
"signal-exit": {
"version": "3.0.2",
"resolved": "http://registry.npm.taobao.org/signal-exit/download/signal-exit-3.0.2.tgz",

@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"@icedesign/base": "^0.2.5",
"@monaco-editor/react": "^2.3.0",
"@novnc/novnc": "^1.1.0",
"antd": "^3.23.2",
"array-flatten": "^2.1.2",
@ -28,6 +29,7 @@
"dotenv": "4.0.0",
"dotenv-expand": "4.2.0",
"echarts": "^4.2.0-rc.2",
"editor.md": "^1.5.0",
"eslint": "4.10.0",
"eslint-config-react-app": "^2.1.0",
"eslint-loader": "1.9.0",
@ -42,6 +44,8 @@
"immutability-helper": "^2.6.6",
"install": "^0.12.2",
"jest": "20.0.4",
"js-base64": "^2.5.1",
"katex": "^0.11.1",
"lodash": "^4.17.5",
"loglevel": "^1.6.1",
"material-ui": "^1.0.0-beta.40",
@ -49,12 +53,14 @@
"monaco-editor": "^0.15.6",
"monaco-editor-webpack-plugin": "^1.7.0",
"npm": "^6.10.1",
"numeral": "^2.0.6",
"object-assign": "4.1.1",
"postcss-flexbugs-fixes": "3.2.0",
"postcss-loader": "2.0.8",
"promise": "8.0.1",
"prop-types": "^15.6.1",
"qs": "^6.6.0",
"quill": "^1.3.7",
"raf": "3.4.0",
"rc-form": "^2.1.7",
"rc-pagination": "^1.16.2",
@ -77,11 +83,12 @@
"react-redux": "5.0.7",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"react-split-pane": "^0.1.87",
"react-split-pane": "^0.1.89",
"react-url-query": "^1.4.0",
"redux": "^4.0.0",
"redux-thunk": "2.3.0",
"rsuite": "^4.0.1",
"sass-loader": "7.3.1",
"store": "^2.0.12",
"style-loader": "0.19.0",
"styled-components": "^4.1.3",
@ -167,7 +174,7 @@
"concat": "^1.0.3",
"happypack": "^5.0.1",
"node-sass": "^4.12.0",
"sass-loader": "^7.3.1",
"reqwest": "^2.0.5",
"webpack-bundle-analyzer": "^3.0.3",
"webpack-parallel-uglify-plugin": "^1.1.0"
}

@ -13,6 +13,7 @@
<!--<meta http-equiv="Expires" content="0" />-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<!-- <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">-->
<!--
Notice the use of %PUBLIC_URL% in the tags above.
@ -186,5 +187,6 @@
<!-- <script type="text/javascript" src="https://testeduplus2.educoder.net/assets/kindeditor/kindeditor.js"></script>
<script type="text/javascript" src="/js/create_kindeditor.js"></script>
<script type="text/javascript" src="https://testeduplus2.educoder.net/javascripts/educoder/edu_application.js"></script> -->
</body>
</html>

@ -40,11 +40,11 @@ import history from './history';
import {SnackbarHOC} from 'educoder'
import {initAxiosInterceptors} from './AppConfig'
import { Provider } from 'react-redux';
import configureStore from './redux/stores/configureStore';
// tpi需要这个来加载css
import {TPMIndexHOC} from './modules/tpm/TPMIndexHOC';
const store = configureStore();
const theme = createMuiTheme({
palette: {
@ -295,7 +295,22 @@ const Ecs = Loadable({
loading: Loading,
})
// 添加开发者社区
const Developer = Loadable({
loader: () => import('./modules/developer'),
loading: Loading
})
// 开发者编辑模块
const NewOrEditTask = Loadable({
loader: () => import('./modules/developer/newOrEditTask'),
loading: Loading
});
// 学员学习
const StudentStudy = Loadable({
loader: () => import('./modules/developer/studentStudy'),
loading: Loading
});
// //个人竞赛报名
// const PersonalCompetit = Loadable({
// loader: () => import('./modules/competition/personal/PersonalCompetit.js'),
@ -462,211 +477,213 @@ class App extends Component {
// console.log("appappapp");
// console.log(mygetHelmetapi);
return (
<LocaleProvider locale={zhCN}>
<MuiThemeProvider theme={theme}>
<Accountnewprofile {...this.props}{...this.state}/>
<LoginDialog {...this.props} {...this.state} Modifyloginvalue={()=>this.Modifyloginvalue()}></LoginDialog>
<Notcompletedysl {...this.props} {...this.state}></Notcompletedysl>
<Trialapplicationysl {...this.props} {...this.state}></Trialapplicationysl>
<Trialapplicationreview {...this.props} {...this.state}></Trialapplicationreview>
<Addcourses {...this.props} {...this.state} HideAddcoursestypess={(i)=>this.HideAddcoursestypess(i)}/>
<AccountProfile {...this.props} {...this.state} />
<Certifiedprofessional {...this.props} {...this.state} ModalCancelsy={this.ModalCancelsy} ModalshowCancelsy={this.ModalshowCancelsy}/>
<Router>
<Switch>
{/*题库*/}
<Route path="/topicbank/:username/:topicstype"
render={
(props) => {
return (<Topicbank {...this.props} {...props} {...this.state} />)
}
}></Route>
{/*题库*/}
<Route path="/topicbank/:topicstype"
render={
(props) => {
return (<Topicbank {...this.props} {...props} {...this.state} />)
}
}></Route>
{/*/!*众包创新*!/*/}
{/*<Route path={"/crowdsourcing"} component={ProjectPackages}/>*/}
{/*竞赛*/}
<Route path={"/competitions"}
render={
(props) => {
return (<NewCompetitions {...this.props} {...props} {...this.state} />)
}
}></Route>
{/*黑客松定制竞赛*/}
<Route
path={"/osshackathon"}
render={
(props)=>{
return(
<Osshackathon {...this.props} {...props} {...this.state} />
)
<Provider store={store}>
<LocaleProvider locale={zhCN}>
<MuiThemeProvider theme={theme}>
<Accountnewprofile {...this.props}{...this.state}/>
<LoginDialog {...this.props} {...this.state} Modifyloginvalue={()=>this.Modifyloginvalue()}></LoginDialog>
<Notcompletedysl {...this.props} {...this.state}></Notcompletedysl>
<Trialapplicationysl {...this.props} {...this.state}></Trialapplicationysl>
<Trialapplicationreview {...this.props} {...this.state}></Trialapplicationreview>
<Addcourses {...this.props} {...this.state} HideAddcoursestypess={(i)=>this.HideAddcoursestypess(i)}/>
<AccountProfile {...this.props} {...this.state} />
<Certifiedprofessional {...this.props} {...this.state} ModalCancelsy={this.ModalCancelsy} ModalshowCancelsy={this.ModalshowCancelsy}/>
<Router>
<Switch>
{/*题库*/}
<Route path="/topicbank/:username/:topicstype"
render={
(props) => {
return (<Topicbank {...this.props} {...props} {...this.state} />)
}
}></Route>
{/*题库*/}
<Route path="/topicbank/:topicstype"
render={
(props) => {
return (<Topicbank {...this.props} {...props} {...this.state} />)
}
}></Route>
{/*/!*众包创新*!/*/}
{/*<Route path={"/crowdsourcing"} component={ProjectPackages}/>*/}
{/*竞赛*/}
<Route path={"/competitions"}
render={
(props) => {
return (<NewCompetitions {...this.props} {...props} {...this.state} />)
}
}></Route>
{/*黑客松定制竞赛*/}
<Route
path={"/osshackathon"}
render={
(props)=>{
return(
<Osshackathon {...this.props} {...props} {...this.state} />
)
}
}
}
/>
/>
{/*认证*/}
<Route path="/account" component={AccountPage}/>
{/*认证*/}
<Route path="/account" component={AccountPage}/>
{/*403*/}
<Route path="/403" component={Shixunauthority}/>
{/*403*/}
<Route path="/403" component={Shixunauthority}/>
<Route path="/500" component={http500}/>
<Route path="/500" component={http500}/>
{/*404*/}
<Route path="/nopage" component={Shixunnopage}/>
{/*404*/}
<Route path="/nopage" component={Shixunnopage}/>
<Route path="/compatibility" component={CompatibilityPageLoadable}/>
<Route
path="/login"
render={
(props) => {
<Route path="/compatibility" component={CompatibilityPageLoadable}/>
<Route
path="/login"
render={
(props) => {
return (<EducoderLogin {...this.props} {...props} {...this.state} />)
return (<EducoderLogin {...this.props} {...props} {...this.state} />)
}
}
}
/>
<Route
path="/register"
render={
(props) => {
return (<EducoderLogin {...this.props} {...props} {...this.state} />)
/>
<Route
path="/register"
render={
(props) => {
return (<EducoderLogin {...this.props} {...props} {...this.state} />)
}
}
}
/>
<Route
path="/otherloginstart" component={Otherloginstart}
/>
<Route
path={"/otherloginqq"} component={Otherloginsqq}
/>
<Route
path="/otherlogin" component={Otherlogin}
/>
<Route path="/users/:username"
render={
(props) => {
return (<InfosIndex {...this.props} {...props} {...this.state} />)
/>
<Route
path="/otherloginstart" component={Otherloginstart}
/>
<Route
path={"/otherloginqq"} component={Otherloginsqq}
/>
<Route
path="/otherlogin" component={Otherlogin}
/>
<Route path="/users/:username"
render={
(props) => {
return (<InfosIndex {...this.props} {...props} {...this.state} />)
}
}></Route>
<Route path="/banks"
render={
(props) => {
return (<BanksIndex {...this.props} {...props} {...this.state} />)
}
}></Route>
{/*<Route*/}
{/*path="/personalcompetit"*/}
{/*render={*/}
{/*(props) => (<PersonalCompetit {...this.props} {...props} {...this.state}></PersonalCompetit>)*/}
{/*}*/}
{/*/>*/}
<Route
path="/changepassword"
render={
(props) => {
return (<EducoderLogin {...this.props} {...props} {...this.state} />)
}
}
}></Route>
/>
<Route
path="/interesse" component={Interestpage}
<Route path="/banks"
render={
(props) => {
return (<BanksIndex {...this.props} {...props} {...this.state} />)
}
}></Route>
{/*<Route*/}
{/*path="/personalcompetit"*/}
{/*render={*/}
{/*(props) => (<PersonalCompetit {...this.props} {...props} {...this.state}></PersonalCompetit>)*/}
{/*}*/}
{/*/>*/}
<Route
path="/changepassword"
render={
(props) => {
return (<EducoderLogin {...this.props} {...props} {...this.state} />)
/>
<Route path="/shixuns/new" component={Newshixuns}>
</Route>
<Route path="/tasks/:stageId" component={IndexWrapperComponent}/>
<Route path="/shixuns/:shixunId" component={TPMIndexComponent}>
</Route>
{/*列表页*/}
<Route path="/shixuns" component={TPMShixunsIndexComponent}/>
{/*实训课程(原实训路径)*/}
<Route path="/paths" component={ShixunPaths}></Route>
<Route path="/search"
render={
(props)=>(<SearchPage {...this.props} {...props} {...this.state}></SearchPage>)
}
></Route>
{/*课堂*/}
<Route path="/courses" component={CoursesIndex} {...this.props} {...this.state}></Route>
{/* <Route path="/forums" component={ForumsIndexComponent}>
</Route> */}
{/* 教学案例 */}
<Route path="/moop_cases"render={
(props) => (<MoopCases {...this.props} {...props} {...this.state} />)
}/>
<Route path="/forums"
render={
(props)=>(<ForumsIndexComponent {...this.props} {...props} {...this.state}></ForumsIndexComponent>)
}
}
/>
<Route
path="/interesse" component={Interestpage}
/>
<Route path="/shixuns/new" component={Newshixuns}>
</Route>
<Route path="/tasks/:stageId" component={IndexWrapperComponent}/>
<Route path="/shixuns/:shixunId" component={TPMIndexComponent}>
</Route>
{/*列表页*/}
<Route path="/shixuns" component={TPMShixunsIndexComponent}/>
{/*实训课程(原实训路径)*/}
<Route path="/paths" component={ShixunPaths}></Route>
<Route path="/search"
render={
(props)=>(<SearchPage {...this.props} {...props} {...this.state}></SearchPage>)
}
></Route>
{/*课堂*/}
<Route path="/courses" component={CoursesIndex} {...this.props} {...this.state}></Route>
{/* <Route path="/forums" component={ForumsIndexComponent}>
</Route> */}
{/* 教学案例 */}
<Route path="/moop_cases"render={
(props) => (<MoopCases {...this.props} {...props} {...this.state} />)
}/>
<Route path="/forums"
render={
(props)=>(<ForumsIndexComponent {...this.props} {...props} {...this.state}></ForumsIndexComponent>)
}
>
</Route>
<Route path="/comment" component={CommentComponent}/>
{/*<Route path="/testMaterial" component={TestMaterialDesignComponent}/>*/}
{/*<Route path="/test" component={TestIndex}/>*/}
{/*<Route path="/testCodeMirror" component={TestCodeMirrorComponent}/>*/}
{/*<Route path="/testRCComponent" component={TestComponent}/>*/}
{/*<Route path="/testUrlQuery" component={TestUrlQueryComponent}/>*/}
{/*<Route*/}
{/*path="/registration"*/}
{/*render={*/}
{/*(props) => (<Registration {...this.props} {...props} {...this.state}></Registration>)*/}
{/*}*/}
{/*/>*/}
<Route path="/messages"
render={
(props)=>(<Messagerouting {...this.props} {...props} {...this.state}></Messagerouting>)
}
></Route>
<Route path="/help/:type"
render={
(props)=>(<Help {...this.props} {...props} {...this.state}></Help>)
}/>
<Route path="/ecs"
render={
(props)=>(<Ecs {...this.props} {...props} {...this.state}></Ecs>)
}/>
<Route exact path="/"
// component={ShixunsHome}
render={
(props)=>(<ShixunsHome {...this.props} {...props} {...this.state}></ShixunsHome>)
}
/>
<Route component={Shixunnopage}/>
</Switch>
</Router>
</MuiThemeProvider>
</LocaleProvider>
>
</Route>
<Route path="/comment" component={CommentComponent}/>
{/*<Route path="/testMaterial" component={TestMaterialDesignComponent}/>*/}
{/*<Route path="/test" component={TestIndex}/>*/}
{/*<Route path="/testCodeMirror" component={TestCodeMirrorComponent}/>*/}
{/*<Route path="/testRCComponent" component={TestComponent}/>*/}
{/*<Route path="/testUrlQuery" component={TestUrlQueryComponent}/>*/}
{/*<Route*/}
{/*path="/registration"*/}
{/*render={*/}
{/*(props) => (<Registration {...this.props} {...props} {...this.state}></Registration>)*/}
{/*}*/}
{/*/>*/}
<Route path="/messages"
render={
(props)=>(<Messagerouting {...this.props} {...props} {...this.state}></Messagerouting>)
}
></Route>
<Route path="/help/:type"
render={
(props)=>(<Help {...this.props} {...props} {...this.state}></Help>)
}/>
<Route path="/ecs"
render={
(props)=>(<Ecs {...this.props} {...props} {...this.state}></Ecs>)
}/>
<Route path="/problems/new/:id?" component={NewOrEditTask} />
<Route path="/problems/:id/edit" component={NewOrEditTask} />
<Route path="/myproblems/:id" component={StudentStudy} />
<Route path="/problems" component={Developer}/>
<Route exact path="/"
// component={ShixunsHome}
render={
(props)=>(<ShixunsHome {...this.props} {...props} {...this.state}></ShixunsHome>)
}
/>
<Route component={Shixunnopage}/>
</Switch>
</Router>
</MuiThemeProvider>
</LocaleProvider>
</Provider>
);
}
}

@ -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,225 @@
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { Menu, Icon, List, Avatar,Row, Col,Tag,Pagination,Alert} from 'antd';
import {getImageUrl} from 'educoder';
import axios from 'axios';
import './Competitionsindex.css';
import NoneData from "../../courses/coursesPublic/NoneData";
import LoadingSpin from '../../../common/LoadingSpin';
class CompetitionsIndex extends Component{
constructor(props) {
super(props)
this.state={
current: 'all',
datas:undefined,
page:1,
category:undefined
}
}
componentDidMount(){
window.document.title = '竞赛';
let{category,page}=this.state;
this.getdata(category,page)
}
getdata=(category,page)=>{
const Url =`/competitions.json`;
axios.get(Url,{params:{
category:category,
page:page,
per_page:15,
}
}).then((response) => {
if(response.status===200){
this.setState({
datas:response.data.competitions,
count:response.data.count,
})
}
})
.catch(function (error) {
console.log(error);
});
}
handleClick = e => {
this.setState({
current: e.key,
datas:undefined
});
let{category,page}=this.state;
this.getdata(e.key,page)
};
PaginationCourse=(pageNumber)=>{
let {category}=this.state;
this.setState({
page: pageNumber,
})
this.getdata(category,pageNumber);
}
render() {
let {datas,page,count}=this.state;
return (
<div>
<div className="clearfix">
<div>
<div className="clearfix">
<style>
{
`
.courses-head{
width: 100%;
height: 300px;
background-image: url(${getImageUrl(this.props.mygetHelmetapi && this.props.mygetHelmetapi.competition_banner_url === null ?`images/educoder/competitions/courses.jpg`:this.props.mygetHelmetapi&&this.props.mygetHelmetapi.competition_banner_url)});
background-color: #081C4B;
background-position: center;
background-repeat: no-repeat;
}
`
}
</style>
<div className="courses-head pr Competitionshead ">
<div className="edu-txt-center pathNavLine">
<div className="inline path-nav"></div>
</div>
</div>
<div className="competitionstitle mb20">
<div className="competitionstitle2">
<Menu onClick={this.handleClick} selectedKeys={[this.state.current]} mode="horizontal">
<Menu.Item key="all" className={"competitionmr50"}>
<span className={"competitionsvalue"}>全部</span>
</Menu.Item>
<Menu.Item key="nearly_published" className={"competitionmr50"}>
<span className={"competitionsvalue"}>即将发布</span>
</Menu.Item>
<Menu.Item key="progressing" className={"competitionmr50"}>
<span className={"competitionsvalue"}>进行中</span>
</Menu.Item>
<Menu.Item key="ended" className={"competitionmr50"}>
<span className={"competitionsvalue"}>往期比赛</span>
</Menu.Item>
</Menu>
</div>
</div>
<div className={"educontent clearfix mtf10 CompetitionsIndex mb20"}>
{datas===undefined?"":datas.length===0?"":<List
itemLayout="vertical"
size="large"
dataSource={datas&&datas}
renderItem={(item,key) => (
<a target="_blank" href={item.competition_status==="ended"?`/competitions/${item.identifier}`:item.competition_status==="nearly_published"?item.permission.editable==true?`/competitions/${item.identifier}`:null:item.competition_status==="progressing"?`/competitions/${item.identifier}`:null}
className={item.competition_status==="ended"?"competitionstitlesshou":item.competition_status==="nearly_published"?
item.permission.editable==true?"competitionstitlesshou":"endedfont":"competitionstitlesshou"}
>
<div className={"CompetitionsList"} >
{item.competition_status==="nearly_published"?
item.permission.editable==true?"":<div className={"CompetitionsListzhezhao"}>即将发布 敬请期待</div>:""}
{/*<div className={"CompetitionsListzhezhao"}>即将发布 敬请期待</div>*/}
{/*{item.description===null||item.description===undefined||item.description===""?<style>*/}
{/*{*/}
{/*`*/}
{/*// .CompetitionsIndex .ant-list-vertical .ant-list-item-action{*/}
{/*// margin-top:50px;*/}
{/*// }*/}
{/*`*/}
{/*}*/}
{/*</style>:""}*/}
<img className={"competitonimg"}
src={item.competition_status==="ended"?getImageUrl(`images/educoder/competitions/groups1.png`):item.competition_status==="nearly_published"?getImageUrl(`images/educoder/competitions/groups2.png`):item.competition_status==="progressing"?getImageUrl(`images/educoder/competitions/groups3.png`):""} />
<List.Item
key={key}
actions={[
<span>竞赛时间: {item.start_time}{item.end_time}</span>,
<span>报名截止时间{item.enroll_end_time}</span>,
]}
extra={
<div className={"pt50"} style={{"width":'314px'}}>
<Row gutter={16}>
<Col className="gutter-row" span={6}>
<div className="gutter-box CompetitionsIndexdadels">奖金</div>
</Col>
<Col className="gutter-row" span={6}>
<div className="gutter-box CompetitionsIndexdadels">浏览数</div>
</Col>
<Col className="gutter-row" span={6}>
<div className="gutter-box CompetitionsIndexdadels">报名数</div>
</Col>
</Row>
<Row gutter={16}>
<Col className="gutter-row" span={6}>
<div className="gutter-box CompetitionsIndexbottomvalue">¥{item.bonus}</div>
</Col>
<Col className="gutter-row" span={6}>
<div className="gutter-box CompetitionsIndexbottomvalue">{item.competition_status==="nearly_published"?"--":item.visits_count}</div>
</Col>
<Col className="gutter-row" span={6}>
<div className="gutter-box CompetitionsIndexbottomvalue">{item.competition_status==="nearly_published"?"--":item.member_count}</div>
</Col>
</Row>
</div>
}
>
<List.Item.Meta
title={<a className={item.competition_status==="ended"?"competitionstitlesshou":item.competition_status==="nearly_published"?
item.permission.editable==true?"competitionstitlesshou":"endedfont":"competitionstitlesshou"}>
<a target="_blank" className={"competitionstitles"}
href={item.competition_status==="ended"?`/competitions/${item.identifier}`:item.competition_status==="nearly_published"? item.permission.editable==true?`/competitions/${item.identifier}`:null:item.competition_status==="progressing"?`/competitions/${item.identifier}`:null}
>{item.name}{item.sub_title===null?"":`——${item.sub_title}`}</a>
{/*<span>{item.sub_title===null?"":*/}
{/*<Tag className="competitionsrelative" color="#87d068">{item.sub_title}</Tag>}*/}
{/*</span>*/}
</a>}
/>
<span className={"span666"}>{item.description}</span>
</List.Item>
</div>
</a>
)
}
/>}
{datas===undefined?"":count===undefined?"":count >15 ?<div className="mb40 edu-txt-center padding20-30"
>
<Pagination
showQuickJumper
defaultCurrent={1}
pageSize={15}
total={count===undefined?"":count}
current={page}
onChange={this.PaginationCourse}
/>
</div>:""}
{
datas===undefined?<LoadingSpin/>:datas && datas.length===0? <NoneData></NoneData>:""
}
</div>
</div>
</div>
</div>
</div>
)
}
}
export default CompetitionsIndex;

@ -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,267 @@
import React, { Component } from 'react';
import { Breadcrumb,Layout,Table, Divider, Tag,Badge,Tooltip} from 'antd';
import { Link } from 'react-router-dom';
import axios from 'axios';
import './Competitionteams.css';
const { Content } = Layout;
class Competitionteams extends Component{
constructor(props) {
super(props)
this.state={
shixundata: undefined,
coursedata:undefined,
}
}
componentDidMount(){
window.document.title = '竞赛';
if(this.props.match.params.identifier!=null){
let url=`/competitions/${this.props.match.params.identifier}/common_header.json`;
axios.get(url).then((response) => {
if(response.status===200){
this.setState({
data:response.data,
})
}
}).catch((error) => {
console.log(error)
})
}
this.getshixundata();
this.getcoursedata();
}
getshixundata=()=>{
const Url =`/competitions/${this.props.match.params.identifier}/competition_teams/${this.props.match.params.competition_team_id}/shixun_detail.json`;
axios.get(Url).then((response) => {
if(response.status===200){
// let data={
// shixuns: [
// {
// creator: "黄井泉", // 创建者
// shixun_name: "单链表的学习与应用I", // 实训名称
// shixun_identifier: "mnf6b7z3",
// forked: false, // false:原创
// myshixuns_count: 179, // 学习人数
// forked_myshixun_count: 0, // 被fork发布的学习人数
// valid_count: 82, // 有效作品数
// score: 1320 // 应用值
// }
// ],
// shixun_count: 1, // 实训总计
// total_myshixun_count: 179, // 学习人数总计
// total_forked_myshixun_count: 0, // 被fork发布的学习人数总计
// total_valid_count: 82, // 有效作品数总计
// total_shixun_score: 1320 // 应用值总计
// }
let data=response.data;
let newarr=data.shixuns;
let newobj={
creator:"合计:",
shixun_name:data.shixun_count,
myshixuns_count:data.total_myshixun_count,
forked_myshixun_count:data.total_forked_myshixun_count,
valid_count:data.total_valid_count,
score:data.total_shixun_score
}
newarr.push(newobj)
this.setState({
shixundata:newarr
})
}
})
.catch(function (error) {
console.log(error);
});
}
getcoursedata=()=>{
const Url =`/competitions/${this.props.match.params.identifier}/competition_teams/${this.props.match.params.competition_team_id}/course_detail.json`;
axios.get(Url).then((response) => {
if(response.status===200){
// let data={
// courses: [
// {
// creator: "周海芳", // 创建者
// creator_login: "Nancy", // login
// course_name: "大学计算机基础2018年秋季",
// course_id: 1502,
// students_count: 122, // 学生数量
// shixun_homework_count: 8, // 发布的实训作业数量
// valid_count: 977, // 有效作品数
// score: 29810 // 应用值
// }
// ],
// total_course_count: 1, // 课堂总计
// total_students_count: 122, // 学生数总计
// total_shixun_homework_count: 8, // 实训作业数总计
// total_valid_count: 977, // 有效作品数总计
// total_course_score: 29810 // 应用值总计
// }
let data=response.data;
let newarr=data.courses;
let newobj={
creator:"合计:",
course_name:data.total_course_count,
students_count:data.total_students_count,
shixun_homework_count:data.total_shixun_homework_count,
valid_count:data.total_valid_count,
score:data.total_course_score
}
newarr.push(newobj)
this.setState({
coursedata:newarr
})
}
})
.catch(function (error) {
console.log(error);
});
}
render() {
let {data}=this.state;
const shixuncolumns = [
{
title: '创建者',
dataIndex: 'creator',
key: 'creator',
render: (text, record) => <div className={record.creator==="合计:"?"teamsLayoutheji":""}>{text}</div>,
},
{
title: '名称',
dataIndex: 'shixun_name',
key: 'shixun_name',
render: (text, record) =>
<div className={record.creator==="合计:"?"teamsLayoucolor-orange":""}>{text}{record.forked===true?<Badge count={"原创"} style={{ backgroundColor: '#459BE5' }} />:""}</div>,
},
{
title: '学习人数',
dataIndex: 'myshixuns_count',
key: 'myshixuns_count',
render: (text, record) => <div className={record.creator==="合计:"?"teamsLayoucolor-orange":""}>{text}</div>,
},
{
title: '被fork发布的学习人数',
dataIndex: 'forked_myshixun_count',
key: 'forked_myshixun_count',
render: (text, record) =>
<Tooltip placement="bottom" title={"fork该实训产生的新实训学习总人数"}>
<div className={record.creator==="合计:"?"teamsLayoucolor-orange":""}>{text}</div>
</Tooltip>,
},
{
title: '有效作品数',
dataIndex: 'valid_count',
key: 'valid_count',
render: (text, record) =>
<Tooltip placement="bottom" title={"至少完成了1个关卡"}>
<div className={record.creator==="合计:"?"teamsLayoucolor-orange":""}>{text}</div>
</Tooltip>,
},
{
title: '应用值',
dataIndex: 'score',
key: 'score',
render: (text, record) => <div className={record.creator==="合计:"?"teamsLayoucolor-orange":""}>{text}</div>,
},
];
const coursecolumns = [
{
title: '创建者',
dataIndex: 'creator',
key: 'creator',
render: (text, record) => <div className={record.creator==="合计:"?"teamsLayoutheji":""}>{text}</div>,
},
{
title: '名称',
dataIndex: 'course_name',
key: 'course_name',
render: (text, record) => <div className={record.creator==="合计:"?"teamsLayoucolor-orange":""}>{text}</div>,
},
{
title: '学生数量',
dataIndex: 'students_count',
key: 'students_count',
render: (text, record) => <div className={record.creator==="合计:"?"teamsLayoucolor-orange":""}>{text}</div>,
},
{
title: '发布的实训作业数量',
dataIndex: 'shixun_homework_count',
key: 'shixun_homework_count',
render: (text, record) =>
<div className={record.creator==="合计:"?"teamsLayoucolor-orange":""}>{text}</div>
,
},
{
title: '有效作品数',
dataIndex: 'valid_count',
key: 'valid_count',
render: (text, record) =>
<Tooltip placement="bottom" title={"至少完成了1个关卡"}>
<div className={record.creator==="合计:"?"teamsLayoucolor-orange":""}>{text}</div>
</Tooltip>,
},
{
title: '应用值',
dataIndex: 'score',
key: 'score',
render: (text, record) => <div className={record.creator==="合计:"?"teamsLayoucolor-orange":""}>{text}</div>,
},
];
// console.log(this.state.shixundata)
return (
<div className={"educontent clearfix mt20 "}>
<Breadcrumb separator=">">
<Breadcrumb.Item><Link to={"/competitions"}>{data&&data.name}</Link></Breadcrumb.Item>
<Breadcrumb.Item><Link to={`/competitions/${this.props.match.params.identifier}/enroll`}>报名</Link></Breadcrumb.Item>
<Breadcrumb.Item>战队详情</Breadcrumb.Item>
</Breadcrumb>
<Layout className={"teamsLayout"}>
<Content className={"teamsLayoutitle"}>实训项目</Content>
<Content className={"teamsLayoutContent"}>
<Table className="teamsLayoutTable" columns={shixuncolumns} dataSource={this.state.shixundata} bordered pagination={false}/>
</Content>
<Content className={"teamsLayoutitle mt40"}>翻转课堂</Content>
<Content className={"teamsLayoutContents"}>
<Table className="teamsLayoutTable" columns={coursecolumns} dataSource={this.state.coursedata} bordered pagination={false}/>
</Content>
</Layout>
</div>
)
}
}
export default Competitionteams;

@ -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,548 @@
import React, { Component } from 'react';
import { Breadcrumb,Layout,Table, Divider, Tag,Badge,Row, Col,Button, Menu, Icon} from 'antd';
import { Link } from 'react-router-dom';
import axios from 'axios';
import {markdownToHTML,getImageUrl} from 'educoder';
import CompetitionContents from './CompetitionCommonChild/CompetitionContents';
import CompetitionContentsChart from './CompetitionCommonChild/CompetitionContentsChart';
import CompetitionContentsMd from './CompetitionCommonChild/CompetitionContentsMd';
import CompetitionContentspdf from './CompetitionCommonChild/CompetitionContentspdf';
import './CompetitionCommon.css';
const {Sider } = Layout;
class CompetitionCommon extends Component{
constructor(props) {
super(props)
this.state={
data: undefined,
bannerdata: undefined,
module_type: undefined,
mdContentdata: undefined,
chart_rules: undefined,
Competitionedittype: false,
chartdata: undefined,
has_url: false,
signupdata: undefined
}
}
componentDidMount(){
window.document.title = '竞赛';
if(this.props.match.params.identifier!=null){
this.getbannerdata();
// this.setState({
// thiskeys:this.props.location.search.replace('?menu=', '')
// })
// let url=`/competitions/${this.props.match.params.identifier}.json`;
// axios.get(url).then((response) => {
// if(response.status===200){
// this.setState({
// bannerdata:response.data
// })
// }
// }).catch((error) => {
// //console.log(error)
// })
}
}
componentDidUpdate = (prevProps) => {
if (prevProps.user != this.props.user) {
//console.log("componentDidUpdatess");
//console.log(this.props.user);
if (this.props.user && this.props.user.login != "") {
const zul = `/competitions/${this.props.match.params.identifier}/competition_staff.json`;
axios.get((zul)).then((result) => {
if (result) {
if (result.data) {
this.setState({
signupdata: result.data
})
}
}
}).catch((error) => {
////console.log(error);
})
}
}
}
//获取头部信息
getbannerdata=()=>{
// let menuid=this.props.location.search.replace('?menu=', '');
let query=this.props.location&&this.props.location.search;
const types = query.split('&')
const menuid = types[0].split('?menu=')
let url=`/competitions/${this.props.match.params.identifier}/common_header.json`;
axios.get(url).then((response) => {
if(response.status===200){
this.setState({
data: response.data,
thiskeys: menuid[1] === undefined || menuid[1] === "" ? response.data.competition_modules[0].id : menuid[1],
mode: response.data.mode
})
if(menuid[1]===undefined||menuid[1]===""){
this.getrightdata(
response.data.competition_modules[0].id,
response.data.competition_modules[0].module_type,
response.data.competition_modules[0].module_url,
response.data.competition_modules[0].has_url
)
}else{
let newlist=response.data.competition_modules;
newlist.map((item,key)=>{
if(`${item.id}`===`${menuid[1]}`){
this.getrightdata(
item.id,
item.module_type,
item.module_url,
item.has_url
)
}
})
}
}
}).catch((error) => {
//console.log(error)
})
//this.props.user 有可能为空
if (this.props.user && this.props.user.login != "") {
const zul = `/competitions/${this.props.match.params.identifier}/competition_staff.json`;
axios.get((zul)).then((result) => {
if (result) {
if (result.data) {
this.setState({
signupdata: result.data
})
}
}
}).catch((error) => {
////console.log(error);
})
}
}
getrightdatas=(e)=>{
let keys = parseInt(e.key);
this.getlistdata(keys)
this.props.history.replace(`?menu=${keys}`);
}
getlistdata=(keys,listkey)=>{
let{data}=this.state;
this.setState({
thiskeys:keys
})
data&&data.competition_modules.map((item,key)=>{
if(item.module_type!="enroll") {
if (keys === item.id) {
this.getrightdata(item.id, item.module_type, item.module_url, item.has_url, listkey)
return
}
}
})
}
getnewchartdata=(typeid,tabkey)=>{
if(typeid==="chart"){
let url=`/competitions/${this.props.match.params.identifier}/chart_rules.json`;
axios.get(url)
.then((response) => {
if(response.status===200){
this.setState({
chart_rules:response.data,
tabkey: tabkey === undefined ? response.data.stages[0].id === null ? "0" : `${response.data.stages[0].id}` : tabkey
})
}
}).catch((error) => {
//console.log(error)
})
}
}
getrightdata=(id,typeid,module_url,has_url,listkey)=>{
// if(typeid==="enroll"){
// this.props.history.replace(`/competitions/${this.props.match.params.identifier}/enroll`);
// return
// }
this.getnewchartdata(typeid, listkey)
if(has_url===false){
let url=`${module_url}`;
axios.get(url).then((response) => {
if(response.status===200){
if(typeid==="chart"){
this.setState({
chartdata:response.data
})
}else{
this.setState({
mdContentdata:response.data
})
}
}
}).catch((error) => {
//console.log(error)
})
}else{
if (module_url.substring(0, 7) == 'http://' || module_url.substring(0, 8) == 'https://') {
// window.location.href= module_url
window.open(module_url)
}else{
window.open(`https://${module_url}`)
// window.location.href=;
}
return
}
this.setState({
module_id:id,
module_type:typeid,
has_url:has_url
})
}
Competitionedit=()=>{
this.setState({
Competitionedittype: true
})
}
hideCompetitionedit=()=>{
this.setState({
Competitionedittype:false
})
}
newgotocourse=(url)=>{
if(this.props.checkIfLogin()===false){
this.props.showLoginDialog()
return
}
if(this.props.checkIfProfileCompleted()===false){
this.props.showProfileCompleteDialog()
return
}
window.open(url);
}
gotocourse=(url)=>{
if(this.props.checkIfLogin()===false){
this.props.showLoginDialog()
return
}
if(this.props.checkIfProfileCompleted()===false){
this.props.showProfileCompleteDialog()
return
}
if(url===undefined){
let {data,signupdata}=this.state;
// if(signupdata.enrolled===true){
// this.props.history.replace(`/courses/${data.course_id}`);
// }else{ }
if (data.member_of_course === true) {
// this.props.history.replace(`/courses/${data.course_id}`);
window.open(`/courses/${data.course_id}`)
} else {
// 以学生身份调用加入课堂 进入课堂首页
let url = "/courses/apply_to_join_course.json"
axios.post(url, {
invite_code: data.invite_code,
student: 1
}
).then((response) => {
if (response.data.status === 0) {
// this.props.history.replace();
this.getbannerdata()
window.open(`/courses/${data.course_id}`);
}
})
}
}else{
if (url === "personal") {
let urls = `/competitions/${this.props.match.params.identifier}/enroll`;
this.Personalregistration(urls)
} else {
window.open(url);
}
}
}
Personalregistration = (urls) => {
let {signupdata} = this.state;
if (signupdata.enroll_ended === true) {
//已截止
this.props.showNotification(`报名已截止`);
return;
}
if (signupdata.enrolled === true) {
this.props.showNotification(`你已经报名,不能重复报名!`);
return;
}
const url = `/competitions/${this.props.match.params.identifier}/competition_teams.json`;
axios.post(url).then((response) => {
if (response) {
if (response.data) {
this.props.showNotification(`报名成功,预祝您夺得桂冠!`);
// this.props.history.replace(urls);
this.getbannerdata()
window.open(urls)
}
}
}).catch((error) => {
});
}
Competitioncallback=(key)=>{
this.setState({
tabkey:key
})
let url=`/competitions/${this.props.match.params.identifier}/charts.json`;
axios.get(url,{params:{
stage_id:key===0||key===null?undefined:key
}}).then((response) => {
if(response.status===200){
this.setState({
chartdata:response.data
})
}
}).catch((error) => {
//console.log(error)
})
}
render() {
let {data, module_type, Competitionedittype, signupdata} = this.state;
return (
data===undefined?"":<div className={"educontent clearfix mt20 "}>
<Breadcrumb separator=">">
<Breadcrumb.Item><Link to={"/competitions"}>在线竞赛</Link></Breadcrumb.Item>
<Breadcrumb.Item>{data && data.name}</Breadcrumb.Item>
</Breadcrumb>
<div className={"mt10 relative"}>
<Row className={"CompetitionCommonbanner"}>
{data.competition_status === "nearly_published" ?
data && data.permission.editable === true ? "" :
<div className={"CompetitionsListzhezhao"}>即将发布 敬请期待</div> : ""}
<img className={"Commonimg"}
src={data.competition_status === "ended" ? getImageUrl(`images/educoder/competitions/groups1.png`) : data.competition_status === "nearly_published" ? getImageUrl(`images/educoder/competitions/groups2.png`) : data.competition_status === "progressing" ? getImageUrl(`images/educoder/competitions/groups3.png`) : ""}/>
<Col span={15} className={"Commonimgbox"}>
<img className={"image_urlbox"}
src={data === undefined ? getImageUrl(`images/educoder/competitions/mainbanner.jpg`) : data.avatar_url === null ? getImageUrl(`images/educoder/competitions/mainbanner.jpg`) : getImageUrl(data.avatar_url)}/>
</Col>
<Col className={"CompetitionCommonbannerfont"} span={9}>
<Col
className={data && data.name.length > 15 ? "competitionbannerdiv mt30" : "competitionbannerdiv mt30"}>{data && data.name}</Col>
<Col className={"competitionbannerdiv mt10"}>
<Col className={"Competitioncolor9b"}>竞赛时间<span
className={"color000"}>{data && data.start_time}{data && data.end_time}</span></Col>
{/*<Col></Col>*/}
</Col>
<style>
{
`
.ant-col-6{
width: 30%;
}
`
}
</style>
<Col className={"competitionbannerdiv mt10"}>
<Row gutter={16}>
<Col className="gutter-row" span={6}>
<div className="gutter-box CompetitionsIndexdadels Competitioncolor77">奖金</div>
</Col>
<Col className="gutter-row ml20 rankbeicenter" span={6}>
<div className="gutter-box CompetitionsIndexdadels Competitioncolor77">浏览数</div>
</Col>
<Col className="gutter-row rankbeicenter" span={6}>
<div className="gutter-box CompetitionsIndexdadels Competitioncolor77">报名数</div>
</Col>
</Row>
<Row gutter={16}>
<Col className="gutter-row" span={6}>
<div
className="gutter-box CompetitionsIndexbottomvalue Competitioncolor516">¥{data && data.bonus}</div>
</Col>
<Col className="gutter-row ml20 rankbeicenter" span={6}>
<div
className="gutter-box CompetitionsIndexbottomvalue Competitioncolor516">{data.competition_status === "nearly_published" ? "--" : data && data.visits_count}</div>
</Col>
<Col className="gutter-row rankbeicenter" span={6}>
{data.competition_status === "ended" ?
<div className={data.mode === 2 ?data.member_of_course==true?"gutter-box CompetitionsIndexbottomvalue Competitioncolor516 cursorpointer":"gutter-box CompetitionsIndexbottomvalue Competitioncolor516":"gutter-box CompetitionsIndexbottomvalue Competitioncolor516 cursorpointer"}
// 已结束onClick={data.competition_status === "nearly_published" ? "" : () => this.gotocourse(`/competitions/${this.props.match.params.identifier}/enroll`)}
onClick={ data.mode === 2 ?data.member_of_course==true?() => this.newgotocourse(`/courses/${data.course_id}`):"":() => this.newgotocourse(`/competitions/${this.props.match.params.identifier}/enroll`)}
>{data && data.member_count}</div>
:data.competition_status === "nearly_published" ?
<div className="gutter-box CompetitionsIndexbottomvalue Competitioncolor516"
// onClick={data.competition_status === "nearly_published" ? "" : () => this.gotocourse(`/competitions/${this.props.match.params.identifier}/enroll`)}
>{"--"}</div> :
data.competition_status === "progressing" ?
data.mode === 2 ?
<div className="gutter-box CompetitionsIndexbottomvalue Competitioncolor516 cursorpointer" onClick={() => this.gotocourse()}>{data && data.member_count}</div> : signupdata && signupdata.personal === true ?
<div className="gutter-box CompetitionsIndexbottomvalue Competitioncolor516 cursorpointer" onClick={() => this.gotocourse("personal")}>{data && data.member_count}</div> : <div
className="gutter-box CompetitionsIndexbottomvalue Competitioncolor516 cursorpointer"
onClick={() => this.gotocourse(`/competitions/${this.props.match.params.identifier}/enroll`)}>{data && data.member_count}</div>
:""}
</Col>
</Row>
</Col>
{data.competition_status === "ended" ? <style>
{
`
.Competitionfontsize22{
height: 50px;
border-radius: 4px;
}
`
}
</style> : <style>
{
`
.Competitionfontsize22{
height: 50px;
background: rgba(76,172,255,1);
border-radius: 4px;
}
`
}
</style>}
<Col className={"competitionbannerdiv mt20"}>
{data.competition_status === "ended" ?
<Button type="primary" block className={"Competitionfontsize22"} disabled={true}>
已结束
</Button> : data.enroll_end === true ?
<Button type="primary" block className={"Competitionfontsize22"} disabled={true}>
{data.competition_status === "nearly_published" ? "未发布" : "报名截止"}
</Button> :
data.competition_status === "progressing" ?
<Button type="primary" block className={"Competitionfontsize22"}>
{data.mode === 2 ?
<a onClick={() => this.gotocourse()}>立即报名</a> : signupdata && signupdata.personal === true ?
<a onClick={() => this.gotocourse("personal")}>立即报名</a> : <a
onClick={() => this.gotocourse(`/competitions/${this.props.match.params.identifier}/enroll`)}>立即报名</a>}
</Button>:""}
</Col>
<Col
className={"mt10 Competitionfontsize16"}>{data && data.enroll_end_time === null ? "" : `报名截止时间:${data && data.enroll_end_time}`}</Col>
</Col>
</Row>
</div>
<style>
{
`
.CompetitionMenu .ant-menu-item a{
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap
}
`
}
</style>
<Layout className={'teamsLayout mt40'}>
<Sider>
<Menu mode="inline" className="CompetitionMenu" selectedKeys={[`${this.state.thiskeys}`]}
onClick={(e) => this.getrightdatas(e)}>
{data && data.competition_modules.map((item, key) => {
if (item.module_type != "enroll") {
return (
<Menu.Item key={item.id}>
{/*{item.has_url===false?<span*/}
{/*// onClick={()=>this.getrightdata(item.id,item.module_type,item.module_url,item.has_url)}*/}
{/*>{item.name}</span>:<a*/}
{/*// target="_blank"*/}
{/*href={item.module_url}*/}
{/*// onClick={()=>this.getrightdata(item.id,item.module_type)}*/}
{/*>{item.name}</a>}*/}
<a title={item.name}>{item.name}</a>
</Menu.Item>
)
}
})}
</Menu>
{/*<Menu mode="inline" className="CompetitionMenu" onClick={(e)=>this.isdownloadpdf(e)}>*/}
{/*<Menu.Item>*/}
{/*<a>证书下载</a>*/}
{/*</Menu.Item>*/}
{/*</Menu>*/}
</Sider>
{module_type === "certificate" ? "" : <Layout className={"teamsLayoutleft CompetitionContents mb30"}>
{this.state.module_type === "chart" ? Competitionedittype === false ? <CompetitionContentsChart
{...this.props}
{...this.state}
Competitionedit={() => this.Competitionedit()}
Competitioncallback={(e) => this.Competitioncallback(e)}
/> : "" : Competitionedittype === false ? <CompetitionContents
Competitionedit={() => this.Competitionedit()}
{...this.props}
{...this.state}
/> : ""}
{/*<CompetitionContentsChart*/}
{/*{...this.props}*/}
{/*{...this.state}*/}
{/*/>*/}
{Competitionedittype === true ? <CompetitionContentsMd
hideCompetitionedit={() => this.hideCompetitionedit()}
getlistdata={(keys, listkey) => this.getlistdata(keys, listkey)}
Competitioncallback={(e) => this.Competitioncallback(e)}
{...this.props}
{...this.state}
/> : ""}
</Layout>}
{module_type === "certificate" ? <Layout className={"teamsLayoutleft CompetitionContents mb30"}>
<CompetitionContentspdf
{...this.props}
{...this.state}
/>
</Layout> : ""}
</Layout>
</div>
)
}
}
export default CompetitionCommon;

@ -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,423 @@
import React, { Component } from 'react';
import {Button,Layout,Tabs,Icon, Card, Avatar, Row, Col ,Table,Badge} from 'antd';
import {markdownToHTML,getImageUrl} from 'educoder';
import axios from 'axios';
const { Content } = Layout;
const { TabPane } = Tabs;
const { Meta } = Card;
class CompetitionContents extends Component{
constructor(props) {
super(props)
this.state={
personal:undefined
}
}
componentDidMount(){
window.document.title = '竞赛';
let url=`/competitions/${this.props.match.params.identifier}/competition_staff.json`;
axios.get(url)
.then((response) => {
if(response.status===200){
console.log(response)
this.setState({
personal:response.data.personal
})
}
}).catch((error) => {
console.log(error)
})
this.props.MdifHasAnchorJustScorll();
}
derivefun=(url)=>{
axios.get(url).then((response)=>{
if(response === undefined){
return
}
if(response.data.status&&response.data.status===-1){
this.props.showNotification(response.data.message);
}else if(response.data.status&&response.data.status===-2){
// if(response.data.message === "100"){
// // 已超出文件导出的上限数量100 ),建议:
//
// this.setState({
// DownloadType:true,
// DownloadMessageval:100
// })
// }else {
// //因附件资料超过500M
// this.setState({
// DownloadType:true,
// DownloadMessageval:500
// })
// }
this.props.showNotification(response.data.message);
}else {
// this.props.showNotification(`正在下载中`);
// window.open("/api"+url, '_blank');
this.props.slowDownload(url);
}
}).catch((error) => {
console.log(error)
});
}
render() {
this.props.MdifHasAnchorJustScorll();
const operations = <div>
<Button className={"fr"} type="primary" ghost onClick={()=>this.props.Competitionedit()}>编辑</Button>
<Button className={"fr mr20"} type="primary" ghost>
<a onClick={()=>this.derivefun(`/competitions/${this.props.match.params.identifier}/charts.xlsx`)}>导出</a>
</Button>
</div>
const columns = [
{
title: 'usersum',
dataIndex: 'usersum',
key: 'name',
render: text => <span className={"color-blue"}>{text}</span>,
},
{
title: 'userimg',
dataIndex: 'userimg',
key: 'userimg',
render: (text, record) =>(
<a href={`/users/${record.user_login}`} target={"_blank"} className="color-dark">
<div className={"relativef"}>
<img className={"Competitionuserimg mb10"} src={getImageUrl(`images/${record.userimg===null?`avatars/User/0?1442652658`:record.userimg}`)}/>
<div className={"competimgabsoluteijmg"}><Badge count={record.competition_prize} style={{ backgroundColor: '#459BE5' }} title={record.competition_prize}/></div>
</div>
</a>),
},
{
title: 'username',
dataIndex: 'username',
key: 'username',
render: text => <span title={text} title={text}>{text}</span>,
},
{
title: 'school',
dataIndex: 'school',
key: 'school',
render: text => <span title={text} title={text}>{text}</span>,
},
{
title: 'spendtime',
dataIndex: 'spendtime',
key: 'spendtime',
render: text => <span>{text}</span>,
},
{
title: 'score',
dataIndex: 'score',
key: 'score',
render: text => <span className={"color-blue"}>{text}</span>,
},
];
const datas = [];
let {chart_rules, chartdata, data} = this.props;
let {personal}=this.state;
if(this.props&&this.props.mode!=1){
columns.some((item,key)=> {
if (item.title === "spendtime") {
columns.splice(key, 1)
return true
}
}
)
}
{chartdata===undefined?"":chartdata.teams.length===0?"":chartdata.teams.map((item,key)=>{
let list={
usersum:key+1,
userimg:item.user_image,
username:personal===undefined||personal===null?item.record_user_name:personal===true?item.record_user_name:item.team_name,
school:item.school_name,
spendtime:item.spend_time,
score:item.score<50?"< 50 分":item.score,
user_login:item.user_login,
competition_prize:item.competition_prize
}
datas.push(list)
})}
// console.log(this.props&&this.props.mode)
// console.log(columns)
return (
<div>
<style>
{
`
.ant-tabs-nav .ant-tabs-tab{
font-size: 20px;
margin: 0 10px 0 0;
}
.ant-badge{
width:90px;
}
.ant-badge sup{
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
`
}
</style>
{chart_rules === undefined ? "" :
<Tabs onChange={(e) => this.props.Competitioncallback(e)} activeKey={this.props.tabkey}
tabBarExtraContent={data && data.permission.editable === true ? this.props.Competitionedittype === false ? this.props.has_url === false ? operations : "" : "" : ""}>
{chart_rules.stages.map((item,key)=>{
return(
<TabPane tab={item.name} key={item.id===null?0:item.id}>
{chart_rules.rule_contents.map((items,keys)=>{
if(item.id===items.competition_stage_id){
return(
<Content key={keys} className={"markdown-body "} dangerouslySetInnerHTML={{__html: markdownToHTML(items.content===undefined||items.content===null?"":items.content).replace(/▁/g, "▁▁▁")}}></Content>
)
}else if(item.id===null&&items.competition_stage_id===0){
return(
<Content key={keys} className={"markdown-body "} dangerouslySetInnerHTML={{__html: markdownToHTML(items.content===undefined||items.content===null?"":items.content).replace(/▁/g, "▁▁▁")}}></Content>
)
}
})}
</TabPane>
)
})}
</Tabs>}
<Col className="gutter-row Competitioncharts mt30 mb30">总排名</Col>
{chartdata===undefined?"":chartdata.teams.length===0?<Col className="gutter-row Competitioncharts mt30 mb30 rankbeicenter">
<img src={getImageUrl(`images/educoder/competitions/trophy.png`)}/>
<div className={"rankbei mt10"}>要抓住一切机会向所有人证明你自己证明你能够迎接荣耀</div>
</Col>:""}
<Row calssName={"Competition399"}>
{chartdata===undefined?"":chartdata.teams.length===0?"":chartdata.teams.map((item,key)=>{
if(key===1){
return(
<Col className="mt40" xs={{ span: 5, offset: 1 }} lg={{ span: 6, offset: 2 }}>
<Card
className={"Competitionthird"}
cover={
<div className={"Competitionthirdbox center"}>
<li className="pr Competitioncenter">
<img src={getImageUrl("images/educoder/huangguan-two.png")}/>
<div className={"mt10"}>
<a href={`/users/${item.user_login}`} target={"_blank"} className="color-dark ">
<div className={"relativef"}>
<img className={"rankingimg"} src={getImageUrl(`images/${item.user_image===null?`avatars/User/0?1442652658`:item.user_image}`)} />
<div className={"competimgabsolute"}><Badge count={item.competition_prize} style={{ backgroundColor: '#459BE5' }} title={item.competition_prize}/></div>
</div>
<p className="task-hide rankName mt5 jinshaifont">{personal===undefined||personal===null?item.record_user_name:personal===true?item.record_user_name:item.team_name}</p>
</a>
</div>
</li>
</div>
}
>
<Meta
title={<div className={"center"}>
<div className={"rankfonttop"} title={item.school_name}>{item.school_name}</div>
{this.props&&this.props.mode===1?<div className={"rankfontmid"}>{item.spend_time}</div>:""}
</div>}
description={
<div className={"rankfontbottom"}>{item.score<50?"< 50 分":item.score}</div>
}
/>
</Card>
</Col>
)
}
})}
{chartdata===undefined?"":chartdata.teams.length===0?"":chartdata.teams.map((item,key)=>{
if(key===0){
return(
<Col xs={{ span: 11, offset: 1 }} lg={{ span: 6, offset: 1 }}>
<Card
className={"Competitionfirst"}
cover={
<div className={"Competitionfirstbox center"}>
<li className="pr Competitioncenter">
<img src={getImageUrl("images/educoder/huangguan.png")}/>
<div className={"mt10"}>
<a href={`/users/${item.user_login}`} target={"_blank"} className="color-dark">
<div className={"relativef"}>
<img className={"rankingimg"} src={getImageUrl(`images/${item.user_image===null?`avatars/User/0?1442652658`:item.user_image}`)} />
<div className={"competimgabsolute"}><Badge count={item.competition_prize} style={{ backgroundColor: '#459BE5' }} title={item.competition_prize}/></div>
</div>
<p className="task-hide rankName mt5 jinshaifont">{personal===undefined||personal===null?item.record_user_name:personal===true?item.record_user_name:item.team_name}</p>
</a>
</div>
</li>
</div>
}
>
<Meta
title={<div className={"center"}>
<div className={"rankfonttop"} title={item.school_name}>{item.school_name}</div>
{this.props&&this.props.mode===1?<div className={"rankfontmid"}>{item.spend_time}</div>:""}
</div>}
description={
<div className={"rankfontbottoms"}>{item.score<50?"< 50 分":item.score}</div>
}
/>
</Card>
</Col>
)
}
})}
{chartdata===undefined?"":chartdata.teams.length===0?"":chartdata.teams.map((item,key)=>{
if(key===2){
return(
<Col className="mt30" xs={{ span: 5, offset: 1 }} lg={{ span: 6, offset: 1 }}>
<Card
className={"Competitionsecondary "}
cover={
<div className={"Competitionsecondarybox center"}>
<li className=" pr Competitioncenter ">
<img src={getImageUrl("images/educoder/huangguan-three.png")}/>
<div className={"mt10"}>
<a href={`/users/${item.user_login}`} target={"_blank"} className="color-dark">
<div className={"relativef"}>
<img className={"rankingimg"} src={getImageUrl(`images/${item.user_image===null?`avatars/User/0?1442652658`:item.user_image}`)} />
<div className={"competimgabsolute"}><Badge count={item.competition_prize} style={{ backgroundColor: '#459BE5' }} title={item.competition_prize}/></div>
</div>
<p className="task-hide rankName mt5 jinshaifont">{personal===undefined||personal===null?item.record_user_name:personal===true?item.record_user_name:item.team_name}</p>
</a>
</div>
</li>
</div>
}
>
<Meta
title={<div className={"center"}>
<div className={"rankfonttop"} title={item.school_name}>{item.school_name}</div>
{this.props&&this.props.mode===1?<div className={"rankfontmid"}>{item.spend_time}</div>:""}
</div>}
description={
<div className={"rankfontbottom"}>{item.score<50?"< 50 分":item.score}</div>
}
/>
</Card>
</Col>
)
}
})}
</Row>
{chartdata===undefined?"":chartdata.user_ranks.length===0?"":<div className={"youranklist mt50"}>
<style>
{
`
.ant-col-9 {
width: 35.5%;
}
.col13{
width:13%;
text-align:left;
}
.col24{
width: 24%;
text-align: left;
padding-left: 33px;
}
.col15{
width: 14%;
text-align: right;
}
.col12{
width: 12%;
text-align: right;
}
.col10{
width: 10%;
}
.col6{
width: 6%;
}
`
}
</style>
{chartdata.user_ranks.map((item,key)=>{
return(
<Row type="flex" key={key}>
<Col span={4} order={1} className={"col13"}>
<span className={"ranknames"}>您当前排名:{item.rank}</span>
</Col>
<Col className="userranksclass" span={3} order={2} className={"col24"}>
{personal===undefined||personal===null?item.record_user_name:personal===true?item.user_name:item.team_name}
</Col>
<Col className="textleft" span={9} order={3}>
{/*{item.team_name}*/}
</Col>
{item.cost_time=== "--"?<Col span={3} order={4} className={"col6"}>
{this.props&&this.props.mode===1?item.cost_time:""}
</Col>:<Col span={3} order={4} className={"col10"}>
{this.props&&this.props.mode===1?item.cost_time:""}
</Col>}
{item.cost_time=== "--"?<Col className="textright" span={3} order={5} className={"col15"}>
<span className={"ranknameslast"}>{item.score<50?"< 50 分":item.score}</span>
</Col>:<Col className="textright" span={3} order={5} className={"col12"}>
<span className={"ranknameslast"}>{item.score<50?"< 50 分":item.score}</span>
</Col>}
</Row>
)
})}
</div>}
<Row className={"mt20 mb80"}>
<style>
{
`
.ant-table-tbody > tr > td:nth-last-child(4){
overflow: hidden;
max-width: 100px;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
white-space: nowrap;
cursor: default;
}
.ant-table-tbody > tr > td:nth-last-child(3){
overflow: hidden;
max-width: 200px;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
white-space: nowrap;
cursor: default;
}
.ant-table-tbody>tr>td, .ant-table-thead>tr>th {
padding: 5px;
}
`
}
</style>
{chartdata === undefined ? "" : chartdata.teams.length === 0 ? "" :
<Table className="Competitiontransparent" columns={columns} dataSource={datas} showHeader={false}
pagination={false}/>}
</Row>
</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,292 @@
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 Bankcardnumberverification 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)
try {
this.props.triggerRef(this);
}catch (e) {
}
console.log(this.props.bank_account);
//初始化值
if (this.props.bank_account) {
this.props.form.setFieldsValue({
openingbank: this.props.bank_account.bank,
subbranch: this.props.bank_account.second_bank,
subbranchs: this.props.bank_account.card_no,
})
this.setState({
openingbank: this.props.bank_account.bank,
subbranch: this.props.bank_account.second_bank,
subbranchs: this.props.bank_account.card_no,
})
}
}
setdata(bank,second_bank,card_no){
this.props.form.setFieldsValue({
openingbank: bank,
subbranch: second_bank,
subbranchs:card_no,
})
}
componentDidUpdate = (prevProps) => {
if (prevProps.bank_account != this.props.bank_account) {
console.log("componentDidUpdate");
console.log(this.props);//是新数据
console.log(prevProps);//是老数据
////console.log("Registration");
////console.log("componentDidUpdate");
////console.log(this.props.user.admin);
try {
if(this.props.bank_account){
this.props.form.setFieldsValue({
openingbank: this.props.bank_account.bank,
subbranch: this.props.bank_account.second_bank,
subbranchs: this.props.bank_account.card_no,
})
}
this.setState({
openingbank: this.props.bank_account.bank,
subbranch: this.props.bank_account.second_bank,
subbranchs: this.props.bank_account.card_no,
})
}catch (e) {
}
}
}
yhBankstrue = () => {
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
let url = `/competitions/${this.props.match.params.identifier}/prize_leader_account.json`;
axios.patch(url, {
user_id:this.props.userdata.id,
bank: values.openingbank,
second_bank: values.subbranch ,
card_no: values.subbranchs
})
.then((result) => {
try {
if (result.data.status === 0) {
try {
// console.log(values.openingbank);
// console.log(values.subbranch);
// console.log(values.subbranchs);
this.props.form.setFieldsValue({
openingbank: values.openingbank,
subbranch: values.subbranch,
subbranchs: values.subbranchs,
});
this.setState({
openingbank: values.openingbank,
subbranch: values.subbranch,
subbranchs: values.subbranchs,
})
}catch (e) {
}
try {
this.props.showNotification(`提交成功,等待审核!`);
}catch (e) {
}
try {
this.props.getdata(this.props.userdata.id);
}catch (e) {
}
try {
this.props.GetawardinformationAPI();
}catch (e) {
}
}
} catch (e) {
}
}).catch((error) => {
console.log(error);
})
}
})
}
render() {
const {getFieldDecorator} = this.props.form;
const {updating, seconds, secondsFlag, basicInfo, phonebool, emailbool, certification, formationdata, bank_account_editable, leader, bank_account} = this.state
return (
<div>
<div className="flexdirections mt36">
<p className="fontcolorsyslhei font-16">//开户行及银行卡号</p>
<p className="fontcolorsyslhui font-14 ml11">为保障奖金的及时发放请队长如实填写你名下的银行卡信息</p>
</div>
<div className="ml38">
<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;
}
.settingFormsy label{
color: #666666;
font-size: 14px !important ;
}
.settingFormsy input {
width: 340px;
height: 40px;
}
.settingFormsy input.validateInput {
width: 220px;
}
.settingFormsy .formItemInline button {
width: 110px;
margin-left: 10px;
}
.settingFormsy .ant-form-item-label {
width: 60px;
text-align: left;
}
.formItemInline .ant-form-explain{
position:absolute;
bottom:-22px;
left:0px;
width:100%;
}
.yslzxueshi .ant-input{
height: 40px !important;
width: 276px !important
}
// class="ant-col ant-form-item-label"
`}</style>
<div className="settingFormsy">
<React.Fragment>
<Form>
<Form.Item
label="开户行:"
className="formItemInline hideRequireTag mb20 mt20"
>
{getFieldDecorator('openingbank', {
rules: [{
initialValue:this.state.openingbank,
required: true,
message: '请输入开户行',
}],
})(
<Input placeholder={`例如:中国工商银行`}></Input>
)}
</Form.Item>
<Form.Item
label="支行:"
className="formItemInline hideRequireTag mb20 mt20"
>
{getFieldDecorator('subbranch', {
rules: [{
initialValue: this.state.subbranch,
required: true,
message: '请输入支行',
}],
})(
<Input placeholder={`例如:长沙天河支行`}></Input>
)}
</Form.Item>
<Form.Item
label="账号:"
className="formItemInline hideRequireTag mb20 mt20"
>
{getFieldDecorator('subbranchs', {
rules: [{
initialValue: this.state.subbranchs,
required: true,
message: '请输入账号',
}],
})(
<Input placeholder={`请填写银行卡账号`}></Input>
)}
</Form.Item>
<div className="flexdirections yslzxueshi ml38 mt34">
<p className="fontcolorsyslhui1 font-14 w300 myysllineheight myyslminwidth"></p>
<div className="myyslminwidth276 flexdirections">
{/*<div className="buttongo mycompitcursor" onClick={()=>this.yhBanksfalse()}><p className="fontwenzi mycompitcursor" >取消</p></div>*/}
<Button type="primary" onClick={() => this.yhBankstrue()}>确定</Button>
</div>
</div>
</Form>
</React.Fragment>
</div>
</div>
</div>
)
}
}
const Bankcardnumberverifications = Form.create({name: 'Bankcardnumberverification'})(Bankcardnumberverification);
export default Bankcardnumberverifications;

@ -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,215 @@
import React, { Component } from 'react';
import {Button,Layout,Row, Col,Divider,Table} from 'antd';
import axios from 'axios';
import {getImageUrl} from 'educoder';
import './CompetitionContentspdfdownload.css';
// import NoneData from "../../../courses/shixunHomework/shixunHomework";
class CompetitionContentspdfdownload extends Component{
constructor(props) {
super(props)
this.state={
data:undefined,
teams:undefined
}
}
componentDidMount(){
window.document.title = '竞赛';
let url=`/competitions/${this.props.match.params.identifier}/prize.json`;
let query=this.props.location&&this.props.location.search;
const types = query.split('user_id=')
let userid;
if(types[1]===undefined){
userid=this.props.user&&this.props.user.user_id;
}else{
userid=types[1];
}
axios.get(url,{params:{
user_id:userid,
}
}).then((response) => {
if(response.status===200){
let datas=response.data.teams;
if(datas.length>0){
datas.map((item,key)=>{
let lista=item.team_members;
if(lista.length>0){
console.log(lista)
lista.map((i,k)=>{
i["bank_account"]=item.bank_account;
})
}
})
}
this.setState({
data:response.data,
teams:datas,
})
}
}).catch((error) => {
console.log(error)
})
}
render() {
let {data,teams}=this.state;
const columns = [
{
title: '角色',
dataIndex: 'type',
key: 'type',
render: (text, record) => (
<span>
{record.role}
</span>
),
},
{
title: '姓名',
dataIndex: 'name',
key: 'name',
render: (text, record) => (
<span>
{record.name}
</span>
),
},
{
title: '实名认证',
dataIndex: 'namecertify',
key: 'namecertify',
render: (text, record) => (
<span>
{record.real_name_auth==="authed"?<span className={"pdfdownloadfont00CC5F"}>已认证</span>:record.real_name_auth==="authing"?<span className={"pdfdownloadfontFF6602"}></span>:record.real_name_auth==="not_authed"?<span className={"color-red"}></span>:""}
</span>
),
},
{
title: '职业认证',
key: 'certify',
dataIndex: 'certify',
render: (text, record) => (
<span>
{record.professional_auth==="authed"?<span className={"pdfdownloadfont00CC5F"}>已认证</span>:record.professional_auth==="authing"?<span className={"pdfdownloadfontFF6602"}></span>:record.professional_auth==="not_authed"?<span className={"color-red"}></span>:""}
</span>
),
},
{
title: '手机绑定',
key: 'mobile',
dataIndex: 'mobile',
render: (text, record) => (
<span>
{record.phone_binded===true?<span className={"pdfdownloadfont00CC5F"}>已绑定</span>:<span className={"color-red"}></span>}
</span>
),
},
{
title: '邮箱绑定',
key: 'mail',
dataIndex: 'mail',
render: (text, record) => (
<span>
{record.email_binded===true?<span className={"pdfdownloadfont00CC5F"}>已绑定</span>:<span className={"color-red"}></span>}
</span>
),
},
{
title: '开户行及银行卡号信息(队长填写)',
key: 'idcard',
dataIndex: 'idcard',
render: (value, record, index) => {
if (index === 0&&record.bank_account!=null) {
return {
children: <span>{record.bank_account.bank + record.bank_account.second_bank + record.bank_account.card_no}</span>,
};
}
},
},
];
let people=[ { url: '/api/competitions/xxxxx/certificates/1/personal' },
{ url: '/api/competitions/xxxxx/certificates/2/personal' },]
return (
<React.Fragment>
<Row>
<Col className={"pdfdownload"}>温馨提示填写的个人信息经审批后将提供个人获奖证书下载团队队员信息全部审批后将提供团队获奖证书下载</Col>
</Row>
<Row className={"mt30"}>
<Col className={"pdfpicture"}>证书情况</Col>
</Row>
<Row className={"mt30"}>
<Col>个人证书{data&&data.personal_certifications.length===0&&data&&data.all_certified===false?
<span><span className={"pdfpicture font-14"}>暂未生成</span> <span className={"ml20"}><span className={"pdfpicture font-14"}></span><a className={"pdfdownloadfont4CACFF"} onClick={()=>this.props.Competitioncallback("2")}></a></span></span>:
data&&data.personal_certifications.length===0&&data&&data.all_certified===true?
<span><span className={"pdfpicture font-14"}>暂未生成</span> <span className={"ml20"}><span className={"pdfpicture font-14"}></span></span></span>:
data&&data.personal_certifications.map((item,key)=>{
return(
<span className={"mr10"} key={key}>
<img src={ getImageUrl(`images/educoder/pdfs.png`)} />
<a className={"pdfdownloadfont4CACFF ml10"} href={item.url} download=""
title="下载" mce_href="#">立即下载</a>
</span>
)
})}</Col>
</Row>
<Row className={"mt30"}>
<Col>团队证书
{data&&data.team_certifications.length===0?<span className={"pdfpicture font-14"}>暂未生成</span>
:data&&data.team_certifications.map((item,key)=>{
return(
<span className={"mr10"} key={key}>
<img src={ getImageUrl(`images/educoder/pdfs.png`)} />
<a className={"pdfdownloadfont4CACFF ml10"} href={item.url} download=""
title="下载" mce_href="#">立即下载</a>
</span>
)
})}
</Col>
</Row>
<Divider />
<style>
{
`
.pdfdownloadTable .ant-table-column-title{
color:#000;
}
.pdfdownloadTable .ant-table-tbody > tr > td{
color:#666666;
border-bottom: 1px solid transparent;
}
.pdfdownloadTable .ant-table-thead > tr > th, .ant-table-tbody > tr > td{
text-align: center;
}
`
}
</style>
{teams&&teams.map((item,key)=>{
return(
<Row className={"mt30"} key={key}>
<Col className={"pdfpicture mb20"}>{item.name}战队信息填报概况</Col>
<Table columns={columns} dataSource={item.team_members} className={"pdfdownloadTable"} pagination={false}/>
</Row>
)
})
}
</React.Fragment>
)
}
}
export default CompetitionContentspdfdownload;

@ -0,0 +1,596 @@
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";
import Phonenumberverifications from './Phonenumberverification';
import Mailboxvalidations from './Mailboxvalidation'
import Bankcardnumberverifications from './Bankcardnumberverification'
export const identityMap = {"teacher": "教师", "student": "学生", "professional": "专业人士"}
class CompetitionContentspdfpeopledata 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,
userdata:undefined
}
}
componentDidMount() {
window.document.title = '竞赛';
console.log("获取用户信息");
console.log(this.props);
this.GetawardinformationAPI();
let query=this.props.location&&this.props.location.search;
const types = query.split('user_id=')
let userid;
if(types[1]===undefined){
userid=this.props.user&&this.props.user.user_id;
}else{
userid=types[1];
}
this.getdata(userid);
this.GetuseridApi(userid);
}
GetuseridApi=(id)=>{
//个人信息API 获取个人信息
let url = `/users/accounts/${id}.json`;
axios.get(url).then((result) => {
if (result.data) {
console.log("GetuseridApi");
console.log(result.data);
this.setState({
userdata:result.data
})
}
}).catch((error) => {
console.log(error);
})
}
GetawardinformationAPI = () => {
let url = `/competitions/${this.props.match.params.identifier}/prize.json`;
let query=this.props.location&&this.props.location.search;
const types = query.split('user_id=')
let userid;
if(types[1]===undefined){
userid=this.props.user&&this.props.user.user_id;
}else{
userid=types[1];
}
axios.get(url,{params:{
user_id:userid,
}
}).then((result) => {
if (result.data) {
this.setState({
formationdata: result.data.formationdata,
bank_account_editable: result.data.bank_account_editable, //队长是否可以编辑
leader: result.data.leader, //是否是队长
bank_account: result.data.bank_account, //队长银行卡号信息
})
}
}).catch((error) => {
console.log(error);
})
}
getdata = (id) => {
this.setState({
certification: 3
})
this.GetuseridApi(id);
};
// 绑定手机
onPhoneSubmit = () => {
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
let {id} =this.state.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.setState({
phonebool: false
})
this.getdata(this.state.userdata.id);
}
}).catch((error) => {
console.log(error);
})
} else {
this.props.showNotification("请输入有效的11位手机号码");
}
}
})
}
// 绑定邮箱
onEmailSubmit = () => {
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
let {id} =this.state.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.setState({
emailbool: false
})
this.getdata(this.state.userdata.id);
}
}).catch((error) => {
console.log(error);
})
} else {
this.props.showNotification("请输入正确的邮箱地址");
}
}
})
}
//取消编辑
hideUpdating = (i) => {
if (i === 1) {
this.setState({
phonebool: false
})
} else if (i === 2) {
this.setState({
emailbool: false
})
} 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
})
}
//立即认证
checkBasicInfo = (index) => {
if (this.state.userdata.base_info_completed == true) {
this.showRealNameCertificationModal(index)
} else {
try {
this.props.confirm({
okText: `立即完善`,
content: `请先完善基本信息`,
onOk: () => {
this.props.history.push('/account/profile/edit')
}
})
} catch (e) {
this.props.history.push(`/account/profile/edit`);
}
}
}
showRealNameCertificationModal = (index) => {
this.setState({
certification: index,
}, () => {
if (index == 1) {
this.realNameCertificationModal1.setVisible(true)
} else if (index == 2) {
this.realNameCertificationModal2.setVisible(true)
}
})
}
//绑定银行确认
yhBankstrue = () => {
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
let url = `/competitions/${this.props.match.params.identifier}/prize_leader_account.json`;
axios.patch(url, {
user_id:this.state.userdata.id,
bank: values.openingbank,
second_bank: values.subbranchs,
card_no: values.subbranch
})
.then((result) => {
try {
if (result.data.status == 0) {
// console.log(JSON.stringify(result));
this.props.showNotification(`提交成功`);
this.getdata(this.state.userdata.id);
this.GetawardinformationAPI();
}
} catch (e) {
}
}).catch((error) => {
console.log(error);
})
}
})
}
//取消认证弹框
onCancel = () => {
this.getdata(this.state.userdata.id);
}
bindRef = ref => { this.child = ref }
//撤销认证
Cancellationofapplication = (index) => {
let userid;
let query=this.props.location&&this.props.location.search;
const types = query.split('user_id=')
if(types[1]===undefined){
userid=this.props.user&&this.props.user.user_id;
}else{
userid=types[1];
}
let url = ""
if (index === 1) {
url = `/users/accounts/${userid}/authentication_apply.json`;
} else if (index === 2) {
url = `/users/accounts/${userid}/professional_auth_apply.json`;
}
axios.delete(url)
.then((response) => {
try {
if (response.data.status == 0) {
if (index === 1) {
this.props.showNotification('撤销实名认证成功')
} else if (index === 2) {
this.props.showNotification('撤销职业认证成功')
}
try {
this.getdata(this.state.userdata.id);
}catch (e) {
}
}
} catch (e) {
}
})
.catch(function (error) {
console.log(error);
});
}
render() {
const admins=this.props.user.admin;
if(admins===undefined||admins===null) {
admins === false;
}
const {updating, seconds, secondsFlag,userdata, basicInfo, phonebool, emailbool, certification, formationdata, bank_account_editable, leader, bank_account} = this.state
return (
<div className="flexdidirectionss mt17">
{this.state.certification === 1 &&userdata?
<RealNameCertificationModal {...this.props} {...this.state} basicInfo={userdata}
wrappedComponentRef={(form) => this.realNameCertificationModal1 = form}
certification={certification}
Getdata={(id) => this.getdata(id)}
onCancel={() => this.onCancel()}
></RealNameCertificationModal>
: ""}
{this.state.certification === 2 &&userdata ?
<RealNameCertificationModal {...this.props} {...this.state} basicInfo={userdata}
wrappedComponentRef={(form) => this.realNameCertificationModal2 = form}
certification={certification}
Getdata={(id) => this.getdata(id)}
onCancel={() => this.onCancel()}
></RealNameCertificationModal> : ""}
<div className="flexdirections ">
<p className="fontcolorsyslhei font-16 w98"><span className="fontcolorsysl font-16">*</span><span></span>
</p>
{userdata && userdata.authentication == "uncertified" ?
<p className="fontcolorsyslhui font-14 w200">通过实名认证后才能获得证书</p> : ""
}
{userdata && userdata.authentication == "uncertified" ?
<p className="fontcolorsysllan font-14 ml3 mycompitcursor"
onClick={() => this.checkBasicInfo(1)}>立即认证</p> : ""
}
</div>
<div className="flexdirections ml38 mt23">
<p className="fontcolorsyslhui1">姓名</p>
<p className="fontcolorsyslhei flexdirections">{userdata && userdata.name}
{
userdata && userdata.authentication == "uncertified" ? "" :
userdata && userdata.authentication == "applying" ?
<p className="flexdirections"><p className="iconfont icon-tishi colorgreenorg font-14 ml20"></p><p
className="colorgreenorg font-14 ml6">待审核</p><p
className="fontcolorsysllan borcolors font-12 w64 mycompitcursor"
onClick={() => this.Cancellationofapplication(1)}>撤销认证</p></p>
:
<p className="flexdirections">
<p className="iconfont icon-wancheng colorgreenlight font-14 ml20"></p><p
className="colorgreenlight font-14 ml6">已认证</p>
</p>
}
</p>
</div>
<div className="flexdirections ml38 mt19">
<p className="fontcolorsyslhui1">性别</p>
<p className="fontcolorsyslhei"> {userdata && userdata.gender == 0 ? "男" : "女"}</p>
</div>
<div className="flexdirections mt36 ">
<p className="fontcolorsyslhei font-16 w98"><span className="fontcolorsysl font-16">*</span><span></span>
</p>
{
userdata && userdata.professional_certification == "uncertified" ?
<p className="fontcolorsyslhui font-14 w200">通过职业认证后才能获得证书</p>
: ""}
{
userdata && userdata.professional_certification == "uncertified" ?
<p className="fontcolorsysllan font-14 ml3 mycompitcursor" onClick={() => this.checkBasicInfo(2)}>立即认证</p>
: ""}
</div>
<div className="flexdirections ml38 mt23">
<p className="fontcolorsyslhui1">职业</p>
<p
className="fontcolorsyslhei flexdirections">{userdata && userdata.identity && identityMap[userdata.identity]}
{
userdata && userdata.professional_certification == "uncertified" ?
"" :
userdata && userdata.professional_certification == "applying" ?
<p className="flexdirections">
<p className="iconfont icon-tishi colorgreenorg font-14 ml20"></p><p
className="colorgreenorg font-14 ml6">待审核</p><p
className="fontcolorsysllan borcolors font-12 w64 mycompitcursor ml6"
onClick={() => this.Cancellationofapplication(2)}>撤销认证</p>
</p>
:
<p className="flexdirections">
<p className="iconfont icon-wancheng colorgreenlight font-14 ml20"></p><p
className="colorgreenlight font-14 ml6">已认证</p>
<p className="fontcolorsysllan borcolors font-12 w64 mycompitcursor ml6"
onClick={() => this.checkBasicInfo(2)}>重新认证</p>
</p>
}
</p>
</div>
<div className="flexdirections ml38 mt19">
<p
className="fontcolorsyslhui1">{userdata && userdata.technical_title ? "职称:" : ""}{userdata && userdata.student_id ? "学号:" : ""}</p>
<p className="fontcolorsyslhei">{userdata && (userdata.technical_title || userdata.student_id)}</p>
</div>
<div className="flexdirections ml38 mt19">
<p className="fontcolorsyslhui1">学校</p>
<p className="fontcolorsyslhei">{userdata && userdata.school_name}</p>
</div>
<div className="flexdirections ml38 mt19">
<p className="fontcolorsyslhui1">院系</p>
<p className="fontcolorsyslhei"> {userdata && userdata.department_name}</p>
</div>
<div className="flexdirections mt36 ">
<p className="fontcolorsyslhei font-16 w98"><span className="fontcolorsysl font-16">*</span><span></span>
</p>
</div>
<div className="flexdirections ml38 mt23">
<p className="fontcolorsyslhui1 w60 ">手机号</p>
{
userdata && userdata.phone ?
<p className="fontcolorsyslhei w200 ">{userdata && userdata.phone}</p>
:
<p className="fontcolorsysljin w200 ">未绑定</p>
}
<p className="fontcolorsysllan mycompitcursor"
onClick={() => this.phonebools()}>{userdata && userdata.phone ? (phonebool === false ? "更换" : "") : (phonebool === false ? "立即绑定" : "")}</p>
</div>
{/*手机号绑定*/}
{
phonebool === true &&userdata ?
<Phonenumberverifications {...this.props} {...this.state} basicInfo={userdata}
hideUpdating={(i) => this.hideUpdating(i)}
getdata={(id) => this.getdata(id)}></Phonenumberverifications>
: ""
}
<div className="flexdirections ml38 mt19">
<p className="fontcolorsyslhui1 w60">Email</p>
<p className="fontcolorsyslhei w200">{userdata && userdata.mail}</p>
<p className="fontcolorsysllan mycompitcursor"
onClick={() => this.emailbools()}>{userdata && userdata.mail ? (emailbool === false ? "更换" : "") : (emailbool === false ? "立即绑定" : "")}</p>
</div>
{
emailbool === false ? "" :
(
userdata?
<Mailboxvalidations {...this.props} {...this.state} basicInfo={userdata}
hideUpdating={(i) => this.hideUpdating(i)}
getdata={(id) => this.getdata(id)}></Mailboxvalidations>
:""
)
}
{
leader === true ?
<div>
{
bank_account_editable === true ?
<Bankcardnumberverifications triggerRef={this.bindRef} {...this.props} {...this.state} basicInfo={userdata}
hideUpdating={(i) => this.hideUpdating(i)}
getdata={(id) => this.getdata(id)}
GetawardinformationAPI={() => this.GetawardinformationAPI()}
bank_account={this.state.bank_account}
></Bankcardnumberverifications>
:
admins===true?
<Bankcardnumberverifications triggerRef={this.bindRef} {...this.props} {...this.state} basicInfo={userdata}
hideUpdating={(i) => this.hideUpdating(i)}
getdata={(id) => this.getdata(id)}
GetawardinformationAPI={() => this.GetawardinformationAPI()}
bank_account={this.state.bank_account}
></Bankcardnumberverifications>
:
<div>
<div className="flexdirections mt36">
<p className="fontcolorsyslhei font-16">//开户行及银行卡号</p>
<p className="fontcolorsyslhui font-14 ml11">为保障奖金的及时发放请队长如实填写你名下的银行卡信息</p>
</div>
{
bank_account && bank_account ?
<div>
<div className="flexdirections ml38 mt19">
<p className="fontcolorsyslhui1 w56">开户行</p>
<p className="fontcolorsyslhei">{bank_account && bank_account.bank}</p>
</div>
<div className="flexdirections ml38 mt19">
<p className="fontcolorsyslhui1 w56" style={{textAlign: "right"}}>支行</p>
<p className="fontcolorsyslhei">{bank_account && bank_account.second_bank}</p>
</div>
<div className="flexdirections ml38 mt19">
<p className="fontcolorsyslhui1 w56" style={{textAlign: "right"}}>账号</p>
<p className="fontcolorsyslhei"> {bank_account && bank_account.card_no}</p>
</div>
</div>
:
""
}
</div>
}
</div>
:
<div>
{
admins===true?
<Bankcardnumberverifications triggerRef={this.bindRef} {...this.props} {...this.state} basicInfo={userdata}
hideUpdating={(i) => this.hideUpdating(i)}
getdata={(id) => this.getdata(id)}
GetawardinformationAPI={() => this.GetawardinformationAPI()}
bank_account={this.state.bank_account}
></Bankcardnumberverifications>
:""
}
</div>
}
</div>
)
}
}
export default CompetitionContentspdfpeopledata;

@ -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,92 @@
/*
* @Description: 自定义测试化用例
* @Author: tangjiang
* @Github:
* @Date: 2019-11-27 19:46:14
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-27 23:49:21
*/
import './index.scss';
import React, { useState, useEffect, useRef, useImperativeHandle, forwardRef } from 'react';
import { Form, Input} from 'antd';
const FormItem = Form.Item;
const { TextArea } = Input;
/**
* @description 初始化测试用例: 当有inputValue值时, 显示表单输入框否则显示文本提示信息
* @param {*} props
* props: {
* inputValue: '' // 初始值
* onDebuggerCode: func // 点击调试代码执行函数
* }
*/
function InitTabCtx (props, ref) {
// useImperativeHandle // 让子组件只暴露一定的api给父组件
const tabRef = useRef(null);
const { inputValue, onDebuggerCode } = props;
useImperativeHandle(ref, () => ({
handleTestCodeFormSubmit: (cb) => {
console.log('父组件调用我啦~~~~~~~~~');
_handleTestCodeFormSubmit(cb);
}
}));
// 渲染文本提示信息
const renderText = () => (<span className={'ctx_default'}>请在这里添加测试用例点击调试代码时将从这里读取输入来测试你的代码...</span>);
// 渲染表单信息
const renderForm = () => {
const {form: { getFieldDecorator } } = props;
return (
<Form className={'user_case_form'}>
<FormItem
className={'input_area flex_l'}
label='输入'
>
{
getFieldDecorator('input', {
rules: [
{ required: true, message: '输入值不能为空'}
],
initialValue: inputValue
})(<TextArea rows={5} />)
}
</FormItem>
</Form>
)
}
// 初始渲染内容
const [renderCtx, setRenderCtx] = useState(() => {
return function () {
return renderText();
};
});
// 输入值变化时更新渲染内容
useEffect(() => {
setRenderCtx(() => {
return renderForm;
});
}, [inputValue]);
const _handleTestCodeFormSubmit = (cb) => {
const {form} = props;
form.validateFields((err, values) => {
if (!err) { // 表单验证通过时,调用测试接口
cb && cb(); // 调用回调函数,切换 tab
onDebuggerCode && onDebuggerCode(values);
}
});
}
return(
<div ref={tabRef}>
{renderCtx()}
</div>
)
}
export default Form.create()(forwardRef(InitTabCtx));

@ -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,127 @@
/*
* @Description: 显示tab中的内容
* @Author: tangjiang
* @Date: 2019-11-18 10:43:03
* @Last Modified by: tangjiang
* @Last Modified time: 2019-11-18 11:35:12
*/
import './index.scss';
import React, { PureComponent } from 'react';
import { Icon, Form, Input } from 'antd';
import { connect } from 'react-redux';
import actions from '../../../../redux/actions';
const FormItem = Form.Item;
const { TextArea } = Input;
const tabCtx = (ctx, props) => (<p {...props}>{ctx}</p>);
const renderUserCase = (ctx, position, props) => {
const {form: { getFieldDecorator }, testCases = []} = props;
const testCase = testCases[0] || {}; // 获取第一个测试用例
return (
<Form className={'user_case_form'}>
<FormItem
className={'input_area flex_l'}
label='输入'
>
{
getFieldDecorator('input', {
rules: [
{ required: true, message: '输入值不能为空'}
],
initialValue: testCase.input
})(<TextArea rows={5} />)
}
</FormItem>
{/* <FormItem
className={'input_area flex_r'}
label="输出">
{
getFieldDecorator('output', {
rules: [
{required: true, message: '输出值不能为空'}
],
initialValue: testCase.output
})(<Input />)
}
</FormItem> */}
</Form>
)
};
const defaultCtx = (<span className={'ctx_default'}>请在这里添加测试用例点击调试代码时将从这里读取输入来测试你的代码...</span>)
const loadingCtx = (<span className={'ctx_loading'}><Icon className={'ctx_icon'} type="loading"/>加载中...</span>);
const loadedCtx = (<span className={'ctx_loaded'}><Icon className={'ctx_icon'} type="loading"/>加载完成</span>);
const maps = {
// default: (ctx, position) => (<p className={`tab_ctx_area tab_ctx_default pos_${position}`}>{ctx}</p>),
// loading: (ctx, position) => (<p className={`tab_ctx_area tab_ctx_loading pos_${position}`}>{ctx}</p>),
// loaded: (ctx, position) => (<p className={`tab_ctx_area tab_ctx_loaded pos_${position}`}>{ctx}</p>),
// final: (ctx, position) => (<p className={`tab_ctx_area tab_ctx_final pos_${position}`}>{ctx}</p>)
// 无测试用例时,显示提示信息, ctx: 显示的信息, position: 显示信息的位置
default: (ctx, position) => tabCtx(defaultCtx, { className: `tab_ctx_area tab_ctx_default pos_${position}` }),
// 调度代码加载中
loading: (ctx, position) => tabCtx(loadingCtx, { className: `tab_ctx_area tab_ctx_loading pos_${position}` }),
// 调度代码加载完成
loaded: (ctx, position) => tabCtx(loadedCtx, { className: `tab_ctx_area tab_ctx_loaded pos_${position}` }),
// 显示结果
final: (ctx, position) => tabCtx(ctx, { className: `tab_ctx_area tab_ctx_final pos_${position}` }),
// 显示自定义测试用例面板
userCase: (ctx, position, props) => renderUserCase(ctx, position, props)
}
class InitTabCtx extends PureComponent {
state = {
ctx: '',
position: ''
}
handleTestCodeFormSubmit = (cb) => {
const {form, debuggerCode} = this.props;
console.log(debuggerCode);
form.validateFields((err, values) => {
if (!err) { // 表单验证通过时,调用测试接口
cb && cb(); // 调用回调函数,切换 tab
console.log('表单值:', values);
debuggerCode(values);
}
});
}
componentDidMount () {
const { testCases = []} = this.props;
this.setState({
status: testCases.length > 0 ? 'userCase' : 'default'
});
}
render () {
/**
* @param state 当前状态 default: 显示提示信息 init: 加载初始内容 loading: 加载中 loaded: 加载完成 final: 显示最终内容
* @param position: start | cetner | end
* @param testCase: 自定义测试用例
* @returns
*/
const { testCodeStatus} = this.props;
const { ctx, position } = this.state;
// console.log('===>>>>> 测试用例集合: ', testCases);
return(
<React.Fragment>
{ maps[testCodeStatus](ctx, position, this.props) }
</React.Fragment>
)
}
}
const mapStateToProps = (state) => {
const ojFormReducer = state.ojFormReducer;
return {
testCases: ojFormReducer.testCases, // 测试用例
testCodeStatus: ojFormReducer.testCodeStatus
};
};
const mapDispatchToProps = (dispatch) => ({
debuggerCode: (value) => dispatch(actions.debuggerCode(value))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(Form.create()(InitTabCtx));

@ -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,18 @@
/*
* @Description: 开发者社区入口文件此处提供全局store并且此处Provier只能有一个子无互
* @Author: tangjiang
* @Date: 2019-11-13 20:14:04
* @Last Modified by: tangjiang
* @Last Modified time: 2019-11-15 20:43:27
*/
import React from 'react';
import { TPMIndexHOC } from '../tpm/TPMIndexHOC';
import DeveloperHome from './DeveloperHome';
const App = () => {
return (
<DeveloperHome />
);
}
export default TPMIndexHOC(App);

@ -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,125 @@
/*
* @Description: 新建或编辑任务
* @Author: tangjiang
* @Date: 2019-11-15 16:38:34
* @Last Modified by: tangjiang
* @Last Modified time: 2019-11-19 23:23:41
*/
import './index.scss';
import React, { useCallback, useEffect } from 'react';
import { connect } from 'react-redux';
import SplitPane from 'react-split-pane';// import { Form } from 'antd';
import { Button, Icon } from 'antd';
import { Link } from 'react-router-dom';
import LeftPane from './leftpane';
import RightPane from './rightpane';
// import RightPane from './rightpane/index';
import actions from '../../../redux/actions';
const NewOrEditTask = (props) => {
const {
publishLoading,
handlePublish,
changeSubmitLoadingStatus,
changePublishLoadingStatus,
identifier,
} = props;
// 表单提交
const handleSubmitForm = (code) => {
props.saveOjFormCode(code); // 保存代码块值
// TODO
// identifier 存在时
if (props.identifier) {
props.handleUpdateOjForm(props);
} else {
props.handleFormSubmit(props); // 提交表单
}
};
useEffect(() => {
// console.log('获取路由参数: ====', props.match.params);
const id = props.match.params.id;
// 保存OJForm的id号指明是编辑还是新增
props.saveOJFormId(id);
if (id) { // id号即 identifier
// TODO id 存在时, 编辑, 获取 store 中的记录数
props.getOJFormById(id);
} else {
// 清空store中的测试用例集合
props.clearOJFormStore();
}
return () => {}
}, []);
const handleClickPublish = () => {
// console.log('public has click');
changePublishLoadingStatus(true);
handlePublish(props, 'publish');
}
return (
<div className={'new_add_task_wrap'}>
<div className={'task_header'}>
<Link to="/problems" className={'header_btn'} >
<Icon type="left" style={{ marginRight: '5px'}}/>后退
</Link>
<span className={'header_title'}>{props.name || ''}</span>
<Button
style={{ display: identifier ? 'none' : 'block'}}
loading={publishLoading}
className={`header_btn`}
type="primary"
onClick={handleClickPublish}>立即发布</Button>
</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 onSubmitForm={handleSubmitForm}/>
<div />
</SplitPane>
</SplitPane>
</div>
</div>
)
}
const mapStateToProps = (state) => {
const { ojForm, identifier } = state.ojFormReducer;
const { publishLoading } = state.commonReducer;
return {
name: ojForm.name,
identifier,
publishLoading
}
};
const mapDispatchToProps = (dispatch) => ({
// 保存提交的代码值
saveOjFormCode: (value) => dispatch(actions.saveOjFormCode(value)),
// 表单提交时,调用表单验证功能
handleFormSubmit: (props) => dispatch(actions.validateOjForm(props)),
// 发布表单
handlePublish: (props, type) => dispatch(actions.validateOjForm(props, type)),
// 更新OJForm
handleUpdateOjForm: (props) => dispatch(actions.validateOjForm(props)),
// 根据id号获取表单信息
getOJFormById: (id) => dispatch(actions.getOJFormById(id)),
// 保存 OJ form id值
saveOJFormId: (id) => dispatch(actions.saveOJFormId(id)),
// 清空测试用例的集合
clearOJFormStore: () => dispatch(actions.clearOJFormStore()),
// 按钮状态
changeSubmitLoadingStatus: (flag) => dispatch(actions.changeSubmitLoadingStatus(flag)),
//
changePublishLoadingStatus: (flag) => dispatch(actions.changePublishLoadingStatus(flag))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(NewOrEditTask);

@ -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,127 @@
/*
* @Description: 显示tab中的内容
* @Author: tangjiang
* @Date: 2019-11-18 10:43:03
* @Last Modified by: tangjiang
* @Last Modified time: 2019-11-18 11:35:12
*/
import './index.scss';
import React, { PureComponent } from 'react';
import { Icon, Form, Input } from 'antd';
import { connect } from 'react-redux';
import actions from '../../../../../redux/actions';
const FormItem = Form.Item;
const { TextArea } = Input;
const tabCtx = (ctx, props) => (<p {...props}>{ctx}</p>);
const renderUserCase = (ctx, position, props) => {
const {form: { getFieldDecorator }, testCases = []} = props;
const testCase = testCases[0] || {}; // 获取第一个测试用例
return (
<Form className={'user_case_form'}>
<FormItem
className={'input_area flex_l'}
label='输入'
>
{
getFieldDecorator('input', {
rules: [
{ required: true, message: '输入值不能为空'}
],
initialValue: testCase.input
})(<TextArea rows={5} />)
}
</FormItem>
{/* <FormItem
className={'input_area flex_r'}
label="输出">
{
getFieldDecorator('output', {
rules: [
{required: true, message: '输出值不能为空'}
],
initialValue: testCase.output
})(<Input />)
}
</FormItem> */}
</Form>
)
};
const defaultCtx = (<span className={'ctx_default'}>请在这里添加测试用例点击调试代码时将从这里读取输入来测试你的代码...</span>)
const loadingCtx = (<span className={'ctx_loading'}><Icon className={'ctx_icon'} type="loading"/>加载中...</span>);
const loadedCtx = (<span className={'ctx_loaded'}><Icon className={'ctx_icon'} type="loading"/>加载完成</span>);
const maps = {
// default: (ctx, position) => (<p className={`tab_ctx_area tab_ctx_default pos_${position}`}>{ctx}</p>),
// loading: (ctx, position) => (<p className={`tab_ctx_area tab_ctx_loading pos_${position}`}>{ctx}</p>),
// loaded: (ctx, position) => (<p className={`tab_ctx_area tab_ctx_loaded pos_${position}`}>{ctx}</p>),
// final: (ctx, position) => (<p className={`tab_ctx_area tab_ctx_final pos_${position}`}>{ctx}</p>)
// 无测试用例时,显示提示信息, ctx: 显示的信息, position: 显示信息的位置
default: (ctx, position) => tabCtx(defaultCtx, { className: `tab_ctx_area tab_ctx_default pos_${position}` }),
// 调度代码加载中
loading: (ctx, position) => tabCtx(loadingCtx, { className: `tab_ctx_area tab_ctx_loading pos_${position}` }),
// 调度代码加载完成
loaded: (ctx, position) => tabCtx(loadedCtx, { className: `tab_ctx_area tab_ctx_loaded pos_${position}` }),
// 显示结果
final: (ctx, position) => tabCtx(ctx, { className: `tab_ctx_area tab_ctx_final pos_${position}` }),
// 显示自定义测试用例面板
userCase: (ctx, position, props) => renderUserCase(ctx, position, props)
}
class InitTabCtx extends PureComponent {
state = {
ctx: '',
position: ''
}
handleTestCodeFormSubmit = (cb) => {
const {form, debuggerCode} = this.props;
console.log(debuggerCode);
form.validateFields((err, values) => {
if (!err) { // 表单验证通过时,调用测试接口
cb && cb(); // 调用回调函数,切换 tab
console.log('表单值:', values);
debuggerCode(values);
}
});
}
componentDidMount () {
const { testCases = []} = this.props;
this.setState({
status: testCases.length > 0 ? 'userCase' : 'default'
});
}
render () {
/**
* @param state 当前状态 default: 显示提示信息 init: 加载初始内容 loading: 加载中 loaded: 加载完成 final: 显示最终内容
* @param position: start | cetner | end
* @param testCase: 自定义测试用例
* @returns
*/
const { testCodeStatus} = this.props;
const { ctx, position } = this.state;
// console.log('===>>>>> 测试用例集合: ', testCases);
return(
<React.Fragment>
{ maps[testCodeStatus](ctx, position, this.props) }
</React.Fragment>
)
}
}
const mapStateToProps = (state) => {
const ojFormReducer = state.ojFormReducer;
return {
testCases: ojFormReducer.testCases, // 测试用例
testCodeStatus: ojFormReducer.testCodeStatus
};
};
const mapDispatchToProps = (dispatch) => ({
debuggerCode: (value) => dispatch(actions.debuggerCode(value))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(Form.create()(InitTabCtx));

@ -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,183 @@
<!--
* @Description:
* @Author: tangjiang
* @Github:
* @Date: 2019-11-25 09:46:10
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-25 10:10:11
-->
## Quill配置
### 容器
- css 或者 DOM元素
```
const editor = new Quill(container)
```
### 配置项
var options = {
debug: 'info',
modules: {
toolbar: '#toolbar' // toolbar为一个代码块在页面中指定所需要的工具
},
placeholder: '', //
readOnly: false,
theme: 'snow'
}
const editor = new Quill('#editor', options);
- 对应的接口模型
```
export interface QuillOptionsStatic {
debug?: string | boolean;
modules: StringMap;
placeholder?: string;
readOnly?: boolean;
theme?: string;
formats?: string[];
bounds?: HTMLElement | string;
scrollingContainer?: HTMLElement | string;
strict?: boolean;
}
```
### 格式化
<br> Inline </br>
- background 背景色
- bold 粗体
- color 颜色
- font 字体
- code 内联代码
- italic 斜体
- link 链接
- size 大小
- strike 删除线
- script 上标/下标
- underline 下划线
<br> Block </br>
- blockquote 引用
- header 标题
- indent 缩进
- list 列表
- align 对齐方式
- direction 文字方向
- code-block 代码块
<br> Embeds </br>
- formula 公式 (需要 Katex)
- image 图片
- video 视频
### Quill 常用模块
- 工具栏
- 键盘
- 历史记录
- 剪贴板
- 语法高量
<b> 用法 </b>
> 工具栏模块 [toolbar](src="https://quilljs.com/docs/modules/toolbar/")
modules: {
toolbar: {
container: '#toolbar',
xx: {}
}
}
> 键盘模块 [keyboard](src="https://quilljs.com/docs/modules/keyboard/")
modules: {
keyboard: {
bindings: {
tab: {
key: 9,
handler: function () {}
}
}
}
}
> 历史模块
负责记录模块负责处理Quill的撤销和重做
modules: {
history: {
delay: 2000, // 在2000毫秒内的更改将被合并为单次更改
maxStack: 500, // 历史记录撤销/重做堆栈的大小
userOnly: true // 仅撤销或重做用户的更改
}
}
> 剪贴板模块
处理 Quill 和外部应用程序之间的复制
modules: {
clipboard: {
matchers: [
['B', xx]
]
}
}
> 语法高亮模块
语法高亮模块通过自动检测和应用语法突出显示来增强代码块格式。该模块依赖 [highlight.js](url="https://highlightjs.org/") 库用作解析和格式化代码块。
hljs.configure({ // optionally configure hljs
languages: ['javascript', 'ruby', 'python']
});
var quill = new Quill('#editor', {
modules: {
syntax: true, // Include syntax module
toolbar: [['code-block']] // Include button in toolbar
},
theme: 'snow'
});
> 模块扩展
Quill 中的模块可以被扩展和重新注册,从而替换原始模块
var Clipboard = Quill.import('modules/clipboard');
var Delta = Quill.import('delta');
class PlainClipboard extends Clipboard {
convert(html = null) {
if (typeof html === 'string') {
this.container.innerHTML = html;
}
let text = this.container.innerText;
this.container.innerHTML = '';
return new Delta().insert(text);
}
}
Quill.register('modules/clipboard', PlainClipboard, true);
// Will be created with instance of PlainClipboard
var quill = new Quill('#editor');

@ -0,0 +1,135 @@
/*
* @Description: Quill 编辑器
* @Author: tangjiang
* @Github:
* @Date: 2019-11-25 09:46:03
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-27 19:28:50
*/
import 'quill/dist/quill.core.css';
import 'quill/dist/quill.bubble.css';
import 'quill/dist/quill.snow.css';
import 'katex/dist/katex.css';
import './index.scss';
import React from 'react';
import katex from 'katex';
const Quill = require('quill');
// 将katex挂载到window上
window.katex = katex;
window.Quill = Quill;
// 指定 Quill 默认配置项
const defaultOptions = [
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 自定义标题大小
['bold', 'italic', 'underline', 'strike'], // 切换按钮
['blockquote', 'code-block'], // 代码块
[{ 'list': 'ordered' }, { 'list': 'bullet' }], // 列表
[{ 'script': 'sub'}, { 'script': 'super' }], // 上标/下标
[{ 'indent': '-1'}, { 'indent': '+1' }], // 减少缩进/缩进
[{ 'direction': 'rtl' }],
[{ 'size': ['small', 'large', 'huge', false] }], // 用户自定义下拉
[{ 'color': [] }, { 'background': [] }], // 字体颜色与背景色
[{ 'font': [] }, { 'align': [] }], // 字体与对齐方式
['formula', 'image', 'video'], // 数学公式、图片、视频
['clean'], // 清除格式
];
/**
* @description 抽取一个React编辑器组件基于Quill
* @class QuillEditor类
* @param [object] props 接收的属性
*
* props: {
* options: {} // 编辑器配置信息, 不传使用 defaultOptions, 传了的话 使用用户自定义的,
* placeholder: '' // 编辑器提示信息
* innerHtml: '', // 编辑器内容
* onEditorChange: '', // 编辑器内容改变时调用此方法, 返回更改的内容
* }
* @return [stirng] content 返回编辑器内容
*/
class QuillEditor extends React.Component {
state = {
quillEditor: null,
// quillOptions: defaultOptions
}
constructor (props) {
super(props);
this.editorRef = React.createRef(null);
}
componentDidMount () {
const { options, placeholder = '', readOnly = false } = this.props;
let { quillEditor } = this.state;
const renderOptions = options || defaultOptions;
const editorOption = {
placeholder: placeholder,
modules: {
toolbar: renderOptions
},
readOnly,
theme: 'snow',
}
// 实例化 Quill 编辑器
quillEditor = new Quill(this.editorRef.current, editorOption);
this.setState({
quillEditor: quillEditor
});
// 开启一个定时器读取 html初始时, 如果没有最多执行10次后自动清
let count = 0;
this.timer = setInterval(() => {
count++;
if (count >= 10 || this.props.htmlCtx) {
quillEditor.container.firstChild.innerHTML = this.props.htmlCtx;
clearInterval(this.timer);
this.timer = null;
}
// console.log('定时器====>>>>>', count);
}, 50);
// quillEditor.setText('<p>aaa</p>');
quillEditor.on('editor-change', this.handleQuillChange);
// console.log('====>>>', quillEditor);
}
// 处理quill事件 editor-change
/**
* @param [string] eventName 事件名
* @param [object] args 参数
*/
handleQuillChange = (eventName, ...args) => {
const { onEditorChange } = this.props;
// 获取编辑器内容
const innerHTML = this.state.quillEditor.container.firstChild.innerHTML;
onEditorChange && onEditorChange(innerHTML);
// if ('text-change' === eventName) {
// const {delta, oldDelta, source} = args;
// console.log('textChange', delta, oldDelta, source);
// } else if ('selection-change' === eventName) {
// const {range, oldRange, source} = args;
// console.log('selectionChange', range, oldRange, source);
// }
}
componentWillUnmount () {
// 删除事件监听
this.state.quillEditor.off(this.handleQuillChange);
}
render () {
const styles = this.props.style || {}
return (
<div
id="quill_editor"
style={styles}
className={'quill_editor_area'}
ref={this.editorRef}>
</div>
);
}
}
export default QuillEditor;

@ -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);

@ -829,7 +829,9 @@ submittojoinclass=(value)=>{
headtypes = '/crowdsourcing';
}else if(match.path.startsWith('/moop_cases')){
headtypes = '/moop_cases';
}else {
}else if(match.path.startsWith('/developer')){
headtypes = '/developer'
}else {
headtypes = '/';
}
}
@ -848,7 +850,9 @@ submittojoinclass=(value)=>{
headtypes = '/crowdsourcing';
}else if(match.path.startsWith('/moop_cases')){
headtypes = '/moop_cases';
}else {
}else if(match.path.startsWith('/developer')){
headtypes = '/developer'
}else {
headtypes = '/';
}
}
@ -867,7 +871,9 @@ submittojoinclass=(value)=>{
headtypes = '/crowdsourcing';
}else if(match.path.startsWith('/moop_cases')){
headtypes = '/moop_cases';
}else {
}else if(match.path.startsWith('/developer')){
headtypes = '/developer'
}else {
headtypes = '/';
}
}

@ -1,3 +1,7 @@
html{
height: 100%;
overflow: hidden;
}
body {
overflow: auto !important;
font-family: "Microsoft YaHei";
@ -6,7 +10,7 @@ body {
#root {
/* ie兼容性 */
position: relative;
min-height: 100%;
min-height: 100%;
}
body>.-task-title {
opacity: 1 !important;

@ -705,7 +705,9 @@ export function TPMIndexHOC(WrappedComponent) {
`
.newContainers{
min-width: 1200px;
max-width: unset;
max-width: unset;
height: 100%;
min-height: 100%;
overflow: hidden;
}
.newHeaders{
@ -725,6 +727,7 @@ export function TPMIndexHOC(WrappedComponent) {
}
.indexHOC > .ant-spin-nested-loading {
background: #000;
height: 100%;
}
.indexHOC > .ant-spin-nested-loading > div > .ant-spin .ant-spin-dot {
top: 50% !important;

@ -0,0 +1,128 @@
# actions 文件下文件的配置
## actionTypes.js
此文件指定所有请求的action类型, 类型名统一用大写形式表示
const types = {
ADD_TODO: 'ADD_TODO'
}
export default types;
## testActions.js
此文件针对每一个模块指定 action 方法, 最终通过 index.js 文件统一导出
import types from './actionTypes';
export default function toggleTodo() {
return {
type: types.ADD_TODO
}
}
## index.js
此文件为默认的导出文件, 里边包含所有指定的其它 actions 文件
import toggleTodo from './testAction.js';
export default {
toggleTodo
}
# reducers 文件下文件配置
## testReducer.js
修改state值的唯一方式 根据 action 类型打开对应的 reducer
import types from '../actions/actionTypes';
const initialState = { // 指定状态
count: 0
};
const testReducer = (state = initialState, action) => {
switch (action.type) {
case types.ADD_TODO:
return {
...state,
count: state.count + 1
};
default:
return state;
}
}
export default testReducer;
# stores 文件配置
import { createStore } from 'redux';
import rootReducer from '../reducers';
const configureStore = () => createStore(rootReducer);
export default configureStore;
# 使用
```
import React from 'react';
import { Provider } from 'react-redux';
import DeveloperHome from './DeveloperHome';
// import store from '../../redux/stors/configureStore';
import configureStore from '../../redux/stores/configureStore'
const store = configureStore();
const App = () => {
return (
<Provider store={store}>
<DeveloperHome />
</Provider>
);
}
export default App;
```
````
import React, { PureComponent, Fragment } from 'react';
import { connect } from 'react-redux';
import actions from '../../redux/actions';
class DeveloperHome extends PureComponent {
render () {
const { testReducer, handleClick } = this.props;
return (
<Fragment>
<h2> Developer Home, { testReducer.count } </h2>
<button onClick={() => handleClick() }>add</button>
</Fragment>
);
}
}
/**
* @param {*} state store
* @param {*} ownProps DeveloperHome 中的 props
*/
const mapStateToProps = (state, ownProps) => {
return {
testReducer: state.testReducer
};
}
const mapDispatchToProps = (dispatch) => {
return {
handleClick: () => dispatch(actions.toggleTodo())
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(DeveloperHome);
````

@ -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,233 @@
/*
* @Description:
* @Author: tangjiang
* @Github:
* @Date: 2019-11-27 13:42:11
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-29 11:11:13
*/
import types from "./actionTypes";
import { Base64 } from 'js-base64';
import {
fetchStartProgram,
fetchUserProgramDetail,
fetchDebuggerCode, fetchCodeSubmit,
fetchUserCommitRecord,
fetchUserCommitRecordDetail,
fetchUpdateCode,
fetchUserCodeSubmit
} from "../../services/ojService";
// 进入编程页面时,首先调用开启编程题接口
export const startProgramQuestion = (id) => {
return (dispatch) => {
fetchStartProgram(id).then(res => {
const { status, data } = res;
if (status === 200) {
const {identifier} = data;
dispatch({
type: types.SAVE_USER_PROGRAM_ID,
payload: identifier
});
// 调用用户编程详情接口
fetchUserProgramDetail(identifier).then(res => {
const { status, data = {} } = res;
if (status === 200) {
dispatch({
type: types.USER_PROGRAM_DETAIL,
payload: data
});
}
})
}
})
}
}
/**
* @description 更新代码
* @param {*} identifier
* @param {*} inputValue 输入值: 自定义 | 系统返回的
* @param {*} type 测评类型 debug | submit
*/
export const updateCode = (identifier, inputValue, type) => {
return (dispatch, getState) => {
const { userCode, isUpdateCode } = getState().ojForUserReducer;
// console.log(userCode, isUpdateCode);
isUpdateCode && fetchUpdateCode(identifier, {
code: userCode
}).then(res => {
// 是否更新了代码, 目的是当代码没有更新时不调用更新代码接口,目录没有实现
// TODO 需要优化
dispatch({
type: types.IS_UPDATE_CODE,
flag: false
});
// debuggerCode(identifier, inputValue);
dispatch(debuggerCode(identifier, inputValue, type));
});
}
}
/**
* @description 调试代码
* @param {*} identifier
* @param {*} inputValue 输入值: 自定义 | 系统返回的
* @param {*} type 测评类型 debug | submit
*/
export const debuggerCode = (identifier,value, type) => {
return (dispatch, getState) => {
// 调用之前 先保存 code
// TODO
// console.log(identifier, value);
const {hack: {time_limit = 0}} = getState().ojForUserReducer;
if (!type || type === 'debug') {
dispatch({ // 加载中...
type: types.TEST_CODE_STATUS,
payload: 'loading'
});
}
fetchDebuggerCode(identifier, value).then(res => {
console.log('调用调试代码成功并返回结果: ', res);
const { status } = res;
if (status === 200) {
// 调试代码成功后,调用轮循接口, 注意: 代码执行的时间要小于设置的时间限制
const intervalTime = 500;
let count = 1;
/**
* @param {*} excuteTime 执行时间
* @param {*} finalTime 总时间
* @param {*} count 执行次数
* @param {*} timer 定时器
*/
function getCodeSubmit (intervalTime, finalTime, count, timer){
const excuteTime = (count++) * intervalTime; // 当前执行时间
// console.log(count);
fetchCodeSubmit(identifier, { mode: type }).then(res => {
const { data } = res;
const { status } = data;
// 清除定时器条件: 评测通过或者评测时间大于指定时间
if (+status === 0 || (excuteTime / 1000) > (finalTime + 1)) {
clearInterval(timer); //
timer = null;
const { error_msg }= data.data;
console.log('后台返回错误信息======++++', Base64.decode(error_msg));
const returnData = data.data;
if (!type || type === 'debug') {
dispatch({ // 加载完成
type: types.TEST_CODE_STATUS,
payload: 'loaded'
});
dispatch({ // 改变 loading 值
type: types.LOADING_STATUS,
payload: false
});
// 加载状态变成finish
dispatch({ // 加载完成
type: types.TEST_CODE_STATUS,
payload: 'finish'
});
}
dispatch({
type: types.COMMIT_RECORD_DETAIL,
payload: returnData
});
// 改变tab值至 record
dispatch({
type: types.CHANGE_USER_CODE_TAB,
payload: 'record'
});
// 重新调用一下提交记录接口
dispatch(getUserCommitRecord(identifier));
}
}).catch(err => {
clearInterval(timer);
timer = null;
});
}
let timer = setInterval(() => {
getCodeSubmit(intervalTime, time_limit, count++, timer);
}, intervalTime);
}
});
}
}
// 获取提交记录
export const getUserCommitRecord = (identifier) => {
return (dispatch) => {
fetchUserCommitRecord(identifier).then(res => {
console.log('提交记录======>>>', res);
const {status, data} = res;
if (status === 200) {
dispatch({
type: types.COMMIT_RECORD,
payload: data
})
}
});
}
}
// 获取提交记录详情
export const getUserCommitRecordDetail = () => {
return (dispatch) => {
fetchUserCommitRecordDetail().then(res => {
console.log('提交记录详情======》》》》', res);
});
}
}
// 保存用户时时输入的代码
export const saveUserInputCode = (code) => {
return {
type: types.SAVE_USER_CODE,
payload: code
}
}
// 监听是否更新代码块内容
// export const isUpdateCodeCtx = (flag) => {
// return {
// type: types.IS_UPDATE_CODE,
// payload: flag
// };
// }
// 改变学员测评 tab 值
export const changeUserCodeTab = (key) => {
return {
type: types.CHANGE_USER_CODE_TAB,
payload: key
}
}
/**
* @description 用户提交代码 先调用保存代码接口再调提交接口成功后调用调试接口
* @param {*} identifier
*/
export const submitUserCode = (identifier, inputValue, type) => {
return (dispatch, getState) => {
const { userCode, isUpdateCode } = getState().ojForUserReducer;
isUpdateCode && fetchUpdateCode(identifier, {
code: userCode
}).then(res => {
// 是否更新了代码, 目的是当代码没有更新时不调用更新代码接口,目录没有实现
// TODO 需要优化
dispatch({
type: types.IS_UPDATE_CODE,
flag: false
});
fetchUserCodeSubmit(identifier).then(res => {
console.log('用户提交代码成功======》》》》》', res);
if (res.status === 200) {
dispatch(debuggerCode(identifier, inputValue, type || 'submit'));
}
})
});
}
}

@ -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,266 @@
/*
* @Description:
* @Author: tangjiang
* @Github:
* @Date: 2019-11-20 16:40:32
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-27 23:39:56
*/
import { Base64 } from 'js-base64';
import types from '../actions/actionTypes';
const init = {
ojForm: {
name: '', // 任务名称
language: '',
description: '',
difficult: 1,
category: 1,
openOrNot: 1,
timeLimit: ''
},
ojFormValidate: {
name: {
validateStatus: '',
errMsg: ''
},
language: {
validateStatus: '',
errMsg: ''
},
description: {
validateStatus: '',
errMsg: ''
},
difficult: {
validateStatus: '',
errMsg: ''
},
category: {
validateStatus: '',
errMsg: ''
},
openOrNot: {
validateStatus: '',
errMsg: ''
},
timeLimit: {
validateStatus: '',
errMsg: ''
}
},
ojTestCaseValidate: [],
testCases: [
// {
// input: "11 22",
// output: "33",
// position: 1, // 当前测试用例位置
// isAdd: true // 是否是新增
// }
], // 测试用例集合
position: 1, // TODO 每次加载信息时同步指定positio值
score: 200, // 分值: 选择难易度后自动计算分值 200 | 500 | 1000
code: '', // 提交的代码
identifier: '', // OJ表单id
loading: false, // 僵尸loading标志
testCodeStatus: 'default', // 调试代码状态 default(默认值) | loading(加载中) | loaded(加载完成) | userCase(用户自定义测试用例) | finish(测试完成)
}
const tcValidateObj = {
input: {
errMsg: '',
validateStatus: ''
},
output: {
errMsg: '',
validateStatus: ''
}
}
const scoreMaps = {
1: 200,
2: 500,
3: 1000
};
const initialState = Object.assign({}, init);
const ojFormReducer = (state = initialState, action) => {
let ojFormValidate = {};
let ojForm = {};
if (action.payload) {
ojFormValidate = action.payload.ojFormValidate;
ojForm = action.payload.ojForm;
}
const returnState = (state, ojForm, ojFormValidate) => {
return {
...state,
ojFormValidate: Object.assign({}, state.ojFormValidate, ojFormValidate),
ojForm: Object.assign({}, state.ojForm, ojForm)
};
}
switch (action.type) {
case types.VALIDATE_OJ_FORM:
// 验证成功后,调用后台接口
return returnState(state, ojForm, ojFormValidate);
case types.SAVE_OJ_FORM_CODE:
return {
...state,
code: action.payload
}
case types.VALIDATE_OJ_NAME:
// 验证任务名称
return returnState(state, ojForm, ojFormValidate);
case types.VALIDATE_OJ_DESCRIPTION:
return returnState(state, ojForm, ojFormValidate);
case types.VALIDATE_OJ_LANGUAGE:
return returnState(state, ojForm, ojFormValidate);
case types.VALIDATE_OJ_DIFFICULT:
const curDifficult = action.payload.ojForm.difficult.trim();
if (action.payload.ojForm.difficult) {
state.score = scoreMaps[`${curDifficult}`];
}
return returnState(state, ojForm, ojFormValidate);
case types.VALIDATE_OJ_CATEGORY:
return returnState(state, ojForm, ojFormValidate);
case types.VALIDATE_OJ_OPENORNOT:
return returnState(state, ojForm, ojFormValidate);
case types.VALIDATE_OJ_TIMELIMIT:
return returnState(state, ojForm, ojFormValidate);
case types.ADD_TEST_CASE:
// 新增测试用例及验证
const tcValidate = tcValidateObj;
const tcVArrs = state.ojTestCaseValidate.concat([tcValidate]);
// state.testCases.push(action.payload);
const tcArrs = state.testCases.concat(action.payload);
state.position++; // 位置递增
return {
...state,
testCases: [...tcArrs],
ojTestCaseValidate: [...tcVArrs]
};
case types.DELETE_TEST_CASE:
const { position } = action.payload;
// 根据 position 去查找当前元素在数组中的位置
const index = state.testCases.findIndex((item) => item.position === position);
if (index > -1) {
state.testCases.splice(index, 1); // 删除当前元素
state.ojTestCaseValidate.splice(index, 1); // 删除测试用例对应的校验
}
return {
...state
};
case types.SAVE_OJ_FORM_ID:
state.identifier = action.payload;
return {
...state
}
case types.SAVE_EDIT_OJ_FORM_AND_TEST_CASE: // 保存编辑的值
/**
* 1. 将当前值保存至OJForm中
* 2. 将当前的测试用例保存至 testCases中 并增加 isAdd: false 属性
* 3. 设置position的值, 即新增下一个测试用例的位置
* 4. 自定义测试用例是否需要返回
* 5. 代码执行的结果
* 6. 更改测试用例状态
* 7. 添加测试用例验证
*/
const { code = '', description, language, name, hack_sets = [], time_limit, difficult, category } = action.payload;
const currentOjForm = {
name, // 任务名称
language,
description,
difficult,
category,
openOrNot: 1,
timeLimit: time_limit
};
// state.code = code; // 保存代码块值
let curPosition = 0;
const curTestCases = [];
const curTcValidates = [];
hack_sets.forEach(hack => {
if (hack.position > curPosition) {
curPosition = hack.position;
}
curTcValidates.push(tcValidateObj); // 一个测试用例对应一个校验
curTestCases.push(Object.assign({}, hack, { isAdd: false }));
// state.testCases.push(Object.assign({}, hack, { isAdd: false }));
});
let cbcode = '';
if (typeof code === 'string') {
cbcode = Base64.decode(code);
} else if (Array.isArray(code)) {
cbcode = Base64.decode(code[code.length - 1]);
}
// state.position = curPosition; // 计算下一个测试用例的位置值
return {
...state,
ojForm: currentOjForm,
position: curPosition + 1,
code: cbcode,
testCases: curTestCases,
testCodeStatus: hack_sets.length > 0 ? 'userCase' : 'default'
}
case types.CLEAR_JSFORM_STORE:
state = Object.assign({}, init);
return {
...state
}
// case types.TEST_CODE_STATUS:
// return {
// ...state,
// testCodeStatus: action.payload // 当前状态值
// }
case types.VALIDATE_TEST_CODE_ARRS:
return {
...state,
ojTestCaseValidate: action.payload
}
case types.TEST_CASE_INPUT_CHANGE:
const { input } = action.payload;
// 更新验证消息
const curIOjTestValidate = state.ojTestCaseValidate.map((tc, i) => {
if (i === action.payload.index) {
return Object.assign({}, tc, {input});
}
return tc;
});
let curITestValues = state.testCases.map((tc, i) => {
if (i === action.payload.index) {
return Object.assign({}, tc, { input: action.payload.value })
}
return tc;
});
return {
...state,
ojTestCaseValidate: [...curIOjTestValidate],
testCases: [...curITestValues]
}
case types.TEST_CASE_OUTPUT_CHANGE:
const { output } = action.payload;
// 更新验证消息
const curOOjTestValidate = state.ojTestCaseValidate.map((tc, i) => {
if (i === action.payload.index) {
return Object.assign({}, tc, {output});
}
return tc;
});
let curOTestValues = state.testCases.map((tc, i) => {
if (i === action.payload.index) {
return Object.assign({}, tc, { output: action.payload.value })
}
return tc;
});
return {
...state,
ojTestCaseValidate: [...curOOjTestValidate],
testCases: [...curOTestValues]
}
default:
return state;
}
}
export default ojFormReducer;

@ -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>&nbsp;2019&nbsp;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>&nbsp;&nbsp;&nbsp;&amp;&nbsp;&nbsp;&nbsp;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…
Cancel
Save