diff --git a/public/react/package.json b/public/react/package.json index b06c70e5f..66db5e6f8 100644 --- a/public/react/package.json +++ b/public/react/package.json @@ -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", diff --git a/public/react/src/App.js b/public/react/src/App.js index 217848308..1c15b3dab 100644 --- a/public/react/src/App.js +++ b/public/react/src/App.js @@ -667,8 +667,15 @@ class App extends Component { }/> - - + () + } /> + () + } /> { 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 ( @@ -128,13 +131,13 @@ const ControlSetting = (props) => { const mapStateToProps = (state) => { const {commonReducer, ojForUserReducer} = state; - const { loading, excuteState, submitLoading } = commonReducer; - const { user_program_identifier, commitRecordDetail } = ojForUserReducer; + const {loading, excuteState, submitLoading } = commonReducer; + const { commitRecordDetail } = ojForUserReducer; return { loading, submitLoading, excuteState, - identifier: user_program_identifier, + // identifier: user_program_identifier, commitRecordDetail // 提交详情 }; }; diff --git a/public/react/src/modules/developer/components/errorResult/index.js b/public/react/src/modules/developer/components/errorResult/index.js new file mode 100644 index 000000000..236a0cb9d --- /dev/null +++ b/public/react/src/modules/developer/components/errorResult/index.js @@ -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 = ( + + 输入: [input] + 输出: [output] + 预期: [expected_output] + + ); + break; + case 2: // 评测超时 + return ( + + 执行超时,限制时限: {`${execute_time}s`} + + ); + break; + case 3: // 创建pod失败 + break; + case 4: // 编译失败 + break; + case 5: // 执行失败 + break; + default: + return result; + } + return result; + } + + return ( + + {renderError(detail)} + + ); +} + +export default ErrorResult; diff --git a/public/react/src/modules/developer/components/errorResult/index.scss b/public/react/src/modules/developer/components/errorResult/index.scss new file mode 100644 index 000000000..e69de29bb diff --git a/public/react/src/modules/developer/components/execResult/index.js b/public/react/src/modules/developer/components/execResult/index.js index c659021ed..5f8526ca4 100644 --- a/public/react/src/modules/developer/components/execResult/index.js +++ b/public/react/src/modules/developer/components/execResult/index.js @@ -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 = () => ( @@ -65,6 +66,7 @@ function ExecResult (props) { 输入: {input} 输出: {output} + 执行用时: {`${execute_time}s`} ); } 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]); diff --git a/public/react/src/modules/developer/components/initTabCtx/index.js b/public/react/src/modules/developer/components/initTabCtx/index.js index c4b989354..221558cdc 100644 --- a/public/react/src/modules/developer/components/initTabCtx/index.js +++ b/public/react/src/modules/developer/components/initTabCtx/index.js @@ -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); } })); diff --git a/public/react/src/modules/developer/components/myMonacoEditor/index.js b/public/react/src/modules/developer/components/myMonacoEditor/index.js index 56f30ea2e..989fc07ba 100644 --- a/public/react/src/modules/developer/components/myMonacoEditor/index.js +++ b/public/react/src/modules/developer/components/myMonacoEditor/index.js @@ -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) => { ) -} +}; 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 diff --git a/public/react/src/modules/developer/newOrEditTask/index.js b/public/react/src/modules/developer/newOrEditTask/index.js index 1c6da31c1..6e1ec7a4a 100644 --- a/public/react/src/modules/developer/newOrEditTask/index.js +++ b/public/react/src/modules/developer/newOrEditTask/index.js @@ -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) => { 后退 - {props.name || ''} + {props.name || ''} { - + @@ -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( diff --git a/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/AddTestDemo.js b/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/AddTestDemo.js index 1d001ee2d..b4b5983f9 100644 --- a/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/AddTestDemo.js +++ b/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/AddTestDemo.js @@ -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'; @@ -15,21 +15,19 @@ const { Panel } = Collapse; const { TextArea } = Input; const FormItem = Form.Item; const AddTestDemo = (props) => { - const { + 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 ( - - + handleChangeCollapse()}> + 输入} - validateStatus={input.validateStatus} - help={input.errMsg} + validateStatus={testCaseValidate.input.validateStatus} + help={testCaseValidate.input.errMsg} colon={ false } > { 输出} - validateStatus={output.validateStatus} - help={output.errMsg} + validateStatus={testCaseValidate.output.validateStatus} + help={testCaseValidate.output.errMsg} colon={ false } > { onChange={handleOutputChange} disabled={isDisabled(testCase)}/> + {renderSubmitBtn()} @@ -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)); diff --git a/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index.js b/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index.js index c1cb08ec3..702e78040 100644 --- a/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index.js +++ b/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index.js @@ -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) => ( - - )); + console.log(openTestCodeIndex); + return testCases.map((item, i) => { + return + }); }; // 添加测试用例 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 ( - + @@ -242,13 +308,15 @@ class EditTab extends PureComponent { {/* 添加测试用例 */} - + 测试用例 添加测试用例 { renderTestCase() } + + ) } @@ -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) => ({ diff --git a/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index.scss b/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index.scss index 34f00c7cb..f8fa3bd71 100644 --- a/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index.scss +++ b/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index.scss @@ -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{ diff --git a/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index2.js b/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index2.js new file mode 100644 index 000000000..ab9b0f34e --- /dev/null +++ b/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index2.js @@ -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 ( + + {name} + + ({subTitle}) + + + ) + } else { + return ( + {name} + ) + } + }; + // 获取下拉列表项 + const getOptions = (key) => { + return maps[key].map((opt, i) => { + return ( + {opt.title} + ); + }); + }; + // 向外暴露的方法 + 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 ( + + ) + }); + }; + // 提交测试用例 + 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 ( + + + {myLabel(jcLabel['name'])}} + > + { + getFieldDecorator('name', { + rules: [ + { required: true, message: '任务名称不能为空' } + ], + initialValue: ojForm.name + })() + } + + + {myLabel(jcLabel['language'])}} + > + { + getFieldDecorator('language', { + rules: [ + { required: true, message: '语言不能为空' } + ], + initialValue: ojForm.language + })( + + {getOptions('language')} + ) + } + + + {myLabel(jcLabel['description'])}} + > + { + getFieldDecorator('description', { + rules: [ + { required: true, message: '描述信息不能为空' } + ], + initialValue: ojForm.description + })() + } + + + {myLabel(jcLabel['difficult'], '任务的难易程度')}} + > + { + getFieldDecorator('difficult', { + rules: [ + { required: true, message: '难度不能为空' } + ], + initialValue: `${ojForm.difficult || ''}` + })( + + {getOptions('difficult')} + + ) + } + + + {myLabel(jcLabel['timeLimit'], '程序允许时间限制时长,单位:秒')}} + > + { + getFieldDecorator('timeLimit', { + rules: [ + { required: true, message: '时间限制不能为空' } + ], + initialValue: ojForm.timeLimit + })() + } + + + {myLabel(jcLabel['category'], '任务所属分类')}} + > + { + getFieldDecorator('category', { + rules: [ + { required: true, message: '任务名称不能为空' } + ], + initialValue: `${ojForm.category || ''}` + })( + + {getOptions('category')} + + ) + } + + + {myLabel(jcLabel['openOrNot'])}} + > + { + getFieldDecorator('openOrNot', { + rules: [ + { required: true, message: '任务名称不能为空' } + ], + initialValue: `${ojForm.openOrNot}` + })( + + {getOptions('openOrNot')} + + ) + } + + + {/* 添加测试用例 */} + + 测试用例 + 添加测试用例 + + + { renderTestCase() } + + + ); +} + +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) +)); diff --git a/public/react/src/modules/developer/newOrEditTask/leftpane/index.js b/public/react/src/modules/developer/newOrEditTask/leftpane/index.js index e18f41802..1743551b5 100644 --- a/public/react/src/modules/developer/newOrEditTask/leftpane/index.js +++ b/public/react/src/modules/developer/newOrEditTask/leftpane/index.js @@ -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 } diff --git a/public/react/src/modules/developer/newOrEditTask/leftpane/index.scss b/public/react/src/modules/developer/newOrEditTask/leftpane/index.scss index 66330a556..6df450932 100644 --- a/public/react/src/modules/developer/newOrEditTask/leftpane/index.scss +++ b/public/react/src/modules/developer/newOrEditTask/leftpane/index.scss @@ -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'; diff --git a/public/react/src/modules/developer/newOrEditTask/rightpane/index.js b/public/react/src/modules/developer/newOrEditTask/rightpane/index.js index 0dc8ccd4d..d1b022cac 100644 --- a/public/react/src/modules/developer/newOrEditTask/rightpane/index.js +++ b/public/react/src/modules/developer/newOrEditTask/rightpane/index.js @@ -1,215 +1,63 @@ /* - * @Description: 右侧代码块 - * @Author: tangjiang - * @Date: 2019-11-18 08:42:04 - * @Last Modified by: tangjiang - * @Last Modified time: 2019-11-20 00:00:34 + * @Description: + * @Author: tangjiang + * @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 '; - }); // 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); - }); - } +function RightPane (props, ref) { - // 提交按钮点击 - 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 { + // identifier, + onSubmitForm, + saveOjFormCode + } = props; - // 控制台点击时 添加active属性 - const classNames = `control_tab ${showTextResult ? 'move_up move_up_final' : 'move_down_final'}`; - - // 配置编辑器属性 - const editorOptions = { - selectOnLineNumbers: true, - automaticLayout: true, - fontSize: `${fontSize}px` - } - - // 返回渲染值 + // 代码改变时,保存 + const handleCodeChange = (code) => { + // 保存用户输入的代码 + saveOjFormCode(code); + } + // 启动调试代码 + // const handleDebuggerCode = (value) => { + // console.log('调用的代码调试====', value); + // } return ( - - - - - - - {/** 代码编辑器 */} - - {/* 控制台信息 */} - - - - this.formRef = form }/> - - - 代码执行结果 - - - - 控制台 - - {/* 调试代码 */} - {props.identifier ? '更新' : '提交'} - - - - - - - - - - ); + + + + + ) } 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); diff --git a/public/react/src/modules/developer/newOrEditTask/rightpane/index1.js b/public/react/src/modules/developer/newOrEditTask/rightpane/index1.js new file mode 100644 index 000000000..0dc8ccd4d --- /dev/null +++ b/public/react/src/modules/developer/newOrEditTask/rightpane/index1.js @@ -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 '; + }); // 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 ( + + + + + + + {/** 代码编辑器 */} + + {/* 控制台信息 */} + + + + this.formRef = form }/> + + + 代码执行结果 + + + + 控制台 + + {/* 调试代码 */} + {props.identifier ? '更新' : '提交'} + + + + + + + + + + ); +} + +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); diff --git a/public/react/src/modules/developer/quillEditor/index.js b/public/react/src/modules/developer/quillEditor/index.js index 57fd9f2a4..eeadbd095 100644 --- a/public/react/src/modules/developer/quillEditor/index.js +++ b/public/react/src/modules/developer/quillEditor/index.js @@ -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 = { diff --git a/public/react/src/modules/developer/split_pane_resizer.scss b/public/react/src/modules/developer/split_pane_resizer.scss index fbc4fdd1b..7700cf7db 100644 --- a/public/react/src/modules/developer/split_pane_resizer.scss +++ b/public/react/src/modules/developer/split_pane_resizer.scss @@ -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; diff --git a/public/react/src/modules/developer/studentStudy/index.js b/public/react/src/modules/developer/studentStudy/index.js index d3c11df91..3af736006 100644 --- a/public/react/src/modules/developer/studentStudy/index.js +++ b/public/react/src/modules/developer/studentStudy/index.js @@ -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) => { - + - 我是昵称 + {(mygetHelmetapi &&mygetHelmetapi.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( diff --git a/public/react/src/modules/developer/studentStudy/leftpane/commitRecord/index.js b/public/react/src/modules/developer/studentStudy/leftpane/commitRecord/index.js index ddb3cf42e..a2e7c7465 100644 --- a/public/react/src/modules/developer/studentStudy/leftpane/commitRecord/index.js +++ b/public/react/src/modules/developer/studentStudy/leftpane/commitRecord/index.js @@ -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 ( - {/* - + + 执行结果: {reviewResult[status]} - - - 复制错误信息 - - + + + + 复制错误信息 + + + 显示详情 - - */} - {/* 错误代码 */} + + + {error_msg} ); } 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 ( - {renderCtx()} + {renderRecordDetail()} 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) => ({ diff --git a/public/react/src/modules/developer/studentStudy/leftpane/commitRecord/index.scss b/public/react/src/modules/developer/studentStudy/leftpane/commitRecord/index.scss index 102ffb4e4..bed7b8f96 100644 --- a/public/react/src/modules/developer/studentStudy/leftpane/commitRecord/index.scss +++ b/public/react/src/modules/developer/studentStudy/leftpane/commitRecord/index.scss @@ -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; } } \ No newline at end of file diff --git a/public/react/src/modules/developer/studentStudy/leftpane/index.js b/public/react/src/modules/developer/studentStudy/leftpane/index.js index 5de9d5e8c..f1327e006 100644 --- a/public/react/src/modules/developer/studentStudy/leftpane/index.js +++ b/public/react/src/modules/developer/studentStudy/leftpane/index.js @@ -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'; diff --git a/public/react/src/modules/developer/studentStudy/leftpane/index.scss b/public/react/src/modules/developer/studentStudy/leftpane/index.scss index 4b9d41ac0..19d2703c9 100644 --- a/public/react/src/modules/developer/studentStudy/leftpane/index.scss +++ b/public/react/src/modules/developer/studentStudy/leftpane/index.scss @@ -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; diff --git a/public/react/src/modules/developer/studentStudy/rightpane/index.js b/public/react/src/modules/developer/studentStudy/rightpane/index.js index 0e1125bb8..216deffdb 100644 --- a/public/react/src/modules/developer/studentStudy/rightpane/index.js +++ b/public/react/src/modules/developer/studentStudy/rightpane/index.js @@ -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 ( - - + + ); } @@ -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( diff --git a/public/react/src/redux/actions/actionTypes.js b/public/react/src/redux/actions/actionTypes.js index a33134285..c6c50bce6 100644 --- a/public/react/src/redux/actions/actionTypes.js +++ b/public/react/src/redux/actions/actionTypes.js @@ -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; diff --git a/public/react/src/redux/actions/index.js b/public/react/src/redux/actions/index.js index 23b111618..212ff81e8 100644 --- a/public/react/src/redux/actions/index.js +++ b/public/react/src/redux/actions/index.js @@ -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 } \ No newline at end of file diff --git a/public/react/src/redux/actions/ojForUser.js b/public/react/src/redux/actions/ojForUser.js index 66c0503e3..337a60144 100644 --- a/public/react/src/redux/actions/ojForUser.js +++ b/public/react/src/redux/actions/ojForUser.js @@ -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,22 +68,25 @@ 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, { - code: userCode - }).then(res => { - // 是否更新了代码, 目的是当代码没有更新时不调用更新代码接口,目录没有实现 - // TODO 需要优化 - dispatch({ - type: types.IS_UPDATE_CODE, - flag: false + if (isUpdateCode) { + fetchUpdateCode(identifier, { + code: userCode + }).then(res => { + // 是否更新了代码, 目的是当代码没有更新时不调用更新代码接口,目录没有实现 + // TODO 需要优化 + dispatch({ + type: types.IS_UPDATE_CODE, + flag: false + }); + // debuggerCode(identifier, inputValue); + dispatch(debuggerCode(identifier, inputValue, type)); }); - // debuggerCode(identifier, inputValue); + } 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; + 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({ // 加载完成 - type: types.TEST_CODE_STATUS, - payload: 'loaded' - }); dispatch({ // 改变 loading 值 type: types.LOADING_STATUS, payload: false }); - // 加载状态变成finish - dispatch({ // 加载完成 + // 保存执行状态 + dispatch({ type: types.TEST_CODE_STATUS, payload: 'finish' }); + } else { + // 回滚提交按钮状态 + dispatch({ + type: types.SUBMIT_LOADING_STATUS, + payload: false + }); + // 改变tab值至提交记录(只在提交时才跳转,测评时,切换到代码执行结果就可以了) + dispatch({ + type: types.CHANGE_USER_CODE_TAB, + payload: 'record' + }); + // 重新调用一下提交记录接口 + dispatch(getUserCommitRecord(identifier)); } - - dispatch({ - type: types.COMMIT_RECORD_DETAIL, - payload: returnData - }); - // 改变tab值至 record - dispatch({ - type: types.CHANGE_USER_CODE_TAB, - payload: 'record' + } + }).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 }); - // 重新调用一下提交记录接口 - dispatch(getUserCommitRecord(identifier)); } - }).catch(err => { - clearInterval(timer); - timer = null; }); } + // 开启定时器,调用监听接口 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({ diff --git a/public/react/src/redux/actions/ojForm.js b/public/react/src/redux/actions/ojForm.js index e88dffc05..35a23fd2e 100644 --- a/public/react/src/redux/actions/ojForm.js +++ b/public/react/src/redux/actions/ojForm.js @@ -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 + } +} \ No newline at end of file diff --git a/public/react/src/redux/reducers/commonReducer.js b/public/react/src/redux/reducers/commonReducer.js index c0b6c5c48..c551a803f 100644 --- a/public/react/src/redux/reducers/commonReducer.js +++ b/public/react/src/redux/reducers/commonReducer.js @@ -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"; diff --git a/public/react/src/redux/reducers/ojForUserReducer.js b/public/react/src/redux/reducers/ojForUserReducer.js index a15d7d4a7..0ce70baf4 100644 --- a/public/react/src/redux/reducers/ojForUserReducer.js +++ b/public/react/src/redux/reducers/ojForUserReducer.js @@ -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'; diff --git a/public/react/src/redux/reducers/ojFormReducer.js b/public/react/src/redux/reducers/ojFormReducer.js index c60328c27..bdf67af1b 100644 --- a/public/react/src/redux/reducers/ojFormReducer.js +++ b/public/react/src/redux/reducers/ojFormReducer.js @@ -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; } diff --git a/public/react/src/services/ojService.js b/public/react/src/services/ojService.js index 30b0d35a2..f8d888890 100644 --- a/public/react/src/services/ojService.js +++ b/public/react/src/services/ojService.js @@ -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); }
输入: [input]
输出: [output]
预期: [expected_output]
执行超时,限制时限: {`${execute_time}s`}
输入: {input}
输出: {output}
执行用时: {`${execute_time}s`}
{props.name || ''}
- {/* 调试代码 */} - {props.identifier ? '更新' : '提交'} -
+ {/* 调试代码 */} + {props.identifier ? '更新' : '提交'} +
执行结果: {reviewResult[status]} - - - 复制错误信息 - - +
+ + 复制错误信息 + +
显示详情 - -