chromesetting
杨树林 5 years ago
commit 0439a9a505

@ -15,7 +15,8 @@ class Admins::ShixunSettingsController < Admins::BaseController
hidden: params[:hidden].present? ? params[:hidden] : false,
homepage_show: params[:homepage_show].present? ? params[:homepage_show] : false,
task_pass: params[:task_pass].present? ? params[:task_pass] : false,
code_hidden: params[:code_hidden].present? ? params[:code_hidden] : false
code_hidden: params[:code_hidden].present? ? params[:code_hidden] : false,
vip: params[:vip].present? ? params[:vip] : false
}
@shixuns_type_check = MirrorRepository.pluck(:type_name,:id)
@ -126,6 +127,6 @@ class Admins::ShixunSettingsController < Admins::BaseController
end
def setting_params
params.permit(:use_scope,:excute_time,:close,:status,:can_copy,:webssh,:hidden,:homepage_show,:task_pass,:code_hidden,:page_no, :id,tag_repertoires:[])
params.permit(:use_scope,:excute_time,:close,:status,:can_copy,:webssh,:hidden,:homepage_show,:task_pass,:code_hidden,:vip,:page_no,:id,tag_repertoires:[])
end
end

@ -1,8 +1,8 @@
class HacksController < ApplicationController
before_action :require_login, except: [:index]
before_action :find_hack, only: [:edit, :update, :publish, :start, :update_set, :delete_set]
before_action :require_teacher_identity, only: [:create, :update_set, :edit, :update]
before_action :require_auth_identity, only: [:update, :edit, :publish, :update_set, :delete_set]
before_action :find_hack, only: [:edit, :update, :publish, :start, :update_set, :delete_set, :destroy]
before_action :require_teacher_identity, only: [:create]
before_action :require_auth_identity, only: [:update, :edit, :publish, :update_set, :delete_set, :destroy]
# 开启编程,如果第一次开启,创建一条记录,如果已经开启过的话,直接返回标识即可
@ -112,6 +112,11 @@ class HacksController < ApplicationController
def new;end
def destroy
@hack.destroy
render_ok
end
private
# 实名认证老师,管理员与运营人员权限
def require_teacher_identity

@ -216,7 +216,8 @@ class MyshixunsController < ApplicationController
begin
shixun_tomcat = edu_setting('tomcat_webssh')
uri = "#{shixun_tomcat}/bridge/webssh/getConnectInfo"
params = {tpiID:@myshixun.id, podType:@myshixun.shixun.try(:webssh),
# 由于中间层采用混合云的方式因为local参数表示在有文件生成的实训是在本地生成还是在其他云端生成评测文件
params = {tpiID:@myshixun.id, podType:@myshixun.shixun.try(:webssh), local: @myshixun.shixun.show_type != -1,
containers:(Base64.urlsafe_encode64(shixun_container_limit @myshixun.shixun))}
res = uri_post uri, params
if res && res['code'].to_i != 0

@ -51,6 +51,7 @@ class Admins::ShixunSettingsQuery < ApplicationQuery
all_shixuns = all_shixuns.where(homepage_show: params[:homepage_show]) if params[:homepage_show]
all_shixuns = all_shixuns.where(task_pass: params[:task_pass]) if params[:task_pass]
all_shixuns = all_shixuns.where(code_hidden: params[:code_hidden]) if params[:code_hidden]
all_shixuns = all_shixuns.where(vip: params[:vip]) if params[:vip]
custom_sort(all_shixuns, params[:sort_by], params[:sort_direction])
end

@ -65,6 +65,12 @@
<span class="only_view">只看已隐藏文件目录</span>
</label>
</div>
<div class="mr-5">
<label for="vip">
<%= check_box_tag :vip, !@sort_json[:vip],@sort_json[:vip], class:"shixun-settings-select" %>
<span class="only_view">只看vip</span>
</label>
</div>
</div>
</div>
<% end %>

@ -15,7 +15,7 @@
<th>
操作
<div class="setting-chosen">
ssh/隐藏/首页/跳关/隐藏目录
ssh/隐藏/首页/跳关/隐藏目录/vip
</div>
</th>
</thead>

@ -48,6 +48,7 @@
<%= check_box_tag :homepage_show,!shixun.homepage_show,shixun.homepage_show,remote:true,data:{id:shixun.id,toggle:"tooltip",placement:"top"},class:"shixun-setting-form",title:"首页" %>
<%= check_box_tag :task_pass,!shixun.task_pass,shixun.task_pass,remote:true,data:{id:shixun.id,toggle:"tooltip",placement:"top"},class:"shixun-setting-form" ,title:"跳关"%>
<%= check_box_tag :code_hidden,!shixun.code_hidden,shixun.code_hidden,remote:true,data:{id:shixun.id,toggle:"tooltip",placement:"top"},class:"shixun-setting-form" ,title:"隐藏目录"%>
<%= check_box_tag :vip,!shixun.vip,shixun.vip,remote:true,data:{id:shixun.id,toggle:"tooltip",placement:"top"},class:"shixun-setting-form" ,title:"vip"%>
</td>
<script>

@ -0,0 +1,5 @@
class AddVipToShixun < ActiveRecord::Migration[5.2]
def change
add_column :shixuns, :vip, :boolean, default: 0
end
end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 74 KiB

@ -316,6 +316,11 @@ const RecordDetail = Loadable({
loader: () => import('./modules/developer/recordDetail'),
loading: Loading
});
// jupyter tpi
const JupyterTPI = Loadable({
loader: () => import('./modules/tpm/jupyter'),
loading: Loading
});
// //个人竞赛报名
// const PersonalCompetit = Loadable({
// loader: () => import('./modules/competition/personal/PersonalCompetit.js'),
@ -697,6 +702,10 @@ class App extends Component {
(props) => (<Developer {...this.props} {...props} {...this.state} />)
}/>
<Route path="/jupytertpi"
component={JupyterTPI}
/>
<Route exact path="/"
// component={ShixunsHome}
render={

@ -0,0 +1,15 @@
/*
* @Description: 引入阿里图标库
* @Author: tangjiang
* @Github:
* @Date: 2019-12-10 09:03:48
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-10 09:05:41
*/
import { Icon } from 'antd';
const MyIcon = Icon.createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/font_1535266_ss6796i6f6j.js'
});
export default MyIcon;

@ -9,13 +9,15 @@
import './index.scss';
import React from 'react';
import { Table, Button, Dropdown, Icon, Menu, Card, Input, Select, Tag } from 'antd';
import { Table, Button, Dropdown, Icon, Menu, Card, Input, Select, Tag, Modal } from 'antd';
import { connect } from 'react-redux';
import actions from '../../redux/actions';
import MultipTags from './components/multiptags';
import { Link } from 'react-router-dom';
// import { Link } from 'react-router-dom';
import CONST from '../../constants';
import { withRouter } from 'react-router';
import { toStore } from 'educoder';
// import MyIcon from '../../common/components/MyIcon';
const {tagBackground, diffText} = CONST;
const { Search } = Input;
@ -104,14 +106,30 @@ class DeveloperHome extends React.PureComponent {
options = {
title: '操作',
key: 'action',
fixed: 'right',
// fixed: 'right',
width: 100,
render: (text, record) => (
<span>
<Button type="primary" onClick={() => this.handleClickEditor(record.identifier)}>编辑
<React.Fragment>
<Button
shape="circle"
type="primary"
icon="edit"
size="small"
onClick={() => this.handleClickEditor(record.identifier)}
>
{/* <Link to={`/problems/${record.identifier}/edit`}></Link> */}
</Button>
</span>
<Button
shape="circle"
type="danger"
icon="close"
size="small"
style={{ marginLeft: '10px' }}
onClick={() => this.handleClickDelete(record)}
>
{/* <Link to={`/problems/${record.identifier}/edit`}></Link> */}
</Button>
</React.Fragment>
),
}
@ -222,6 +240,22 @@ class DeveloperHome extends React.PureComponent {
this.props.history.push(`/problems/${identifier}/edit`)
}
}
// 删除
handleClickDelete = (record) => {
const { deleteItem } = this.props;
Modal.confirm({
title: '删除',
content: `确定要删除${record.name}吗?`,
okText: '确定',
cancelText: '取消',
onOk () {
// 调用删除接口
console.log(record.identifier);
deleteItem(record.identifier);
}
});
}
// table条件变化时
handleTableChange = (pagination, filters, sorter) => {
const {field, order} = sorter;
@ -374,7 +408,7 @@ class DeveloperHome extends React.PureComponent {
// console.log('name has click', record);
// 先调用start接口获取返回的 identifier, 再跳转到开启编辑
if (this.isLogin()) {
// console.log(record);
toStore('hack_identifier', record.identifier); // 保存当前编辑的id号
this.props.startProgramQuestion(record.identifier, this.props);
}
}
@ -499,7 +533,8 @@ const mapDispatchToProps = (dispatch) => ({
handleClick: () => dispatch(actions.toggleTodo()),
fetchOJList: (params) => dispatch(actions.getOJList(params)),
changePaginationInfo: (obj) => dispatch(actions.changePaginationInfo(obj)),
startProgramQuestion: (id, props) => dispatch(actions.startProgramQuestion(id, props))
startProgramQuestion: (id, props) => dispatch(actions.startProgramQuestion(id, props)),
deleteItem: (identifier) => dispatch(actions.deleteItem(identifier))
});
export default withRouter(connect(

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-27 16:02:36
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-03 09:19:54
* @LastEditTime: 2019-12-10 09:30:27
*/
import './index.scss';
import React, { useState, useRef } from 'react';

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-28 08:44:54
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-03 12:38:27
* @LastEditTime: 2019-12-10 09:24:02
*/
import './index.scss';
import React, { useState, useEffect } from 'react';
@ -19,7 +19,7 @@ function ExecResult (props) {
// 指定渲染初始, 加载中, 加载完成页面内容
const renderInit = () => (
<div className={'excute_result_area excute_flex_center'}>
<span className={'init_ctx'}>先点击调试代码运行您的代码</span>
<span className={'init_ctx'}>填写测试用例的输入值点击调试代码</span>
</div>
);
const renderLoading = () => (

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-27 19:46:14
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-03 09:14:59
* @LastEditTime: 2019-12-10 09:31:00
*/
import './index.scss';
import React, { useState, useEffect, useRef, useImperativeHandle, forwardRef } from 'react';
@ -42,7 +42,6 @@ function InitTabCtx (props, ref) {
<Form className={'user_case_form'}>
<FormItem
className={'input_area flex_l'}
label='输入'
>
{
getFieldDecorator('input', {
@ -50,7 +49,10 @@ function InitTabCtx (props, ref) {
{ required: true, message: '输入值不能为空'}
],
initialValue: inputValue
})(<TextArea rows={5} />)
})(<TextArea
rows={8}
placeholder="请填写测试用例的输入值,点击“调试代码”"
/>)
}
</FormItem>
</Form>

@ -27,7 +27,7 @@ export default class MultipTags extends PureComponent {
<span className={`tag-txt ${type}`}>
{ text }
</span>
<span className={'tag-numb'}>
<span className={`tag-numb ${type}`}>
{ result }
</span>
</div>

@ -5,38 +5,75 @@
.tag-txt, .tag-numb{
display: inline-block;
vertical-align: middle;
padding: 0 10px;
// line-height: 20px;
// height: 20px;
box-sizing: border-box;
font-size: 12px;
text-align: center;
height: 24px;
padding: 0 10px;
}
.tag-txt{
border: 1px solid transparent;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
color: #fff;
.tag-txt,
.tag-numb{
border: 1px solid transparent;
&.primary{
background: #1890ff;
// background: #28BD8B;
border-color: #28BD8B;
color: #28BD8B;
}
&.warning{
background: #faad14;
// background: #FF9802;
border-color: #FF9802;
color: #FF9802;
}
&.success{
background: #52c41a;
// background: #52c41a;
border-color: #28BD8B;
color: #28BD8B;
}
&.error{
background: #f5222d;
// background: #FF5555;
border-color: #FF5555;
color: #FF5555;
}
}
.tag-txt{
position: relative;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
// color: #fff;
border-right: none;
&::before {
position: absolute;
content: '';
right: 0;
top: 5px;
bottom:5px;
border-right: 1px solid transparent;
}
&.primary::before{
border-right-color: #28BD8B;
}
&.warning::before{
border-right-color: #FF9802;
}
&.success::before{
border-right-color: #28BD8B;
}
&.error::before{
border-right-color: #FF5555;
}
}
.tag-numb{
border: 1px solid rgba(238, 238, 238, 1);
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
border-left-color: transparent;
margin-left: -1px;
min-width: 40px;
border-left: none;
}
}

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-27 15:02:52
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-06 16:50:54
* @LastEditTime: 2019-12-10 09:20:42
*/
import './index.scss';
import React, { useState, useRef, useEffect } from 'react';
@ -14,6 +14,8 @@ import { connect } from 'react-redux';
import MonacoEditor from '@monaco-editor/react';
import SettingDrawer from '../../components/monacoSetting';
import CONST from '../../../../constants';
import MyIcon from '../../../../common/components/MyIcon';
// import actions from '../../../../redux/actions';
const { fontSetting, opacitySetting } = CONST;
@ -38,7 +40,7 @@ function MyMonacoEditor (props, ref) {
const [theme, setTheme] = useState(() => { // 主题 theme
return fromStore('oj_theme') || 'dark';
});
const [ height, setHeight ] = useState('calc(100% - 112px)');
const [ height, setHeight ] = useState('calc(100% - 56px)');
const editorRef = useRef(null);
useEffect(() => {
@ -48,7 +50,7 @@ function MyMonacoEditor (props, ref) {
}, [props]);
useEffect(() => {
setHeight(showOrHideControl ? 'calc(100% - 382px)' : 'calc(100% - 112px)');
setHeight(showOrHideControl ? 'calc(100% - 382px)' : 'calc(100% - 56px)');
}, [showOrHideControl]);
// 控制侧边栏设置的显示
@ -101,13 +103,19 @@ function MyMonacoEditor (props, ref) {
})
}
const renderRestore = identifier ? (
<MyIcon type="iconzaicizairu" />
) : '';
return (
<React.Fragment>
<div className={"monaco_editor_area"}>
<div className="code_title">
{/* 未保存时 ? '学员初始代码文件' : main.x */}
<span className='flex_strict' style={{ color: '#fff'}}>{identifier ? '' : '学员初始代码文件'}</span>
<span className='flex_strict'>{identifier ? '已保存' : ''}</span>
<span onClick={handleRestoreCode} className="flex_normal">{identifier ? '恢复初始代码' : ''}</span>
<Icon className={'code-icon'} type="setting" onClick={handleShowDrawer}/>
<span onClick={handleRestoreCode} className="flex_normal">{renderRestore}</span>
{/* <Icon className={'code-icon'} type="setting" onClick={handleShowDrawer}/> */}
<MyIcon className='code-icon' type="iconshezhi" onClick={handleShowDrawer}/>
</div>
<MonacoEditor
height={height}

@ -4,7 +4,8 @@
display: flex;
align-items: center;
// justify-content: space-between;
background: #000;
// background: #000;
background: #333333;
color: #fff;
height: 56px;
padding: 0 30px;
@ -19,5 +20,39 @@
.code-icon{
cursor: pointer;
}
.flex_strict,
.flex_normal,
.code-icon{
color: #888;
}
}
}
.setting_drawer{
// .ant-drawer-body{
// // height: calc(100vh - 120px);
// // overflow-y: auto;
// }
.ant-drawer-content{
top: 120px;
bottom: 56px;
height: calc(100vh - 176px);
background: #333333;
color: #fff;
.setting_h2{
color: #fff;
}
select{
color: #fff;
background: #222222;
height: 24px;
// line-height: 24px;
margin-top: 4px;
}
select option{
background: gold;
color: #fff;
}
}
}

@ -0,0 +1,28 @@
/*
* @Description: 用户头像及昵称
* @Author: tangjiang
* @Github:
* @Date: 2019-12-09 17:11:28
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-09 17:36:55
*/
import './index.scss';
import React from 'react';
import { getImageUrl } from 'educoder'
function UserInfo (props) {
const {image_url, name} = props.userInfo;
return (
<div className={'avator_nicker'}>
<img alt="用户头像" className={'student_img'} src={getImageUrl(`images/${image_url}` || 'images/educoder/headNavLogo.png?1526520218')} />
<span className={'student_nicker'}>
{name || ''}
</span>
</div>
);
}
export default UserInfo
export {
UserInfo
};

@ -0,0 +1,25 @@
.avator_nicker{
position: absolute;
color: #fff;
line-height: 65px;
// height: 65px;
.student_img,
.student_nicker{
display: inline-block;
vertical-align: top;
}
.student_nicker{
margin-left: 10px;
}
.student_img{
width: 30px;
height: 30px;
border-radius: 50%;
margin-top: 15px;
}
}

@ -9,10 +9,12 @@ import './index.scss';
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import SplitPane from 'react-split-pane';// import { Form } from 'antd';
import { Button, Icon } from 'antd';
import { Link } from 'react-router-dom';
import { Button } from 'antd';
import LeftPane from './leftpane';
import RightPane from './rightpane';
import { withRouter } from 'react-router';
import { toStore } from 'educoder';
import UserInfo from '../components/userInfo';
// import RightPane from './rightpane/index';
import actions from '../../../redux/actions';
@ -22,10 +24,15 @@ const NewOrEditTask = (props) => {
handlePublish,
// testCases = [],
// ojTestCaseValidate = [],
// changeSubmitLoadingStatus,
identifier,
isPublish,
userInfo,
submitLoading,
changeSubmitLoadingStatus,
changePublishLoadingStatus,
startProgramQuestion,
getUserInfoForNew,
// updateTestAndValidate,
identifier,
} = props;
// 表单提交
@ -34,12 +41,15 @@ const NewOrEditTask = (props) => {
if (props.identifier) {
props.handleUpdateOjForm(props);
} else {
// 改变loading状态
changeSubmitLoadingStatus(true);
props.handleFormSubmit(props); // 提交表单
}
};
useEffect(() => {
// 获取用户信息
getUserInfoForNew();
// console.log('获取路由参数: ====', props.match.params);
const id = props.match.params.id;
// 保存OJForm的id号指明是编辑还是新增
@ -55,25 +65,101 @@ const NewOrEditTask = (props) => {
return () => {}
}, []);
// 模拟挑战
const startChallenge = () => {
// 调用 start 接口, 成功后跳转到模拟页面
startProgramQuestion(identifier, props);
}
// 取消
const handleClickCancel = () => {
// 清空当前输入值并跳转至列表页
props.clearOJFormStore();
// 清空描述信息
toStore('oj_description', '');
setInterval(function () {
props.history.push('/problems');
}, 500);
}
// 发布
const handleClickPublish = () => {
// console.log('public has click');
changePublishLoadingStatus(true);
handlePublish(props, 'publish');
}
// 取消保存/取消按钮
const renderSaveOrCancel = () => {
return (
<React.Fragment>
<Button
onClick={handleClickCancel}
style={{ background: '#666666', color: '#fff', border: 'none' }}
>取消</Button>
<Button
type="primary"
loading={submitLoading}
onClick={handleSubmitForm}
>保存</Button>
</React.Fragment>
);
}
// 发布/模拟挑战
const renderPubOrFight = () => {
const pubButton = isPublish ? '' : (<Button
type="primary"
loading={publishLoading}
onClick={handleClickPublish}
>立即发布</Button>);
return (
<React.Fragment>
<Button
type="primary"
loading={submitLoading}
onClick={handleSubmitForm}
>更新</Button>
{pubButton}
<Button type="primary" onClick={startChallenge}>模拟挑战</Button>
</React.Fragment>
)
}
// 渲染退出
const renderQuit = () => {
return identifier ? (
<Button type="link"
style={{
position: 'absolute',
right: '10px',
top: '15px',
color: '#5091FF'
}}
onClick={handleClickCancel}
>退出</Button>
) : ''
}
return (
<div className={'new_add_task_wrap'}>
<div className={'task_header'}>
<Link to="/problems" className={'header_btn'} >
{/* <Link to="/problems" className={'header_btn'} >
<Icon type="left" style={{ marginRight: '5px'}}/>后退
</Link>
</Link> */}
<UserInfo userInfo={userInfo}/>
<p className={'header_title'}>{props.name || ''}</p>
<Button
{ renderQuit() }
{/* <Link style={{
position: 'absolute',
right: '30px',
top: 0,
color: '#5091FF'
}} to="/problems">退出</Link> */}
{/* <Button
style={{ display: identifier ? 'none' : 'block'}}
loading={publishLoading}
className={`header_btn`}
type="primary"
onClick={handleClickPublish}>立即发布</Button>
onClick={handleClickPublish}>立即发布</Button> */}
</div>
<div className="split-pane-area">
<SplitPane split="vertical" minSize={350} maxSize={-350} defaultSize="40%">
@ -86,18 +172,30 @@ const NewOrEditTask = (props) => {
</SplitPane>
</SplitPane>
</div>
{/* 控制台 */}
<div className='new_add_task_ctl'>
{
/* 录入时: 取消 保存 */
/* 保存未发布: 立即发布 模拟挑战 */
}
{ !identifier ? renderSaveOrCancel() : renderPubOrFight() }
</div>
</div>
)
}
const mapStateToProps = (state) => {
const { ojForm, identifier, testCases } = state.ojFormReducer;
const { publishLoading } = state.commonReducer;
const { ojForm, identifier, testCases, isPublish } = state.ojFormReducer;
const { publishLoading, submitLoading } = state.commonReducer;
const { userInfo } = state.userReducer;
return {
name: ojForm.name,
identifier,
testCases,
publishLoading
isPublish, // 是否已发布
publishLoading,
submitLoading,
userInfo
}
};
@ -122,9 +220,13 @@ const mapDispatchToProps = (dispatch) => ({
changePublishLoadingStatus: (flag) => dispatch(actions.changePublishLoadingStatus(flag)),
// 测试用例及验证
updateTestAndValidate: (obj) => dispatch(actions.updateTestAndValidate(obj)),
// 开启模拟挑战
startProgramQuestion: (id, props) => dispatch(actions.startProgramQuestion(id, props)),
// 新建时获取信息
getUserInfoForNew: () => dispatch(actions.getUserInfoForNew())
});
export default connect(
export default withRouter(connect(
mapStateToProps,
mapDispatchToProps
)(NewOrEditTask);
)(NewOrEditTask));

@ -1,2 +1,18 @@
@import '../split_pane_resizer.scss';
.new_add_task_wrap {
.split-pane-area{
height: calc(100vh - 121px);
}
}
.new_add_task_ctl{
display: flex;
align-items: center;
justify-content: center;
height: 56px;
background: #333333;
> button{
margin-right: 20px;
}
}

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-21 09:19:38
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-04 15:44:12
* @LastEditTime: 2019-12-10 19:37:35
*/
import './index.scss';
import React, { useState } from 'react';
@ -16,7 +16,7 @@ const { TextArea } = Input;
const FormItem = Form.Item;
const AddTestDemo = (props) => {
const {
key,
// key,
onSubmitTest,
onDeleteTest,
testCase,
@ -158,7 +158,8 @@ const AddTestDemo = (props) => {
help={testCaseValidate.output.errMsg}
colon={ false }
>
<Input
<TextArea
rows={5}
value={testCase.output}
onChange={handleOutputChange}
disabled={isDisabled(testCase)}/>

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-20 10:35:40
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-06 20:20:27
* @LastEditTime: 2019-12-09 10:22:03
*/
import 'quill/dist/quill.core.css';
import 'quill/dist/quill.bubble.css';
@ -236,22 +236,41 @@ class EditTab extends React.Component {
<div className={'editor_area'} id="textCase">
<Form className={'editor_form'}>
<FormItem
className={`input_area flex_60`}
label={<span>{myLabel(jcLabel['name'])}</span>}
validateStatus={ojFormValidate.name.validateStatus}
help={ojFormValidate.name.errMsg}
className={`input_area flex_50 flex_50_left`}
label={<span>{myLabel(jcLabel['difficult'])}</span>}
validateStatus={ojFormValidate.difficult.validateStatus}
help={ojFormValidate.difficult.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}
/>
<Select onChange={this.handleChangeDifficult} value={`${ojForm.difficult}`}>
{getOptions('difficult')}
</Select>
</FormItem>
<FormItem
className={`input_area flex_40`}
className={`input_area flex_50 flex_50_right`}
label={<span>{myLabel(jcLabel['category'], '合理的分类有利于快速检索')}</span>}
validateStatus={ojFormValidate.category.validateStatus}
help={ojFormValidate.category.errMsg}
colon={ false }
>
<Select onChange={this.handleChangeCategory} value={`${ojForm.category}`}>
{getOptions('category')}
</Select>
</FormItem>
<FormItem
className={`input_area flex_50 flex_50_left`}
label={<span>{myLabel(jcLabel['timeLimit'], '程序允许时间限制时长,单位:秒')}</span>}
validateStatus={ojFormValidate.timeLimit.validateStatus}
help={ojFormValidate.timeLimit.errMsg}
colon={ false }
>
<InputNumber value={ojForm.timeLimit} min={0} style={{ width: '100%' }} onChange={this.handleTimeLimitChange}/>
</FormItem>
<FormItem
className={`input_area flex_50 flex_50_right`}
label={<span>{myLabel(jcLabel['language'])}</span>}
validateStatus={ojFormValidate.language.validateStatus}
help={ojFormValidate.language.errMsg}
@ -261,6 +280,23 @@ class EditTab extends React.Component {
{getOptions('language')}
</Select>
</FormItem>
<FormItem
className={`input_area flex_100`}
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_100`}
label={<span>{myLabel(jcLabel['description'])}</span>}
@ -276,38 +312,8 @@ class EditTab extends React.Component {
options={quillConfig}
/>
</FormItem>
<FormItem
className={`input_area flex_50 flex_50_left`}
label={<span>{myLabel(jcLabel['difficult'], '任务的难易程度')}</span>}
validateStatus={ojFormValidate.difficult.validateStatus}
help={ojFormValidate.difficult.errMsg}
colon={ false }
>
<Select onChange={this.handleChangeDifficult} value={`${ojForm.difficult}`}>
{getOptions('difficult')}
</Select>
</FormItem>
<FormItem
className={`input_area flex_50 flex_50_right`}
label={<span>{myLabel(jcLabel['timeLimit'], '程序允许时间限制时长,单位:秒')}</span>}
validateStatus={ojFormValidate.timeLimit.validateStatus}
help={ojFormValidate.timeLimit.errMsg}
colon={ false }
>
<InputNumber value={ojForm.timeLimit} min={0} style={{ width: '100%' }} onChange={this.handleTimeLimitChange}/>
</FormItem>
<FormItem
className={`input_area flex_50 flex_50_left`}
label={<span>{myLabel(jcLabel['category'], '任务所属分类')}</span>}
validateStatus={ojFormValidate.category.validateStatus}
help={ojFormValidate.category.errMsg}
colon={ false }
>
<Select onChange={this.handleChangeCategory} value={`${ojForm.category}`}>
{getOptions('category')}
</Select>
</FormItem>
<FormItem
{/* <FormItem
className={`input_area flex_50 flex_50_right`}
label={<span>{myLabel(jcLabel['openOrNot'], '社区:您的任务将向整个社会公开')}</span>}
validateStatus={ojFormValidate.openOrNot.validateStatus}
@ -317,13 +323,13 @@ class EditTab extends React.Component {
<Select onChange={this.handleChangeOpenOrNot} value={`${ojForm.openOrNot}`}>
{getOptions('openOrNot')}
</Select>
</FormItem>
</FormItem> */}
</Form>
{/* 添加测试用例 */}
<div className={'test_demo_title'} ref={this.headerRef}>
<h2>测试用例</h2>
<Button type="primary" onClick={handleAddTest}>添加测试用例</Button>
<Button type="primary" ghost onClick={handleAddTest}>添加测试用例</Button>
</div>
<div className="test_demo_ctx">
{ renderTestCase() }

@ -7,44 +7,84 @@
*/
import './index.scss';
import React, { useState } from 'react';
import { Tabs } from 'antd';
import React, { useState, useMemo } from 'react';
// import { Tabs } from 'antd';
import EditorTab from './editorTab';
import PrevTab from './prevTab';
import CommitTab from './commitTab';
const { TabPane } = Tabs;
// const { TabPane } = Tabs;
function LeftPane (props) {
const navItem = [
{
title: '编辑',
key: 'editor'
}, {
title: '预览',
key: 'prev'
}
];
const Comp = {
editor: (<EditorTab />),
prev: (<PrevTab />)
};
const [defaultActiveKey, setDefaultActiveKey] = useState('editor');
const tabArrs = [
{ title: '编辑', key: 'editor', content: (<EditorTab />) },
{ title: '预览', key: 'prev', content: (<PrevTab />) },
// { title: '提交记录', key: 'commit', content: (<CommitTab />) },
];
// const tabArrs = [
// { title: '编辑', key: 'editor', content: (<EditorTab />) },
// { title: '预览', key: 'prev', content: (<PrevTab />) },
// // { title: '提交记录', key: 'commit', content: (<CommitTab />) },
// ];
const tabs = tabArrs.map((tab) => {
const Comp = tab.content;
return (
<TabPane tab={tab.title} key={tab.key}>
{ Comp }
</TabPane>
)
});
// 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);
}
// const handleTabChange = (key) => {
// setDefaultActiveKey(key);
// }
// 执行表单提交函数
const renderComp = useMemo(() => {
return Comp[defaultActiveKey];
}, [defaultActiveKey]);
const renderNavItem = navItem.map((item) => {
const _classes = item.key === defaultActiveKey ? 'add_editor_item active' : 'add_editor_item';
return (
<li
key={item.key}
className={_classes}
onClick={() => setDefaultActiveKey(item.key)}
>
<span className={'item-span'}>{item.title}</span>
</li>
)
});
return (
<Tabs activeKey={defaultActiveKey} onChange={handleTabChange}>
{ tabs }
</Tabs>
// <Tabs activeKey={defaultActiveKey} onChange={handleTabChange}>
// { tabs }
// </Tabs>
<React.Fragment>
<ul className={'add_editor_list_area'}>
{ renderNavItem }
</ul>
<div className="comp_ctx">
{ renderComp }
</div>
</React.Fragment>
)
};

@ -4,21 +4,21 @@
* @Github:
* @Date: 2019-12-01 10:18:35
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-06 17:50:58
* @LastEditTime: 2019-12-09 11:38:15
*/
import './index.scss';
import React from 'react';
import { connect } from 'react-redux';
import MyMonacoEditor from '../../components/myMonacoEditor';
import ControlSetting from '../../components/controlSetting';
// import ControlSetting from '../../components/controlSetting';
import actions from '../../../../redux/actions';
function RightPane (props, ref) {
const {
// identifier,
code,
onSubmitForm,
// code,
// onSubmitForm,
saveOjFormCode
} = props;
@ -48,12 +48,12 @@ function RightPane (props, ref) {
code={props.code}
onCodeChange={handleCodeChange}/>
<ControlSetting
{/* <ControlSetting
// identifier={identifier}
inputValue={props.input}
onSubmitForm={onSubmitForm}
// onDebuggerCode={handleDebuggerCode}
/>
/> */}
</div>
)
}

@ -3,7 +3,7 @@
// justify-content: center;
background-color: #222;
height: 100%;
// justify-content: ;
// height: calc(100vh - 178px);
.code-title,
.controller-pane,
.pane_control_opts{

@ -1,215 +0,0 @@
/*
* @Description: 右侧代码块
* @Author: tangjiang
* @Date: 2019-11-18 08:42:04
* @Last Modified by: tangjiang
* @Last Modified time: 2019-11-20 00:00:34
*/
import './index.scss';
import React, { Fragment, useState, useRef, useEffect } from 'react';
import { Icon, Drawer, Tabs, Button, notification } from 'antd';
import _ from 'lodash';
import MonacoEditor from '@monaco-editor/react';
import { connect } from 'react-redux';
import InitTabCtx from './initTabCtx';
import SettingDrawer from '../../components/monacoSetting';
import CONST from '../../../../constants';
import actions from '../../../../redux/actions';
const { fontSetting, opacitySetting } = CONST;
const { TabPane } = Tabs;
const RightPaneCode = (props) => {
const [showDrawer, setShowDrawer] = useState(false); // 控制配置滑框
const [defaultActiveKey, setDefaultActiveKey] = useState('1'); // 当前选中的tab
const [showTextResult, setShowTextResult] = useState(false); // 是否点击控制台按钮
const [editCode, setEditCode] = useState(()=> {
return '#include <stdio.h>';
}); // monaco编辑器内容
const [language, setLanguage] = useState('C')
const [fontSize,setFontSize] = useState(12);
const editorRef = useRef(null); // 编辑器ref
useEffect(() => {
if (props.language) {
// console.log('当前输入的代码:', editCode);
// console.log('当前输入的语言:', props.language);
setLanguage(props.language)
}
}, [props.language]);
useEffect(() => {
}, [props.testCases]);
useEffect(() => {
}, [editCode]);
// 监听store中编辑器内容变化
useEffect(() => {
setEditCode(props.code);
}, [props.code]);
// 打开设置
const handleShowDrawer = (e) => {
e.preventDefault();
setShowDrawer(true);
}
// 关闭设置
const handleDrawerClose = (e) => {
e.preventDefault();
setShowDrawer(false);
}
// 切换tab
const handleTabChange = (key) => {
setDefaultActiveKey(key);
}
// 显示/隐藏tab
const handleShowControl = () => {
setShowTextResult(!showTextResult);
}
// 侧边栏改变字体大小
const handleFontSizeChange = (value) => {
setFontSize(value);
}
// 文本框内容变化时,记录文本框内容
const handleEditorChange = (origin, monaco) => {
editorRef.current = monaco; // 获取当前monaco实例
setEditCode(origin); // 保存编辑器初始值
editorRef.current.onDidChangeModelContent(e => { // 监听编辑器内容的变化
// TODO 需要优化 节流
const val = editorRef.current.getValue();
setEditCode(val);
// 保存当前代码
props.saveOjFormCode(val);
});
}
// 提交按钮点击
const handleSubmit = (e) => {
e.preventDefault();
if (!editCode) {
notification['error']({
message: '必填',
description: '代码块内容必须输入!'
});
editorRef.current.focus();
return;
}
props.changePublishLoadingStatus(true);
const { onSubmitForm } = props;
onSubmitForm(editCode);
}
// 调试测试代码
// const handleTestCode = () => {
// // 打开控制台信息
// setShowTextResult(true);
// this.formRef.handleTestCodeFormSubmit(() => {
// // 当验证通过后 切换tab 到代码执行结果
// setDefaultActiveKey('2');
// });
// }
// 控制台点击时 添加active属性
const classNames = `control_tab ${showTextResult ? 'move_up move_up_final' : 'move_down_final'}`;
// 配置编辑器属性
const editorOptions = {
selectOnLineNumbers: true,
automaticLayout: true,
fontSize: `${fontSize}px`
}
// 返回渲染值
return (
<Fragment>
<div className={'right_pane_code_wrap'}>
<div className={'code-title'}>
<span></span>
<Icon className={'code-icon'} type="setting" onClick={handleShowDrawer}/>
</div>
{/** 代码编辑器 */}
<MonacoEditor
height={showTextResult ? 'calc(100% - 382px)' : 'calc(100% - 112px)'}
width="100%"
language={language.toLowerCase()}
value={editCode}
options={editorOptions}
theme="dark"
editorDidMount={handleEditorChange}
/>
{/* 控制台信息 */}
<div className="pane_control_area">
<Tabs
className={classNames}
activeKey={defaultActiveKey}
tabBarStyle={{ backgroundColor: '#000', color: '#fff' }}
onChange={handleTabChange}
>
<TabPane tab={'自定义测试用例'} key={'1'} style={{ height: '280px', overflowY: 'auto' }}>
<InitTabCtx wrappedComponentRef={(form) => this.formRef = form }/>
</TabPane>
<TabPane tab={'代码执行结果'} key={'2'} style={{ height: '280px', overflowY: 'auto' }}>
<h2>代码执行结果</h2>
</TabPane>
</Tabs>
<div className="pane_control_opts">
<Button type="link" style={{ color: '#fff' }} onClick={handleShowControl}>控制台 <Icon type={ showTextResult ? "down" : "up" } /></Button>
<p>
{/* <Button ghost
style={{ marginRight: '10px', color: '#28BD8B', borderColor: '#28BD8B' }}
onClick={handleTestCode}
disabled={!props.identifier || props.testCases.length === 0}
>调试代码</Button> */}
<Button
loading={props.submitLoading}
type="primary"
onClick={handleSubmit}
>{props.identifier ? '更新' : '提交'}</Button>
</p>
</div>
</div>
</div>
<Drawer
className={'setting_drawer'}
placement="right"
closable={false}
onClose={handleDrawerClose}
visible={showDrawer}
>
<SettingDrawer {...fontSetting} onChangeFontSize={handleFontSizeChange}/>
<SettingDrawer {...opacitySetting}/>
</Drawer>
</Fragment>
);
}
const mapStateToProps = (state) => {
const { ojForm, testCases, identifier, code } = state.ojFormReducer;
const { submitLoading } = state.commonReducer;
return {
language: ojForm.language,
testCases,
identifier,
code,
submitLoading
}
};
const mapDispatchToProps = (dispatch) => ({
saveOjFormCode: (code) => dispatch(actions.saveOjFormCode(code)),
changePublishLoadingStatus: (flag) => dispatch(actions.changeSubmitLoadingStatus(flag))
});
//
export default connect(
mapStateToProps,
mapDispatchToProps
)(RightPaneCode);

@ -4,13 +4,14 @@
* @Github:
* @Date: 2019-11-25 09:46:03
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-04 23:09:38
* @LastEditTime: 2019-12-10 16:10:23
*/
// import 'quill/dist/quill.core.css';
// import 'quill/dist/quill.bubble.css';
// import 'quill/dist/quill.snow.css';
// import 'katex/dist/katex.css';
import './index.scss';
import 'katex/dist/katex.min.css';
import React from 'react';
import katex from 'katex';
const Quill = require('quill');
@ -120,15 +121,12 @@ class QuillEditor extends React.Component {
render () {
const styles = this.props.style || {}
return (
<div>
<div
id="quill_editor"
style={styles}
className={'quill_editor_area'}
ref={this.editorRef}>
</div>
</div>
<div
id="quill_editor"
style={styles}
className={'quill_editor_area'}
ref={this.editorRef}>
</div>
);
}
}

@ -1,4 +0,0 @@
.quill_editor_area{
height: 300px;
overflow-y: auto;
}

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-12-04 08:36:21
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-05 13:52:04
* @LastEditTime: 2019-12-10 18:55:02
*/
import './index.scss';
import React, { useState, useEffect } from 'react';
@ -14,23 +14,26 @@ import ErrorResult from '../components/errorResult';
import { Link } from 'react-router-dom';
import MonacoEditor from '@monaco-editor/react';
import { connect } from 'react-redux';
import { getImageUrl } from 'educoder';
// import { getImageUrl } from 'educoder';
import actions from '../../../redux/actions';
import CONST from '../../../constants';
import UserInfo from '../components/userInfo';
const {reviewResult} = CONST;
function RecordDetail (props) {
const {
match: { params },
mygetHelmetapi = {},
recordDetail,
identifier,
getUserCommitRecordDetail
// identifier,
getUserCommitRecordDetail,
saveEditorCodeForDetail
} = props;
const id = params.id;
const [detail, setDetail] = useState({});
const [user, setUser] = useState({});
const [identifier, setIdentifier] = useState('');
useEffect(() => {
// 根据id获取记录详情
@ -40,17 +43,27 @@ function RecordDetail (props) {
useEffect(() => {
setDetail(recordDetail);
console.log('详情: ', recordDetail);
if (recordDetail) {
const { user, myproblem_identifier, code } = recordDetail;
setUser(user);
setIdentifier(myproblem_identifier);
if (code) {
saveEditorCodeForDetail(code);
}
}
}, [recordDetail]);
return (
<div className="record_detail_area">
<div className="record_detail_header">
<div className="avator_nicker">
<img alt="用户头像" className={'student_img'} src={getImageUrl((mygetHelmetapi && mygetHelmetapi.nav_logo_url) || 'images/educoder/headNavLogo.png?1526520218')} />
{/* <div className="avator_nicker">
<img alt="用户头像" className={'student_img'} src={getImageUrl( (user && `images/${user.image_url}`)|| 'images/educoder/headNavLogo.png?1526520218')} />
<span className={'student_nicker'}>
{(mygetHelmetapi &&mygetHelmetapi.name) || ''}
{(user && user.name) || ''}
</span>
</div>
</div> */}
<UserInfo userInfo={user || {}}/>
<div className={'study_name'}>
<span>{detail.name || 'test'}</span>
</div>
@ -82,7 +95,13 @@ function RecordDetail (props) {
</div>
<div className="detail_ctx_header">
<h2 className="header_h2">提交内容</h2>
<Button className={'header_btn'} type="primary">编辑代码</Button>
<Button
className={'header_btn'}
type="primary"
>
{/* 编辑代码 */}
<Link to={`/myproblems/${identifier}`}>编辑代码</Link>
</Button>
</div>
<div className="result_code_area">
<MonacoEditor
@ -100,15 +119,16 @@ function RecordDetail (props) {
}
const mapStateToProps = (state) => {
const {recordDetail, user_program_identifier} = state.ojForUserReducer;
const {recordDetail} = state.ojForUserReducer;
return {
identifier: user_program_identifier,
// identifier: user_program_identifier,
recordDetail
}
}
const mapDispatchToProps = (dispatch) => ({
// 根据id号获取记录详情
getUserCommitRecordDetail: (id, type) => dispatch(actions.getUserCommitRecordDetail(id, type))
getUserCommitRecordDetail: (id, type) => dispatch(actions.getUserCommitRecordDetail(id, type)),
saveEditorCodeForDetail: (code) => dispatch(actions.saveEditorCodeForDetail(code))
});
export default connect(

@ -7,7 +7,8 @@
.student_study_header,
.record_detail_header{
height: 65px;
background:rgba(34,34,34,1);
// background:rgba(34,34,34,1);
background: #1E1E1E;
padding:0 30px;
}
@ -45,11 +46,6 @@
.pane_right_area
{
position: relative;
height: calc(100% - 65px);
.left_pane,
.right_pane{
height: 100%;
}
}
.student_study_header,
@ -89,6 +85,41 @@
}
}
.add_editor_list_area{
background: #fff;
padding: 0 30px;
margin: 0;
.add_editor_item{
display: inline-block;
height: 56px;
line-height: 56px;
box-sizing: border-box;
margin-right: 30px;
border-bottom: 2px solid transparent;
transition: all .3s;
cursor: pointer;
.item-span{
color: #666;
font-size: 16px;
}
// > span{
// cursor: pointer;
// }
&.active{
border-bottom-color: #5091FF;
.item-span{
color: #5091FF;
}
}
}
}
.comp_ctx{
height: calc(100vh - 178px);
overflow-y: hidden;
}
.split-pane-area,
.split-pane-left{
.ant-tabs-nav-wrap{
@ -109,9 +140,8 @@
.editor_area,
.prev_area{
height: calc(100vh - 110px);
height: 100%;
overflow-y: auto;
// padding: 20px 0;
}
}

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-23 10:53:19
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-06 18:59:22
* @LastEditTime: 2019-12-10 19:16:18
*/
import './index.scss';
import React, { useEffect } from 'react';
@ -12,22 +12,26 @@ import { connect } from 'react-redux';
import SplitPane from 'react-split-pane';
import LeftPane from './leftpane';
import RightPane from './rightpane';
import { Link } from 'react-router-dom';
import { getImageUrl } from 'educoder'
// import { Link } from 'react-router-dom';
// import { getImageUrl } from 'educoder'
// import RightPane from '../newOrEditTask/rightpane';
import { Button } from 'antd';
import { Icon } from 'antd';
import UserInfo from '../components/userInfo';
import actions from '../../../redux/actions';
import { fromStore} from 'educoder';
import { withRouter } from 'react-router';
const StudentStudy = (props) => {
const {
mygetHelmetapi = {}
userInfo,
hack_identifier
} = props;
useEffect(() => {
const {
match: { params },
getUserProgramDetail,
saveUserProgramIdentifier,
saveUserProgramIdentifier
} = props;
let { id } = params;
@ -37,22 +41,48 @@ const StudentStudy = (props) => {
// startProgramQuestion(id);
getUserProgramDetail(id);
}, []);
const _hack_id = hack_identifier || fromStore('hack_identifier');
// 处理编辑
const handleClickEditor = () => {
props.saveEditorCodeForDetail();
props.history.push(`/problems/${_hack_id}/edit`);
}
// 处理退出
const handleClickQuit = () => {
props.saveEditorCodeForDetail();
props.history.push('/problems');
}
return (
<div className={'student_study_warp'}>
<div className={'student_study_header'}>
<div className={'avator_nicker'}>
{/* <div className={'avator_nicker'}>
<img alt="用户头像" className={'student_img'} src={getImageUrl((mygetHelmetapi && mygetHelmetapi.nav_logo_url) || 'images/educoder/headNavLogo.png?1526520218')} />
<span className={'student_nicker'}>
{(mygetHelmetapi &&mygetHelmetapi.name) || ''}
</span>
</div>
</div> */}
<UserInfo userInfo={userInfo}/>
<div className={'study_name'}>
<span>乘积最大序列</span>
</div>
<div className={'study_quit'}>
<Button>
<Link to="/problems">退出</Link>
{/* to={`/problems/${_hack_id}/edit`} */}
<span onClick={handleClickEditor} className="quit-btn">
<Icon type="form" className="quit-icon"/> 编辑
</span>
{/* to="/problems" */}
<span onClick={handleClickQuit} className="quit-btn">
<Icon type="poweroff" className="quit-icon"/> 退出
</span>
{/* <Button type="link" icon="form" className='quit-btn'>
<Link to="/problems">编辑</Link>
</Button>
<Button type="link" icon="poweroff" className='quit-btn'>
<Link to="/problems">退出</Link>
</Button> */}
</div>
</div>
<div className="split-pane-area">
@ -70,19 +100,27 @@ const StudentStudy = (props) => {
)
}
const mapStateToProps = (state) => ({});
const mapStateToProps = (state) => {
const { userInfo } = state.userReducer;
const { hack_identifier } = state.ojForUserReducer;
return {
userInfo,
hack_identifier
};
};
const mapDispatchToProps = (dispatch) => ({
// 调用开启编辑
// startProgramQuestion: (id) => dispatch(actions.startProgramQuestion(id))
// 调用编程题详情
getUserProgramDetail: (id) => dispatch(actions.getUserProgramDetail(id)),
saveUserProgramIdentifier: (id) => dispatch(actions.saveUserProgramIdentifier(id))
saveUserProgramIdentifier: (id) => dispatch(actions.saveUserProgramIdentifier(id)),
saveEditorCodeForDetail: (code) => dispatch(actions.saveEditorCodeForDetail(code))
});
export default connect(
export default withRouter(connect(
mapStateToProps,
mapDispatchToProps
)(StudentStudy);
)(StudentStudy));

@ -1,4 +1,7 @@
@import '../split_pane_resizer.scss';
.split-pane-area{
height: calc(100vh - 65px);
}
.right_pane_code_wrap{
position: relative;

@ -1,5 +1,6 @@
.commit_record_area{
padding: 20px 30px;
// padding: 20px 30px;
padding: 0 30px;
.record_header{
display: flex;
// justify-content: space-between;

@ -4,10 +4,10 @@
* @Github:
* @Date: 2019-11-23 11:33:41
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-03 14:13:19
* @LastEditTime: 2019-12-09 19:57:21
// */
import './index.scss';
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useMemo } from 'react';
import { Tabs, Divider } from 'antd';
import { connect } from 'react-redux';
import Comment from './comment';
@ -15,13 +15,14 @@ import CommitRecord from './commitRecord';
import TaskDescription from './taskDescription';
import TextNumber from './../../components/textNumber';
import actions from '../../../../redux/actions';
const { TabPane } = Tabs;
// const { TabPane } = Tabs;
const LeftPane = (props) => {
const { hack, userCodeTab, changeUserCodeTab } = props;
const { pass_count, submit_count } = hack;
const [defaultActiveKey, setDefaultActiveKey] = useState('task');
console.log(pass_count, submit_count);
const tabArrs = [
{ title: '任务描述', key: 'task', content: (<TaskDescription />) },
@ -29,25 +30,58 @@ const LeftPane = (props) => {
// { title: '评论', key: 'comment', content: (<Comment />) },
];
const navItem = [
{
title: '任务描述',
key: 'task'
},
{
title: '提交记录',
key: 'record'
}
];
const Comp = {
task: (<TaskDescription />),
record: (<CommitRecord />)
};
useEffect(() => {
setDefaultActiveKey(userCodeTab);
}, [userCodeTab])
const tabs = tabArrs.map((tab) => {
const Comp = tab.content;
// 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 renderComp = useMemo(() => {
return Comp[defaultActiveKey];
}, [defaultActiveKey]);
const renderNavItem = navItem.map((item) => {
const _classes = item.key === defaultActiveKey ? 'add_editor_item active' : 'add_editor_item';
return (
<TabPane tab={tab.title} key={tab.key}>
{ Comp }
</TabPane>
<li
key={item.key}
className={_classes}
onClick={() => setDefaultActiveKey(item.key)}
>
<span className={'item-span'}>{item.title}</span>
</li>
)
});
// tab切换时
const handleTabChange = (key) => {
// setDefaultActiveKey(key);
changeUserCodeTab(key);
}
// 点击消息
const handleClickMessage = () => {
console.log('点击的消息图标---------');
@ -65,9 +99,27 @@ const LeftPane = (props) => {
return (
<React.Fragment>
<Tabs className={'user_code_tab_area'} activeKey={defaultActiveKey} onChange={handleTabChange}>
{/* <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> */}
<ul className={'add_editor_list_area'}>
{ renderNavItem }
</ul>
<div className="comp_ctx">
{ renderComp }
</div>
<div className={'number_area'}>
<div className="number_flex flex_count">
<TextNumber text="通过次数" number={pass_count} position="vertical"/>

@ -15,7 +15,8 @@
width: 100%;
// background: pink;
padding: 0 30px;
background-color: rgba(250,250,250,1);
// background-color: rgba(250,250,250,1);
background: #fff;
.flex_count,
.flex_info{
@ -28,18 +29,23 @@
}
}
.task_description_area,
.commit_record_area{
padding: 0 30px;
height: calc(100vh - 166px);
overflow-y: auto;
// height: calc(100vh - 178px);
// overflow-y: auto;
}
.task_description_area{
.task_desc_area{
height: calc(100vh - 242px);
padding: 0 0 0 15px;
}
.desc_area_header{
display: flex;
justify-content: space-between;
align-items: center;
height: 64px;
padding: 0 30px;
.header_flex{
font-size: 14px;
.flex_label{
@ -51,4 +57,26 @@
}
}
}
}
.student_study_header{
.study_quit{
position: absolute;
right: 30px;
}
.quit-btn{
cursor: pointer;
margin-left: 30px;
color: #888888;
transition: all .3s;
&:hover{
color: #5091FF;
}
// &:last-child{
// color: #888888;
// }
.quit-icon{
margin-right: 5px;
}
}
}

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-27 09:49:30
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-29 13:46:11
* @LastEditTime: 2019-12-09 19:21:55
*/
import '../index.scss';
import React from 'react';
@ -39,12 +39,18 @@ const TaskDescription = (props) => {
<Link to="/" style={{ color: '#5091FF'}}>{username}</Link>
</p>
</div>
<QuillEditor
<div className="task_desc_area">
<QuillEditor
htmlCtx={description}
readOnly={true}
/>
</div>
{/* <QuillEditor
htmlCtx={description}
readOnly={true}
options={[]}
style={{ height: "calc(100% - 109px)" }}
/>
style={{ backgroundColor: 'gold' }}
/> */}
{/* <div dangerouslySetInnerHTML={{__html: description}}></div> */}
</div>
)

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-27 14:59:51
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-06 18:48:52
* @LastEditTime: 2019-12-10 19:00:30
*/
import React, { useState, useEffect } from 'react';
import {connect} from 'react-redux';
@ -21,6 +21,7 @@ const RightPane = (props) => {
input,
hack,
updateCode,
editor_code,
saveUserInputCode,
restoreInitialCode,
saveUserCodeForInterval
@ -29,9 +30,12 @@ const RightPane = (props) => {
const [editorCode, setEditorCode] = useState('');
useEffect(() => {
console.log('1111111');
setEditorCode(hack.code);
}, [hack]);
if (editor_code) {
setEditorCode(editor_code);
} else {
setEditorCode(hack.code);
}
}, [hack, editor_code]);
const handleSubmitForm = () => {
// 提交时, 先调用提交接口,提交成功后,循环调用测评接口
@ -49,7 +53,7 @@ const RightPane = (props) => {
timer = setInterval(() => {
clearInterval(timer);
timer = null;
saveUserCodeForInterval(identifier, code);
saveUserCodeForInterval && saveUserCodeForInterval(identifier, code);
}, 3000);
}
// 保存用户代码块
@ -85,10 +89,11 @@ const RightPane = (props) => {
const mapStateToProps = (state) => {
const {user_program_identifier, hack, userTestInput} = state.ojForUserReducer;
const {user_program_identifier, hack, userTestInput, editor_code} = state.ojForUserReducer;
// const { language, code } = hack;
return {
hack,
editor_code,
input: userTestInput,
submitInput: hack.input,
identifier: user_program_identifier

@ -0,0 +1,50 @@
import React, {Component} from 'react';
class Bottomsubmit extends Component {
constructor(props) {
super(props)
this.state = {
}
}
cannelfun=()=>{
window.location.href=this.props.url
}
render() {
return (
<div>
<style>
{
`
.newFooter{
display:none;
}
`
}
</style>
<div className="clearfix bor-bottom-greyE edu-back-white orderingbox newshixunbottombtn">
<div className=" edu-txt-center padding13-30">
<button type="button" className="ant-btn mr20 newshixunmode backgroundFFF" onClick={()=>this.cannelfun()}><span> </span></button>
<button type="button" className="ant-btn newshixunmode mr40 ant-btn-primary" type="primary" htmlType="submit" onClick={()=>this.props.onSubmits()}><span> </span></button>
</div>
</div>
</div>
);
}
}
export default Bottomsubmit;

@ -23,7 +23,7 @@ const versionNum = '0001';
// let _url_origin = getUrl()
let _url_origin='';
if(window.location.port === "3007"){
_url_origin="http://pre-newweb.educoder.net";
_url_origin="https://test-newweb.educoder.net";
}
// let _url_origin=`https://www.educoder.net`;

@ -21,10 +21,16 @@ export default class Shixuninformation extends Component {
render() {
let {can_copy}=this.state;
return (
<div>
1111
<div className="clearfix mt20 ml30">
<span className="color-grey-6 mt5 fl" style={{minWidth: '95px'}}>复制:</span>
<span className="fl mt5">
<Checkbox checked={can_copy === undefined ? false : can_copy} onChange={this.can_copy}></Checkbox>
<label style={{top:'6px'}} className="color-grey-9 ml10">(勾选则允许已认证的教师复制该实训)</label>
</span>
</div>
</div>
);
}

@ -1,33 +1,482 @@
import React, { Component } from 'react';
import React, {Component} from 'react';
import {Input, Select, Radio, Checkbox, Popconfirm, message, Modal,Icon,DatePicker,Breadcrumb,Upload,Button,notification, Tooltip,Tabs} from 'antd';
import MonacoEditor from 'react-monaco-editor';
import {
Input,
Select,
Radio,
Checkbox,
Popconfirm,
message,
Modal,
Icon,
DatePicker,
Breadcrumb,
Upload,
Button,
notification,
Tooltip,
Tabs,
Form
} from 'antd';
import axios from 'axios';
import './css/TPMsettings.css';
import TPMMDEditor from "../challengesnew/TPMMDEditor";
import { getImageUrl, toPath, getUrl ,appendFileSizeToUploadFileAll, getUploadActionUrl} from 'educoder';
import {getImageUrl, toPath, getUrl, appendFileSizeToUploadFileAll, getUploadActionUrl} from 'educoder';
import {TPMIndexHOC} from "../TPMIndexHOC";
import './css/TPMsettings.css';
import '../newshixuns/css/Newshixuns.css';
export default class Shixuninformation extends Component {
class Shixuninformation extends Component {
constructor(props) {
super(props)
this.contentMdRef = React.createRef();
this.state = {
NAME_COUNT: 60,
shixunmemoMDvalue: "",
language: "java",
}
}
getshixunmemoMDvalue = (value, e) => {
this.setState({
shixunmemoMDvalue: value
})
}
render() {
const {getFieldDecorator} = this.props.form;
const {newshixunlist, languagewrite, systemenvironment, testcoderunmode, fileList, postapplytitle, postapplyvisible, shixunmemoMDvalue} = this.state;
const {shixun_service_configs}=this.props;
let operateauthority = this.props.identity === 1 ? true : this.props.identity < 5 && this.state.status == 0 ? true : false;
const uploadProps = {
width: 600,
fileList,
multiple: true,
// https://github.com/ant-design/ant-design/issues/15505
// showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。
// showUploadList: false,
action: `${getUploadActionUrl()}`,
onChange: this.handleChange,
onRemove: this.onAttachmentRemove,
beforeUpload: (file, fileList) => {
if (this.state.fileList.length >= 1) {
return false
}
// console.log('beforeUpload', file.name);
const isLt150M = file.size / 1024 / 1024 < 50;
if (!isLt150M) {
// this.props.showNotification(`文件大小必须小于50MB`);
notification.open(
{
message: '提示',
description:
'文件大小必须小于50MB',
}
)
}
if (this.state.file !== undefined) {
console.log("763")
this.setState({
file: file
})
} else {
this.setState({
file: file
})
}
console.log("handleChange2");
return isLt150M;
},
}
return (
<div>
1111
<Form onSubmit={this.handleSubmit}>
<Form.Item
label="名称"
style={{"borderBottom": 'none'}}
className="mt15"
>
{getFieldDecorator('name', {
rules: [{
required: true, message: '请输入选题名称',
}, {
max: 60, message: '请输入名称最大限制60个字符',
}, {
whitespace: true, message: '请勿输入空格'
}],
})(
<Input placeholder="请输入名称最大限制60个字符"
className={"input-100-45 greyInput"}
onInput={this.shixunNameInput} autoComplete="off"
addonAfter={`${String(!this.state.shixunName ? 0 : this.state.shixunName.length)}/${this.state.NAME_COUNT}`}
className="newViewAfter"/>
)}
</Form.Item>
<Form.Item
label="简介"
style={{"borderBottom": 'none', 'marginBottom': '0px'}}
className="chooseDes pr"
>
<TPMMDEditor ref={this.contentMdRef} placeholder="请输入简介" mdID={'courseContentMD'}
refreshTimeout={1500}
className="courseMessageMD"
// initValue={this.state.description === null ? "" : this.state.description}
></TPMMDEditor>
</Form.Item>
<Form.Item
label={"难易度"}
style={{"borderBottom": 'none'}}
className="chooseDes pr"
>
{getFieldDecorator('select', {
rules: [{required: true, message: '请选择难易度'}],
})(
<div className="with15 fl pr">
<Select placeholder="请选择难易度"
style={{width: 180}}
onChange={this.Selectthestudent}
>
<Option value={1}>初级</Option>
<Option value={2}>中级</Option>
<Option value={3}>中高级</Option>
<Option value={4}>高级</Option>
</Select>
</div>
)}
<span className="fl ml20 color-grey">实训的难易程度</span>
</Form.Item>
<Form.Item
label={"实验环境"}
style={{"borderBottom": 'none', 'width': '18%', 'float': 'left'}}
className="chooseDes pr"
>
<div>
{getFieldDecorator('selectleft', {
rules: [{required: true, message: '请选择主类别'}],
})(
<div className="width100 fl mr20">
<Select placeholder="请选择主类别"
style={{width: 180}}
onChange={this.selectleft}
defaultOpen={false}
>
{
newshixunlist === undefined ? "" : newshixunlist.main_type.map((item, key) => {
return (
<Option value={item.id} key={key}>
<Tooltip placement="right"
title={item.description === "" ? "无描述" : item.description}>
{item.type_name}
</Tooltip>
</Option>
)
})
}
</Select>
</div>
)}
</div>
</Form.Item>
<Form.Item
style={{"borderBottom": 'none', 'width': '61%', 'float': 'left', 'marginTop': '40px'}}
className="chooseDes pr"
>
<div className=" fl pr mr20">
{getFieldDecorator('selectright', {
rules: [{required: true, message: '请选择小类别'}],
})(
<div className=" fl pr mr20">
<Select placeholder="请选择小类别"
style={{width: 180}}
onChange={this.selectright}
defaultOpen={false}
>
{
newshixunlist === undefined ? "" : newshixunlist.small_type.map((item, key) => {
return (
<Option value={item.id} key={key}>
<Tooltip placement="right"
title={item.description === "" ? "无描述" : item.description}>
{item.type_name}
</Tooltip>
</Option>
)
})
}
</Select>
</div>
)}
<span className="fl ml20 color-grey lineh-20">
<div>
<div className={"font-12"}>
已安装软件hadoop3.1.0jdk1.8
</div>
<div className={"font-12"}>
说明添加了hadoop3.1.0jdk1.8的源码包添加了hadoop3.1.0jdk1.8的源码包
</div>
</div>
</span>
</div>
</Form.Item>
<div className={"both"}></div>
<div className=" color-grey lineh-20 mb20">
没有实验环境
<a className="color-blue" onClick={this.post_apply}> 申请新建</a>
</div>
<Form.Item
label={"评测脚本"}
style={{"borderBottom": 'none'}}
className="chooseDes pr"
>
{getFieldDecorator('select', {
rules: [{required: true, message: '请选择评测脚本'}],
})(
<div className="with15 fl pr">
<Select placeholder="请选择评测脚本"
style={{width: 180}}
onChange={this.Selectthestudent}
>
<Option value={1}>初级</Option>
<Option value={2}>中级</Option>
<Option value={3}>中高级</Option>
<Option value={4}>高级</Option>
</Select>
</div>
)}
<span className="fl ml20 color-blue">
使用自定义脚本
<span className={"color-grey ml10"}>
<Icon type="exclamation-circle"/>
</span>
</span>
</Form.Item>
<div className="mt30 clearfix df">
<div
className={operateauthority === false ? 'nonemodel' : ""}
></div>
<div className="flex1">
<div className="fl" style={{border: '1px solid #ccc'}}>
<MonacoEditor
height="450"
width="1100"
language={this.state.language}
value={shixunmemoMDvalue}
options={{
selectOnLineNumbers: true
}}
onChange={this.getshixunmemoMDvalue}
// onChange={this.getshixunmemoMDvalue}
/>
</div>
</div>
</div>
<Form.Item label="私密版本库:">
<span className="ant-form-text"><Checkbox>若需要对学员隐藏部分版本库内容时请选中选中即启用私密版本库请将需要对学员隐藏的文件存储在私密版本库</Checkbox></span>
</Form.Item>
</Form>
{this.props.identity<3?<div className="edu-back-white padding40-20 mb20">
<p className="color-grey-6 font-16 mb30">服务配置</p>
{ shixun_service_configs&&shixun_service_configs.map((item,key)=>{
return(
<div key={key}>
<div id="5">
<div className="color-grey-6 font-16 mt30 mb20" id="shixun_scenario_type_name">
<span className={"fl"}>{item.name}</span>
{/*<span className={"fr mr40"} onClick={()=>this.Deselectlittle(item.mirror_repository_id)}><i className="fa fa-times-circle color-grey-c font-16 fl"></i></span>*/}
</div>
<div className="clearfix mb5">
<label className="panel-form-label fl">CPU()</label>
<div className="pr fl with80 status_con">
<input type="text" value={item.cpu_limit} onInput={(e)=>this.setConfigsInputs(e,key,1)}
className="panel-box-sizing task-form-100 task-height-40" placeholder="请输入类别名称" />
</div>
<div className="cl"></div>
</div>
<div className="clearfix mb5">
<label className="panel-form-label fl">最低CPU()</label>
<div className="pr fl with80 status_con">
<input type="text" value={item.lower_cpu_limit} onInput={(e)=>this.setConfigsInputs(e,key,2)}
className="panel-box-sizing task-form-100 task-height-40" placeholder="请输入类别名称" />
</div>
<div className="cl"></div>
</div>
<div className="clearfix mb5">
<label className="panel-form-label fl">内存限制(M)</label>
<div className="pr fl with80 status_con">
<input type="text" value={item.memory_limit} onInput={(e)=>this.setConfigsInputs(e,key,3)}
className="panel-box-sizing task-form-100 task-height-40" placeholder="请输入类别名称" />
</div>
<div className="cl"></div>
</div>
<div className="clearfix mb5">
<label className="panel-form-label fl">内存要求(M)</label>
<div className="pr fl with20 status_con">
<input type="text" value={item.request_limit} onInput={(e)=>this.setConfigsInputs(e,key,4)}
className="panel-box-sizing task-form-100 task-height-40" placeholder="请输入类别名称" />
</div>
<label className="panel-form-label fl" style={{width: '48%'}}>温馨提示纯编程类型实训建议使用默认值对于大数据等建议使用最大内存的30%</label>
<div className="cl"></div>
</div>
</div>
</div>
)
})}
</div> :""}
{postapplyvisible === true ? <style>
{
`
body{
overflow: hidden !important;
}
`
}
</style> : ""}
<Modal
keyboard={false}
title="申请新建"
visible={postapplyvisible}
closable={false}
footer={null}
width={850}
heigth={720}
>
<div>
<li className="clearfix ml82">
<label className="fl mt10 "><span
className="color-red fl mt3">*</span>&nbsp;&nbsp;</label>
<textarea
className={this.state.languagewritetype === true ? "fl task-form-80 task-height-150 bor-reds" : "fl task-form-80 task-height-150"}
style={{width: '89%', height: '100px'}}
onInput={this.setlanguagewrite}
value={languagewrite}
placeholder="请填写该镜像是基于什么语言示例Python"
id="demand_info"></textarea>
</li>
<div className={"color-red shixunspanred"}>{this.state.languagewritetype === true ? "请填写该镜像语言" : ""}</div>
<li className="clearfix ml1">
<label className="panel-form-label fl ml50"><span
className="color-red fl mt3">*</span>&nbsp;&nbsp;</label>
<textarea
className={this.state.systemenvironmenttype === true ? "fl task-form-80 task-height-150 bor-reds" : "fl task-form-80 task-height-150"}
onInput={this.setsystemenvironment}
style={{height: '100px'}}
value={systemenvironment}
placeholder="请填写该镜像是基于什么linux系统环境,代码运行环境"
id="demand_info"></textarea>
</li>
<div
className={"color-red shixunspanred"}>{this.state.systemenvironmenttype === true ? "请填写该镜像语言系统环境" : ""}</div>
<li className="clearfix">
<label className="fl mt10"><span
className="color-red fl mt3">*</span>&nbsp;&nbsp;</label>
<textarea
className={this.state.testcoderunmodetype === true ? "fl task-form-80 task-height-150 bor-reds" : "fl task-form-80 task-height-150"}
onInput={this.settestcoderunmode}
value={testcoderunmode}
style={{height: '100px'}}
placeholder="请填写该镜像中测试代码运行方式"
id="demand_info"></textarea>
</li>
<div
className={"color-red shixunspanred"}>{this.state.testcoderunmodetype === true ? "请填写该镜像测试代码运行方式" : ""}</div>
<li className="clearfix ml50">
<label className="panel-form-label fl mt-5"><span
className="color-red fl">*</span>&nbsp;&nbsp;</label>
<div className="mt10" style={{
display: "inline-block"
}}>
<Upload {...uploadProps}>
<Icon type="upload" className="fl mt3"> </Icon>
<span className="color-blue fl cdefault">上传附件</span>
<span className="color-grey-c fl ml10 ">(单个文件50M以内)</span>
</Upload>
</div>
</li>
<div className={"color-red shixunspanred"}>
{this.state.attachmentidstype === true ? "请上传附件" : ""}
</div>
<li className="edu-txt-center clearfix ">
<a className="pop_close task-btn mr30"
onClick={() => this.sendhideModaly()}
>取消</a>
<Button type="primary" onClick={() => this.sendsure_apply()}
className="task-btn task-btn-orange">确定</Button>
</li>
<div className="cl"></div>
</div>
{/*</Form>*/}
</Modal>
<Modal
keyboard={false}
title="提示"
visible={postapplytitle}
closable={false}
footer={null}
>
<div>
<div className="task-popup-content"><p
className="task-popup-text-center font-16"><span
className="font-17 mt10">新建申请已提交请等待管理员的审核</span></p>
<li className="font-14 mt15 color-grey-6 edu-txt-center">我们将在1-2个工作日内与您联系
</li>
</div>
<div className="task-popup-OK clearfix">
<a className="task-btn task-btn-orange"
onClick={this.yeshidemodel}>知道啦</a>
</div>
</div>
</Modal>
</div>
);
}
}
const TopShixuninformation = Form.create({name: 'newshixun'})(Shixuninformation);
export default TopShixuninformation;

File diff suppressed because it is too large Load Diff

@ -111,3 +111,18 @@ a.newuse_scope-btn {
.ml82{
margin-left:82px;
}
.Permanentban{
color:#5091FF !important;
border-color: #5091FF !important;
}
/*tab*/
.ant-tabs-nav{
padding-bottom:18px;
padding-top: 18px;
}
.ant-tabs-extra-content{
margin-top: 18px;
}

@ -353,10 +353,10 @@ export default class TPMsettings extends Component {
task_pass: response.data.shixun.task_pass,
test_set_permission: response.data.shixun.test_set_permission,
hide_code: response.data.shixun.hide_code,
code_edit_permission: response.data.shixun.code_edit_permission,
code_edit_permission: response.data.shixun.code_edit_permission,
code_hidden: response.data.shixun.code_hidden,
is_secret_repository: response.data.shixun.is_secret_repository,
init_is_secret_repository: response.data.shixun.is_secret_repository,
init_is_secret_repository: response.data.shixun.is_secret_repository,
forbid_copy: response.data.shixun.forbid_copy,
vnc: response.data.shixun.vnc,
vnc_evaluate: response.data.shixun.vnc_evaluate,

@ -0,0 +1,47 @@
/*
* @Description: jupyter tpi
* @Author: tangjiang
* @Github:
* @Date: 2019-12-11 08:35:23
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-11 09:13:09
*/
import './index.scss';
import React from 'react';
import SplitPane from 'react-split-pane';
import { Button } from 'antd';
import UserInfo from '../../developer/components/userInfo';
function JupyterTPI (props) {
return (
<div className="jupyter_area">
<div className="jupyter_header">
<UserInfo userInfo={{}} />
<p className="jupyter_title">
<span className="title_desc">MySQL数据库编程开发实训(基础篇)</span>
<span className="title_time">时间</span>
</p>
<p className="jupyter_btn">
{/* sync | poweroff */}
<Button className="btn_common" type="link" icon="sync">重置实训</Button>
<Button className="btn_common" type="link" icon="poweroff">退出实训</Button>
</p>
</div>
<div className="jupyter_ctx">
<SplitPane split="vertical" minSize={350} maxSize={-350} defaultSize="30%">
<div className={'split-pane-left'}>
左侧内容
</div>
<SplitPane split="vertical" defaultSize="100%" allowResize={false}>
<div>右侧内容</div>
<div />
</SplitPane>
</SplitPane>
</div>
</div>
);
}
export default JupyterTPI;

@ -0,0 +1,93 @@
.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;
}
.jupyter_area{
.jupyter_header{
position: relative;
height: 60px;
line-height: 60px;
background-color: #070F1A;
.jupyter_title{
display: flex;
flex-direction: column;
// justify-content: space-around;
align-items: center;
height: 100%;
color: #fff;
.title_desc{
margin-top: 12px;
font-size: 16px;
}
.title_time{
font-size: 12px;
}
// text-align: center;
}
.jupyter_btn{
position: absolute;
right: 10px;
top: 14px;
.btn_common:hover{
// background-color: #29BD8B;
color: #29BD8B;
}
}
}
.jupyter_ctx{
position: relative;
height: calc(100vh - 60px);
}
}

@ -4,34 +4,20 @@ import {TPMIndexHOC} from '../TPMIndexHOC';
import {SnackbarHOC} from 'educoder';
import {Select, Radio, Input, Modal, Button, Form, Tooltip, Upload,Icon} from 'antd';
import locale from 'antd/lib/date-picker/locale/zh_CN';
import {Select, Radio, Input, Modal, Button, Form, Tooltip, Upload, Icon} from 'antd';
import axios from 'axios';
import {getUrl,getUploadActionUrl} from 'educoder';
import moment from 'moment';
import {getUploadActionUrl} from 'educoder';
import './css/Newshixuns.css';
import TPMMDEditor from "../challengesnew/TPMMDEditor";
let path = getUrl("/editormd/lib/");
const $ = window.$;
let timeout;
let currentValue;
import Bottomsubmit from "../../modals/Bottomsubmit";
const Option = Select.Option;
const RadioGroup = Radio.Group;
const confirm = Modal.confirm;
class Newshixuns extends Component {
constructor(props) {
super(props)
@ -39,20 +25,21 @@ class Newshixuns extends Component {
this.state = {
shixunName: undefined,
NAME_COUNT: 60,
Radiovalue: "1",
is_jupyter: "1",
newshixunlist: undefined,
languagewrite:undefined,
systemenvironment:undefined,
testcoderunmode:undefined,
postapplyvisible:undefined
language: undefined,
runtime: undefined,
run_method: undefined,
postapplyvisible: undefined,
}
}
componentDidMount() {
this.props.form.setFieldsValue({
radiogroup: `1`,
is_jupyter: `1`,
});
let newshixunUrl = `/shixuns/new.json`;
axios.get(newshixunUrl).then((response) => {
if (response.status === 200) {
@ -60,10 +47,8 @@ class Newshixuns extends Component {
this.setState({
newshixunlist: response.data
});
this.contentMdRef.current.setValue(!response.data.sample[0][1] ? "" : response.data.sample[0][1]);
}
}
}).catch((error) => {
console.log(error)
@ -82,7 +67,7 @@ class Newshixuns extends Component {
console.log(error)
});
// const mdContnet = this.contentMdRef.current.getValue().trim();
}
shixunNameInput = (e) => {
@ -101,11 +86,42 @@ class Newshixuns extends Component {
});
};
handleSubmit = e => {
e.preventDefault();
handleSubmit = (e) => {
const mdContnet = this.contentMdRef.current.getValue().trim();
this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
let Url = `/shixuns.json`;
axios.post(Url, {
description: mdContnet,
main_type: values.main_type,
is_jupyter: values.is_jupyter === "2" ? true : false,
sub_type: values.sub_type,
shixun: {
name: values.name,
trainee: values.select,
task_pass: 1
}
}
).then((response) => {
if (response.status === 200) {
debugger
window.location.href = "/shixuns/" + response.data.shixun_identifier + "/challenges";
// window.open("/shixuns/"+response.data.shixun_identifier+"/challenges");
} else {
this.setState({
bottonloading: false
})
}
}).catch((error) => {
console.log(error)
this.setState({
bottonloading: false
})
})
}
});
};
@ -115,16 +131,28 @@ class Newshixuns extends Component {
});
}
selectleft = (value) => {
main_type = (value, e) => {
this.props.form.setFieldsValue({
selectleft: value,
main_type: value,
});
this.setState({
mainvalues: e.props.name
})
}
selectright=(value)=>{
sub_type = (value, e) => {
this.props.form.setFieldsValue({
selectright: value,
sub_type: value,
});
let newlist = ""
e.map((item, key) => {
if(item.props.name!=""){
newlist = newlist + `${item.props.name}`
}
})
this.setState({
subvalues: newlist
})
}
post_apply = () => {
@ -134,132 +162,106 @@ class Newshixuns extends Component {
}
sendhideModaly = () => {
this.setState({
postapplyvisible: false,
})
if(this.state.file !== undefined){
console.log("580");
if (this.state.file !== undefined) {
// this.deleteAttachment(this.state.file);
this.setState({
file:undefined,
deleteisnot:true,
languagewrite:"",
systemenvironment:"",
testcoderunmode:"",
fileList:[]
file: undefined,
deleteisnot: true,
language: "",
runtime: "",
run_method: "",
fileList: []
})
}else {
} else {
this.setState({
file:undefined,
deleteisnot:true,
languagewrite:"",
systemenvironment:"",
testcoderunmode:"",
fileList:[]
file: undefined,
deleteisnot: true,
language: "",
runtime: "",
run_method: "",
fileList: []
})
}
}
sendsure_apply = () => {
let {languagewrite,systemenvironment,testcoderunmode} = this.state;
// console.log("点击确定")
// console.log("languagewrite"+languagewrite);
// console.log("systemenvironment"+systemenvironment);
// console.log("testcoderunmode"+testcoderunmode);
// let attachment_ids = undefined
// if (this.state.fileList) {
// attachment_ids = this.state.fileList.map(item => {
// return item.response ? item.response.id : item.id
// })
// }
if(languagewrite === undefined || languagewrite === "" ){
let {language, runtime, run_method} = this.state;
if (!language || language === "") {
// this.props.showNotification(`请填写该镜像是基于什么语言`);
this.setState({
languagewritetype:true
languagewritetype: true
})
return
}
if(systemenvironment === undefined || systemenvironment === ""){
if (!runtime || runtime === "") {
// this.props.showNotification(`请填写该镜像是基于什么语言系统环境`);
this.setState({
systemenvironmenttype:true
systemenvironmenttype: true
})
return;
}
if(testcoderunmode === undefined || testcoderunmode === "") {
if (!run_method || run_method === "") {
// this.props.showNotification(`请填写该镜像中测试代码运行方式`);
this.setState({
testcoderunmodetype:true
testcoderunmodetype: true
})
return;
}
var attachment_ids=undefined;
var attachment_ids = undefined;
if (this.state.fileList) {
attachment_ids = this.state.fileList.map(item => {
return item.response ? item.response.id : item.id
})
}
if( attachment_ids === undefined || attachment_ids.length===0){
// notification.open(
// {
// message: '提示',
// description:
// '请上传附件!',
//
// }
// )
if (attachment_ids === undefined || attachment_ids.length === 0) {
this.setState({
attachmentidstype:true
attachmentidstype: true
})
return;
}
// console.log("attachment_ids"+attachment_ids);
// alert(languagewrite +" "+systemenvironment +" "+testcoderunmode + " "+attachment_ids);
var data={
language:languagewrite,
runtime:systemenvironment,
run_method:testcoderunmode,
attachment_id:attachment_ids[0],
var data = {
language: language,
runtime: runtime,
run_method: run_method,
attachment_id: attachment_ids[0],
}
var url =`/shixuns/apply_shixun_mirror.json`;
axios.post(url,data
var url = `/shixuns/apply_shixun_mirror.json`;
axios.post(url, data
).then((response) => {
try {
if (response.data) {
// const { id } = response.data;
// if (id) {
if(this.state.file !== undefined){
console.log("549");
// this.deleteAttachment(this.state.file);
if (this.state.file !== undefined) {
this.setState({
file:undefined,
deleteisnot:true,
languagewrite:"",
systemenvironment:"",
testcoderunmode:"",
fileList:[]
file: undefined,
deleteisnot: true,
language: "",
runtime: "",
run_method: "",
fileList: []
})
}else {
} else {
this.setState({
file:undefined,
deleteisnot:true,
languagewrite:"",
systemenvironment:"",
testcoderunmode:"",
fileList:[]
file: undefined,
deleteisnot: true,
language: "",
runtime: "",
run_method: "",
fileList: []
})
}
// this.props.showNotification('提交成功!');
notification.open(
{
message: '提示',
@ -269,10 +271,9 @@ class Newshixuns extends Component {
}
)
this.sendhideModaly()
// this.props.history.push(`/courses/${cid}/graduation_topics`);
// }
}
}catch (e) {
} catch (e) {
}
@ -280,19 +281,47 @@ class Newshixuns extends Component {
}
setlanguage = (e) => {
this.setState({
language: e.target.value
})
if (e.target.value) {
this.setState({
languagewritetype: false
})
}
}
setruntime = (e) => {
this.setState({
runtime: e.target.value
})
if (e.target.value) {
this.setState({
systemenvironmenttype: false
})
}
}
setrun_method = (e) => {
this.setState({
run_method: e.target.value
})
if (e.target.value) {
this.setState({
testcoderunmodetype: false
})
}
}
render() {
const {getFieldDecorator} = this.props.form;
const {newshixunlist,languagewrite,systemenvironment,testcoderunmode,fileList,postapplytitle,postapplyvisible} = this.state;
const {newshixunlist, fileList, postapplytitle, postapplyvisible} = this.state;
const uploadProps = {
width: 600,
fileList,
multiple: true,
// https://github.com/ant-design/ant-design/issues/15505
// showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。
// showUploadList: false,
action: `${getUploadActionUrl()}`,
action: `${getUploadActionUrl()}`,
onChange: this.handleChange,
onRemove: this.onAttachmentRemove,
beforeUpload: (file, fileList) => {
@ -300,10 +329,10 @@ class Newshixuns extends Component {
if (this.state.fileList.length >= 1) {
return false
}
// console.log('beforeUpload', file.name);
const isLt150M = file.size / 1024 / 1024 < 50;
if (!isLt150M) {
// this.props.showNotification(`文件大小必须小于50MB`);
notification.open(
{
message: '提示',
@ -313,18 +342,15 @@ class Newshixuns extends Component {
}
)
}
if(this.state.file !== undefined){
console.log("763")
if (this.state.file !== undefined) {
this.setState({
file:file
file: file
})
}else {
} else {
this.setState({
file:file
file: file
})
}
console.log("handleChange2");
return isLt150M;
},
}
@ -333,16 +359,16 @@ class Newshixuns extends Component {
<style>
{
`
.newFooter{
display:none;
}
.ant-form-item{
margin-bottom:5px;
}
`
}
</style>
<div className="educontent mt20 mb60 clearfix">
<div className="new_shixun">
<div className="mb10 edu-back-white">
<div className="mb10 edu-back-white pd40px">
<div className="padding10-20 bor-bottom-greyE color-grey-3 clearfix">
<span className="fl font-18 lineh-35">新建实训项目</span>
@ -351,9 +377,9 @@ class Newshixuns extends Component {
target="_blank">实训制作指南</a> : ""}
</div>
<div className="padding10-20 color-grey-3 clearfix">
<Form onSubmit={this.handleSubmit}>
<Form>
<Form.Item label="实训类型">
{getFieldDecorator('radiogroup')(
{getFieldDecorator('is_jupyter')(
<Radio.Group onChange={this.RadiovalueonChange}>
<Radio value="1">普通实训</Radio>
<Radio value="2">Jupyter实训</Radio>
@ -369,8 +395,8 @@ class Newshixuns extends Component {
required: true, message: '请输入选题名称',
}, {
max: 60, message: '请输入名称最大限制60个字符',
},{
whitespace:true,message:'请勿输入空格'
}, {
whitespace: true, message: '请勿输入空格'
}],
})(
<Input placeholder="请输入名称最大限制60个字符"
@ -408,8 +434,8 @@ class Newshixuns extends Component {
>
<Option value={1}>初级</Option>
<Option value={2}>中级</Option>
<Option value={3}>高级</Option>
<Option value={4}></Option>
<Option value={3}>高级</Option>
<Option value={4}></Option>
</Select>
</div>
@ -418,28 +444,27 @@ class Newshixuns extends Component {
</Form.Item>
<Form.Item
<Form.Item
label={"实验环境"}
style={{"borderBottom": 'none','width': '18%','float': 'left'}}
style={{"borderBottom": 'none', 'width': '18%', 'float': 'left'}}
className="chooseDes pr"
>
<div>
{getFieldDecorator('selectleft', {
{getFieldDecorator('main_type', {
rules: [{required: true, message: '请选择主类别'}],
})(
<div className="width100 fl mr20">
<Select placeholder="请选择主类别"
style={{width: 180}}
onChange={this.selectleft}
onChange={this.main_type}
defaultOpen={false}
>
{
newshixunlist === undefined ? "" : newshixunlist.main_type.map((item, key) => {
return (
<Option value={item.id} key={key}>
<Option value={item.id} key={key} name={item.description}>
<Tooltip placement="right"
title={item.description === "" ? "无描述" : item.description}>
{item.type_name}
@ -449,33 +474,39 @@ class Newshixuns extends Component {
})
}
</Select>
<span className="fl color-grey lineh-20">
没有实验环境
<a className="color-blue" onClick={this.post_apply}> 申请新建</a>
</span>
</div>
)}
</div>
</Form.Item>
</Form.Item>
<style>
{
`
.Selectlittle .ant-select-selection__rendered{
line-height:25px;
}
`
}
</style>
<Form.Item
style={{"borderBottom": 'none','width': '61%','float': 'left','margin-top': '40px'}}
style={{"borderBottom": 'none', 'width': '82%', 'float': 'left', 'marginTop': '40px'}}
className="chooseDes pr"
>
<div className=" fl pr mr20">
{getFieldDecorator('selectright', {
rules: [{required: true, message: '请选择小类别'}],
})(
>
<div className=" fl pr mr20">
{getFieldDecorator('sub_type')(
<div className=" fl pr mr20">
<Select placeholder="请选择小类别"
mode="multiple"
style={{width: 180}}
onChange={this.selectright}
onChange={this.sub_type}
defaultOpen={false}
className={"Selectlittle"}
>
{
newshixunlist === undefined ? "" : newshixunlist.small_type.map((item, key) => {
return (
<Option value={item.id} key={key}>
<Option value={item.id} key={key} name={item.description}>
<Tooltip placement="right"
title={item.description === "" ? "无描述" : item.description}>
{item.type_name}
@ -486,34 +517,36 @@ class Newshixuns extends Component {
}
</Select>
</div>
)}
<span className="fl ml20 color-grey lineh-20">
<div>
<div className={"font-12"}>
已安装软件hadoop3.1.0jdk1.8
</div>
<div className={"font-12"}>
说明添加了hadoop3.1.0jdk1.8的源码包添加了hadoop3.1.0jdk1.8的源码包
</div>
{this.state.mainvalues === undefined && this.state.subvalues === undefined ? "" :
<div className={"font-12"} style={{'max-width':'700px'}}>
{`已安装软件:${this.state.mainvalues===undefined?"":this.state.mainvalues}${this.state.subvalues===undefined?"":this.state.subvalues}`}
{`说明:添加了${this.state.mainvalues===undefined?"":this.state.mainvalues}${this.state.subvalues===undefined?"":this.state.subvalues}`}
</div>}
</div>
</span>
</div>
</Form.Item>
</Form>
{postapplyvisible===true?<style>
<div className={"both"}></div>
<div className=" color-grey lineh-20">
没有实验环境
<a className="color-blue" onClick={this.post_apply}> 申请新建</a>
</div>
{postapplyvisible === true ? <style>
{
`
`
body{
overflow: hidden !important;
}
`
}
</style>:""}
}
</style> : ""}
<Modal
keyboard={false}
@ -525,49 +558,54 @@ class Newshixuns extends Component {
heigth={720}
>
<div>
<li className="clearfix ml82" >
<li className="clearfix ml82">
<label className="fl mt10 "><span
className="color-red fl mt3">*</span>&nbsp;&nbsp;</label>
<textarea className={this.state.languagewritetype===true?"fl task-form-80 task-height-150 bor-reds":"fl task-form-80 task-height-150"}
style={{width:'89%',height:'100px'}}
onInput={this.setlanguagewrite}
value={languagewrite}
placeholder="请填写该镜像是基于什么语言示例Python"
id="demand_info"></textarea>
<textarea
className={this.state.languagewritetype === true ? "fl task-form-80 task-height-150 bor-reds" : "fl task-form-80 task-height-150"}
style={{width: '89%', height: '100px'}}
onInput={this.setlanguage}
value={this.state.language}
placeholder="请填写该镜像是基于什么语言示例Python"
id="demand_info"></textarea>
</li>
<div className={"color-red shixunspanred"}>{this.state.languagewritetype===true?"请填写该镜像语言":""}</div>
<div
className={"color-red shixunspanred"}>{this.state.languagewritetype === true ? "请填写该镜像语言" : ""}</div>
<li className="clearfix ml1">
<label className="panel-form-label fl ml50"><span
className="color-red fl mt3">*</span>&nbsp;&nbsp;</label>
<textarea className={this.state.systemenvironmenttype===true?"fl task-form-80 task-height-150 bor-reds":"fl task-form-80 task-height-150"}
onInput={this.setsystemenvironment}
style={{height:'100px'}}
value={systemenvironment}
placeholder="请填写该镜像是基于什么linux系统环境,代码运行环境"
id="demand_info"></textarea>
<textarea
className={this.state.systemenvironmenttype === true ? "fl task-form-80 task-height-150 bor-reds" : "fl task-form-80 task-height-150"}
onInput={this.setruntime}
style={{height: '100px'}}
value={this.state.runtime}
placeholder="请填写该镜像是基于什么linux系统环境,代码运行环境"
id="demand_info"></textarea>
</li>
<div className={"color-red shixunspanred"}>{this.state.systemenvironmenttype===true?"请填写该镜像语言系统环境":""}</div>
<div
className={"color-red shixunspanred"}>{this.state.systemenvironmenttype === true ? "请填写该镜像语言系统环境" : ""}</div>
<li className="clearfix">
<label className="fl mt10" ><span
<label className="fl mt10"><span
className="color-red fl mt3">*</span>&nbsp;&nbsp;</label>
<textarea
className={this.state.testcoderunmodetype===true?"fl task-form-80 task-height-150 bor-reds":"fl task-form-80 task-height-150"}
onInput={this.settestcoderunmode}
value={testcoderunmode}
style={{height:'100px'}}
placeholder="请填写该镜像中测试代码运行方式"
id="demand_info"></textarea>
className={this.state.testcoderunmodetype === true ? "fl task-form-80 task-height-150 bor-reds" : "fl task-form-80 task-height-150"}
onInput={this.setrun_method}
value={this.state.run_method}
style={{height: '100px'}}
placeholder="请填写该镜像中测试代码运行方式"
id="demand_info"></textarea>
</li>
<div className={"color-red shixunspanred"}>{this.state.testcoderunmodetype===true?"请填写该镜像测试代码运行方式":""}</div>
<div
className={"color-red shixunspanred"}>{this.state.testcoderunmodetype === true ? "请填写该镜像测试代码运行方式" : ""}</div>
<li className="clearfix ml50">
<label className="panel-form-label fl mt-5"><span
className="color-red fl">*</span>&nbsp;&nbsp;</label>
<div className="mt10" style={{
<div className="mt10" style={{
display: "inline-block"
}}>
<Upload {...uploadProps}>
<Icon type="upload" className="fl mt3" > </Icon>
<Icon type="upload" className="fl mt3"> </Icon>
<span className="color-blue fl cdefault">上传附件</span>
<span className="color-grey-c fl ml10 ">(单个文件50M以内)</span>
@ -576,13 +614,13 @@ class Newshixuns extends Component {
</li>
<div className={"color-red shixunspanred"}>
{this.state.attachmentidstype===true?"请上传附件":""}
{this.state.attachmentidstype === true ? "请上传附件" : ""}
</div>
<li className="edu-txt-center clearfix ">
<a className="pop_close task-btn mr30"
onClick={() => this.sendhideModaly()}
>取消</a>
<Button type="primary" onClick={()=>this.sendsure_apply()}
<Button type="primary" onClick={() => this.sendsure_apply()}
className="task-btn task-btn-orange">确定</Button>
</li>
<div className="cl"></div>
@ -591,7 +629,6 @@ class Newshixuns extends Component {
</Modal>
<Modal
keyboard={false}
title="提示"
@ -617,14 +654,7 @@ class Newshixuns extends Component {
</div>
</div>
</div>
<div className="clearfix bor-bottom-greyE edu-back-white orderingbox newshixunbottombtn">
<div className=" edu-txt-center padding13-30">
<button type="button" className="ant-btn mr20 newshixunmode `backgroundFFF`" ><span> </span></button>
<button type="button" className="ant-btn newshixunmode mr40 ant-btn-primary" type="primary" htmlType="submit" onClick={this.handleSubmit}><span> </span></button>
</div>
</div>
<Bottomsubmit url={"/shixuns"} onSubmits={() => this.handleSubmit()}/>
</div>
);

@ -822,4 +822,12 @@ a.white-btn.use_scope-btn:hover{
.backgroundFFF{
background: #FFF !important;
}
.relative{
position: relative;
}
.pd40px{
padding-bottom: 40px;
}

@ -48,6 +48,8 @@ const types = {
GET_COMMIT_RECORD_DETAIL_BY_ID: 'GET_COMMIT_RECORD_DETAIL_BY_ID', // 根据id号获取提交记录详情
RESTORE_INITIAL_CODE: 'RESTORE_INITIAL_CODE', // 恢复初始代码
SAVE_USER_INFO: 'SAVE_USER_INFO', // 只在用户信息
SAVE_HACK_IDENTIFIER: 'SAVE_HACK_IDENTIFIER', // 用户界面跑到编辑界面需要用的id值
SAVE_EDITOR_CODE: 'SAVE_EDITOR_CODE', // 保存详情页面中编辑时的代码
}
export default types;

@ -7,7 +7,11 @@
*/
import toggleTodo from './testAction.js';
import {getOJList, changePaginationInfo} from './ojList';
import {
getOJList,
changePaginationInfo,
deleteItem
} from './ojList';
import {
validateOjForm,
saveOjFormCode,
@ -41,6 +45,8 @@ import {
getUserProgramDetail,
saveUserProgramIdentifier,
restoreInitialCode,
saveUserCodeForInterval,
saveEditorCodeForDetail,
// isUpdateCodeCtx
} from './ojForUser';
@ -52,9 +58,14 @@ import {
isMyPublish,
} from './common';
import {
getUserInfoForNew
} from './user';
export default {
toggleTodo,
getOJList,
deleteItem,
changePaginationInfo,
getOJFormById,
saveOJFormId,
@ -90,5 +101,8 @@ export default {
updateOpenTestCaseIndex,
saveUserProgramIdentifier,
restoreInitialCode,
getUserInfoForNew,
saveUserCodeForInterval,
saveEditorCodeForDetail
// isUpdateCodeCtx
}

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-27 13:42:11
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-06 17:56:02
* @LastEditTime: 2019-12-10 19:05:36
*/
import types from "./actionTypes";
import { Base64 } from 'js-base64';
@ -31,6 +31,11 @@ export const startProgramQuestion = (id, props) => {
type: types.SAVE_USER_PROGRAM_ID,
payload: identifier
});
// 保存id值
dispatch({
type: types.SAVE_HACK_IDENTIFIER,
payload: id
});
// 跳转至开启编程
if (identifier) {
// let data = Object.assign({}, props);
@ -74,8 +79,13 @@ export const getUserProgramDetail = (identifier, type) => {
dispatch({
type: types.GET_COMMIT_RECORD_DETAIL_BY_ID,
payload: data
})
});
}
// 保存用户登录信息
dispatch({
type: types.SAVE_USER_INFO,
payload: data.user
});
}
});
}
@ -277,10 +287,12 @@ export const getUserCommitRecordDetail = (identifier) => {
return (dispatch) => {
fetchUserCommitRecordDetail(identifier).then(res => {
console.log('提交记录详情======》》》》', res);
const { data } = res;
if (data.status === 401) return;
dispatch({
type: types.GET_COMMIT_RECORD_DETAIL_BY_ID,
payload: res.data
})
payload: data
});
});
}
}
@ -327,6 +339,11 @@ export const submitUserCode = (identifier, inputValue, type) => {
});
return;
};
// 将编辑代码清空
dispatch({
type: types.SAVE_EDITOR_CODE,
payload: ''
});
dispatch(debuggerCode(identifier, inputValue, type || 'submit'));
}
}).catch(() => {
@ -383,3 +400,10 @@ export const restoreInitialCode = (identifier) => {
}
}
// 保存详情页面中的编辑代码
export const saveEditorCodeForDetail = (code) => {
return {
type: types.SAVE_EDITOR_CODE,
payload: code
}
}

@ -4,13 +4,13 @@
* @Github:
* @Date: 2019-11-20 16:35:46
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-06 20:24:11
* @LastEditTime: 2019-12-10 19:54:56
*/
import types from './actionTypes';
import CONST from '../../constants';
import { fetchPostOjForm, fetchGetOjById, publishTask } from '../../services/ojService';
import { Base64 } from 'js-base64';
import { message, notification } from 'antd';
import { message, notification, Modal } from 'antd';
import { toStore } from 'educoder';
const { jcLabel } = CONST;
// 表单字段映射
@ -85,6 +85,7 @@ 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;
@ -104,14 +105,35 @@ export const validateOjForm = (props, type) => {
});
// 验证测试用例中的数组是否都有对应的值
const tcValidResult = [];
testCases.forEach(obj => {
// 验证测试用例: 1.必须要有输出值 2. 输入值与输出值必须唯一
testCases.forEach((obj, i) => {
let tempObj = {};
['input', 'output'].forEach(key => {
const value = obj[key];
const validateResult = emptyValidate(key, value);
// 非空校验
let validateResult = emptyValidate(key, value);
const errMsg = validateResult[key].errMsg;
if (errMsg) {
hasSuccess = false;
} else {
// 唯一性校验
const bool = testCases.some((item, j) => {
if (i !== j) {
return (item[key] === value);
} else {
return false;
}
});
if (bool) {
hasSuccess = false;
validateResult = {
[key]: {
validateStatus: 'error',
errMsg: key === 'input' ? '输入值必须是唯一的' : '输出值必须是唯一的'
}
};
}
}
Object.assign(tempObj, validateResult);
});
@ -151,6 +173,8 @@ export const validateOjForm = (props, type) => {
payload: false
});
}
/** 表单验证结束 */
/** 表单验证通过后,调用保存 or 更新 or 发布 */
if (hasSuccess) {
// console.log('表单保存的数据为: ', getState());
const {ojFormReducer} = getState();
@ -205,6 +229,7 @@ export const validateOjForm = (props, type) => {
paramsObj['identifier'] = identifier;
}
// 接口调用成功后,跳转至列表页
function linkToDev () {
toStore('oj_description', '');
dispatch({
@ -216,51 +241,61 @@ export const validateOjForm = (props, type) => {
}, 1000);
}
fetchPostOjForm(paramsObj).then(res => {
// TODO
if (res.status === 200) { // 保存成功后,重新跳转至列表页
const {identifier} = res.data
if (type === 'publish') { // 存在发布时,直接调用发布接口
if (identifier) {
publishTask(identifier).then(res => {
if (res.data.status === 0) {
message.success('发布成功!');
linkToDev();
}
dispatch({
type: types.PUBLISH_LOADING_STATUS,
payload: false
});
}).catch(() => {
dispatch({
type: types.PUBLISH_LOADING_STATUS,
payload: false
});
// 调用保存或更新
if (type === 'publish') {
// 提示发布信息
Modal.confirm({
title: '提示',
content: `
发布后即可应用到自己管理的课堂,
是否确定发布?`,
okText: '确定',
cancelText: '取消',
onOk () {
publishTask(identifier).then(res => {
if (res.data.status === 0) {
message.success('发布成功!');
linkToDev();
}
dispatch({
type: types.PUBLISH_LOADING_STATUS,
payload: false
});
} else {
}).catch(() => {
dispatch({
type: types.PUBLISH_LOADING_STATUS,
payload: false
});
}
} else {
});
},
onCancel () {
dispatch({
type: types.PUBLISH_LOADING_STATUS,
payload: false
});
}
});
} else {
// 调用更新
fetchPostOjForm(paramsObj).then(res => {
if (res.status === 200) { // 保存成功后,重新跳转至列表页
if (res.data.identifier) {
message.success('保存成功!');
linkToDev();
}
dispatch({
type: types.SUBMIT_LOADING_STATUS,
payload: false
});
}
}).catch(err => {
dispatch({
type: types.SUBMIT_LOADING_STATUS,
payload: false
});
}
}
}).catch(err => {
dispatch({
type: types.SUBMIT_LOADING_STATUS,
payload: false
});
});
);
}
}
}
};
@ -361,6 +396,11 @@ export const getOJFormById = (id) => {
type: types.SAVE_EDIT_OJ_FORM_AND_TEST_CASE,
payload: res.data
});
// 保存用户信息
dispatch({
type: types.SAVE_USER_INFO,
payload: res.data.user
});
});
}
}
@ -371,7 +411,7 @@ export const saveOJFormId = (id) => {
payload: id
}
}
// 清空测试用例集合
// 清空
export const clearOJFormStore = () => {
return {
type: types.CLEAR_JSFORM_STORE
@ -380,27 +420,76 @@ export const clearOJFormStore = () => {
// 测试用例输入值改变时
export const testCaseInputChange = (value, index) => {
const validate = emptyValidate('input', value)['input'];
return {
type: types.TEST_CASE_INPUT_CHANGE,
payload: {
input: validate,
value,
index
return (dispatch, getState) => {
// 非空校验
let validate = emptyValidate('input', value)['input'];
if (!validate.errMsg) {
// 唯一性校验
const {testCases} = getState().ojFormReducer;
const bool = testCases.some((item, i) => {
if (i !== index) {
return item['input'] === value;
} else {
return false;
}
});
if (bool) {
validate = {
validateStatus: 'error',
errMsg: '输入值必须唯一'
};
}
}
dispatch({
type: types.TEST_CASE_INPUT_CHANGE,
payload: {
input: validate,
value,
index
}
});
}
}
// 测试用例输出值改变时
export const testCaseOutputChange = (value, index) => {
const validate = emptyValidate('output', value)['output'];
return {
type: types.TEST_CASE_OUTPUT_CHANGE,
payload: {
output: validate,
value,
index
// const validate = emptyValidate('output', value)['output'];
// return {
// type: types.TEST_CASE_OUTPUT_CHANGE,
// payload: {
// output: validate,
// value,
// index
// }
// }
return (dispatch, getState) => {
// 非空校验
let validate = emptyValidate('output', value)['output'];
if (!validate.errMsg) {
// 唯一性校验
const {testCases} = getState().ojFormReducer;
const bool = testCases.some((item, i) => {
if (i !== index) {
return item['output'] === value;
} else {
return false;
}
});
if (bool) {
validate = {
validateStatus: 'error',
errMsg: '输入值必须唯一'
};
}
}
dispatch({
type: types.TEST_CASE_OUTPUT_CHANGE,
payload: {
output: validate,
value,
index
}
});
}
}

@ -4,10 +4,10 @@
* @Github:
* @Date: 2019-11-20 10:48:24
* @LastEditors: tangjiang
* @LastEditTime: 2019-11-29 11:09:54
* @LastEditTime: 2019-12-10 20:40:55
*/
import types from './actionTypes';
import { fetchOJList } from '../../services/ojService';
import { fetchOJList, fetchDeleteOJItem } from '../../services/ojService';
export const getOJList = (params) => {
return (dispatch) => {
@ -35,3 +35,15 @@ export const changePaginationInfo = (obj) => {
payload: obj
}
}
// 删除
export const deleteItem = (identifier) => {
return (dispatch, getState) => {
const {pagination} = getState().ojListReducer;
fetchDeleteOJItem(identifier).then(res => {
if (res.status === 200) {
dispatch(getOJList(pagination));
}
});
}
}

@ -4,13 +4,24 @@
* @Github:
* @Date: 2019-12-06 15:09:22
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-06 15:15:00
* @LastEditTime: 2019-12-09 20:34:50
*/
import types from './actionTypes';
import { fetchUserInfoForNew } from '../../services/ojService';
// 获取用户信息
export default function getUserInfo () {
export const getUserInfoForNew = () => {
return (dispatch) => {
// 调用获取用户信息, 如果没有登录直接调用登录,成功后保存当前用户信息
fetchUserInfoForNew().then(res => {
// console.log('获取用户信息成功: ', res);
const { data } = res;
if (data.status === 401) return;
dispatch({
type: types.SAVE_USER_INFO,
payload: data.user
});
})
}
}

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-27 13:41:48
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-05 13:37:17
* @LastEditTime: 2019-12-10 10:09:59
*/
import types from "../actions/actionTypes";
import { Base64 } from 'js-base64';
@ -20,6 +20,8 @@ const initialState = {
userCodeTab: 'task', // 学员测评tab位置: task | record | comment
userTestInput: '', // 用户自定义输入值
recordDetail: {}, // 根据id号获取的记录详情
hack_identifier: '', // 用户界面编辑时
editor_code: '' // 保存编辑代码
};
const ojForUserReducer = (state = initialState, action) => {
@ -109,6 +111,16 @@ const ojForUserReducer = (state = initialState, action) => {
...state,
hack: Object.assign({}, curHack)
}
case types.SAVE_HACK_IDENTIFIER:
return {
...state,
hack_identifier: action.payload
}
case types.SAVE_EDITOR_CODE:
return {
...state,
editor_code: action.payload
}
default:
return state;
}

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-20 16:40:32
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-05 09:35:29
* @LastEditTime: 2019-12-09 16:30:46
*/
import { Base64 } from 'js-base64';
import types from '../actions/actionTypes';
@ -16,8 +16,8 @@ const init = {
description: '',
difficult: 1,
category: 1,
openOrNot: 1,
timeLimit: ''
// openOrNot: 1,
timeLimit: 3
},
ojFormValidate: {
name: {
@ -40,10 +40,10 @@ const init = {
validateStatus: '',
errMsg: ''
},
openOrNot: {
validateStatus: '',
errMsg: ''
},
// openOrNot: {
// validateStatus: '',
// errMsg: ''
// },
timeLimit: {
validateStatus: '',
errMsg: ''
@ -64,7 +64,8 @@ const init = {
identifier: '', // OJ表单id
loading: false, // 僵尸loading标志
testCodeStatus: 'default', // 调试代码状态 default(默认值) | loading(加载中) | loaded(加载完成) | userCase(用户自定义测试用例) | finish(测试完成)
openTestCodeIndex: [0] // 展开的测试用例: 数组, 当出错时,展开所有出错的测试用例, 默认展开第一个
openTestCodeIndex: [0], // 展开的测试用例: 数组, 当出错时,展开所有出错的测试用例, 默认展开第一个
isPublish: 0, // 是否是发布状态: 0 未发布 1 已发布
}
const tcValidateObj = {
@ -167,7 +168,7 @@ const ojFormReducer = (state = initialState, action) => {
* 6. 更改测试用例状态
* 7. 添加测试用例验证
*/
const { code = '', description, language, name, hack_sets = [], time_limit, difficult, category } = action.payload;
const { code = '', description, language, name, hack_sets = [], time_limit, difficult, category, status } = action.payload;
const currentOjForm = {
name, // 任务名称
language,
@ -203,7 +204,8 @@ const ojFormReducer = (state = initialState, action) => {
code: cbcode,
testCases: curTestCases,
testCasesValidate: curTcValidates,
testCodeStatus: hack_sets.length > 0 ? 'userCase' : 'default'
testCodeStatus: hack_sets.length > 0 ? 'userCase' : 'default',
isPublish: status
}
case types.CLEAR_JSFORM_STORE:
state = Object.assign({}, init);

@ -4,7 +4,7 @@
* @Github:
* @Date: 2019-11-20 10:55:38
* @LastEditors: tangjiang
* @LastEditTime: 2019-12-04 18:05:10
* @LastEditTime: 2019-12-10 20:46:16
*/
import axios from 'axios';
@ -20,6 +20,12 @@ export async function fetchOJList (params) {
return axios.get('/problems.json', { params: obj });
}
// 删除OJ列表
export async function fetchDeleteOJItem (identifier) {
const url = `/problems/${identifier}.json`;
return axios.delete(url);
}
// 提交
export async function fetchPostOjForm (paramsObj) {
const { params, submitType, identifier } = paramsObj;
@ -106,3 +112,9 @@ export async function fetchRestoreInitialCode (identifier) {
const url = `/myproblems/${identifier}/restore_initial_code.json`;
return axios.post(url);
}
// 新建时调用获取用户信息接口
export async function fetchUserInfoForNew () {
const url = `/problems/new.json`;
return axios.get(url);
}

Loading…
Cancel
Save