parent
b3f9dae41a
commit
12b725d215
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* @Description:
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-20 23:10:48
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-22 10:05:42
|
||||
*/
|
||||
const jcLabel = {
|
||||
name: '任务名称',
|
||||
language: '编程语言',
|
||||
description: '描述',
|
||||
difficult: '难易度',
|
||||
category: '分类',
|
||||
openOrNot: '公开程序',
|
||||
timeLimit: '时间限制'
|
||||
}
|
||||
|
||||
export default jcLabel;
|
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* @Description: 添加测试用例
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-21 09:19:38
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-22 19:45:48
|
||||
*/
|
||||
import './index.scss';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Collapse, Icon, Input, Form, Button, Modal } from 'antd';
|
||||
import { connect } from 'react-redux';
|
||||
const { Panel } = Collapse;
|
||||
const { TextArea } = Input;
|
||||
const FormItem = Form.Item;
|
||||
const AddTestDemo = (props) => {
|
||||
const {
|
||||
form: { getFieldDecorator },
|
||||
onSubmitTest,
|
||||
onDeleteTest,
|
||||
testCase,
|
||||
key
|
||||
} = props;
|
||||
|
||||
const [isEditor, setIsEditor] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
// console.log('测试用例属性: ====>>>>', props);
|
||||
// 删除操作
|
||||
const handleDeletePanel = (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
// console.log('点击的删除按钮')
|
||||
Modal.confirm({
|
||||
title: '删除',
|
||||
content: '确定要删除当前测试用例吗?',
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
onOk() {
|
||||
console.log('确定删除');
|
||||
onDeleteTest(testCase);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 右侧删除图标
|
||||
const genExtra = () => (
|
||||
<Icon
|
||||
type="close"
|
||||
onClick={handleDeletePanel}
|
||||
/>
|
||||
)
|
||||
|
||||
// 取消操作
|
||||
const handleReset = (e) => {
|
||||
e.preventDefault();
|
||||
props.form.resetFields();
|
||||
}
|
||||
|
||||
// 保存
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
props.form.validateFields((err, values) => {
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
console.log('提交表单: ', values);
|
||||
onSubmitTest(values);
|
||||
});
|
||||
}
|
||||
// 编辑后保存
|
||||
const handleEditorOrSave = (e) => {
|
||||
if (!isEditor) {
|
||||
setIsEditor(true);
|
||||
} else {
|
||||
// TODO 调用修改测试用例接口
|
||||
setIsEditor(false); // 保存后 设置 false
|
||||
setLoading(true);
|
||||
}
|
||||
}
|
||||
|
||||
const renderSubmitBtn = () => {
|
||||
const { identifier, testCase } = props;
|
||||
// console.log('========', identifier);
|
||||
// 1. 新增时,不显示按钮
|
||||
if (identifier) {
|
||||
if (testCase.isAdd) {
|
||||
return (
|
||||
<FormItem style={{ textAlign: 'right' }}>
|
||||
<Button style={{ marginRight: '20px' }} onClick={handleReset}>取消</Button>
|
||||
<Button type="primary" onClick={handleSubmit}>保存</Button>
|
||||
</FormItem>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<FormItem style={{ textAlign: 'right' }}>
|
||||
<Button onClick={handleEditorOrSave} loading={loading}>{isEditor ? '保存' : (loading ? '保存' : '编辑')}</Button>
|
||||
</FormItem>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 指定文本框是否可编辑
|
||||
const isDisabled = (testCase) => {
|
||||
return testCase.isAdd && !isEditor;
|
||||
};
|
||||
|
||||
return (
|
||||
<Collapse className={'collapse_area'}>
|
||||
<Panel header={`测试用例${testCase.position}`} extra={genExtra()} key={key}>
|
||||
<Form>
|
||||
<FormItem
|
||||
label='输入'
|
||||
>
|
||||
{
|
||||
getFieldDecorator('input', {
|
||||
rules: [
|
||||
{ required: true, message: '输入值不能为空'}
|
||||
],
|
||||
initialValue: testCase && testCase.input
|
||||
})(<TextArea rows={5} disabled={isDisabled(testCase)}/>)
|
||||
}
|
||||
</FormItem>
|
||||
<FormItem label="输出">
|
||||
{
|
||||
getFieldDecorator('output', {
|
||||
rules: [
|
||||
{required: true, message: '输出值不能为空'}
|
||||
],
|
||||
initialValue: testCase && testCase.output
|
||||
})(<Input disabled={isDisabled(testCase)}/>)
|
||||
}
|
||||
</FormItem>
|
||||
{/* <FormItem style={{ textAlign: 'right' }}>
|
||||
<Button style={{ marginRight: '20px' }} onClick={handleReset}>取消</Button>
|
||||
<Button type="primary" onClick={handleSubmit}>保存</Button>
|
||||
</FormItem> */}
|
||||
{renderSubmitBtn()}
|
||||
</Form>
|
||||
</Panel>
|
||||
</Collapse>
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
const ojFormReducer = state.ojFormReducer;
|
||||
return {
|
||||
identifier: ojFormReducer.identifier
|
||||
}
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(Form.create()(AddTestDemo));
|
@ -1,41 +1,288 @@
|
||||
/*
|
||||
* @Description:
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-20 10:35:40
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-22 19:10:53
|
||||
*/
|
||||
import './index.scss';
|
||||
import React, { PureComponent } from 'react';
|
||||
import { Form, Input, Button } from 'antd';
|
||||
import { Form, Input, Select, InputNumber, Button } from 'antd';
|
||||
import { connect } from 'react-redux';
|
||||
import AddTestDemo from './AddTestDemo';
|
||||
import actions from '../../../../../redux/actions';
|
||||
import jcLabel from '../../../../../constants';
|
||||
import MEditor from '../../../meditor/MEditor';
|
||||
// import { getUrl } from 'educoder';
|
||||
|
||||
// const path = getUrl("/editormd/lib/")
|
||||
// const mEditor = require('editor.md');
|
||||
// console.log(mEditor);
|
||||
|
||||
const FormItem = Form.Item;
|
||||
const { Option } = Select;
|
||||
const { TextArea } = Input;
|
||||
const maps = {
|
||||
language: [
|
||||
{ title: 'C', key: 'C' },
|
||||
{ title: 'C++', key: 'C++' },
|
||||
{ title: 'Python', key: 'Python' },
|
||||
{ title: 'Java', key: 'Java' }
|
||||
],
|
||||
difficult: [
|
||||
{ title: '简单', key: '1' },
|
||||
{ title: '中等', key: '2'},
|
||||
{ title: '困难', key: '3' }
|
||||
],
|
||||
category: [
|
||||
{ title: '程序设计', key: '1' },
|
||||
{ title: '算法', key: '2'}
|
||||
],
|
||||
openOrNot: [
|
||||
{ title: '公开', key: '1' },
|
||||
{ title: '私有', key: '0' }
|
||||
]
|
||||
}
|
||||
class EditTab extends PureComponent {
|
||||
|
||||
handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
this.props.form.validateFieldsAndScroll((err, value) => {
|
||||
if (!err) {
|
||||
console.log(value);
|
||||
}
|
||||
})
|
||||
constructor (props) {
|
||||
super(props);
|
||||
this.editorRef = React.createRef();
|
||||
}
|
||||
componentDidMount () {
|
||||
// this.createEditorMd('editormd_section', '100%', 400, '', '', '', path);
|
||||
}
|
||||
// 改变任务名称
|
||||
handleNameChange = (e) => {
|
||||
const value = e.target.value;
|
||||
const {
|
||||
validateOJName,
|
||||
// validateOjLanguage,
|
||||
// validateOjDescription,
|
||||
// validateOjDifficult,
|
||||
// validateOjTimeLimit,
|
||||
// validateOjCategory,
|
||||
// validateOpenOrNot
|
||||
} = this.props;
|
||||
validateOJName(value);
|
||||
}
|
||||
// 改变语言
|
||||
handleLanguageChange = (value) => {
|
||||
this.props.validateOjLanguage(value);
|
||||
}
|
||||
// 改变描述信息
|
||||
handleChangeDescription = (e) => {
|
||||
const value = e.target.value;
|
||||
this.props.validateOjDescription(value);
|
||||
}
|
||||
// 改变难易度
|
||||
handleChangeDifficult = (value) => {
|
||||
this.props.validateOjDifficult(value);
|
||||
}
|
||||
// 改变时间限制
|
||||
handleTimeLimitChange = (value) => {
|
||||
this.props.validateOjTimeLimit(value);
|
||||
}
|
||||
// 改变分类
|
||||
handleChangeCategory = (value) => {
|
||||
this.props.validateOjCategory(value);
|
||||
}
|
||||
// 改变公开程序
|
||||
handleChangeOpenOrNot = (value) => {
|
||||
this.props.validateOpenOrNot(value);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { form } = this.props;
|
||||
const { getFieldDecorator } = form;
|
||||
const {
|
||||
ojFormReducer: {ojForm, ojFormValidate},
|
||||
testCases = [], // 测试用例集合
|
||||
position, // 添加的测试用例位置
|
||||
addTestCase, // 添加测试用例
|
||||
deleteTestCase, // 删除测试用例
|
||||
} = this.props;
|
||||
console.log('当前位置: ', position);
|
||||
// 表单label
|
||||
const myLabel = (name, subTitle) => {
|
||||
if (subTitle) {
|
||||
return (
|
||||
<span className={'label_text'}>
|
||||
{name}
|
||||
<span className={'label_sub_text'}>
|
||||
({subTitle})
|
||||
</span>
|
||||
</span>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<span className={'label_text'}>{name}</span>
|
||||
)
|
||||
}
|
||||
};
|
||||
// 编程语言
|
||||
const getOptions = (key) => {
|
||||
return maps[key].map((opt, i) => {
|
||||
return (
|
||||
<Option value={opt.key} key={`opt_${i}`}>{opt.title}</Option>
|
||||
);
|
||||
});
|
||||
};
|
||||
// 提交测试用例
|
||||
const handleSubmitTest = (obj) => {
|
||||
console.log('提交的测试用例: ', obj);
|
||||
};
|
||||
// 删除测试用例
|
||||
const handleDeleteTest = (obj) => {
|
||||
console.log('删除的测试用例: ', obj);
|
||||
deleteTestCase(obj);
|
||||
};
|
||||
const renderTestCase = () => {
|
||||
return testCases.map((item, i) => (
|
||||
<AddTestDemo
|
||||
key={`key_${i}`}
|
||||
onSubmitTest={handleSubmitTest}
|
||||
onDeleteTest={handleDeleteTest}
|
||||
testCase={item}
|
||||
/>
|
||||
));
|
||||
};
|
||||
// 添加测试用例
|
||||
const handleAddTest = () => {
|
||||
console.log('添加测试用例');
|
||||
const obj = {
|
||||
input: '',
|
||||
output: '',
|
||||
position: position,
|
||||
isAdd: true // 新增的测试用例
|
||||
}
|
||||
addTestCase(obj);
|
||||
// TODO 点击新增时,需要滚到到最底部
|
||||
// this.editorRef.current.scrollTop
|
||||
// const oDiv = this.editorRef.current;
|
||||
// oDiv.scrollTo(oDiv.scrollLeft, 99999);
|
||||
// console.log(oDiv.scrollTop);
|
||||
// oDiv.scrollTop = 99999;
|
||||
}
|
||||
return (
|
||||
<div className={'editor_area'}>
|
||||
<Form onSubmit={this.handleSubmit}>
|
||||
<FormItem label="任务名称">
|
||||
{ getFieldDecorator('name', {
|
||||
rules: [
|
||||
{
|
||||
required: true, message: '任务名称不能为空'
|
||||
}
|
||||
]
|
||||
})(<Input placeholder="请输入任务名称" />)}
|
||||
<div className={'editor_area'} ref={this.editorRef}>
|
||||
<Form className={'editor_form'}>
|
||||
<FormItem
|
||||
className={`input_area flex_60`}
|
||||
label={<span>{myLabel(jcLabel['name'])}</span>}
|
||||
validateStatus={ojFormValidate.name.validateStatus}
|
||||
help={ojFormValidate.name.errMsg}
|
||||
colon={ false }
|
||||
>
|
||||
<Input
|
||||
maxLength={60}
|
||||
placeholder="请输入任务名称"
|
||||
value={ojForm.name}
|
||||
suffix={<span style={{ fontSize: '12px', color: 'rgba(0, 0, 0, 0.45)' }}>{60 - ojForm.name.length}</span>}
|
||||
onChange={this.handleNameChange}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
className={`input_area flex_40`}
|
||||
label={<span>{myLabel(jcLabel['language'])}</span>}
|
||||
validateStatus={ojFormValidate.language.validateStatus}
|
||||
help={ojFormValidate.language.errMsg}
|
||||
colon={ false }
|
||||
>
|
||||
<Select onChange={this.handleLanguageChange} defaultValue={`${ojForm.language}`}>
|
||||
{getOptions('language')}
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
className={`input_area flex_100`}
|
||||
label={<span>{myLabel(jcLabel['description'])}</span>}
|
||||
validateStatus={ojFormValidate.description.validateStatus}
|
||||
help={ojFormValidate.description.errMsg}
|
||||
colon={ false }
|
||||
>
|
||||
<TextArea rows={5} onChange={this.handleChangeDescription}/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
className={`input_area flex_50 flex_50_left`}
|
||||
label={<span>{myLabel(jcLabel['difficult'], '任务的难易程度')}</span>}
|
||||
validateStatus={ojFormValidate.difficult.validateStatus}
|
||||
help={ojFormValidate.difficult.errMsg}
|
||||
colon={ false }
|
||||
>
|
||||
<Select onChange={this.handleChangeDifficult} defaultValue={`${ojForm.difficult}`}>
|
||||
{getOptions('difficult')}
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button htmlType="submit">提交</Button>
|
||||
<FormItem
|
||||
className={`input_area flex_50 flex_50_right`}
|
||||
label={<span>{myLabel(jcLabel['timeLimit'], '程序允许时间限制时长,单位:秒')}</span>}
|
||||
validateStatus={ojFormValidate.timeLimit.validateStatus}
|
||||
help={ojFormValidate.timeLimit.errMsg}
|
||||
colon={ false }
|
||||
>
|
||||
<InputNumber min={0} style={{ width: '100%' }} onChange={this.handleTimeLimitChange}/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
className={`input_area flex_50 flex_50_left`}
|
||||
label={<span>{myLabel(jcLabel['category'], '任务所属分类')}</span>}
|
||||
validateStatus={ojFormValidate.category.validateStatus}
|
||||
help={ojFormValidate.category.errMsg}
|
||||
colon={ false }
|
||||
>
|
||||
<Select onChange={this.handleChangeCategory} defaultValue={`${ojForm.category}`}>
|
||||
{getOptions('category')}
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
className={`input_area flex_50 flex_50_right`}
|
||||
label={<span>{myLabel(jcLabel['openOrNot'], '社区:您的任务将向整个社会公开')}</span>}
|
||||
validateStatus={ojFormValidate.openOrNot.validateStatus}
|
||||
help={ojFormValidate.openOrNot.errMsg}
|
||||
colon={ false }
|
||||
>
|
||||
<Select onChange={this.handleChangeOpenOrNot} defaultValue={`${ojForm.openOrNot}`}>
|
||||
{getOptions('openOrNot')}
|
||||
</Select>
|
||||
</FormItem>
|
||||
</Form>
|
||||
|
||||
{/* 添加测试用例 */}
|
||||
<div className="test_demo_title">
|
||||
<h2>测试用例</h2>
|
||||
<Button type="primary" onClick={handleAddTest}>添加测试用例</Button>
|
||||
</div>
|
||||
<div className="test_demo_ctx">
|
||||
{ renderTestCase() }
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const EditTabForm = Form.create()(EditTab);
|
||||
export default EditTabForm;
|
||||
const mapStateToProps = (state) => {
|
||||
const ojFormReducer = state.ojFormReducer;
|
||||
return {
|
||||
ojFormReducer,
|
||||
testCases: ojFormReducer.testCases, // 测试用例
|
||||
position: ojFormReducer.position, // 测试用例位置
|
||||
}
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
// 任务名称校验
|
||||
validateOJName: (value) => dispatch(actions.validateOJName(value)),
|
||||
validateOjLanguage: (value) => dispatch(actions.validateOjLanguage(value)),
|
||||
validateOjDescription: (value) => dispatch(actions.validateOjDescription(value)),
|
||||
validateOjDifficult: (value) => dispatch(actions.validateOjDifficult(value)),
|
||||
validateOjTimeLimit: (value) => dispatch(actions.validateOjTimeLimit(value)),
|
||||
validateOjCategory: (value) => dispatch(actions.validateOjCategory(value)),
|
||||
validateOpenOrNot: (value) => dispatch(actions.validateOpenOrNot(value)),
|
||||
// 新增测试用例
|
||||
addTestCase: (value) => dispatch(actions.addTestCase(value)),
|
||||
// 删除测试用例
|
||||
deleteTestCase: (value) => dispatch(actions.deleteTestCase(value)),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(EditTab);
|
||||
|
@ -0,0 +1,66 @@
|
||||
.editor_area{
|
||||
height: calc(100vh - 110px);
|
||||
overflow-y: auto;
|
||||
padding: 20px 0;
|
||||
.editor_form{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.label_text{
|
||||
position: relative;
|
||||
font-size: 14px;
|
||||
&::before{
|
||||
display: inline-block;
|
||||
margin-right: 4px;
|
||||
color: #f5222d;
|
||||
font-size: 14px;
|
||||
font-family: SimSun,sans-serif;
|
||||
line-height: 1;
|
||||
content: '*';
|
||||
}
|
||||
}
|
||||
.input_area{
|
||||
display: inline-block;
|
||||
&.flex_60{
|
||||
padding-right: 20px;
|
||||
width: 60%;
|
||||
}
|
||||
&.flex_40{
|
||||
width: 40%;
|
||||
}
|
||||
&.flex_100{
|
||||
width: 100%;
|
||||
}
|
||||
&.flex_50{
|
||||
width: 50%;
|
||||
}
|
||||
&.flex_50_left{
|
||||
padding-right: 10px;
|
||||
}
|
||||
&.flex_50_right{
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
.label_sub_text{
|
||||
font-size: 12px;
|
||||
color: #999999;
|
||||
}
|
||||
.test_demo_title,
|
||||
.test_demo_ctx,
|
||||
.editor_form{
|
||||
margin: 0 30px;
|
||||
}
|
||||
.test_demo_title{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 60px;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.collapse_area{
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
|
||||
.new_add_task_wrap,
|
||||
.student_study_warp{
|
||||
height: 100vh;
|
||||
.task_header,
|
||||
.student_study_header{
|
||||
height: 65px;
|
||||
background:rgba(34,34,34,1);
|
||||
padding:0 30px;
|
||||
}
|
||||
|
||||
.task_header{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// justify-content: space-between;
|
||||
.header_btn,
|
||||
.header_title{
|
||||
color: #fff;
|
||||
}
|
||||
.header_btn{
|
||||
width: 88px;
|
||||
}
|
||||
.header_title{
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.split-pane-area{
|
||||
position: relative;
|
||||
height: calc(100% - 65px);
|
||||
.left_pane,
|
||||
.right_pane{
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.student_study_header{
|
||||
position: relative;
|
||||
.avator_nicker,
|
||||
.study_quit,
|
||||
.study_name{
|
||||
color: #fff;
|
||||
line-height: 65px;
|
||||
}
|
||||
|
||||
.avator_nicker,
|
||||
.study_quit{
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.student_nicker{
|
||||
margin-left: 20px;
|
||||
}
|
||||
.study_quit{
|
||||
float: right;
|
||||
}
|
||||
.study_name{
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.Resizer {
|
||||
background: #000;
|
||||
opacity: 0.2;
|
||||
z-index: 1;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-moz-background-clip: padding;
|
||||
-webkit-background-clip: padding;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
.Resizer:hover {
|
||||
-webkit-transition: all 2s ease;
|
||||
transition: all 2s ease;
|
||||
}
|
||||
|
||||
.Resizer.horizontal {
|
||||
height: 11px;
|
||||
margin: -5px 0;
|
||||
border-top: 5px solid rgba(255, 255, 255, 0);
|
||||
border-bottom: 5px solid rgba(255, 255, 255, 0);
|
||||
cursor: row-resize;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.Resizer.horizontal:hover {
|
||||
border-top: 5px solid rgba(0, 0, 0, 0.5);
|
||||
border-bottom: 5px solid rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.Resizer.vertical {
|
||||
width: 11px;
|
||||
margin: 0 -5px;
|
||||
border-left: 5px solid rgba(255, 255, 255, 0);
|
||||
border-right: 5px solid rgba(255, 255, 255, 0);
|
||||
cursor: col-resize;
|
||||
}
|
||||
|
||||
.Resizer.vertical:hover {
|
||||
border-left: 5px solid rgba(0, 0, 0, 0.5);
|
||||
border-right: 5px solid rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
.Resizer.disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.Resizer.disabled:hover {
|
||||
border-color: transparent;
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* @Description: 学员学习
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-23 10:53:19
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-23 11:48:10
|
||||
*/
|
||||
import './index.scss';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import SplitPane from 'react-split-pane';
|
||||
import LeftPane from './leftpane';
|
||||
import RightPane from './rightpane';
|
||||
import { Button, Avatar } from 'antd';
|
||||
|
||||
const StudentStudy = (props) => {
|
||||
|
||||
return (
|
||||
<div className={'student_study_warp'}>
|
||||
<div className={'student_study_header'}>
|
||||
<div className={'avator_nicker'}>
|
||||
<Avatar icon="user" />
|
||||
<span className={'student_nicker'}>
|
||||
我是昵称
|
||||
</span>
|
||||
</div>
|
||||
<div className={'study_name'}>
|
||||
<span>乘积最大序列</span>
|
||||
</div>
|
||||
<div className={'study_quit'}>
|
||||
<Button>退出</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="split-pane-area">
|
||||
<SplitPane split="vertical" minSize={200} maxSize={-200} defaultSize="50%">
|
||||
<div className={'split-pane-left'}>
|
||||
<LeftPane />
|
||||
</div>
|
||||
<SplitPane split="vertical" defaultSize="100%" allowResize={false}>
|
||||
<RightPane />
|
||||
<div />
|
||||
</SplitPane>
|
||||
</SplitPane>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => ({});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(StudentStudy);
|
||||
|
||||
|
@ -0,0 +1 @@
|
||||
@import '../split_pane_resizer.scss';
|
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* @Description:
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-23 11:33:41
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-23 11:34:18
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
const LeftPane = (props) => {
|
||||
|
||||
return (
|
||||
<h2>左侧内容</h2>
|
||||
);
|
||||
}
|
||||
|
||||
export default LeftPane;
|
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* @Description:
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-23 11:34:32
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-23 11:34:48
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
const RightPane = (props) => {
|
||||
|
||||
return (
|
||||
<h2>右侧内容</h2>
|
||||
);
|
||||
}
|
||||
|
||||
export default RightPane;
|
@ -0,0 +1,234 @@
|
||||
/*
|
||||
* @Description: 开发者社区编辑模块
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-20 16:35:46
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-22 20:07:04
|
||||
*/
|
||||
import types from './actionTypes';
|
||||
import jcLabel from '../../constants';
|
||||
import { fetchPostOjForm, fetchGetOjById } from '../../services/ojService';
|
||||
|
||||
// 表单字段映射
|
||||
const maps = {
|
||||
name: {
|
||||
label: jcLabel['name'],
|
||||
type: types.VALIDATE_OJ_NAME
|
||||
},
|
||||
language: {
|
||||
label: jcLabel['language'],
|
||||
type: types.VALIDATE_OJ_LANGUAGE
|
||||
},
|
||||
description: {
|
||||
label: jcLabel['description'],
|
||||
type: types.VALIDATE_OJ_DESCRIPTION
|
||||
},
|
||||
difficult: {
|
||||
label: jcLabel['difficult'],
|
||||
type: types.VALIDATE_OJ_DIFFICULT
|
||||
},
|
||||
timeLimit: {
|
||||
label: jcLabel['timeLimit'],
|
||||
type: types.VALIDATE_OJ_TIMELIMIT
|
||||
},
|
||||
category: {
|
||||
label: jcLabel['category'],
|
||||
type: types.VALIDATE_OJ_CATEGORY
|
||||
},
|
||||
openOrNot: {
|
||||
label: jcLabel['openOrNot'],
|
||||
type: types.VALIDATE_OJ_OPENORNOT
|
||||
}
|
||||
};
|
||||
|
||||
// 非空校验
|
||||
const emptyValidate = (key, value) => {
|
||||
if ([undefined, '', null].includes(value)) {
|
||||
return {
|
||||
[key]: {
|
||||
validateStatus: 'error',
|
||||
errMsg: `${maps[key].label}不能为空`
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
[key]: {
|
||||
validateStatus: '',
|
||||
errMsg: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 组装字段值及校验信息
|
||||
const payloadInfo = (key, value, errMsg, validateInfo) => ({
|
||||
ojForm: {
|
||||
[key]: errMsg ? '' : value
|
||||
},
|
||||
ojFormValidate: {
|
||||
[key]: validateInfo
|
||||
}
|
||||
});
|
||||
|
||||
// 表单提交验证
|
||||
export const validateOjForm = () => {
|
||||
return (dispatch, getState) => {
|
||||
const {ojForm} = getState().ojFormReducer;
|
||||
let keys = Object.keys(ojForm);
|
||||
// 循环判断每个字段是否为空
|
||||
let hasSuccess = true;
|
||||
keys.forEach(key => {
|
||||
const value = ojForm[key];
|
||||
const validateResult = emptyValidate(key, value);
|
||||
const errMsg = validateResult[key].errMsg;
|
||||
if (errMsg) {
|
||||
hasSuccess = false;
|
||||
dispatch(
|
||||
{
|
||||
type: maps[key].type,
|
||||
payload: payloadInfo(key, value, errMsg, validateResult[key])
|
||||
}
|
||||
)
|
||||
}
|
||||
});
|
||||
// 验证成功后,调用提交接口
|
||||
if (hasSuccess) {
|
||||
console.log('表单保存的数据为: ', getState());
|
||||
const {ojFormReducer} = getState();
|
||||
const {code, score, ojForm, testCases} = ojFormReducer;
|
||||
const {category, description, difficult, language, name, openOrNot, timeLimit} = ojForm;
|
||||
const params = {
|
||||
hack: {
|
||||
name,
|
||||
description,
|
||||
difficult,
|
||||
category,
|
||||
'open_or_not': openOrNot,
|
||||
'time_limit': timeLimit,
|
||||
score
|
||||
},
|
||||
'hack_sets': testCases,
|
||||
'hack_codes': {
|
||||
code,
|
||||
language
|
||||
}
|
||||
}
|
||||
fetchPostOjForm(params).then(res => {
|
||||
// TODO
|
||||
}).catch(err => {
|
||||
// TODO
|
||||
});
|
||||
// dispatch({type: types.VALIDATE_OJ_FORM});
|
||||
}
|
||||
}
|
||||
};
|
||||
// 保存提交的代码
|
||||
export const saveOjFormCode = (value) => {
|
||||
return {
|
||||
type: types.SAVE_OJ_FORM_CODE,
|
||||
payload: value
|
||||
};
|
||||
}
|
||||
// 验证任务名称
|
||||
export const validateOJName = (value) => {
|
||||
const validate = emptyValidate('name', value)['name'];
|
||||
const errMsg = validate.errMsg;
|
||||
return {
|
||||
type: types.VALIDATE_OJ_NAME,
|
||||
payload: payloadInfo('name', value, errMsg, validate)
|
||||
}
|
||||
};
|
||||
// 验证编程语言
|
||||
export const validateOjLanguage = (value) => {
|
||||
const validate = emptyValidate('language', value)['language'];
|
||||
const errMsg = validate.errMsg;
|
||||
return {
|
||||
type: types.VALIDATE_OJ_LANGUAGE,
|
||||
payload: payloadInfo('language', value, errMsg, validate)
|
||||
}
|
||||
};
|
||||
// 验证描述
|
||||
export const validateOjDescription = (value) => {
|
||||
// createAction('description', value, types.VALIDATE_OJ_DESCRIPTION);
|
||||
const validate = emptyValidate('description', value)['description'];
|
||||
const errMsg = validate.errMsg;
|
||||
return {
|
||||
type: types.VALIDATE_OJ_DESCRIPTION,
|
||||
payload: payloadInfo('description', value, errMsg, validate)
|
||||
}
|
||||
};
|
||||
// 验证难易度
|
||||
export const validateOjDifficult = (value) => {
|
||||
// createAction('difficult', value, types.VALIDATE_OJ_DIFFICULT);
|
||||
const validate = emptyValidate('difficult', value)['difficult'];
|
||||
const errMsg = validate.errMsg;
|
||||
return {
|
||||
type: types.VALIDATE_OJ_DIFFICULT,
|
||||
payload: payloadInfo('difficult', value, errMsg, validate)
|
||||
}
|
||||
};
|
||||
// 验证时间限制
|
||||
export const validateOjTimeLimit = (value) => {
|
||||
// createAction('timeLimit', value, types.VALIDATE_OJ_TIMELIMIT);
|
||||
const validate = emptyValidate('timeLimit', value)['timeLimit'];
|
||||
const errMsg = validate.errMsg;
|
||||
return {
|
||||
type: types.VALIDATE_OJ_TIMELIMIT,
|
||||
payload: payloadInfo('timeLimit', value, errMsg, validate)
|
||||
}
|
||||
};
|
||||
// 验证分类
|
||||
export const validateOjCategory = (value) => {
|
||||
// createAction('category', value, types.VALIDATE_OJ_CATEGORY);
|
||||
const validate = emptyValidate('category', value)['category'];
|
||||
const errMsg = validate.errMsg;
|
||||
return {
|
||||
type: types.VALIDATE_OJ_CATEGORY,
|
||||
payload: payloadInfo('category', value, errMsg, validate)
|
||||
}
|
||||
};
|
||||
// 验证公开程序
|
||||
export const validateOpenOrNot = (value) => {
|
||||
const validate = emptyValidate('openOrNot', value)['openOrNot'];
|
||||
const errMsg = validate.errMsg;
|
||||
return {
|
||||
type: types.VALIDATE_OJ_OPENORNOT,
|
||||
payload: payloadInfo('openOrNot', value, errMsg, validate)
|
||||
}
|
||||
};
|
||||
// 新增测试用例
|
||||
export const addTestCase = (obj) => {
|
||||
return {
|
||||
type: types.ADD_TEST_CASE,
|
||||
payload: obj
|
||||
}
|
||||
}
|
||||
// 删除测试用例
|
||||
export const deleteTestCase = (obj) => {
|
||||
return {
|
||||
type: types.DELETE_TEST_CASE,
|
||||
payload: obj
|
||||
}
|
||||
}
|
||||
// 更新id号编辑OJ
|
||||
export const getOJFormById = (id) => {
|
||||
return (dispatch) => {
|
||||
fetchGetOjById(id).then(res => {
|
||||
console.log('获取OJ表单信息成功: ', res);
|
||||
})
|
||||
}
|
||||
}
|
||||
// 保存表单 id 信息
|
||||
export const saveOJFormId = (id) => {
|
||||
return {
|
||||
type: types.SAVE_OJ_FORM_ID,
|
||||
payload: id
|
||||
}
|
||||
}
|
||||
// 清空测试用例集合
|
||||
export const clearOJFormStore = () => {
|
||||
return {
|
||||
type: types.CLEAR_JSFORM_STORE
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* @Description: 开发者社区action
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-20 10:48:24
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-22 16:44:44
|
||||
*/
|
||||
import types from './actionTypes';
|
||||
import { fetchOJList } from '../../services/ojService';
|
||||
|
||||
export const getOJList = (params) => {
|
||||
return (dispatch) => {
|
||||
fetchOJList(params).then((res) => {
|
||||
console.log('获取列表信息成功: ', res);
|
||||
const { data } = res;
|
||||
dispatch({
|
||||
type: types.GET_OJ_LIST,
|
||||
payload: data
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* @Description:
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-20 16:40:32
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-22 20:08:07
|
||||
*/
|
||||
import types from '../actions/actionTypes';
|
||||
|
||||
const init = {
|
||||
ojForm: {
|
||||
name: '', // 任务名称
|
||||
language: '',
|
||||
description: '',
|
||||
difficult: 1,
|
||||
category: 1,
|
||||
openOrNot: 1,
|
||||
timeLimit: ''
|
||||
},
|
||||
ojFormValidate: {
|
||||
name: {
|
||||
validateStatus: '',
|
||||
errMsg: ''
|
||||
},
|
||||
language: {
|
||||
validateStatus: '',
|
||||
errMsg: ''
|
||||
},
|
||||
description: {
|
||||
validateStatus: '',
|
||||
errMsg: ''
|
||||
},
|
||||
difficult: {
|
||||
validateStatus: '',
|
||||
errMsg: ''
|
||||
},
|
||||
category: {
|
||||
validateStatus: '',
|
||||
errMsg: ''
|
||||
},
|
||||
openOrNot: {
|
||||
validateStatus: '',
|
||||
errMsg: ''
|
||||
},
|
||||
timeLimit: {
|
||||
validateStatus: '',
|
||||
errMsg: ''
|
||||
}
|
||||
},
|
||||
testCases: [
|
||||
// {
|
||||
// input: "11 22",
|
||||
// output: "33",
|
||||
// position: 1, // 当前测试用例位置
|
||||
// isAdd: true // 是否是新增
|
||||
// }
|
||||
], // 测试用例集合
|
||||
position: 1, // TODO 每次加载信息时同步指定positio值
|
||||
score: 200, // 分值: 选择难易度后自动计算分值 200 | 500 | 1000
|
||||
code: '', // 提交的代码
|
||||
identifier: '' // OJ表单id
|
||||
}
|
||||
|
||||
const initialState = Object.assign({}, init);
|
||||
|
||||
const ojFormReducer = (state = initialState, action) => {
|
||||
let ojFormValidate = {};
|
||||
let ojForm = {};
|
||||
if (action.payload) {
|
||||
ojFormValidate = action.payload.ojFormValidate;
|
||||
ojForm = action.payload.ojForm;
|
||||
|
||||
}
|
||||
|
||||
const returnState = (state, ojForm, ojFormValidate) => {
|
||||
return {
|
||||
...state,
|
||||
ojFormValidate: Object.assign({}, state.ojFormValidate, ojFormValidate),
|
||||
ojForm: Object.assign({}, state.ojForm, ojForm)
|
||||
};
|
||||
}
|
||||
switch (action.type) {
|
||||
case types.VALIDATE_OJ_FORM:
|
||||
// 验证成功后,调用后台接口
|
||||
return returnState(state, ojForm, ojFormValidate);
|
||||
case types.SAVE_OJ_FORM_CODE:
|
||||
return {
|
||||
...state,
|
||||
code: action.payload
|
||||
}
|
||||
case types.VALIDATE_OJ_NAME:
|
||||
// 验证任务名称
|
||||
return returnState(state, ojForm, ojFormValidate);
|
||||
case types.VALIDATE_OJ_DESCRIPTION:
|
||||
return returnState(state, ojForm, ojFormValidate);
|
||||
case types.VALIDATE_OJ_LANGUAGE:
|
||||
return returnState(state, ojForm, ojFormValidate);
|
||||
case types.VALIDATE_OJ_DIFFICULT:
|
||||
const difficult = action.payload.ojForm.difficult.trim();
|
||||
if (action.payload.ojForm.difficult) {
|
||||
state.score = scoreMaps[`${difficult}`];
|
||||
}
|
||||
return returnState(state, ojForm, ojFormValidate);
|
||||
case types.VALIDATE_OJ_CATEGORY:
|
||||
return returnState(state, ojForm, ojFormValidate);
|
||||
case types.VALIDATE_OJ_OPENORNOT:
|
||||
return returnState(state, ojForm, ojFormValidate);
|
||||
case types.VALIDATE_OJ_TIMELIMIT:
|
||||
return returnState(state, ojForm, ojFormValidate);
|
||||
case types.ADD_TEST_CASE:
|
||||
state.testCases.push(action.payload);
|
||||
state.position++; // 位置递增
|
||||
return {
|
||||
...state
|
||||
};
|
||||
case types.DELETE_TEST_CASE:
|
||||
const { position } = action.payload;
|
||||
const { testCases } = state;
|
||||
// 根据 position 去查找当前元素在数组中的位置
|
||||
const index = testCases.findIndex((item) => item.position === position);
|
||||
if (index > -1) {
|
||||
testCases.splice(index, 1); // 删除当前元素
|
||||
}
|
||||
return {
|
||||
...state
|
||||
};
|
||||
case types.SAVE_OJ_FORM_ID:
|
||||
state.identifier = action.payload;
|
||||
return {
|
||||
...state
|
||||
}
|
||||
case types.CLEAR_JSFORM_STORE:
|
||||
state = Object.assign({}, init);
|
||||
return {
|
||||
...state
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export default ojFormReducer;
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* @Description:
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-21 22:17:03
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-22 09:17:59
|
||||
*/
|
||||
import types from '../actions/actionTypes';
|
||||
|
||||
const initialState = {
|
||||
hacks_list: [],
|
||||
top_data: {},
|
||||
hacks_count: 0 // 总条数
|
||||
};
|
||||
|
||||
const ojListReducer = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case types.GET_OJ_LIST:
|
||||
return {
|
||||
...state,
|
||||
...action.payload
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export default ojListReducer;
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* @Description: 开发者社区接口
|
||||
* @Author: tangjiang
|
||||
* @Github:
|
||||
* @Date: 2019-11-20 10:55:38
|
||||
* @LastEditors: tangjiang
|
||||
* @LastEditTime: 2019-11-22 16:48:35
|
||||
*/
|
||||
|
||||
import axios from 'axios';
|
||||
|
||||
export async function fetchOJList (params) {
|
||||
console.log('传递的参数: ', params);
|
||||
const obj = {};
|
||||
Object.keys(params).forEach(key => {
|
||||
if (params[key]) {
|
||||
obj[key] = params[key];
|
||||
}
|
||||
});
|
||||
return axios.get('/problems.json', { params: obj });
|
||||
}
|
||||
|
||||
// 提交
|
||||
export async function fetchPostOjForm (params) {
|
||||
const url = `/problems.json`;
|
||||
return axios.post(url, params);
|
||||
}
|
||||
|
||||
// 根据id号获取OJ信息
|
||||
export async function fetchGetOjById (id) {
|
||||
const url = `/problems/${id}/edit.json`;
|
||||
return axios.get(url);
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in new issue