Merge branch 'dev_aliyun' of https://bdgit.educoder.net/Hjqreturn/educoder into dev_aliyun

newyslclassrooms
caicai8 5 years ago
commit 42d0b45c6e

@ -2,9 +2,13 @@ class CourseModulesController < ApplicationController
before_action :require_login, :check_auth before_action :require_login, :check_auth
before_action :set_module, except: [:unhidden_modules] before_action :set_module, except: [:unhidden_modules]
before_action :find_course, only: [:unhidden_modules] before_action :find_course, only: [:unhidden_modules]
before_action :teacher_or_admin_allowed, except: [:add_second_category] before_action :teacher_or_admin_allowed, except: [:show, :add_second_category]
before_action :teacher_allowed, only: [:add_second_category] before_action :teacher_allowed, only: [:add_second_category]
def show
end
# 模块置顶 # 模块置顶
def sticky_module def sticky_module
# position为1则不做处理否则该模块的position置为1position小于当前模块的position加1 # position为1则不做处理否则该模块的position置为1position小于当前模块的position加1

@ -30,14 +30,14 @@ class CoursesController < ApplicationController
:informs, :update_informs, :online_learning, :update_task_position, :tasks_list, :informs, :update_informs, :online_learning, :update_task_position, :tasks_list,
:join_excellent_course, :export_couser_info, :export_member_act_score, :new_informs, :join_excellent_course, :export_couser_info, :export_member_act_score, :new_informs,
:delete_informs, :change_member_role, :course_groups, :join_course_group, :statistics, :delete_informs, :change_member_role, :course_groups, :join_course_group, :statistics,
:work_score, :act_score, :calculate_all_shixun_scores] :work_score, :act_score, :calculate_all_shixun_scores, :move_to_category]
before_action :user_course_identity, except: [:join_excellent_course, :index, :create, :new, :apply_to_join_course, before_action :user_course_identity, except: [:join_excellent_course, :index, :create, :new, :apply_to_join_course,
:search_course_list, :get_historical_course_students, :mine, :search_slim, :board_list] :search_course_list, :get_historical_course_students, :mine, :search_slim, :board_list]
before_action :teacher_allowed, only: [:update, :destroy, :settings, :search_teacher_candidate, before_action :teacher_allowed, only: [:update, :destroy, :settings, :search_teacher_candidate,
:transfer_to_course_group, :delete_from_course, :export_member_scores_excel, :transfer_to_course_group, :delete_from_course, :export_member_scores_excel,
:search_users, :add_students_by_search, :get_historical_courses, :add_teacher_popup, :search_users, :add_students_by_search, :get_historical_courses, :add_teacher_popup,
:add_teacher, :export_couser_info, :export_member_act_score, :add_teacher, :export_couser_info, :export_member_act_score,
:update_informs, :new_informs, :delete_informs, :switch_to_student] :update_informs, :new_informs, :delete_informs, :switch_to_student, :move_to_category]
before_action :admin_allowed, only: [:set_invite_code_halt, :set_public_or_private, :change_course_admin, before_action :admin_allowed, only: [:set_invite_code_halt, :set_public_or_private, :change_course_admin,
:set_course_group, :create_group_by_importing_file, :set_course_group, :create_group_by_importing_file,
:update_task_position, :tasks_list] :update_task_position, :tasks_list]
@ -118,6 +118,21 @@ class CoursesController < ApplicationController
render_ok render_ok
end end
# 视频移动到目录
def move_to_category
tip_exception("请选择要移动的目录") if params[:new_category_id].blank?
category = @course.course_second_categories.find_by(id: params[:new_category_id])
if params[:new_category_id].to_i == 0 || category.present?
videos = @course.course_videos.where(id: params[:video_ids])
videos.update_all(course_second_category_id: params[:new_category_id])
normal_status(0, "操作成功")
else
normal_status(-1, "目录不存在")
end
end
def visits_plus_one def visits_plus_one
new_visits = @course.visits + 1 new_visits = @course.visits + 1
@course.update_visits(new_visits) @course.update_visits(new_visits)
@ -1259,7 +1274,7 @@ class CoursesController < ApplicationController
@is_teacher = @user_course_identity < Course::ASSISTANT_PROFESSOR @is_teacher = @user_course_identity < Course::ASSISTANT_PROFESSOR
@course_modules = @course.course_modules.where(hidden: 0) @course_modules = @course.course_modules.where(hidden: 0)
@hidden_modules = @course.course_modules.where(hidden: 1) @hidden_modules = @course.course_modules.where(hidden: 1)
@second_category_type = ["shixun_homework", "graduation", "attachment", "board", "course_group"] @second_category_type = ["shixun_homework", "graduation", "attachment", "board", "course_group", "video"]
end end
def board_list def board_list

@ -13,7 +13,8 @@ class HackUserLastestCodesController < ApplicationController
end end
def update_code def update_code
@my_hack.update_attribute(:code, params[:code]) # 防止tpm改动了语言所以更新语言字段
@my_hack.update_attributes(code: params[:code], language: @hack.language)
render_ok render_ok
end end
@ -87,7 +88,7 @@ class HackUserLastestCodesController < ApplicationController
# 只有编译出错时,才正则匹配错误行数 # 只有编译出错时,才正则匹配错误行数
error_line= error_line=
if ojEvaResult['status'] == "4" || ojEvaResult['status'] == "5" if ojEvaResult['status'] == "4" || ojEvaResult['status'] == "5"
regular_match_error_line ojEvaResult['outPut'], @my_hack.hack.language regular_match_error_line ojEvaResult['outPut'], @hack.language
end end
# debug 与submit 公用的参数 # debug 与submit 公用的参数
@ -100,7 +101,7 @@ class HackUserLastestCodesController < ApplicationController
if ojEvaResult['execMode'] == "debug" if ojEvaResult['execMode'] == "debug"
save_debug_data ds_params save_debug_data ds_params
elsif ojEvaResult['execMode'] == "submit" elsif ojEvaResult['execMode'] == "submit"
save_submit_data ds_params.merge(expected_output: testCase['expectedOutput'], save_submit_data ds_params.merge(expected_output: testCase['expectedOutput'], language: @hack.language,
error_test_set_id: ojEvaResult['failCaseNum']) error_test_set_id: ojEvaResult['failCaseNum'])
end end
# 评测完成后,还原评测中的状态 # 评测完成后,还原评测中的状态
@ -138,7 +139,7 @@ class HackUserLastestCodesController < ApplicationController
debug_params = {execMode: exec_mode, debug_params = {execMode: exec_mode,
tpiID: @my_hack.identifier, tpiID: @my_hack.identifier,
testCases: testCases, testCases: testCases,
platform: @my_hack.language, platform: @my_hack.hack.language,
codeFileContent: @my_hack.code, codeFileContent: @my_hack.code,
timeLimit: @hack.time_limit, timeLimit: @hack.time_limit,
sec_key: Time.now.to_i} sec_key: Time.now.to_i}
@ -149,7 +150,7 @@ class HackUserLastestCodesController < ApplicationController
# 正则错误行数 # 正则错误行数
def regular_match_error_line content, language def regular_match_error_line content, language
content = Base64.decode64(content).force_encoding("utf-8") content = Base64.decode64(content) #.force_encoding("utf-8")
logger.info("######content: #{content}") logger.info("######content: #{content}")
case language case language
when 'Java' when 'Java'

@ -6,7 +6,7 @@ class HomeworkCommonsController < ApplicationController
before_action :require_login, :check_auth, except: [:index, :choose_category] before_action :require_login, :check_auth, except: [:index, :choose_category]
before_action :find_course, only: [:index, :create, :new, :shixuns, :subjects, :create_shixun_homework, :publish_homework, before_action :find_course, only: [:index, :create, :new, :shixuns, :subjects, :create_shixun_homework, :publish_homework,
:end_homework, :set_public, :choose_category, :move_to_category, :choose_category, :end_homework, :set_public, :move_to_category, :choose_category,
:create_subject_homework, :multi_destroy, :add_to_homework_bank] :create_subject_homework, :multi_destroy, :add_to_homework_bank]
before_action :find_homework, only: [:edit, :show, :update, :group_list, :homework_code_repeat, :code_review_results, before_action :find_homework, only: [:edit, :show, :update, :group_list, :homework_code_repeat, :code_review_results,
:code_review_detail, :show_comment, :settings, :works_list, :update_settings, :code_review_detail, :show_comment, :settings, :works_list, :update_settings,

@ -87,16 +87,18 @@ module CoursesHelper
# 子目录对应的url # 子目录对应的url
def category_url category, course def category_url category, course
case category.category_type case category.category_type
when "shixun_homework" when "shixun_homework"
"/courses/#{course.id}/shixun_homework/#{category.id}" "/courses/#{course.id}/shixun_homework/#{category.id}"
when "graduation" when "graduation"
if category.name == "毕设选题" if category.name == "毕设选题"
"/courses/#{course.id}/graduation_topics/#{category.course_module_id}" "/courses/#{course.id}/graduation_topics/#{category.course_module_id}"
else else
"/courses/#{course.id}/graduation_tasks/#{category.course_module_id}" "/courses/#{course.id}/graduation_tasks/#{category.course_module_id}"
end end
when "attachment" when "attachment"
"/courses/#{course.id}/file/#{category.id}" "/courses/#{course.id}/file/#{category.id}"
when "video"
"/courses/#{course.id}/course_videos/#{category.id}"
end end
end end
@ -113,6 +115,8 @@ module CoursesHelper
end end
when "attachment" when "attachment"
get_attachment_count(course, category.id) get_attachment_count(course, category.id)
when "video"
get_video_count(course, category.id)
end end
end end
@ -237,6 +241,11 @@ module CoursesHelper
category_id.to_i == 0 ? course.attachments.size : course.attachments.where(course_second_category_id: category_id).size category_id.to_i == 0 ? course.attachments.size : course.attachments.where(course_second_category_id: category_id).size
end end
# 获取课堂的视频数
def get_video_count(course, category_id)
category_id.to_i == 0 ? course.course_videos.size : course.course_videos.where(course_second_category_id: category_id).size
end
# 获取课堂的作业数 # 获取课堂的作业数
def get_homework_commons_count(course, type, category_id) def get_homework_commons_count(course, type, category_id)
category_id == 0 ? HomeworkCommon.where(course_id: course.id, homework_type: type).size : category_id == 0 ? HomeworkCommon.where(course_id: course.id, homework_type: type).size :

@ -8,6 +8,8 @@ module AliyunVod::Service::VideoUpload
FileName: filename FileName: filename
}.merge(base_params) }.merge(base_params)
# TODO 获取视频的同时,可以指定转码组,在这里指定
# 参数TemplateGroupId 转码组的id.
# 分类 # 分类
cate_id = AliyunVod.cate_id cate_id = AliyunVod.cate_id
params[:CateId] = cate_id if cate_id.present? params[:CateId] = cate_id if cate_id.present?

@ -0,0 +1,8 @@
json.course_module do
json.id @course_module.id
json.module_name @course_module.module_name
json.module_type @course_module.module_type
json.course_second_categories do
json.array! @course_module.course_second_categories, :id, :name
end
end

@ -3,7 +3,7 @@ json.message "返回成功"
json.data do json.data do
json.(@hack_user, :id, :status, :error_line, :error_msg, :expected_output, json.(@hack_user, :id, :status, :error_line, :error_msg, :expected_output,
:input, :output, :execute_time, :execute_memory, :created_at, :code) :input, :output, :execute_time, :execute_memory, :created_at, :code)
json.language @hack_user.hack.language json.language @hack_user.language
json.name @hack_user.hack.name json.name @hack_user.hack.name
json.myproblem_identifier @my_hack.identifier json.myproblem_identifier @my_hack.identifier
json.pass_sets_count @pass_set_count json.pass_sets_count @pass_set_count

@ -1,7 +1,7 @@
json.records do json.records do
json.array! @records do |hack_user| json.array! @records do |hack_user|
json.(hack_user, :id, :created_at, :status, :execute_time, :execute_memory) json.(hack_user, :id, :created_at, :status, :execute_time, :execute_memory)
json.language hack_user.hack.language json.language hack_user.language
end end
end end

@ -524,6 +524,7 @@ Rails.application.routes.draw do
get 'statistics' get 'statistics'
get 'course_videos' get 'course_videos'
delete 'delete_course_video' delete 'delete_course_video'
post :move_to_category
post :inform_up post :inform_up
post :inform_down post :inform_down
get :calculate_all_shixun_scores get :calculate_all_shixun_scores

@ -0,0 +1,5 @@
class AddLanguageForHackUserCodes < ActiveRecord::Migration[5.2]
def change
add_column :hack_user_codes, :language, :string
end
end

@ -0,0 +1,5 @@
class AddCourseSecondCategoryIdToCourseVideos < ActiveRecord::Migration[5.2]
def change
add_column :course_videos, :course_second_category_id, :integer, index: true, default: 0
end
end

@ -2,12 +2,13 @@ desc "同步学院或者单位评测数"
namespace :sync_evaluate do namespace :sync_evaluate do
task outpus_count: :environment do task outpus_count: :environment do
School.find_each do |school| School.find_in_batches(batch_size: 50) do |school|
Parallel.each_with_index(school, in_processes: 5) do |s| Parallel.each_with_index(school, in_processes: 5) do |s|
puts "school_id:#{s.id}" puts "school_id:#{s.id}"
evaluate_count = Game.find_by_sql("select sum(g.evaluate_count) as e_count from games g, user_extensions ue where evaluate_count = Game.find_by_sql("select sum(g.evaluate_count) as e_count from games g, user_extensions ue where
g.user_id = ue.user_id and ue.school_id = '#{s.id}'").first.try(:e_count) g.user_id = ue.user_id and ue.school_id = '#{s.id}'").first.try(:e_count)
puts "evaluate_count: #{evaluate_count}"
report = SchoolReport.find_or_initialize_by(school_id: s.id) report = SchoolReport.find_or_initialize_by(school_id: s.id)
report.school_name = s.name report.school_name = s.name
report.shixun_evaluate_count = evaluate_count report.shixun_evaluate_count = evaluate_count

@ -77,7 +77,7 @@ const CONST = {
] ]
}, },
opacitySetting: { opacitySetting: {
title: '代码格式', title: '快捷键',
type: 'label', type: 'label',
content: [ content: [
{ {

@ -372,13 +372,13 @@ class ShixunhomeWorkItem extends Component{
{this.props.isAdmin?<span onClick={(event)=>this.stopPro(event)} className={discussMessage&&discussMessage.shixun_status>1?this.props.isAdminOrCreator()?" newhomepagePostSettingname fr":" homepagePostSettingbox fr":" newwidthSettin fr"} style={{"right":"-2px","top":"6px","display":"block"}}> {this.props.isAdmin?<span onClick={(event)=>this.stopPro(event)} className={discussMessage&&discussMessage.shixun_status>1?this.props.isAdminOrCreator()?" newhomepagePostSettingname fr":" mr20 fr":"fr mr20"} style={{"right":"-2px","top":"6px","display":"block"}}>
{discussMessage&&discussMessage.shixun_status>1?<Link className="btn colorblue font-16 fontweight400 mr20" to={"/shixuns/"+discussMessage.shixun_identifier+"/challenges"} target={"_blank"}>进入学习</Link>: {discussMessage&&discussMessage.shixun_status>1?<Link className="btn colorblue font-16 fontweight400" to={"/shixuns/"+discussMessage.shixun_identifier+"/challenges"} target={"_blank"}>进入学习</Link>:
<a className={"btn colorfff font-16 fontweight400 "}>进入学习</a> ""
} }
{this.props.isAdminOrCreator()?<a onClick={(event)=>this.editname(discussMessage.name,discussMessage.homework_id,event)} className={"btn colorblue font-16 fontweight400 "}>重命名</a>:""} {this.props.isAdminOrCreator()?<a onClick={(event)=>this.editname(discussMessage.name,discussMessage.homework_id,event)} className={"ml20 btn colorblue font-16 fontweight400 "}>重命名</a>:""}
{/*<WordsBtn className="btn colorblue ml20 font-16" to={`/courses/${this.props.match.params.coursesId}/${this.state.shixuntypes}/${discussMessage.homework_id}/settings?tab=3`} > 设置</WordsBtn>*/} {/*<WordsBtn className="btn colorblue ml20 font-16" to={`/courses/${this.props.match.params.coursesId}/${this.state.shixuntypes}/${discussMessage.homework_id}/settings?tab=3`} > 设置</WordsBtn>*/}
<WordsBtn className="btn colorblue font-16 ml15 fontweight400 " to={`/courses/${this.props.match.params.coursesId}/${this.state.shixuntypes}/${discussMessage.homework_id}/settings?tab=3`} > 设置</WordsBtn> <WordsBtn className="btn colorblue font-16 ml15 fontweight400 " to={`/courses/${this.props.match.params.coursesId}/${this.state.shixuntypes}/${discussMessage.homework_id}/settings?tab=3`} > 设置</WordsBtn>
</span>:""} </span>:""}
@ -433,7 +433,7 @@ class ShixunhomeWorkItem extends Component{
: :
<span className="mr15 color-grey9">{discussMessage.status_time}</span> <span className="mr15 color-grey9">{discussMessage.status_time}</span>
} }

@ -8,7 +8,7 @@
*/ */
import './index.scss'; import './index.scss';
import React, { useState, useRef, useEffect } from 'react'; import React, { useState, useRef, useEffect } from 'react';
import { Tabs, Button, Icon, notification } from 'antd'; import { Tabs, Button, Icon, notification } from 'antd';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import InitTabCtx from '../initTabCtx'; import InitTabCtx from '../initTabCtx';
import ExecResult from '../execResult'; import ExecResult from '../execResult';
@ -17,14 +17,13 @@ import actions from '../../../../redux/actions';
const { TabPane } = Tabs; const { TabPane } = Tabs;
const ControlSetting = (props) => { const ControlSetting = (props) => {
const { const {
hack,
userCode,
inputValue, inputValue,
loading, loading,
submitLoading, submitLoading,
identifier, identifier,
excuteState, excuteState,
editor_code,
// showOrHideControl, // showOrHideControl,
commitTestRecordDetail, commitTestRecordDetail,
changeLoadingState, changeLoadingState,
@ -59,7 +58,7 @@ const ControlSetting = (props) => {
// 调试代码 // 调试代码
const handleTestCode = (e) => { const handleTestCode = (e) => {
if (!userCode) { if (!editor_code) {
notification.warning({ notification.warning({
message: '提示', message: '提示',
description: '代码块内容不能为空' description: '代码块内容不能为空'
@ -74,11 +73,11 @@ const ControlSetting = (props) => {
setDefaultActiveKey('2'); setDefaultActiveKey('2');
}); });
} }
// 提交 // 提交
const handleSubmit = (e) => { const handleSubmit = (e) => {
e.preventDefault(); e.preventDefault();
if (!userCode) { if (!editor_code) {
notification.warning({ notification.warning({
message: '提示', message: '提示',
description: '代码块内容不能为空' description: '代码块内容不能为空'
@ -103,56 +102,53 @@ const ControlSetting = (props) => {
return ( return (
<div className="pane_control_area"> <div className="pane_control_area">
<div <div
className="pane_control_collapse" className="pane_control_collapse"
onClick={handleShowControl} onClick={handleShowControl}
style={{ top: showTextResult ? '-267px' : 0 }} style={{ top: showTextResult ? '-267px' : 0 }}
> >
{/* <i className="iconfont icon-xiajiantou icon"></i> */} {/* <i className="iconfont icon-xiajiantou icon"></i> */}
<Icon type={ showTextResult ? "down" : "up" } /> <Icon type={showTextResult ? "down" : "up"} />
</div> </div>
<Tabs <Tabs
className={classNames} className={classNames}
activeKey={defaultActiveKey} activeKey={defaultActiveKey}
tabBarStyle={{ backgroundColor: 'rgba(18,28,36,1)', color: '#fff' }} tabBarStyle={{ backgroundColor: 'rgba(18,28,36,1)', color: '#fff' }}
onChange={handleTabChange} onChange={handleTabChange}
> >
<TabPane tab={'自定义测试用例'} key={'1'} style={{ height: '280px', overflowY: 'auto' }}> <TabPane tab={'自定义测试用例'} key={'1'} style={{ height: '280px', overflowY: 'auto' }}>
<InitTabCtx <InitTabCtx
inputValue={inputValue} inputValue={inputValue}
wrappedComponentRef={(form) => formRef.current = form} wrappedComponentRef={(form) => formRef.current = form}
onDebuggerCode={handleDebuggerCode} onDebuggerCode={handleDebuggerCode}
/> />
</TabPane> </TabPane>
<TabPane tab={'代码执行结果'} key={'2'} style={{ height: '280px', overflowY: 'auto' }}> <TabPane tab={'代码执行结果'} key={'2'} style={{ height: '280px', overflowY: 'auto' }}>
<ExecResult <ExecResult
excuteState={excuteState} excuteState={excuteState}
excuteDetail={commitTestRecordDetail} excuteDetail={commitTestRecordDetail}
/> />
</TabPane> </TabPane>
</Tabs> </Tabs>
<div className="pane_control_opts"> <div className="pane_control_opts">
<Button <Button
type="link" type="link"
style={{ color: '#fff' }} style={{ color: '#fff' }}
// onClick={handleShowControl}
> >
控制台 控制台
{/* <Icon type={ showTextResult ? "down" : "up" } /> */}
</Button> </Button>
<p> <p>
<Button ghost <Button ghost
loading={loading} loading={loading}
style={{ marginRight: '10px', color: '#28BD8B', borderColor: '#28BD8B' }} style={{ marginRight: '10px', color: '#28BD8B', borderColor: '#28BD8B' }}
onClick={handleTestCode} onClick={handleTestCode}
disabled={!identifier} disabled={!identifier}
>调试代码</Button> >调试代码</Button>
<Button <Button
loading={submitLoading} loading={submitLoading}
type="primary" type="primary"
onClick={handleSubmit} onClick={handleSubmit}
> >
{/* {props.identifier ? '更新' : '提交'} */} 提交
提交
</Button> </Button>
</p> </p>
</div> </div>
@ -161,12 +157,12 @@ const ControlSetting = (props) => {
} }
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
const {commonReducer, ojForUserReducer} = state; const { commonReducer, ojForUserReducer } = state;
const {loading, excuteState, submitLoading, showOrHideControl } = commonReducer; const { loading, excuteState, submitLoading, showOrHideControl } = commonReducer;
const { commitTestRecordDetail, hack, userCode } = ojForUserReducer; const { commitTestRecordDetail, hack, editor_code } = ojForUserReducer;
return { return {
hack, hack,
userCode, editor_code,
loading, loading,
submitLoading, submitLoading,
excuteState, excuteState,

@ -1,58 +1,67 @@
.tab_ctx_area{ .tab_ctx_area {
display: flex; display: flex;
height: 100%; height: 100%;
color: #666; color: #666;
font-size: 14px; font-size: 14px;
&.pos_start{
&.pos_start {
justify-content: flex-start; justify-content: flex-start;
} }
&.pos_center{
&.pos_center {
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
&.pos_end{
&.pos_end {
justify-content: flex-end; justify-content: flex-end;
} }
.ctx_default{
.ctx_default {
margin: 10px 20px; margin: 10px 20px;
} }
.ctx_loading, .ctx_loading,
.ctx_loaded{ .ctx_loaded {
display: flex; display: flex;
position: relative; position: relative;
flex-direction: column; flex-direction: column;
top: -20px; top: -20px;
color: #1890ff; color: #1890ff;
.ctx_icon{
.ctx_icon {
font-size: 40px; font-size: 40px;
margin-bottom: 10px; margin-bottom: 10px;
} }
} }
} }
.user_case_form{ .user_case_form {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
margin-top: 20px; padding-top: 20px;
.input_area{
.input_area {
flex: 1; flex: 1;
.ant-form-item-required{
.ant-form-item-required {
color: #fff; color: #fff;
} }
} }
.flex_l{
.flex_l {
padding: 0 10px 0 10px; padding: 0 10px 0 10px;
color: #fff; color: #fff;
} }
.flex_r{
.flex_r {
padding: 0 20px 0 10px; padding: 0 20px 0 10px;
} }
.input_textarea_style{ .input_textarea_style {
// background:rgba(30,30,30,1) !important; background: rgba(7, 15, 25, 1) !important;
background:rgba(7,15,25,1) !important;
color: #fff; color: #fff;
border-color: transparent; border: 0px solid transparent !important;
outline: none; outline: none;
} }
} }

@ -11,7 +11,8 @@ import React, { useState, useRef, useEffect } from 'react';
import { Drawer, Tooltip, Badge } from 'antd'; import { Drawer, Tooltip, Badge } from 'antd';
import { fromStore, CNotificationHOC } from 'educoder'; import { fromStore, CNotificationHOC } from 'educoder';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import MonacoEditor from '@monaco-editor/react'; import { ControlledEditor } 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 MyIcon from '../../../../common/components/MyIcon';
@ -47,8 +48,6 @@ function MyMonacoEditor(props, ref) {
return fromStore('oj_theme') || 'dark'; return fromStore('oj_theme') || 'dark';
}); });
const [height, setHeight] = useState('calc(100% - 56px)'); const [height, setHeight] = useState('calc(100% - 56px)');
const editorRef = useRef(null);
console.log(language, code, '-------========----------')
useEffect(() => { useEffect(() => {
setHeight(showOrHideControl ? 'calc(100% - 378px)' : 'calc(100% - 56px)'); setHeight(showOrHideControl ? 'calc(100% - 378px)' : 'calc(100% - 56px)');
@ -71,21 +70,10 @@ function MyMonacoEditor(props, ref) {
setTheme(value); setTheme(value);
} }
// 文本框内容变化时,记录文本框内容
const handleEditorChange = (_, monaco) => {
editorRef.current = monaco; // 获取当前monaco实例
}
useEffect(() => { function onChangeHandler(ev, value) {
if (editorRef.current) { onCodeChange(value);
editorRef.current.onDidChangeModelContent(e => { // 监听编辑器内容的变化 }
const val = editorRef.current.getValue();
onCodeChange(val);
});
}
}, [
editorRef.current
])
// 配置编辑器属性 // 配置编辑器属性
const editorOptions = { const editorOptions = {
@ -152,17 +140,16 @@ function MyMonacoEditor(props, ref) {
<MyIcon className='code-icon' type="iconshezhi" onClick={handleShowDrawer} style={{ fontSize: '18px' }} /> <MyIcon className='code-icon' type="iconshezhi" onClick={handleShowDrawer} style={{ fontSize: '18px' }} />
</Tooltip> </Tooltip>
</div> </div>
<MonacoEditor <ControlledEditor
height={height} height={height}
width="100%" width="100%"
language={language && language.toLowerCase()} language={language && language.toLowerCase()}
value={code || ''} value={code || ''}
options={editorOptions} options={editorOptions}
theme={theme} // dark || light theme={theme} // dark || light
editorDidMount={handleEditorChange} onChange={onChangeHandler}
/> />
</div> </div>
<Drawer <Drawer
className={'setting_drawer'} className={'setting_drawer'}
placement="right" placement="right"
@ -176,6 +163,7 @@ function MyMonacoEditor(props, ref) {
/> />
<SettingDrawer {...opacitySetting} /> <SettingDrawer {...opacitySetting} />
</Drawer> </Drawer>
</React.Fragment> </React.Fragment>
) )
}; };

@ -1,12 +1,19 @@
.user_case_form,
.excute_result_area {
background-color: rgba(7, 15, 25, 1);
}
.monaco_editor_area { .monaco_editor_area {
height: 100%; height: 100%;
background-color: rgba(7, 15, 25, 1);
.code_title { .code_title {
display: flex; display: flex;
align-items: center; align-items: center;
background-color: rgba(18, 28, 36, 1);
color: #fff; color: #fff;
height: 56px; height: 56px;
background-color: rgba(18, 28, 36, 1);
padding: 0 20px; padding: 0 20px;
.flex_strict { .flex_strict {
@ -30,6 +37,17 @@
} }
} }
// .margin,
// .margin-view-overlays,
// .current-line{
// width: 40px !important;
// }
// .monaco-editor .margin-view-overlays .line-numbers{
// text-align: center;
// }
// .monaco-scrollable-element{
// left: 40px !important;
// }
} }
.setting_drawer { .setting_drawer {
@ -71,6 +89,13 @@
animation-iteration-count: 3; animation-iteration-count: 3;
} }
// .monaco-editor, .monaco-editor-background, .monaco-editor .inputarea.ime-input,
// .monaco-editor .margin,
// .minimap slider-mouseover,
// .minimap-decorations-layer{
// background:rgba(3,19,40,1) !important;
// }
@keyframes blink { @keyframes blink {
50% { 50% {
color: #fff; color: #fff;

@ -26,7 +26,7 @@ const { Option } = Select;
const maps = { const maps = {
language: [ language: [
{ title: 'C', key: 'C' }, { title: 'C', key: 'C' },
{ title: 'C++', key: 'C++' }, { title: 'C++', key: 'cpp' },
{ title: 'Python', key: 'Python' }, { title: 'Python', key: 'Python' },
{ title: 'Java', key: 'Java' } { title: 'Java', key: 'Java' }
], ],
@ -300,7 +300,6 @@ class EditTab extends React.Component {
{ header: 1 }, { header: 2 }, { header: 1 }, { header: 2 },
// {size: ['12px', '14px', '16px', '18px', '20px', false]}, // {size: ['12px', '14px', '16px', '18px', '20px', false]},
'bold', 'italic', 'underline', 'strike', // 切换按钮 'bold', 'italic', 'underline', 'strike', // 切换按钮
'blockquote', 'code-block', // 代码块
{ align: [] }, { 'list': 'ordered' }, { 'list': 'bullet' }, // 列表 { align: [] }, { 'list': 'ordered' }, { 'list': 'bullet' }, // 列表
{ 'script': 'sub' }, { 'script': 'super' }, { 'script': 'sub' }, { 'script': 'super' },
{ 'color': [] }, { 'background': [] }, // 字体颜色与背景色 { 'color': [] }, { 'background': [] }, // 字体颜色与背景色

@ -108,18 +108,12 @@ function StudentStudy(props) {
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'}>
<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> */}
<UserInfo userInfo={userInfo} /> <UserInfo userInfo={userInfo} />
<div className={'study_name'}> <div className={'study_name'}>
<span>{hack.name}</span> <span>{hack.name}</span>
</div> </div>
<div className={'study_quit'}> <div className={'study_quit'}>
{/* to={`/problems/${_hack_id}/edit`} */}
<span <span
style={{ display: userInfo.hack_manager ? 'inline-block' : 'none' }} style={{ display: userInfo.hack_manager ? 'inline-block' : 'none' }}
onClick={() => handleClickEditor(hack.identifier)} onClick={() => handleClickEditor(hack.identifier)}
@ -127,16 +121,9 @@ function StudentStudy(props) {
> >
<Icon type="form" className="quit-icon" /> 编辑 <Icon type="form" className="quit-icon" /> 编辑
</span> </span>
{/* to="/problems" */}
<span onClick={handleClickQuit} className="quit-btn"> <span onClick={handleClickQuit} className="quit-btn">
<Icon type="poweroff" className="quit-icon" /> 退出 <Icon type="poweroff" className="quit-icon" /> 退出
</span> </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> </div>
<div className="split-pane-area"> <div className="split-pane-area">

@ -19,7 +19,7 @@ import ErrorResult from '../../../components/errorResult';
const numberal = require('numeral'); const numberal = require('numeral');
const {reviewResult} = CONST; const { reviewResult } = CONST;
// 表格列 // 表格列
const columns = [ const columns = [
{ {
@ -35,7 +35,7 @@ const columns = [
dataIndex: 'status', dataIndex: 'status',
render: (value, record) => ( render: (value, record) => (
<Link to={`/myproblems/record_detail/${record.id}`}> <Link to={`/myproblems/record_detail/${record.id}`}>
<span style={{ color: value === 0 ? '#28BD8B' : '#E6262E'}}>{reviewResult[value]}</span> <span style={{ color: value === 0 ? '#28BD8B' : '#E6262E' }}>{reviewResult[value]}</span>
</Link> </Link>
) )
}, },
@ -72,28 +72,18 @@ const CommitRecord = (props) => {
identifier, identifier,
pages, pages,
commitRecord, commitRecord,
// excuteState,
language, language,
operateType,
commitRecordDetail, commitRecordDetail,
getUserCommitRecord, getUserCommitRecord,
changeRecordPagination changeRecordPagination
} = props; } = props;
const [current, setCurrent] = useState(1); const [current, setCurrent] = useState(pages.page);
// const [pagination, setPagination] = useState(paginationConfig);
// const [tableData, setTableData] = useState([]);
// 复制面板 // 复制面板
let clipboard; let clipboard;
// const [recordDetail, setRecordDetail] = useState({});
// const [renderCtx, setRenderCtx] = useState(() => {
// return function () {
// return '';
// }
// });
// 渲染提交记录详情 // 渲染提交记录详情
const renderRecordDetail = (commitRecordDetail = {}) => { const renderRecordDetail = (commitRecordDetail = {}) => {
const {id, status} = commitRecordDetail; const { id, status } = commitRecordDetail;
if (Object.keys(commitRecordDetail).length > 0) { if (Object.keys(commitRecordDetail).length > 0) {
// console.log('当前状态====》》》', status); // console.log('当前状态====》》》', status);
const classes = status === 0 ? 'record_result_suc' : 'record_result_err'; const classes = status === 0 ? 'record_result_suc' : 'record_result_err';
@ -105,22 +95,22 @@ const CommitRecord = (props) => {
<p className={'record_result'}> <p className={'record_result'}>
执行结果: <span className={classes}>{reviewResult[status]}</span> 执行结果: <span className={classes}>{reviewResult[status]}</span>
</p> </p>
<p <p
id="copyError" id="copyError"
onClick={clickCopyErrInfo} onClick={clickCopyErrInfo}
className={showErrorCopy} data-clipboard-target="#errcode"> className={showErrorCopy} data-clipboard-target="#errcode">
<span> <span>
复制错误信息 <Icon type="copy" className={'icon_style'}/> 复制错误信息 <Icon type="copy" className={'icon_style'} />
</span> </span>
</p> </p>
<p className={'show_detail'} style={{ visibility: id ? 'visible' : 'hidden' }}> <p className={'show_detail'} style={{ visibility: id ? 'visible' : 'hidden' }}>
<Link to={`/myproblems/record_detail/${id}`}> <Link to={`/myproblems/record_detail/${id}`}>
显示详情 <Icon type="right" className={'icon_style'}/> 显示详情 <Icon type="right" className={'icon_style'} />
</Link> </Link>
</p> </p>
</div> </div>
<div id="errcode" className={showErrorCode}> <div id="errcode" className={showErrorCode}>
<ErrorResult detail={commitRecordDetail} language={language}/> <ErrorResult detail={commitRecordDetail} language={language} />
</div> </div>
</React.Fragment> </React.Fragment>
); );
@ -131,7 +121,7 @@ const CommitRecord = (props) => {
// 根据id获取用户提交记录 // 根据id获取用户提交记录
useEffect(() => { useEffect(() => {
getUserCommitRecord(identifier); getUserCommitRecord(identifier);
}, []); }, [identifier]);
// 提交记录变化时,同步到表单数据 // 提交记录变化时,同步到表单数据
// useEffect(() => { // useEffect(() => {
// // const len = commitRecord.length; // // const len = commitRecord.length;
@ -149,9 +139,9 @@ const CommitRecord = (props) => {
// 复制功能 // 复制功能
let count = 0; let count = 0;
// useEffect(() => { // useEffect(() => {
// }, []); // }, []);
const clickCopyErrInfo = () => { const clickCopyErrInfo = () => {
count = 0; count = 0;
if (!clipboard) { if (!clipboard) {
@ -174,76 +164,27 @@ const CommitRecord = (props) => {
changeRecordPagination(page); changeRecordPagination(page);
// 调用查询接口 // 调用查询接口
getUserCommitRecord(identifier); getUserCommitRecord(identifier);
// setPagination(Object.assign({}, pagination, { current: page}));
// console.log('======>>>>>>', pagination)
} }
// console.log(commitRecord);
const _style = { const _style = {
display: pages.total > pages.limit ? 'block' : 'none' display: pages.total > pages.limit ? 'block' : 'none'
}; };
const {status, id} = commitRecordDetail || {};
const classes = status === 0 ? 'record_result_suc' : 'record_result_err';
const showErrorCode = status !== 0 ? `ecord_error_info show_error_code` : `ecord_error_info`;
const showErrorCopy = status !== 0 ? `copy_error show_error_copy` : `copy_error`;
// if (!clipboard) {
// console.log('==========>>>>>>>', 11111111111);
// clipboard = new ClipboardJS('#copyError');
// }
// clipboard.on('success', (e) => {
// e.clearSelection();
// // if (count > 0) return;
// // count++;
// message.success('复制成功');
// setTimeout(() => {
// message.destroy();
// }, 3000);
// });
// return () => {
// clipboard = null;
// }
return ( return (
<div className={'commit_record_area'}> <div className={'commit_record_area'}>
{renderRecordDetail(commitRecordDetail)} {renderRecordDetail(commitRecordDetail)}
{/* <div className={'record_header'}>
<p className={'record_result'}>
执行结果: <span className={classes}>{reviewResult[status]}</span>
</p>
<p
id="copyError"
onClick={clickCopyErrInfo}
className={showErrorCopy} data-clipboard-target="#errcode">
<span>
复制错误信息 <Icon type="copy" className={'icon_style'}/>
</span>
</p>
<p className={'show_detail'} style={{ visibility: id ? 'visible' : 'hidden' }}>
<Link to={`/myproblems/record_detail/${id}`}>
显示详情 <Icon type="right" className={'icon_style'}/>
</Link>
</p>
</div>
<div id="errcode" className={showErrorCode}>
<ErrorResult detail={commitRecordDetail} language={language}/>
</div> */}
<div className="commit_record_table_pagination"> <div className="commit_record_table_pagination">
<Table <Table
columns={columns} columns={columns}
rowKey={function (record) { return `key_${record.id}`}} rowKey={function (record) { return `key_${record.id}` }}
dataSource={commitRecord} dataSource={commitRecord}
// pagination={pagination}
// onChange={handleTableChange}
pagination={false} pagination={false}
/> />
<div className="commit_record_pagination" style={_style}> <div className="commit_record_pagination" style={_style}>
<Pagination <Pagination
showQuickJumper showQuickJumper
pageSize={pages.limit} pageSize={pages.limit}
current={current} current={current}
total={pages.total} total={pages.total}
onChange={handlePaginationChange} onChange={handlePaginationChange}
/> />

@ -57,7 +57,7 @@ const RightPane = (props) => {
timer = setInterval(function () { timer = setInterval(function () {
clearInterval(timer); clearInterval(timer);
timer = null; timer = null;
saveUserCodeForInterval(identifier); saveUserCodeForInterval(identifier, value);
}, 5000); }, 5000);
} }
} }
@ -99,13 +99,17 @@ const RightPane = (props) => {
}); });
} }
const { getFieldDecorator } = props.form; const { getFieldDecorator } = props.form;
let language = hack.language
if (language === 'C++') {
language = 'cpp'
}
return ( return (
<div className={'right_pane_code_wrap'}> <div className={'right_pane_code_wrap'}>
<MyMonacoEditor <MyMonacoEditor
notice={notice} notice={notice}
identifier={identifier} identifier={identifier}
language={hack.language} language={language}
code={editor_code || hack.code} code={editor_code || hack.code}
hadCodeUpdate={hadCodeUpdate} hadCodeUpdate={hadCodeUpdate}
onCodeChange={handleCodeChange} onCodeChange={handleCodeChange}
@ -118,9 +122,6 @@ const RightPane = (props) => {
onClick={handleClickNote} onClick={handleClickNote}
></span> ></span>
{/* <div className="student_notes">
<TextArea rows={5} />
</div> */}
<div className={noteClazz}> <div className={noteClazz}>
<Form> <Form>
<FormItem> <FormItem>

@ -102,13 +102,12 @@ export const getUserProgramDetail = (identifier, type) => {
export const saveUserCodeForInterval = (identifier, code) => { export const saveUserCodeForInterval = (identifier, code) => {
return (dispatch, getState) => { return (dispatch, getState) => {
const { userCode } = getState().ojForUserReducer;
dispatch({ dispatch({
type: types.AUTO_UPDATE_CODE, type: types.AUTO_UPDATE_CODE,
payload: true payload: true
}); });
fetchUpdateCode(identifier, { fetchUpdateCode(identifier, {
code: Base64.encode(userCode) code: Base64.encode(code)
}).then(res => { }).then(res => {
if (res.data.status === 401) { if (res.data.status === 401) {
return; return;
@ -134,29 +133,28 @@ export const saveUserCodeForInterval = (identifier, code) => {
* @param {*} inputValue 输入值: 自定义 | 系统返回的 * @param {*} inputValue 输入值: 自定义 | 系统返回的
* @param {*} type 测评类型 debug | submit * @param {*} type 测评类型 debug | submit
*/ */
//原来的方法未能区分从编辑入口进来的情况,这时代码也是更新了的。
export const updateCode = (identifier, inputValue, type) => { export const updateCode = (identifier, inputValue, type) => {
console.log(1111);
return (dispatch, getState) => { return (dispatch, getState) => {
const { userCode, isUpdateCode } = getState().ojForUserReducer; const { editor_code, isUpdateCode } = getState().ojForUserReducer;
if (isUpdateCode) { if (isUpdateCode) {
fetchUpdateCode(identifier, { fetchUpdateCode(identifier, {
code: Base64.encode(userCode) code: Base64.encode(editor_code)
}).then(res => { }).then(res => {
// 是否更新了代码, 目的是当代码没有更新时不调用更新代码接口,目录没有实现 if (res) {
// TODO 需要优化 if (res.data.status === 401) {
if (res.data.status === 401) { dispatch({ // 改变 loading 值
dispatch({ // 改变 loading 值 type: types.LOADING_STATUS,
type: types.LOADING_STATUS, payload: false
payload: false });
return;
};
dispatch({
type: types.IS_UPDATE_CODE,
flag: false
}); });
return; dispatch(debuggerCode(identifier, inputValue, type));
}; }
dispatch({
type: types.IS_UPDATE_CODE,
flag: false
});
// debuggerCode(identifier, inputValue);
dispatch(debuggerCode(identifier, inputValue, type));
}); });
} else { } else {
// 没有更新时,直接调用调试接口 // 没有更新时,直接调用调试接口
@ -394,8 +392,7 @@ export const changeUserCodeTab = (key) => {
*/ */
export const submitUserCode = (identifier, inputValue, type) => { export const submitUserCode = (identifier, inputValue, type) => {
return (dispatch, getState) => { return (dispatch, getState) => {
const { userCode, isUpdateCode, hack } = getState().ojForUserReducer; const { editor_code, isUpdateCode, hack } = getState().ojForUserReducer;
function userCodeSubmit() { function userCodeSubmit() {
fetchUserCodeSubmit(identifier).then(res => { fetchUserCodeSubmit(identifier).then(res => {
if (res.status === 200) { if (res.status === 200) {
@ -418,7 +415,7 @@ export const submitUserCode = (identifier, inputValue, type) => {
} }
if (isUpdateCode) { if (isUpdateCode) {
fetchUpdateCode(identifier, { fetchUpdateCode(identifier, {
code: Base64.encode(userCode) code: Base64.encode(editor_code)
}).then(res => { }).then(res => {
// 是否更新了代码, 目的是当代码没有更新时不调用更新代码接口,目录没有实现 // 是否更新了代码, 目的是当代码没有更新时不调用更新代码接口,目录没有实现
if (res.data.status === 401) { if (res.data.status === 401) {

@ -132,6 +132,7 @@ export const validateOjForm = (props, type, cb) => {
} }
}); });
// 验证测试用例中的数组是否都有对应的值 // 验证测试用例中的数组是否都有对应的值
let tcbool=false;
const tcValidResult = []; const tcValidResult = [];
// 验证测试用例: 1.必须要有输出值 2. 输入值与输出值必须唯一 // 验证测试用例: 1.必须要有输出值 2. 输入值与输出值必须唯一
testCases.forEach((obj, i) => { testCases.forEach((obj, i) => {
@ -141,8 +142,25 @@ export const validateOjForm = (props, type, cb) => {
// 非空校验 // 非空校验
let validateResult = emptyValidate(key, value); let validateResult = emptyValidate(key, value);
const errMsg = validateResult[key].errMsg; const errMsg = validateResult[key].errMsg;
if (errMsg) { // console.log("-------");
// hasSuccess = false; // console.log(errMsg);
// console.log(key);
// console.log(value);
if (errMsg) {
if(key==="output"){
hasSuccess = false;
if(tcbool===false){
tcbool=true
notification['error']({
message: '提示',
description: '测试用例必须填写输出!'
});
}
}
} else { } else {
// 唯一性校验 // 唯一性校验
const bool = testCases.some((item, j) => { const bool = testCases.some((item, j) => {
@ -167,39 +185,7 @@ export const validateOjForm = (props, type, cb) => {
}); });
tcValidResult.push(tempObj); tcValidResult.push(tempObj);
}); });
try {
if(ojForm.sub_discipline_id.length===0){
hasSuccess = false;
notification['error']({
message: '提示',
description: '课程必须选择!'
});
}else if(ojForm.timeLimit===null){
hasSuccess = false;
notification['error']({
message: '提示',
description: '时间限制必须输入!'
});
} else if(ojForm.name.length===0){
hasSuccess = false;
notification['error']({
message: '提示',
description: '任务名称必须输入!'
});
}else if(ojForm.description.length===0){
hasSuccess = false;
notification['error']({
message: '提示',
description: '描述必须输入!'
});
}
}catch (e) {
}
// if (testCases.length === 0) { // if (testCases.length === 0) {
// hasSuccess = false; // hasSuccess = false;
@ -234,6 +220,43 @@ export const validateOjForm = (props, type, cb) => {
payload: false payload: false
}); });
} }
try {
if(ojForm.sub_discipline_id.length===0){
hasSuccess = false;
notification['error']({
message: '提示',
description: '课程必须选择!'
});
}else if(ojForm.timeLimit===null){
hasSuccess = false;
notification['error']({
message: '提示',
description: '时间限制必须输入!'
});
} else if(ojForm.name.length===0){
hasSuccess = false;
notification['error']({
message: '提示',
description: '任务名称必须输入!'
});
}else if(ojForm.description.length===0){
hasSuccess = false;
notification['error']({
message: '提示',
description: '描述必须输入!'
});
}
}catch (e) {
}
/** 表单验证结束 */ /** 表单验证结束 */
/** 表单验证通过后,调用保存 or 更新 or 发布 */ /** 表单验证通过后,调用保存 or 更新 or 发布 */
if (hasSuccess) { if (hasSuccess) {

@ -98,7 +98,7 @@ const ojForUserReducer = (state = initialState, action) => {
// let curCode = Base64.encode(action.payload); // let curCode = Base64.encode(action.payload);
return { return {
...state, ...state,
userCode: action.payload, editor_code: action.payload,
isUpdateCode: true, isUpdateCode: true,
} }
case types.IS_UPDATE_CODE: case types.IS_UPDATE_CODE:
@ -151,7 +151,8 @@ const ojForUserReducer = (state = initialState, action) => {
case types.SAVE_EDITOR_CODE: case types.SAVE_EDITOR_CODE:
return { return {
...state, ...state,
editor_code: action.payload editor_code: action.payload,
isUpdateCode: true
} }
case types.SAVE_USE_TEST_CASE_VALUE: case types.SAVE_USE_TEST_CASE_VALUE:
return { return {

Loading…
Cancel
Save