优化一版

dev_forge
tangjiang 5 years ago
parent de9f6d3222
commit 2f1e350735

@ -89,6 +89,7 @@
"redux-thunk": "2.3.0",
"rsuite": "^4.0.1",
"sass-loader": "7.3.1",
"scroll-into-view": "^1.12.3",
"store": "^2.0.12",
"style-loader": "0.19.0",
"styled-components": "^4.1.3",

@ -667,8 +667,15 @@ class App extends Component {
}/>
<Route path="/problems/new/:id?" component={NewOrEditTask} />
<Route path="/problems/:id/edit" component={NewOrEditTask} />
<Route path="/myproblems/:id" component={StudentStudy} />
<Route
path="/problems/:id/edit"
render={
(props) => (<NewOrEditTask {...props} />)
} />
<Route path="/myproblems/:id"
render={
(props) => (<StudentStudy {...this.props} {...props} {...this.state} />)
} />
<Route path="/problems" component={Developer}/>
<Route exact path="/"

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-27 16:02:36
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-28 16:48:50
* @LastEditTime: 2019-12-03 09:19:54
*/
import './index.scss';
import React, { useState, useRef } from 'react';
@ -27,7 +27,9 @@ const ControlSetting = (props) => {
changeLoadingState,
changeSubmitLoadingStatus,
showOrHideControl,
// debuggerCode
// debuggerCode,
// startDebuggerCode, // 外部存入
onDebuggerCode,
updateCode,
onSubmitForm
} = props;
@ -62,7 +64,7 @@ const ControlSetting = (props) => {
// 提交
const handleSubmit = (e) => {
e.preventDefault();
changeSubmitLoadingStatus(true)
changeSubmitLoadingStatus(true);
onSubmitForm && onSubmitForm();
}
@ -71,9 +73,10 @@ const ControlSetting = (props) => {
// 改变状态值
changeLoadingState(true);
// 调用代码保存接口, 成功后再调用调试接口
updateCode(identifier, values, 'debug');
// updateCode(identifier, values, 'debug');
// 调用调试接口
// debuggerCode(identifier, values);
onDebuggerCode(values);
}
return (
@ -129,12 +132,12 @@ const ControlSetting = (props) => {
const mapStateToProps = (state) => {
const {commonReducer, ojForUserReducer} = state;
const {loading, excuteState, submitLoading } = commonReducer;
const { user_program_identifier, commitRecordDetail } = ojForUserReducer;
const { commitRecordDetail } = ojForUserReducer;
return {
loading,
submitLoading,
excuteState,
identifier: user_program_identifier,
// identifier: user_program_identifier,
commitRecordDetail // 提交详情
};
};

@ -0,0 +1,64 @@
/*
* @Description:
* @Author: tangjiang
* @Github:
* @Date: 2019-12-03 15:20:55
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-03 15:31:15
*/
import React from 'react';
function ErrorResult (props) {
const { detail } = props;
const renderError = (detail = {}) => {
const {
status,
error_line,
error_msg,
expected_output,
input,
output,
execute_time,
execute_memory
} = detail;
// 根据状态渲染不同的错误信息
let result = null;
switch (status) {
case -1:
result = (
<div className={'error_result_wrap'}>
<p>输入: [input]</p>
<p>输出: [output]</p>
<p>预期: [expected_output]</p>
</div>
);
break;
case 2: // 评测超时
return (
<div className={'error_result_wrap'}>
<p>执行超时限制时限: {`${execute_time}s`}</p>
</div>
);
break;
case 3: // 创建pod失败
break;
case 4: // 编译失败
break;
case 5: // 执行失败
break;
default:
return result;
}
return result;
}
return (
<React.Fragment>
{renderError(detail)}
</React.Fragment>
);
}
export default ErrorResult;

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-28 08:44:54
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-28 15:14:42
* @LastEditTime: 2019-12-03 12:38:27
*/
import './index.scss';
import React, { useState, useEffect } from 'react';
@ -15,6 +15,7 @@ const {reviewResult} = CONST;
function ExecResult (props) {
const { excuteState, excuteDetail } = props;
// console.log('执行状态: ======', excuteState);
// 指定渲染初始, 加载中, 加载完成页面内容
const renderInit = () => (
<div className={'excute_result_area excute_flex_center'}>
@ -65,6 +66,7 @@ function ExecResult (props) {
<React.Fragment>
<p className={'result_info_style'}>输入: {input}</p>
<p className={'result_info_style'}>输出: {output}</p>
<p className={'result_info_style'}>执行用时: {`${execute_time}s`}</p>
</React.Fragment>
);
} else if (state === 4){
@ -109,6 +111,7 @@ function ExecResult (props) {
// 渲染状态变化时渲染相应的内容
useEffect(() => {
// console.log('执行状态====》》》》', excuteState);
if ('loading' === excuteState) {
setRenderCtx(() => (renderLoading));
} else if ('loaded' === excuteState) {
@ -120,7 +123,7 @@ function ExecResult (props) {
// 提交详情变化时
useEffect(() => {
console.log('提交记录详情=====>>>>>', excuteDetail);
// console.log('提交记录详情=====>>>>>', excuteDetail);
setCodeResult(excuteDetail);
}, [excuteDetail]);

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-27 19:46:14
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-27 23:49:21
* @LastEditTime: 2019-12-03 09:14:59
*/
import './index.scss';
import React, { useState, useEffect, useRef, useImperativeHandle, forwardRef } from 'react';
@ -28,7 +28,7 @@ function InitTabCtx (props, ref) {
useImperativeHandle(ref, () => ({
handleTestCodeFormSubmit: (cb) => {
console.log('父组件调用我啦~~~~~~~~~');
// console.log('父组件调用我啦~~~~~~~~~');
_handleTestCodeFormSubmit(cb);
}
}));

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-27 15:02:52
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-28 12:39:39
* @LastEditTime: 2019-12-03 09:29:54
*/
import './index.scss';
import React, { useState, useRef, useEffect } from 'react';
@ -13,17 +13,18 @@ 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';
// import actions from '../../../../redux/actions';
const { fontSetting, opacitySetting } = CONST;
const MyMonacoEditor = (props) => {
function MyMonacoEditor (props, ref) {
const {
language,
code,
showOrHideControl,
saveUserInputCode
// saveUserInputCode,
onCodeChange
} = props;
const [showDrawer, setShowDrawer] = useState(false); // 控制配置滑框
const [editCode, setEditCode] = useState('');
@ -41,6 +42,7 @@ const MyMonacoEditor = (props) => {
useEffect(() => {
setHeight(showOrHideControl ? 'calc(100% - 382px)' : 'calc(100% - 112px)');
}, [showOrHideControl]);
// 控制侧边栏设置的显示
const handleShowDrawer = () => {
setShowDrawer(true);
@ -62,8 +64,9 @@ const MyMonacoEditor = (props) => {
// TODO 需要优化 节流
const val = editorRef.current.getValue();
setEditCode(val);
onCodeChange(val);
// 值一变化保存当前代码值
saveUserInputCode(val);
// saveUserInputCode(val);
});
}
@ -104,7 +107,7 @@ const MyMonacoEditor = (props) => {
</Drawer>
</React.Fragment>
)
}
};
const mapStateToProps = (state) => {
const { showOrHideControl } = state.commonReducer;
@ -114,9 +117,10 @@ const mapStateToProps = (state) => {
};
const mapDispatchToProps = (dispatch) => ({
saveUserInputCode: (code) => dispatch(actions.saveUserInputCode(code)),
// saveUserInputCode: (code) => dispatch(actions.saveUserInputCode(code)),
});
// MyMonacoEditor = React.forwardRef(MyMonacoEditor);
export default connect(
mapStateToProps,
mapDispatchToProps

@ -6,10 +6,10 @@
* @Last Modified time: 2019-11-19 23:23:41
*/
import './index.scss';
import React, { useCallback, useEffect } from 'react';
import React, { useState, useEffect, useRef } from 'react';
import { connect } from 'react-redux';
import SplitPane from 'react-split-pane';// import { Form } from 'antd';
import { Button, Icon } from 'antd';
import { Button, Icon, notification } from 'antd';
import { Link } from 'react-router-dom';
import LeftPane from './leftpane';
import RightPane from './rightpane';
@ -20,15 +20,76 @@ const NewOrEditTask = (props) => {
const {
publishLoading,
handlePublish,
testCases = [],
// ojTestCaseValidate = [],
changeSubmitLoadingStatus,
changePublishLoadingStatus,
updateTestAndValidate,
identifier,
} = props;
const leftRef = useRef(null);
const rightRef = useRef(null);
const [formDate, setFormData] = useState({});
// const isNullOrUndefiendOrZero = (value) => {
// if ([undefined, null, ''].includes(value)) {
// return true;
// }
// return false;
// }
// 表单提交
const handleSubmitForm = (code) => {
props.saveOjFormCode(code); // 保存代码块值
// TODO
// identifier 存在时
const handleSubmitForm = () => {
// 调用输入表单验证功能
// let hasErrorForTestCase = false;
// const tempTestCaseValidate = [];
// // 验证测试用例
// testCases.forEach(tc => {
// const obj = {};
// if (isNullOrUndefiendOrZero(tc.input)) {
// hasErrorForTestCase = true;
// obj['input'] = {
// validateStatus: 'error',
// errMsg: '输入值不能为空'
// }
// } else {
// obj['input'] = {
// validateStatus: '',
// errMsg: ''
// }
// }
// if (isNullOrUndefiendOrZero(tc.output)) {
// hasErrorForTestCase = true;
// obj['output'] = {
// validateStatus: 'error',
// errMsg: '输出值不能为空'
// }
// } else {
// obj['output'] = {
// validateStatus: '',
// errMsg: ''
// }
// }
// // 更改测试用例值
// tempTestCaseValidate.push(obj);
// });
// // 验证代码块
// if (!formValue || hasErrorForTestCase) {
// // 验证不通过时, 回滚按钮状态
// changeSubmitLoadingStatus(false);
// changePublishLoadingStatus(false);
// updateTestAndValidate({ // 测试用例
// testCaseValidate: tempTestCaseValidate
// });
// return;
// }
// console.log('调用提交接口===========》》》》》》》》》》》》');
// props.saveOjFormCode(code); // 保存代码块值
// // TODO
// // identifier 存在时
if (props.identifier) {
props.handleUpdateOjForm(props);
} else {
@ -65,7 +126,7 @@ const NewOrEditTask = (props) => {
<Link to="/problems" className={'header_btn'} >
<Icon type="left" style={{ marginRight: '5px'}}/>后退
</Link>
<span className={'header_title'}>{props.name || ''}</span>
<p className={'header_title'}>{props.name || ''}</p>
<Button
style={{ display: identifier ? 'none' : 'block'}}
loading={publishLoading}
@ -89,11 +150,12 @@ const NewOrEditTask = (props) => {
}
const mapStateToProps = (state) => {
const { ojForm, identifier } = state.ojFormReducer;
const { ojForm, identifier, testCases } = state.ojFormReducer;
const { publishLoading } = state.commonReducer;
return {
name: ojForm.name,
identifier,
testCases,
publishLoading
}
};
@ -115,8 +177,10 @@ const mapDispatchToProps = (dispatch) => ({
clearOJFormStore: () => dispatch(actions.clearOJFormStore()),
// 按钮状态
changeSubmitLoadingStatus: (flag) => dispatch(actions.changeSubmitLoadingStatus(flag)),
//
changePublishLoadingStatus: (flag) => dispatch(actions.changePublishLoadingStatus(flag))
// 发布按钮状态
changePublishLoadingStatus: (flag) => dispatch(actions.changePublishLoadingStatus(flag)),
// 测试用例及验证
updateTestAndValidate: (obj) => dispatch(actions.updateTestAndValidate(obj)),
});
export default connect(

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-21 09:19:38
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-26 15:47:06
* @LastEditTime: 2019-12-02 19:20:30
*/
import './index.scss';
import React, { useState } from 'react';
@ -16,20 +16,18 @@ const { TextArea } = Input;
const FormItem = Form.Item;
const AddTestDemo = (props) => {
const {
key,
onSubmitTest,
onDeleteTest,
testCase,
key,
ojTestCaseValidate,
index
testCaseValidate,
isOpen
} = props;
const [isEditor, setIsEditor] = useState(false); // 是否是编辑
// console.log('测试用例属性: ====>>>>', props);
// 删除操作
const handleDeletePanel = (e) => {
e.stopPropagation();
e.preventDefault();
// console.log('点击的删除按钮')
Modal.confirm({
title: '删除',
@ -124,15 +122,27 @@ const AddTestDemo = (props) => {
const isDisabled = (testCase) => {
return !testCase.isAdd && !isEditor;
};
const {input = {}, output = {}} = (ojTestCaseValidate[index] = {});
// const {input = {}, output = {}} = (testCasesValidate[index] = {});
const activePane = {
defaultActiveKey: [isOpen ? '1' : '']
};
console.log(activePane);
// 切换手风琴
const handleChangeCollapse = () => {
const {index, updateOpenTestCaseIndex} = props;
updateOpenTestCaseIndex(index);
}
return (
<Collapse className={'collapse_area'}>
<Panel header={`测试用例${testCase.position}`} extra={genExtra()} key={key}>
<Collapse className={'collapse_area'} activeKey={isOpen?'1':''} onChange={() => handleChangeCollapse()}>
<Panel header={`测试用例${testCase.position}`} extra={genExtra()} key="1">
<Form>
<FormItem
label={<span className={'label_text'}>输入</span>}
validateStatus={input.validateStatus}
help={input.errMsg}
validateStatus={testCaseValidate.input.validateStatus}
help={testCaseValidate.input.errMsg}
colon={ false }
>
<TextArea
@ -143,8 +153,8 @@ const AddTestDemo = (props) => {
</FormItem>
<FormItem
label={<span className={'label_text'}>输出</span>}
validateStatus={output.validateStatus}
help={output.errMsg}
validateStatus={testCaseValidate.output.validateStatus}
help={testCaseValidate.output.errMsg}
colon={ false }
>
<Input
@ -152,6 +162,7 @@ const AddTestDemo = (props) => {
onChange={handleOutputChange}
disabled={isDisabled(testCase)}/>
</FormItem>
{renderSubmitBtn()}
</Form>
</Panel>
</Collapse>
@ -159,20 +170,21 @@ const AddTestDemo = (props) => {
}
const mapStateToProps = (state) => {
const {identifier, loading, ojTestCaseValidate} = state.ojFormReducer;
const {identifier, loading} = state.ojFormReducer;
console.log(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))
testCaseInputChange: (value, index) => dispatch(actions.testCaseInputChange(value, index)),
updateOpenTestCaseIndex: (index) => dispatch(actions.updateOpenTestCaseIndex(index)),
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(AddTestDemo);
)(Form.create()(AddTestDemo));

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-20 10:35:40
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-27 19:04:03
* @LastEditTime: 2019-12-03 08:41:14
*/
import 'quill/dist/quill.core.css';
import 'quill/dist/quill.bubble.css';
@ -18,10 +18,10 @@ import AddTestDemo from './AddTestDemo';
import QuillEditor from '../../../quillEditor';
import actions from '../../../../../redux/actions';
import CONST from '../../../../../constants';
const scrollIntoView = require('scroll-into-view');
const {jcLabel} = CONST;
const FormItem = Form.Item;
const { Option } = Select;
const maps = {
language: [
{ title: 'C', key: 'C' },
@ -47,7 +47,55 @@ class EditTab extends PureComponent {
constructor (props) {
super(props);
this.editorRef = React.createRef();
// this.editorRef = React.createRef();
this.scrollRef = React.createRef();
this.headerRef = React.createRef();
this.state = {
scrollEl: null,
targetEl: null,
scrollHeight: 0, // 滚动元素的高度
top: 500,
bottom: 20,
offsetTop: 0
}
}
componentDidMount () {
const oWrap = document.getElementById('textCase');
const scrollHeight = oWrap.offsetHeight;
const oTarget = this.headerRef.current;
const offsetTop = oTarget.offsetTop;
this.setState({
scrollEl: oWrap,
targetEl: oTarget,
offsetTop: offsetTop, // 记录初始位置
scrollHeight,
}, () => {
this.state.scrollEl.addEventListener('scroll', this.handleScroll, false);
});
}
componentWillUnmount () {
this.state.scrollEl.removeEventListener('scroll', this.handleScroll, false);
}
// 处理滚动
handleScroll = (e) => {
const oTarget = this.state.targetEl;
const tOffsetTop = oTarget.offsetTop;
const tOffsetHeight = oTarget.offsetHeight;
const scrollTop = e.target.scrollTop;
const { scrollHeight, offsetTop} = this.state;
// 滚动距离 + 元素的高度 大于 offsetTop值时 添加 fix_top 样式
console.log(tOffsetTop, tOffsetHeight, scrollTop, scrollHeight, offsetTop);
if ((scrollTop + tOffsetHeight > tOffsetTop) && (tOffsetTop >= offsetTop)) {
oTarget.className = `test_demo_title fix_top`;
} else if ((scrollHeight + scrollTop < offsetTop) && (scrollHeight <= offsetTop)){
oTarget.className = `test_demo_title`;
} else if ((tOffsetTop < offsetTop) && (tOffsetTop + tOffsetHeight + scrollTop) < offsetTop) {
oTarget.className = `test_demo_title`;
}
}
// 改变任务名称
@ -80,14 +128,20 @@ class EditTab extends PureComponent {
handleChangeOpenOrNot = (value) => {
this.props.validateOpenOrNot(value);
}
// 滚动到底部
scrollToBottom = () => {
scrollIntoView(this.scrollRef.current);
}
render () {
const {
ojFormReducer: {ojForm, ojFormValidate},
ojForm,
ojFormValidate,
testCases = [], // 测试用例集合
position, // 添加的测试用例位置
addTestCase, // 添加测试用例
deleteTestCase, // 删除测试用例
testCasesValidate,
openTestCodeIndex = []
} = this.props;
// console.log('当前位置: ', position);
// console.log('OJForm: ', ojForm);
@ -127,35 +181,47 @@ class EditTab extends PureComponent {
deleteTestCase(obj);
};
const renderTestCase = () => {
return testCases.map((item, i) => (
<AddTestDemo
key={`key_${i}`}
console.log(openTestCodeIndex);
return testCases.map((item, i) => {
return <AddTestDemo
key={`${i}`}
isOpen={openTestCodeIndex.includes(i)}
onSubmitTest={handleSubmitTest}
onDeleteTest={handleDeleteTest}
testCase={item}
testCaseValidate={testCasesValidate[i]}
index={i}
/>
));
});
};
// 添加测试用例
const handleAddTest = () => {
console.log('添加测试用例');
const obj = {
const {position} = this.props;
const obj = { // 测试用例参数
input: '',
output: '',
position: position,
isAdd: true // 新增的测试用例
}
addTestCase(obj);
const validateObj = { // 测试用例验证参数
input: {
validateStatus: '',
errMsg: ''
},
output: {
validateStatus: '',
errMsg: ''
}
}
// this.scrollRef.current.scrollTo(1000);
addTestCase({testCase: obj, tcValidate: validateObj});
// TODO 点击新增时,需要滚到到最底部
// this.editorRef.current.scrollTop
// const oDiv = this.editorRef.current;
// oDiv.scrollTo(oDiv.scrollLeft, 99999);
// console.log(oDiv.scrollTop);
// oDiv.scrollTop = 99999;
this.scrollToBottom();
}
return (
<div className={'editor_area'}>
<div className={'editor_area'} id="textCase">
<Form className={'editor_form'}>
<FormItem
className={`input_area flex_60`}
@ -192,7 +258,7 @@ class EditTab extends PureComponent {
>
<QuillEditor
style={{ height: '300px' }}
placeholder="init content"
placeholder="请输入描述信息"
onEditorChange={this.handleChangeDescription}
htmlCtx={ojForm.description}
/>
@ -242,13 +308,15 @@ class EditTab extends PureComponent {
</Form>
{/* 添加测试用例 */}
<div className="test_demo_title">
<div className={'test_demo_title'} ref={this.headerRef}>
<h2>测试用例</h2>
<Button type="primary" onClick={handleAddTest}>添加测试用例</Button>
</div>
<div className="test_demo_ctx">
{ renderTestCase() }
</div>
<div style={ {float:"left", clear: "both", background: 'red' } } ref={this.scrollRef}></div>
</div>
)
}
@ -256,11 +324,21 @@ class EditTab extends PureComponent {
const mapStateToProps = (state) => {
const ojFormReducer = state.ojFormReducer;
const {
ojForm,
position,
testCases,
openTestCodeIndex,
testCasesValidate,
ojFormValidate} = ojFormReducer;
return {
ojFormReducer,
testCases: ojFormReducer.testCases, // 测试用例
position: ojFormReducer.position, // 测试用例位置
}
ojForm,
testCases,
testCasesValidate,
ojFormValidate,
position,
openTestCodeIndex
};
};
const mapDispatchToProps = (dispatch) => ({

@ -57,6 +57,17 @@
height: 60px;
border-bottom: 1px solid #d9d9d9;
margin-bottom: 20px;
&.fix_top{
position: absolute;
top: 43px;
left: -30px;
right: -30px;
padding: 0 30px;
// background: gold;
background: rgb(249,249,249);
z-index: 1000;
}
}
.collapse_area{

@ -0,0 +1,334 @@
/*
* @Description:
* @Author: tangjiang
* @Github:
* @Date: 2019-12-01 09:17:07
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-02 16:33:35
*/
import 'quill/dist/quill.core.css';
import 'quill/dist/quill.bubble.css';
import 'quill/dist/quill.snow.css';
import './index.scss';
import React, { useState, useImperativeHandle, useRef, useEffect } from 'react';
import { Form, Input, InputNumber, Button, Select } 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 { Option } = Select;
const FormItem = Form.Item;
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' }
]
}
function EditTab (props, ref) {
const {
form,
ojForm,
position,
testCases,
addTestCase,
deleteTestCase,
testCasesValidate,
getFormData
} = props;
const { getFieldDecorator } = form;
const formRef = useRef(null);
const [description, setDescription] = useState('');
// 获取表单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>
);
});
};
// 向外暴露的方法
useImperativeHandle(ref, () => ({
validateForm () {
props.form.validateFields((err, values) => {
if (!err) {
getFormData(() => {
return values;
});
} else {
return;
}
})
}
}));
// 添加测试用例
const handleAddTest = () => {
const obj = { // 测试用例参数
input: '',
output: '',
position: position,
isAdd: true // 新增的测试用例
}
const validateObj = { // 测试用例验证参数
input: {
validateStatus: '',
errMsg: ''
},
output: {
validateStatus: '',
errMsg: ''
}
}
addTestCase({testCase: obj, tcValidate: validateObj});
// TODO 点击新增时,需要滚到到最底部
// this.editorRef.current.scrollTop
// const oDiv = this.editorRef.current;
// oDiv.scrollTo(oDiv.scrollLeft, 99999);
// console.log(oDiv.scrollTop);
// oDiv.scrollTop = 99999;
}
// 渲染测试用例
const renderTestCase = () => {
return testCases.map((item, i) => {
return (
<AddTestDemo
key={`key_${i}`}
onSubmitTest={handleSubmitTest}
onDeleteTest={handleDeleteTest}
testCase={item}
testCaseValidate={testCasesValidate[i]}
index={i}
/>
)
});
};
// 提交测试用例
const handleSubmitTest = (obj) => {
console.log('提交的测试用例: ', obj);
};
// 删除测试用例
const handleDeleteTest = (obj) => {
console.log('删除的测试用例: ', obj);
deleteTestCase(obj);
};
// 描述信息改变时
const handleChangeDescription = (value) => {
console.log('描述信息改变: ', value);
if (value) {
setDescription(value);
}
}
useEffect(() => {
if (description) {
props.form.setFieldsValue({
description: description
}, function () {
console.log('设置成功。。。');
});
}
}, [description]);
return (
<div className={'editor_area'}>
<Form
hideRequiredMark={true}
className={'editor_form'}
ref={formRef}>
<FormItem
className={`input_area flex_60`}
label={<span>{myLabel(jcLabel['name'])}</span>}
>
{
getFieldDecorator('name', {
rules: [
{ required: true, message: '任务名称不能为空' }
],
initialValue: ojForm.name
})(<Input placeholder="请输入任务名称"/>)
}
</FormItem>
<FormItem
className={`input_area flex_40`}
label={<span>{myLabel(jcLabel['language'])}</span>}
>
{
getFieldDecorator('language', {
rules: [
{ required: true, message: '语言不能为空' }
],
initialValue: ojForm.language
})(
<Select>
{getOptions('language')}
</Select>)
}
</FormItem>
<FormItem
className={`input_area flex_100`}
label={<span>{myLabel(jcLabel['description'])}</span>}
>
{
getFieldDecorator('description', {
rules: [
{ required: true, message: '描述信息不能为空' }
],
initialValue: ojForm.description
})(<QuillEditor
style={{ height: '300px' }}
placeholder="请输入描述信息"
htmlCtx={ojForm.description}
onEditorChange={handleChangeDescription}
/>)
}
</FormItem>
<FormItem
className={`input_area flex_50 flex_50_left`}
label={<span>{myLabel(jcLabel['difficult'], '任务的难易程度')}</span>}
>
{
getFieldDecorator('difficult', {
rules: [
{ required: true, message: '难度不能为空' }
],
initialValue: `${ojForm.difficult || ''}`
})(
<Select>
{getOptions('difficult')}
</Select>
)
}
</FormItem>
<FormItem
className={`input_area flex_50 flex_50_right`}
label={<span>{myLabel(jcLabel['timeLimit'], '程序允许时间限制时长,单位:秒')}</span>}
>
{
getFieldDecorator('timeLimit', {
rules: [
{ required: true, message: '时间限制不能为空' }
],
initialValue: ojForm.timeLimit
})(<InputNumber min={0} style={{ width: '100%' }} />)
}
</FormItem>
<FormItem
className={`input_area flex_50 flex_50_left`}
label={<span>{myLabel(jcLabel['category'], '任务所属分类')}</span>}
>
{
getFieldDecorator('category', {
rules: [
{ required: true, message: '任务名称不能为空' }
],
initialValue: `${ojForm.category || ''}`
})(
<Select>
{getOptions('category')}
</Select>
)
}
</FormItem>
<FormItem
className={`input_area flex_50 flex_50_right`}
label={<span>{myLabel(jcLabel['openOrNot'])}</span>}
>
{
getFieldDecorator('openOrNot', {
rules: [
{ required: true, message: '任务名称不能为空' }
],
initialValue: `${ojForm.openOrNot}`
})(
<Select>
{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;
const {ojForm, position, testCases, testCasesValidate} = ojFormReducer;
return {
ojForm,
testCases,
testCasesValidate,
position
};
}
const mapDispatchToProps = (dispatch) => ({
// 新增测试用例
addTestCase: (value) => dispatch(actions.addTestCase(value)),
// 删除测试用例
deleteTestCase: (value) => dispatch(actions.deleteTestCase(value)),
})
// EditTab = React.formRef(EditTab);
// EditTab = React.forwardRef(EditTab);
export default connect(
mapStateToProps,
mapDispatchToProps
)(Form.create()(
React.forwardRef(EditTab)
));

@ -12,11 +12,10 @@ 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 = () => {
function LeftPane (props) {
const [defaultActiveKey, setDefaultActiveKey] = useState('editor');
@ -40,6 +39,8 @@ const LeftPane = () => {
setDefaultActiveKey(key);
}
// 执行表单提交函数
return (
<Tabs activeKey={defaultActiveKey} onChange={handleTabChange}>
{ tabs }

@ -1,25 +1 @@
// .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';

@ -1,215 +1,63 @@
/*
* @Description: 右侧代码块
* @Description:
* @Author: tangjiang
* @Date: 2019-11-18 08:42:04
* @Last Modified by: tangjiang
* @Last Modified time: 2019-11-20 00:00:34
* @Github:
* @Date: 2019-12-01 10:18:35
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-03 09:11:50
*/
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 React from 'react';
import { connect } from 'react-redux';
import InitTabCtx from './initTabCtx';
import SettingDrawer from '../../components/monacoSetting';
import CONST from '../../../../constants';
import MyMonacoEditor from '../../components/myMonacoEditor';
import ControlSetting from '../../components/controlSetting';
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
function RightPane (props, ref) {
useEffect(() => {
if (props.language) {
// console.log('当前输入的代码:', editCode);
// console.log('当前输入的语言:', props.language);
setLanguage(props.language)
}
}, [props.language]);
useEffect(() => {
}, [props.testCases]);
useEffect(() => {
}, [editCode]);
const {
// identifier,
onSubmitForm,
saveOjFormCode
} = props;
// 监听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 handleCodeChange = (code) => {
// 保存用户输入的代码
saveOjFormCode(code);
}
// 提交按钮点击
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');
// });
// 启动调试代码
// const handleDebuggerCode = (value) => {
// console.log('调用的代码调试====', value);
// }
// 控制台点击时 添加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}
<MyMonacoEditor language={props.language} code={props.code} onCodeChange={handleCodeChange}/>
<ControlSetting
// identifier={identifier}
inputValue={props.input}
onSubmitForm={onSubmitForm}
// onDebuggerCode={handleDebuggerCode}
/>
{/* 控制台信息 */}
<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;
const { ojForm, testCases, code, identifier } = state.ojFormReducer;
return {
language: ojForm.language,
testCases,
identifier,
code,
submitLoading
identifier,
language: ojForm.language,
input: (testCases[0] && testCases[0].input) || '',
}
};
const mapDispatchToProps = (dispatch) => ({
saveOjFormCode: (code) => dispatch(actions.saveOjFormCode(code)),
changePublishLoadingStatus: (flag) => dispatch(actions.changeSubmitLoadingStatus(flag))
// 保存提交的代码值
saveOjFormCode: (value) => dispatch(actions.saveOjFormCode(value)),
});
//
export default connect(
mapStateToProps,
mapDispatchToProps
)(RightPaneCode);
)(RightPane);

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

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-25 09:46:03
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-29 15:04:12
* @LastEditTime: 2019-12-02 16:34:29
*/
import 'quill/dist/quill.core.css';
import 'quill/dist/quill.bubble.css';
@ -61,7 +61,7 @@ class QuillEditor extends React.Component {
componentDidMount () {
const { options, placeholder = '', readOnly = false } = this.props;
let { quillEditor } = this.state;
console.log(placeholder);
const renderOptions = options || defaultOptions;
const editorOption = {

@ -10,24 +10,38 @@
}
.task_header{
display: flex;
align-items: center;
// justify-content: space-between;
position: relative;
line-height: 65px;
.header_btn,
.header_title{
color: #fff;
line-height: 65px;
}
.header_btn{
width: 88px;
}
.header_title{
flex: 1;
text-align: center;
}
.header_btn{
position: absolute;
line-height: 32px;
top: 18px;
&:last-child{
right: 30px;
}
}
// .header_btn{
// width: 88px;
// }
// .header_title{
// flex: 1;
// text-align: center;
// }
}
.split-pane-area,
.pane_right_area{
.pane_right_area
{
position: relative;
height: calc(100% - 65px);
.left_pane,
@ -52,7 +66,11 @@
}
.student_nicker{
margin-left: 20px;
margin-left: 10px;
}
.student_img{
width: 30px;
height: 30px;
}
.study_quit{
float: right;

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-23 10:53:19
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-29 20:00:34
* @LastEditTime: 2019-12-03 14:49:32
*/
import './index.scss';
import React, { useEffect } from 'react';
@ -13,15 +13,28 @@ import SplitPane from 'react-split-pane';
import LeftPane from './leftpane';
import RightPane from './rightpane';
import { Link } from 'react-router-dom';
import { getImageUrl } from 'educoder'
// import RightPane from '../newOrEditTask/rightpane';
import { Button, Avatar } from 'antd';
import { Button } from 'antd';
import actions from '../../../redux/actions';
const StudentStudy = (props) => {
const {
mygetHelmetapi = {}
} = props;
useEffect(() => {
const { match: { params }, getUserProgramDetail } = props;
const {
match: { params },
getUserProgramDetail,
saveUserProgramIdentifier,
} = props;
console.log(props);
let { id } = params;
// console.log(id);
// 保存当前的id
saveUserProgramIdentifier(id);
// startProgramQuestion(id);
getUserProgramDetail(id);
}, []);
@ -29,9 +42,9 @@ const StudentStudy = (props) => {
<div className={'student_study_warp'}>
<div className={'student_study_header'}>
<div className={'avator_nicker'}>
<Avatar icon="user" />
<img alt="用户头像" className={'student_img'} src={getImageUrl((mygetHelmetapi && mygetHelmetapi.nav_logo_url) || 'images/educoder/headNavLogo.png?1526520218')} />
<span className={'student_nicker'}>
我是昵称
{(mygetHelmetapi &&mygetHelmetapi.name) || ''}
</span>
</div>
<div className={'study_name'}>
@ -64,7 +77,8 @@ const mapDispatchToProps = (dispatch) => ({
// 调用开启编辑
// startProgramQuestion: (id) => dispatch(actions.startProgramQuestion(id))
// 调用编程题详情
getUserProgramDetail: (id) => dispatch(actions.getUserProgramDetail(id))
getUserProgramDetail: (id) => dispatch(actions.getUserProgramDetail(id)),
saveUserProgramIdentifier: (id) => dispatch(actions.saveUserProgramIdentifier(id))
});
export default connect(

@ -4,15 +4,17 @@
* @Github:
* @Date: 2019-11-27 09:49:33
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-29 12:06:01
* @LastEditTime: 2019-12-03 15:14:34
*/
import './index.scss';
import React, { useState, useEffect } from 'react';
import { Table, Icon } from 'antd';
import { Table, Icon, message } from 'antd';
import { connect } from 'react-redux';
import actions from '../../../../../redux/actions';
import CONST from '../../../../../constants';
import moment from 'moment';
import ClipboardJS from 'clipboard';
const numberal = require('numeral');
const {reviewResult} = CONST;
@ -63,6 +65,7 @@ const CommitRecord = (props) => {
const {
identifier,
commitRecord,
// excuteState,
commitRecordDetail,
getUserCommitRecord
} = props;
@ -87,52 +90,63 @@ const CommitRecord = (props) => {
status,
expected_output
} = commitRecordDetail;
console.log('========', commitRecordDetail);
if (Object.keys(commitRecordDetail).length > 0) {
console.log('当前状态====》》》', status);
const classes = status === 0 ? 'record_result_suc' : 'record_result_err';
const showErrorCode = status !== 0 ? `ecord_error_info show_error_code` : `ecord_error_info`;
const showErrorCopy = status !== 0 ? `copy_error show_error_copy` : `copy_error`;
return (
<React.Fragment>
{/* <div className={'record_header'}>
<span className={'record_result'}>
<div className={'record_header'}>
<p className={'record_result'}>
执行结果: <span className={classes}>{reviewResult[status]}</span>
</span>
<span className={'copy_error'}>
</p>
<p className={showErrorCopy} data-clipboard-target="#errcode">
<span>
复制错误信息 <Icon type="copy" className={'icon_style'}/>
</span>
<span className={'show_detail'}>
</p>
<p className={'show_detail'}>
显示详情 <Icon type="right" className={'icon_style'}/>
</span>
</div> */}
{/* <div className={'record_error_info'}>错误代码</div> */}
</p>
</div>
<div id="errcode" className={showErrorCode}>{error_msg}</div>
</React.Fragment>
);
} else {
return '';
}
}
// 根据id获取用户提交记录
useEffect(() => {
// console.log('调用记录详情=====>>>');
getUserCommitRecord(identifier);
}, []);
// 提交记录变化时,同步到表单数据
useEffect(() => {
// console.log('====>>>>+++++++++++++', commitRecord);
setTableData(commitRecord);
}, [commitRecord]);
// 提交详情变化时,显示当前提交信息
useEffect(() => {
// setRecordDetail(commitRecordDetail);
setRenderCtx(() => (renderRecordDetail))
}, [commitRecordDetail]);
console.log(commitRecord);
// 复制功能
useEffect(() => {
if (commitRecordDetail.status !== 0) {
const clipboard = new ClipboardJS('.copy_error');
clipboard.on('success', (e) => {
message.success('复制成功');
e.clearSelection();
});
}
}, [commitRecordDetail.status]);
// console.log(commitRecord);
return (
<div className={'commit_record_area'}>
{renderCtx()}
{renderRecordDetail()}
<Table
columns={columns}
rowKey={record => Math.random()}
rowKey={function (record) { return `key_${record.id}`}}
dataSource={tableData}
pagination={pagination}
/>
@ -142,17 +156,20 @@ const CommitRecord = (props) => {
const mapStateToProps = (state) => {
const {
ojForUserReducer
ojForUserReducer,
commonReducer
} = state;
const {
user_program_identifier,
commitRecordDetail,
commitRecord
} = ojForUserReducer;
const { excuteState } = commonReducer;
return {
identifier: user_program_identifier,
commitRecordDetail,
commitRecord // 提交记录
commitRecord, // 提交记录
excuteState, // 代码执行状态
}
}
const mapDispatchToProps = (dispatch) => ({

@ -12,21 +12,25 @@
// width:1px;
}
.copy_error{
visibility: hidden;
text-align: right;
flex: 1;
padding-right: 40px;
&.show_error_copy{
visibility: visible;
}
.show_detail{
// width: 1px;
}
.copy_error,
.show_detail{
color: #5091FF;
font-size: 14px;
cursor: pointer;
}
.icon_style{
font-size: 12px;
margin-left: 2px;
cursor: pointer;
}
.record_result_suc{
color: #28BD8B;
@ -36,6 +40,14 @@
}
}
.record_error_info{
display: none;
padding: 20px 30px;
}
.show_error_code{
display: block;
background: rgba(250,250,250,1);
padding: 20px 30px;
color: #E51C24;
margin-bottom: 20px;
}
}

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-23 11:33:41
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-29 11:10:57
* @LastEditTime: 2019-12-03 14:13:19
// */
import './index.scss';
import React, { useState, useEffect } from 'react';

@ -15,7 +15,7 @@
width: 100%;
// background: pink;
padding: 0 30px;
// background-color: #fff;
background-color: rgba(250,250,250,1);
.flex_count,
.flex_info{
@ -28,10 +28,13 @@
}
}
.task_description_area{
.task_description_area,
.commit_record_area{
padding: 0 30px;
height: calc(100vh - 166px);
overflow-y: auto;
}
.task_description_area{
.desc_area_header{
display: flex;
justify-content: space-between;

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-27 14:59:51
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-28 17:17:25
* @LastEditTime: 2019-12-03 09:31:59
*/
import React from 'react';
import {connect} from 'react-redux';
@ -14,7 +14,15 @@ import actions from '../../../../redux/actions';
const RightPane = (props) => {
const {identifier, submitInput, submitUserCode} = props;
const {
identifier,
submitInput,
submitUserCode,
input,
updateCode,
saveUserInputCode
} = props;
const handleSubmitForm = () => {
console.log('提交了表单内容');
// 提交时, 先调用提交接口,提交成功后,循环调用测评接口
@ -22,10 +30,31 @@ const RightPane = (props) => {
// // 提交时,先调用评测接口, 评测通过后才调用保存接口
// updateCode(identifier, submitInput, 'submit');
}
// 代码块内容变化时
const handleCodeChange = (code) => {
// 保存用户提交的代码块
console.log(code);
// 保存用户代码块
saveUserInputCode(code);
}
// 代码调试
const handleDebuggerCode = (value) => {
// 调用保存代码块接口,成功后,调用调试接口
updateCode(identifier, value, 'debug');
}
return (
<div className={'right_pane_code_wrap'}>
<MyMonacoEditor language={props.language} code={props.code}/>
<ControlSetting inputValue={props.input} onSubmitForm={handleSubmitForm}/>
<MyMonacoEditor
language={props.language}
code={props.code}
onCodeChange={handleCodeChange}
/>
<ControlSetting
identifier={identifier}
inputValue={input}
onDebuggerCode={handleDebuggerCode}
onSubmitForm={handleSubmitForm}/>
</div>
);
}
@ -45,8 +74,11 @@ const mapStateToProps = (state) => {
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))
submitUserCode: (identifier, inputValue, type) => dispatch(actions.submitUserCode(identifier, inputValue, type)),
// 更新代码块内容
updateCode: (identifier, inputValue, type) => dispatch(actions.updateCode(identifier, inputValue, type)),
// 保存用户代码块
saveUserInputCode: (code) => dispatch(actions.saveUserInputCode(code)),
});
export default connect(

@ -43,6 +43,8 @@ const types = {
PUBLISH_LOADING_STATUS: 'PUBLISH_LOADING_STATUS', // 发布按钮
IS_MY_SOURCE: 'IS_MY_SOURCE',
CHANGE_PAGINATION_INFO: 'CHANGE_PAGINATION_INFO', // 改变分页数据
UPDATE_TEST_AND_VALIDATE: 'UPDATE_TEST_AND_VALIDATE', // 更新测试用例及验证值
UPDATE_OPEN_TESTCASE_INDEX: 'UPDATE_OPEN_TESTCASE_INDEX', // 更新测试用例索引
}
export default types;

@ -25,6 +25,8 @@ import {
deleteTestCase,
testCaseInputChange,
testCaseOutputChange,
updateTestAndValidate,
updateOpenTestCaseIndex,
} from './ojForm';
import {
@ -36,7 +38,8 @@ import {
saveUserInputCode,
changeUserCodeTab,
submitUserCode,
getUserProgramDetail
getUserProgramDetail,
saveUserProgramIdentifier,
// isUpdateCodeCtx
} from './ojForUser';
@ -81,6 +84,9 @@ export default {
submitUserCode,
changePublishLoadingStatus,
isMyPublish,
getUserProgramDetail
getUserProgramDetail,
updateTestAndValidate,
updateOpenTestCaseIndex,
saveUserProgramIdentifier
// isUpdateCodeCtx
}

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-27 13:42:11
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-29 20:07:09
* @LastEditTime: 2019-12-03 11:19:46
*/
import types from "./actionTypes";
import { Base64 } from 'js-base64';
@ -37,6 +37,14 @@ export const startProgramQuestion = (id, props) => {
}
}
// 保存 identifier, 防止刷新时读取不到
export const saveUserProgramIdentifier = (identifier) => {
return {
type: types.SAVE_USER_PROGRAM_ID,
payload: identifier
}
}
// 获取用户编程题详情
export const getUserProgramDetail = (identifier) => {
// 调用用户编程详情接口
@ -60,11 +68,10 @@ export const getUserProgramDetail = (identifier) => {
* @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, {
if (isUpdateCode) {
fetchUpdateCode(identifier, {
code: userCode
}).then(res => {
// 是否更新了代码, 目的是当代码没有更新时不调用更新代码接口,目录没有实现
@ -76,6 +83,10 @@ export const updateCode = (identifier, inputValue, type) => {
// debuggerCode(identifier, inputValue);
dispatch(debuggerCode(identifier, inputValue, type));
});
} else {
// 没有更新时,直接调用调试接口
dispatch(debuggerCode(identifier, inputValue, type));
}
}
}
@ -114,56 +125,76 @@ export const debuggerCode = (identifier,value, type) => {
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;
const { status } = res.data; // 评测返回结果
// 清除定时器条件: 评测通过或者评测时间大于指定时间
if (+status === 0 || (excuteTime / 1000) > (finalTime + 1)) {
clearInterval(timer); //
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'
let returnData = null;
if (status === 1) { // 结果没有返回
returnData = {
error_line: -1,
error_msg: '',
execute_memory: '',
execute_time: finalTime,
input: '',
output: '',
status: 2,
expected_output: ''
};
} else { // 成功返回结果
returnData = res.data.data;
}
// 返回评测结果
dispatch({
type: types.COMMIT_RECORD_DETAIL,
payload: returnData
});
if (!type || type === 'debug') {
dispatch({ // 改变 loading 值
type: types.LOADING_STATUS,
payload: false
});
// 加载状态变成finish
dispatch({ // 加载完成
// 保存执行状态
dispatch({
type: types.TEST_CODE_STATUS,
payload: 'finish'
});
}
} else {
// 回滚提交按钮状态
dispatch({
type: types.COMMIT_RECORD_DETAIL,
payload: returnData
type: types.SUBMIT_LOADING_STATUS,
payload: false
});
// 改变tab值至 record
// 改变tab值至提交记录(只在提交时才跳转,测评时,切换到代码执行结果就可以了)
dispatch({
type: types.CHANGE_USER_CODE_TAB,
payload: 'record'
});
// 将按钮状态回滚
dispatch({
type: types.SUBMIT_LOADING_STATUS,
payload: false
});
// 重新调用一下提交记录接口
dispatch(getUserCommitRecord(identifier));
}
}).catch(err => {
}
}).catch(err => { // 评测异常时
// 清除定时器
clearInterval(timer);
timer = null;
// 回滚按钮状态
if (!type || type === 'debug') {
dispatch({ // 改变 loading 值
type: types.LOADING_STATUS,
payload: false
});
} else { // 回滚提交按钮状态
dispatch({
type: types.SUBMIT_LOADING_STATUS,
payload: false
});
}
});
}
// 开启定时器,调用监听接口
let timer = setInterval(() => {
getCodeSubmit(intervalTime, time_limit, count++, timer);
}, intervalTime);
@ -174,7 +205,7 @@ export const debuggerCode = (identifier,value, type) => {
payload: ''
});
dispatch({
type: types.LOADING_STATUS,
type: types.SUBMIT_LOADING_STATUS,
payload: false
});
});
@ -185,7 +216,6 @@ export const debuggerCode = (identifier,value, type) => {
export const getUserCommitRecord = (identifier) => {
return (dispatch) => {
fetchUserCommitRecord(identifier).then(res => {
console.log('提交记录======>>>', res);
const {status, data} = res;
if (status === 200) {
dispatch({

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-20 16:35:46
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-29 18:55:43
* @LastEditTime: 2019-12-02 19:22:58
*/
import types from './actionTypes';
import CONST from '../../constants';
@ -83,6 +83,7 @@ const payloadInfo = (key, value, errMsg, validateInfo) => ({
export const validateOjForm = (props, type) => {
return (dispatch, getState) => {
const {ojForm, testCases, identifier, code } = getState().ojFormReducer;
console.log('code', code);
let keys = Object.keys(ojForm);
// 循环判断每个字段是否为空
let hasSuccess = true;
@ -103,13 +104,11 @@ export const validateOjForm = (props, type) => {
// 验证测试用例中的数组是否都有对应的值
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;
}
@ -119,26 +118,38 @@ export const validateOjForm = (props, type) => {
});
if (testCases.length === 0) {
hasSuccess = false;
notification['error']({
message: '必填',
description: '测试用例必须输入!'
});
return false;
}
if (!code) {
hasSuccess = false;
notification['error']({
message: '必填',
description: '代码块内容必须输入!'
});
return;
}
// 更改测试用例验证结果
dispatch({
type: types.VALIDATE_TEST_CODE_ARRS,
payload: tcValidResult
type: types.UPDATE_TEST_AND_VALIDATE,
payload: {
testCaseValidate: tcValidResult
}
});
// 验证成功后,调用提交接口
if (!hasSuccess) {
dispatch({ // 提交
type: types.SUBMIT_LOADING_STATUS,
payload: false
});
dispatch({ // 发布
type: types.PUBLISH_LOADING_STATUS,
payload: false
});
}
if (hasSuccess) {
// console.log('表单保存的数据为: ', getState());
const {ojFormReducer} = getState();
@ -160,11 +171,6 @@ export const validateOjForm = (props, type) => {
language
};
// const tempTc = testCases.map(tc => {
// delete tc.isAdd
// return tc;
// });
// console.log(tempTc);
if (!identifier) { // 新增
const tempTc = testCases.map(tc => {
delete tc.isAdd
@ -389,3 +395,25 @@ export const testCaseOutputChange = (value, index) => {
// // 调试代码时,更改对应的状态值
// export const changeTestCodeStatus = () => {
// 更新测试用命及验证
export const updateTestAndValidate = (obj) => {
return (dispatch) => {
dispatch({
type: types.UPDATE_TEST_AND_VALIDATE,
payload: obj
});
}
// return {
// type: types.UPDATE_TEST_AND_VALIDATE,
// payload: obj
// }
}
// 更新测试用例索引
export const updateOpenTestCaseIndex = (value) => {
return {
type: types.UPDATE_OPEN_TESTCASE_INDEX,
payload: value
}
}

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-27 16:27:09
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-28 21:14:36
* @LastEditTime: 2019-12-03 11:00:19
*/
import types from "../actions/actionTypes";

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-27 13:41:48
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-29 20:07:57
* @LastEditTime: 2019-12-03 10:54:34
*/
import types from "../actions/actionTypes";
import { Base64 } from 'js-base64';

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-20 16:40:32
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-27 23:39:56
* @LastEditTime: 2019-12-02 19:22:30
*/
import { Base64 } from 'js-base64';
import types from '../actions/actionTypes';
@ -49,21 +49,22 @@ const init = {
errMsg: ''
}
},
ojTestCaseValidate: [],
testCases: [
// {
// input: "11 22",
// output: "33",
// input: "",
// output: "",
// position: 1, // 当前测试用例位置
// isAdd: true // 是否是新增
// }
], // 测试用例集合
testCasesValidate: [], // 测试用例验证
position: 1, // TODO 每次加载信息时同步指定positio值
score: 200, // 分值: 选择难易度后自动计算分值 200 | 500 | 1000
code: '', // 提交的代码
identifier: '', // OJ表单id
loading: false, // 僵尸loading标志
testCodeStatus: 'default', // 调试代码状态 default(默认值) | loading(加载中) | loaded(加载完成) | userCase(用户自定义测试用例) | finish(测试完成)
openTestCodeIndex: [0] // 展开的测试用例: 数组, 当出错时,展开所有出错的测试用例, 默认展开第一个
}
const tcValidateObj = {
@ -129,16 +130,16 @@ const ojFormReducer = (state = initialState, action) => {
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);
const { testCase, tcValidate } = action.payload;
const tcArrs = state.testCases.concat([testCase]);
const tcValidateArrs = state.testCasesValidate.concat([tcValidate]);
state.position++; // 位置递增
const len = tcArrs.length - 1;
return {
...state,
testCases: [...tcArrs],
ojTestCaseValidate: [...tcVArrs]
testCasesValidate: [...tcValidateArrs],
openTestCodeIndex: [len] // 当前展开的测试用例
};
case types.DELETE_TEST_CASE:
const { position } = action.payload;
@ -201,6 +202,7 @@ const ojFormReducer = (state = initialState, action) => {
position: curPosition + 1,
code: cbcode,
testCases: curTestCases,
testCasesValidate: curTcValidates,
testCodeStatus: hack_sets.length > 0 ? 'userCase' : 'default'
}
case types.CLEAR_JSFORM_STORE:
@ -258,6 +260,26 @@ const ojFormReducer = (state = initialState, action) => {
ojTestCaseValidate: [...curOOjTestValidate],
testCases: [...curOTestValues]
}
case types.UPDATE_TEST_AND_VALIDATE: // 保存或更新测试用例值
const tempValitate = action.payload.testCaseValidate;
const curOjTestCaseValidate = state.testCasesValidate.map((tc, i) => {
return Object.assign({}, tc, tempValitate[i]);
});
return {
...state,
testCasesValidate: [...curOjTestCaseValidate]
}
case types.UPDATE_OPEN_TESTCASE_INDEX:
const tempArr = [];
const tIndex = state.openTestCodeIndex.findIndex(i => i === action.payload);
if (tIndex === -1) {
tempArr.push(action.payload);
}
console.log(tempArr);
return {
...state,
openTestCodeIndex: tempArr
}
default:
return state;
}

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-20 10:55:38
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-28 18:58:20
* @LastEditTime: 2019-12-03 14:10:13
*/
import axios from 'axios';
@ -65,6 +65,7 @@ export async function fetchUserProgramDetail (identifier) {
// 获取提交记录
export async function fetchUserCommitRecord (identifier) {
console.log('identifier=====', identifier);
const url = `/myproblems/${identifier}/submit_records.json`;
return axios.get(url);
}

Loading…
Cancel
Save