chromesetting
cxt 5 years ago
commit 8f8ca3475b

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

@ -173,64 +173,70 @@ class ChallengesController < ApplicationController
# tab 0:过关任务的更新; 1:评测设置的更新; 2:表示参考答案的更新;
def update
ActiveRecord::Base.transaction do
tab = params[:tab].to_i
@challenge.update_attributes(challenge_params)
if tab == 0 && @challenge.st == 0
@challenge.challenge_tags.delete_all
if params[:challenge_tag].present?
params[:challenge_tag].each do |input|
ChallengeTag.create!(:name => input, :challenge_id => @challenge.id)
begin
ActiveRecord::Base.transaction do
tab = params[:tab].to_i
@challenge.update_attributes(challenge_params)
if tab == 0 && @challenge.st == 0
@challenge.challenge_tags.delete_all
if params[:challenge_tag].present?
params[:challenge_tag].each do |input|
ChallengeTag.create!(:name => input, :challenge_id => @challenge.id)
end
end
end
elsif tab == 1
path = @challenge.path
exec_path = @challenge.exec_path
test_set = @challenge.test_sets
sets_output = test_set.map(&:output)
sets_input = test_set.map(&:input)
sets_open = test_set.map(&:is_public)
set_score = test_set.map(&:score)
set_match_rule = test_set.map(&:match_rule)
params_hidden = params[:test_set].map{|set| set[:hidden].to_i == 0}
params_output = params[:test_set].map{|set| set[:output] }
params_input = params[:test_set].map{|set| set[:input] }
params_score = params[:test_set].map{|set| set[:score]}
params_test_set = params[:test_set].map{|set| set[:match_rule]}
# 测试集变化则需要更新(输入、 输出、 是否隐藏)
if sets_output != params_output || sets_open != params_hidden || sets_input != params_input ||
set_score != params_score || params_test_set != set_match_rule
test_set.delete_all unless test_set.blank?
params[:test_set].each_with_index do |set, index|
# last 末尾匹配, full: 全完匹配
logger.info("set: #{set}; match_rule : #{set[:match_rule]}")
match_rule = set[:match_rule] == 'last' ? 'last' : 'full'
TestSet.create(:challenge_id => @challenge.id,
:input => "#{set[:input]}",
:output => "#{set[:output]}",
:is_public => params_hidden[index],
:score => set[:score],
:match_rule => "#{match_rule}",
:position => (index + 1))
elsif tab == 1
path = @challenge.path
exec_path = @challenge.exec_path
test_set = @challenge.test_sets
sets_output = test_set.map(&:output)
sets_input = test_set.map(&:input)
sets_open = test_set.map(&:is_public)
set_score = test_set.map(&:score)
set_match_rule = test_set.map(&:match_rule)
params_hidden = params[:test_set].map{|set| set[:hidden].to_i == 0}
params_output = params[:test_set].map{|set| set[:output] }
params_input = params[:test_set].map{|set| set[:input] }
params_score = params[:test_set].map{|set| set[:score]}
params_test_set = params[:test_set].map{|set| set[:match_rule]}
# 测试集变化则需要更新(输入、 输出、 是否隐藏)
if sets_output != params_output || sets_open != params_hidden || sets_input != params_input ||
set_score != params_score || params_test_set != set_match_rule
test_set.delete_all unless test_set.blank?
params[:test_set].each_with_index do |set, index|
# last 末尾匹配, full: 全完匹配
logger.info("set: #{set}; match_rule : #{set[:match_rule]}")
match_rule = set[:match_rule] == 'last' ? 'last' : 'full'
TestSet.create(:challenge_id => @challenge.id,
:input => "#{set[:input]}",
:output => "#{set[:output]}",
:is_public => params_hidden[index],
:score => set[:score],
:match_rule => "#{match_rule}",
:position => (index + 1))
end
@challenge.update_column(:modify_time, Time.now)
# 测试集的
@shixun.myshixuns.update_all(:system_tip => 0)
end
@challenge.update_column(:modify_time, Time.now)
# 测试集的
@shixun.myshixuns.update_all(:system_tip => 0)
end
if params[:challenge][:show_type].to_i == -1
@challenge.update_attributes(picture_path: nil, web_route: nil, expect_picture_path: nil, original_picture_path: nil)
if params[:challenge][:show_type].to_i == -1
@challenge.update_attributes(picture_path: nil, web_route: nil, expect_picture_path: nil, original_picture_path: nil)
end
# 关卡评测执行文件如果被修改,需要修改脚本内容
script = modify_shixun_script @shixun, @shixun.evaluate_script
@shixun.shixun_info.update_column(:evaluate_script, script)
# TODO:
# if path != params[:challenge][:path]
# shixun_modify_status_without_publish(@shixun, 1)
# end
#Attachment.attach_files(@challenge, params[:attachments])
end
# 关卡评测执行文件如果被修改,需要修改脚本内容
script = modify_shixun_script @shixun, @shixun.evaluate_script
@shixun.shixun_info.update_column(:evaluate_script, script)
# TODO:
# if path != params[:challenge][:path]
# shixun_modify_status_without_publish(@shixun, 1)
# end
#Attachment.attach_files(@challenge, params[:attachments])
end
end
rescue => e
logger_error("##update_challenges: ##{e.message}")
tip_exception("更新失败!")
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

@ -17,10 +17,17 @@ class SyncTrustieJob < ApplicationJob
"number": count
}
uri = URI.parse(url)
# http = Net::HTTP.new(uri.hostname, uri.port)
if api_host
http = Net::HTTP.new(uri.hostname, uri.port)
http.send_request('PUT', uri.path, sync_json.to_json, {'Content-Type' => 'application/json'})
Rails.logger.info("#######_________response__sync__end_____#########")
if api_host.include?("https://")
http.use_ssl = true
end
response = http.send_request('PUT', uri.path, sync_json.to_json, {'Content-Type' => 'application/json'})
Rails.logger.info("#######_________response__sync__end_____#########{response.body}")
end
end
end

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

@ -0,0 +1,7 @@
class ModifyTypeForTestSets < ActiveRecord::Migration[5.2]
def change
change_column :test_sets, :input, :longtext
change_column :test_sets, :output, :longtext
end
end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 74 KiB

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

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