chromesetting
cxt 5 years ago
commit 8f8ca3475b

@ -15,7 +15,8 @@ class Admins::ShixunSettingsController < Admins::BaseController
hidden: params[:hidden].present? ? params[:hidden] : false, hidden: params[:hidden].present? ? params[:hidden] : false,
homepage_show: params[:homepage_show].present? ? params[:homepage_show] : false, homepage_show: params[:homepage_show].present? ? params[:homepage_show] : false,
task_pass: params[:task_pass].present? ? params[:task_pass] : 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) @shixuns_type_check = MirrorRepository.pluck(:type_name,:id)
@ -126,6 +127,6 @@ class Admins::ShixunSettingsController < Admins::BaseController
end end
def setting_params 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
end end

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

@ -1,8 +1,8 @@
class HacksController < ApplicationController class HacksController < ApplicationController
before_action :require_login, except: [:index] before_action :require_login, except: [:index]
before_action :find_hack, only: [:edit, :update, :publish, :start, :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, :update_set, :edit, :update] before_action :require_teacher_identity, only: [:create]
before_action :require_auth_identity, only: [:update, :edit, :publish, :update_set, :delete_set] 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 new;end
def destroy
@hack.destroy
render_ok
end
private private
# 实名认证老师,管理员与运营人员权限 # 实名认证老师,管理员与运营人员权限
def require_teacher_identity def require_teacher_identity

@ -216,7 +216,8 @@ class MyshixunsController < ApplicationController
begin begin
shixun_tomcat = edu_setting('tomcat_webssh') shixun_tomcat = edu_setting('tomcat_webssh')
uri = "#{shixun_tomcat}/bridge/webssh/getConnectInfo" 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))} containers:(Base64.urlsafe_encode64(shixun_container_limit @myshixun.shixun))}
res = uri_post uri, params res = uri_post uri, params
if res && res['code'].to_i != 0 if res && res['code'].to_i != 0

@ -17,10 +17,17 @@ class SyncTrustieJob < ApplicationJob
"number": count "number": count
} }
uri = URI.parse(url) uri = URI.parse(url)
# http = Net::HTTP.new(uri.hostname, uri.port)
if api_host if api_host
http = Net::HTTP.new(uri.hostname, uri.port) 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 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(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(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(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]) custom_sort(all_shixuns, params[:sort_by], params[:sort_direction])
end end

@ -65,6 +65,12 @@
<span class="only_view">只看已隐藏文件目录</span> <span class="only_view">只看已隐藏文件目录</span>
</label> </label>
</div> </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>
</div> </div>
<% end %> <% end %>

@ -15,7 +15,7 @@
<th> <th>
操作 操作
<div class="setting-chosen"> <div class="setting-chosen">
ssh/隐藏/首页/跳关/隐藏目录 ssh/隐藏/首页/跳关/隐藏目录/vip
</div> </div>
</th> </th>
</thead> </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 :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 :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 :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> </td>
<script> <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 './index.scss';
import React from 'react'; 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 { connect } from 'react-redux';
import actions from '../../redux/actions'; import actions from '../../redux/actions';
import MultipTags from './components/multiptags'; import MultipTags from './components/multiptags';
import { Link } from 'react-router-dom'; // import { Link } from 'react-router-dom';
import CONST from '../../constants'; import CONST from '../../constants';
import { withRouter } from 'react-router'; import { withRouter } from 'react-router';
import { toStore } from 'educoder';
// import MyIcon from '../../common/components/MyIcon';
const {tagBackground, diffText} = CONST; const {tagBackground, diffText} = CONST;
const { Search } = Input; const { Search } = Input;
@ -104,14 +106,30 @@ class DeveloperHome extends React.PureComponent {
options = { options = {
title: '操作', title: '操作',
key: 'action', key: 'action',
fixed: 'right', // fixed: 'right',
width: 100, width: 100,
render: (text, record) => ( render: (text, record) => (
<span> <React.Fragment>
<Button type="primary" onClick={() => this.handleClickEditor(record.identifier)}>编辑 <Button
shape="circle"
type="primary"
icon="edit"
size="small"
onClick={() => this.handleClickEditor(record.identifier)}
>
{/* <Link to={`/problems/${record.identifier}/edit`}></Link> */} {/* <Link to={`/problems/${record.identifier}/edit`}></Link> */}
</Button> </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`) 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条件变化时 // table条件变化时
handleTableChange = (pagination, filters, sorter) => { handleTableChange = (pagination, filters, sorter) => {
const {field, order} = sorter; const {field, order} = sorter;
@ -374,7 +408,7 @@ class DeveloperHome extends React.PureComponent {
// console.log('name has click', record); // console.log('name has click', record);
// 先调用start接口获取返回的 identifier, 再跳转到开启编辑 // 先调用start接口获取返回的 identifier, 再跳转到开启编辑
if (this.isLogin()) { if (this.isLogin()) {
// console.log(record); toStore('hack_identifier', record.identifier); // 保存当前编辑的id号
this.props.startProgramQuestion(record.identifier, this.props); this.props.startProgramQuestion(record.identifier, this.props);
} }
} }
@ -499,7 +533,8 @@ const mapDispatchToProps = (dispatch) => ({
handleClick: () => dispatch(actions.toggleTodo()), handleClick: () => dispatch(actions.toggleTodo()),
fetchOJList: (params) => dispatch(actions.getOJList(params)), fetchOJList: (params) => dispatch(actions.getOJList(params)),
changePaginationInfo: (obj) => dispatch(actions.changePaginationInfo(obj)), 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( export default withRouter(connect(

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

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

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

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

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

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

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

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

@ -4,7 +4,7 @@
* @Github: * @Github:
* @Date: 2019-11-20 10:35:40 * @Date: 2019-11-20 10:35:40
* @LastEditors: tangjiang * @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.core.css';
import 'quill/dist/quill.bubble.css'; import 'quill/dist/quill.bubble.css';
@ -236,22 +236,41 @@ class EditTab extends React.Component {
<div className={'editor_area'} id="textCase"> <div className={'editor_area'} id="textCase">
<Form className={'editor_form'}> <Form className={'editor_form'}>
<FormItem <FormItem
className={`input_area flex_60`} className={`input_area flex_50 flex_50_left`}
label={<span>{myLabel(jcLabel['name'])}</span>} label={<span>{myLabel(jcLabel['difficult'])}</span>}
validateStatus={ojFormValidate.name.validateStatus} validateStatus={ojFormValidate.difficult.validateStatus}
help={ojFormValidate.name.errMsg} help={ojFormValidate.difficult.errMsg}
colon={ false } colon={ false }
> >
<Input <Select onChange={this.handleChangeDifficult} value={`${ojForm.difficult}`}>
maxLength={60} {getOptions('difficult')}
placeholder="请输入任务名称" </Select>
value={ojForm.name} </FormItem>
suffix={<span style={{ fontSize: '12px', color: 'rgba(0, 0, 0, 0.45)' }}>{60 - ojForm.name.length}</span>}
onChange={this.handleNameChange} <FormItem
/> 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>
<FormItem <FormItem
className={`input_area flex_40`} className={`input_area flex_50 flex_50_right`}
label={<span>{myLabel(jcLabel['language'])}</span>} label={<span>{myLabel(jcLabel['language'])}</span>}
validateStatus={ojFormValidate.language.validateStatus} validateStatus={ojFormValidate.language.validateStatus}
help={ojFormValidate.language.errMsg} help={ojFormValidate.language.errMsg}
@ -261,6 +280,23 @@ class EditTab extends React.Component {
{getOptions('language')} {getOptions('language')}
</Select> </Select>
</FormItem> </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 <FormItem
className={`input_area flex_100`} className={`input_area flex_100`}
label={<span>{myLabel(jcLabel['description'])}</span>} label={<span>{myLabel(jcLabel['description'])}</span>}
@ -276,38 +312,8 @@ class EditTab extends React.Component {
options={quillConfig} options={quillConfig}
/> />
</FormItem> </FormItem>
<FormItem
className={`input_area flex_50 flex_50_left`} {/* <FormItem
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
className={`input_area flex_50 flex_50_right`} className={`input_area flex_50 flex_50_right`}
label={<span>{myLabel(jcLabel['openOrNot'], '社区:您的任务将向整个社会公开')}</span>} label={<span>{myLabel(jcLabel['openOrNot'], '社区:您的任务将向整个社会公开')}</span>}
validateStatus={ojFormValidate.openOrNot.validateStatus} validateStatus={ojFormValidate.openOrNot.validateStatus}
@ -317,13 +323,13 @@ class EditTab extends React.Component {
<Select onChange={this.handleChangeOpenOrNot} value={`${ojForm.openOrNot}`}> <Select onChange={this.handleChangeOpenOrNot} value={`${ojForm.openOrNot}`}>
{getOptions('openOrNot')} {getOptions('openOrNot')}
</Select> </Select>
</FormItem> </FormItem> */}
</Form> </Form>
{/* 添加测试用例 */} {/* 添加测试用例 */}
<div className={'test_demo_title'} ref={this.headerRef}> <div className={'test_demo_title'} ref={this.headerRef}>
<h2>测试用例</h2> <h2>测试用例</h2>
<Button type="primary" onClick={handleAddTest}>添加测试用例</Button> <Button type="primary" ghost onClick={handleAddTest}>添加测试用例</Button>
</div> </div>
<div className="test_demo_ctx"> <div className="test_demo_ctx">
{ renderTestCase() } { renderTestCase() }

@ -7,44 +7,84 @@
*/ */
import './index.scss'; import './index.scss';
import React, { useState } from 'react'; import React, { useState, useMemo } from 'react';
import { Tabs } from 'antd'; // import { Tabs } from 'antd';
import EditorTab from './editorTab'; import EditorTab from './editorTab';
import PrevTab from './prevTab'; import PrevTab from './prevTab';
import CommitTab from './commitTab'; import CommitTab from './commitTab';
const { TabPane } = Tabs; // const { TabPane } = Tabs;
function LeftPane (props) { function LeftPane (props) {
const navItem = [
{
title: '编辑',
key: 'editor'
}, {
title: '预览',
key: 'prev'
}
];
const Comp = {
editor: (<EditorTab />),
prev: (<PrevTab />)
};
const [defaultActiveKey, setDefaultActiveKey] = useState('editor'); const [defaultActiveKey, setDefaultActiveKey] = useState('editor');
const tabArrs = [ // const tabArrs = [
{ title: '编辑', key: 'editor', content: (<EditorTab />) }, // { title: '编辑', key: 'editor', content: (<EditorTab />) },
{ title: '预览', key: 'prev', content: (<PrevTab />) }, // { title: '预览', key: 'prev', content: (<PrevTab />) },
// { title: '提交记录', key: 'commit', content: (<CommitTab />) }, // // { title: '提交记录', key: 'commit', content: (<CommitTab />) },
]; // ];
const tabs = tabArrs.map((tab) => { // const tabs = tabArrs.map((tab) => {
const Comp = tab.content; // const Comp = tab.content;
return ( // return (
<TabPane tab={tab.title} key={tab.key}> // <TabPane tab={tab.title} key={tab.key}>
{ Comp } // { Comp }
</TabPane> // </TabPane>
) // )
}); // });
// tab切换时 // tab切换时
const handleTabChange = (key) => { // const handleTabChange = (key) => {
setDefaultActiveKey(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 ( return (
<Tabs activeKey={defaultActiveKey} onChange={handleTabChange}> // <Tabs activeKey={defaultActiveKey} onChange={handleTabChange}>
{ tabs } // { tabs }
</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: * @Github:
* @Date: 2019-12-01 10:18:35 * @Date: 2019-12-01 10:18:35
* @LastEditors: tangjiang * @LastEditors: tangjiang
* @LastEditTime: 2019-12-06 17:50:58 * @LastEditTime: 2019-12-09 11:38:15
*/ */
import './index.scss'; import './index.scss';
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import MyMonacoEditor from '../../components/myMonacoEditor'; import MyMonacoEditor from '../../components/myMonacoEditor';
import ControlSetting from '../../components/controlSetting'; // import ControlSetting from '../../components/controlSetting';
import actions from '../../../../redux/actions'; import actions from '../../../../redux/actions';
function RightPane (props, ref) { function RightPane (props, ref) {
const { const {
// identifier, // identifier,
code, // code,
onSubmitForm, // onSubmitForm,
saveOjFormCode saveOjFormCode
} = props; } = props;
@ -48,12 +48,12 @@ function RightPane (props, ref) {
code={props.code} code={props.code}
onCodeChange={handleCodeChange}/> onCodeChange={handleCodeChange}/>
<ControlSetting {/* <ControlSetting
// identifier={identifier} // identifier={identifier}
inputValue={props.input} inputValue={props.input}
onSubmitForm={onSubmitForm} onSubmitForm={onSubmitForm}
// onDebuggerCode={handleDebuggerCode} // onDebuggerCode={handleDebuggerCode}
/> /> */}
</div> </div>
) )
} }

@ -3,7 +3,7 @@
// justify-content: center; // justify-content: center;
background-color: #222; background-color: #222;
height: 100%; height: 100%;
// justify-content: ; // height: calc(100vh - 178px);
.code-title, .code-title,
.controller-pane, .controller-pane,
.pane_control_opts{ .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: * @Github:
* @Date: 2019-11-25 09:46:03 * @Date: 2019-11-25 09:46:03
* @LastEditors: tangjiang * @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.core.css';
// import 'quill/dist/quill.bubble.css'; // import 'quill/dist/quill.bubble.css';
// import 'quill/dist/quill.snow.css'; // import 'quill/dist/quill.snow.css';
// import 'katex/dist/katex.css'; // import 'katex/dist/katex.css';
import './index.scss'; import './index.scss';
import 'katex/dist/katex.min.css';
import React from 'react'; import React from 'react';
import katex from 'katex'; import katex from 'katex';
const Quill = require('quill'); const Quill = require('quill');
@ -120,15 +121,12 @@ class QuillEditor extends React.Component {
render () { render () {
const styles = this.props.style || {} const styles = this.props.style || {}
return ( return (
<div> <div
<div id="quill_editor"
id="quill_editor" style={styles}
style={styles} className={'quill_editor_area'}
className={'quill_editor_area'} ref={this.editorRef}>
ref={this.editorRef}>
</div>
</div> </div>
); );
} }
} }

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

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

@ -7,7 +7,8 @@
.student_study_header, .student_study_header,
.record_detail_header{ .record_detail_header{
height: 65px; height: 65px;
background:rgba(34,34,34,1); // background:rgba(34,34,34,1);
background: #1E1E1E;
padding:0 30px; padding:0 30px;
} }
@ -45,11 +46,6 @@
.pane_right_area .pane_right_area
{ {
position: relative; position: relative;
height: calc(100% - 65px);
.left_pane,
.right_pane{
height: 100%;
}
} }
.student_study_header, .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-area,
.split-pane-left{ .split-pane-left{
.ant-tabs-nav-wrap{ .ant-tabs-nav-wrap{
@ -109,9 +140,8 @@
.editor_area, .editor_area,
.prev_area{ .prev_area{
height: calc(100vh - 110px); height: 100%;
overflow-y: auto; overflow-y: auto;
// padding: 20px 0;
} }
} }

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

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

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

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

@ -15,7 +15,8 @@
width: 100%; width: 100%;
// background: pink; // background: pink;
padding: 0 30px; padding: 0 30px;
background-color: rgba(250,250,250,1); // background-color: rgba(250,250,250,1);
background: #fff;
.flex_count, .flex_count,
.flex_info{ .flex_info{
@ -28,18 +29,23 @@
} }
} }
.task_description_area,
.commit_record_area{ .commit_record_area{
padding: 0 30px; padding: 0 30px;
height: calc(100vh - 166px); // height: calc(100vh - 178px);
overflow-y: auto; // overflow-y: auto;
} }
.task_description_area{ .task_description_area{
.task_desc_area{
height: calc(100vh - 242px);
padding: 0 0 0 15px;
}
.desc_area_header{ .desc_area_header{
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
height: 64px; height: 64px;
padding: 0 30px;
.header_flex{ .header_flex{
font-size: 14px; font-size: 14px;
.flex_label{ .flex_label{
@ -52,3 +58,25 @@
} }
} }
} }
.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: * @Github:
* @Date: 2019-11-27 09:49:30 * @Date: 2019-11-27 09:49:30
* @LastEditors: tangjiang * @LastEditors: tangjiang
* @LastEditTime: 2019-11-29 13:46:11 * @LastEditTime: 2019-12-09 19:21:55
*/ */
import '../index.scss'; import '../index.scss';
import React from 'react'; import React from 'react';
@ -39,12 +39,18 @@ const TaskDescription = (props) => {
<Link to="/" style={{ color: '#5091FF'}}>{username}</Link> <Link to="/" style={{ color: '#5091FF'}}>{username}</Link>
</p> </p>
</div> </div>
<QuillEditor <div className="task_desc_area">
<QuillEditor
htmlCtx={description}
readOnly={true}
/>
</div>
{/* <QuillEditor
htmlCtx={description} htmlCtx={description}
readOnly={true} readOnly={true}
options={[]} options={[]}
style={{ height: "calc(100% - 109px)" }} style={{ backgroundColor: 'gold' }}
/> /> */}
{/* <div dangerouslySetInnerHTML={{__html: description}}></div> */} {/* <div dangerouslySetInnerHTML={{__html: description}}></div> */}
</div> </div>
) )

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

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

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

@ -4,7 +4,7 @@
* @Github: * @Github:
* @Date: 2019-11-27 13:42:11 * @Date: 2019-11-27 13:42:11
* @LastEditors: tangjiang * @LastEditors: tangjiang
* @LastEditTime: 2019-12-06 17:56:02 * @LastEditTime: 2019-12-10 19:05:36
*/ */
import types from "./actionTypes"; import types from "./actionTypes";
import { Base64 } from 'js-base64'; import { Base64 } from 'js-base64';
@ -31,6 +31,11 @@ export const startProgramQuestion = (id, props) => {
type: types.SAVE_USER_PROGRAM_ID, type: types.SAVE_USER_PROGRAM_ID,
payload: identifier payload: identifier
}); });
// 保存id值
dispatch({
type: types.SAVE_HACK_IDENTIFIER,
payload: id
});
// 跳转至开启编程 // 跳转至开启编程
if (identifier) { if (identifier) {
// let data = Object.assign({}, props); // let data = Object.assign({}, props);
@ -74,8 +79,13 @@ export const getUserProgramDetail = (identifier, type) => {
dispatch({ dispatch({
type: types.GET_COMMIT_RECORD_DETAIL_BY_ID, type: types.GET_COMMIT_RECORD_DETAIL_BY_ID,
payload: data payload: data
}) });
} }
// 保存用户登录信息
dispatch({
type: types.SAVE_USER_INFO,
payload: data.user
});
} }
}); });
} }
@ -277,10 +287,12 @@ export const getUserCommitRecordDetail = (identifier) => {
return (dispatch) => { return (dispatch) => {
fetchUserCommitRecordDetail(identifier).then(res => { fetchUserCommitRecordDetail(identifier).then(res => {
console.log('提交记录详情======》》》》', res); console.log('提交记录详情======》》》》', res);
const { data } = res;
if (data.status === 401) return;
dispatch({ dispatch({
type: types.GET_COMMIT_RECORD_DETAIL_BY_ID, type: types.GET_COMMIT_RECORD_DETAIL_BY_ID,
payload: res.data payload: data
}) });
}); });
} }
} }
@ -327,6 +339,11 @@ export const submitUserCode = (identifier, inputValue, type) => {
}); });
return; return;
}; };
// 将编辑代码清空
dispatch({
type: types.SAVE_EDITOR_CODE,
payload: ''
});
dispatch(debuggerCode(identifier, inputValue, type || 'submit')); dispatch(debuggerCode(identifier, inputValue, type || 'submit'));
} }
}).catch(() => { }).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: * @Github:
* @Date: 2019-11-20 16:35:46 * @Date: 2019-11-20 16:35:46
* @LastEditors: tangjiang * @LastEditors: tangjiang
* @LastEditTime: 2019-12-06 20:24:11 * @LastEditTime: 2019-12-10 19:54:56
*/ */
import types from './actionTypes'; import types from './actionTypes';
import CONST from '../../constants'; import CONST from '../../constants';
import { fetchPostOjForm, fetchGetOjById, publishTask } from '../../services/ojService'; import { fetchPostOjForm, fetchGetOjById, publishTask } from '../../services/ojService';
import { Base64 } from 'js-base64'; import { Base64 } from 'js-base64';
import { message, notification } from 'antd'; import { message, notification, Modal } from 'antd';
import { toStore } from 'educoder'; import { toStore } from 'educoder';
const { jcLabel } = CONST; const { jcLabel } = CONST;
// 表单字段映射 // 表单字段映射
@ -85,6 +85,7 @@ export const validateOjForm = (props, type) => {
return (dispatch, getState) => { return (dispatch, getState) => {
const {ojForm, testCases, identifier, code } = getState().ojFormReducer; const {ojForm, testCases, identifier, code } = getState().ojFormReducer;
// console.log('code', code); // console.log('code', code);
/** 表单验证开始 */
let keys = Object.keys(ojForm); let keys = Object.keys(ojForm);
// 循环判断每个字段是否为空 // 循环判断每个字段是否为空
let hasSuccess = true; let hasSuccess = true;
@ -104,14 +105,35 @@ export const validateOjForm = (props, type) => {
}); });
// 验证测试用例中的数组是否都有对应的值 // 验证测试用例中的数组是否都有对应的值
const tcValidResult = []; const tcValidResult = [];
testCases.forEach(obj => { // 验证测试用例: 1.必须要有输出值 2. 输入值与输出值必须唯一
testCases.forEach((obj, i) => {
let tempObj = {}; let tempObj = {};
['input', 'output'].forEach(key => { ['input', 'output'].forEach(key => {
const value = obj[key]; const value = obj[key];
const validateResult = emptyValidate(key, value); // 非空校验
let validateResult = emptyValidate(key, value);
const errMsg = validateResult[key].errMsg; const errMsg = validateResult[key].errMsg;
if (errMsg) { if (errMsg) {
hasSuccess = false; 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); Object.assign(tempObj, validateResult);
}); });
@ -151,6 +173,8 @@ export const validateOjForm = (props, type) => {
payload: false payload: false
}); });
} }
/** 表单验证结束 */
/** 表单验证通过后,调用保存 or 更新 or 发布 */
if (hasSuccess) { if (hasSuccess) {
// console.log('表单保存的数据为: ', getState()); // console.log('表单保存的数据为: ', getState());
const {ojFormReducer} = getState(); const {ojFormReducer} = getState();
@ -205,6 +229,7 @@ export const validateOjForm = (props, type) => {
paramsObj['identifier'] = identifier; paramsObj['identifier'] = identifier;
} }
// 接口调用成功后,跳转至列表页
function linkToDev () { function linkToDev () {
toStore('oj_description', ''); toStore('oj_description', '');
dispatch({ dispatch({
@ -216,51 +241,61 @@ export const validateOjForm = (props, type) => {
}, 1000); }, 1000);
} }
fetchPostOjForm(paramsObj).then(res => { // 调用保存或更新
// TODO if (type === 'publish') {
if (res.status === 200) { // 保存成功后,重新跳转至列表页 // 提示发布信息
const {identifier} = res.data Modal.confirm({
if (type === 'publish') { // 存在发布时,直接调用发布接口 title: '提示',
if (identifier) { content: `
publishTask(identifier).then(res => { 发布后即可应用到自己管理的课堂,
if (res.data.status === 0) { 是否确定发布?`,
message.success('发布成功!'); okText: '确定',
linkToDev(); cancelText: '取消',
} onOk () {
dispatch({ publishTask(identifier).then(res => {
type: types.PUBLISH_LOADING_STATUS, if (res.data.status === 0) {
payload: false message.success('发布成功!');
}); linkToDev();
}).catch(() => { }
dispatch({ dispatch({
type: types.PUBLISH_LOADING_STATUS, type: types.PUBLISH_LOADING_STATUS,
payload: false payload: false
});
}); });
} else { }).catch(() => {
dispatch({ dispatch({
type: types.PUBLISH_LOADING_STATUS, type: types.PUBLISH_LOADING_STATUS,
payload: false 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) { if (res.data.identifier) {
message.success('保存成功!'); message.success('保存成功!');
linkToDev(); linkToDev();
} }
dispatch({
type: types.SUBMIT_LOADING_STATUS,
payload: false
});
}
}).catch(err => {
dispatch({ dispatch({
type: types.SUBMIT_LOADING_STATUS, type: types.SUBMIT_LOADING_STATUS,
payload: false 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, type: types.SAVE_EDIT_OJ_FORM_AND_TEST_CASE,
payload: res.data payload: res.data
}); });
// 保存用户信息
dispatch({
type: types.SAVE_USER_INFO,
payload: res.data.user
});
}); });
} }
} }
@ -371,7 +411,7 @@ export const saveOJFormId = (id) => {
payload: id payload: id
} }
} }
// 清空测试用例集合 // 清空
export const clearOJFormStore = () => { export const clearOJFormStore = () => {
return { return {
type: types.CLEAR_JSFORM_STORE type: types.CLEAR_JSFORM_STORE
@ -380,27 +420,76 @@ export const clearOJFormStore = () => {
// 测试用例输入值改变时 // 测试用例输入值改变时
export const testCaseInputChange = (value, index) => { export const testCaseInputChange = (value, index) => {
const validate = emptyValidate('input', value)['input']; return (dispatch, getState) => {
return { // 非空校验
type: types.TEST_CASE_INPUT_CHANGE, let validate = emptyValidate('input', value)['input'];
payload: { if (!validate.errMsg) {
input: validate, // 唯一性校验
value, const {testCases} = getState().ojFormReducer;
index 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) => { export const testCaseOutputChange = (value, index) => {
const validate = emptyValidate('output', value)['output']; // const validate = emptyValidate('output', value)['output'];
return { // return {
type: types.TEST_CASE_OUTPUT_CHANGE, // type: types.TEST_CASE_OUTPUT_CHANGE,
payload: { // payload: {
output: validate, // output: validate,
value, // value,
index // 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: * @Github:
* @Date: 2019-11-20 10:48:24 * @Date: 2019-11-20 10:48:24
* @LastEditors: tangjiang * @LastEditors: tangjiang
* @LastEditTime: 2019-11-29 11:09:54 * @LastEditTime: 2019-12-10 20:40:55
*/ */
import types from './actionTypes'; import types from './actionTypes';
import { fetchOJList } from '../../services/ojService'; import { fetchOJList, fetchDeleteOJItem } from '../../services/ojService';
export const getOJList = (params) => { export const getOJList = (params) => {
return (dispatch) => { return (dispatch) => {
@ -35,3 +35,15 @@ export const changePaginationInfo = (obj) => {
payload: 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: * @Github:
* @Date: 2019-12-06 15:09:22 * @Date: 2019-12-06 15:09:22
* @LastEditors: tangjiang * @LastEditors: tangjiang
* @LastEditTime: 2019-12-06 15:15:00 * @LastEditTime: 2019-12-09 20:34:50
*/ */
import types from './actionTypes'; import types from './actionTypes';
import { fetchUserInfoForNew } from '../../services/ojService';
// 获取用户信息 // 获取用户信息
export default function getUserInfo () { export const getUserInfoForNew = () => {
return (dispatch) => { 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: * @Github:
* @Date: 2019-11-27 13:41:48 * @Date: 2019-11-27 13:41:48
* @LastEditors: tangjiang * @LastEditors: tangjiang
* @LastEditTime: 2019-12-05 13:37:17 * @LastEditTime: 2019-12-10 10:09:59
*/ */
import types from "../actions/actionTypes"; import types from "../actions/actionTypes";
import { Base64 } from 'js-base64'; import { Base64 } from 'js-base64';
@ -20,6 +20,8 @@ const initialState = {
userCodeTab: 'task', // 学员测评tab位置: task | record | comment userCodeTab: 'task', // 学员测评tab位置: task | record | comment
userTestInput: '', // 用户自定义输入值 userTestInput: '', // 用户自定义输入值
recordDetail: {}, // 根据id号获取的记录详情 recordDetail: {}, // 根据id号获取的记录详情
hack_identifier: '', // 用户界面编辑时
editor_code: '' // 保存编辑代码
}; };
const ojForUserReducer = (state = initialState, action) => { const ojForUserReducer = (state = initialState, action) => {
@ -109,6 +111,16 @@ const ojForUserReducer = (state = initialState, action) => {
...state, ...state,
hack: Object.assign({}, curHack) 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: default:
return state; return state;
} }

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

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

Loading…
Cancel
Save