parent
5661c0d4b2
commit
51ea118369
@ -0,0 +1,154 @@
|
|||||||
|
/*
|
||||||
|
* @Description: 右侧代码块控制台
|
||||||
|
* @Author: tangjiang
|
||||||
|
* @Github:
|
||||||
|
* @Date: 2019-11-27 16:02:36
|
||||||
|
* @LastEditors: tangjiang
|
||||||
|
* @LastEditTime: 2019-11-28 16:48:50
|
||||||
|
*/
|
||||||
|
import './index.scss';
|
||||||
|
import React, { useState, useRef } from 'react';
|
||||||
|
import { Tabs, Button, Icon } from 'antd';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import InitTabCtx from '../initTabCtx';
|
||||||
|
import ExecResult from '../execResult';
|
||||||
|
import actions from '../../../../redux/actions';
|
||||||
|
|
||||||
|
const { TabPane } = Tabs;
|
||||||
|
const ControlSetting = (props) => {
|
||||||
|
|
||||||
|
const {
|
||||||
|
inputValue,
|
||||||
|
loading,
|
||||||
|
submitLoading,
|
||||||
|
identifier,
|
||||||
|
excuteState,
|
||||||
|
commitRecordDetail,
|
||||||
|
changeLoadingState,
|
||||||
|
changeSubmitLoadingStatus,
|
||||||
|
showOrHideControl,
|
||||||
|
// debuggerCode
|
||||||
|
updateCode,
|
||||||
|
onSubmitForm
|
||||||
|
} = props;
|
||||||
|
const [defaultActiveKey, setDefaultActiveKey] = useState('1'); // 当前选中的tab
|
||||||
|
const [showTextResult, setShowTextResult] = useState(false); // 是否点击控制台按钮
|
||||||
|
const formRef = useRef(null);
|
||||||
|
|
||||||
|
const classNames = `control_tab ${showTextResult ? 'move_up move_up_final' : 'move_down_final'}`;
|
||||||
|
|
||||||
|
// 切换tab
|
||||||
|
const handleTabChange = (key) => {
|
||||||
|
setDefaultActiveKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示/隐藏tab
|
||||||
|
const handleShowControl = () => {
|
||||||
|
setShowTextResult(!showTextResult);
|
||||||
|
showOrHideControl(!showTextResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调试代码
|
||||||
|
const handleTestCode = (e) => {
|
||||||
|
// console.log(formRef.current.handleTestCodeFormSubmit);
|
||||||
|
// 调出控制台界面
|
||||||
|
setShowTextResult(true);
|
||||||
|
showOrHideControl(true);
|
||||||
|
formRef.current.handleTestCodeFormSubmit(() => {
|
||||||
|
setDefaultActiveKey('2');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交
|
||||||
|
const handleSubmit = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
changeSubmitLoadingStatus(true)
|
||||||
|
onSubmitForm && onSubmitForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理调度代码
|
||||||
|
const handleDebuggerCode = (values) => {
|
||||||
|
// 改变状态值
|
||||||
|
changeLoadingState(true);
|
||||||
|
// 调用代码保存接口, 成功后再调用调试接口
|
||||||
|
updateCode(identifier, values, 'debug');
|
||||||
|
// 调用调试接口
|
||||||
|
// debuggerCode(identifier, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="pane_control_area">
|
||||||
|
<Tabs
|
||||||
|
className={classNames}
|
||||||
|
activeKey={defaultActiveKey}
|
||||||
|
tabBarStyle={{ backgroundColor: '#000', color: '#fff' }}
|
||||||
|
onChange={handleTabChange}
|
||||||
|
>
|
||||||
|
<TabPane tab={'自定义测试用例'} key={'1'} style={{ height: '280px', overflowY: 'auto' }}>
|
||||||
|
<InitTabCtx
|
||||||
|
inputValue={inputValue}
|
||||||
|
wrappedComponentRef={(form) => formRef.current = form}
|
||||||
|
onDebuggerCode={handleDebuggerCode}
|
||||||
|
/>
|
||||||
|
</TabPane>
|
||||||
|
<TabPane tab={'代码执行结果'} key={'2'} style={{ height: '280px', overflowY: 'auto' }}>
|
||||||
|
<ExecResult
|
||||||
|
excuteState={excuteState}
|
||||||
|
excuteDetail={commitRecordDetail}
|
||||||
|
/>
|
||||||
|
</TabPane>
|
||||||
|
</Tabs>
|
||||||
|
<div className="pane_control_opts">
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
style={{ color: '#fff' }}
|
||||||
|
onClick={handleShowControl}>
|
||||||
|
控制台 <Icon type={ showTextResult ? "down" : "up" } />
|
||||||
|
</Button>
|
||||||
|
<p>
|
||||||
|
<Button ghost
|
||||||
|
loading={loading}
|
||||||
|
style={{ marginRight: '10px', color: '#28BD8B', borderColor: '#28BD8B' }}
|
||||||
|
onClick={handleTestCode}
|
||||||
|
disabled={!identifier}
|
||||||
|
>调试代码</Button>
|
||||||
|
<Button
|
||||||
|
loading={submitLoading}
|
||||||
|
type="primary"
|
||||||
|
onClick={handleSubmit}
|
||||||
|
>
|
||||||
|
{/* {props.identifier ? '更新' : '提交'} */}
|
||||||
|
提交
|
||||||
|
</Button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state) => {
|
||||||
|
const {commonReducer, ojForUserReducer} = state;
|
||||||
|
const { loading, excuteState, submitLoading } = commonReducer;
|
||||||
|
const { user_program_identifier, commitRecordDetail } = ojForUserReducer;
|
||||||
|
return {
|
||||||
|
loading,
|
||||||
|
submitLoading,
|
||||||
|
excuteState,
|
||||||
|
identifier: user_program_identifier,
|
||||||
|
commitRecordDetail // 提交详情
|
||||||
|
};
|
||||||
|
};
|
||||||
|
// changeSubmitLoadingStatus
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
showOrHideControl: (flag) => dispatch(actions.showOrHideControl(flag)),
|
||||||
|
changeLoadingState: (flag) => dispatch(actions.changeLoadingState(flag)),
|
||||||
|
changeSubmitLoadingStatus: (flag) => dispatch(actions.changeSubmitLoadingStatus(flag)),
|
||||||
|
debuggerCode: (identifier, values) => dispatch(actions.debuggerCode(identifier, values)),
|
||||||
|
// inputValue 输入值
|
||||||
|
updateCode: (identifier, inputValue, type) => dispatch(actions.updateCode(identifier, inputValue, type))
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(ControlSetting);
|
@ -0,0 +1,103 @@
|
|||||||
|
.pane_control_area{
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
// height: 56px;
|
||||||
|
.control_tab{
|
||||||
|
position: absolute;
|
||||||
|
bottom: -325px;
|
||||||
|
width: 100%;
|
||||||
|
// transition: all .2s;
|
||||||
|
opacity: 0;
|
||||||
|
// animation: .3s ease-in-out move_up;
|
||||||
|
// &.active{
|
||||||
|
// bottom: 0;
|
||||||
|
// opacity: 1;
|
||||||
|
// }
|
||||||
|
&.move_up{
|
||||||
|
animation: move_up .3s ease-in;
|
||||||
|
}
|
||||||
|
&.move_up_final {
|
||||||
|
bottom: 0;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
&.move_down{
|
||||||
|
animation: move_down .3s ease-in-out;
|
||||||
|
}
|
||||||
|
&.move_down_final{
|
||||||
|
bottom: -325px;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.ant-tabs-bar{
|
||||||
|
padding: 0 10px;
|
||||||
|
margin: 0px;
|
||||||
|
border-bottom: transparent;
|
||||||
|
}
|
||||||
|
.ant-tabs-ink-bar{
|
||||||
|
bottom: 1px;
|
||||||
|
}
|
||||||
|
// .tab_ctx_area.pos_center{
|
||||||
|
// background: #222;
|
||||||
|
// }
|
||||||
|
.pane_control_opts{
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 20;
|
||||||
|
height: 56px;
|
||||||
|
padding-right: 30px;
|
||||||
|
padding-left: 10px;
|
||||||
|
background: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting_drawer{
|
||||||
|
.setting_h2{
|
||||||
|
line-height: 50px;
|
||||||
|
}
|
||||||
|
.setting_desc{
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
.flex_item{
|
||||||
|
line-height: 32px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@keyframes move_up {
|
||||||
|
0%{
|
||||||
|
opacity: 0;
|
||||||
|
// bottom: -325px;
|
||||||
|
}
|
||||||
|
90%{
|
||||||
|
opacity: 0.5;
|
||||||
|
// bottom: 0px;
|
||||||
|
}
|
||||||
|
100%{
|
||||||
|
opacity: 1;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes move_down{
|
||||||
|
0%{
|
||||||
|
opacity: 1;
|
||||||
|
bottom: 0
|
||||||
|
}
|
||||||
|
10%{
|
||||||
|
opacity: .2;
|
||||||
|
}
|
||||||
|
20%{
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100%{
|
||||||
|
opacity: 0;
|
||||||
|
bottom: -325px;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* @Description: 执行结果
|
||||||
|
* @Author: tangjiang
|
||||||
|
* @Github:
|
||||||
|
* @Date: 2019-11-28 08:44:54
|
||||||
|
* @LastEditors: tangjiang
|
||||||
|
* @LastEditTime: 2019-11-28 15:14:42
|
||||||
|
*/
|
||||||
|
import './index.scss';
|
||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { Icon } from 'antd';
|
||||||
|
import CONST from '../../../../constants';
|
||||||
|
|
||||||
|
const {reviewResult} = CONST;
|
||||||
|
function ExecResult (props) {
|
||||||
|
|
||||||
|
const { excuteState, excuteDetail } = props;
|
||||||
|
// 指定渲染初始, 加载中, 加载完成页面内容
|
||||||
|
const renderInit = () => (
|
||||||
|
<div className={'excute_result_area excute_flex_center'}>
|
||||||
|
<span className={'init_ctx'}>请先点击“调试代码”运行您的代码</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
const renderLoading = () => (
|
||||||
|
<div className={'excute_result_area excute_flex_center'}>
|
||||||
|
<span className={'loading_ctx'}>
|
||||||
|
<Icon className={'ctx_icon'} type="loading"/>
|
||||||
|
<span>加载中...</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
const readerLoaded = () => (
|
||||||
|
<div className={'excute_result_area excute_flex_center'}>
|
||||||
|
<span className={'loaded_ctx'}>
|
||||||
|
<Icon className={'ctx_icon'} type="loading"/>
|
||||||
|
<span>加载完成</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
const renderFinish = () => {
|
||||||
|
const {
|
||||||
|
error_line,
|
||||||
|
error_msg,
|
||||||
|
execute_memory,
|
||||||
|
execute_time,
|
||||||
|
input,
|
||||||
|
output,
|
||||||
|
status,
|
||||||
|
expected_output
|
||||||
|
} = codeResult;
|
||||||
|
|
||||||
|
const excuteHeader = (state) => {
|
||||||
|
const review_class = state === 0 ? `excute_suc` : `excute_err`;
|
||||||
|
return (
|
||||||
|
<p className={'excute_head_area'}>
|
||||||
|
<span className={'excute_head_txt'}>执行结果: </span>
|
||||||
|
<span className={review_class}>{reviewResult[`${state}`]}</span>
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const excuteCtx = (state) => {
|
||||||
|
if (state === 0) {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<p className={'result_info_style'}>输入: {input}</p>
|
||||||
|
<p className={'result_info_style'}>输出: {output}</p>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
} else if (state === 4){
|
||||||
|
return (
|
||||||
|
<p className={'result_info_style'}>
|
||||||
|
{error_msg}
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
} else if (state === -1) {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<p className={'result_info_style'}>输入: {input}</p>
|
||||||
|
<p className={'result_info_style'}>输出: {output}</p>
|
||||||
|
<p className={'result_info_style'}>预期输出: {expected_output}</p>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
} else if (state === 5) {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<p className={'result_info_style'}> 执行出错信息: {error_msg}</p>
|
||||||
|
<p className={'result_info_style'}>最后执行的输入: {input}</p>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className={'excute_result_info'}>
|
||||||
|
{excuteHeader(status)}
|
||||||
|
{excuteCtx(status)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 渲染状态
|
||||||
|
const [renderCtx, setRenderCtx] = useState(() => {
|
||||||
|
return function () {
|
||||||
|
return renderInit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 提交记录详情
|
||||||
|
const [codeResult, setCodeResult] = useState({})
|
||||||
|
|
||||||
|
// 渲染状态变化时渲染相应的内容
|
||||||
|
useEffect(() => {
|
||||||
|
if ('loading' === excuteState) {
|
||||||
|
setRenderCtx(() => (renderLoading));
|
||||||
|
} else if ('loaded' === excuteState) {
|
||||||
|
setRenderCtx(() => (readerLoaded));
|
||||||
|
} else if ('finish' === excuteState) {
|
||||||
|
setRenderCtx(() => (renderFinish));
|
||||||
|
}
|
||||||
|
}, [excuteState]);
|
||||||
|
|
||||||
|
// 提交详情变化时
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('提交记录详情=====>>>>>', excuteDetail);
|
||||||
|
setCodeResult(excuteDetail);
|
||||||
|
}, [excuteDetail]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{renderCtx()}
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ExecResult;
|
@ -0,0 +1,47 @@
|
|||||||
|
.excute_result_area{
|
||||||
|
display: flex;
|
||||||
|
height: 224px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&.excute_flex_center{
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.init_ctx{
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
.loading_ctx,
|
||||||
|
.loaded_ctx{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
color: #1890ff;
|
||||||
|
.ctx_icon{
|
||||||
|
font-size: 40px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.excute_result_info{
|
||||||
|
padding: 20px 30px;
|
||||||
|
color: #fff;
|
||||||
|
height: 220px;
|
||||||
|
/* overflow-y: auto; */
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.result_info_style{
|
||||||
|
word-wrap: break-word;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.excute_head_area{
|
||||||
|
line-height: 30px;
|
||||||
|
.excute_suc{
|
||||||
|
color: #28BD8B;
|
||||||
|
}
|
||||||
|
.excute_err{
|
||||||
|
color: #E51C24;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
.tab_ctx_area{
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
&.pos_start{
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
&.pos_center{
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
&.pos_end{
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
.ctx_default{
|
||||||
|
margin: 10px 20px;
|
||||||
|
}
|
||||||
|
.ctx_loading,
|
||||||
|
.ctx_loaded{
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
flex-direction: column;
|
||||||
|
top: -20px;
|
||||||
|
color: #1890ff;
|
||||||
|
.ctx_icon{
|
||||||
|
font-size: 40px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.user_case_form{
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-top: 20px;
|
||||||
|
.input_area{
|
||||||
|
flex: 1;
|
||||||
|
.ant-form-item-required{
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.flex_l{
|
||||||
|
padding: 0 10px 0 20px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.flex_r{
|
||||||
|
padding: 0 20px 0 10px;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* @Description: 编辑器侧边栏设置信息
|
||||||
|
* @Author: tangjiang
|
||||||
|
* @Github:
|
||||||
|
* @Date: 2019-11-25 17:50:33
|
||||||
|
* @LastEditors: tangjiang
|
||||||
|
* @LastEditTime: 2019-11-27 14:40:36
|
||||||
|
*/
|
||||||
|
import React from 'react';
|
||||||
|
import { Select } from 'antd';
|
||||||
|
|
||||||
|
const { Option } = Select;
|
||||||
|
const SettingDrawer = (props) => {
|
||||||
|
/**
|
||||||
|
* title: '', // 一级标题
|
||||||
|
* type: '', // 类型: 目录 select 和 文本
|
||||||
|
* content: [] // 显示的内容 { text: '' , value: string | [{ key: 1, value: '', text: '' }] }
|
||||||
|
*/
|
||||||
|
const {title, type = 'label', content = [] } = props;
|
||||||
|
|
||||||
|
const handleFontSize = (value) => {
|
||||||
|
const {onChangeFontSize} = props;
|
||||||
|
// console.log('fong size change: ', value);
|
||||||
|
onChangeFontSize && onChangeFontSize(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderCtx = (title, content = [], type = 'label') => {
|
||||||
|
const result = content.map((ctx, index) => {
|
||||||
|
const subText = ctx.text;
|
||||||
|
const value = ctx.value;
|
||||||
|
let renderResult = '';
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
renderResult = (
|
||||||
|
<div className={'setting_desc'} key={`lab_${index}`}>
|
||||||
|
<span className={'flex_item'}>{subText}</span>
|
||||||
|
<span className={'flex_item'}>{ctx.value}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else if (Array.isArray(value)) {
|
||||||
|
if (type === 'select') {
|
||||||
|
const child = ctx.value.map((opt, i) => (
|
||||||
|
<Option key={opt.key || `${opt.value}`} value={opt.value}>
|
||||||
|
{opt.text}
|
||||||
|
</Option>
|
||||||
|
));
|
||||||
|
renderResult = (
|
||||||
|
<div className={'setting_desc'} key={`sel_${index}`}>
|
||||||
|
<span className={'flex_item'}>{ctx.text}</span>
|
||||||
|
<Select className={'flex_item'} style={{ width: '100px'}} onChange={handleFontSize}>
|
||||||
|
{child}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return renderResult;
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<h2 className={'setting_h2'}>{title}</h2>
|
||||||
|
{ result }
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className={'setting_area'}>
|
||||||
|
{renderCtx(title, content, type)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SettingDrawer;
|
@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* @Description: 抽取代码编辑器
|
||||||
|
* @Author: tangjiang
|
||||||
|
* @Github:
|
||||||
|
* @Date: 2019-11-27 15:02:52
|
||||||
|
* @LastEditors: tangjiang
|
||||||
|
* @LastEditTime: 2019-11-28 12:39:39
|
||||||
|
*/
|
||||||
|
import './index.scss';
|
||||||
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
|
import { Icon, Drawer } from 'antd';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import MonacoEditor from '@monaco-editor/react';
|
||||||
|
import SettingDrawer from '../../components/monacoSetting';
|
||||||
|
import CONST from '../../../../constants';
|
||||||
|
import actions from '../../../../redux/actions';
|
||||||
|
|
||||||
|
const { fontSetting, opacitySetting } = CONST;
|
||||||
|
|
||||||
|
const MyMonacoEditor = (props) => {
|
||||||
|
|
||||||
|
const {
|
||||||
|
language,
|
||||||
|
code,
|
||||||
|
showOrHideControl,
|
||||||
|
saveUserInputCode
|
||||||
|
} = props;
|
||||||
|
const [showDrawer, setShowDrawer] = useState(false); // 控制配置滑框
|
||||||
|
const [editCode, setEditCode] = useState('');
|
||||||
|
// const [curLang, setCurLang] = useState('C');
|
||||||
|
const [fontSize, setFontSize] = useState(12);
|
||||||
|
const [ height, setHeight ] = useState('calc(100% - 112px)');
|
||||||
|
const editorRef = useRef(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (code) {
|
||||||
|
setEditCode(code);
|
||||||
|
}
|
||||||
|
}, [code]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setHeight(showOrHideControl ? 'calc(100% - 382px)' : 'calc(100% - 112px)');
|
||||||
|
}, [showOrHideControl]);
|
||||||
|
// 控制侧边栏设置的显示
|
||||||
|
const handleShowDrawer = () => {
|
||||||
|
setShowDrawer(true);
|
||||||
|
}
|
||||||
|
// 关闭设置
|
||||||
|
const handleDrawerClose = () => {
|
||||||
|
setShowDrawer(false);
|
||||||
|
}
|
||||||
|
// 侧边栏改变字体大小
|
||||||
|
const handleFontSizeChange = (value) => {
|
||||||
|
setFontSize(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文本框内容变化时,记录文本框内容
|
||||||
|
const handleEditorChange = (origin, monaco) => {
|
||||||
|
editorRef.current = monaco; // 获取当前monaco实例
|
||||||
|
setEditCode(origin); // 保存编辑器初始值
|
||||||
|
editorRef.current.onDidChangeModelContent(e => { // 监听编辑器内容的变化
|
||||||
|
// TODO 需要优化 节流
|
||||||
|
const val = editorRef.current.getValue();
|
||||||
|
setEditCode(val);
|
||||||
|
// 值一变化保存当前代码值
|
||||||
|
saveUserInputCode(val);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置编辑器属性
|
||||||
|
const editorOptions = {
|
||||||
|
selectOnLineNumbers: true,
|
||||||
|
automaticLayout: true,
|
||||||
|
fontSize: `${fontSize}px`
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<div className={"monaco_editor_area"}>
|
||||||
|
<div className="code_title">
|
||||||
|
<span>已保存</span>
|
||||||
|
<Icon className={'code-icon'} type="setting" onClick={handleShowDrawer}/>
|
||||||
|
</div>
|
||||||
|
<MonacoEditor
|
||||||
|
height={height}
|
||||||
|
width="100%"
|
||||||
|
language={language && language.toLowerCase()}
|
||||||
|
value={editCode}
|
||||||
|
options={editorOptions}
|
||||||
|
theme="dark"
|
||||||
|
editorDidMount={handleEditorChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Drawer
|
||||||
|
className={'setting_drawer'}
|
||||||
|
placement="right"
|
||||||
|
closable={false}
|
||||||
|
onClose={handleDrawerClose}
|
||||||
|
visible={showDrawer}
|
||||||
|
>
|
||||||
|
<SettingDrawer {...fontSetting} onChangeFontSize={handleFontSizeChange}/>
|
||||||
|
<SettingDrawer {...opacitySetting}/>
|
||||||
|
</Drawer>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state) => {
|
||||||
|
const { showOrHideControl } = state.commonReducer;
|
||||||
|
return {
|
||||||
|
showOrHideControl
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
saveUserInputCode: (code) => dispatch(actions.saveUserInputCode(code)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(MyMonacoEditor);
|
@ -0,0 +1,15 @@
|
|||||||
|
.monaco_editor_area{
|
||||||
|
height: 100%;
|
||||||
|
.code_title{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
background: #000;
|
||||||
|
color: #fff;
|
||||||
|
height: 56px;
|
||||||
|
padding: 0 30px;
|
||||||
|
.code-icon{
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* @Description: 文字 | 图标 + 数字样式
|
||||||
|
* @Author: tangjiang
|
||||||
|
* @Github:
|
||||||
|
* @Date: 2019-11-27 10:58:37
|
||||||
|
* @LastEditors: tangjiang
|
||||||
|
* @LastEditTime: 2019-11-27 14:22:38
|
||||||
|
*/
|
||||||
|
import './index.scss';
|
||||||
|
import React from 'react';
|
||||||
|
import { Icon } from 'antd';
|
||||||
|
const numberal = require('numeral');
|
||||||
|
|
||||||
|
const TextNumber = (props) => {
|
||||||
|
/**
|
||||||
|
* text: 显示的文本信息
|
||||||
|
* number: 显示的数字
|
||||||
|
* position: 位置 vertical | horizontal (默认)
|
||||||
|
* type: 内容 文字或图标
|
||||||
|
* onIconClick: 点击图标时的回调函数
|
||||||
|
*/
|
||||||
|
const { text, number, position = 'horizontal', type = 'label', onIconClick} = props;
|
||||||
|
|
||||||
|
const handleIconClick = () => {
|
||||||
|
onIconClick && onIconClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderNumb = () => {
|
||||||
|
let tempNumb = number;
|
||||||
|
if ((tempNumb || tempNumb === 0) && (typeof Number(tempNumb) === 'number')) {
|
||||||
|
tempNumb = numberal(tempNumb).format('0,0');
|
||||||
|
return (
|
||||||
|
<span className={'numb_value'}>{tempNumb}</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
const renderCtx = () => {
|
||||||
|
if (type === 'icon') { // 图标加文字时
|
||||||
|
return (
|
||||||
|
<div className={`text_number_area text_icon_numb flex_${position}`}>
|
||||||
|
<Icon onClick={handleIconClick} type={text} className={'numb_icon'}></Icon>
|
||||||
|
{renderNumb()}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<div className={`text_number_area text_label_numb flex_${position}`}>
|
||||||
|
<span className={'text_label'}>{text}</span>
|
||||||
|
{renderNumb()}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{renderCtx()}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TextNumber;
|
@ -0,0 +1,43 @@
|
|||||||
|
.text_number_area{
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex_vertical{
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex_horizontal{
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_label_numb,
|
||||||
|
.text_icon_numb{
|
||||||
|
line-height: 18px;
|
||||||
|
vertical-align: top;
|
||||||
|
.numb_value{
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_label_numb{
|
||||||
|
.numb_value{
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
.text_label{
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_icon_numb{
|
||||||
|
.numb_icon{
|
||||||
|
font-size: 16px;
|
||||||
|
margin-right: 5px;
|
||||||
|
color: #333333;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.numb_value{
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,25 @@
|
|||||||
.split-pane-left{
|
// .split-pane-left{
|
||||||
.ant-tabs-nav-wrap{
|
// .ant-tabs-nav-wrap{
|
||||||
padding: 0 30px;
|
// padding: 0 30px;
|
||||||
}
|
// }
|
||||||
.ant-tabs-bar{
|
// .ant-tabs-bar{
|
||||||
margin: 0;
|
// margin: 0;
|
||||||
}
|
// }
|
||||||
// .ant-tabs-tabpane{
|
// // .ant-tabs-tabpane{
|
||||||
// padding-top: 10px;
|
// // padding-top: 10px;
|
||||||
// height: calc(100vh - 110px);
|
// // height: calc(100vh - 110px);
|
||||||
// overflow: auto;
|
// // overflow: auto;
|
||||||
// }
|
// // }
|
||||||
}
|
|
||||||
|
// .ant-form-item-control{
|
||||||
|
// line-height: 1;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// .editor_area,
|
||||||
|
// .prev_area{
|
||||||
|
// height: calc(100vh - 110px);
|
||||||
|
// overflow-y: auto;
|
||||||
|
// padding: 20px 0;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
@import '../../split_pane_resizer.scss';
|
||||||
|
@ -1,15 +1,57 @@
|
|||||||
import React, { PureComponent } from 'react';
|
/*
|
||||||
// import connect from 'react-redux';
|
* @Description: 代码预览页面
|
||||||
|
* @Author: tangjiang
|
||||||
|
* @Github:
|
||||||
|
* @Date: 2019-11-24 10:09:55
|
||||||
|
* @LastEditors: tangjiang
|
||||||
|
* @LastEditTime: 2019-11-27 19:30:51
|
||||||
|
*/
|
||||||
|
import './index.scss';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import {Empty} from 'antd';
|
||||||
|
import QuillEditor from '../../../quillEditor';
|
||||||
|
const PrevTab = (props) => {
|
||||||
|
// const { } = props;
|
||||||
|
const [desc, setDesc] = useState('');
|
||||||
|
|
||||||
class PrevTab extends PureComponent {
|
useEffect(() => {
|
||||||
|
setDesc(props.description);
|
||||||
|
}, [props.description]);
|
||||||
|
|
||||||
state = {}
|
const renderHtml = () => {
|
||||||
render () {
|
if (!desc) {
|
||||||
return (
|
return (
|
||||||
<h2>预览页</h2>
|
<div className={'no_result'}>
|
||||||
)
|
<Empty />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<div className={'render_html'} dangerouslySetInnerHTML={{ __html: desc }}></div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className={`prev_area`}>
|
||||||
|
{/* {renderHtml()} */}
|
||||||
|
{/* <div dangerouslySetInnerHTML={{ __html: desc }}></div> */}
|
||||||
|
<QuillEditor
|
||||||
|
style={{ height: 'calc(100% - 45px)', overflowY: 'auto' }}
|
||||||
|
options={[]}
|
||||||
|
readOnly={true}
|
||||||
|
htmlCtx={props.description}/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
const mapStateToProps = (state) => {
|
||||||
|
const { ojForm } = state.ojFormReducer;
|
||||||
|
return {
|
||||||
|
description: ojForm.description
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// export default connect()(PrevTab);
|
export default connect(
|
||||||
export default PrevTab;
|
mapStateToProps
|
||||||
|
)(PrevTab);
|
@ -0,0 +1,11 @@
|
|||||||
|
.no_result{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.render_html{
|
||||||
|
padding: 20px 30px;
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* @Description: 编辑器侧边栏设置信息
|
||||||
|
* @Author: tangjiang
|
||||||
|
* @Github:
|
||||||
|
* @Date: 2019-11-25 17:50:33
|
||||||
|
* @LastEditors: tangjiang
|
||||||
|
* @LastEditTime: 2019-11-27 14:40:25
|
||||||
|
*/
|
||||||
|
import React from 'react';
|
||||||
|
import { Select } from 'antd';
|
||||||
|
|
||||||
|
const { Option } = Select;
|
||||||
|
const SettingDrawer = (props) => {
|
||||||
|
/**
|
||||||
|
* title: '', // 一级标题
|
||||||
|
* type: '', // 类型: 目录 select 和 文本
|
||||||
|
* content: [] // 显示的内容 { text: '' , value: string | [{ key: 1, value: '', text: '' }] }
|
||||||
|
*/
|
||||||
|
const {title, type = 'label', content = [] } = props;
|
||||||
|
|
||||||
|
const handleFontSize = (value) => {
|
||||||
|
const {onChangeFontSize} = props;
|
||||||
|
// console.log('fong size change: ', value);
|
||||||
|
onChangeFontSize && onChangeFontSize(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderCtx = (title, content = [], type = 'label') => {
|
||||||
|
const result = content.map((ctx, index) => {
|
||||||
|
const subText = ctx.text;
|
||||||
|
const value = ctx.value;
|
||||||
|
let renderResult = '';
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
renderResult = (
|
||||||
|
<div className={'setting_desc'} key={`lab_${index}`}>
|
||||||
|
<span className={'flex_item'}>{subText}</span>
|
||||||
|
<span className={'flex_item'}>{ctx.value}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else if (Array.isArray(value)) {
|
||||||
|
if (type === 'select') {
|
||||||
|
const child = ctx.value.map((opt, i) => (
|
||||||
|
<Option key={opt.key || `${opt.value}`} value={opt.value}>
|
||||||
|
{opt.text}
|
||||||
|
</Option>
|
||||||
|
));
|
||||||
|
renderResult = (
|
||||||
|
<div className={'setting_desc'} key={`sel_${index}`}>
|
||||||
|
<span className={'flex_item'}>{ctx.text}</span>
|
||||||
|
<Select className={'flex_item'} style={{ width: '100px'}} onChange={handleFontSize}>
|
||||||
|
{child}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return renderResult;
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<h2 className={'setting_h2'}>{title}</h2>
|
||||||
|
{ result }
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className={'setting_area'}>
|
||||||
|
{renderCtx(title, content, type)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SettingDrawer;
|
@ -0,0 +1,3 @@
|
|||||||
|
.quill_editor_area{
|
||||||
|
height: 300px;
|
||||||
|
}
|
@ -1 +1,7 @@
|
|||||||
@import '../split_pane_resizer.scss';
|
@import '../split_pane_resizer.scss';
|
||||||
|
|
||||||
|
.right_pane_code_wrap{
|
||||||
|
position: relative;
|
||||||
|
background-color: #222;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* @Description:
|
||||||
|
* @Author: tangjiang
|
||||||
|
* @Github:
|
||||||
|
* @Date: 2019-11-27 09:49:35
|
||||||
|
* @LastEditors: tangjiang
|
||||||
|
* @LastEditTime: 2019-11-27 09:52:53
|
||||||
|
*/
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const Comment = (props) => {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<h2> Comment </h2>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Comment;
|
@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
* @Description: 提交记录
|
||||||
|
* @Author: tangjiang
|
||||||
|
* @Github:
|
||||||
|
* @Date: 2019-11-27 09:49:33
|
||||||
|
* @LastEditors: tangjiang
|
||||||
|
* @LastEditTime: 2019-11-28 19:54:56
|
||||||
|
*/
|
||||||
|
import './index.scss';
|
||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { Table, Icon } from 'antd';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import actions from '../../../../../redux/actions';
|
||||||
|
import CONST from '../../../../../constants';
|
||||||
|
import moment from 'moment';
|
||||||
|
const numberal = require('numeral');
|
||||||
|
|
||||||
|
const {reviewResult} = CONST;
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '提交时间',
|
||||||
|
dataIndex: 'created_at',
|
||||||
|
render: (created_at) => (
|
||||||
|
<span>
|
||||||
|
{moment(created_at, 'YYYYMMDD HHmmss').fromNow()}
|
||||||
|
</span>)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '提交结果',
|
||||||
|
dataIndex: 'status',
|
||||||
|
render: (value) => (<span style={{ color: value === 0 ? '#28BD8B' : '#E6262E'}}>{reviewResult[value]}</span>)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '执行用时',
|
||||||
|
dataIndex: 'execute_time',
|
||||||
|
render: (value) => (<span>{`${value}s`}</span>)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '内存消耗',
|
||||||
|
dataIndex: 'execute_memory',
|
||||||
|
render: (value) => {
|
||||||
|
if (value) {
|
||||||
|
return <span>{numberal(+value).format('0.00b')}</span>
|
||||||
|
} else {
|
||||||
|
return (<span>0MB</span>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '语言',
|
||||||
|
dataIndex: 'language'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const paginationConfig = {
|
||||||
|
total: 1, // 总条数
|
||||||
|
pageSize: 10, // 每页显示条数
|
||||||
|
current: 1, // 当前页数
|
||||||
|
showQuickJumper: true
|
||||||
|
}
|
||||||
|
const CommitRecord = (props) => {
|
||||||
|
|
||||||
|
const {
|
||||||
|
identifier,
|
||||||
|
commitRecord,
|
||||||
|
commitRecordDetail,
|
||||||
|
getUserCommitRecord
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const [pagination, setPagination] = useState(paginationConfig);
|
||||||
|
const [tableData, setTableData] = useState([]);
|
||||||
|
// const [recordDetail, setRecordDetail] = useState({});
|
||||||
|
const [renderCtx, setRenderCtx] = useState(() => {
|
||||||
|
return function () {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 渲染提交记录详情
|
||||||
|
const renderRecordDetail = () => {
|
||||||
|
const {
|
||||||
|
error_line,
|
||||||
|
error_msg,
|
||||||
|
execute_memory,
|
||||||
|
execute_time,
|
||||||
|
input,
|
||||||
|
output,
|
||||||
|
status,
|
||||||
|
expected_output
|
||||||
|
} = commitRecordDetail;
|
||||||
|
console.log('========', commitRecordDetail);
|
||||||
|
if (Object.keys(commitRecordDetail).length > 0) {
|
||||||
|
const classes = status === 0 ? 'record_result_suc' : 'record_result_err';
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<div className={'record_header'}>
|
||||||
|
<span className={'record_result'}>
|
||||||
|
执行结果: <span className={classes}>{reviewResult[status]}</span>
|
||||||
|
</span>
|
||||||
|
<span className={'copy_error'}>
|
||||||
|
复制错误信息 <Icon type="copy" className={'icon_style'}/>
|
||||||
|
</span>
|
||||||
|
<span className={'show_detail'}>
|
||||||
|
显示详情 <Icon type="right" className={'icon_style'}/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{/* <div className={'record_error_info'}>错误代码</div> */}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// console.log('调用记录详情=====>>>');
|
||||||
|
getUserCommitRecord(identifier);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// console.log('====>>>>+++++++++++++', commitRecord);
|
||||||
|
setTableData(commitRecord);
|
||||||
|
}, [commitRecord]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// setRecordDetail(commitRecordDetail);
|
||||||
|
setRenderCtx(() => (renderRecordDetail))
|
||||||
|
}, [commitRecordDetail]);
|
||||||
|
|
||||||
|
console.log(commitRecord);
|
||||||
|
return (
|
||||||
|
<div className={'commit_record_area'}>
|
||||||
|
{renderCtx()}
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
rowKey={record => Math.random()}
|
||||||
|
dataSource={tableData}
|
||||||
|
pagination={pagination}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state) => {
|
||||||
|
const {
|
||||||
|
ojForUserReducer
|
||||||
|
} = state;
|
||||||
|
const {
|
||||||
|
user_program_identifier,
|
||||||
|
commitRecordDetail,
|
||||||
|
commitRecord
|
||||||
|
} = ojForUserReducer;
|
||||||
|
return {
|
||||||
|
identifier: user_program_identifier,
|
||||||
|
commitRecordDetail,
|
||||||
|
commitRecord // 提交记录
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
getUserCommitRecord: (identifier) => dispatch(actions.getUserCommitRecord(identifier))
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(CommitRecord);
|
@ -0,0 +1,41 @@
|
|||||||
|
.commit_record_area{
|
||||||
|
padding: 20px 30px;
|
||||||
|
.record_header{
|
||||||
|
display: flex;
|
||||||
|
// justify-content: space-between;
|
||||||
|
// background: gold;
|
||||||
|
height: 66px;
|
||||||
|
align-items: center;
|
||||||
|
.record_result{
|
||||||
|
color: #333333;
|
||||||
|
font-size: 16px;
|
||||||
|
// width:1px;
|
||||||
|
}
|
||||||
|
.copy_error{
|
||||||
|
text-align: right;
|
||||||
|
flex: 1;
|
||||||
|
padding-right: 40px;
|
||||||
|
}
|
||||||
|
.show_detail{
|
||||||
|
// width: 1px;
|
||||||
|
}
|
||||||
|
.copy_error,
|
||||||
|
.show_detail{
|
||||||
|
color: #5091FF;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.icon_style{
|
||||||
|
font-size: 12px;
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
.record_result_suc{
|
||||||
|
color: #28BD8B;
|
||||||
|
}
|
||||||
|
.record_result_err{
|
||||||
|
color: #E51C24;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.record_error_info{
|
||||||
|
padding: 20px 30px;
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +1,102 @@
|
|||||||
/*
|
/*
|
||||||
* @Description:
|
* @Description: 学员测评页面
|
||||||
* @Author: tangjiang
|
* @Author: tangjiang
|
||||||
* @Github:
|
* @Github:
|
||||||
* @Date: 2019-11-23 11:33:41
|
* @Date: 2019-11-23 11:33:41
|
||||||
* @LastEditors: tangjiang
|
* @LastEditors: tangjiang
|
||||||
* @LastEditTime: 2019-11-23 11:34:18
|
* @LastEditTime: 2019-11-28 16:12:58
|
||||||
*/
|
// */
|
||||||
import React from 'react';
|
import './index.scss';
|
||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { Tabs, Divider } from 'antd';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import Comment from './comment';
|
||||||
|
import CommitRecord from './commitRecord';
|
||||||
|
import TaskDescription from './taskDescription';
|
||||||
|
import TextNumber from './../../components/textNumber';
|
||||||
|
import actions from '../../../../redux/actions';
|
||||||
|
const { TabPane } = Tabs;
|
||||||
|
|
||||||
const LeftPane = (props) => {
|
const LeftPane = (props) => {
|
||||||
|
|
||||||
|
const { hack, userCodeTab, changeUserCodeTab } = props;
|
||||||
|
const { pass_count, submit_count } = hack;
|
||||||
|
const [defaultActiveKey, setDefaultActiveKey] = useState('task');
|
||||||
|
console.log(pass_count, submit_count);
|
||||||
|
const tabArrs = [
|
||||||
|
{ title: '任务描述', key: 'task', content: (<TaskDescription />) },
|
||||||
|
{ title: '提交记录', key: 'record', content: (<CommitRecord />) },
|
||||||
|
// { title: '评论', key: 'comment', content: (<Comment />) },
|
||||||
|
];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('==========>>>>>', userCodeTab)
|
||||||
|
setDefaultActiveKey(userCodeTab);
|
||||||
|
}, [userCodeTab])
|
||||||
|
|
||||||
|
const tabs = tabArrs.map((tab) => {
|
||||||
|
const Comp = tab.content;
|
||||||
|
return (
|
||||||
|
<TabPane tab={tab.title} key={tab.key}>
|
||||||
|
{ Comp }
|
||||||
|
</TabPane>
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
// tab切换时
|
||||||
|
const handleTabChange = (key) => {
|
||||||
|
// setDefaultActiveKey(key);
|
||||||
|
changeUserCodeTab(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击消息
|
||||||
|
const handleClickMessage = () => {
|
||||||
|
console.log('点击的消息图标---------');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击点赞
|
||||||
|
const handleClickLike = () => {
|
||||||
|
console.log('点击的Like---------');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击不喜欢
|
||||||
|
const handleClickDisLike = () => {
|
||||||
|
console.log('点击的DisLike---------');
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<h2>左侧内容</h2>
|
<React.Fragment>
|
||||||
|
<Tabs className={'user_code_tab_area'} activeKey={defaultActiveKey} onChange={handleTabChange}>
|
||||||
|
{ tabs }
|
||||||
|
</Tabs>
|
||||||
|
<div className={'number_area'}>
|
||||||
|
<div className="number_flex flex_count">
|
||||||
|
<TextNumber text="通过次数" number={pass_count} position="vertical"/>
|
||||||
|
<Divider type="vertical" style={{ height: '20px', margin: '10px 20px' }}/>
|
||||||
|
<TextNumber text="提交次数" number={submit_count} position="vertical"/>
|
||||||
|
</div>
|
||||||
|
{/* <div className="number_flex flex_info">
|
||||||
|
<TextNumber text="message" number={4235} type="icon" onIconClick={handleClickMessage}/>
|
||||||
|
<TextNumber text="like" number={4235} type="icon" onIconClick={handleClickLike}/>
|
||||||
|
<TextNumber text="dislike" type="icon" onIconClick={handleClickDisLike}/>
|
||||||
|
</div> */}
|
||||||
|
</div>
|
||||||
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LeftPane;
|
const mapStateToProps = (state) => {
|
||||||
|
const { hack, userCodeTab} = state.ojForUserReducer;
|
||||||
|
return {
|
||||||
|
hack,
|
||||||
|
userCodeTab
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// changeUserCodeTab
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
changeUserCodeTab: (key) => dispatch(actions.changeUserCodeTab(key))
|
||||||
|
});
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(LeftPane);
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
@import '../../split_pane_resizer.scss';
|
||||||
|
|
||||||
|
.user_code_tab_area{
|
||||||
|
.ant-tabs-tabpane{
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.number_area{
|
||||||
|
display: flex;
|
||||||
|
position: absolute;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
bottom: 0px;
|
||||||
|
height: 56px;
|
||||||
|
width: 100%;
|
||||||
|
// background: pink;
|
||||||
|
padding: 0 30px;
|
||||||
|
// background-color: #fff;
|
||||||
|
|
||||||
|
.flex_count,
|
||||||
|
.flex_info{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.flex_info{
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.task_description_area{
|
||||||
|
padding: 0 30px;
|
||||||
|
height: calc(100vh - 166px);
|
||||||
|
overflow-y: auto;
|
||||||
|
.desc_area_header{
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
height: 64px;
|
||||||
|
.header_flex{
|
||||||
|
font-size: 14px;
|
||||||
|
.flex_label{
|
||||||
|
color: #999999;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.flex_value{
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* @Description:
|
||||||
|
* @Author: tangjiang
|
||||||
|
* @Github:
|
||||||
|
* @Date: 2019-11-27 09:49:30
|
||||||
|
* @LastEditors: tangjiang
|
||||||
|
* @LastEditTime: 2019-11-27 19:36:41
|
||||||
|
*/
|
||||||
|
import '../index.scss';
|
||||||
|
import React from 'react';
|
||||||
|
import { Tag } from 'antd';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import QuillEditor from '../../../quillEditor';
|
||||||
|
import CONST from '../../../../../constants';
|
||||||
|
const {tagBackground, diffText} = CONST;
|
||||||
|
|
||||||
|
const TaskDescription = (props) => {
|
||||||
|
|
||||||
|
const { hack = {} } = props;
|
||||||
|
const {language, difficult, time_limit, username, description} = hack;
|
||||||
|
return (
|
||||||
|
<div className={'task_description_area'}>
|
||||||
|
<div className={'desc_area_header'}>
|
||||||
|
<p className={'header_flex'}>
|
||||||
|
<span className={'flex_label'}>编程语言:</span>
|
||||||
|
<span className={'flex_value'}>{language}</span>
|
||||||
|
</p>
|
||||||
|
<p className={'header_flex'}>
|
||||||
|
<span className={'flex_label'}>难度:</span>
|
||||||
|
<Tag color={tagBackground[+difficult]}>{diffText[+difficult]}</Tag>
|
||||||
|
</p>
|
||||||
|
<p className={'header_flex'}>
|
||||||
|
<span className={'flex_label'}>程序运行时间限制:</span>
|
||||||
|
<span className={'flex_value'}>{time_limit}秒</span>
|
||||||
|
</p>
|
||||||
|
<p className={'header_flex'}>
|
||||||
|
<span className={'flex_label'}>出题者:</span>
|
||||||
|
<Link to="/" style={{ color: '#5091FF'}}>{username}</Link>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<QuillEditor
|
||||||
|
htmlCtx={description}
|
||||||
|
readOnly={true}
|
||||||
|
options={[]}
|
||||||
|
style={{ height: "calc(100% - 109px)" }}
|
||||||
|
/>
|
||||||
|
{/* <div dangerouslySetInnerHTML={{__html: description}}></div> */}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state) => {
|
||||||
|
const { hack } = state.ojForUserReducer;
|
||||||
|
return {
|
||||||
|
hack
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps
|
||||||
|
)(TaskDescription);
|
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* @Description: 控制全局
|
||||||
|
* @Author: tangjiang
|
||||||
|
* @Github:
|
||||||
|
* @Date: 2019-11-27 16:30:50
|
||||||
|
* @LastEditors: tangjiang
|
||||||
|
* @LastEditTime: 2019-11-28 21:15:34
|
||||||
|
*/
|
||||||
|
import types from "./actionTypes";
|
||||||
|
|
||||||
|
// 切换控制台显示与隐藏
|
||||||
|
export const showOrHideControl = (flag) => {
|
||||||
|
return {
|
||||||
|
type: types.SHOW_OR_HIDE_CONTROL,
|
||||||
|
payload: flag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 改变 loading 状态值
|
||||||
|
export const changeLoadingState = (flag) => {
|
||||||
|
return {
|
||||||
|
type: types.LOADING_STATUS,
|
||||||
|
payload: flag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 改变提交按钮状态值
|
||||||
|
export const changeSubmitLoadingStatus = (flag) => {
|
||||||
|
return {
|
||||||
|
type: types.SUBMIT_LOADING_STATUS,
|
||||||
|
payload: flag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发布按钮状态
|
||||||
|
export const changePublishLoadingStatus = (flag) => {
|
||||||
|
return {
|
||||||
|
type: types.PUBLISH_LOADING_STATUS,
|
||||||
|
payload: flag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否是我发布的
|
||||||
|
export const isMyPublish = (flag) => {
|
||||||
|
return {
|
||||||
|
type: types.IS_MY_SOURCE,
|
||||||
|
payload: flag
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* @Description: 全局控制 reducer
|
||||||
|
* @Author: tangjiang
|
||||||
|
* @Github:
|
||||||
|
* @Date: 2019-11-27 16:27:09
|
||||||
|
* @LastEditors: tangjiang
|
||||||
|
* @LastEditTime: 2019-11-28 21:14:36
|
||||||
|
*/
|
||||||
|
import types from "../actions/actionTypes";
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
showOrHideControl: false,
|
||||||
|
loading: false,
|
||||||
|
excuteState: '', // 代码执行状态
|
||||||
|
submitLoading: false, // 提交按钮状态
|
||||||
|
publishLoading: false, // 发布
|
||||||
|
isMySource: false
|
||||||
|
}
|
||||||
|
|
||||||
|
const commonReducer = (state = initialState, action) => {
|
||||||
|
|
||||||
|
switch (action.type) {
|
||||||
|
case types.SHOW_OR_HIDE_CONTROL:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
showOrHideControl: action.payload
|
||||||
|
}
|
||||||
|
case types.LOADING_STATUS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
loading: action.payload
|
||||||
|
}
|
||||||
|
case types.TEST_CODE_STATUS: // 改变代码调试状态
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
excuteState: action.payload
|
||||||
|
}
|
||||||
|
case types.SUBMIT_LOADING_STATUS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
submitLoading: action.payload
|
||||||
|
}
|
||||||
|
case types.PUBLISH_LOADING_STATUS:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
publishLoading: action.payload
|
||||||
|
}
|
||||||
|
case types.IS_MY_SOURCE:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
isMySource: action.payload
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default commonReducer;
|
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* @Description: 用户编程信息
|
||||||
|
* @Author: tangjiang
|
||||||
|
* @Github:
|
||||||
|
* @Date: 2019-11-27 13:41:48
|
||||||
|
* @LastEditors: tangjiang
|
||||||
|
* @LastEditTime: 2019-11-28 17:34:13
|
||||||
|
*/
|
||||||
|
import types from "../actions/actionTypes";
|
||||||
|
import { Base64 } from 'js-base64';
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
user_program_identifier: '', // 开启OJ题的唯一标题
|
||||||
|
hack: {}, // 编程题主要内容
|
||||||
|
test_case: {}, // 测试用例
|
||||||
|
commitRecordDetail: {}, // 提交记录详情
|
||||||
|
commitRecord: [], // 提交记录
|
||||||
|
userCode: '', // 保存当前用户输入的代码
|
||||||
|
isUpdateCode: false, // 是否更新了代码内容
|
||||||
|
userCodeTab: 'task', // 学员测评tab位置: task | record | comment
|
||||||
|
userTestInput: '', // 用户自定义输入值
|
||||||
|
};
|
||||||
|
|
||||||
|
const ojForUserReducer = (state = initialState, action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case types.SAVE_USER_PROGRAM_ID:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
user_program_identifier: action.payload
|
||||||
|
}
|
||||||
|
case types.USER_PROGRAM_DETAIL:
|
||||||
|
const { hack, test_case } = action.payload;
|
||||||
|
const { code }= hack;
|
||||||
|
let tempCode = Base64.decode(code)
|
||||||
|
Object.assign(hack, {code: tempCode});
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
hack: Object.assign({}, hack),
|
||||||
|
test_case: Object.assign({}, test_case)
|
||||||
|
}
|
||||||
|
case types.COMMIT_RECORD_DETAIL:
|
||||||
|
let result = action.payload;
|
||||||
|
result['output'] = Base64.decode(result['output']);
|
||||||
|
result['error_msg'] = Base64.decode(result['error_msg']);
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
commitRecordDetail: Object.assign({}, result)
|
||||||
|
}
|
||||||
|
case types.COMMIT_RECORD:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
commitRecord: [...action.payload]
|
||||||
|
}
|
||||||
|
case types.SAVE_USER_CODE:
|
||||||
|
let curCode = Base64.encode(action.payload);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
userCode: curCode,
|
||||||
|
isUpdateCode: true
|
||||||
|
}
|
||||||
|
case types.IS_UPDATE_CODE:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
isUpdateCode: action.payload
|
||||||
|
}
|
||||||
|
case types.CHANGE_USER_CODE_TAB:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
userCodeTab: action.payload
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ojForUserReducer;
|
Loading…
Reference in new issue