diff --git a/.gitignore b/.gitignore index 6a4516d20..3c9f5950e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ # Ignore bundler config. /.bundle +/bundle # Ignore lock config file *.lock @@ -71,3 +72,13 @@ vendor/bundle/ /workspace /log /public/admin +/mysql_data + + +.generators +.rakeTasks +db/bak/ +docker/ +educoder.sql +redis_data/ +Dockerfile \ No newline at end of file diff --git a/Gemfile b/Gemfile index b4a30658d..6757a157f 100644 --- a/Gemfile +++ b/Gemfile @@ -108,3 +108,6 @@ gem 'diffy' # oauth2 gem 'omniauth', '~> 1.9.0' gem 'omniauth-oauth2', '~> 1.6.0' + +# global var +gem 'request_store' diff --git a/app/assets/javascripts/admins/courses/index.js b/app/assets/javascripts/admins/courses/index.js index 336d8c7c9..0e3473dff 100644 --- a/app/assets/javascripts/admins/courses/index.js +++ b/app/assets/javascripts/admins/courses/index.js @@ -26,6 +26,15 @@ $(document).on('turbolinks:load', function() { }); }); + // 清空 + searchForm.on('click', '.clear-btn', function () { + searchForm.find('select[name="status"]').val(''); + searchForm.find('.school-select').val('').trigger('change'); + searchForm.find('input[name="keyword"]').val(''); + searchForm.find('#homepage_show').attr('checked', false); + searchForm.find('input[type="submit"]').trigger('click'); + }); + // ************** 学校选择 ************* searchForm.find('.school-select').select2({ theme: 'bootstrap4', diff --git a/app/assets/javascripts/admins/projects/index.js b/app/assets/javascripts/admins/projects/index.js new file mode 100644 index 000000000..534a065ca --- /dev/null +++ b/app/assets/javascripts/admins/projects/index.js @@ -0,0 +1,11 @@ +$(document).on('turbolinks:load', function () { + if ($('body.admins-projects-index-page').length > 0) { + var $form = $('.search-form'); + + // 清空 + $form.on('click', '.clear-btn', function () { + $form.find('input[name="search"]').val(''); + $form.find('input[type="submit"]').trigger('click'); + }); + } +}); diff --git a/app/assets/javascripts/admins/shixun_feedback_messages/index.js b/app/assets/javascripts/admins/shixun_feedback_messages/index.js index c0b32ba32..c25a6b744 100644 --- a/app/assets/javascripts/admins/shixun_feedback_messages/index.js +++ b/app/assets/javascripts/admins/shixun_feedback_messages/index.js @@ -1,12 +1,14 @@ $(document).on('turbolinks:load', function(){ if ($('body.admins-shixun-feedback-messages-index-page').length > 0) { + $(".content-img img").addClass("w-20").addClass("preview-image"); + var baseOptions = { autoclose: true, language: 'zh-CN', format: 'yyyy-mm-dd 00:00:00', startDate: '2017-04-01' - } + }; var defineDateRangeSelect = function(element){ var options = $.extend({inputs: $(element).find('.start-date, .end-date')}, baseOptions); @@ -14,9 +16,9 @@ $(document).on('turbolinks:load', function(){ $(element).find('.start-date').datepicker().on('changeDate', function(e){ $(element).find('.end-date').datepicker('setStartDate', e.date); - }) + }); }; defineDateRangeSelect('.grow-date-input-daterange'); } -}) \ No newline at end of file +}); \ No newline at end of file diff --git a/app/assets/javascripts/admins/shixun_modify_records/index.js b/app/assets/javascripts/admins/shixun_modify_records/index.js new file mode 100644 index 000000000..13b348b04 --- /dev/null +++ b/app/assets/javascripts/admins/shixun_modify_records/index.js @@ -0,0 +1,12 @@ +$(document).on('turbolinks:load', function () { + if ($('body.admins-shixun-modify-records-index-page').length > 0) { + var $form = $('.search-form'); + + // 清空 + $form.on('click', '.clear-btn', function () { + $form.find('select[name="date"]').val('weekly'); + $form.find('input[name="user_name"]').val(''); + $form.find('input[type="submit"]').trigger('click'); + }); + } +}); diff --git a/app/assets/stylesheets/admins/diff.scss b/app/assets/stylesheets/admins/diff.scss new file mode 100644 index 000000000..34944ae4c --- /dev/null +++ b/app/assets/stylesheets/admins/diff.scss @@ -0,0 +1,14 @@ +.diff{overflow:auto;} +.diff ul{background:#fff;overflow:auto;font-size:13px;list-style:none;margin:0;padding:0 1rem;display:table;width:100%;} +.diff del, .diff ins{display:block;text-decoration:none;} +.diff li{padding:0; display:table-row;margin: 0;height:1em;} +.diff li.ins{background:#dfd; color:#080} +.diff li.del{background:#fee; color:#b00} +.diff li:hover{background:#ffc} + +/* try 'whitespace:pre;' if you don't want lines to wrap */ +.diff del, .diff ins, .diff span{white-space:pre-wrap;font-family:courier;} +.diff del strong{font-weight:normal;background:#fcc;} +.diff ins strong{font-weight:normal;background:#9f9;} +.diff li.diff-comment { display: none; } +.diff li.diff-block-info { background: none repeat scroll 0 0 gray; } \ No newline at end of file diff --git a/app/assets/stylesheets/admins/shixun_feedback_messages.scss b/app/assets/stylesheets/admins/shixun_feedback_messages.scss new file mode 100644 index 000000000..92b92c01c --- /dev/null +++ b/app/assets/stylesheets/admins/shixun_feedback_messages.scss @@ -0,0 +1,7 @@ +.admins-shixun-feedback-messages-index-page { + .content-img { + img { + height: 60px; + } + } +} \ No newline at end of file diff --git a/app/constraint/admin_constraint.rb b/app/constraint/admin_constraint.rb index 3ddf9a11e..2cf5649a7 100644 --- a/app/constraint/admin_constraint.rb +++ b/app/constraint/admin_constraint.rb @@ -1,7 +1,8 @@ class AdminConstraint def matches?(request) - return false unless request.session[:user_id] - user = User.find request.session[:user_id] + laboratory = Laboratory.first + return false unless request.session[:"#{laboratory.try(:identifier).split('.').first}_user_id"] + user = User.find request.session[:"#{laboratory.try(:identifier).split('.').first}_user_id"] user && user.admin? end end \ No newline at end of file diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb index eab3c34a3..4525b0deb 100644 --- a/app/controllers/accounts_controller.rb +++ b/app/controllers/accounts_controller.rb @@ -69,6 +69,8 @@ class AccountsController < ApplicationController return normal_status(-2, "验证码已失效") if !verifi_code&.effective? end + return normal_status(-1, "8~16位密码,支持字母数字和符号") unless params[:password] =~ CustomRegexp::PASSWORD + code = generate_identifier User, 8, pre login = pre + code @user = User.new(admin: false, login: login, mail: email, phone: phone, type: "User") @@ -145,6 +147,7 @@ class AccountsController < ApplicationController end return normal_status(-2, "验证码不正确") if verifi_code.try(:code) != code.strip return normal_status(-2, "验证码已失效") if !verifi_code&.effective? + return normal_status(-1, "8~16位密码,支持字母数字和符号") unless params[:new_password] =~ CustomRegexp::PASSWORD user.password, user.password_confirmation = params[:new_password], params[:new_password_confirmation] ActiveRecord::Base.transaction do @@ -154,7 +157,7 @@ class AccountsController < ApplicationController sucess_status rescue Exception => e uid_logger_error(e.message) - tip_exception("密码重置失败") + tip_exception(e.message) end end diff --git a/app/controllers/admins/daily_school_statistics_controller.rb b/app/controllers/admins/daily_school_statistics_controller.rb index eb9c75fbc..f0911f239 100644 --- a/app/controllers/admins/daily_school_statistics_controller.rb +++ b/app/controllers/admins/daily_school_statistics_controller.rb @@ -6,6 +6,7 @@ class Admins::DailySchoolStatisticsController < Admins::BaseController total_count, statistics = Admins::SchoolDailyStatisticService.call(params) @statistics = paginate statistics, total_count: total_count + @params_page = params[:page] || 1 respond_to do |format| format.html { load_statistic_total } diff --git a/app/controllers/admins/myshixuns_controller.rb b/app/controllers/admins/myshixuns_controller.rb index f70a64554..adc34e95e 100644 --- a/app/controllers/admins/myshixuns_controller.rb +++ b/app/controllers/admins/myshixuns_controller.rb @@ -6,6 +6,7 @@ class Admins::MyshixunsController < Admins::BaseController myshixuns = Admins::MyshixunQuery.call(params) @myshixuns = paginate myshixuns.includes(:last_executable_task, :last_task, shixun: :user, user: { user_extension: :school }) + @params_page = params[:page] || 1 myshixun_ids = @myshixuns.map(&:id) @finish_game_count = Game.where(myshixun_id: myshixun_ids, status: 2).group(:myshixun_id).count diff --git a/app/controllers/admins/school_statistics_controller.rb b/app/controllers/admins/school_statistics_controller.rb index fdd10c70f..5133cc997 100644 --- a/app/controllers/admins/school_statistics_controller.rb +++ b/app/controllers/admins/school_statistics_controller.rb @@ -10,6 +10,7 @@ class Admins::SchoolStatisticsController < Admins::BaseController @grow_summary = service.grow_summary total_count, statistics = service.call + @params_page = params[:page] || 1 @statistics = paginate statistics, total_count: total_count end diff --git a/app/controllers/admins/schools_controller.rb b/app/controllers/admins/schools_controller.rb index 7e1626d98..8c4f1d59e 100644 --- a/app/controllers/admins/schools_controller.rb +++ b/app/controllers/admins/schools_controller.rb @@ -5,7 +5,7 @@ class Admins::SchoolsController < Admins::BaseController schools = Admins::SchoolQuery.call(params) - @schools = paginate schools.includes(:user_extensions) + @schools = paginate schools school_ids = @schools.map(&:id) @department_count = Department.where(school_id: school_ids).group(:school_id).count diff --git a/app/controllers/admins/shixun_modify_records_controller.rb b/app/controllers/admins/shixun_modify_records_controller.rb new file mode 100644 index 000000000..43d9a4a16 --- /dev/null +++ b/app/controllers/admins/shixun_modify_records_controller.rb @@ -0,0 +1,9 @@ +class Admins::ShixunModifyRecordsController < Admins::BaseController + + def index + records = Admins::ShixunModifyRecordQuery.call(params) + + @records = paginate records.includes(:diff_record_content) + end + +end diff --git a/app/controllers/admins/shixun_settings_controller.rb b/app/controllers/admins/shixun_settings_controller.rb index bd54018cd..6dbbc077a 100644 --- a/app/controllers/admins/shixun_settings_controller.rb +++ b/app/controllers/admins/shixun_settings_controller.rb @@ -15,7 +15,8 @@ class Admins::ShixunSettingsController < Admins::BaseController hidden: params[:hidden].present? ? params[:hidden] : false, homepage_show: params[:homepage_show].present? ? params[:homepage_show] : false, task_pass: params[:task_pass].present? ? params[:task_pass] : false, - code_hidden: params[:code_hidden].present? ? params[:code_hidden] : false + code_hidden: params[:code_hidden].present? ? params[:code_hidden] : false, + vip: params[:vip].present? ? params[:vip] : false } @shixuns_type_check = MirrorRepository.pluck(:type_name,:id) @@ -126,6 +127,6 @@ class Admins::ShixunSettingsController < Admins::BaseController end def setting_params - params.permit(:use_scope,:excute_time,:close,:status,:can_copy,:webssh,:hidden,:homepage_show,:task_pass,:code_hidden,:page_no, :id,tag_repertoires:[]) + params.permit(:use_scope,:excute_time,:close,:status,:can_copy,:webssh,:hidden,:homepage_show,:task_pass,:code_hidden,:vip,:page_no,:id,tag_repertoires:[]) end end diff --git a/app/controllers/admins/shixuns_controller.rb b/app/controllers/admins/shixuns_controller.rb index e2d2830ad..6593f27c2 100644 --- a/app/controllers/admins/shixuns_controller.rb +++ b/app/controllers/admins/shixuns_controller.rb @@ -8,6 +8,9 @@ class Admins::ShixunsController < Admins::BaseController @pending_shixuns = shixuns.where(status:1).size @processed_shixuns = shixuns.where(status:2).size @closed_shixuns = shixuns.where(status:3).size + @none_public_shixuns = shixuns.where(public:0).size + @pending_public_shixuns = shixuns.where(public:1).size + @processed_pubic_shixuns = shixuns.where(public:2).size @shixuns_type_check = MirrorRepository.pluck(:type_name,:id) @params_page = params[:page] || 1 @shixuns = paginate shixuns.preload(:user,:challenges) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index b00778958..5eb272a62 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -283,7 +283,7 @@ class ApplicationController < ActionController::Base # 测试版前端需求 logger.info("subdomain:#{request.subdomain}") - if request.subdomain == "test-newweb" + if request.subdomain != "www" if params[:debug] == 'teacher' #todo 为了测试,记得讲debug删除 User.current = User.find 81403 elsif params[:debug] == 'student' @@ -303,7 +303,7 @@ class ApplicationController < ActionController::Base current_domain_session = session[:"#{default_yun_session}"] if current_domain_session # existing session - (User.active.find(current_domain_session) rescue nil) + User.current = (User.active.find(current_domain_session) rescue nil) elsif autologin_user = try_to_autologin autologin_user elsif params[:format] == 'atom' && params[:key] && request.get? && accept_rss_auth? @@ -465,9 +465,9 @@ class ApplicationController < ActionController::Base # 实训主类别列表,自带描述 def shixun_main_type list = [] - mirrors = MirrorRepository.select([:id, :type_name, :description]).published_main_mirror + mirrors = MirrorRepository.select([:id, :type_name, :description, :name]).published_main_mirror mirrors.try(:each) do |mirror| - list << {id: mirror.id, type_name: mirror.type_name, description: mirror.try(:description)} + list << {id: mirror.id, type_name: mirror.type_name, description: mirror.try(:description), mirror_name: mirror.name} end list end @@ -475,9 +475,9 @@ class ApplicationController < ActionController::Base # 小类别列表 def shixun_small_type list = [] - mirrors = MirrorRepository.select([:id, :type_name, :description]).published_small_mirror + mirrors = MirrorRepository.select([:id, :type_name, :description, :name]).published_small_mirror mirrors.try(:each) do |mirror| - list << {id: mirror.id, type_name: mirror.type_name, description: mirror.description} + list << {id: mirror.id, type_name: mirror.type_name, description: mirror.description, mirror_name: mirror.name} end list end diff --git a/app/controllers/attachments_controller.rb b/app/controllers/attachments_controller.rb index 0c9c49621..5fc81c5d5 100644 --- a/app/controllers/attachments_controller.rb +++ b/app/controllers/attachments_controller.rb @@ -18,9 +18,9 @@ class AttachmentsController < ApplicationController pdf_attachment = params[:disposition] || "attachment" if pdf_attachment == "inline" - send_file absolute_path(local_path(@file)),filename: @file.filename, disposition: 'inline',type: 'application/pdf' + send_file absolute_path(local_path(@file)),filename: @file.title, disposition: 'inline',type: 'application/pdf' else - send_file(absolute_path(local_path(@file)), filename: @file.filename,stream:false, type: @file.content_type.presence || 'application/octet-stream') + send_file(absolute_path(local_path(@file)), filename: @file.title,stream:false, type: @file.content_type.presence || 'application/octet-stream') end update_downloads(@file) end @@ -28,52 +28,56 @@ class AttachmentsController < ApplicationController def create # 1. 本地存储 # 2. 上传到云 - upload_file = params["file"] || params["#{params[:file_param_name]}"] # 这里的file_param_name是为了方便其他插件名称 - uid_logger("#########################file_params####{params["#{params[:file_param_name]}"]}") - raise "未上传文件" unless upload_file - - folder = edu_setting('attachment_folder') - raise "存储目录未定义" unless folder.present? - - month_folder = current_month_folder - save_path = File.join(folder, month_folder) - - ext = file_ext(upload_file.original_filename) - - local_path, digest = file_save_to_local(save_path, upload_file.tempfile, ext) - - content_type = upload_file.content_type.presence || 'application/octet-stream' - - # remote_path = file_save_to_ucloud(local_path[folder.size, local_path.size], local_path, content_type) - remote_path = nil # TODO 暂时本地上传,待域名配置后方可上传至云端 - - logger.info "local_path: #{local_path}" - logger.info "remote_path: #{remote_path}" - - - disk_filename = local_path[save_path.size + 1, local_path.size] - #存数据库 - # - @attachment = Attachment.where(disk_filename: disk_filename, - author_id: current_user.id, - cloud_url: remote_path).first + begin + upload_file = params["file"] || params["#{params[:file_param_name]}"] # 这里的file_param_name是为了方便其他插件名称 + uid_logger("#########################file_params####{params["#{params[:file_param_name]}"]}") + raise "未上传文件" unless upload_file + + folder = edu_setting('attachment_folder') + raise "存储目录未定义" unless folder.present? + + month_folder = current_month_folder + save_path = File.join(folder, month_folder) + + ext = file_ext(upload_file.original_filename) + + local_path, digest = file_save_to_local(save_path, upload_file.tempfile, ext) + + content_type = upload_file.content_type.presence || 'application/octet-stream' + + # remote_path = file_save_to_ucloud(local_path[folder.size, local_path.size], local_path, content_type) + remote_path = nil # TODO 暂时本地上传,待域名配置后方可上传至云端 + + logger.info "local_path: #{local_path}" + logger.info "remote_path: #{remote_path}" + + + disk_filename = local_path[save_path.size + 1, local_path.size] + #存数据库 + # + @attachment = Attachment.where(disk_filename: disk_filename, + author_id: current_user.id, + cloud_url: remote_path).first + if @attachment.blank? + @attachment = Attachment.new + @attachment.filename = upload_file.original_filename + @attachment.disk_filename = local_path[save_path.size + 1, local_path.size] + @attachment.filesize = upload_file.tempfile.size + @attachment.content_type = content_type + @attachment.digest = digest + @attachment.author_id = current_user.id + @attachment.disk_directory = month_folder + @attachment.cloud_url = remote_path + @attachment.save! + else + logger.info "文件已存在,id = #{@attachment.id}, filename = #{@attachment.filename}" + end - if @attachment.blank? - @attachment = Attachment.new - @attachment.filename = upload_file.original_filename - @attachment.disk_filename = local_path[save_path.size + 1, local_path.size] - @attachment.filesize = upload_file.tempfile.size - @attachment.content_type = content_type - @attachment.digest = digest - @attachment.author_id = current_user.id - @attachment.disk_directory = month_folder - @attachment.cloud_url = remote_path - @attachment.save! - else - logger.info "文件已存在,id = #{@attachment.id}, filename = #{@attachment.filename}" + render_json + rescue => e + uid_logger_error(e.message) + tip_exception(e.message) end - - render_json end def destroy @@ -196,4 +200,5 @@ class AttachmentsController < ApplicationController end end end + end diff --git a/app/controllers/challenges_controller.rb b/app/controllers/challenges_controller.rb index 0b5140743..354d63f6d 100644 --- a/app/controllers/challenges_controller.rb +++ b/app/controllers/challenges_controller.rb @@ -13,6 +13,9 @@ class ChallengesController < ApplicationController include ShixunsHelper include ChallengesHelper + + + # 新建实践题 def new @position = @shixun.challenges.count + 1 @@ -160,6 +163,8 @@ class ChallengesController < ApplicationController @shixun.increment!(:visits) end + + def show @tab = params[:tab].nil? ? 1 : params[:tab].to_i challenge_num = @shixun.challenges_count @@ -173,64 +178,70 @@ class ChallengesController < ApplicationController # tab 0:过关任务的更新; 1:评测设置的更新; 2:表示参考答案的更新; def update - ActiveRecord::Base.transaction do - tab = params[:tab].to_i - @challenge.update_attributes(challenge_params) - if tab == 0 && @challenge.st == 0 - @challenge.challenge_tags.delete_all - if params[:challenge_tag].present? - params[:challenge_tag].each do |input| - ChallengeTag.create!(:name => input, :challenge_id => @challenge.id) + begin + ActiveRecord::Base.transaction do + tab = params[:tab].to_i + @challenge.update_attributes(challenge_params) + if tab == 0 && @challenge.st == 0 + @challenge.challenge_tags.delete_all + if params[:challenge_tag].present? + params[:challenge_tag].each do |input| + ChallengeTag.create!(:name => input, :challenge_id => @challenge.id) + end end - end - elsif tab == 1 - path = @challenge.path - exec_path = @challenge.exec_path - test_set = @challenge.test_sets - sets_output = test_set.map(&:output) - sets_input = test_set.map(&:input) - sets_open = test_set.map(&:is_public) - set_score = test_set.map(&:score) - set_match_rule = test_set.map(&:match_rule) - params_hidden = params[:test_set].map{|set| set[:hidden].to_i == 0} - params_output = params[:test_set].map{|set| set[:output] } - params_input = params[:test_set].map{|set| set[:input] } - params_score = params[:test_set].map{|set| set[:score]} - params_test_set = params[:test_set].map{|set| set[:match_rule]} - # 测试集变化则需要更新(输入、 输出、 是否隐藏) - if sets_output != params_output || sets_open != params_hidden || sets_input != params_input || - set_score != params_score || params_test_set != set_match_rule - test_set.delete_all unless test_set.blank? - params[:test_set].each_with_index do |set, index| - # last: 末尾匹配, full: 全完匹配 - logger.info("set: #{set}; match_rule : #{set[:match_rule]}") - match_rule = set[:match_rule] == 'last' ? 'last' : 'full' - TestSet.create(:challenge_id => @challenge.id, - :input => "#{set[:input]}", - :output => "#{set[:output]}", - :is_public => params_hidden[index], - :score => set[:score], - :match_rule => "#{match_rule}", - :position => (index + 1)) + elsif tab == 1 + path = @challenge.path + exec_path = @challenge.exec_path + test_set = @challenge.test_sets + sets_output = test_set.map(&:output) + sets_input = test_set.map(&:input) + sets_open = test_set.map(&:is_public) + set_score = test_set.map(&:score) + set_match_rule = test_set.map(&:match_rule) + params_hidden = params[:test_set].map{|set| set[:hidden].to_i == 0} + params_output = params[:test_set].map{|set| set[:output] } + params_input = params[:test_set].map{|set| set[:input] } + params_score = params[:test_set].map{|set| set[:score]} + params_test_set = params[:test_set].map{|set| set[:match_rule]} + # 测试集变化则需要更新(输入、 输出、 是否隐藏) + if sets_output != params_output || sets_open != params_hidden || sets_input != params_input || + set_score != params_score || params_test_set != set_match_rule + test_set.delete_all unless test_set.blank? + params[:test_set].each_with_index do |set, index| + # last: 末尾匹配, full: 全完匹配 + logger.info("set: #{set}; match_rule : #{set[:match_rule]}") + match_rule = set[:match_rule] == 'last' ? 'last' : 'full' + TestSet.create(:challenge_id => @challenge.id, + :input => "#{set[:input]}", + :output => "#{set[:output]}", + :is_public => params_hidden[index], + :score => set[:score], + :match_rule => "#{match_rule}", + :position => (index + 1)) + end + @challenge.update_column(:modify_time, Time.now) + # 测试集的 + @shixun.myshixuns.update_all(:system_tip => 0) end - @challenge.update_column(:modify_time, Time.now) - # 测试集的 - @shixun.myshixuns.update_all(:system_tip => 0) - end - if params[:challenge][:show_type].to_i == -1 - @challenge.update_attributes(picture_path: nil, web_route: nil, expect_picture_path: nil, original_picture_path: nil) + if params[:challenge][:show_type].to_i == -1 + @challenge.update_attributes(picture_path: nil, web_route: nil, expect_picture_path: nil, original_picture_path: nil) + end + # 关卡评测执行文件如果被修改,需要修改脚本内容 + script = modify_shixun_script @shixun, @shixun.evaluate_script + @shixun.shixun_info.update_column(:evaluate_script, script) + # TODO: + # if path != params[:challenge][:path] + # shixun_modify_status_without_publish(@shixun, 1) + # end + #Attachment.attach_files(@challenge, params[:attachments]) end - # 关卡评测执行文件如果被修改,需要修改脚本内容 - script = modify_shixun_script @shixun, @shixun.evaluate_script - @shixun.shixun_info.update_column(:evaluate_script, script) - # TODO: - # if path != params[:challenge][:path] - # shixun_modify_status_without_publish(@shixun, 1) - # end - #Attachment.attach_files(@challenge, params[:attachments]) - end + end + rescue => e + logger_error("##update_challenges: ##{e.message}") + tip_exception("更新失败!") end + end # 参考答案的'增,删,改' diff --git a/app/controllers/colleges_controller.rb b/app/controllers/colleges_controller.rb new file mode 100644 index 000000000..707255866 --- /dev/null +++ b/app/controllers/colleges_controller.rb @@ -0,0 +1,175 @@ +class CollegesController < ApplicationController + include PaginateHelper + + # layout 'college' + + before_action :require_login + before_action :check_college_present! + before_action :check_manage_permission! + + helper_method :current_school, :current_college + + def statistics + # 教师、学生总数 + count_statistic = UserExtension.where(school_id: current_school.id) + .select('SUM(IF(identity=0, 1, 0)) AS teachers_count, SUM(IF(identity=1, 1, 0)) AS students_count').first + @teachers_count = count_statistic['teachers_count'] + @students_count = count_statistic['students_count'] + + # 课堂总数 + @courses_count = Course.where(school_id: current_school.id, is_delete: 0).where.not(id: 1309).count + # 实训总数 + @shixuns_count = Shixun.visible.joins('left join user_extensions on user_extensions.user_id = shixuns.user_id') + .where(user_extensions: { school_id: current_school.id }).count + render json: { teachers_count: @teachers_count, students_count: @students_count, courses_count: @courses_count, shixuns_count: @shixuns_count, school: current_school.name } + end + + def shixun_time + time_sum = Game.joins('left join user_extensions on user_extensions.user_id = games.user_id') + .where(user_extensions: { school_id: current_school.id }).sum(:cost_time) + shixun_time_sum = (time_sum / (24 * 60 * 60.0)).ceil + + render json: { shixun_time: shixun_time_sum } + end + + def shixun_report_count + shixun_report_count = StudentWork.where(work_status: [1, 2]).where('myshixun_id != 0') + .joins('left join user_extensions on user_extensions.user_id = student_works.user_id') + .where(user_extensions: { school_id: current_school.id }).count + render json: { shixun_report_count: shixun_report_count } + end + + def teachers + @teachers = User.find_by_sql("SELECT users.id, users.login, users.lastname, users.firstname, users.nickname, IFNULL((SELECT count(shixuns.id) FROM shixuns where shixuns.user_id =users.id group by shixuns.user_id), 0) AS publish_shixun_count, + (SELECT count(c.id) FROM courses c, course_members m WHERE c.id != 1309 and m.course_id = c.id AND m.user_id=users.id AND m.role in (1,2,3) and c.school_id = #{current_school.id} AND c.is_delete = 0) as course_count + FROM `users`, user_extensions ue where ue.school_id=#{current_school.id} and users.id=ue.user_id and ue.identity=0 ORDER BY publish_shixun_count desc, course_count desc, id desc LIMIT 10") + # ).order("publish_shixun_count desc, experience desc").limit(10) + # @teacher_count = UserExtension.where(school_id: current_school.id) + # .select('SUM(IF(identity=0, 1, 0)) AS teachers_count').first.teachers_count + @teachers = + @teachers.map do |teacher| + course_ids = Course.find_by_sql("SELECT c.id FROM courses c, course_members m WHERE c.id != 1309 and m.course_id = c.id AND m.role in (1,2,3) AND m.user_id=#{teacher.id} AND c.is_delete = 0 and c.school_id = #{current_school.id}") + course_count = course_ids.size + homeworks = HomeworkCommon.where(:homework_type => 4, :course_id => course_ids.map(&:id)) + un_shixun_work_count = homeworks.where("publish_time > '#{Time.now}' or publish_time is null").count + shixun_work_count = homeworks.size - un_shixun_work_count + student_count = StudentsForCourse.where(:course_id => course_ids.map(&:id)).count + myshixun_ids = StudentWork.select("myshixun_id").where("homework_common_id in (#{homeworks.map(&:id).join(',').strip == "" ? -1 : homeworks.map(&:id).join(',')}) and myshixun_id is not null") + complete_myshixun = Myshixun.select("id").where(:status => 1, :id => myshixun_ids.map(&:myshixun_id)).size + all_myshixun = Myshixun.select("id").where(:id => myshixun_ids.map(&:myshixun_id)).size + complete_rate = all_myshixun == 0 ? 0 : ((complete_myshixun * 100) / all_myshixun).try(:round, 2).to_f + real_name = teacher.show_real_name + teacher = teacher.attributes.dup.merge({ + real_name: real_name, + course_count: course_count, + shixun_work_count: shixun_work_count, + un_shixun_work_count: un_shixun_work_count, + student_count: student_count, + complete_rate: complete_rate + }).to_json + JSON.parse(teacher) + end + end + + def shixun_chart_data + shixun_ids = HomeworkCommonsShixun.joins(homework_common: :course).where(courses: {school_id: current_school.id, is_delete: 0}).where('courses.id != 1309').pluck('distinct shixun_id') + shixun_count_map = ShixunTagRepertoire.joins(:tag_repertoire).where(shixun_id: shixun_ids).group('tag_repertoires.name').order('count_shixun_id desc').count(:shixun_id) + + names = [] + data = [] + shixun_count_map.each do |name, count| + break if names.size == 9 + + names << name + data << { value: count, name: name } + end + + if shixun_count_map.keys.size > 9 + other_count = shixun_count_map.values[9..-1].reduce(:+) + names << 'Others' + data << { name: 'Others', value: other_count } + end + + render json: { names: names, data: data } + end + + # 在线课堂 + def course_statistics + courses = Course.where(school_id: current_school.id, is_delete: 0).where.not(id: 1309) + + @course_count = courses.size + courses = courses.left_joins(practice_homeworks: { student_works: { myshixun: :games } }) + .select('courses.id, courses.name, courses.is_end, IFNULL(sum(games.evaluate_count), 0) evaluating_count') + .group('courses.id').order('is_end asc, evaluating_count desc') + + @courses = paginate courses + + course_ids = @courses.map(&:id) + @student_count = StudentsForCourse.where(course_id: course_ids).group(:course_id).count + @shixun_work_count = HomeworkCommon.where(homework_type: 4, course_id: course_ids).group(:course_id).count + @attachment_count = Attachment.where(container_id: course_ids, container_type: 'Course').group(:container_id).count + @message_count = Message.joins(:board).where(boards: { parent_id: 0, course_id: course_ids }).group('boards.course_id').count + @active_time = CourseActivity.where(course_id: course_ids).group(:course_id).maximum(:created_at) + @exercise_count = Exercise.where(course_id: course_ids).group(:course_id).count + @poll_count = Poll.where(course_id: course_ids).group(:course_id).count + @other_work_count = HomeworkCommon.where(homework_type: [1,3], course_id: course_ids).group(:course_id).count + end + + # 学生实训 + def student_shixun + # @student_count = User.joins(:user_extension).where(user_extensions: { school_id: current_school.id, identity: 1 }).count + @students = User.joins(:user_extension).where(user_extensions: { school_id: current_school.id, identity: 1 }).includes(:user_extension).order('experience desc').limit(10) + + student_ids = @students.map(&:id) + @shixun_count = Myshixun.where(user_id: student_ids).group(:user_id).count + @study_shixun_count = Myshixun.where(user_id: student_ids, status: 0).group(:user_id).count + end + + def student_hot_evaluations + games = Game.joins(:myshixun).joins('join shixun_tag_repertoires str on str.shixun_id = myshixuns.shixun_id') + games = games.joins('join tag_repertoires tr on tr.id = str.tag_repertoire_id') + games = games.joins("join user_extensions ue on ue.user_id = myshixuns.user_id and ue.school_id = #{current_school.id}") + evaluate_count_map = games.group('tr.name').reorder('sum_games_evaluate_count desc').limit(10).sum('games.evaluate_count') + + render json: { names: evaluate_count_map.keys, values: evaluate_count_map.values } + end + + private + + def require_login + return if User.current.logged? + + redirect_to "/login?back_url=#{CGI::escape(request.fullpath)}" + end + + def check_college_present! + return if current_college.present? + + redirect_to '/404' + end + + def check_manage_permission! + return if can_manage_college? + + redirect_to '/403' + end + + def can_manage_college? + return true if current_user.admin_or_business? # 超级管理员|运营 + return true if current_college.is_a?(Department) && current_college.member?(current_user) # 部门管理员 + return true if current_user.is_teacher? && current_user.school_id == current_school.id # 学校老师 + # return true if current_school.customers.exists? && current_user.partner&.partner_customers&.exists?(customer_id: current_school.customer_id) + + false + end + + def current_school + current_college.is_a?(School) ? current_college : current_college.school + end + + def current_college + @_current_college ||= begin + Department.find_by(identifier: params[:id]) || School.find_by(id: params[:id]) + end + end +end \ No newline at end of file diff --git a/app/controllers/courses_controller.rb b/app/controllers/courses_controller.rb index de24f7a8e..52911e905 100644 --- a/app/controllers/courses_controller.rb +++ b/app/controllers/courses_controller.rb @@ -29,7 +29,7 @@ class CoursesController < ApplicationController :informs, :update_informs, :online_learning, :update_task_position, :tasks_list, :join_excellent_course, :export_couser_info, :export_member_act_score, :new_informs, :delete_informs, :change_member_role, :course_groups, :join_course_group, :statistics, - :work_score, :act_score] + :work_score, :act_score, :calculate_all_shixun_scores] 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] before_action :teacher_allowed, only: [:update, :destroy, :settings, :search_teacher_candidate, @@ -48,7 +48,7 @@ class CoursesController < ApplicationController before_action :validate_page_size, only: :mine before_action :course_tasks, only: [:tasks_list, :update_task_position] before_action :validate_inform_params, only: [:update_informs, :new_informs] - before_action :course_member_allowed, only: [:statistics, :work_score, :act_score] + before_action :course_member_allowed, only: [:statistics, :work_score, :act_score, :calculate_all_shixun_scores] if RUBY_PLATFORM =~ /linux/ require 'simple_xlsx_reader' @@ -1299,8 +1299,10 @@ class CoursesController < ApplicationController begin @all_members = @course.students search = params[:search] ? "#{params[:search].strip}" : "" #用户名或学生学号id搜索 - group_id = params[:group_id] #分班的班级id - @all_members = @all_members.where(course_group_id: group_id.map(&:to_i)) unless group_id.blank? + if params[:group_id].present? + group_ids = params[:group_id].is_a?(String) ? [params[:group_id].to_i] : params[:group_id].map(&:to_i) + @all_members = @all_members.where(course_group_id: group_ids) + end unless search.blank? @all_members = @all_members.joins(user: [:user_extension]).where('concat(users.lastname, users.firstname) like ? or user_extensions.student_id like ?',"%#{search}%","%#{search}%") end @@ -1316,7 +1318,7 @@ class CoursesController < ApplicationController @c_tasks = @course.graduation_tasks.task_published.order("graduation_tasks.publish_time asc, graduation_tasks.created_at asc") set_export_cookies - member_to_xlsx(@course, @all_members, @c_homeworks, @c_exercises, @c_tasks) + member_to_xlsx(@course, @all_members.includes(user: :user_extension), @c_homeworks, @c_exercises, @c_tasks) filename_ = "#{current_user.real_name}_#{@course.name}_总成绩_#{Time.now.strftime('%Y%m%d_%H%M%S')}" render xlsx: "#{format_sheet_name filename_.strip}",template: "courses/export_member_scores_excel.xlsx.axlsx", locals: {course_scores:@course_user_scores,shixun_works:@shixun_work_arrays, @@ -1332,6 +1334,16 @@ class CoursesController < ApplicationController end end + # 计算课堂所有已发布的实训作业成绩 + def calculate_all_shixun_scores + tip_exception(-1, "课堂已结束") if @course.is_end + shixun_homeworks = @course.homework_commons.homework_published.where(homework_type: 4) + shixun_homeworks.includes(:homework_challenge_settings, :published_settings, :homework_commons_shixun).each do |homework| + homework.update_homework_work_score + end + normal_status(0, "更新成功") + end + def search_slim courses = current_user.manage_courses.not_deleted.processing diff --git a/app/controllers/files_controller.rb b/app/controllers/files_controller.rb index 0299fdf7d..25e0de44a 100644 --- a/app/controllers/files_controller.rb +++ b/app/controllers/files_controller.rb @@ -47,26 +47,26 @@ class FilesController < ApplicationController def bulk_publish return normal_status(403, "您没有权限进行操作") if current_user.course_identity(@course) >= 5 - tip_exception("请至少选择一个分班") if params[:group_ids].blank? && @course.course_groups.size != 0 + # tip_exception("请至少选择一个分班") if params[:group_ids].blank? && @course.course_groups.size != 0 attachments = @course.attachments.by_ids(@attachment_ids) ActiveRecord::Base.transaction do # 有分班设置时 - if @course.course_group_module? && @course.course_groups_count != 0 && params[:group_ids] - group_ids = params[:group_ids]&.reject(&:blank?) - charge_group_ids = @course.charge_group_ids(current_user) - publish_groups = charge_group_ids & group_ids if group_ids - - attachments.each do |atta| - if atta.published? && !atta.unified_setting || !atta.published? - create_atta_group_settings atta - atta.update_all(unified_setting: 0) if atta.unified_setting - none_publish_settings = atta.attachment_group_settings.where(course_group_id: publish_groups).none_published - none_publish_settings.update_all(publish_time: Time.now) - end - end - end + # if @course.course_group_module? && @course.course_groups_count != 0 && params[:group_ids] + # group_ids = params[:group_ids]&.reject(&:blank?) + # charge_group_ids = @course.charge_group_ids(current_user) + # publish_groups = charge_group_ids & group_ids if group_ids + # + # attachments.each do |atta| + # if atta.published? && !atta.unified_setting || !atta.published? + # create_atta_group_settings atta + # atta.update_attributes!(unified_setting: 0) if atta.unified_setting + # none_publish_settings = atta.attachment_group_settings.where(course_group_id: publish_groups).none_published + # none_publish_settings.update_all(publish_time: Time.now) + # end + # end + # end # 未发布的资源更新状态 attachments.where(is_publish: 0).update_all(is_publish: 1, publish_time: Time.now) @@ -140,7 +140,7 @@ class FilesController < ApplicationController def public_with_course_and_project @attachments = Attachment.publiced.simple_columns .contains_course_and_project - .includes(:author => :user_extension) + .includes(:container, author: :user_extension) .by_filename_or_user_name(params[:search]) .ordered(sort: 0, sort_type: 'created_on') @@ -361,15 +361,16 @@ class FilesController < ApplicationController def publish_params tip_exception("缺少发布参数") if params[:delay_publish].blank? @unified_setting = 1 - if params[:delay_publish].to_i == 1 && @course.course_group_module? && @course.course_groups_count != 0 - tip_exception("分班发布设置不能为空") if params[:group_settings].blank? - min_publish_time = params[:group_settings].pluck(:publish_time).reject(&:blank?).min - max_publish_time = params[:group_settings].pluck(:publish_time).reject(&:blank?).max - tip_exception("分班发布设置不能为空") if min_publish_time.blank? - - # 分班设置中的时间一样且包含所有分班 则按统一设置处理,否则是非统一设置 - @unified_setting = 0 unless min_publish_time == max_publish_time && params[:group_settings].pluck(:group_id).flatten.sort == @course.course_groups.pluck(:id).sort - elsif params[:delay_publish].to_i == 1 + # if params[:delay_publish].to_i == 1 && @course.course_group_module? && @course.course_groups_count != 0 + # tip_exception("分班发布设置不能为空") if params[:group_settings].blank? + # min_publish_time = params[:group_settings].pluck(:publish_time).reject(&:blank?).min + # max_publish_time = params[:group_settings].pluck(:publish_time).reject(&:blank?).max + # tip_exception("分班发布设置不能为空") if min_publish_time.blank? + # + # # 分班设置中的时间一样且包含所有分班 则按统一设置处理,否则是非统一设置 + # @unified_setting = 0 unless min_publish_time == max_publish_time && params[:group_settings].pluck(:group_id).flatten.sort == @course.course_groups.pluck(:id).sort + # els + if params[:delay_publish].to_i == 1 tip_exception("缺少延期发布的时间参数") if params[:publish_time].blank? min_publish_time = params[:publish_time] end diff --git a/app/controllers/games_controller.rb b/app/controllers/games_controller.rb index 3f7a451fd..d55eb9211 100644 --- a/app/controllers/games_controller.rb +++ b/app/controllers/games_controller.rb @@ -1,15 +1,18 @@ class GamesController < ApplicationController before_action :require_login, :check_auth - before_action :find_game + before_action :find_game, except: [:jupyter] before_action :find_shixun, only: [:show, :answer, :rep_content, :choose_build, :game_build, :game_status] - before_action :allowed + before_action :allowed, except: [:jupyter] #require 'iconv' include GamesHelper include ApplicationHelper + + + def show uid_logger("--games show start") # 防止评测中途ajaxE被取消;3改成0是为了处理首次进入下一关的问题 @@ -88,6 +91,22 @@ class GamesController < ApplicationController end end + + def jupyter + # Jupyter没有challenge + @myshixun = Myshixun.find_by_identifier params[:identifier] + unless current_user.id == @myshixun.user_id || current_user.admin_or_business? + raise Educoder::TipException.new(403, "..") + end + @shixun = @myshixun.shixun + # 判断tpm是否修改了 + begin + @tpm_modified = @myshixun.repository_is_modified(@shixun.repo_path) # 判断TPM和TPI的版本库是否被改了 + rescue + uid_logger("实训平台繁忙,繁忙等级(81)") + end + end + def reset_vnc_link begin # 删除vnc的pod diff --git a/app/controllers/hack_user_lastest_codes_controller.rb b/app/controllers/hack_user_lastest_codes_controller.rb index 4cd13d6d9..814d16d51 100644 --- a/app/controllers/hack_user_lastest_codes_controller.rb +++ b/app/controllers/hack_user_lastest_codes_controller.rb @@ -1,13 +1,14 @@ class HackUserLastestCodesController < ApplicationController before_action :require_login, except: [:listen_result] - before_action :find_my_hack, only: [:show, :code_debug, :code_submit, :update_code, + before_action :find_my_hack, only: [:show, :code_debug, :code_submit, :update_code, :sync_code, :listen_result, :result, :submit_records, :restore_initial_code] before_action :update_user_hack_status, only: [:code_debug, :code_submit] - before_action :require_auth_identity, only: [:update_code, :restore_initial_code] + before_action :require_auth_identity, only: [:update_code, :restore_initial_code, :sync_code] before_action :require_manager_identity, only: [:update_code] def show @my_hack.update_attribute(:submit_status, 0) if @my_hack.submit_status == 1 + @modify = @my_hack.modify_time.to_i < @hack.hack_codes.first.modify_time.to_i end def update_code @@ -20,6 +21,11 @@ class HackUserLastestCodesController < ApplicationController @my_hack.update_attribute(:code, @hack.code) end + # 同步代码 + def sync_code + @my_hack.update_attributes(code: @hack.code, modify_time: Time.now) + end + # 调试代码 def code_debug exec_mode = "debug" @@ -62,6 +68,7 @@ class HackUserLastestCodesController < ApplicationController # 提交记录详情 def record_detail @hack_user = HackUserCode.find params[:id] + @my_hack = @hack_user.hack_user_lastest_code end # 接收中间件返回结果接口 diff --git a/app/controllers/hacks_controller.rb b/app/controllers/hacks_controller.rb index ca0ebcb3b..b7f6a30a2 100644 --- a/app/controllers/hacks_controller.rb +++ b/app/controllers/hacks_controller.rb @@ -1,21 +1,23 @@ class HacksController < ApplicationController before_action :require_login, except: [:index] - before_action :find_hack, only: [:edit, :update, :publish, :start, :update_set, :delete_set] - before_action :require_teacher_identity, only: [:create, :update_set] - before_action :require_auth_identity, only: [:update, :edit, :publish, :update_set, :delete_set] + before_action :find_hack, only: [:edit, :update, :publish, :start, :update_set, :delete_set, :destroy, :cancel_publish] + before_action :require_teacher_identity, only: [:create] + before_action :require_auth_identity, only: [:update, :edit, :publish, :update_set, :delete_set, :destroy, :cancel_publish] # 开启编程,如果第一次开启,创建一条记录,如果已经开启过的话,直接返回标识即可 def start # 未发布的编程题,只能作者、或管理员访问 start_hack_auth - user_hack = @hack.hack_user_lastest_codes.mine(current_user.id) + user_hack = @hack.hack_user_lastest_codes.where(user_id: current_user.id).first + logger.info("#user_hack: #{user_hack}") identifier = if user_hack.present? + logger.info("#####user_hack_id:#{user_hack.id}") user_hack.identifier else user_identifier = generate_identifier HackUserLastestCode, 12 - user_code = {user_id: current_user.id, code: @hack.code, + user_code = {user_id: current_user.id, code: @hack.code, modify_time: Time.now, identifier: user_identifier, language: @hack.language} @hack.hack_user_lastest_codes.create!(user_code) user_identifier @@ -47,10 +49,10 @@ class HacksController < ApplicationController hack.identifier = generate_identifier Hack, 8 hack.save! # 创建测试集与代码 - logger.info("hack_sets_params:#{hack_sets_params}") - logger.info("hack_code_params:#{hack_code_params}") hack.hack_sets.create!(hack_sets_params) - hack.hack_codes.create!(hack_code_params) + hack_codes = hack.hack_codes.new(hack_code_params) + hack_codes.modify_time = Time.now + hack_codes.save! end render_ok({identifier: hack.identifier}) rescue Exception => e @@ -69,7 +71,8 @@ class HacksController < ApplicationController # 新建 @hack.hack_sets.create!(hack_sets_params) # 更新代码 - @hack.hack_codes.create!(hack_code_params) + code_params = params[:hack_codes][:code] != @hack.code ? hack_code_params.merge(modify_time: Time.now) : hack_code_params + @hack.hack_codes.first.update_attributes!(code_params) end render_ok rescue Exception => e @@ -98,6 +101,12 @@ class HacksController < ApplicationController render_ok end + # 取消发布 + def cancel_publish + @hack.update_attribute(:status, 0) + render_ok + end + # 发布列表 def unpulished_list limit = params[:limit] || 16 @@ -109,6 +118,13 @@ class HacksController < ApplicationController def edit;end + def new;end + + def destroy + @hack.destroy + render_ok + end + private # 实名认证老师,管理员与运营人员权限 def require_teacher_identity diff --git a/app/controllers/homework_commons_controller.rb b/app/controllers/homework_commons_controller.rb index 4a31fc474..3a60dcde5 100644 --- a/app/controllers/homework_commons_controller.rb +++ b/app/controllers/homework_commons_controller.rb @@ -78,7 +78,7 @@ class HomeworkCommonsController < ApplicationController when '4' sql_str = %Q((homework_detail_manuals.comment_status = #{order} and homework_detail_manuals.appeal_time > '#{Time.now}')) when '5' - sql_str = %Q((homework_detail_manuals.comment_status = #{order} or (anonymous_comment = 0 and homework_commons.end_time <= '#{Time.now}'))) + sql_str = %Q((anonymous_comment = 0 or (anonymous_comment = 1 and homework_detail_manuals.comment_status = #{order})) and ((allow_late= 0 and homework_commons.end_time <= '#{Time.now}') or (allow_late= 1 and late_time <= '#{Time.now}'))) else sql_str = %Q(homework_detail_manuals.comment_status = #{order}) end @@ -167,11 +167,11 @@ class HomeworkCommonsController < ApplicationController if params[:work_status].present? params_work_status = params[:work_status] work_status = params_work_status.map{|status| status.to_i} - all_student_works = @student_works.left_joins(:myshixun) - @student_works = all_student_works.where(work_status: work_status) - - @student_works = @student_works.or(all_student_works.where(work_status: 0)).or(all_student_works.where(myshixuns: {status: 0})) if work_status.include?(3) - @student_works = @student_works.or(all_student_works.where(myshixuns: {status: 1})) if work_status.include?(4) + if @homework.homework_type == "practice" + @student_works = @student_works.where(compelete_status: work_status) + else + @student_works = @student_works.where(work_status: work_status) + end end # 分班情况 @@ -198,7 +198,6 @@ class HomeworkCommonsController < ApplicationController end @work_count = @student_works.size - @work_excel = @student_works.where("work_status > 0") # 排序 rorder = params[:order].blank? ? "update_time" : params[:order] @@ -208,6 +207,7 @@ class HomeworkCommonsController < ApplicationController elsif rorder == "student_id" @student_works = @student_works.joins(user: :user_extension).order("user_extensions.#{rorder} #{b_order}") end + @work_excel = @student_works # 分页参数 page = params[:page] || 1 @@ -453,7 +453,7 @@ class HomeworkCommonsController < ApplicationController # 课堂结束后不能再更新 unless @course.is_end - UpdateHomeworkSettingService.call(@homework, publish_params) + UpdateHomeworkPublishSettingService.call(@homework, publish_params) # 作业未发布时,unified_setting参数不能为空 =begin if @homework.publish_time.nil? || @homework.publish_time > Time.now @@ -585,8 +585,8 @@ class HomeworkCommonsController < ApplicationController tip_exception("缺少answer_open_evaluation参数") if params[:answer_open_evaluation].nil? tip_exception("缺少work_efficiency参数") if params[:work_efficiency].nil? tip_exception("缺少eff_score参数") if params[:work_efficiency] && params[:eff_score].blank? - tip_exception("效率分不能小于等于0") if params[:eff_score] && params[:eff_score].to_f <= 0 - tip_exception("效率分不能大于总分值") if params[:eff_score] && params[:eff_score].to_f.round(2) > params[:total_score].to_f.round(2) + tip_exception("效率分不能小于等于0") if params[:work_efficiency] && params[:eff_score] && params[:eff_score].to_f <= 0 + tip_exception("效率分不能大于总分值") if params[:work_efficiency] && params[:eff_score] && params[:eff_score].to_f.round(2) > params[:total_score].to_f.round(2) tip_exception("缺少shixun_evaluation参数") if params[:shixun_evaluation].blank? tip_exception("缺少challenge_settings参数") if params[:challenge_settings].blank? # tip_exception("缺少challenge_id参数") if params[:challenge_settings][:challenge_id].blank? @@ -1501,8 +1501,12 @@ class HomeworkCommonsController < ApplicationController @user = @student_work.user tip_exception("当前用户无作品可以显示") if @student_work.nil? # 查询最新一次的查重标识query_id - group_id = @course.course_members.where(user_id: params[:user_id]).pluck(:course_group_id).first - query_id = @homework.homework_group_reviews.where(:course_group_id => group_id).last.try(:query_id) + group_id = @course.students.where(user_id: params[:user_id]).pluck(:course_group_id).first + homework_group_review = @homework.homework_group_reviews.where(:course_group_id => group_id).last || @homework.homework_group_reviews.last + query_id = homework_group_review.try(:query_id) + Rails.logger.info("##################------query_id: #{query_id}") + tip_exception(-1, "query_id有误") unless query_id.present? + results = ReviewService.query_result({user_id: params[:user_id], query_id: query_id}) @shixun = @homework.shixuns.take if results.status == 0 @@ -1652,7 +1656,7 @@ class HomeworkCommonsController < ApplicationController end def publish_params - params.permit(:unified_setting, :publish_time, :end_time, group_settings: []) + params.permit(:unified_setting, :publish_time, :end_time, group_settings: [:publish_time, :end_time, group_id: []]) end end diff --git a/app/controllers/item_banks_controller.rb b/app/controllers/item_banks_controller.rb new file mode 100644 index 000000000..104851676 --- /dev/null +++ b/app/controllers/item_banks_controller.rb @@ -0,0 +1,14 @@ +class LibrariesController < ApplicationController + include PaginateHelper + + def index + default_sort('updated_at', 'desc') + + @items = ItemBankQuery.call(params) + @items = paginate courses.includes(:school, :students, :attachments, :homework_commons, teacher: :user_extension) + end + + def create + + end +end \ No newline at end of file diff --git a/app/controllers/jupyters_controller.rb b/app/controllers/jupyters_controller.rb new file mode 100644 index 000000000..96ebf3452 --- /dev/null +++ b/app/controllers/jupyters_controller.rb @@ -0,0 +1,46 @@ + +class JupytersController < ApplicationController + include JupyterService + + before_action :shixun, only: [:open, :open1, :test, :save] + + def save_with_tpi + myshixun = Myshixun.find_by(identifier: params[:identifier]) + jupyter_save_with_game(myshixun, params[:jupyter_port]) + render json: {status: 0} + end + + def save_with_tpm + shixun = Shixun.find_by(identifier: params[:identifier]) + jupyter_save_with_shixun(shixun, params[:jupyter_port]) + render json: {status: 0} + end + + def get_info_with_tpi + myshixun = Myshixun.find_by(identifier: params[:identifier]) + url = jupyter_url_with_game(myshixun) + port = jupyter_port_with_game(myshixun) + render json: {status: 0, url: url, port: port} + end + + def get_info_with_tpm + shixun = Shixun.find_by(identifier: params[:identifier]) + url = jupyter_url_with_shixun(shixun) + port = jupyter_port_with_shixun(shixun) + render json: {status: 0, url: url, port: port} + end + + def reset_with_tpi + myshixun = Myshixun.find_by(identifier: params[:identifier]) + info = jupyter_tpi_reset(myshixun) + render json: {status: 0, url: info[:url], port: info[:port]} + end + + def reset_with_tpm + shixun = Shixun.find_by(identifier: params[:identifier]) + info = jupyter_tpm_reset(shixun) + render json: {status: 0, url: info[:url], port: info[:port]} + end + + +end \ No newline at end of file diff --git a/app/controllers/myshixuns_controller.rb b/app/controllers/myshixuns_controller.rb index 4b2e3f54f..0e9ba477a 100644 --- a/app/controllers/myshixuns_controller.rb +++ b/app/controllers/myshixuns_controller.rb @@ -1,384 +1,411 @@ -class MyshixunsController < ApplicationController - before_action :require_login, :check_auth, :except => [:training_task_status, :code_runinng_message] - before_action :find_myshixun, :except => [:training_task_status, :code_runinng_message] - before_action :find_repo_name, :except => [:training_task_status, :code_runinng_message] - skip_before_action :verify_authenticity_token, :only => [:html_content] - - ## TPI关卡列表 - def challenges - # @challenges = Challenge.where(shixun_id: params[:shixun_id]) - @shixun = @myshixun.shixun - @games = @myshixun.games.includes(:challenge).reorder("challenges.position") - @identity = current_user.game_identity(@games.first) - end - - - # For Admin - # 强制重置实训 - # 前段需要按照操作过程提示 - def reset_my_game - unless (current_user.admin? || current_user.id == @myshixun.user_id) - tip_exception("403", "") - end - begin - ActiveRecord::Base.transaction do - begin - @shixun = Shixun.select(:id, :identifier, :challenges_count).find(@myshixun.shixun_id) - @myshixun.destroy! - StudentWork.where(:myshixun_id => @myshixun.id).update_all(myshixun_id: 0, work_status: 0, work_score: nil, - final_score: nil, efficiency: 0, eff_score: 0, calculation_time: nil, cost_time: 0, compelete_status: 0) - rescue Exception => e - logger.error("######reset_my_game_failed:#{e.message}") - raise("ActiveRecord::RecordInvalid") - end - end - # 删除版本库 - GitService.delete_repository(repo_path: @repo_path) unless @shixun.is_choice_type? - rescue Exception => e - if e.message != "ActiveRecord::RecordInvalid" - logger.error("######delete_repository_error-:#{e.message}") - end - raise "delete_repository_error:#{e.message}" - end - end - - # 代码运行中的信息接口 - # 这个方法是中间层主动调用的,点击评测后,中间层会发送参数过来,告诉目前Pod的启动情况,一次评测会调用两次请求 - def code_runinng_message - begin - jsonTestDetails = JSON.parse(params[:jsonTestDetails]) - game_id = jsonTestDetails['buildID'] - message = jsonTestDetails['textMsg'] - if game_id.present? && message.present? - game = Game.find game_id - msg = game.run_code_message - # 只有评测中的game才会创建和更新代码评测中的信息 - if game.status == 1 || game.status == 2 - if msg.blank? - RunCodeMessage.create!(:game_id => game_id, :status => 1, :message => message) - else - msg.update_attributes(:status => (msg.status + 1), :message => message) - end - end - render :json => {:data => "success"} - end - rescue Exception => e - render :json => {:data => "failed, exception_message: #{e}"} - end - end - - # 中间层评测接口 - # taskId 即返回的game id - # 返回结果:params [:stauts] 0 表示成功,其它则失败 - # msg 错误信息 - # output 为测试用户编译输出结果 - # myshixun:status 1为完成实训 - # @jenkins: caseId对应test_set的position,passed: 1表示成功,0表示失败 - # resubmit 1:表示已通关后重新评测;0:表示非重新评测 - # retry_status 0:初始值;1:重新评测失败;2:重新评测成功 - # tpiRepoPath 中间层图片的workspace路径 - # params[:jsonTestDetails] = '{"buildID":"19284","compileSuccess":"1", - # "msg":[{"caseId":"1","expectedOutput":"MSAyIDMNCg","input":"MiAzIDE","output":"MSAyIDMNCg","passed":"1"}, - # {"caseId":"2","expectedOutput":"LTMgMSA2DQo","input":"LTMgNiAx","output":"LTMgMSA2DQo","passed":"1"}, - # {"caseId":"3","expectedOutput":"LTcgLTUgLTMNCg","input":"LTcgLTMgLTU","output":"LTcgLTUgLTMNCg","passed":"1"}], - # "outPut":"Y29tcGlsZSBzdWNjZXNzZnVsbHk","resubmit":"","status":"0"}' - # params[:timeCost] = '{"evaluateEnd":"2017-11-24 11:04:37","pull":"0.086", - # "createPod":"1.610","evaluateAllTime":2820,"evaluateStart":"2017-11-24 11:04:35","execute":"0.294"}' - # params[:pics] = "a.png,b.png,c.png" - def training_task_status - - ActiveRecord::Base.transaction do - begin - t1 = Time.now - uid_logger_dubug("@@@222222#{params[:jsonTestDetails]}") - jsonTestDetails = JSON.parse(params[:jsonTestDetails]) - timeCost = JSON.parse(params[:timeCost]) - brige_end_time = Time.parse(timeCost['evaluateEnd']) if timeCost['evaluateEnd'].present? - return_back_time = format("%.3f", ( t1.to_f - brige_end_time.to_f)).to_f - status = jsonTestDetails['status'] - game_id = jsonTestDetails['buildID'] - sec_key = jsonTestDetails['sec_key'] - - uid_logger_dubug("training_task_status start-#{game_id}-1#{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}") - resubmit = jsonTestDetails['resubmit'] - outPut = tran_base64_decode64(jsonTestDetails['outPut']) - - jenkins_testsets = jsonTestDetails['msg'] - compile_success = jsonTestDetails['compileSuccess'] - # message = Base64.decode64(params[:msg]) unless params[:msg].blank? - - game = Game.find(game_id) - myshixun = game.myshixun - challenge = game.challenge - # test_sets = challenge.test_sets - if challenge.picture_path.present? - #pics = params[:files] - pics = params[:tpiRepoPath] - game.update_column(:picture_path, pics) - end - max_query_index = game.outputs ? (game.outputs.first.try(:query_index).to_i + 1) : 1 - test_set_score = 0 - unless jenkins_testsets.blank? - jenkins_testsets.each_with_index do |j_test_set, i| - actual_output = tran_base64_decode64(j_test_set['output']) - #ts_time += j_test_set['testSetTime'].to_i - - # is_public = test_sets.where(:position => j_test_set['caseId']).first.try(:is_public) - ts_time = format("%.2f", j_test_set['testSetTime'].to_f/1000000000).to_f if j_test_set['testSetTime'] - ts_mem = format("%.2f", j_test_set['testSetMem'].to_f/1024/1024).to_f if j_test_set['testSetMem'] - - Output.create!(:code => status, :game_id => game_id, :out_put => outPut, :test_set_position => j_test_set['caseId'], - :actual_output => actual_output, :result => j_test_set['passed'].to_i, :query_index => max_query_index, - :compile_success => compile_success.to_i, :sec_key => sec_key, :ts_time => ts_time, :ts_mem => ts_mem) - # 如果设置了按测试集给分,则需要统计测试集的分值 - if challenge.test_set_score && j_test_set['passed'].to_i == 1 - test_set_score += challenge.test_sets.where(:position => j_test_set['caseId']).pluck(:score).first - end - end - end - record = EvaluateRecord.where(:identifier => sec_key).first - answer_deduction_percentage = (100 - game.answer_deduction) / 100.to_f # 查看答案后剩余分数的百分比. - # answer_deduction是查看答案的扣分比例 - # status:0表示评测成功 - if status == "0" - if resubmit.present? - game.update_attributes!(:retry_status => 2, :resubmit_identifier => resubmit) - challenge.path.split(";").each do |path| - game_passed_code(path.try(:strip), myshixun, game_id) - end - else - game.update_attributes!(:status => 2, - :end_time => Time.now, - :accuracy => format("%.4f", 1.0 / game.query_index)) - myshixun.update_attributes!(:status => 1) if game.had_done == 1 - challenge.path.split(";").each do |path| - game_passed_code(path.try(:strip), myshixun, game_id) - end - # 如果是已经发布的实训,则需要给出相应的奖励 - if challenge.shixun.try(:status) > 1 - score = (challenge.score * answer_deduction_percentage).to_i - if score > 0 - reward_attrs = { container_id: game.id, container_type: 'Game', score: score } - RewardGradeService.call(game.user, reward_attrs) - RewardExperienceService.call(game.user, reward_attrs) - end - # 需要扣除查看答案的分数 - game.update_attributes!(:final_score => score) - end - - # 更新实训关联的作品分数 TODO: 更新作品分数 - # HomeworksService.new.update_myshixun_work_score myshixun - end - # 如果过关了,下一关的状态是3(为开启),则需要把状态改成1(已开启) - # next_game = game.next_game - next_game = game.next_game(myshixun.shixun_id, game.myshixun_id, challenge.position) - next_game.update_column(:status, 0) if next_game.present? && next_game.status == 3 - # status == "-1" 表示返回结果错误 - else - if resubmit.present? - game.update_attributes!(:retry_status => 1, :resubmit_identifier => resubmit) - else - # 评测没通关则,测试集对的个数给分,并且还要扣除用户是否查看答案的值 - test_set_percentage = test_set_score / 100.to_f # 测试集得分比 - score = (challenge.score * test_set_percentage * answer_deduction_percentage).to_i - # 如果分数比上次多,则更新成绩 - game_update = - if game.final_score < score - {final_score: score, status: 0} - else - {status: 0} - end - game.update_attributes!(game_update) - end - end - test_cases_time = format("%.3f", (Time.now.to_f - t1.to_f)).to_f - if record.present? - consume_time = format("%.3f", (Time.now - record.created_at)).to_f - - record.update_attributes!(:consume_time => consume_time, :git_pull => timeCost['pull'] , :create_pod => timeCost['createPod'], - :pod_execute => timeCost['execute'], :test_cases => test_cases_time, - :brige => timeCost['evaluateAllTime'], :return_back => return_back_time) - end - sucess_status - # rescue Exception => e - # tip_exception(e.message) - # uid_logger_error("training_task_status error: #{e}") - # raise ActiveRecord::Rollback - end - end - end - - # 连接webssh - def open_webssh - username = edu_setting('webssh_username') - password = edu_setting('webssh_password') - old_time = Time.now.to_i - begin - shixun_tomcat = edu_setting('tomcat_webssh') - uri = "#{shixun_tomcat}/bridge/webssh/getConnectInfo" - params = {tpiID:@myshixun.id, podType:@myshixun.shixun.try(:webssh), - containers:(Base64.urlsafe_encode64(shixun_container_limit @myshixun.shixun))} - res = uri_post uri, params - if res && res['code'].to_i != 0 - tip_exception("实训云平台繁忙(繁忙等级:92)") - end - render :json => {:host => res['address'], - :port => res['port'], - :ws_url => res['ws_address'], - :username => username, - :password => password, - :game_id => @myshixun.id, - :webssh_url => "#{shixun_tomcat}/bridge"} - rescue Exception => e - logger.error(e) - render :json => {:error => e.try(:message)} - ensure - use_time = Time.now.to_i - old_time - logger.info "open_webssh tpiID #{@myshixun.id} use time #{use_time}" - end - end - - include GitCommon - - # -----Repository - # TODO: 之类需要一个resubmit参数,但是是关于games. - def update_file - begin - @hide_code = Shixun.where(id: @myshixun.shixun_id).pluck(:hide_code).first - tip_exception("技术平台为空!") if @myshixun.mirror_name.blank? - path = params[:path].strip unless params[:path].blank? - game_id = params[:game_id] - game = Game.find(game_id) - @content_modified = 0 - - # params[:evaluate] 实训评测时更新必须给的参数,需要依据该参数做性能统计,其它类型的更新可以跳过 - # 自动保存的时候evaluate为0;点评测的时候为1 - if params[:evaluate] == 1 - exec_time = game.challenge.try(:exec_time) - @sec_key = generate_identifier(EvaluateRecord, 12) - record = EvaluateRecord.create!(:user_id => current_user.id, :shixun_id => @myshixun.shixun_id, :game_id => game_id, - :identifier => @sec_key, :exec_time => exec_time) - uid_logger_dubug("-- game build: file update #{@sec_key}, record id is #{record.id}, time is **** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}") - end - # 隐藏代码文件 和 VNC的都不需要走版本库 - unless @hide_code || (@myshixun.shixun&.vnc_evaluate && params[:evaluate].present?) - # 远程版本库文件内容 - last_content = GitService.file_content(repo_path: @repo_path, path: path)["content"] - - content = - if python_file?(path) - params[:content].gsub(/\t/, ' ').gsub(/ /, ' ') - else - params[:content] - end - uid_logger_dubug("###11222333####{content}") - uid_logger_dubug("###222333####{last_content}") - - if content != last_content - @content_modified = 1 - - author_name = current_user.real_name - author_email = current_user.git_mail - message = params[:evaluate] == 0 ? "System automatically submitted" : "User submitted" - uid_logger_dubug("112233#{author_name}") - uid_logger_dubug("112233#{author_email}") - @content = GitService.update_file(repo_path: @repo_path, - file_path: path, - message: message, - content: content, - author_name: author_name, - author_email: author_email) - end - end - - if game.status == 2 - @resubmit = Time.now.to_i - end - - # 评测时间记录 - if record.present? - consume_time = format("%.3f", (Time.now.to_f - record.created_at.to_f)).to_f - record.update_attributes!(:file_update => consume_time) - end - rescue Exception => e - uid_logger_error(e.message) - tip_exception("文件内容更新异常,请稍后重试") - end - end - - # 渲染实训代码 - # educodercss: 字符串以 ‘,’分隔,存储的是版本库css的路径 - # educoderscript: 字符串以 ‘,’分隔,存储的是版本库js的路径 - # contents: html实训的整体内容 - def html_content - @contents = params[:contents] || "" - edu_css = params[:educodercss] - edu_js = params[:educoderscript] - if @contents.present? - @contents = @contents.gsub("w3equalsign", "=").gsub("w3scrw3ipttag", "script").gsub("edulink", "link").html_safe - end - # css - if edu_css.present? - css_path = edu_css.split(",") - css_path.each do |path| - file_content = git_fle_content(@repo_path, path)["content"] - file_content = tran_base64_decode64(file_content) unless file_content.blank? - @contents = @contents.sub(/EDUCODERCSS/, "") - end - end - # js - if edu_js.present? - js_path = edu_js.split(",") - js_path.each do |path| - file_content = git_fle_content(@repo_path, path)["content"] - file_content = tran_base64_decode64(file_content) unless file_content.blank? - @contents = @contents.sub(/EDUCODERJS/, "") - end - end - respond_to do |format| - format.json - format.html{render :layout => false} - end - end - - # 最新可以用的并发测试接口 - def sigle_mul_test - codes = %W(1 2 3 4 5 6 7 8 9 A B C D E F G H J K L N M O P Q R S T U V W X Y Z) - begin - identifiers = Myshixun.where(:shixun_id => params[:shixun_id].split(",")).pluck(:identifier) - ide = identifiers[rand(identifiers.length)] - myshixun = Myshixun.where(:identifier => ide).first - - game = myshixun.games.last - logger.warn("###2mul test game_build start ") - identifier = game.try(:identifier) - if game.status == 2 - code = codes.sample(8).join - resubmit = "#{code}_#{myshixun.id}" - end - logger.warn("###3mul test game_build start ...") - EvaluateRecord.create!(:user_id => myshixun.user_id, :shixun_id => myshixun.shixun.id, :game_id => game.id) - redirect_to "/api/games/#{identifier}/game_build?resubmit=#{resubmit}&content_modified=0&first=1" - rescue Exception => e - logger.error("mul test failed ===> #{e.message}") - end - end - - - # -----End - - private - def find_myshixun - @myshixun = Myshixun.find_by!(identifier: params[:identifier]) - end - - def find_repo_name - @repo_path = @myshixun.try(:repo_path) - @path = params[:path] - end - - def python_file?(path) - false if path.blank? - path.to_s.split(".").last.downcase == "py" - end -end +class MyshixunsController < ApplicationController + before_action :require_login, :check_auth, :except => [:training_task_status, :code_runinng_message] + before_action :find_myshixun, :except => [:training_task_status, :code_runinng_message] + before_action :find_repo_name, :except => [:training_task_status, :code_runinng_message] + skip_before_action :verify_authenticity_token, :only => [:html_content] + + ## TPI关卡列表 + def challenges + # @challenges = Challenge.where(shixun_id: params[:shixun_id]) + @shixun = @myshixun.shixun + @games = @myshixun.games.includes(:challenge).reorder("challenges.position") + @identity = current_user.game_identity(@games.first) + end + + + # For Admin + # 强制重置实训 + # 前段需要按照操作过程提示 + def reset_my_game + unless (current_user.admin? || current_user.id == @myshixun.user_id) + tip_exception("403", "") + end + begin + ActiveRecord::Base.transaction do + begin + @shixun = Shixun.select(:id, :identifier, :challenges_count).find(@myshixun.shixun_id) + @myshixun.destroy! + StudentWork.where(:myshixun_id => @myshixun.id).update_all(myshixun_id: 0, work_status: 0, work_score: nil, + final_score: nil, efficiency: 0, eff_score: 0, calculation_time: nil, cost_time: 0, compelete_status: 0) + rescue Exception => e + logger.error("######reset_my_game_failed:#{e.message}") + raise("ActiveRecord::RecordInvalid") + end + end + # 删除版本库 + GitService.delete_repository(repo_path: @repo_path) unless @shixun.is_choice_type? + rescue Exception => e + if e.message != "ActiveRecord::RecordInvalid" + logger.error("######delete_repository_error-:#{e.message}") + end + raise "delete_repository_error:#{e.message}" + end + end + + # 代码运行中的信息接口 + # 这个方法是中间层主动调用的,点击评测后,中间层会发送参数过来,告诉目前Pod的启动情况,一次评测会调用两次请求 + def code_runinng_message + begin + jsonTestDetails = JSON.parse(params[:jsonTestDetails]) + game_id = jsonTestDetails['buildID'] + message = jsonTestDetails['textMsg'] + if game_id.present? && message.present? + game = Game.find game_id + msg = game.run_code_message + # 只有评测中的game才会创建和更新代码评测中的信息 + if game.status == 1 || game.status == 2 + if msg.blank? + RunCodeMessage.create!(:game_id => game_id, :status => 1, :message => message) + else + msg.update_attributes(:status => (msg.status + 1), :message => message) + end + end + render :json => {:data => "success"} + end + rescue Exception => e + render :json => {:data => "failed, exception_message: #{e}"} + end + end + + # 中间层评测接口 + # taskId 即返回的game id + # 返回结果:params [:stauts] 0 表示成功,其它则失败 + # msg 错误信息 + # output 为测试用户编译输出结果 + # myshixun:status 1为完成实训 + # @jenkins: caseId对应test_set的position,passed: 1表示成功,0表示失败 + # resubmit 1:表示已通关后重新评测;0:表示非重新评测 + # retry_status 0:初始值;1:重新评测失败;2:重新评测成功 + # tpiRepoPath 中间层图片的workspace路径 + # params[:jsonTestDetails] = '{"buildID":"19284","compileSuccess":"1", + # "msg":[{"caseId":"1","expectedOutput":"MSAyIDMNCg","input":"MiAzIDE","output":"MSAyIDMNCg","passed":"1"}, + # {"caseId":"2","expectedOutput":"LTMgMSA2DQo","input":"LTMgNiAx","output":"LTMgMSA2DQo","passed":"1"}, + # {"caseId":"3","expectedOutput":"LTcgLTUgLTMNCg","input":"LTcgLTMgLTU","output":"LTcgLTUgLTMNCg","passed":"1"}], + # "outPut":"Y29tcGlsZSBzdWNjZXNzZnVsbHk","resubmit":"","status":"0"}' + # params[:timeCost] = '{"evaluateEnd":"2017-11-24 11:04:37","pull":"0.086", + # "createPod":"1.610","evaluateAllTime":2820,"evaluateStart":"2017-11-24 11:04:35","execute":"0.294"}' + # params[:pics] = "a.png,b.png,c.png" + def training_task_status + + ActiveRecord::Base.transaction do + begin + t1 = Time.now + uid_logger_dubug("@@@222222#{params[:jsonTestDetails]}") + jsonTestDetails = JSON.parse(params[:jsonTestDetails]) + timeCost = JSON.parse(params[:timeCost]) + brige_end_time = Time.parse(timeCost['evaluateEnd']) if timeCost['evaluateEnd'].present? + return_back_time = format("%.3f", ( t1.to_f - brige_end_time.to_f)).to_f + status = jsonTestDetails['status'] + game_id = jsonTestDetails['buildID'] + sec_key = jsonTestDetails['sec_key'] + + uid_logger_dubug("training_task_status start-#{game_id}-1#{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}") + resubmit = jsonTestDetails['resubmit'] + outPut = tran_base64_decode64(jsonTestDetails['outPut']) + + jenkins_testsets = jsonTestDetails['msg'] + compile_success = jsonTestDetails['compileSuccess'] + # message = Base64.decode64(params[:msg]) unless params[:msg].blank? + + game = Game.find(game_id) + myshixun = game.myshixun + challenge = game.challenge + # test_sets = challenge.test_sets + if challenge.picture_path.present? + #pics = params[:files] + pics = params[:tpiRepoPath] + game.update_column(:picture_path, pics) + end + max_query_index = game.outputs ? (game.outputs.first.try(:query_index).to_i + 1) : 1 + test_set_score = 0 + unless jenkins_testsets.blank? + jenkins_testsets.each_with_index do |j_test_set, i| + actual_output = tran_base64_decode64(j_test_set['output']) + #ts_time += j_test_set['testSetTime'].to_i + + # is_public = test_sets.where(:position => j_test_set['caseId']).first.try(:is_public) + ts_time = format("%.2f", j_test_set['testSetTime'].to_f/1000000000).to_f if j_test_set['testSetTime'] + ts_mem = format("%.2f", j_test_set['testSetMem'].to_f/1024/1024).to_f if j_test_set['testSetMem'] + + Output.create!(:code => status, :game_id => game_id, :out_put => outPut, :test_set_position => j_test_set['caseId'], + :actual_output => actual_output, :result => j_test_set['passed'].to_i, :query_index => max_query_index, + :compile_success => compile_success.to_i, :sec_key => sec_key, :ts_time => ts_time, :ts_mem => ts_mem) + # 如果设置了按测试集给分,则需要统计测试集的分值 + if challenge.test_set_score && j_test_set['passed'].to_i == 1 + test_set_score += challenge.test_sets.where(:position => j_test_set['caseId']).pluck(:score).first + end + end + end + record = EvaluateRecord.where(:identifier => sec_key).first + answer_deduction_percentage = (100 - game.answer_deduction) / 100.to_f # 查看答案后剩余分数的百分比. + # answer_deduction是查看答案的扣分比例 + # status:0表示评测成功 + if status == "0" + if resubmit.present? + game.update_attributes!(:retry_status => 2, :resubmit_identifier => resubmit) + challenge.path.split(";").each do |path| + game_passed_code(path.try(:strip), myshixun, game_id) + end + else + game.update_attributes!(:status => 2, + :end_time => Time.now, + :accuracy => format("%.4f", 1.0 / game.query_index)) + myshixun.update_attributes!(:status => 1) if game.had_done == 1 + challenge.path.split(";").each do |path| + game_passed_code(path.try(:strip), myshixun, game_id) + end + # 如果是已经发布的实训,则需要给出相应的奖励 + if challenge.shixun.try(:status) > 1 + score = (challenge.score * answer_deduction_percentage).to_i + if score > 0 + reward_attrs = { container_id: game.id, container_type: 'Game', score: score } + RewardGradeService.call(game.user, reward_attrs) + RewardExperienceService.call(game.user, reward_attrs) + end + # 需要扣除查看答案的分数 + game.update_attributes!(:final_score => score) + end + + # 更新实训关联的作品分数 TODO: 更新作品分数 + # HomeworksService.new.update_myshixun_work_score myshixun + end + # 如果过关了,下一关的状态是3(为开启),则需要把状态改成1(已开启) + # next_game = game.next_game + next_game = game.next_game(myshixun.shixun_id, game.myshixun_id, challenge.position) + next_game.update_column(:status, 0) if next_game.present? && next_game.status == 3 + # status == "-1" 表示返回结果错误 + else + if resubmit.present? + game.update_attributes!(:retry_status => 1, :resubmit_identifier => resubmit) + else + # 评测没通关则,测试集对的个数给分,并且还要扣除用户是否查看答案的值 + test_set_percentage = test_set_score / 100.to_f # 测试集得分比 + score = (challenge.score * test_set_percentage * answer_deduction_percentage).to_i + # 如果分数比上次多,则更新成绩 + game_update = + if game.final_score < score + {final_score: score, status: 0} + else + {status: 0} + end + game.update_attributes!(game_update) + end + end + test_cases_time = format("%.3f", (Time.now.to_f - t1.to_f)).to_f + if record.present? + consume_time = format("%.3f", (Time.now - record.created_at)).to_f + + record.update_attributes!(:consume_time => consume_time, :git_pull => timeCost['pull'] , :create_pod => timeCost['createPod'], + :pod_execute => timeCost['execute'], :test_cases => test_cases_time, + :brige => timeCost['evaluateAllTime'], :return_back => return_back_time) + end + sucess_status + # rescue Exception => e + # tip_exception(e.message) + # uid_logger_error("training_task_status error: #{e}") + # raise ActiveRecord::Rollback + end + end + end + + # 连接webssh + def open_webssh + username = edu_setting('webssh_username') + password = edu_setting('webssh_password') + old_time = Time.now.to_i + begin + shixun_tomcat = edu_setting('tomcat_webssh') + uri = "#{shixun_tomcat}/bridge/webssh/getConnectInfo" + # 由于中间层采用混合云的方式,因为local参数表示在有文件生成的实训是在本地生成,还是在其他云端生成评测文件 + local = @myshixun.shixun.challenges.where.not(show_type: -1).count == 0 + params = {tpiID:@myshixun.id, podType:@myshixun.shixun.try(:webssh), local: local, + containers:(Base64.urlsafe_encode64(shixun_container_limit @myshixun.shixun))} + res = uri_post uri, params + if res && res['code'].to_i != 0 + tip_exception("实训云平台繁忙(繁忙等级:92)") + end + render :json => {:host => res['address'], + :port => res['port'], + :ws_url => res['ws_address'], + :username => username, + :password => password, + :game_id => @myshixun.id, + :webssh_url => "#{shixun_tomcat}/bridge"} + rescue Exception => e + logger.error(e) + render :json => {:error => e.try(:message)} + ensure + use_time = Time.now.to_i - old_time + logger.info "open_webssh tpiID #{@myshixun.id} use time #{use_time}" + end + end + + include GitCommon + + # -----Repository + # TODO: 之类需要一个resubmit参数,但是是关于games. + def update_file + begin + @hide_code = Shixun.where(id: @myshixun.shixun_id).pluck(:hide_code).first + tip_exception("实验环境不能为空,请查看实训模板的环境配置项是否正确!") if (@myshixun.mirror_name.blank? || @myshixun.mirror_name.first.to_s == "-1") + path = params[:path].strip unless params[:path].blank? + game_id = params[:game_id] + game = Game.find(game_id) + @content_modified = 0 + + # params[:evaluate] 实训评测时更新必须给的参数,需要依据该参数做性能统计,其它类型的更新可以跳过 + # 自动保存的时候evaluate为0;点评测的时候为1 + if params[:evaluate] == 1 + exec_time = game.challenge.try(:exec_time) + @sec_key = generate_identifier(EvaluateRecord, 12) + record = EvaluateRecord.create!(:user_id => current_user.id, :shixun_id => @myshixun.shixun_id, :game_id => game_id, + :identifier => @sec_key, :exec_time => exec_time) + uid_logger_dubug("-- game build: file update #{@sec_key}, record id is #{record.id}, time is **** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}") + end + # 隐藏代码文件 和 VNC的都不需要走版本库 + unless @hide_code || (@myshixun.shixun&.vnc_evaluate && params[:evaluate].present?) + # 远程版本库文件内容 + last_content = GitService.file_content(repo_path: @repo_path, path: path)["content"] + + content = + if python_file?(path) + params[:content].gsub(/\t/, ' ').gsub(/ /, ' ') + else + params[:content] + end + uid_logger_dubug("###11222333####{content}") + uid_logger_dubug("###222333####{last_content}") + + if content != last_content + @content_modified = 1 + + author_name = current_user.real_name + author_email = current_user.git_mail + message = params[:evaluate] == 0 ? "System automatically submitted" : "User submitted" + uid_logger_dubug("112233#{author_name}") + uid_logger_dubug("112233#{author_email}") + @content = GitService.update_file(repo_path: @repo_path, + file_path: path, + message: message, + content: content, + author_name: author_name, + author_email: author_email) + end + end + + if game.status == 2 + @resubmit = Time.now.to_i + end + + # 评测时间记录 + if record.present? + consume_time = format("%.3f", (Time.now.to_f - record.created_at.to_f)).to_f + record.update_attributes!(:file_update => consume_time) + end + rescue Exception => e + uid_logger_error(e.message) + tip_exception("文件内容更新异常,请稍后重试") + end + end + + # 渲染实训代码 + # educodercss: 字符串以 ‘,’分隔,存储的是版本库css的路径 + # educoderscript: 字符串以 ‘,’分隔,存储的是版本库js的路径 + # contents: html实训的整体内容 + def html_content + @contents = params[:contents] || "" + edu_css = params[:educodercss] + edu_js = params[:educoderscript] + if @contents.present? + @contents = @contents.gsub("w3equalsign", "=").gsub("w3scrw3ipttag", "script").gsub("edulink", "link").html_safe + end + # css + if edu_css.present? + css_path = edu_css.split(",") + css_path.each do |path| + file_content = git_fle_content(@repo_path, path)["content"] + file_content = tran_base64_decode64(file_content) unless file_content.blank? + @contents = @contents.sub(/EDUCODERCSS/, "") + end + end + # js + if edu_js.present? + js_path = edu_js.split(",") + js_path.each do |path| + file_content = git_fle_content(@repo_path, path)["content"] + file_content = tran_base64_decode64(file_content) unless file_content.blank? + @contents = @contents.sub(/EDUCODERJS/, "") + end + end + respond_to do |format| + format.json + format.html{render :layout => false} + end + end + + # 最新可以用的并发测试接口 + def sigle_mul_test + codes = %W(1 2 3 4 5 6 7 8 9 A B C D E F G H J K L N M O P Q R S T U V W X Y Z) + begin + identifiers = Myshixun.where(:shixun_id => params[:shixun_id].split(",")).pluck(:identifier) + ide = identifiers[rand(identifiers.length)] + myshixun = Myshixun.where(:identifier => ide).first + + game = myshixun.games.last + logger.warn("###2mul test game_build start ") + identifier = game.try(:identifier) + if game.status == 2 + code = codes.sample(8).join + resubmit = "#{code}_#{myshixun.id}" + end + logger.warn("###3mul test game_build start ...") + EvaluateRecord.create!(:user_id => myshixun.user_id, :shixun_id => myshixun.shixun.id, :game_id => game.id) + redirect_to "/api/games/#{identifier}/game_build?resubmit=#{resubmit}&content_modified=0&first=1" + rescue Exception => e + logger.error("mul test failed ===> #{e.message}") + end + end + + def sync_code + shixun_tomcat = edu_setting('cloud_bridge') + begin + git_myshixun_url = repo_ip_url @myshixun.repo_path + git_shixun_url = repo_ip_url @myshixun.shixun.try(:repo_path) + git_myshixun_url = Base64.urlsafe_encode64(git_myshixun_url) + git_shixun_url = Base64.urlsafe_encode64(git_shixun_url) + # todo: identifier 是以前的密码,用来验证的,新版如果不需要,和中间层协调更改. + params = {tpiID: "#{@myshixun.try(:id)}", tpiGitURL: "#{git_myshixun_url}", tpmGitURL: "#{git_shixun_url}", + identifier: "xinhu1ji2qu3"} + uri = "#{shixun_tomcat}/bridge/game/resetTpmRepository" + res = uri_post uri, params + if (res && res['code'] != 0) + tip_exception("实训云平台繁忙(繁忙等级:95)") + end + shixun_new_commit = GitService.commits(repo_path: @myshixun.shixun.repo_path).first["id"] + @myshixun.update_attributes!(commit_id: shixun_new_commit, reset_time: @myshixun.shixun.try(:reset_time)) + # 更新完成后,弹框则隐藏不再提示 + @myshixun.update_column(:system_tip, false) + render_ok + rescue Exception => e + tip_exception("立即更新代码失败!#{e.message}") + end + end + + + # -----End + + private + def find_myshixun + @myshixun = Myshixun.find_by!(identifier: params[:identifier]) + end + + def find_repo_name + @repo_path = @myshixun.try(:repo_path) + @path = params[:path] + end + + def python_file?(path) + false if path.blank? + path.to_s.split(".").last.downcase == "py" + end +end diff --git a/app/controllers/polls_controller.rb b/app/controllers/polls_controller.rb index 068a0dafd..0c442a613 100644 --- a/app/controllers/polls_controller.rb +++ b/app/controllers/polls_controller.rb @@ -1409,23 +1409,29 @@ class PollsController < ApplicationController poll_questions.each do |q| user_poll_votes = u_user.poll_votes.find_current_vote("poll_question_id",q.id) if user_poll_votes.present? - user_poll_answer_ids = user_poll_votes.pluck(:poll_answer_id).reject(&:blank?) - user_poll_vote_texts = user_poll_votes.pluck(:vote_text).reject(&:blank?) - if user_poll_answer_ids.count > 0 - answer_content = q.poll_answers.find_answer_by_custom("id",user_poll_answer_ids) - if user_poll_answer_ids.count >1 - u_answer = answer_content.pluck(:answer_text).join(";") + if q.question_type < 3 + user_poll_answer_ids = user_poll_votes.pluck(:poll_answer_id).reject(&:blank?) + if user_poll_answer_ids.count > 0 + answer_content = q.poll_answers.find_answer_by_custom("id",user_poll_answer_ids) + if user_poll_answer_ids.count >1 + u_answer = answer_content.pluck(:answer_text).join(";") + else + u_answer = answer_content.first&.answer_text + end else - u_answer = answer_content.first.answer_text + u_answer = "--" end - elsif user_poll_vote_texts.count > 0 - if user_poll_vote_texts.count > 1 - u_answer = user_poll_vote_texts.join(";") + else + user_poll_vote_texts = user_poll_votes.pluck(:vote_text).reject(&:blank?) + if user_poll_vote_texts.count > 0 + if user_poll_vote_texts.count > 1 + u_answer = user_poll_vote_texts.join(";") + else + u_answer = user_poll_vote_texts.first + end else - u_answer = user_poll_vote_texts.first + u_answer = "--" end - else - u_answer = "--" end else u_answer = "--" diff --git a/app/controllers/shixuns_controller.rb b/app/controllers/shixuns_controller.rb index 03fdbf97f..aa25d0875 100644 --- a/app/controllers/shixuns_controller.rb +++ b/app/controllers/shixuns_controller.rb @@ -6,27 +6,28 @@ class ShixunsController < ApplicationController before_action :require_login, :check_auth, except: [:download_file, :index, :menus, :show, :show_right, :ranking_list, :discusses, :collaborators, :fork_list, :propaedeutics] - before_action :check_account, only: [:new, :create, :shixun_exec] + before_action :check_account, only: [:new, :create, :shixun_exec, :jupyter_exec] before_action :find_shixun, except: [:index, :new, :create, :menus, :get_recommend_shixuns, :propaedeutics, :departments, :apply_shixun_mirror, :get_mirror_script, :download_file, :shixun_list, :batch_send_to_course] before_action :shixun_access_allowed, except: [:index, :new, :create, :menus, :get_recommend_shixuns, - :propaedeutics, :departments, :apply_shixun_mirror, + :propaedeutics, :departments, :apply_shixun_mirror, :jupyter_exec, :get_mirror_script, :download_file, :shixun_list, :batch_send_to_course] - before_action :find_repo_name, only: [:repository, :commits, :file_content, :update_file, :shixun_exec, :copy, :add_file] + before_action :find_repo_name, only: [:repository, :commits, :file_content, :update_file, :shixun_exec, :copy, + :add_file, :jupyter_exec] - before_action :allowed, only: [:update, :close, :update_propaedeutics, :settings, :publish, + before_action :allowed, only: [:update, :close, :update_propaedeutics, :settings, :publish, :apply_public, :shixun_members_added, :change_manager, :collaborators_delete, - :cancel_publish, :add_collaborators, :add_file] + :cancel_apply_public, :cancel_publish, :add_collaborators, :add_file] before_action :portion_allowed, only: [:copy] before_action :special_allowed, only: [:send_to_course, :search_user_courses] ## 获取课程列表 def index - @shixuns = current_laboratory.shixuns.unhidden + @shixuns = current_laboratory.shixuns.unhidden.publiced ## 方向 if params[:tag_level].present? && params[:tag_id].present? @@ -66,15 +67,11 @@ class ShixunsController < ApplicationController ## 排序参数 bsort = params[:sort] || 'desc' - case params[:order_by] || 'publish_time' - when 'new' - @shixuns = @shixuns.order("shixuns.status = 2 desc, shixuns.created_at #{bsort}") + case params[:order_by] || 'new' when 'hot' - @shixuns = @shixuns.order("shixuns.status = 2 desc, shixuns.myshixuns_count #{bsort}") - when 'mine' - @shixuns = @shixuns.order("shixuns.created_at #{bsort}") + @shixuns = @shixuns.order("shixuns.public = 2 desc, shixuns.myshixuns_count #{bsort}") else - @shixuns = @shixuns.order("shixuns.status = 2 desc, shixuns.publish_time #{bsort}") + @shixuns = @shixuns.order("shixuns.public = 2 desc, shixuns.publish_time #{bsort}") end # 用id计数会快10+MS左右,对于搜索的内容随着数据的增加,性能会提升一些。 @@ -168,9 +165,10 @@ class ShixunsController < ApplicationController def show_right owner = @shixun.owner - #@fans_count = owner.fan_count - #@followed_count = owner.follow_count @user_own_shixuns = owner.shixuns.published.count + @user_tags = @shixun.user_tags_name(current_user) + @shixun_tags = @shixun.challenge_tags_name + @myshixun = @shixun.myshixuns.find_by(user_id: current_user.id) end # 排行榜 @@ -206,7 +204,7 @@ class ShixunsController < ApplicationController @new_shixun = Shixun.new @new_shixun.attributes = @shixun.attributes.dup.except("id","user_id","visits","gpid","status", "identifier", "averge_star", "homepage_show","repo_name", "myshixuns_count", "challenges_count", - "can_copy", "created_at", "updated_at") + "can_copy", "created_at", "updated_at", "public") @new_shixun.user_id = User.current.id @new_shixun.averge_star = 5 @new_shixun.identifier = generate_identifier Shixun, 8 @@ -264,7 +262,12 @@ class ShixunsController < ApplicationController project_fork(@new_shixun, @repo_path, current_user.login) ShixunMember.create!(:user_id => User.current.id, :shixun_id => @new_shixun.try(:id), :role => 1) - + # 如果是jupyter,先创建一个目录,为了挂载(因为后续数据集,开启Pod后环境在没销毁前,你上传数据集是挂载不上目录的,因此要先创建目录,方便中间层挂载) + if @new_shixun.is_jupyter? + folder = EduSetting.get('shixun_folder') + path = "#{folder}/#{@new_shixun.identifier}" + FileUtils.mkdir_p(path, :mode => 0777) unless File.directory?(path) + end # 同步复制关卡 if @shixun.challenges.present? @shixun.challenges.each do |challenge| @@ -343,7 +346,11 @@ class ShixunsController < ApplicationController #合作者 def collaborators @user = current_user - @members = @shixun.shixun_members.includes(:user) + ## 分页参数 + page = params[:page] || 1 + limit = params[:limit] || 10 + @member_count = @shixun.shixun_members.count + @members = @shixun.shixun_members.includes(:user).page(page).per(limit) end def fork_list @@ -364,73 +371,162 @@ class ShixunsController < ApplicationController end def create - # 评测脚本的一些操作 - main_type, sub_type = params[:main_type], params[:small_type] - mirror = MirrorScript.where(:mirror_repository_id => main_type) - - identifier = generate_identifier Shixun, 8 - @shixun = Shixun.new(shixun_params) - @shixun.identifier = identifier - @shixun.user_id = current_user.id - @shixun.reset_time, @shixun.modify_time = Time.now, Time.now - - if sub_type.blank? - shixun_script = mirror.first.try(:script) - else - main_mirror = MirrorRepository.find(main_type).type_name - sub_mirror = MirrorRepository.where(id: sub_type).pluck(:type_name) - if main_mirror == "Java" && sub_mirror.include?("Mysql") - shixun_script = mirror.last.try(:script) - else - shixun_script = mirror.first.try(:script) - shixun_script = modify_shixun_script @shixun, shixun_script - end - end + @shixun = CreateShixunService.call(current_user, shixun_params, params) + end - ActiveRecord::Base.transaction do - begin - @shixun.save! - # shixun_info关联ß - ShixunInfo.create!(shixun_id: @shixun.id, evaluate_script: shixun_script, description: params[:description]) - # 实训的公开范围 - if params[:scope_partment].present? - arr = [] - ids = School.where(:name => params[:scope_partment]).pluck(:id).uniq - ids.each do |id| - arr << { :school_id => id, :shixun_id => @shixun.id } - end - ShixunSchool.create!(arr) - end + # 保存jupyter到版本库 + def update_jupyter + jupyter_save_with_shixun(@shixun, params[:jupyter_port]) + end - # 实训合作者 - @shixun.shixun_members.create!(user_id: current_user.id, role: 1) - - # 镜像-实训关联表 - ShixunMirrorRepository.create!(:shixun_id => @shixun.id, :mirror_repository_id => main_type.to_i) if main_type.present? - # 实训主镜像服务配置 - ShixunServiceConfig.create!(:shixun_id => @shixun.id, :mirror_repository_id => main_type.to_i) - if sub_type.present? - sub_type.each do |mirror| - ShixunMirrorRepository.create!(:shixun_id => @shixun.id, :mirror_repository_id => mirror) - # 实训子镜像服务配置 - name = MirrorRepository.find_by(id: mirror).try(:name) #查看镜像是否有名称,如果没有名称就不用服务配置 - ShixunServiceConfig.create!(:shixun_id => @shixun.id, :mirror_repository_id => mirror) if name.present? + def update + # 镜像方面 + mirror_ids = MirrorRepository.where(id: params[:main_type]) + .or( MirrorRepository.where(id: params[:sub_type])).pluck(:id).uniq + old_mirror_ids = @shixun.shixun_mirror_repositories + .where(mirror_repository_id: params[:main_type]) + .or(@shixun.shixun_mirror_repositories.where(mirror_repository_id: params[:sub_type])) + .pluck(:mirror_repository_id).uniq + new_mirror_id = (mirror_ids - old_mirror_ids).map{|id| {mirror_repository_id: id}} # 转换成数组hash方便操作 + logger.info("##########new_mirror_id: #{new_mirror_id}") + logger.info("##########old_mirror_ids: #{old_mirror_ids}") + logger.info("##########mirror_ids: #{mirror_ids}") + # 服务配置方面 + service_create_params = service_config_params[:shixun_service_configs] + .select{|config| !old_mirror_ids.include?(config[:mirror_repository_id]) && + MirrorRepository.find(config[:mirror_repository_id]).name.present?} + service_update_params = service_config_params[:shixun_service_configs] + .select{|config| old_mirror_ids.include?(config[:mirror_repository_id])} + logger.info("#########service_create_params: #{service_create_params}") + logger.info("#########service_update_params: #{service_update_params}") + begin + ActiveRecord::Base.transaction do + @shixun.update_attributes(shixun_params) + @shixun.shixun_info.update_attributes(shixun_info_params) + # 镜像变动 + @shixun.shixun_mirror_repositories.where.not(mirror_repository_id: old_mirror_ids).destroy_all + @shixun.shixun_mirror_repositories.create!(new_mirror_id) if new_mirror_id.present? + # 镜像变动要更换服务配置 + @shixun.shixun_service_configs.where.not(mirror_repository_id: old_mirror_ids).destroy_all + @shixun.shixun_service_configs.create!(service_create_params) if service_create_params.present? + service_update_params&.map do |service| + smr = @shixun.shixun_service_configs.find_by(mirror_repository_id: service[:mirror_repository_id]) + logger.info("########smr: #{smr}") + smr.update_attributes(service) if smr.present? + end + # 添加第二仓库(管理员权限) + if params[:is_secret_repository] + add_secret_repository if @shixun.shixun_secret_repository.blank? + else + # 如果有仓库,就要删 + if @shixun.shixun_secret_repository&.repo_name + @shixun.shixun_secret_repository.lock! + GitService.delete_repository(repo_path: @shixun.shixun_secret_repository.repo_path) + @shixun.shixun_secret_repository.destroy end end + end + rescue => e + uid_logger_error(e.message) + tip_exception("基本信息更新失败:#{e.message}") + end + end + + # 实训权限设置 + def update_permission_setting + # 查找需要增删的高校id + school_id = School.where(:name => params[:scope_partment]).pluck(:id) + old_school_ids = @shixun.shixun_schools.pluck(:school_id) + school_params = (school_id - old_school_ids).map{|id| {school_id: id}} + begin + ActiveRecord::Base.transaction do + @shixun.update_attributes!(shixun_params) + @shixun.shixun_schools.where.not(school_id: school_id).destroy_all if school_id.present? + @shixun.shixun_schools.create!(school_params) + end + rescue => e + uid_logger_error("实训权限设置失败--------#{e.message}") + tip_exception("实训权限设置失败") + end + end - # 创建版本库 - repo_path = repo_namespace(User.current.login, @shixun.identifier) - GitService.add_repository(repo_path: repo_path) - # todo: 为什么保存的时候要去除后面的.git呢?? - @shixun.update_column(:repo_name, repo_path.split(".")[0]) + # 实训学习页面设置 + def update_learn_setting + begin + ActiveRecord::Base.transaction do + @shixun.update_attributes!(shixun_params) + end + rescue => e + uid_logger_error("实训学习页面设置失败--------#{e.message}") + tip_exception("实训学习页面设置失败") + end + end - # 将实训标志为该云上实验室建立 - Laboratory.current.laboratory_shixuns.create!(shixun: @shixun, ownership: true) - rescue Exception => e - uid_logger_error(e.message) - tip_exception("实训创建失败") - raise ActiveRecord::Rollback + # Jupyter数据集 + def get_data_sets + page = params[:page] || 1 + limit = params[:limit] || 10 + data_sets = @shixun.data_sets + @data_count = data_sets.count + @data_sets= data_sets.order("created_on desc").page(page).per(limit) + @absolute_folder = edu_setting('shixun_folder') + end + + # 实训测试集附件 + def upload_data_sets + begin + + upload_file = params["file"] + raise "未上传文件" unless upload_file + folder = edu_setting('shixun_folder') + raise "存储目录未定义" unless folder.present? + rep_name = @shixun.data_sets.pluck(:filename).include?(upload_file.original_filename) + raise "文件名已经存在\"#{upload_file.original_filename}\", 请删除后再上传" if rep_name + tpm_folder = params[:identifier] # 这个是实训的identifier + save_path = File.join(folder, tpm_folder) + ext = file_ext(upload_file.original_filename) + local_path, digest = file_save_to_local(save_path, upload_file.tempfile, ext) + content_type = upload_file.content_type.presence || 'application/octet-stream' + disk_filename = local_path[save_path.size + 1, local_path.size] + @attachment = Attachment.where(disk_filename: disk_filename, + author_id: current_user.id).first + if @attachment.blank? + @attachment = Attachment.new + @attachment.filename = upload_file.original_filename + @attachment.disk_filename = local_path[save_path.size + 1, local_path.size] + @attachment.filesize = upload_file.tempfile.size + @attachment.content_type = content_type + @attachment.digest = digest + @attachment.author_id = current_user.id + @attachment.disk_directory = tpm_folder + @attachment.container_id = @shixun.id + @attachment.container_type = @shixun.class.name + @attachment.attachtype = 2 + @attachment.save! + else + logger.info "文件已存在,id = #{@attachment.id}, filename = #{@attachment.filename}" end + render_ok + rescue => e + uid_logger_error(e.message) + tip_exception(e.message) + end + end + + # 多文件删除 + def destroy_data_sets + files = Attachment.where(id: params[:id]) + shixun_folder= edu_setting("shixun_folder") + begin + files.each do |file| + file_path = "#{shixun_folder}/#{file.relative_path_filename}" + delete_file(file_path) + end + files.destroy_all + render_ok + rescue => e + uid_logger_error(e.message) + tip_exception(e.message) end end @@ -462,61 +558,6 @@ class ShixunsController < ApplicationController tip_exception("申请失败") end - def update - ActiveRecord::Base.transaction do - begin - @shixun.shixun_mirror_repositories.destroy_all - if params[:main_type].present? - ShixunMirrorRepository.create(:shixun_id => @shixun.id, :mirror_repository_id => params[:main_type].to_i) - end - if params[:small_type].present? - params[:small_type].each do |mirror| - ShixunMirrorRepository.create(:shixun_id => @shixun.id, :mirror_repository_id => mirror) - end - end - @shixun.update_attributes(shixun_params) - @shixun.shixun_info.update_attributes(shixun_info_params) - @shixun.shixun_schools.delete_all - # scope_partment: 高校的名称 - if params[:scope_partment].present? - arr = [] - ids = School.where(:name => params[:scope_partment]).pluck(:id).uniq - ids.each do |id| - arr << { :school_id => id, :shixun_id => @shixun.id } - end - ShixunSchool.create!(arr) - end - # 超级管理员和运营人员才能保存 中间层服务器pod信息的配置 - # 如果镜像改动了,则也需要更改 - mirror = @shixun.shixun_service_configs.map(&:mirror_repository_id).sort - new_mirror = params[:shixun_service_configs].map{|c| c[:mirror_repository_id]}.sort - if current_user.admin? || current_user.business? || (mirror != new_mirror) - @shixun.shixun_service_configs.destroy_all - service_config_params[:shixun_service_configs].each do |config| - name = MirrorRepository.find_by_id(config[:mirror_repository_id])&.name - # 不保存没有镜像的配置 - @shixun.shixun_service_configs.create!(config) if name.present? - end - end - # 添加第二仓库 - if params[:is_secret_repository] - add_secret_repository if @shixun.shixun_secret_repository.blank? - else - # 如果有仓库,就要删 - if @shixun.shixun_secret_repository&.repo_name - @shixun.shixun_secret_repository.lock! - GitService.delete_repository(repo_path: @shixun.shixun_secret_repository.repo_path) - @shixun.shixun_secret_repository.destroy - end - end - - rescue Exception => e - uid_logger_error("实训保存失败--------#{e.message}") - tip_exception("实训保存失败") - raise ActiveRecord::Rollback - end - end - end # 永久关闭实训 def close @@ -552,6 +593,8 @@ class ShixunsController < ApplicationController # @evaluate_scirpt = @shixun.evaluate_script || "无" end + + # 获取脚本内容 def get_script_contents mirrir_script = MirrorScript.find(params[:script_id]) @@ -708,154 +751,86 @@ class ShixunsController < ApplicationController end end - # def shixun_exec - # if is_shixun_opening? - # tip_show_exception(-3, "#{@shixun.opening_time.strftime('%Y-%m-%d %H:%M:%S')}") - # end - # current_myshixun = @shixun.current_myshixun(current_user.id) - # - # min_challenges = @shixun.challenges.pluck(:id , :st) - # # 因为读写分离有延迟,所以如果是重置来的请求可以先跳过,重置过来的params[:reset]为1 - # if current_myshixun && params[:reset] != "1" - # games = current_myshixun.games - # # 如果TPM和TPI的管卡数不相等或者关卡顺序错了,说明实训被极大的改动,需要重置,实训发布前打过的实训都需要重置 - # if is_shixun_reset?(games, min_challenges, current_myshixun) - # # 这里页面弹框要收到 当前用户myshixun的identifier. - # tip_show_exception("/myshixuns/#{current_myshixun.try(:identifier)}/reset_my_game") - # end - # - # - # if current_myshixun.repo_name.nil? - # g = Gitlab.client - # repo_name = g.project(current_myshixun.gpid).try(:path_with_namespace) - # current_myshixun.update_column(:repo_name, repo_name) - # end - # - # # 如果存在实训,则直接进入实训 - # # 如果实训允许跳关,传参params[:challenge_id]跳入具体的关卡 - # @current_task = - # if params[:challenge_id] - # game = games.where(challenge_id: params[:challenge_id]).take - # if @shixun.task_pass || game.status != 3 - # game - # else - # current_myshixun.current_task(games) - # end - # else - # current_myshixun.current_task(games) - # end - # else - # # 如果未创建关卡一定不能开启实训,否则TPI没法找到当前的关卡 - # if @shixun.challenges_count == 0 - # tip_exception("开启实战前请先创建实训关卡") - # end - # - # # 判断实训是否全为选择题 - # is_choice_type = (min_challenges.size == min_challenges.select{|challenge| challenge.last == 1}.count) - # if !is_choice_type - # commit = GitService.commits(repo_path: @repo_path).try(:first) - # uid_logger("First comit########{commit}") - # tip_exception("开启实战前请先在版本库中提交代码") if commit.blank? - # commit_id = commit["id"] - # end - # - # begin - # ActiveRecord::Base.transaction do - # begin - # myshixun_identifier = generate_identifier Myshixun, 10 - # myshixun_params = {user_id: current_user.id, identifier: myshixun_identifier, - # modify_time: @shixun.modify_time, reset_time: @shixun.reset_time, - # onclick_time: Time.now, commit_id: commit_id} - # @myshixun = @shixun.myshixuns.create!(myshixun_params) - # # 其它创建关卡等操作 - # challenges = @shixun.challenges - # # 之所以增加user_id是为了方便统计查询性能 - # game_attrs = %i[challenge_id myshixun_id status user_id open_time identifier modify_time created_at updated_at] - # Game.bulk_insert(*game_attrs) do |worker| - # base_attr = {myshixun_id: @myshixun.id, user_id: @myshixun.user_id} - # challenges.each_with_index do |challenge, index| - # status = (index == 0 ? 0 : 3) - # game_identifier = generate_identifier(Game, 12) - # worker.add(base_attr.merge(challenge_id: challenge.id, status: status, open_time: Time.now, - # identifier: game_identifier, modify_time: challenge.modify_time)) - # end - # end - # @current_task = @myshixun.current_task(@myshixun.games) - # rescue Exception => e - # logger.error("------ActiveRecord::RecordInvalid: #{e.message}") - # raise("ActiveRecord::RecordInvalid") - # end - # end - # # 如果实训是纯选择题,则不需要去fork仓库以及中间层的相关操作了 - # ActiveRecord::Base.transaction do - # unless is_choice_type - # # fork仓库 - # cloud_bridge = edu_setting('cloud_bridge') - # project_fork(@myshixun, @repo_path, current_user.login) - # rep_url = Base64.urlsafe_encode64(repo_ip_url @repo_path) - # uid_logger("start openGameInstance") - # uri = "#{cloud_bridge}/bridge/game/openGameInstance" - # logger.info("end openGameInstance") - # params = {tpiID: "#{@myshixun.id}", tpmGitURL: rep_url, tpiRepoName: @myshixun.repo_name.split("/").last} - # uid_logger("openGameInstance params is #{params}") - # interface_post uri, params, 83, "实训云平台繁忙(繁忙等级:83)" - # end - # end - # rescue Exception => e - # logger.info("shixun_exec error: #{e.message}") - # if e.message != "ActiveRecord::RecordInvalid" - # logger.error("##########project_fork error #{e.message}") - # @myshixun.destroy! - # end - # raise "实训云平台繁忙(繁忙等级:81)" - # end - # end - # end - - # gameID 及实训ID - # status: 0 , 1 申请过, 2,实训关卡路径未填, 3 实训标签未填, 4 实训未创建关卡 + # jupyter开启挑战 + def jupyter_exec + begin + if is_shixun_opening? + tip_show_exception(-3, "#{@shixun.opening_time.strftime('%Y-%m-%d %H:%M:%S')}") + end + current_myshixun = @shixun.current_myshixun(current_user.id) + if current_myshixun + @myshixun = current_myshixun + else + commit = GitService.commits(repo_path: @repo_path).try(:first) + uid_logger("First comit########{commit}") + tip_exception("开启挑战前,请先在Jupyter中填写内容") if commit.blank? + commit_id = commit["id"] + cloud_bridge = edu_setting('cloud_bridge') + myshixun_identifier = generate_identifier Myshixun, 10 + ActiveRecord::Base.transaction do + @myshixun = @shixun.myshixuns.create!(user_id: current_user.id, identifier: myshixun_identifier, + modify_time: @shixun.modify_time, reset_time: @shixun.reset_time, + onclick_time: Time.now, commit_id: commit_id) + # fork仓库 + project_fork(@myshixun, @repo_path, current_user.login) + rep_url = Base64.urlsafe_encode64(repo_ip_url @repo_path) + uri = "#{cloud_bridge}/bridge/game/openGameInstance" + params = {tpiID: "#{@myshixun.id}", tpmGitURL: rep_url, tpiRepoName: @myshixun.repo_name.split("/").last} + interface_post uri, params, 83, "实训云平台繁忙(繁忙等级:83)" + end + end + rescue => e + uid_logger_error(e.message) + tip_exception("#{e.message}") + end + end + def publish @status = 0 @position = [] begin - if @shixun.challenges.count == 0 - @status = 4 - else - @shixun.challenges.each do |challenge| - if challenge.challenge_tags.count == 0 - @status = 3 - @position << challenge.position + unless @shixun.is_jupyter? + if @shixun.challenges.count == 0 + @status = 4 + else + @shixun.challenges.each do |challenge| + if challenge.challenge_tags.count == 0 + @status = 3 + @position << challenge.position + end end - end - unfinish_challenge = @shixun.challenges.where(:st => 0, :path => nil) - if unfinish_challenge.count > 0 && !@shixun.is_choice_type? - @status = 2 - @pos = [] - unfinish_challenge.each do |challenge| - @pos << challenge.position + unfinish_challenge = @shixun.challenges.where(:st => 0, :path => nil) + if unfinish_challenge.count > 0 && !@shixun.is_choice_type? + @status = 2 + @pos = [] + unfinish_challenge.each do |challenge| + @pos << challenge.position + end end end end if @status == 0 - @shixun.update_attributes!(:status => 1) - apply = ApplyAction.where(:container_type => "ApplyShixun", :container_id => @shixun.id).order("created_at desc").first - if apply && apply.status == 0 - @status = 0 - else - ApplyAction.create(:container_type => "ApplyShixun", :container_id => @shixun.id, :user_id => current_user.id, :status => 0) - #begin - # status = Trustie::Sms.send(mobile: '18711011226', send_type:'publish_shixun' , name: '管理员') - #rescue => e - # Rails.logger.error "发送验证码出错: #{e}" - #end - @status = 1 - end + @shixun.update_attributes!(:status => 2) end rescue Exception => e logger.error("pushlish game #{e}") end end + def apply_public + tip_exception(-1, "请先发布实训再申请公开") if @shixun.status != 2 + ActiveRecord::Base.transaction do + @shixun.update_attributes!(public: 1) + apply = ApplyAction.where(:container_type => "ApplyShixun", :container_id => @shixun.id).order("created_at desc").first + if apply && apply.status == 0 + @status = 0 + else + ApplyAction.create(:container_type => "ApplyShixun", :container_id => @shixun.id, :user_id => current_user.id, :status => 0) + end + end + normal_status(0, "申请成功") + end + # 设置私密版本库的在tpm中的目录 def set_secret_dir raise("设置路径不能为空") if params[:secret_dir_path].blank? @@ -993,14 +968,24 @@ class ShixunsController < ApplicationController :disposition => 'attachment' #inline can open in browser end + # 撤销申请公开 + def cancel_apply_public + tip_exception("实训已经公开,无法撤销") if @shixun.public == 2 + ActiveRecord::Base.transaction do + apply = ApplyAction.where(:container_type => "ApplyShixun", :container_id => @shixun.id).order("created_at desc").first + if apply && apply.status == 0 + apply.update_attributes!(status: 3) + apply.tidings&.destroy_all + end + @shixun.update_column(:public, 0) + end + normal_status(0, "成功撤销申请") + end + # 撤销发布 def cancel_publish - tip_exception("实训已经发布,无法撤销") if @shixun.status == 2 - apply = ApplyAction.where(:container_type => "ApplyShixun", :container_id => @shixun.id).order("created_at desc").first - if apply && apply.status == 0 - apply.update_attribute(:status, 3) - apply.tidings.destroy_all - end + tip_exception("请先撤销申请公开,再撤销发布") if @shixun.public == 1 + tip_exception("实训已经公开,无法撤销") if @shixun.public == 2 @shixun.update_column(:status, 0) end @@ -1021,10 +1006,9 @@ class ShixunsController < ApplicationController private def shixun_params - raise("实训名称不能为空") if params[:shixun][:name].blank? params.require(:shixun).permit(:name, :trainee, :webssh, :can_copy, :use_scope, :vnc, :test_set_permission, :task_pass, :multi_webssh, :opening_time, :mirror_script_id, :code_hidden, - :hide_code, :forbid_copy, :vnc_evaluate, :code_edit_permission) + :hide_code, :forbid_copy, :vnc_evaluate, :code_edit_permission, :is_jupyter) end def validate_review_shixun_params @@ -1033,8 +1017,6 @@ private end def shixun_info_params - raise("实训描述不能为空") if params[:shixun_info][:description].blank? - raise("评测脚本不能为空") if params[:shixun_info][:evaluate_script].blank? params.require(:shixun_info).permit(:description, :evaluate_script) end @@ -1102,4 +1084,47 @@ private ShixunSecretRepository.create!(repo_name: repo_path.split(".")[0], shixun_id: @shixun.id) end + def file_save_to_local(save_path, temp_file, ext) + unless Dir.exists?(save_path) + FileUtils.mkdir_p(save_path) ##不成功这里会抛异常 + end + + digest = md5_file(temp_file) + digest = "#{digest}_#{(Time.now.to_f * 1000).to_i}" + local_file_path = File.join(save_path, digest) + ext + save_temp_file(temp_file, local_file_path) + + [local_file_path, digest] + end + + def save_temp_file(temp_file, save_file_path) + File.open(save_file_path, 'wb') do |f| + temp_file.rewind + while (buffer = temp_file.read(8192)) + f.write(buffer) + end + end + end + + def file_ext(file_name) + ext = '' + exts = file_name.split(".") + if exts.size > 1 + ext = ".#{exts.last}" + end + ext + end + + def delete_file(file_path) + File.delete(file_path) if File.exist?(file_path) + end + + def md5_file(temp_file) + md5 = Digest::MD5.new + temp_file.rewind + while (buffer = temp_file.read(8192)) + md5.update(buffer) + end + md5.hexdigest + end end diff --git a/app/controllers/student_works_controller.rb b/app/controllers/student_works_controller.rb index 637155a1a..d71ca8037 100644 --- a/app/controllers/student_works_controller.rb +++ b/app/controllers/student_works_controller.rb @@ -529,8 +529,8 @@ class StudentWorksController < ApplicationController @echart_data = student_efficiency(@homework, @work) @myself_eff = @echart_data[:efficiency_list].find { |item| item.last == @user.id } @myself_consume = @echart_data[:consume_list].find { |item| item.last == @user.id } - filename_ = "#{@use&.student_id}_#{@use&.real_name}_#{@shixun&.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}" - filename = Base64.urlsafe_encode64(filename_.strip) + filename_ = "#{@user&.student_id}_#{@user&.real_name}_#{@shixun&.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}.pdf" + filename = filename_.strip.tr("+/", "-_") stylesheets = %w(shixun_work/shixun_work.css shared/codemirror.css) if params[:export].present? && params[:export] normal_status(0,"正在下载中") @@ -559,6 +559,7 @@ class StudentWorksController < ApplicationController if @work.work_status == 0 @work.work_status = 1 @work.commit_time = Time.now + @work.compelete_status = 1 if @homework.homework_type == "practice" # 分组作业更新分组id @work.group_id = @homework.max_group_id if @homework.homework_type == "group" end @@ -739,7 +740,8 @@ class StudentWorksController < ApplicationController comment: comment) challenge_score.create_tiding current_user.id if @work.work_status != 0 && @work.myshixun - HomeworksService.new.update_myshixun_work_score @work, @work.myshixun, @work.myshixun&.games, @homework, @homework.homework_challenge_settings + games = @work.myshixun.games.where(challenge_id: @homework.homework_challenge_settings.pluck(:challenge_id)) + HomeworksService.new.update_myshixun_work_score @work, @work.myshixun, games, @homework, @homework.homework_challenge_settings else update_none_commit_work @work, @homework end @@ -877,6 +879,7 @@ class StudentWorksController < ApplicationController def update_none_commit_work work, homework if work.work_status == 0 work.work_status = 1 + work.compelete_status = 1 work.commit_time = homework.end_time work.update_time = Time.now end diff --git a/app/controllers/subjects_controller.rb b/app/controllers/subjects_controller.rb index 0cd57ecc8..6a9438a79 100644 --- a/app/controllers/subjects_controller.rb +++ b/app/controllers/subjects_controller.rb @@ -204,7 +204,7 @@ class SubjectsController < ApplicationController def add_shixun_to_stage identifier = generate_identifier Shixun, 8 ActiveRecord::Base.transaction do - @shixun = Shixun.create!(name: params[:name], user_id: current_user.id, identifier: identifier) + @shixun = Shixun.create!(name: params[:name], user_id: current_user.id, identifier: identifier, is_jupyter: params[:is_jupyter]) # 添加合作者 @shixun.shixun_members.create!(user_id: current_user.id, role: 1) # 创建长字段 diff --git a/app/controllers/users/authentication_applies_controller.rb b/app/controllers/users/authentication_applies_controller.rb index 3c43be553..bf5aa0d40 100644 --- a/app/controllers/users/authentication_applies_controller.rb +++ b/app/controllers/users/authentication_applies_controller.rb @@ -1,5 +1,6 @@ class Users::AuthenticationAppliesController < Users::BaseAccountController before_action :private_user_resources! + before_action :check_account, only: [:create] def create Users::ApplyAuthenticationService.call(observed_user, create_params) diff --git a/app/controllers/users/professional_auth_applies_controller.rb b/app/controllers/users/professional_auth_applies_controller.rb index d1ee70953..9fc8e73db 100644 --- a/app/controllers/users/professional_auth_applies_controller.rb +++ b/app/controllers/users/professional_auth_applies_controller.rb @@ -1,5 +1,6 @@ class Users::ProfessionalAuthAppliesController < Users::BaseAccountController before_action :private_user_resources! + before_action :check_account, only: [:create] def create Users::ApplyProfessionalAuthService.call(observed_user, create_params) diff --git a/app/controllers/weapps/courses_controller.rb b/app/controllers/weapps/courses_controller.rb index 1ccefcbe2..cb3c195d6 100644 --- a/app/controllers/weapps/courses_controller.rb +++ b/app/controllers/weapps/courses_controller.rb @@ -6,7 +6,7 @@ class Weapps::CoursesController < Weapps::BaseController before_action :teacher_or_admin_allowed, only: [:change_member_roles, :delete_course_teachers] def create - return render_error("只有老师身份才能创建课堂") unless current_user.is_teacher? + # return render_error("只有老师身份才能创建课堂") unless current_user.is_teacher? course = Course.new(tea_id: current_user.id) Weapps::CreateCourseService.call(course, course_params) render_ok @@ -93,8 +93,8 @@ class Weapps::CoursesController < Weapps::BaseController end if course_group_id.present? - course_group = CourseGroup.find(course_group_id) if course_group_id != 0 - @students = @students.where(course_group_id: course_group&.id.to_i) + @course_group = CourseGroup.find(course_group_id) if course_group_id != 0 + @students = @students.where(course_group_id: @course_group&.id.to_i) end @students_count = @students.size @@ -107,7 +107,7 @@ class Weapps::CoursesController < Weapps::BaseController # 批量修改角色 def change_member_roles @course = current_course - tip_exception("请至少选择一个角色") if params[:roles].blank? + tip_exception("请至少选择一个角色") if params[:roles].reject(&:blank?).blank? tip_exception("不能具有老师、助教两种角色") if params[:roles].include?("PROFESSOR") && params[:roles].include?("ASSISTANT_PROFESSOR") params[:user_ids].each do |user_id| @@ -150,13 +150,13 @@ class Weapps::CoursesController < Weapps::BaseController new_student.is_active = 0 if correspond_teacher_exist new_student.save! - CourseAddStudentCreateWorksJob.perform_later(@course.id, user_id) + CourseAddStudentCreateWorksJob.perform_later(@course.id, [user_id]) # StudentJoinCourseNotifyJob.perform_later(current_user.id, course.id) elsif !params[:roles].include?("STUDENT") && student_member.present? # 删除学生身份时激活老师身份 teacher_member.update_attributes!(is_active: 1) if student_member.is_active && teacher_member.present? student_member.destroy! - CourseDeleteStudentDeleteWorksJob.perform_later(@course.id, user_id) + CourseDeleteStudentDeleteWorksJob.perform_later(@course.id, [user_id]) # CourseDeleteStudentNotifyJob.perform_later(@course.id, [params[:user_id]], current_user.id) elsif params[:roles].include?("STUDENT") && student_member.present? && !params[:roles].include?("PROFESSOR") && !params[:roles].include?("ASSISTANT_PROFESSOR") # 学生身份存在且学生没有教师身份时更新is_active @@ -167,6 +167,15 @@ class Weapps::CoursesController < Weapps::BaseController normal_status(0, "修改成功") end + # 分班列表 + def course_groups + @course_groups = @course.course_groups + @course_groups = @course_groups.where("name like ?", "%#{params[:search]}%") unless params[:search].blank? + @all_group_count = @course_groups.size + @teachers = @course.teachers.includes(:user, :teacher_course_groups) if @user_course_identity < Course::NORMAL + @current_group_id = @course.students.where(user_id: current_user.id).take&.course_group_id if @user_course_identity == Course::STUDENT + end + private def course_params diff --git a/app/controllers/weapps/homework_commons_controller.rb b/app/controllers/weapps/homework_commons_controller.rb index 00d2ce926..ebadd00b0 100644 --- a/app/controllers/weapps/homework_commons_controller.rb +++ b/app/controllers/weapps/homework_commons_controller.rb @@ -6,8 +6,9 @@ class Weapps::HomeworkCommonsController < Weapps::BaseController def update_settings begin # 课堂结束后不能再更新 - if @course.is_end - UpdateHomeworkSettingService.call(@homework, publish_params) + unless @course.is_end + UpdateHomeworkPublishSettingService.call(@homework, publish_params) + render_ok else tip_exception("课堂已结束不能再更新") end @@ -31,7 +32,7 @@ class Weapps::HomeworkCommonsController < Weapps::BaseController end def publish_params - params.permit(:unified_setting, :publish_time, :end_time, group_settings: []) + params.permit(:unified_setting, :publish_time, :end_time, group_settings: [:publish_time, :end_time, group_id: []]) end end \ No newline at end of file diff --git a/app/forms/users/update_password_form.rb b/app/forms/users/update_password_form.rb index 4da341839..db674384c 100644 --- a/app/forms/users/update_password_form.rb +++ b/app/forms/users/update_password_form.rb @@ -3,5 +3,5 @@ class Users::UpdatePasswordForm attr_accessor :password, :old_password - validates :password, presence: true + validates :password, presence: true, length: { minimum: 8, maximum: 16 }, format: { with: CustomRegexp::PASSWORD, message: "8~16位密码,支持字母数字和符号" } end \ No newline at end of file diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index be633b2cc..cabf8d244 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -210,7 +210,7 @@ module ApplicationHelper # 普通/分组 作业作品状态数组 def student_work_status homework, user_id, course, work status = [] - homework_setting = homework.homework_group_setting user_id + homework_setting = homework.homework_group_setting user_id, true work = work || StudentWork.create(homework_common_id: homework.id, user_id: user_id) late_time = homework.late_time || course.end_date diff --git a/app/helpers/challenges_helper.rb b/app/helpers/challenges_helper.rb index c6d05817d..fc0101dff 100644 --- a/app/helpers/challenges_helper.rb +++ b/app/helpers/challenges_helper.rb @@ -4,4 +4,7 @@ module ChallengesHelper str.gsub(/\A\r/, "\r\r") end + + + end diff --git a/app/helpers/export_helper.rb b/app/helpers/export_helper.rb index 31e1ecf45..ca76ee953 100644 --- a/app/helpers/export_helper.rb +++ b/app/helpers/export_helper.rb @@ -22,21 +22,21 @@ module ExportHelper end end - shixun_homeworks = shixun_homeworks&.includes(score_student_works: :user) + shixun_homeworks = shixun_homeworks&.includes(:student_works) common_homeworks = homeworks.search_homework_type(1) #全部普通作业 common_titles = common_homeworks.pluck(:name)+ ["总得分"] - common_homeworks = common_homeworks&.includes(score_student_works: :user) + common_homeworks = common_homeworks&.includes(:student_works) group_homeworks = homeworks.search_homework_type(3) #全部分组作业 group_titles = group_homeworks.pluck(:name)+ ["总得分"] - group_homeworks = group_homeworks&.includes(score_student_works: :user) + group_homeworks = group_homeworks&.includes(:student_works) task_titles = tasks.pluck(:name) + ["总得分"] - tasks = tasks&.includes(user: :user_extension, score_graduation_works: :user) + tasks = tasks&.includes(:graduation_works) exercise_titles = exercises.pluck(:exercise_name) + ["总得分"] - exercises = exercises&.includes(user: :user_extension, score_exercise_users: :user) + exercises = exercises&.includes(:exercise_users) total_user_score_array = [] #学生总成绩集合 @@ -67,7 +67,7 @@ module ExportHelper #实训作业 if shixun_homeworks.size > 0 shixun_homeworks.each do |s| - user_student_work = s.score_student_works.select{|work| work.user_id == user.id}.first #当前用户的对该作业的回答 + user_student_work = s.student_works.select{|work| work.user_id == user.id}.first #当前用户的对该作业的回答 if user_student_work.nil? h_score = 0.0 #该作业的得分为0 else @@ -82,7 +82,7 @@ module ExportHelper #普通作业 if common_homeworks.size > 0 common_homeworks.each do |c| - user_student_work_1 = c.score_student_works.select{|work| work.user_id == user.id}.first #当前用户的对该作业的回答 + user_student_work_1 = c.student_works.select{|work| work.user_id == user.id}.first #当前用户的对该作业的回答 if user_student_work_1.nil? h_score_1 = 0.0 #该作业的得分为0 else @@ -97,7 +97,7 @@ module ExportHelper #分组作业 if group_homeworks.size > 0 group_homeworks.each do |g| - user_student_work_3 = g.score_student_works.select{|work| work.user_id == user.id}.first #当前用户的对该作业的回答 + user_student_work_3 = g.student_works.select{|work| work.user_id == user.id}.first #当前用户的对该作业的回答 if user_student_work_3.nil? h_score_3 = 0.0 #该作业的得分为0 else @@ -112,7 +112,7 @@ module ExportHelper #毕设作业 if tasks.size > 0 tasks.each do |task| - graduation_work = task.score_graduation_works.select{|work| work.user_id == user.id}.first + graduation_work = task.graduation_works.select{|work| work.user_id == user.id}.first if graduation_work.nil? t_score = 0.0 else @@ -127,7 +127,7 @@ module ExportHelper #试卷 if exercises.size > 0 exercises.each do |ex| - exercise_work = ex.score_exercise_users.select{|work| work.user_id == user.id}.first + exercise_work = ex.exercise_users.select{|work| work.user_id == user.id}.first if exercise_work.nil? e_score = 0.0 else @@ -163,9 +163,12 @@ module ExportHelper count_2 = common_homeworks.size count_3 = group_homeworks.size count_4 = tasks.size + + all_user_ids = all_members.pluck(:user_id) + #实训作业 shixun_homeworks.each_with_index do |s,index| - all_student_works = s.score_student_works #该实训题的全部用户回答 + all_student_works = s.student_works.where(user_id: all_user_ids) #该实训题的全部用户回答 title_no = index.to_i + 1 student_work_to_xlsx(all_student_works,s) shixun_work_display_name = format_sheet_name (title_no.to_s + "." + s.name).strip.first(30) @@ -175,7 +178,7 @@ module ExportHelper #普通作业 common_homeworks.each_with_index do |c,index| - all_student_works = c.score_student_works #当前用户的对该作业的回答 + all_student_works = c.student_works.where(user_id: all_user_ids) #当前用户的对该作业的回答 title_no = count_1 + index.to_i + 1 student_work_to_xlsx(all_student_works,c) @@ -187,7 +190,7 @@ module ExportHelper #分组作业 group_homeworks.each_with_index do |c,index| - all_student_works = c.score_student_works #当前用户的对该作业的回答 + all_student_works = c.student_works.where(user_id: all_user_ids) #当前用户的对该作业的回答 title_no = count_1 + count_2 + index.to_i + 1 student_work_to_xlsx(all_student_works,c) work_name = format_sheet_name (title_no.to_s + "." + c.name).strip.first(30) @@ -197,7 +200,7 @@ module ExportHelper #毕设任务 tasks.each_with_index do |c,index| - all_student_works = c.score_graduation_works #当前用户的对该作业的回答 + all_student_works = c.graduation_works.where(user_id: all_user_ids) #当前用户的对该作业的回答 title_no = count_1 + count_2 + count_3 + index.to_i + 1 graduation_work_to_xlsx(all_student_works,c,current_user) work_name = format_sheet_name (title_no.to_s + "." + c.name).strip.first(30) @@ -207,7 +210,7 @@ module ExportHelper #试卷的导出 exercises.each_with_index do |c,index| - all_student_works = c.score_exercise_users #当前用户的对该作业的回答 + all_student_works = c.exercise_users.where(user_id: all_user_ids) #当前用户的对该作业的回答 title_no = count_1 + count_2 + count_3 + count_4 + index.to_i + 1 get_export_users(c,course,all_student_works) work_name = format_sheet_name (title_no.to_s + "." + c.exercise_name).strip.first(30) @@ -426,7 +429,7 @@ module ExportHelper end else #实训题 shixun = homework.shixuns.take - shixun_head_cells = %w(完成情况 通关时间 学员在EduCoder做实训花费的时间 总评测次数 获得经验值 关卡得分) + shixun_head_cells = %w(截止前完成关卡 通关时间 学员在EduCoder做实训花费的时间 总评测次数 获得经验值 关卡得分) eff_boolean = homework.work_efficiency if eff_boolean eff_score_cell = ["效率分"] @@ -452,16 +455,18 @@ module ExportHelper course_name = course.students.find_by(user_id: w.user_id).try(:course_group_name) w_5 = course_name.present? ? course_name : "--" #0: 未提交, 1 按时提交, 2 延迟提交 - if w.work_status == 0 - w_6 = "未提交" - elsif w.work_status == 1 - w_6 = "按时提交" - elsif w.work_status == 2 - w_6 = "延迟提交" + if w.compelete_status == 0 + w_6 = "未开启" + elsif w.compelete_status == 1 + w_6 = "未通关" + elsif w.compelete_status == 2 + w_6 = "按时通关" + elsif w.compelete_status == 3 + w_6 = "迟交通关" else w_6 = "--" end - w_7 = w.work_status == 0 ? '--' : myshixun.try(:passed_count).to_s+"/"+shixun.challenges_count.to_s + w_7 = myshixun&.time_passed_count(homework.homework_group_setting(w.user_id)&.end_time).to_i.to_s+"/"+shixun.challenges_count.to_s w_8 = myshixun ? myshixun.try(:passed_time).to_s == "--" ? "--" : format_time(myshixun.try(:passed_time)) : "--" # 通关时间 w_9 = myshixun ? (myshixun.try(:passed_count).to_i > 0 ? myshixun.total_spend_time : '--') : "--" #总耗时 w_10 = myshixun ? myshixun.output_times : 0 #评测次数 @@ -478,7 +483,7 @@ module ExportHelper w_14 = nil end w_15 = w.work_score.nil? ? "--" : w.work_score.round(1) - w_16 = w.update_time ? format_time(w.update_time) : "--" "更新时间" + w_16 = w.update_time ? format_time(w.update_time) : "--" myshixun_complete = myshixun && myshixun.status == 1 w_17 = myshixun_complete && w.cost_time ? (game_spend_time w.cost_time) : "未完成" teacher_comment = w.shixun_work_comments.select{|comment| comment.challenge_id == 0}.first diff --git a/app/helpers/homework_commons_helper.rb b/app/helpers/homework_commons_helper.rb index efc14dc5e..f962288eb 100644 --- a/app/helpers/homework_commons_helper.rb +++ b/app/helpers/homework_commons_helper.rb @@ -24,40 +24,37 @@ module HomeworkCommonsHelper time = course.end_date.strftime("%Y-%m-%d") time_status = 6 else - if homework_common.end_time && homework_common.end_time < Time.now && homework_common.allow_late && - (homework_common.late_time.nil? || homework_common.late_time > Time.now) - status << "补交中" - end - ho_detail_manual = homework_common.homework_detail_manual if ho_detail_manual # 作业状态大于“提交”状态时,不用考虑分班权限 if ho_detail_manual.comment_status > 1 - case ho_detail_manual.comment_status - when 3 - if ho_detail_manual.evaluation_end && ho_detail_manual.evaluation_end > Time.now - status << "匿评中" - time = "提交剩余时间:" + how_much_time(ho_detail_manual.evaluation_end) - time_status = 3 - end - when 4 - if ho_detail_manual.appeal_time && ho_detail_manual.appeal_time > Time.now - status << "申诉中" - time = "申诉剩余时间:" + how_much_time(ho_detail_manual.appeal_time) - time_status = 4 - end - when 2, 5, 6 - status << "评阅中" - time = course.end_date.present? ? ("评阅剩余时间:" + how_much_time(course.end_date.end_of_day)) : "" - time_status = 5 - end - - # 如果还在补交阶段则显示补交结束时间 if homework_common.end_time && homework_common.end_time < Time.now && homework_common.allow_late && - homework_common.late_time && homework_common.late_time > Time.now + (homework_common.late_time.nil? || homework_common.late_time > Time.now) + status << "补交中" time = "补交剩余时间:" + how_much_time(homework_common.late_time) time_status = 2 end + + case ho_detail_manual.comment_status + when 3 + if ho_detail_manual.evaluation_end && ho_detail_manual.evaluation_end > Time.now + status << "匿评中" + time = "匿评剩余时间:" + how_much_time(ho_detail_manual.evaluation_end) + time_status = 3 + end + when 4 + if ho_detail_manual.appeal_time && ho_detail_manual.appeal_time > Time.now + status << "申诉中" + time = "申诉剩余时间:" + how_much_time(ho_detail_manual.appeal_time) + time_status = 4 + end + else + if status.blank? + status << "已截止" + time = course.end_date.present? ? ("评阅剩余时间:" + how_much_time(course.end_date.end_of_day)) : "" + time_status = 5 + end + end else # member = course.course_members.find_by(user_id: user.id, is_active: 1) # teacher_course_groups = member.try(:teacher_course_groups) @@ -65,25 +62,28 @@ module HomeworkCommonsHelper # 作业统一设置、游客身份、超级管理员、分班权限不限的老师身份 if homework_common.unified_setting || identity > Course::STUDENT || identity == Course::ADMIN || - (identity < Course::STUDENT && teacher_course_groups.blank?) + (identity < Course::STUDENT && teacher_course_groups.blank?) case ho_detail_manual.comment_status - when 0 - status << "未发布" - time = homework_common.publish_time.present? ? "将于 #{format_time(homework_common.publish_time)} 发布" : "创建于#{time_from_now(homework_common.created_at)}" - time_status = 0 - when 1 - if homework_common.end_time && homework_common.end_time >= Time.now - status << "提交中" - time = "提交剩余时间:" + how_much_time(homework_common.end_time) - time_status = 1 - elsif homework_common.end_time && homework_common.end_time < Time.now + when 0 + status << "未发布" + time = homework_common.publish_time.present? ? "将于 #{format_time(homework_common.publish_time)} 发布" : "创建于#{time_from_now(homework_common.created_at)}" + time_status = 0 + when 1 + if homework_common.end_time && homework_common.end_time >= Time.now + status << "提交中" + time = "提交剩余时间:" + how_much_time(homework_common.end_time) + time_status = 1 + elsif homework_common.end_time && homework_common.end_time < Time.now + if homework_common.allow_late && (homework_common.late_time.nil? || homework_common.late_time >= Time.now) + status << "补交中" + time = "补交剩余时间:" + how_much_time(homework_common.late_time) + time_status = 2 + else + status << "已截止" + time = course.end_date.present? ? ("评阅剩余时间:" + how_much_time(course.end_date.end_of_day)) : "" time_status = 5 - if homework_common.allow_late && (homework_common.late_time.nil? || homework_common.late_time >= Time.now) - time = "补交剩余时间:" + how_much_time(homework_common.late_time) - time_status = 2 - end - status << "评阅中" end + end end else # 未分班的学生始终显示“未发布”(按理不会来到这个判断) @@ -99,9 +99,9 @@ module HomeworkCommonsHelper else # 多个分班权限的取最小publish_time,最大end_time min_publish_time = homework_common.homework_group_settings.where.not(publish_time: nil). - where(course_group_id: teacher_course_groups.pluck(:course_group_id)).pluck(:publish_time).min + where(course_group_id: teacher_course_groups.pluck(:course_group_id)).pluck(:publish_time).min max_end_time = homework_common.homework_group_settings.where.not(end_time: nil). - where(course_group_id: teacher_course_groups.pluck(:course_group_id)).pluck(:end_time).max + where(course_group_id: teacher_course_groups.pluck(:course_group_id)).pluck(:end_time).max end if min_publish_time.nil? @@ -116,24 +116,22 @@ module HomeworkCommonsHelper status << "提交中" time = "提交剩余时间:" + how_much_time(max_end_time) time_status = 1 - elsif homework_common.allow_late && (homework_common.late_time.nil? || homework_common.late_time >= Time.now) - status << "评阅中" - time = "补交剩余时间:" + how_much_time(homework_common.late_time) - time_status = 2 else - status << "评阅中" - time = "" + status << "已截止" + time = course.end_date.present? ? ("评阅剩余时间:" + how_much_time(course.end_date.end_of_day)) : "" time_status = 5 end end end - status << "未开启补交" if !homework_common.allow_late && time_status != 0 + status << "未开启补交" if !homework_common.allow_late && time_status == 1 end end end - # 如果作业状态都没有的话,在课堂结束前,都显示评阅中 + # 如果作业状态都没有的话,在课堂结束前,都显示已截止 if status.blank? - status << "评阅中" + status << "已截止" + time = course.end_date.present? ? ("评阅剩余时间:" + how_much_time(course.end_date.end_of_day)) : "" + time_status = 5 end result[:status] = status result[:time] = time @@ -144,7 +142,7 @@ module HomeworkCommonsHelper # 阶段剩余时间 def left_time homework, user_id - setting = homework.homework_group_setting(user_id) + setting = homework.homework_group_setting(user_id, true) if setting.publish_time && setting.publish_time < Time.now if setting.end_time > Time.now status = "剩余提交时间" @@ -224,10 +222,10 @@ module HomeworkCommonsHelper # 作品状态 def practice_homework_status homework, member - [{id: 3, name: "未通关", count: homework.un_complete_count(member)}, - {id: 4, name: "已通关", count: homework.complete_count(member)}, - {id: 1, name: "按时完成", count: homework.finished_count(member)}, - {id: 2, name: "延时完成", count: homework.delay_finished_count(member)}] + [{id: 0, name: "未开启", count: homework.compelete_status_count(member, 0)}, + {id: 1, name: "未通关", count: homework.compelete_status_count(member, 1)}, + {id: 2, name: "按时通关", count: homework.compelete_status_count(member, 2)}, + {id: 3, name: "迟交通关", count: homework.compelete_status_count(member, 3)}] end # 作品状态 diff --git a/app/helpers/shixuns_helper.rb b/app/helpers/shixuns_helper.rb index b41750bed..655a7ed04 100644 --- a/app/helpers/shixuns_helper.rb +++ b/app/helpers/shixuns_helper.rb @@ -27,6 +27,17 @@ module ShixunsHelper end end + def shixun_public_status shixun + case shixun.try(:public) + when 0,nil + "未公开" + when 1 + "待审核" + when 2 + "已公开" + end + end + # 已完成实训所获得的经验值 def myshixun_exp myshixun score = 0 diff --git a/app/jobs/sync_trustie_job.rb b/app/jobs/sync_trustie_job.rb index 33df5b529..dec09debc 100644 --- a/app/jobs/sync_trustie_job.rb +++ b/app/jobs/sync_trustie_job.rb @@ -17,10 +17,17 @@ class SyncTrustieJob < ApplicationJob "number": count } uri = URI.parse(url) + # http = Net::HTTP.new(uri.hostname, uri.port) + if api_host http = Net::HTTP.new(uri.hostname, uri.port) - http.send_request('PUT', uri.path, sync_json.to_json, {'Content-Type' => 'application/json'}) - Rails.logger.info("#######_________response__sync__end_____#########") + + if api_host.include?("https://") + http.use_ssl = true + end + + response = http.send_request('PUT', uri.path, sync_json.to_json, {'Content-Type' => 'application/json'}) + Rails.logger.info("#######_________response__sync__end_____#########{response.body}") end end end diff --git a/app/libs/custom_regexp.rb b/app/libs/custom_regexp.rb index 2a2a99384..2980f2ed2 100644 --- a/app/libs/custom_regexp.rb +++ b/app/libs/custom_regexp.rb @@ -3,4 +3,5 @@ module CustomRegexp EMAIL = /\A[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+\z/ LASTNAME = /\A[a-zA-Z0-9\u4e00-\u9fa5]+\z/ NICKNAME = /\A[\u4e00-\u9fa5_a-zA-Z0-9]+\z/ + PASSWORD = /\A[a-z_A-Z0-9\-\.!@#\$%\\\^&\*\)\(\+=\{\}\[\]\/",'_<>~\·`\?:;|]{8,16}\z/ end \ No newline at end of file diff --git a/app/models/attachment.rb b/app/models/attachment.rb index 37884e40b..ac051428f 100644 --- a/app/models/attachment.rb +++ b/app/models/attachment.rb @@ -21,18 +21,31 @@ class Attachment < ApplicationRecord scope :contains_only_project, -> { where(container_type: 'Project') } scope :contains_course_and_project, -> { contains_only_course.or(contains_only_project) } scope :mine, -> (author_id) { where(author_id: author_id) } - scope :simple_columns, -> { select(:id, :filename, :filesize, :created_on, :cloud_url, :author_id, :content_type) } + scope :simple_columns, -> { select(:id, :filename, :filesize, :created_on, :cloud_url, :author_id, :content_type, :container_type, :container_id) } scope :search_by_container, -> (ids) {where(container_id: ids)} scope :unified_setting, -> {where("unified_setting = ? ", 1)} validates_length_of :description, maximum: 100 + DCODES = %W(2 3 4 5 6 7 8 9 a b c f e f g h i j k l m n o p q r s t u v w x y z) + def diskfile File.join(File.join(Rails.root, "files"), disk_directory.to_s, disk_filename.to_s) end + def relative_path_filename + File.join(disk_directory.to_s, disk_filename.to_s) + end + def title - filename + title = filename + if container && container.is_a?(StudentWork) && author_id != User.current.id + course = container&.homework_common&.course + unless User.current.teacher_of_course?(course) + title = "#{Time.now.strftime('%Y%m%d%H%M%S')}_#{DCODES.sample(8).join}" + File.extname(filename) + end + end + title end def downloads_count diff --git a/app/models/competition.rb b/app/models/competition.rb index 23fa268ec..96fd14c1c 100644 --- a/app/models/competition.rb +++ b/app/models/competition.rb @@ -165,7 +165,7 @@ class Competition < ApplicationRecord def get_module_name type case type - when 'home' then '首页' + when 'home' then '赛制介绍' when 'enroll' then '报名' when 'inform' then '通知公告' when 'chart' then '排行榜' diff --git a/app/models/game.rb b/app/models/game.rb index d82392a59..062ad15cc 100644 --- a/app/models/game.rb +++ b/app/models/game.rb @@ -119,6 +119,12 @@ class Game < ApplicationRecord # self.outputs.pluck(:query_index).first #end + # 是否查看了答案(通关的是否在通关前看的答案) + def view_answer + answer_exists = Grade.where("container_type = 'Answer' and container_id = #{self.id} and created_at < '#{self.end_time}'").exists? + answer_open != 0 ? (status == 2 ? answer_exists : true) : false + end + # 用户关卡得分 def get_user_final_score diff --git a/app/models/hack.rb b/app/models/hack.rb index 80724d3d4..506cd4942 100644 --- a/app/models/hack.rb +++ b/app/models/hack.rb @@ -3,6 +3,8 @@ class Hack < ApplicationRecord # diffcult: 难度 1:简单;2:中等; 3:困难 # 编程题 validates_length_of :name, maximum: 60 + validates :description, presence: { message: "描述不能为空" } + validates :name, presence: { message: "名称不能为空" } # 测试集 has_many :hack_sets, ->{order("position asc")}, :dependent => :destroy # 代码 @@ -39,4 +41,9 @@ class Hack < ApplicationRecord hack_sets.first&.input end + # 管理员 + def manager?(user) + user_id == user.id || user.admin_or_business? + end + end diff --git a/app/models/hack_set.rb b/app/models/hack_set.rb index 5dab862b5..6afe05663 100644 --- a/app/models/hack_set.rb +++ b/app/models/hack_set.rb @@ -1,6 +1,7 @@ class HackSet < ApplicationRecord - validates :input, presence: { message: "测试集输入不能为空" } + #validates :input, presence: { message: "测试集输入不能为空" } validates :output, presence: { message: "测试集输出不能为空" } + validates_uniqueness_of :input, scope: [:hack_id, :input], message: "多个测试集的输入不能相同" # 编程题测试集 belongs_to :hack end diff --git a/app/models/hack_user_code.rb b/app/models/hack_user_code.rb index 072fd4e31..eee394e39 100644 --- a/app/models/hack_user_code.rb +++ b/app/models/hack_user_code.rb @@ -1,6 +1,7 @@ class HackUserCode < ApplicationRecord # 用户编程题的信息 belongs_to :hack + belongs_to :hack_user_lastest_code scope :created_order, ->{ order("created_at desc")} end diff --git a/app/models/hack_user_lastest_code.rb b/app/models/hack_user_lastest_code.rb index b4a707603..830f16dde 100644 --- a/app/models/hack_user_lastest_code.rb +++ b/app/models/hack_user_lastest_code.rb @@ -8,7 +8,7 @@ class HackUserLastestCode < ApplicationRecord belongs_to :user has_many :hack_user_codes, dependent: :destroy has_one :hack_user_debug - scope :mine, ->(author_id){ find_by(user_id: author_id) } + scope :mine, ->(author_id){ where(user_id: author_id).first } scope :mine_hack, ->(author_id){ where(user_id: author_id) } scope :passed, -> {where(status: 1)} diff --git a/app/models/homework_common.rb b/app/models/homework_common.rb index d37650c37..58b52bdd2 100644 --- a/app/models/homework_common.rb +++ b/app/models/homework_common.rb @@ -108,7 +108,7 @@ class HomeworkCommon < ApplicationRecord # 是否在补交阶段内 def late_duration - homework_setting = self.homework_group_setting(User.current.id) + homework_setting = self.homework_group_setting(User.current.id, true) !course.is_end && self.publish_time && self.publish_time < Time.now && homework_setting.end_time && homework_setting.end_time < Time.now && self.allow_late && (self.late_time.nil? || self.late_time > Time.now) end @@ -119,7 +119,7 @@ class HomeworkCommon < ApplicationRecord if self.course.is_end || (self.allow_late && self.late_time && self.late_time < Time.now) status = true elsif !self.allow_late - homework_setting = self.homework_group_setting(User.current.id) + homework_setting = self.homework_group_setting(User.current.id, true) status = homework_setting.end_time && homework_setting.end_time < Time.now end status @@ -241,14 +241,8 @@ class HomeworkCommon < ApplicationRecord self.teacher_works(member).delay_finished.count end - # 未通关数 - def un_complete_count member - teacher_works(member).count - complete_count(member) - end - - # 通关数 - def complete_count member - Myshixun.where(id: self.teacher_works(member).pluck(:myshixun_id), status: 1).count + def compelete_status_count member, status + teacher_works(member).where(compelete_status: status).count end # 分组作业的最大分组id @@ -257,12 +251,13 @@ class HomeworkCommon < ApplicationRecord end # 作业的分班设置时间 - def homework_group_setting user_id + def homework_group_setting user_id, current_user=false if unified_setting homework_setting = self else - member = course.course_member(user_id) - group_setting = self.homework_group_settings.find_by_course_group_id(member.try(:course_group_id)) + # 当前用户是从course_member中取,否则是从学生中取(双重身份的原因) + member = current_user ? course.course_member(user_id) : course.students.find_by(user_id: user_id) + group_setting = self.homework_group_settings.select{ |setting| setting.course_group_id == member.try(:course_group_id)}.first homework_setting = group_setting.present? ? group_setting : self end homework_setting diff --git a/app/models/item_analysis.rb b/app/models/item_analysis.rb new file mode 100644 index 000000000..8f6e71302 --- /dev/null +++ b/app/models/item_analysis.rb @@ -0,0 +1,3 @@ +class ItemAnalysis < ApplicationRecord + belongs_to :item_bank +end diff --git a/app/models/item_bank.rb b/app/models/item_bank.rb new file mode 100644 index 000000000..8078a55e0 --- /dev/null +++ b/app/models/item_bank.rb @@ -0,0 +1,11 @@ +class ItemBank < ApplicationRecord + # difficulty: 1 简单 2 适中 3 困难 + # item_type: 0 单选 1 多选 2 判断 3 填空 4 简答 5 实训 6 编程 + enum item_type: { SINGLE: 0, MULTIPLE: 1, JUDGMENT: 2, COMPLETION: 3, SUBJECTIVE: 4, PRACTICAL: 5, PROGRAM: 6 } + + belongs_to :user + + has_one :item_analysis, dependent: :destroy + has_many :item_choices, dependent: :destroy + has_many :item_baskets, dependent: :destroy +end diff --git a/app/models/item_basket.rb b/app/models/item_basket.rb new file mode 100644 index 000000000..d736d9bc0 --- /dev/null +++ b/app/models/item_basket.rb @@ -0,0 +1,4 @@ +class ItemBasket < ApplicationRecord + belongs_to :item_bank + belongs_to :user +end diff --git a/app/models/item_choice.rb b/app/models/item_choice.rb new file mode 100644 index 000000000..ccc35698e --- /dev/null +++ b/app/models/item_choice.rb @@ -0,0 +1,3 @@ +class ItemChoice < ApplicationRecord + belongs_to :item_bank +end diff --git a/app/models/laboratory.rb b/app/models/laboratory.rb index 2ba86ed90..dab2f6f39 100644 --- a/app/models/laboratory.rb +++ b/app/models/laboratory.rb @@ -38,12 +38,20 @@ class Laboratory < ApplicationRecord find_by_identifier(subdomain) end - def self.current=(laboratory) - Thread.current[:current_laboratory] = laboratory + # def self.current=(laboratory) + # Thread.current[:current_laboratory] = laboratory + # end + # + # def self.current + # Thread.current[:current_laboratory] ||= Laboratory.find(1) + # end + + def self.current=(user) + RequestStore.store[:current_laboratory] = user end def self.current - Thread.current[:current_laboratory] ||= Laboratory.find(1) + RequestStore.store[:current_laboratory] ||= User.anonymous end def shixuns diff --git a/app/models/laboratory_setting.rb b/app/models/laboratory_setting.rb index 4eacaf460..e53b54cd3 100644 --- a/app/models/laboratory_setting.rb +++ b/app/models/laboratory_setting.rb @@ -67,6 +67,7 @@ class LaboratorySetting < ApplicationRecord { 'name' => '在线竞赛', 'link' => '/competitions', 'hidden' => false }, { 'name' => '教学案例', 'link' => '/moop_cases', 'hidden' => false }, { 'name' => '交流问答', 'link' => '/forums', 'hidden' => false }, + { 'name' => '开发者社区', 'link' => '/problems', 'hidden' => false }, ], footer: nil } diff --git a/app/models/myshixun.rb b/app/models/myshixun.rb index 54dcf9011..1a89c755e 100644 --- a/app/models/myshixun.rb +++ b/app/models/myshixun.rb @@ -28,6 +28,11 @@ class Myshixun < ApplicationRecord "#{self.repo_name}.git" end + + def repo_save_path + self.repo_name.split('/').last + end + def is_complete? self.status == 1 end @@ -83,9 +88,15 @@ class Myshixun < ApplicationRecord self.games.select{|game| game.status == 2}.size end - # 查看答案的关卡数 + # 指定时间前完成的关卡数 + def time_passed_count time + time.present? ? self.games.select{|game| game.status == 2 && game.end_time < time}.size : 0 + end + + # 查看答案的关卡数,只统计通关前看的关卡 def view_answer_count - self.games.select{|game| game.status == 2 && game.answer_open != 0}.size + answer_ids = user.grades.joins("join games on grades.container_id = games.id").where("container_type = 'Answer' and games.status=2 and games.end_time > grades.created_at").pluck(:container_id) + self.games.select{|game| game.status == 2 && game.answer_open != 0 && answer_ids.include?(game.id)}.size end # 通关时间 diff --git a/app/models/searchable/shixun.rb b/app/models/searchable/shixun.rb index c574ecb1d..359b8b4dc 100644 --- a/app/models/searchable/shixun.rb +++ b/app/models/searchable/shixun.rb @@ -52,7 +52,8 @@ module Searchable::Shixun challenges_count: challenges_count, study_count: myshixuns_count, star: averge_star, - level: shixun_level + level: shixun_level, + is_jupyter: is_jupyter } end diff --git a/app/models/shixun.rb b/app/models/shixun.rb index 0f9842739..de014f87b 100644 --- a/app/models/shixun.rb +++ b/app/models/shixun.rb @@ -3,12 +3,14 @@ class Shixun < ApplicationRecord attr_accessor :page_no #管理员页面 实训配置更新状态时,需要接受page_no参数 # status: 0:编辑 1:申请发布 2:正式发布 3:关闭 -1:软删除 + # public: 0:未公开 1:申请公开 2:公开 # hide_code: 隐藏代码窗口 # code_hidden: 隐藏代码目录 # task_pass: 跳关 # webssh 0:不开启webssh;1:开启练习模式; 2:开启评测模式 # trainee 实训的难度 # vnc: VCN实训是否用于评测 + validates_presence_of :name, message: "实训名称不能为空" has_many :challenges, -> {order("challenges.position asc")}, dependent: :destroy has_many :challenge_tags, through: :challenges has_many :myshixuns, :dependent => :destroy @@ -54,6 +56,8 @@ class Shixun < ApplicationRecord has_many :laboratory_shixuns, dependent: :destroy belongs_to :laboratory, optional: true + # Jupyter数据集,附件 + has_many :data_sets, ->{where(attachtype: 2)}, class_name: 'Attachment', as: :container, dependent: :destroy scope :search_by_name, ->(keyword) { where("name like ? or description like ? ", "%#{keyword}%", "%#{keyword}%") } @@ -76,6 +80,7 @@ class Shixun < ApplicationRecord scope :published_closed, lambda{ where(status: [2, 3]) } scope :none_closed, lambda{ where(status: [0, 1, 2]) } scope :unhidden, lambda{ where(hidden: 0, status: 2) } + scope :publiced, lambda{ where(public: 2) } scope :field_for_recommend, lambda{ select([:id, :name, :identifier, :myshixuns_count]) } scope :find_by_ids,lambda{|k| where(id:k)} @@ -280,7 +285,7 @@ class Shixun < ApplicationRecord end def has_manager?(user) - return true if user.admin? + return true if user.admin? || user.business? shixun_members.where(role: [1, 2]).exists?(user_id: user.id) end diff --git a/app/models/shixun_info.rb b/app/models/shixun_info.rb index 74a49412e..321b4c44a 100644 --- a/app/models/shixun_info.rb +++ b/app/models/shixun_info.rb @@ -1,8 +1,6 @@ class ShixunInfo < ApplicationRecord belongs_to :shixun validates_uniqueness_of :shixun_id - validates_presence_of :shixun_id - after_commit :create_diff_record private diff --git a/app/models/shixun_mirror_repository.rb b/app/models/shixun_mirror_repository.rb index 9376aac0b..841be6bb2 100644 --- a/app/models/shixun_mirror_repository.rb +++ b/app/models/shixun_mirror_repository.rb @@ -2,4 +2,5 @@ class ShixunMirrorRepository < ApplicationRecord belongs_to :shixun belongs_to :mirror_repository validates_uniqueness_of :shixun_id, :scope => :mirror_repository_id + validates_presence_of :shixun_id, :mirror_repository_id end diff --git a/app/models/shixun_service_config.rb b/app/models/shixun_service_config.rb index 6d106fc07..4dda75a25 100644 --- a/app/models/shixun_service_config.rb +++ b/app/models/shixun_service_config.rb @@ -1,4 +1,6 @@ class ShixunServiceConfig < ApplicationRecord belongs_to :shixun belongs_to :mirror_repository + + validates_presence_of :shixun_id, :mirror_repository_id end diff --git a/app/models/student_work.rb b/app/models/student_work.rb index d4f372823..4da23a30f 100644 --- a/app/models/student_work.rb +++ b/app/models/student_work.rb @@ -123,6 +123,26 @@ class StudentWork < ApplicationRecord end end + # 实训作业的作品状态 0:未提交,1:未通关,2:按时通关(提交截止前通关),3:迟交通关(提交截止-补交截止间通关) + def real_work_status + status = work_status + if status > 0 && myshixun + if myshixun.status != 1 + status = 1 + else + homework_end_time = homework_common.homework_group_setting(user_id)&.end_time + if homework_end_time.present? && homework_end_time > myshixun.passed_time + status = 2 + elsif homework_end_time.present? && homework_common.allow_late && homework_common.late_time > myshixun.passed_time + status = 3 + else + status = 1 + end + end + end + status + end + # 更新作品成绩 def set_work_score if work_status > 0 && homework_common && !self.ultimate_score diff --git a/app/models/user.rb b/app/models/user.rb index 0a16b7aa3..f25ecc4b8 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -546,12 +546,20 @@ class User < ApplicationRecord mail.present? end + # def self.current=(user) + # Thread.current[:current_user] = user + # end + # + # def self.current + # Thread.current[:current_user] ||= User.anonymous + # end + def self.current=(user) - Thread.current[:current_user] = user + RequestStore.store[:current_user] = user end def self.current - Thread.current[:current_user] ||= User.anonymous + RequestStore.store[:current_user] ||= User.anonymous end def self.anonymous diff --git a/app/queries/admins/school_query.rb b/app/queries/admins/school_query.rb index 888cded97..3206f0858 100644 --- a/app/queries/admins/school_query.rb +++ b/app/queries/admins/school_query.rb @@ -17,6 +17,7 @@ class Admins::SchoolQuery < ApplicationQuery if keyword schools = schools.where('schools.name LIKE ?', "%#{keyword}%") end + schools = schools.left_joins(:user_extensions).select('schools.*, IFNULL(count(user_extensions.user_id),0) users_count').group('schools.id') custom_sort schools, params[:sort_by], params[:sort_direction] end end \ No newline at end of file diff --git a/app/queries/admins/shixun_modify_record_query.rb b/app/queries/admins/shixun_modify_record_query.rb new file mode 100644 index 000000000..227e2f4c2 --- /dev/null +++ b/app/queries/admins/shixun_modify_record_query.rb @@ -0,0 +1,33 @@ +class Admins::ShixunModifyRecordQuery < ApplicationQuery + attr_reader :params + + def initialize(params) + @params = params + end + + def call + if params[:user_name].blank? || params[:date].blank? + records = DiffRecord.none + else + records = DiffRecord.joins(:user).where("concat(users.lastname, users.firstname) like ?", "%#{params[:user_name].strip}%") + if time_range.present? + records = records.where(created_at: time_range) + end + end + records.order("diff_records.created_at desc") + end + + private + + def time_range + @_time_range ||= begin + case params[:date] + when 'weekly' then 1.weeks.ago..Time.now + when 'monthly' then 1.months.ago..Time.now + when 'quarterly' then 3.months.ago..Time.now + when 'yearly' then 1.years.ago..Time.now + else '' + end + end + end +end diff --git a/app/queries/admins/shixun_query.rb b/app/queries/admins/shixun_query.rb index 0d726f267..0f8523599 100644 --- a/app/queries/admins/shixun_query.rb +++ b/app/queries/admins/shixun_query.rb @@ -21,7 +21,17 @@ class Admins::ShixunQuery < ApplicationQuery [0,1,2,3] end + public = + case params[:public] + when "editing" then [0] + when "pending" then [1] + when "processed" then [2] + else + [0,1,2] + end + all_shixuns = all_shixuns.where(status: status) if status.present? + all_shixuns = all_shixuns.where(public: public) if public.present? if params[:tag].present? all_shixuns = all_shixuns.joins(:mirror_repositories).where("mirror_repositories.id = ?",params[:tag].to_i) diff --git a/app/queries/admins/shixun_settings_query.rb b/app/queries/admins/shixun_settings_query.rb index ab871b58e..377e7bf60 100644 --- a/app/queries/admins/shixun_settings_query.rb +++ b/app/queries/admins/shixun_settings_query.rb @@ -51,6 +51,7 @@ class Admins::ShixunSettingsQuery < ApplicationQuery all_shixuns = all_shixuns.where(homepage_show: params[:homepage_show]) if params[:homepage_show] all_shixuns = all_shixuns.where(task_pass: params[:task_pass]) if params[:task_pass] all_shixuns = all_shixuns.where(code_hidden: params[:code_hidden]) if params[:code_hidden] + all_shixuns = all_shixuns.where(vip: params[:vip]) if params[:vip] custom_sort(all_shixuns, params[:sort_by], params[:sort_direction]) end diff --git a/app/services/admins/check_shixun_mirrors_service.rb b/app/services/admins/check_shixun_mirrors_service.rb index 8334df485..4aa0af4cf 100644 --- a/app/services/admins/check_shixun_mirrors_service.rb +++ b/app/services/admins/check_shixun_mirrors_service.rb @@ -75,13 +75,12 @@ class Admins::CheckShixunMirrorsService < ApplicationService def bridge_images @_bridge_images ||= begin - url = EduSetting.get('cloud_bridge') + url = "#{EduSetting.get('cloud_bridge')}/bridge/docker/images" res = Faraday.get(url) - res_body = JSON.parse(res.body) + res = JSON.parse(res.body) + raise Error, '拉取镜像信息异常' if res && res['code'] != 0 - raise Error, '拉取镜像信息异常' if res_body && res_body['code'].to_i != 0 - - res_body + res rescue => e Rails.logger.error("get response failed ! #{e.message}") raise Error, '实训云平台繁忙(繁忙等级:84)' diff --git a/app/services/admins/shixun_auths/agree_apply_service.rb b/app/services/admins/shixun_auths/agree_apply_service.rb index 4734e03bb..b8875cf09 100644 --- a/app/services/admins/shixun_auths/agree_apply_service.rb +++ b/app/services/admins/shixun_auths/agree_apply_service.rb @@ -10,7 +10,7 @@ class Admins::ShixunAuths::AgreeApplyService < ApplicationService def call ActiveRecord::Base.transaction do apply.update!(status: 1, dealer_id: user.id) - shixun.update!(status: 2, publish_time: Time.now) + shixun.update!(public: 2, publish_time: Time.now) # 奖励金币、经验 reward_grade_and_experience! diff --git a/app/services/admins/shixun_auths/refuse_apply_service.rb b/app/services/admins/shixun_auths/refuse_apply_service.rb index 49416a2b0..76d420e53 100644 --- a/app/services/admins/shixun_auths/refuse_apply_service.rb +++ b/app/services/admins/shixun_auths/refuse_apply_service.rb @@ -10,7 +10,7 @@ class Admins::ShixunAuths::RefuseApplyService < ApplicationService def call ActiveRecord::Base.transaction do - shixun.update!(status: 0) + shixun.update!(public: 0) apply.update!(status: 2, reason: reason, dealer_id: user.id) deal_tiding! diff --git a/app/services/create_diff_record_service.rb b/app/services/create_diff_record_service.rb index 8365404e2..3943af1ae 100644 --- a/app/services/create_diff_record_service.rb +++ b/app/services/create_diff_record_service.rb @@ -25,21 +25,23 @@ class CreateDiffRecordService < ApplicationService index = 0 fragment_size = 1 Diffy::Diff.new(before, after).each do |line| - unless line =~ /^[\+-]/ - if arr.empty? && index < fragment_size - content += line - index += 1 - else - index = 0 - arr << line - arr.shift if arr.size > fragment_size + unless line.include?("\\ 文件尾没有 newline 字符") + unless line =~ /^[\+-]/ + if arr.empty? && index < fragment_size + content += line + index += 1 + else + index = 0 + arr << line + arr.shift if arr.size > fragment_size + end + next end - next - end - content += arr.join('') if arr.present? - content += line - arr.clear + content += arr.join('') if arr.present? + content += line + arr.clear + end end content end diff --git a/app/services/git_service.rb b/app/services/git_service.rb index 076f62920..544e830a4 100644 --- a/app/services/git_service.rb +++ b/app/services/git_service.rb @@ -45,4 +45,4 @@ class GitService end end -end \ No newline at end of file +end diff --git a/app/services/homeworks_service.rb b/app/services/homeworks_service.rb index 0dc814c89..487a79bfe 100644 --- a/app/services/homeworks_service.rb +++ b/app/services/homeworks_service.rb @@ -322,11 +322,16 @@ class HomeworksService work.efficiency = format("%.2f", efficiency) if myshixun_endtime <= homework_end_or_late_time - work.compelete_status = myshixun_endtime < setting_time.publish_time ? 2 : 1 + # 2是按时通关, 3是迟交通关 + work.compelete_status = myshixun_endtime < setting_time.end_time ? 2 : 3 # 如果作业的最大效率值有变更则更新所有作品的效率分 homework.update_column("max_efficiency", work.efficiency) if homework.work_efficiency && homework.max_efficiency < work.efficiency + else + work.compelete_status = 1 # 未通关 end + else + work.compelete_status = 1 # 未通关 end work.late_penalty = work.work_status == 2 ? homework.late_penalty : 0 @@ -346,7 +351,7 @@ class HomeworksService work.work_score = format("%.2f",(score < 0 ? 0 : score).to_f) unless work.ultimate_score #logger.info("#############work_score: #{score}") work.calculation_time = Time.now - work.save! + work.save(validate: false) end end end \ No newline at end of file diff --git a/app/services/jupyter_service.rb b/app/services/jupyter_service.rb new file mode 100644 index 000000000..0f5af6cb8 --- /dev/null +++ b/app/services/jupyter_service.rb @@ -0,0 +1,195 @@ +#coding=utf-8 + +module JupyterService + + def _open_shixun_jupyter(shixun) + if shixun.is_jupyter? + shixun_tomcat = edu_setting('cloud_bridge') + uri = "#{shixun_tomcat}/bridge/jupyter/get" + tpiID = "tpm#{shixun.id}" + mount = shixun.data_sets.present? + params = {tpiID: tpiID, identifier: shixun.identifier, needMount: mount, + :containers => "#{Base64.urlsafe_encode64(shixun_container_limit(shixun))}"} + + logger.info "test_juypter: uri->#{uri}, params->#{params}" + res = uri_post uri, params + if res && res['code'].to_i != 0 + raise("实训云平台繁忙(繁忙等级:99)") + end + + logger.info "test_juypter: #{res}" + + @shixun_jupyter_port = res['port'] + + "#{jupyter_service(res['port'])}/notebooks/data/workspace/myshixun_#{tpiID}/01.ipynb" + end + end + + def jupyter_url_with_shixun(shixun) + #打开tpm - juypter接口 + _open_shixun_jupyter(shixun) + end + + def jupyter_port_with_shixun(shixun) + if @shixun_jupyter_port.to_i <=0 + _open_shixun_jupyter(shixun) + end + @shixun_jupyter_port + end + + + def _open_game_jupyter(myshixun) + ## 打开tpi + shixun = myshixun.shixun + + if shixun.is_jupyter? + shixun_tomcat = edu_setting('cloud_bridge') + uri = "#{shixun_tomcat}/bridge/jupyter/get" + + tpiID = myshixun.id + mount = myshixun.shixun.data_sets.present? + params = {tpiID: tpiID, identifier: shixun.identifier, myshixunIdentifier: myshixun.identifier, needMount: mount, + :containers => "#{Base64.urlsafe_encode64(shixun_container_limit(shixun))}"} + res = uri_post uri, params + + logger.info "test_juypter: #{res}" + + if res && res['code'].to_i != 0 + raise("实训云平台繁忙(繁忙等级:99)") + end + + @game_jupyter_port = res['port'] + + repo_save_path = myshixun.repo_save_path + + "#{jupyter_service(res['port'])}/notebooks/data/workspace/myshixun_#{tpiID}/#{repo_save_path}/01.ipynb" + end + end + + + def jupyter_url_with_game(myshixun) + _open_game_jupyter(myshixun) + end + + def jupyter_port_with_game(myshixun) + if @game_jupyter_port.to_i <=0 + _open_game_jupyter(myshixun) + end + @game_jupyter_port + end + + def jupyter_save_with_shixun(shixun,jupyter_port) + author_name = current_user.real_name + author_email = current_user.git_mail + message = "User submitted" + + tpiID = "tpm#{shixun.id}" + + src_url = "#{jupyter_service(jupyter_port)}/nbconvert/notebook/data/workspace/myshixun_#{tpiID}/01.ipynb?download=true" + + response = Faraday.get(src_url) + + if response.status.to_i != 200 + raise("获取文件内容失败:#{response.status}") + end + + content = response.body.force_encoding('utf-8') + + c = GitService.update_file(repo_path: shixun.repo_path, + file_path: "01.ipynb", + message: message, + content: content, + author_name: author_name, + author_email: author_email) + + return c.size + end + + def jupyter_save_with_game(myshixun,jupyter_port) + author_name = current_user.real_name + author_email = current_user.git_mail + message = "User submitted" + + tpiID = myshixun.id + + repo_save_path = myshixun.repo_save_path + src_url = "#{jupyter_service(jupyter_port)}/nbconvert/notebook/data/workspace/myshixun_#{tpiID}/#{repo_save_path}/01.ipynb?download=true" + response = Faraday.get(src_url) + + if response.status.to_i != 200 + raise("获取文件内容失败:#{response.status}") + end + + content = response.body.force_encoding('utf-8') + + c = GitService.update_file(repo_path: myshixun.repo_path, + file_path: "01.ipynb", + message: message, + content: content, + author_name: author_name, + author_email: author_email) + + return c.size + end + + + ##重置jupyter环境 + def jupyter_tpi_reset(myshixun) + jupyter_delete_tpi(myshixun) + url = jupyter_url_with_game(myshixun) + port = jupyter_port_with_game(myshixun) + {url: url, port: port} + end + + ## 重置tpm环境 + def jupyter_tpm_reset(shixun) + jupyter_delete_tpm(shixun) + + url = jupyter_url_with_shixun(shixun) + port = jupyter_port_with_shixun(shixun) + + {url: url, port: port} + end + + + + # 删除pod + def jupyter_delete_tpi(myshixun) + myshixun_id = myshixun.id + digest = myshixun.identifier + edu_setting('bridge_secret_key') + digest_key = Digest::SHA1.hexdigest("#{digest}") + begin + shixun_tomcat = edu_setting('cloud_bridge') + uri = "#{shixun_tomcat}/bridge/jupyter/delete" + Rails.logger.info("#{current_user} => cloese_jupyter digest is #{digest}") + params = {:tpiID => myshixun_id, :digestKey => digest_key, :identifier => myshixun.identifier} + res = uri_post uri, params + if res && res['code'].to_i != 0 + raise("实训云平台繁忙(繁忙等级:110)") + end + end + end + + + def jupyter_delete_tpm(shixun) + tpiID = "tpm#{shixun.id}" + digest = shixun.identifier + edu_setting('bridge_secret_key') + digest_key = Digest::SHA1.hexdigest("#{digest}") + begin + shixun_tomcat = edu_setting('cloud_bridge') + uri = "#{shixun_tomcat}/bridge/jupyter/delete" + Rails.logger.info("#{current_user} => cloese_jupyter digest is #{digest}") + params = {:tpiID => tpiID, :digestKey => digest_key, :identifier => shixun.identifier} + res = uri_post uri, params + if res && res['code'].to_i != 0 + raise("实训云平台繁忙(繁忙等级:110)") + end + end + end + + def jupyter_service jupyter_port + edu_setting('jupyter_service').gsub("PORT", jupyter_port) + end + + +end diff --git a/app/services/search_service.rb b/app/services/search_service.rb index 5eb11f398..a45875e07 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -30,7 +30,6 @@ class SearchService < ApplicationService model_options = { includes: modal_name.searchable_includes } - model_options.deep_merge!(where: { status: 2 }) if modal_name == Shixun model_options.deep_merge!(extra_options) model_options.deep_merge!(default_options) @@ -40,7 +39,7 @@ class SearchService < ApplicationService def extra_options case params[:type].to_s.strip when 'shixun' then - { where: { id: Laboratory.current.shixuns.pluck(:id) } } + { where: { id: Laboratory.current.shixuns.where(public: 2, status: 2, fork_from: nil).or(Laboratory.current.shixuns.where(status: 2, id: User.current.shixuns)).pluck(:id) } } when 'subject' then { where: { id: Laboratory.current.subjects.pluck(:id) } } when 'course' then diff --git a/app/services/shixun_search_service.rb b/app/services/shixun_search_service.rb index 580208dfe..f5af69179 100644 --- a/app/services/shixun_search_service.rb +++ b/app/services/shixun_search_service.rb @@ -25,7 +25,7 @@ class ShixunSearchService < ApplicationService else none_shixun_ids = ShixunSchool.where("school_id != #{User.current.school_id}").pluck(:shixun_id) - @shixuns = @shixuns.where.not(id: none_shixun_ids).where(hidden: 0) + @shixuns = @shixuns.where.not(id: none_shixun_ids).where(hidden: 0, status: 2, public: 2).or(@shixuns.where(id: User.current.shixuns)) end end @@ -38,6 +38,7 @@ class ShixunSearchService < ApplicationService @shixuns = @shixuns.where(trainee: params[:diff]) end + Rails.logger.info("search_shixun_ids: #{@shixuns.pluck(:id)}") Shixun.search(keyword, search_options) end diff --git a/app/services/shixuns/create_shixun_service.rb b/app/services/shixuns/create_shixun_service.rb new file mode 100644 index 000000000..c85455901 --- /dev/null +++ b/app/services/shixuns/create_shixun_service.rb @@ -0,0 +1,115 @@ +class CreateShixunService < ApplicationService + attr_reader :user, :params, :permit_params + + def initialize(user, permit_params, params) + @user = user + @params = params + @permit_params = permit_params + end + + def call + shixun = Shixun.new(permit_params) + identifier = Util::UUID.generate_identifier(Shixun, 8) + shixun.identifier= identifier + shixun.user_id = user.id + main_mirror = MirrorRepository.find params[:main_type] + sub_mirrors = MirrorRepository.where(id: params[:sub_type]) + begin + ActiveRecord::Base.transaction do + shixun.save! + # 获取脚本内容 + shixun_script = get_shixun_script(shixun, main_mirror, sub_mirrors) + # 创建额外信息 + ShixunInfo.create!(shixun_id: shixun.id, evaluate_script: shixun_script, description: params[:description]) + # 创建合作者 + shixun.shixun_members.create!(user_id: user.id, role: 1) + # 创建镜像 + ShixunMirrorRepository.create!(:shixun_id => shixun.id, :mirror_repository_id => main_mirror.id) + # 创建主服务配置 + ShixunServiceConfig.create!(:shixun_id => shixun.id, :mirror_repository_id => main_mirror.id) + # 创建子镜像相关数据(实训镜像关联表,子镜像服务配置) + sub_mirrors.each do |sub| + ShixunMirrorRepository.create!(:shixun_id => shixun.id, :mirror_repository_id => sub.id) + # 实训子镜像服务配置 + name = sub.name #查看镜像是否有名称,如果没有名称就不用服务配置 + ShixunServiceConfig.create!(:shixun_id => shixun.id, :mirror_repository_id => sub.id) if name.present? + end + # 创建版本库 + repo_path = repo_namespace(user.login, shixun.identifier) + GitService.add_repository(repo_path: repo_path) + shixun.update_column(:repo_name, repo_path.split(".")[0]) + # 如果是云上实验室,创建相关记录 + if !Laboratory.current.main_site? + Laboratory.current.laboratory_shixuns.create!(shixun: shixun, ownership: true) + end + # 如果是jupyter,先创建一个目录,为了挂载(因为后续数据集,开启Pod后环境在没销毁前,你上传数据集是挂载不上目录的,因此要先创建目录,方便中间层挂载) + if shixun.is_jupyter? + folder = EduSetting.get('shixun_folder') + path = "#{folder}/#{identifier}" + FileUtils.mkdir_p(path, :mode => 0777) unless File.directory?(path) + end + return shixun + end + rescue => e + Rails.logger.error("shixun_create_error: #{e.message}") + raise("创建实训失败!") + end + end + + private + + def get_shixun_script shixun, main_mirror, sub_mirrors + if !shixun.is_jupyter? + mirror = main_mirror.mirror_scripts + if main_mirror.blank? + modify_shixun_script shixun, mirror.first&.(:script) + else + sub_name = sub_mirrors.pluck(:type_name) + if main_mirror.type_name == "Java" && sub_name.include?("Mysql") + mirror.last.try(:script) + else + shixun_script = mirror.first&.script + modify_shixun_script shixun, shixun_script + end + end + end + end + + def modify_shixun_script shixun, script + if script.present? + source_class_name = [] + challenge_program_name = [] + shixun.challenges.map(&:exec_path).each do |exec_path| + challenge_program_name << "\"#{exec_path}\"" + if shixun.main_mirror_name == "Java" + if exec_path.nil? || exec_path.split("src/")[1].nil? + source = "\"\"" + else + source = "\"#{exec_path.split("src/")[1].split(".java")[0]}\"" + end + logger.info("----source: #{source}") + source_class_name << source.gsub("/", ".") if source.present? + elsif shixun.main_mirror_name.try(:first) == "C#" + if exec_path.nil? || exec_path.split(".")[1].nil? + source = "\"\"" + else + source = "\"#{exec_path.split(".")[0]}.exe\"" + end + source_class_name << source if source.present? + end + end + script = if script.include?("sourceClassName") && script.include?("challengeProgramName") + script.gsub(/challengeProgramNames=\(.*\)/,"challengeProgramNames=\(#{challenge_program_name.reject(&:blank?).join(" ")}\)").gsub(/sourceClassNames=\(.*\)/, "sourceClassNames=\(#{source_class_name.reject(&:blank?).join(" ")}\)") + else + script.gsub(/challengeProgramNames=\(.*\)/,"challengeProgramNames=\(#{challenge_program_name.reject(&:blank?).join(" ")}\)").gsub(/sourceClassNames=\(.*\)/, "sourceClassNames=\(#{challenge_program_name.reject(&:blank?).join(" ")}\)") + end + end + return script + end + + # 版本库目录空间 + def repo_namespace(user, shixun_identifier) + "#{user}/#{shixun_identifier}.git" + end + +end diff --git a/app/services/subjects/copy_subject_service.rb b/app/services/subjects/copy_subject_service.rb index 94157dc7b..f44191fda 100644 --- a/app/services/subjects/copy_subject_service.rb +++ b/app/services/subjects/copy_subject_service.rb @@ -60,7 +60,7 @@ class Subjects::CopySubjectService < ApplicationService shixun = stage_shixun.shixun to_shixun = Shixun.new to_shixun.attributes = shixun.attributes.dup.except('id', 'user_id', 'identifier', 'homepage_show', - 'use_scope', 'averge_star', 'myshixuns_count', 'challenges_count') + 'use_scope', 'averge_star', 'myshixuns_count', 'challenges_count', "public") to_shixun.identifier = Util::UUID.generate_identifier(Shixun, 8) to_shixun.user_id = user.id if laboratory diff --git a/app/services/update_homework_publish_setting_service.rb b/app/services/update_homework_publish_setting_service.rb index 00d618522..69a68b613 100644 --- a/app/services/update_homework_publish_setting_service.rb +++ b/app/services/update_homework_publish_setting_service.rb @@ -1,4 +1,4 @@ -class UpdateHomeworkSettingService < ApplicationService +class UpdateHomeworkPublishSettingService < ApplicationService attr_reader :homework, :params def initialize(homework, params) @@ -7,6 +7,7 @@ class UpdateHomeworkSettingService < ApplicationService end def call + puts params course = homework.course # 作业未发布时,unified_setting参数不能为空 if homework.publish_time.nil? || homework.publish_time > Time.now @@ -37,9 +38,9 @@ class UpdateHomeworkSettingService < ApplicationService tip_exception("分班id不能为空") if setting[:group_id].length == 0 tip_exception("发布时间不能为空") if setting[:publish_time].blank? tip_exception("截止时间不能为空") if setting[:end_time].blank? - tip_exception("发布时间不能早于当前时间") if setting[:publish_time] <= strf_time(Time.now) - tip_exception("截止时间不能早于当前时间") if setting[:end_time] <= strf_time(Time.now) - tip_exception("截止时间不能早于发布时间") if setting[:publish_time] > setting[:end_time] + tip_exception("发布时间不能早于当前时间") if setting[:publish_time].to_time <= Time.now + tip_exception("截止时间不能早于当前时间") if setting[:end_time].to_time <= Time.now + tip_exception("截止时间不能早于发布时间") if setting[:publish_time].to_time > setting[:end_time].to_time tip_exception("截止时间不能晚于课堂结束时间(#{course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if course.end_date.present? && setting[:end_time] > course.end_date.end_of_day @@ -74,15 +75,15 @@ class UpdateHomeworkSettingService < ApplicationService else if homework.end_time > Time.now && homework.unified_setting tip_exception("截止时间不能为空") if params[:end_time].blank? - tip_exception("截止时间不能早于当前时间") if params[:end_time] <= strf_time(Time.now) + tip_exception("截止时间不能早于当前时间") if params[:end_time].to_time <= Time.now tip_exception("截止时间不能晚于课堂结束时间(#{course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if - course.end_date.present? && params[:end_time] > strf_time(course.end_date.end_of_day) + course.end_date.present? && params[:end_time].to_time > course.end_date.end_of_day homework.end_time = params[:end_time] elsif !homework.unified_setting homework.create_homework_group_settings - tip_exception("分班发布设置不能为空") if params[:group_settings].blank? + tip_exception("分班发布设置不能为空") if params[:group_settings].reject(&:blank?).blank? params[:group_settings].each do |setting| group_settings = HomeworkGroupSetting.where(homework_common_id: homework.id, course_group_id: setting[:group_id]) @@ -90,12 +91,12 @@ class UpdateHomeworkSettingService < ApplicationService tip_exception("发布时间不能为空") if setting[:publish_time].blank? tip_exception("截止时间不能为空") if setting[:end_time].blank? # 如果该发布规则 没有已发布的分班则需判断发布时间 - tip_exception("发布时间不能早于等于当前时间") if setting[:publish_time] <= strf_time(Time.now) && group_settings.group_published.count == 0 + tip_exception("发布时间不能早于等于当前时间") if setting[:publish_time].to_time <= Time.now && group_settings.group_published.count == 0 - tip_exception("截止时间不能早于等于当前时间") if setting[:end_time] <= strf_time(Time.now) && group_settings.none_end.count > 0 - tip_exception("截止时间不能早于发布时间") if setting[:publish_time] > setting[:end_time] + tip_exception("截止时间不能早于等于当前时间") if setting[:end_time].to_time <= Time.now && group_settings.none_end.count > 0 + tip_exception("截止时间不能早于发布时间") if setting[:publish_time].to_time > setting[:end_time].to_time tip_exception("截止时间不能晚于课堂结束时间(#{course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if - course.end_date.present? && setting[:end_time] > strf_time(course.end_date.end_of_day) + course.end_date.present? && setting[:end_time].to_time > course.end_date.end_of_day group_settings.none_published.update_all(publish_time: setting[:publish_time]) group_settings.none_end.update_all(end_time: setting[:end_time]) @@ -107,4 +108,9 @@ class UpdateHomeworkSettingService < ApplicationService homework.save! HomeworkCommonPushNotifyJob.perform_later(homework.id, publish_group_ids) if send_tiding end + + private + def tip_exception(status = -1, message) + raise Educoder::TipException.new(status, message) + end end \ No newline at end of file diff --git a/app/services/users/shixun_service.rb b/app/services/users/shixun_service.rb index ef399ce8c..279d147f4 100644 --- a/app/services/users/shixun_service.rb +++ b/app/services/users/shixun_service.rb @@ -44,7 +44,7 @@ class Users::ShixunService def user_policy_filter(relations) # 只有自己或者管理员才有过滤筛选及查看全部状态下实训功能 if self_or_admin? - relations = relations.where.not(status: -1) + relations = relations.where.not(status: -1).where(hidden: false) status_filter(relations) else relations.where(status: [2, 3], hidden: false) diff --git a/app/services/users/subject_service.rb b/app/services/users/subject_service.rb index 8a4fdce3b..53ff3f4b9 100644 --- a/app/services/users/subject_service.rb +++ b/app/services/users/subject_service.rb @@ -35,7 +35,7 @@ class Users::SubjectService def user_policy_filter(relations) # 只有自己或者管理员才有过滤筛选及查看全部状态下实训功能 if self_or_admin? - status_filter(relations) + status_filter(relations.unhidden) else relations.where(status: 2, hidden: false) end diff --git a/app/templates/shared/main.css b/app/templates/shared/main.css index 09d295421..704246b74 100644 --- a/app/templates/shared/main.css +++ b/app/templates/shared/main.css @@ -557,7 +557,6 @@ a.user_orangebg_btn{background-color:#FF6800;color: #fff;} a.user_greybg_btn{background-color:#747A7F;color: #fff;} /*.user_white_btn{border: 1px solid #ffffff;color: #ffffff!important;}*/ - .pointer{cursor: pointer} .cdefault{cursor: default} diff --git a/app/views/admins/courses/shared/_list.html.erb b/app/views/admins/courses/shared/_list.html.erb index 633616462..4105c8153 100644 --- a/app/views/admins/courses/shared/_list.html.erb +++ b/app/views/admins/courses/shared/_list.html.erb @@ -1,30 +1,31 @@ + - + - + - - + + <% if courses.present? %> - <% courses.each do |course| %> + <% courses.each_with_index do |course, index| %> - <%= render 'admins/courses/shared/td', course: course %> + <%= render partial: 'admins/courses/shared/td', locals: {course: course, no: index} %> <% end %> <% else %> diff --git a/app/views/admins/courses/shared/_td.html.erb b/app/views/admins/courses/shared/_td.html.erb index 1d2da2a33..51cc4b199 100644 --- a/app/views/admins/courses/shared/_td.html.erb +++ b/app/views/admins/courses/shared/_td.html.erb @@ -1,3 +1,4 @@ +
序号 ID 课堂名称成员成员 资源 普通作业 分组作业 实训作业 试卷评测次数评测次数 私有 状态 单位 创建者 <%= sort_tag('创建时间', name: 'created_at', path: admins_courses_path) %> 首页邮件通知操作邮件通知操作
<%= list_index_no((params[:page] || 1).to_i, no) %> <%= course.id %> <%= link_to(course.name, "/courses/#{course.id}", target: '_blank') %> diff --git a/app/views/admins/courses/update.js.erb b/app/views/admins/courses/update.js.erb index 1278a2e1f..983ac22f0 100644 --- a/app/views/admins/courses/update.js.erb +++ b/app/views/admins/courses/update.js.erb @@ -1 +1,3 @@ -$("#course-item-<%= @course.id %>").html("<%= j render partial: "admins/courses/shared/td",locals: {course: @course} %>") \ No newline at end of file +var index = $("#course-item-<%= @course.id %>").children(":first").html(); +$("#course-item-<%= @course.id %>").html("<%= j render partial: "admins/courses/shared/td",locals: {course: @course, no: 1} %>"); +$("#course-item-<%= @course.id %>").children(":first").html(index); \ No newline at end of file diff --git a/app/views/admins/customers/shared/_list.html.erb b/app/views/admins/customers/shared/_list.html.erb index 0ba5638cb..6f84db4e5 100644 --- a/app/views/admins/customers/shared/_list.html.erb +++ b/app/views/admins/customers/shared/_list.html.erb @@ -1,15 +1,17 @@ - + + <% if customers.present? %> - <% customers.each do |customer| %> + <% customers.each_with_index do |customer, index| %> +
客户名称序号客户名称 <%= sort_tag('添加时间', name: 'created_at', path: admins_partner_customers_path(current_partner)) %> 操作
<%= list_index_no((params[:page] || 1).to_i, index) %> <%= customer.school&.name %> <%= customer.created_at&.strftime('%Y-%m-%d %H:%M') %> diff --git a/app/views/admins/daily_school_statistics/shared/_list.html.erb b/app/views/admins/daily_school_statistics/shared/_list.html.erb index 611acdd2a..6982891ee 100644 --- a/app/views/admins/daily_school_statistics/shared/_list.html.erb +++ b/app/views/admins/daily_school_statistics/shared/_list.html.erb @@ -1,8 +1,8 @@ + - @@ -16,13 +16,14 @@ - + <% if statistics.present? %> - <% statistics.each do |statistic| %> + <% statistics.each_with_index do |statistic, index| %> +
序号 单位名称<%= sort_tag('教师总数', name: 'teacher_count', path: admins_daily_school_statistics_path) %> <%= sort_tag('学生总数', name: 'student_count', path: admins_daily_school_statistics_path) %> <%= sort_tag('课堂总数', name: 'course_count', path: admins_daily_school_statistics_path) %> <%= sort_tag('实训作业总数', name: 'homework_count', path: admins_daily_school_statistics_path) %> <%= sort_tag('其它作业总数', name: 'other_homework_count', path: admins_daily_school_statistics_path) %><%= sort_tag('动态时间', name: 'nearly_course_time', path: admins_daily_school_statistics_path) %><%= sort_tag('动态时间', name: 'nearly_course_time', path: admins_daily_school_statistics_path) %>
<%= list_index_no(@params_page.to_i, index) %> <%= link_to statistic[:name], "/colleges/#{statistic[:id]}/statistics", target: '_blank', data: { toggle: 'tooltip', title: '点击查看学校统计概况' } %> diff --git a/app/views/admins/department_applies/shared/_list.html.erb b/app/views/admins/department_applies/shared/_list.html.erb index 0a1d803be..87d5ab66f 100644 --- a/app/views/admins/department_applies/shared/_list.html.erb +++ b/app/views/admins/department_applies/shared/_list.html.erb @@ -1,18 +1,20 @@ + - + <% if applies.present? %> - <% applies.each do |apply| %> + <% applies.each_with_index do |apply, index| %> + diff --git a/app/views/admins/department_members/create.js.erb b/app/views/admins/department_members/create.js.erb index 4355c7432..6bf0a6ac3 100644 --- a/app/views/admins/department_members/create.js.erb +++ b/app/views/admins/department_members/create.js.erb @@ -1,4 +1,6 @@ $('.modal.admin-add-department-member-modal').modal('hide'); $.notify({ message: '操作成功' }); -$('.department-list-table .department-item-<%= current_department.id %>').html("<%= j(render partial: 'admins/departments/shared/department_item', locals: { department: current_department }) %>") \ No newline at end of file +var index = $(".department-item-<%= current_department.id %>").children(":first").html(); +$('.department-list-table .department-item-<%= current_department.id %>').html("<%= j(render partial: 'admins/departments/shared/department_item', locals: { department: current_department, index: 1 }) %>"); +$(".department-item-<%= current_department.id %>").children(":first").html(index); \ No newline at end of file diff --git a/app/views/admins/departments/shared/_department_item.html.erb b/app/views/admins/departments/shared/_department_item.html.erb index 64b4ee70b..62f3a81ac 100644 --- a/app/views/admins/departments/shared/_department_item.html.erb +++ b/app/views/admins/departments/shared/_department_item.html.erb @@ -1,3 +1,4 @@ + <% not_list = defined?(:users_count) %> diff --git a/app/views/admins/departments/shared/_list.html.erb b/app/views/admins/departments/shared/_list.html.erb index 6af63d6f4..09ba2a65f 100644 --- a/app/views/admins/departments/shared/_list.html.erb +++ b/app/views/admins/departments/shared/_list.html.erb @@ -1,10 +1,11 @@
序号 ID 部门名称 单位名称创建者创建者 <%= sort_tag('创建于', name: 'created_at', path: admins_department_applies_path) %> 操作
<%= list_index_no((params[:page] || 1).to_i, index) %> <%= apply.id %> <%= apply.name %> <%= apply.school.try(:name) %><%= list_index_no((params[:page] || 1).to_i, index) %><%= overflow_hidden_span department.name, width: 150 %>
+ - + @@ -14,9 +15,9 @@ <% if departments.present? %> - <% departments.each do |department| %> + <% departments.each_with_index do |department, index| %> - <%= render 'admins/departments/shared/department_item', department: department %> + <%= render partial: 'admins/departments/shared/department_item', locals: {department: department, index: index} %> <% end %> <% else %> diff --git a/app/views/admins/departments/update.js.erb b/app/views/admins/departments/update.js.erb index 359bac59c..d20ca9524 100644 --- a/app/views/admins/departments/update.js.erb +++ b/app/views/admins/departments/update.js.erb @@ -1,4 +1,6 @@ $('.modal.admin-edit-department-modal').modal('hide'); $.notify({ message: '操作成功' }); -$('.department-list-table .department-item-<%= current_department.id %>').html("<%= j(render partial: 'admins/departments/shared/department_item', locals: { department: current_department }) %>") \ No newline at end of file +var index = $(".department-item-<%= current_department.id %>").children(":first").html(); +$('.department-list-table .department-item-<%= current_department.id %>').html("<%= j(render partial: 'admins/departments/shared/department_item', locals: {department: current_department, index: 1}) %>"); +$(".department-item-<%= current_department.id %>").children(":first").html(index); \ No newline at end of file diff --git a/app/views/admins/identity_authentications/shared/_list.html.erb b/app/views/admins/identity_authentications/shared/_list.html.erb index 607feb93a..6e12d1c54 100644 --- a/app/views/admins/identity_authentications/shared/_list.html.erb +++ b/app/views/admins/identity_authentications/shared/_list.html.erb @@ -3,6 +3,7 @@
序号 部门名称 单位名称 用户数已职业认证已职业认证 部门管理员 统计链接 云主机数
+ <% unless is_processed %> - + <% unless is_processed %> <% if applies.present? %> - <% applies.each do |apply| %> + <% applies.each_with_index do |apply, index| %> <% user = apply.user %> + <% unless is_processed %> <% end %> diff --git a/app/views/admins/laboratories/shared/_laboratory_item.html.erb b/app/views/admins/laboratories/shared/_laboratory_item.html.erb index 81a45ca71..765616926 100644 --- a/app/views/admins/laboratories/shared/_laboratory_item.html.erb +++ b/app/views/admins/laboratories/shared/_laboratory_item.html.erb @@ -1,4 +1,5 @@ <% school = laboratory.school %> +
序号 <%= check_box_tag('all-check', 1, false, id: nil, class: 'batch-all-check-box', @@ -13,7 +14,7 @@ 姓名 身份证号 学校/单位职称职称 照片 @@ -33,9 +34,10 @@
<%= list_index_no((params[:page] || 1).to_i, index) %><%= check_box_tag('ids[]', apply.id, false, id: nil, class: 'batch-check-box') %><%= list_index_no((params[:page] || 1).to_i, index) %> <%= school&.name || 'EduCoder主站' %> <% if laboratory.identifier %> diff --git a/app/views/admins/laboratories/shared/_list.html.erb b/app/views/admins/laboratories/shared/_list.html.erb index 2520b3276..a731be44a 100644 --- a/app/views/admins/laboratories/shared/_list.html.erb +++ b/app/views/admins/laboratories/shared/_list.html.erb @@ -1,6 +1,7 @@ + @@ -9,14 +10,14 @@ - + <% if laboratories.present? %> - <% laboratories.each do |laboratory| %> + <% laboratories.each_with_index do |laboratory, index| %> - <%= render 'admins/laboratories/shared/laboratory_item', laboratory: laboratory %> + <%= render partial: 'admins/laboratories/shared/laboratory_item', locals: {laboratory: laboratory, index: index} %> <% end %> <% else %> diff --git a/app/views/admins/laboratories/update.js.erb b/app/views/admins/laboratories/update.js.erb index 1b9f307a6..149539ad6 100644 --- a/app/views/admins/laboratories/update.js.erb +++ b/app/views/admins/laboratories/update.js.erb @@ -1 +1,3 @@ -$(".laboratory-item-<%= @laboratory.id %>").html("<%= j render partial: "admins/laboratories/shared/laboratory_item",locals: {laboratory: @laboratory} %>") \ No newline at end of file +var index = $(".laboratory-item-<%= @laboratory.id %>").children(":first").html(); +$(".laboratory-item-<%= @laboratory.id %>").html("<%= j render partial: "admins/laboratories/shared/laboratory_item",locals: {laboratory: @laboratory, index: 1} %>"); +$(".laboratory-item-<%= @laboratory.id %>").children(":first").html(index); \ No newline at end of file diff --git a/app/views/admins/laboratories/update_sync_course.js.erb b/app/views/admins/laboratories/update_sync_course.js.erb index 5ae68f673..663f56ee1 100644 --- a/app/views/admins/laboratories/update_sync_course.js.erb +++ b/app/views/admins/laboratories/update_sync_course.js.erb @@ -1 +1,3 @@ -$("#laboratory-item-<%= @laboratory.id %>").html("<%= j render partial: 'admins/laboratories/shared/laboratory_item', locals: {laboratory: @laboratory} %>") \ No newline at end of file +var index = $(".laboratory-item-<%= @laboratory.id %>").children(":first").html(); +$("#laboratory-item-<%= @laboratory.id %>").html("<%= j render partial: 'admins/laboratories/shared/laboratory_item', locals: {laboratory: @laboratory, index: 1} %>"); +$(".laboratory-item-<%= @laboratory.id %>").children(":first").html(index); \ No newline at end of file diff --git a/app/views/admins/laboratory_shixuns/shared/_list.html.erb b/app/views/admins/laboratory_shixuns/shared/_list.html.erb index e1244c472..462486f4b 100644 --- a/app/views/admins/laboratory_shixuns/shared/_list.html.erb +++ b/app/views/admins/laboratory_shixuns/shared/_list.html.erb @@ -1,21 +1,22 @@
序号 单位名称 域名 统计链接同步课堂 同步实践课程 同步实训操作操作
- + + - + <% if laboratory_shixuns.present? %> - <% laboratory_shixuns.each do |laboratory_shixun| %> + <% laboratory_shixuns.each_with_index do |laboratory_shixun, index| %> - <%= render partial: 'admins/laboratory_shixuns/shared/td', locals: { laboratory_shixun: laboratory_shixun } %> + <%= render partial: 'admins/laboratory_shixuns/shared/td', locals: { laboratory_shixun: laboratory_shixun, index: index } %> <% end %> <% else %> diff --git a/app/views/admins/laboratory_shixuns/shared/_td.html.erb b/app/views/admins/laboratory_shixuns/shared/_td.html.erb index dbdf0df75..d987b53b4 100644 --- a/app/views/admins/laboratory_shixuns/shared/_td.html.erb +++ b/app/views/admins/laboratory_shixuns/shared/_td.html.erb @@ -1,5 +1,6 @@ <%- shixun = laboratory_shixun.shixun -%> +
实训名称序号实训名称 技术平台 技术体系 封面 创建者状态状态 执行时间 操作
<%= list_index_no((params[:page] || 1).to_i, index) %> <%= link_to "/shixuns/#{shixun.identifier}", target: '_blank' do %> <%= shixun.name %> diff --git a/app/views/admins/laboratory_subjects/shared/_list.html.erb b/app/views/admins/laboratory_subjects/shared/_list.html.erb index 55f67dea8..c40d02260 100644 --- a/app/views/admins/laboratory_subjects/shared/_list.html.erb +++ b/app/views/admins/laboratory_subjects/shared/_list.html.erb @@ -1,7 +1,8 @@ - + + @@ -13,10 +14,11 @@ <% if laboratory_subjects.present? %> - <% laboratory_subjects.each do |laboratory_subject| %> + <% laboratory_subjects.each_with_index do |laboratory_subject, index| %> <%- subject = laboratory_subject.subject -%> +
课程名称序号课程名称 技术体系 等级体系 封面
<%= list_index_no((params[:page] || 1).to_i, index) %> <%= link_to(subject.name, "/paths/#{subject.id}", target: '_blank') %> 首页 diff --git a/app/views/admins/laboratory_users/create.js.erb b/app/views/admins/laboratory_users/create.js.erb index f43fd7887..604e62943 100644 --- a/app/views/admins/laboratory_users/create.js.erb +++ b/app/views/admins/laboratory_users/create.js.erb @@ -1,4 +1,6 @@ $('.modal.admin-add-laboratory-user-modal').modal('hide'); $.notify({ message: '操作成功' }); -$('.laboratory-list-table .laboratory-item-<%= current_laboratory.id %>').html("<%= j(render partial: 'admins/laboratories/shared/laboratory_item', locals: { laboratory: current_laboratory }) %>") \ No newline at end of file +var index = $(".laboratory-item-<%= current_laboratory.id %>").children(":first").html(); +$('.laboratory-list-table .laboratory-item-<%= current_laboratory.id %>').html("<%= j(render partial: 'admins/laboratories/shared/laboratory_item', locals: {laboratory: current_laboratory, index: 1}) %>"); +$(".laboratory-item-<%= current_laboratory.id %>").children(":first").html(index); \ No newline at end of file diff --git a/app/views/admins/library_applies/shared/_list.html.erb b/app/views/admins/library_applies/shared/_list.html.erb index 783d0db30..fde3d1d1a 100644 --- a/app/views/admins/library_applies/shared/_list.html.erb +++ b/app/views/admins/library_applies/shared/_list.html.erb @@ -3,11 +3,12 @@ + - + <% if is_processed %> @@ -18,10 +19,11 @@ <% if applies.present? %> - <% applies.each do |apply| %> + <% applies.each_with_index do |apply, index| %> <% user = apply.library.user %> <% library = apply.library %> +
序号 头像 姓名 教学案例 案例描述时间时间拒绝原因 状态
<%= list_index_no((params[:page] || 1).to_i, index) %> <%= link_to "/users/#{user.login}", class: 'professional-authentication-avatar', target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } do %> diff --git a/app/views/admins/myshixuns/shared/_list.html.erb b/app/views/admins/myshixuns/shared/_list.html.erb index 440437320..af43c3c22 100644 --- a/app/views/admins/myshixuns/shared/_list.html.erb +++ b/app/views/admins/myshixuns/shared/_list.html.erb @@ -1,9 +1,10 @@ + - + @@ -14,14 +15,23 @@ <% if myshixuns.present? %> - <% myshixuns.each do |myshixun| %> + <% myshixuns.each_with_index do |myshixun, index| %> + diff --git a/app/views/admins/partners/shared/_list.html.erb b/app/views/admins/partners/shared/_list.html.erb index 0bebc2a4b..71153c175 100644 --- a/app/views/admins/partners/shared/_list.html.erb +++ b/app/views/admins/partners/shared/_list.html.erb @@ -1,15 +1,17 @@
序号 ID 标识实训名称实训名称 实训老师 完成 经验值
<%= list_index_no(@params_page.to_i, index) %> <%= myshixun.id %> <%= myshixun.identifier %> - <% current_task = myshixun.last_executable_task || myshixun.last_task %> - <%= link_to "/tasks/#{current_task.identifier}", target: '_blank' do %> - <%= overflow_hidden_span myshixun.shixun.name, width: 280 %> + <% if myshixun.shixun.is_jupyter? %> + <%= link_to "/tasks/#{myshixun.identifier}/jupyter", target: '_blank' do %> + <%= overflow_hidden_span myshixun.shixun.name, width: 280 %> + <% end %> + <% else %> + <% current_task = myshixun.last_executable_task || myshixun.last_task %> + <% if current_task %> + <%= link_to "/tasks/#{current_task.identifier}", target: '_blank' do %> + <%= overflow_hidden_span myshixun.shixun.name, width: 280 %> + <% end %> + <% end %> <% end %> <%= myshixun.shixun.user.real_name %>
- + + <% if partners.present? %> - <% partners.each do |partner| %> + <% partners.each_with_index do |partner, index| %> + diff --git a/app/views/admins/professional_authentications/shared/_list.html.erb b/app/views/admins/professional_authentications/shared/_list.html.erb index 6ec6355ba..7ffa5837e 100644 --- a/app/views/admins/professional_authentications/shared/_list.html.erb +++ b/app/views/admins/professional_authentications/shared/_list.html.erb @@ -3,6 +3,7 @@
名称序号名称 <%= sort_tag('添加时间', name: 'created_at', path: admins_partners_path) %> 操作
<%= list_index_no((params[:page] || 1).to_i, index) %> <%= link_to partner.school&.name || partner.name, customers_partner_path(partner), target: '_blank' %>
+ <% unless is_processed %> - + <% unless is_processed %> <% if applies.present? %> - <% applies.each do |apply| %> + <% applies.each_with_index do |apply, index| %> <% user = apply.user %> + <% unless is_processed %> <% end %> diff --git a/app/views/admins/project_package_applies/shared/_list.html.erb b/app/views/admins/project_package_applies/shared/_list.html.erb index d94c96184..bec0912ca 100644 --- a/app/views/admins/project_package_applies/shared/_list.html.erb +++ b/app/views/admins/project_package_applies/shared/_list.html.erb @@ -3,11 +3,12 @@
序号 <%= check_box_tag('all-check', 1, false, id: nil, class: 'batch-all-check-box', @@ -12,7 +13,7 @@ 头像 姓名 学校/单位职称职称 照片 @@ -31,9 +32,10 @@
<%= list_index_no((params[:page] || 1).to_i, index) %><%= check_box_tag('ids[]', apply.id, false, id: nil, class: 'batch-check-box') %>
+ - + <% if is_processed %> @@ -18,10 +19,11 @@ <% if applies.present? %> - <% applies.each do |apply| %> + <% applies.each_with_index do |apply, index| %> <% package = apply.project_package %> <% user = package.creator %> +
序号 头像 姓名 众包需求 需求描述时间时间拒绝原因 状态
<%= list_index_no((params[:page] || 1).to_i, index) %> <%= link_to "/users/#{user.login}", class: 'professional-authentication-avatar', target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } do %> diff --git a/app/views/admins/projects/shared/_list.html.erb b/app/views/admins/projects/shared/_list.html.erb index 8a84a72d0..e2a6307ad 100644 --- a/app/views/admins/projects/shared/_list.html.erb +++ b/app/views/admins/projects/shared/_list.html.erb @@ -1,6 +1,7 @@ + @@ -11,14 +12,15 @@ - + <% if projects.present? %> - <% projects.each do |project| %> + <% projects.each_with_index do |project, index| %> +
序号 ID 项目名称 公开里程碑 成员 管理员<%= sort_tag('创建时间', name: 'created_at', path: admins_projects_path) %><%= sort_tag('创建时间', name: 'created_at', path: admins_projects_path) %> 操作
<%= list_index_no((params[:page] || 1).to_i, index) %> <%= project.id %> <%= link_to(project.name, "/projects/#{project.id}", target: '_blank') %> diff --git a/app/views/admins/school_statistics/shared/_list.html.erb b/app/views/admins/school_statistics/shared/_list.html.erb index aa043f097..9d2a1c209 100644 --- a/app/views/admins/school_statistics/shared/_list.html.erb +++ b/app/views/admins/school_statistics/shared/_list.html.erb @@ -20,7 +20,8 @@ - + + @@ -32,8 +33,9 @@ <% if statistics.present? %> - <% statistics.each do |statistic| %> + <% statistics.each_with_index do |statistic, index| %> +
单位名称序号单位名称 <%= sort_tag('新增教师', name: 'teacher_increase_count', path: admins_school_statistics_path) %> <%= sort_tag('新增学生', name: 'student_increase_count', path: admins_school_statistics_path) %> <%= sort_tag('新增课堂', name: 'course_increase_count', path: admins_school_statistics_path) %>
<%= list_index_no(@params_page.to_i, index) %> <%= link_to statistic.school_name, "/colleges/#{statistic.school_id}/statistics", target: '_blank', data: { toggle: 'tooltip', title: '点击查看学校统计概况' } %> diff --git a/app/views/admins/schools/index.html.erb b/app/views/admins/schools/index.html.erb index 9c41067ee..56cbadd9d 100644 --- a/app/views/admins/schools/index.html.erb +++ b/app/views/admins/schools/index.html.erb @@ -4,7 +4,7 @@
<%= form_tag(admins_schools_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %> - <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '部门名称检索') %> + <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '单位名称检索') %> <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> <% end %> diff --git a/app/views/admins/schools/shared/_list.html.erb b/app/views/admins/schools/shared/_list.html.erb index ec7475028..dd0a054e3 100644 --- a/app/views/admins/schools/shared/_list.html.erb +++ b/app/views/admins/schools/shared/_list.html.erb @@ -1,6 +1,7 @@ + @@ -11,13 +12,14 @@ - + <% if schools.present? %> - <% schools.each do |school| %> + <% schools.each_with_index do |school, index| %> + - +
序号 ID LOGO 标识码<%= sort_tag('用户数', name: 'users_count', path: admins_schools_path) %> 部门数 <%= sort_tag('创建时间', name: 'created_at', path: admins_schools_path) %>操作操作
<%= list_index_no((params[:page] || 1).to_i, index) %> <%= school.id %> <% if Util::FileManage.exists?(school) %> @@ -31,7 +33,7 @@ <%= school.province %> <%= school.city %> <%= school.address %><%= school.user_extensions.count %><%= school.users_count %> <%= @department_count.fetch(school.id, 0) %> <%= school.created_at&.strftime('%Y-%m-%d %H:%M') %> diff --git a/app/views/admins/shared/_sidebar.html.erb b/app/views/admins/shared/_sidebar.html.erb index df344fd1a..4b84068e7 100644 --- a/app/views/admins/shared/_sidebar.html.erb +++ b/app/views/admins/shared/_sidebar.html.erb @@ -27,8 +27,9 @@
  • <%= sidebar_item(admins_shixun_settings_path, '实训配置', icon: 'cog', controller: 'admins-shixun_settings') %>
  • <%= sidebar_item(admins_mirror_repositories_path, '镜像管理', icon: 'cubes', controller: 'admins-mirror_repositories') %>
  • <%= sidebar_item(admins_myshixuns_path, '学员实训列表', icon: 'server', controller: 'admins-myshixuns') %>
  • -
  • <%= sidebar_item(admins_shixun_recycles_path, '实训回收站', icon: 'recycle', controller: 'admins-myshixuns') %>
  • - <% end %> +
  • <%= sidebar_item(admins_shixun_modify_records_path, '实训修改记录', icon: 'eraser', controller: 'admins-shixun_modify_records') %>
  • +
  • <%= sidebar_item(admins_shixun_recycles_path, '实训回收站', icon: 'recycle', controller: 'admins-shixun_recycles') %>
  • + <% end %>
  • diff --git a/app/views/admins/shixun_authorizations/shared/_list.html.erb b/app/views/admins/shixun_authorizations/shared/_list.html.erb index c1dec5f45..c3828ec86 100644 --- a/app/views/admins/shixun_authorizations/shared/_list.html.erb +++ b/app/views/admins/shixun_authorizations/shared/_list.html.erb @@ -3,11 +3,12 @@ + - + <% if is_processed %> @@ -19,12 +20,13 @@ <% if applies.present? %> - <% applies.each do |apply| %> + <% applies.each_with_index do |apply, index| %> <% user = apply.user %> <% shixun = shixun_map[apply.container_id] %> <% content_review = shixun.shixun_reviews.select{|sr| sr.review_type == 'Content'}.first %> <% perference_review = shixun.shixun_reviews.select{|sr| sr.review_type == 'Performance'}.first %> + <% identifier = Game.find_by(challenge_id: discuss.challenge_id, user_id: discuss.user_id)&.identifier %> - + diff --git a/app/views/admins/shixun_modify_records/index.html.erb b/app/views/admins/shixun_modify_records/index.html.erb new file mode 100644 index 000000000..cecba30cb --- /dev/null +++ b/app/views/admins/shixun_modify_records/index.html.erb @@ -0,0 +1,25 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('实训修改记录') %> +<% end %> + +
    + <%= form_tag(admins_shixun_modify_records_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %> +
    + + <%= text_field_tag :user_name, params[:user_name], class: 'form-control flex-1', placeholder: '真实姓名搜索' %> +
    + +
    + + <% data_arrs = [['最近一周', 'weekly'], ['最近一个月', 'monthly']] %> + <%= select_tag(:date, options_for_select(data_arrs, params[:date]), class: 'form-control') %> +
    + + <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> + + <% end %> +
    + +
    + <%= render partial: 'admins/shixun_modify_records/shared/list', locals: { records: @records } %> +
    \ No newline at end of file diff --git a/app/views/admins/shixun_modify_records/index.js.erb b/app/views/admins/shixun_modify_records/index.js.erb new file mode 100644 index 000000000..fc9dc033b --- /dev/null +++ b/app/views/admins/shixun_modify_records/index.js.erb @@ -0,0 +1 @@ +$('.shixun-modify-record-list-container').html("<%= j( render partial: 'admins/shixun_modify_records/shared/list', locals: { records: @records } ) %>"); \ No newline at end of file diff --git a/app/views/admins/shixun_modify_records/shared/_list.html.erb b/app/views/admins/shixun_modify_records/shared/_list.html.erb new file mode 100644 index 000000000..525583d8e --- /dev/null +++ b/app/views/admins/shixun_modify_records/shared/_list.html.erb @@ -0,0 +1,36 @@ +<% if records.present? %> + <% records.each do |record| %> +
    +
    + <%= record.user.real_name %> + <%= format_time record.created_at %> +
    + +
    + 实训名称:<%= record.container&.shixun&.name %> + <% if record.container_type == "Challenge" %> + / + 关卡名:<%= record.container&.subject %> + <% end %> +
    + +
    +
      + <% record.diff_record_content&.content&.split("\n").each do |line| %> + <% if line =~ /^[\+]/ %> +
    • <%= line %>
    • + <% elsif line =~ /^[\-]/ %> +
    • <%= line %>
    • + <% else %> +
    • <%= line %>
    • + <% end %> + <% end %> +
    +
    +
    + <% end %> +<% else %> + <%= render 'admins/shared/no_data_for_table' %> +<% end %> + +<%= render partial: 'admins/shared/paginate', locals: { objects: records } %> \ No newline at end of file diff --git a/app/views/admins/shixun_recycles/shared/_list.html.erb b/app/views/admins/shixun_recycles/shared/_list.html.erb index 60d2742e9..5518fc753 100644 --- a/app/views/admins/shixun_recycles/shared/_list.html.erb +++ b/app/views/admins/shixun_recycles/shared/_list.html.erb @@ -1,16 +1,18 @@
    序号 头像 创建者 实训名称 审核情况任务数任务数 时间拒绝原因
    <%= list_index_no((params[:page] || 1).to_i, index) %> <%= link_to "/users/#{user.login}", class: 'shixun-authorization-avatar', target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } do %> diff --git a/app/views/admins/shixun_feedback_messages/shared/_list.html.erb b/app/views/admins/shixun_feedback_messages/shared/_list.html.erb index af81f9e58..dfe140428 100644 --- a/app/views/admins/shixun_feedback_messages/shared/_list.html.erb +++ b/app/views/admins/shixun_feedback_messages/shared/_list.html.erb @@ -15,7 +15,7 @@ <%= (@params_page.to_i - 1) * 20 + index + 1 %><%= link_to discuss.dis.name, "/tasks/#{identifier}", target: '_blank'%><%= content_safe discuss.content %><%= content_safe discuss.content %> <%= discuss.user.show_real_name %> <%= format_time discuss.created_at %>
    + - + <% if shixuns.present? %> - <% shixuns.each do |shixun| %> + <% shixuns.each_with_index do |shixun, index| %> + diff --git a/app/views/admins/shixun_settings/index.html.erb b/app/views/admins/shixun_settings/index.html.erb index 7aab73f64..66286926a 100644 --- a/app/views/admins/shixun_settings/index.html.erb +++ b/app/views/admins/shixun_settings/index.html.erb @@ -65,6 +65,12 @@ 只看已隐藏文件目录 +
    + +
    <% end %> diff --git a/app/views/admins/shixun_settings/shared/_list.html.erb b/app/views/admins/shixun_settings/shared/_list.html.erb index 7591fe323..549c7edf4 100644 --- a/app/views/admins/shixun_settings/shared/_list.html.erb +++ b/app/views/admins/shixun_settings/shared/_list.html.erb @@ -1,12 +1,13 @@
    序号 ID 实训名称子站源子站源 创建者 <%= sort_tag('创建于', name: 'created_at', path: admins_shixun_recycles_path) %> 操作
    <%= list_index_no((params[:page] || 1).to_i, index) %> <%= shixun.identifier %> <%= link_to overflow_hidden_span(shixun.name), "/shixuns/#{shixun.identifier}", :target => "_blank", :title => shixun.name %> <%= shixun.laboratory&.school&.name %>
    - + + - - - + + + @@ -14,7 +15,7 @@ diff --git a/app/views/admins/shixun_settings/shared/_td.html.erb b/app/views/admins/shixun_settings/shared/_td.html.erb index f4a05f178..fc5af645a 100644 --- a/app/views/admins/shixun_settings/shared/_td.html.erb +++ b/app/views/admins/shixun_settings/shared/_td.html.erb @@ -1,3 +1,4 @@ + diff --git a/public/react/scripts/start.js b/public/react/scripts/start.js index 5c11cf35f..321148cd3 100644 --- a/public/react/scripts/start.js +++ b/public/react/scripts/start.js @@ -1,114 +1,114 @@ -'use strict'; - -// Do this as the first thing so that any code reading it knows the right env. -process.env.BABEL_ENV = 'development'; -process.env.NODE_ENV = 'development'; - - -// Makes the script crash on unhandled rejections instead of silently -// ignoring them. In the future, promise rejections that are not handled will -// terminate the Node.js process with a non-zero exit code. -process.on('unhandledRejection', err => { - throw err; -}); - -// Ensure environment variables are read. -require('../config/env'); - -const fs = require('fs'); -const chalk = require('chalk'); -const webpack = require('webpack'); -const WebpackDevServer = require('webpack-dev-server'); -const clearConsole = require('react-dev-utils/clearConsole'); -const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); -const { - choosePort, - createCompiler, - prepareProxy, - prepareUrls, -} = require('react-dev-utils/WebpackDevServerUtils'); -const openBrowser = require('react-dev-utils/openBrowser'); -const paths = require('../config/paths'); -const config = require('../config/webpack.config.dev'); -const createDevServerConfig = require('../config/webpackDevServer.config'); - -const useYarn = fs.existsSync(paths.yarnLockFile); -const isInteractive = process.stdout.isTTY; - -const portSetting = require(paths.appPackageJson).port -if ( portSetting ) { - process.env.port = portSetting -} - -// Warn and crash if required files are missing -if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { - process.exit(1); -} - -// Tools like Cloud9 rely on this. -const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3007; -const HOST = process.env.HOST || '0.0.0.0'; - -if (process.env.HOST) { - console.log( - chalk.cyan( - `Attempting to bind to HOST environment variable: ${chalk.yellow( - chalk.bold(process.env.HOST) - )}` - ) - ); - console.log( - `If this was unintentional, check that you haven't mistakenly set it in your shell.` - ); - console.log(`Learn more here: ${chalk.yellow('http://bit.ly/2mwWSwH')}`); - console.log(); -} - -// We attempt to use the default port but if it is busy, we offer the user to -// run on a different port. `choosePort()` Promise resolves to the next free port. -choosePort(HOST, DEFAULT_PORT) - .then(port => { - if (port == null) { - // We have not found a port. - return; - } - const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; - const appName = require(paths.appPackageJson).name; - const urls = prepareUrls(protocol, HOST, port); - // Create a webpack compiler that is configured with custom messages. - const compiler = createCompiler(webpack, config, appName, urls, useYarn); - // Load proxy config - const proxySetting = require(paths.appPackageJson).proxy; - console.log('-------------------------proxySetting:', proxySetting) - const proxyConfig = prepareProxy(proxySetting, paths.appPublic); - // Serve webpack assets generated by the compiler over a web sever. - const serverConfig = createDevServerConfig( - proxyConfig, - urls.lanUrlForConfig - ); - const devServer = new WebpackDevServer(compiler, serverConfig); - // Launch WebpackDevServer. - devServer.listen(port, HOST, err => { - if (err) { - return console.log(err); - } - if (isInteractive) { - clearConsole(); - } - console.log(chalk.cyan('Starting the development server...\n')); - openBrowser(urls.localUrlForBrowser); - }); - - ['SIGINT', 'SIGTERM'].forEach(function(sig) { - process.on(sig, function() { - devServer.close(); - process.exit(); - }); - }); - }) - .catch(err => { - if (err && err.message) { - console.log(err.message); - } - process.exit(1); - }); +'use strict'; + +// Do this as the first thing so that any code reading it knows the right env. +process.env.BABEL_ENV = 'development'; +process.env.NODE_ENV = 'development'; + + +// Makes the script crash on unhandled rejections instead of silently +// ignoring them. In the future, promise rejections that are not handled will +// terminate the Node.js process with a non-zero exit code. +process.on('unhandledRejection', err => { + throw err; +}); + +// Ensure environment variables are read. +require('../config/env'); + +const fs = require('fs'); +const chalk = require('chalk'); +const webpack = require('webpack'); +const WebpackDevServer = require('webpack-dev-server'); +const clearConsole = require('react-dev-utils/clearConsole'); +const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); +const { + choosePort, + createCompiler, + prepareProxy, + prepareUrls, +} = require('react-dev-utils/WebpackDevServerUtils'); +const openBrowser = require('react-dev-utils/openBrowser'); +const paths = require('../config/paths'); +const config = require('../config/webpack.config.dev'); +const createDevServerConfig = require('../config/webpackDevServer.config'); + +const useYarn = fs.existsSync(paths.yarnLockFile); +const isInteractive = process.stdout.isTTY; + +const portSetting = require(paths.appPackageJson).port +if ( portSetting ) { + process.env.port = portSetting +} + +// Warn and crash if required files are missing +if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { + process.exit(1); +} + +// Tools like Cloud9 rely on this. +const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3007; +const HOST = process.env.HOST || '0.0.0.0'; + +if (process.env.HOST) { + console.log( + chalk.cyan( + `Attempting to bind to HOST environment variable: ${chalk.yellow( + chalk.bold(process.env.HOST) + )}` + ) + ); + console.log( + `If this was unintentional, check that you haven't mistakenly set it in your shell.` + ); + console.log(`Learn more here: ${chalk.yellow('http://bit.ly/2mwWSwH')}`); + console.log(); +} + +// We attempt to use the default port but if it is busy, we offer the user to +// run on a different port. `choosePort()` Promise resolves to the next free port. +choosePort(HOST, DEFAULT_PORT) + .then(port => { + if (port == null) { + // We have not found a port. + return; + } + const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; + const appName = require(paths.appPackageJson).name; + const urls = prepareUrls(protocol, HOST, port); + // Create a webpack compiler that is configured with custom messages. + const compiler = createCompiler(webpack, config, appName, urls, useYarn); + // Load proxy config + const proxySetting = require(paths.appPackageJson).proxy; + console.log('-------------------------proxySetting:', proxySetting) + const proxyConfig = prepareProxy(proxySetting, paths.appPublic); + // Serve webpack assets generated by the compiler over a web sever. + const serverConfig = createDevServerConfig( + proxyConfig, + urls.lanUrlForConfig + ); + const devServer = new WebpackDevServer(compiler, serverConfig); + // Launch WebpackDevServer. + devServer.listen(port, HOST, err => { + if (err) { + return console.log(err); + } + if (isInteractive) { + clearConsole(); + } + console.log(chalk.cyan('Starting the development server...\n')); + openBrowser(urls.localUrlForBrowser); + }); + + ['SIGINT', 'SIGTERM'].forEach(function(sig) { + process.on(sig, function() { + devServer.close(); + process.exit(); + }); + }); + }) + .catch(err => { + if (err && err.message) { + console.log(err.message); + } + process.exit(1); + }); diff --git a/public/react/src/App.js b/public/react/src/App.js index 46a92166d..07fd13e49 100644 --- a/public/react/src/App.js +++ b/public/react/src/App.js @@ -2,7 +2,7 @@ import React, {Component} from 'react'; import './public-path'; import logo from './logo.svg'; import './App.css'; -import {LocaleProvider} from 'antd' +import {ConfigProvider} from 'antd' import zhCN from 'antd/lib/locale-provider/zh_CN'; import { BrowserRouter as Router, @@ -72,6 +72,7 @@ const Otherlogin=Loadable({ loading: Loading, }) + const Otherloginstart=Loadable({ loader: () => import('./modules/login/Otherloginstart'), loading: Loading, @@ -300,6 +301,11 @@ const Developer = Loadable({ loader: () => import('./modules/developer'), loading: Loading }) +// 学院统计 +const College = Loadable({ + loader: () => import('./college/College'), + loading: Loading +}) // 开发者编辑模块 const NewOrEditTask = Loadable({ @@ -316,6 +322,11 @@ const RecordDetail = Loadable({ loader: () => import('./modules/developer/recordDetail'), loading: Loading }); +// jupyter tpi +const JupyterTPI = Loadable({ + loader: () => import('./modules/tpm/jupyter'), + loading: Loading +}); // //个人竞赛报名 // const PersonalCompetit = Loadable({ // loader: () => import('./modules/competition/personal/PersonalCompetit.js'), @@ -358,7 +369,7 @@ class App extends Component { mydisplay:true, }) }; - + disableVideoContextMenu = () => { window.$( "body" ).on( "mousedown", "video", function(event) { if(event.which === 3) { @@ -483,7 +494,7 @@ class App extends Component { // console.log(mygetHelmetapi); return ( - + this.Modifyloginvalue()}> @@ -577,14 +588,14 @@ class App extends Component { { - + return () } }> { + (props) => { return () } }> @@ -603,20 +614,47 @@ class App extends Component { } } /> - + {/*/>*/} + () + }/> + {/* jupyter */} + { + return () + } + } + /> - - + {/**/} + {/**/} + + () + } + > + {/*列表页 实训项目列表*/} + {/**/} + + + () + } + > + + - {/*列表页*/} - {/*实训课程(原实训路径)*/} @@ -637,8 +675,8 @@ class App extends Component { () }/> - - () } @@ -671,21 +709,20 @@ class App extends Component { (props)=>() }/> - { - // debugger; - // console.log(this.props, props, this.state); - // return () - // } - // } + { + return () + } + } /> + () } /> - () } @@ -694,6 +731,7 @@ class App extends Component { render={ (props) => () } /> + () @@ -705,12 +743,12 @@ class App extends Component { (props)=>() } /> - + - + ); } @@ -827,4 +865,4 @@ moment.defineLocale('zh-cn', { doy: 4 // The week that contains Jan 4th is the first week of the year. } }); -export default SnackbarHOC()(App) ; \ No newline at end of file +export default SnackbarHOC()(App) ; diff --git a/public/react/src/AppConfig.js b/public/react/src/AppConfig.js index bd25f6977..ac39562a7 100644 --- a/public/react/src/AppConfig.js +++ b/public/react/src/AppConfig.js @@ -10,14 +10,13 @@ broadcastChannelOnmessage('refreshPage', () => { }) function locationurl(list){ - debugger if (window.location.port === "3007") { } else { - window.location.replace(list) + window.location.href=list } } -let hashTimeout +let hashTimeout // TODO 开发期多个身份切换 let debugType ="" @@ -31,6 +30,13 @@ if (isDev) { window.location.search.indexOf('debug=s') != -1 ? 'student' : window.location.search.indexOf('debug=a') != -1 ? 'admin' : parsed.debug || 'admin' } +// 超管 +// debugType="admin"; +// 老师 +//ebugType="teacher"; +// 学生 +// debugType="student"; + window._debugType = debugType; export function initAxiosInterceptors(props) { initOnlineOfflineListener() @@ -45,7 +51,8 @@ export function initAxiosInterceptors(props) { // proxy = "https://testeduplus2.educoder.net" //proxy="http://47.96.87.25:48080" proxy="https://pre-newweb.educoder.net" - proxy="https://test-newweb.educoder.net" + proxy="https://test-newweb.educoder.net" + //proxy="https://test-jupyterweb.educoder.net" //proxy="http://192.168.2.63:3001" // 在这里使用requestMap控制,避免用户通过双击等操作发出重复的请求; @@ -82,7 +89,6 @@ export function initAxiosInterceptors(props) { url = `${config.url}`; } } - if(`${config[0]}`!=`true`){ if (window.location.port === "3007") { // if (url.indexOf('.json') == -1) { @@ -103,15 +109,15 @@ export function initAxiosInterceptors(props) { } // // console.log(config); - if (config.method === "post") { - if (requestMap[config.url] === true) { // 避免重复的请求 导致页面f5刷新 也会被阻止 显示这个方法会影响到定制信息 - // console.log(config); - // console.log(JSON.parse(config)); - // console.log(config.url); - // console.log("被阻止了是重复请求================================="); - return false; - } - } + // if (config.method === "post") { + // if (requestMap[config.url] === true) { // 避免重复的请求 导致页面f5刷新 也会被阻止 显示这个方法会影响到定制信息 + // // console.log(config); + // // console.log(JSON.parse(config)); + // // console.log(config.url); + // // console.log("被阻止了是重复请求================================="); + // return false; + // } + // } // 非file_update请求 if (config.url.indexOf('update_file') === -1) { requestMap[config.url] = true; @@ -218,7 +224,7 @@ export function initAxiosInterceptors(props) { return Promise.reject(error); }); // ----------------------------------------------------------------------------------- - + } diff --git a/public/react/src/college/College.js b/public/react/src/college/College.js new file mode 100644 index 000000000..bc43252b7 --- /dev/null +++ b/public/react/src/college/College.js @@ -0,0 +1,1259 @@ +import React, {Component} from "react"; +import {Link, NavLink} from 'react-router-dom'; +import {WordsBtn, ActionBtn,SnackbarHOC,getImageUrl} from 'educoder'; +import axios from 'axios'; +import { + notification, + Spin, + Table, + Pagination, +} from "antd"; +import Colleagechart from './colleagechart/Colleagechart' +import Colleagechartzu from './colleagechart/Colleagechartzu' +import {TPMIndexHOC} from "../modules/tpm/TPMIndexHOC"; +import NoneData from './../modules/courses/coursesPublic/NoneData'; + +import './colleagecss/colleage.css'; +import Shixunechart from "../modules/courses/shixunHomework/shixunreport/Shixunechart"; +class College extends Component { + constructor(props) { + super(props); + // this.answerMdRef = React.createRef(); + this.state = { + coursesloading:false, + columns: [ + { + title: '名称', + dataIndex: 'name', + key: 'name', + align: 'center', + className: "edu-txt-center font-14 maxnamewidth247", + render: (text, record) => ( + { + record.name + } + ) + }, + { + title: '管理教师', + dataIndex: 'teachers', + key: 'teachers', + align: 'center', + className: "edu-txt-center font-14 maxnamewidth340", + render: (text, record) => ( + + { + record.teachers + } + + ) + }, + { + title: '评测次数', + dataIndex: 'times', + key: 'times', + align: 'center', + className: "edu-txt-center font-14 maxnamewidth175", + render: (text, record) => ( + + { + record.evaluating_count + } + + ), + }, + { + title: '学生', + key: 'student', + dataIndex: 'student', + align: 'center', + className: "edu-txt-center font-14 maxnamewidth255", + render: (text, record) => ( + + { + record.student_count + } + + ) + }, + { + title: '实训作业', + dataIndex: 'training', + key: 'training', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.shixun_work_count + } + + ) + + }, + { + title: '资源', + dataIndex: 'resources', + key: 'resources', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.attachment_count + } + + ), + }, + { + title: '帖子', + dataIndex: 'posts', + key: 'posts', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + { + record.message_count + } + ) + }, + { + title: '其它任务', + dataIndex: 'othertasks', + key: 'othertasks', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.other_work_count + } + + ) + }, + { + title: '状态', + dataIndex: 'states', + key: 'states', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.is_end? + "已结束" + : + "正在进行" + } + + ) + }, + { + title: '时间', + dataIndex: 'timemy', + key: 'timemy', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.activity_time + } + + ) + }, + ], + page:1, + limit:10, + total_users:50, + teachersloading:false, + teacherranking:[ + { + title: '排名', + dataIndex: 'ranking', + key: 'ranking', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.id + } + + ) + }, + { + title: '姓名', + dataIndex: 'name', + key: 'name', + align: 'center', + className: "edu-txt-center font-14 maxnamewidth105", + render: (text, record) => ( + { + record.name + } + + ) + }, + { + title: '管理课堂', + dataIndex: 'classroom', + key: 'classroom', + align: 'center', + className: "edu-txt-center font-14 maxnamewidth175", + render: (text, record) => ( + + { + record.course_count + } + + ), + }, + { + title: '已发布实训作业', + key: 'assignment', + dataIndex: 'assignment', + align: 'center', + className: "edu-txt-center font-14 maxnamewidth255", + render: (text, record) => ( + + { + record.shixun_work_count + } + + ) + }, + { + title: '未发布实训作业', + dataIndex: 'released', + key: 'released', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.un_shixun_work_count + } + + ) + + }, + { + title: '学生数', + dataIndex: 'studentnumber', + key: 'studentnumber', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.student_count + } + + ), + }, + { + title: '完成率', + dataIndex: 'completionrate', + key: 'completionrate', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.complete_rate+"%" + } + + ) + }, + { + title: '发布实训', + dataIndex: 'releasetraining', + key: 'releasetraining', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.publish_shixun_count + } + + ) + } + ], + studentranking:[ + { + title: '排名', + dataIndex: 'ranking', + key: 'ranking', + align: 'center', + className: "edu-txt-center font-14", + width:'100px', + render: (text, record) => ( + + { + record.id===1? + :record.id===2? + + :record.id===3? + + :record.id + } + + ) + }, + { + title: '姓名', + dataIndex: 'name', + key: 'name', + align: 'center', + className: "edu-txt-center font-14 maxnamewidth105", + width:'100px', + render: (text, record) => ( + { + record.name + } + + ) + }, + { + title: '学号', + dataIndex: 'studentid', + key: 'studentid', + align: 'center', + className: "edu-txt-center font-14 maxnamewidth175", + render: (text, record) => ( + + { + record.student_id + } + + ), + }, + { + title: '完成实训', + key: 'training', + dataIndex: 'training', + align: 'center', + className: "edu-txt-center font-14 maxnamewidth255", + render: (text, record) => ( + + { + record.shixun_count + } + + ) + }, + { + title: '在学实训', + dataIndex: 'learning', + key: 'learning', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.study_shixun_count + } + + ) + + }, + { + title: '金币', + dataIndex: 'goldcoin', + key: 'goldcoin', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.grade + } + + ), + }, + { + title: '经验值', + dataIndex: 'empirical', + key: 'empirical', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.experience + } + + ) + }, + ], + school:"", + teachers_count:null, + students_count:null, + courses_count:null, + shixuns_count:null, + shixun_report_count:null, + shixun_time:null, + courses:null, + course_count:0, + pages:1, + limits:10, + teachers:null, + teacher_count:0, + students:null, + student_count:0, + shixun_chart_data:null, + shixun_chart_datanames:null, + studentionsnames:null, + studentionsvalues:null + } + + } + + + componentDidMount(){ + console.log("College"); + console.log(this.props.match.params.id); + this.gettop(); + this.Numberofinternshipreports(); + this.Actualcombattimeoftrainees(); + this.Classnumber(1,10); + this.Teacherranking(1,10); + this.Studentranking(1,10); + this.Onlinetraining(); + this.Hottest(); + } + //头部 + gettop=()=>{ + const id =this.props.match.params.id; + const url=`/colleges/${id}/statistics.json`; + axios.get(url).then((response) => { + if(response===null||response===undefined){ + this.setState({ + teachers_count:0, + students_count:0, + courses_count:0, + shixuns_count:0, + school:"", + }) + return + } + if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { + this.setState({ + teachers_count:0, + students_count:0, + courses_count:0, + shixuns_count:0, + school:"", + }) + }else{ + this.setState({ + teachers_count:response.data.teachers_count, + students_count:response.data.students_count, + courses_count:response.data.courses_count, + shixuns_count:response.data.shixuns_count, + school:response.data.school, + }) + } + }).catch((error) => { + console.log(error) + this.setState({ + teachers_count:0, + students_count:0, + courses_count:0, + shixuns_count:0, + school:"", + }) + }); + } + + //获取实训报告数 + Numberofinternshipreports=()=>{ + const id =this.props.match.params.id; + const url=`/colleges/${id}/shixun_report_count.json`; + axios.get(url).then((response) => { + if(response===null||response===undefined){ + this.setState({ + shixun_report_count:0, + }) + return + } + if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { + this.setState({ + shixun_report_count:0, + }) + }else{ + if (response.data.status === -1){ + this.setState({ + shixun_report_count:0, + }) + return + } + this.setState({ + shixun_report_count:response.data.shixun_report_count, + }) + } + }).catch((error) => { + console.log(error) + this.setState({ + shixun_report_count:0, + }) + }); + } + + //学员实战时间 + Actualcombattimeoftrainees=()=>{ + const id =this.props.match.params.id; + const url=`/colleges/${id}/shixun_time.json`; + axios.get(url).then((response) => { + if(response===null||response===undefined){ + this.setState({ + shixun_time:0, + }) + return + } + if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { + this.setState({ + shixun_time:0, + }) + }else{ + if (response.data.status === -1){ + this.setState({ + shixun_time:0, + }) + return + } + this.setState({ + shixun_time:response.data.shixun_time, + }) + } + }).catch((error) => { + console.log(error) + this.setState({ + shixun_time:0, + }) + }); + } + + //课堂信息 + Classnumber=(page,per_page)=>{ + const id =this.props.match.params.id; + const url=`/colleges/${id}/course_statistics.json`; + this.setState({ + coursesloading:true + }) + axios.get(url,{params:{ + page:page, + per_page:per_page, + } + }).then((response) => { + if(response===null||response===undefined){ + this.setState({ + courses:[], + course_count:0 + }) + return + } + if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { + this.setState({ + courses:[], + course_count:0 + }) + }else{ + if (response.data.status === -1){ + this.setState({ + courses:[], + course_count:0 + }) + return + } + this.setState({ + courses:response.data.courses, + course_count:response.data.course_count, + page:page, + limit:per_page + }) + } + this.setState({ + coursesloading:false + }) + }).catch((error) => { + this.setState({ + courses:[], + course_count:0, + coursesloading:false + }) + }); + } + + //教师排名 + Teacherranking=(page,per_page)=>{ + const id =this.props.match.params.id; + const url=`/colleges/${id}/teachers.json`; + this.setState({ + teachersloading:true + }) + axios.get(url,{params:{ + page:page, + per_page:per_page, + } + }).then((response) => { + if(response===null||response===undefined){ + this.setState({ + teachers:[], + teacher_count:0 + }) + return + } + if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { + this.setState({ + teachers:[], + teacher_count:0 + }) + }else{ + if (response.data.status === -1){ + this.setState({ + teachers:[], + teacher_count:0 + }) + return + } + let teachers=[]; + if(response.data.teachers){ + for(let i=0;i { + this.setState({ + teachers:[], + teacher_count:0, + teachersloading:false + }) + }); + } + //学生排名 + + Studentranking=(page,per_page)=>{ + const id =this.props.match.params.id; + const url=`/colleges/${id}/student_shixun.json`; + this.setState({ + studentsloading:true + }) + axios.get(url,{params:{ + page:page, + per_page:per_page, + } + }).then((response) => { + if(response===null||response===undefined){ + this.setState({ + students:[], + student_count:0, + }) + return + } + if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { + this.setState({ + students:[], + student_count:0, + }) + }else{ + if (response.data.status === -1){ + this.setState({ + students:[], + student_count:0, + }) + return + } + let students=[]; + if(response.data.teachers){ + for(let i=0;i { + this.setState({ + students:[], + student_count:0, + studentsloading:false + }) + }); + } + + //在线实训情况 + Onlinetraining=()=>{ + const id =this.props.match.params.id; + const url=`/colleges/${id}/shixun_chart_data.json`; + axios.get(url).then((response) => { + if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { + this.setState({ + shixun_chart_data:[], + shixun_chart_datanames:[] + }) + }else{ + if (response.data.status === -1){ + this.setState({ + shixun_chart_data:[], + shixun_chart_datanames:[] + }) + return + } + + this.setState({ + shixun_chart_data:response.data.data, + shixun_chart_datanames:response.data.names + }) + } + }).catch((error) => { + this.setState({ + shixun_chart_data:[], + shixun_chart_datanames:[] + }) + }); + } + //最热测评 + Hottest=()=>{ + const id =this.props.match.params.id; + const url=`/colleges/${id}/student_hot_evaluations.json`; + axios.get(url).then((response) => { + if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { + this.setState({ + studentionsnames: [], + studentionsvalues: [] + }) + }else { + if (response.data.status === -1) { + this.setState({ + studentionsnames: [], + studentionsvalues: [] + }) + return + } + this.setState({ + studentionsnames: response.data.names, + studentionsvalues: response.data.values + }) + } + }).catch((error) => { + this.setState({ + studentionsnames: [], + studentionsvalues: [] + }) + }); + } + + table1handleChange(){ + + } + + //塞选 + paginationonChange=(pageNumber)=>{ + this.Classnumber(pageNumber,10); + } + + paginationonChanges=(pageNumber)=>{ + this.Teacherranking(pageNumber,10); + } + paginationonChangess=(pageNumber)=>{ + this.Studentranking(pageNumber,10); + } + render() { + let {columns,page,limit,total_users,teacherranking,studentranking, + teachers_count,students_count, courses_count, shixuns_count,shixun_report_count,shixun_time,courses,course_count,school,teachers, + pages,limits, teacher_count,teachersloading,coursesloading,pagess,limitss,studentsloading,students,student_count,shixun_chart_data, + shixun_chart_datanames, studentionsnames,studentionsvalues + } = this.state; + + return ( +
    +
    +
    +
    {school}
    +
    +
    + {/*//教师1*/} +
    + 教师 +
    +
    + {teachers_count?teachers_count:0} +
    +
    + +
    +
    + 学生 +
    +
    + {students_count?students_count:0} +
    +
    + +
    +
    + 课堂 +
    +
    + {courses_count?courses_count:0} +
    +
    + +
    +
    + 共建实训 +
    +
    + {shixuns_count?shixuns_count:0} +
    +
    + {/*//教师2*/} + + + + +
    +
    +
    +
    +

    + 基本使用情况 +

    + {/*基本使用情况1*/} +
    +

    + 教师 +

    +

    + 学生 +

    +

    + 课堂 +

    +

    + 共建实训 +

    +

    + 实习报告 +

    +

    + 学员实战时间 +

    +
    + + + {/*基本使用情况2*/} +
    +
    + { + teachers_count? +
    {teachers_count}
    + : + + } +
    +
    + { + students_count? +
    {students_count}
    + : + + } +
    +
    + { + courses_count? +
    {courses_count}
    + : + + } +
    +
    + { + shixuns_count? +
    {shixuns_count}
    + : + + } +
    +
    + { + shixun_report_count? +
    {shixun_report_count}
    + : + + } +
    +
    + { + shixun_time? +
    {shixun_time}
    + : + + } +
    +
    + {/*基本使用情况3结束*/} +
    + + +
    +

    + 课堂 +

    + { + courses===null? +
    + +
    + + : + JSON.stringify(courses) === "[]" ? + + + + : +
    +
    + +
    + {courses === undefined ? "" :
    ID序号ID 实训名称 技术平台 权限技术体系上传图片小程序封面技术体系上传图片小程序封面 创建者 关闭 复制 操作
    - ssh/隐藏/首页/跳关/隐藏目录 + ssh/隐藏/首页/跳关/隐藏目录/vip
    <%= page_no %> <%= shixun.identifier %> @@ -47,6 +48,7 @@ <%= check_box_tag :homepage_show,!shixun.homepage_show,shixun.homepage_show,remote:true,data:{id:shixun.id,toggle:"tooltip",placement:"top"},class:"shixun-setting-form",title:"首页" %> <%= check_box_tag :task_pass,!shixun.task_pass,shixun.task_pass,remote:true,data:{id:shixun.id,toggle:"tooltip",placement:"top"},class:"shixun-setting-form" ,title:"跳关"%> <%= check_box_tag :code_hidden,!shixun.code_hidden,shixun.code_hidden,remote:true,data:{id:shixun.id,toggle:"tooltip",placement:"top"},class:"shixun-setting-form" ,title:"隐藏目录"%> + <%= check_box_tag :vip,!shixun.vip,shixun.vip,remote:true,data:{id:shixun.id,toggle:"tooltip",placement:"top"},class:"shixun-setting-form" ,title:"vip"%>
    } + + + { + course_count>=11? +
    +
    + +
    + +
    + :"" + } + + + } + + + + + +
    +
    +

    + 教师排名 +

    + { + teachers===null? +
    + +
    + + : + JSON.stringify(teachers) === "[]" ? + + + + : +
    + +
    + {teachers === undefined ? "" :
    } + + + } + + + + {/*
    */} + {/*
    */} + {/* */} + {/*
    */} + + {/*
    */} + + +
    +

    + 在线实训情况 +

    + { + shixun_chart_data===null? +
    + +
    + + : + JSON.stringify(shixun_chart_data) === "[]" ? + + + + : + + + + } + +
    +
    + + + +
    +
    +

    + 学生排名 +

    + { + students === null ? +
    + +
    + + : + JSON.stringify(students) === "[]" ? + + + + : +
    + +
    + {students === undefined ? "" :
    } + + + } + {/*
    */} + {/*
    */} + {/* */} + {/*
    */} + + {/*
    */} + + + + +
    + +
    +

    + 最热评测 +

    + { + studentionsnames===null? +
    + +
    + + : + JSON.stringify(studentionsnames) === "[]" ? + + + + : + + + + } + + +
    +
    + + + ) + } +} +export default SnackbarHOC() (TPMIndexHOC ( College )); + + diff --git a/public/react/src/college/colleagechart/Colleagechart.js b/public/react/src/college/colleagechart/Colleagechart.js new file mode 100644 index 000000000..fca01280a --- /dev/null +++ b/public/react/src/college/colleagechart/Colleagechart.js @@ -0,0 +1,84 @@ +import React, {Component} from "react"; +import {WordsBtn} from 'educoder'; +import {Table} from "antd"; +import {Link,Switch,Route,Redirect} from 'react-router-dom'; +const echarts = require('echarts'); + + + +function startechart(data,datanane){ + var effChart = echarts.init(document.getElementById('shixun_skill_chart')); + + var option = { + + tooltip : { + trigger: 'item', + formatter: "{d}%
    " + }, + legend: { + // orient: 'vertical', + // top: 'middle', + bottom: 50, + left: 'center', + data: datanane + }, + series : [ + { + type: 'pie', + radius : '65%', + center: ['50%', '35%'], + selectedMode: 'single', + data:data, + itemStyle: { + emphasis: { + shadowBlur: 10, + shadowOffsetX: 0, + shadowColor: 'rgba(0, 0, 0, 0.5)' + } + } + } + ] + }; + effChart.setOption(option); +} +class Colleagechart extends Component { + + constructor(props) { + super(props); + this.state = { + } + } + + componentDidMount() { + startechart(this.props.data,this.props.datanane) + } + + + componentDidUpdate = (prevProps) => { + if (prevProps.data!= this.props.data) { + startechart(this.props.data,this.props.datanane) + } + } + + + render() { + let {data}=this.props; + + return ( +
    + +
    +
    + + + + +
    + + ) + } +} + +export default Colleagechart; diff --git a/public/react/src/college/colleagechart/Colleagechartzu.js b/public/react/src/college/colleagechart/Colleagechartzu.js new file mode 100644 index 000000000..5c0269700 --- /dev/null +++ b/public/react/src/college/colleagechart/Colleagechartzu.js @@ -0,0 +1,149 @@ +import React, {Component} from "react"; +import {WordsBtn} from 'educoder'; +import {Table} from "antd"; +import {Link,Switch,Route,Redirect} from 'react-router-dom'; +const echarts = require('echarts'); + + + +function startechart(names, values){ + var effChart = echarts.init(document.getElementById('shixun_skill_charts')); + + var Color = ['#962e66', '#623363', '#CCCCCC', '#9A9A9A', '#FF8080', '#FF80C2', '#B980FF', '#80B9FF', '#6FE9FF', '#4DE8B4', '#F8EF63', '#FFB967']; + + var option = { + backgroundColor: '#fff', + grid: { + left: '3%', + right: '8%', + bottom: '15%', + containLabel: true + }, + + tooltip: { + show: "true", + trigger: 'item', + formatter: '{c0}', + backgroundColor: 'rgba(0,0,0,0.7)', // 背景 + padding: [8, 10], //内边距 + extraCssText: 'box-shadow: 0 0 3px rgba(255, 255, 255, 0.4);', //添加阴影 + axisPointer: { // 坐标轴指示器,坐标轴触发有效 + type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' + } + }, + xAxis: { + type: 'value', + axisTick: { + show: false + }, + axisLine: { + show: true, + lineStyle: { + color: '#CCCCCC' + } + }, + splitLine: { + show: false, + lineStyle: { + color: '#CCCCCC' + } + }, + axisLabel: { + textStyle: { + color: '#656565', + fontWeight: 'normal', + fontSize: '12' + }, + formatter: '{value}' + } + }, + yAxis: { + type: 'category', + axisLine: { + lineStyle: { + color: '#cccccc' + } + }, + splitLine: { + show: false + }, + axisTick: { + show: false + }, + splitArea: { + show: false + }, + axisLabel: { + inside: false, + textStyle: { + color: '#656565', + fontWeight: 'normal', + fontSize: '12' + } + }, + data: names + }, + series: [{ + name: '', + type: 'bar', + itemStyle: { + normal: { + show: true, + color: function(params) { + return Color[params.dataIndex] + }, + barBorderRadius: 50, + borderWidth: 0, + borderColor: '#333' + } + }, + barGap: '0%', + barCategoryGap: '50%', + data: values + } + + ] + }; + effChart.setOption(option); +} +class Colleagechartzu extends Component { + + constructor(props) { + super(props); + this.state = { + } + } + + componentDidMount() { + startechart(this.props.data,this.props.datavule) + } + + + componentDidUpdate = (prevProps) => { + if (prevProps.data!= this.props.data) { + startechart(this.props.data,this.props.datavule) + } + } + + + render() { + let {data}=this.props; + + return ( +
    + +
    +
    + + + + +
    + + ) + } +} + +export default Colleagechartzu; diff --git a/public/react/src/college/colleagecss/colleage.css b/public/react/src/college/colleagecss/colleage.css new file mode 100644 index 000000000..801b86603 --- /dev/null +++ b/public/react/src/college/colleagecss/colleage.css @@ -0,0 +1,213 @@ +.yslstatistic-header { + width: 100%; + height: 240px; + background-image: url('/images/educoder/statistics.jpg'); + background-size: 100% 100%; +} +.yslborder{ + border: 1px solid; +} +.yslstatistic-header-title{ + flex: 1; + display: flex; + align-items: center; + color: #4CACFF; + font-size: 32px; +} +.yslstatistic-header-content{ + width: 100%; + display: flex; + justify-content: space-around; +} +.yslstatistic-header-item{ + margin-bottom: 22px; + display: flex; + flex-direction: column; + align-items: center; + color: #fff; +} +.yslstatistic-header-item-label{ + color: #989898; +} + +.yslstatistic-base-item-label{ + width: 217px; + text-align: center; + font-size: 16px; + height: 48px; + line-height: 48px; + color: #686868; + background: #F5F5F5; + border-top: 1px solid #EBEBEB; +} +.yslstatistic-base-item-labels{ + width: 217px; + text-align: center; + height: 100px; + line-height: 100px; + background: #ffffff; + border-top: 1px solid #EBEBEB; + border-bottom: 1px solid #EBEBEB; +} +.yslstatistic-base-item-labelsp{ + color: #000000; + font-size: 24px; +} +.yslstatistic-base-item-labelsspan{ + color: #000000; + margin-left: 5px; + font-size: 16px; +} +.jibenshiyong100{ + width: 100%; +} + +.yslstatistic-header-item-content{ + font-size: 24px; +} +/* 中间居中 */ +.intermediatecenter{ + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} +/* 简单居中 */ +.intermediatecenterysls{ + display: flex; + align-items: center; +} +.spacearound{ + display: flex; + justify-content: space-around; + +} +.spacebetween{ + display: flex; + justify-content: space-between; +} +/* 头顶部居中 */ +.topcenter{ + display: -webkit-flex; + flex-direction: column; + align-items: center; + +} + + +/* x轴正方向排序 */ +/* 一 二 三 四 五 六 七 八 */ +.sortinxdirection{ + display: flex; + flex-direction:row; +} +/* x轴反方向排序 */ +/* 八 七 六 五 四 三 二 一 */ +.xaxisreverseorder{ + display: flex; + flex-direction:row-reverse; +} +/* 垂直布局 正方向*/ +/* 一 + 二 + 三 + 四 + 五 + 六 + 七 + 八 */ +.verticallayout{ + display: flex; + flex-direction:column; +} +/* 垂直布局 反方向*/ +.reversedirection{ + display: flex; + flex-direction:column-reverse; +} + +.h4{ + font-size: 1.5rem; + font-weight: 500 !important; +} +.ysllinjibenshiyong{ + font-weight: 500; + line-height: 1.2; + padding: 2rem 1.25rem; + border-bottom: unset; + background:#fff; +} +.linjibenshiyong{ + font-weight: 500; + line-height: 1.2; + padding: 2rem 1.25rem; + border-bottom: unset; + background:#fff; + box-shadow:0px 6px 12px 0px rgba(0,0,0,0.1); + border-radius:2px; +} +.yslslinjibenshiyong{ + font-weight: 500; + line-height: 1.2; + border-bottom: unset; + box-shadow:0px 6px 12px 0px rgba(0,0,0,0.1); + border-radius:2px; +} +.yinyin{ + background: #fff; + box-shadow:0px 6px 12px 0px rgba(0,0,0,0.1); + border-radius:2px; +} +.edu-back-eeee{ + background:#EEEEEE !important; +} +.mt-4{ + margin-top: 1.5rem !important; +} + +.statistic-label{ + padding: 2rem 1.25rem; + font-size: 1.5rem; + font-weight: 400 !important; +} +.mb50{ + padding-bottom: 50px !important; +} +.mt40{ + margin-top: 40px; +} +.mb80{ + margin-bottom: 80px; +} +.task-hide{overflow:hidden; white-space: nowrap; text-overflow:ellipsis;} +a:hover{ + color:#0056b3; +} +.color-blue{ + color: #4CACFF; +} + +.color-huang{ + color:#ffc107 !important +} +.maxnamewidth105{ + max-width: 105px; + overflow:hidden; + text-overflow:ellipsis; + white-space:nowrap; + cursor: default; +} +.maxnamewidth247{ + max-width: 247px; + overflow:hidden; + text-overflow:ellipsis; + white-space:nowrap; + cursor: default; +} +.maxnamewidth340{ + max-width: 340px; + overflow:hidden; + text-overflow:ellipsis; + white-space:nowrap; + cursor: default; +} diff --git a/public/react/src/common/Env.js b/public/react/src/common/Env.js index c80497509..9830d7725 100644 --- a/public/react/src/common/Env.js +++ b/public/react/src/common/Env.js @@ -1,8 +1,8 @@ -export function isDev() { - return window.location.port === "3007"; -} - -// const isMobile -export const isMobile = (/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent.toLowerCase())); - -// const isWeiXin = (/MicroMessenger/i.test(navigator.userAgent.toLowerCase())); +export function isDev() { + return window.location.port === "3007"; +} + +// const isMobile +export const isMobile = (/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent.toLowerCase())); + +// const isWeiXin = (/MicroMessenger/i.test(navigator.userAgent.toLowerCase())); diff --git a/public/react/src/common/UrlTool.js b/public/react/src/common/UrlTool.js index db97642b7..bc463e662 100644 --- a/public/react/src/common/UrlTool.js +++ b/public/react/src/common/UrlTool.js @@ -37,6 +37,22 @@ export function getUrl(path, goTest) { } return `${path ? path: ''}`; } + +export function getUrlmys(path, goTest) { + // https://www.educoder.net + // https://testbdweb.trustie.net + + // 如果想所有url定位到测试版,可以反注释掉下面这行 + //goTest = true + // testbdweb.educoder.net testbdweb.trustie.net + // const local = goTest ? 'https://testeduplus2.educoder.net' : 'http://localhost:3000' + // const local = 'https://testeduplus2.educoder.net' + const local = 'https://test-jupyterweb.educoder.net' + if (isDev) { + return `${local}${path?path:''}` + } + return `${path ? path: ''}`; +} export function getStaticUrl() { const local = TEST_HOST; if (isDev) { @@ -55,6 +71,10 @@ export function getUrl2(path, goTest) { export function getUploadActionUrl(path, goTest) { return `${getUrl()}/api/attachments.json${isDev ? `?debug=${window._debugType || 'admin'}` : ''}` } +export function getUploadActionUrltwo(id) { + + return `${getUrlmys()}/api/shixuns/${id}/upload_data_sets.json${isDev ? `?debug=${window._debugType || 'admin'}` : ''}` +} export function getUploadActionUrlOfAuth(id) { return `${getUrl()}/api/users/accounts/${id}/auth_attachment.json${isDev ? `?debug=${window._debugType || 'admin'}` : ''}` } @@ -85,4 +105,4 @@ export function htmlEncode(str) { s = s.replace(/\'/g, "'");//IE下不支持实体名称 s = s.replace(/\"/g, """); return s; -} \ No newline at end of file +} diff --git a/public/react/src/common/components/ModalConfirm.js b/public/react/src/common/components/ModalConfirm.js new file mode 100644 index 000000000..bb29fed03 --- /dev/null +++ b/public/react/src/common/components/ModalConfirm.js @@ -0,0 +1,30 @@ +/* + * @Description: + * @Author: tangjiang + * @Github: + * @Date: 2019-12-13 10:28:15 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-13 10:37:17 + */ +import { Modal } from 'antd'; + +export function ModalConfirm ( + title, + content, + handleOk, + handleCancel +) { + + Modal.confirm({ + title, + content, + okText: '确定', + cancelText: '取消', + onOk () { + handleOk && handleOk(); + }, + onCancel () { + handleCancel && handleCancel(); + } + }); +} diff --git a/public/react/src/common/components/MyIcon.js b/public/react/src/common/components/MyIcon.js new file mode 100644 index 000000000..aa9f29a19 --- /dev/null +++ b/public/react/src/common/components/MyIcon.js @@ -0,0 +1,15 @@ +/* + * @Description: 引入阿里图标库 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-10 09:03:48 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-12 10:53:47 + */ +import { Icon } from 'antd'; + +const MyIcon = Icon.createFromIconfontCN({ + scriptUrl: '//at.alicdn.com/t/font_1535266_i4ilpm93kp.js' +}); + +export default MyIcon; diff --git a/public/react/src/common/components/comment/CommentForm.js b/public/react/src/common/components/comment/CommentForm.js new file mode 100644 index 000000000..73e36cff9 --- /dev/null +++ b/public/react/src/common/components/comment/CommentForm.js @@ -0,0 +1,116 @@ +/* + * @Description: 评论表单 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-17 17:32:55 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-18 17:51:44 + */ +import React, { useState } from 'react'; +import { Form, Button, Input } from 'antd'; +import QuillForEditor from '../../quillForEditor'; +import { QuillDeltaToHtmlConverter } from 'quill-delta-to-html' +const FormItem = Form.Item; + +function CommentForm (props) { + + const { + commentCtxChagne, + onCancel, + onSubmit, + form + } = props; + + const { getFieldDecorator } = form; + const [ctx, setCtx] = useState(''); + + const options = [ + ['bold', 'italic', 'underline'], + [{header: [1,2,3,false]}], + ['blockquote', 'code-block'], + ['link', 'image'], + ['formula'] + ]; + // const { form: { getFieldDecorator } } = props; + const [showQuill, setShowQuill] = useState(false); + // 点击输入框 + const handleInputClick = () => { + setShowQuill(true); + } + // 取消 + const handleCancle = () => { + setShowQuill(false); + onCancel && onCancel(); + } + + // 编辑器内容变化时 + const handleContentChange = (content) => { + setCtx(content); + try { + const _html = new QuillDeltaToHtmlConverter(content.ops, {}).convert(); + // props.form.setFieldsValue({'comment': _html.replace(/<\/?[^>]*>/g, '')}); + props.form.setFieldsValue({'comment': _html}); + } catch (error) { + console.log(error); + } + } + // 发送 + const handleSubmit = (e) => { + e.preventDefault(); + props.form.validateFields((err, values) => { + if (!err) { + setShowQuill(false); + const content = ctx; + props.form.setFieldsValue({'comment': ''}); + setCtx(''); + console.log(content); + onSubmit && onSubmit(content); + } + }); + } + return ( + + + { + getFieldDecorator('comment', { + rules: [ + { required: true, message: '评论内容不能为空'} + ], + })( + + ) + } + + + + + + + + + ); +} + +export default Form.create()(CommentForm); diff --git a/public/react/src/common/components/comment/CommentIcon.js b/public/react/src/common/components/comment/CommentIcon.js new file mode 100644 index 000000000..5440e2579 --- /dev/null +++ b/public/react/src/common/components/comment/CommentIcon.js @@ -0,0 +1,32 @@ +/* + * @Description: + * @Author: tangjiang + * @Github: + * @Date: 2019-12-18 10:49:46 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-18 11:39:23 + */ +import './index.scss'; +import React from 'react'; +import { Icon } from 'antd'; +function CommentIcon ({ + type, // 图标类型 + count, // 评论数 + iconClick, + ...props +}) { + + // 点击图标 + const handleSpanClick = () => { + iconClick && iconClick(); + } + + return ( + + + { count } + + ) +} + +export default CommentIcon; diff --git a/public/react/src/common/components/comment/CommentItem.js b/public/react/src/common/components/comment/CommentItem.js new file mode 100644 index 000000000..19da645f5 --- /dev/null +++ b/public/react/src/common/components/comment/CommentItem.js @@ -0,0 +1,165 @@ +/* + * @Description: 评论单列 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-17 17:35:17 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-19 18:02:28 + */ +import './index.scss'; +import React, { useState } from 'react'; +import CommentIcon from './CommentIcon'; +import { getImageUrl, CNotificationHOC } from 'educoder' +import { Icon } from 'antd'; +import moment from 'moment'; +// import QuillForEditor from '../../quillForEditor'; +import CommentForm from './CommentForm'; + +// import {ModalConfirm} from '../ModalConfirm'; +function CommentItem ({ + options, + confirm +}) { + // 显示评论输入框 + const [showQuill, setShowQuill] = useState(false); + // 加载更多评论内容 + const [showMore, setShowMore] = useState(false); + // 箭头方向 + const [arrow, setArrow] = useState(false); + // 删除评论 + const deleteComment = () => { + console.log('删除评论...'); + confirm({ + title: '提示', + content: (

    确定要删除该条回复吗?

    ), + onOk () { + console.log('点击了删除'); + } + }); + // ModalConfirm('提示', (

    确定要删除该条回复吗?

    ), () => { + // console.log('点击了删除'); + // }); + } + + // 评论头像 + const commentAvatar = (url) => ( + + ); + + // 评论信息 + const commentInfo = () => ( +

    + 用户名 + {moment(new Date(), 'YYYYMMDD HHmmss').fromNow()} + +

    + ); + + // 评论内容 + const commentCtx = (ctx) => ( +

    + 这是评论内容这是评论内容这是评论内容这是评论内容这是评论内容这是评论内容这是评论内容这是评论内容这是评论内容这是评论内容 +

    + ); + + // 加载更多 + const handleOnLoadMore = () => { + if (!arrow) { + // 展开所有 + } else { + // 收起 + } + setArrow(!arrow); + }; + + // 评论追加内容 + const commentAppend = () => { + + return ( +
      +
    • + {commentAvatar()} +
      + {commentInfo()} + {commentCtx()} +
      +
    • +
    • + {commentAvatar()} +
      + {commentInfo()} + {commentCtx()} +
      +
    • +
    • + {commentAvatar()} +
      + {commentInfo()} + {commentCtx()} +
      +
    • + +
    • +

      展开其余23条评论

      +

      + +

      +
    • +
    + ); + }; + // 点击图标 + const handleIconClick = () => {} + + // 点击评论icon + const handleClickMessage = () => { + setShowQuill(true); + } + + // 点击取消 + const handleClickCancel = () => { + setShowQuill(false); + } + + // 点击保存 + const handleClickSubmit = (content) => { + // 保存并关闭 + setShowQuill(false); + console.log('获取保存内容', content); + } + + return ( +
  • + {commentAvatar()} +
    + {commentInfo()} + {commentCtx()} + + {commentAppend()} + +
    + + {/* 回复 */} + + {/* 点赞 */} + +
    + +
    + +
    +
    +
  • + ); +} + +export default CNotificationHOC() (CommentItem); diff --git a/public/react/src/common/components/comment/CommentList.js b/public/react/src/common/components/comment/CommentList.js new file mode 100644 index 000000000..9d8cde87b --- /dev/null +++ b/public/react/src/common/components/comment/CommentList.js @@ -0,0 +1,20 @@ +/* + * @Description: 评论列表页 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-17 17:34:00 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-18 11:48:09 + */ +import './index.scss'; +import React from 'react'; +import CommentItem from './CommentItem'; +function CommentList ({}) { + return ( +
      + +
    + ); +} + +export default CommentList; diff --git a/public/react/src/common/components/comment/index.js b/public/react/src/common/components/comment/index.js new file mode 100644 index 000000000..f0ecf3309 --- /dev/null +++ b/public/react/src/common/components/comment/index.js @@ -0,0 +1,22 @@ +/* + * @Description: 评论组件 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-17 17:31:33 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-18 11:47:39 + */ +import React from 'react'; +import CommentForm from './CommentForm'; +import CommentList from './CommentList'; +function Comment (props) { + + return ( + + + + + ); +} + +export default Comment; diff --git a/public/react/src/common/components/comment/index.scss b/public/react/src/common/components/comment/index.scss new file mode 100644 index 000000000..816e6da6c --- /dev/null +++ b/public/react/src/common/components/comment/index.scss @@ -0,0 +1,111 @@ +$bdColor: rgba(244,244,244,1); +$bgColor: rgba(250,250,250,1); +$lh14: 14px; +$lh22: 22px; +$fz14: 14px; +$fz12: 12px; +$ml: 20px; + +.comment_list_wrapper{ + box-sizing: border-box; + border-top: 1px solid $bdColor; + + .comment_item_area{ + display: flex; + padding: 20px 0; + box-sizing: border-box; + border-bottom: 1px solid $bdColor; + .flex-image{ + width: 48px; + height: 48px; + border-radius: 50%; + } + .item-desc{ + flex: 1; + margin-left: $ml; + } + .item-header{ + font-size: $fz14; + line-height: $lh14; + color: #333; + .item-time{ + font-size: $fz12; + line-height: $lh14; + margin-left: $ml; + } + .item-close{ + float: right; + cursor: pointer; + } + } + .item-ctx{ + line-height: $lh22; + font-size: $fz12; + color: #333; + margin-top: 10px; + } + .comment_icon_area{ + display: flex; + justify-content: flex-end; + margin-top: 10px; + + .comment-icon-margin{ + margin-left: 30px; + } + } + + .comment_item_quill{ + margin-top: 20px; + } + } + .comment_icon_count{ + cursor: pointer; + font-size: 12px; + line-height: 1.5; + + .comment_icon{ + color: #333; + } + .comment_count{ + color: #999999; + margin-left: 10px; + transition: color .3s; + } + + &:hover{ + .comment_icon, + .comment_count{ + color: #5091FF; + } + } + } + .comment_item_append_list{ + position: relative; + background-color: $bgColor; + border-radius: 5px; + padding: 0 15px 10px; + margin: 15px 0; + &::before { + position: absolute; + left: 15px; + bottom: 100%; + height: 0; + width: 0; + content: ''; + // border: 5px solid transparent; + border: 10px solid transparent; + border-bottom-color: $bgColor; + } + + .comment_item_loadmore{ + padding-top: 10px; + cursor: pointer; + .loadmore-txt, + .loadmore-icon{ + color: #999; + text-align: center; + font-size: $fz12; + } + } + } +} \ No newline at end of file diff --git a/public/react/src/common/educoder.js b/public/react/src/common/educoder.js index 004cd91c0..c9aa7ac77 100644 --- a/public/react/src/common/educoder.js +++ b/public/react/src/common/educoder.js @@ -2,11 +2,11 @@ // export { default as OrderStateUtil } from '../routes/Order/components/OrderStateUtil'; -export { getImageUrl as getImageUrl, getUrl as getUrl, getUrl2 as getUrl2, setImagesUrl as setImagesUrl - , getUploadActionUrl as getUploadActionUrl, getUploadActionUrlOfAuth as getUploadActionUrlOfAuth +export { getImageUrl as getImageUrl, getUrl as getUrl, getUrlmys as getUrlmys, getUrl2 as getUrl2, setImagesUrl as setImagesUrl + , getUploadActionUrl as getUploadActionUrl,getUploadActionUrltwo as getUploadActionUrltwo , getUploadActionUrlOfAuth as getUploadActionUrlOfAuth , getTaskUrlById as getTaskUrlById, TEST_HOST ,htmlEncode as htmlEncode } from './UrlTool'; export { default as queryString } from './UrlTool2'; - + export { SnackbarHOC as SnackbarHOC } from './SnackbarHOC'; export { trigger as trigger, on as on, off as off @@ -31,7 +31,7 @@ export { trace_collapse, trace, debug, info, warn, error, trace_c, debug_c, info export { EDU_ADMIN, EDU_BUSINESS, EDU_SHIXUN_MANAGER, EDU_SHIXUN_MEMBER, EDU_CERTIFICATION_TEACHER , EDU_GAME_MANAGER, EDU_TEACHER, EDU_NORMAL} from './Const' - + export { default as AttachmentList } from './components/attachment/AttachmentList' export { themes, ThemeContext } from './context/ThemeContext' diff --git a/public/react/src/common/quillForEditor/ImageBlot.js b/public/react/src/common/quillForEditor/ImageBlot.js new file mode 100644 index 000000000..091bd2c1f --- /dev/null +++ b/public/react/src/common/quillForEditor/ImageBlot.js @@ -0,0 +1,54 @@ +/* + * @Description: 重写图片 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-16 15:50:45 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-17 16:44:48 + */ +import Quill from "quill"; + +const BlockEmbed = Quill.import('blots/block/embed'); + +export default class ImageBlot extends BlockEmbed { + + static create(value) { + + const node = super.create(); + + node.setAttribute('alt', value.alt); + node.setAttribute('src', value.url); + + if (value.width) { + node.setAttribute('width', value.width); + } + if (value.height) { + node.setAttribute('height', value.height); + } + // 宽度和高度都不存在时, + if (!value.width && !value.height) { + node.setAttribute('display', 'block'); + node.setAttribute('width', '100%'); + } + // 给图片添加点击事件 + node.onclick = () => { + value.onClick && value.onClick(value.url); + } + return node; + } + + static value (node) { + + return { + alt: node.getAttribute('alt'), + url: node.getAttribute('src'), + onclick: node.onclick, + // width: node.width, + // height: node.height, + display: node.getAttribute('display') + }; + } +} + +ImageBlot.blotName = 'image'; +ImageBlot.tagName = 'img'; \ No newline at end of file diff --git a/public/react/src/common/quillForEditor/deepEqual.js b/public/react/src/common/quillForEditor/deepEqual.js new file mode 100644 index 000000000..6f2b276bf --- /dev/null +++ b/public/react/src/common/quillForEditor/deepEqual.js @@ -0,0 +1,47 @@ +function deepEqual (prev, current) { + if (prev === current) { // 基本类型比较,值,类型都相同 或者同为 null or undefined + return true; + } + + if ((!prev && current) + || (prev && !current) + || (!prev && !current) + ) { + return false; + } + + if (Array.isArray(prev)) { + if (!Array.isArray(current)) return false; + if (prev.length !== current.length) return false; + + for (let i = 0; i < prev.length; i++) { + if (!deepEqual(current[i], prev[i])) { + return false; + } + } + return true; + } + + if (typeof current === 'object') { + if (typeof prev !== 'object') return false; + const prevKeys = Object.keys(prev); + const curKeys = Object.keys(current); + + if (prevKeys.length !== curKeys.length) return false; + + prevKeys.sort(); + curKeys.sort(); + + for (let i = 0; i < prevKeys.length; i++) { + if (prevKeys[i] !== curKeys[i]) return false; + const key = prevKeys[i]; + if (!deepEqual(prev[key], current[key])) return false; + } + + return true; + } + + return false; +} + +export default deepEqual; diff --git a/public/react/src/common/quillForEditor/index.js b/public/react/src/common/quillForEditor/index.js new file mode 100644 index 000000000..6e6c01886 --- /dev/null +++ b/public/react/src/common/quillForEditor/index.js @@ -0,0 +1,166 @@ +/* + * @Description: quill 编辑器 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-18 08:49:30 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-19 16:58:50 + */ +import 'quill/dist/quill.core.css'; // 核心样式 +import 'quill/dist/quill.snow.css'; // 有工具栏 +import 'quill/dist/quill.bubble.css'; // 无工具栏 +import 'katex/dist/katex.min.css'; // katex 表达式样式 +import React, { useState, useRef, useEffect } from 'react'; +import Quill from 'quill'; +import katex from 'katex'; +import deepEqual from './deepEqual.js' +import { fetchUploadImage } from '../../services/ojService.js'; +import { getImageUrl } from 'educoder' +import ImageBlot from './ImageBlot'; + +window.Quill = Quill; +window.katex = katex; +Quill.register(ImageBlot); + +function QuillForEditor ({ + placeholder, + readOnly, + options, + value, + imgAttrs = {}, // 指定图片的宽高 + style = {}, + wrapStyle = {}, + showUploadImage, + onContentChange +}) { + // toolbar 默认值 + const defaultConfig = [ + ['bold', 'italic', 'underline'], + [{align: []}, {list: 'ordered'}, {list: 'bullet'}], // 列表 + [{script: 'sub'}, {script: 'super'}], + [{ 'color': [] }, { 'background': [] }], + [{header: [1,2,3,4,5,false]}], + ['blockquote', 'code-block'], + ['link', 'image', 'video'], + ['formula'], + ['clean'] + ]; + + const editorRef = useRef(null); + // quill 实例 + const [quill, setQuill] = useState(null); + const [selection, setSelection] = useState(null); + + // 文本内容变化时 + const handleOnChange = content => { + // console.log('编辑器内容====》》》》', content); + onContentChange && onContentChange(content); + }; + + const renderOptions = options || defaultConfig; + // quill 配置信息 + const quillOption = { + modules: { + toolbar: renderOptions + }, + readOnly, + placeholder, + theme: readOnly ? 'bubble' : 'snow' + }; + + + useEffect(() => { + const _quill = new Quill(editorRef.current, quillOption); + setQuill(_quill); + + // 处理图片上传功能 + _quill.getModule('toolbar').addHandler('image', (e) => { + const input = document.createElement('input'); + input.setAttribute('type', 'file'); + input.setAttribute('accept', 'image/*'); + input.click(); + + input.onchange = async (e) => { + const file = input.files[0]; // 获取文件信息 + const formData = new FormData(); + formData.append('file', file); + + const range = _quill.getSelection(true); + let fileUrl = ''; // 保存上传成功后图片的url + // 上传文件 + const result = await fetchUploadImage(formData); + // 获取上传图片的url + if (result.data && result.data.id) { + fileUrl = getImageUrl(`api/attachments/${result.data.id}`); + } + // 根据id获取文件路径 + const { width, height } = imgAttrs; + // console.log('上传图片的url:', fileUrl); + if (fileUrl) { + _quill.insertEmbed(range.index, 'image', { + url: fileUrl, + alt: '图片信息', + onClick: showUploadImage, + width, + height + }); + } + } + }); + }, []); + + // 设置值 + useEffect(() => { + if (!quill) return + const previous = quill.getContents() + const current = value + + if (!deepEqual(previous, current)) { + setSelection(quill.getSelection()) + if (typeof value === 'string') { + quill.clipboard.dangerouslyPasteHTML(value, 'api') + } else { + quill.setContents(value) + } + } + }, [quill, value, setQuill]); + + // 清除选择区域 + useEffect(() => { + if (quill && selection) { + quill.setSelection(selection) + setSelection(null) + } + }, [quill, selection, setSelection]); + + // 设置placeholder值 + useEffect(() => { + if (!quill || !quill.root) return; + quill.root.dataset.placeholder = placeholder; + }, [quill, placeholder]); + + // 处理内容变化 + useEffect(() => { + if (!quill) return; + if (typeof handleOnChange !== 'function') return; + let handler; + quill.on( + 'text-change', + (handler = () => { + handleOnChange(quill.getContents()); // getContents: 检索编辑器内容 + }) + ); + return () => { + quill.off('text-change', handler); + } + }, [quill, handleOnChange]); + + // 返回结果 + return ( +
    +
    +
    + ); +} + +export default QuillForEditor; diff --git a/public/react/src/common/reactQuill/ImageBlot.js b/public/react/src/common/reactQuill/ImageBlot.js new file mode 100644 index 000000000..091bd2c1f --- /dev/null +++ b/public/react/src/common/reactQuill/ImageBlot.js @@ -0,0 +1,54 @@ +/* + * @Description: 重写图片 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-16 15:50:45 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-17 16:44:48 + */ +import Quill from "quill"; + +const BlockEmbed = Quill.import('blots/block/embed'); + +export default class ImageBlot extends BlockEmbed { + + static create(value) { + + const node = super.create(); + + node.setAttribute('alt', value.alt); + node.setAttribute('src', value.url); + + if (value.width) { + node.setAttribute('width', value.width); + } + if (value.height) { + node.setAttribute('height', value.height); + } + // 宽度和高度都不存在时, + if (!value.width && !value.height) { + node.setAttribute('display', 'block'); + node.setAttribute('width', '100%'); + } + // 给图片添加点击事件 + node.onclick = () => { + value.onClick && value.onClick(value.url); + } + return node; + } + + static value (node) { + + return { + alt: node.getAttribute('alt'), + url: node.getAttribute('src'), + onclick: node.onclick, + // width: node.width, + // height: node.height, + display: node.getAttribute('display') + }; + } +} + +ImageBlot.blotName = 'image'; +ImageBlot.tagName = 'img'; \ No newline at end of file diff --git a/public/react/src/common/reactQuill/ReactQuill.js b/public/react/src/common/reactQuill/ReactQuill.js new file mode 100644 index 000000000..1b4209409 --- /dev/null +++ b/public/react/src/common/reactQuill/ReactQuill.js @@ -0,0 +1,45 @@ +/* + * @Description: + * @Author: tangjiang + * @Github: + * @Date: 2019-12-09 09:09:42 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-18 08:46:20 + */ +import 'quill/dist/quill.core.css'; // 核心样式 +import 'quill/dist/quill.snow.css'; // 有工具栏 +import 'quill/dist/quill.bubble.css'; // 无工具栏 +import 'katex/dist/katex.min.css'; // katex 表达式样式 +import React, { useState, useReducer, useEffect } from 'react'; +import useQuill from './useQuill'; + +function ReactQuill ({ + disallowColors, // 不可见时颜色 + placeholder, // 提示信息 + uploadImage, // 图片上传 + onChange, // 内容变化时 + options, // 配置信息 + value, // 显示的内容 + style, + showUploadImage // 显示上传图片 +}) { + + const [element, setElement] = useState(); // quill 渲染节点 + + useQuill({ + disallowColors, + placeholder, + uploadImage, + onChange, + options, + value, + showUploadImage, + element + }); + + return ( +
    + ); +} + +export default ReactQuill; diff --git a/public/react/src/common/reactQuill/deepEqual.js b/public/react/src/common/reactQuill/deepEqual.js new file mode 100644 index 000000000..6f2b276bf --- /dev/null +++ b/public/react/src/common/reactQuill/deepEqual.js @@ -0,0 +1,47 @@ +function deepEqual (prev, current) { + if (prev === current) { // 基本类型比较,值,类型都相同 或者同为 null or undefined + return true; + } + + if ((!prev && current) + || (prev && !current) + || (!prev && !current) + ) { + return false; + } + + if (Array.isArray(prev)) { + if (!Array.isArray(current)) return false; + if (prev.length !== current.length) return false; + + for (let i = 0; i < prev.length; i++) { + if (!deepEqual(current[i], prev[i])) { + return false; + } + } + return true; + } + + if (typeof current === 'object') { + if (typeof prev !== 'object') return false; + const prevKeys = Object.keys(prev); + const curKeys = Object.keys(current); + + if (prevKeys.length !== curKeys.length) return false; + + prevKeys.sort(); + curKeys.sort(); + + for (let i = 0; i < prevKeys.length; i++) { + if (prevKeys[i] !== curKeys[i]) return false; + const key = prevKeys[i]; + if (!deepEqual(prev[key], current[key])) return false; + } + + return true; + } + + return false; +} + +export default deepEqual; diff --git a/public/react/src/common/reactQuill/flatten.js b/public/react/src/common/reactQuill/flatten.js new file mode 100644 index 000000000..237cb543f --- /dev/null +++ b/public/react/src/common/reactQuill/flatten.js @@ -0,0 +1,26 @@ +/* + * @Description: 将多维数组转变成一维数组 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-09 09:35:01 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-16 11:36:22 + */ +function flatten (array) { + return flatten.rec(array, []); +} + +flatten.rec = function flatten (array, result) { + + for (let item of array) { + if (Array.isArray(item)) { + flatten(item, result); + } else { + result.push(item); + } + } + + return result; +} + +export default flatten; diff --git a/public/react/src/common/reactQuill/index.js b/public/react/src/common/reactQuill/index.js new file mode 100644 index 000000000..56a1a8d1f --- /dev/null +++ b/public/react/src/common/reactQuill/index.js @@ -0,0 +1,108 @@ +/* + * @Description: 入口文件 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-17 10:41:48 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-17 20:34:40 + */ +import React, { useState, useCallback, useEffect } from 'react'; +import ReactQuill from './lib'; + +function Wrapper (props) { + // 默认工具栏配置项 + const toolbarConfig = [ + ['bold', 'italic', 'underline'], + [{align: []}, {list: 'ordered'}, {list: 'bullet'}], // 列表 + [{script: 'sub'}, {script: 'super'}], + [{header: [1,2,3,4,5,false]}], + ['blockquote', 'code-block'], + ['link', 'image', 'video'], + ['formula'], + ['clean'] + ]; + + const [placeholder] = useState(props.placeholder || 'placeholder'); + const [disableBold] = useState(false); + const [value, setValue] = useState(props.value || ''); + const [toolbar, setToolbar] = useState(toolbarConfig); + const [theme, setTheme] = useState(props.theme || 'snow'); + const [readOnly] = useState(props.readOnly || false); + + const { + onContentChagne, // 当编辑器内容变化时调用该函数 + showUploadImage, // 显示上传图片, 返回url,主要用于点击图片放大 + } = props; + + // 配置信息 + const options = { + modules: { + toolbar: toolbar, + clipboard: { + matchVisual: false + } + }, + readOnly: readOnly, + theme: theme + } + // 配置信息 + useEffect (() => { + if (props.options) { + setToolbar(props.options); + } + setTheme(props.theme || 'snow'); + setValue(props.value); + }, [props]); + + // 当内容变化时 + const handleOnChange = useCallback( + contents => { + if (disableBold) { + setValue({ + ops: contents.ops.map(x => { + x = {...x}; + if (x && x.attributes && x.attributes.bold) { + x.attributes = { ...x.attributes }; + delete x.attributes.bold; + if (!Object.keys(x.attributes).length) { + delete x.attributes; + } + } + return x; + }) + }); + } else { + setValue(contents); + } + onContentChagne && onContentChagne(contents); + }, [disableBold] + ); + + // 图片上传 + const handleUploadImage = (files) => { + console.log('选择的图片信息', files); + } + + // 显示图片 + const handleShowUploadImage = (url) => { + // console.log('上传的图片url:', url); + showUploadImage && showUploadImage(url); + } + + return ( + + handleShowUploadImage(url)} + /> + + ); +} + +export default Wrapper; +// ReactDOM.render(, document.querySelector('#root')); diff --git a/public/react/src/common/reactQuill/index.scss b/public/react/src/common/reactQuill/index.scss new file mode 100644 index 000000000..b6da52bf5 --- /dev/null +++ b/public/react/src/common/reactQuill/index.scss @@ -0,0 +1,32 @@ +#quill-toolbar{ + .quill-btn{ + vertical-align: middle; + } + .quill_image{ + display: inline-block; + position: relative; + vertical-align: middle; + width: 28px; + height: 24px; + overflow: hidden; + .image_input{ + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + opacity: 0; + } + .ql-image{ + position: relative; + left: 0; + top: 0; + } + } +} + +.react_quill_area{ + .ql-toolbar:not(:last-child) { + display: none; + } +} \ No newline at end of file diff --git a/public/react/src/common/reactQuill/lib.js b/public/react/src/common/reactQuill/lib.js new file mode 100644 index 000000000..430a95bb7 --- /dev/null +++ b/public/react/src/common/reactQuill/lib.js @@ -0,0 +1,13 @@ +/* + * @Description: 导出 ReactQuill + * @Author: tangjiang + * @Github: + * @Date: 2019-12-09 09:08:24 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-16 11:37:13 + */ +import ReactQuill from './ReactQuill'; +import useQuill from './useQuill'; + +export default ReactQuill; +export { useQuill }; diff --git a/public/react/src/common/reactQuill/useDeepEqualMemo.js b/public/react/src/common/reactQuill/useDeepEqualMemo.js new file mode 100644 index 000000000..948e21781 --- /dev/null +++ b/public/react/src/common/reactQuill/useDeepEqualMemo.js @@ -0,0 +1,27 @@ +/* + * @Description: + * @Author: tangjiang + * @Github: + * @Date: 2019-12-12 19:48:55 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-16 11:38:16 + */ +import { useState, useEffect } from 'react'; +import deepEqual from './deepEqual'; + +function useDeepEqual (input) { + + const [value, setValue] = useState(input); + + useEffect(() => { + + if (!deepEqual(input, value)) { + setValue(input) + } + + }, [input, value]); + + return value; +} + +export default useDeepEqual; diff --git a/public/react/src/common/reactQuill/useMountQuill.js b/public/react/src/common/reactQuill/useMountQuill.js new file mode 100644 index 000000000..c2313c480 --- /dev/null +++ b/public/react/src/common/reactQuill/useMountQuill.js @@ -0,0 +1,148 @@ +/* + * @Description: 创建 reactQuill实例 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-09 09:31:42 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-17 20:42:05 + */ +import Quill from 'quill'; // 导入quill +import { useState, useEffect, useMemo } from 'react'; +import flatten from './flatten.js'; +import useDeepEqualMemo from './useDeepEqualMemo'; +import Katex from 'katex'; +import ImageBlot from './ImageBlot'; +import { fetchUploadImage } from '../../services/ojService.js'; +import { getImageUrl } from 'educoder' +window.katex = Katex; + +Quill.register(ImageBlot); + +function useMountQuill ({ + element, + options: passedOptions, + uploadImage, + showUploadImage, + imgAttrs = {} // 指定图片的宽高属性 +}) { + + // 是否引入 katex + const [katexLoaded, setKatexLoaded] = useState(Boolean(window.katex)) + const [quill, setQuill] = useState(null); + + const options = useDeepEqualMemo(passedOptions); + console.log('use mount quill: ', passedOptions); + + // 判断options中是否包含公式 + const requireKatex = useMemo(() => { + return flatten(options.modules.toolbar).includes('formula'); + }, [options]); + + // 加载katex + useEffect(() => { + if (!requireKatex) return; + if (katexLoaded) return; + + const interval = setInterval(() => { + if (window.katex) { + setKatexLoaded(true); + clearInterval(interval); + } + }); + + return () => { // 定义回调清除定时器 + clearInterval(interval); + } + + }, [ + setKatexLoaded, + katexLoaded, + requireKatex + ]); + + // 加载 quill + useEffect(() => { + if (!element) return; + if (requireKatex && !katexLoaded) { + element.innerHTML = ` +
    + Loading Katex... +
    + ` + } + // 清空内容 + element.innerHTML = ''; + console.log(element); + // 创建 quill 节点 + const quillNode = document.createElement('div'); + element.appendChild(quillNode); // 将quill节点追回到 element 元素中 + + const quill = new Quill(element, options); + setQuill(quill); + // 加载上传图片功能 + if (typeof uploadImage === 'function') { + quill.getModule('toolbar').addHandler('image', (e) => { + // 创建type类型输入框加载本地图片 + const input = document.createElement('input'); + input.setAttribute('type', 'file'); + input.setAttribute('accept', 'image/*'); + input.click(); + + input.onchange = async (e) => { + const file = input.files[0]; // 获取文件信息 + const formData = new FormData(); + formData.append('file', file); + + // const reader = new FileReader(); + // reader.readAsDataURL(file); + // console.log('文件信息===>>', reader); + // reader.onload = function (e) { + // debugger; + // console.log('文件信息===>>', e.target.result); + // const image = new Image(); + // image.src = e.target.result; + + // image.onload = function () { + // // file.width = + // console.log(image.width, image.height); + // } + // } + + const range = quill.getSelection(true); + let fileUrl = ''; // 保存上传成功后图片的url + // 上传文件 + const result = await fetchUploadImage(formData); + // 获取上传图片的url + if (result.data && result.data.id) { + fileUrl = getImageUrl(`api/attachments/${result.data.id}`); + } + // 根据id获取文件路径 + const { width, height } = imgAttrs; + // console.log('上传图片的url:', fileUrl); + if (fileUrl) { + quill.insertEmbed(range.index, 'image', { + url: fileUrl, + alt: '', + onClick: showUploadImage, + width, + height + }); + } + } + }); + } + + return () => { + element.innerHTML = ''; + } + }, [ + element, + options, + requireKatex, + katexLoaded, + ]); + + return quill; +} + +export default useMountQuill; diff --git a/public/react/src/common/reactQuill/useQuill.js b/public/react/src/common/reactQuill/useQuill.js new file mode 100644 index 000000000..b959dbc52 --- /dev/null +++ b/public/react/src/common/reactQuill/useQuill.js @@ -0,0 +1,60 @@ +/* + * @Description: + * @Author: tangjiang + * @Github: + * @Date: 2019-12-09 09:09:50 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-17 15:46:50 + */ +import useQuillPlaceholder from './useQuillPlaceholder'; +import useQuillValueSync from './useQuillValueSync'; +import useQuillOnChange from './useQuillOnChange'; +import useMountQuill from './useMountQuill'; +import { useEffect } from 'react'; + +function useQuill ({ + disallowColors, + placeholder, + uploadImage, + onChange, + options, + value, + element, + showUploadImage +}) { + + // 获取 quill 实例 + const quill = useMountQuill({ + element, + options, + uploadImage, + showUploadImage + }); + + useEffect(() => { + if (disallowColors && quill) { + quill.clipboard.addMatcher(Node.ELEMENT_NODE, (node, delta) => { + delta.ops = delta.ops.map(op => { + if (op.attributes && op.attributes.color) { + const { color, ...attributes } = op.attributes; + return { + ...op, + attributes + } + } + return op; + }); + return delta; + }); + } + }, [ + disallowColors, + quill + ]); + + useQuillPlaceholder(quill, placeholder); + useQuillValueSync(quill, value); + useQuillOnChange(quill, onChange); +} + +export default useQuill; diff --git a/public/react/src/common/reactQuill/useQuillOnChange.js b/public/react/src/common/reactQuill/useQuillOnChange.js new file mode 100644 index 000000000..45333a4e1 --- /dev/null +++ b/public/react/src/common/reactQuill/useQuillOnChange.js @@ -0,0 +1,33 @@ +/* + * @Description: + * @Author: tangjiang + * @Github: + * @Date: 2019-12-12 19:49:11 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-16 11:39:27 + */ +import { useEffect } from 'react'; + +function useQuillOnChange (quill, onChange) { + + useEffect(() => { + + if (!quill) return; + if (typeof onChange !== 'function') return; + + let handler; + + quill.on( + 'text-change', + (handler = () => { + onChange(quill.getContents()); // getContents: 检索编辑器内容 + }) + ); + + return () => { + quill.off('text-change', handler); + } + }, [quill, onChange]); +} + +export default useQuillOnChange; diff --git a/public/react/src/common/reactQuill/useQuillPlaceholder.js b/public/react/src/common/reactQuill/useQuillPlaceholder.js new file mode 100644 index 000000000..ccc341568 --- /dev/null +++ b/public/react/src/common/reactQuill/useQuillPlaceholder.js @@ -0,0 +1,22 @@ +/* + * @Description: + * @Author: tangjiang + * @Github: + * @Date: 2019-12-09 09:28:34 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-16 11:39:48 + */ +import { useEffect } from 'react' + +function useQuillPlaceholder ( + quill, + placeholder +) { + + useEffect(() => { + if (!quill || !quill.root) return; + quill.root.dataset.placeholder = placeholder; + }, [quill, placeholder]); +} + +export default useQuillPlaceholder; diff --git a/public/react/src/common/reactQuill/useQuillValueSync.js b/public/react/src/common/reactQuill/useQuillValueSync.js new file mode 100644 index 000000000..696d88949 --- /dev/null +++ b/public/react/src/common/reactQuill/useQuillValueSync.js @@ -0,0 +1,31 @@ +import { useEffect, useState } from 'react' +import deepEqual from './deepEqual.js' + +function useQuillValueSync(quill, value) { + const [selection, setSelection] = useState(null) + + useEffect(() => { + if (!quill) return + + const previous = quill.getContents() + const current = value + + if (!deepEqual(previous, current)) { + setSelection(quill.getSelection()) + if (typeof value === 'string') { + quill.clipboard.dangerouslyPasteHTML(value, 'api') + } else { + quill.setContents(value) + } + } + }, [quill, value, setSelection]) + + useEffect(() => { + if (quill && selection) { + quill.setSelection(selection) + setSelection(null) + } + }, [quill, selection, setSelection]) +} + +export default useQuillValueSync diff --git a/public/react/src/constants/index.js b/public/react/src/constants/index.js index abae13db0..3e7aceab0 100644 --- a/public/react/src/constants/index.js +++ b/public/react/src/constants/index.js @@ -4,7 +4,7 @@ * @Github: * @Date: 2019-11-20 23:10:48 * @LastEditors: tangjiang - * @LastEditTime: 2019-11-28 14:41:42 + * @LastEditTime: 2019-12-06 15:53:27 */ const CONST = { jcLabel: { @@ -20,8 +20,25 @@ const CONST = { title: '代码格式', type: 'select', content: [ + { + text: '显示风格', + type: 'style', + value: [ + { + key: 'dark', + text: '黑色背景', + value: 'dark' + }, + { + key: 'light', + text: '白色背景', + value: 'light' + } + ] + }, { - text: '字体大小', + text: '字体大小', + type: 'font', value: [ { key: 1, diff --git a/public/react/src/context/TPIContextProvider.js b/public/react/src/context/TPIContextProvider.js index dce678480..ed7eb2210 100644 --- a/public/react/src/context/TPIContextProvider.js +++ b/public/react/src/context/TPIContextProvider.js @@ -41,17 +41,17 @@ const styles = MUIDialogStyleUtil.getTwoButtonStyle() // 主题自定义 const theme = createMuiTheme({ palette: { - primary: { + primary: { main: '#4CACFF', contrastText: 'rgba(255, 255, 255, 0.87)' - }, + }, secondary: { main: '#4CACFF' }, // This is just green.A700 as hex. }, }); -const testSetsExpandedArrayInitVal = [false, false, false, false, false, - false, false, false, false, false, - false, false, false, false, false, +const testSetsExpandedArrayInitVal = [false, false, false, false, false, + false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false] window.__fetchAllFlag = false; // 是否调用过fetchAll TODO 如何多次使用provider? @@ -70,7 +70,7 @@ class TPIContextProvider extends Component { this.readGameAnswer = this.readGameAnswer.bind(this) this.praisePlus = this.praisePlus.bind(this) - + this.onGamePassed = this.onGamePassed.bind(this) this.onPathChange = this.onPathChange.bind(this) @@ -80,7 +80,7 @@ class TPIContextProvider extends Component { this.onShowUpdateDialog = this.onShowUpdateDialog.bind(this) this.updateDialogClose = this.updateDialogClose.bind(this) - + // this.showEffectDisplay(); this.state = { @@ -142,7 +142,7 @@ class TPIContextProvider extends Component { // request // var shixunId = this.props.match.params.shixunId; var stageId = this.props.match.params.stageId; - + window.__fetchAllFlag = false; this.fetchAll(stageId); this.costTimeInterval = window.setInterval(()=> { @@ -192,7 +192,7 @@ class TPIContextProvider extends Component { onGamePassed(passed) { const { game } = this.state // 随便给个分,以免重新评测时又出现评星组件(注意:目前game.star没有显示在界面上,如果有则不能这么做) - // game.star = 6; + // game.star = 6; this.setState({ game: update(game, {star: { $set: 6 }}), currentGamePassed: !!passed @@ -253,14 +253,14 @@ pop_box_new(htmlvalue, 480, 182); const { praise_count, praise } = response.data; // challenge.praise_count = praise_tread_count; // challenge.user_praise = praise; - this.setState({ challenge: update(challenge, + this.setState({ challenge: update(challenge, { praise_count: { $set: praise_count }, user_praise: { $set: praise }, }) }) } - + }) .catch(function (error) { console.log(error); @@ -286,11 +286,11 @@ pop_box_new(htmlvalue, 480, 182); } const { myshixun } = this.state; // myshixun.system_tip = false; - + challenge.path = path; const newChallenge = this.handleChallengePath(challenge); - this.setState({ challenge: newChallenge, + this.setState({ challenge: newChallenge, myshixun: update(myshixun, {system_tip: { $set: false }}), }) } @@ -313,7 +313,7 @@ pop_box_new(htmlvalue, 480, 182); newResData2OldResData(newResData) { newResData.latest_output = newResData.last_compile_output - // newResData.power + // newResData.power newResData.record = newResData.record_onsume_time // 老版用的hide_code @@ -329,7 +329,7 @@ pop_box_new(htmlvalue, 480, 182); newResData.output_sets.test_sets = newResData.test_sets // JSON.stringify() newResData.output_sets.test_sets_count = newResData.test_sets_count // newResData.output_sets.had_passed_testsests_error_count = newResData.sets_error_count - newResData.output_sets.had_passed_testsests_error_count = newResData.test_sets_count + newResData.output_sets.had_passed_testsests_error_count = newResData.test_sets_count - newResData.sets_error_count // allowed_hidden_testset // sets_error_count @@ -354,8 +354,8 @@ pop_box_new(htmlvalue, 480, 182); let output_sets = resData.output_sets; if (resData.st === 0) { // 代码题 challenge = this.handleChallengePath(challenge) - - const mirror_name = (resData.mirror_name && resData.mirror_name.join) + + const mirror_name = (resData.mirror_name && resData.mirror_name.join) ? resData.mirror_name.join(';') : (resData.mirror_name || ''); if (mirror_name.indexOf('Html') !== -1) { challenge.isHtml = true; @@ -364,7 +364,7 @@ pop_box_new(htmlvalue, 480, 182); challenge.isWeb = true; } else if (mirror_name.indexOf('Android') !== -1) { challenge.isAndroid = true; - } + } if (output_sets && output_sets.test_sets && typeof output_sets.test_sets == 'string') { const test_sets_array = JSON.parse("[" + output_sets.test_sets + "]"); @@ -378,7 +378,7 @@ pop_box_new(htmlvalue, 480, 182); const $ = window.$ window.setTimeout(()=>{ var lens = $("#choiceRepositoryView textarea").length; - + for(var i = 1; i <= lens; i++){ window.editormd.markdownToHTML("choose_subject_" + i, { htmlDecode: "style,script,iframe", // you can filter tags decode @@ -404,14 +404,14 @@ pop_box_new(htmlvalue, 480, 182); game.isPassThrough = true } resData.game = game; - + const { tpm_cases_modified, tpm_modified, tpm_script_modified, myshixun } = resData; if (myshixun.system_tip) { // system_tip为true的时候 不弹框提示用户更新 resData.showUpdateDialog = false } else { let needUpdateScript = (tpm_modified || tpm_script_modified) && challenge.st === 0; - resData.showUpdateDialog = needUpdateScript || tpm_cases_modified + resData.showUpdateDialog = needUpdateScript || tpm_cases_modified } /** @@ -458,7 +458,7 @@ pop_box_new(htmlvalue, 480, 182); // const EDU_NORMAL = 7 // 普通用户 - /** + /** EDU_ADMIN = 1 # 超级管理员 EDU_BUSINESS = 2 # 运营人员 EDU_SHIXUN_MANAGER = 3 # 实训管理员 @@ -467,7 +467,7 @@ pop_box_new(htmlvalue, 480, 182); EDU_GAME_MANAGER = 6 # TPI的创建者 EDU_TEACHER = 7 # 平台老师,但是未认证 EDU_NORMAL = 8 # 普通用户 - */ + */ // myshixun_manager power is_teacher resData.power = 0 @@ -495,8 +495,8 @@ pop_box_new(htmlvalue, 480, 182); } else if (resData.user.identity === EDU_TEACHER) { // resData.is_teacher = true } else if (resData.user.identity === EDU_NORMAL) { - - } + + } return resData } @@ -524,7 +524,7 @@ pop_box_new(htmlvalue, 480, 182); loading: true, currentGamePassed: false, // 切换game时重置passed字段 }) - + // test // var data = {"st":0,"discusses_count":0,"game_count":3,"record_onsume_time":0.36,"prev_game":null,"next_game":"7p9xwo2hklqv","praise_count":0,"user_praise":false,"time_limit":20,"tomcat_url":"http://47.96.157.89","is_teacher":false,"myshixun_manager":true,"game":{"id":2192828,"myshixun_id":580911,"user_id":57844,"created_at":"2019-09-03T15:50:49.000+08:00","updated_at":"2019-09-03T15:51:05.000+08:00","status":2,"final_score":0,"challenge_id":10010,"open_time":"2019-09-03T15:50:49.000+08:00","identifier":"hknvz4oaw825","answer_open":0,"end_time":"2019-09-03T15:51:04.000+08:00","retry_status":0,"resubmit_identifier":null,"test_sets_view":false,"picture_path":null,"accuracy":1.0,"modify_time":"2019-09-03T15:23:33.000+08:00","star":0,"cost_time":14,"evaluate_count":1,"answer_deduction":0},"challenge":{"id":10010,"shixun_id":3516,"subject":"1.1 列表操作","position":1,"task_pass":"[TOC]\n\n---\n\n####任务描述\n\n\n数据集a包含1-10共10个整数,请以a为输入数据,编写python程序,实现如下功能:\n①\t用2种方法输出a中所有奇数\n②\t输出大于3,小于7的偶数\n③\t用2种方法输出[1,2,3,…10,11,…20]\n④\t输出a的最大值、最小值。\n⑤\t用2种方法输出[10,9,…2,1]\n⑥\t输出[1,2,3,1,2,3,1,2,3,1,2,3]\n\n\n####相关知识\n\n\n请自行学习相关知识\n\n\n---\n开始你的任务吧,祝你成功!","score":100,"path":"1-1-stu.py","st":0,"web_route":null,"modify_time":"2019-09-03T15:23:33.000+08:00","exec_time":20,"praises_count":0},"shixun":{"id":3516,"name":"作业1——Python程序设计","user_id":77620,"gpid":null,"visits":23,"created_at":"2019-09-03T14:18:17.000+08:00","updated_at":"2019-09-03T15:58:16.000+08:00","status":0,"language":null,"authentication":false,"identifier":"6lzjig58","trainee":1,"major_id":null,"webssh":2,"homepage_show":false,"hidden":false,"fork_from":null,"can_copy":true,"modify_time":"2019-09-03T14:18:17.000+08:00","reset_time":"2019-09-03T14:18:17.000+08:00","publish_time":null,"closer_id":null,"end_time":null,"git_url":null,"vnc":null,"myshixuns_count":3,"challenges_count":3,"use_scope":0,"mirror_script_id":20,"image_text":null,"code_hidden":false,"task_pass":true,"exec_time":20,"test_set_permission":true,"sigle_training":false,"hide_code":false,"multi_webssh":false,"excute_time":null,"repo_name":"p09218567/6lzjig58","averge_star":5.0,"opening_time":null,"users_count":1,"forbid_copy":false,"pod_life":0},"myshixun":{"id":580911,"shixun_id":3516,"is_public":true,"user_id":57844,"gpid":null,"created_at":"2019-09-03T15:50:49.000+08:00","updated_at":"2019-09-03T15:59:04.000+08:00","status":0,"identifier":"k36hm4rwav","commit_id":"f25e1713882156480fc45ce0af57eff395a5037f","modify_time":"2019-09-03T14:18:17.000+08:00","reset_time":"2019-09-03T14:18:17.000+08:00","system_tip":false,"git_url":null,"onclick_time":"2019-09-03T15:50:49.000+08:00","repo_name":"p53276410/k36hm4rwav20190903155049"},"user":{"user_id":57844,"login":"p53276410","name":"文振乾","grade":24624,"identity":1,"image_url":"avatars/User/57844","school":"EduCoder团队"},"tpm_modified":true,"tpm_cases_modified":false,"mirror_name":["Python3.6"],"has_answer":false,"test_sets":[{"is_public":true,"result":true,"input":"","output":"result of a:\n[1, 3, 5, 7, 9]\n[1, 3, 5, 7, 9]\nresult of b:\n[2, 4, 6, 8, 10]\nresult of c:\n[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]\n[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]\nresult of d:\nThe minimum is:1\nThe maxium is:10\nresult of e:\n[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]\n[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\nresult of f:\n[10, 9, 8, 10, 9, 8, 10, 9, 8, 10, 9, 8]\n","actual_output":"result of a:\r\n[1, 3, 5, 7, 9]\r\n[1, 3, 5, 7, 9]\r\nresult of b:\r\n[2, 4, 6, 8, 10]\r\nresult of c:\r\n[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]\r\n[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]\r\nresult of d:\r\nThe minimum is:1\r\nThe maxium is:10\r\nresult of e:\r\n[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]\r\n[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\r\nresult of f:\r\n[10, 9, 8, 10, 9, 8, 10, 9, 8, 10, 9, 8]\r\n","compile_success":1,"ts_time":0.05,"ts_mem":8.77}],"allowed_unlock":true,"last_compile_output":"compile successfully","test_sets_count":1,"sets_error_count":0} // data.test_sets[0].actual_output = data.test_sets[0].actual_output.replace(/\r\n/g, '\n') @@ -534,7 +534,7 @@ pop_box_new(htmlvalue, 480, 182); // data.vnc_url= "http://47.96.157.89:41158/vnc_lite.html?password=headless" // this._handleResponseData(data) - // return + // return axios.get(url, { // https://stackoverflow.com/questions/48861290/the-value-of-the-access-control-allow-origin-header-in-the-response-must-not-b @@ -550,7 +550,7 @@ pop_box_new(htmlvalue, 480, 182); return; } if (response.data.status == 404) { - // 如果第一次发生404,则隔1s后再调用一次本接口;(因为ucloud主从同步可能有延迟) + // 如果第一次发生404,则隔1s后再调用一次本接口;(因为ucloud主从同步可能有延迟) if (!noTimeout) { setTimeout(() => { this.fetchAll(stageId, true) @@ -562,12 +562,12 @@ pop_box_new(htmlvalue, 480, 182); } this._handleResponseData(response.data) - + }) .catch(function (error) { console.log(error); }); - + } readGameAnswer(resData) { @@ -583,7 +583,7 @@ pop_box_new(htmlvalue, 480, 182); grade: resData.grade }) } - + } closeTaskResultLayer() { this.setState({ @@ -605,7 +605,7 @@ pop_box_new(htmlvalue, 480, 182); currentGamePassed = true; - + this._updateCostTime(true, true); } this.setState({ @@ -618,14 +618,14 @@ pop_box_new(htmlvalue, 480, 182); currentPassedGameGainGold: gold, currentPassedGameGainExperience: experience, }) - } + } initDisplayInterval = () => { const challenge = this.state.challenge if (this.showWebDisplayButtonTimeout) { window.clearTimeout(this.showWebDisplayButtonTimeout) } this.showWebDisplayButtonTimeout = window.setTimeout(() => { - this.setState({ challenge: update(challenge, + this.setState({ challenge: update(challenge, { showWebDisplayButton: { $set: false }, }) @@ -650,7 +650,7 @@ pop_box_new(htmlvalue, 480, 182); this.displayInterval = null return; } - + remain -= 1; }, 1000) } @@ -716,7 +716,7 @@ pop_box_new(htmlvalue, 480, 182); const currentGamePassed = this.props.game !== 2 && status === 2 - + // 评测通过了,立即同步costTime currentGamePassed && this._updateCostTime(true, true); @@ -738,7 +738,7 @@ pop_box_new(htmlvalue, 480, 182); // const test_sets_array = JSON.parse("[" + output_sets.test_sets + "]"); // output_sets.test_sets_array = test_sets_array; // } - + // 检查是否编译通过 let compileSuccess = false; if (test_sets && test_sets.length) { @@ -754,7 +754,7 @@ pop_box_new(htmlvalue, 480, 182); if (currentGamePassed) { game.status = 2; // game.isPassThrough = true - game.next_game = next_game; + game.next_game = next_game; } else { this.showDialog({ contentText:
    @@ -764,7 +764,7 @@ pop_box_new(htmlvalue, 480, 182); isSingleButton: true }) } - + this.setState({ testSetsExpandedArray: testSetsExpandedArrayInitVal.slice(0), // 重置测试集展开状态 @@ -775,12 +775,12 @@ pop_box_new(htmlvalue, 480, 182); output_sets, game, next_game, - + latest_output: last_compile_output, record: record_consume_time, grade, had_done, - + }) } resetTestSetsExpandedArray = () => { @@ -809,15 +809,15 @@ pop_box_new(htmlvalue, 480, 182); output_sets = Object.assign({}, output_sets); // const test_sets_array = JSON.parse("[" + response.data.test_sets + "]"); output_sets.test_sets_array = response.data.test_sets; - this.setState({ + this.setState({ output_sets: output_sets, grade: this.state.grade + deltaScore, - game : update(game, {test_sets_view: { $set: true }}), - testSetsExpandedArray: testSetsExpandedArrayInitVal.slice(0) + game : update(game, {test_sets_view: { $set: true }}), + testSetsExpandedArray: testSetsExpandedArrayInitVal.slice(0) }) this.handleGdialogClose(); } - + }) .catch(function (error) { console.log(error); @@ -841,10 +841,10 @@ pop_box_new(htmlvalue, 480, 182); }) } - /* + /* TODO 写成HOC组件,更好复用 全局的Dialog this.props.showDialog调用即可 - @param contentText dialog显示的提示文本 + @param contentText dialog显示的提示文本 @param callback 确定按钮回调方法 @param moreButtonsRender 除了“确定”、“取消”按钮外的其他按钮 @param okButtonText “确定”按钮显示文本,如 继续查看 @@ -908,13 +908,13 @@ pop_box_new(htmlvalue, 480, 182); match: this.props.match }} - > + > this.handleGdialogClose()} - > + > {"提示"} @@ -930,7 +930,7 @@ pop_box_new(htmlvalue, 480, 182); >知道啦
    : - @@ -938,7 +938,7 @@ pop_box_new(htmlvalue, 480, 182); onClick={() => this.onGdialogOkBtnClick() } color="primary" autoFocus> { this.okButtonText ? this.okButtonText : '确定' } - } + } {this.moreButtonsRender && this.moreButtonsRender()} diff --git a/public/react/src/images/oj/oj_banner.jpg b/public/react/src/images/oj/oj_banner.jpg new file mode 100644 index 000000000..8b74270d3 Binary files /dev/null and b/public/react/src/images/oj/oj_banner.jpg differ diff --git a/public/react/src/modules/courses/ListPageIndex.js b/public/react/src/modules/courses/ListPageIndex.js index 0a0ff0daf..5ff2c3c0e 100644 --- a/public/react/src/modules/courses/ListPageIndex.js +++ b/public/react/src/modules/courses/ListPageIndex.js @@ -200,7 +200,7 @@ class ListPageIndex extends Component{
    -
    +
    {/*left_nav*/}
    diff --git a/public/react/src/modules/courses/Resource/index.js b/public/react/src/modules/courses/Resource/index.js index c4ec4df02..ce4fb8dc2 100644 --- a/public/react/src/modules/courses/Resource/index.js +++ b/public/react/src/modules/courses/Resource/index.js @@ -756,7 +756,7 @@ class Fileslists extends Component{ Savesname={this.state.Savesname} Cancel={this.state.Cancel} Saves={this.state.Saves} - course_groups={this.state.course_groups} + // course_groups={this.state.course_groups} />:""} {/*发送*/} diff --git a/public/react/src/modules/courses/busyWork/CommonWorkItem.js b/public/react/src/modules/courses/busyWork/CommonWorkItem.js index 75676557a..82e2d2d96 100644 --- a/public/react/src/modules/courses/busyWork/CommonWorkItem.js +++ b/public/react/src/modules/courses/busyWork/CommonWorkItem.js @@ -205,15 +205,16 @@ class CommonWorkItem extends Component{ {item.uncommit_count===undefined?"":{item.uncommit_count} 未交} { item.status_time!="" && - + {item.status_time} - - } + } + {/**/} + {/**/} {isAdmin &&
    { this.props.toEditPage(this.props.match.params, item.homework_id) }}>编辑 { this.props.toWorkSettingPage(this.props.match.params, item.homework_id) }}>设置 diff --git a/public/react/src/modules/courses/busyWork/commonWork.js b/public/react/src/modules/courses/busyWork/commonWork.js index ce8cff558..d114b6777 100644 --- a/public/react/src/modules/courses/busyWork/commonWork.js +++ b/public/react/src/modules/courses/busyWork/commonWork.js @@ -414,7 +414,7 @@ class commonWork extends Component{ 补交中 匿评中 申诉中 - 评阅中 + 已截止
    } diff --git a/public/react/src/modules/courses/common/CourseLayoutComponent.js b/public/react/src/modules/courses/common/CourseLayoutComponent.js index 3a4403e43..c88bd8798 100644 --- a/public/react/src/modules/courses/common/CourseLayoutComponent.js +++ b/public/react/src/modules/courses/common/CourseLayoutComponent.js @@ -49,7 +49,7 @@ class CourseLayoutcomponent extends Component{
    -
    +
    {/*left_nav*/}
    diff --git a/public/react/src/modules/courses/coursesDetail/CoursesDetail.js b/public/react/src/modules/courses/coursesDetail/CoursesDetail.js index 350d2e45f..8a5519d2e 100644 --- a/public/react/src/modules/courses/coursesDetail/CoursesDetail.js +++ b/public/react/src/modules/courses/coursesDetail/CoursesDetail.js @@ -58,7 +58,7 @@ class CoursesDetail extends Component{
    -
    +
    {/*left_nav*/}
    diff --git a/public/react/src/modules/courses/coursesPublic/NewShixunModel.js b/public/react/src/modules/courses/coursesPublic/NewShixunModel.js index 99a8ad232..5ee763951 100644 --- a/public/react/src/modules/courses/coursesPublic/NewShixunModel.js +++ b/public/react/src/modules/courses/coursesPublic/NewShixunModel.js @@ -19,7 +19,8 @@ class NewShixunModel extends Component{ order:'desc', diff:0, limit:15, - sort:"myshixuns_count" + sort:"myshixuns_count", + belongtoindex:0, } } componentDidMount() { @@ -161,7 +162,17 @@ class NewShixunModel extends Component{ }) let{status,order,diff,limit,sort}=this.state; if(this.props.type==='shixuns'){ - this.getdatalist(1,value,status,undefined,order,diff,limit) + this.getdatalist(1,value,status,undefined,order,diff,limit); + if(value==="all"){ + this.setState({ + belongtoindex:0 + }) + }else{ + this.setState({ + belongtoindex:1 + }) + } + }else{ this.getdatalist(1,value,undefined,undefined,order,undefined,limit,undefined,sort) } @@ -322,6 +333,7 @@ class NewShixunModel extends Component{ this.getdatalist(page,type,status,keyword,order,diff,limit) } + updatepathlist=(sorts,orders)=>{ let{page,type,keyword,order,diff,limit,status,sort}=this.state; let seartorders; @@ -352,7 +364,7 @@ class NewShixunModel extends Component{ } render() { - let {diff,Grouplist,status,shixun_list,shixuns_count,page,type,order,sort}=this.state; + let {diff,Grouplist,status,shixun_list,shixuns_count,page,type,order,sort,belongtoindex}=this.state; // let {visible,patheditarry}=this.props; // console.log(Grouplist) // console.log(allGrouplist) @@ -428,6 +440,9 @@ class NewShixunModel extends Component{ .ant-drawer-body { padding:15px 24px 24px 0px; } + .ant-dropdown { + z-index:11000 + } ` } @@ -443,7 +458,7 @@ class NewShixunModel extends Component{
    -
    +
    @@ -469,7 +484,36 @@ class NewShixunModel extends Component{ onSearch={ (value)=>this.setdatafuns(value)} />
    -
    + + + + + + + {this.props.type==='shixuns'? +
    +

    筛选:

    +

    this.belongto("all")}>全部实训

    +

    this.belongto("mine")}>我的实训

    +
    :"" + } + {/*{this.props.type==='shixuns'? */} + {/* */} + {/* {diff===0?"难度":diff===1?"初级":diff===2?"中级":diff===3?"高级":diff===4?"顶级":""}*/} + {/* */} + {/*:""}*/} + {this.props.type==='shixuns'? +
    +

    难度:

    +

    this.DropdownClick(0)}>全部

    +

    this.DropdownClick(1)}>初级

    +

    this.DropdownClick(2)}>中级

    +

    this.DropdownClick(3)}>高级

    +

    this.DropdownClick(4)}>顶级

    +
    :"" + } + +
    @@ -506,17 +550,18 @@ class NewShixunModel extends Component{ :"":""} - {this.props.type==='shixuns'? - - {diff===0?"难度":diff===1?"初级":diff===2?"中级":diff===3?"高级":diff===4?"顶级":""} - - :""} +
    -
    - {/*this.props.hideNewShixunModelType()}>返回*/} - this.belongto("mine")}>我的{this.props.type==='shixuns'?'实训':"课程"} - this.belongto("all")}>全部{this.props.type==='shixuns'?'实训':"课程"} +
    + + {this.props.type==='shixuns'?"": + this.belongto("mine")}>我的课程 + } + + {this.props.type==='shixuns'?"": + this.belongto("all")}>全部课程 + }
    @@ -536,11 +581,26 @@ class NewShixunModel extends Component{ className="fl task-hide edu-txt-left mt3" name="shixun_homework[]" > + + { + this.props.type==='shixuns'? + ( + item.is_jupyter===true? +
    +

    + Jupyter +

    +
    + :"" + ) + :"" + } +
    + } + - -
    -
    - -
  • this.changeTag("all")} className={ type==="all" ? "active edu-filter-cir-grey font-12":"edu-filter-cir-grey font-12"}>全部
  • -
  • this.changeTag("my")} className={ type==="my" ? " active edu-filter-cir-grey font-12":"edu-filter-cir-grey font-12"}>我的资源
  • -
    - this.SenttotheSearch(value)} - style={{width: '100%'}} - /> + - -
      -
    • 资源名称
    • -
    • 大小
    • -
    • 发布人
    • -
    • 时间
    • -
    +
      +
    • 资源名称
    • +
    • 大小
    • +
    • 发布人
    • +
    • 时间
    • +
    - -
    - - { - Resourcelist === undefined ?"":Resourcelist.files.length===0?: - - - { - Resourcelist.files.map((item,key)=>{ - return( -
    -
  • - - - -
  • -
  • {item.filesize}
  • -
  • {item.author.name}
  • -
  • {formatDate(item.created_on)}
  • -
    - ) - }) - } -
    - - } -
    +
    -
    - 发布设置: - - - 立即发布 - - - - 延期发布 - {this.props.course_groups.length>0?"":this.onChangeTimepublish(e,index,undefined,1)} - disabledTime={disabledDateTime} - disabledDate={disabledDate} - disabled={this.state.Radiovalue===1?false:true} - />} - - - (按照设置的时间定时发布) - - - - {this.props.course_groups.length>0?this.props.isStudent()===true?"":
    - {this.state.Radiovalue===1?:""} { - course_group_publish_times.map((item,key)=>{ - return( -
    - - - this.onChangeTimepublish(e,index,key,2)} - // onChange={ this.onChangeTimepublish } - disabledTime={disabledDateTime} - disabledDate={disabledDate} - /> - {key!=0?this.deletegrouppublish(key)}>:""} - {key+1===this.props.course_groups.length?"":} -
    - ) - }) + Resourcelist === undefined ?"":Resourcelist.files.length===0?: + + + { + Resourcelist.files.map((item,key)=>{ + return( +
    +
  • + + + +
  • +
  • {item.filesize}
  • +
  • {item.author.name}
  • +
  • {formatDate(item.created_on)}
  • +
    + ) + }) + } +
    } -
    :""} -
    +
    + +
    + 发布设置: + + + 立即发布 + + + + 延期发布 + this.onChangeTimepublish(e,index,undefined,1)} + disabledTime={disabledDateTime} + disabledDate={disabledDate} + disabled={this.state.Radiovalue===1?false:true} + /> + + + (按照设置的时间定时发布) + +
    - {this.state.patheditarrytype===true?

    请选择资源

    :""} - {this.state.Radiovaluetype===true?

    发布时间不能为空

    :""} -
    + {this.state.patheditarrytype===true?

    请选择资源

    :""} + {this.state.Radiovaluetype===true?

    发布时间不能为空

    :""} +
    -
    - - :""} + + :""}
    ) } diff --git a/public/react/src/modules/courses/coursesPublic/sendResource.js b/public/react/src/modules/courses/coursesPublic/sendResource.js index b0f4b1b69..73a0f6630 100644 --- a/public/react/src/modules/courses/coursesPublic/sendResource.js +++ b/public/react/src/modules/courses/coursesPublic/sendResource.js @@ -14,12 +14,12 @@ function range(start, end) { } return result; } - function disabledDateTime() { return { + // disabledHours: () => range(0, 24).splice(4, 20), disabledMinutes: () => range(1, 30).concat(range(31, 60)), - // disabledSeconds: () => range(0,60) - } + // disabledSeconds: () => [0, 60], + }; } function disabledDate(current) { @@ -45,8 +45,8 @@ class Sendresource extends Component{ // moment(new Date()).format('YYYY-MM-DD HH:mm:ss'), course_group_publish_times:[ { - group_id : undefined, - publish_time :undefined, + course_group_id : undefined, + publish_time :"" }], course_groups:undefined, course_groups_count:undefined, @@ -86,34 +86,34 @@ class Sendresource extends Component{ } onAttachmentRemove = (file) => { - if(!file.percent || file.percent == 100){ - const url = `/attachments/${file.response ? file.response.id : file.uid}.json` - axios.delete(url, { - }) - .then((response) => { - if (response.data) { - const { status } = response.data; - if (status == 0) { - this.setState({ - fileListtype:false, - fileList:[] - }) - } - - } - }) - .catch(function (error) { - console.log(error); - }); - this.setState({ - fileListtype:false, - }) - }else{ - this.setState({ - fileListtype:false, - fileList:[] - }) - } + if(!file.percent || file.percent == 100){ + const url = `/attachments/${file.response ? file.response.id : file.uid}.json` + axios.delete(url, { + }) + .then((response) => { + if (response.data) { + const { status } = response.data; + if (status == 0) { + this.setState({ + fileListtype:false, + fileList:[] + }) + } + + } + }) + .catch(function (error) { + console.log(error); + }); + this.setState({ + fileListtype:false, + }) + }else{ + this.setState({ + fileListtype:false, + fileList:[] + }) + } } ModalCancelModalCancel=()=>{ @@ -127,7 +127,7 @@ class Sendresource extends Component{ } Saves=()=>{ - let {fileList,description,is_public,datatime,Radiovalue,course_group_publish_times} =this.state; + let {fileList,description,is_public,datatime,Radiovalue} =this.state; let newfileList=[]; for(var list of fileList){ @@ -141,7 +141,7 @@ class Sendresource extends Component{ return } - if(this.state.Radiovalue===1&&this.props.course_groups.length===0){ + if(this.state.Radiovalue===1){ if(datatime===undefined||datatime===null||datatime=== ""){ this.setState({ Radiovaluetype:true @@ -156,7 +156,6 @@ class Sendresource extends Component{ - if(description===undefined){ }else if(description.length>100){ @@ -177,10 +176,9 @@ class Sendresource extends Component{ course_second_category_id:this.props.coursesidtype===undefined||this.props.coursesidtype==="node"?0:attachmentId, attachment_ids:newfileList, is_public:is_public, - publish_time:this.props.course_groups.length===0?Radiovalue===1?datatime===undefined? undefined:datatime:undefined:"", + publish_time:Radiovalue===1?datatime===undefined? undefined:datatime:undefined, description:description, delay_publish:Radiovalue, - group_settings:this.props.course_groups.length===0?"":course_group_publish_times }).then((result)=>{ if(result.data.status===0){ @@ -239,42 +237,10 @@ class Sendresource extends Component{ Radiovalue: e.target.value, }); } - - addgrouppublish=()=>{ - let newlist=this.state.course_group_publish_times; - newlist.push( { - group_id : undefined, - publish_time :undefined, - }) - this.setState({ - course_group_publish_times:newlist - }) - } - deletegrouppublish=(key)=>{ - let newlist=this.state.course_group_publish_times; - newlist.splice(key,1); - this.setState({ - course_group_publish_times:newlist - }) - } - selectassigngroups=(e,index,key)=>{ - let {course_group_publish_times}=this.state; - let newgroup_publish=course_group_publish_times; - for(var i=0; i {/*提示*/} @@ -406,8 +372,8 @@ class Sendresource extends Component{ {this.props.course_is_public===true?
    公开: - 选中,所有用户可见,否则课堂成员可见 - + 选中,所有用户可见,否则课堂成员可见 +
    :""} - {this.props.course_groups.length>0?this.props.isStudent()===true?"":
    - {this.state.Radiovalue===1?:""} - { - course_group_publish_times.map((item,key)=>{ - return( -
    - - - this.onChangeTimepublish(e,index,key,2)} - // onChange={ this.onChangeTimepublish } - disabledTime={disabledDateTime} - disabledDate={disabledDate} - /> - {key!=0?this.deletegrouppublish(key)}>:""} - {key+1===this.props.course_groups.length?"":} -
    - ) - }) - - } -
    :""}
    diff --git a/public/react/src/modules/courses/elearning/YslDetailCards.js b/public/react/src/modules/courses/elearning/YslDetailCards.js index 3c6b5daff..436e0a984 100644 --- a/public/react/src/modules/courses/elearning/YslDetailCards.js +++ b/public/react/src/modules/courses/elearning/YslDetailCards.js @@ -48,7 +48,9 @@ class YslDetailCards extends Component{ }; hideparagraph=()=>{ this.setState({ - showparagraph:false + showparagraph:false, + showparagraphkey:null, + showparagraphindex:null }) }; startgameid=(id)=>{ @@ -490,13 +492,13 @@ class YslDetailCards extends Component{ : } - + {key+1}-{index+1}  {line.shixun_name} { - this.props.current_user&&this.props.current_user.admin===false&&line.shixun_status==="暂未公开"? + line.allow_visit===false&&line.shixun_status==="暂未公开"?
  • 暂未公开
  • :
  • @@ -509,6 +511,15 @@ class YslDetailCards extends Component{
  • } + + {line.allow_visit===false&&line.shixun_status==="暂未公开"?"":
  • + { + showparagraphkey === key && showparagraphindex === index ? "" : + 实验任务 {line.challenges_count} + } +
  • } +
    ) }) } @@ -518,6 +529,7 @@ class YslDetailCards extends Component{ { this.props.isAdmin()===true? { // let { time } = this.state; - let h=moment(parseInt(time)*1000).hour()-8; - let m=moment(parseInt(time)*1000).minutes(); - let s=moment(parseInt(time)*1000).seconds(); - this.timer = setInterval(() => { - if(time>0){ - if(s==0){ - if(m > 0){ - m--; - } - s=59; - }else{ - s--; - } - this.setState({ - hour:h, - minute:m, - second:s - }) - if(h==0 && m==0 && s==0){ - clearInterval(this.timer); - this.autoCommitExercise(); - } - }else{ - clearInterval(this.timer); - } - },1000) + // let h=moment(parseInt(time)*1000).hour()-8; + // let m=moment(parseInt(time)*1000).minutes(); + // let s=moment(parseInt(time)*1000).seconds(); + // this.timer = setInterval(() => { + // if(time>0){ + // if(s==0){ + // if(m > 0){ + // m--; + // } + // s=59; + // }else{ + // s--; + // } + // this.setState({ + // hour:h, + // minute:m, + // second:s + // }) + // if(h==0 && m==0 && s==0){ + // clearInterval(this.timer); + // this.autoCommitExercise(); + // } + // }else{ + // clearInterval(this.timer); + // } + // },1000) } + //自动交卷 autoCommitExercise=()=>{ let eId=this.props.match.params.Id; @@ -140,13 +148,18 @@ class ExerciseReviewAndAnswer extends Component{ modalsBottomval:"不能再修改答题", ModalCancel:undefined, ModalSave:this.sureCommit, - Loadtype:true + Loadtype:true, + time:null, }) this.props.showNotification(`${result.data.message}`); } if(result.data.status===-2){ - this.remainTime(parseInt(result.data.message)) + // this.remainTime(parseInt(result.data.message)) + this.setState({ + time:parseInt(result.data.mess) + }) + this.deadline(parseInt(result.data.message)) } } }).catch((error)=>{ @@ -244,7 +257,8 @@ class ExerciseReviewAndAnswer extends Component{ isSpin:false }) if(result.data.exercise.left_time != null){ - this.remainTime(result.data.exercise.left_time); + // this.remainTime(result.data.exercise.left_time); + this.deadline(result.data.exercise.left_time) } } }).catch((error)=>{ @@ -571,6 +585,19 @@ class ExerciseReviewAndAnswer extends Component{ this.props.history.push(`/courses/${coursesId}/exercises/${eId}/student_exercise_list?tab=0`) } + deadline=(time)=>{ + if(time===null){ + this.setState({ + Datetime:0 + }) + }else{ + this.setState({ + Datetime:Date.now() + time * 1000 + }) + // return Date.now() + time * 1000 ; + } + + } render(){ let coursesId=this.props.match.params.coursesId; let eId=this.props.match.params.Id; @@ -613,6 +640,7 @@ class ExerciseReviewAndAnswer extends Component{ const { current_user } = this.props // console.log(data&&data.exercise.user_name) document.title=courseName&&courseName; + return(
    @@ -663,6 +691,7 @@ class ExerciseReviewAndAnswer extends Component{

    {exercise && exercise.exercise_name} + @@ -671,15 +700,18 @@ class ExerciseReviewAndAnswer extends Component{ 返回 : time && time != 0 ? -

    -
  • { hour >= 10 ? hour : '0'+hour}
  • - : -
  • { minute >= 10 ? minute : '0'+minute}
  • - : -
  • { second >= 10 ? second : '0'+second}
  • -
    :"" +
    + +
    + :"" } - + {/*
    */} + {/*
  • { hour >= 10 ? hour : '0'+hour}
  • */} + {/* :*/} + {/*
  • { minute >= 10 ? minute : '0'+minute}
  • */} + {/* :*/} + {/*
  • { second >= 10 ? second : '0'+second}
  • */} + {/*
    */} { isAdmin && 打回重做 } diff --git a/public/react/src/modules/courses/graduation/tasks/GraduationTasksnew.js b/public/react/src/modules/courses/graduation/tasks/GraduationTasksnew.js index 745a249b0..a5d4d7011 100644 --- a/public/react/src/modules/courses/graduation/tasks/GraduationTasksnew.js +++ b/public/react/src/modules/courses/graduation/tasks/GraduationTasksnew.js @@ -483,8 +483,8 @@ class GraduationTasksnew extends Component { + 上传附件 + (单个文件150M以内) diff --git a/public/react/src/modules/courses/poll/PollNew.js b/public/react/src/modules/courses/poll/PollNew.js index 248434295..3b50695e8 100644 --- a/public/react/src/modules/courses/poll/PollNew.js +++ b/public/react/src/modules/courses/poll/PollNew.js @@ -866,6 +866,7 @@ class PollNew extends Component { let newarr = [...arr]; newarr.splice(indexo, 1); if(bool === true) { + console.log("shangchu1"); this.setState({ adddom: newarr, q_countst: 0, @@ -878,6 +879,8 @@ class PollNew extends Component { }) }else{ + console.log("shangchu2"); + this.setState({ adddom: newarr, q_countst: 0, @@ -1037,7 +1040,7 @@ class PollNew extends Component { if(object.question.max_choices){ if(object.question.max_choices>0){ if (object.question.max_choices < object.question.min_choices) { - this.props.showNotification(`可选:最小和最大限制须同时为数值或者“--"`); + this.props.showNotification(`可选的最大限制不能小于最小限制`); return; } } @@ -1257,7 +1260,7 @@ class PollNew extends Component { if(object.question.max_choices){ if(object.question.max_choices>0){ if (object.question.max_choices < object.question.min_choices) { - this.props.showNotification(`可选:最小和最大限制须同时为数值或者“--"`); + this.props.showNotification(`可选的最大限制不能小于最小限制`); return; } } @@ -1416,6 +1419,7 @@ class PollNew extends Component { } if(bool === true){ + console.log("tianjiadao1"); this.setState({ q_countst: 1, bindingid:undefined, @@ -1423,6 +1427,7 @@ class PollNew extends Component { newoption: false, }) }else { + console.log("tianjiadao2"); this.setState({ q_countst: 1, Newdisplay:false, @@ -1448,10 +1453,7 @@ class PollNew extends Component { // indexo 第几个数组 //object 单个数组数据 Deleteadddomthree = (indexo, object,bool) => { - this.setState({ - Newdisplay:false, - newoption: false, - }) + // console.log("deleteadddom 349") var thiss = this; let arr = this.state.adddom; @@ -1479,6 +1481,12 @@ class PollNew extends Component { this.props.showNotification('题目不能为空!'); return } + + if (newarr[indexo].question.question_title.match(/^[ ]*$/)) { + this.props.showNotification('题目不能为空!'); + return + } + if (max > 0) { if (object.question.question_type === 1) { this.props.showNotification('选项内容不能为空!'); @@ -1578,7 +1586,7 @@ class PollNew extends Component { if(object.question.max_choices){ if(object.question.max_choices>0){ if (object.question.max_choices < object.question.min_choices) { - this.props.showNotification(`可选:最小和最大限制须同时为数值或者“--"`); + this.props.showNotification(`可选的最大限制不能小于最小限制`); return; } } @@ -1789,7 +1797,7 @@ class PollNew extends Component { if(object.question.max_choices){ if(object.question.max_choices>0){ if (object.question.max_choices < object.question.min_choices) { - this.props.showNotification(`可选:最小和最大限制须同时为数值或者“--"`); + this.props.showNotification(`可选的最大限制不能小于最小限制`); return; } } @@ -2015,6 +2023,12 @@ class PollNew extends Component { if (result.data.status === 0) { this.props.showNotification(`已完成`); thiss.thisinitializationdatanew(); + console.log("已完成了了了1"); + this.setState({ + Newdisplay:false, + newoption: false, + + }) // console.log("确认创建问题") // console.log(result) // try { @@ -2026,11 +2040,24 @@ class PollNew extends Component { // // } } + }else{ + this.setState({ + Newdisplay:true, + newoption: false, + + }) } // } catch (e) { // // } + }).catch((error) => { + console.log(error) + this.setState({ + Newdisplay:true, + newoption: false, + + }) }) @@ -2088,13 +2115,27 @@ class PollNew extends Component { axios.put(url,datay).then((result) => { try { if (result.data.status === 0) { + console.log("编辑题目成功1"); this.props.showNotification(`编辑题目成功`); thiss.thisinitializationdatanew(); + this.setState({ + Newdisplay:false, + newoption: false, + }) } } catch (e) { // console.log("调用了edittotheserver") - console.log(e) + this.setState({ + Newdisplay:true, + newoption: false, + }) } + }).catch((error) => { + console.log(error) + this.setState({ + Newdisplay:true, + newoption: false, + }) }) } @@ -2679,7 +2720,8 @@ class PollNew extends Component { // console.log("2301"); // console.log(newr); // window.history.pushState('','',newUrl+'?tab='+e); - window.location.href = `/courses/${coursesId}/polls/${result.data.data.id}/edit`; + + // window.location.href = `/courses/${coursesId}/polls/${result.data.data.id}/edit`; }) } else { @@ -2767,6 +2809,7 @@ class PollNew extends Component { height: '30px', lineHeight: '30px', }; + const hejiine=this.state.mysingles + this.state.mydoubles + this.state.mymainsint; // console.log(this.state.projects===undefined?"":this.state.projects.poll_questions) var displaymysave = (mysave === true) ? "" : "display:none;"; @@ -2936,7 +2979,7 @@ class PollNew extends Component { { - this.state.mysingles + this.state.mydoubles + this.state.mymainsint === 0 ? "" : + hejiine=== 0 ? "" :
    {this.state.mysingles === 0 ? "" : 合计 {this.state.mysingles === undefined ? "" : this.state.mydoubles === undefined ? "" : this.state.mymainsint === undefined ? "" : this.state.mysingles + this.state.mydoubles + this.state.mymainsint} + className="color-blue">{this.state.mysingles === undefined ? "" : this.state.mydoubles === undefined ? "" : this.state.mymainsint === undefined ? "" : hejiine}
    } @@ -3573,7 +3616,7 @@ class PollNew extends Component { { this.state.Newdisplay === true?
    - {this.state.adddom === undefined ? "" : this.state.adddom.map((itemo, indexo) => { + {this.state.adddom === undefined ? "publishtimeids123123" : this.state.adddom.map((itemo, indexo) => { // console.log('打印this.state.adddom') // console.log(this.state.adddom); let arrid = itemo.question.id; diff --git a/public/react/src/modules/courses/shixunHomework/Listofworksstudentone.js b/public/react/src/modules/courses/shixunHomework/Listofworksstudentone.js index 855f860ef..03bf13f13 100644 --- a/public/react/src/modules/courses/shixunHomework/Listofworksstudentone.js +++ b/public/react/src/modules/courses/shixunHomework/Listofworksstudentone.js @@ -46,6 +46,7 @@ const {Option} = Select; //作品列表(学生) let allow_lates=false; let answer_open_evaluation=false; +// Curcomlevel class Listofworksstudentone extends Component { //unifiedsetting 统一设置 //allowreplenishment 允许补交 @@ -60,6 +61,9 @@ class Listofworksstudentone extends Component { //要提交的分班状态checkedValuesineinfo //searchtext 输入的姓名和学号 //order 排序时间 + //当前成绩work_score + //关卡得分final_score + this.state = { jobsettingsdata: undefined, endTime: "2018/11/10 17:10:00", @@ -100,32 +104,32 @@ class Listofworksstudentone extends Component { key: 'number', align: "center", className: 'font-14', - width: '100px', + width: '90px', render: (text, record) => ( - + {record.number === undefined ? -- : record.number === "" ? -- : record.number === "--" ? -- : {record.number} } @@ -183,48 +187,46 @@ class Listofworksstudentone extends Component { dataIndex: 'stduynumber', key: 'stduynumber', align: "center", - className: 'font-14 maxnamewidth145', - width: '145px', + className: 'font-14 maxnamewidth120', + width: '120px', render: (text, record) => ( - {record.stduynumber === undefined ? -- : record.stduynumber === null ? -- : record.stduynumber === "" ? -- : { record.stduynumber } - } - ), }, @@ -233,43 +235,43 @@ class Listofworksstudentone extends Component { key: 'classroom', dataIndex: 'classroom', align: "center", - className: 'font-14 maxnamewidth145', - width: '145px', + className: 'font-14 maxnamewidth120', + width: '120px', render: (text, record) => ( - + {record.classroom === undefined ? - -- : record.classroom === "" ? - -- : record.classroom === null ? - -- : - {record.classroom} } ), }, { - title: '提交状态', + title: '作品状态', dataIndex: 'submitstate', key: 'submitstate', align: "center", @@ -277,15 +279,17 @@ class Listofworksstudentone extends Component { width: '98px', render: (text, record) => ( - {record.submitstate === undefined ? "--" : record.submitstate === "" ? "--" : record.submitstate === null ? "--" : record.submitstate} + } : record.submitstate === "按时通关" ? {color: '#29BD8B', textAlign: "center", width: '98px',} + : record.submitstate === "未通关" ? {color: '#F69707', textAlign: "center", width: '98px',} + : { + color: '#747A7F', + textAlign: "center", + width: '98px', + }}>{record.submitstate === undefined ? "--" : record.submitstate === "" ? "--" : record.submitstate === null ? "--" : record.submitstate} ), @@ -310,14 +314,14 @@ class Listofworksstudentone extends Component { } }> {record.cost_time === null ? "--" : record.cost_time === undefined ? "--" : record.cost_time === "--" ? "--" : - {record.cost_time === null ? "--" : record.cost_time === undefined ? "--" : record.cost_time} + } + >{record.cost_time === null ? "--" : record.cost_time === undefined ? "--" : record.cost_time} } @@ -340,7 +344,26 @@ class Listofworksstudentone extends Component { // ), // }, { - title: '完成情况', + title: '最新完成关卡', + dataIndex: 'curcomlevel', + key: 'curcomlevel', + align: "center", + className: 'font-14', + width: '99px', + render: (text, record) => ( + + {record.Curcomlevel + "/" + this.state.challenges_count} + + ), + }, + { + title: '截止前完成关卡', dataIndex: 'completion', key: 'completion', align: "center", @@ -359,9 +382,12 @@ class Listofworksstudentone extends Component { ), }, { - title: '关卡得分', - dataIndex: 'levelscore', - key: 'levelscore', + title:关卡得分 + 计算规则:
    + 截止前学员完成的关卡才有成绩
    + }>
    , + dataIndex: 'final_score', + key: 'final_score', align: 'center', className: 'font-14', width: '99px', @@ -369,19 +395,19 @@ class Listofworksstudentone extends Component { - = 90 ? {color: '#DD1717', textAlign: "center", width: '99px',} : { + } : parseInt(record.final_score) >= 90 ? {color: '#DD1717', textAlign: "center", width: '99px',} : { color: '#747A7F', textAlign: "center", width: '99px', - }}>{record.levelscore} + }}>{record.final_score} ) }, @@ -435,26 +461,26 @@ class Listofworksstudentone extends Component { }, { title: '当前成绩', - dataIndex: 'levelscore', - key: 'levelscore', + dataIndex: 'work_score', + key: 'work_score', align: "center", className: 'font-14', width: '99px', render: (text, record) => ( { - record.levelscore && record.levelscore === "--" ? - {record.levelscore} + record.work_score && record.work_score === "--" ? + {record.work_score} : - = 90 ? { + = 90 ? { color: '#DD1717', textAlign: "center", width: '99px', - } : parseInt(record.levelscore) <= 60 ? { + } : parseInt(record.work_score) <= 60 ? { color: '#FF6800', textAlign: "center", width: '99px', - } : {color: '#747A7F', textAlign: "center", width: '99px',}}>{record.levelscore} + } : {color: '#747A7F', textAlign: "center", width: '99px',}}>{record.work_score} } @@ -466,10 +492,11 @@ class Listofworksstudentone extends Component { key: 'operating', align: "center", className: 'font-14', + width: '40px', render: (text, record) => ( - + { - record.submitstate === "未提交" ? -- + record.submitstate === "未开启" ? -- : ( - {record.submitstate === undefined ? "--" : record.submitstate === "" ? "--" : record.submitstate === null ? "--" : record.submitstate} + } : record.submitstate === "按时通关" ? {color: '#29BD8B', textAlign: "center", width: '98px',} + : record.submitstate === "未通关" ? {color: '#F69707', textAlign: "center", width: '98px',} + : { + color: '#747A7F', + textAlign: "center", + width: '98px', + }}>{record.submitstate === undefined ? "--" : record.submitstate === "" ? "--" : record.submitstate === null ? "--" : record.submitstate} ), @@ -709,7 +738,26 @@ class Listofworksstudentone extends Component { // ), // }, { - title: '完成情况', + title: '最新完成关卡', + dataIndex: 'curcomlevel', + key: 'curcomlevel', + align: "center", + className: 'font-14', + width: '99px', + render: (text, record) => ( + + {record.Curcomlevel + "/" + this.state.challenges_count} + + ), + }, + { + title: '截止前完成关卡', dataIndex: 'completion', key: 'completion', align: "center", @@ -728,9 +776,12 @@ class Listofworksstudentone extends Component { ), }, { - title: '关卡得分', - dataIndex: 'levelscore', - key: 'levelscore', + title:关卡得分 + 计算规则:
    + 截止前学员完成的关卡才有成绩
    + }>
    , + dataIndex: 'final_score', + key: 'final_score', align: 'center', className: 'font-14', width: '99px', @@ -738,19 +789,19 @@ class Listofworksstudentone extends Component { - = 90 ? {color: '#DD1717', textAlign: "center", width: '99px',} : { + } : parseInt(record.final_score) >= 90 ? {color: '#DD1717', textAlign: "center", width: '99px',} : { color: '#747A7F', textAlign: "center", width: '99px', - }}>{record.levelscore} + }}>{record.final_score}
    ) }, @@ -804,26 +855,26 @@ class Listofworksstudentone extends Component { }, { title: '当前成绩', - dataIndex: 'levelscore', - key: 'levelscore', + dataIndex: 'work_score', + key: 'work_score', align: "center", className: 'font-14', width: '99px', render: (text, record) => ( { - record.levelscore && record.levelscore === "--" ? - {record.levelscore} + record.work_score && record.work_score === "--" ? + {record.work_score} : - = 90 ? { + = 90 ? { color: '#DD1717', textAlign: "center", width: '99px', - } : parseInt(record.levelscore) <= 60 ? { + } : parseInt(record.work_score) <= 60 ? { color: '#FF6800', textAlign: "center", width: '99px', - } : {color: '#747A7F', textAlign: "center", width: '99px',}}>{record.levelscore} + } : {color: '#747A7F', textAlign: "center", width: '99px',}}>{record.work_score} } @@ -835,10 +886,12 @@ class Listofworksstudentone extends Component { key: 'operating', align: "center", className: 'font-14', + width: '40px', + render: (text, record) => ( - + { - record.submitstate === "未提交" ? -- + record.submitstate === "未开启" ? -- : ( - {record.submitstate} + } : record.submitstate === "按时通关" ? {color: '#29BD8B', textAlign: "center"} + : record.submitstate === "未通关" ? {color: '#F69707', textAlign: "center", width: '98px'} + : { + color: '#747A7F', + textAlign: "center" + }}>{record.submitstate} ) @@ -1036,7 +1091,26 @@ class Listofworksstudentone extends Component { // ), // }, { - title: '完成情况', + title: '最新完成关卡', + dataIndex: 'curcomlevel', + key: 'curcomlevel', + align: "center", + className: 'font-14', + width: '99px', + render: (text, record) => ( + + {record.Curcomlevel + "/" + this.state.challenges_count} + + ), + }, + { + title: '截止前完成关卡', dataIndex: 'completion', key: 'completion', align: 'center', @@ -1051,23 +1125,26 @@ class Listofworksstudentone extends Component { ) }, { - title: '关卡得分', - dataIndex: 'levelscore', - key: 'levelscore', + title:关卡得分 + 计算规则:
    + 截止前学员完成的关卡才有成绩
    + }>
    , + dataIndex: 'final_score', + key: 'final_score', align: 'center', className: 'font-14', render: (text, record) => ( - = 90 ? {color: '#DD1717', "text-align": "center"} : { + } : parseInt(record.final_score) >= 90 ? {color: '#DD1717', "text-align": "center"} : { color: '#747A7F', "text-align": "center" - }}>{record.levelscore} + }}>{record.final_score} ) }, @@ -1094,12 +1171,12 @@ class Listofworksstudentone extends Component { { record.efficiencyscore && record.efficiencyscore === "--" ? ( this.state.allow_late && this.state.allow_late === false ? - + -- : this.state.allow_late && this.state.allow_late === true ? - + -- : @@ -1125,8 +1202,8 @@ class Listofworksstudentone extends Component { }, { title: '当前成绩', - dataIndex: 'finalscore', - key: 'finalscore', + dataIndex: 'work_score', + key: 'work_score', align: 'center', className: 'font-14', sorter: true, @@ -1138,35 +1215,35 @@ class Listofworksstudentone extends Component { record.ultimate_score === true ? {/*
    {record.user_name}{record.user_login}
    */} -
    {record.finalscore === "--" ? 最终调整成绩:0分 : - 最终调整成绩:{record.finalscore}分}
    +
    {record.work_score === "--" ? 最终调整成绩:0分 : + 最终调整成绩:{record.work_score}分}
    }> { - record.finalscore && record.finalscore === "--" ? + record.work_score && record.work_score === "--" ? {record.finalscore} + }}>{record.work_score} : - = 90 ? {color: '#DD1717', "text-align": "center"} : { + } : parseInt(record.work_score) >= 90 ? {color: '#DD1717', "text-align": "center"} : { color: '#747A7F', "text-align": "center" - }}>{record.finalscore} + }}>{record.work_score} } : {/*
    {record.user_name}{record.user_login}
    */} -
    {record.levelscore === "--" ? 关卡得分:0分 : - 关卡得分:{record.levelscore}分}
    +
    {record.final_score === "--" ? 关卡得分:0分 : + 关卡得分:{record.final_score}分}
    {record.efficiencyscore === "--" ? 效率评分:0分 : 效率评分:{record.efficiencyscore}分}
    @@ -1176,27 +1253,27 @@ class Listofworksstudentone extends Component { {answer_open_evaluation===true?"":
    查看参考答案:{record.view_answer_count}关
    } -
    {record.finalscore === "--" ? 最终成绩:0分 : - 最终成绩:{record.finalscore}分}
    +
    {record.work_score === "--" ? 最终成绩:0分 : + 最终成绩:{record.work_score}分}
    }> { - record.finalscore && record.finalscore === "--" ? + record.work_score && record.work_score === "--" ? {record.finalscore} + }}>{record.work_score} : - = 90 ? {color: '#DD1717', "text-align": "center"} : { + } : parseInt(record.work_score) >= 90 ? {color: '#DD1717', "text-align": "center"} : { color: '#747A7F', "text-align": "center" - }}>{record.finalscore} + }}>{record.work_score} } @@ -1211,9 +1288,11 @@ class Listofworksstudentone extends Component { display: 'block', align: 'center', className: 'font-14', + width: '40px', + render: (text, record) => ( - record.submitstate === "未提交" ? - this.Viewstudenttraininginformationtysl2(e, record)} onClick={() => this.Viewstudenttraininginformationt(record)}>评阅 : @@ -1315,19 +1394,21 @@ class Listofworksstudentone extends Component { ) }, { - title: '提交状态', + title: '作品状态', dataIndex: 'submitstate', key: 'submitstate', align: 'center', className: 'font-14', render: (text, record) => ( - {record.submitstate} + } : record.submitstate === "按时通关" ? {color: '#29BD8B', textAlign: "center"} + : record.submitstate === "未通关" ? {color: '#F69707', textAlign: "center", width: '98px'} + : { + color: '#747A7F', + textAlign: "center" + }}>{record.submitstate} ) @@ -1360,20 +1441,39 @@ class Listofworksstudentone extends Component { } }> {record.cost_time === null ? "--" : record.cost_time === undefined ? "--" : record.cost_time === "--" ? "--" : - {record.cost_time === null ? "--" : record.cost_time === undefined ? "--" : record.cost_time} - + } + >{record.cost_time === null ? "--" : record.cost_time === undefined ? "--" : record.cost_time} + } ) }, { - title: '完成情况', + title: '最新完成关卡', + dataIndex: 'curcomlevel', + key: 'curcomlevel', + align: "center", + className: 'font-14', + width: '99px', + render: (text, record) => ( + + {record.Curcomlevel + "/" + this.state.challenges_count} + + ), + }, + { + title: '截止前完成关卡', dataIndex: 'completion', key: 'completion', align: 'center', @@ -1388,23 +1488,26 @@ class Listofworksstudentone extends Component { ) }, { - title: '关卡得分', - dataIndex: 'levelscore', - key: 'levelscore', + title:关卡得分 + 计算规则:
    + 截止前学员完成的关卡才有成绩
    + }>
    , + dataIndex: 'final_score', + key: 'final_score', align: 'center', className: 'font-14', render: (text, record) => ( - = 90 ? {color: '#DD1717', textAlign: "center"} : { + } : parseInt(record.final_score) >= 90 ? {color: '#DD1717', textAlign: "center"} : { color: '#747A7F', textAlign: "center" - }}>{record.levelscore} + }}>{record.final_score} ) }, @@ -1431,12 +1534,12 @@ class Listofworksstudentone extends Component { { record.efficiencyscore && record.efficiencyscore === "--" ? ( this.state.allow_late && this.state.allow_late === false ? - + -- : this.state.allow_late && this.state.allow_late === true ? - + -- : @@ -1462,8 +1565,8 @@ class Listofworksstudentone extends Component { }, { title: '当前成绩', - dataIndex: 'finalscore', - key: 'finalscore', + dataIndex: 'work_score', + key: 'work_score', align: 'center', className: 'font-14', sorter: true, @@ -1475,35 +1578,35 @@ class Listofworksstudentone extends Component { record.ultimate_score === true ? {/*
    {record.user_name}{record.user_login}
    */} -
    {record.finalscore === "--" ? 最终调整成绩:0分 : - 最终调整成绩:{record.finalscore}分}
    +
    {record.work_score === "--" ? 最终调整成绩:0分 : + 最终调整成绩:{record.work_score}分}
    }> { - record.finalscore && record.finalscore === "--" ? + record.work_score && record.work_score === "--" ? {record.finalscore} + }}>{record.work_score} : - = 90 ? {color: '#DD1717', textAlign: "center"} : { + } : parseInt(record.work_score) >= 90 ? {color: '#DD1717', textAlign: "center"} : { color: '#747A7F', textAlign: "center" - }}>{record.finalscore} + }}>{record.work_score} } : {/*
    {record.user_name}{record.user_login}
    */} -
    {record.levelscore === "--" ? 关卡得分:0分 : - 关卡得分:{record.levelscore}分}
    +
    {record.final_score === "--" ? 关卡得分:0分 : + 关卡得分:{record.final_score}分}
    {record.efficiencyscore === "--" ? 效率评分:0分 : 效率评分:{record.efficiencyscore}分}
    @@ -1513,27 +1616,27 @@ class Listofworksstudentone extends Component { {answer_open_evaluation===true?"":
    查看参考答案:{record.view_answer_count}关
    } -
    {record.finalscore === "--" ? 最终成绩:0分 : - 最终成绩:{record.finalscore}分}
    +
    {record.work_score === "--" ? 最终成绩:0分 : + 最终成绩:{record.work_score}分}
    }> { - record.finalscore && record.finalscore === "--" ? + record.work_score && record.work_score === "--" ? {record.finalscore} + }}>{record.work_score} : - = 90 ? {color: '#DD1717', textAlign: "center"} : { + } : parseInt(record.work_score) >= 90 ? {color: '#DD1717', textAlign: "center"} : { color: '#747A7F', textAlign: "center" - }}>{record.finalscore} + }}>{record.work_score} } @@ -1548,9 +1651,10 @@ class Listofworksstudentone extends Component { display: 'block', align: 'center', className: 'font-14', + width: '40px', render: (text, record) => ( - record.submitstate === "未提交" ? - this.Viewstudenttraininginformationtysl2(e, record)} onClick={() => this.Viewstudenttraininginformationt(record)}>评阅 : @@ -1710,7 +1814,7 @@ class Listofworksstudentone extends Component { Getalistofworks = (homeworkid, bool) => { // console.log("获取作品列表"); // console.log("935"); - // debugger + // debugger111111111111111 let searchtype = this.props.history.location.search; let urll = `/homework_commons/${homeworkid}/works_list.json`; var datasysl = { @@ -1723,16 +1827,9 @@ class Listofworksstudentone extends Component { course_group: this.state.checkedValuesineinfo, } axios.post(urll, datasysl).then((result) => { - // console.log("980000000____________________"); - if (result === undefined) { return } - // if(result.data.message!==undefined){ - // - // return; - // } - this.setState({ teacherdata: result.data, task_status: result.data.task_status, @@ -1791,7 +1888,7 @@ class Listofworksstudentone extends Component { // 获取作品列表 Getalistofworkstwo = (ordervlue, checkedValuesine, checkedValuesineinfo, searchtext, page, limit) => { - // console.log("获取作品列表"); + // console.log("获取作品列表");222222222222 var homeworkid = this.props.match.params.homeworkid; let urll = `/homework_commons/${homeworkid}/works_list.json`; // console.log(homeworkid); @@ -1799,7 +1896,7 @@ class Listofworksstudentone extends Component { // search:搜索关键字 // order:排序方式:更新时间:update_time,当前成绩:work_score,学号:student_id // b_order:desc:倒序 , asc:顺序 - // work_status:0:未提交, 1:按时完成, 2:延时完成,“”不限 + // work_status:0:未开启, 1:按时完成, 2:延时完成,“”不限 // course_group:分班情况 [232, 231], []:不限(空数组) var order = "asc"; if (ordervlue === "updated_at") { @@ -1977,7 +2074,7 @@ class Listofworksstudentone extends Component { stduynumber: teacherdata.student_id, classroom: teacherdata.group_name, cost_time: teacherdata.cost_time, - submitstate: teacherdata.work_status === 0 ? "未提交" : teacherdata.work_status === 1 ? "按时完成" : teacherdata.work_status === 2 ? "延时完成" : "未提交", + submitstate: teacherdata.work_status === 0 ? "未开启" : teacherdata.work_status === 1 ? "未通关" : teacherdata.work_status === 2 ? "按时通关" : "迟交通关", // updatetime:this.state.teacherdata.student_works[i].update_time, // updatetime:"", updatetime: timedata === "Invalid date" ? "--" : timedata, @@ -1985,12 +2082,14 @@ class Listofworksstudentone extends Component { levelscore: teacherdata.final_score, view_answer_count: teacherdata.view_answer_count, efficiencyscore: teacherdata.eff_score, - finalscore: teacherdata.work_score, + final_score: teacherdata.final_score===null||teacherdata.final_score===undefined||teacherdata.final_score===""?"--":teacherdata.final_score, + work_score: teacherdata.work_score===null||teacherdata.work_score===undefined||teacherdata.work_score===""?"--":teacherdata.work_score, operating: "查看", late_penalty: teacherdata.late_penalty === null ? "0" : teacherdata.late_penalty === undefined ? "0" : teacherdata.late_penalty, ultimate_score: teacherdata.ultimate_score, user_name: teacherdata.user_name, user_login: teacherdata.user_login, + Curcomlevel: teacherdata.current_complete_count===undefined||teacherdata.current_complete_count===null||teacherdata.current_complete_count===""?0:teacherdata.current_complete_count, }) // } @@ -2019,20 +2118,22 @@ class Listofworksstudentone extends Component { stduynumber: student_works[i].student_id, classroom: student_works[i].group_name, cost_time: student_works[i].cost_time, - submitstate: student_works[i].work_status === 0 ? "未提交" : student_works[i].work_status === 1 ? "按时完成" : student_works[i].work_status === 2 ? "延时完成" : "未提交", + submitstate: student_works[i].work_status === 0 ? "未开启" : student_works[i].work_status === 1 ? "未通关" : student_works[i].work_status === 2 ? "按时通关" : "迟交通关", // updatetime:this.state.teacherdata.student_works[i].update_time, // updatetime:"", updatetime: timedata === "Invalid date" ? "--" : timedata, completion: student_works[i].complete_count === null ? "0" : student_works[i].complete_count === undefined ? "0" : student_works[i].complete_count, levelscore: student_works[i].final_score, + final_score: student_works[i].final_score=== null ||student_works[i].final_score=== undefined||student_works[i].final_score=== ""?"--":student_works[i].final_score, + work_score: student_works[i].work_score===null||student_works[i].work_score===undefined||student_works[i].work_score===""?"--":student_works[i].work_score, efficiencyscore: student_works[i].eff_score === "0.0" ? "--" : student_works[i].eff_score === "0" ? "--" : student_works[i].eff_score, - finalscore: student_works[i].work_score, view_answer_count: student_works[i].view_answer_count, operating: "查看", late_penalty: student_works[i].late_penalty === null ? "0" : student_works[i].late_penalty === undefined ? "0" : student_works[i].late_penalty, ultimate_score: student_works[i].ultimate_score, user_name: student_works[i].user_name, user_login: student_works[i].user_login, + Curcomlevel:student_works[i].current_complete_count===null||student_works[i].current_complete_count===null||student_works[i].current_complete_count===""?0:student_works[i].current_complete_count, }) } @@ -2178,7 +2279,7 @@ class Listofworksstudentone extends Component { stduynumber: teacherdata.student_id, classroom: teacherdata.group_name, cost_time: teacherdata.cost_time, - submitstate: teacherdata.work_status === 0 ? "未提交" : teacherdata.work_status === 1 ? "按时完成" : teacherdata.work_status === 2 ? "延时完成" : "未提交", + submitstate: teacherdata.work_status === 0 ? "未开启" : teacherdata.work_status === 1 ? "未通关" : teacherdata.work_status === 2 ? "按时通关" : "迟交通关", // updatetime:this.state.teacherdata.student_works[i].update_time, // updatetime:"", updatetime: timedata === "Invalid date" ? "--" : timedata, @@ -2186,12 +2287,15 @@ class Listofworksstudentone extends Component { levelscore: teacherdata.final_score, efficiencyscore: teacherdata.eff_score, view_answer_count: teacherdata.view_answer_count, - finalscore: teacherdata.work_score, + final_score: teacherdata.final_score===null||teacherdata.final_score===undefined||teacherdata.final_score===""?"--":teacherdata.final_score, + work_score:teacherdata.work_score===null||teacherdata.work_score===undefined||teacherdata.work_score===""?"--":teacherdata.work_score, operating: "查看", late_penalty: teacherdata.late_penalty === null ? "0" : teacherdata.late_penalty === undefined ? "0" : teacherdata.late_penalty, ultimate_score: teacherdata.ultimate_score, user_name: teacherdata.user_name, user_login: teacherdata.user_login, + Curcomlevel: teacherdata.current_complete_count===null|| teacherdata.current_complete_count===undefined|| teacherdata.current_complete_count===""?0: teacherdata.current_complete_count, + }) // } @@ -2320,7 +2424,7 @@ class Listofworksstudentone extends Component { } Startsorting = (ordervlue, checkedValuesine, checkedValuesineinfo, searchtext, page, limit) => { - // console.log("获取作品列表Listofworksstudenttwo"); + // console.log("获取作品列表Listofworksstudenttwo");33333333 var homeworkid = this.props.match.params.homeworkid; let urll = `/homework_commons/${homeworkid}/works_list.json`; // console.log(homeworkid); @@ -2328,7 +2432,7 @@ class Listofworksstudentone extends Component { // search:搜索关键字 // order:排序方式:更新时间:update_time,当前成绩:work_score,学号:student_id // b_order:desc:倒序 , asc:顺序 - // work_status:0:未提交, 1:按时完成, 2:延时完成,“”不限 + // work_status:0:未开启, 1:按时完成, 2:延时完成,“”不限 // course_group:分班情况 [232, 231], []:不限(空数组) var order = "asc"; if (ordervlue === "updated_at") { @@ -2441,7 +2545,7 @@ class Listofworksstudentone extends Component { //searchtext 输入的姓名和学号 //order 排序时间 //debug=t 是老师的意思 - // console.log(ordervlue)7009 + // console.log(ordervlue)70094444444 var homeworkid = this.props.match.params.homeworkid; let urll = `/homework_commons/${homeworkid}/works_list.json?`; @@ -2537,20 +2641,22 @@ class Listofworksstudentone extends Component { stduynumber: student_works[i].student_id, classroom: student_works[i].group_name, cost_time: student_works[i].cost_time, - submitstate: student_works[i].work_status === 0 ? "未提交" : student_works[i].work_status === 1 ? "按时完成" : student_works[i].work_status === 2 ? "延时完成" : "未提交", + submitstate: student_works[i].work_status === 0 ? "未开启" : student_works[i].work_status === 1 ? "未通关" : student_works[i].work_status === 2 ? "按时通关" : "迟交通关", // updatetime:this.state.teacherdata.student_works[i].update_time, // updatetime:"", updatetime: timedata === "Invalid date" ? "--" : timedata, completion: student_works[i].complete_count === null ? "0" : student_works[i].complete_count === undefined ? "0" : student_works[i].complete_count, levelscore: student_works[i].final_score, efficiencyscore: student_works[i].eff_score === "0.0" ? "--" : student_works[i].eff_score === "0" ? "--" : student_works[i].eff_score, - finalscore: student_works[i].work_score, + final_score: student_works[i].final_score===null||student_works[i].final_score===undefined||student_works[i].final_score===""?"--":student_works[i].final_score, + work_score:student_works[i].work_score===null||student_works[i].work_score===undefined||student_works[i].work_score===""?"--":student_works[i].work_score, view_answer_count: student_works[i].view_answer_count, operating: "查看", late_penalty: student_works[i].late_penalty === null ? "0" : student_works[i].late_penalty === undefined ? "0" : student_works[i].late_penalty, ultimate_score: student_works[i].ultimate_score, user_name: student_works[i].user_name, user_login: student_works[i].user_login, + Curcomlevel: student_works[i].current_complete_count===undefined||student_works[i].current_complete_count===null||student_works[i].current_complete_count===""?0:student_works[i].current_complete_count, }) } @@ -3356,13 +3462,42 @@ class Listofworksstudentone extends Component { } render() { - let {columns, course_groupysls, datajs, isAdmin, homework_status, course_groupyslstwo, unlimited, unlimitedtwo, course_group_info, orders, task_status, checkedValuesine, searchtext, teacherlist, visible, visibles, game_list, columnsstu, columnsstu2, limit, experience, boolgalist, viewtrainingdata, teacherdata, page, data, jobsettingsdata, styletable, datas, order, loadingstate, computeTimetype} = this.state; + let {columns,columnss, course_groupysls, datajs, isAdmin, homework_status, course_groupyslstwo, unlimited, unlimitedtwo, course_group_info, orders, task_status, checkedValuesine, searchtext, teacherlist, visible, visibles, game_list, columnsstu, columnsstu2, limit, experience, boolgalist, viewtrainingdata, teacherdata, page, data, jobsettingsdata, styletable, datas, order, loadingstate, computeTimetype} = this.state; const antIcon = ; let course_is_end = this.props.current_user && this.props.current_user.course_is_end; // console.log("Listofworksstudentone.js"); // console.log(orders); - + let homewrok=false; + if(homework_status && homework_status.length > 0){ + for(var i=0;i tr > th, .ant-table-tbody > tr > td { - padding: 9px; + padding: 0px; } `}
    @@ -3846,10 +3981,10 @@ class Listofworksstudentone extends Component { {teacherdata === undefined ? "" : teacherdata.left_time === undefined ? "" : teacherdata.left_time === null ? "" : {teacherdata.left_time.status} } - {teacherdata === undefined ? "0" : teacherdata.left_time === undefined ? "0" : teacherdata.left_time === null ? "0" : - {teacherdata.left_time.time} - } + {teacherdata === undefined ? "0" : teacherdata.left_time === undefined ? "0" : teacherdata.left_time === null ? "0" : + {teacherdata.left_time.time} + } @@ -3901,7 +4036,7 @@ class Listofworksstudentone extends Component { height: 58px; } .ysltableow .ant-table-thead > tr > th, .ant-table-tbody > tr > td { - padding: 9px; + padding: 0px; } `}
    @@ -4005,7 +4140,7 @@ class Listofworksstudentone extends Component { height: 58px; } .ysltableows .ant-table-thead > tr > th, .ant-table-tbody > tr > td { - padding: 9px; + padding: 0px; } ` } @@ -4043,9 +4178,9 @@ class Listofworksstudentone extends Component { {teacherdata === undefined ? "" : teacherdata.left_time === undefined ? "" : teacherdata.left_time === null ? "" : {teacherdata.left_time.status}} - {teacherdata === undefined ? "0" : teacherdata.left_time === undefined ? "0" : teacherdata.left_time === null ? "0" : - {teacherdata.left_time.time}} + {teacherdata === undefined ? "0" : teacherdata.left_time === undefined ? "0" : teacherdata.left_time === null ? "0" : + {teacherdata.left_time.time}}
    diff --git a/public/react/src/modules/courses/shixunHomework/ShixunHomeworkPage.js b/public/react/src/modules/courses/shixunHomework/ShixunHomeworkPage.js index 5073a6e9c..f98ecf762 100644 --- a/public/react/src/modules/courses/shixunHomework/ShixunHomeworkPage.js +++ b/public/react/src/modules/courses/shixunHomework/ShixunHomeworkPage.js @@ -232,9 +232,9 @@ class ShixunHomeworkPage extends Component { typelist={teacherdatapage === undefined ? [""] : teacherdatapage.homework_status} /> this.gotohome()}>返回 - 实训详情 + {teacherdatapage&&teacherdatapage.shixun_status>1?实训详情:""}
    @@ -336,10 +336,10 @@ class ShixunHomeworkPage extends Component { {teacherdatapage.commit_des} } - {teacherdatapage === undefined ? "" : 1&&teacherdatapage&&teacherdatapage.time_status<5?} + />:""} {this.props.isStudent() ? ( teacherdatapage&&teacherdatapage.redo_work===true? diff --git a/public/react/src/modules/courses/shixunHomework/ShixunWorkReport.js b/public/react/src/modules/courses/shixunHomework/ShixunWorkReport.js index 23f590dcd..10b12eb0a 100644 --- a/public/react/src/modules/courses/shixunHomework/ShixunWorkReport.js +++ b/public/react/src/modules/courses/shixunHomework/ShixunWorkReport.js @@ -315,6 +315,7 @@ class ShixunWorkReport extends Component { // let showAppraiseModals=this.props&&this.props.isAdminOrTeacher()===true?work_comment===null||work_comment===undefined?false:true:work_comment===null||work_comment===undefined?false:true; let showAppraiseModals=work_comment===null||work_comment===undefined?false:true; document.title=data&&data.course_name; + return ( data===undefined?"": @@ -366,10 +367,15 @@ class ShixunWorkReport extends Component {

    {data&&data.shixun_name}

    {/*{this.props.isAdmin()?导出实训报告数据:""}*/} 返回 - {this.props.isAdmin() ? this.confirmysl(`/student_works/${homeworkid}/export_shixun_work_report.pdf`)} - > 导出实训报告数据 : ""} + > 导出实训报告数据: + parseInt(this.props&&this.props.user.user_id)===parseInt(data&&data.user_id)?this.confirmysl(`/student_works/${homeworkid}/export_shixun_work_report.pdf`)} + > 导出实训报告数据:"" + } {/*{this.props.isAdmin() ?work_comment_hidden===true? "":this.showAppraiseModal(1)}*/} diff --git a/public/react/src/modules/courses/shixunHomework/ShixunhomeWorkItem.js b/public/react/src/modules/courses/shixunHomework/ShixunhomeWorkItem.js index dc59d93ed..a9bea3118 100644 --- a/public/react/src/modules/courses/shixunHomework/ShixunhomeWorkItem.js +++ b/public/react/src/modules/courses/shixunHomework/ShixunhomeWorkItem.js @@ -355,18 +355,23 @@ class ShixunhomeWorkItem extends Component{ .homepagePostSettingbox{ width:139px !important; } + .colorfff{ + color:#fff !important; + } ` } {this.props.isAdmin?this.stopPro(event)} className={this.props.isAdminOrCreator()?"homepagePostSetting homepagePostSettingname":"homepagePostSetting homepagePostSettingbox"} style={{"right":"-2px","top":"6px","display":"block"}}> - 实训详情 + {discussMessage&&discussMessage.shixun_status>1?实训详情: + 实训详情} {this.props.isAdminOrCreator()?this.editname(discussMessage.name,discussMessage.homework_id,event)} className={"btn colorblue ml20 font-16 fontweight400"}>重命名:""} {/* 设置*/} 设置 :""} - {this.props.isStudent===true?this.props.course_identity===5? + {this.props.isStudent===true?this.props.course_identity===5?discussMessage&&discussMessage.shixun_status>1? + discussMessage.time_status<5? {startbtn===false? (discussMessage.task_operation[0] == '继续挑战' || discussMessage.task_operation[0] == '查看实战' ? @@ -377,7 +382,7 @@ class ShixunhomeWorkItem extends Component{ this.taskoperationId(discussMessage.task_operation[1])}> {discussMessage.task_operation[0]} ):开启中} - :"":"" + :"":"":"":"" } @@ -393,21 +398,13 @@ class ShixunhomeWorkItem extends Component{ { discussMessage.time_status===1? - {discussMessage.status_time} - :discussMessage.time_status===2? - {discussMessage.status_time} - :discussMessage.time_status===3? - {discussMessage.status_time} - :discussMessage.time_status===4? - {discussMessage.status_time} - : {discussMessage.status_time} } diff --git a/public/react/src/modules/courses/shixunHomework/Trainingjobsetting.js b/public/react/src/modules/courses/shixunHomework/Trainingjobsetting.js index 1b20f56bc..03ae5ec33 100644 --- a/public/react/src/modules/courses/shixunHomework/Trainingjobsetting.js +++ b/public/react/src/modules/courses/shixunHomework/Trainingjobsetting.js @@ -132,7 +132,9 @@ class Trainingjobsetting extends Component { CalculateMax: 100,//总分值 borredszf:"ml10 color-grey-9", borredszfl:true, - borredszfls:'' + borredszfls:'', + total_scoretwo:0, + total_score:0, } // console.log("获取到的值") // console.log("Trainingjobsetting") @@ -302,7 +304,10 @@ class Trainingjobsetting extends Component { proportion: result.data.shixun_evaluation === 0 ? "均分比例" : result.data.shixun_evaluation === 1 ? "经验值比例" : result.data.shixun_evaluation === 2 ? "自定义分值" : "", publicwork: result.data.work_public, challenge_settings: result.data.challenge_settings, - code_review: result.data.code_review + code_review: result.data.code_review, + total_scoretwo:result.data.total_score, + total_score:result.data.total_score, + }) this.props.Getdataback(result, result.data); @@ -646,6 +651,7 @@ class Trainingjobsetting extends Component { if (this.state.completionefficiencyscore === true) { latedeductiontwos = this.state.latedeductiontwo; } + //从这里开始记得等于0的时候还要做判断 if (challenge_scoredata.length > 0) { var len = 0; for (var k = 0; k < challenge_scoredata.length; k++) { @@ -680,6 +686,16 @@ class Trainingjobsetting extends Component { } + }else{ + try { + if(parseFloat(this.state.CalculateMax)!==parseFloat(0)){ + this.props.showNotification(`分值之和必须等于总分值:${this.state.CalculateMax}分`); + this.scrollToAnchor("zongfentimeid"); + return; + } + }catch (e) { + + } } var url = `/homework_commons/${homeworkid}/update_settings.json`; @@ -707,7 +723,9 @@ class Trainingjobsetting extends Component { shixun_evaluation: this.state.proportion === "均分比例" ? 0 : this.state.proportion === "经验值比例" ? 1 : this.state.proportion === "自定义分值" ? 2 : 0, challenge_settings: array, score_open: this.state.publicwork, - total_score:this.state.CalculateMax + total_score:this.state.CalculateMax, + total_scoretwo:this.state.CalculateMax,//记录总分值点击取消的时候还原总分值 + } } else { // //非统一配置 @@ -738,7 +756,8 @@ class Trainingjobsetting extends Component { shixun_evaluation: this.state.proportion === "均分比例" ? 0 : this.state.proportion === "经验值比例" ? 1 : this.state.proportion === "自定义分值" ? 2 : 0, challenge_settings: array, score_open: this.state.publicwork, - total_score:this.state.CalculateMax + total_score:this.state.CalculateMax, + total_scoretwo:this.state.CalculateMax } } @@ -1193,8 +1212,7 @@ class Trainingjobsetting extends Component { } //总比分 Totalscorecalculation = (value) => { - - + debugger this.setState({ CalculateMax: value, }) @@ -2150,6 +2168,9 @@ class Trainingjobsetting extends Component { }) this.refs.targetElementTrainingjobsetting.scrollIntoView(); this.getTrainingjobsetting(false); + this.setState({ + CalculateMax:this.state.total_scoretwo, + }) } rulesCheckInfo = (rules) => { diff --git a/public/react/src/modules/courses/shixunHomework/shixunHomework.js b/public/react/src/modules/courses/shixunHomework/shixunHomework.js index d16562892..4705e7a8f 100644 --- a/public/react/src/modules/courses/shixunHomework/shixunHomework.js +++ b/public/react/src/modules/courses/shixunHomework/shixunHomework.js @@ -1208,7 +1208,7 @@ class ShixunHomework extends Component{ {this.props.isAdmin()?未发布:""} 提交中 补交中 - 评阅中 + 已截止
    diff --git a/public/react/src/modules/courses/shixunHomework/style.css b/public/react/src/modules/courses/shixunHomework/style.css index 657ebded0..3129b640e 100644 --- a/public/react/src/modules/courses/shixunHomework/style.css +++ b/public/react/src/modules/courses/shixunHomework/style.css @@ -73,6 +73,14 @@ white-space:nowrap; cursor: default; } + +.maxnamewidth120{ + max-width: 120px; + overflow:hidden; + text-overflow:ellipsis; + white-space:nowrap; + cursor: default; +} .maxnamewidth145{ max-width: 145px; overflow:hidden; diff --git a/public/react/src/modules/courses/statistics/Statistics.js b/public/react/src/modules/courses/statistics/Statistics.js index 00a47a81d..281052426 100644 --- a/public/react/src/modules/courses/statistics/Statistics.js +++ b/public/react/src/modules/courses/statistics/Statistics.js @@ -1,5 +1,5 @@ import React,{ Component } from "react"; -import {Table, Pagination,Tooltip,Spin, Row, Col ,Checkbox,Tabs,Menu, Dropdown, Icon,Input} from "antd"; +import {Table, Pagination,Popover,Spin, Row, Col ,Tabs, Icon} from "antd"; import { WordsBtn,on, off, trigger ,getImageUrl,sortDirections} from 'educoder'; import axios from'axios'; import Dropdownbox from './Dropdownbox'; @@ -99,7 +99,6 @@ class Statistics extends Component{ } getwork_scoredata=(page,group_ids,sort,key)=>{ - this.setState({ page:page, sort:sort, @@ -189,8 +188,45 @@ class Statistics extends Component{ } + //计算成绩 + setComputeTimet = (homeworkid) => { + let url = `/courses/${homeworkid}/calculate_all_shixun_scores.json`; + try { + this.props.yslslowCheckresults(); + } catch (e) { + + } + axios.get(url).then((response) => { + if (response) { + if (response.data.status === 0) { + let{page,group_ids,sort}=this.state; + setTimeout(() => { + try { + this.props.showNotification(`${response.data.message}`); + } catch (e) { + + } + try { + this.props.yslslowCheckresultsNo(); + } catch (e) { + + } + this.getwork_scoredata(page,group_ids,sort); + }, 2500); + } + } + }).catch((error) => { + try { + this.props.yslslowCheckresultsNo(); + } catch (e) { + + } + console.log(error) + }); + }; + render(){ - let {nd1,nd2,nd3,data,bomdata,course_members}=this.state; + let {nd1,nd2,nd3,data,bomdata,course_members,activeKey}=this.state; const columns = [ { @@ -356,26 +392,69 @@ class Statistics extends Component{ ) } - // console.log(this.props.isAdmin) - - - const operations = - {course_grouptype===false||this.state.course_groups.length===0?"":this.getwork_scoredata(1,group_idss,'desc')} - />} - this.derivefun(this.state.activeKey==="1"?`/courses/${this.props.match.params.coursesId}/export_member_scores_excel.xlsx`:`/courses/${this.props.match.params.coursesId}/export_member_act_score.xlsx`)}>导出 + // console.log("Statistics"); + // console.log(this.props.user.course_is_end) + + const course_is_endismy=this.props&&this.props.user&&this.props.user.course_is_end; + const operations = + {course_grouptype===false||this.state.course_groups.length===0?"": + this.props.isAdmin()===true? + this.getwork_scoredata(1,group_idss,'desc') + } + /> + :"" + } + { + this.state.activeKey==="1"? + ( + course_is_endismy===false? + this.setComputeTimet(this.props.match.params.coursesId)}>获取最新成绩 + : + "" + ) + :"" + } + { + this.props.isAdmin()===true? + this.derivefun(this.state.activeKey==="1"?`/courses/${this.props.match.params.coursesId}/export_member_scores_excel.xlsx`:`/courses/${this.props.match.params.coursesId}/export_member_act_score.xlsx`)}>导出 + :"" + } ; + + const content = ( +
    +

    + 课堂总成绩 * 70 % +

    +

    + 课堂活跃度 * 10% +

    +

    + 课外学习成绩 * 20% +

    +

    + 其中,课外学习成绩= 当前学生经验值 / 课堂学生经验值 最大值*100 +

    +
    + ); return(

    - -

    + + 明星学员 + + 计算规则 + + + + @@ -542,7 +621,7 @@ class Statistics extends Component{ } ` } - + {bomdata===undefined||bomdata===null?"":bomdata.length===0?:
    ( - - - + + ), } @@ -179,7 +203,6 @@ class DeveloperHome extends React.PureComponent { componentDidMount() { // 是否是我的,如果是我的 显示编辑按钮 const { isMySource } = this.props; - if (isMySource) { this.handleFilterSearch({come_from: 'mine'}); let _columns = this.columns.concat([this.options]); @@ -222,6 +245,31 @@ class DeveloperHome extends React.PureComponent { this.props.history.push(`/problems/${identifier}/edit`) } } + + // 删除 + handleClickDelete = (record) => { + const { deleteItem } = this.props; + this.props.confirm({ + title: '提示', + content: `确定要删除${record.name}吗?`, + onOk () { + // 调用删除接口 + console.log(record.identifier); + deleteItem(record.identifier); + } + }); + // Modal.confirm({ + // title: '删除', + // content: `确定要删除${record.name}吗?`, + // okText: '确定', + // cancelText: '取消', + // onOk () { + // // 调用删除接口 + // console.log(record.identifier); + // deleteItem(record.identifier); + // } + // }); + } // table条件变化时 handleTableChange = (pagination, filters, sorter) => { const {field, order} = sorter; @@ -312,6 +360,14 @@ class DeveloperHome extends React.PureComponent { }); this.handleFilterSearch({category: +item.key === 0 ? '' : +item.key}); } + // 下拉语言 + handleLanguageMenuClick = (item) => { + this.addShowFilterCtx({ + type: 'language', + key: item.key + }); + this.handleFilterSearch({language: item.key}) + } // 难度下拉 handleHardMenuClick = (item) => { this.addShowFilterCtx({ @@ -374,7 +430,7 @@ class DeveloperHome extends React.PureComponent { // console.log('name has click', record); // 先调用start接口获取返回的 identifier, 再跳转到开启编辑 if (this.isLogin()) { - // console.log(record); + toStore('hack_identifier', record.identifier); // 保存当前编辑的id号 this.props.startProgramQuestion(record.identifier, this.props); } } @@ -387,6 +443,7 @@ class DeveloperHome extends React.PureComponent { // const { testReducer, handleClick } = this.props; const { ojListReducer: {hacks_list, top_data, hacks_count}, + user, pagination } = this.props; const {passed_count = 0, simple_count = 0, medium_count = 0, diff_count = 0} = top_data; @@ -409,7 +466,11 @@ class DeveloperHome extends React.PureComponent { >{ctx} )}); }; - + // console.log('=====>>>>>>>>>.', this.props); + + const newBtnStyle = user && (user.admin || (user.is_teacher && user.professional_certification) || user.business) + ? { display: 'block'} + : { display: 'none'}; return (
    @@ -423,7 +484,8 @@ class DeveloperHome extends React.PureComponent {
    -
    @@ -434,6 +496,9 @@ class DeveloperHome extends React.PureComponent { 分类 + + 语言 + 难度 @@ -499,11 +564,12 @@ const mapDispatchToProps = (dispatch) => ({ handleClick: () => dispatch(actions.toggleTodo()), fetchOJList: (params) => dispatch(actions.getOJList(params)), changePaginationInfo: (obj) => dispatch(actions.changePaginationInfo(obj)), - startProgramQuestion: (id, props) => dispatch(actions.startProgramQuestion(id, props)) + startProgramQuestion: (id, props) => dispatch(actions.startProgramQuestion(id, props)), + deleteItem: (identifier) => dispatch(actions.deleteItem(identifier)) }); export default withRouter(connect( mapStateToProps, mapDispatchToProps -)(DeveloperHome)); +)(CNotificationHOC() (DeveloperHome))); // export default DeveloperHome; diff --git a/public/react/src/modules/developer/components/controlSetting/index.js b/public/react/src/modules/developer/components/controlSetting/index.js index b6ab66c10..1d4d7834c 100644 --- a/public/react/src/modules/developer/components/controlSetting/index.js +++ b/public/react/src/modules/developer/components/controlSetting/index.js @@ -4,10 +4,10 @@ * @Github: * @Date: 2019-11-27 16:02:36 * @LastEditors: tangjiang - * @LastEditTime: 2019-12-03 09:19:54 + * @LastEditTime: 2019-12-19 19:47:32 */ import './index.scss'; -import React, { useState, useRef } from 'react'; +import React, { useState, useRef, useEffect } from 'react'; import { Tabs, Button, Icon } from 'antd'; import { connect } from 'react-redux'; import InitTabCtx from '../initTabCtx'; @@ -23,14 +23,15 @@ const ControlSetting = (props) => { submitLoading, identifier, excuteState, + showOrHideControl, commitRecordDetail, changeLoadingState, changeSubmitLoadingStatus, - showOrHideControl, + changeShowOrHideControl, // debuggerCode, // startDebuggerCode, // 外部存入 onDebuggerCode, - updateCode, + // updateCode, onSubmitForm } = props; const [defaultActiveKey, setDefaultActiveKey] = useState('1'); // 当前选中的tab @@ -44,10 +45,14 @@ const ControlSetting = (props) => { setDefaultActiveKey(key); } + useEffect(() => { + setShowTextResult(props.showOrHideControl); + }, [props]); + // 显示/隐藏tab const handleShowControl = () => { setShowTextResult(!showTextResult); - showOrHideControl(!showTextResult); + changeShowOrHideControl(!showTextResult); } // 调试代码 @@ -55,7 +60,7 @@ const ControlSetting = (props) => { // console.log(formRef.current.handleTestCodeFormSubmit); // 调出控制台界面 setShowTextResult(true); - showOrHideControl(true); + changeShowOrHideControl(true); formRef.current.handleTestCodeFormSubmit(() => { setDefaultActiveKey('2'); }); @@ -84,7 +89,7 @@ const ControlSetting = (props) => { @@ -131,19 +136,20 @@ const ControlSetting = (props) => { const mapStateToProps = (state) => { const {commonReducer, ojForUserReducer} = state; - const {loading, excuteState, submitLoading } = commonReducer; + const {loading, excuteState, submitLoading, showOrHideControl } = commonReducer; const { commitRecordDetail } = ojForUserReducer; return { loading, submitLoading, excuteState, + showOrHideControl, // identifier: user_program_identifier, commitRecordDetail // 提交详情 }; }; // changeSubmitLoadingStatus const mapDispatchToProps = (dispatch) => ({ - showOrHideControl: (flag) => dispatch(actions.showOrHideControl(flag)), + changeShowOrHideControl: (flag) => dispatch(actions.changeShowOrHideControl(flag)), changeLoadingState: (flag) => dispatch(actions.changeLoadingState(flag)), changeSubmitLoadingStatus: (flag) => dispatch(actions.changeSubmitLoadingStatus(flag)), debuggerCode: (identifier, values) => dispatch(actions.debuggerCode(identifier, values)), diff --git a/public/react/src/modules/developer/components/controlSetting/index.scss b/public/react/src/modules/developer/components/controlSetting/index.scss index 0c7f726f5..31beda8a5 100644 --- a/public/react/src/modules/developer/components/controlSetting/index.scss +++ b/public/react/src/modules/developer/components/controlSetting/index.scss @@ -2,6 +2,8 @@ position: absolute; bottom: 0; width: 100%; + // background: red; + // background:rgba(30,30,30,1); // height: 56px; .control_tab{ position: absolute; @@ -51,7 +53,8 @@ height: 56px; padding-right: 30px; padding-left: 10px; - background: #000; + background: rgba(18,28,36,1); + // background:rgba(48,48,48,1); } .setting_drawer{ @@ -67,6 +70,14 @@ font-size: 12px; } } + + .ant-drawer-body{ + height: calc(100vh - 120px); + overflow-y: auto; + } + .ant-drawer-content{ + top: 120px; + } } diff --git a/public/react/src/modules/developer/components/execResult/index.js b/public/react/src/modules/developer/components/execResult/index.js index 5f8526ca4..6f9341b9a 100644 --- a/public/react/src/modules/developer/components/execResult/index.js +++ b/public/react/src/modules/developer/components/execResult/index.js @@ -4,7 +4,7 @@ * @Github: * @Date: 2019-11-28 08:44:54 * @LastEditors: tangjiang - * @LastEditTime: 2019-12-03 12:38:27 + * @LastEditTime: 2019-12-19 10:44:16 */ import './index.scss'; import React, { useState, useEffect } from 'react'; @@ -19,7 +19,7 @@ function ExecResult (props) { // 指定渲染初始, 加载中, 加载完成页面内容 const renderInit = () => (
    - 请先点击“调试代码”运行您的代码 + 请填写测试用例的输入值,点击“调试代码”
    ); const renderLoading = () => ( @@ -38,6 +38,15 @@ function ExecResult (props) { ); + + const renderError = () => ( +
    + + 未知异常 + +
    + ) + const renderFinish = () => { const { error_line, @@ -60,6 +69,7 @@ function ExecResult (props) { ) } + // console.log('执行结果====》》》》', status); const excuteCtx = (state) => { if (state === 0) { return ( @@ -118,6 +128,8 @@ function ExecResult (props) { setRenderCtx(() => (readerLoaded)); } else if ('finish' === excuteState) { setRenderCtx(() => (renderFinish)); + } else if ('error' === excuteState) { + setRenderCtx(() => (renderError)) } }, [excuteState]); diff --git a/public/react/src/modules/developer/components/initTabCtx/index.js b/public/react/src/modules/developer/components/initTabCtx/index.js index 221558cdc..3e707daa1 100644 --- a/public/react/src/modules/developer/components/initTabCtx/index.js +++ b/public/react/src/modules/developer/components/initTabCtx/index.js @@ -4,7 +4,7 @@ * @Github: * @Date: 2019-11-27 19:46:14 * @LastEditors: tangjiang - * @LastEditTime: 2019-12-03 09:14:59 + * @LastEditTime: 2019-12-19 10:47:05 */ import './index.scss'; import React, { useState, useEffect, useRef, useImperativeHandle, forwardRef } from 'react'; @@ -26,6 +26,7 @@ function InitTabCtx (props, ref) { const { inputValue, onDebuggerCode } = props; + console.log('default value', inputValue); useImperativeHandle(ref, () => ({ handleTestCodeFormSubmit: (cb) => { // console.log('父组件调用我啦~~~~~~~~~'); @@ -33,6 +34,10 @@ function InitTabCtx (props, ref) { } })); + useEffect(() => { + console.log('初始值: ========', props); + }, [props]); + // 渲染文本提示信息 const renderText = () => (请在这里添加测试用例,点击“调试代码”时将从这里读取输入来测试你的代码...); // 渲染表单信息 @@ -42,7 +47,6 @@ function InitTabCtx (props, ref) {
    { getFieldDecorator('input', { @@ -50,7 +54,11 @@ function InitTabCtx (props, ref) { { required: true, message: '输入值不能为空'} ], initialValue: inputValue - })( +

    执行命令不能为空

    + + +
  • + + +
  • + + + + +
    +
  • + + +
  • +
    {this.state.languagewritetype === true ? "请填写该镜像语言" : ""}
    +
  • + + +
  • +
    {this.state.systemenvironmenttype === true ? "请填写该镜像语言系统环境" : ""}
    +
  • + + + +
  • +
    {this.state.testcoderunmodetype === true ? "请填写该镜像测试代码运行方式" : ""}
    +
  • + +
    + + + 上传附件 + (单个文件50M以内) + + +
    + +
  • +
    + {this.state.attachmentidstype === true ? "请上传附件" : ""} +
    +
  • + this.sendhideModaly()} + >取消 + +
  • +
    +
    +
    + + {this.props.identity < 5 ? + : ""} + + + ); + } +} + +const TopShixuninformation = Form.create({name: 'newshixun'})(Shixuninformation); + +export default TopShixuninformation; + + + diff --git a/public/react/src/modules/tpm/TPMsettings/TPMsettings.js b/public/react/src/modules/tpm/TPMsettings/TPMsettings.js index 7acaf98d6..860798722 100644 --- a/public/react/src/modules/tpm/TPMsettings/TPMsettings.js +++ b/public/react/src/modules/tpm/TPMsettings/TPMsettings.js @@ -1,2435 +1,231 @@ -import React, { Component } from 'react'; +import React, {Component} from 'react'; -import MonacoEditor from 'react-monaco-editor'; - -//MonacoDiffEditor 对比模式 -import {Input, Select, Radio, Checkbox, Popconfirm, message, Modal,Icon,DatePicker,Breadcrumb,Upload,Button,notification, Tooltip} from 'antd'; - -// import "antd/dist/antd.css"; - -import locale from 'antd/lib/date-picker/locale/zh_CN'; - -import moment from 'moment'; - -import axios from 'axios'; +import { + Button, + Tabs, + Modal +} from 'antd'; import './css/TPMsettings.css'; -import { getImageUrl, toPath, getUrl ,appendFileSizeToUploadFileAll, getUploadActionUrl} from 'educoder'; - -let origin = getUrl(); +import TopShixuninformation from './Shixuninformation'; -let path = getUrl("/editormd/lib/") +import Configuration from './Configuration'; -const $ = window.$; +import LearningSettings from './LearningSettings'; -let timeout; - -let currentValue; +import axios from 'axios'; -const Option = Select.Option; +const {TabPane} = Tabs; -const RadioGroup = Radio.Group; -const confirm = Modal.confirm; // 处理整点 半点 -// 取传入时间往后的第一个半点 -export function handleDateStrings(dateString) { - if (!dateString) return dateString; - const ar = dateString.split(':') - if (ar[1] == '00' || ar[1] == '30') { - return dateString - } - const miniute = parseInt(ar[1]); - if (miniute < 30 || miniute == 60) { - return [ar[0], '30'].join(':') - } - if (miniute < 60) { - // 加一个小时 - const tempStr = [ar[0], '00'].join(':'); - const format = "YYYY-MM-DD HH:mm"; - const _moment = moment(tempStr, format) - _moment.add(1, 'hours') - return _moment.format(format) - } - - return dateString -} - -// 恢复数据 -function md_rec_data(k,mdu,id, editor){ - if(window.sessionStorage.getItem(k+mdu) !== null){ - editor.setValue(window.sessionStorage.getItem(k+mdu)); - md_clear_data(k,mdu,id); - } -} - -// 保存数据 -function md_add_data(k,mdu,d){ - window.sessionStorage.setItem(k+mdu,d); -} - -// 清空保存的数据 -function md_clear_data(k,mdu,id){ - window.sessionStorage.removeItem(k+mdu); - var id1 = "#e_tip_"+id; - var id2 = "#e_tips_"+id; - if(k == 'content'){ - $(id2).html(""); - }else{ - $(id1).html(""); - } -} - -function md_elocalStorage(editor,mdu,id){ - if (window.sessionStorage){ - var oc = window.sessionStorage.getItem('content'+mdu); - if(oc !== null ){ - $("#e_tips_"+id).data('editor', editor); - var h = '您上次有已保存的数据,是否恢复 ? / 不恢复'; - $("#e_tips_"+id).html(h); - } - setInterval(function() { - var d = new Date(); - var h = d.getHours(); - var m = d.getMinutes(); - var s = d.getSeconds(); - h = h < 10 ? '0' + h : h; - m = m < 10 ? '0' + m : m; - s = s < 10 ? '0' + s : s; - if(editor.getValue().trim() != ""){ - md_add_data("content",mdu,editor.getValue()); - var id1 = "#e_tip_"+id; - var id2 = "#e_tips_"+id; - - $(id1).html(" 数据已于 " + h + ':' + m + ':' + s +" 保存 "); - $(id2).html(""); - } - },10000); - - }else{ - $("#e_tip_"+id).after('您的浏览器不支持localStorage.无法开启自动保存草稿服务,请升级浏览器!'); - } -} - -function create_editorMD(id, width, high, placeholder, imageUrl,initValue, callback) { - var editorName = window.editormd(id, { - width: width, - height: high, - path: path, // "/editormd/lib/" - markdown : initValue, - syncScrolling: "single", - tex: true, - tocm: true, - emoji: true, - taskList: true, - codeFold: true, - searchReplace: true, - htmlDecode: "style,script,iframe", - sequenceDiagram: true, - autoFocus: false, - placeholder: placeholder, - toolbarIcons: function () { - // Or return editormd.toolbarModes[name]; // full, simple, mini - // Using "||" set icons align right. - return ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "|", "testIcon", "testIcon1", '|', "image", "table", '|', "watch", "clear"] - }, - toolbarCustomIcons: { - testIcon: "
    ", - testIcon1: "
    " - }, - //这个配置在simple.html中并没有,但是为了能够提交表单,使用这个配置可以让构造出来的HTML代码直接在第二个隐藏的textarea域中,方便post提交表单。 - saveHTMLToTextarea: true, - // 用于增加自定义工具栏的功能,可以直接插入HTML标签,不使用默认的元素创建图标 - dialogMaskOpacity: 0.6, - imageUpload: true, - imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp", "JPG", "JPEG", "GIF", "PNG", "BMP", "WEBP"], - imageUploadURL: imageUrl,//url - onload: function () { - // this.previewing(); - $("#" + id + " [type=\"latex\"]").bind("click", function () { - editorName.cm.replaceSelection("```latex"); - editorName.cm.replaceSelection("\n"); - editorName.cm.replaceSelection("\n"); - editorName.cm.replaceSelection("```"); - var __Cursor = editorName.cm.getDoc().getCursor(); - editorName.cm.setCursor(__Cursor.line - 1, 0); - }); - - $("#" + id + " [type=\"inline\"]").bind("click", function () { - editorName.cm.replaceSelection("`$$$$`"); - var __Cursor = editorName.cm.getDoc().getCursor(); - editorName.cm.setCursor(__Cursor.line, __Cursor.ch - 3); - editorName.cm.focus(); - }); - $("[type=\"inline\"]").attr("title", "行内公式"); - $("[type=\"latex\"]").attr("title", "多行公式"); - callback && callback() - } - }); - return editorName; -} - - -function updatamakedown(id){ - setTimeout(()=>{ - var shixunDescr = window.editormd.markdownToHTML(id, { - htmlDecode: "style,script,iframe", - taskList: true, - tex: true, - flowChart: true, - sequenceDiagram: true - }); - $("#"+id+" p:first").addClass("ReactMarkdown"); - $('#collaborators_list_info').show() - }, 200) -} - -function range(start, end) { - const result = []; - for (let i = start; i < end; i++) { - result.push(i); - } - return result; -} -function disabledDateTime() { - return { - // disabledHours: () => range(0, 24).splice(4, 20), - disabledMinutes: () => range(1, 30).concat(range(31, 60)), - // disabledSeconds: () => [0, 60], - }; -} -function disabledDate(current) { - return current && current < moment().endOf('day').subtract(1, 'days'); -} export default class TPMsettings extends Component { constructor(props) { super(props) this.state = { - fileList: [], - commandLine: 0, - Openpublic: 0, - settingsData: undefined, - webssh: 0, - use_scope: 0, - shixunsstatus: 0, - shixunsID: undefined, - exec_time: undefined, - trainee: undefined, - can_copy: undefined, - task_pass: undefined, - test_set_permission: undefined, - code_edit_permission: undefined, - hide_code: undefined, - code_hidden: undefined, - forbid_copy: undefined, - vnc: undefined, - name: undefined, - scope_partment: undefined, - scopetype: false, - departmentslist: undefined, - description: '', - evaluate_script:undefined, - standard_scripts: undefined, - choice_main_type: "", - choice_small_type: [], - choice_standard_scripts:undefined, - editordescriptios: undefined, - editorevaluate_scripts: undefined, - choice_standard_scriptssum: undefined, - visibleTemplate: false, - Executiveordervalue: "", - Compilecommandvalue: "", - Executivetyoe: false, - postapplyvisible: false, - sendsure_applyvalue: undefined, - postapplytitle: false, - shixunnametype: false, - shixunmaintype: false, - evaluate_scripttype: false, - exec_timetype: false, - traineetype: false, - standard_scriptsModal:false, - standard_scriptsModals:false, - SelectTheCommandtype:false, - multi_webssh:false, - status:0, - opers:false, - operss:false, - testscripttiptype:false, - opersss:false, - operateshixunstype:false, - opening_time:"", - opensmail:false, - scope_partmenttype:false, - newuse_scope:undefined, - scope_partments:0, - shixun_service_configs:undefined, - shixun_service_configlist:undefined, - pod_exist_time: undefined, - pod_exist_timetype: false, - shixunmemoMDvalue:"", - language:"", - deleteisnot:true + activeKeys:"1" } } - descriptionMD=(initValue, id)=> { - - this.contentChanged = false; - const placeholder = ""; -// amp; -// 编辑时要传memoId - const imageUrl = `/api/attachments.json`; -// 创建editorMd - - const description_editormd =create_editorMD(id, '100%', 400, placeholder, imageUrl, initValue,()=> { - setTimeout(() => { - description_editormd.resize() - description_editormd.cm && description_editormd.cm.refresh() - }, 500) - - if (initValue != undefined) { - description_editormd.setValue(initValue) - } - description_editormd.cm.on("change", (_cm, changeObj) => { - console.log('....contentChanged') - this.contentChanged = true; - }) - }); - md_elocalStorage(description_editormd, `MemoQuestion_${id}`, `${id}Question`); - this.description_editormd = description_editormd; - window.description_editormd = description_editormd; - } - - evaluate_scriptMD=(initValue, id)=> { - this.contentChanged = false; - const placeholder = ""; -// amp; -// 编辑时要传memoId - const imageUrl = `/api/attachments.json`; -// 创建editorMd - - const evaluate_script_editormd =create_editorMD(id, '100%', 400, placeholder, imageUrl, initValue,()=> { - setTimeout(() => { - evaluate_script_editormd.resize() - evaluate_script_editormd.cm && evaluate_script_editormd.cm.refresh() - }, 500) - - if (initValue != undefined) { - evaluate_script_editormd.setValue(initValue) - } - evaluate_script_editormd.cm.on("change", (_cm, changeObj) => { - console.log('....contentChanged') - this.contentChanged = true; - }) - }); - md_elocalStorage(evaluate_script_editormd, `MemoQuestion_${id}`, `${id}Question`); - this.evaluate_script_editormd = evaluate_script_editormd; - window.evaluate_script_editormd = evaluate_script_editormd; - - } - componentDidMount() { + this.getdatas("1") + } - let id=this.props.match.params.shixunId; - - let Url=`/shixuns/`+id+`/settings.json`; + getdatas = (key) => { - axios.get(Url).then((response)=> { + let id = this.props.match.params.shixunId; + let Url = `/shixuns/` + id + `/settings.json`; + axios.get(Url).then((response) => { // alert(response.data.shixun.choice_standard_scripts) - if(response.status===200){ + if (response.status === 200) { + if(response.data){ + if (response.data.shixun&&response.data.shixun.scope_partment.length > 0) { + this.setState({ + scopetype: true + }) + } + } this.setState({ - shixunsID: id, - settingsData: response.data, - webssh: response.data.shixun.webssh, - use_scope: response.data.shixun.use_scope, - shixunsstatus: response.data.shixun.status, - exec_time: response.data.shixun.exec_time, - trainee: response.data.shixun.trainee, - can_copy: response.data.shixun.can_copy, - task_pass: response.data.shixun.task_pass, - test_set_permission: response.data.shixun.test_set_permission, - hide_code: response.data.shixun.hide_code, - code_edit_permission: response.data.shixun.code_edit_permission, - code_hidden: response.data.shixun.code_hidden, - is_secret_repository: response.data.shixun.is_secret_repository, - init_is_secret_repository: response.data.shixun.is_secret_repository, - forbid_copy: response.data.shixun.forbid_copy, - vnc: response.data.shixun.vnc, - vnc_evaluate: response.data.shixun.vnc_evaluate, - name: response.data.shixun.name, - scope_partment: response.data.shixun.scope_partment, - description: response.data.shixun.description, - evaluate_script: response.data.shixun.evaluate_script, - choice_main_type: response.data.shixun.choice_main_type, - choice_small_type: response.data.shixun.choice_small_type, - choice_standard_scripts: response.data.shixun.choice_standard_scripts, - standard_scripts:response.data.shixun.standard_scripts, - multi_webssh:response.data.shixun.multi_webssh, - status:response.data.shixun.status, - opening_time:response.data.shixun.opening_time, - newuse_scope:response.data.shixun.use_scope, - scope_partments: response.data.shixun.scope_partment.length, - shixunmemoMDvalue:response.data.shixun.evaluate_script, - shixun_service_configs:response.data.shixun.shixun_service_configs, - shixun_service_configlist:response.data.shixun.shixun_service_configs, + data: response.data }) - - // if(response.data.status===403){ - // message: "您没有权限进行该操作" - // this.setState({ - // :true - // message403:response.data.message - // }) - // } - - - if(response.data.shixun.multi_webssh===true){ - this.setState({ - SelectTheCommandtype:true - }) - }else{ - this.setState({ - SelectTheCommandtype:false - }) - } - if (response.data.shixun.scope_partment.length > 0) { - this.setState({ - scopetype: true - }) - } - // console.log(response.data.shixun.description) - // console.log(response.data.shixun.evaluate_script) - // console.log(response.data.shixun.description) - // this.props.identity<4&&this.props.status==0||this.props.identity===1&&this.props.status==2 - - - // this.evaluate_scriptMD(response.data.shixun.evaluate_script, "shixunmemoMD"); - - this.descriptionMD(response.data.shixun.description, "shixundescription"); - - // this.bigClass() - // if (response.data.shixun.status === 2) { - // - // } else if (response.data.shixun.status === 1) { - // this.props.showSnackbar("这个实训已发布不能修改!"); - // } else if (response.data.shixun.status === 3) { - // this.props.showSnackbar("这个实训已关闭不能修改!"); - // } } }); - - - let departmentsUrl = `/shixuns/departments.json`; - axios.get(departmentsUrl).then((response) => { - if (response.status === 200) { - if (response.data.message === undefined) { - this.setState({ - departmentslist: response.data.shools_name - }); - } - } - }).catch((error) => { - console.log(error) - }); - - - - } - - SelectshixunCommand=(e)=>{ - // console.log( e.target.value) - const webssh = e.target.value - if (webssh == 2) { - this.setState({ - webssh: webssh, - SelectTheCommandtype: true, - multi_webssh:false - }); - } else { - if (this.state.init_is_secret_repository && !this.state.vnc && this.state.is_secret_repository == true) { - this.confirmDeleteSecretRepo({ - onOk: () => { - this.setState({ - webssh: webssh, - SelectTheCommandtype: false, - multi_webssh:false - }); - } - }) - } else { - if (!this.state.vnc) { - this.setState({ - is_secret_repository: false, - }) - } - this.setState({ - webssh: webssh, - SelectTheCommandtype: false, - multi_webssh:false - }); - } - } - - // this.setState({ - // webssh: webssh, - // }); - // if(webssh===2){ - // this.setState({ - // SelectTheCommandtype: true, - // multi_webssh:false - // }); + // + // if(key==="3"&&this.props.shixunsDetails&&this.props.shixunsDetails.is_jupyter === true){ + // window.location.href =`/shixuns/${this.props.match.params.shixunId}/challenges`; // }else{ - // this.setState({ - // SelectTheCommandtype: false, - // multi_webssh:false - // }); + // if(key){ + // this.setState({ + // activeKeys:key + // }) + // }else{ + // window.location.href =`/shixuns/${this.props.match.params.shixunId}/challenges`; + // } + // // } - } - - SelectOpenpublic=(e)=>{ - this.setState({ - Openpublic: e.target.value - }); - } - - can_copy=(e)=>{ - let sum = "" - if (e.target.checked === false) { - sum = 0 - } else if (e.target.checked === true) { - sum = 1 - } - this.setState({ - can_copy: sum, - }); - - } - - task_pass=(e)=>{ - - let sum = "" - if (e.target.checked === false) { - sum = 0 - } else if (e.target.checked === true) { - sum = 1 - } - this.setState({ - task_pass: sum, - }); - } - - test_set_permission=(e)=>{ - let sum = "" - if (e.target.checked === false) { - sum = 0 - } else if (e.target.checked === true) { - sum = 1 - } - this.setState({ - test_set_permission: sum, - }); } - hide_code=(e)=>{ - let sum = "" - if (e.target.checked === false) { - sum = 0 - } else if (e.target.checked === true) { - sum = 1 - } - this.setState({ - hide_code: sum, - }); - - } - code_edit_permission = (e) => { - this.setState({ - code_edit_permission: e.target.checked - }) - } - code_hidden=(e)=>{ - let sum = "" - if (e.target.checked === false) { - sum = 0 - } else if (e.target.checked === true) { - sum = 1 - } + operateshixuns = (value) => { this.setState({ - code_hidden: sum, - }); - - } - confirmDeleteSecretRepo = ({title, onOk}) => { - confirm({ - title: title ||
    -
    已创建的私密版本库及其内容,将在“保存”时被删除。
    -
    是否确认取消勾选?
    -
    , - okText: '确定', - cancelText: '取消', - onOk: () => { - this.setState({ is_secret_repository: false }) - onOk && onOk() - }, - onCancel() { - }, - }); - } - is_secret_repository = (e) => { - const checked = e.target.checked - if (!checked) { - if (this.state.init_is_secret_repository) { - this.confirmDeleteSecretRepo({ - }) - } else { - this.setState({ is_secret_repository: false }) - } - } else { - this.setState({ is_secret_repository: true }) - } - } - forbid_copy = (e) => { - let sum = "" - if (e.target.checked === false) { - sum = 0 - } else if (e.target.checked === true) { - sum = 1 - } - this.setState({ - forbid_copy: sum, - }); - } - shixun_vnc_evaluate=(e) => { - this.setState({ - vnc_evaluate: e.target.checked, - }); - - } - - shixun_vnc=(e)=>{ - // let sum = "" - // if (e.target.checked === false) { - // sum = 0 - // } else if (e.target.checked === true) { - // sum = 1 - // } - const vnc = e.target.checked; - if (!vnc) { - if (this.state.init_is_secret_repository && this.state.webssh != 2 && this.state.is_secret_repository == true) { - this.confirmDeleteSecretRepo({ - onOk: () => { - this.setState({ - vnc: e.target.checked, - vnc_evaluate: false, - }); - } - }) - } else { - if (this.state.webssh != 2) { - this.setState({ - is_secret_repository: false - }) - } - this.setState({ - vnc: e.target.checked, - vnc_evaluate: false, - }); - } - } else { - this.setState({ - vnc: e.target.checked, - vnc_evaluate: false, - }); - } - } - shixunsname = (e) => { - // let {shixunsstatus}=this.state; - // if(shixunsstatus>0){ - // return - // } - this.setState({ - name: e.target.value, - shixunnametype:false + operateshixunstype: true, + delType: value }) } - bigClass = (value) => { - // choice_main_type - // choice_small_type - let {settingsData,shixun_service_configs,choice_main_type,choice_small_type}=this.state; - - let list=[] - list.push(choice_main_type) - choice_small_type.map((item,key)=>{ - list.push(item) - }) - - let newshixun_service_configs=shixun_service_configs; - - let newshixun_service_configsagin=[] - - newshixun_service_configs.map((item,key)=>{ - list.map((its,index)=>{ - if(item.mirror_repository_id===its){ - newshixun_service_configsagin.push(item) - } - }) - }) - - - settingsData.shixun.main_type.some((item,key)=> { - if (item.id === value) { - newshixun_service_configsagin[0]={ - mirror_repository_id:value, - name:item.type_name, - cpu_limit:1, - lower_cpu_limit:0.1, - memory_limit:1024, - request_limit:10 - } - return true - } - } - ) - let url = `/shixuns/get_mirror_script.json?mirror_id=`+value; - axios.get(url).then((response) => { - if (response.status === 200) { - // console.log(response.data) - this.setState({ - choice_main_type: value, - standard_scripts:response.data, - choice_standard_scripts:null, - shixun_service_configs:newshixun_service_configsagin, - shixun_service_configlist:newshixun_service_configsagin, - }) - } - }).catch((error) => { - console.log(error) - }); - - - - } - Deselectlittle=(value)=>{ - - let {shixun_service_configs,choice_small_type}=this.state; - let newshixun_service_configs=shixun_service_configs; - let newchoice_small_type=choice_small_type; - - newshixun_service_configs.some((item,key)=> { - if (item.mirror_repository_id === value) { - newshixun_service_configs.splice(key, 1) - return true - } - } - ) - - newchoice_small_type.some((item,key)=> { - if (item === value) { - newchoice_small_type.splice(key, 1) - return true - } - } - ) - - - this.setState({ - choice_small_type: newchoice_small_type, - shixun_service_configs:newshixun_service_configs, - shixun_service_configlist:newshixun_service_configs, - }) - } - littleClass = (value) => { - - let {settingsData,shixun_service_configs,choice_small_type,choice_main_type}=this.state; - let newshixun_service_configs=shixun_service_configs; - let newchoice_small_type=choice_small_type; - // if(Array.isArray(value)===true){ - // value.map((item,key)=>{ - // settingsData.shixun.small_type.some((items,keys)=> { - // if (items.id === item) { - // newshixun_service_configs.push({ - // mirror_repository_id:value, - // name:items.type_name, - // cpu_limit:1, - // lower_cpu_limit:0.1, - // memory_limit:1024, - // request_limit:10 - // }) - // return true - // } - // } - // ) - // }) - // } - - let list=[] - list.push(choice_main_type) - choice_small_type.map((item,key)=>{ - list.push(item) - }) - - let newshixun_service_configsagin=[] - - newshixun_service_configs.map((item,key)=>{ - list.map((its,index)=>{ - if(item.mirror_repository_id===its){ - newshixun_service_configsagin.push(item) - } - }) - }) - - settingsData.shixun.small_type.some((items,keys)=> { - if (items.id === value) { - newshixun_service_configsagin.push({ - mirror_repository_id:value, - name:items.type_name, - cpu_limit:1, - lower_cpu_limit:0.1, - memory_limit:1024, - request_limit:10 - }) - return true - } - } - ) - - newchoice_small_type.push(value) - - this.setState({ - choice_small_type: newchoice_small_type, - shixun_service_configs:newshixun_service_configsagin, - shixun_service_configlist:newshixun_service_configsagin, - }) - } - onPodExistTimeChange = (e) => { + hideoperateshixuns = () => { this.setState({ - pod_exist_time: e.target.value, - pod_exist_timetype: false, + operateshixunstype: false }) } - Timevalue = (e) => { - this.setState({ - exec_time: e.target.value - }) - } - SelectOpenpublic = (e) => { - this.setState({ - scopetype: false, - use_scope: e.target.value, - }); - if (e.target.value === 1) { - this.setState({ - scopetype: true - }); - } - - } - deleteScopeInput = (key) => { - let {scope_partment} = this.state; - let datalist = scope_partment; - datalist.splice(key, 1); - this.setState({ - scope_partment: datalist - }); - } - shixunScopeInput = (e) => { - let {scope_partment} = this.state; - let datalist = scope_partment; - if (datalist===undefined) { - datalist=[] - } - - datalist.push(e) - // else { - // datalist[id] = e - // } - this.setState({ - scope_partment: datalist - }); - } - // adduse_scopeinput = () => { - // let {scope_partment} = this.state; - // let array = scope_partment; - // let newarray = "" - // array.push(newarray) - // this.setState({ - // scope_partment: array, - // }); - // } - submit_edit_shixun = () => { - if (this.saving == true) return; - this.saving = true; - if(this.state.status===-1){ - this.props.showSnackbar("该实训已被删除,保存失败!"); - return - } - - let { - name, choice_main_type, choice_small_type, choice_standard_scripts, scope_partment, choice_standard_scriptssum, vnc_evaluate, - evaluate_script, webssh, use_scope, trainee, can_copy, task_pass, test_set_permission, hide_code, code_hidden, forbid_copy, vnc,multi_webssh, - opening_time,shixunmemoMDvalue,shixun_service_configlist, is_secret_repository, code_edit_permission - } = this.state; - - let newshixun_service_configlist = shixun_service_configlist.map(v => { - let v1 = Object.assign({},v); - delete v1.name; - return v1 - }); - - // let operateauthority= - // this.props.identity===1?true:this.props.identity<5&&this.state.status==0?true:false; - // this.props.identity<5&&this.state.status==0||this.props.identity===1&&this.state.status==2||this.props.identity===1&&this.state.status==1; - - const description_editormd = this.description_editormd.getValue(); - - let evaluate_script_editormd; - - if(this.state.status==0||this.state.status==1||this.state.status==2&&this.props.identity===1){ - // evaluate_script_editormd = this.evaluate_script_editormd.getValue(); - evaluate_script_editormd = shixunmemoMDvalue - }else{ - evaluate_script_editormd = evaluate_script; - } - - - - if (name === "") { - this.setState({ - shixunnametype: true - }) - $('html').animate({ - scrollTop: 10 - }, 1000); - return - } - if (choice_main_type === "") { - this.setState({ - shixunmaintype: true - }) - $('html').animate({ - scrollTop: 800 - }, 1000); - return - } - if (evaluate_script_editormd === "") { - this.setState({ - evaluate_scripttype: true - }) - $('html').animate({ - scrollTop: 1200 - }, 1000); - return - } - if(use_scope===1){ - - if(scope_partment===undefined||scope_partment.length===0){ - this.setState({ - scope_partmenttype: true - }) - $('html').animate({ - scrollTop: 2500 - }, 1000); - this.props.showSnackbar("公开程度,指定单位为空"); - return - } - } - // if (exec_time === "") { - // this.setState({ - // exec_timetype: true - // }) - // $('html').animate({ - // scrollTop: 1500 - // }, 1000); - // return - // } - - // if (!pod_exist_time) { - // this.setState({ - // pod_exist_timetype: true - // }) - // $("html, body").animate({ scrollTop: $('#pod_exist_time').offset().top - 100 }, 1000) - // return - // } - - if (trainee === "") { - this.setState({ - traineetype: true - }) - return - } + shixunsdel = () => { let id = this.props.match.params.shixunId; + let cul = `/shixuns/` + id + `.json`; - let newmulti_webssh=multi_webssh; - - - if(newmulti_webssh===null){ - newmulti_webssh=false - } - - //exec_time: exec_time, - let Url = `/shixuns/` + id + `.json`; - let data = { - shixun:{ - - name: name, - webssh: webssh, - use_scope: use_scope, - can_copy: can_copy, - vnc: vnc===null?undefined:vnc, - vnc_evaluate: vnc_evaluate===null?undefined:vnc_evaluate, - test_set_permission: test_set_permission, - code_hidden: code_hidden, - code_edit_permission: code_edit_permission, - trainee: trainee, - task_pass: task_pass, - hide_code: hide_code, - forbid_copy: forbid_copy, - multi_webssh:newmulti_webssh, - opening_time:opening_time, - mirror_script_id:choice_standard_scriptssum===undefined?choice_standard_scripts:choice_standard_scriptssum, - }, - shixun_info:{ - description: description_editormd, - evaluate_script: evaluate_script_editormd, - }, - is_secret_repository: is_secret_repository, - main_type: choice_main_type, - small_type: choice_small_type, - scope_partment: scope_partment, - shixun_service_configs:newshixun_service_configlist - } + axios.delete(cul).then((response) => { + if (response.data.status === 1) { + this.props.showNotification("操作成功"); + this.setState({ + operateshixunstype: false, + }); - axios.put(Url, data).then((response) => { - // console.log(response) - this.saving = false; - if(response.status){ - if (response.data.status === -1) { - this.props.showSnackbar(response.data.message); - return - } else { - window.location.href = "/shixuns/" + response.data.shixun_identifier + "/challenges"; - } + window.location.href = "/shixuns"; + // this.props.history.replace( "/shixuns/"); } - }).catch((error) => { console.log(error) - this.saving = false; }) - - } - shixunsfetch = (value, callback) => { - if (timeout) { - clearTimeout(timeout); - timeout = null; - } - currentValue = value; - - function fake() { - let departmentsUrl = `/shixuns/departments.json?q=` + currentValue; - axios.get(departmentsUrl).then((response) => { - callback(response.data.shools_name); - }).catch((error) => { - console.log(error) - }); - } - - timeout = setTimeout(fake, 300); - } - shixunHandleSearch = (value) => { - this.shixunsfetch(value, departmentslist => this.setState({departmentslist})); - } - - - shixunsclose = () => { let id = this.props.match.params.shixunId; let cul = `/shixuns/` + id + `/close.json`; axios.post(cul).then((response) => { - if(response.data.status===1){ - this.props.showSnackbar("操作成功"); + if (response.data.status === 1) { + this.props.showNotification("操作成功"); this.setState({ operateshixunstype: false, }); window.location.href = "/shixuns/" + id + "/challenges"; + // this.props.history.replace( "/shixuns/" + id + "/challenges"); } }).catch((error) => { console.log(error) }) } - shixunsdel= () => { - let id = this.props.match.params.shixunId; - let cul = `/shixuns/` + id +`.json`; - - axios.delete(cul).then((response) => { - if(response.data.status===1){ - this.props.showSnackbar("操作成功"); - this.setState({ - operateshixunstype: false, - }); - - window.location.href = "/shixuns"; - } - }).catch((error) => { - console.log(error) - }) - } - - Executiveorder = (e) => { - this.setState({ - Executiveordervalue: e.target.value - }) - } - - Compilecommand = (e) => { - this.setState({ - Compilecommandvalue: e.target.value - }) - } - - handleCancelTemplate = (e) => { - this.setState({ - Executiveordervalue: "", - Compilecommandvalue: "", - visibleTemplate: false - }) - } - - hideModalTemplate = (e) => { - let id = this.props.match.params.shixunId; - let {Executiveordervalue, Compilecommandvalue} = this.state; - - if (Executiveordervalue === "") { - this.setState({ - Executivetyoe: true, - }); - return - } - // Executiveordervalue=String(Executiveordervalue); - // Compilecommandvalue=String(Compilecommandvalue); - let trl = `/shixuns/${id}/get_custom_script.json?compile=${Executiveordervalue}&excutive=${Compilecommandvalue}` - axios.get(trl).then((response) => { - // this.evaluate_scriptMD(response.data.shixun_script, "shixunmemoMD"); - this.setState({ - shixunmemoMDvalue:response.data.shixun_script - }) - }).catch((error) => { - console.log(error) - }); - this.setState({ - visibleTemplate: false - }) - } - - showModal = () => { - this.setState({ - visibleTemplate: true, - }); - } - Selecttrainee = (value) => { - this.setState({ - trainee: value, - }); - } - - post_apply = () => { - this.setState({ - postapplyvisible: true - }) - } - - sendsure_applyvalues = (e) => { - this.setState({ - sendsure_applyvalue: e.target.value - }) - } - - setlanguagewrite = (e)=>{ - this.setState({ - languagewrite: e.target.value - }) - } - - setsystemenvironment = (e) => { - this.setState({ - systemenvironment: e.target.value - }) - } - - settestcoderunmode = (e) => { - this.setState({ - testcoderunmode: e.target.value - }) - - } - - sendsure_apply = () => { - let {languagewrite,systemenvironment,testcoderunmode} = this.state; - // console.log("点击确定") - // console.log("languagewrite"+languagewrite); - // console.log("systemenvironment"+systemenvironment); - // console.log("testcoderunmode"+testcoderunmode); - - // let attachment_ids = undefined - // if (this.state.fileList) { - // attachment_ids = this.state.fileList.map(item => { - // return item.response ? item.response.id : item.id - // }) - // } - if(languagewrite === undefined || languagewrite === "" ){ - // this.props.showNotification(`请填写该镜像是基于什么语言`); - this.setState({ - languagewritetype:true - }) - return - } - if(systemenvironment === undefined || systemenvironment === ""){ - // this.props.showNotification(`请填写该镜像是基于什么语言系统环境`); - this.setState({ - systemenvironmenttype:true - }) - return; - - } - if(testcoderunmode === undefined || testcoderunmode === "") { - // this.props.showNotification(`请填写该镜像中测试代码运行方式`); - this.setState({ - testcoderunmodetype:true - }) - return; - } - var attachment_ids=undefined; - if (this.state.fileList) { - attachment_ids = this.state.fileList.map(item => { - return item.response ? item.response.id : item.id - }) - } - - if( attachment_ids === undefined || attachment_ids.length===0){ - - // notification.open( - // { - // message: '提示', - // description: - // '请上传附件!', - // - // } - // ) - this.setState({ - attachmentidstype:true - }) - return; - } - // console.log("attachment_ids"+attachment_ids); - - // alert(languagewrite +" "+systemenvironment +" "+testcoderunmode + " "+attachment_ids); - - var data={ - language:languagewrite, - runtime:systemenvironment, - run_method:testcoderunmode, - attachment_id:attachment_ids[0], - } - var url =`/shixuns/apply_shixun_mirror.json`; - axios.post(url,data - ).then((response) => { - - try { - if (response.data) { - // const { id } = response.data; - // if (id) { - if(this.state.file !== undefined){ - console.log("549"); - // this.deleteAttachment(this.state.file); - this.setState({ - file:undefined, - deleteisnot:true, - languagewrite:"", - systemenvironment:"", - testcoderunmode:"", - fileList:[] - }) - }else { - this.setState({ - file:undefined, - deleteisnot:true, - languagewrite:"", - systemenvironment:"", - testcoderunmode:"", - fileList:[] - }) - } - // this.props.showNotification('提交成功!'); - notification.open( - { - message: '提示', - description: - '提交成功!', - - } - ) - this.sendhideModaly() - // this.props.history.push(`/courses/${cid}/graduation_topics`); - // } - } - }catch (e) { - - } - - }) - - } - - sendhideModaly = () => { - this.setState({ - postapplyvisible: false, - }) - if(this.state.file !== undefined){ - console.log("580"); - // this.deleteAttachment(this.state.file); - this.setState({ - file:undefined, - deleteisnot:true, - languagewrite:"", - systemenvironment:"", - testcoderunmode:"", - fileList:[] - }) - }else { - this.setState({ - file:undefined, - deleteisnot:true, - languagewrite:"", - systemenvironment:"", - testcoderunmode:"", - fileList:[] - }) - } - } - - yeshidemodel = () => { - this.setState({ - postapplytitle: false - }) - } - - SelectScput = (value, e) => { - this.setState({ - choice_standard_scriptssum: value, - language:e.props.name, - choice_standard_scripts: {id:e.props.value,value:""}, - standard_scriptsModal:true - }) - } - - hidestandard_scriptsModal=()=>{ - this.setState({ - standard_scriptsModal:false, - standard_scriptsModals:false - }) - } - - get_mirror_script=()=>{ - let {choice_standard_scriptssum}=this.state; - let id = this.props.match.params.shixunId; - let pul = "/shixuns/" + id + "/get_script_contents.json?script_id=" + choice_standard_scriptssum; - axios.get(pul).then((response) => { - if(response.status===200){ - // this.evaluate_scriptMD(response.data.content, "shixunmemoMD"); - this.setState({ - standard_scriptsModal:false, - standard_scriptsModals:true, - shixunmemoMDvalue:response.data.content - }) - } - - }).catch((error) => { - console.log(error) - }) - } - - - SelectTheCommandonChange=(e)=>{ - this.setState({ - multi_webssh:e.target.checked - }) - } - - bigopen=()=>{ - this.setState({ - opers:true - }) - - } - - bigopens=()=>{ - this.setState({ - opers:false, - operss:false, - opersss:false, - opensmail:false - }) - - } - bigopensmal=(e)=>{ - this.setState({ - opensmail:true - }) - - } - sbigopen=(e)=>{ - this.setState({ - operss:true - }) - - } - - sbigopens=()=>{ - this.setState({ - operss:false - }) - } - sbigopenss=(e)=>{ - this.setState({ - opersss:true - }) - - } - - sbigopensss=()=>{ - this.setState({ - opersss:false - }) - } - testscripttip=(val)=>{ - if(val===0){ - this.setState({ - testscripttiptype:true - }) - }else if(val===1){ - this.setState({ - testscripttiptype:false - }) - } - } - - operateshixuns=(value)=>{ - this.setState({ - operateshixunstype:true, - delType:value - }) - } - - hideoperateshixuns=()=>{ + callback = (key) => { this.setState({ - operateshixunstype:false + activeKeys:key }) } - onChangeTimePicker =(value, dateString)=> { - this.setState({ - opening_time: dateString=== ""?"":moment(handleDateStrings(dateString)) - }) - } - - getshixunmemoMDvalue=(value, e)=>{ - - this.setState({ - shixunmemoMDvalue:value - }) - } - - setConfigsInputs=(e,keys,str)=>{ - - let {shixun_service_configs}=this.state; - let newshixun_service_configs=shixun_service_configs; - newshixun_service_configs.map((item,key)=>{ - if(key===keys){ - switch (str) { - case 1: - item.cpu_limit=e.target.value - break; - case 2: - item.lower_cpu_limit=e.target.value - break; - case 3: - item.memory_limit=e.target.value - break; - case 4: - item.request_limit=e.target.value - break; - } - } - }) - - this.setState({ - shixun_service_configs:newshixun_service_configs, - shixun_service_configlist:newshixun_service_configs, - }) - - } - - handleChange = (info) => { - let {fileList}=this.state; - - if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') { - console.log("handleChange1"); - - // if(fileList.length===0){ - let fileLists = info.fileList; - this.setState({ fileList:fileLists, - deleteisnot:false}); - // } - } - } - onAttachmentRemove = (file) => { - if(!file.percent || file.percent == 100){ - confirm({ - title: '确定要删除这个附件吗?', - okText: '确定', - cancelText: '取消', - // content: 'Some descriptions', - onOk: () => { - console.log("665") - this.deleteAttachment(file) - }, - onCancel() { - console.log('Cancel'); - }, - }); - return false; - } + render() { - } + let showtabs = this.props.shixunsDetails === undefined ? "" : this.props.shixunsDetails&&this.props.shixunsDetails.is_jupyter === true ? "" : "学习页面设置" - deleteAttachment = (file) => { - console.log(file); - let id=file.response ==undefined ? file.id : file.response.id - const url = `/attachments/${id}.json` - axios.delete(url, { - }) - .then((response) => { - if (response.data) { - const { status } = response.data; - if (status == 0) { - // console.log('--- success') - - this.setState((state) => { - - const index = state.fileList.indexOf(file); - const newFileList = state.fileList.slice(); - newFileList.splice(index, 1); - return { - fileList: newFileList, - deleteisnot:true - }; - }); - } - } - }) - .catch(function (error) { - console.log(error); - }); - } - - - - render() { - let { - postapplyvisible, - postapplytitle, - shixunnametype, - shixunmaintype, - evaluate_scripttype, - traineetype, - standard_scripts, - name, - settingsData, - webssh, - is_secret_repository, - use_scope, - shixunsID, - can_copy, - choice_standard_scripts, - Executiveordervalue, - Executivetyoe, - Compilecommandvalue, - task_pass, - test_set_permission, - hide_code, - forbid_copy, - code_edit_permission, - code_hidden, - vnc, - vnc_evaluate, - scopetype, - scope_partment, - departmentslist, - trainee, - choice_main_type, - choice_small_type, - standard_scriptsModal, - standard_scriptsModals, - SelectTheCommandtype, - testscripttiptype, - operateshixunstype, - opening_time, - scope_partmenttype, - newuse_scope, - scope_partments, - shixunmemoMDvalue,delType, - shixun_service_configs, - fileList, - } = this.state; - - let options; - - if (departmentslist != undefined) { - options = this.state.departmentslist.map((d, k) => { - return ( - - ) - }) - } - const uploadProps = { - width: 600, - fileList, - multiple: true, - // https://github.com/ant-design/ant-design/issues/15505 - // showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。 - // showUploadList: false, - action: `${getUploadActionUrl()}`, - onChange: this.handleChange, - onRemove: this.onAttachmentRemove, - beforeUpload: (file, fileList) => { - if (this.state.fileList.length >= 1) { - return false - } - // console.log('beforeUpload', file.name); - const isLt150M = file.size / 1024 / 1024 < 50; - if (!isLt150M) { - // this.props.showNotification(`文件大小必须小于50MB`); - notification.open( - { - message: '提示', - description: - '文件大小必须小于50MB', - - } - ) - } - if(this.state.file !== undefined){ - console.log("763") - this.setState({ - file:file - }) - }else { - this.setState({ - file:file - }) - } - - console.log("handleChange2"); - return isLt150M; - }, - } - const dateFormat = 'YYYY-MM-DD HH:mm:ss'; - let operateauthority=this.props.identity===1?true:this.props.identity<5&&this.state.status==0?true:false; + // let a="isvnc"; + // let b="isVNC"; + // console.log(a.indexOf("vnc")) + // console.log(b.indexOf("vnc")) + // console.log( this.props.shixunsDetails === undefined ? "" : this.props.shixunsDetails.is_jupyter === false ? "学习页面设置" : "") return ( -
    - - 实训详情 - 配置 - - -
    -
    - 配置 - { - this.props.identity===1&&this.state.status==2? - this.operateshixuns(2)}> - 永久关闭 - :"" - } - { - this.props.identity < 5 && this.state.status==0? - this.operateshixuns(1)}> - 删除实训 - :"" - } - { - this.props.identity == 1 && this.state.status == 2 ? - this.operateshixuns(1)}> - 删除实训 - :"" - } - - -
    - {delType===1?

    是否确认删除 ?

    :

    关闭后,
    用户不能再开始挑战了是否确认关闭 ?

    } -
    -
    - 取消 - {delType===1?确定:确定} -
    -
    - -
    - -
    - -

    实训名称

    - -
    - * -
    -
    - {settingsData === undefined ? "" : - } -
    -
    - 必填项 -
    -
    - - -
    - -
    -
    - -
    - -

    简介

    - -
    - -
    -
    -
    -

    -

    -
    - -
    -
    -

    技术平台

    - - -
    - * -
    - -

    - 列表中没有? - 申请新建 -

    - - -
    -
  • - - -
  • -
    {this.state.languagewritetype===true?"请填写该镜像语言":""}
    -
  • - - -
  • -
    {this.state.systemenvironmenttype===true?"请填写该镜像语言系统环境":""}
    -
  • - - - -
  • -
    {this.state.testcoderunmodetype===true?"请填写该镜像测试代码运行方式":""}
    -
  • - -
    - - - 上传附件 - (单个文件50M以内) - -
    -
  • -
    - {this.state.attachmentidstype===true?"请上传附件":""} -
    -
  • - this.sendhideModaly()} - >取消 - -
  • -
    -
    - -
    - - - - - -
    -

    新建申请已提交,请等待管理员的审核

    -
  • 我们将在1-2个工作日内与您联系 -
  • -
    -
    - 知道啦 -
    -
    -
    -
    - -
    - -
    -
    - 必填项 -
    - {/*

    请在配置页面完成后续的评测脚本设置操作

    */} - -
    -
    - {/*
    */} - {/*
    */} -
    -

    评测脚本

    -
    - - -
    -

    原有脚本将被新的脚本覆盖,无法撤销

    -

    是否确认执行覆盖操作

    -
    - - -
    - - -

    评测脚本生成成功!

    - -
    - - { - this.props.identity<5||this.props.power==true? - 使用自定义脚本 : "" - } -
    - this.testscripttip(0)}> -
    - -
    -

    - 使用自定义模板,平台无法自动更新脚本,
    - 请在关卡创建完后手动更新脚本中的必填参
    - 数和以下2个数组元素:
    - challengeProgramNames
    - sourceClassNames

    - 示例:有2个关卡的实训

    - 各关卡的待编译文件为:
    - src/step1/HelloWorld.java
    - src/step2/Other.java

    - 各关卡的编译后生成的执行文件为:
    - step1.HelloWorld
    - step2.Other

    - 则数组元素更新如下:
    - challengeProgramNames=("src/step1/
    - HelloWorld.java" "src/step2/Other.java")
    - sourceClassNames=("step1.HelloWorld
    - " "step2.Other")

    - 其它参数可按实际需求定制 -

    -
    -

    - this.testscripttip(1)}>知道了 -

    -
    -
    - - -
    -
  • - - -

    执行命令不能为空

    -
  • - -
  • - - -
  • -
    -
    -
    -
    - -
    -
    - * -
    - - -
    - {/**/} - -
    - - - {/*
    */} - {/*{evaluate_script===undefined?"":evaluate_script}*/} - - {/*
    */} - - - -
    - -
    -
    -
    - - 必填项 -
    -

    -

    -
    -
    - - {/*
    */} - {/***/} - - {/*

    程序最大执行时间

    */} - - {/* 秒*/} - - {/*
    */} - {/*必填项*/} - {/*
    */} - {/*
    */} - - {/*
    - * - -

    Pod存活时间

    - - - -
    - 必填项 -
    -
    */} - - -
    -

    命令行

    - - 无命令行窗口 (选中则不给学员的实践任务提供命令窗口) - 命令行练习窗口 (选中则给学员提供用于练习操作的命令行窗口) - 命令行评测窗口 (选中则给学员提供用于关卡评测的命令行窗口) - - 多个命令行窗口(选中则允许学员同时开启多个命令行窗口) - - -
    - -
    -

    公开程度

    - - 对所有公开 (选中则所有已被试用授权的用户可以学习) - 对指定单位公开 (选中则下方指定单位的已被试用授权的用户可以学习) - - -
    -
    -
    -
    -
    - -
    - (搜索并选中添加单位名称) -
    - {/*+*/} - {/*添加*/} -
    - -
    - - {/*{*/} - {/*scope_partment===undefined?"":scope_partment.map((item,key)=>{*/} - {/*return(*/} - {/*
    */} - {/*this.deleteScopeInput(key)} style={{ color: 'rgba(0,0,0,.25)' }} />}*/} - {/*value={item}*/} - {/*/>*/} - {/*
    */} - - {/*)*/} - {/*})*/} - {/*}*/} -
    - - - 请选择需要公开的单位 - -
    -
    -
    - -
    -

    发布信息

    - -
    - * - 面向学员: - -
    - -
    - 实训难易度定位,不限定用户群体 -
    - 必填项 -
    - -
    -
    - 复制: - - - - -
    - -
    - 跳关: - - - - -
    -
    - 测试集解锁: - - - - -
    - - {!code_hidden && !hide_code &&
    - 代码开放修改: - - - - -
    } - -
    - 隐藏代码窗口: - - - - -
    - -
    - 代码目录隐藏: - - - - -
    - - { (vnc || webssh == 2) &&
    - 私密版本库: - - - - -
    } - -
    - 禁用复制粘贴: - - - - -
    - -
    - 开启时间: - - - - -
    - - {this.props.identity<3?
    - VNC图形化: - - - - -
    :""} - {this.props.identity<3 && vnc ?
    - VNC图形化评测: - - - - -
    :""} - - - -
    - - {this.props.identity<3?
    -

    服务配置

    - { shixun_service_configs&&shixun_service_configs.map((item,key)=>{ - - return( -
    -
    -
    - {item.name} - {/*this.Deselectlittle(item.mirror_repository_id)}>*/} -
    -
    - -
    - this.setConfigsInputs(e,key,1)} - className="panel-box-sizing task-form-100 task-height-40" placeholder="请输入类别名称" /> -
    -
    -
    -
    - -
    - this.setConfigsInputs(e,key,2)} - className="panel-box-sizing task-form-100 task-height-40" placeholder="请输入类别名称" /> -
    -
    -
    -
    - -
    - this.setConfigsInputs(e,key,3)} - className="panel-box-sizing task-form-100 task-height-40" placeholder="请输入类别名称" /> -
    -
    -
    -
    - -
    - this.setConfigsInputs(e,key,4)} - className="panel-box-sizing task-form-100 task-height-40" placeholder="请输入类别名称" /> -
    - -
    -
    -
    -
    - ) - - })} -
    :""} - -

    - { - // this.props.identity<4&&this.props.status==0? - this.props.identity<5? -

    - 保存 - 取消 -
    :"" - } -

    +
    + + + + + { + this.props.identity < 5 && this.state.data && this.state.data.shixun.status == 0 ? + + : "" + } + { + this.props.identity == 1 && this.state.data && this.state.data.shixun.status == 2 ? + : "" + } + { + this.props.identity === 1 && this.state.data && this.state.data.shixun.status == 2 ? + : "" + } +
    + }> + + this.getdatas(key)} + /> + + + this.getdatas(key)} + /> + + {this.props.shixunsDetails === undefined ? "" : this.props.shixunsDetails&&this.props.shixunsDetails.is_jupyter === false ? + + this.getdatas(key)} + /> + :"" } + + +
    + {this.state.delType === 1 ?

    是否确认删除 ?

    : +

    关闭后,
    用户不能再开始挑战了是否确认关闭 ?

    } +
    +
    + 取消 + {this.state.delType === 1 ? 确定 : + 确定} +
    +
    +
    - ); } } diff --git a/public/react/src/modules/tpm/TPMsettings/css/TPMsettings.css b/public/react/src/modules/tpm/TPMsettings/css/TPMsettings.css index 8047bbde8..7abf2c70b 100644 --- a/public/react/src/modules/tpm/TPMsettings/css/TPMsettings.css +++ b/public/react/src/modules/tpm/TPMsettings/css/TPMsettings.css @@ -111,3 +111,43 @@ a.newuse_scope-btn { .ml82{ margin-left:82px; } + +.Permanentban{ + color:#5091FF !important; + border-color: #5091FF !important; +} + +/*tab*/ +.ant-tabs-nav{ + padding-bottom:18px; + padding-top: 18px; +} + +.ant-tabs-extra-content{ + margin-top: 18px; +} + +.pdb30{ + padding-bottom: 30px; +} + +.openrenyuan{ + margin-top: 5px !important; + display: inline-block; +} + +.ml81{ + margin-left:81px; +} + +.ml32s{ + margin-left: 32px; +} + +.ml64{ + margin-left: 64px; +} + +.ml160{ + margin-left: 160px; +} \ No newline at end of file diff --git a/public/react/src/modules/tpm/TPMsettings/oldTPMsettings.js b/public/react/src/modules/tpm/TPMsettings/oldTPMsettings.js new file mode 100644 index 000000000..8688e9669 --- /dev/null +++ b/public/react/src/modules/tpm/TPMsettings/oldTPMsettings.js @@ -0,0 +1,2437 @@ +import React, { Component } from 'react'; + +import MonacoEditor from 'react-monaco-editor'; + +//MonacoDiffEditor 对比模式 +import {Input, Select, Radio, Checkbox, Popconfirm, message, Modal,Icon,DatePicker,Breadcrumb,Upload,Button,notification, Tooltip} from 'antd'; + +// import "antd/dist/antd.css"; + +import locale from 'antd/lib/date-picker/locale/zh_CN'; + +import moment from 'moment'; + +import axios from 'axios'; + +import './css/TPMsettings.css'; + +import { getImageUrl, toPath, getUrl ,appendFileSizeToUploadFileAll, getUploadActionUrl} from 'educoder'; + +let origin = getUrl(); + +let path = getUrl("/editormd/lib/") + +const $ = window.$; + +let timeout; + +let currentValue; + +const Option = Select.Option; + +const RadioGroup = Radio.Group; +const confirm = Modal.confirm; +// 处理整点 半点 +// 取传入时间往后的第一个半点 +export function handleDateStrings(dateString) { + if (!dateString) return dateString; + const ar = dateString.split(':') + if (ar[1] == '00' || ar[1] == '30') { + return dateString + } + const miniute = parseInt(ar[1]); + if (miniute < 30 || miniute == 60) { + return [ar[0], '30'].join(':') + } + if (miniute < 60) { + // 加一个小时 + const tempStr = [ar[0], '00'].join(':'); + const format = "YYYY-MM-DD HH:mm"; + const _moment = moment(tempStr, format) + _moment.add(1, 'hours') + return _moment.format(format) + } + + return dateString +} + +// 恢复数据 +function md_rec_data(k,mdu,id, editor){ + if(window.sessionStorage.getItem(k+mdu) !== null){ + editor.setValue(window.sessionStorage.getItem(k+mdu)); + md_clear_data(k,mdu,id); + } +} + +// 保存数据 +function md_add_data(k,mdu,d){ + window.sessionStorage.setItem(k+mdu,d); +} + +// 清空保存的数据 +function md_clear_data(k,mdu,id){ + window.sessionStorage.removeItem(k+mdu); + var id1 = "#e_tip_"+id; + var id2 = "#e_tips_"+id; + if(k == 'content'){ + $(id2).html(""); + }else{ + $(id1).html(""); + } +} + +function md_elocalStorage(editor,mdu,id){ + if (window.sessionStorage){ + var oc = window.sessionStorage.getItem('content'+mdu); + if(oc !== null ){ + $("#e_tips_"+id).data('editor', editor); + var h = '您上次有已保存的数据,是否恢复 ? / 不恢复'; + $("#e_tips_"+id).html(h); + } + setInterval(function() { + var d = new Date(); + var h = d.getHours(); + var m = d.getMinutes(); + var s = d.getSeconds(); + h = h < 10 ? '0' + h : h; + m = m < 10 ? '0' + m : m; + s = s < 10 ? '0' + s : s; + if(editor.getValue().trim() != ""){ + md_add_data("content",mdu,editor.getValue()); + var id1 = "#e_tip_"+id; + var id2 = "#e_tips_"+id; + + $(id1).html(" 数据已于 " + h + ':' + m + ':' + s +" 保存 "); + $(id2).html(""); + } + },10000); + + }else{ + $("#e_tip_"+id).after('您的浏览器不支持localStorage.无法开启自动保存草稿服务,请升级浏览器!'); + } +} + +function create_editorMD(id, width, high, placeholder, imageUrl,initValue, callback) { + var editorName = window.editormd(id, { + width: width, + height: high, + path: path, // "/editormd/lib/" + markdown : initValue, + syncScrolling: "single", + tex: true, + tocm: true, + emoji: true, + taskList: true, + codeFold: true, + searchReplace: true, + htmlDecode: "style,script,iframe", + sequenceDiagram: true, + autoFocus: false, + placeholder: placeholder, + toolbarIcons: function () { + // Or return editormd.toolbarModes[name]; // full, simple, mini + // Using "||" set icons align right. + return ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "|", "testIcon", "testIcon1", '|', "image", "table", '|', "watch", "clear"] + }, + toolbarCustomIcons: { + testIcon: "
    ", + testIcon1: "
    " + }, + //这个配置在simple.html中并没有,但是为了能够提交表单,使用这个配置可以让构造出来的HTML代码直接在第二个隐藏的textarea域中,方便post提交表单。 + saveHTMLToTextarea: true, + // 用于增加自定义工具栏的功能,可以直接插入HTML标签,不使用默认的元素创建图标 + dialogMaskOpacity: 0.6, + imageUpload: true, + imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp", "JPG", "JPEG", "GIF", "PNG", "BMP", "WEBP"], + imageUploadURL: imageUrl,//url + onload: function () { + // this.previewing(); + $("#" + id + " [type=\"latex\"]").bind("click", function () { + editorName.cm.replaceSelection("```latex"); + editorName.cm.replaceSelection("\n"); + editorName.cm.replaceSelection("\n"); + editorName.cm.replaceSelection("```"); + var __Cursor = editorName.cm.getDoc().getCursor(); + editorName.cm.setCursor(__Cursor.line - 1, 0); + }); + + $("#" + id + " [type=\"inline\"]").bind("click", function () { + editorName.cm.replaceSelection("`$$$$`"); + var __Cursor = editorName.cm.getDoc().getCursor(); + editorName.cm.setCursor(__Cursor.line, __Cursor.ch - 3); + editorName.cm.focus(); + }); + $("[type=\"inline\"]").attr("title", "行内公式"); + $("[type=\"latex\"]").attr("title", "多行公式"); + + callback && callback() + } + }); + return editorName; +} + + +function updatamakedown(id){ + setTimeout(()=>{ + var shixunDescr = window.editormd.markdownToHTML(id, { + htmlDecode: "style,script,iframe", + taskList: true, + tex: true, + flowChart: true, + sequenceDiagram: true + }); + $("#"+id+" p:first").addClass("ReactMarkdown"); + $('#collaborators_list_info').show() + }, 200) +} + +function range(start, end) { + const result = []; + for (let i = start; i < end; i++) { + result.push(i); + } + return result; +} +function disabledDateTime() { + return { + // disabledHours: () => range(0, 24).splice(4, 20), + disabledMinutes: () => range(1, 30).concat(range(31, 60)), + // disabledSeconds: () => [0, 60], + }; +} + +function disabledDate(current) { + return current && current < moment().endOf('day').subtract(1, 'days'); +} +export default class TPMsettings extends Component { + constructor(props) { + super(props) + this.state = { + fileList: [], + commandLine: 0, + Openpublic: 0, + settingsData: undefined, + webssh: 0, + use_scope: 0, + shixunsstatus: 0, + shixunsID: undefined, + exec_time: undefined, + trainee: undefined, + can_copy: undefined, + task_pass: undefined, + test_set_permission: undefined, + code_edit_permission: undefined, + hide_code: undefined, + code_hidden: undefined, + forbid_copy: undefined, + vnc: undefined, + name: undefined, + scope_partment: undefined, + scopetype: false, + departmentslist: undefined, + description: '', + evaluate_script:undefined, + standard_scripts: undefined, + choice_main_type: "", + choice_small_type: [], + choice_standard_scripts:undefined, + editordescriptios: undefined, + editorevaluate_scripts: undefined, + choice_standard_scriptssum: undefined, + visibleTemplate: false, + Executiveordervalue: "", + Compilecommandvalue: "", + Executivetyoe: false, + postapplyvisible: false, + sendsure_applyvalue: undefined, + postapplytitle: false, + shixunnametype: false, + shixunmaintype: false, + evaluate_scripttype: false, + exec_timetype: false, + traineetype: false, + standard_scriptsModal:false, + standard_scriptsModals:false, + SelectTheCommandtype:false, + multi_webssh:false, + status:0, + opers:false, + operss:false, + testscripttiptype:false, + opersss:false, + operateshixunstype:false, + opening_time:"", + opensmail:false, + scope_partmenttype:false, + newuse_scope:undefined, + scope_partments:0, + shixun_service_configs:undefined, + shixun_service_configlist:undefined, + pod_exist_time: undefined, + pod_exist_timetype: false, + shixunmemoMDvalue:"", + language:"", + deleteisnot:true + } + } + descriptionMD=(initValue, id)=> { + + this.contentChanged = false; + const placeholder = ""; +// amp; +// 编辑时要传memoId + const imageUrl = `/api/attachments.json`; +// 创建editorMd + + const description_editormd =create_editorMD(id, '100%', 400, placeholder, imageUrl, initValue,()=> { + setTimeout(() => { + description_editormd.resize() + description_editormd.cm && description_editormd.cm.refresh() + }, 500) + + if (initValue != undefined) { + description_editormd.setValue(initValue) + } + description_editormd.cm.on("change", (_cm, changeObj) => { + console.log('....contentChanged') + this.contentChanged = true; + }) + }); + md_elocalStorage(description_editormd, `MemoQuestion_${id}`, `${id}Question`); + this.description_editormd = description_editormd; + window.description_editormd = description_editormd; + } + + evaluate_scriptMD=(initValue, id)=> { + this.contentChanged = false; + const placeholder = ""; +// amp; +// 编辑时要传memoId + const imageUrl = `/api/attachments.json`; +// 创建editorMd + + const evaluate_script_editormd =create_editorMD(id, '100%', 400, placeholder, imageUrl, initValue,()=> { + setTimeout(() => { + evaluate_script_editormd.resize() + evaluate_script_editormd.cm && evaluate_script_editormd.cm.refresh() + }, 500) + + if (initValue != undefined) { + evaluate_script_editormd.setValue(initValue) + } + evaluate_script_editormd.cm.on("change", (_cm, changeObj) => { + console.log('....contentChanged') + this.contentChanged = true; + }) + }); + md_elocalStorage(evaluate_script_editormd, `MemoQuestion_${id}`, `${id}Question`); + this.evaluate_script_editormd = evaluate_script_editormd; + window.evaluate_script_editormd = evaluate_script_editormd; + + } + + + + componentDidMount() { + + let id=this.props.match.params.shixunId; + + let Url=`/shixuns/`+id+`/settings.json`; + + axios.get(Url).then((response)=> { + // alert(response.data.shixun.choice_standard_scripts) + if(response.status===200){ + this.setState({ + shixunsID: id, + settingsData: response.data, + webssh: response.data.shixun.webssh, + use_scope: response.data.shixun.use_scope, + shixunsstatus: response.data.shixun.status, + exec_time: response.data.shixun.exec_time, + trainee: response.data.shixun.trainee, + can_copy: response.data.shixun.can_copy, + task_pass: response.data.shixun.task_pass, + test_set_permission: response.data.shixun.test_set_permission, + hide_code: response.data.shixun.hide_code, + code_edit_permission: response.data.shixun.code_edit_permission, + code_hidden: response.data.shixun.code_hidden, + is_secret_repository: response.data.shixun.is_secret_repository, + init_is_secret_repository: response.data.shixun.is_secret_repository, + forbid_copy: response.data.shixun.forbid_copy, + vnc: response.data.shixun.vnc, + vnc_evaluate: response.data.shixun.vnc_evaluate, + name: response.data.shixun.name, + scope_partment: response.data.shixun.scope_partment, + description: response.data.shixun.description, + evaluate_script: response.data.shixun.evaluate_script, + choice_main_type: response.data.shixun.choice_main_type, + choice_small_type: response.data.shixun.choice_small_type, + choice_standard_scripts: response.data.shixun.choice_standard_scripts, + standard_scripts:response.data.shixun.standard_scripts, + multi_webssh:response.data.shixun.multi_webssh, + status:response.data.shixun.status, + opening_time:response.data.shixun.opening_time, + newuse_scope:response.data.shixun.use_scope, + scope_partments: response.data.shixun.scope_partment.length, + shixunmemoMDvalue:response.data.shixun.evaluate_script, + shixun_service_configs:response.data.shixun.shixun_service_configs, + shixun_service_configlist:response.data.shixun.shixun_service_configs, + }) + + // if(response.data.status===403){ + // message: "您没有权限进行该操作" + // this.setState({ + // :true + // message403:response.data.message + // }) + // } + + + if(response.data.shixun.multi_webssh===true){ + this.setState({ + SelectTheCommandtype:true + }) + }else{ + this.setState({ + SelectTheCommandtype:false + }) + } + if (response.data.shixun.scope_partment.length > 0) { + this.setState({ + scopetype: true + }) + } + // console.log(response.data.shixun.description) + // console.log(response.data.shixun.evaluate_script) + // console.log(response.data.shixun.description) + // this.props.identity<4&&this.props.status==0||this.props.identity===1&&this.props.status==2 + + + // this.evaluate_scriptMD(response.data.shixun.evaluate_script, "shixunmemoMD"); + + this.descriptionMD(response.data.shixun.description, "shixundescription"); + + // this.bigClass() + // if (response.data.shixun.status === 2) { + // + // } else if (response.data.shixun.status === 1) { + // this.props.showSnackbar("这个实训已发布不能修改!"); + // } else if (response.data.shixun.status === 3) { + // this.props.showSnackbar("这个实训已关闭不能修改!"); + // } + } + + }); + + + let departmentsUrl = `/shixuns/departments.json`; + axios.get(departmentsUrl).then((response) => { + if (response.status === 200) { + if (response.data.message === undefined) { + this.setState({ + departmentslist: response.data.shools_name + }); + } + } + }).catch((error) => { + console.log(error) + }); + + + + } + + SelectshixunCommand=(e)=>{ + // console.log( e.target.value) + const webssh = e.target.value + if (webssh == 2) { + this.setState({ + webssh: webssh, + SelectTheCommandtype: true, + multi_webssh:false + }); + } else { + if (this.state.init_is_secret_repository && !this.state.vnc && this.state.is_secret_repository == true) { + this.confirmDeleteSecretRepo({ + onOk: () => { + this.setState({ + webssh: webssh, + SelectTheCommandtype: false, + multi_webssh:false + }); + } + }) + } else { + if (!this.state.vnc) { + this.setState({ + is_secret_repository: false, + }) + } + this.setState({ + webssh: webssh, + SelectTheCommandtype: false, + multi_webssh:false + }); + } + } + + // this.setState({ + // webssh: webssh, + // }); + // if(webssh===2){ + // this.setState({ + // SelectTheCommandtype: true, + // multi_webssh:false + // }); + // }else{ + // this.setState({ + // SelectTheCommandtype: false, + // multi_webssh:false + // }); + // } + } + + SelectOpenpublic=(e)=>{ + this.setState({ + Openpublic: e.target.value + }); + } + + can_copy=(e)=>{ + let sum = "" + if (e.target.checked === false) { + sum = 0 + } else if (e.target.checked === true) { + sum = 1 + } + this.setState({ + can_copy: sum, + }); + + } + + task_pass=(e)=>{ + + let sum = "" + if (e.target.checked === false) { + sum = 0 + } else if (e.target.checked === true) { + sum = 1 + } + this.setState({ + task_pass: sum, + }); + } + + test_set_permission=(e)=>{ + let sum = "" + if (e.target.checked === false) { + sum = 0 + } else if (e.target.checked === true) { + sum = 1 + } + this.setState({ + test_set_permission: sum, + }); + + } + + hide_code=(e)=>{ + let sum = "" + if (e.target.checked === false) { + sum = 0 + } else if (e.target.checked === true) { + sum = 1 + } + this.setState({ + hide_code: sum, + }); + + } + code_edit_permission = (e) => { + this.setState({ + code_edit_permission: e.target.checked + }) + } + code_hidden=(e)=>{ + let sum = "" + if (e.target.checked === false) { + sum = 0 + } else if (e.target.checked === true) { + sum = 1 + } + this.setState({ + code_hidden: sum, + }); + + } + confirmDeleteSecretRepo = ({title, onOk}) => { + confirm({ + title: title ||
    +
    已创建的私密版本库及其内容,将在“保存”时被删除。
    +
    是否确认取消勾选?
    +
    , + okText: '确定', + cancelText: '取消', + onOk: () => { + this.setState({ is_secret_repository: false }) + onOk && onOk() + }, + onCancel() { + }, + }); + } + is_secret_repository = (e) => { + const checked = e.target.checked + if (!checked) { + if (this.state.init_is_secret_repository) { + this.confirmDeleteSecretRepo({ + }) + } else { + this.setState({ is_secret_repository: false }) + } + } else { + this.setState({ is_secret_repository: true }) + } + } + forbid_copy = (e) => { + let sum = "" + if (e.target.checked === false) { + sum = 0 + } else if (e.target.checked === true) { + sum = 1 + } + this.setState({ + forbid_copy: sum, + }); + } + shixun_vnc_evaluate=(e) => { + this.setState({ + vnc_evaluate: e.target.checked, + }); + + } + + shixun_vnc=(e)=>{ + // let sum = "" + // if (e.target.checked === false) { + // sum = 0 + // } else if (e.target.checked === true) { + // sum = 1 + // } + const vnc = e.target.checked; + if (!vnc) { + if (this.state.init_is_secret_repository && this.state.webssh != 2 && this.state.is_secret_repository == true) { + this.confirmDeleteSecretRepo({ + onOk: () => { + this.setState({ + vnc: e.target.checked, + vnc_evaluate: false, + }); + } + }) + } else { + if (this.state.webssh != 2) { + this.setState({ + is_secret_repository: false + }) + } + this.setState({ + vnc: e.target.checked, + vnc_evaluate: false, + }); + } + } else { + this.setState({ + vnc: e.target.checked, + vnc_evaluate: false, + }); + } + } + shixunsname = (e) => { + // let {shixunsstatus}=this.state; + // if(shixunsstatus>0){ + // return + // } + this.setState({ + name: e.target.value, + shixunnametype:false + }) + } + + bigClass = (value) => { + // choice_main_type + // choice_small_type + let {settingsData,shixun_service_configs,choice_main_type,choice_small_type}=this.state; + + let list=[] + list.push(choice_main_type) + choice_small_type.map((item,key)=>{ + list.push(item) + }) + + let newshixun_service_configs=shixun_service_configs; + + let newshixun_service_configsagin=[] + + newshixun_service_configs.map((item,key)=>{ + list.map((its,index)=>{ + if(item.mirror_repository_id===its){ + newshixun_service_configsagin.push(item) + } + }) + }) + + + settingsData.shixun.main_type.some((item,key)=> { + if (item.id === value) { + newshixun_service_configsagin[0]={ + mirror_repository_id:value, + name:item.type_name, + cpu_limit:1, + lower_cpu_limit:0.1, + memory_limit:1024, + request_limit:10 + } + return true + } + } + ) + let url = `/shixuns/get_mirror_script.json?mirror_id=`+value; + axios.get(url).then((response) => { + if (response.status === 200) { + // console.log(response.data) + this.setState({ + choice_main_type: value, + standard_scripts:response.data, + choice_standard_scripts:null, + shixun_service_configs:newshixun_service_configsagin, + shixun_service_configlist:newshixun_service_configsagin, + }) + } + }).catch((error) => { + console.log(error) + }); + + + + } + Deselectlittle=(value)=>{ + + let {shixun_service_configs,choice_small_type}=this.state; + let newshixun_service_configs=shixun_service_configs; + let newchoice_small_type=choice_small_type; + + newshixun_service_configs.some((item,key)=> { + if (item.mirror_repository_id === value) { + newshixun_service_configs.splice(key, 1) + return true + } + } + ) + + newchoice_small_type.some((item,key)=> { + if (item === value) { + newchoice_small_type.splice(key, 1) + return true + } + } + ) + + + this.setState({ + choice_small_type: newchoice_small_type, + shixun_service_configs:newshixun_service_configs, + shixun_service_configlist:newshixun_service_configs, + }) + } + littleClass = (value) => { + + let {settingsData,shixun_service_configs,choice_small_type,choice_main_type}=this.state; + let newshixun_service_configs=shixun_service_configs; + let newchoice_small_type=choice_small_type; + // if(Array.isArray(value)===true){ + // value.map((item,key)=>{ + // settingsData.shixun.small_type.some((items,keys)=> { + // if (items.id === item) { + // newshixun_service_configs.push({ + // mirror_repository_id:value, + // name:items.type_name, + // cpu_limit:1, + // lower_cpu_limit:0.1, + // memory_limit:1024, + // request_limit:10 + // }) + // return true + // } + // } + // ) + // }) + // } + + let list=[] + list.push(choice_main_type) + choice_small_type.map((item,key)=>{ + list.push(item) + }) + + let newshixun_service_configsagin=[] + + newshixun_service_configs.map((item,key)=>{ + list.map((its,index)=>{ + if(item.mirror_repository_id===its){ + newshixun_service_configsagin.push(item) + } + }) + }) + + settingsData.shixun.small_type.some((items,keys)=> { + if (items.id === value) { + newshixun_service_configsagin.push({ + mirror_repository_id:value, + name:items.type_name, + cpu_limit:1, + lower_cpu_limit:0.1, + memory_limit:1024, + request_limit:10 + }) + return true + } + } + ) + + newchoice_small_type.push(value) + + this.setState({ + choice_small_type: newchoice_small_type, + shixun_service_configs:newshixun_service_configsagin, + shixun_service_configlist:newshixun_service_configsagin, + }) + } + onPodExistTimeChange = (e) => { + this.setState({ + pod_exist_time: e.target.value, + pod_exist_timetype: false, + }) + } + Timevalue = (e) => { + this.setState({ + exec_time: e.target.value + }) + } + SelectOpenpublic = (e) => { + this.setState({ + scopetype: false, + use_scope: e.target.value, + }); + if (e.target.value === 1) { + this.setState({ + scopetype: true + }); + } + + } + deleteScopeInput = (key) => { + let {scope_partment} = this.state; + let datalist = scope_partment; + datalist.splice(key, 1); + this.setState({ + scope_partment: datalist + }); + } + + shixunScopeInput = (e) => { + let {scope_partment} = this.state; + let datalist = scope_partment; + if (datalist===undefined) { + datalist=[] + } + + datalist.push(e) + // else { + // datalist[id] = e + // } + this.setState({ + scope_partment: datalist + }); + } + // adduse_scopeinput = () => { + // let {scope_partment} = this.state; + // let array = scope_partment; + // let newarray = "" + // array.push(newarray) + // this.setState({ + // scope_partment: array, + // }); + // } + submit_edit_shixun = () => { + if (this.saving == true) return; + this.saving = true; + if(this.state.status===-1){ + this.props.showSnackbar("该实训已被删除,保存失败!"); + return + } + + let { + name, choice_main_type, choice_small_type, choice_standard_scripts, scope_partment, choice_standard_scriptssum, vnc_evaluate, + evaluate_script, webssh, use_scope, trainee, can_copy, task_pass, test_set_permission, hide_code, code_hidden, forbid_copy, vnc,multi_webssh, + opening_time,shixunmemoMDvalue,shixun_service_configlist, is_secret_repository, code_edit_permission + } = this.state; + + let newshixun_service_configlist = shixun_service_configlist.map(v => { + let v1 = Object.assign({},v); + delete v1.name; + return v1 + }); + + // let operateauthority= + // this.props.identity===1?true:this.props.identity<5&&this.state.status==0?true:false; + // this.props.identity<5&&this.state.status==0||this.props.identity===1&&this.state.status==2||this.props.identity===1&&this.state.status==1; + + const description_editormd = this.description_editormd.getValue(); + + let evaluate_script_editormd; + + if(this.state.status==0||this.state.status==1||this.state.status==2&&this.props.identity===1){ + // evaluate_script_editormd = this.evaluate_script_editormd.getValue(); + evaluate_script_editormd = shixunmemoMDvalue + }else{ + evaluate_script_editormd = evaluate_script; + } + + + + if (name === "") { + this.setState({ + shixunnametype: true + }) + $('html').animate({ + scrollTop: 10 + }, 1000); + return + } + if (choice_main_type === "") { + this.setState({ + shixunmaintype: true + }) + $('html').animate({ + scrollTop: 800 + }, 1000); + return + } + if (evaluate_script_editormd === "") { + this.setState({ + evaluate_scripttype: true + }) + $('html').animate({ + scrollTop: 1200 + }, 1000); + return + } + if(use_scope===1){ + + if(scope_partment===undefined||scope_partment.length===0){ + this.setState({ + scope_partmenttype: true + }) + $('html').animate({ + scrollTop: 2500 + }, 1000); + this.props.showSnackbar("公开程度,指定单位为空"); + return + } + } + // if (exec_time === "") { + // this.setState({ + // exec_timetype: true + // }) + // $('html').animate({ + // scrollTop: 1500 + // }, 1000); + // return + // } + + // if (!pod_exist_time) { + // this.setState({ + // pod_exist_timetype: true + // }) + // $("html, body").animate({ scrollTop: $('#pod_exist_time').offset().top - 100 }, 1000) + // return + // } + + if (trainee === "") { + this.setState({ + traineetype: true + }) + return + } + + let id = this.props.match.params.shixunId; + + let newmulti_webssh=multi_webssh; + + + if(newmulti_webssh===null){ + newmulti_webssh=false + } + + //exec_time: exec_time, + let Url = `/shixuns/` + id + `.json`; + let data = { + shixun:{ + + name: name, + webssh: webssh, + use_scope: use_scope, + can_copy: can_copy, + vnc: vnc===null?undefined:vnc, + vnc_evaluate: vnc_evaluate===null?undefined:vnc_evaluate, + test_set_permission: test_set_permission, + code_hidden: code_hidden, + code_edit_permission: code_edit_permission, + trainee: trainee, + task_pass: task_pass, + hide_code: hide_code, + forbid_copy: forbid_copy, + multi_webssh:newmulti_webssh, + opening_time:opening_time, + mirror_script_id:choice_standard_scriptssum===undefined?choice_standard_scripts:choice_standard_scriptssum, + }, + shixun_info:{ + description: description_editormd, + evaluate_script: evaluate_script_editormd, + }, + is_secret_repository: is_secret_repository, + main_type: choice_main_type, + small_type: choice_small_type, + scope_partment: scope_partment, + shixun_service_configs:newshixun_service_configlist + } + + axios.put(Url, data).then((response) => { + // console.log(response) + this.saving = false; + if(response.status){ + if (response.data.status === -1) { + this.props.showSnackbar(response.data.message); + return + } else { + window.location.href = "/shixuns/" + response.data.shixun_identifier + "/challenges"; + } + } + + }).catch((error) => { + console.log(error) + this.saving = false; + }) + + + } + shixunsfetch = (value, callback) => { + if (timeout) { + clearTimeout(timeout); + timeout = null; + } + currentValue = value; + + function fake() { + let departmentsUrl = `/shixuns/departments.json?q=` + currentValue; + axios.get(departmentsUrl).then((response) => { + callback(response.data.shools_name); + }).catch((error) => { + console.log(error) + }); + } + + timeout = setTimeout(fake, 300); + } + shixunHandleSearch = (value) => { + this.shixunsfetch(value, departmentslist => this.setState({departmentslist})); + } + + + + + shixunsclose = () => { + let id = this.props.match.params.shixunId; + let cul = `/shixuns/` + id + `/close.json`; + axios.post(cul).then((response) => { + if(response.data.status===1){ + this.props.showSnackbar("操作成功"); + this.setState({ + operateshixunstype: false, + }); + + window.location.href = "/shixuns/" + id + "/challenges"; + } + }).catch((error) => { + console.log(error) + }) + } + + shixunsdel= () => { + let id = this.props.match.params.shixunId; + let cul = `/shixuns/` + id +`.json`; + + axios.delete(cul).then((response) => { + if(response.data.status===1){ + this.props.showSnackbar("操作成功"); + this.setState({ + operateshixunstype: false, + }); + + window.location.href = "/shixuns"; + } + }).catch((error) => { + console.log(error) + }) + } + + Executiveorder = (e) => { + this.setState({ + Executiveordervalue: e.target.value + }) + } + + Compilecommand = (e) => { + this.setState({ + Compilecommandvalue: e.target.value + }) + } + + handleCancelTemplate = (e) => { + this.setState({ + Executiveordervalue: "", + Compilecommandvalue: "", + visibleTemplate: false + }) + } + + hideModalTemplate = (e) => { + let id = this.props.match.params.shixunId; + let {Executiveordervalue, Compilecommandvalue} = this.state; + + if (Executiveordervalue === "") { + this.setState({ + Executivetyoe: true, + }); + return + } + // Executiveordervalue=String(Executiveordervalue); + // Compilecommandvalue=String(Compilecommandvalue); + let trl = `/shixuns/${id}/get_custom_script.json?compile=${Executiveordervalue}&excutive=${Compilecommandvalue}` + axios.get(trl).then((response) => { + // this.evaluate_scriptMD(response.data.shixun_script, "shixunmemoMD"); + this.setState({ + shixunmemoMDvalue:response.data.shixun_script + }) + }).catch((error) => { + console.log(error) + }); + this.setState({ + visibleTemplate: false + }) + } + + showModal = () => { + this.setState({ + visibleTemplate: true, + }); + } + Selecttrainee = (value) => { + this.setState({ + trainee: value, + }); + } + + post_apply = () => { + this.setState({ + postapplyvisible: true + }) + } + + sendsure_applyvalues = (e) => { + this.setState({ + sendsure_applyvalue: e.target.value + }) + } + + setlanguagewrite = (e)=>{ + this.setState({ + languagewrite: e.target.value + }) + } + + setsystemenvironment = (e) => { + this.setState({ + systemenvironment: e.target.value + }) + } + + settestcoderunmode = (e) => { + this.setState({ + testcoderunmode: e.target.value + }) + + } + + sendsure_apply = () => { + let {languagewrite,systemenvironment,testcoderunmode} = this.state; + // console.log("点击确定") + // console.log("languagewrite"+languagewrite); + // console.log("systemenvironment"+systemenvironment); + // console.log("testcoderunmode"+testcoderunmode); + + // let attachment_ids = undefined + // if (this.state.fileList) { + // attachment_ids = this.state.fileList.map(item => { + // return item.response ? item.response.id : item.id + // }) + // } + if(languagewrite === undefined || languagewrite === "" ){ + // this.props.showNotification(`请填写该镜像是基于什么语言`); + this.setState({ + languagewritetype:true + }) + return + } + if(systemenvironment === undefined || systemenvironment === ""){ + // this.props.showNotification(`请填写该镜像是基于什么语言系统环境`); + this.setState({ + systemenvironmenttype:true + }) + return; + + } + if(testcoderunmode === undefined || testcoderunmode === "") { + // this.props.showNotification(`请填写该镜像中测试代码运行方式`); + this.setState({ + testcoderunmodetype:true + }) + return; + } + var attachment_ids=undefined; + if (this.state.fileList) { + attachment_ids = this.state.fileList.map(item => { + return item.response ? item.response.id : item.id + }) + } + + if( attachment_ids === undefined || attachment_ids.length===0){ + + // notification.open( + // { + // message: '提示', + // description: + // '请上传附件!', + // + // } + // ) + this.setState({ + attachmentidstype:true + }) + return; + } + // console.log("attachment_ids"+attachment_ids); + + // alert(languagewrite +" "+systemenvironment +" "+testcoderunmode + " "+attachment_ids); + + var data={ + language:languagewrite, + runtime:systemenvironment, + run_method:testcoderunmode, + attachment_id:attachment_ids[0], + } + var url =`/shixuns/apply_shixun_mirror.json`; + axios.post(url,data + ).then((response) => { + + try { + if (response.data) { + // const { id } = response.data; + // if (id) { + if(this.state.file !== undefined){ + console.log("549"); + // this.deleteAttachment(this.state.file); + this.setState({ + file:undefined, + deleteisnot:true, + languagewrite:"", + systemenvironment:"", + testcoderunmode:"", + fileList:[] + }) + }else { + this.setState({ + file:undefined, + deleteisnot:true, + languagewrite:"", + systemenvironment:"", + testcoderunmode:"", + fileList:[] + }) + } + // this.props.showNotification('提交成功!'); + notification.open( + { + message: '提示', + description: + '提交成功!', + + } + ) + this.sendhideModaly() + // this.props.history.push(`/courses/${cid}/graduation_topics`); + // } + } + }catch (e) { + + } + + }) + + } + + sendhideModaly = () => { + this.setState({ + postapplyvisible: false, + }) + if(this.state.file !== undefined){ + console.log("580"); + // this.deleteAttachment(this.state.file); + this.setState({ + file:undefined, + deleteisnot:true, + languagewrite:"", + systemenvironment:"", + testcoderunmode:"", + fileList:[] + }) + }else { + this.setState({ + file:undefined, + deleteisnot:true, + languagewrite:"", + systemenvironment:"", + testcoderunmode:"", + fileList:[] + }) + } + } + + yeshidemodel = () => { + this.setState({ + postapplytitle: false + }) + } + + SelectScput = (value, e) => { + this.setState({ + choice_standard_scriptssum: value, + language:e.props.name, + choice_standard_scripts: {id:e.props.value,value:""}, + standard_scriptsModal:true + }) + } + + hidestandard_scriptsModal=()=>{ + this.setState({ + standard_scriptsModal:false, + standard_scriptsModals:false + }) + } + + get_mirror_script=()=>{ + let {choice_standard_scriptssum}=this.state; + let id = this.props.match.params.shixunId; + let pul = "/shixuns/" + id + "/get_script_contents.json?script_id=" + choice_standard_scriptssum; + axios.get(pul).then((response) => { + if(response.status===200){ + // this.evaluate_scriptMD(response.data.content, "shixunmemoMD"); + this.setState({ + standard_scriptsModal:false, + standard_scriptsModals:true, + shixunmemoMDvalue:response.data.content + }) + } + + }).catch((error) => { + console.log(error) + }) + } + + + SelectTheCommandonChange=(e)=>{ + this.setState({ + multi_webssh:e.target.checked + }) + } + + bigopen=()=>{ + this.setState({ + opers:true + }) + + } + + bigopens=()=>{ + this.setState({ + opers:false, + operss:false, + opersss:false, + opensmail:false + }) + + } + bigopensmal=(e)=>{ + this.setState({ + opensmail:true + }) + + } + sbigopen=(e)=>{ + this.setState({ + operss:true + }) + + } + + sbigopens=()=>{ + this.setState({ + operss:false + }) + } + sbigopenss=(e)=>{ + this.setState({ + opersss:true + }) + + } + + sbigopensss=()=>{ + this.setState({ + opersss:false + }) + } + testscripttip=(val)=>{ + if(val===0){ + this.setState({ + testscripttiptype:true + }) + }else if(val===1){ + this.setState({ + testscripttiptype:false + }) + } + } + + operateshixuns=(value)=>{ + this.setState({ + operateshixunstype:true, + delType:value + }) + } + + hideoperateshixuns=()=>{ + this.setState({ + operateshixunstype:false + }) + } + onChangeTimePicker =(value, dateString)=> { + this.setState({ + opening_time: dateString=== ""?"":moment(handleDateStrings(dateString)) + }) + } + + getshixunmemoMDvalue=(value, e)=>{ + + this.setState({ + shixunmemoMDvalue:value + }) + } + + setConfigsInputs=(e,keys,str)=>{ + + let {shixun_service_configs}=this.state; + let newshixun_service_configs=shixun_service_configs; + newshixun_service_configs.map((item,key)=>{ + if(key===keys){ + switch (str) { + case 1: + item.cpu_limit=e.target.value + break; + case 2: + item.lower_cpu_limit=e.target.value + break; + case 3: + item.memory_limit=e.target.value + break; + case 4: + item.request_limit=e.target.value + break; + } + } + }) + + this.setState({ + shixun_service_configs:newshixun_service_configs, + shixun_service_configlist:newshixun_service_configs, + }) + + } + + handleChange = (info) => { + let {fileList}=this.state; + + if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') { + console.log("handleChange1"); + + // if(fileList.length===0){ + let fileLists = info.fileList; + this.setState({ fileList:fileLists, + deleteisnot:false}); + // } + } + } + + onAttachmentRemove = (file) => { + if(!file.percent || file.percent == 100){ + confirm({ + title: '确定要删除这个附件吗?', + okText: '确定', + cancelText: '取消', + // content: 'Some descriptions', + onOk: () => { + console.log("665") + this.deleteAttachment(file) + }, + onCancel() { + console.log('Cancel'); + }, + }); + return false; + } + + } + + deleteAttachment = (file) => { + console.log(file); + let id=file.response ==undefined ? file.id : file.response.id + const url = `/attachments/${id}.json` + axios.delete(url, { + }) + .then((response) => { + if (response.data) { + const { status } = response.data; + if (status == 0) { + // console.log('--- success') + + this.setState((state) => { + + const index = state.fileList.indexOf(file); + const newFileList = state.fileList.slice(); + newFileList.splice(index, 1); + return { + fileList: newFileList, + deleteisnot:true + }; + }); + } + } + }) + .catch(function (error) { + console.log(error); + }); + } + + + + render() { + let { + postapplyvisible, + postapplytitle, + shixunnametype, + shixunmaintype, + evaluate_scripttype, + traineetype, + standard_scripts, + name, + settingsData, + webssh, + is_secret_repository, + use_scope, + shixunsID, + can_copy, + choice_standard_scripts, + Executiveordervalue, + Executivetyoe, + Compilecommandvalue, + task_pass, + test_set_permission, + hide_code, + forbid_copy, + code_edit_permission, + code_hidden, + vnc, + vnc_evaluate, + scopetype, + scope_partment, + departmentslist, + trainee, + choice_main_type, + choice_small_type, + standard_scriptsModal, + standard_scriptsModals, + SelectTheCommandtype, + testscripttiptype, + operateshixunstype, + opening_time, + scope_partmenttype, + newuse_scope, + scope_partments, + shixunmemoMDvalue,delType, + shixun_service_configs, + fileList, + } = this.state; + + let options; + + if (departmentslist != undefined) { + options = this.state.departmentslist.map((d, k) => { + return ( + + ) + }) + } + const uploadProps = { + width: 600, + fileList, + multiple: true, + // https://github.com/ant-design/ant-design/issues/15505 + // showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。 + // showUploadList: false, + action: `${getUploadActionUrl()}`, + onChange: this.handleChange, + onRemove: this.onAttachmentRemove, + beforeUpload: (file, fileList) => { + if (this.state.fileList.length >= 1) { + return false + } + // console.log('beforeUpload', file.name); + const isLt150M = file.size / 1024 / 1024 < 50; + if (!isLt150M) { + // this.props.showNotification(`文件大小必须小于50MB`); + notification.open( + { + message: '提示', + description: + '文件大小必须小于50MB', + + } + ) + } + if(this.state.file !== undefined){ + console.log("763") + this.setState({ + file:file + }) + }else { + this.setState({ + file:file + }) + } + + console.log("handleChange2"); + return isLt150M; + }, + } + const dateFormat = 'YYYY-MM-DD HH:mm:ss'; + let operateauthority=this.props.identity===1?true:this.props.identity<5&&this.state.status==0?true:false; + + return ( +
    + + 实训详情 + 配置 + + +
    +
    + 配置 + { + this.props.identity===1&&this.state.status==2? + this.operateshixuns(2)}> + 永久关闭 + :"" + } + { + this.props.identity < 5 && this.state.status==0? + this.operateshixuns(1)}> + 删除实训 + :"" + } + { + this.props.identity == 1 && this.state.status == 2 ? + this.operateshixuns(1)}> + 删除实训 + :"" + } + + +
    + {delType===1?

    是否确认删除 ?

    :

    关闭后,
    用户不能再开始挑战了是否确认关闭 ?

    } +
    +
    + 取消 + {delType===1?确定:确定} +
    +
    + +
    + +
    + +

    实训名称

    + +
    + * +
    +
    + {settingsData === undefined ? "" : + } +
    +
    + 必填项 +
    +
    + + +
    + +
    +
    + +
    + +

    简介

    + +
    + +
    +
    +
    +

    +

    +
    + +
    +
    +

    技术平台

    + + +
    + * +
    + +

    + 列表中没有? + 申请新建 +

    + + +
    +
  • + + +
  • +
    {this.state.languagewritetype===true?"请填写该镜像语言":""}
    +
  • + + +
  • +
    {this.state.systemenvironmenttype===true?"请填写该镜像语言系统环境":""}
    +
  • + + + +
  • +
    {this.state.testcoderunmodetype===true?"请填写该镜像测试代码运行方式":""}
    +
  • + +
    + + + 上传附件 + (单个文件50M以内) + +
    +
  • +
    + {this.state.attachmentidstype===true?"请上传附件":""} +
    +
  • + this.sendhideModaly()} + >取消 + +
  • +
    +
    + +
    + + + + + +
    +

    新建申请已提交,请等待管理员的审核

    +
  • 我们将在1-2个工作日内与您联系 +
  • +
    +
    + 知道啦 +
    +
    +
    +
    + +
    + +
    +
    + 必填项 +
    + {/*

    请在配置页面完成后续的评测脚本设置操作

    */} + +
    +
    + {/*
    */} + {/*
    */} +
    +

    评测脚本

    +
    + + +
    +

    原有脚本将被新的脚本覆盖,无法撤销

    +

    是否确认执行覆盖操作

    +
    + + +
    + + +

    评测脚本生成成功!

    + +
    + + { + this.props.identity<5||this.props.power==true? + 使用自定义脚本 : "" + } +
    + this.testscripttip(0)}> +
    + +
    +

    + 使用自定义模板,平台无法自动更新脚本,
    + 请在关卡创建完后手动更新脚本中的必填参
    + 数和以下2个数组元素:
    + challengeProgramNames
    + sourceClassNames

    + 示例:有2个关卡的实训

    + 各关卡的待编译文件为:
    + src/step1/HelloWorld.java
    + src/step2/Other.java

    + 各关卡的编译后生成的执行文件为:
    + step1.HelloWorld
    + step2.Other

    + 则数组元素更新如下:
    + challengeProgramNames=("src/step1/
    + HelloWorld.java" "src/step2/Other.java")
    + sourceClassNames=("step1.HelloWorld
    + " "step2.Other")

    + 其它参数可按实际需求定制 +

    +
    +

    + this.testscripttip(1)}>知道了 +

    +
    +
    + + +
    +
  • + + +

    执行命令不能为空

    +
  • + +
  • + + +
  • +
    +
    +
    +
    + +
    +
    + * +
    + + +
    + {/**/} + +
    + + + {/*
    */} + {/*{evaluate_script===undefined?"":evaluate_script}*/} + + {/*
    */} + + + +
    + +
    +
    +
    + + 必填项 +
    +

    +

    +
    +
    + + {/*
    */} + {/***/} + + {/*

    程序最大执行时间

    */} + + {/* 秒*/} + + {/*
    */} + {/*必填项*/} + {/*
    */} + {/*
    */} + + {/*
    + * + +

    Pod存活时间

    + + + +
    + 必填项 +
    +
    */} + + +
    +

    命令行

    + + 无命令行窗口 (选中则不给学员的实践任务提供命令窗口) + 命令行练习窗口 (选中则给学员提供用于练习操作的命令行窗口) + 命令行评测窗口 (选中则给学员提供用于关卡评测的命令行窗口) + + 多个命令行窗口(选中则允许学员同时开启多个命令行窗口) + + +
    + +
    +

    公开程度

    + + 对所有公开 (选中则所有已被试用授权的用户可以学习) + 对指定单位公开 (选中则下方指定单位的已被试用授权的用户可以学习) + + +
    +
    +
    +
    +
    + +
    + (搜索并选中添加单位名称) +
    + {/*+*/} + {/*添加*/} +
    + +
    + + {/*{*/} + {/*scope_partment===undefined?"":scope_partment.map((item,key)=>{*/} + {/*return(*/} + {/*
    */} + {/*this.deleteScopeInput(key)} style={{ color: 'rgba(0,0,0,.25)' }} />}*/} + {/*value={item}*/} + {/*/>*/} + {/*
    */} + + {/*)*/} + {/*})*/} + {/*}*/} +
    + + + 请选择需要公开的单位 + +
    +
    +
    + +
    +

    发布信息

    + +
    + * + 面向学员: + +
    + +
    + 实训难易度定位,不限定用户群体 +
    + 必填项 +
    + +
    +
    + 复制: + + + + +
    + +
    + 跳关: + + + + +
    +
    + 测试集解锁: + + + + +
    + + {!code_hidden && !hide_code &&
    + 代码开放修改: + + + + +
    } + +
    + 隐藏代码窗口: + + + + +
    + +
    + 代码目录隐藏: + + + + +
    + + { (vnc || webssh == 2) &&
    + 私密版本库: + + + + +
    } + +
    + 禁用复制粘贴: + + + + +
    + +
    + 开启时间: + + + + +
    + + {this.props.identity<3?
    + VNC图形化: + + + + +
    :""} + {this.props.identity<3 && vnc ?
    + VNC图形化评测: + + + + +
    :""} + + + +
    + + {this.props.identity<3?
    +

    服务配置

    + { shixun_service_configs&&shixun_service_configs.map((item,key)=>{ + + return( +
    +
    +
    + {item.name} + {/*this.Deselectlittle(item.mirror_repository_id)}>*/} +
    +
    + +
    + this.setConfigsInputs(e,key,1)} + className="panel-box-sizing task-form-100 task-height-40" placeholder="请输入类别名称" /> +
    +
    +
    +
    + +
    + this.setConfigsInputs(e,key,2)} + className="panel-box-sizing task-form-100 task-height-40" placeholder="请输入类别名称" /> +
    +
    +
    +
    + +
    + this.setConfigsInputs(e,key,3)} + className="panel-box-sizing task-form-100 task-height-40" placeholder="请输入类别名称" /> +
    +
    +
    +
    + +
    + this.setConfigsInputs(e,key,4)} + className="panel-box-sizing task-form-100 task-height-40" placeholder="请输入类别名称" /> +
    + +
    +
    +
    +
    + ) + + })} +
    :""} + +

    + { + // this.props.identity<4&&this.props.status==0? + this.props.identity<5? +

    + 保存 + 取消 +
    :"" + } + +

    + +
    + ); + } +} + + diff --git a/public/react/src/modules/tpm/challengesnew/TPMMDEditor.js b/public/react/src/modules/tpm/challengesnew/TPMMDEditor.js index 11b88a037..22326e9e7 100644 --- a/public/react/src/modules/tpm/challengesnew/TPMMDEditor.js +++ b/public/react/src/modules/tpm/challengesnew/TPMMDEditor.js @@ -344,7 +344,7 @@ export default class TPMMDEditor extends Component {
    - {noStorage == true ? ' ' :

     

    } + {noStorage == true ? ' ' :
     
    } {/* {noStorage == true ? ' ' :

     

    } */}
    diff --git a/public/react/src/modules/tpm/challengesnew/TPManswer.js b/public/react/src/modules/tpm/challengesnew/TPManswer.js index bc9523422..9187e09b0 100644 --- a/public/react/src/modules/tpm/challengesnew/TPManswer.js +++ b/public/react/src/modules/tpm/challengesnew/TPManswer.js @@ -309,14 +309,14 @@ export default class TPManswer extends Component { 下一关 } - + 实践类型 - + 实践类型 + + 选择题类型 + data-tip-down="新增选择题类型的任务">+ 选择题类型 diff --git a/public/react/src/modules/tpm/challengesnew/TPManswer2.js b/public/react/src/modules/tpm/challengesnew/TPManswer2.js index ba38776cd..dfdc58db4 100644 --- a/public/react/src/modules/tpm/challengesnew/TPManswer2.js +++ b/public/react/src/modules/tpm/challengesnew/TPManswer2.js @@ -272,14 +272,14 @@ export default class TPManswer extends Component { 下一关 } - + 实践类型 - + 实践类型 + + 选择题类型 + data-tip-down="新增选择题类型的任务">+ 选择题类型 diff --git a/public/react/src/modules/tpm/challengesnew/TPMevaluation.js b/public/react/src/modules/tpm/challengesnew/TPMevaluation.js index c4e28a883..34eb1a075 100644 --- a/public/react/src/modules/tpm/challengesnew/TPMevaluation.js +++ b/public/react/src/modules/tpm/challengesnew/TPMevaluation.js @@ -831,14 +831,14 @@ export default class TPMevaluation extends Component { 下一关 } - 4||this.props.identity===undefined||this.props.status===2||this.props.status===1? "none":'block'}} - data-tip-down="新增代码编辑类型的任务">+ 实践类型 - + 实践类型 + 4||this.props.identity===undefined||this.props.status===2||this.props.status===1?"none":'block'}} - data-tip-down="新增选择题类型的任务">+ 选择题类型 + data-tip-down="新增选择题类型的任务">+ 选择题类型 diff --git a/public/react/src/modules/tpm/component/TPMNav.js b/public/react/src/modules/tpm/component/TPMNav.js index ff8f57aa5..24de62f1f 100644 --- a/public/react/src/modules/tpm/component/TPMNav.js +++ b/public/react/src/modules/tpm/component/TPMNav.js @@ -5,7 +5,11 @@ import { BrowserRouter as Router, Route, Link } from "react-router-dom"; class TPMNav extends Component { render() { - const { user, match, shixun, secret_repository } = this.props; + // console.log("componentDidMount"); + // console.log("TPMNavTPMNavTPMNavTPMNav"); + // console.log(this.props); + + const { user, match, shixun, secret_repository,is_jupyter} = this.props; let isAdminOrCreator = false; if (user) { isAdminOrCreator = user.admin || user.manager @@ -15,7 +19,15 @@ class TPMNav extends Component { // console.log(this.props.propaedeutics) const challengesPath = `/shixuns/${shixunId}/challenges`; // console.log(match.path) + // console.log("TPMNavTPMNavTPMNav"); + // console.log(is_jupyter); + const is_teacher = this.props&&this.props.current_user&&this.props.current_user.is_teacher?this.props.current_user.is_teacher:""; + const isfalse=true; return ( + + + isfalse?"": +
    背景知识 } - { this.props.identity >4||this.props.identity===undefined ?"":版本库} + { this.props.identity >4||this.props.identity===undefined ?"": + (this.props.is_jupyter===false? + 版本库 + :"") + } + + + {this.props.identity >4||this.props.identity===undefined ?"": secret_repository && 私密版本库} 合作者 - 评论 + {/*jupyter*/} + { + this.props.is_jupyter===true? + 数据集 + :"" + } + { + this.props.is_jupyter === false ? + 评论 + :"" + } + { + this.props.is_jupyter === false ? 排行榜 + className={`${match.url.indexOf('ranking_list') != -1 ? 'active' : ''} fl mr40`}>排行榜:"" + } - {this.props.identity >2||this.props.identity===undefined?"":审核情况} + {this.props.identity >2||this.props.identity===undefined?"": + 审核情况 + } - 4||this.props.identity===undefined ? "none" : 'block'}} - >配置 + {this.props.identity >4||this.props.identity===undefined ? "":配置}
    ); } diff --git a/public/react/src/modules/tpm/component/TPMRightSection.js b/public/react/src/modules/tpm/component/TPMRightSection.js index 4306fc6e3..88894794b 100644 --- a/public/react/src/modules/tpm/component/TPMRightSection.js +++ b/public/react/src/modules/tpm/component/TPMRightSection.js @@ -1,205 +1,256 @@ -import React, { Component } from 'react'; +import React, {Component} from 'react'; -import { BrowserRouter as Router, Route, Link } from "react-router-dom"; +import {BrowserRouter as Router, Route, Link} from "react-router-dom"; import axios from 'axios'; -import { getImageUrl,} from "educoder"; +import {getImageUrl,} from "educoder"; import './TPMright.css'; -import {Icon,Tooltip} from 'antd'; +import {Progress, Tooltip} from 'antd'; // import "antd/dist/antd.css"; class TPMRightSection extends Component { - constructor(props) { - super(props) - this.state = { - - TPMRightSection:false, - clickNewsubscripttype:false - } - } - - // componentDidMount() { - // let id=this.props.match.params.shixunId; - // - // let shixunsDetailsURL=`/shixuns/`+id+`/show_right.json`; - // - // axios.get(shixunsDetailsURL).then((response)=> { - // if(response.status===200){ - // this.setState({ - // TPMRightSectionData: response.data - // }); - // } - // }).catch((error)=>{ - // console.log(error) - // }); - // } - - // shouldComponentUpdate(nextProps, nextState) { - // return nextProps.TPMRightSectionData !== this.state.TPMRightSectionData - // } - clickNewsubscript=(val)=>{ - if(val===0){ - this.setState({ - TPMRightSection:true, - clickNewsubscripttype:true - }) - }else{ - this.setState({ - TPMRightSection:false, - clickNewsubscripttype:false - }) - } - - } - render() { - let {TPMRightSection,clickNewsubscripttype}=this.state; - let {TPMRightSectionData}=this.props - - return ( -
    - { - TPMRightSectionData===undefined?"": -
    -
    -

    创建者

    -
    - - 头像 - -
    - -

    {TPMRightSectionData===undefined?"":TPMRightSectionData.creator===undefined?"":TPMRightSectionData.creator.name}

    -
    - 发布 {TPMRightSectionData.user_shixuns_count} - {/*粉丝 {TPMRightSectionData.fans_count}*/} - {/* 取消关注 */} -
    - -
    -
    -
    - { - TPMRightSectionData === undefined ? "" :TPMRightSectionData.tags===undefined?"": TPMRightSectionData.tags.length === 0 ? "" : -
    -

    技能标签 {TPMRightSectionData.tags.length}

    -
    -
    - { TPMRightSectionData.tags.map((item,key)=>{ - return( - {item.tag_name} - )}) - } -
    -
    - - -
    15&&clickNewsubscripttype===false?"newsubscript mb9 color-grey-9":"newsubscript mb9 color-grey-9 none"} - - data-tip-down="显示全部" - onClick={()=>this.clickNewsubscript(0)}>... -
    - - -
    this.clickNewsubscript(1)}> -
    - -
    - - - } - - -
    -

    所属课程

    -
    - { - TPMRightSectionData===undefined?"":TPMRightSectionData.paths===undefined?"":TPMRightSectionData.paths.map((i,k)=>{ - - return( - -
    - - - 实训 - - -
    - {i.name} -

    - - - {i.stages_count} - - - {/**/} - {/*{i.score_count}*/} - {/**/} - - - {i.members_count} - - -

    -
    - -
    - - ) - }) - } -
    -
    - - {TPMRightSectionData === undefined?"":TPMRightSectionData.paths===undefined?"":TPMRightSectionData.paths.length === 0 ? "" : - this.props.user&&this.props.user.main_site===true?
    -

    推荐实训

    -
    - { - TPMRightSectionData===undefined?"":TPMRightSectionData.recommands===undefined?"":TPMRightSectionData.recommands.map((item,key)=>{ - return( -
    - - 69?1526971094 - -
    - - {item.name} - -

    - {item.stu_num} 人学习 -

    -

    {item.level}

    -
    -
    - ) - }) - } -
    -
    :"" - } -
    - } -
    - ) - - - } + constructor(props) { + super(props) + this.state = { + + TPMRightSection: false, + clickNewsubscripttype: false + } + } + + // componentDidMount() { + // let id=this.props.match.params.shixunId; + // + // let shixunsDetailsURL=`/shixuns/`+id+`/show_right.json`; + // + // axios.get(shixunsDetailsURL).then((response)=> { + // if(response.status===200){ + // this.setState({ + // TPMRightSectionData: response.data + // }); + // } + // }).catch((error)=>{ + // console.log(error) + // }); + // } + + // shouldComponentUpdate(nextProps, nextState) { + // return nextProps.TPMRightSectionData !== this.state.TPMRightSectionData + // } + clickNewsubscript = (val) => { + if (val === 0) { + this.setState({ + TPMRightSection: true, + clickNewsubscripttype: true + }) + } else { + this.setState({ + TPMRightSection: false, + clickNewsubscripttype: false + }) + } + + } + + render() { + let {TPMRightSection, clickNewsubscripttype} = this.state; + let {TPMRightSectionData} = this.props + let Progresssum; + if(TPMRightSectionData&&TPMRightSectionData.complete_count!=null){ + Progresssum=(parseInt(TPMRightSectionData&&TPMRightSectionData.complete_count) / parseInt(TPMRightSectionData&&TPMRightSectionData.challenge_count))*100; + } + + + return ( +
    + { + TPMRightSectionData === undefined ? "" : +
    +
    +

    创建者

    +
    + + 头像 + +
    + +
    +
    + {TPMRightSectionData === undefined ? "" : TPMRightSectionData.creator === undefined ? "" : TPMRightSectionData.creator.name} +
    +
    + {TPMRightSectionData.user_shixuns_count} +
    +
    +
    + 发布实训项目 + {/*粉丝 {TPMRightSectionData.fans_count}*/} + {/* 取消关注 */} +
    + +
    +
    +
    + + {this.props&&this.props.status>1?
    +
    + 学习统计 + + 已完成 {TPMRightSectionData&&TPMRightSectionData.complete_count===null?0:TPMRightSectionData&&TPMRightSectionData.complete_count} 个 / 共 {TPMRightSectionData&&TPMRightSectionData.challenge_count} 关 + + +
    + + +
    :""} + { + TPMRightSectionData === undefined ? "" : TPMRightSectionData.tags === undefined ? "" : TPMRightSectionData.tags.length === 0 ? "" : +
    +

    + 技能标签 + + 已获得 {TPMRightSectionData&&TPMRightSectionData.user_tag_count} 个 / 共 {TPMRightSectionData&&TPMRightSectionData.tag_count} 个 + + +

    +
    +
    + {TPMRightSectionData.tags.map((item, key) => { + return ( + {item.tag_name} + ) + }) + } +
    +
    + + +
    15 && clickNewsubscripttype === false ? "textcenter color-grey-9 mt20 rightjinengs" : "none"} + onClick={() => this.clickNewsubscript(0)}> + +
    +

    展开全部

    +

    +
    +
    +
    + + +
    this.clickNewsubscript(1)}> + +
    +

    +

    收起全部

    +
    +
    +
    + +
    + + + } + + +
    +

    所属课程

    +
    + { + TPMRightSectionData === undefined ? "" : TPMRightSectionData.paths === undefined ? "" : TPMRightSectionData.paths.map((i, k) => { + + return ( + +
    + + + 实训 + + +
    + {i.name} +

    + + + {i.stages_count} + + + {/**/} + {/*{i.score_count}*/} + {/**/} + + + {i.members_count} + + +

    +
    + +
    + + ) + }) + } +
    +
    + + {TPMRightSectionData === undefined ? "" : TPMRightSectionData.paths === undefined ? "" : TPMRightSectionData.paths.length === 0 ? "" : + this.props.user && this.props.user.main_site === true ?
    +

    推荐实训

    +
    + { + TPMRightSectionData === undefined ? "" : TPMRightSectionData.recommands === undefined ? "" : TPMRightSectionData.recommands.map((item, key) => { + return ( +
    + + 69?1526971094 + +
    + + {item.name} + +

    + {item.stu_num} 人学习 +

    +

    {item.level}

    +
    +
    + ) + }) + } +
    +
    : "" + } +
    + } +
    + ) + + + } } export default TPMRightSection; diff --git a/public/react/src/modules/tpm/component/TPMright.css b/public/react/src/modules/tpm/component/TPMright.css index c664d75c4..0d6b306fd 100644 --- a/public/react/src/modules/tpm/component/TPMright.css +++ b/public/react/src/modules/tpm/component/TPMright.css @@ -1,5 +1,5 @@ /*b新标签*/ -.newedu-filter-btn{ +.newedu-filter-btn { display: block; float: left; padding: 0 9px; @@ -11,24 +11,28 @@ margin-right: 10px; margin-bottom: 9px; } -.newedbox{ + +.newedbox { /*flex-wrap: wrap;*/ /*display: -webkit-flex; !* Safari *!*/ /*display: flex;*/ width: 360px; - position:relative; + position: relative; overflow: hidden; } -.newsubscript{ + +.newsubscript { position: absolute; right: 23px; bottom: 16px; cursor: pointer; } -.newsubscript:hover{ - color:deepskyblue; + +.newsubscript:hover { + color: deepskyblue; } -.edu-filter-btn29BD8B{ + +.edu-filter-btn29BD8B { display: block; float: left; padding: 0 9px; @@ -40,40 +44,91 @@ margin-right: 10px; margin-bottom: 9px; } -.relative{ - position:relative; + +.relative { + position: relative; } -.newedboxheight{ + +.newedboxheight { max-height: 177px; overflow-y: hidden; } -.newminheight{ + +.newminheight { /*max-height: 670px;*/ max-height: 300px; overflow-y: auto; } -.delSubentry{ - font-size:7px; - font-family:MicrosoftYaHei; - font-weight:400; - color:rgba(76,172,255,1); - line-height:9px; +.delSubentry { + font-size: 7px; + font-family: MicrosoftYaHei; + font-weight: 400; + color: rgba(76, 172, 255, 1); + line-height: 9px; cursor: pointer; } -.operationalter .delSubentry{ - font-size:15px !important; + +.operationalter .delSubentry { + font-size: 15px !important; line-height: 25px; } + /*临时的tpi关闭按钮样式*/ .headerRight a { color: #1a3f5f; } + /*实训做成弹窗a标签样式调整*/ -.-task-list-title a:link, .-task-list-title a:visited {color: #bcc6cd;} -.-task-list-title a:hover{ +.-task-list-title a:link, .-task-list-title a:visited { + color: #bcc6cd; +} + +.-task-list-title a:hover { color: #459be5; } -.headerLeft .-header-right{ + +.headerLeft .-header-right { height: 32px; +} + +.creatorname { + font-size: 16px; + color: rgba(51, 51, 51, 1); +} + +.creatornamelist { + -o-text-overflow: ellipsis; + text-overflow: ellipsis; + white-space: nowrap; + cursor: default; +} + +.width80center { + width: 80px; + text-align: center; +} + +.pd302020zuoze { + padding: 30px 20px 20px; +} + +.textcenter{ + text-align: center; +} +.padd252020px{ + padding: 25px 20px 15px; +} + +.rightjinengs{ + height: 35px; + margin-top: 20px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.borderbottomf4{ + border-bottom:1px solid #F4F4F4; } \ No newline at end of file diff --git a/public/react/src/modules/tpm/jupyter/index.js b/public/react/src/modules/tpm/jupyter/index.js new file mode 100644 index 000000000..cd947df85 --- /dev/null +++ b/public/react/src/modules/tpm/jupyter/index.js @@ -0,0 +1,230 @@ +/* + * @Description: jupyter tpi + * @Author: tangjiang + * @Github: + * @Date: 2019-12-11 08:35:23 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-13 15:25:50 + */ +import './index.scss'; +import React, { useEffect, useState } from 'react'; +import SplitPane from 'react-split-pane'; +import { Button, Modal } from 'antd'; +import { + connect +} from 'react-redux'; +import UserInfo from '../../developer/components/userInfo'; +import actions from '../../../redux/actions'; +import LeftPane from './leftPane'; +import RightPane from './rightPane'; +function JupyterTPI (props) { + + // 获取 identifier 值 + const { + match: { + params = {} + }, + url, + loading, // 保存按钮状态 + total, + pagination, + dataSets, // 数据集 + jupyter_info, + getJupyterInfo, + syncJupyterCode, + jupyter_tpi_url_state, + getJupyterTpiDataSet, + getJupyterTpiUrl, + saveJupyterTpi, + changeLoadingState, + changeGetJupyterUrlState, + jupyter_identifier, + changeCurrentPage + } = props; + + const {identifier} = params; + const [userInfo, setUserInfo] = useState({}); + const [jupyterInfo, setJupyterInfo] = useState({}); + const [updateTip, setUpdateTip] = useState(true); + const [myIdentifier, setMyIdentifier] = useState(''); + useEffect(() => { + /* 先调用 jupyter的 TPI 接口, + * 获取 用户信息, + * 实训的 identifier, 状态, 名称, 是否被修改等信息 + */ + getJupyterInfo(identifier); + }, [identifier]); + + useEffect(() => { + // 设置jupyter信息 + setJupyterInfo(jupyter_info || {}); + const {user, tpm_modified, myshixun_identifier} = jupyter_info; + if (user) { + setUserInfo(user); + } + + if (myshixun_identifier) { + setMyIdentifier(myshixun_identifier); + } + + // 同步代码 + if (tpm_modified && updateTip && myshixun_identifier) { + setUpdateTip(false); + Modal.confirm({ + title: '更新通知', + content: (
    +

    关卡任务的代码文件有更新啦

    +

    更新操作将保留已完成的评测记录和成绩

    +

    还未完成评测的任务代码,请自行保存

    +
    ), + okText: '确定', + cancelText: '取消', + onOk () { + syncJupyterCode(myshixun_identifier, '同步成功'); + } + }) + } + }, [props]); + + // 重置实训 + const handleClickResetTpi = () => { + Modal.confirm({ + title: '重置实训', + content: ( +

    + 你在本文件中修改的内容将丢失,
    + 是否确定重新加载初始代码? +

    + ), + okText: '确定', + cancelText: '取消', + onOk () { + console.log('调用重置代码....', myIdentifier); + if (myIdentifier) { + syncJupyterCode(myIdentifier, '重置成功'); + } + } + }) + } + + // 退出实训 + const handleClickQuitTpi = () => { + // console.log(jupyterInfo); + const { identifier } = jupyterInfo; + if (!identifier) return; + props.history.push(`/shixuns/${identifier}/challenges`); + } + + // 重新获取 jupyter url + const handleOnReloadUrl = (id) => { + // console.log('jupyter 信息: ', jupyterInfo); + // 改变加载状态值 + changeGetJupyterUrlState(-1); + getJupyterTpiUrl({identifier: myIdentifier}); + } + + // 保存代码 + const handleOnSave = () => { + // 改变按钮状态 + changeLoadingState(true); + saveJupyterTpi(); + } + + // 分页信息改变时 + const handlePageChange = (current) => { + // 改变当前页 + changeCurrentPage(current); + // 分页查找数据 + getJupyterTpiDataSet(jupyter_identifier); + } + + return ( +
    +
    + +

    + {jupyterInfo.name} + +

    +

    + {/* sync | poweroff */} + + +

    +
    +
    + +
    + +
    + + +
    + + +
    +
    + ); +} + +const mapStateToProps = (state) => { + const { + jupyter_info, + jupyter_tpi_url, + jupyter_data_set, + jupyter_tpi_url_state, + jupyter_data_set_count, + jupyter_pagination, + jupyter_identifier + } = state.jupyterReducer; + const { loading } = state.commonReducer; + return { + loading, + jupyter_info, + url: jupyter_tpi_url, + dataSets: jupyter_data_set, + jupyter_tpi_url_state, + total: jupyter_data_set_count, + pagination: jupyter_pagination, + jupyter_identifier + }; +} + +const mapDispatchToProps = (dispatch) => ({ + changeGetJupyterUrlState: (status) => dispatch(actions.changeGetJupyterUrlState(status)), + getJupyterInfo: (identifier) => dispatch(actions.getJupyterInfo(identifier)), + // 重置代码 + syncJupyterCode: (identifier, msg) => dispatch(actions.syncJupyterCode(identifier, msg)), + getJupyterTpiDataSet: (identifier, current) => dispatch(actions.getJupyterTpiDataSet(identifier, current)), + getJupyterTpiUrl: (identifier) => dispatch(actions.getJupyterTpiUrl(identifier)), + saveJupyterTpi: () => dispatch(actions.saveJupyterTpi()), + changeLoadingState: (flag) => dispatch(actions.changeLoadingState(flag)), + changeCurrentPage: (current) => dispatch(actions.changeCurrentPage(current)) +}); + +export default connect( + mapStateToProps, + mapDispatchToProps +)(JupyterTPI); diff --git a/public/react/src/modules/tpm/jupyter/index.scss b/public/react/src/modules/tpm/jupyter/index.scss new file mode 100644 index 000000000..430bb1c6e --- /dev/null +++ b/public/react/src/modules/tpm/jupyter/index.scss @@ -0,0 +1,105 @@ +.Resizer { + background: #000; + opacity: 0.2; + z-index: 1; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + -moz-background-clip: padding; + -webkit-background-clip: padding; + background-clip: padding-box; +} + +.Resizer:hover { + -webkit-transition: all 2s ease; + transition: all 2s ease; +} + +.Resizer.horizontal { + height: 11px; + margin: -5px 0; + border-top: 5px solid rgba(255, 255, 255, 0); + border-bottom: 5px solid rgba(255, 255, 255, 0); + cursor: row-resize; + width: 100%; +} + +.Resizer.horizontal:hover { + border-top: 5px solid rgba(0, 0, 0, 0.5); + border-bottom: 5px solid rgba(0, 0, 0, 0.5); +} + +.Resizer.vertical { + width: 11px; + margin: 0 -5px; + border-left: 5px solid rgba(255, 255, 255, 0); + border-right: 5px solid rgba(255, 255, 255, 0); + cursor: col-resize; +} + +.Resizer.vertical:hover { + border-left: 5px solid rgba(0, 0, 0, 0.5); + border-right: 5px solid rgba(0, 0, 0, 0.5); +} +.Resizer.disabled { + cursor: not-allowed; +} +.Resizer.disabled:hover { + border-color: transparent; +} + +.jupyter_area{ + + .jupyter_header{ + position: relative; + height: 60px; + line-height: 60px; + background-color: #070F1A; + padding-left: 30px; + .jupyter_title{ + display: flex; + flex-direction: column; + // justify-content: space-around; + align-items: center; + height: 100%; + color: #fff; + .title_desc{ + margin-top: 12px; + font-size: 16px; + } + .title_time{ + font-size: 12px; + } + // text-align: center; + } + + .jupyter_btn{ + position: absolute; + right: 10px; + top: 14px; + + .btn_common{ + color: #888; + } + .btn_common:hover{ + // background-color: #29BD8B; + // color: #29BD8B; + color: #1890ff; + } + } + } + + + .jupyter_ctx{ + position: relative; + height: calc(100vh - 60px); + } + + .update_notice{ + text-align: center; + .update_txt{ + line-height: 18px; + font-size: 14px; + } + } +} \ No newline at end of file diff --git a/public/react/src/modules/tpm/jupyter/leftPane/index.js b/public/react/src/modules/tpm/jupyter/leftPane/index.js new file mode 100644 index 000000000..d87be9064 --- /dev/null +++ b/public/react/src/modules/tpm/jupyter/leftPane/index.js @@ -0,0 +1,88 @@ +/* + * @Description: + * @Author: tangjiang + * @Github: + * @Date: 2019-12-12 10:34:03 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-13 22:46:51 + */ +import './index.scss'; +import React, { useState, useEffect } from 'react'; +import {Icon, Empty, Pagination, Tooltip } from 'antd'; +import MyIcon from '../../../../common/components/MyIcon'; + +function LeftPane (props) { + + // 获取数据集 + const { + dataSets = [], + total, + pagination, + onPageChange + } = props; + + const emptyCtx = ( +
    + +
    + ); + + // const listCtx = ; + const [renderCtx, setRenderCtx] = useState(() => (emptyCtx)); + + useEffect(() => { + if (dataSets.length > 0) { + console.log('数据集的个数: ', dataSets.length); + const oList = dataSets.map((item, i) => { + return ( +
  • + + + {item.title} + +
  • + ); + }); + + const oUl = ( +
      + { oList } +
    + ); + + setRenderCtx(oUl); + } + }, [props]); + + // 分页处理 + const handleChangePage = (page) => { + // console.log(page, pageSize); + // setCurrent(page); + onPageChange && onPageChange(page); + } + return ( +
    +

    + 数据集 + {/* 数据集 */} +

    + { renderCtx } +
    + +
    + +
    + ) +} + +export default LeftPane; \ No newline at end of file diff --git a/public/react/src/modules/tpm/jupyter/leftPane/index.scss b/public/react/src/modules/tpm/jupyter/leftPane/index.scss new file mode 100644 index 000000000..9c95b1aae --- /dev/null +++ b/public/react/src/modules/tpm/jupyter/leftPane/index.scss @@ -0,0 +1,72 @@ +.jupyter_data_sets_area{ + height: 100%; + background: #fff; + .jupyter_h2_title{ + height: 44px; + line-height: 44px; + // background-color: #EEEEEE; + background: #fff; + padding: 0 30px; + font-size: 16px; + // box-size: border-box; + box-sizing: border-box; + border-bottom: 1px solid rgba(238,238,238,1); + .jupyter_data_icon{ + // color: #7286ff; + color: #1890ff; + font-size: 24px; + position: relative; + top: 2px; + transform: scale(1.5); + margin-right: 5px; + } + } + + .jupyter_data_list, + .jupyter_empty{ + height: calc(100vh - 160px); + overflow-y: auto; + } + + .jupyter_data_list{ + .jupyter_item{ + line-height:45px; + border-bottom: 1px solid rgba(238,238,238, 1); + padding: 0 30px 0 60px; + overflow: hidden; + text-overflow:ellipsis; + white-space: nowrap; + cursor: pointer; + transition: .3s; + &:hover{ + background-color: rgba(235, 235, 235, .3); + } + .jupyter_icon{ + color: rgb(74, 188, 125); + font-size: 16px; + transform: scale(1.2); + margin-right: 5px; + } + .jupyter_name{ + color: #000; + font-size: 16px; + } + } + } + + .jupyter_empty{ + display: flex; + align-items: center; + justify-content: center; + width: 100%; + } + + .jupyter_pagination{ + display: flex; + justify-content: center; + align-items: center; + height: 56px; + box-sizing: border-box; + border-top: 1px solid rgba(238,238,238,1); + } +} \ No newline at end of file diff --git a/public/react/src/modules/tpm/jupyter/rightPane/index.js b/public/react/src/modules/tpm/jupyter/rightPane/index.js new file mode 100644 index 000000000..020639abc --- /dev/null +++ b/public/react/src/modules/tpm/jupyter/rightPane/index.js @@ -0,0 +1,91 @@ +/* + * @Description: + * @Author: tangjiang + * @Github: + * @Date: 2019-12-12 15:04:20 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-13 11:25:22 + */ +import './index.scss'; +import React, { useEffect, useState } from 'react'; +import { Spin, Button } from 'antd'; +function RightPane (props) { + const { + status, + url, + onReloadUrl, + onSave, + loading + } = props; + + const [renderCtx, setRenderCtx] = useState(() => loadInit); + // 重新获取 url + const handleClickReload = () => { + onReloadUrl && onReloadUrl(); + } + + const loadInit = ( +
    + +
    + ); + + const loadError = ( +
    + +

    + 实训加载失败, + 重新加载 +

    +
    + ); + + // 保存 + const handleClickSubmit = () => { + console.log('调用了保存接口....'); + onSave && onSave(); + } + + useEffect(() => { + if (status === -1) { + setRenderCtx(() => loadInit); + } else if (status === 0 && url) { + setRenderCtx(() => ( + +
    +
    + +
    +
    + +
    +
    + + )); + } else { + setRenderCtx(() => loadError); + } + }, [status, url, loading]); + + return ( +
    + { renderCtx } +
    + ) +} + +export default RightPane; + diff --git a/public/react/src/modules/tpm/jupyter/rightPane/index.scss b/public/react/src/modules/tpm/jupyter/rightPane/index.scss new file mode 100644 index 000000000..4facded6b --- /dev/null +++ b/public/react/src/modules/tpm/jupyter/rightPane/index.scss @@ -0,0 +1,74 @@ +.jupyter_right_pane_area{ + position: relative; + height: calc(100vh - 60px); + // background: pink; + + .jupyter_load_url_error, + .jupyter_loading_init{ + display: flex; + position: relative; + align-items: center; + justify-content: center; + height: 100%; + &::before{ + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + content: ''; + + } + } + + .jupyter_loading_init{ + &::before{ + background-color: rgba(0,0,0,.2); + } + } + + .jupyter_load_url_error{ + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + // &::before{ + // background-color: rgba(0,0,0,.2); + // } + .jupyter_error_txt{ + position: relative; + z-index: 1; + font-size: 12px; + .jupyter_reload{ + cursor: pointer; + color: #1890ff; + } + } + + .icon-error{ + position: relative; + color: #DCE0E6; + transform: scale(5); + top: -35px; + } + } + + .jupyter_result{ + height: 100%; + .jupyter_iframe{ + height: calc(100% - 56px); + // background: pink; + .jupyter_iframe_style{ + border: none; + outline: none; + } + } + .jupyter_submit{ + display: flex; + align-items: center; + height: 56px; + justify-content: flex-end; + padding-right: 30px; + } + } +} \ No newline at end of file diff --git a/public/react/src/modules/tpm/newshixuns/Newshixuns.js b/public/react/src/modules/tpm/newshixuns/Newshixuns.js index 1eaee9ad6..cc537496a 100644 --- a/public/react/src/modules/tpm/newshixuns/Newshixuns.js +++ b/public/react/src/modules/tpm/newshixuns/Newshixuns.js @@ -2,1351 +2,746 @@ import React, {Component} from 'react'; import {TPMIndexHOC} from '../TPMIndexHOC'; -import {SnackbarHOC,appendFileSizeToUploadFileAll, getUploadActionUrl} from 'educoder'; +import {SnackbarHOC} from 'educoder'; -import {Input, Select, Radio, Checkbox, Modal, Icon, DatePicker,Upload,Button,message,Form,notification,Tooltip} from 'antd'; - -// import "antd/dist/antd.css"; - -import locale from 'antd/lib/date-picker/locale/zh_CN'; +import {Select, Radio, Input, Modal, Button, Form, Tooltip, Upload, Icon, notification} from 'antd'; import axios from 'axios'; -import './css/Newshixuns.css'; - -import {getUrl} from 'educoder' +import {getUploadActionUrl} from 'educoder'; -import moment from 'moment'; - -let path = getUrl("/editormd/lib/") - -const $ = window.$; +import './css/Newshixuns.css'; -let timeout; +import TPMMDEditor from "../challengesnew/TPMMDEditor"; -let currentValue; +import Bottomsubmit from "../../modals/Bottomsubmit"; const Option = Select.Option; -const RadioGroup = Radio.Group; -const confirm = Modal.confirm; - - -// 处理整点 半点 -// 取传入时间往后的第一个半点 -export function handleDateStrings(dateString) { - if (!dateString) return dateString; - const ar = dateString.split(':') - if (ar[1] == '00' || ar[1] == '30') { - return dateString - } - const miniute = parseInt(ar[1]); - if (miniute < 30 || miniute == 60) { - return [ar[0], '30'].join(':') - } - if (miniute < 60) { - // 加一个小时 - const tempStr = [ar[0], '00'].join(':'); - const format = "YYYY-MM-DD HH:mm"; - const _moment = moment(tempStr, format) - _moment.add(1, 'hours') - return _moment.format(format) - } - - return dateString -} - - - -// 恢复数据 -function md_rec_data(k, mdu, id, editor) { - if (window.sessionStorage.getItem(k + mdu) !== null) { - editor.setValue(window.sessionStorage.getItem(k + mdu)); - md_clear_data(k, mdu, id); - } -} - -// 保存数据 -function md_add_data(k, mdu, d) { - window.sessionStorage.setItem(k + mdu, d); -} - -// 清空保存的数据 -function md_clear_data(k, mdu, id) { - window.sessionStorage.removeItem(k + mdu); - var id1 = "#e_tip_" + id; - var id2 = "#e_tips_" + id; - if (k == 'content') { - $(id2).html(""); - } else { - $(id1).html(""); - } -} - -function md_elocalStorage(editor, mdu, id) { - if (window.sessionStorage) { - var oc = window.sessionStorage.getItem('content' + mdu); - if (oc !== null) { - $("#e_tips_" + id).data('editor', editor); - var h = '您上次有已保存的数据,是否恢复 ? / 不恢复'; - $("#e_tips_" + id).html(h); - } - setInterval(function () { - var d = new Date(); - var h = d.getHours(); - var m = d.getMinutes(); - var s = d.getSeconds(); - h = h < 10 ? '0' + h : h; - m = m < 10 ? '0' + m : m; - s = s < 10 ? '0' + s : s; - if (editor.getValue().trim() != "") { - md_add_data("content", mdu, editor.getValue()); - var id1 = "#e_tip_" + id; - var id2 = "#e_tips_" + id; - - $(id1).html(" 数据已于 " + h + ':' + m + ':' + s + " 保存 "); - $(id2).html(""); - } - }, 10000); - - } else { - $("#e_tip_" + id).after('您的浏览器不支持localStorage.无法开启自动保存草稿服务,请升级浏览器!'); - } -} - - -function create_editorMD(id, width, high, placeholder, imageUrl, callback) { - var editorName = window.editormd(id, { - width: width, - height: high, - path: path, // "/editormd/lib/" - - syncScrolling: "single", - tex: true, - tocm: true, - emoji: true, - taskList: true, - codeFold: true, - searchReplace: true, - htmlDecode: "style,script,iframe", - sequenceDiagram: true, - autoFocus: false, - toolbarIcons: function () { - // Or return editormd.toolbarModes[name]; // full, simple, mini - // Using "||" set icons align right. - return ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "|", "testIcon", "testIcon1", '|', "image", "table", '|', "watch", "clear"] - }, - toolbarCustomIcons: { - testIcon: "
    ", - testIcon1: "
    " - }, - //这个配置在simple.html中并没有,但是为了能够提交表单,使用这个配置可以让构造出来的HTML代码直接在第二个隐藏的textarea域中,方便post提交表单。 - saveHTMLToTextarea: true, - // 用于增加自定义工具栏的功能,可以直接插入HTML标签,不使用默认的元素创建图标 - dialogMaskOpacity: 0.6, - placeholder: placeholder, - imageUpload: true, - imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp", "JPG", "JPEG", "GIF", "PNG", "BMP", "WEBP"], - imageUploadURL: imageUrl,//url - onload: function () { - // this.previewing(); - $("#" + id + " [type=\"latex\"]").bind("click", function () { - editorName.cm.replaceSelection("```latex"); - editorName.cm.replaceSelection("\n"); - editorName.cm.replaceSelection("\n"); - editorName.cm.replaceSelection("```"); - var __Cursor = editorName.cm.getDoc().getCursor(); - editorName.cm.setCursor(__Cursor.line - 1, 0); - }); - - $("#" + id + " [type=\"inline\"]").bind("click", function () { - editorName.cm.replaceSelection("`$$$$`"); - var __Cursor = editorName.cm.getDoc().getCursor(); - editorName.cm.setCursor(__Cursor.line, __Cursor.ch - 3); - editorName.cm.focus(); - }); - $("[type=\"inline\"]").attr("title", "行内公式"); - $("[type=\"latex\"]").attr("title", "多行公式"); - - md_elocalStorage(editorName, `memoNew_${id}`, "memoNew"); +class Newshixuns extends Component { + constructor(props) { + super(props) + this.contentMdRef = React.createRef(); + this.state = { + shixunName: undefined, + NAME_COUNT: 60, + is_jupyter: "1", + newshixunlist: undefined, + language: undefined, + runtime: undefined, + run_method: undefined, + postapplyvisible: undefined, + fileList: [], + Radiovalue:"1" + } + } + + + componentDidMount() { + this.props.form.setFieldsValue({ + is_jupyter: `1`, + }); - callback && callback() + let newshixunUrl = `/shixuns/new.json`; + axios.get(newshixunUrl).then((response) => { + if (response.status === 200) { + if (response.data.message === undefined) { + this.setState({ + newshixunlist: response.data + }); + this.contentMdRef.current.setValue(!response.data.sample[0][1] ? "" : response.data.sample[0][1]); } + } + }).catch((error) => { + console.log(error) }); - return editorName; -} - -function range(start, end) { - const result = []; - for (let i = start; i < end; i++) { - result.push(i); - } - return result; -} -function disabledDateTime() { - return { - // disabledHours: () => range(0, 24).splice(4, 20), - disabledMinutes: () => range(1, 30).concat(range(31, 60)), - // disabledSeconds: () => [0, 60], - }; -} -function disabledDate(current) { - return current && current < moment().endOf('day').subtract(1, 'days'); -} -class Newshixuns extends Component { - constructor(props) { - super(props) - this.state = { - fileList: [], - newshixunlist: undefined, - departmentslist: undefined, - name: "", - main_type: "", - small_type: "", - trainee: "", - webssh: 0, - use_scope: 0, - can_copy: "", - scope_partment: undefined, - vnc: "", - scopetype: false, - postapplyvisible: false, - sendsure_applyvalue: undefined, - postapplytitle: false, - shixun_nametype: false, - main_types: false, - trainee_types: false, - SelectTheCommandtype: false, - opers: false, - operss: false, - TimePickervalue: "", - opensmail: false, - onSearchvalue: "", - scope_partmenttype: false, - languagewrite: undefined, - systemenvironment:undefined, - testcoderunmode:undefined, - file:undefined, - deleteisnot:true, - languagewritetype:false, - systemenvironmenttype:false, - testcoderunmodetype:false, - attachmentidstype:false, - datalisttype:false, - bottonloading:false + let departmentsUrl = `/shixuns/departments.json`; + axios.get(departmentsUrl).then((response) => { + if (response.status === 200) { + if (response.data.message === undefined) { + this.setState({ + departmentslist: response.data.shools_name + }); } - } + } + }).catch((error) => { + console.log(error) + }); - initMD(initValue) { - this.contentChanged = false; - const placeholder = ""; - // amp; - // 编辑时要传memoId - const imageUrl = `/api/attachments.json`; - // 创建editorMd - - const taskpass_editormd = create_editorMD("memoMD", '100%', 400, placeholder, imageUrl, () => { - setTimeout(() => { - taskpass_editormd.resize() - taskpass_editormd.cm && taskpass_editormd.cm.refresh() - }, 500) - - if (initValue) { - taskpass_editormd.setValue(initValue) - } - taskpass_editormd.cm.on("change", (_cm, changeObj) => { - // console.log('....contentChanged') - this.contentChanged = true; - }) - }); - this.taskpass_editormd = taskpass_editormd; - window.taskpass_editormd = taskpass_editormd; - } + } - componentDidMount() { - let newshixunUrl = `/shixuns/new.json`; - axios.get(newshixunUrl).then((response) => { - if (response.status === 200) { - if (response.data.message===undefined) { - this.setState({ - newshixunlist: response.data - }); - this.initMD(response.data.sample[0][1]); - } + shixunNameInput = (e) => { + this.setState({ + shixunName: e.target.value + }) - } - }).catch((error) => { - console.log(error) - }); + this.props.form.setFieldsValue({ + name: e.target.value, + }); + } - let departmentsUrl = `/shixuns/departments.json`; - axios.get(departmentsUrl).then((response) => { - if (response.status === 200) { - if (response.data.message===undefined) { - this.setState({ - departmentslist: response.data.shools_name - }); - } + RadiovalueonChange = (e) => { + this.setState({ + Radiovalue: e.target.value, + }); + this.props.form.setFieldsValue({ + is_jupyter: e.target.value, + }); + }; + + handleSubmit = (e) => { + this.setState({ + loading: true + }) + const mdContnet = this.contentMdRef.current.getValue().trim(); + this.props.form.validateFieldsAndScroll((err, values) => { + debugger + if (!err) { + console.log('Received values of form: ', values); + + let Url = `/shixuns.json`; + axios.post(Url, { + description: mdContnet, + main_type: values.main_type, + sub_type: values.sub_type, + shixun: { + name: values.name, + trainee: values.select, + is_jupyter: values.is_jupyter === "2" ? true : false, } + } + ).then((response) => { + if (response.status === 200) { + window.location.href = "/shixuns/" + response.data.shixun_identifier + "/challenges"; + // window.open("/shixuns/"+response.data.shixun_identifier+"/challenges"); + } else { + this.setState({ + loading: false + }) + } }).catch((error) => { - console.log(error) - }); - } - - setlanguagewrite = (e)=>{ - this.setState({ - languagewrite: e.target.value + this.setState({ + loading: false + }) }) - } - - setsystemenvironment = (e) => { - this.setState({ - systemenvironment: e.target.value - }) - } - settestcoderunmode = (e) => { + }else{ this.setState({ - testcoderunmode: e.target.value + loading: false }) + } + }); + }; + Selectthestudent = (value) => { + this.props.form.setFieldsValue({ + select: value, + }); + } + main_type = (value, e) => { + this.props.form.setFieldsValue({ + main_type: value, + }); + this.setState({ + mainvalues: e.props.name + }) + } + + sub_type = (value, e) => { + this.props.form.setFieldsValue({ + sub_type: value, + }); + let newlist = "" + e.map((item, key) => { + if (item.props.name != "") { + newlist = newlist + `${item.props.name}` + } + }) + this.setState({ + subvalues: newlist + }) + } + + post_apply = () => { + this.setState({ + postapplyvisible: true + }) + } + + + sendhideModaly = () => { + this.setState({ + postapplyvisible: false, + }) + if (this.state.file !== undefined) { + // this.deleteAttachment(this.state.file); + this.setState({ + file: undefined, + deleteisnot: true, + language: "", + runtime: "", + run_method: "", + fileList: [] + }) + } else { + this.setState({ + file: undefined, + deleteisnot: true, + language: "", + runtime: "", + run_method: "", + fileList: [] + }) } - shixunname = (e) => { - this.setState({ - name: e.target.value, - shixun_nametype: false - }); - } + } - bigClass = (value) => { - this.setState({ - main_type: value - }) - } - littleClass = (value) => { - this.setState({ - small_type: value - }) - } + sendsure_apply = () => { + let {language, runtime, run_method} = this.state; - Selectthestudent = (value) => { - this.setState({ - trainee: value - }) + if (!language || language === "") { + // this.props.showNotification(`请填写该镜像是基于什么语言`); + this.setState({ + languagewritetype: true + }) + return } + if (!runtime || runtime === "") { + // this.props.showNotification(`请填写该镜像是基于什么语言系统环境`); + this.setState({ + systemenvironmenttype: true + }) + return; - SelectTheCommand = (e) => { - this.setState({ - webssh: e.target.value, - }); - - if (e.target.value === 2) { - this.setState({ - SelectTheCommandtype: true, - multi_webssh: false - }); - } else { - this.setState({ - SelectTheCommandtype: false, - multi_webssh: false - }); - } } - - Selectpublic = (e) => { - this.setState({ - scopetype: false, - use_scope: e.target.value, - }); - if (e.target.value === 1) { - this.setState({ - scopetype: true - }); - } - - } - - Teacherscopy = (e) => { - let sum = "" - if (e.target.checked === false) { - sum = 0 - } else if (e.target.checked === true) { - sum = 1 - } - this.setState({ - can_copy: sum, - }); - } - - TeachersUbuntu = (e) => { - let sum = "" - if (e.target.checked === false) { - sum = 0 - } else if (e.target.checked === true) { - sum = 1 - } - this.setState({ - vnc: sum, - }); + if (!run_method || run_method === "") { + // this.props.showNotification(`请填写该镜像中测试代码运行方式`); + this.setState({ + testcoderunmodetype: true + }) + return; } - adduse_scopeinput = () => { - let {scope_partment} = this.state; - let array = scope_partment; - let newarray = "" - array.push(newarray) - this.setState({ - scope_partment: array, - }); + var attachment_ids = undefined; + if (this.state.fileList) { + attachment_ids = this.state.fileList.map(item => { + return item.response ? item.response.id : item.id + }) } - shixunScopeInput = (e, id) => { - let types=false - let {scope_partment} = this.state; - let datalist = scope_partment; - if (datalist === undefined) { - datalist = [] - } - - datalist.map((item,key)=>{ - if(e===item){ - types=true - this.setState({ - datalisttype:true - }) - return - } - }) - - if(types===false){ - datalist.push(e) - this.setState({ - scope_partment: datalist, - onSearchvalue: "" - }); - } - - + if (attachment_ids === undefined || attachment_ids.length === 0) { + this.setState({ + attachmentidstype: true + }) + return; } - deleteScopeInput = (key) => { - let {scope_partment} = this.state; - let datalist = scope_partment; - datalist.splice(key, 1); - this.setState({ - scope_partment: datalist - }); + var data = { + language: language, + runtime: runtime, + run_method: run_method, + attachment_id: attachment_ids[0], } + var url = `/shixuns/apply_shixun_mirror.json`; + axios.post(url, data + ).then((response) => { - //提交数据 - submit_new_shixun = () => { - const mdVal = this.taskpass_editormd.getValue(); - let {can_copy, main_type, name, scope_partment, small_type, trainee, use_scope, vnc, webssh, multi_webssh, TimePickervalue} = this.state; - let Url = `/shixuns.json` - if (name === "") { - this.setState({ - shixun_nametype: true - }) - this.props.showSnackbar("实训名称为空"); - $('html').animate({ - scrollTop: 10 - }, 1000); - return - } - if (main_type === "") { - this.setState({ - main_types: true - }) - $('html').animate({ - scrollTop: 700 - }, 1000); - this.props.showSnackbar("请选择技术平台大类别"); - - return - } + try { + if (response.data) { - if (use_scope === 1) { - if (scope_partment === undefined || scope_partment.length === 0) { - this.setState({ - scope_partmenttype: true - }) - $('html').animate({ - scrollTop: 900 - }, 1000); - this.props.showSnackbar("公开程度,指定单位为空"); - return - } - } - if (trainee === "") { + if (this.state.file !== undefined) { this.setState({ - trainee_types: true + file: undefined, + deleteisnot: true, + language: "", + runtime: "", + run_method: "", + fileList: [] }) - // $('html').animate({ - // scrollTop: 700 - // }, 1000); - this.props.showSnackbar("请选择发布信息"); - return - } - let newmulti_webssh = multi_webssh; - if (newmulti_webssh === true) { - newmulti_webssh = 1 - } else { - newmulti_webssh = "" - } - this.setState({ - bottonloading:true - }) - axios.post(Url, { - name: name, - can_copy: can_copy, - description: mdVal, - main_type: main_type, - scope_partment: scope_partment, - small_type: small_type, - trainee: trainee, - use_scope: use_scope, - vnc: vnc, - webssh: webssh, - multi_webssh: newmulti_webssh, - task_pass: 1, - opening_time: TimePickervalue - } - ).then((response) => { - if (response.status === 200) { - window.location.href = "/shixuns/" + response.data.shixun_identifier + "/challenges"; - // window.open("/shixuns/"+response.data.shixun_identifier+"/challenges"); - }else{ - this.setState({ - bottonloading:false - }) - } - }).catch((error) => { - console.log(error) - this.setState({ - bottonloading:false - }) - }) - } - - - shixunsfetch = (value, callback) => { - if (timeout) { - clearTimeout(timeout); - timeout = null; - } - currentValue = value; - - function fake() { - let departmentsUrl = `/shixuns/departments.json?q=` + currentValue; - axios.get(departmentsUrl).then((response) => { - if (response.data.message===undefined) { - callback(response.data.shools_name); - } - }).catch((error) => { - console.log(error) - }); - } - - timeout = setTimeout(fake, 300); - } - - shixunHandleSearch = (value) => { - - this.shixunsfetch(value, departmentslist => this.setState({departmentslist})); - - this.setState({ - onSearchvalue: "" - }) - } - - post_apply = () => { - this.setState({ - postapplyvisible: true - }) - } - sendsure_apply = () => { - let {languagewrite,systemenvironment,testcoderunmode} = this.state; - // console.log("点击确定") - // console.log("languagewrite"+languagewrite); - // console.log("systemenvironment"+systemenvironment); - // console.log("testcoderunmode"+testcoderunmode); - - // let attachment_ids = undefined - // if (this.state.fileList) { - // attachment_ids = this.state.fileList.map(item => { - // return item.response ? item.response.id : item.id - // }) - // } - if(languagewrite === undefined || languagewrite === "" ){ - // this.props.showNotification(`请填写该镜像是基于什么语言`); + } else { this.setState({ - languagewritetype:true - }) - return - } - if(systemenvironment === undefined || systemenvironment === ""){ - // this.props.showNotification(`请填写该镜像是基于什么语言系统环境`); - this.setState({ - systemenvironmenttype:true - }) - return; - - } - if(testcoderunmode === undefined || testcoderunmode === "") { - // this.props.showNotification(`请填写该镜像中测试代码运行方式`); - this.setState({ - testcoderunmodetype:true - }) - return; - } - var attachment_ids=undefined; - if (this.state.fileList) { - attachment_ids = this.state.fileList.map(item => { - return item.response ? item.response.id : item.id + file: undefined, + deleteisnot: true, + language: "", + runtime: "", + run_method: "", + fileList: [] }) - } - - if( attachment_ids === undefined || attachment_ids.length===0){ - - // notification.open( - // { - // message: '提示', - // description: - // '请上传附件!', - // - // } - // ) - this.setState({ - attachmentidstype:true - }) - return; - } - // console.log("attachment_ids"+attachment_ids); - - // alert(languagewrite +" "+systemenvironment +" "+testcoderunmode + " "+attachment_ids); - - var data={ - language:languagewrite, - runtime:systemenvironment, - run_method:testcoderunmode, - attachment_id:attachment_ids[0], - } - var url =`/shixuns/apply_shixun_mirror.json`; - axios.post(url,data - ).then((response) => { + } - try { - if (response.data) { - // const { id } = response.data; - // if (id) { - if(this.state.file !== undefined){ - console.log("549"); - // this.deleteAttachment(this.state.file); - this.setState({ - file:undefined, - deleteisnot:true, - languagewrite:"", - systemenvironment:"", - testcoderunmode:"", - fileList:[] - }) - }else { - this.setState({ - file:undefined, - deleteisnot:true, - languagewrite:"", - systemenvironment:"", - testcoderunmode:"", - fileList:[] - }) - } - // this.props.showNotification('提交成功!'); - notification.open( - { - message: '提示', - description: - '提交成功!', - - } - ) - this.sendhideModaly() - // this.props.history.push(`/courses/${cid}/graduation_topics`); - // } - } - }catch (e) { + notification.open( + { + message: '提示', + description: + '新建申请已提交,请等待管理员审核。', } + ) + this.sendhideModaly() - }) - - } - sendhideModaly = () => { - this.setState({ - postapplyvisible: false, - }) - if(this.state.file !== undefined){ - console.log("580"); - // this.deleteAttachment(this.state.file); - this.setState({ - file:undefined, - deleteisnot:true, - languagewrite:"", - systemenvironment:"", - testcoderunmode:"", - fileList:[] - }) - }else { - this.setState({ - file:undefined, - deleteisnot:true, - languagewrite:"", - systemenvironment:"", - testcoderunmode:"", - fileList:[] - }) } - } - sendsure_applyvalues = (e) => { - this.setState({ - sendsure_applyvalue: e.target.value - }) - } - yeshidemodel = () => { - this.setState({ - postapplytitle: false - }) - } + } catch (e) { - SelectTheCommandonChange = (e) => { - this.setState({ - multi_webssh: e.target.checked - }) - } + } + }) - bigopen = (e) => { - this.setState({ - opers: true - }) + } + setlanguage = (e) => { + this.setState({ + language: e.target.value + }) + if (e.target.value) { + this.setState({ + languagewritetype: false + }) } - - bigopens = (e) => { - this.setState({ - opers: false, - operss: false, - opensmail: false - }) - + } + setruntime = (e) => { + this.setState({ + runtime: e.target.value + }) + if (e.target.value) { + this.setState({ + systemenvironmenttype: false + }) } - bigopensmal = (e) => { - this.setState({ - opensmail: true - }) + } + setrun_method = (e) => { + this.setState({ + run_method: e.target.value + }) + if (e.target.value) { + this.setState({ + testcoderunmodetype: false + }) } + } - sbigopen = (e) => { - this.setState({ - operss: true - }) - - } - // sbigopens=()=>{ - // this.setState({ - // operss:false - // }) - // } + // 附件相关 START + handleChange = (info) => { + if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') { + let {fileList} = this.state; - onChangeTimePicker = (value, dateString) => { + if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') { + console.log("handleChange1"); + // if(fileList.length===0){ + let fileLists = info.fileList; this.setState({ - TimePickervalue: dateString=== ""?"":moment(handleDateStrings(dateString)) - }) - } - - // 附件相关 START - handleChange = (info) => { - if(info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') { - let {fileList} = this.state; - - if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') { - console.log("handleChange1"); - // if(fileList.length===0){ - let fileLists = info.fileList; - this.setState({ - // fileList:appendFileSizeToUploadFileAll(fileList), - fileList: fileLists, - deleteisnot: false - }); - // } - } - } - } - onAttachmentRemove = (file) => { - if(!file.percent || file.percent == 100){ - confirm({ - title: '确定要删除这个附件吗?', - okText: '确定', - cancelText: '取消', - // content: 'Some descriptions', - onOk: () => { - console.log("665") - this.deleteAttachment(file) - }, - onCancel() { - console.log('Cancel'); - }, - }); - return false; - } - - } - deleteAttachment = (file) => { - console.log(file); - let id=file.response ==undefined ? file.id : file.response.id - const url = `/attachments/${id}.json` - axios.delete(url, { - }) - .then((response) => { - if (response.data) { - const { status } = response.data; - if (status == 0) { - // console.log('--- success') - - this.setState((state) => { - - const index = state.fileList.indexOf(file); - const newFileList = state.fileList.slice(); - newFileList.splice(index, 1); - return { - fileList: newFileList, - deleteisnot:true - }; - }); - } - } - }) - .catch(function (error) { - console.log(error); + // fileList:appendFileSizeToUploadFileAll(fileList), + fileList: fileLists, + deleteisnot: false + }); + // } + } + } + } + + onAttachmentRemove = (file) => { + if (!file.percent || file.percent == 100) { + Modal.confirm({ + title: '确定要删除这个附件吗?', + okText: '确定', + cancelText: '取消', + // content: 'Some descriptions', + onOk: () => { + console.log("665") + this.deleteAttachment(file) + }, + onCancel() { + console.log('Cancel'); + }, + }); + return false; + } + + } + + deleteAttachment = (file) => { + console.log(file); + let id = file.response == undefined ? file.id : file.response.id + const url = `/attachments/${id}.json` + axios.delete(url, {}) + .then((response) => { + if (response.data) { + const {status} = response.data; + if (status == 0) { + // console.log('--- success') + + this.setState((state) => { + + const index = state.fileList.indexOf(file); + const newFileList = state.fileList.slice(); + newFileList.splice(index, 1); + return { + fileList: newFileList, + deleteisnot: true + }; }); - } - - - handleSubmit=()=>{ - // console.log(this.state.languagewrite) - // console.log(this.state.systemenvironment) - // console.log(this.state.testcoderunmode) - var attachment_ids; - if (this.state.fileList) { - attachment_ids = this.state.fileList.map(item => { - return item.response ? item.response.id : item.id - }) + } + } + }) + .catch(function (error) { + console.log(error); + }); + } + + render() { + const {getFieldDecorator} = this.props.form; + const {newshixunlist, fileList, postapplytitle, postapplyvisible} = this.state; + const uploadProps = { + width: 600, + fileList, + multiple: true, + action: `${getUploadActionUrl()}`, + onChange: this.handleChange, + onRemove: this.onAttachmentRemove, + beforeUpload: (file, fileList) => { + + if (this.state.fileList.length >= 1) { + return false } - // console.log(attachment_ids); - // var data={ - // language:"", - // runtime:"", - // run_method:"", - // attachment_id:"", - // } - // axios.post(url,data - // ).then((response) => { - // if (response.data) { - // // const { id } = response.data; - // // if (id) { - // this.props.showNotification('提交成功!'); - // // this.props.history.push(`/courses/${cid}/graduation_topics`); - // // } - // } - // }) + const isLt150M = file.size / 1024 / 1024 < 50; + if (!isLt150M) { + notification.open( + { + message: '提示', + description: + '文件大小必须小于50MB', - } - render() { - const { getFieldDecorator } = this.props.form; - let {testcoderunmode ,systemenvironment,languagewrite,deleteisnot, fileList,TimePickervalue, scope_partmenttype, opensmail, newshixunlist, name, scope_partment, departmentslist, postapplyvisible, sendsure_applyvalue, postapplytitle, shixun_nametype, main_types, trainee_types, SelectTheCommandtype, opers, datalisttype, onSearchvalue} = this.state; - let options - if (departmentslist != undefined) { - options = this.state.departmentslist.map((d, k) => { - return ( - - ) - }) + } + ) } - const uploadProps = { - width: 600, - fileList, - multiple: true, - // https://github.com/ant-design/ant-design/issues/15505 - // showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。 - // showUploadList: false, - action: `${getUploadActionUrl()}`, - onChange: this.handleChange, - onRemove: this.onAttachmentRemove, - beforeUpload: (file, fileList) => { - - if (this.state.fileList.length >= 1) { - return false - } - // console.log('beforeUpload', file.name); - const isLt150M = file.size / 1024 / 1024 < 50; - if (!isLt150M) { - // this.props.showNotification(`文件大小必须小于50MB`); - notification.open( - { - message: '提示', - description: - '文件大小必须小于50MB', - - } - ) - } - if(this.state.file !== undefined){ - console.log("763") - this.setState({ - file:file - }) - }else { - this.setState({ - file:file - }) - } - - console.log("handleChange2"); - return isLt150M; - }, + if (this.state.file !== undefined) { + this.setState({ + file: file + }) + } else { + this.setState({ + file: file + }) } - // const uploadProps = { - // width: 600, - // fileList, - // multiple: true, - // // https://github.com/ant-design/ant-design/issues/15505 - // // showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。 - // // showUploadList: false, - // action: `${getUrl()}/api/attachments.json`, - // onChange: this.handleChange, - // onRemove: this.onAttachmentRemove, - // beforeUpload: (file) => { - // // console.log('beforeUpload', file.name); - // const isLt50M = file.size / 1024 / 1024 < 50; - // if (!isLt50M) { - // this.props.showNotification('文件大小必须小于150MB!'); - // } - // return isLt50M; - // }, - // }; - - return ( - -
    -
    -
    - -
    -

    - 创建实训 - {this.props.user&&this.props.user.main_site===true?实训制作指南:""} -

    - -
    -

    实训名称

    -
    - * -
    - - - 必填项 - -
    - -
    -
    - -
    - - -
    - -

    简介

    - -
    -
    - -
    -
    -

    -

    -
    - -
    -

    技术平台

    -
    - * -
    - -

    - 列表中没有? - 申请新建 -

    - - - {/**/} -
    -
  • - - -
  • -
    {this.state.languagewritetype===true?"请填写该镜像语言":""}
    -
  • - - -
  • -
    {this.state.systemenvironmenttype===true?"请填写该镜像语言系统环境":""}
    -
  • - - - -
  • -
    {this.state.testcoderunmodetype===true?"请填写该镜像测试代码运行方式":""}
    -
  • - -
    - - - 上传附件 - (单个文件50M以内) - - -
    - -
  • -
    - {this.state.attachmentidstype===true?"请上传附件":""} -
    -
  • - this.sendhideModaly()} - >取消 - -
  • -
    -
    - {/**/} -
    - - - - -
    -

    新建申请已提交,请等待管理员的审核

    -
  • 我们将在1-2个工作日内与您联系 -
  • -
    -
    - 知道啦 -
    -
    -
    -
    -
    - -
    -

    请在配置页面完成后续的评测脚本设置操作

    -
    - 必填项 -
    -
    -
    - - -
    -

    命令行

    -
    - - 无命令行窗口 (选中则不给学员的实践任务提供命令窗口) - 命令行练习窗口 (选中则给学员提供用于练习操作的命令行窗口) - 命令行评测窗口 (选中则给学员提供用于关卡评测的命令行窗口) - - 多个命令行窗口(选中则允许学员同时开启多个命令行窗口) - - -
    -
    - - -
    -

    公开程度

    -
    - - 对所有公开 (选中则所有已被试用授权的用户可以学习) - 对指定单位公开 (选中则下方指定单位的已被试用授权的用户可以学习) - - -
    -
    -
    -
    -
    - -
    - (搜索选中添加单位名称) - {this.state.datalisttype===true?请勿选择重复单位:""} - {/*+ 添加*/} -
    -
    - -
    -
    - { - scope_partment === undefined ? "" : scope_partment.map((item, key) => { - return ( -
  • {item} - this.deleteScopeInput(key)}>× -
  • - ) - }) - } -
    - {/*{*/} - {/*scope_partment===undefined?"":scope_partment.map((item,key)=>{*/} - {/*return(*/} - {/*
    */} - {/*this.deleteScopeInput(key)} style={{ color: 'rgba(0,0,0,.25)' }} />}*/} - {/*value={item}*/} - {/*/>*/} - {/*
    */} - - {/*)*/} - {/*})*/} - {/*}*/} -
    - - - - 请选择需要公开的单位 - -
    -
    -
    -
    + return isLt150M; + }, + } + return ( +
    + +
    +
    + +
    + +
    + 新建实训项目 + {this.props.user && this.props.user.main_site === true ? + 实训制作指南 : ""} +
    +
    +
    + + {getFieldDecorator('is_jupyter')( + + 普通实训 + {this.props.user&&this.props.user.admin===true||this.props.user&&this.props.user.business===true?Jupyter实训:""} + , + )} + + + {getFieldDecorator('name', { + rules: [{ + required: true, message: '请输入名称', + }, { + max: 60, message: '请输入名称,最大限制60个字符', + }, { + whitespace: true, message: '请勿输入空格' + }], + })( + + )} + + + + + + + + {getFieldDecorator('select', { + rules: [{required: true, message: '请选择难易度'}], + })( +
    + + +
    + )} + (实训的难易程度) +
    + + + +
    + + {getFieldDecorator('main_type', { + rules: [{required: true, message: '请选择主类别'}], + })( +
    + + -
    -

    发布信息

    -
    -
    - *面向学员: -
    - -
    - 实训难易度定位,不限定用户群体 -
    - 必填项 -
    -
    -
    -
  • - 复制: - - -
  • -
    - 开启时间: -
  • - - -
  • -
    - {/*
    */} - {/*

    VNC图形化

    */} - {/*
  • */} - {/**/} - {/**/} - {/*
  • */} - {/*
    */} - - -
    - - 取消 + )} +
    + + + + +
    + {getFieldDecorator('sub_type')( +
    +
    + )} + +
    + {this.state.mainvalues === undefined && this.state.subvalues === undefined || this.state.mainvalues === "" && this.state.subvalues === "" ? "" : +
    + {`${this.state.mainvalues === undefined || this.state.mainvalues === "" ? "" : `已安装软件:` + this.state.mainvalues}`} + {`${this.state.subvalues === undefined || this.state.subvalues === "" ? "" : this.state.mainvalues === undefined || this.state.mainvalues === "" ? `已安装软件:` + this.state.subvalues : this.state.subvalues}`} + {`${this.state.mainvalues === undefined || this.state.mainvalues === "" ? "" : `说明:添加了` + this.state.mainvalues}${this.state.subvalues === undefined || this.state.subvalues === "" ? "" : + this.state.mainvalues === undefined || this.state.mainvalues === "" ? `说明:添加了` + this.state.subvalues : this.state.subvalues}`} +
    } + +
    +
    +
    +
    -
    + +
    +
    + 没有实验环境? + 申请新建
    + {postapplyvisible === true ? : ""} + + +
    +
  • + + +
  • +
    {this.state.languagewritetype === true ? "请填写该镜像语言" : ""}
    +
  • + + +
  • +
    {this.state.systemenvironmenttype === true ? "请填写该镜像语言系统环境" : ""}
    +
  • + + + +
  • +
    {this.state.testcoderunmodetype === true ? "请填写该镜像测试代码运行方式" : ""}
    +
  • + +
    + + + 上传附件 + (单个文件50M以内) + + +
    + +
  • +
    + {this.state.attachmentidstype === true ? "请上传附件" : ""} +
    +
  • + this.sendhideModaly()} + >取消 + +
  • +
    +
    + {/**/} +
    + +
    +
    +
    +
    + this.handleSubmit()} loadings={this.state.loading}/> +
    - ); - } + ); + } } -const NewshixunsNew = Form.create({ name: 'newshixunsnew' })(Newshixuns); + +const NewshixunsNew = Form.create({name: 'newshixun'})(Newshixuns); + export default SnackbarHOC()(TPMIndexHOC(NewshixunsNew)); diff --git a/public/react/src/modules/tpm/newshixuns/Shixunmd.js b/public/react/src/modules/tpm/newshixuns/Shixunmd.js new file mode 100644 index 000000000..608c7ea63 --- /dev/null +++ b/public/react/src/modules/tpm/newshixuns/Shixunmd.js @@ -0,0 +1,111 @@ +import React, { Component } from 'react'; +import {Button,Form,Input} from 'antd'; +import axios from 'axios'; +import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor'; +class Osshackathonmd extends Component{ + constructor(props) { + super(props) + this.contentMdRef = React.createRef(); + this.state={ + title_num: 0, + title_value: undefined + } + } + componentDidUpdate =(prevState)=>{ + // if(prevState!=this.props){ + // let url=`/osshackathon/edit_hackathon.json`; + // axios.get(url).then((result)=>{ + // if(result.status==200){ + // this.setState({ + // title_value:result.data.name + // }) + // this.contentMdRef.current.setValue(result.data.description); + // } + // }) + // } + } + componentDidMount(){ + let url=`/osshackathon/edit_hackathon.json`; + axios.get(url).then((result)=>{ + if(result.status==200){ + this.setState({ + title_value:result.data.name + }) + this.contentMdRef.current.setValue(result.data.description === null ? "" : result.data.description); + } + }) + } + + + // 输入title + changeTitle = (e) => { + // title_num: 60 - parseInt(e.target.value.length), + this.setState({ + title_num: e.target.value.length, + title_value: e.target.value + }) + + } + handleSubmit = () => { + let {title_value}=this.state; + const mdContnet = this.contentMdRef.current.getValue().trim(); + // if(mdContnet.length>10000){ + // this.props.showNotification("内容超过10000个字"); + // return + // } + + let url=`/osshackathon/update_hackathon.json`; + axios.post(url,{ + name:title_value, + description:mdContnet, + } + ).then((response) => { + if(response.data.status===0){ + this.props.getosshackathon() + this.props.hidehackathonedit() + this.props.showNotification(`提交成功`); + } + }).catch((error) => { + console.log(error) + }) + + } + render() { + + + // console.log(this.props.tabkey) + // console.log(chart_rules) + + return ( +
    +
    + + + + + + + + + + + + +
    + + ) + } +} +export default Osshackathonmd; diff --git a/public/react/src/modules/tpm/newshixuns/css/Newshixuns.css b/public/react/src/modules/tpm/newshixuns/css/Newshixuns.css index e241dcf0d..9a85f33d4 100644 --- a/public/react/src/modules/tpm/newshixuns/css/Newshixuns.css +++ b/public/react/src/modules/tpm/newshixuns/css/Newshixuns.css @@ -392,6 +392,442 @@ a.white-btn.use_scope-btn:hover{ border-color: #096dd9; } -.ant-btn:hover, .ant-btn:focus, .ant-btn:active, .ant-btn.active{ - background-color: #4CACFF; +/*.ant-btn:hover, .ant-btn:focus, .ant-btn:active, .ant-btn.active{*/ +/* background-color: #4CACFF;*/ +/*}*/ + +.newViewAfter .ant-input{ + line-height: 40px !important; + height: 40px !important; + box-shadow: none!important; +} + +.width30{ + width: 30%; +} + +.newshixunheadersear{ + display: -ms-flexbox; + display: flex; + -ms-flex-pack: center; + justify-content: center; + margin: 0 auto; +} +.packinput .ant-input{ + height: 55px; + width:663px !important; + font-size: 14px; + /*color: #681616 !important;*/ + border-color: #E1EDF8 !important; + padding-left: 20px; +} + +.packinput .ant-input-group-addon .ant-btn{ + width:137px !important; + font-size: 18px; + height: 53px; + background:rgba(76,172,255,1); + +} +.tabtitle{ + height: 62px !important; + -webkit-box-shadow: 3px 10px 21px 0px rgba(76, 76, 76, 0.15); + box-shadow: 3px 10px 21px 0px rgba(76, 76, 76, 0.15); + border-radius: 6px; + background: #fff; + display: -ms-flexbox; + display: flex; + -ms-flex-pack: center; + justify-content: center; +} +.tabtitles2{ + background: #fff; + height: 62px !important; + width: 1200px; +} + +.tabtitless{ + height: 62px !important; + line-height: 62px !important; + +} +.tabtitle1{ + +} +.tabtitle2{ + margin-left: 30px !important; + +} + + +.counttit{ + display: -ms-flexbox; + display: flex; + -ms-flex-pack: center; + justify-content: center; +} + +.counttittext{ + text-align: left; + width: 1200px; + height: 18px; + color: #888888; + font-size: 13px; + margin-top: 24px; + + +} +.counttittexts{ + color: #4CACFF !important; + font-size: 13px; +} + +.mainx{ + display: -ms-flexbox; + display: flex; + -ms-flex-pack: center; + justify-content: center; + margin-top: 17px; +} +.project-packages-list{ + +} +.project-package-item{ + display: -ms-flexbox; + display: flex; + -ms-flex-direction:column; + flex-direction:column; + margin-bottom: 20px; + padding: 20px; + background: white; + /* box-shadow: 1px 3px 3px 1px rgba(156,156,156,0.16); */ + +} +.xuxianpro{ + height: 20px; + border-bottom: 1px dashed; + border-color: #EAEAEA; + margin-bottom: 18px; +} +.magr11{ + margin-top: 11px; +} +.highlight{ + color: #4CACFF; +} +.fonttext{ + font-size: 20px; + font-weight:bold; +} + +.fontextcolor{ + color: #777777; +} +.tzbq{ + margin-left: 68px; +} +.tzbqx{ + /* margin-left: 24px; */ +} +.bjyss{ + background: #F8F8F8; +} +.zj{ + overflow:hidden; + -o-text-overflow:ellipsis; + text-overflow:ellipsis; + white-space:nowrap +} +.ziticor{ + color: #777777; + font-size: 13px; +} +.foohter{ + margin-top: 20px; + display: -ms-flexbox; + display: flex; + -ms-flex-direction:row; + flex-direction:row; +} + +.maxwidth1100{ + max-width: 1100px; + overflow:hidden; + -o-text-overflow:ellipsis; + text-overflow:ellipsis; + white-space:nowrap; + font-size: 18px !important; + font-weight: 500; + color: rgba(51,51,51,1) !important; +} + + +.newshixunmodelmidfont{ + font-size: 14px; + font-weight: 400; + color: #999999; + margin-top: 15px; + margin-left: 30px; + max-width: 1100px; + overflow: hidden; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 2; +} + +.newshixunmodelbotfont{ + font-size:12px; + font-weight:400; + color:rgba(102,102,102,1); + margin-top: 15px; + margin-left: 30px; +} + +.newshixunlist{ + max-height:227px; + width: 1200px; +} + +.xuxianpro { + height: 20px; + border-bottom: 1px dashed; + border-color: #eaeaea; + margin-bottom: 18px; +} + +.newshixunpd030{ + padding: 0px 30px; +} + +.pd303010{ + padding: 30px 30px 10px; +} + +.newshixunfont12{ + font-size: 12px; + color: rgba(76,172,255,1); + line-height: 21px; +} + +.newshixunmode{ + width: 100px; + height: 38px; + border-radius: 3px; + /*border: 1px solid rgba(191,191,191,1);*/ +} + +.ntopsj { + position: absolute; + top: -4px; +} + +.nyslbottomsj { + position: absolute; + bottom: -6px; +} + +.inherits .ant-dropdown-menu-item{ + cursor: inherit !important; +} + +.menus{ + width: 91px; + text-align: center; +} + +.newshixunmodelbotfont span{ + display: inline-block; + margin-right: 34px; +} + +.minhegiht300{ + min-height: 300px; +} + +.newshixunlist:hover{ + -webkit-box-shadow: 1px 6px 16px rgba(156,156,156,0.16); + box-shadow: 1px 6px 16px rgba(156,156,156,0.16); + opacity: 1; + border-radius: 2px; +} + +.newshixun500{ + max-width: 500px; + overflow: hidden; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; + white-space: nowrap; +} + +.mt3 { + margin-top: 3px !important; +} + +.highlight{ + color: #4CACFF; +} + +.newshixunbottombtn{ + position: fixed; + z-index: 1000; + bottom: 0px; + width: 100%; + height: 63px; + background: rgba(255,255,255,1); + -webkit-box-shadow: 0px -4px 4px 0px rgba(0,0,0,0.05); + box-shadow: 0px -4px 4px 0px rgba(0,0,0,0.05); +} + + +.mb60shixun{ + margin-bottom: 60px !important; +} + +.padding13-30 { + padding: 13px 30px; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} + +.displaymodulat { + display: -ms-flexbox; + display: flex; + display: -webkit-flex; + -ms-flex-direction: column; + flex-direction: column; + -ms-flex-align: center; + align-items: center; +} + +.WordNumberTextarea { + outline: none; /* 去掉输入字符时的默认样式 */ + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + background-color: white; + text-shadow: none; + -webkit-writing-mode: horizontal-tb !important; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + resize: none; /*禁止拉伸*/ + border: none; /*去掉默认边框*/ + width: 100%; + height: 130px; + border: none; + display: block; +} + +.WordNumbernote { + padding: 0; + margin: 0; + list-style: none; + text-decoration: none; + -webkit-box-sizing: border-box; + box-sizing: border-box; + overflow: hidden; + height: auto; + border: 1px solid rgba(234, 234, 234, 1); + border-radius: 0.125rem; + margin: 10px 10px 0px 10px; + padding: 10px 10px 5px 10px; + backgroud: rgba(234, 234, 234, 1); + width: 530px; + margin-left: 10px; + margin-top: 25px; + height: 214px !important; +} + +.WordNumbernote .WordNumberTextarea { + outline: none; + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + background-color: white; + text-shadow: none; + -webkit-writing-mode: horizontal-tb !important; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + resize: none; + border: none; + width: 100%; + height: 169px !important; + border: none; + display: block; +} + +.WordNumberTextarea-count { + display: inline-block; + float: right; + font-size: 16px; + color: #adadad; + padding-right: 0.25rem; +} + +.borerinput { + border: 1px solid #DD1717 !important; +} + +.borerinputs { + border: 1px solid #eee !important; +} + + +.mexertwo { + display: -ms-flexbox; + display: flex; + -ms-flex-direction: initial; + flex-direction: initial; +} + +.mexeheigth { + line-height: 40px; +} + +.mexeheigth2 { + line-height: 40px; + width: 74px; +} + +.minbuttionte { + /* display: flex; */ + margin-top: 20px; + width: 100%; + /* align-items: center; */ + margin-bottom: 17px; + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + -ms-flex-align: center; + align-items: center; + -ms-flex-pack: center; + justify-content: center; + -ms-flex-direction: initial; + flex-direction: initial; +} + +.initialflex{ + display: -ms-flexbox; + display: flex; + -ms-flex-direction:initial; + flex-direction:initial; +} + +.newshixunheadersear{ + margin: 0 auto; +} + +.newshixunmodels{ + margin: 0 auto; +} + +.backgroundFFF{ + background: #FFF !important; +} + +.relative{ + position: relative; +} + +.pd40px{ + padding-bottom: 40px; } \ No newline at end of file diff --git a/public/react/src/tpm/newshixuns/Newshixuns.js b/public/react/src/modules/tpm/newshixuns/oldNewshixuns.js similarity index 100% rename from public/react/src/tpm/newshixuns/Newshixuns.js rename to public/react/src/modules/tpm/newshixuns/oldNewshixuns.js diff --git a/public/react/src/modules/tpm/shixunchild/Challenges/Challenges.js b/public/react/src/modules/tpm/shixunchild/Challenges/Challenges.js index b6d70af87..548e7224a 100644 --- a/public/react/src/modules/tpm/shixunchild/Challenges/Challenges.js +++ b/public/react/src/modules/tpm/shixunchild/Challenges/Challenges.js @@ -1,24 +1,18 @@ import React, { Component } from 'react'; -import { Redirect } from 'react-router'; +import { Link } from "react-router-dom"; -import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom"; - -import PropTypes from 'prop-types'; - -import classNames from 'classnames'; - -import { getImageUrl ,markdownToHTML, configShareForCustom} from 'educoder' +import { getImageUrl ,markdownToHTML, configShareForCustom} from 'educoder'; import { CircularProgress } from 'material-ui/Progress'; -import { Modal, Spin, Tooltip ,message,Icon} from 'antd'; +import { Modal, Spin, Tooltip ,message,Icon,Button,Divider} from 'antd'; -import 'antd/lib/pagination/style/index.css'; +import axios from 'axios'; -import '../shixunchildCss/Challenges.css' +import 'antd/lib/pagination/style/index.css'; -import axios from 'axios'; +import '../shixunchildCss/Challenges.css'; import AccountProfile from"../../../user/AccountProfile"; @@ -39,6 +33,9 @@ class Challenges extends Component { hidestartshixunsreplacevalue:"", operationstrue:false, isSpin:false, + boxoffsetHeigh:0, + opentitletype:true, + isopentitletype:"Less", } } @@ -56,6 +53,13 @@ class Challenges extends Component { ChallengesDataList: response.data, sumidtype: false, }); + + if(response.data.description=== ""||response.data.description===null||response.data.description===undefined){ + this.setState({ + isopentitletype:"Less", + }) + } + } } }).catch((error) => { @@ -64,9 +68,34 @@ class Challenges extends Component { } componentDidMount() { - setTimeout(this.ChallengesList(), 1000); + this.ChallengesList() + } + componentDidUpdate = (prevProps,prevState) => { + + //防止陷入无限循环 + if(prevState.ChallengesDataList!=this.state.ChallengesDataList){ + let boxoffsetHeigh; + let box=document.getElementById("shixunchallengesid"); + if(box){ + boxoffsetHeigh=box.offsetHeight + if(boxoffsetHeigh<300){ + this.setState({ + isopentitletype:"Less", + boxoffsetHeigh:boxoffsetHeigh + }) + }else{ + this.setState({ + opentitletype:true, + isopentitletype:"greater", + boxoffsetHeigh:boxoffsetHeigh + }) + } + console.log(boxoffsetHeigh) + } + } + } updatamakedown = (id) => { setTimeout(() => { var shixunDescr = window.editormd.markdownToHTML(id, { @@ -301,6 +330,13 @@ class Challenges extends Component { }) } + opentitle=()=>{ + this.setState({ + opentitletype:!this.state.opentitletype, + + }) + } + render() { let { ChallengesDataList, startbtns, sumidtype ,startshixunCombattype,shixunsreplace,shixunsmessage,hidestartshixunsreplacevalue,operationstrue,AccountProfiletype} = this.state; let { loadingContent } = this.props; @@ -309,6 +345,7 @@ class Challenges extends Component { } let id = this.props.match.params.shixunId; const antIcon = ; + return ( {AccountProfiletype===true? : -
    -

    - {this.props.identity < 5 && ChallengesDataList&&ChallengesDataList.shixun_status=== 0 ? - - - - 实践任务 - - - : "" - } - {this.props.identity < 5 && ChallengesDataList&&ChallengesDataList.shixun_status=== 0 ? - - - - 选择题任务 - - : "" - } -

    -

    - 简介 - - - - - +

    + {/*

    */} + {/* {this.props.identity < 5 && ChallengesDataList&&ChallengesDataList.shixun_status=== 0 ?*/} + {/* */} + {/* */} + {/* */} + {/* 实践任务*/} + {/* */} + + {/* : ""*/} + {/* }*/} + {/* {this.props.identity < 5 && ChallengesDataList&&ChallengesDataList.shixun_status=== 0 ?*/} + {/* */} + {/* */} + {/* */} + {/* 选择题任务*/} + {/* */} + {/* : ""*/} + {/* }*/} + {/*

    */} + + +

    +

    + 简介 + + {this.props.identity < 5 && ChallengesDataList&&ChallengesDataList.shixun_status < 3 ? + + {/**/} + 编辑 + :""} + {this.props.user && this.props.user.main_site === true ? + this.props.identity < 5?实训制作指南 : "":""} +
    + {this.state.opentitletype===true?:""} +
    +
    + + +
    + {ChallengesDataList === undefined || ChallengesDataList&&ChallengesDataList.description=== ""||ChallengesDataList&&ChallengesDataList.description===null||ChallengesDataList&&ChallengesDataList.description===undefined? +
    + {this.props.identity < 5?:} +

    暂时还没有相关数据哦!

    +
    + :

    + {ChallengesDataList === undefined ? "" :ChallengesDataList&&ChallengesDataList.description===null?"": +

    + } +

    } +
    +
    + + {this.state.isopentitletype==="Less"?"":this.state.opentitletype===true?this.opentitle()} className={"pointer Breadcrumbfont color-grey-9 "}> + 阅读全文 + :this.opentitle()} className={"pointer Breadcrumbfont color-grey-9 "}> + 收起全文 + } +

    -
    -

    - {ChallengesDataList === undefined ? "" :ChallengesDataList&&ChallengesDataList.description===null?"": -

    +
    + 全部任务 + + {/* */} + {/**/} + {/* */} + {this.props.identity < 5 && ChallengesDataList&&ChallengesDataList.shixun_status=== 0 ? + + + : "" } -

    - - {/* - - */} + {this.props.identity < 5 && ChallengesDataList&&ChallengesDataList.shixun_status=== 0 ? + + + : "" + } + {/**/} + {/* */} + {/* */} +
    -

    - 全部任务 - {/*{this.props.identity < 5 && ChallengesDataList&&ChallengesDataList.shixun_status=== 0 ?*/} - {/**/} - {/**/} - {/**/} - {/*实践任务*/} - {/**/} - - {/* : ""*/} - {/*}*/} - {/*{this.props.identity < 5 && ChallengesDataList&&ChallengesDataList.shixun_status=== 0 ?*/} - {/**/} - {/**/} - {/**/} - {/*选择题任务*/} - {/**/} - {/* : ""*/} - {/*}*/} -

    +
    -
    {ChallengesDataList === undefined ?
    - -

    暂时还没有相关数据哦!

    + {this.props.identity < 5?:} +

    暂时还没有相关数据哦!

    : ChallengesDataList.challenge_list === undefined ?
    - -

    暂时还没有相关数据哦!

    + {this.props.identity < 5?:} +

    暂时还没有相关数据哦!

    : ChallengesDataList.challenge_list.length === 0 ?
    - -

    暂时还没有相关数据哦!

    + {this.props.identity < 5?:} +

    暂时还没有相关数据哦!

    : ChallengesDataList.challenge_list.map((item, key) => { - - let newstatus = 2; - if(ChallengesDataList.challenge_list[key - 1]!=undefined){ - newstatus=ChallengesDataList.challenge_list[key - 1].status; - } return (
    +
    +
    第{key+1}关:{item.st === 0 ?"实践题":"选择题"} + + {/*this.startshixunCombat(false,undefined, item.challenge_id)}*/} + {/* // onClick={() => this.startshixunCombat(false)}*/} + {/* title={"查看挑战关卡"}*/} + {/*>已完成*/} + {/*判断比较复杂 有排第一不能是灰色按钮*/} + {item.status === 2 ? + + : "" + } -
    - - {item.st === 0 ? - - - - : - - - - } - - 第{key+1}关 + {/* this.startshixunCombat(false,undefined, item.challenge_id)} + // onClick={() => this.startshixunCombat(false)} + >直接挑战 */} + { + ChallengesDataList.allow_skip === true && item.status === 1? + + : "" + } - {this.props.identity<5? - item.st === 1 ? - this.EditTraining(this.props.identity, item.challenge_id, "/editquestion")} - className="font-16 color05101a">{item.name} - : - this.EditTraining(this.props.identity, item.challenge_id, "/editcheckpoint")} - className="font-16 color05101a">{item.name}: this.startshixunCombat(this.props.identity, item.challenge_id, "/editcheckpoint")} - className="font-16 color05101a">{item.name} - } + {/* + this.startshixunCombat(false,undefined, item.challenge_id)} + style={{marginTop: '-2px'}}>直接挑战 + + */} + { + ChallengesDataList.allow_skip === false ? item.status === 1? + + :"":"" - - - - + } + {/**/} + {/* this.startshixunCombat(false,undefined, item.challenge_id):""}*/} + {/* style={{marginTop: '-2px'}}>直接挑战*/} + {/**/} + { + item.status === 0 ? + + :"" + } +
    + + {item.delete_url != undefined && this.delOperations(item.challenge_id)} style={{ display:this.props.user.admin===true?"block":this.props.identity < 5 && ChallengesDataList.shixun_status === 0 ? "block" : 'none' }} - className="fl ring-op-green mr25"> - + className="fl mr25"> + {/**/} + } @@ -492,9 +597,10 @@ class Challenges extends Component { this.operations(item.challenge_id, "up")} style={{ display:this.props.user.admin===true?"block":this.props.identity < 5 && ChallengesDataList.shixun_status === 0 ? "block" : 'none' }} - className="fl ring-op-green mr25"> - + className="fl mr25"> + {/**/} + } @@ -502,8 +608,9 @@ class Challenges extends Component { this.operations(item.challenge_id, "down")} style={{ display: this.props.user.admin===true?"block":this.props.identity < 5 && ChallengesDataList.shixun_status=== 0 ? "block" : 'none' }} - className="fl ring-op-green mr25"> - + className="fl mr25"> + {/**/} + @@ -512,78 +619,68 @@ class Challenges extends Component { { item.st === 1 ? - - - + to={"/shixuns/" + ChallengesDataList.shixun_identifier + "/challenges/" + item.challenge_id + "/editquestion"} + className="fl"> + {/**/} + + : - - - + to={"/shixuns/" + ChallengesDataList.shixun_identifier + "/challenges/" + item.challenge_id + "/editcheckpoint"} + className="fl"> + {/**/} + + - }
    -
    - {item.passed_count} 人完成挑战 - {item.playing_count} 人正在挑战 - 完成挑战可获得经验值 {item.score} - - - {/*判断比较复杂 有排第一不能是灰色按钮*/} - {item.status === 2 ? - this.startshixunCombat(false,undefined, item.challenge_id)} - // onClick={() => this.startshixunCombat(false)} - title={"查看挑战关卡"} - >已完成 : "" - } - - { - ChallengesDataList.allow_skip === true && item.status === 1? - this.startshixunCombat(false,undefined, item.challenge_id)} - // onClick={() => this.startshixunCombat(false)} - >直接挑战 : "" - } - - - { - ChallengesDataList.allow_skip === false ? item.status === 1? - - this.startshixunCombat(false,undefined, item.challenge_id)} - style={{marginTop: '-2px'}}>直接挑战 - :"":"" - - } - - { - item.status === 0 ? - - this.startshixunCombat(false,undefined, item.challenge_id):""} - style={{marginTop: '-2px'}}>直接挑战 - :"" +
    + {/*onClick={() => this.EditTraining(this.props.identity, item.challenge_id, "/editquestion")}*/} + {this.props.identity<5? + item.st === 1 ? +
    {item.name}
    + : +
    {item.name}
    :
    this.startshixunCombat(this.props.identity, item.challenge_id, "/editcheckpoint")} + className="font-16 color05101a fonthiddens">{item.name}
    } + {/* onClick={() => this.EditTraining(this.props.identity, item.challenge_id, "/editcheckpoint")}*/} + + + +
    +
    + + 正在挑战: {item.playing_count}人 + 完成挑战: {item.passed_count}人 + 可获经验: {item.score}点
    diff --git a/public/react/src/modules/tpm/shixunchild/Challenges/Challengesjupyter.js b/public/react/src/modules/tpm/shixunchild/Challenges/Challengesjupyter.js new file mode 100644 index 000000000..e04ad17c9 --- /dev/null +++ b/public/react/src/modules/tpm/shixunchild/Challenges/Challengesjupyter.js @@ -0,0 +1,395 @@ +import React, { Component } from 'react'; +import { Link } from "react-router-dom"; +import { markdownToHTML, configShareForCustom,getImageUrl} from 'educoder' +import { Divider, Tooltip } from 'antd'; +import LoadingSpin from '../../../../common/LoadingSpin'; +import 'antd/lib/pagination/style/index.css'; +import '../shixunchildCss/Challenges.css'; +import axios from 'axios'; +const $ = window.$; + + +class Challengesjupyter extends Component { + constructor(props) { + super(props) + this.state = { + ChallengesDataList: undefined, + operate: true, + startbtns: false, + iFrameHeight: '0px', + jupyter_port:0, + jupyter_url:null, + username:"", + booljupyterurls:false, + loading:false, + boxoffsetHeigh:0, + opentitletype:true, + isopentitletype:"Less", + } + } + + ChallengesList = () => { + let id = this.props.match.params.shixunId; + let ChallengesURL = `/shixuns/` + id + `/challenges.json`; + + axios.get(ChallengesURL).then((response) => { + if (response.status === 200) { + if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { + + }else{ + configShareForCustom(this.props.shixunsDetails.name, response.data.description) + this.setState({ + ChallengesDataList: response.data, + sumidtype: false, + }); + if(response.data.description=== ""||response.data.description===null||response.data.description===undefined){ + this.setState({ + isopentitletype:"Less", + }) + } + } + } + }).catch((error) => { + //console.log(error) + }); + } + componentDidUpdate = (prevProps,prevState) => { + + //防止陷入无限循环 + if(prevState.ChallengesDataList!=this.state.ChallengesDataList){ + let boxoffsetHeigh; + let box=document.getElementById("shixunchallengesid"); + if(box){ + boxoffsetHeigh=box.offsetHeight + if(boxoffsetHeigh<300){ + this.setState({ + isopentitletype:"Less", + boxoffsetHeigh:boxoffsetHeigh + }) + }else{ + this.setState({ + isopentitletype:"greater", + opentitletype:true, + boxoffsetHeigh:boxoffsetHeigh + }) + } + + } + } + + } + componentDidMount() { + setTimeout(this.ChallengesList(), 1000); + let id = this.props.match.params.shixunId; + let ChallengesURL = `/jupyters/get_info_with_tpm.json`; + let datas={ + identifier:id, + } + axios.get(ChallengesURL, {params: datas}).then((response) => { + if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { + setTimeout(() => { + this.setState({ + booljupyterurls:true, + }) + }, 600) + }else{ + if(response.data.status===0){ + + setTimeout(() => { + this.setState({ + jupyter_url:response.data.url, + jupyter_port:response.data.port, + booljupyterurls:true, + }) + }, 800) + + }else{ + setTimeout(() => { + this.setState({ + booljupyterurls:true, + }) + }, 600) + + } + } + + + }).catch((error) => { + setTimeout(() => { + this.setState({ + booljupyterurls:true, + }) + }, 600) + + }); + } + + updatamakedowns = () => { + this.setState({ + loading:true, + booljupyterurls:false + }) + let id = this.props.match.params.shixunId; + let ChallengesURL = `/jupyters/get_info_with_tpm.json`; + let datas={ + identifier:id, + } + axios.get(ChallengesURL, {params: datas}).then((response) => { + if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { + setTimeout(() => { + this.setState({ + booljupyterurls:true, + }) + }, 600) + }else{ + if(response.data.status===0){ + setTimeout(() => { + this.setState({ + jupyter_url:response.data.url, + jupyter_port:response.data.port, + booljupyterurls:true, + }) + }, 800) + this.setState({ + + }) + }else{ + setTimeout(() => { + this.setState({ + booljupyterurls:true, + }) + }, 600) + } + } + }).catch((error) => { + setTimeout(() => { + this.setState({ + booljupyterurls:true, + }) + }, 600) + + }); + + } + + + modifyjupyter=()=>{ + let id=this.props.match.params.shixunId; + var jupyter_port=""; + try{ + jupyter_port= parseInt(this.state.jupyter_port); + }catch (e) { + jupyter_port=this.state.jupyter_port; + + } + const url=`/jupyters/save_with_tpm.json`; + const data={ + identifier:id, + jupyter_port:jupyter_port, + } + axios.get(url, {params: data}) + .then((result) => { + if (result.data.status === 0) { + this.props.showNotification(`应用成功`); + } + }).catch((error) => { + }) + + } + opentitle=()=>{ + this.setState({ + opentitletype:!this.state.opentitletype + }) + } + + + render() { + let{ChallengesDataList,booljupyterurls}=this.state; + let id = this.props.match.params.shixunId; + //老师 + const is_teacher = this.props&&this.props.current_user&&this.props.current_user.is_teacher?this.props.current_user.is_teacher:false; + //运营人员 + const business = this.props&&this.props.current_user&&this.props.current_user.business?this.props.current_user.business:false; + //管理员 + const admin = this.props&&this.props.current_user&&this.props.current_user.admin?this.props.current_user.admin:false; + + let mysidentity =false; + try { + mysidentity =this.props.identity < 5 &&ChallengesDataList&& ChallengesDataList.shixun_status< 3?true:false; + }catch (e) { + + } + + return ( + +
    +

    +

    + 简介 + + + 编辑 + + +
    + {this.state.opentitletype===true?:""} +
    + +
    +

    + {/*{ChallengesDataList === undefined ? "" :ChallengesDataList&&ChallengesDataList.description===null?"":*/} + {/*

    */} + {/*}*/} + + {ChallengesDataList === undefined || ChallengesDataList&&ChallengesDataList.description=== ""||ChallengesDataList&&ChallengesDataList.description===null||ChallengesDataList&&ChallengesDataList.description===undefined? +
    + {this.props.identity < 5?:} +

    暂时还没有相关数据哦!

    +
    + :

    + {ChallengesDataList === undefined ? "" :ChallengesDataList&&ChallengesDataList.description===null?"": +

    + } +

    } +

    + { + booljupyterurls===true? + ( + this.state.jupyter_url === null? +
    + +

    加载实训失败,

    this.updatamakedowns()}>重新加载

    + +
    + + :"" + ) + :"" + } +
    + + {this.state.isopentitletype==="Less"?"":this.state.opentitletype===true?this.opentitle()} className={"pointer Breadcrumbfont color-grey-9 "}> + 阅读全文 + :this.opentitle()} className={"pointer Breadcrumbfont color-grey-9 "}> + 收起全文 + } +
    + +

    + +
    + + { + this.state.jupyter_url === null || this.state.jupyter_url === undefined ? + "" + : + ( + admin===true||business===true||mysidentity===true? +
    +
    +

    任务详情

    +

    (请将实训题目写在下方并保存)

    +
    +
    +
    this.modifyjupyter(this.state)}>

    应用到实训

    +
    +
    + : + "" + ) + + } + + + { + admin===true||business===true||mysidentity===true? +
    +
    + { + this.state.jupyter_url===null || this.state.jupyter_url===undefined? + ( + booljupyterurls===false? + + :"" + ) + : + + } +
    +
    + :"" + } +
    +
    + +
    + + ) + } +} + +export default Challengesjupyter; diff --git a/public/react/src/modules/tpm/shixunchild/Collaborators/Collaborators.css b/public/react/src/modules/tpm/shixunchild/Collaborators/Collaborators.css index 31917086f..b059ab406 100644 --- a/public/react/src/modules/tpm/shixunchild/Collaborators/Collaborators.css +++ b/public/react/src/modules/tpm/shixunchild/Collaborators/Collaborators.css @@ -3,7 +3,194 @@ line-height: 30px; } +.height28 { + height: 30px; + line-height:28px; +} + .line27{ line-height: 27px; vertical-align: 1px; +} +/* 中间居中 */ +.intermediatecenter{ + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} +/* 简单居中 */ +.intermediatecenterysls{ + display: flex; + align-items: center; +} +.spacearound{ + display: flex; + justify-content: space-around; + +} +.spacebetween{ + display: flex; + justify-content: space-between; +} +/* 头顶部居中 */ +.topcenter{ + display: -webkit-flex; + flex-direction: column; + align-items: center; + +} + + +/* x轴正方向排序 */ +/* 一 二 三 四 五 六 七 八 */ +.sortinxdirection{ + display: flex; + flex-direction:row; + +} +/* x轴反方向排序 */ +/* 八 七 六 五 四 三 二 一 */ +.xaxisreverseorder{ + display: flex; + flex-direction:row-reverse; +} +/* 垂直布局 正方向*/ +/* 一 + 二 + 三 + 四 + 五 + 六 + 七 + 八 */ +.verticallayout{ + display: flex; + flex-direction:column; +} +/* 垂直布局 反方向*/ +.reversedirection{ + display: flex; + flex-direction:column-reverse; +} + +.yslwushiwidth{ + width: 50%; +} +.yslwushiwidth90{ + width: 90%; +} +.yslwushiwidth10{ + width: 10%; +} +.yslwushiwidthbuton{ + width: 110px; +} +.yslwushiwidthcolortest{ + color: #A8A8A8; + font-size:16px; +} +.yslusername{ + color: #000000; + font-size: 18px; +} +.yslusercjz{ + width:60px; + height:28px; + border-radius:3px; + border:1px solid #F38B03; +} +.yslusercjztest{ + width:60px; + height:28px; + font-size:16px; + color:#F38B03; + line-height:28px; + text-align: center; +} +.w18{ + width: 18px; +} + +.maxnamewidth150{ + width: 150px; + max-width: 150px; + overflow:hidden; + text-overflow:ellipsis; + white-space:nowrap; + cursor: default; +} +.fabushixunwidth{ + color: #000000; + font-size: 16px; +} +.fabushixunwidthcolor{ + color: #4CACFF; + font-size: 16px; +} +.divfontexdivs{ + border-left: 1px solid #eeeeee; + border-top: 1px solid #eeeeee; + border-right: 1px solid #eeeeee; + border-bottom: 1px solid #eeeeee; +} + +.space-between{ + justify-content:space-between +} + +.heighthezuo34{ + height:34px; + line-height: 34px; +} +.hezuozhe630{ + width: 630px; +} +.hezuozhe634{ + width: 634px; +} +.color333hezuo{ + color:#333333; +} + +.color888hezuo{ + color:#888888; +} + +.newyslusercjz{ + display: inline-block; + position: absolute; + bottom: 0px; + left: -18px; + width: 44px; + height: 18px; + border-radius: 13px; + background: #F38B03; + color: #fff; +} + +.newyslusercjztest{ + width: 45px; + height: 29px; + font-size: 10px; + color: #fff; + line-height: 18px; + text-align: center; +} + +.fontnewreds{ + color: rgb(255, 85, 85); +} + +.fontneweees { + color: #BBBBBB; +} + +.maxfont450{ + width: 450px; + max-width:450px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + cursor: default; } \ No newline at end of file diff --git a/public/react/src/modules/tpm/shixunchild/Collaborators/Collaborators.js b/public/react/src/modules/tpm/shixunchild/Collaborators/Collaborators.js index d67599bf1..a5e055e41 100644 --- a/public/react/src/modules/tpm/shixunchild/Collaborators/Collaborators.js +++ b/public/react/src/modules/tpm/shixunchild/Collaborators/Collaborators.js @@ -1,18 +1,8 @@ -import React, { Component } from 'react'; +import React, {Component} from 'react'; -import { Redirect } from 'react-router'; +import {Modal, Button, Radio, Input, Checkbox, message, Spin, Icon, Pagination} from 'antd'; -import {Modal, Button, Radio, Input, Checkbox,message,Spin, Icon} from 'antd'; - -import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom"; - -import PropTypes from 'prop-types'; - -import classNames from 'classnames'; - -import { CircularProgress } from 'material-ui/Progress'; - -import { getImageUrl, toPath } from 'educoder' +import {getImageUrl, toPath} from 'educoder' import axios from 'axios'; @@ -21,638 +11,754 @@ import NoneData from "../../../courses/coursesPublic/NoneData"; import './Collaborators.css'; -const $ = window.$; - const RadioGroup = Radio.Group; -const Search = Input.Search; + class Collaborators extends Component { - constructor(props) { - super(props) - this.state = { - collaboratorList: [], - Collaboratorsvisible: false, - Collaboratorsvisibleadmin: false, - value: 1, - page: 1, - Searchadmin: undefined, - allChangechecked: false, - Collaboratorslist: [], - Collaboratorslisttype: false, - collaborators_deletetype: false, - collaborators_deletevalue: null, - onSearchcalue:"", - collaboratorListsum:10, - collaboratorListsumtype:true, - user_name:undefined, - school_name:undefined, - spinnings:false, - useristrue:false - } - } - componentDidMount() { - let id=this.props.match.params.shixunId; - - let collaborators=`/shixuns/`+id+`/collaborators.json`; - axios.get(collaborators).then((response)=> { - if(response.status===200){ - if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { - - }else{ - this.setState({ - collaboratorList: response.data - }); - } - - } - }).catch((error)=>{ - console.log(error) - }); + constructor(props) { + super(props) + this.state = { + collaboratorList: [], + Collaboratorsvisible: false, + Collaboratorsvisibleadmin: false, + value: 1, + page: 1, + Searchadmin: undefined, + allChangechecked: false, + Collaboratorslist: [], + Collaboratorslisttype: false, + collaborators_deletetype: false, + collaborators_deletevalue: null, + onSearchcalue: "", + collaboratorListsum: 10, + collaboratorListsumtype: true, + user_name: undefined, + school_name: undefined, + spinnings: false, + useristrue: false, + mylistansum: 6, + limit: 20, } + } - updatacomponentDiddata = () => { - let id = this.props.match.params.shixunId; - - let collaborators = `/shixuns/` + id + `/collaborators.json`; - axios.get(collaborators).then((response) => { - if (response.status === 200) { - if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { - - }else{ - this.setState({ - collaboratorList: response.data - }); - } - } - }).catch((error) => { - console.log(error) - }); - } - CollaboratorsshowModal = (type) => { + componentDidMount() { + let id = this.props.match.params.shixunId; - if (type === "cooperation") { - this.setState({ - Collaboratorsvisibleadmin: false, - Collaboratorslist:[], - Searchadmin:[] - }); - } else if (type === "admin") { - this.setState({ - Collaboratorsvisible: false, - Collaboratorslist:[], - Searchadmin:[] - }); - } else if (type === "collaborators_deletetype") { - this.setState({ - collaborators_deletetype: false, - }); + let collaborators = `/shixuns/` + id + `/collaborators.json`; + axios.get(collaborators).then((response) => { + if (response.status === 200) { + if (response.data.status === 403 || response.data.status === 401 || response.data.status === 500) { + + } else { + this.setState({ + collaboratorList: response.data + }); } - } - showCollaboratorsvisible = (type) => { + } + }).catch((error) => { + console.log(error) + }); + } - this.setState({ - Collaboratorslist: [], - Searchadmin:undefined, - onSearchcalue:"" - }) - let admintype = this.props.identity; - if (admintype>4) { - this.props.showSnackbar("您没有权限"); - return - } - if (type === "cooperation") { - this.setState({ - Collaboratorsvisibleadmin: true, - }); - } else if ("admin") { - let id = this.props.match.params.shixunId; - let url = "/shixuns/" + id + "/change_manager.json"; - axios.get(url).then((response) => { - if (response.status === 200) { - // this.setState({ - // Collaboratorsvisible: true - // }) - if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { - - }else{ - this.setState({ - Collaboratorsvisible: true, - Collaboratorslist: response.data - }) - } - } - }).catch((error) => { - console.log(error) - }); + updatacomponentDiddata = (pageNumber) => { + let id = this.props.match.params.shixunId; + + let collaborators = `/shixuns/` + id + `/collaborators.json`; + axios.get((collaborators),{params:{ + page:pageNumber?pageNumber:undefined + }}).then((response) => { + if (response.status === 200) { + if (response.data.status === 403 || response.data.status === 401 || response.data.status === 500) { + } else { + this.setState({ + collaboratorList: response.data + }); } + } + }).catch((error) => { + console.log(error) + }); + } + CollaboratorsshowModal = (type) => { + + if (type === "cooperation") { + this.setState({ + Collaboratorsvisibleadmin: false, + Collaboratorslist: [], + Searchadmin: [] + }); + } else if (type === "admin") { + this.setState({ + Collaboratorsvisible: false, + Collaboratorslist: [], + Searchadmin: [] + }); + } else if (type === "collaborators_deletetype") { + this.setState({ + collaborators_deletetype: false, + }); + } + } + + showCollaboratorsvisible = (type) => { + + this.setState({ + Collaboratorslist: [], + Searchadmin: undefined, + onSearchcalue: "" + }) + let admintype = this.props.identity; + if (admintype > 4) { + this.props.showNotification("您没有权限"); + return } + if (type === "cooperation") { + this.setState({ + Collaboratorsvisibleadmin: true, + }); + } else if ("admin") { + let id = this.props.match.params.shixunId; + let url = "/shixuns/" + id + "/change_manager.json"; + axios.get(url).then((response) => { + if (response.status === 200) { + // this.setState({ + // Collaboratorsvisible: true + // }) + if (response.data.status === 403 || response.data.status === 401 || response.data.status === 500) { + + } else { + this.setState({ + Collaboratorsvisible: true, + Collaboratorslist: response.data + }) + } + } + }).catch((error) => { + console.log(error) + }); - onChange = (e) => { - this.setState({ - value: e.target.value, - }); } - onSearchadmins=(e)=>{ + } + + onChange = (e) => { + this.setState({ + value: e.target.value, + }); + } + onSearchadmins = (e) => { + this.setState({ + onSearchcalue: e.target.value + }) + } + onSearchadmin = (value) => { + + let {collaboratorList, user_name, school_name} = this.state; + this.setState({ + // Searchadmin: undefined, + spinnings: true, + }) + // if (value === "") { + // this.setState({ + // Searchadmin: [], + // collaboratorList: collaboratorList + // }) + // } else { + // + // } + let id = this.props.match.params.shixunId; + let url = "/shixuns/" + id + "/add_collaborators.json"; + axios.get(url, { + params: { + user_name: user_name, + school_name: school_name, + } + }).then((response) => { + if (response.data.status === 403 || response.data.status === 401 || response.data.status === 500) { this.setState({ - onSearchcalue:e.target.value + spinnings: false }) - } - onSearchadmin = (value) => { - - let {collaboratorList,user_name,school_name} = this.state; - this.setState({ - // Searchadmin: undefined, - spinnings:true, - }) - // if (value === "") { - // this.setState({ - // Searchadmin: [], - // collaboratorList: collaboratorList - // }) - // } else { - // - // } - let id = this.props.match.params.shixunId; - let url = "/shixuns/" + id + "/add_collaborators.json"; - axios.get(url,{params:{ - user_name:user_name , - school_name:school_name, - }}).then((response) => { - if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { - this.setState({ - spinnings:false - }) - }else{ - let newlist = response.data.users; - for (var i = 0; i < newlist.length; i++) { - newlist[i].checked = false - } - this.setState({ - Searchadmin: newlist, - collaboratorList: collaboratorList, - spinnings:false - }) - } - - }).catch((error) => { - console.log(error) - }); - } - - selectChangenickname = (e, key) => { - - let {Searchadmin} = this.state; - let newlist = Searchadmin; + } else { + let newlist = response.data.users; for (var i = 0; i < newlist.length; i++) { - newlist[key].checked = e.target.checked - } - - let arrlist = []; - let alltype = false; - for (var z = 0; z < newlist.length; z++) { - if (newlist[z].checked === true) { - arrlist.push(newlist[z]) - } + newlist[i].checked = false } + this.setState({ + Searchadmin: newlist, + collaboratorList: collaboratorList, + spinnings: false + }) + } - if (Searchadmin.length === arrlist.length) { - alltype = true - } else { - alltype = false - } + }).catch((error) => { + console.log(error) + }); + } - if(newlist.length===0){ - this.setState({ - Searchadmin: newlist, - allChangechecked: alltype, - }) - }else{ - this.setState({ - Searchadmin: newlist, - allChangechecked: alltype, - useristrue:false - }) - } + selectChangenickname = (e, key) => { + let {Searchadmin} = this.state; + let newlist = Searchadmin; + for (var i = 0; i < newlist.length; i++) { + newlist[key].checked = e.target.checked + } + let arrlist = []; + let alltype = false; + for (var z = 0; z < newlist.length; z++) { + if (newlist[z].checked === true) { + arrlist.push(newlist[z]) + } } - allChange = (e) => { - let {Searchadmin} = this.state; - let newlist = Searchadmin; - for (var i = 0; i < newlist.length; i++) { - newlist[i].checked = e.target.checked - } - this.setState({ - Searchadmin: newlist, - allChangechecked: e.target.checked - }) + if (Searchadmin.length === arrlist.length) { + alltype = true + } else { + alltype = false } - submit_add_collaborators_form = () => { - let id = this.props.match.params.shixunId; - let {Searchadmin,collaboratorList} = this.state; - let newlist = Searchadmin; - let user_ids = [] - if (newlist.length === 0) { - this.setState({ - Collaboratorslisttype: true - }) - return - } - for (var i = 0; i < newlist.length; i++) { - if (newlist[i].checked === true) { - user_ids.push(newlist[i].user_id) - } - } + if (newlist.length === 0) { + this.setState({ + Searchadmin: newlist, + allChangechecked: alltype, + }) + } else { + this.setState({ + Searchadmin: newlist, + allChangechecked: alltype, + useristrue: false + }) + } - for(var i=0; i { - if(user_ids.length===0){ - this.setState({ - useristrue:true - }) - return - } - let url = "/shixuns/" + id + "/shixun_members_added.json"; - axios.post(url, { - user_ids: user_ids - }).then((response) => { - this.updatacomponentDiddata(); - this.props.showSnackbar(response.data.message); - this.setState({ - Collaboratorsvisibleadmin: false, - Collaboratorslist:[], - Searchadmin:[] - }) - }).catch((error) => { - console.log(error) - }); + let {Searchadmin} = this.state; + let newlist = Searchadmin; + for (var i = 0; i < newlist.length; i++) { + newlist[i].checked = e.target.checked } - addadminredio = (e) => { - this.setState({ - addadminrediovalue: e - }) - + this.setState({ + Searchadmin: newlist, + allChangechecked: e.target.checked + }) + } + submit_add_collaborators_form = () => { + + let id = this.props.match.params.shixunId; + let {Searchadmin, collaboratorList} = this.state; + let newlist = Searchadmin; + let user_ids = [] + if (newlist.length === 0) { + this.setState({ + Collaboratorslisttype: true + }) + return + } + for (var i = 0; i < newlist.length; i++) { + if (newlist[i].checked === true) { + user_ids.push(newlist[i].user_id) + } } - submit_addadminredio = () => { - - let {addadminrediovalue} = this.state; - let id = this.props.match.params.shixunId; + for (var i = 0; i < user_ids.length; i++) { + for (var j = 0; j < collaboratorList.length; j++) { + if (user_ids[i] === collaboratorList[j].user.user_id) { + message.error("添加失败,重复添加!"); - let url = "/shixuns/" + id + "/change_manager.json"; - if(addadminrediovalue===undefined){ - this.setState({ - Collaboratorsvisible: false, - Collaboratorslist:[], - Searchadmin:[] - }); - this.props.showSnackbar("所选人员为空,没有更换成功"); - this.CollaboratorsshowModal("admin") - return + return } + } + } + if (user_ids.length === 0) { + this.setState({ + useristrue: true + }) + return + } + let url = "/shixuns/" + id + "/shixun_members_added.json"; + axios.post(url, { + user_ids: user_ids + }).then((response) => { + this.updatacomponentDiddata(); + this.props.showNotification(response.data.message); + this.setState({ + Collaboratorsvisibleadmin: false, + Collaboratorslist: [], + Searchadmin: [] + }) + }).catch((error) => { + console.log(error) + }); + } + addadminredio = (e) => { + this.setState({ + addadminrediovalue: e + }) - axios.post(url, { - user_id: addadminrediovalue - }).then((response) => { - this.setState({ - Collaboratorsvisible: false, - Collaboratorslist:[], - Searchadmin:[] - }); - this.updatacomponentDiddata(); - this.props.showSnackbar(response.data.message); - }).catch((error) => { - console.log(error) - }); + } + submit_addadminredio = () => { + + let {addadminrediovalue} = this.state; + + let id = this.props.match.params.shixunId; + + let url = "/shixuns/" + id + "/change_manager.json"; + if (addadminrediovalue === undefined) { + this.setState({ + Collaboratorsvisible: false, + Collaboratorslist: [], + Searchadmin: [] + }); + this.props.showNotification("所选人员为空,没有更换成功"); + this.CollaboratorsshowModal("admin") + return } - collaborators_delete = (value) => { - this.setState({ - collaborators_deletetype: true, - collaborators_deletevalue: value - }) + axios.post(url, { + user_id: addadminrediovalue + }).then((response) => { + this.setState({ + Collaboratorsvisible: false, + Collaboratorslist: [], + Searchadmin: [] + }); + this.updatacomponentDiddata(); + this.props.showNotification(response.data.message); + // window.location.reload(); + }).catch((error) => { + console.log(error) + }); + } + + collaborators_delete = (value) => { + this.setState({ + collaborators_deletetype: true, + collaborators_deletevalue: value + }) + + } + collaborators_deletes = () => { + let {collaborators_deletevalue} = this.state; + if (collaborators_deletevalue === null) { + return } - collaborators_deletes = () => { - let {collaborators_deletevalue} = this.state; - if (collaborators_deletevalue === null) { - return - } - let id = this.props.match.params.shixunId; - let url = "/shixuns/" + id + "/collaborators_delete.json?user_id=" + collaborators_deletevalue; - axios.delete(url).then((response) => { - if (this.props.current_user.user_id == collaborators_deletevalue) { - this.props.history.push('/shixuns') - return; - } - this.props.showSnackbar(response.data.message); - this.updatacomponentDiddata(); - this.setState({ - collaborators_deletetype: false - }) - }).catch((error) => { - console.log(error) - }); - } + let id = this.props.match.params.shixunId; + let url = "/shixuns/" + id + "/collaborators_delete.json?user_id=" + collaborators_deletevalue; + axios.delete(url).then((response) => { + if (this.props.current_user.user_id == collaborators_deletevalue) { + this.props.history.push('/shixuns') + return; + } + this.props.showNotification(response.data.message); + this.updatacomponentDiddata(); + this.setState({ + collaborators_deletetype: false + }) + }).catch((error) => { + console.log(error) + }); + } - loadMore=()=>{ - let {collaboratorList}=this.state; - this.setState({ - collaboratorListsum:collaboratorList.length, - collaboratorListsumtype:false - }) + loadMore = () => { + let {collaboratorList} = this.state; + this.setState({ + collaboratorListsum: collaboratorList.length, + collaboratorListsumtype: false + }) } - contentViewScrolledit=(e)=>{ - - //滑动到底判断 - let newscrollTop=parseInt(e.currentTarget.scrollTop); - let allclientHeight=e.currentTarget.clientHeight+newscrollTop; - - if(e.currentTarget.scrollHeight-allclientHeight===0||e.currentTarget.scrollHeight-allclientHeight===1||e.currentTarget.scrollHeight-allclientHeight===-1){ - let {page,collaboratorList,user_name,school_name,Searchadmin} = this.state; - let newpage=page+1; - let newSearchadmin=Searchadmin - let id = this.props.match.params.shixunId; - let url = "/shixuns/" + id + "/add_collaborators.json"; - axios.get(url,{params:{ - user_name:user_name , - school_name:school_name, - page:newpage - }}).then((response) => { - if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { - - }else{ - let newlist = response.data.users; - for (var i = 0; i < newlist.length; i++) { - newlist[i].checked = false - newSearchadmin.push(newlist[i]) - } - - this.setState({ - Searchadmin: newSearchadmin, - collaboratorList: collaboratorList, - page:newpage - }) - } - - }).catch((error) => { - console.log(error) - }); - - } - - } - - render() { - let { - collaboratorList, - Collaboratorsvisible, - Collaboratorsvisibleadmin, - Searchadmin, - allChangechecked, - Collaboratorslist, - Collaboratorslisttype, - collaborators_deletetype, - onSearchcalue, - collaboratorListsum, - collaboratorListsumtype, - user_name, - school_name, - useristrue - } = this.state; - let {loadingContent} = this.props; - const radioStyle = { - display: 'block', - height: '30px', - lineHeight: '30px', - }; - - const antIcon = ; - - console.log(Searchadmin) - return ( - -

    - this.showCollaboratorsvisible("cooperation")} - className="edu-default-btn edu-greenback-btn fr mr20 height40" - data-remote="true"> - + 添加合作者 - - this.showCollaboratorsvisible("admin")} - style={{display:this.props.identity===1?"block":"none"}} - data-remote="true" - className="edu-default-btn edu-greenback-btn fr mr20 height40">更换管理员 -

    - - - -
    - 选择的成员将会成为新的管理员
    您将不再拥有管理员的权限,但您仍是合作团队的一员 -
    + contentViewScrolledit = (e) => { + //滑动到底判断 + let newscrollTop = parseInt(e.currentTarget.scrollTop); + let allclientHeight = e.currentTarget.clientHeight + newscrollTop; -
    -
      -
    • - + if (e.currentTarget.scrollHeight - allclientHeight === 0 || e.currentTarget.scrollHeight - allclientHeight === 1 || e.currentTarget.scrollHeight - allclientHeight === -1) { + let {page, collaboratorList, user_name, school_name, Searchadmin} = this.state; + let newpage = page + 1; + let newSearchadmin = Searchadmin + let id = this.props.match.params.shixunId; + let url = "/shixuns/" + id + "/add_collaborators.json"; + axios.get(url, { + params: { + user_name: user_name, + school_name: school_name, + page: newpage + } + }).then((response) => { + if (response.data.status === 403 || response.data.status === 401 || response.data.status === 500) { - { - Collaboratorslist.length === 0 ? "" : Collaboratorslist.map((item, key) => { - return ( - this.addadminredio(item.user_id)}>{item.name} - ) - }) - } + } else { + let newlist = response.data.users; + for (var i = 0; i < newlist.length; i++) { + newlist[i].checked = false + newSearchadmin.push(newlist[i]) + } + + this.setState({ + Searchadmin: newSearchadmin, + collaboratorList: collaboratorList, + page: newpage + }) + } - -
    • -
    -
    + }).catch((error) => { + console.log(error) + }); - + } -
    - - {Collaboratorsvisibleadmin===true? - {/* this.onSearchadmin(value)}*/} - {/*onInput={this.onSearchadmins}*/} - {/*style={{width: '100%'}}*/} - {/*/>*/} - 姓名或手机号: - - {this.setState({user_name: e.target.value})}} - style={{ width: '215px'}} - > - 单位: - {this.setState({school_name: e.target.value})}} - style={{ width: '215px'}} - > - - - this.onSearchadmin()} - style={{ height: '30px', lineHeight: '30px', width: '70px'}} - >搜索 -

    - 姓名 - 职业 - 单位 -

    -
    - -
    -
      - {Searchadmin === undefined ?
    • - 请试试搜索一下 -
    • :Searchadmin.length === 0 ?: Searchadmin.map((item, key) => { - return ( -
    • - this.selectChangenickname(e, key)} - id={item.user_id}> - {item.nickname} - {item.identify} - {item.school_name} -
    • - ) - }) - - } -
    -
    -
    -
    -
    - - - 全选 -
    - 请至少选择一个用户 -
    -
    - - -
    - {useristrue===true?请先选择用户:""} - -
    :""} - -
    - { - collaboratorList===undefined?"":collaboratorList.map((item,key)=>{ - if(key - - 用户头像 -
    -

    - {item.user.name} - - {item.user.shixun_manager === true ? "(管理员)" : ""} -

    - -

    {item.user.identity}{item.user.school_name}

    - -

    - 发布  {item.user.user_shixuns_count} - {/*粉丝  */} - {/*{item.user.fans_count}*/} - {/**/} -

    - - {/*

    {item.user.brief_introduction}

    */} - - -
    - - {item.user.shixun_manager === true ? "" : this.collaborators_delete(item.user.user_id)}>删除} - {/*取消关注*/} -
    - - ) - } - }) - } - -
    -
    确定要删除吗?
    -
    - - -
    -
    - -
    10&&collaboratorListsumtype===true?"":"none"} - style={{textAlign:'center',borderTop:'1px solid #eee'}}> - 加载更多 + } + onChangepageNumber=(pageNumber)=>{ + this.updatacomponentDiddata(pageNumber); + } + toggleHover=(type,key)=>{ + this.setState({ + hovertype:type, + hoverkey:type===false?undefined:key + }) + } + render() { + let { + collaboratorList, + Collaboratorsvisible, + Collaboratorsvisibleadmin, + Searchadmin, + allChangechecked, + Collaboratorslist, + Collaboratorslisttype, + collaborators_deletetype, + onSearchcalue, + collaboratorListsum, + collaboratorListsumtype, + user_name, + school_name, + useristrue, + mylistansum, + page, + limit + } = this.state; + let {loadingContent} = this.props; + const radioStyle = { + display: 'block', + height: '30px', + lineHeight: '30px', + }; + + const antIcon = ; + + // console.log(collaboratorList) + return ( + + +
    +

    +

    +

    {collaboratorList && collaboratorList.member_count}人

    +
    + + + +

    + + + +
    + 选择的成员将会成为新的管理员
    您将不再拥有管理员的权限,但您仍是合作团队的一员 +
    + + +
    +
      +
    • + + + { + Collaboratorslist.length === 0 ? "" : Collaboratorslist.map((item, key) => { + return ( + this.addadminredio(item.user_id)}>{item.name} + ) + }) + } + + +
    • +
    +
    + + + +
    + + {Collaboratorsvisibleadmin === true ? + {/* this.onSearchadmin(value)}*/} + {/*onInput={this.onSearchadmins}*/} + {/*style={{width: '100%'}}*/} + {/*/>*/} + 姓名或手机号: + + { + this.setState({user_name: e.target.value}) + }} + style={{width: '215px'}} + > + 单位: + { + this.setState({school_name: e.target.value}) + }} + style={{width: '215px'}} + > + + + this.onSearchadmin()} + style={{height: '30px', lineHeight: '30px', width: '70px'}} + >搜索 +

    + 姓名 + 职业 + 单位 +

    + + +
    + +
    +
      + {Searchadmin === undefined ?
    • + 请试试搜索一下 +
    • : Searchadmin.length === 0 ? : Searchadmin.map((item, key) => { + return ( +
    • + this.selectChangenickname(e, key)} + id={item.user_id}> + {item.nickname} + {item.identify} + {item.school_name} +
    • + ) + }) + + } +
    +
    +
    +
    +
    + + + 全选 +
    + 请至少选择一个用户
    +
    + + +
    + {useristrue === true ? 请先选择用户 : ""} + +
    : ""} + +
    + { + collaboratorList === undefined || collaboratorList.length === 0 ? "" : collaboratorList.members.map((item, key) => { + return ( +
    + + 用户头像 + +

    {item.user.shixun_manager === true ? "创建者" : ""}

    +
    + + +
    +

    + {item.user.name} + {item.user.shixun_manager === true ? "" : + this.collaborators_delete(item.user.user_id)} + onMouseEnter={()=>this.toggleHover(true,key)} + onMouseLeave={()=>this.toggleHover(false,key)} + > } + {/*{item.user.shixun_manager === true ? "创建者" : ""}

    */} +

    + +

    +

    + {/*

    {item.user.identity}

    */} +

    {item.user.school_name}

    +

    发布实训项目  {item.user.user_shixuns_count}

    +

    + {/*删除n12.17*/} + {/*
    */} + {/* {item.user.shixun_manager === true ? "" :*/} + + {/* this.collaborators_delete(item.user.user_id)}>*/} + {/* */} + {/* }*/} + {/*
    */} + +

    + {/*

    */} + {/* */} + {/* /!*粉丝  *!/*/} + {/* /!*{item.user.fans_count}*!/*/} + {/* /!**!/*/} + {/*

    */} + {/*

    {item.user.brief_introduction}

    */} +
    + {/*取消关注*/} +
    + ) + }) + } + +
    +
    确定要删除吗?
    +
    + + +
    +
    + + {/* 10 && collaboratorListsumtype === true ? "" : "none"}*/} + {/* style={{textAlign: 'center', borderTop: '1px solid #eee'}}>*/} + {/* 加载更多*/} + {/* /!*{*!/*/} + {/* /!* mylistansum>5?*!/*/} + {/* /!*
    *!/*/} + {/* /!* *!/*/} + {/* /!*
    *!/*/} + {/* /!* :""*!/*/} + {/* /!*}*!/*/} + + {/*
    */} +
    + + {collaboratorList && collaboratorList.member_count>10?
    + +
    :""} + - ); - } + ); + } } export default Collaborators; diff --git a/public/react/src/modules/tpm/shixunchild/Propaedeutics/Propaedeu_tics.js b/public/react/src/modules/tpm/shixunchild/Propaedeutics/Propaedeu_tics.js index f40e9fa63..c28833d96 100644 --- a/public/react/src/modules/tpm/shixunchild/Propaedeutics/Propaedeu_tics.js +++ b/public/react/src/modules/tpm/shixunchild/Propaedeutics/Propaedeu_tics.js @@ -79,15 +79,27 @@ class Propaedeutics extends Component {

    - - + + {/**/} + 编辑 +

    + + { loadingContent ? : -
    +
    {PropaedeuticsListcontent===undefined?"":

    diff --git a/public/react/src/modules/tpm/shixunchild/Repository/RepositoryAddFile.js b/public/react/src/modules/tpm/shixunchild/Repository/RepositoryAddFile.js index 9ca535bb4..e074908e3 100644 --- a/public/react/src/modules/tpm/shixunchild/Repository/RepositoryAddFile.js +++ b/public/react/src/modules/tpm/shixunchild/Repository/RepositoryAddFile.js @@ -135,6 +135,9 @@ class RepositoryAddFile extends Component { .breadcrumb .ant-breadcrumb-separator{ margin:0px 2px; } + .backgroudwhite{ + display:none; + } /*.filecode .CodeMirror.cm-s-railscasts{ border:1px solid #E5E5E5; } diff --git a/public/react/src/modules/tpm/shixunchild/Repository/TPMRepositoryCommits.js b/public/react/src/modules/tpm/shixunchild/Repository/TPMRepositoryCommits.js index 663c5fcf3..37a4217a6 100644 --- a/public/react/src/modules/tpm/shixunchild/Repository/TPMRepositoryCommits.js +++ b/public/react/src/modules/tpm/shixunchild/Repository/TPMRepositoryCommits.js @@ -1,145 +1,146 @@ -import React, { Component } from 'react'; - -import { Redirect } from 'react-router'; - -import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom"; - -import PropTypes from 'prop-types'; - -import classNames from 'classnames'; - -import axios from 'axios'; - -import TPMNav from '../../component/TPMNav' -import TPMRightSection from '../../component/TPMRightSection' -import { CircularProgress } from 'material-ui/Progress'; - -import { trace_collapse } from 'educoder' -const $ = window.$; - -// 点击按钮复制功能 -function jsCopy(){ - var e = document.getElementById("copy_rep_content"); - e.select(); - document.execCommand("Copy"); -} -class TPMRepositoryCommits extends Component { - constructor(props) { - super(props) - this.state = { - RepositoryList: undefined, - } - } - componentDidMount() { - let id = this.props.match.params.shixunId; - - let collaborators=`/shixuns/`+id+`/commits.json`; - axios.post(collaborators, { - secret_repository: this.props.secret_repository_tab - }).then((response)=> { - - if(response.status===200){ - this.setState({ - RepositoryList: response.data - }); - } - trace_collapse('repo commits res', response.data) - - }).catch((error)=>{ - console.log(error) - }); - - } - render() { - const { loadingContent, creator, shixun, myshixun, recommend_shixuns, current_user, watched, - aboutFocus, user, match - } = this.props; - let { RepositoryList } = this.state; - return ( - - -

    - {/* 可能会影响到其他页面的样式,需要测试、协商 */} -
    - - { loadingContent ? - - : - -
    -
    - - 提交记录 - - {/*  35 */} - - 返回 - -
    - - -
    -
      - { RepositoryList === undefined ? "" : RepositoryList.commits.map( (item, key)=>{ - return ( -
    • - {item.email} -

      - {item.title} -

      - {item.time} - -
      -
    • ) - }) - } -
    -
    -
    - } -
    - -
    - -
    -
    - - - - - ); - } -} - -/** - { RepositoryList === undefined ? "" : RepositoryList.commits.map( (item, key)=>{ - // {"email":"李暾","title":"2\n","id":"80cb6fc55a14bdd64a9c99913f416966238ed3de","time":"49年前"} - return ( -
    -
    {item.email}
    -
    {item.title}
    -
    {item.id}
    -
    {item.time}
    -
    - ) - }) - */ -export default TPMRepositoryCommits; +import React, { Component } from 'react'; + +import { Redirect } from 'react-router'; + +import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom"; + +import PropTypes from 'prop-types'; + +import classNames from 'classnames'; + +import axios from 'axios'; + +import TPMNav from '../../component/TPMNav' +import TPMRightSection from '../../component/TPMRightSection' +import { CircularProgress } from 'material-ui/Progress'; + +import { trace_collapse } from 'educoder' +const $ = window.$; + +// 点击按钮复制功能 +function jsCopy(){ + var e = document.getElementById("copy_rep_content"); + e.select(); + document.execCommand("Copy"); +} +class TPMRepositoryCommits extends Component { + constructor(props) { + super(props) + this.state = { + RepositoryList: undefined, + } + } + componentDidMount() { + let id = this.props.match.params.shixunId; + + let collaborators=`/shixuns/`+id+`/commits.json`; + axios.post(collaborators, { + secret_repository: this.props.secret_repository_tab + }).then((response)=> { + + if(response.status===200){ + this.setState({ + RepositoryList: response.data + }); + } + trace_collapse('repo commits res', response.data) + + }).catch((error)=>{ + console.log(error) + }); + + } + render() { + const { loadingContent, creator, shixun, myshixun, recommend_shixuns, current_user, watched, + aboutFocus, user, match + } = this.props; + let { RepositoryList } = this.state; + return ( + + +
    + {/* 可能会影响到其他页面的样式,需要测试、协商 */} +
    + + { loadingContent ? + + : + +
    +
    + + 提交记录 + + {/*  35 */} + + 返回 + +
    + + +
    +
      + { RepositoryList === undefined ? "" : RepositoryList.commits.map( (item, key)=>{ + return ( +
    • + {item.email} +

      + {item.title} +

      + {item.time} + +
      +
    • ) + }) + } +
    +
    +
    + } +
    + +
    + +
    +
    + + +
    + + ); + } +} + +/** + { RepositoryList === undefined ? "" : RepositoryList.commits.map( (item, key)=>{ + // {"email":"李暾","title":"2\n","id":"80cb6fc55a14bdd64a9c99913f416966238ed3de","time":"49年前"} + return ( +
    +
    {item.email}
    +
    {item.title}
    +
    {item.id}
    +
    {item.time}
    +
    + ) + }) + */ +export default TPMRepositoryCommits; diff --git a/public/react/src/modules/tpm/shixunchild/shixunchildCss/Challenges.css b/public/react/src/modules/tpm/shixunchild/shixunchildCss/Challenges.css index 493a95301..90524719c 100644 --- a/public/react/src/modules/tpm/shixunchild/shixunchildCss/Challenges.css +++ b/public/react/src/modules/tpm/shixunchild/shixunchildCss/Challenges.css @@ -25,4 +25,164 @@ .addshixuns{ height: 27px; line-height: 25px; -} \ No newline at end of file +} +.challenbaocun{ + width:103px; + height:30px; + background:#29BD8B; + border-radius:3px; + cursor:pointer +} +.challenbaocuntest{ + width:103px; + height:30px; + font-size:16px; + color:#FFFFFF; + text-align: center; + line-height:30px; + cursor:pointer +} +.renwuxiangqdiv{ + width:72px; + height:30px; + font-size:18px; + color:#000000; + line-height:30px; +} +.renwuxiangqdivtest{ + height:30px; + font-size:16px; + line-height:30px; +} + +.renwuxiangssi{ + width: 50%; +} +.renwuxiangssit{ + width: 50%; +} + +.pb47{ + padding-bottom: 47px; +} +.colorbluetwo{ + color: #1E8FFD; + font-size: 12px; + cursor:pointer; + margin-right: 18px; +} +.colorbluetest{ + color: #06101A; + font-size: 12px; + cursor:default +} +.shixunbingbaocun{ + font-size:14px; + color:#888888; +} +.intermediatecenter{ + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} +.iconfontysl{ + vertical-align:middle; + font-family:"iconfont" !important; + font-style:normal; + -webkit-font-smoothing: antialiased; + -webkit-text-stroke-width: 0.2px; + font-size: 100px; + color: #F5F5F5; +} +.juplbool{ + position: relative; +} + +.juplboolp{ + position: absolute; + bottom: 21px; +} + + +.shixunjianjie{ + height: 76px; + line-height: 35px; + padding: 20px; + border-bottom: 1px solid #eeee; + /*margin-bottom: 10px;*/ +} + +.pd20{ + padding:20px; +} + +.shixunbingbaocun12{ + font-size:12px; + color:#888888; +} + +.shixunbingbaocun33312{ + font-size:12px; + color:#333333 +} + +.shixunjianjiecballenges{ + height: 76px; + line-height: 35px; + padding: 20px; + border-bottom: 1px solid #eeee; +} + +.padding1020pxshixun{ + padding: 10px 20px 0px 20px; +} + +.shixunstartbutton33BD8C{ + background: #33BD8C !important; + border: #33BD8C !important; +} + +.shixunstartbuttonFF6601{ + background: #FF6601 !important; + border: #FF6601 !important; +} + +.shixunstartbutton666666{ + font-size:14px; + color:#666666; +} + +.newedu-nodata-img{ + width: 300px; + margin: 50px 0px; + display: block; + margin-left: 41%; +} + +.fonthiddens{ + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.icon-bianji_Hover,.icon-xiayi_Hover,.icon-shangyi_Hover,.icon-shanchu_Hover{ + color:#BBBBBB !important; +} + +.icon-bianji_Hover:hover{ + color: rgb(80, 145, 255) !important; +} + +.icon-xiayi_Hover:hover{ + color: rgb(51, 189, 140) !important; +} + +.icon-shangyi_Hover:hover{ + color: rgb(51, 189, 140) !important; +} + +.icon-shanchu_Hover:hover{ + color:rgb(255, 85, 85) !important; +} + diff --git a/public/react/src/modules/tpm/shixuns/ShixunCard.js b/public/react/src/modules/tpm/shixuns/ShixunCard.js index 9f62ed6b7..dc07759cb 100644 --- a/public/react/src/modules/tpm/shixuns/ShixunCard.js +++ b/public/react/src/modules/tpm/shixuns/ShixunCard.js @@ -79,9 +79,18 @@ class ShixunCard extends Component {
    :""} -
    +
    {middleshixundata === undefined || middleshixundata.length === 0?" ":middleshixundata.map((item,key)=>{ @@ -95,6 +104,33 @@ class ShixunCard extends Component { left: 10px; bottom: 125px; } + .tag-org{ + position: absolute; + left: 0px; + top: 20px; + } + .tag-org-name{ + width:66px; + height:28px; + background:#FF6802; + width:66px; + height:28px; + border-radius:0px 20px 20px 0px; + } + .tag-org-name-test{ + width:45px; + height:23px; + font-size:14px; + color:#FFFFFF; + line-height:19px; + margin-right: 6px; + } + .intermediatecenter{ + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } ` } @@ -105,6 +141,14 @@ class ShixunCard extends Component { {/**/}
    } + { + item.is_jupyter===true? +
    +

    Jupyter

    + {/**/} +
    + :""} +
    diff --git a/public/react/src/modules/tpm/shixuns/ShixunCardList.js b/public/react/src/modules/tpm/shixuns/ShixunCardList.js index d95ef75fe..937e96894 100644 --- a/public/react/src/modules/tpm/shixuns/ShixunCardList.js +++ b/public/react/src/modules/tpm/shixuns/ShixunCardList.js @@ -160,8 +160,42 @@ class ShixunCardList extends Component { this.props.Shixunsupcircles("asc") } } + + //头部获取是否已经登录了 + getUser=(url,type)=>{ + if(this.props.checkIfLogin()===false){ + this.props.showLoginDialog() + return + } + if(this.props.checkIfProfileCompleted()===false){ + this.props.showProfileCompleteDialog() + return + } + + if(url !== undefined || url!==""){ + window.location.href = url; + } + + + } render(){ let {mine,InputValue,upcircle}=this.state; + + // console.log("NewHeadermygetHelmetapi123123123123"); + let shixuntype=false; + if(this.props&&this.props.mygetHelmetapi!=null){ + let shixun="/shixuns"; + let paths="/paths"; + let courses="/courses"; + this.props.mygetHelmetapi.navbar.map((item,key)=>{ + var reg = RegExp(item.link); + if(shixun.match(reg)){ + if(item.hidden===true){ + shixuntype=true + } + } + }) + } return (
    @@ -184,7 +218,7 @@ class ShixunCardList extends Component { id="hot" onClick={(e)=>this.latestHot(e,3)}>最热
    - + {shixuntype===true?"":this.getUser("/shixuns/new")}>+新建实训项目} {/*
  • this.diff_search(0)}>全部难度
  • -
  • this.diff_search(1)}>初级学员
  • -
  • this.diff_search(2)}>中级学员
  • -
  • this.diff_search(3)}>高级学员
  • -
  • this.diff_search(4)}>顶级学员
  • +
  • this.diff_search(1)}>初级
  • +
  • this.diff_search(2)}>中级
  • +
  • this.diff_search(3)}>中高级
  • +
  • this.diff_search(4)}>高级
  • diff --git a/public/react/src/modules/tpm/shixuns/ShixunsIndex.js b/public/react/src/modules/tpm/shixuns/ShixunsIndex.js index 15579610d..7b6e54bc8 100644 --- a/public/react/src/modules/tpm/shixuns/ShixunsIndex.js +++ b/public/react/src/modules/tpm/shixuns/ShixunsIndex.js @@ -394,6 +394,8 @@ class ShixunsIndex extends Component { parsedid={parsedid} newtag_level={newtag_level} newpalce={newpalce} + {...this.props} + {...this.state} /> - + {/*下方图片*/} { + this.props.modalCancel() + } + + + + + render() { + + return( + +
    + { + this.props.itemtypebool===true? + +

    文件名重复

    + + :"" + } +

    {this.props.tittest}

    + +
    +
    + ) + } +} + +export default Tpmdatasetmodel; diff --git a/public/react/src/modules/tpm/tpmmodel/common.css b/public/react/src/modules/tpm/tpmmodel/common.css new file mode 100644 index 000000000..66780b2da --- /dev/null +++ b/public/react/src/modules/tpm/tpmmodel/common.css @@ -0,0 +1,318 @@ +/*.login_register_content, .login_register_content .ant-tabs-tabpane {*/ +/* !*display: flex;*!*/ +/* justify-content: center;*/ +/*}*/ +.login_register_content .ant-input { + background:rgb(244,244,244); +} +.login_register_content .loginInputzhucheyslass{ + border:1px solid red !important; +} +.login_register_content .loginInputzhucheyslass:hover{ + border:1px solid red !important; +} +.login_register_content { + width: 434px; + box-shadow:3px 10px 21px 0px rgba(76,76,76,0.15); + border-radius:6px; + background: #fff; +} + .login_register_content .ant-tabs-ink-bar { + width: 21px !important; + left: 19px; + } + .login_register_content .ant-tabs { + width: 354px; + } + + .login_section { + width: 100%; + display:flex; + justify-content: center; + align-items: center; + flex-direction: column; + } +.login_sectionysl{ + width: 100%; + display:flex; + align-items: center; + flex-direction: column; +} + .loginInput { + width: 100%; + margin-bottom: 16px; + height: 45px; + } + + .educouddiv { + display: flex; + flex-direction: column; + } + + + .left_right { + width: 100%; + display: flex; + justify-content: space-between; + } + .login_btn { + width: 100%; + margin-top: 26px; + margin-bottom: 26px; + } + .dragValidator { + margin-bottom: 16px; + } + .ysldivhome1{ + display: flex; + flex-direction: row; + margin-left: 100px; + margin-right: 129px; + } + .ysldivhome2{ + width: 800px; + display: flex; + flex-flow: row wrap; + align-content:stretch; + } + .ysldivhomediv{ + width: 101px; + } + .ysldivhomediv1{ + width: 17%; + height: 100px; + border-radius:50%; + box-shadow:3px 10px 21px 0px rgba(76,76,76,0.15); + background: #fff; + display: flex; + flex-direction:column; + margin-left: 6.5%; + margin-top: 1%; + } +.ysldivhomediv2{ + width: 17%; + height: 100px; + border-radius:50%; + box-shadow:3px 10px 21px 0px rgba(76,76,76,0.15); + background: #fff; + display: flex; + flex-direction:column; + margin-left: 6.5%; + margin-top: 3%; +} + .ysldivhomedivtxt{ + width:86%; + height:27px; + margin-bottom: 5px; + font-size: 14px; + text-align: center; + + } + .ysldivhomedivimgsy{ + + } + .ysldivhomedivimg{ + width: 80%; + } + .ysllogin_register_contents{ + display: flex; + justify-content: center; + + } + .ysllogin_section { + display: flex; + align-items: center; + flex-direction: column; + box-shadow:3px 10px 21px 0px rgba(76,76,76,0.15); + border-radius:6px; + background: #fff; + } + .yslspans1{ + text-align: center; + font-size: 13px; + color: #111C24; + } + .yslspans2{ + text-align: center; + font-size: 13px; + color: #05101A; + margin-top: 1%; + } + .yslspans3{ + text-align: center; + font-size: 12px; + color: #656565; + } + .yslbutton{ + width:255px; + height: 36px; + margin-top: 20px; + + } + .mt22{ + margin-top: 22px; + } + .gouxuanimg{ + margin-right: 10px; + margin-bottom: 2px; + } + .textall{ + text-align: center; + font-size: 13px; + color: #4B4B4B; + } + .div1img{ + display: flex; + justify-content:center; + width: 42%; + margin-left: 30%; + + } +.yslgouxuanimg{ + width: 20%; + height: 20px; + margin-left: 86%; + float: right; +} +.yslgouxuanimg2{ + height: 20px; +} + +.yslbutondls{ + display: flex; + flex-direction:row; +} +。yslinpulsy input{ + +} +.loginInputzhuche{ + width: 100%; + background-color: #fff!important; + height: 45px !important; + padding: 5px; + +} +.loginInputzhucheyslass { + width: 100%; + background-color: #fff!important; + height: 45px !important; + padding: 5px; + +} +.loginInputzhucheyslass .ant-input{ + width: 100%; + background-color: #fff!important; + height: 45px !important; + padding: 5px; + position: relative; + right: 5px; + width: 103%; + border: 1px solid #FF0000!important; + border-radius: 4px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.loginInputzhucheyslass .ant-input:hover{ + border: 1px solid #FF0000!important; + +} +.loginInputzhuche .ant-input{ + width: 100%; + background-color: #fff!important; + height: 45px !important; + padding: 5px; + position: relative; + right: 5px; + width: 103%; +} + +.loginInputzhucheysl{ + width: 100%; + background-color: #fff!important; + height: 45px !important; + padding: 5px; + border-radius: 4px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; + +} +.loginInputzhucheysl .ant-input{ + width: 100%; + background-color: #fff!important; + height: 45px !important; + padding: 5px; + position: relative; + right: 5px; + width: 103%; + border: 1px solid #FF0000!important; + border-radius: 4px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +.loginInputzhucheysl .ant-input:hover{ + border: 1px solid #FF0000!important; +} + +.bth100{ + width: 100px; + margin: 0 auto; +} +.ant-input-affix-wrapper .ant-input-prefix, .ant-input-affix-wrapper .ant-input-suffix { + background: transparent !important; +} + +.startlogin{ + color:#888; +} +.weixinheight390{ + height: 390px; +} +#log_reg_content { + padding: 38px 30px 20px !important; +} +.task-btn-blues{ + background:#5091FF; + color:#fff; + width:100px; + height:32px; + border-radius:4px; +} +a:hover.task-btn-blue-test{ + height:16px; + font-size:14px; + font-family:MicrosoftYaHei; + color:#FFFFff; + line-height:30px; +} +.task-btn-blue-test{ + height:16px; + font-size:14px; + font-family:MicrosoftYaHei; + color:#FFFFff; + line-height:30px; +} +.tabeltext-alignleftysl{ + font-size:14px; + color:#000000; + line-height:19px; + +} +.tabeltext-alignleftysltwo{ + font-size:14px; + color:#848282; + line-height:19px; + +} +/* 中间居中 */ +.intermediatecenter{ + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} diff --git a/public/react/src/modules/tpm/tpmmodel/tpmmodel.css b/public/react/src/modules/tpm/tpmmodel/tpmmodel.css new file mode 100644 index 000000000..168de71b3 --- /dev/null +++ b/public/react/src/modules/tpm/tpmmodel/tpmmodel.css @@ -0,0 +1,129 @@ +.tpmborder{ + border: 1px solid; +} +/* 中间居中 */ +.intermediatecenter{ + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} +/* 简单居中 */ +.intermediatecenterysls{ + display: flex; + align-items: center; +} +.spacearound{ + display: flex; + justify-content: space-around; + +} +.spacebetween{ + display: flex; + justify-content: space-between; +} +/* 头顶部居中 */ +.topcenter{ + display: -webkit-flex; + flex-direction: column; + align-items: center; + +} + + +/* x轴正方向排序 */ +/* 一 二 三 四 五 六 七 八 */ +.sortinxdirection{ + display: flex; + flex-direction:row; +} +/* x轴反方向排序 */ +/* 八 七 六 五 四 三 二 一 */ +.xaxisreverseorder{ + display: flex; + flex-direction:row-reverse; +} +/* 垂直布局 正方向*/ +/* 一 + 二 + 三 + 四 + 五 + 六 + 七 + 八 */ +.verticallayout{ + display: flex; + flex-direction:column; +} +/* 垂直布局 反方向*/ +.reversedirection{ + display: flex; + flex-direction:column-reverse; +} +.deletebutom{ + width:85px; + height:30px; + background:#C4C4C4; + border-radius:3px; + cursor:pointer + +} +.deletebutomtext{ + width:28px; + height:19px; + font-size:14px; + color:#FFFFFF; + line-height:19px; + cursor:pointer +} +.deletebuttom{ + width:85px; + height:30px; + background:#29BD8B; + border-radius:3px; + cursor:pointer +} +.deletebuttomtest{ + width:56px; + height:19px; + font-size:14px; + color:#FFFFFF; + line-height:19px; + cursor:pointer + +} +.tpmwidth{ + width: 50%; +} +.mr21{ + margin-right: 21px; +} + +.wenjiantit{ + width: 220px; +} +.zuihoushijian{ + width: 125px; +} +.zuihouxiugairen{ + width: 70px; +} +.wenjiandaxiao{ + width: 56px; +} +.deletebutomtextcode{ + width:85px; + height:30px; + background:#FF5555; + border-radius:3px; + cursor:pointer + +} +.light-row{ + background: #F7F7F8; +} +.dark-row{ + background: #FFFFFF; + +} diff --git a/public/react/src/modules/user/account/AccountBasicEdit.js b/public/react/src/modules/user/account/AccountBasicEdit.js index 033f433b0..7d6fc13d2 100644 --- a/public/react/src/modules/user/account/AccountBasicEdit.js +++ b/public/react/src/modules/user/account/AccountBasicEdit.js @@ -136,7 +136,7 @@ class AccountBasic extends Component { } handleSubmit = () => { - this.props.form.validateFieldsAndScroll((err, values) => { + this.props.form.validateFieldsAndScroll({ force: true }, (err, values) => { console.log(values); let {basicInfo}=this.props; if(!err ){ @@ -148,12 +148,12 @@ class AccountBasic extends Component { this.props.showNotification('请先选择院系/部门') return; } - + let url=`/users/accounts/${basicInfo.id}.json` axios.put((url),{ nickname:values.nickname, // 认证中的不能修改 - name: basicInfo.authentication == 'uncertified' ? + name: basicInfo.authentication == 'uncertified' ? (this.state.showRealName ? values.name : this.state.realName ) : basicInfo.name, show_realname:this.state.showRealName, gender:parseInt(values.sex), @@ -173,7 +173,7 @@ class AccountBasic extends Component { } this.props.getBasicInfo(); this.props.history.push('/account/profile') - + } }).catch((error)=>{ console.log(error); @@ -357,7 +357,7 @@ class AccountBasic extends Component { }) } - + addOrgSuccess = (name) => { // const schoolList = this.state.schoolList.slice(0) @@ -377,7 +377,7 @@ class AccountBasic extends Component { } showApplyForAddOrgModal = () => { - this.applyForAddOrgForm.setVisible(true) + this.applyForAddOrgForm.setVisible(true) } showApplyForAddChildOrgModal = () => { let{school,schoolList}=this.state; @@ -385,18 +385,18 @@ class AccountBasic extends Component { return item.name == school; }); if(arr.length > 0){ - this.applyForAddChildOrgForm.setVisible(true) + this.applyForAddChildOrgForm.setVisible(true) }else{ this.props.showNotification("请先选择正确的单位或者学校!"); } } - checkNameLength = (rule, value, callback) => { - if (value && value.length <= MAX_NAME_LENGTH) { - callback(); - return; - } - callback(`请输入真实姓名,最大限制${MAX_NAME_LENGTH}个字符`); - } + // checkNameLength = (rule, value, callback) => { + // if (value && value.length <= MAX_NAME_LENGTH) { + // callback(); + // return; + // } + // callback(`请输入真实姓名,最大限制${MAX_NAME_LENGTH}个字符`); + // } // 切换职称 changeJob=(e)=>{ this.setState({ @@ -410,9 +410,53 @@ class AccountBasic extends Component { }) } } - + //昵称 + handleSubmitName(rule, value, callback){ + if (value) { + let iconRule1 = /[`~!@#$%^&*()\-+=<>?:"{}|,.\/;'\\[\]·~!@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、]/im; +// 判断是否含有emoji表情 + let iconRule2 = /[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF][\u200D|\uFE0F]|[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF]|[0-9|*|#]\uFE0F\u20E3|[0-9|#]\u20E3|[\u203C-\u3299]\uFE0F\u200D|[\u203C-\u3299]\uFE0F|[\u2122-\u2B55]|\u303D|[\A9|\AE]\u3030|\uA9|\uAE|\u3030/ig; +// 如果为true,字符串含有emoji表情 ,false不含 + const iconRule2s =iconRule2.test(value); +// 如果为true,字符串含有特殊符号 ,false不 + const iconRule1s =iconRule1.test(value); + + if (iconRule2s===true|| iconRule1s===true) { + callback('2-20位中英文、数字及下划线'); + } + else if(value.length<2){ + callback('2-20位中英文、数字及下划线'); + }else if(value.length>=21){ + callback('2-20位中英文、数字及下划线'); + } + } + callback(); + } + + // 姓名 + handleSubmitNames(rule, value, callback){ + if (value) { + let iconRule1 = /[`~!@#$%^&()_\-+=<>?:"{}|,.\/;'\\[\]·~!@#¥%……&()——\-+={}|《》?:“”【】、;‘’,。、]/im; +// 判断是否含有emoji表情 + let iconRule2 = /[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF][\u200D|\uFE0F]|[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF]|[0-9|*|#]\uFE0F\u20E3|[0-9|#]\u20E3|[\u203C-\u3299]\uFE0F\u200D|[\u203C-\u3299]\uFE0F|[\u2122-\u2B55]|\u303D|[\A9|\AE]\u3030|\uA9|\uAE|\u3030/ig; +// 如果为true,字符串含有emoji表情 ,false不含 + const iconRule2s =iconRule2.test(value); +// 如果为true,字符串含有特殊符号 ,false不 + const iconRule1s =iconRule1.test(value); + + if (iconRule2s===true|| iconRule1s===true) { + callback('2-10位中英文、数字'); + } + else if(value.length<2){ + callback('2-10位中英文、数字'); + }else if(value.length>=11){ + callback('2-10位中英文、数字'); + } + } + callback(); + } render() { - let{ + let{ nameLength, showRealName, filterSchoolList, @@ -446,10 +490,10 @@ class AccountBasic extends Component { {...propsWithoutForm} addOrgSuccess={this.addOrgSuccess} > this.applyForAddChildOrgForm = form} + {...propsWithoutForm} wrappedComponentRef={(form) => this.applyForAddChildOrgForm = form} addChildOrgSuccess={this.addChildOrgSuccess} > - +
    @@ -350,6 +378,15 @@ class InfosShixun extends Component{ {/**/}
    } + { + item.is_jupyter===true? +
    +

    Jupyter

    + {/**/} +
    + :""} + + diff --git a/public/react/src/redux/actions/actionTypes.js b/public/react/src/redux/actions/actionTypes.js index f6bc7f3b9..1129a3235 100644 --- a/public/react/src/redux/actions/actionTypes.js +++ b/public/react/src/redux/actions/actionTypes.js @@ -22,7 +22,8 @@ const types = { SAVE_OJ_FORM: 'SAVE_OJ_FORM', // 保存表单 ADD_TEST_CASE: 'ADD_TEST_CASE', // 添加测试用例 DELETE_TEST_CASE: 'DELETE_TEST_CASE', // 删除测试用例 - SAVE_TEST_CASE: '保存测试用例', // 保存测试用例 + SAVE_TEST_CASE: 'SAVE_TEST_CASE', // 保存测试用例 + SAVE_USE_TEST_CASE_VALUE: 'SAVE_USE_TEST_CASE_VALUE', // 用户自定义测试用例值 CLEAR_JSFORM_STORE: 'CLEAR_JSFORM_STORE', // 清空测试用例 SAVE_EDIT_OJ_FORM_AND_TEST_CASE: 'SAVE_EDIT_OJ_FORM_AND_TEST_CASE', // 保存根据id获取的表单及测试用例值 TEST_CODE_STATUS: 'TEST_CODE_STATUS', // 代码调试状态 @@ -46,7 +47,20 @@ const types = { UPDATE_TEST_AND_VALIDATE: 'UPDATE_TEST_AND_VALIDATE', // 更新测试用例及验证值 UPDATE_OPEN_TESTCASE_INDEX: 'UPDATE_OPEN_TESTCASE_INDEX', // 更新测试用例索引 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_HACK_IDENTIFIER: 'SAVE_HACK_IDENTIFIER', // 用户界面跑到编辑界面需要用的id值 + SAVE_EDITOR_CODE: 'SAVE_EDITOR_CODE', // 保存详情页面中编辑时的代码 + /*** jupyter */ + GET_JUPYTER_DATA_SETS: 'GET_JUPYTER_DATA_SETS', // jupyter 数据集 + GET_JUPYTER_TPI_URL: 'GET_JUPYTER_TPI_URL', // 获取 jupyter url + SAVE_JUPYTER_IDENTIFIER: 'SAVE_JUPYTER_IDENTIFIER', // 保存jupyter identifier + SAVE_JUPYTER_INFO: 'SAVE_JUPYTER_INFO', // 保存 jupyter 信息 + CHANGE_JUPYTER_URL_STATE: 'CHANGE_JUPYTER_URL_STATE', // 获取url返回的状态值 + SAVE_JUPYTER_TPI: 'SAVE_JUPYTER_TPI', // 保存 jupyter tpi + CHANGE_JUPYTER_CURRENT_PAGE: 'CHANGE_JUPYTER_CURRENT_PAGE', + SAVE_NOTICE_COUNT: 'SAVE_NOTICE_COUNT', // 保存代码块是否更新 + AUTO_UPDATE_CODE: 'AUTO_UPDATE_CODE', // 自动更新代码 } export default types; diff --git a/public/react/src/redux/actions/common.js b/public/react/src/redux/actions/common.js index bcd451481..b95d2d2bb 100644 --- a/public/react/src/redux/actions/common.js +++ b/public/react/src/redux/actions/common.js @@ -4,12 +4,12 @@ * @Github: * @Date: 2019-11-27 16:30:50 * @LastEditors: tangjiang - * @LastEditTime: 2019-11-28 21:15:34 + * @LastEditTime: 2019-12-19 19:42:10 */ import types from "./actionTypes"; // 切换控制台显示与隐藏 -export const showOrHideControl = (flag) => { +export const changeShowOrHideControl = (flag) => { return { type: types.SHOW_OR_HIDE_CONTROL, payload: flag diff --git a/public/react/src/redux/actions/index.js b/public/react/src/redux/actions/index.js index 2e113032d..b42c4c2b6 100644 --- a/public/react/src/redux/actions/index.js +++ b/public/react/src/redux/actions/index.js @@ -7,7 +7,11 @@ */ import toggleTodo from './testAction.js'; -import {getOJList, changePaginationInfo} from './ojList'; +import { + getOJList, + changePaginationInfo, + deleteItem +} from './ojList'; import { validateOjForm, saveOjFormCode, @@ -27,6 +31,7 @@ import { testCaseOutputChange, updateTestAndValidate, updateOpenTestCaseIndex, + handleClickCancelPublish, } from './ojForm'; import { @@ -41,20 +46,37 @@ import { getUserProgramDetail, saveUserProgramIdentifier, restoreInitialCode, + saveUserCodeForInterval, + saveEditorCodeForDetail, // isUpdateCodeCtx } from './ojForUser'; import { - showOrHideControl, + changeShowOrHideControl, changeLoadingState, changeSubmitLoadingStatus, changePublishLoadingStatus, isMyPublish, } from './common'; +import { + getUserInfoForNew +} from './user'; + +import { + getJupyterTpiDataSet, + getJupyterTpiUrl, + getJupyterInfo, + syncJupyterCode, + changeGetJupyterUrlState, + saveJupyterTpi, + changeCurrentPage +} from './jupyter'; + export default { toggleTodo, getOJList, + deleteItem, changePaginationInfo, getOJFormById, saveOJFormId, @@ -68,13 +90,14 @@ export default { validateOjTimeLimit, validateOjCategory, validateOpenOrNot, + handleClickCancelPublish, addTestCase, deleteTestCase, testCaseInputChange, testCaseOutputChange, debuggerCode, startProgramQuestion, - showOrHideControl, + changeShowOrHideControl, changeLoadingState, getUserCommitRecord, getUserCommitRecordDetail, @@ -90,5 +113,16 @@ export default { updateOpenTestCaseIndex, saveUserProgramIdentifier, restoreInitialCode, + getUserInfoForNew, + saveUserCodeForInterval, + saveEditorCodeForDetail, + // jupyter + getJupyterTpiDataSet, + getJupyterTpiUrl, + getJupyterInfo, + syncJupyterCode, + changeGetJupyterUrlState, + saveJupyterTpi, + changeCurrentPage // isUpdateCodeCtx } \ No newline at end of file diff --git a/public/react/src/redux/actions/jupyter.js b/public/react/src/redux/actions/jupyter.js new file mode 100644 index 000000000..57c12e6d3 --- /dev/null +++ b/public/react/src/redux/actions/jupyter.js @@ -0,0 +1,157 @@ +/* + * @Description: jupyter tpi 相关内容 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-12 09:01:30 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-13 23:03:27 + */ +import types from "./actionTypes"; +import { message } from 'antd'; +import { + fetchJupyterTpiDataSet, + fetchJupyterTpiUrl, + fetchJupyterInfo, + fetchSyncJupyterCode, + fetchSaveJupyterTpi +} from "../../services/jupyterServer"; + +// 获取 jupyter 相关信息 +export const getJupyterInfo = (id) => { + return (dispatch, getState) => { + const { jupyter_pagination } = getState().jupyterReducer; + console.log(jupyter_pagination); + fetchJupyterInfo(id).then(res => { + if (res.data.status === 401) return; + if (res.status === 200) { + const { data } = res; + // if (data.status === 0) { + dispatch({ + type: types.SAVE_JUPYTER_INFO, + payload: data + }); + const { identifier, myshixun_identifier } = data; + dispatch(saveJupyterIdentifier(identifier)); + // 调用获取数据集接口 + dispatch(getJupyterTpiDataSet(identifier, jupyter_pagination)); + // 调用获取url接口 + dispatch(getJupyterTpiUrl({identifier: myshixun_identifier})); + // } + } + }) + } +} +// 获取 jupyter tpi 数据集 +export const getJupyterTpiDataSet = (identifier, params) => { + return (dispatch, getState) => { + if (!params) { + params = getState().jupyterReducer.jupyter_pagination; + } + fetchJupyterTpiDataSet(identifier, params).then(res => { + if (res.data.status === 401) return; // 用户未登录 + if (res.status === 200) { + const {data_sets, data_sets_count} = res.data; + dispatch({ + type: types.GET_JUPYTER_DATA_SETS, + payload: { + data_sets, + data_sets_count + } + }); + } + }); + } +} +// 获取 jupyter tpi 地址 +export const getJupyterTpiUrl = (obj) => { + return (dispatch, getState) => { + const {jupyter_info} = getState().jupyterReducer; + if (!obj.identifier && !jupyter_info.myshixun_identifier) return; + const id = obj.identifier || jupyter_info.myshixun_identifier; + fetchJupyterTpiUrl({identifier: id}).then(res => { + if (res.data.status === 401) return; // 用户未登录 + console.log('获取url', res); + if (res.status === 200) { + const { status, url = '', port } = res.data; + dispatch({ + type: types.GET_JUPYTER_TPI_URL, + payload: { + status, + url, + port + } + }) + } + }) + } +} +// 保存 jupyter identifer +export const saveJupyterIdentifier = (identifier) => { + return { + type: types.SAVE_JUPYTER_IDENTIFIER, + payload: identifier + } +} +// 重置代码 +export const syncJupyterCode = (identifier, msg) => { + return (dispatch) => { + fetchSyncJupyterCode(identifier).then(res => { + // console.log('同步代码成功: ', res); + if (res.data.status === 401) return; + if (res.status === 200) { + const {status} = res.data + if (status === 0) { + message.success(msg); + setTimeout(() => { + window.location.reload(); + }, 300); + } + } + }) + } +} +// 改变状态值 +export const changeGetJupyterUrlState = (status) => { + return { + type: types.CHANGE_JUPYTER_URL_STATE, + payload: status + } +} +// 保存 jupyter tpi +export const saveJupyterTpi = () => { + return (dispatch, getState) => { + + const { jupyter_tpi_code, jupyter_info }= getState().jupyterReducer; + // console.log(jupyter_info.myshixun_identifier, jupyter_tpi_code); + if (!jupyter_info.myshixun_identifier) return; + const params = { + identifier: jupyter_info.myshixun_identifier, + jupyter_port: jupyter_tpi_code + }; + console.log(params); + fetchSaveJupyterTpi(params).then(res => { + dispatch({ + type: types.LOADING_STATUS, + payload: false + }); + if (res.status === 200) { + const { data } = res; + if (data.status === 0) { + message.success('保存成功!') + } + } + }).catch(() => { + dispatch({ + type: types.LOADING_STATUS, + payload: false + }); + }); + } +} +// 改变当前页数 +export const changeCurrentPage = (current) => { + return { + type: types.CHANGE_JUPYTER_CURRENT_PAGE, + payload: current + } +} \ No newline at end of file diff --git a/public/react/src/redux/actions/ojForUser.js b/public/react/src/redux/actions/ojForUser.js index 7854210a3..9cb814d80 100644 --- a/public/react/src/redux/actions/ojForUser.js +++ b/public/react/src/redux/actions/ojForUser.js @@ -4,7 +4,7 @@ * @Github: * @Date: 2019-11-27 13:42:11 * @LastEditors: tangjiang - * @LastEditTime: 2019-12-04 15:50:54 + * @LastEditTime: 2019-12-19 15:11:56 */ import types from "./actionTypes"; import { Base64 } from 'js-base64'; @@ -19,6 +19,7 @@ import { fetchUserCodeSubmit, fetchRestoreInitialCode } from "../../services/ojService"; +import { notification } from "antd"; // 进入编程页面时,首先调用开启编程题接口 export const startProgramQuestion = (id, props) => { @@ -31,10 +32,24 @@ export const startProgramQuestion = (id, props) => { type: types.SAVE_USER_PROGRAM_ID, payload: identifier }); + // 保存id值 + dispatch({ + type: types.SAVE_HACK_IDENTIFIER, + payload: id + }); // 跳转至开启编程 - debugger; - props.history.push(`/myproblems/${identifier}`); - // Redirect.to + if (identifier) { + // let data = Object.assign({}, props); + // const path = { + // pathname: `/myproblems/${identifier}`, + // state: data + // } + // console.log(path); + // props.history.push(`/myproblems/${identifier}`); + props.history.push({ + pathname: `/myproblems/${identifier}`, + }); + } } }) } @@ -55,6 +70,7 @@ export const getUserProgramDetail = (identifier, type) => { fetchUserProgramDetail(identifier).then(res => { const { status, data = {} } = res; if (status === 200) { + if (data.status === 401) return; if (!type) { dispatch({ type: types.USER_PROGRAM_DETAIL, @@ -64,15 +80,63 @@ export const getUserProgramDetail = (identifier, type) => { dispatch({ type: types.GET_COMMIT_RECORD_DETAIL_BY_ID, payload: data - }) + }); } + // 保存默认测试用例 + dispatch({ + type: types.SAVE_USE_TEST_CASE_VALUE, + payload: data.test_case || {} + }); + // 代码是否更新 + let _modify_code = false; + if (data.hack) { + _modify_code = data.hack.modify_code; + } + dispatch({ + type: types.SAVE_NOTICE_COUNT, + payload: _modify_code + }) + // 保存用户登录信息 + dispatch({ + type: types.SAVE_USER_INFO, + payload: data.user + }); } }); } } +export const saveUserCodeForInterval = (identifier, code) => { + return (dispatch) => { + dispatch({ + type: types.AUTO_UPDATE_CODE, + payload: true + }); + fetchUpdateCode(identifier, { + code: Base64.encode(code) + }).then(res => { + if (res.data.status === 401) { + return; + }; + + setTimeout(() => { + dispatch({ + type: types.AUTO_UPDATE_CODE, + payload: false + }) + }, 1000); + console.log('代码保存成功', res); + }).catch(() => { + dispatch({ + type: types.AUTO_UPDATE_CODE, + payload: false + }) + }); + } +} + /** - * @description 更新代码 + * @description 保存或更新之前先更新代码 * @param {*} identifier * @param {*} inputValue 输入值: 自定义 | 系统返回的 * @param {*} type 测评类型 debug | submit @@ -86,6 +150,13 @@ export const updateCode = (identifier, inputValue, type) => { }).then(res => { // 是否更新了代码, 目的是当代码没有更新时不调用更新代码接口,目录没有实现 // TODO 需要优化 + if (res.data.status === 401) { + dispatch({ // 改变 loading 值 + type: types.LOADING_STATUS, + payload: false + }); + return; + }; dispatch({ type: types.IS_UPDATE_CODE, flag: false @@ -118,7 +189,7 @@ export const debuggerCode = (identifier,value, type) => { payload: 'loading' }); } - + fetchDebuggerCode(identifier, value).then(res => { // console.log('调用调试代码成功并返回结果: ', res); const { status } = res; @@ -132,7 +203,13 @@ export const debuggerCode = (identifier,value, type) => { * @param {*} count 执行次数 * @param {*} timer 定时器 */ - + if (res.data.status === 401) { + dispatch({ // 改变 loading 值 + type: types.LOADING_STATUS, + payload: false + }); + return; + }; function getCodeSubmit (intervalTime, finalTime, count, timer){ const excuteTime = (count++) * intervalTime; // 当前执行时间 fetchCodeSubmit(identifier, { mode: type }).then(res => { @@ -212,7 +289,11 @@ export const debuggerCode = (identifier,value, type) => { }).catch(() => { dispatch({ type: types.TEST_CODE_STATUS, - payload: '' + payload: 'error' + }); + dispatch({ + type: types.LOADING_STATUS, + payload: false }); dispatch({ type: types.SUBMIT_LOADING_STATUS, @@ -241,6 +322,12 @@ export const getUserCommitRecordDetail = (identifier) => { return (dispatch) => { fetchUserCommitRecordDetail(identifier).then(res => { console.log('提交记录详情======》》》》', res); + const { data } = res; + if (data.status === 401) return; + dispatch({ + type: types.GET_COMMIT_RECORD_DETAIL_BY_ID, + payload: data + }); }); } } @@ -280,6 +367,18 @@ export const submitUserCode = (identifier, inputValue, type) => { fetchUserCodeSubmit(identifier).then(res => { // console.log('用户提交代码成功======》》》》》', res); if (res.status === 200) { + if (res.data.status === 401) { + dispatch({ + type: types.SUBMIT_LOADING_STATUS, + payload: false + }); + return; + }; + // 将编辑代码清空 + dispatch({ + type: types.SAVE_EDITOR_CODE, + payload: '' + }); dispatch(debuggerCode(identifier, inputValue, type || 'submit')); } }).catch(() => { @@ -295,6 +394,13 @@ export const submitUserCode = (identifier, inputValue, type) => { }).then(res => { // 是否更新了代码, 目的是当代码没有更新时不调用更新代码接口,目录没有实现 // TODO 需要优化 + if (res.data.status === 401) { + dispatch({ + type: types.SUBMIT_LOADING_STATUS, + payload: false + }); + return; + }; dispatch({ type: types.IS_UPDATE_CODE, flag: false @@ -312,11 +418,38 @@ export const submitUserCode = (identifier, inputValue, type) => { } } -export const restoreInitialCode = (identifier) => { +// 恢复初始代码 +export const restoreInitialCode = (identifier, msg) => { return (dispatch) => { fetchRestoreInitialCode(identifier).then(res => { - console.log('恢复初始代码====》》》》', res); + if (res.data.status === 401) return; + // console.log('恢复初始代码====》》》》', res); + const {status, data} = res; + if (status === 200) { + dispatch({ + type: types.RESTORE_INITIAL_CODE, + payload: data.code + }); + notification.success({ + message: '提示', + description: msg + }); + dispatch({ + type: types.SAVE_NOTICE_COUNT, + payload: false + }); + } }); } } +// 保存详情页面中的编辑代码 +export const saveEditorCodeForDetail = (code) => { + return { + type: types.SAVE_EDITOR_CODE, + payload: code + } +} + +// 更新通知状态 + diff --git a/public/react/src/redux/actions/ojForm.js b/public/react/src/redux/actions/ojForm.js index 35a23fd2e..740bad895 100644 --- a/public/react/src/redux/actions/ojForm.js +++ b/public/react/src/redux/actions/ojForm.js @@ -4,13 +4,19 @@ * @Github: * @Date: 2019-11-20 16:35:46 * @LastEditors: tangjiang - * @LastEditTime: 2019-12-02 19:22:58 + * @LastEditTime: 2019-12-19 17:20:48 */ import types from './actionTypes'; import CONST from '../../constants'; -import { fetchPostOjForm, fetchGetOjById, publishTask } from '../../services/ojService'; +import { + fetchPostOjForm, + fetchGetOjById, + publishTask, + cancelPublicTask +} from '../../services/ojService'; import { Base64 } from 'js-base64'; -import { message, notification } from 'antd'; +import { notification } from 'antd'; +import { toStore } from 'educoder'; const { jcLabel } = CONST; // 表单字段映射 const maps = { @@ -79,11 +85,24 @@ const payloadInfo = (key, value, errMsg, validateInfo) => ({ } }); +// 接口调用成功后,跳转至列表页 +function linkToDev (dispatch, props) { + toStore('oj_description', ''); + dispatch({ + type: types.IS_MY_SOURCE, + payload: true + }); + setTimeout(() => { + props.history.push('/problems'); + }, 1000); +} + // 表单提交验证 export const validateOjForm = (props, type) => { return (dispatch, getState) => { const {ojForm, testCases, identifier, code } = getState().ojFormReducer; - console.log('code', code); + // console.log('code', code); + /** 表单验证开始 */ let keys = Object.keys(ojForm); // 循环判断每个字段是否为空 let hasSuccess = true; @@ -103,14 +122,35 @@ export const validateOjForm = (props, type) => { }); // 验证测试用例中的数组是否都有对应的值 const tcValidResult = []; - testCases.forEach(obj => { + // 验证测试用例: 1.必须要有输出值 2. 输入值与输出值必须唯一 + testCases.forEach((obj, i) => { let tempObj = {}; ['input', 'output'].forEach(key => { const value = obj[key]; - const validateResult = emptyValidate(key, value); + // 非空校验 + let validateResult = emptyValidate(key, value); const errMsg = validateResult[key].errMsg; if (errMsg) { hasSuccess = false; + } else { + // 唯一性校验 + const bool = testCases.some((item, j) => { + if (i !== j) { + return (item[key] === value); + } else { + return false; + } + }); + + if (bool) { + hasSuccess = false; + validateResult = { + [key]: { + validateStatus: 'error', + errMsg: key === 'input' ? '输入值必须是唯一的' : '输出值必须是唯一的' + } + }; + } } Object.assign(tempObj, validateResult); }); @@ -120,18 +160,18 @@ export const validateOjForm = (props, type) => { if (testCases.length === 0) { hasSuccess = false; notification['error']({ - message: '必填', + message: '提示', description: '测试用例必须输入!' }); } - if (!code) { - hasSuccess = false; - notification['error']({ - message: '必填', - description: '代码块内容必须输入!' - }); - } + // if (!code) { + // hasSuccess = false; + // notification['error']({ + // message: '必填', + // description: '代码块内容必须输入!' + // }); + // } // 更改测试用例验证结果 dispatch({ type: types.UPDATE_TEST_AND_VALIDATE, @@ -150,6 +190,8 @@ export const validateOjForm = (props, type) => { payload: false }); } + /** 表单验证结束 */ + /** 表单验证通过后,调用保存 or 更新 or 发布 */ if (hasSuccess) { // console.log('表单保存的数据为: ', getState()); const {ojFormReducer} = getState(); @@ -158,7 +200,7 @@ export const validateOjForm = (props, type) => { let paramsObj = {}; const hack = { // 编程题干 name, - description, + description: JSON.stringify(description), difficult, category, 'open_or_not': openOrNot, @@ -204,55 +246,83 @@ export const validateOjForm = (props, type) => { paramsObj['identifier'] = identifier; } - function linkToDev () { - dispatch({ - type: types.IS_MY_SOURCE, - payload: true + // 调用保存或更新 + if (type === 'publish') { + // 提示发布信息 + publishTask(identifier).then(res => { + if (res.data.status === 0) { + // message.success('发布成功!'); + notification.success({ + message: '提示', + description: '发布成功!' + }); + linkToDev(dispatch, props); + } + dispatch({ + type: types.PUBLISH_LOADING_STATUS, + payload: false + }); + }).catch(() => { + dispatch({ + type: types.PUBLISH_LOADING_STATUS, + payload: false + }); }); - setTimeout(() => { - props.history.push('/problems'); - }, 1000); - } - - fetchPostOjForm(paramsObj).then(res => { - // TODO - if (res.status === 200) { // 保存成功后,重新跳转至列表页 - const {identifier} = res.data - if (type === 'publish') { // 存在发布时,直接调用发布接口 - identifier && publishTask(identifier).then(res => { - if (res.status === 200) { - message.success('发布成功!'); - linkToDev(); - } - dispatch({ - type: types.PUBLISH_LOADING_STATUS, - payload: false + } else { + // 调用更新 + fetchPostOjForm(paramsObj).then(res => { + if (res.status === 200) { // 保存成功后,重新跳转至列表页 + if (res.data.status === 0) { + // message.success(paramsObj['submitType'] === 'update' ? '更新成功' : '保存成功'); + notification.success({ + message: '提示', + description: paramsObj['submitType'] === 'update' ? '更新成功' : '保存成功' }); - }).catch(() => { + linkToDev(dispatch, props); + } dispatch({ - type: types.PUBLISH_LOADING_STATUS, + type: types.SUBMIT_LOADING_STATUS, payload: false }); - }); - } else { - message.success('保存成功!'); - linkToDev(); + } + }).catch(err => { dispatch({ type: types.SUBMIT_LOADING_STATUS, payload: false }); } - } - - }).catch(err => { - dispatch({ - type: types.SUBMIT_LOADING_STATUS, - payload: false - }); - }); + ); + } } } }; +// 撤销发布 +export const handleClickCancelPublish = (props, identifier) => { + return (dispatch) => { + cancelPublicTask(identifier).then(res => { + dispatch({ + type: types.PUBLISH_LOADING_STATUS, + payload: false + }); + if (res.status = 200) { + const { data} = res; + if (data.status === 0) { + // message.success('撤销发布成功!'); + notification.success({ + message: '提示', + description: '撤销发布成功!' + }); + linkToDev(dispatch, props); + } + } + }).catch(() => { + dispatch({ + type: types.PUBLISH_LOADING_STATUS, + payload: false + }); + }) + } +} // 保存提交的代码 export const saveOjFormCode = (value) => { return { @@ -350,6 +420,11 @@ export const getOJFormById = (id) => { type: types.SAVE_EDIT_OJ_FORM_AND_TEST_CASE, payload: res.data }); + // 保存用户信息 + dispatch({ + type: types.SAVE_USER_INFO, + payload: res.data.user + }); }); } } @@ -360,7 +435,7 @@ export const saveOJFormId = (id) => { payload: id } } -// 清空测试用例集合 +// 清空值 export const clearOJFormStore = () => { return { type: types.CLEAR_JSFORM_STORE @@ -369,27 +444,84 @@ export const clearOJFormStore = () => { // 测试用例输入值改变时 export const testCaseInputChange = (value, index) => { - const validate = emptyValidate('input', value)['input']; - return { - type: types.TEST_CASE_INPUT_CHANGE, - payload: { - input: validate, - value, - index + return (dispatch, getState) => { + // 非空校验 + let validate = emptyValidate('input', value)['input']; + if (!validate.errMsg) { + // 唯一性校验 + let _errMsg = ''; + const {testCases} = getState().ojFormReducer; + const bool = testCases.some((item, i) => { + if (i !== index) { + if (item['input'] === value) { + _errMsg=`与测试用例${index}的输入值重复了,请重新填写`; + } + return item['input'] === value; + } else { + return false; + } + }); + if (bool) { + validate = { + validateStatus: 'error', + errMsg: _errMsg + }; + } } + dispatch({ + type: types.TEST_CASE_INPUT_CHANGE, + payload: { + input: validate, + value, + index + } + }); } } // 测试用例输出值改变时 export const testCaseOutputChange = (value, index) => { - const validate = emptyValidate('output', value)['output']; - return { - type: types.TEST_CASE_OUTPUT_CHANGE, - payload: { - output: validate, - value, - index + // const validate = emptyValidate('output', value)['output']; + // return { + // type: types.TEST_CASE_OUTPUT_CHANGE, + // payload: { + // output: validate, + // value, + // index + // } + // } + return (dispatch, getState) => { + // 非空校验 + let validate = emptyValidate('output', value)['output']; + if (!validate.errMsg) { + // 唯一性校验 + const {testCases} = getState().ojFormReducer; + let _errMsg = ''; + const bool = testCases.some((item, i) => { + if (i !== index) { + if (item['output'] === value) { + _errMsg=`与测试用例${index}的输入值重复了,请重新填写`; + } + return item['output'] === value; + } else { + return false; + } + }); + if (bool) { + validate = { + validateStatus: 'error', + errMsg: _errMsg + }; + } } + dispatch({ + type: types.TEST_CASE_OUTPUT_CHANGE, + payload: { + output: validate, + value, + index + } + }); } } diff --git a/public/react/src/redux/actions/ojList.js b/public/react/src/redux/actions/ojList.js index 4839ebe04..63b64fd7b 100644 --- a/public/react/src/redux/actions/ojList.js +++ b/public/react/src/redux/actions/ojList.js @@ -4,10 +4,10 @@ * @Github: * @Date: 2019-11-20 10:48:24 * @LastEditors: tangjiang - * @LastEditTime: 2019-11-29 11:09:54 + * @LastEditTime: 2019-12-10 20:40:55 */ import types from './actionTypes'; -import { fetchOJList } from '../../services/ojService'; +import { fetchOJList, fetchDeleteOJItem } from '../../services/ojService'; export const getOJList = (params) => { return (dispatch) => { @@ -35,3 +35,15 @@ export const changePaginationInfo = (obj) => { payload: obj } } + +// 删除 +export const deleteItem = (identifier) => { + return (dispatch, getState) => { + const {pagination} = getState().ojListReducer; + fetchDeleteOJItem(identifier).then(res => { + if (res.status === 200) { + dispatch(getOJList(pagination)); + } + }); + } +} diff --git a/public/react/src/redux/actions/user.js b/public/react/src/redux/actions/user.js new file mode 100644 index 000000000..f7a0706e3 --- /dev/null +++ b/public/react/src/redux/actions/user.js @@ -0,0 +1,27 @@ +/* + * @Description: + * @Author: tangjiang + * @Github: + * @Date: 2019-12-06 15:09:22 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-09 20:34:50 + */ +import types from './actionTypes'; + +import { fetchUserInfoForNew } from '../../services/ojService'; + +// 获取用户信息 +export const getUserInfoForNew = () => { + return (dispatch) => { + // 调用获取用户信息, 如果没有登录直接调用登录,成功后保存当前用户信息 + fetchUserInfoForNew().then(res => { + // console.log('获取用户信息成功: ', res); + const { data } = res; + if (data.status === 401) return; + dispatch({ + type: types.SAVE_USER_INFO, + payload: data.user + }); + }) + } +} \ No newline at end of file diff --git a/public/react/src/redux/reducers/commonReducer.js b/public/react/src/redux/reducers/commonReducer.js index c551a803f..8a2e927cc 100644 --- a/public/react/src/redux/reducers/commonReducer.js +++ b/public/react/src/redux/reducers/commonReducer.js @@ -4,7 +4,7 @@ * @Github: * @Date: 2019-11-27 16:27:09 * @LastEditors: tangjiang - * @LastEditTime: 2019-12-03 11:00:19 + * @LastEditTime: 2019-12-12 17:36:51 */ import types from "../actions/actionTypes"; diff --git a/public/react/src/redux/reducers/index.js b/public/react/src/redux/reducers/index.js index 2f60bf8e3..9c28448a3 100644 --- a/public/react/src/redux/reducers/index.js +++ b/public/react/src/redux/reducers/index.js @@ -12,10 +12,15 @@ import ojFormReducer from './ojFormReducer'; import ojListReducer from './ojListReducer'; import ojForUserReducer from './ojForUserReducer'; import commonReducer from './commonReducer'; +import userReducer from './userReducer'; +import jupyterReducer from './jupyterReducer'; + export default combineReducers({ testReducer, ojFormReducer, ojListReducer, ojForUserReducer, - commonReducer + commonReducer, + userReducer, + jupyterReducer }); diff --git a/public/react/src/redux/reducers/jupyterReducer.js b/public/react/src/redux/reducers/jupyterReducer.js new file mode 100644 index 000000000..f8825fb36 --- /dev/null +++ b/public/react/src/redux/reducers/jupyterReducer.js @@ -0,0 +1,70 @@ +/* + * @Description: + * @Author: tangjiang + * @Github: + * @Date: 2019-12-12 09:01:39 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-13 15:28:45 + */ +import types from "../actions/actionTypes"; + +const initState = { + jupyter_tpi_url: '', + jupyter_info: {}, // 保存用户信息及实训相关的内容 + jupyter_data_set: [], + jupyter_identifier: '', + jupyter_tpi_url_state: -1, // 获取 url 状态值: 0 成功, 其它 失败 + jupyter_tpi_code: '', // 端口号 + jupyter_data_set_count: 1, // 数据集总数 + jupyter_pagination: { + page: 1, + limit: 20 // 默认加载20条 + } +}; + +const JupyterReducer = (state = initState, action) => { + switch (action.type) { + case types.GET_JUPYTER_DATA_SETS: + const { data_sets, data_sets_count } = action.payload; + return { + ...state, + jupyter_data_set: data_sets, + jupyter_data_set_count: data_sets_count + } + case types.GET_JUPYTER_TPI_URL: + const {url, status, port} = action.payload; + return { + ...state, + jupyter_tpi_url: url, + jupyter_tpi_url_state: status, + jupyter_tpi_code: port + } + case types.SAVE_JUPYTER_IDENTIFIER: + console.log('保存的jupyter_identifier', action.payload); + return { + ...state, + jupyter_identifier: action.payload + } + case types.SAVE_JUPYTER_INFO: + return { + ...state, + jupyter_info: action.payload + } + case types.CHANGE_JUPYTER_URL_STATE: + return { + ...state, + jupyter_tpi_url_state: action.payload + } + case types.CHANGE_JUPYTER_CURRENT_PAGE: + return { + ...state, + jupyter_pagination: Object.assign({}, state.jupyter_pagination, { page: action.payload }) + } + default: + return { + ...state + } + } +} + +export default JupyterReducer; diff --git a/public/react/src/redux/reducers/ojForUserReducer.js b/public/react/src/redux/reducers/ojForUserReducer.js index 8f6fe4b0c..0c7594dd2 100644 --- a/public/react/src/redux/reducers/ojForUserReducer.js +++ b/public/react/src/redux/reducers/ojForUserReducer.js @@ -4,7 +4,7 @@ * @Github: * @Date: 2019-11-27 13:41:48 * @LastEditors: tangjiang - * @LastEditTime: 2019-12-04 09:02:30 + * @LastEditTime: 2019-12-19 20:10:39 */ import types from "../actions/actionTypes"; import { Base64 } from 'js-base64'; @@ -20,9 +20,14 @@ const initialState = { userCodeTab: 'task', // 学员测评tab位置: task | record | comment userTestInput: '', // 用户自定义输入值 recordDetail: {}, // 根据id号获取的记录详情 + hack_identifier: '', // 用户界面编辑时 + editor_code: '', // 保存编辑代码 + notice: false, // 通知 + hadCodeUpdate: false, // 更新代码 }; const ojForUserReducer = (state = initialState, action) => { + let tempDetail = null; switch (action.type) { case types.SAVE_USER_PROGRAM_ID: return { @@ -33,7 +38,13 @@ const ojForUserReducer = (state = initialState, action) => { const { hack, test_case } = action.payload; const { code }= hack; let tempCode = Base64.decode(code) - Object.assign(hack, {code: tempCode}); + let tempDesc; + try { + tempDesc = JSON.parse(hack.description); + } catch (error) { + tempDesc = hack.description; + } + Object.assign(hack, {code: tempCode, description: tempDesc}); return { ...state, hack: Object.assign({}, hack), @@ -80,10 +91,59 @@ const ojForUserReducer = (state = initialState, action) => { userCodeTab: action.payload } case types.GET_COMMIT_RECORD_DETAIL_BY_ID: + tempDetail = action.payload; + if (tempDetail['error_msg']) { + tempDetail['error_msg'] = Base64.decode(tempDetail['error_msg']); + } + if (tempDetail['expected_output']) { + tempDetail['expected_output'] = Base64.decode(tempDetail['expected_output']); + } + if (tempDetail['output']) { + tempDetail['output'] = Base64.decode(tempDetail['output']); + } + if (tempDetail['code']) { + tempDetail['code'] = Base64.decode(tempDetail['code']); + } return { ...state, - recordDetail: action.payload + recordDetail: tempDetail } + case types.RESTORE_INITIAL_CODE: + const curHack = state.hack; + let restoreCode = action.payload + if (restoreCode) { + curHack['code'] = Base64.decode(restoreCode); + } + console.log('当前的代码内容:', curHack); + return { + ...state, + hack: Object.assign({}, curHack) + } + case types.SAVE_HACK_IDENTIFIER: + return { + ...state, + hack_identifier: action.payload + } + case types.SAVE_EDITOR_CODE: + return { + ...state, + editor_code: action.payload + } + case types.SAVE_USE_TEST_CASE_VALUE: + return { + ...state, + userTestInput: action.payload.input + } + case types.SAVE_NOTICE_COUNT: + return { + ...state, + notice: action.payload + }; + case types.AUTO_UPDATE_CODE: + return { + ...state, + hadCodeUpdate: action.payload + }; default: return state; } diff --git a/public/react/src/redux/reducers/ojFormReducer.js b/public/react/src/redux/reducers/ojFormReducer.js index 8b73a28b0..39af4a5f8 100644 --- a/public/react/src/redux/reducers/ojFormReducer.js +++ b/public/react/src/redux/reducers/ojFormReducer.js @@ -4,7 +4,7 @@ * @Github: * @Date: 2019-11-20 16:40:32 * @LastEditors: tangjiang - * @LastEditTime: 2019-12-04 15:36:26 + * @LastEditTime: 2019-12-17 16:19:04 */ import { Base64 } from 'js-base64'; import types from '../actions/actionTypes'; @@ -16,8 +16,8 @@ const init = { description: '', difficult: 1, category: 1, - openOrNot: 1, - timeLimit: '' + // openOrNot: 1, + timeLimit: 3 }, ojFormValidate: { name: { @@ -40,10 +40,10 @@ const init = { validateStatus: '', errMsg: '' }, - openOrNot: { - validateStatus: '', - errMsg: '' - }, + // openOrNot: { + // validateStatus: '', + // errMsg: '' + // }, timeLimit: { validateStatus: '', errMsg: '' @@ -64,7 +64,8 @@ const init = { identifier: '', // OJ表单id loading: false, // 僵尸loading标志 testCodeStatus: 'default', // 调试代码状态 default(默认值) | loading(加载中) | loaded(加载完成) | userCase(用户自定义测试用例) | finish(测试完成) - openTestCodeIndex: [0] // 展开的测试用例: 数组, 当出错时,展开所有出错的测试用例, 默认展开第一个 + openTestCodeIndex: [0], // 展开的测试用例: 数组, 当出错时,展开所有出错的测试用例, 默认展开第一个 + isPublish: 0, // 是否是发布状态: 0 未发布 1 已发布 } const tcValidateObj = { @@ -145,12 +146,16 @@ const ojFormReducer = (state = initialState, action) => { const { position } = action.payload; // 根据 position 去查找当前元素在数组中的位置 const index = state.testCases.findIndex((item) => item.position === position); + const tempTestCase = state.testCases || []; + const tempTestValicate = state.testCasesValidate || []; if (index > -1) { - state.testCases.splice(index, 1); // 删除当前元素 - state.testCasesValidate.splice(index, 1); // 删除测试用例对应的校验 + tempTestCase.splice(index, 1); // 删除当前元素 + tempTestValicate.splice(index, 1); // 删除测试用例对应的校验 } return { - ...state + ...state, + testCases: [...tempTestCase], + testCasesValidate: [...tempTestValicate] }; case types.SAVE_OJ_FORM_ID: state.identifier = action.payload; @@ -167,11 +172,11 @@ const ojFormReducer = (state = initialState, action) => { * 6. 更改测试用例状态 * 7. 添加测试用例验证 */ - const { code = '', description, language, name, hack_sets = [], time_limit, difficult, category } = action.payload; + const { code = '', description, language, name, hack_sets = [], time_limit, difficult, category, status } = action.payload; const currentOjForm = { name, // 任务名称 language, - description, + description: JSON.parse(description), difficult, category, openOrNot: 1, @@ -203,7 +208,8 @@ const ojFormReducer = (state = initialState, action) => { code: cbcode, testCases: curTestCases, testCasesValidate: curTcValidates, - testCodeStatus: hack_sets.length > 0 ? 'userCase' : 'default' + testCodeStatus: hack_sets.length > 0 ? 'userCase' : 'default', + isPublish: status } case types.CLEAR_JSFORM_STORE: state = Object.assign({}, init); @@ -262,12 +268,18 @@ const ojFormReducer = (state = initialState, action) => { } case types.UPDATE_TEST_AND_VALIDATE: // 保存或更新测试用例值 const tempValitate = action.payload.testCaseValidate; + const openColArrs = []; const curOjTestCaseValidate = state.testCasesValidate.map((tc, i) => { + if (tempValitate[i].input.errMsg || tempValitate[i].output.errMsg) { + openColArrs.push(i); + } return Object.assign({}, tc, tempValitate[i]); }); + // console.log('+++++++++++', openColArrs); return { ...state, - testCasesValidate: [...curOjTestCaseValidate] + testCasesValidate: [...curOjTestCaseValidate], + openTestCodeIndex: openColArrs } case types.UPDATE_OPEN_TESTCASE_INDEX: const tempArr = []; diff --git a/public/react/src/redux/reducers/userReducer.js b/public/react/src/redux/reducers/userReducer.js new file mode 100644 index 000000000..0e2718be9 --- /dev/null +++ b/public/react/src/redux/reducers/userReducer.js @@ -0,0 +1,31 @@ +/* + * @Description: 保存信息数据 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-06 15:09:29 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-06 15:16:15 + */ +import types from "../actions/actionTypes"; + +const initialState = { + userInfo: {} // 当前登录用户信息 +}; + +const userReducer = (state = initialState, action) => { + switch (action.type) { + case types.SAVE_USER_INFO: + return { + userInfo: action.payload + } + default: + return { + ...state + } + } +} + +export default userReducer; +export { + userReducer +}; diff --git a/public/react/src/search/SearchPage.js b/public/react/src/search/SearchPage.js index 1bf1d83d4..c5e405861 100644 --- a/public/react/src/search/SearchPage.js +++ b/public/react/src/search/SearchPage.js @@ -20,12 +20,13 @@ class SearchPage extends Component{ page:1, perpages:20, data:[], + jupyterbool:false, } } //切换tab changeTab=(e)=>{ - // course 课堂, shixun 开发社区 subject 实践课程 memo 交流问答 + // course 2 课堂, shixun 0 实训项目 subject 1 实践课程 memo 3交流问答 let types =""; if(parseInt(e.key)===0){ @@ -106,7 +107,7 @@ class SearchPage extends Component{ } }).then((response) => { this.setState({ loading: false }) - + if(response === undefined){ return @@ -178,7 +179,19 @@ class SearchPage extends Component{
    - +
    {data === undefined ? "" : data.map((item, key) => { return ( @@ -193,10 +206,28 @@ class SearchPage extends Component{
    {/*标题*/} - +
    + + + { + type==="shixun"? + ( + item.is_jupyter===true? +

    Jupyter

    + :"" + ) + :"" + } +
    + {/*描述*/}
    + + + + {item.content.content === undefined || item.content.content===0?"": item.content.content.map((item4, key4) => { return ( {/*挑战名字*/} - - + + {item.content.challenge_names === undefined || item.content.challenge_names===0?"": item.content.challenge_names.map((item5, key5) => { return (
    @@ -269,13 +300,13 @@ class SearchPage extends Component{ {/* 主讲:{item.author_name} - {item.author_school_name} + {item.author_school_name} 任务: {item.challenges_count===undefined?0:item.challenges_count} - + 学习人数: @@ -287,7 +318,7 @@ class SearchPage extends Component{ {/* */} {item.author_name} {item.author_school_name} - + {!!item.challenges_count && {/* */} 任务: @@ -325,7 +356,7 @@ class SearchPage extends Component{ {/* */} 回复数:{item.all_replies_count} } - + {/* @@ -354,7 +385,7 @@ class SearchPage extends Component{ { count && count && count> perpages ? -
    +
    @@ -368,4 +399,4 @@ class SearchPage extends Component{ } } -export default SnackbarHOC() (TPMIndexHOC ( SearchPage )); \ No newline at end of file +export default SnackbarHOC() (TPMIndexHOC ( SearchPage )); diff --git a/public/react/src/search/searchc.css b/public/react/src/search/searchc.css index 15c34650b..a885f3a12 100644 --- a/public/react/src/search/searchc.css +++ b/public/react/src/search/searchc.css @@ -131,4 +131,46 @@ margin-top: 20px; display: flex; flex-direction:row; -} \ No newline at end of file +} +.jupytertext{ + width:54px; + height:24px; + text-align: center; + border-radius:5px; + border:1px solid #FF6802; + margin-top: 3px; + +} +.jupytertextp{ + width:54px; + height:16px; + line-height:16px; + font-size:12px; + color:#FF6802; + line-height:16px; +} +/* x轴正方向排序 */ +/* 一 二 三 四 五 六 七 八 */ +.sortinxdirection{ + display: flex; + flex-direction:row; +} +/* x轴反方向排序 */ +/* 八 七 六 五 四 三 二 一 */ +.xaxisreverseorder{ + display: flex; + flex-direction:row-reverse; +} +.intermediatecenter{ + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} +.jupytertextheig{ + height: 32px; + line-height: 32px; +} +.ml9{ + margin-left: 9px; +} diff --git a/public/react/src/services/jupyterServer.js b/public/react/src/services/jupyterServer.js new file mode 100644 index 000000000..6ee5ec828 --- /dev/null +++ b/public/react/src/services/jupyterServer.js @@ -0,0 +1,35 @@ +/* + * @Description: jupyter相关接口 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-12 09:07:07 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-13 16:00:45 + */ +import axios from 'axios'; + +// 获取 jupyter实训相关的内容 +export async function fetchJupyterInfo (identifier) { + const url = `/tasks/${identifier}/jupyter.json`; + return axios.get(url); +} +// 获取数据集 +export async function fetchJupyterTpiDataSet (identifier, params) { + const url = `/shixuns/${identifier}/get_data_sets.json`; + return axios.get(url, { params }); +} +// 获取 tpi url +export async function fetchJupyterTpiUrl (params) { + const url = `/jupyters/get_info_with_tpi.json`; + return axios.get(url, { params }); +} +// 同步代码功能 +export async function fetchSyncJupyterCode (identifier) { + const url = `/myshixuns/${identifier}/sync_code.json`; + return axios.post(url); +} +// jupyter 保存 +export async function fetchSaveJupyterTpi (params) { + const url = `/jupyters/save_with_tpi.json`; + return axios.get(url, { params }); +} \ No newline at end of file diff --git a/public/react/src/services/ojService.js b/public/react/src/services/ojService.js index 1f3ba0ad6..373805b73 100644 --- a/public/react/src/services/ojService.js +++ b/public/react/src/services/ojService.js @@ -4,10 +4,11 @@ * @Github: * @Date: 2019-11-20 10:55:38 * @LastEditors: tangjiang - * @LastEditTime: 2019-12-04 10:53:41 + * @LastEditTime: 2019-12-17 14:10:37 */ import axios from 'axios'; +import { func } from 'prop-types'; export async function fetchOJList (params) { console.log('传递的参数: ', params); @@ -20,6 +21,12 @@ export async function fetchOJList (params) { return axios.get('/problems.json', { params: obj }); } +// 删除OJ列表 +export async function fetchDeleteOJItem (identifier) { + const url = `/problems/${identifier}.json`; + return axios.delete(url); +} + // 提交 export async function fetchPostOjForm (paramsObj) { const { params, submitType, identifier } = paramsObj; @@ -73,16 +80,15 @@ export async function fetchUserCommitRecord (identifier) { // 获取提交记录详情 export async function fetchUserCommitRecordDetail (identifier) { const url = `/myproblems/record_detail.json`; - debugger; const params = {id: identifier}; return axios.get(url, {params}); } // 恢复初始代码 -export async function restoreInitialCode (identifier) { - const url = `/myproblems/${identifier}/restore_initial_code.json`; - return axios.get(url); -} +// export async function restoreInitialCode (identifier) { +// const url = `/myproblems/${identifier}/restore_initial_code.json`; +// return axios.get(url); +// } // 发布任务 export async function publishTask (identifier) { @@ -90,6 +96,12 @@ export async function publishTask (identifier) { return axios.post(url); } +// 撤销发布 +export async function cancelPublicTask (identifier) { + const url = `/problems/${identifier}/cancel_publish.json`; + return axios.post(url); +} + // 更新用户编辑代码 export async function fetchUpdateCode (identifier, params) { const url = `/myproblems/${identifier}/update_code.json`; @@ -104,6 +116,25 @@ export async function fetchUserCodeSubmit (identifier) { // 恢复初始代码 export async function fetchRestoreInitialCode (identifier) { - const url = `/myproblems/${identifier}/restore_initial_code.json`; + // const url = `/myproblems/${identifier}/restore_initial_code.json`; + const url = `/myproblems/${identifier}/sync_code.json`; return axios.post(url); } + +// 新建时调用获取用户信息接口 +export async function fetchUserInfoForNew () { + const url = `/problems/new.json`; + return axios.get(url); +} + +// 文件上传 +export async function fetchUploadImage (file) { + const url = `/attachments.json`; + return axios.post(url, file) +} + +// 根据id号获取图片url +export async function fetchUploadImageUrl (id) { + const url = `/attachments/${id}`; + return axios.get(url); +} diff --git a/public/react/src/tpm/1.js b/public/react/src/tpm/1.js deleted file mode 100644 index 1a1494e08..000000000 --- a/public/react/src/tpm/1.js +++ /dev/null @@ -1,83 +0,0 @@ -import React, { Component } from 'react'; -import { Redirect } from 'react-router'; - -import PropTypes from 'prop-types'; - -import Comments from '../comment/Comments' - -import { commentHOC } from '../comment/CommentsHOC' -import { CircularProgress } from 'material-ui/Progress'; - -import './TPMShixunDiscuss.css' - -import TPMRightSection from './component/TPMRightSection' -import TPMNav from './component/TPMNav' - -class TPMShixunDiscuss extends Component { - constructor(props) { - super(props) - - } - - componentWillReceiveProps(newProps, newContext) { - if (newProps.shixun && newProps.shixun.id && (!this.props || !this.props.shixun || this.props.shixun.id != newProps.shixun.id) ) { - window.document.title = newProps.shixun.name - // this.props.fetchCommentIfNotFetched && - // this.props.fetchCommentIfNotFetched(); - } - } - - componentDidMount() { - // TODO 加了HOC后 mount了两次 - this.props.fetchCommentIfNotFetched && - this.props.fetchCommentIfNotFetched(); - } - // - - onPaginationChange = (page) => { - window.$("html,body").animate({"scrollTop":160}) - this.props.onPaginationChange(page) - } - - render() { - const { loadingComments, creator, shixun, myshixun, recommend_shixuns, current_user, watched, - aboutFocus, user, match - } = this.props; - - let _user = user; - if (user) { - _user = Object.assign({}, user); - _user.user_url = `/users/${user.login}` - } - return ( - -
    - -
    - - { loadingComments ? - : - - } -
    - -
    - -
    -
    -
    - - ); - } -} - -export default commentHOC( TPMShixunDiscuss ); diff --git a/public/react/src/tpm/Audit_situationComponent.js b/public/react/src/tpm/Audit_situationComponent.js deleted file mode 100644 index 4d6c413da..000000000 --- a/public/react/src/tpm/Audit_situationComponent.js +++ /dev/null @@ -1,260 +0,0 @@ -import React, { Component } from 'react'; -import { Redirect } from 'react-router'; -import { List,Typography,Tag,Modal,Radio} from 'antd'; - -import TPMRightSection from './component/TPMRightSection'; -import TPMNav from './component/TPMNav'; -import axios from 'axios'; - -class Audit_situationComponent extends Component { - constructor(props) { - super(props) - this.state = { - datas:undefined, - value:undefined, - } - } - - componentDidMount() { - this.getdatas() - - } - - - getdatas=()=>{ - - let url=`/shixuns/${this.props.match.params.shixunId}/review_newest_record.json`; - axios.get(url).then((response) => { - - if(response.data===undefined||JSON.stringify(response.data) == "{}"||response.data===null){ - this.setState({ - datas:[ - { - name: '内容审核情况', - id:"Content", - }, - { - name: '性能审核情况', - id:"Performance", - }, - ] - }) - }else{ - let newlist=[] - if(response.data.content_info!=undefined&&response.data.perference_info===undefined){ - let arr=[ - { - name: '内容审核情况', - id:"Content", - status:response.data.content_info.status, - username:response.data.content_info.username, - time:response.data.content_info.time, - }, - { - name: '性能审核情况', - id:"Performance", - }, - ] - newlist=arr - } - - if(response.data.content_info===undefined&&response.data.perference_info!=undefined){ - let arr=[ - { - name: '内容审核情况', - id:"Content", - }, - { - name: '性能审核情况', - id:"Performance", - status:response.data.perference_info.status, - username:response.data.perference_info.username, - time:response.data.perference_info.time, - }, - ] - newlist=arr - } - - if(response.data.content_info!=undefined&&response.data.perference_info!=undefined){ - let arr=[ - { - name: '内容审核情况', - id:"Content", - status:response.data.content_info.status, - username:response.data.content_info.username, - time:response.data.content_info.time, - }, - { - name: '性能审核情况', - id:"Performance", - status:response.data.perference_info.status, - username:response.data.perference_info.username, - time:response.data.perference_info.time, - }, - ] - newlist=arr - } - - this.setState({ - datas:newlist - }) - - } - }).catch((error) => { - console.log(error) - }); - } - - showModal = (id,status) => { - debugger - this.setState({ - visible: true, - editid:id, - value:status - }); - }; - - handleOk=(id,editid)=>{ - let url = `/shixuns/${this.props.match.params.shixunId}/review_shixun.json`; - axios.post(url, { - status: id===undefined?1:id, - review_type: editid, - }).then((response) => { - if(response.data.status===0){ - this.props.showNotification(response.data.message); - this.setState({ - visible: false, - }); - this.getdatas() - } - }).catch((error) => { - console.log(error) - }); - }; - - handleCancel = e => { - this.setState({ - visible: false, - }); - }; - - onChange = e => { - this.setState({ - value: e.target.value, - }); - }; - render() { - const { tpmLoading, shixun, user, match } = this.props; - let {value,editid,datas}=this.state; - - console.log(this.props) - return ( - - - {this.state.visible===true? -
    - - - - 已完成 - 未完成 - - - - -
    - -
    :""} - - - - { tpmLoading ?
    : - -
    - -
    - - - -
    - {this.props.identity >2||this.props.identity===undefined?"": ( - this.showModal(item.id,item.status)} key={key}> - - , - ]} - > - -
    {item.name}
    - {item.status===undefined?"":item.status===1?已完成:未完成} -
    } - description={ -
    - {item.time===undefined?"":审核时间: {item.time}} - {item.username===undefined?"":审核人: {item.username}} -
    - } - /> - - )} - />} -
    - -
    - -
    - -
    -
    - - } - - ); - } -} - -export default Audit_situationComponent; diff --git a/public/react/src/tpm/NewFooter.js b/public/react/src/tpm/NewFooter.js deleted file mode 100644 index 4ff1cc46a..000000000 --- a/public/react/src/tpm/NewFooter.js +++ /dev/null @@ -1,67 +0,0 @@ -import React, { Component } from 'react'; -import { Redirect } from 'react-router'; -import { Link } from 'react-router-dom'; -import { getImageUrl, toPath } from 'educoder' -import PropTypes from 'prop-types'; - -class NewFooter extends Component { - constructor(props) { - super(props) - - } - - componentWillReceiveProps(newProps, newContext) { - - } - - - render() { - return ( -
    - {/*newContainers*/} -
    - {this.props.user&&this.props.user.main_site===true?
    - {/*
    - - 高校智能化教学与实训平台 - - - EduCoder.net -
    */} -
      -
    • 网站首页
    • -
    • 关于我们
    • -
    • 联系我们
    • -
    • 合作伙伴
    • -
    • 服务协议
    • -
    • 帮助中心
    • -
    • 意见反馈
    • -
    -
    :""} -
    - {this.props.mygetHelmetapi === null ? "" : - this.props.mygetHelmetapi===undefined|| this.props.mygetHelmetapi.footer===null||this.props.mygetHelmetapi.footer===undefined? -

    - © 2019 EduCoder - 湘ICP备17009477号 - - 湘公网安备43019002000962号 - - Trustie   &   IntelliDE inside. 版权所有 湖南智擎科技有限公司 -

    - : -
    - - } - -
    -
    -
    -
    - ); - } -} - -export default NewFooter; diff --git a/public/react/src/tpm/NewHeader.js b/public/react/src/tpm/NewHeader.js deleted file mode 100644 index 41855c15a..000000000 --- a/public/react/src/tpm/NewHeader.js +++ /dev/null @@ -1,1427 +0,0 @@ -import React, { Component } from 'react'; -import { BrowserRouter as Router, Route, Link } from "react-router-dom"; -import { Redirect } from 'react-router'; -import AccountProfile from"../user/AccountProfile"; -import PropTypes from 'prop-types'; -import Certifiedprofessional from "../../modules/modals/Certifiedprofessional" - -// import searchImg from '../../../../images/educoder/icon/search.svg' - -// /images/educoder/icon/search.svg - -import { getImageUrl, toPath ,trigger,broadcastChannelPostMessage} from 'educoder' - -import axios from 'axios'; - -import { Modal,Checkbox ,Radio,Input,message,notification } from 'antd'; - -import Addcourses from '../courses/coursesPublic/Addcourses'; - -import LoginDialog from '../login/LoginDialog'; - -import Trialapplication from '../login/Trialapplication' - -import 'antd/lib/modal/style/index.css'; - -import 'antd/lib/checkbox/style/index.css'; - -import 'antd/lib/radio/style/index.css'; - -import 'antd/lib/input/style/index.css'; - -import './TPMIndex.css'; - -const $ = window.$ -// TODO 这部分脚本从公共脚本中直接调用 - -const RadioGroup = Radio.Group; -const { Search } = Input; -let old_url; - -/* - _logined_header.html.erb - _unlogin_header.html.erb -*/ -window._header_componentHandler = null; -class NewHeader extends Component { - constructor(props) { - super(props) - this.state={ - Addcoursestypes:false, - tojoinitemtype:false, - tojoinclasstitle:undefined, - rolearr:["",""], - Checkboxteacherchecked:false, - Checkboxstudentchecked:false, - Checkboxteachingchecked:false, - Checkboxteachertype:false, - Checkboxteachingtype:false, - code_notice:false, - checked_notice:false, - RadioGroupvalue:undefined, - submitapplications:false, - isRender:false, - showSearchOpentype:false, - showTrial:false, - setevaluatinghides:false, - occupation:0, - mydisplay:false, - headtypesonClickbool:false, - headtypess:"/", - mygetHelmetapi2: null, - } - console.log("176") - // console.log(props); - // console.log("NewHeader1234567890"); - // console.log(this.props); - } - componentDidUpdate = (prevProps) => { - // console.log("componentDidMount2"); - // console.log(this.state.mygetHelmetapi2); - if(this.state.mygetHelmetapi2===undefined){ - this.getAppdata(); - } - } - componentDidMount() { - console.log("componentDidMount1"); - this.getAppdata(); - window._header_componentHandler = this; - - //下拉框的显示隐藏 - var hoverTimeout; - var hoveredPanel; - $(".edu-menu-panel").hover(function(){ - if (hoverTimeout) { // 一次只显示一个panel - if (hoveredPanel && hoveredPanel != this) { - $(hoveredPanel).find(".edu-menu-list").hide() - } - clearTimeout(hoverTimeout); - hoverTimeout = null; - } - hoveredPanel = this; - $(this).find(".edu-menu-list").show(); - },function(){ - var that =this; - // 延迟hide - hoverTimeout = setTimeout(function() { - $(that).find(".edu-menu-list").hide(); - }, 800) - - }); - - //获取游览器地址 - try { - window.sessionStorage.setItem("yslgeturls", JSON.stringify(window.location.href)) - } catch (e) { - - } - // axios.interceptors.response.use((response) => { - // if (response != undefined) - // if (response && response.data.status === -1) { - // if (response.data.message === "该课堂要求成员完成实名认证") { - // - // } else if (response.data.message === "该课堂要求成员完成职业认证") { - // console.log("该课堂要求成员完成职业认证"); - // this.HideAddcoursestypess(2); - // - // - // - // return - // } else if (response.data.message === "该课堂要求成员完成实名和职业认证") { - // console.log("该课堂要求成员完成实名和职业认证"); - // this.HideAddcoursestypess(3); - // return - // - // } - // } - // return response; - // }, (error) => { - // - // }); - } - - componentDidUpdate = (prevProps) => { - // if(prevProps.user!=this.props.user){ - // // console.log("216") - // // console.log(prevProps.user); - // // console.log(this.props.user); - // if(this.props.user !== undefined){ - // this.setState({ - // user_phone_binded :this.props.user.user_phone_binded, - // }) - // } - // - // - // } - } - - openNotification = (messge) => { - notification.open({ - message: "提示", - description: - messge, - }); - }; - - componentWillReceiveProps(newProps, oldProps) { - this.setState({ - user:newProps.user - }) - if(newProps.Headertop!=undefined){ - old_url=newProps.Headertop.old_url - } - - } - getCookie=(key)=>{ - var arr,reg = RegExp('(^| )'+key+'=([^;]+)(;|$)'); - if (arr = document.cookie.match(reg)) //["username=liuwei;", "", "liuwei", ";"] - return decodeURIComponent(arr[2]); - else - return null; - } - - delCookie=(name)=>{ - var exp = new Date(); - exp.setTime(exp.getTime() - 1); - var cval=this.getCookie(name); - if(cval!=null){ - document.cookie= name + "="+cval+";expires="+exp.toGMTString(); - } - } - onLogout = () => { - const url = `/accounts/logout.json` - this.delCookie("autologin_trustie") - axios.get(url, { - }) - .then((response) => { - if(response.data.status===1){ - this.setState({ - user:undefined - }) - // let path="/"; - // this.props.history.push(path); - // broadcastChannelPostMessage('refreshPage') - window.location.href ="/login" - message.success('退出成功'); - } - }); - } - - tojoinclass=()=>{ - let{user} =this.state; - if(user===undefined){ - this.setState({ - isRender:true - }) - return - } - if(user&&user.login===""){ - this.setState({ - isRender:true - }) - return; - } - - if(user&&user.profile_completed===false){ - this.setState({ - AccountProfiletype:true - }) - return; - } - - this.setState({ - Addcoursestypes:true, - }) - } - - tojoinitem=()=>{ - if(this.props.user&&this.props.user.email===undefined||this.props.user&&this.props.user.email===null||this.props.user&&this.props.user.email===""){ - this.openNotification("请先绑定邮箱,谢谢"); - return - } - let{user} =this.state; - if(user===undefined){ - this.setState({ - isRender:true - }) - return - } - if(user&&user.login===""){ - this.setState({ - isRender:true - }) - return; - } - - if(user&&user.profile_completed===false){ - this.setState({ - AccountProfiletype:true - }) - return; - } - - this.setState({ - tojoinitemtype:true - }) - } - - - submitstatevalue=(sum,value,data)=>{ - this.setState({ - Addcoursestypes:false, - tojoinitemtype:false, - tojoinclasstitle:undefined, - rolearr:["",""], - Checkboxteacherchecked:false, - Checkboxstudentchecked:false, - Checkboxteachingchecked:false, - Checkboxteachertype:false, - Checkboxteachingtype:false, - code_notice:false, - checked_notice:false, - submitapplicationssum:sum, - submitapplications:true, - submitapplicationsvalue:value, - submitapplicationsvaluedata:data, - RadioGroupvalue:undefined - }) - } - - onChangeRadioGroup = (e) => { - this.setState({ - RadioGroupvalue: e.target.value, - }); - } - - submitsubmitapplications=()=>{ - let { - submitapplicationssum, - submitapplicationsvaluedata - }=this.state; - this.setState({ - submitapplications:false, - RadioGroupvalue:undefined - }) - if(submitapplicationssum===0){ - if(submitapplicationsvaluedata!=undefined){ - window.location.href = "/courses/"+submitapplicationsvaluedata; - } - }else if(submitapplicationssum===1){ - if(submitapplicationsvaluedata!=undefined){ - window.location.href = "/projects/"+submitapplicationsvaluedata; - } - } - } - - hidesubmitapplications=()=>{ - this.setState({ - Addcoursestypes:false, - tojoinitemtype:false, - tojoinclasstitle:undefined, - rolearr:["",""], - Checkboxteacherchecked:false, - Checkboxstudentchecked:false, - Checkboxteachingchecked:false, - Checkboxteachertype:false, - Checkboxteachingtype:false, - code_notice:false, - checked_notice:false, - submitapplications:false, - RadioGroupvalue:undefined - }) - } - educoderlogin=()=>{ - //登录账号 - this.setState({ - isRender:true - }) - // var url = `/accounts/logout.json`; - // - // axios.get((url)).then((result) => { - // if(result!==undefined){ - // // this.setState({ - // // isRender:true - // // }) - // window.location.href = "/"; - // } - // }).catch((error) => { - // console.log(error); - // }) - } - educoderloginysl=()=>{ - //退出账号 - // this.setState({ - // isRender:true - // }) - var url = `/accounts/logout.json`; - - axios.get((url)).then((result) => { - if(result!==undefined){ - // this.setState({ - // isRender:true - // }) - // broadcastChannelPostMessage('refreshPage') - window.location.href = "/"; - } - }).catch((error) => { - console.log(error); - }) - } - - hideAddcoursestypes=()=>{ - this.setState({ - Addcoursestypes:false - }) - }; - HideAddcoursestypess=(i)=>{ - console.log("调用了"); - this.setState({ - Addcoursestypes:false, - mydisplay:true, - occupation:i, - }) - }; - ModalCancelsy=()=>{ - this.setState({ - mydisplay:false, - }) - }; - - - hidetojoinclass=()=>{ - this.setState({ - tojoinclasstype:false, - tojoinitemtype:false, - tojoinclasstitle:undefined, - rolearr:["",""], - Checkboxteacherchecked:false, - Checkboxstudentchecked:false, - Checkboxteachingchecked:false, - Checkboxteachertype:false, - Checkboxteachingtype:false, - code_notice:false, - checked_notice:false, - RadioGroupvalue:undefined - }) - } - -submittojoinclass=(value)=>{ - let {tojoinclasstitle,rolearr,RadioGroupvalue}=this.state; - - if(tojoinclasstitle===undefined){ - this.setState({ - code_notice:true - }) - return - } - let newrolearr=rolearr; - // if(value===1){ - if(tojoinclasstitle.length<6){ - this.setState({ - code_notice:true - }) - return - } - // }else if(value===0){ - // if(tojoinclasstitle.length<5){ - // this.setState({ - // code_notice:true - // }) - // return - // } - // } - if(tojoinclasstitle===""||tojoinclasstitle===undefined){ - this.setState({ - code_notice:true - }) - return - }else{ - this.setState({ - code_notice:false - }) - } - - let pamst=[]; - let num=0; - for(var i = 0 ; i { - // if( response.data.state===0){ - // this.submitstatevalue(0,"加入成功",response.data.course_id) - // }else if( response.data.state===1){ - // }else if( response.data.state===2){ - // this.submitstatevalue( 0,"课堂已过期! 请联系课堂管理员重启课堂。(在配置课堂处)") - // }else if( response.data.state===3){ - // this.submitstatevalue( 0,"您已是课堂成员)",response.data.course_id) - // }else if( response.data.state===4){ - // this.submitstatevalue( 0,"您输入的邀请码错误)") - // }else if( response.data.state===5){ - // this.submitstatevalue( 0,"您还未登录") - // }else if( response.data.state===6){ - // this.submitstatevalue( 0,"申请已提交,请等待审核") - // }else if( response.data.state===7){ - // this.submitstatevalue( 0," 您已经发送过申请了,请耐心等待") - // }else if( response.data.state===8){ - // this.submitstatevalue( 0,"您已经是该课堂的教师了",response.data.course_id) - // }else if( response.data.state==9){ - // this.submitstatevalue( 0,"您已经是该课堂的教辅了",response.data.course_id) - // }else if( response.data.state==10){ - // this.submitstatevalue(0,"您已经是该课堂的管理员了",response.data.course_id) - // }else if( response.data.state==11){ - // this.submitstatevalue(0," 该课堂已归档,请联系老师") - // }else if( response.data.state==12){ - // this.submitstatevalue(0,"您已经发送过申请了,请耐心等待师") - // }else if( response.data.state==13){ - // this.submitstatevalue(0,"您申请已提交,请等待审核") - // }else if( response.data.state==14){ - // this.submitstatevalue("此邀请码已停用,请与老师联系") - // }else if( response.data.state==15){ - // this.submitstatevalue(0,"您已是课堂成员! 加入分班请在课堂具体分班页面进行") - // }else { - // this.submitstatevalue(0," 未知错误,请稍后再试") - // } - // }) - // - // } - - if(value===1){ - let url="/project_applies.json" - // const form = new FormData(); - // form.append('code', tojoinclasstitle); - // form.append('role', RadioGroupvalue); - // form.append('type', 1); - axios.post(url,{ - code:tojoinclasstitle, - role:RadioGroupvalue - } - ).then((response) => { - if( response.data.status===1){ - this.submitstatevalue(1,"您输入的邀请码错误") - }else if( response.data.status===2){ - this.submitstatevalue( 1,"您已经是该项目成员",response.data.project) - }else if( response.data.status===3){ - this.submitstatevalue( 1,"请选择一个角色") - }else if( response.data.status===4){ - this.submitstatevalue( 1,"您的申请已提交,请等待项目管理员审批") - }else if( response.data.status===5){ - this.submitstatevalue( 1,"您已经申请加入该项目了,请耐心等待") - }else if( response.data.status===6){ - this.submitstatevalue( 1,"您已成功加入项目",response.data.project) - }else if( response.data.status===0){ - if(RadioGroupvalue==="reporter"){ - this.openNotification("您加入项目成功!"); - window.location.href=`/projects/${response.data.project_id}`; - }else{ - this.openNotification("您的申请已提交,请等待项目管理员审批!"); - } - } - }) - } - this.hidetojoinclass() -} - - // trialapplications =()=>{ - // console.log("点击了") - // this.setState({ - // isRenders: true, - // showTrial:true, - // }) - // } - - // 关闭 - cancelModulationModels = () => { - this.setState({isRenders: false}) - } - - inputjoinclassvalue=(e)=>{ - console.log(e.target.value.length); - if(e.target.value.length>=7){ - this.openNotification("请输入6位项目邀请码!"); - return - } - this.setState({ - tojoinclasstitle:e.target.value - }) - } - - showSearchOpen=(e)=>{ - this.setState({ - showSearchOpentype:true - }) - - } - - hideshowSearchOpen=(e)=>{ - let {setevaluatinghides}=this.state; - if(setevaluatinghides===true){ - this.setState({ - showSearchOpentype:false, - setevaluatinghides:false - }) - - } - } - - onKeywordSearchKeyDown = (value) => { - let url=`/search?value=${value}`; - this.props.history.push(url) - } - - onKeywordSearchKeyDowns=()=>{ - this.setState( - { - setevaluatinghides:false - } - ) - } - - setevaluatinghides=()=>{ - this.setState( - { - setevaluatinghides:true - } - ) - } - //头部获取是否已经登录了 - getUser=(url,type)=>{ - - if(type==="projects"){ - if(this.props.user&&this.props.user.email===undefined||this.props.user&&this.props.user.email===null||this.props.user&&this.props.user.email===""){ - this.openNotification("请先绑定邮箱,谢谢"); - return - } - } - // console.log("点击了503") - // console.log(url); - let{user} =this.state; - - if(user===undefined){ - this.setState({ - isRender:true - }) - return - } - - if(user&&user.login===""){ - this.setState({ - isRender:true - }) - return; - } - - if(user&&user.profile_completed===false){ - this.setState({ - AccountProfiletype:true - }) - return; - } - - if(url !== undefined || url!==""){ - window.location.href = url; - } - - - } - - //修改登录方法 - Modifyloginvalue=()=>{ - this.setState({ - isRender:false, - }) - } - - hideAccountProfile=()=>{ - this.setState({ - AccountProfiletype:false - }) - }; - headtypesonClick=(url,bool)=>{ - this.setState({ - headtypess:url, - headtypesonClickbool:bool, - }) - } - //获取数据为空的时候 - gettablogourlnull = () => { - this.setState({ - mygetHelmetapi2: undefined - }); - document.title = "EduCoder"; - var link = document.createElement('link'), - oldLink = document.getElementById('dynamic-favicon'); - link.id = 'dynamic-favicon'; - link.rel = 'shortcut icon'; - link.href = "/react/build/./favicon.ico"; - if (oldLink) { - document.head.removeChild(oldLink); - } - document.head.appendChild(link); - }; - - //获取数据的时候 - gettablogourldata = (response) => { - document.title = response.data.setting.name; - var link = document.createElement('link'), - oldLink = document.getElementById('dynamic-favicon'); - link.id = 'dynamic-favicon'; - link.rel = 'shortcut icon'; - link.href = '/' + response.data.setting.tab_logo_url; - if (oldLink) { - document.head.removeChild(oldLink); - } - document.head.appendChild(link); - } - - getAppdata=()=>{ - let url = "/setting.json"; - axios.get(url).then((response) => { - // console.log("app.js开始请求/setting.json"); - // console.log("获取当前定制信息"); - if(response){ - if(response.data){ - this.setState({ - mygetHelmetapi2:response.data.setting - }); - try { - if (response.data.setting.tab_logo_url) { - this.gettablogourldata(response); - } else { - this.gettablogourlnull(); - } - } catch (e) { - this.gettablogourlnull(); - } - - - } else { - - this.gettablogourlnull(); - - } - - } else { - this.gettablogourlnull(); - - } - - }).catch((error) => { - this.gettablogourlnull(); - - }); - }; - render() { - const isLogin = true; // 这里不会出现未登录的情况,服务端在服务端路由时发现如果是未登录,则跳转到登录页了。 - const {match,} = this.props; - - let {Addcoursestypes, - tojoinitemtype, - tojoinclasstitle, - Checkboxteacherchecked, - Checkboxstudentchecked, - Checkboxteachingchecked, - Checkboxteachertype, - Checkboxteachingtype, - code_notice, - checked_notice, - AccountProfiletype, - submitapplications, - submitapplicationsvalue, - user, - isRender, - showSearchOpentype, - headtypesonClickbool, - headtypess, - mygetHelmetapi2, - }=this.state; - /* - 用户名称 用户头像url - */ - let activeIndex = false; - let activeForums = false; - let activeShixuns = false; - let activePaths = false; - let coursestype=false; - let activePackages=false; - let activeMoopCases=false; - - - if (match.path === '/forums') { - activeForums = true; - } else if (match.path.startsWith('/shixuns')) { - activeShixuns = true; - }else if (match.path.startsWith('/paths')) { - activePaths = true; - } else if (match.path.startsWith('/courses')) { - coursestype = true; - }else if (match.path.startsWith('/crowdsourcing')) { - activePackages = true; - }else if(match.path.startsWith('/moop_cases')){ - activeMoopCases = true; - }else { - activeIndex = true; - } - - let headtypes='/'; - - // console.log("mygetHelmetapi2"); - // console.log(mygetHelmetapi2); - if(mygetHelmetapi2){ - if(mygetHelmetapi2.navbar){ - if(mygetHelmetapi2.navbar.length>0){ - // console.log("mygetHelmetapi2.navbar.length>0====-=-=--=-=-=-="); - // - // console.log(match.path); - if(match.path==='/'){ - if(headtypesonClickbool===false){ - headtypes=undefined; - }else{ - headtypes=headtypess; - } - - }else { - const developer = { - name: '开发者社区', - link: '/developer', - hidden: false - }; - mygetHelmetapi2.navbar.push(developer); - for(var i=0;i{ - var reg = RegExp(item.link); - if(shixun.match(reg)){ - if(item.hidden===true){ - shixuntype=true - } - } - if(paths.match(reg)){ - if(item.hidden===true){ - pathstype=true - } - } - if(courses.match(reg)){ - if(item.hidden===true){ - coursestypes=true - } - } - }) - } - return ( - -
    - - {isRender===true?this.Modifyloginvalue()} - {...this.props} - {...this.state} - />:""} - - {AccountProfiletype===true?this.hideAccountProfile()} - {...this.props} - {...this.state} - />:""} - this.headtypesonClick("/",false)} className={"fl mr30 ml25 mt10"}> - { - mygetHelmetapi2 === null ? - "" - : - mygetHelmetapi2===undefined||mygetHelmetapi2.nav_logo_url===null||mygetHelmetapi2.nav_logo_url===undefined? - 高校智能化教学与实训平台 - : - 高校智能化教学与实训平台 - } - - - - { - mygetHelmetapi2 === null ? - "" : - mygetHelmetapi2!==undefined&&mygetHelmetapi2.navbar!==null&&mygetHelmetapi2.navbar!==undefined&&mygetHelmetapi2.navbar.length>0? -
    -
      - {/*
    • 首页
    • */} - {/*
    • 实训路径
    • */} - { - mygetHelmetapi2.navbar && mygetHelmetapi2.navbar.map((item,key)=>{ - // console.log("headtypes"); - // console.log(headtypes);hidden - var str=new RegExp("http"); - var strbool=false; - //test方法返回值为(true或者false) - if(item.link){ - if(str.test(item.link)===true){ - strbool=true - }else{ - strbool=false - } - } - // console.log(item.hidden); - return( -
    • this.headtypesonClick(item.link,true)} className={`${headtypes===undefined?'pr':headtypes===item.link?'pr active':'pr'}`} style={item.hidden==false?{display: 'block'}:{display: 'none'}}> - { - strbool===true? - {item.name} - : - {item.name} - } -
    • - ) - }) - } - {/*
    • */} - {/* 实践课程*/} - {/*
    • */} - - {/*
    • 课堂
    • */} - {/*
    • */} - {/* /!*课堂*!/*/} - {/* 翻转课堂*/} - {/*
    • */} - - {/*
    • */} - {/* 实训项目*/} - {/* */} - {/* */} - {/*
    • */} - - - {/*
    • 教学案例
    • */} - {/*
    • */} - {/* 在线竞赛*/} - {/* */} - {/*
    • */} - {/*
    • 教学案例
    • */} - {/*
    • */} - {/*众包创新*/} - {/*
    • */} - {/*
    • 交流问答
    • */} -
    • 工程认证
    • - -
    • 0 ? 'block' : 'none'}}> - 职业路径 -
      0 ? 'block' : 'none'}}> -
        - {this.props.Headertop === undefined ? "" : this.props.Headertop.career_url.map((item, key) => { - return( -
      • {item.name}
      • - ) - }) - } -
      -
      -
    • -
    -
    - // :mygetHelmetapi2===undefined||mygetHelmetapi2.navbar===null||mygetHelmetapi2.navbar===undefined||mygetHelmetapi2.navbar.length===0? - //
    - // - //
      - // {/*
    • 首页
    • */} - // - // {/*
    • 实训路径
    • */} - //
    • - // 实践课程 - //
    • - // - // {/*
    • 课堂
    • */} - //
    • - // {/*课堂*/} - // 翻转课堂 - //
    • - // - //
    • - // 实训项目 - // {/**/} - // {/**/} - //
    • - // - //
    • 0 ? 'block' : 'none'}}> - // 职业路径 - //
      0 ? 'block' : 'none'}}> - //
        - // {this.props.Headertop === undefined ? "" : this.props.Headertop.career_url.map((item, key) => { - // return( - //
      • {item.name}
      • - // ) - // }) - // } - //
      - //
      - //
    • - // - // {/*
    • 教学案例
    • */} - //
    • - // 在线竞赛 - // {/**/} - //
    • - //
    • 教学案例
    • - // {/*
    • */} - // {/*众包创新*/} - // {/*
    • */} - //
    • 交流问答
    • - //
    • 工程认证
    • - //
    - //
    - : -
    - -
      - {/*
    • 首页
    • */} - - {/*
    • 实训路径
    • */} -
    • - 实践课程 -
    • - - {/*
    • 课堂
    • */} -
    • - {/*课堂*/} - 翻转课堂 -
    • - -
    • - 实训项目 - {/**/} - {/**/} -
    • - -
    • 0 ? 'block' : 'none'}}> - 职业路径 -
      0 ? 'block' : 'none'}}> -
        - {this.props.Headertop === undefined ? "" : this.props.Headertop.career_url.map((item, key) => { - return( -
      • {item.name}
      • - ) - }) - } -
      -
      -
    • - - {/*
    • 教学案例
    • */} -
    • - 在线竞赛 - {/**/} -
    • -
    • 教学案例
    • - {/*
    • */} - {/*众包创新*/} - {/*
    • */} -
    • 交流问答
    • -
    • 工程认证
    • -
    -
    - } - - - - - - - -
    -
    - {/**/} -
    - - 实训 - - -
    - {/**/} - {/*搜索框*/} - {showSearchOpentype===true?
    this.hideshowSearchOpen(e)} onMouseLeave={()=>this.setevaluatinghides()}> - this.onKeywordSearchKeyDowns()} - onSearch={(value) => this.onKeywordSearchKeyDown(value)} - // onPressEnter={this.onKeywordSearchKeyDown} - style={{ width: 300,height:32}} - autoFocus={true} - /> -
    :""} - - {/**/} - {/*/!**!/*/} - {/**/} - - {/**/} - {/* TODO 需要服务端接口提供最近搜索 -
    -
    最近搜索
    - - -
    */} -
    -
    - - {/* - <%= link_to '登录', signin_path, :className => "mr5" %> - - <%= link_to '注册', user_join_path, :className => "ml5" %> - */} - { user===undefined? - - this.educoderlogin()} className="mr5 color-white">登录 - - 注册 - :user.login===""? - this.educoderlogin()} className="mr5 color-white">登录 - - 注册 - : -
    - - - - - -
    - } - {/*href="https://www.educoder.net/login"*/} -
    - {/*{ loadHeader()}*/} - {showSearchOpentype===true?"":this.props.user&&this.props.user.main_site===true?this.showSearchOpen(e)}> - {/*"/images/educoder/icon/search.svg" - */} - - - :""} - - {/*
    */} -
    - - - - -
    -
    - - {coursestypes===true&&this.props.user&&this.props.user.main_site===false?"":} -
    -
    - - {this.props.user&&this.props.user.main_site===true? :""} - - -
    -
    -
      -
      -

      - {submitapplicationsvalue} -

      -
      -
    • - 取消 - 确定 -
    • - -
    -
    -
    -
    - -
    - -
    - - ); - } -} - -export default NewHeader; - diff --git a/public/react/src/tpm/SiderBar.js b/public/react/src/tpm/SiderBar.js deleted file mode 100644 index 9312b24fd..000000000 --- a/public/react/src/tpm/SiderBar.js +++ /dev/null @@ -1,143 +0,0 @@ -import React, { Component } from 'react'; -import { getImageUrl} from 'educoder'; -import './TPMIndex.css'; - -const $ = window.$; - -$(window).resize(function(){ - rightSlider(); -}); - -$(window).scroll(function(){ - if($(".gotop").length>0){ - if($(document).scrollTop()>0){ - $(".-task-sidebar .gotop").show(); - $(".gotop").click(function(){ - $("html,body").scrollTop(0); - }); - } - if($(document).scrollTop()==0){ - $(".-task-sidebar .gotop").hide(); - } - } -}); - -function rightSlider(){ - var poi=parseInt((parseInt($(window).width())- 1200 )/2)-81; - // console.log(parseInt($(window).width())+" "+poi); - if(poi>0){ - $(".-task-sidebar").css("right",poi); - }else{ - $(".-task-sidebar").css("right","0px"); - } - $(".-task-sidebar").show(); -} - - -function _initSider() { - var $descSide = $("
    ").appendTo("body"); - $(".-task-sidebar>div").hover(function(){ - //移入显示二维码 - if($(this).hasClass("scan")){ - $(".scan_ewm").show().css({right:"75px",opacity:0}).stop().animate({ - right:"45px",opacity:1 - }) - return; - } - var $tool = $(this).attr("tooltips"); - $descSide.html($tool+"
    "); - $descSide.data('_dom', this) - $descSide.show().css({ - left:$(this).offset().left - $descSide.width()-30, - opacity:0, - top:$(this).offset().top - }).stop().animate({ - left:$(this).offset().left - $descSide.width()-5, - opacity:1 - },400); - },function(){ - if($(this).hasClass("scan")){ - $(".scan_ewm").stop().animate({right:"75px",opacity:0},200).hide(); - } - $descSide.stop().animate({ - left:$(this).offset().left - $descSide.width()-30, - opacity:0 - },200).hide(); - }); - rightSlider(); - - $(window).scroll(function() { - if ($descSide.height()) { - var hoverIcon = $descSide.data('_dom') - $descSide.css('top', $(hoverIcon).offset().top) - } - }) -} - -class SiderBar extends Component { - constructor(props) { - super(props) - - } - - componentDidMount() { - _initSider(); - - } - - render() { - - // console.log(this.props) - return ( - -
    - {this.props.mygetHelmetapi&&this.props.mygetHelmetapi.main_site===true?
    -
    - - - -
    - -
    - - - -
    - - - -
    - -

    -

    - - -

    微信扫一扫

    -

    关注公众号

    - -

    -

    -
    - -
    - - - -
    -
    :""} - - -
    - ); - } -} - -export default SiderBar; diff --git a/public/react/src/tpm/TPMBanner.js b/public/react/src/tpm/TPMBanner.js deleted file mode 100644 index b660001c6..000000000 --- a/public/react/src/tpm/TPMBanner.js +++ /dev/null @@ -1,1056 +0,0 @@ -import React, { Component } from 'react'; - -import { Redirect } from 'react-router'; - -import {BrowserRouter as Router, Route, Link, Switch} from "react-router-dom"; - -import PropTypes from 'prop-types'; - -import { Rating ,Progress} from "@icedesign/base"; - -import {Modal,Input,Radio,Pagination,message,Spin,Icon,Tooltip,Rate} from 'antd'; - -import AccountProfile from"../user/AccountProfile"; - -import 'antd/lib/pagination/style/index.css'; - -import axios from 'axios' - -import Modals from '../modals/Modals'; - -import './shixuns/css/TPMBanner.css'; - -let $ = window.$; - -const Search = Input.Search; - -const RadioGroup = Radio.Group; - -class TPMBanner extends Component { - constructor(props) { - super(props) - this.state={ - Forkvisible: false, - Senttothetype:false, - Senttothevcalue:undefined, - courses_count:1, - course_list:[], - pagenum:1, - publishbox:"", - publishboxstatus:0, - pages:1, - Issuevisible:false, - evaluation_set_position:[], - tag_position:[], - Forkauthentication:false, - can_fork:undefined, - certi_url:undefined, - showradios:false, - startbtn:false, - Searchvalue:"", - startshixunCombattype:false, - shixunsmessage:"", - shixunsreplace:false, - hidestartshixunsreplacevalue:"", - isIE:false, - Forkvisibletype: false, - isSpin:false, - Senttothevcaluetype:false - } - } - - // star_info:[0, 0, 0, 0, 0, 0], - // star_infos:[0, 0, 0, 0, 0, 0], - // shixunsDetails:{}, - // shixunId: undefined, - // componentWillReceiveProps(newProps, newContext){ - // this.setState({ - // shixunsDetails: newProps.shixunsDetails - // }); - // } - - IEVersion=()=>{ - var userAgent = navigator.userAgent; //取得浏览器的userAgent字符串 - var isIE = userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1; //判断是否IE<11浏览器 - var isEdge = userAgent.indexOf("Edge") > -1 && !isIE; //判断是否IE的Edge浏览器 - var isIE11 = userAgent.indexOf('Trident') > -1 && userAgent.indexOf("rv:11.0") > -1; - if(isIE) { - var reIE = new RegExp("MSIE (\\d+\\.\\d+);"); - reIE.test(userAgent); - var fIEVersion = parseFloat(RegExp["$1"]); - if(fIEVersion == 7) { - return 7; - } else if(fIEVersion == 8) { - return 8; - } else if(fIEVersion == 9) { - return 9; - } else if(fIEVersion == 10) { - return 10; - } else { - return 6;//IE版本<=7 - } - } else if(isEdge) { - return 'edge';//edge - } else if(isIE11) { - return 11; //IE11 - }else{ - return -1;//不是ie浏览器 - } - } - componentDidMount() { - let thiisie=this.IEVersion(); - if(thiisie!=-1){ - this.setState({ - isIE:true - }) - }else{ - this.setState({ - isIE:false - }) - } - } - /* - * Fork - * */ - copyForkvisible = () => { - let {shixunsDetails} = this.props; - if (shixunsDetails.can_fork === null) { - this.setState({ - Forkvisible: true - }) - } else { - this.setState({ - Forkvisible: false, - Forkauthentication: true, - can_fork: shixunsDetails.can_fork.can_fork, - certi_url: shixunsDetails.can_fork.certi_url, - }) - } - - } - - hideForkvisible = () => { - this.setState({ - Forkvisible: false, - Forkauthentication:false - }) - } - - addForkvisible = () => { - this.setState({ - Forkvisibletype: true, - }) - let id = this.props.match.params.shixunId; - let url = "/shixuns/" + id + "/copy.json"; - axios.post(url).then((response) => { - if(response.data.status===401){ - - }else{ - this.setState({ - Forkvisible: false, - Forkauthentication: false, - // Forkvisibletype:false - }) - window.location.href = "/shixuns/" + response.data.shixun + "/challenges"; - } - - }).catch((error) => { - console.log(error) - }); - - } - /* - * 发送至按钮 - * */ - Senttothe=()=>{ - if(this.props.checkIfLogin()===false){ - this.props.showLoginDialog() - return - } - - // if(this.props.checkIfProfileCompleted()===false){ - // this.setState({ - // AccountProfiletype:true - // }) - // return - // } - // - // if(this.props.checkIfProfessionalCertification()===false){ - // this.setState({ - // AccountProfiletype:true - // }) - // return - // } - let id = this.props.match.params.shixunId; - let url="/shixuns/" + id +"/search_user_courses.json"; - this.setState({ - Senttothetype:true - }) - - axios.get(url, { - params: { - page:1, - limit:10 - }}).then((response) => { - this.setState({ - courses_count:response.data.courses_count, - course_list:response.data.course_list - }) - }).catch((error) => { - console.log(error) - }); - } - - SenttotheSearch=(value)=>{ - let id = this.props.match.params.shixunId; - let url="/shixuns/" + id +"/search_user_courses.json?search="+value; - axios.get(encodeURI(url), { - params: { - page:1, - limit:10 - }}).then((response) => { - this.setState({ - courses_count:response.data.courses_count, - course_list:response.data.course_list, - pages:1, - Searchvalue:value - }) - }).catch((error) => { - console.log(error) - }); - } - - onChangeSenttothevcalue=(e)=>{ - this.setState({ - Senttothevcalue:e.target.value - }) - } - onChangesendeSenttothe=(pageNumber)=>{ - let{Searchvalue}=this.state; - let id = this.props.match.params.shixunId; - let url="/shixuns/" + id +"/search_user_courses.json?search="+Searchvalue; - axios.get(url, { - params: { - page:pageNumber, - limit:10 - }}).then((response) => { - this.setState({ - courses_count:response.data.courses_count, - course_list:response.data.course_list, - pagenum: pageNumber, - pages: pageNumber - }) - }).catch((error) => { - console.log(error) - }); - } - sendeSenttothevcalue=()=>{ - - let {Senttothevcalue}=this.state; - - if(Senttothevcalue===undefined){ - this.setState({ - Senttothevcaluetype:true - }) - return - } - let id = this.props.match.params.shixunId; - let url="/shixuns/" + id +"/send_to_course.json"; - axios.post(url,{ - course_id:Senttothevcalue - }).then((response) => { - - this.props.showSnackbar(response.data.message); - this.setState({ - Senttothetype:false, - Searchvalue:"", - pages:1 - }) - // window.location.href = response.data.url; - // response.data.course_id - this.props.history.replace(response.data.first_category_url); - - }).catch((error) => { - console.log(error) - }); - - } - - hideSenttothevcalue=()=>{ - this.setState({ - Senttothetype:false, - Searchvalue:"", - pages:1 - }) - - - } - - /* - * 撤销发布按钮 - * */ - - ModalCancel=()=>{ - this.setState({ - Modalstype:false - }) - } - ModalSave=()=>{ - let id = this.props.match.params.shixunId; - let url="/shixuns/" + id +"/cancel_publish.json"; - axios.get(url).then((response) => { - this.props.showSnackbar(response.data.message); - window.location.reload() - }).catch((error) => { - console.log(error) - }); - } - cancel_publish=()=>{ - this.setState({ - Modalstype:true, - Modalstopval:"是否确认撤销发布?", - ModalCancel:this.ModalCancel, - ModalSave:this.ModalSave, - }) - } - - - /* - * 申请发布按钮 - * */ - applyrelease=()=>{ - let id = this.props.match.params.shixunId; - let url="/shixuns/" + id +"/publish.json"; - axios.get(url).then((response) => { - let evaluation_set_position - if(response.data.evaluation_set_position===null){ - evaluation_set_position=[] - }else{ - evaluation_set_position=response.data.evaluation_set_position - } - this.setState({ - Issuevisible:true, - tag_position:response.data.tag_position, - evaluation_set_position:evaluation_set_position, - publishboxstatus:response.data.status, - }) - }).catch((error) => { - console.log(error) - }); - }; - - hiddenIssuevisible=(val)=>{ - this.setState({ - Issuevisible:false - }) - if(val===0||val===1){ - window.location.reload() - } - - } - - //重置按钮 - // resetshixunCombat=(id)=>{ - // let zrl="/myshixuns/"+id+"/reset_my_game.json"; - // axios.get(zrl).then((response) => { - // window.location.href = "/shixuns/" + response.data.shixun_identifier + "/challenges"; - // message.success('重置成功'); - // }).catch((error) => { - // console.log(error) - // }); - // } - - // reset_my_game - hidestartshixunsreplace=(url)=>{ - this.setState({ - isSpin:true, - }) - axios.get(url).then((response) => { - if(response.status===200){ - // let path="/shixuns/"+response.data.shixun_identifier+"/challenges"; - // this.props.history.push(path); - message.success('重置成功,正在进入实训!'); - this.startshixunCombat(response.data.shixun_identifier, 1); - this.setState({ - shixunsreplace:false, - isSpin:false, - }) - - // message.success('重置成功,正在进入实训!'); - // this.startshixunCombat(); - }} - ).catch((error) => { - this.setState({ - startbtn:false, - shixunsreplace:false, - isSpin:false - }) - }); - - } - - - //开始实战按钮 - startshixunCombat=(id, reset)=>{ - - if(this.props.checkIfLogin()===false){ - this.props.showLoginDialog() - return - } - - if(this.props.checkIfProfileCompleted()===false){ - this.setState({ - AccountProfiletype:true - }) - return - } - - // if(this.props.checkIfProfessionalCertification()===false){ - // this.setState({ - // AccountProfiletype:true - // }) - // return - // } - - let {shixunsDetails} = this.props - if( shixunsDetails.shixun_status>1){ - this.setState({ - startbtn:true, - hidestartshixunsreplacevalue:"" - }) - }else{ - this.setState({ - hidestartshixunsreplacevalue:"" - }) - } - - - let url="/shixuns/"+id+"/shixun_exec.json" ; - if (reset) { - url += '?reset=' + reset - } - axios.get(url).then((response) => { - if(response.status===200){ - if(response.data.status===-2){ - // this.resetshixunCombat(response.data.message); - this.setState({ - startbtn:false, - shixunsreplace:true, - hidestartshixunsreplacevalue:response.data.message+".json" - }) - // this.shixunexec(response.data.message+".json") - }else if(response.data.status===-1){ - console.log(response) - }else if(response.data.status===-3){ - this.setState({ - shixunsmessage:response.data.message, - startshixunCombattype:true, - startbtn:false - }) - }else{ - // let path="/tasks/"+response.data.game_identifier; - // this.props.history.push(path); - - - // this.context.router.history.push(path); - if(response.data.status!=401){ - window.location.href = "/tasks/"+response.data.game_identifier; - } - - } - } - }).catch((error) => { - this.setState({ - startbtn:false - }) - }); - } - - tocertification=()=>{ - let{certi_url}=this.state; - this.setState({ - Forkauthentication:false - }) - window.location.href=certi_url; - } - - SenttotheValue=(e)=>{ - this.setState({ - Searchvalue:e.target.value - }) - } - - hidestartshixunCombattype=()=>{ - this.setState({ - startshixunCombattype:false - }) - } - - hideAccountProfile=()=>{ - this.setState({ - AccountProfiletype:false - }) - } - - - showonMouseOver=()=>{ - $("#ratePanel").show(); - this.setState({ - showradios:true - }) - } - - hideonMouseOut=()=>{ - $("#ratePanel").hide(); - this.setState({ - showradios:false - }) - } - - render() { - let { - Forkvisible, - Senttothetype, - Senttothevcalue, - evaluation_set_position, - Forkauthentication, - can_fork, - certi_url, - tag_position, - courses_count, - course_list, - Issuevisible, - publishboxstatus, - showradios, - startbtn, - Searchvalue, - startshixunCombattype, - shixunsmessage, - pages, - shixunsreplace, - hidestartshixunsreplacevalue, - Forkvisibletype, - AccountProfiletype, - isIE} = this.state; - let {shixunsDetails, shixunId, star_info, star_infos} = this.props; - let challengeBtnTipText = ''; - let challengeBtnText = '模拟实战'; - // let star_info=[] - // if (shixunsDetails.status === 0) { - // - // } else if (shixunsDetails.status === 1) { - // - // } else if (shixunsDetails.status === 2) { - // challengeBtnTipText = '开始学习并完成实战任务' - // - // } - if(shixunsDetails!=undefined){ - if (shixunsDetails.shixun_status === 0 ) { - challengeBtnText = '继续实战' - } else if (shixunsDetails.shixun_status === 1) { - challengeBtnText = '查看实战' - } else if (shixunsDetails.shixun_status === 3) { - challengeBtnText = '继续实战' - }else{ - challengeBtnText = "开始实战" - } - } - - - // let list=shixunsDetails.task_operation; - // if(list!=undefined){ - // if (shixunsDetails.status === 0 ) { - // for(var i=0; i; - const MyRate = ({ defaultValue, ...rest }) => { - let myValue = defaultValue; - // console.log(myValue-Math.floor(myValue)) - // if (myValue < Math.ceil(myValue)) { - // myValue = Math.floor(myValue) + 0.5; - // } - - return ; - }; - return ( - - shixunsDetails===undefined?"": -
    -
    - - {AccountProfiletype===true?this.hideAccountProfile()} - {...this.props} - {...this.state} - />:""} - - - {this.state.Modalstype===true?:""} - -
    -

    - {shixunsDetails.name} - { - shixunsDetails.fork_from === undefined || shixunsDetails.fork_from === null ? "" : - - - - } - -

    -
    - {/**/} -
      -
    • - 学习人数 - {shixunsDetails.stu_num} -
    • - {/*
    • */} - {/*经验值*/} - {/*{shixunsDetails.experience}*/} - {/*
    • */} -
    • - 难度系数 - {shixunsDetails.diffcult} - -
    • -
    - -
    this.showonMouseOver()} onMouseOut={()=>this.hideonMouseOut()}> -
    学员评分
    -
    - -
    -
    this.hideonMouseOut()}> -
    - -
    -
    -
    - {star_infos[0]}分 - 总评分 -
    - {showradios === true ? - - : ""} -
    -
    -
    -
    -
    -
    - {showradios === true ? - - : ""} -
    - - {star_infos[1]}% -
    -
    -
    - {showradios === true ? - - : ""} -
    - - {star_infos[2]}% -
    -
    -
    - {showradios === true ? - - : ""} -
    - - {star_infos[3]}% -
    -
    -
    - {showradios === true ? - - : ""} -
    - - {star_infos[4]}% -
    -
    -
    - {showradios === true ? - - : ""} -
    - - {star_infos[5]}% -
    -
    -
    -
    -
    - -
    - - { - startbtn === false && shixunsDetails.shixun_status != -1 ? - - this.startshixunCombat(this.props.match.params.shixunId)} - className="fr user_default_btn task-btn-orange font-18" - id="shixun_operation" data-remote="true" - > - {shixunsDetails.task_operation === undefined ? "" : shixunsDetails.shixun_status > 1 ? shixunsDetails.task_operation[0] : "模拟实战"} - - - : "" - } - - -
    -

    目前该实训项目尚在内测中,将于{shixunsmessage}之后开放,谢谢!

    -
    -
    - {/*取消*/} - 知道啦 -
    - {/*

    */} - {/*知道了*/} - {/*

    */} -
    - - - -
    -

    实训已经更新了,正在为您重置!

    -
    - -
    -
    - - - - - - { - startbtn === true ? - 开启中 : "" - } - - {/*{*/} - {/*shixunsDetails.status=== 3 && shixunsDetails.task_operation[0]==="开始实战"?*/} - {/*{shixunsDetails.task_operation===undefined?"":shixunsDetails.task_operation[0]}:""*/} - {/*}*/} - - {shixunsDetails.shixun_status === 0 && this.props.identity < 5 ? - 申请发布 : "" - } - - - { - publishboxstatus === 0 ?
    -

    - 发布申请已提交,请等待管理员的审核
    -

    -
    : publishboxstatus === 1 ? -
    -

    - 发布申请已提交,请等待管理员的审核
    - • 我们将在1-2个工作日内完成审核 -

    -
    : publishboxstatus === 2 ?
    -

    - 第 - { - evaluation_set_position.map((item, key) => { - return ( - {item}, - ) - }) - } - 关评测设置尚未完成,无法申请发布 -

    -
    : publishboxstatus === 3 ? -
    -

    - 每一个关卡至少需要一个技能标签
    - 第 - { - tag_position.map((item, key) => { - return ( - {item}, - ) - }) - } - 关尚未设置技能标签,请补充 -

    -
    : -
    -

    - 尚未创建任务的实训,不能申请发布 -

    -
    - } - - -
    - - {shixunsDetails.shixun_status === 1 && this.props.identity < 5 ? - 撤销发布 : "" - } - - { - - - 发送至 - - - } - - -
    -
    - -
    - 选择的实训将会发送到指定课堂 -
    - -
    - this.SenttotheSearch(value)} - style={{width: '100%'}} - /> -
    - - -
    12?"cdefault ":"cdefault "}> -
    -
      - - { - course_list === undefined ? "" : course_list.map((item, key) => { - return ( - {item.name} - ) - }) - } - -
    -
    -
    - {this.state.Senttothevcaluetype===true?
    请选择你要发送的课堂
    :""} -
    12 ? "block" : "none"}}> - -
    - -
    -
    -
    -
    - 取消 - 确定 -
    - - -
    - -
    -
    -
    - - {shixunsDetails.shixun_status === 3 && - 已关闭 - } - - {shixunsDetails.shixun_status === -1 && - 已删除 - } - - - {this.props.identity < 8&&shixunsDetails.shixun_status != -1 ?
    - - - Fork - - - - - {Forkvisibletype===true? - - : -
    -

    复制将在后台执行,平台将为你创建
    一个新的同名实训和内容,请问是否继续?

    -
    -
    - 取消 - 确定 -
    -
    - } - - -
    - - -

    {can_fork}
    请问是否前往进行认证?

    -
    -
    - 取消 - 确定 -
    -
    - {!!shixunsDetails.fork_num && - - {shixunsDetails.fork_num} - - } - -
    :""} - -
    - -
    - -
    -
    正在等待管理员的审核。在审核通过前,可以随时撤销发布
    -
    - - ); - } -} - -export default TPMBanner; - diff --git a/public/react/src/tpm/TPMChallenge.js b/public/react/src/tpm/TPMChallenge.js deleted file mode 100644 index 847e8b965..000000000 --- a/public/react/src/tpm/TPMChallenge.js +++ /dev/null @@ -1,54 +0,0 @@ -import React, { Component } from 'react'; -import { Redirect } from 'react-router'; - -import PropTypes from 'prop-types'; - -import { CircularProgress } from 'material-ui/Progress'; - -import './TPMShixunDiscuss.css' - -import Challenges from './shixunchild/Challenges/Challenges' - -import TPMRightSection from './component/TPMRightSection' - -import TPMNav from './component/TPMNav' - -class TPMChallenge extends Component { - constructor(props) { - super(props) - - } - - render() { - const { loadingContent, shixun, user, match - } = this.props; - return ( - -
    - -
    - - - -
    - -
    - -
    -
    -
    - - ); - } -} - -export default TPMChallenge; diff --git a/public/react/src/tpm/TPMChallengeContainer.js b/public/react/src/tpm/TPMChallengeContainer.js deleted file mode 100644 index a7c3c8a2b..000000000 --- a/public/react/src/tpm/TPMChallengeContainer.js +++ /dev/null @@ -1,34 +0,0 @@ -import React, { Component } from 'react'; -import { Redirect } from 'react-router'; -import TPMChallenge from './TPMChallenge'; -class TPMChallengeContainer extends Component { - constructor(props) { - super(props) - this.state = { - tpmLoading: true, - creator: { - owner_id: '', - } - } - } - - render() { - const { tpmLoading } = this.props; - const user = this.props.current_user; - - return ( - - - { tpmLoading ?
    : - - - } - -
    - ); - } -} - -export default TPMChallengeContainer; diff --git a/public/react/src/tpm/TPMCollaborators.js b/public/react/src/tpm/TPMCollaborators.js deleted file mode 100644 index cfab39ca5..000000000 --- a/public/react/src/tpm/TPMCollaborators.js +++ /dev/null @@ -1,53 +0,0 @@ -import React, { Component } from 'react'; -import { Redirect } from 'react-router'; - -import PropTypes from 'prop-types'; - -import { CircularProgress } from 'material-ui/Progress'; - -import './TPMShixunDiscuss.css' - -import Collaborators from './shixunchild/Collaborators/Collaborators' -import TPMRightSection from './component/TPMRightSection' -import TPMNav from './component/TPMNav' - -class TPMCollaborators extends Component { - constructor(props) { - super(props) - } - - - render() { - const { loadingContent, creator, shixun, myshixun, recommend_shixuns, current_user, watched, - aboutFocus, user, match - } = this.props; - return ( - -
    - -
    - - - -
    - -
    - -
    -
    -
    - - ); - } -} - -export default TPMCollaborators; diff --git a/public/react/src/tpm/TPMCollaboratorsContainer.js b/public/react/src/tpm/TPMCollaboratorsContainer.js deleted file mode 100644 index 80049cee9..000000000 --- a/public/react/src/tpm/TPMCollaboratorsContainer.js +++ /dev/null @@ -1,47 +0,0 @@ -import React, { Component } from 'react'; -import { Redirect } from 'react-router'; - -import PropTypes from 'prop-types'; - -import TPMCollaborators from './TPMCollaborators' - -import axios from 'axios'; - -class TPMChallengeContainer extends Component { - constructor(props) { - super(props) - this.state = { - } - } - - componentWillReceiveProps(newProps, newContext) { - - } - - componentDidMount() { - // this.props.showShixun(); - } - - - - render() { - const { tpmLoading } = this.props; - const user = this.props.current_user; - return ( - - { tpmLoading ?
    : - - - } -
    - ); - } -} - -export default TPMChallengeContainer; diff --git a/public/react/src/tpm/TPMFork/TPMForklist.js b/public/react/src/tpm/TPMFork/TPMForklist.js deleted file mode 100644 index 59d9d23b8..000000000 --- a/public/react/src/tpm/TPMFork/TPMForklist.js +++ /dev/null @@ -1,213 +0,0 @@ -import React, {Component} from 'react'; - -import {Input, Select, Radio, Checkbox, Popconfirm, message, Modal,Pagination,Tooltip,Spin} from 'antd'; - -import {BrowserRouter as Router, Route, Link, Switch} from "react-router-dom"; - -// import "antd/dist/antd.css"; - -import { Rating } from "@icedesign/base"; - -import axios from 'axios'; - -import {getImageUrl, toPath, getUrl} from 'educoder' - -import './shixunCss/fork.css'; - -let origin = getUrl(); - -let path = getUrl("/editormd/lib/") - -const $ = window.$; - -let timeout; - -let currentValue; - - - -export default class TPMFork_listComponent extends Component { - constructor(props) { - super(props) - this.state = { - shixuns:undefined, - total_count:0, - shixunsID:undefined, - Forkvisible: true, - Forkcurrent: 1 - } - } - - - componentDidMount() { - let id = this.props.match.params.shixunId; - - let Url="/shixuns/"+id+"/fork_list.json"; - axios.get(Url, { - params: { - page:1, - limit:8 - } - }).then((response)=> { - this.setState({ - shixunsID:id, - shixuns:response.data.shixuns, - total_count:response.data.total_count, - Forkvisible:false - }) - }).catch((error)=>{ - console.log(error) - }); - } - - - TPMForkonChange=(pageNumber)=>{ - let id = this.props.match.params.shixunId; - this.setState({ - Forkvisible:true - }) - let Url="/shixuns/"+id+"/fork_list.json"; - axios.get(Url, { - params: { - page:pageNumber, - limit:8 - } - }).then((response)=> { - this.setState({ - shixunsID:id, - shixuns:response.data.shixuns, - total_count:response.data.total_count, - Forkvisible: false, - Forkcurrent: pageNumber - }) - }).catch((error)=>{ - console.log(error) - }); - } - render() { - - let {shixuns, total_count, shixunsID, Forkvisible, Forkcurrent} = this.state; - - const MyRate = ({ defaultValue, ...rest }) => { - let myValue = defaultValue; - // console.log(myValue-Math.floor(myValue)) - // if (myValue < Math.ceil(myValue)) { - // myValue = Math.floor(myValue) + 0.5; - // } - - return ; - }; - return ( - -
    - -
    - Fork实训列表 - 返回 -
    - - - {/**/} - -
    - - { shixuns===undefined?" ":shixuns.map((item,key)=>{ - return( -
    - -
    - {item.tag_name} - {/**/} -
    - -
    - -

    非试用内容,需要授权

    -
    - - - - - -
    -

    - - {item.name} - -

    - -

    - - - - {item.score_info===null?"5分":item.score_info+"分"} -

    - -

    - - - - {item.challenges_count} - - - - {/**/} - {/**/} - {/*{item.exp}*/} - {/**/} - {/**/} - - - - {item.stu_num} - - - - - {item.level} -

    - -
    -
    - ) - }) - } -
    - - {/*
    {total_count}
    */} -
    8 ? "block" : "none"}}> - {/*
    */} - -
    - - {/**/} -
    - - ) - } -} - - diff --git a/public/react/src/tpm/TPMFork/shixunCss/fork.css b/public/react/src/tpm/TPMFork/shixunCss/fork.css deleted file mode 100644 index 8cd2b9304..000000000 --- a/public/react/src/tpm/TPMFork/shixunCss/fork.css +++ /dev/null @@ -1,3 +0,0 @@ -.ml105 { - margin-left: 15%; -} \ No newline at end of file diff --git a/public/react/src/tpm/TPMFork/shixunCss/tag2.png b/public/react/src/tpm/TPMFork/shixunCss/tag2.png deleted file mode 100644 index 423d2f7e3..000000000 Binary files a/public/react/src/tpm/TPMFork/shixunCss/tag2.png and /dev/null differ diff --git a/public/react/src/tpm/TPMFork_listContainer.js b/public/react/src/tpm/TPMFork_listContainer.js deleted file mode 100644 index bbd55c2ee..000000000 --- a/public/react/src/tpm/TPMFork_listContainer.js +++ /dev/null @@ -1,50 +0,0 @@ -import React, { Component } from 'react'; -import { Redirect } from 'react-router'; - -import PropTypes from 'prop-types'; - -import TPMForklist from './TPMForklist' - -import axios from 'axios'; - -class TPMRanking_listContainer extends Component { - constructor(props) { - super(props) - this.state = { - tpmLoading: true, - creator: { - owner_id: '' - } - } - } - - componentWillReceiveProps(newProps, newContext) { - - } - - componentDidMount() { - this.props.showShixun(); - } - - - render() { - const { tpmLoading } = this.props; - const user = this.props.current_user; - return ( - - { tpmLoading ?
    : - - - } -
    - ); - } -} - -export default TPMRanking_listContainer; diff --git a/public/react/src/tpm/TPMForklist.js b/public/react/src/tpm/TPMForklist.js deleted file mode 100644 index 251821209..000000000 --- a/public/react/src/tpm/TPMForklist.js +++ /dev/null @@ -1,63 +0,0 @@ -import React, { Component } from 'react'; -import { Redirect } from 'react-router'; - -import PropTypes from 'prop-types'; - -import { CircularProgress } from 'material-ui/Progress'; - -import './TPMShixunDiscuss.css' - -import Shixunfork_list from './shixunchild/Shixunfork_list' -import TPMRightSection from './component/TPMRightSection' -import TPMNav from './component/TPMNav' - -class TPMForklist extends Component { - constructor(props) { - super(props) - - } - - componentWillReceiveProps(newProps, newContext) { - - } - - componentDidMount() { - - } - - - - render() { - const { loadingContent, creator, shixun, myshixun, recommend_shixuns, current_user, watched, - aboutFocus, user, match - } = this.props; - - return ( - -
    - -
    - - { loadingContent ? - : - - - } -
    - -
    - -
    -
    -
    - - ); - } -} - -export default TPMForklist; diff --git a/public/react/src/tpm/TPMIndex.css b/public/react/src/tpm/TPMIndex.css deleted file mode 100644 index bbec3d34d..000000000 --- a/public/react/src/tpm/TPMIndex.css +++ /dev/null @@ -1,229 +0,0 @@ -html{ - height: 100%; - overflow: hidden; -} -body { - overflow: auto !important; - font-family: "Microsoft YaHei"; -} - -#root { - /* ie兼容性 */ - position: relative; - min-height: 100%; -} -body>.-task-title { - opacity: 1 !important; -} -/*�����Ŵ󾵵�����·Ŵ󾵵�λ��*/ -#root .search-all { - width: 219px; -} - -/*Header START*/ -.newHeader .logoimg { - margin-top: 16px; - float: left; - width: 97px; -} -.head-right i { - font-size: 20px; - float: none !important; -} -.headIcon, #header_keyword_search { - padding-top: 13px !important; -} -.search-icon { - height: 30px !important; -} -.search-icon i { - font-size: 20px; -} -#header_keyword_search i { - color: #4cacff; -} -.ant-select-selection--multiple{ - padding-bottom: 0px!important; - padding-top:3px; -} -/* 先注释掉下面2个样式,这样写影响范围太广了,并不是所有的select都需要40px高 */ -/* .ant-select-selection--single{ - height:40px!important; -} -.ant-select-selection__rendered{ - line-height: 40px!important; -} */ -.ant-select-selection--multiple .ant-select-selection__rendered>ul>li, .ant-select-selection--multiple>ul>li{ - height: 25px!important; - line-height: 23px!important; - margin-bottom:3px; - margin-top:0px; -} -/*Main START*/ - - -.newContainer{ - background: #fafafa!important; -} - -.ant-modal-title{ - font-size: 16px; - font-weight: bold !important; - color: #333; -} - -.ant-modal-title{ - text-align: center; -} -/*.ant-modal{*/ - /*top:10rem !important;*/ -/*}*/ - -@-moz-document url-prefix() { - .ant-radio-inner { - width: 17px !important; - height: 17px !important; - } -} -/* IE只能用padding,不能用上下居中 */ -.shixunDetail_top{ - display: block!important; - padding-top: 48px; -} -.totalScore{ - display: block!important; - padding-top: 28px; -} -.head-nav ul#header-nav li{ - /*font-weight: 600;*/ -} - -/*.newFooter{*/ - /*position: fixed !important;*/ -/*}*/ - -.edu-menu-panel .edu-menu-listnew:hover .careersiconfont{ - color: #000 !important; -} - - -.newHeader { - background: #24292D !important; - height: 60px !important; -} - - -/*-------------------个人主页:右侧提示区域--------------------------*/ -.-task-sidebar{position:fixed;width:40px;height:180px;right:0;bottom:30px;z-index: 10;} -.-task-sidebar>div{height: 40px;line-height: 40px;box-sizing: border-box;width:40px;background:#4CACFF;color:#fff;font-size:20px;text-align:center;margin-bottom:5px;border-radius: 4px;} -.-task-sidebar>div i{ color:#fff;} -.-task-sidebar>div i:hover{color: #fff!important;} -.gotop{background-color: rgba(208,207,207,0.5)!important;padding: 0px!important;} -.-task-desc{background:#494949;width:90px;line-height: 36px;text-align: center; - position: absolute;color: #fff;font-size: 13px;z-index: 999999;opacity: 0;} -.-task-desc div{position: absolute;top:10px;right: -7px;height: 13px;} -.-task-desc div img{float: left} -.-task-sidebar .scan_ewm{ - position: absolute !important; - right: 45px !important; - bottom: 0px !important; - background-color: #494949 !important; - -webkit-box-sizing: border-box !important; - box-sizing: border-box !important; - font-size: 14px !important; - line-height: 16px !important; - display: none; - height: 213px !important; -} -.trangle_right{position: absolute;right: -5px;bottom: 15px;width: 0;height: 0px;border-top: 6px solid transparent;border-left: 5px solid #494949;border-bottom: 6px solid transparent} - -.HeaderSearch{ - margin-top: 18px; - margin-right: 20px; -} -.HeaderSearch .ant-input-search .ant-input{ - /*height:30px;*/ - background: #373e3f !important; - border: 1px solid #373e3f !important; - -} -.ant-input-search .ant-input-affix-wrapper{ - border:transparent; -} -.ant-input-affix-wrapper:hover .ant-input:not(.ant-input-disabled) { - /* 比较奇怪的需求,先注释掉了,如果需要启用,麻烦增加class限制,别影响别的地方的使用 */ - /* border-color: transparent; */ -} - -.ant-input:focus { - /*border-color: transparent;*/ - border-right-width: 1px !important; - outline: 0; - -webkit-box-shadow: 0 0 0 2px transparent; - box-shadow: 0 0 0 2px transparent; - border: 1px solid #d9d9d9; -} - -.HeaderSearch .ant-input-search .ant-input::-webkit-input-placeholder{ - color: #999; - font-size: 14px; -} - -.HeaderSearch .ant-input-search .ant-input:-moz-placeholder { - color: #999; - font-size: 14px; -} - -.HeaderSearch .ant-input-search .ant-input::-moz-placeholder{ - color: #999; - font-size: 14px; -} - -.HeaderSearch .ant-input-search .ant-input:-ms-input-placeholder{ - color: #999; - font-size: 14px; -} - -.HeaderSearch .ant-input-search .ant-input-suffix .anticon-search { - color: #999; -} - -.HeaderSearch .ant-input-search .ant-input{ - color: #fff; -} - -.HeaderSearch .ant-input-search .ant-input-suffix{ - background: transparent !important; -} - -.roundedRectangles{ - position: absolute; - top: 10px; - right: -22px; -} - -.HeaderSearch{ - width: 325px; - /*right: 20px;*/ -} -.HeaderSearch .ant-input-search{ - right: 20px; -} -.mainheighs{ - height: 100%; - display: block; -} - -.ml18a{ - margin-left:18%; -} - -.logoimg{ - float: left; - min-width: 40px; - height:40px; -} - -.headwith100b{ - width: 100%; -} \ No newline at end of file diff --git a/public/react/src/tpm/TPMIndex.js b/public/react/src/tpm/TPMIndex.js deleted file mode 100644 index 9b3308e04..000000000 --- a/public/react/src/tpm/TPMIndex.js +++ /dev/null @@ -1,416 +0,0 @@ -import React, { Component } from 'react'; - -import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom"; - -import Loading from '../../Loading'; - -import Loadable from 'react-loadable'; - -import { TPMIndexHOC } from './TPMIndexHOC'; - -import { SnackbarHOC } from 'educoder'; - -import TPMBanner from './TPMBanner'; - -import axios from 'axios'; - -import TPMShixunDiscussContainer from './TPMShixunDiscussContainer'; - -import TPMRepositoryComponent from './TPMRepositoryComponent'; - -import TPMRepositoryCommits from './shixunchild/Repository/TPMRepositoryCommits'; - -import TPMsettings from './TPMsettings/TPMsettings'; - -import TPMChallengeComponent from './TPMChallengeContainer'; -import TPMPropaedeuticsComponent from './TPMPropaedeuticsComponent'; -import TPMRanking_listComponent from './TPMRanking_listContainer'; -import TPMCollaboratorsComponent from './TPMCollaboratorsContainer'; -import Audit_situationComponent from './Audit_situationComponent'; - -import '../page/tpiPage.css' - -const $ = window.$ -//任务 -// const TPMChallengeComponent = Loadable({ -// loader: () => import('./TPMChallengeContainer'), -// loading: Loading, -// }) - -//背景知识 -// const TPMPropaedeuticsComponent = Loadable({ -// loader: () => import('./TPMPropaedeuticsComponent'), -// loading: Loading, -// }) - -//版本库 -// const TPMRepositoryComponent = Loadable({ -// loader: () => import('./TPMRepositoryComponent'), -// loading: Loading, -// }) - -// const TPMRepositoryComponent = Loadable({ -// loader: () => import('./TPMRepositoryComponent'), -// loading: Loading, -// }) - -//合作 -// const TPMCollaboratorsComponent = Loadable({ -// loader: () => import('./TPMCollaboratorsContainer'), -// loading: Loading, -// }) - - -//评论 -// const TPMShixunDiscussComponent = Loadable({ -// loader: () => import('./TPMShixunDiscussContainer'), -// loading: Loading, -// }) - -//排行版 -// const TPMRanking_listComponent = Loadable({ -// loader: () => import('./TPMRanking_listContainer'), -// loading: Loading, -// }) - -// //编辑实训 -// const TPMModifysettings = Loadable({ -// loader: () =>import('./modules/tpm/TPMsettings/TPMsettings'), -// loading: Loading, -// }) - -//新建实训 -const TPMchallengesnew = Loadable({ - loader: () => import('./challengesnew/TPMchallengesnew'), - loading: Loading, -}) - -//新建tab2 -const TPMevaluation = Loadable({ - loader: () => import('./challengesnew/TPMevaluation'), - loading: Loading, -}) - -//新建tab3答案 -// const TPManswer = Loadable({ -// loader: () => import('./challengesnew/TPManswer'), -// loading: Loading, -// }) -const TPManswer = Loadable({ - loader: () => import('./challengesnew/TPManswer2'), - loading: Loading, -}) - -//选择题 -const TPMquestion = Loadable({ - loader: () => import('./challengesnew/TPMquestion'), - loading: Loading, -}) - -//fork列表 -const TPMFork_listComponent = Loadable({ - loader: () => import('./TPMFork/TPMForklist'), - loading: Loading, -}) -//背景知识修改 -const TPMUpdatepropaede = Loadable({ - loader: () => import('./TPMUpdatepropaede/TPMUpdatepropaede'), - loading: Loading, -}) - - - -// 版本库添加文件 -const AddFile = Loadable({ - loader: () => import('./shixunchild/Repository/RepositoryAddFile'), - loading: Loading, -}) - -const interceptorUrlArray = ['repository.json', 'commits.json', 'propaedeutics.json' - , 'challenges.json', 'discusses.json', 'ranking_list.json', 'collaborators.json'] -const cacheInterceptorUrlMap = {} -class TPMIndex extends Component { - constructor(props) { - super(props) - this.state = { - loadingContent: false, - power: false, - shixunsDetails: {}, - shixunId: undefined, - star_info: [0, 0, 0, 0, 0, 0], - star_infos: [0, 0, 0, 0, 0, 0], - identity:undefined, - TPMRightSectionData:undefined, - PropaedeuticsList: undefined, - } - } - - componentDidMount = () => { - - let id = this.props.match.params.shixunId; - - // let collaborators = `/shixuns/` + id + `/propaedeutics.json`; - // - // axios.get(collaborators).then((response) => { - // if (response.status === 200) { - // if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { - // - // }else{ - // this.setState({ - // PropaedeuticsList: response.data, - // shixunId: id - // }); - // } - // - // } - // }).catch((error) => { - // console.log(error) - // }); - - let Url = `/shixuns/` + id + `.json`; - axios.get(Url).then((response) => { - if (response.status === 200) { - document.title=response.data.name; - let newstar_info = []; - // let start1= - for (var i = 0; i < response.data.score_info.length; i++) { - - if (i === 0) { - newstar_info.push(response.data.score_info[i]) - } else { - newstar_info.push((response.data.score_info[i] / 100) * 5) - } - } - let newstar_infos = response.data.score_info; - this.setState({ - shixunsDetails: response.data, - shixunId: id, - star_info: newstar_info, - star_infos: newstar_infos, - power: response.data.power, - identity: response.data.identity, - propaedeutics:response.data.propaedeutics, - status: response.data.shixun_status, - secret_repository: response.data.secret_repository, - - }); - } - }).catch((error) => { - this.setState({ - shixunsDetails: undefined, - shixunId: undefined, - star_info: undefined, - star_infos: undefined, - power: undefined, - identity: undefined, - status: undefined, - propaedeutics:undefined - }); - }); - - this.tpmContentRequestInterceptor = axios.interceptors.request.use((config) => { - let url = config.url; - // console.log('tpmContentRequestInterceptor:', url) - for ( let i = 0; i < interceptorUrlArray.length; i++ ) { - if (url.indexOf(interceptorUrlArray[i]) != -1) { - url = url.split('?')[0] - console.log('loadingContent, url:', url) - - this.setState({ loadingContent: true }) - - cacheInterceptorUrlMap[url] = true - } - } - return config; - }, function (error) { - return Promise.reject(error); - }); - - // Add a response interceptor - this.tpmContentResponseInterceptor = axios.interceptors.response.use((response) => { - // console.log('loadingContent finished, url:', response.config.url) - // TODO 依赖了api这个前缀 - let url = response.config.url.split('api')[1]; - url = url.split('?')[0] - if (cacheInterceptorUrlMap[url]) { - - this.setState({ loadingContent: false }) - delete cacheInterceptorUrlMap[response.url] - } - return response; - }, function (error) { - // Do something with response error - return Promise.reject(error); - }); - - - //右侧数据 - let shixunsDetailsURL=`/shixuns/`+id+`/show_right.json`; - axios.get(shixunsDetailsURL).then((response)=> { - this.setState({ - TPMRightSectionData: response.data - }); - }) - - } - componentWillUnmount = () => { - axios.interceptors.request.eject(this.tpmContentRequestInterceptor); - this.tpmContentRequestInterceptor = null; - axios.interceptors.request.eject(this.tpmContentResponseInterceptor); - this.tpmContentResponseInterceptor = null; - } - - - setLoadingContent = (isLoadingContent) => { - this.setState({ loadingContent: isLoadingContent }) - } - - // TpmTPMBannertype(type){ - // - // } - - render() { - let url = window.location.href; - let flag = url.indexOf("add_file")>-1; - return ( -
    - { - !flag && - - } - - - - () - }> - () - }> - - () - }> - - () - }> - - () - }> - () - }> - - {/* */} - - () - }> - - - () - }> - - - {/* */} - - - (this.initForumState(data)} - setSearchValue={this.setSearchValue} - setHotLabelIndex={this.setHotLabelIndex} - />) - }> - - - () - }> - - - () - }> - - () - }> - - () - }> - - () - }> - - {/*评测设置*/} - () - }> - - - {/*参考答案*/} - () - }> - - {/*新建关卡*/} - () - }> - - {/*编辑关卡*/} - () - }> - - {/*新建选择题*/} - () - }> - - {/*修改选择题*/} - () - }> - - {/*修改选择题*/} - () - }> - - () - }> - - - {/**/} - - -
    - ); - } -} - -export default SnackbarHOC() (TPMIndexHOC ( TPMIndex )); diff --git a/public/react/src/tpm/TPMIndexHOC.js b/public/react/src/tpm/TPMIndexHOC.js deleted file mode 100644 index d353ceba7..000000000 --- a/public/react/src/tpm/TPMIndexHOC.js +++ /dev/null @@ -1,754 +0,0 @@ -import React, { Component } from 'react'; - -import PropTypes from 'prop-types'; - -import NewHeader from './NewHeader' -import NewFooter from './NewFooter' -import SiderBar from './SiderBar' -import { getUrl, downloadFile } from 'educoder' -import axios from 'axios'; -import { Spin } from 'antd'; -import './TPMIndex.css'; -import LoginDialog from '../login/LoginDialog'; -import AccountProfile from '../user/AccountProfile'; - -import Trialapplication from "../login/Trialapplication"; -// import "antd/dist/antd.css"; -// import '../../css/educoder/edu-common.css' -// import '../../css/educoder/edu-all.css' -// import '../../css/educoder/edu-main.css' - -const $ = window.$; -const versionNum = '0001'; -// let _url_origin = getUrl() -let _url_origin=''; -if(window.location.port === "3007"){ - _url_origin="http://pre-newweb.educoder.net"; -} - -// let _url_origin=`https://www.educoder.net`; - -if (!window['indexHOCLoaded']) { - window.indexHOCLoaded = true; - //解决首屏加载问题 - - // $('head').append($('') - // .attr('href', `${_url_origin}/stylesheets/educoder/antd.min.css?1525440977`)); - $('head').append($('') - .attr('href', `${_url_origin}/stylesheets/css/edu-common.css?1`)); - - $('head').append($('') - .attr('href', `${_url_origin}/stylesheets/educoder/edu-main.css?1`)); - - // index.html有加载 - $('head').append($('') - .attr('href', `${_url_origin}/stylesheets/educoder/edu-all.css?1`)); - - - // $('head').append($('') - // .attr('href', `${_url_origin}/stylesheets/educoder/css_min_all.css?1525440977`)); - // 加timeout 为了覆盖掉antd的样式 - // setTimeout(() => { - // $('head').append( $('') - // .attr('href', `${_url_origin}/stylesheets/css/edu-common.css?1525440977`) ); - - // $('head').append( $('') - // .attr('href', `${_url_origin}/stylesheets/educoder/edu-all.css?1525440977`) ); - // $('head').append( $('') - // .attr('href', `${_url_origin}/stylesheets/educoder/edu-main.css?1525440977`) ); - // }, 1000); - - $("script").append('') - .attr('src', `${_url_origin}/javascripts/jquery-1.8.3-ui-1.9.2-ujs-2.0.3.js?_t=${versionNum}`); - -} - // `${_url_origin}/javascripts/jquery-1.8.3-ui-1.9.2-ujs-2.0.3.js?_t=${versionNum}` -// TODO css加载完成后再打开页面,行为和tpm其他页面一致 -export function TPMIndexHOC(WrappedComponent) { - // 这里如果extends WrappedComponent 会出现 WrappedComponent mount twice的问题 - return class II extends React.Component { - constructor(props) { - super(props) - window.$('#root').css('position', 'relative') - - this.state = { - tpmLoading: true, - resLoading: true, - Headertop:undefined, - Footerdown:undefined, - coursedata: {}, - - isRender: false, - AccountProfiletype: false, - - globalLoading: false, - dataquerys:{}, - isloginCancel:undefined, - mygetHelmetapi: null, - } - } - - // header里面需要有user - initCommonState(user) { - // 更新头像后,需要改变参数,不然会被图片缓存影响到 --> 后台已加 ?t=${new Date().getTime() - const newUser = Object.assign({}, {...user}, { image_url: `${user.image_url}`}); - this.setState({ - user: newUser, - current_user: newUser - }) - } - showShixun = () => { - const { shixunId } = this.props.match.params - const url = `/api/v1/shixuns/${shixunId}/show_shixun` - - this.setState({ tpmLoading: true }) - axios.get(url, - { - withCredentials: true - } - ).then((response) => { - if (response.data && response.data.shixun) { - this.initCommonState(response.data.current_user) - response.data.tpmLoading = false; - this.setState(response.data); - } - - }).catch((error) => { - console.log(error) - }) - } - aboutFocus = () => { - const { creator, watched } = this.state - /*http://localhost:3000/api/v1/users/155/watch?object_id=156&object_type=user*/ - - const focusUrl = `/api/v1/users/${creator.owner_id}/${watched ? 'unwatch' : 'watch'}?object_id=${creator.owner_id}&object_type=user` - - axios.get(focusUrl,{ - }) - .then((response) => { - const status = response.data.status; - if(status == 1){ - const new_author_info = Object.assign({}, creator) - this.setState({ - watched: !watched - }) - } - }).catch((error) => { - console.log(error) - }) - } - - keyupListener = (e) => { - if (e.key === "Escape") { - this.setState({ globalLoading: false }) - } - } - componentWillUnmount() { - window.removeEventListener('keyup', this.keyupListener) - } - - componentDidMount() { - // console.log("TPMIndexHOC========"); - // console.log(this.props); - window.addEventListener('keyup', this.keyupListener) - - if(this.props.match.path==="/"){ - // document.title="创新源于实践"; - }else if(this.props.match.path==="/403"){ - document.title="你没有权限访问"; - }else if(this.props.match.path==="/nopage"){ - document.title="没有找到该页面"; - }else if(this.props.match.path==="/shixuns"){ - document.title="实训项目"; - }else if(this.props.match.path==="/paths"){ - document.title="实践课程"; - }else if(this.props.match.path==="/courses"){ - document.title="翻转课堂"; - } - - - $.ajaxSetup({ - cache: true - }); - - //帮助后台传参数 - const query = this.props.location.search; - // const type = query.split('?chinaoocTimestamp='); - // console.log("Eduinforms12345"); - // console.log(this.foo(query)); - // console.log(JSON.stringify(this.foo(query))); - var dataqueryss={} - try { - var foqus=this.foo(query); - if(JSON.stringify(foqus) ==="{}"){ - this.setState({ - dataquerys:{}, - }); - }else{ - this.setState({ - dataquerys:foqus, - }); - dataqueryss=foqus; - } - }catch (e) { - this.setState({ - dataquerys:{}, - }) - } - this.fetchUsers(dataqueryss); - - let url=`/users/get_navigation_info.json`; - axios.get(url, { - - }).then((response) => { - // console.log("开始请求/get_navigation_info.json"); - // console.log(response); - if(response!=undefined){ - if(response.status===200){ - this.setState({ - Headertop:response.data.top, - Footerdown:response.data.down - }) - } - } - }); - ///请求定制化的信息 - this.getAppdata(); - } - /** - 课堂权限相关方法,暂时写这里了 ----------------------------------------START - ADMIN = 0 # 超级管理员 - CREATOR = 1 # 课程创建者 - PROFESSOR = 2 # 课程老师 - ASSISTANT_PROFESSOR = 3 # 课程助教 - STUDENT = 4 # 学生 - NORMAL = 5 # 普通用户 - - v2 - # 课程权限判断 - ADMIN = 0 # 超级管理员 - BUSINESS = 1 # 运营人员 - CREATOR = 2 # 课程创建者 课堂管理员 - PROFESSOR = 3 # 课程老师 - ASSISTANT_PROFESSOR = 4 # 课程助教 - STUDENT = 5 # 学生 - NORMAL = 6 # 普通用户 - Anonymous = 7 # 普未登录 - */ - //超管0 - isSuperAdmin = () => { - // return false - return this.state.coursedata&&this.state.coursedata.course_identity === 0 - } - isCourseAdmin = () => { - return this.state.coursedata&&this.state.coursedata.course_identity === 2 - } - //超管、运维0-1 - isClassManagement = () => { - return this.state.coursedata&&this.state.coursedata.course_identity < 2 - } - //超管、运维、课堂管理0-2 - isAdminOrCreator = () => { - return this.state.coursedata&&this.state.coursedata.course_identity < 3 - } - //超管、运维、课堂管理、老师0-3 - isAdminOrTeacher = () => { - return this.state.coursedata&&this.state.coursedata.course_identity < 4 - } - // 超管、运维、课堂管理、老师、助教0-4 - isAdmin = () => { - return this.state.coursedata&&this.state.coursedata.course_identity < 5 - } - // 学生5 - isStudent = () => { - return this.state.coursedata&&this.state.coursedata.course_identity === 5 - } - // 超管、运维、课堂管理、老师、助教、学生0-5 - isAdminOrStudent = () => { - return this.state.coursedata&&this.state.coursedata.course_identity <= 5 - } - // 游客未登录/非课堂成员6> - isNotMember = () => { - return this.state.coursedata&&this.state.coursedata.course_identity >= 6 - } - //课堂是否已结束 - isCourseEnd = () => { - return this.state.current_user ? this.state.current_user.course_is_end : false - } - - // setTrialapplication = ()=>{ - // this.setState({ - // isRenders:true - // }) - // - // } - - //获取数据为空的时候 - gettablogourlnull = () => { - this.setState({ - mygetHelmetapi: undefined - }); - document.title = "EduCoder"; - var link = document.createElement('link'), - oldLink = document.getElementById('dynamic-favicon'); - link.id = 'dynamic-favicon'; - link.rel = 'shortcut icon'; - link.href = "/react/build/./favicon.ico"; - if (oldLink) { - document.head.removeChild(oldLink); - } - document.head.appendChild(link); - }; - - //获取数据的时候 - gettablogourldata = (response) => { - document.title = response.data.setting.name; - var link = document.createElement('link'), - oldLink = document.getElementById('dynamic-favicon'); - link.id = 'dynamic-favicon'; - link.rel = 'shortcut icon'; - link.href = '/' + response.data.setting.tab_logo_url; - if (oldLink) { - document.head.removeChild(oldLink); - } - document.head.appendChild(link); - } - //获取当前定制信息 - getAppdata = () => { - let url = "/setting.json"; - axios.get(url).then((response) => { - // console.log("app.js开始请求/setting.json"); - // console.log("获取当前定制信息"); - if (response) { - if (response.data) { - this.setState({ - mygetHelmetapi: response.data.setting - }); - try { - if (response.data.setting.tab_logo_url) { - this.gettablogourldata(response); - } else { - this.gettablogourlnull(); - } - } catch (e) { - this.gettablogourlnull(); - } - - - } else { - - this.gettablogourlnull(); - - } - - } else { - this.gettablogourlnull(); - - } - - }).catch((error) => { - this.gettablogourlnull(); - - }); - }; - /** - 课堂权限相关方法,暂时写这里了 ----------------------------------------END - */ - fetchUser = () => { - let url = `/users/get_user_info.json` - let courseId; - let query = this.props.location.pathname; - const type = query.split('/'); - if (type[1] == 'courses' && type[2]) { - courseId = parseInt(type[2]) - // url += `?course_id=${courseId}` - } - var datay={}; - if(JSON.stringify(this.state.dataquerys) ==="{}"){ - datay={ - course_id:isNaN(courseId)?undefined:courseId, - school:1 - } - }else{ - datay={ - course_id:isNaN(courseId)?undefined:courseId, - school:1, - chinaoocTimestamp:this.state.dataquerys.chinaoocTimestamp, - websiteName:this.state.dataquerys.websiteName, - chinaoocKey:this.state.dataquerys.chinaoocKey, - } - } - axios.get(url,{params: - datay - }, - { - // withCredentials: true - } - ).then((response) => { - /* - { - "username": "黄井泉", - "login": "Hjqreturn", - "user_id": 12, - "image_url": "avatar/User/12", - "admin": true, - "is_teacher": false, - "tidding_count": 0 - } - */ - if(response=== undefined){ - return - } - if (response.data) { - this.initCommonState(response.data) - this.setState({ - tpmLoading: false, - coursedata: { - course_identity: response.data.course_identity >= 0 ? response.data.course_identity : undefined, - course_public: response.data.course_public, - name: response.data.course_name, - userid:response.data.user_id - }, - - }) - - } - - }).catch((error) => { - console.log(error) - }) - }; - fetchUsers = (yslurlobject) => { - let url = `/users/get_user_info.json` - let courseId; - let query = this.props.location.pathname; - const type = query.split('/'); - if (type[1] == 'courses' && type[2]) { - courseId = parseInt(type[2]) - // url += `?course_id=${courseId}` - } - var datay={}; - if(JSON.stringify(yslurlobject) ==="{}"){ - datay={ - course_id:isNaN(courseId)?undefined:courseId, - school:1 - } - }else{ - datay={ - course_id:isNaN(courseId)?undefined:courseId, - school:1, - chinaoocTimestamp:yslurlobject.chinaoocTimestamp, - websiteName:yslurlobject.websiteName, - chinaoocKey:yslurlobject.chinaoocKey, - } - } - axios.get(url,{params: - datay - }, - { - // withCredentials: true - } - ).then((response) => { - /* - { - "username": "黄井泉", - "login": "Hjqreturn", - "user_id": 12, - "image_url": "avatar/User/12", - "admin": true, - "is_teacher": false, - "tidding_count": 0 - } - */ - if(response=== undefined){ - return - } - if (response.data) { - this.initCommonState(response.data) - this.setState({ - tpmLoading: false, - coursedata: { - course_identity: response.data.course_identity >= 0 ? response.data.course_identity : undefined, - course_public: response.data.course_public, - name: response.data.course_name, - userid:response.data.user_id - }, - - }) - - } - - }).catch((error) => { - console.log(error) - }) - }; - //截取url 数据的 - foo=(url)=> { - var json = {}; - var regExp = /[\?\&](\w+)(=?)(\w*)/g; - var arr; - do { - arr = regExp.exec(url); - // console.log(arr); // arr = [完整的字符串, key, 等号或'', value或''] - - if (arr) { - var key = arr[1]; - var value = arr[3]; - // arr[2] === ''时, value = undefined - if (!arr[2]) - value = undefined; - - json[key] = value; - } - } while (arr); - - return json; - }; - hideLoginDialog = () => { - this.setState({ - isRender: false, - isloginCancel:undefined - }) - } - showLoginDialog = () => { - this.setState({ - isRender: true, - isloginCancel:"iscancel" - }) - } - //验证登录是否成功方法 - checkIfLogin = () => { - return this.state.current_user && this.state.current_user.login != '' - } - - hideAccountProfile = () => { - this.setState({ - AccountProfiletype: false - }) - } - showProfileCompleteDialog = () => { - this.dialogObj = {} - this.setState({ - AccountProfiletype: true - }) - } - //验证是否完善资料 - checkIfProfileCompleted = () => { - return this.state.current_user && this.state.current_user.profile_completed - } - showProfessionalCertificationDialog = () => { - this.dialogObj = { - content: '您需要去完成您的职业认证,才能使用此功能', - okText: '立即完成', - okHref: '/account/certification' - } - this.setState({ - AccountProfiletype: true, - }) - } - checkIfProfessionalCertification = () => { - return this.state.current_user && this.state.current_user.professional_certification - } - - - ShowOnlinePdf = (url) => { - return axios({ - method:'get', - url:url, - responseType: 'arraybuffer', - }).then((result)=>{ - var binaryData = []; - binaryData.push(result.data); - this.url =window.URL.createObjectURL(new Blob(binaryData, {type:"application/pdf"})); - window.open(this.url); - }) - } - DownloadFileA=(title,url)=>{ - let link = document.createElement('a'); - document.body.appendChild(link); - link.href =url; - link.download = title; - //兼容火狐浏览器 - let evt = document.createEvent("MouseEvents"); - evt.initEvent("click", false, false); - link.dispatchEvent(evt); - document.body.removeChild(link); - } - - DownloadOpenPdf=(type,url)=>{ - type===true?window.open(url):window.location.href=url; - } - slowDownload = (url, tip) => { - this._gLoadingTip = tip || '正在生成文件,请稍后...'; - this.setState({ globalLoading: true }) - const fileUrl = url; - downloadFile({ - url: fileUrl, - successCallback: (url) => { - this.setState({ globalLoading: false }) - console.log('successCallback') - }, - failCallback: (responseHtml, url) => { - this.setState({ globalLoading: false }) - console.log('failCallback') - } - }) - } - yslslowCheckresults =(tip) =>{ - this._gLoadingTip = tip || '成绩计算中,请稍候...'; - this.setState({ globalLoading: true }) - } - yslslowCheckresultsNo =() =>{ - this.setState({ globalLoading: false }) - } - - showGlobalLoading = (tip) => { - this._gLoadingTip = tip || '加载中,请稍后...'; - this.setState({ globalLoading: true }) - } - hideGlobalLoading = () => { - this.setState({ globalLoading: false }) - } - - MdifHasAnchorJustScorll=()=>{ - //mdhash滚动 - let anchor = decodeURI(this.props.location.hash).replace('#', ''); - // 对应id的话, 滚动到相应位置 - if (!!anchor) { - let anchorElement = document.getElementsByName(anchor); - if (anchorElement) { - if (anchorElement.length>0){ - anchorElement[anchorElement.length-1].scrollIntoView(); - } - } - } - } - - render() { - let{Headertop,Footerdown, isRender, AccountProfiletype,mygetHelmetapi}=this.state; - const common = { - isSuperAdmin:this.isSuperAdmin, - isAdminOrCreator:this.isAdminOrCreator, - isClassManagement:this.isClassManagement, - isCourseAdmin:this.isCourseAdmin, - - isAdmin: this.isAdmin, - isAdminOrTeacher: this.isAdminOrTeacher, - isStudent: this.isStudent, - isAdminOrStudent: this.isAdminOrStudent, - isNotMember: this.isNotMember, - isCourseEnd: this.isCourseEnd, - - isUserid:this.state.coursedata&&this.state.coursedata.userid, - fetchUser: this.fetchUser, - - showLoginDialog: this.showLoginDialog, - checkIfLogin: this.checkIfLogin, - showProfileCompleteDialog: this.showProfileCompleteDialog, - checkIfProfileCompleted: this.checkIfProfileCompleted, - checkIfProfessionalCertification: this.checkIfProfessionalCertification, - showProfessionalCertificationDialog: this.showProfessionalCertificationDialog, - - ShowOnlinePdf:(url)=>this.ShowOnlinePdf(url), - DownloadFileA:(title,url)=>this.DownloadFileA(title,url), - DownloadOpenPdf:(type,url)=>this.DownloadOpenPdf(type,url), - - slowDownload: this.slowDownload, - showGlobalLoading: this.showGlobalLoading, - hideGlobalLoading: this.hideGlobalLoading, - yslslowCheckresults:this.yslslowCheckresults, - yslslowCheckresultsNo:this.yslslowCheckresultsNo, - MdifHasAnchorJustScorll:this.MdifHasAnchorJustScorll - - }; - // console.log("this.props.mygetHelmetapi"); - // console.log(this.props.mygetHelmetapi); - return ( -
    - {isRender===true ? this.hideLoginDialog()} - {...this.props} - {...this.state} - /> : ""} - {/* AccountProfile 也用作职业认证 */} - {AccountProfiletype===true ? this.hideAccountProfile()} - {...this.props} - {...this.state} - {...this.dialogObj} - />:""} - - {/* 注释掉了1440 影响到了手机屏幕的展示 */} - - - - - -
    - this.initCommonState(user)} - {...this.props} {...this.state} - showShixun={this.showShixun} aboutFocus={this.aboutFocus} - {...common} - > - - -
    - -
    - - - -
    - ); - } - } -} \ No newline at end of file diff --git a/public/react/src/tpm/TPMPropaedeutics.js b/public/react/src/tpm/TPMPropaedeutics.js deleted file mode 100644 index 88a05fde7..000000000 --- a/public/react/src/tpm/TPMPropaedeutics.js +++ /dev/null @@ -1,74 +0,0 @@ -import React, { Component } from 'react'; -import { Redirect } from 'react-router'; - -import PropTypes from 'prop-types'; - -import { CircularProgress } from 'material-ui/Progress'; - -import './TPMShixunDiscuss.css' - -import Propaedeutics from './shixunchild/Propaedeutics/Propaedeu_tics' - -import TPMRightSection from './component/TPMRightSection' - -import TPMNav from './component/TPMNav' - -import axios from 'axios'; - -class TPMPropaedeutics extends Component { - constructor(props) { - super(props) - this.state = { - shixunId: undefined - } - } - - componentWillReceiveProps(newProps, newContext) { - } - - componentDidMount() { - - - } - - - render() { - const { loadingContent, creator, shixun, myshixun, recommend_shixuns, current_user, watched, - aboutFocus, user, match - } = this.props; - // - return ( - -
    - -
    - - - - -
    - -
    - -
    -
    -
    - - ); - } -} - -export default TPMPropaedeutics; diff --git a/public/react/src/tpm/TPMPropaedeuticsComponent.js b/public/react/src/tpm/TPMPropaedeuticsComponent.js deleted file mode 100644 index 7c3eadb89..000000000 --- a/public/react/src/tpm/TPMPropaedeuticsComponent.js +++ /dev/null @@ -1,39 +0,0 @@ -import React, { Component } from 'react'; -import { Redirect } from 'react-router'; - -import PropTypes from 'prop-types'; - -import TPMPropaedeutics from './TPMPropaedeutics' - -import axios from 'axios'; - -class TPMPropaedeuticsComponent extends Component { - constructor(props) { - super(props) - this.state = { - // tpmLoading: true, - // creator: { - // owner_id: '' - // } - } - } - - render() { - const { tpmLoading } = this.props; - - return ( - - { tpmLoading ?
    : - - - } -
    - - - ); - } -} - -export default TPMPropaedeuticsComponent ; diff --git a/public/react/src/tpm/TPMRanking_list.js b/public/react/src/tpm/TPMRanking_list.js deleted file mode 100644 index 7171692a7..000000000 --- a/public/react/src/tpm/TPMRanking_list.js +++ /dev/null @@ -1,59 +0,0 @@ -import React, { Component } from 'react'; -import { Redirect } from 'react-router'; - -import PropTypes from 'prop-types'; - -import { CircularProgress } from 'material-ui/Progress'; - -import './TPMShixunDiscuss.css' - -import Ranking_list from './shixunchild/Ranking_list/Ranking_list' -import TPMRightSection from './component/TPMRightSection' -import TPMNav from './component/TPMNav' - -class TPMRanking_list extends Component { - constructor(props) { - super(props) - - } - - render() { - const { loadingContent, creator, shixun, myshixun, recommend_shixuns, current_user, watched, - aboutFocus, user, match - } = this.props; - - // - return ( - -
    - -
    - - - - - -
    - -
    - -
    -
    -
    - - ); - } -} - -export default TPMRanking_list; diff --git a/public/react/src/tpm/TPMRanking_listContainer.js b/public/react/src/tpm/TPMRanking_listContainer.js deleted file mode 100644 index 98841b1ab..000000000 --- a/public/react/src/tpm/TPMRanking_listContainer.js +++ /dev/null @@ -1,37 +0,0 @@ -import React, { Component } from 'react'; -import { Redirect } from 'react-router'; - -import PropTypes from 'prop-types'; - -import TPMRanking_list from './TPMRanking_list' - -import axios from 'axios'; - -class TPMRanking_listContainer extends Component { - constructor(props) { - super(props) - this.state = { - } - } - - render() { - const { tpmLoading } = this.props; - const user = this.props.current_user; - - return ( - - { tpmLoading ?
    : - - - } -
    - ); - } -} - -export default TPMRanking_listContainer; diff --git a/public/react/src/tpm/TPMRepository.js b/public/react/src/tpm/TPMRepository.js deleted file mode 100644 index 0f8e31258..000000000 --- a/public/react/src/tpm/TPMRepository.js +++ /dev/null @@ -1,58 +0,0 @@ -import React, { Component } from 'react'; -import { Redirect } from 'react-router'; - -import PropTypes from 'prop-types'; - -import { CircularProgress } from 'material-ui/Progress'; - -import './TPMShixunDiscuss.css' - -import Repository from './shixunchild/Repository/Repository' -import TPMRightSection from './component/TPMRightSection' -import TPMNav from './component/TPMNav' - -// import RepositoryChooseModal from './component/modal/RepositoryChooseModal' - -class TPMRepository extends Component { - constructor(props) { - super(props) - } - - - render() { - const { loadingContent, creator, shixun, myshixun, recommend_shixuns, current_user, watched, - aboutFocus, user, match, isContentWidth100 - } = this.props; - - return ( - -
    - {/* 可能会影响到其他页面的样式,需要测试、协商 */} -
    - - {/* */} - { loadingContent ? - : - - } -
    - - { !isContentWidth100 &&
    - -
    } -
    -
    - - ); - } -} - -export default TPMRepository; diff --git a/public/react/src/tpm/TPMRepositoryComponent.js b/public/react/src/tpm/TPMRepositoryComponent.js deleted file mode 100644 index 027f3f705..000000000 --- a/public/react/src/tpm/TPMRepositoryComponent.js +++ /dev/null @@ -1,229 +0,0 @@ -import React, { Component } from 'react'; -import { Redirect } from 'react-router'; - -import PropTypes from 'prop-types'; - -import TPMRepository from './TPMRepository' - -import axios from 'axios'; - -import { trace_collapse, info } from 'educoder' - -import RepositoryCodeEditor from './shixunchild/Repository/RepositoryCodeEditor' - - -class TPMRepositoryComponent extends Component { - constructor(props) { - super(props) - this.nameTypeMap = {} - let pathArray = [] - var splitArray = window.location.pathname.split('shixun_show/'); - if (splitArray[1]) { - pathArray = splitArray[1].split('/') - if (pathArray[pathArray.length - 1] == '') { - // 有可能是这么访问的: http://localhost:3007/shixuns/3ozvy5f8/repository/fsu7tkaw/master/shixun_show/src/ - pathArray.length = pathArray.length - 1; - } - } - this.state = { - repositoryLoading: true, - pathArray: pathArray, - isContentWidth100: this._isFileInPathArray(pathArray) - } - } - componentDidUpdate(prevProps, prevState) { - if (this.props.secret_repository_tab != prevProps.secret_repository_tab) { - this.fetchRepo() - } - } - - - componentDidMount = () => { - - this.fetchRepo() - } - setContentWidth100 = (flag) => { - const newFileContent = flag === false ? '' : this.state.fileContent - this.setState({ - // isCodeFile - isContentWidth100: flag, - fileContent: newFileContent - }) - } - saveCode = (content) => { - const path = this.state.pathArray.join('/') - let id = this.props.match.params.shixunId; - let url = `/shixuns/${id}/update_file.json`; - axios.post(url, { - path: path, - content - }).then((response) => { - if(response.status === 200){ - this.setState({ - fileContent: response.data.content, - repositoryLoading: false - }); - } - trace_collapse('tpm save code res: ', response) - this.props.showSnackbar('文件保存成功') - - }).catch((error)=>{ - console.log(error) - }); - } - fetchCode = (newPathArray) => { - const path = newPathArray.join('/') - - // https://testeduplus2.educoder.net/shixuns/3ozvy5f8/file_content.json - this.setContentWidth100(true) - this.setState({ repositoryLoading: true, pathArray: newPathArray }) - let id = this.props.match.params.shixunId; - let url = `/shixuns/${id}/file_content.json`; - axios.post(url, { - path: path, - secret_repository: this.props.secret_repository_tab - }).then((response) => { - trace_collapse('repository res: ', response) - - if (response.data.status == -1) { - this.props.showSnackbar('无法找到对应的资源,请变更地址或联系管理员!') - return; - } - if(response.status === 200){ - this.setState({ - fileContent: response.data.content, - repositoryLoading: false - }); - this.props.history - .replace(`${this.props.match.url}/master/shixun_show/${newPathArray.join('/')}`) - } - - }).catch((error)=>{ - this.props.showSnackbar('无法找到对应的资源,请变更地址或联系管理员!') - console.log(error) - }); - } - _isFileName = (name) => { - return name.indexOf('.') !== -1 - } - _isFileInPathArray = (array) => { - if (!array || array.length === 0) { - return false - } - return this.nameTypeMap[array[array.length - 1]] !== 'tree' && this._isFileName( array[array.length - 1] ) - } - // listItem 如果是num,则是通过面包屑点击过来的,取pathArray的子集 - fetchRepo = (listItem) => { - const { pathArray } = this.state; - let newPathArray = pathArray.slice(0) - - if (listItem === 0 || listItem) { - this.setContentWidth100(false) - this.nameTypeMap[listItem.name] = listItem.type - if (typeof listItem == 'number') { // 参数是数字的话,做截取 - // if (this._isFileName(newPathArray[listItem])) { // 面包屑中的文件不让点击了 - // listItem--; - // } - newPathArray = newPathArray.slice(0, listItem) - } else if (listItem.type === 'tree') { - newPathArray.push(listItem.name) - } else if (listItem.type === 'blob') { - newPathArray.push(listItem.name) - this.setState({ pathArray: newPathArray }) - this.fetchCode(newPathArray) - return; - } - } - // https://testeduplus2.educoder.net/shixuns/3ozvy5f8/repository.json - this.setState({ repositoryLoading: true, pathArray: newPathArray }) - let urlNewPathArray = newPathArray; - let fileInPathArray = false; - if (newPathArray.length) { - fileInPathArray = this.nameTypeMap[newPathArray[newPathArray.length - 1]] ? this.nameTypeMap[newPathArray[newPathArray.length - 1]] !== 'tree' - : (listItem ? listItem.type !== 'tree' : this._isFileName( newPathArray[newPathArray.length - 1] )) - if ( fileInPathArray ) { - urlNewPathArray = newPathArray.slice(0, newPathArray.length - 1) - } - } - const path = urlNewPathArray.join('/') - - let id = this.props.match.params.shixunId; - let url = `/shixuns/${id}/${this.props.secret_repository_tab ? 'secret_repository' : 'repository'}.json`; - // this.props.setLoadingContent(true) - axios.post(url, { - path: path ? path : '' - }).then((response) => { - // this.props.setLoadingContent(false) - - const trees = response.data.trees - const treeIsFileMap = {} - if (!trees || !Array.isArray(trees)) { - // this.props.showSnackbar('无法找到对应的资源,请变更地址或联系管理员!') - // return; - } else { - trees.forEach(item => { - treeIsFileMap[item.name] = item.type == 'blob' - }) - } - if(response.status === 200){ - this.setState({ - treeIsFileMap, - ...response.data, - repositoryLoading: false - }); - this.props.history - .replace(`${this.props.match.url}` + - (newPathArray.length ? `/master/shixun_show/${newPathArray.join('/')}` : '')) - } - - // 初始化时,repo接口完毕后需要看是否需要fetchCode - if (fileInPathArray) { - this.fetchCode(newPathArray) - } - // info(response) - trace_collapse('repository res: ', response) - - }).catch((error)=>{ - console.log(error) - }); - } - - - render() { - const { isContentWidth100 } = this.state; - - // 需要重构 - return ( - - { !isContentWidth100 ? - - : -
    - {/* 可能会影响到其他页面的样式,需要测试、协商 */} -
    - -
    -
    - } - -
    - - - ); - } -} - -export default TPMRepositoryComponent ; diff --git a/public/react/src/tpm/TPMShixunDiscuss.css b/public/react/src/tpm/TPMShixunDiscuss.css deleted file mode 100644 index 3af4ec269..000000000 --- a/public/react/src/tpm/TPMShixunDiscuss.css +++ /dev/null @@ -1,47 +0,0 @@ -.tpmComment .-fit { - position: inherit; -} -.tpmComment .rc-pagination { - margin-left: auto; - margin-right: auto; - margin-top: 12px; - margin-bottom: 20px; -} -.tpmComment .paginationSection { - background: #FAFAFA; -} -.tpmComment .comment_item_cont.df.clearfix:nth-last-child(1) { - border-bottom: none; -} - -/*.tpmComment .fl.edu-back-white {*/ -/*min-height: 600px;*/ -/*}*/ - - -.user_watch_btn { - cursor: pointer; -} - - -/*md编辑器*/ -.tpmComment .commentItemMDEditor a.task-btn { - background: #4cacff!important; - margin-right: 16px; - margin-top: 16px; -} -/* md编辑器 resizeBar*/ - .tpmComment .commentItemMDEditor .editor__resize { - transform: translateX(-176%) -} - -#ratePanel > div > div > div.fr div.rateYo.fl.mt3 { - height: 20px; - line-height: 20px; - cursor: default; - width: 110px; -} - -.tpmComment .icon-jiangli { - /* margin-top: 2px; */ -} \ No newline at end of file diff --git a/public/react/src/tpm/TPMShixunDiscuss.js b/public/react/src/tpm/TPMShixunDiscuss.js deleted file mode 100644 index 9350060cc..000000000 --- a/public/react/src/tpm/TPMShixunDiscuss.js +++ /dev/null @@ -1,72 +0,0 @@ -import React, { Component } from 'react'; -import { Redirect } from 'react-router'; - -import PropTypes from 'prop-types'; - -import { CircularProgress } from 'material-ui/Progress'; - -import './TPMShixunDiscuss.css' - -import ShixunDiscuss from './shixunchild/ShixunDiscuss/ShixunDiscuss' -import TPMRightSection from './component/TPMRightSection' -import TPMNav from './component/TPMNav' - -import Comments from '../comment/Comments' -import { commentHOC } from '../comment/CommentsHOC' - -class TPMShixunDiscuss extends Component { - constructor(props) { - super(props) - - } - - componentWillReceiveProps(newProps, newContext) { - } - - componentDidMount() { - // TODO 加了HOC后 mount了两次 - this.props.fetchCommentIfNotFetched && - this.props.fetchCommentIfNotFetched(); - } - - render() { - const { loadingContent, creator, shixun, myshixun, recommend_shixuns, current_user, watched, - aboutFocus, user, match - } = this.props; - - return ( - -
    - -
    - - { loadingContent ? - : - - // onPaginationChange={this.onPaginationChange} - // - } -
    - -
    - -
    -
    -
    - - ); - } -} - -export default commentHOC ( TPMShixunDiscuss ); diff --git a/public/react/src/tpm/TPMShixunDiscussContainer.js b/public/react/src/tpm/TPMShixunDiscussContainer.js deleted file mode 100644 index 535840772..000000000 --- a/public/react/src/tpm/TPMShixunDiscussContainer.js +++ /dev/null @@ -1,45 +0,0 @@ -import React, { Component } from 'react'; -import { Redirect } from 'react-router'; - -import PropTypes from 'prop-types'; - -import TPMShixunDiscuss from './TPMShixunDiscuss' - -import axios from 'axios'; - -class TPMShixunDiscussContainer extends Component { - constructor(props) { - super(props) - this.state = { - } - } - - componentWillReceiveProps(newProps, newContext) { - - } - - componentDidMount() { - - } - - render() { - const { tpmLoading } = this.props; - const user = this.props.current_user; - return ( - - { tpmLoading ?
    : - - - } -
    - ); - } -} - -export default TPMShixunDiscussContainer; diff --git a/public/react/src/tpm/TPMUpdatepropaede/TPMUpdatepropaede.js b/public/react/src/tpm/TPMUpdatepropaede/TPMUpdatepropaede.js deleted file mode 100644 index 63872b133..000000000 --- a/public/react/src/tpm/TPMUpdatepropaede/TPMUpdatepropaede.js +++ /dev/null @@ -1,100 +0,0 @@ -import React, {Component} from 'react'; - -import {Input, Select, Radio, Checkbox, Popconfirm, message, Modal} from 'antd'; - -import {BrowserRouter as Router, Route, Link, Switch} from "react-router-dom"; - -import axios from 'axios'; - -import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor'; - -import {getUrl} from 'educoder'; - - - -export default class TPMUpdatepropaede extends Component { - constructor(props) { - super(props) - this.neweditanswerRef=React.createRef(); - this.state = { - shixunId:undefined, - } - } - - componentDidMount() { - let id = this.props.match.params.shixunId; - let url="/shixuns/"+id+"/propaedeutics.json"; - axios.get(url).then((response) => { - if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { - - }else{ - this.setState({ - shixunId:id, - }) - if(response.data.content[0]!=null){ - this.setState({ - editanswersRefval:response.data.content, - }) - this.neweditanswerRef.current.setValue(response.data.content) - }else{ - this.setState({ - editanswersRefval:"", - }) - this.neweditanswerRef.current.setValue('') - } - } - }).catch((error) => { - console.log(error) - }); - - } - - updatepropaedeuticsvalue=()=>{ - let id = this.props.match.params.shixunId; - let url="/shixuns/"+id+"/update_propaedeutics.json"; - const update_propaedeuticsvalue = this.neweditanswerRef.current.getValue().trim(); - axios.post(url,{ - content:update_propaedeuticsvalue - } - ).then((response) => { - if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { - - }else{ - this.props.showSnackbar(response.data.message); - } - }).catch((error) => { - console.log(error) - }); - } - render() { - let {shixunId} = this.state; - return ( - -
    - -
    -
    - 背景知识 - 返回 -
    - -
    - -
    -
    - -
    - 保存 - 取消 -
    - -
    -
    - ) - } -} - - diff --git a/public/react/src/tpm/TPMsettings/TPMsettings.js b/public/react/src/tpm/TPMsettings/TPMsettings.js deleted file mode 100644 index 7acaf98d6..000000000 --- a/public/react/src/tpm/TPMsettings/TPMsettings.js +++ /dev/null @@ -1,2437 +0,0 @@ -import React, { Component } from 'react'; - -import MonacoEditor from 'react-monaco-editor'; - -//MonacoDiffEditor 对比模式 -import {Input, Select, Radio, Checkbox, Popconfirm, message, Modal,Icon,DatePicker,Breadcrumb,Upload,Button,notification, Tooltip} from 'antd'; - -// import "antd/dist/antd.css"; - -import locale from 'antd/lib/date-picker/locale/zh_CN'; - -import moment from 'moment'; - -import axios from 'axios'; - -import './css/TPMsettings.css'; - -import { getImageUrl, toPath, getUrl ,appendFileSizeToUploadFileAll, getUploadActionUrl} from 'educoder'; - -let origin = getUrl(); - -let path = getUrl("/editormd/lib/") - -const $ = window.$; - -let timeout; - -let currentValue; - -const Option = Select.Option; - -const RadioGroup = Radio.Group; -const confirm = Modal.confirm; -// 处理整点 半点 -// 取传入时间往后的第一个半点 -export function handleDateStrings(dateString) { - if (!dateString) return dateString; - const ar = dateString.split(':') - if (ar[1] == '00' || ar[1] == '30') { - return dateString - } - const miniute = parseInt(ar[1]); - if (miniute < 30 || miniute == 60) { - return [ar[0], '30'].join(':') - } - if (miniute < 60) { - // 加一个小时 - const tempStr = [ar[0], '00'].join(':'); - const format = "YYYY-MM-DD HH:mm"; - const _moment = moment(tempStr, format) - _moment.add(1, 'hours') - return _moment.format(format) - } - - return dateString -} - -// 恢复数据 -function md_rec_data(k,mdu,id, editor){ - if(window.sessionStorage.getItem(k+mdu) !== null){ - editor.setValue(window.sessionStorage.getItem(k+mdu)); - md_clear_data(k,mdu,id); - } -} - -// 保存数据 -function md_add_data(k,mdu,d){ - window.sessionStorage.setItem(k+mdu,d); -} - -// 清空保存的数据 -function md_clear_data(k,mdu,id){ - window.sessionStorage.removeItem(k+mdu); - var id1 = "#e_tip_"+id; - var id2 = "#e_tips_"+id; - if(k == 'content'){ - $(id2).html(""); - }else{ - $(id1).html(""); - } -} - -function md_elocalStorage(editor,mdu,id){ - if (window.sessionStorage){ - var oc = window.sessionStorage.getItem('content'+mdu); - if(oc !== null ){ - $("#e_tips_"+id).data('editor', editor); - var h = '您上次有已保存的数据,是否恢复 ? / 不恢复'; - $("#e_tips_"+id).html(h); - } - setInterval(function() { - var d = new Date(); - var h = d.getHours(); - var m = d.getMinutes(); - var s = d.getSeconds(); - h = h < 10 ? '0' + h : h; - m = m < 10 ? '0' + m : m; - s = s < 10 ? '0' + s : s; - if(editor.getValue().trim() != ""){ - md_add_data("content",mdu,editor.getValue()); - var id1 = "#e_tip_"+id; - var id2 = "#e_tips_"+id; - - $(id1).html(" 数据已于 " + h + ':' + m + ':' + s +" 保存 "); - $(id2).html(""); - } - },10000); - - }else{ - $("#e_tip_"+id).after('您的浏览器不支持localStorage.无法开启自动保存草稿服务,请升级浏览器!'); - } -} - -function create_editorMD(id, width, high, placeholder, imageUrl,initValue, callback) { - var editorName = window.editormd(id, { - width: width, - height: high, - path: path, // "/editormd/lib/" - markdown : initValue, - syncScrolling: "single", - tex: true, - tocm: true, - emoji: true, - taskList: true, - codeFold: true, - searchReplace: true, - htmlDecode: "style,script,iframe", - sequenceDiagram: true, - autoFocus: false, - placeholder: placeholder, - toolbarIcons: function () { - // Or return editormd.toolbarModes[name]; // full, simple, mini - // Using "||" set icons align right. - return ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "|", "testIcon", "testIcon1", '|', "image", "table", '|', "watch", "clear"] - }, - toolbarCustomIcons: { - testIcon: "
    ", - testIcon1: "
    " - }, - //这个配置在simple.html中并没有,但是为了能够提交表单,使用这个配置可以让构造出来的HTML代码直接在第二个隐藏的textarea域中,方便post提交表单。 - saveHTMLToTextarea: true, - // 用于增加自定义工具栏的功能,可以直接插入HTML标签,不使用默认的元素创建图标 - dialogMaskOpacity: 0.6, - imageUpload: true, - imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp", "JPG", "JPEG", "GIF", "PNG", "BMP", "WEBP"], - imageUploadURL: imageUrl,//url - onload: function () { - // this.previewing(); - $("#" + id + " [type=\"latex\"]").bind("click", function () { - editorName.cm.replaceSelection("```latex"); - editorName.cm.replaceSelection("\n"); - editorName.cm.replaceSelection("\n"); - editorName.cm.replaceSelection("```"); - var __Cursor = editorName.cm.getDoc().getCursor(); - editorName.cm.setCursor(__Cursor.line - 1, 0); - }); - - $("#" + id + " [type=\"inline\"]").bind("click", function () { - editorName.cm.replaceSelection("`$$$$`"); - var __Cursor = editorName.cm.getDoc().getCursor(); - editorName.cm.setCursor(__Cursor.line, __Cursor.ch - 3); - editorName.cm.focus(); - }); - $("[type=\"inline\"]").attr("title", "行内公式"); - $("[type=\"latex\"]").attr("title", "多行公式"); - - callback && callback() - } - }); - return editorName; -} - - -function updatamakedown(id){ - setTimeout(()=>{ - var shixunDescr = window.editormd.markdownToHTML(id, { - htmlDecode: "style,script,iframe", - taskList: true, - tex: true, - flowChart: true, - sequenceDiagram: true - }); - $("#"+id+" p:first").addClass("ReactMarkdown"); - $('#collaborators_list_info').show() - }, 200) -} - -function range(start, end) { - const result = []; - for (let i = start; i < end; i++) { - result.push(i); - } - return result; -} -function disabledDateTime() { - return { - // disabledHours: () => range(0, 24).splice(4, 20), - disabledMinutes: () => range(1, 30).concat(range(31, 60)), - // disabledSeconds: () => [0, 60], - }; -} - -function disabledDate(current) { - return current && current < moment().endOf('day').subtract(1, 'days'); -} -export default class TPMsettings extends Component { - constructor(props) { - super(props) - this.state = { - fileList: [], - commandLine: 0, - Openpublic: 0, - settingsData: undefined, - webssh: 0, - use_scope: 0, - shixunsstatus: 0, - shixunsID: undefined, - exec_time: undefined, - trainee: undefined, - can_copy: undefined, - task_pass: undefined, - test_set_permission: undefined, - code_edit_permission: undefined, - hide_code: undefined, - code_hidden: undefined, - forbid_copy: undefined, - vnc: undefined, - name: undefined, - scope_partment: undefined, - scopetype: false, - departmentslist: undefined, - description: '', - evaluate_script:undefined, - standard_scripts: undefined, - choice_main_type: "", - choice_small_type: [], - choice_standard_scripts:undefined, - editordescriptios: undefined, - editorevaluate_scripts: undefined, - choice_standard_scriptssum: undefined, - visibleTemplate: false, - Executiveordervalue: "", - Compilecommandvalue: "", - Executivetyoe: false, - postapplyvisible: false, - sendsure_applyvalue: undefined, - postapplytitle: false, - shixunnametype: false, - shixunmaintype: false, - evaluate_scripttype: false, - exec_timetype: false, - traineetype: false, - standard_scriptsModal:false, - standard_scriptsModals:false, - SelectTheCommandtype:false, - multi_webssh:false, - status:0, - opers:false, - operss:false, - testscripttiptype:false, - opersss:false, - operateshixunstype:false, - opening_time:"", - opensmail:false, - scope_partmenttype:false, - newuse_scope:undefined, - scope_partments:0, - shixun_service_configs:undefined, - shixun_service_configlist:undefined, - pod_exist_time: undefined, - pod_exist_timetype: false, - shixunmemoMDvalue:"", - language:"", - deleteisnot:true - } - } - descriptionMD=(initValue, id)=> { - - this.contentChanged = false; - const placeholder = ""; -// amp; -// 编辑时要传memoId - const imageUrl = `/api/attachments.json`; -// 创建editorMd - - const description_editormd =create_editorMD(id, '100%', 400, placeholder, imageUrl, initValue,()=> { - setTimeout(() => { - description_editormd.resize() - description_editormd.cm && description_editormd.cm.refresh() - }, 500) - - if (initValue != undefined) { - description_editormd.setValue(initValue) - } - description_editormd.cm.on("change", (_cm, changeObj) => { - console.log('....contentChanged') - this.contentChanged = true; - }) - }); - md_elocalStorage(description_editormd, `MemoQuestion_${id}`, `${id}Question`); - this.description_editormd = description_editormd; - window.description_editormd = description_editormd; - } - - evaluate_scriptMD=(initValue, id)=> { - this.contentChanged = false; - const placeholder = ""; -// amp; -// 编辑时要传memoId - const imageUrl = `/api/attachments.json`; -// 创建editorMd - - const evaluate_script_editormd =create_editorMD(id, '100%', 400, placeholder, imageUrl, initValue,()=> { - setTimeout(() => { - evaluate_script_editormd.resize() - evaluate_script_editormd.cm && evaluate_script_editormd.cm.refresh() - }, 500) - - if (initValue != undefined) { - evaluate_script_editormd.setValue(initValue) - } - evaluate_script_editormd.cm.on("change", (_cm, changeObj) => { - console.log('....contentChanged') - this.contentChanged = true; - }) - }); - md_elocalStorage(evaluate_script_editormd, `MemoQuestion_${id}`, `${id}Question`); - this.evaluate_script_editormd = evaluate_script_editormd; - window.evaluate_script_editormd = evaluate_script_editormd; - - } - - - - componentDidMount() { - - let id=this.props.match.params.shixunId; - - let Url=`/shixuns/`+id+`/settings.json`; - - axios.get(Url).then((response)=> { - // alert(response.data.shixun.choice_standard_scripts) - if(response.status===200){ - this.setState({ - shixunsID: id, - settingsData: response.data, - webssh: response.data.shixun.webssh, - use_scope: response.data.shixun.use_scope, - shixunsstatus: response.data.shixun.status, - exec_time: response.data.shixun.exec_time, - trainee: response.data.shixun.trainee, - can_copy: response.data.shixun.can_copy, - task_pass: response.data.shixun.task_pass, - test_set_permission: response.data.shixun.test_set_permission, - hide_code: response.data.shixun.hide_code, - code_edit_permission: response.data.shixun.code_edit_permission, - code_hidden: response.data.shixun.code_hidden, - is_secret_repository: response.data.shixun.is_secret_repository, - init_is_secret_repository: response.data.shixun.is_secret_repository, - forbid_copy: response.data.shixun.forbid_copy, - vnc: response.data.shixun.vnc, - vnc_evaluate: response.data.shixun.vnc_evaluate, - name: response.data.shixun.name, - scope_partment: response.data.shixun.scope_partment, - description: response.data.shixun.description, - evaluate_script: response.data.shixun.evaluate_script, - choice_main_type: response.data.shixun.choice_main_type, - choice_small_type: response.data.shixun.choice_small_type, - choice_standard_scripts: response.data.shixun.choice_standard_scripts, - standard_scripts:response.data.shixun.standard_scripts, - multi_webssh:response.data.shixun.multi_webssh, - status:response.data.shixun.status, - opening_time:response.data.shixun.opening_time, - newuse_scope:response.data.shixun.use_scope, - scope_partments: response.data.shixun.scope_partment.length, - shixunmemoMDvalue:response.data.shixun.evaluate_script, - shixun_service_configs:response.data.shixun.shixun_service_configs, - shixun_service_configlist:response.data.shixun.shixun_service_configs, - }) - - // if(response.data.status===403){ - // message: "您没有权限进行该操作" - // this.setState({ - // :true - // message403:response.data.message - // }) - // } - - - if(response.data.shixun.multi_webssh===true){ - this.setState({ - SelectTheCommandtype:true - }) - }else{ - this.setState({ - SelectTheCommandtype:false - }) - } - if (response.data.shixun.scope_partment.length > 0) { - this.setState({ - scopetype: true - }) - } - // console.log(response.data.shixun.description) - // console.log(response.data.shixun.evaluate_script) - // console.log(response.data.shixun.description) - // this.props.identity<4&&this.props.status==0||this.props.identity===1&&this.props.status==2 - - - // this.evaluate_scriptMD(response.data.shixun.evaluate_script, "shixunmemoMD"); - - this.descriptionMD(response.data.shixun.description, "shixundescription"); - - // this.bigClass() - // if (response.data.shixun.status === 2) { - // - // } else if (response.data.shixun.status === 1) { - // this.props.showSnackbar("这个实训已发布不能修改!"); - // } else if (response.data.shixun.status === 3) { - // this.props.showSnackbar("这个实训已关闭不能修改!"); - // } - } - - }); - - - let departmentsUrl = `/shixuns/departments.json`; - axios.get(departmentsUrl).then((response) => { - if (response.status === 200) { - if (response.data.message === undefined) { - this.setState({ - departmentslist: response.data.shools_name - }); - } - } - }).catch((error) => { - console.log(error) - }); - - - - } - - SelectshixunCommand=(e)=>{ - // console.log( e.target.value) - const webssh = e.target.value - if (webssh == 2) { - this.setState({ - webssh: webssh, - SelectTheCommandtype: true, - multi_webssh:false - }); - } else { - if (this.state.init_is_secret_repository && !this.state.vnc && this.state.is_secret_repository == true) { - this.confirmDeleteSecretRepo({ - onOk: () => { - this.setState({ - webssh: webssh, - SelectTheCommandtype: false, - multi_webssh:false - }); - } - }) - } else { - if (!this.state.vnc) { - this.setState({ - is_secret_repository: false, - }) - } - this.setState({ - webssh: webssh, - SelectTheCommandtype: false, - multi_webssh:false - }); - } - } - - // this.setState({ - // webssh: webssh, - // }); - // if(webssh===2){ - // this.setState({ - // SelectTheCommandtype: true, - // multi_webssh:false - // }); - // }else{ - // this.setState({ - // SelectTheCommandtype: false, - // multi_webssh:false - // }); - // } - } - - SelectOpenpublic=(e)=>{ - this.setState({ - Openpublic: e.target.value - }); - } - - can_copy=(e)=>{ - let sum = "" - if (e.target.checked === false) { - sum = 0 - } else if (e.target.checked === true) { - sum = 1 - } - this.setState({ - can_copy: sum, - }); - - } - - task_pass=(e)=>{ - - let sum = "" - if (e.target.checked === false) { - sum = 0 - } else if (e.target.checked === true) { - sum = 1 - } - this.setState({ - task_pass: sum, - }); - } - - test_set_permission=(e)=>{ - let sum = "" - if (e.target.checked === false) { - sum = 0 - } else if (e.target.checked === true) { - sum = 1 - } - this.setState({ - test_set_permission: sum, - }); - - } - - hide_code=(e)=>{ - let sum = "" - if (e.target.checked === false) { - sum = 0 - } else if (e.target.checked === true) { - sum = 1 - } - this.setState({ - hide_code: sum, - }); - - } - code_edit_permission = (e) => { - this.setState({ - code_edit_permission: e.target.checked - }) - } - code_hidden=(e)=>{ - let sum = "" - if (e.target.checked === false) { - sum = 0 - } else if (e.target.checked === true) { - sum = 1 - } - this.setState({ - code_hidden: sum, - }); - - } - confirmDeleteSecretRepo = ({title, onOk}) => { - confirm({ - title: title ||
    -
    已创建的私密版本库及其内容,将在“保存”时被删除。
    -
    是否确认取消勾选?
    -
    , - okText: '确定', - cancelText: '取消', - onOk: () => { - this.setState({ is_secret_repository: false }) - onOk && onOk() - }, - onCancel() { - }, - }); - } - is_secret_repository = (e) => { - const checked = e.target.checked - if (!checked) { - if (this.state.init_is_secret_repository) { - this.confirmDeleteSecretRepo({ - }) - } else { - this.setState({ is_secret_repository: false }) - } - } else { - this.setState({ is_secret_repository: true }) - } - } - forbid_copy = (e) => { - let sum = "" - if (e.target.checked === false) { - sum = 0 - } else if (e.target.checked === true) { - sum = 1 - } - this.setState({ - forbid_copy: sum, - }); - } - shixun_vnc_evaluate=(e) => { - this.setState({ - vnc_evaluate: e.target.checked, - }); - - } - - shixun_vnc=(e)=>{ - // let sum = "" - // if (e.target.checked === false) { - // sum = 0 - // } else if (e.target.checked === true) { - // sum = 1 - // } - const vnc = e.target.checked; - if (!vnc) { - if (this.state.init_is_secret_repository && this.state.webssh != 2 && this.state.is_secret_repository == true) { - this.confirmDeleteSecretRepo({ - onOk: () => { - this.setState({ - vnc: e.target.checked, - vnc_evaluate: false, - }); - } - }) - } else { - if (this.state.webssh != 2) { - this.setState({ - is_secret_repository: false - }) - } - this.setState({ - vnc: e.target.checked, - vnc_evaluate: false, - }); - } - } else { - this.setState({ - vnc: e.target.checked, - vnc_evaluate: false, - }); - } - } - shixunsname = (e) => { - // let {shixunsstatus}=this.state; - // if(shixunsstatus>0){ - // return - // } - this.setState({ - name: e.target.value, - shixunnametype:false - }) - } - - bigClass = (value) => { - // choice_main_type - // choice_small_type - let {settingsData,shixun_service_configs,choice_main_type,choice_small_type}=this.state; - - let list=[] - list.push(choice_main_type) - choice_small_type.map((item,key)=>{ - list.push(item) - }) - - let newshixun_service_configs=shixun_service_configs; - - let newshixun_service_configsagin=[] - - newshixun_service_configs.map((item,key)=>{ - list.map((its,index)=>{ - if(item.mirror_repository_id===its){ - newshixun_service_configsagin.push(item) - } - }) - }) - - - settingsData.shixun.main_type.some((item,key)=> { - if (item.id === value) { - newshixun_service_configsagin[0]={ - mirror_repository_id:value, - name:item.type_name, - cpu_limit:1, - lower_cpu_limit:0.1, - memory_limit:1024, - request_limit:10 - } - return true - } - } - ) - let url = `/shixuns/get_mirror_script.json?mirror_id=`+value; - axios.get(url).then((response) => { - if (response.status === 200) { - // console.log(response.data) - this.setState({ - choice_main_type: value, - standard_scripts:response.data, - choice_standard_scripts:null, - shixun_service_configs:newshixun_service_configsagin, - shixun_service_configlist:newshixun_service_configsagin, - }) - } - }).catch((error) => { - console.log(error) - }); - - - - } - Deselectlittle=(value)=>{ - - let {shixun_service_configs,choice_small_type}=this.state; - let newshixun_service_configs=shixun_service_configs; - let newchoice_small_type=choice_small_type; - - newshixun_service_configs.some((item,key)=> { - if (item.mirror_repository_id === value) { - newshixun_service_configs.splice(key, 1) - return true - } - } - ) - - newchoice_small_type.some((item,key)=> { - if (item === value) { - newchoice_small_type.splice(key, 1) - return true - } - } - ) - - - this.setState({ - choice_small_type: newchoice_small_type, - shixun_service_configs:newshixun_service_configs, - shixun_service_configlist:newshixun_service_configs, - }) - } - littleClass = (value) => { - - let {settingsData,shixun_service_configs,choice_small_type,choice_main_type}=this.state; - let newshixun_service_configs=shixun_service_configs; - let newchoice_small_type=choice_small_type; - // if(Array.isArray(value)===true){ - // value.map((item,key)=>{ - // settingsData.shixun.small_type.some((items,keys)=> { - // if (items.id === item) { - // newshixun_service_configs.push({ - // mirror_repository_id:value, - // name:items.type_name, - // cpu_limit:1, - // lower_cpu_limit:0.1, - // memory_limit:1024, - // request_limit:10 - // }) - // return true - // } - // } - // ) - // }) - // } - - let list=[] - list.push(choice_main_type) - choice_small_type.map((item,key)=>{ - list.push(item) - }) - - let newshixun_service_configsagin=[] - - newshixun_service_configs.map((item,key)=>{ - list.map((its,index)=>{ - if(item.mirror_repository_id===its){ - newshixun_service_configsagin.push(item) - } - }) - }) - - settingsData.shixun.small_type.some((items,keys)=> { - if (items.id === value) { - newshixun_service_configsagin.push({ - mirror_repository_id:value, - name:items.type_name, - cpu_limit:1, - lower_cpu_limit:0.1, - memory_limit:1024, - request_limit:10 - }) - return true - } - } - ) - - newchoice_small_type.push(value) - - this.setState({ - choice_small_type: newchoice_small_type, - shixun_service_configs:newshixun_service_configsagin, - shixun_service_configlist:newshixun_service_configsagin, - }) - } - onPodExistTimeChange = (e) => { - this.setState({ - pod_exist_time: e.target.value, - pod_exist_timetype: false, - }) - } - Timevalue = (e) => { - this.setState({ - exec_time: e.target.value - }) - } - SelectOpenpublic = (e) => { - this.setState({ - scopetype: false, - use_scope: e.target.value, - }); - if (e.target.value === 1) { - this.setState({ - scopetype: true - }); - } - - } - deleteScopeInput = (key) => { - let {scope_partment} = this.state; - let datalist = scope_partment; - datalist.splice(key, 1); - this.setState({ - scope_partment: datalist - }); - } - - shixunScopeInput = (e) => { - let {scope_partment} = this.state; - let datalist = scope_partment; - if (datalist===undefined) { - datalist=[] - } - - datalist.push(e) - // else { - // datalist[id] = e - // } - this.setState({ - scope_partment: datalist - }); - } - // adduse_scopeinput = () => { - // let {scope_partment} = this.state; - // let array = scope_partment; - // let newarray = "" - // array.push(newarray) - // this.setState({ - // scope_partment: array, - // }); - // } - submit_edit_shixun = () => { - if (this.saving == true) return; - this.saving = true; - if(this.state.status===-1){ - this.props.showSnackbar("该实训已被删除,保存失败!"); - return - } - - let { - name, choice_main_type, choice_small_type, choice_standard_scripts, scope_partment, choice_standard_scriptssum, vnc_evaluate, - evaluate_script, webssh, use_scope, trainee, can_copy, task_pass, test_set_permission, hide_code, code_hidden, forbid_copy, vnc,multi_webssh, - opening_time,shixunmemoMDvalue,shixun_service_configlist, is_secret_repository, code_edit_permission - } = this.state; - - let newshixun_service_configlist = shixun_service_configlist.map(v => { - let v1 = Object.assign({},v); - delete v1.name; - return v1 - }); - - // let operateauthority= - // this.props.identity===1?true:this.props.identity<5&&this.state.status==0?true:false; - // this.props.identity<5&&this.state.status==0||this.props.identity===1&&this.state.status==2||this.props.identity===1&&this.state.status==1; - - const description_editormd = this.description_editormd.getValue(); - - let evaluate_script_editormd; - - if(this.state.status==0||this.state.status==1||this.state.status==2&&this.props.identity===1){ - // evaluate_script_editormd = this.evaluate_script_editormd.getValue(); - evaluate_script_editormd = shixunmemoMDvalue - }else{ - evaluate_script_editormd = evaluate_script; - } - - - - if (name === "") { - this.setState({ - shixunnametype: true - }) - $('html').animate({ - scrollTop: 10 - }, 1000); - return - } - if (choice_main_type === "") { - this.setState({ - shixunmaintype: true - }) - $('html').animate({ - scrollTop: 800 - }, 1000); - return - } - if (evaluate_script_editormd === "") { - this.setState({ - evaluate_scripttype: true - }) - $('html').animate({ - scrollTop: 1200 - }, 1000); - return - } - if(use_scope===1){ - - if(scope_partment===undefined||scope_partment.length===0){ - this.setState({ - scope_partmenttype: true - }) - $('html').animate({ - scrollTop: 2500 - }, 1000); - this.props.showSnackbar("公开程度,指定单位为空"); - return - } - } - // if (exec_time === "") { - // this.setState({ - // exec_timetype: true - // }) - // $('html').animate({ - // scrollTop: 1500 - // }, 1000); - // return - // } - - // if (!pod_exist_time) { - // this.setState({ - // pod_exist_timetype: true - // }) - // $("html, body").animate({ scrollTop: $('#pod_exist_time').offset().top - 100 }, 1000) - // return - // } - - if (trainee === "") { - this.setState({ - traineetype: true - }) - return - } - - let id = this.props.match.params.shixunId; - - let newmulti_webssh=multi_webssh; - - - if(newmulti_webssh===null){ - newmulti_webssh=false - } - - //exec_time: exec_time, - let Url = `/shixuns/` + id + `.json`; - let data = { - shixun:{ - - name: name, - webssh: webssh, - use_scope: use_scope, - can_copy: can_copy, - vnc: vnc===null?undefined:vnc, - vnc_evaluate: vnc_evaluate===null?undefined:vnc_evaluate, - test_set_permission: test_set_permission, - code_hidden: code_hidden, - code_edit_permission: code_edit_permission, - trainee: trainee, - task_pass: task_pass, - hide_code: hide_code, - forbid_copy: forbid_copy, - multi_webssh:newmulti_webssh, - opening_time:opening_time, - mirror_script_id:choice_standard_scriptssum===undefined?choice_standard_scripts:choice_standard_scriptssum, - }, - shixun_info:{ - description: description_editormd, - evaluate_script: evaluate_script_editormd, - }, - is_secret_repository: is_secret_repository, - main_type: choice_main_type, - small_type: choice_small_type, - scope_partment: scope_partment, - shixun_service_configs:newshixun_service_configlist - } - - axios.put(Url, data).then((response) => { - // console.log(response) - this.saving = false; - if(response.status){ - if (response.data.status === -1) { - this.props.showSnackbar(response.data.message); - return - } else { - window.location.href = "/shixuns/" + response.data.shixun_identifier + "/challenges"; - } - } - - }).catch((error) => { - console.log(error) - this.saving = false; - }) - - - } - shixunsfetch = (value, callback) => { - if (timeout) { - clearTimeout(timeout); - timeout = null; - } - currentValue = value; - - function fake() { - let departmentsUrl = `/shixuns/departments.json?q=` + currentValue; - axios.get(departmentsUrl).then((response) => { - callback(response.data.shools_name); - }).catch((error) => { - console.log(error) - }); - } - - timeout = setTimeout(fake, 300); - } - shixunHandleSearch = (value) => { - this.shixunsfetch(value, departmentslist => this.setState({departmentslist})); - } - - - - - shixunsclose = () => { - let id = this.props.match.params.shixunId; - let cul = `/shixuns/` + id + `/close.json`; - axios.post(cul).then((response) => { - if(response.data.status===1){ - this.props.showSnackbar("操作成功"); - this.setState({ - operateshixunstype: false, - }); - - window.location.href = "/shixuns/" + id + "/challenges"; - } - }).catch((error) => { - console.log(error) - }) - } - - shixunsdel= () => { - let id = this.props.match.params.shixunId; - let cul = `/shixuns/` + id +`.json`; - - axios.delete(cul).then((response) => { - if(response.data.status===1){ - this.props.showSnackbar("操作成功"); - this.setState({ - operateshixunstype: false, - }); - - window.location.href = "/shixuns"; - } - }).catch((error) => { - console.log(error) - }) - } - - Executiveorder = (e) => { - this.setState({ - Executiveordervalue: e.target.value - }) - } - - Compilecommand = (e) => { - this.setState({ - Compilecommandvalue: e.target.value - }) - } - - handleCancelTemplate = (e) => { - this.setState({ - Executiveordervalue: "", - Compilecommandvalue: "", - visibleTemplate: false - }) - } - - hideModalTemplate = (e) => { - let id = this.props.match.params.shixunId; - let {Executiveordervalue, Compilecommandvalue} = this.state; - - if (Executiveordervalue === "") { - this.setState({ - Executivetyoe: true, - }); - return - } - // Executiveordervalue=String(Executiveordervalue); - // Compilecommandvalue=String(Compilecommandvalue); - let trl = `/shixuns/${id}/get_custom_script.json?compile=${Executiveordervalue}&excutive=${Compilecommandvalue}` - axios.get(trl).then((response) => { - // this.evaluate_scriptMD(response.data.shixun_script, "shixunmemoMD"); - this.setState({ - shixunmemoMDvalue:response.data.shixun_script - }) - }).catch((error) => { - console.log(error) - }); - this.setState({ - visibleTemplate: false - }) - } - - showModal = () => { - this.setState({ - visibleTemplate: true, - }); - } - Selecttrainee = (value) => { - this.setState({ - trainee: value, - }); - } - - post_apply = () => { - this.setState({ - postapplyvisible: true - }) - } - - sendsure_applyvalues = (e) => { - this.setState({ - sendsure_applyvalue: e.target.value - }) - } - - setlanguagewrite = (e)=>{ - this.setState({ - languagewrite: e.target.value - }) - } - - setsystemenvironment = (e) => { - this.setState({ - systemenvironment: e.target.value - }) - } - - settestcoderunmode = (e) => { - this.setState({ - testcoderunmode: e.target.value - }) - - } - - sendsure_apply = () => { - let {languagewrite,systemenvironment,testcoderunmode} = this.state; - // console.log("点击确定") - // console.log("languagewrite"+languagewrite); - // console.log("systemenvironment"+systemenvironment); - // console.log("testcoderunmode"+testcoderunmode); - - // let attachment_ids = undefined - // if (this.state.fileList) { - // attachment_ids = this.state.fileList.map(item => { - // return item.response ? item.response.id : item.id - // }) - // } - if(languagewrite === undefined || languagewrite === "" ){ - // this.props.showNotification(`请填写该镜像是基于什么语言`); - this.setState({ - languagewritetype:true - }) - return - } - if(systemenvironment === undefined || systemenvironment === ""){ - // this.props.showNotification(`请填写该镜像是基于什么语言系统环境`); - this.setState({ - systemenvironmenttype:true - }) - return; - - } - if(testcoderunmode === undefined || testcoderunmode === "") { - // this.props.showNotification(`请填写该镜像中测试代码运行方式`); - this.setState({ - testcoderunmodetype:true - }) - return; - } - var attachment_ids=undefined; - if (this.state.fileList) { - attachment_ids = this.state.fileList.map(item => { - return item.response ? item.response.id : item.id - }) - } - - if( attachment_ids === undefined || attachment_ids.length===0){ - - // notification.open( - // { - // message: '提示', - // description: - // '请上传附件!', - // - // } - // ) - this.setState({ - attachmentidstype:true - }) - return; - } - // console.log("attachment_ids"+attachment_ids); - - // alert(languagewrite +" "+systemenvironment +" "+testcoderunmode + " "+attachment_ids); - - var data={ - language:languagewrite, - runtime:systemenvironment, - run_method:testcoderunmode, - attachment_id:attachment_ids[0], - } - var url =`/shixuns/apply_shixun_mirror.json`; - axios.post(url,data - ).then((response) => { - - try { - if (response.data) { - // const { id } = response.data; - // if (id) { - if(this.state.file !== undefined){ - console.log("549"); - // this.deleteAttachment(this.state.file); - this.setState({ - file:undefined, - deleteisnot:true, - languagewrite:"", - systemenvironment:"", - testcoderunmode:"", - fileList:[] - }) - }else { - this.setState({ - file:undefined, - deleteisnot:true, - languagewrite:"", - systemenvironment:"", - testcoderunmode:"", - fileList:[] - }) - } - // this.props.showNotification('提交成功!'); - notification.open( - { - message: '提示', - description: - '提交成功!', - - } - ) - this.sendhideModaly() - // this.props.history.push(`/courses/${cid}/graduation_topics`); - // } - } - }catch (e) { - - } - - }) - - } - - sendhideModaly = () => { - this.setState({ - postapplyvisible: false, - }) - if(this.state.file !== undefined){ - console.log("580"); - // this.deleteAttachment(this.state.file); - this.setState({ - file:undefined, - deleteisnot:true, - languagewrite:"", - systemenvironment:"", - testcoderunmode:"", - fileList:[] - }) - }else { - this.setState({ - file:undefined, - deleteisnot:true, - languagewrite:"", - systemenvironment:"", - testcoderunmode:"", - fileList:[] - }) - } - } - - yeshidemodel = () => { - this.setState({ - postapplytitle: false - }) - } - - SelectScput = (value, e) => { - this.setState({ - choice_standard_scriptssum: value, - language:e.props.name, - choice_standard_scripts: {id:e.props.value,value:""}, - standard_scriptsModal:true - }) - } - - hidestandard_scriptsModal=()=>{ - this.setState({ - standard_scriptsModal:false, - standard_scriptsModals:false - }) - } - - get_mirror_script=()=>{ - let {choice_standard_scriptssum}=this.state; - let id = this.props.match.params.shixunId; - let pul = "/shixuns/" + id + "/get_script_contents.json?script_id=" + choice_standard_scriptssum; - axios.get(pul).then((response) => { - if(response.status===200){ - // this.evaluate_scriptMD(response.data.content, "shixunmemoMD"); - this.setState({ - standard_scriptsModal:false, - standard_scriptsModals:true, - shixunmemoMDvalue:response.data.content - }) - } - - }).catch((error) => { - console.log(error) - }) - } - - - SelectTheCommandonChange=(e)=>{ - this.setState({ - multi_webssh:e.target.checked - }) - } - - bigopen=()=>{ - this.setState({ - opers:true - }) - - } - - bigopens=()=>{ - this.setState({ - opers:false, - operss:false, - opersss:false, - opensmail:false - }) - - } - bigopensmal=(e)=>{ - this.setState({ - opensmail:true - }) - - } - sbigopen=(e)=>{ - this.setState({ - operss:true - }) - - } - - sbigopens=()=>{ - this.setState({ - operss:false - }) - } - sbigopenss=(e)=>{ - this.setState({ - opersss:true - }) - - } - - sbigopensss=()=>{ - this.setState({ - opersss:false - }) - } - testscripttip=(val)=>{ - if(val===0){ - this.setState({ - testscripttiptype:true - }) - }else if(val===1){ - this.setState({ - testscripttiptype:false - }) - } - } - - operateshixuns=(value)=>{ - this.setState({ - operateshixunstype:true, - delType:value - }) - } - - hideoperateshixuns=()=>{ - this.setState({ - operateshixunstype:false - }) - } - onChangeTimePicker =(value, dateString)=> { - this.setState({ - opening_time: dateString=== ""?"":moment(handleDateStrings(dateString)) - }) - } - - getshixunmemoMDvalue=(value, e)=>{ - - this.setState({ - shixunmemoMDvalue:value - }) - } - - setConfigsInputs=(e,keys,str)=>{ - - let {shixun_service_configs}=this.state; - let newshixun_service_configs=shixun_service_configs; - newshixun_service_configs.map((item,key)=>{ - if(key===keys){ - switch (str) { - case 1: - item.cpu_limit=e.target.value - break; - case 2: - item.lower_cpu_limit=e.target.value - break; - case 3: - item.memory_limit=e.target.value - break; - case 4: - item.request_limit=e.target.value - break; - } - } - }) - - this.setState({ - shixun_service_configs:newshixun_service_configs, - shixun_service_configlist:newshixun_service_configs, - }) - - } - - handleChange = (info) => { - let {fileList}=this.state; - - if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') { - console.log("handleChange1"); - - // if(fileList.length===0){ - let fileLists = info.fileList; - this.setState({ fileList:fileLists, - deleteisnot:false}); - // } - } - } - - onAttachmentRemove = (file) => { - if(!file.percent || file.percent == 100){ - confirm({ - title: '确定要删除这个附件吗?', - okText: '确定', - cancelText: '取消', - // content: 'Some descriptions', - onOk: () => { - console.log("665") - this.deleteAttachment(file) - }, - onCancel() { - console.log('Cancel'); - }, - }); - return false; - } - - } - - deleteAttachment = (file) => { - console.log(file); - let id=file.response ==undefined ? file.id : file.response.id - const url = `/attachments/${id}.json` - axios.delete(url, { - }) - .then((response) => { - if (response.data) { - const { status } = response.data; - if (status == 0) { - // console.log('--- success') - - this.setState((state) => { - - const index = state.fileList.indexOf(file); - const newFileList = state.fileList.slice(); - newFileList.splice(index, 1); - return { - fileList: newFileList, - deleteisnot:true - }; - }); - } - } - }) - .catch(function (error) { - console.log(error); - }); - } - - - - render() { - let { - postapplyvisible, - postapplytitle, - shixunnametype, - shixunmaintype, - evaluate_scripttype, - traineetype, - standard_scripts, - name, - settingsData, - webssh, - is_secret_repository, - use_scope, - shixunsID, - can_copy, - choice_standard_scripts, - Executiveordervalue, - Executivetyoe, - Compilecommandvalue, - task_pass, - test_set_permission, - hide_code, - forbid_copy, - code_edit_permission, - code_hidden, - vnc, - vnc_evaluate, - scopetype, - scope_partment, - departmentslist, - trainee, - choice_main_type, - choice_small_type, - standard_scriptsModal, - standard_scriptsModals, - SelectTheCommandtype, - testscripttiptype, - operateshixunstype, - opening_time, - scope_partmenttype, - newuse_scope, - scope_partments, - shixunmemoMDvalue,delType, - shixun_service_configs, - fileList, - } = this.state; - - let options; - - if (departmentslist != undefined) { - options = this.state.departmentslist.map((d, k) => { - return ( - - ) - }) - } - const uploadProps = { - width: 600, - fileList, - multiple: true, - // https://github.com/ant-design/ant-design/issues/15505 - // showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。 - // showUploadList: false, - action: `${getUploadActionUrl()}`, - onChange: this.handleChange, - onRemove: this.onAttachmentRemove, - beforeUpload: (file, fileList) => { - if (this.state.fileList.length >= 1) { - return false - } - // console.log('beforeUpload', file.name); - const isLt150M = file.size / 1024 / 1024 < 50; - if (!isLt150M) { - // this.props.showNotification(`文件大小必须小于50MB`); - notification.open( - { - message: '提示', - description: - '文件大小必须小于50MB', - - } - ) - } - if(this.state.file !== undefined){ - console.log("763") - this.setState({ - file:file - }) - }else { - this.setState({ - file:file - }) - } - - console.log("handleChange2"); - return isLt150M; - }, - } - const dateFormat = 'YYYY-MM-DD HH:mm:ss'; - let operateauthority=this.props.identity===1?true:this.props.identity<5&&this.state.status==0?true:false; - - return ( -
    - - 实训详情 - 配置 - - -
    -
    - 配置 - { - this.props.identity===1&&this.state.status==2? - this.operateshixuns(2)}> - 永久关闭 - :"" - } - { - this.props.identity < 5 && this.state.status==0? - this.operateshixuns(1)}> - 删除实训 - :"" - } - { - this.props.identity == 1 && this.state.status == 2 ? - this.operateshixuns(1)}> - 删除实训 - :"" - } - - -
    - {delType===1?

    是否确认删除 ?

    :

    关闭后,
    用户不能再开始挑战了是否确认关闭 ?

    } -
    -
    - 取消 - {delType===1?确定:确定} -
    -
    - -
    - -
    - -

    实训名称

    - -
    - * -
    -
    - {settingsData === undefined ? "" : - } -
    -
    - 必填项 -
    -
    - - -
    - -
    -
    - -
    - -

    简介

    - -
    - -
    -
    -
    -

    -

    -
    - -
    -
    -

    技术平台

    - - -
    - * -
    - -

    - 列表中没有? - 申请新建 -

    - - -
    -
  • - - -
  • -
    {this.state.languagewritetype===true?"请填写该镜像语言":""}
    -
  • - - -
  • -
    {this.state.systemenvironmenttype===true?"请填写该镜像语言系统环境":""}
    -
  • - - - -
  • -
    {this.state.testcoderunmodetype===true?"请填写该镜像测试代码运行方式":""}
    -
  • - -
    - - - 上传附件 - (单个文件50M以内) - -
    -
  • -
    - {this.state.attachmentidstype===true?"请上传附件":""} -
    -
  • - this.sendhideModaly()} - >取消 - -
  • -
    -
    - -
    - - - - - -
    -

    新建申请已提交,请等待管理员的审核

    -
  • 我们将在1-2个工作日内与您联系 -
  • -
    -
    - 知道啦 -
    -
    -
    -
    - -
    - -
    -
    - 必填项 -
    - {/*

    请在配置页面完成后续的评测脚本设置操作

    */} - -
    -
    - {/*
    */} - {/*
    */} -
    -

    评测脚本

    -
    - - -
    -

    原有脚本将被新的脚本覆盖,无法撤销

    -

    是否确认执行覆盖操作

    -
    - - -
    - - -

    评测脚本生成成功!

    - -
    - - { - this.props.identity<5||this.props.power==true? - 使用自定义脚本 : "" - } -
    - this.testscripttip(0)}> -
    - -
    -

    - 使用自定义模板,平台无法自动更新脚本,
    - 请在关卡创建完后手动更新脚本中的必填参
    - 数和以下2个数组元素:
    - challengeProgramNames
    - sourceClassNames

    - 示例:有2个关卡的实训

    - 各关卡的待编译文件为:
    - src/step1/HelloWorld.java
    - src/step2/Other.java

    - 各关卡的编译后生成的执行文件为:
    - step1.HelloWorld
    - step2.Other

    - 则数组元素更新如下:
    - challengeProgramNames=("src/step1/
    - HelloWorld.java" "src/step2/Other.java")
    - sourceClassNames=("step1.HelloWorld
    - " "step2.Other")

    - 其它参数可按实际需求定制 -

    -
    -

    - this.testscripttip(1)}>知道了 -

    -
    -
    - - -
    -
  • - - -

    执行命令不能为空

    -
  • - -
  • - - -
  • -
    -
    -
    -
    - -
    -
    - * -
    - - -
    - {/**/} - -
    - - - {/*
    */} - {/*{evaluate_script===undefined?"":evaluate_script}*/} - - {/*
    */} - - - -
    - -
    -
    -
    - - 必填项 -
    -

    -

    -
    -
    - - {/*
    */} - {/***/} - - {/*

    程序最大执行时间

    */} - - {/* 秒*/} - - {/*
    */} - {/*必填项*/} - {/*
    */} - {/*
    */} - - {/*
    - * - -

    Pod存活时间

    - - - -
    - 必填项 -
    -
    */} - - -
    -

    命令行

    - - 无命令行窗口 (选中则不给学员的实践任务提供命令窗口) - 命令行练习窗口 (选中则给学员提供用于练习操作的命令行窗口) - 命令行评测窗口 (选中则给学员提供用于关卡评测的命令行窗口) - - 多个命令行窗口(选中则允许学员同时开启多个命令行窗口) - - -
    - -
    -

    公开程度

    - - 对所有公开 (选中则所有已被试用授权的用户可以学习) - 对指定单位公开 (选中则下方指定单位的已被试用授权的用户可以学习) - - -
    -
    -
    -
    -
    - -
    - (搜索并选中添加单位名称) -
    - {/*+*/} - {/*添加*/} -
    - -
    - - {/*{*/} - {/*scope_partment===undefined?"":scope_partment.map((item,key)=>{*/} - {/*return(*/} - {/*
    */} - {/*this.deleteScopeInput(key)} style={{ color: 'rgba(0,0,0,.25)' }} />}*/} - {/*value={item}*/} - {/*/>*/} - {/*
    */} - - {/*)*/} - {/*})*/} - {/*}*/} -
    - - - 请选择需要公开的单位 - -
    -
    -
    - -
    -

    发布信息

    - -
    - * - 面向学员: - -
    - -
    - 实训难易度定位,不限定用户群体 -
    - 必填项 -
    - -
    -
    - 复制: - - - - -
    - -
    - 跳关: - - - - -
    -
    - 测试集解锁: - - - - -
    - - {!code_hidden && !hide_code &&
    - 代码开放修改: - - - - -
    } - -
    - 隐藏代码窗口: - - - - -
    - -
    - 代码目录隐藏: - - - - -
    - - { (vnc || webssh == 2) &&
    - 私密版本库: - - - - -
    } - -
    - 禁用复制粘贴: - - - - -
    - -
    - 开启时间: - - - - -
    - - {this.props.identity<3?
    - VNC图形化: - - - - -
    :""} - {this.props.identity<3 && vnc ?
    - VNC图形化评测: - - - - -
    :""} - - - -
    - - {this.props.identity<3?
    -

    服务配置

    - { shixun_service_configs&&shixun_service_configs.map((item,key)=>{ - - return( -
    -
    -
    - {item.name} - {/*this.Deselectlittle(item.mirror_repository_id)}>*/} -
    -
    - -
    - this.setConfigsInputs(e,key,1)} - className="panel-box-sizing task-form-100 task-height-40" placeholder="请输入类别名称" /> -
    -
    -
    -
    - -
    - this.setConfigsInputs(e,key,2)} - className="panel-box-sizing task-form-100 task-height-40" placeholder="请输入类别名称" /> -
    -
    -
    -
    - -
    - this.setConfigsInputs(e,key,3)} - className="panel-box-sizing task-form-100 task-height-40" placeholder="请输入类别名称" /> -
    -
    -
    -
    - -
    - this.setConfigsInputs(e,key,4)} - className="panel-box-sizing task-form-100 task-height-40" placeholder="请输入类别名称" /> -
    - -
    -
    -
    -
    - ) - - })} -
    :""} - -

    - { - // this.props.identity<4&&this.props.status==0? - this.props.identity<5? -

    - 保存 - 取消 -
    :"" - } - -

    - -
    - ); - } -} - - diff --git a/public/react/src/tpm/TPMsettings/css/TPMsettings.css b/public/react/src/tpm/TPMsettings/css/TPMsettings.css deleted file mode 100644 index 8047bbde8..000000000 --- a/public/react/src/tpm/TPMsettings/css/TPMsettings.css +++ /dev/null @@ -1,113 +0,0 @@ -.radioStyle{ - display: block; - height: 30px; - } -#settingsMarkdown{ - background:transparent; -} -#challenge_begin{ - height: 30px; - line-height: 30px; -} -#shixundescription .CodeMirror{ - margin-top: 31px !important; - height: 364px !important; -} -#shixundescription .editormd-preview{ - width:578px !important; - top: 40px !important; - height: 364px !important; -} - -#shixunmemoMD .CodeMirror{ - margin-top: 31px !important; - height: 578px !important; -} - -#shixunmemoMD .editormd-preview{ - width: 578px !important; - top: 40px !important; - height: 578px !important; -} - -.radioStyle { - display: block; - height: 30px; -} - -a.white-btn.use_scope-btn:hover { - color: #FFF !important; -} - -.shixunScopeInput { - width: 218px; - height: 33px; - display: block; - margin-bottom: 15px; -} - -.ant-modal-title { - text-align: center; -} - -a.newuse_scope-btn:hover { - border: 1px solid #F06200; - color: #fff !important; - background: #FF7500; -} - -a.newuse_scope-btn { - border: 1px solid #FF7500; - color: #FF7500 !important; -} - -.tpmprompt { - padding-left: 20px; - margin-top: -4px; -} -.ml36{ - margin-left: 26px; -} - -#shixunmemoMD{ - width:98% !important; - height: 620px !important; -} -#shixunmemoMDs{ - width: 98% !important; - height: 420px !important; -} -#shixunmemoMDs .CodeMirror { - /* width: 548px !important; */ - margin-top: 31px !important; - height: 402px !important; -} -.pdr20{ - padding-right:20px; -} - -.nonemodel{ - width: 59%; - height: 468px; - /*background: rgba(0, 0, 0, 0.65);*/ - background: #f5f5f5; - position: absolute; - z-index: 100; - opacity: 0.5; - left: 21.5%; -} - -.shixunmemoMDdiv{ - width: 99%; - height: 615px; -} - -.shixunspanred{ - margin-left: 142px; - margin-top: 5px; - margin-bottom: 5px; -} - -.ml82{ - margin-left:82px; -} diff --git a/public/react/src/tpm/beian.png b/public/react/src/tpm/beian.png deleted file mode 100755 index 9f763946d..000000000 Binary files a/public/react/src/tpm/beian.png and /dev/null differ diff --git a/public/react/src/tpm/challengesnew/TPMMDEditor.js b/public/react/src/tpm/challengesnew/TPMMDEditor.js deleted file mode 100644 index 11b88a037..000000000 --- a/public/react/src/tpm/challengesnew/TPMMDEditor.js +++ /dev/null @@ -1,355 +0,0 @@ -import React, {Component} from 'react'; - -import {Input, Select, Radio, Checkbox, Popconfirm, message, Modal} from 'antd'; - -import {BrowserRouter as Router, Route, Link, Switch} from "react-router-dom"; - -// import "antd/dist/antd.css"; - -import { getImageUrl, toPath, getUrl } from 'educoder'; -import '../../courses/css/Courses.css' - -import axios from 'axios'; - -import './css/TPMchallengesnew.css'; -require('codemirror/lib/codemirror.css'); - -let origin = getUrl(); - -let path = '/editormd/lib/' - path = getUrl("/editormd/lib/") -const $ = window.$; - -let timeout; - -let currentValue; - -const Option = Select.Option; - -const RadioGroup = Radio.Group; - - - -// 保存数据 -function md_add_data(k,mdu,d){ - window.sessionStorage.setItem(k+mdu,d); -} - -// 清空保存的数据 -function md_clear_data(k,mdu,id){ - window.sessionStorage.removeItem(k+mdu); - var id1 = "#e_tip_"+id; - var id2 = "#e_tips_"+id; - if(k == 'content'){ - $(id2).html(" "); - }else{ - $(id1).html(" "); - } -} -window.md_clear_data = md_clear_data -// editor 存在了jquery对象上,应用不需要自己写md_rec_data方法了 -function md_rec_data(k, mdu, id) { - if (window.sessionStorage.getItem(k + mdu) !== null) { - var editor = $("#e_tips_" + id).data('editor'); - editor.setValue(window.sessionStorage.getItem(k + mdu)); - // debugger; - // /shixuns/b5hjq9zm/challenges/3977/tab=3 setValue可能导致editor样式问题 - md_clear_data(k, mdu, id); - } -} -window.md_rec_data = md_rec_data; - -function md_elocalStorage(editor,mdu,id){ - if (window.sessionStorage){ - var oc = window.sessionStorage.getItem('content'+mdu); - if(oc !== null && oc != editor.getValue()){ - console.log("#e_tips_"+id) - $("#e_tips_"+id).data('editor', editor); - var h = '您上次有已保存的数据,是否恢复 ? / 不恢复'; - $("#e_tips_"+id).html(h); - } - setInterval(function() { - var d = new Date(); - var h = d.getHours(); - var m = d.getMinutes(); - var s = d.getSeconds(); - h = h < 10 ? '0' + h : h; - m = m < 10 ? '0' + m : m; - s = s < 10 ? '0' + s : s; - if(editor.getValue().trim() != ""){ - md_add_data("content",mdu,editor.getValue()); - var id1 = "#e_tip_"+id; - var id2 = "#e_tips_"+id; - - var textStart = " 数据已于 " - var text = textStart + h + ':' + m + ':' + s +" 保存 "; - // 占位符 - var oldHtml = $(id2).html(); - if (oldHtml && oldHtml != ' ' && oldHtml.startsWith(textStart) == false) { - $(id2).html( oldHtml.split(' (')[0] + ` (${text})`); - } else { - $(id2).html(text); - } - // $(id2).html(""); - } - },10000); - - }else{ - $("#e_tip_"+id).after('您的浏览器不支持localStorage.无法开启自动保存草稿服务,请升级浏览器!'); - } -} - - -function create_editorMD(id, width, high, placeholder, imageUrl, callback, initValue, - onchange, watch, { noStorage, showNullButton, emoji }, that) { - // 还是出现了setting只有一份,被共用的问题 - - var editorName = window.editormd(id, { - width: width, - height: high===undefined?400:high, - path: path, // "/editormd/lib/" - markdown : initValue, - - dialogLockScreen: false, - watch:watch===undefined?true:watch, - syncScrolling: "single", - tex: true, - tocm: true, - emoji: !!emoji , - taskList: true, - codeFold: true, - searchReplace: true, - htmlDecode: "style,script,iframe", - sequenceDiagram: true, - autoFocus: false, - - // mine - - toolbarIcons: function (mdEditor) { - let react_id = `react_${mdEditor.id}`; - const __that = window[react_id] - - // Or return editormd.toolbarModes[name]; // full, simple, mini - // Using "||" set icons align right. - const icons = ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "link", "|", "testIcon", "testIcon1", '|', "image", "table", '|', "watch", "clear"]; - // 试卷处用到的填空题新增按钮 - if (__that.props.showNullButton) { - icons.push('nullBtton') - } - return icons - }, - toolbarCustomIcons: { - testIcon: "
    ", - testIcon1: "
    ", - nullBtton: "
    点击插入填空项
    ", - }, - //这个配置在simple.html中并没有,但是为了能够提交表单,使用这个配置可以让构造出来的HTML代码直接在第二个隐藏的textarea域中,方便post提交表单。 - saveHTMLToTextarea: true, - // 用于增加自定义工具栏的功能,可以直接插入HTML标签,不使用默认的元素创建图标 - dialogMaskOpacity: 0.6, - placeholder: placeholder, - imageUpload: true, - imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp", "JPG", "JPEG", "GIF", "PNG", "BMP", "WEBP"], - imageUploadURL: imageUrl,//url - onchange: onchange, - onload: function() { - let _id = this.id // 如果要使用this,这里不能使用箭头函数 - let _editorName = this; - let react_id = `react_${_editorName.id}`; - const __that = window[react_id] - - // this.previewing(); - // let _id = id; - $("#" + _id + " [type=\"latex\"]").bind("click", function () { - _editorName.cm.replaceSelection("```latex"); - _editorName.cm.replaceSelection("\n"); - _editorName.cm.replaceSelection("\n"); - _editorName.cm.replaceSelection("```"); - var __Cursor = _editorName.cm.getDoc().getCursor(); - _editorName.cm.setCursor(__Cursor.line - 1, 0); - }); - - $("#" + _id + " [type=\"inline\"]").bind("click", function () { - _editorName.cm.replaceSelection("`$$$$`"); - var __Cursor = _editorName.cm.getDoc().getCursor(); - _editorName.cm.setCursor(__Cursor.line, __Cursor.ch - 3); - _editorName.cm.focus(); - }); - $("[type=\"inline\"]").attr("title", "行内公式"); - $("[type=\"latex\"]").attr("title", "多行公式"); - - if (__that.props.showNullButton) { - const NULL_CH = '▁' - // const NULL_CH = '〇' - // const NULL_CH = '🈳' - - $("#" + _id + " [type=\"nullBtton\"]").bind("click", function () { - _editorName.cm.replaceSelection(NULL_CH); - // var __Cursor = _editorName.cm.getDoc().getCursor(); - // _editorName.cm.setCursor(__Cursor.line - 1, 0); - }); - } - - if (noStorage == true) { - - } else { - md_elocalStorage(_editorName, `MDEditor__${_id}`, _id); - } - - callback && callback(_editorName) - } - }); - return editorName; -} - - -export default class TPMMDEditor extends Component { - constructor(props) { - super(props) - this.state = { - initValue: '' - } - } - componentDidUpdate(prevProps, prevState) { - // 不能加,影响了试卷填空题 - // if (this.props.initValue != prevProps.initValue) { - // this.answers_editormd.setValue(this.props.initValue) - // } - } - - // react_mdEditor_ - componentDidMount = () => { - const { mdID, initValue, placeholder, showNullButton} = this.props; - - let _id = `mdEditor_${mdID}` - this.contentChanged = false; - const _placeholder = placeholder || ""; - // amp; - // 编辑时要传memoId - const imageUrl = `/api/attachments.json`; - // 创建editorMd - let react_id = `react_${_id}`; - // 将实例存到了window - window[react_id] = this - const answers_editormd = create_editorMD(_id, '100%', this.props.height, _placeholder, imageUrl, (_editorName) => { - const __editorName = _editorName; - react_id = `react_${__editorName.id}`; - const that = window[react_id] - - // 一个延迟的recreate或resize,不加这段代码,md初始化可能会出现样式问题 - setTimeout(() => { - if (that.props.needRecreate == true) { - __editorName.recreate() // 注意 必须在setValue之前触发,不然会清空 - } else { - __editorName.resize() - } - console.log('timeout', __editorName.id) - __editorName.cm && __editorName.cm.refresh() - }, that.props.refreshTimeout || 500) - if (this.props.noSetValueOnInit) { - that.onEditorChange() - } else { - if (that.props.initValue != undefined && that.props.initValue != '') { - __editorName.setValue(that.props.initValue) - } - if (that.state.initValue) { - __editorName.setValue(that.state.initValue) - } - } - - __editorName.cm.on("change", (_cm, changeObj) => { - that.contentChanged = true; - if (that.state.showError) { - that.setState({showError: false}) - } - that.onEditorChange() - }) - that.props.onCMBlur && __editorName.cm.on('blur', () => { - that.props.onCMBlur() - }) - that.props.onCMBeforeChange && __editorName.cm.on('beforeChange', (cm,change) => { - that.props.onCMBeforeChange(cm,change) - }) - that.answers_editormd = __editorName; - // 这里应该可以去掉了,方便调试加的 - window[__editorName.id+'_'] = __editorName; - }, initValue, this.onEditorChange,this.props.watch, { - noStorage: this.props.noStorage, - showNullButton: this.props.showNullButton, - emoji: this.props.emoji - }, this); - - } - // 用在form里时,validate失败时出现一个红色边框 - showError = () => { - this.setState({showError: true}) - } - onEditorChange = () => { - if (!this.answers_editormd) return; - const val = this.answers_editormd.getValue(); - //console.log('onEditorChange', this.props.id, val) - try { - this.props.onChange && this.props.onChange(val) - } catch(e) { - // http://localhost:3007/courses/1309/common_homeworks/6566/setting - // 从这个页面,跳转到编辑页面,再在编辑页面点击返回的时候,这里会报错 - console.error('出错') - console.error(e) - } - } - resize = () => { - if (!this.answers_editormd) { // 还未初始化 - return; - } - this.answers_editormd.resize() - this.answers_editormd.cm && this.answers_editormd.cm.refresh() - this.answers_editormd.cm.focus() - } - - getValue = () => { - try { - return this.answers_editormd.getValue() - } catch (e) { - return '' - } - } - setValue = (val) => { - try { - this.answers_editormd.setValue(val) - } catch (e) { - // TODO 这里多实例的时候,前一个实例的state会被后面这个覆盖 参考NewWork.js http://localhost:3007/courses/1309/homework/9300/edit/1 - // 未初始化 - this.setState({ initValue: val }) - } - } - - render() { - - let { - showError - } = this.state; - let { mdID, className, noStorage, imageExpand } = this.props; - let _style = {} - if (showError) { - _style.border = '1px solid red' - } - return ( - -
    - {/* padding10-20 */} -
    - -
    -
    -
    -
    -
    - {noStorage == true ? ' ' :

     

    } - {/* {noStorage == true ? ' ' :

     

    } */} -
    -
    - ) - } -} - - diff --git a/public/react/src/tpm/challengesnew/TPManswer.js b/public/react/src/tpm/challengesnew/TPManswer.js deleted file mode 100644 index bc9523422..000000000 --- a/public/react/src/tpm/challengesnew/TPManswer.js +++ /dev/null @@ -1,366 +0,0 @@ -import React, {Component} from 'react'; - -import {Input, Select, Radio, Checkbox, Popconfirm, message, Modal} from 'antd'; - -import {BrowserRouter as Router, Route, Link, Switch} from "react-router-dom"; - -// import "antd/dist/antd.css"; - -import { getImageUrl, toPath, getUrl } from 'educoder'; - -import axios from 'axios'; - -import './css/TPMchallengesnew.css'; - -let origin = getUrl(); - -let path = getUrl("/editormd/lib/") - -const $ = window.$; - -let timeout; - -let currentValue; - -const Option = Select.Option; - -const RadioGroup = Radio.Group; - - -// 恢复数据 -function md_rec_data(k,mdu,id, editor){ - if(window.sessionStorage.getItem(k+mdu) !== null){ - editor.setValue(window.sessionStorage.getItem(k+mdu)); - md_clear_data(k,mdu,id); - } -} - -// 保存数据 -function md_add_data(k,mdu,d){ - window.sessionStorage.setItem(k+mdu,d); -} - -// 清空保存的数据 -function md_clear_data(k,mdu,id){ - window.sessionStorage.removeItem(k+mdu); - var id1 = "#e_tip_"+id; - var id2 = "#e_tips_"+id; - if(k == 'content'){ - $(id2).html(""); - }else{ - $(id1).html(""); - } -} - -function md_elocalStorage(editor,mdu,id){ - if (window.sessionStorage){ - var oc = window.sessionStorage.getItem('content'+mdu); - if(oc !== null ){ - console.log("#e_tips_"+id) - $("#e_tips_"+id).data('editor', editor); - var h = '您上次有已保存的数据,是否恢复 ? / 不恢复'; - $("#e_tips_"+id).html(h); - } - setInterval(function() { - var d = new Date(); - var h = d.getHours(); - var m = d.getMinutes(); - var s = d.getSeconds(); - h = h < 10 ? '0' + h : h; - m = m < 10 ? '0' + m : m; - s = s < 10 ? '0' + s : s; - if(editor.getValue().trim() != ""){ - md_add_data("content",mdu,editor.getValue()); - var id1 = "#e_tip_"+id; - var id2 = "#e_tips_"+id; - - $(id1).html(" 数据已于 " + h + ':' + m + ':' + s +" 保存 "); - $(id2).html(""); - } - },10000); - - }else{ - $("#e_tip_"+id).after('您的浏览器不支持localStorage.无法开启自动保存草稿服务,请升级浏览器!'); - } -} - - -function create_editorMD(id, width, high, placeholder, imageUrl, callback) { - var editorName = window.editormd(id, { - width: width, - height: high, - path: path, // "/editormd/lib/" - - syncScrolling: "single", - tex: true, - tocm: true, - emoji: true, - taskList: true, - codeFold: true, - searchReplace: true, - htmlDecode: "style,script,iframe", - sequenceDiagram: true, - autoFocus: false, - toolbarIcons: function () { - // Or return editormd.toolbarModes[name]; // full, simple, mini - // Using "||" set icons align right. - return ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "|", "testIcon", "testIcon1", '|', "image", "table", '|', "watch", "clear"] - }, - toolbarCustomIcons: { - testIcon: "
    ", - testIcon1: "
    " - }, - //这个配置在simple.html中并没有,但是为了能够提交表单,使用这个配置可以让构造出来的HTML代码直接在第二个隐藏的textarea域中,方便post提交表单。 - saveHTMLToTextarea: true, - // 用于增加自定义工具栏的功能,可以直接插入HTML标签,不使用默认的元素创建图标 - dialogMaskOpacity: 0.6, - placeholder: placeholder, - imageUpload: true, - imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp", "JPG", "JPEG", "GIF", "PNG", "BMP", "WEBP"], - imageUploadURL: imageUrl,//url - onload: function () { - // this.previewing(); - $("#" + id + " [type=\"latex\"]").bind("click", function () { - editorName.cm.replaceSelection("```latex"); - editorName.cm.replaceSelection("\n"); - editorName.cm.replaceSelection("\n"); - editorName.cm.replaceSelection("```"); - var __Cursor = editorName.cm.getDoc().getCursor(); - editorName.cm.setCursor(__Cursor.line - 1, 0); - }); - - $("#" + id + " [type=\"inline\"]").bind("click", function () { - editorName.cm.replaceSelection("`$$$$`"); - var __Cursor = editorName.cm.getDoc().getCursor(); - editorName.cm.setCursor(__Cursor.line, __Cursor.ch - 3); - editorName.cm.focus(); - }); - $("[type=\"inline\"]").attr("title", "行内公式"); - $("[type=\"latex\"]").attr("title", "多行公式"); - - md_elocalStorage(editorName, `answers__${id}`, "Memoanswers"); - - callback && callback() - } - }); - return editorName; -} - - -export default class TPManswer extends Component { - constructor(props) { - super(props) - this.state = { - choice_url: undefined, - practice_url: undefined, - go_back_url: undefined, - value: 1, - answer:"", - id:undefined, - checkpointId:undefined, - power: false, - prev_challenge: undefined, - next_challenge: undefined, - } - } - - answerMD(initValue, id) { - - this.contentChanged = false; - const placeholder = ""; - // amp; - // 编辑时要传memoId - const imageUrl = `/api/attachments.json`; - // 创建editorMd - - const answers_editormd = create_editorMD(id, '100%', 400, placeholder, imageUrl, () => { - setTimeout(() => { - answers_editormd.resize() - answers_editormd.cm && answers_editormd.cm.refresh() - }, 500) - - if (initValue != undefined) { - answers_editormd.setValue(initValue) - } - answers_editormd.cm.on("change", (_cm, changeObj) => { - console.log('....contentChanged') - this.contentChanged = true; - }) - }); - this.answers_editormd = answers_editormd; - window.answers_editormd = answers_editormd; - - } - - componentDidMount() { - let id = this.props.match.params.shixunId; - let checkpointId=this.props.match.params.checkpointId; - - let newchoice_url= "/shixuns/"+id+"/challenges/newquestion"; - let newpractice_url= "/shixuns/"+id+"/challenges/new"; - let newgo_back_url="/shixuns/"+id+"/challenges"; - this.setState({ - shixunId:id, - checkpointId:checkpointId - }) - - - let url = "/shixuns/" + id + "/challenges/" + checkpointId + "/edit.json?tab=2"; - axios.get(url).then((response) => { - let newprev_challenge = response.data.prev_challenge; - let next_challenge = response.data.next_challenge; - if (newprev_challenge != undefined) { - if(newprev_challenge.st===0){ - newprev_challenge = "/shixuns/" + id + "/challenges/" + newprev_challenge.id + "/editcheckpoint"; - }else{ - newprev_challenge = "/shixuns/" + id + "/challenges/" + newprev_challenge.id + "/editquestion"; - } - } - if (next_challenge != undefined) { - - if(next_challenge.st===0){ - next_challenge = "/shixuns/" + id + "/challenges/" + next_challenge.id+ "/editcheckpoint"; - }else{ - next_challenge = "/shixuns/" + id + "/challenges/" + next_challenge.id+ "/editquestion"; - } - } - this.setState({ - answer:response.data.answer, - power: response.data.power, - choice_url: newchoice_url, // 导航中的新建选择题url - practice_url: newpractice_url, //string 导航中新建实践题url - go_back_url: newgo_back_url, //string 导航中的返回url - position: response.data.position, //int 关卡位置,导航栏中的第几关 - prev_challenge: newprev_challenge, - next_challenge: next_challenge, - }) - - if(response.data.power===false){ - this.props.showSnackbar("没有权限修改"); - } - if(response.data.answer===undefined||response.data.answer===null){ - this.answerMD("", "answerMD"); - }else{ - this.answerMD(response.data.answer, "answerMD"); - } - - }).catch((error) => { - console.log(error) - }); - } - - challenge_answer_submit=()=> { - let id = this.props.match.params.shixunId; - let{checkpointId}=this.state; - let url = "/shixuns/"+id+"/challenges/"+checkpointId+".json"; - const answer_editormdvalue = this.answers_editormd.getValue(); - - axios.put(url,{ - tab:2, - identifier:id, - id:checkpointId, - challenge:{ - answer:answer_editormdvalue - } - } - ).then((response) => { - this.props.showSnackbar(response.data.messages); - - }).catch((error) => { - console.log(error) - }); - } - - render() { - - let { - choice_url, - practice_url, - go_back_url, - position, - task_pass_default, - submit_url, - shixunId, - checkpointId, - power, - prev_challenge, - next_challenge, - } = this.state; - let tab1url="/shixuns/" + shixunId + "/challenges/"+checkpointId+"/editcheckpoint"; - let tab2url="/shixuns/" + shixunId + "/challenges/"+checkpointId+"/tab=2"; - let tab3url="/shixuns/" + shixunId + "/challenges/"+checkpointId+"/tab=3"; - // console.log(this.props) - return ( - -
    -
    - - - - 第{position}关 - 返回 - - {prev_challenge === undefined ? "" : - 上一关 - } - - {next_challenge === undefined ? "" : - 下一关 - } - - + 实践类型 - + 选择题类型 - -
    - -
    - -
  • - 本关任务 -
  • - -
  • - 评测设置 -
  • - -
  • - 参考答案 -
  • -
    - -
    - -
    -

    参考答案

    -
    -
    - -
    -
    -
    -
    -

    -

    -
    - -
    - -
    4||this.props.identity===undefined||power===false?"none":"block"}}> - 提交 - 取消 -
    -
    -
    - ) - } -} - - diff --git a/public/react/src/tpm/challengesnew/TPManswer2.js b/public/react/src/tpm/challengesnew/TPManswer2.js deleted file mode 100644 index 6cbfcd081..000000000 --- a/public/react/src/tpm/challengesnew/TPManswer2.js +++ /dev/null @@ -1,368 +0,0 @@ -import React, {Component} from 'react'; - -import {Input, InputNumber, Select, Radio, Checkbox, Popconfirm, message, Modal, Tooltip} from 'antd'; - -import {BrowserRouter as Router, Route, Link, Switch} from "react-router-dom"; - -// import "antd/dist/antd.css"; - -import { getImageUrl, toPath, getUrl } from 'educoder'; - -import axios from 'axios'; - -import './css/TPMchallengesnew.css'; - -import TPMMDEditor from './TPMMDEditor'; - - -let origin = getUrl(); - -let path = getUrl("/editormd/lib/") - -const $ = window.$; - -let timeout; - -let currentValue; - -const Option = Select.Option; - -const RadioGroup = Radio.Group; - -// const testAnswers = [{ -// "id": 4337, -// "name": "解题思路1", -// "contents": "答案的解题思路1", -// "level": 1, -// "score": 25 -// }, -// { -// "id": 4338, -// "name": "解题思路2", -// "contents": "答案的解题思路2", -// "level": 2, -// "score": 25 -// }] -export default class TPManswer extends Component { - constructor(props) { - super(props) - this.state = { - choice_url: undefined, - practice_url: undefined, - go_back_url: undefined, - value: 1, - answer:"", - id:undefined, - checkpointId:undefined, - power: false, - prev_challenge: undefined, - next_challenge: undefined, - answers: [] //testAnswers - } - } - - componentDidMount() { - let id = this.props.match.params.shixunId; - let checkpointId=this.props.match.params.checkpointId; - - let newchoice_url= "/shixuns/"+id+"/challenges/newquestion"; - let newpractice_url= "/shixuns/"+id+"/challenges/new"; - let newgo_back_url="/shixuns/"+id+"/challenges"; - this.setState({ - shixunId:id, - checkpointId:checkpointId - }) - - - let url = "/shixuns/" + id + "/challenges/" + checkpointId + "/edit.json?tab=2"; - axios.get(url).then((response) => { - let newprev_challenge = response.data.prev_challenge; - let next_challenge = response.data.next_challenge; - if (newprev_challenge != undefined) { - if(newprev_challenge.st===0){ - newprev_challenge = "/shixuns/" + id + "/challenges/" + newprev_challenge.id + "/editcheckpoint"; - }else{ - newprev_challenge = "/shixuns/" + id + "/challenges/" + newprev_challenge.id + "/editquestion"; - } - } - if (next_challenge != undefined) { - - if(next_challenge.st===0){ - next_challenge = "/shixuns/" + id + "/challenges/" + next_challenge.id+ "/editcheckpoint"; - }else{ - next_challenge = "/shixuns/" + id + "/challenges/" + next_challenge.id+ "/editquestion"; - } - } - this.setState({ - answer:response.data.answer, - power: response.data.power, - choice_url: newchoice_url, // 导航中的新建选择题url - practice_url: newpractice_url, //string 导航中新建实践题url - go_back_url: newgo_back_url, //string 导航中的返回url - position: response.data.position, //int 关卡位置,导航栏中的第几关 - prev_challenge: newprev_challenge, - next_challenge: next_challenge, - }) - - if(response.data.power===false){ - this.props.showSnackbar("没有权限修改"); - } - // if(response.data.answer===undefined||response.data.answer===null){ - // this.answerMD("", "answerMD"); - // }else{ - // this.answerMD(response.data.answer, "answerMD"); - // } - - }).catch((error) => { - console.log(error) - }); - - - let urlAnswer = `/shixuns/${id}/challenges/${checkpointId}/answer.json`; - axios.get(urlAnswer).then((response) => { - if (response.data.status === 401) { - - } else if (response.data) { - this.setState({ answers: response.data }) - } - }) - } - - challenge_answer_submit=()=> { - // `levelSection${index}` - // this.refs.md0 - const { answers } = this.state; - const answersParams = answers.slice(0) - console.log(answersParams) - let isValidate = true; - let totalScore = 0; - answersParams.forEach( (item, index) => { - if (!isValidate) { - return; - } - const sectionId = `#levelSection${index}`; - const mdContnet = this.refs[`md${index}`].getValue().trim();; - item.contents = mdContnet; - item.name = item.name.trim() - totalScore += item.score; - delete item.id; - if (!item.name) { - this.props.showSnackbar("请先填写参考答案名称"); - isValidate = false; - } else if (!mdContnet) { - this.props.showSnackbar("请先填写参考答案内容"); - isValidate = false; - } - if (!isValidate) { - $("html, body").animate({ scrollTop: $(`${sectionId}`).offset().top - 100}) - } - }) - if (!isValidate) { - return; - } - if (answersParams.length != 0 && totalScore != 100) { - this.props.showSnackbar("请先保证占比和为100%"); - return; - } - let id = this.props.match.params.shixunId; - let {checkpointId} = this.state; - let url = `/shixuns/${id}/challenges/${checkpointId}/crud_answer.json`; - - axios.post(url,{ - challenge_answer: answersParams - } - ).then((response) => { - if (response.data) { - if (response.data.message) { - this.props.showSnackbar(response.data.message); - } - if (response.data.status == 1) { - $("html").animate({ scrollTop: 0 }) - } - } - - }).catch((error) => { - console.log(error) - }); - } - onNameChange = (e, index) => { - const newAnswer = Object.assign({}, this.state.answers[index]) - newAnswer.name = e.target.value - const newAnswers = this.state.answers.slice(0) - newAnswers[index] = newAnswer - this.setState({ answers: newAnswers}) - } - onScoreChange = (val, index) => { - const newAnswer = Object.assign({}, this.state.answers[index]) - newAnswer.score = val - const newAnswers = this.state.answers.slice(0) - newAnswers[index] = newAnswer - this.setState({ answers: newAnswers}) - } - answerOnChange = (val, index) => { - if (!this.state.answers[index]) { - // 1、2、3删除2会走到这里 - return; - } - const newAnswer = Object.assign({}, this.state.answers[index]) - newAnswer.contents = val - const newAnswers = this.state.answers.slice(0) - newAnswers[index] = newAnswer - this.setState({ answers: newAnswers}) - } - addAnswer = () => { - const newAnswers = this.state.answers.slice(0) - newAnswers.push({ - "name": `解题思路${newAnswers.length + 1}`, - "contents": "", - "score": 10 - }) - this.setState({ answers: newAnswers }) - } - - delanswers=(index)=>{ - let {answers}=this.state; - let newanswers=answers; - newanswers.splice(index,1) - this.setState({ - answers:newanswers - }, () => { - for(let i = index; i < newanswers.length; i ++) { - this.refs[`md${i}`].setValue(newanswers[i].contents) - } - }) - } - render() { - - let { - choice_url, - practice_url, - go_back_url, - position, - task_pass_default, - submit_url, - shixunId, - checkpointId, - power, - prev_challenge, - next_challenge, - answers, - } = this.state; - let tab1url="/shixuns/" + shixunId + "/challenges/"+checkpointId+"/editcheckpoint"; - let tab2url="/shixuns/" + shixunId + "/challenges/"+checkpointId+"/tab=2"; - let tab3url="/shixuns/" + shixunId + "/challenges/"+checkpointId+"/tab=3"; - // console.log(this.props) - return ( - -
    -
    - - - - 第{position}关 - 返回 - - {prev_challenge === undefined ? "" : - 上一关 - } - - {next_challenge === undefined ? "" : - 下一关 - } - - + 实践类型 - + 选择题类型 - -
    - -
    - -
  • - 本关任务 -
  • - -
  • - 评测设置 -
  • - -
  • - 参考答案 -
  • -
    - -
    - -
    -

    - 可以将参考答案分级设置,让学员自行选择级别,每级查看后按照比例扣分值(学员已完成任务再查看,则不影响原因已获得的成绩) -

    -

    - 示例:级别1,扣减分值占比25%;级别2,扣减分值占比35%;级别3,扣减分值占比40%;则学员选择查看级别1的答案,将被扣减25%的分值; - 选择查看级别2的答案,将被扣减60%的分值;选择查看级别3的答案,将被扣减100%的分值。 -

    - - - - { - answers.map((answer, index) => { - return
    - * -

    级别{index + 1}

    - - this.delanswers(index)}> - - - -
    -
    - 名称: - this.onNameChange(e, index)}> - - 扣减分值占比: - this.onScoreChange(e, index)} >% -
    -
    - 参考答案: - this.answerOnChange(val, index)}> -
    -
    -
    - }) - } - -
    4||this.props.identity===undefined||power===false?"none":"block"}}> - 新增 -
    -
    - - -
    - -
    4||this.props.identity===undefined||power===false?"none":"block"}}> - 提交 - 取消 -
    -
    -
    - ) - } -} - - diff --git a/public/react/src/tpm/challengesnew/TPMchallengesnew.js b/public/react/src/tpm/challengesnew/TPMchallengesnew.js deleted file mode 100644 index 9da0b79f5..000000000 --- a/public/react/src/tpm/challengesnew/TPMchallengesnew.js +++ /dev/null @@ -1,615 +0,0 @@ -import React, {Component} from 'react'; - -import {Input, Select, Radio, Checkbox, Popconfirm, message, Modal} from 'antd'; - -import {BrowserRouter as Router, Route, Link, Switch} from "react-router-dom"; - -// import "antd/dist/antd.css"; - -import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor'; - -import axios from 'axios'; - -import './css/TPMchallengesnew.css'; - -import { getImageUrl, toPath } from 'educoder'; - -import {getUrl} from 'educoder'; - -let origin = getUrl(); - -let path = getUrl("/editormd/lib/") - -const $ = window.$; - -let timeout; - -let currentValue; - -const Option = Select.Option; - -const RadioGroup = Radio.Group; - -export default class TPMchallengesnew extends Component { - constructor(props) { - super(props) - this.exercisememoMDRef=React.createRef(); - this.state = { - choice_url: undefined, - practice_url: undefined, - go_back_url: undefined, - task_pass_default: undefined, - submit_url: undefined, - shixunCreatePracticeGroup: 1, - optionsums:[100,200], - activetype:0, - setopen: false, - shixunCreatePractice: undefined, - onshixunsmarkvalue: 100, - shixunsskillvalue: undefined, - shixunsskillvaluelist: [], - tab2url: "", - tab3url: "", - prev_challenge:undefined, - next_challenge:undefined, - power: false, - shixunCreatePracticetype: false, - shixunsskillvaluelisttype: false, - marktype:false, - editPracticesendtype:false, - CreatePracticesendtype:false, - exec_time:20, - shixunExec_timeType:false - } - } - - - componentDidMount() { - let id = this.props.match.params.shixunId; - let checkpointId=this.props.match.params.checkpointId; - - let newchoice_url= "/shixuns/"+id+"/challenges/newquestion"; - let newpractice_url= "/shixuns/"+id+"/challenges/new"; - let newgo_back_url="/shixuns/"+id+"/challenges" - if(checkpointId===undefined){ - //新建模式 - let url = "/shixuns/" + id + "/challenges/new.json" - axios.get(url).then((response) => { - this.setState({ - choice_url: newchoice_url, - practice_url: newpractice_url, - go_back_url: newgo_back_url, - position: response.data.position, - task_pass_default: response.data.task_pass_default, - submit_url: response.data.submit_url, - checkpointId:checkpointId, - exercisememoMDRefval:response.data.task_pass_default - }) - - this.exercisememoMDRef.current.setValue(response.data.task_pass_default||'') - }).catch((error) => { - console.log(error) - }); - }else{ - //编辑模式 - let url="/shixuns/"+id+"/challenges/"+checkpointId+".json?tab=0"; - axios.get(url).then((response) => { - - let optionsum; - if(response.data.difficulty===1){ - optionsum=[100,200]; - }else if(response.data.difficulty===2){ - optionsum=[300,400,500,600]; - }else if(response.data.difficulty===3){ - optionsum=[700,800,900,1000] - } - let newprev_challenge=response.data.prev_challenge; - let next_challenge=response.data.next_challenge; - if (newprev_challenge != undefined) { - if(newprev_challenge.st===0){ - newprev_challenge = "/shixuns/" + id + "/challenges/" + newprev_challenge.id + "/editcheckpoint"; - }else{ - newprev_challenge = "/shixuns/" + id + "/challenges/" + newprev_challenge.id + "/editquestion"; - } - } - if (next_challenge != undefined) { - if(next_challenge.st===0){ - next_challenge = "/shixuns/" + id + "/challenges/" + next_challenge.id+ "/editcheckpoint"; - }else{ - next_challenge = "/shixuns/" + id + "/challenges/" + next_challenge.id+ "/editquestion"; - } - } - this.setState({ - power: response.data.power, - prev_challenge:newprev_challenge, - next_challenge:next_challenge, - choice_url: newchoice_url, - practice_url: newpractice_url, - go_back_url: newgo_back_url, - shixunCreatePractice:response.data.subject, - position:response.data.position, - shixunCreatePracticeGroup:response.data.difficulty, - optionsums:optionsum, - onshixunsmarkvalue:response.data.score, - shixunsskillvaluelist:response.data.tags, - checkpointId:checkpointId, - exec_time:response.data.exec_time, - tab2url: "/shixuns/" + id + "/challenges/"+checkpointId+"/tab=2", - tab3url: "/shixuns/" + id + "/challenges/"+checkpointId+"/tab=3", - exercisememoMDRefval:response.data.task_pass - }) - if(response.data.power===false){ - this.props.showSnackbar("你没有权限修改"); - } - - this.exercisememoMDRef.current.setValue(response.data.task_pass||'') - }).catch((error) => { - console.log(error) - }); - - } - - } - - onshixunCreatePracticeChange = (e) => { - let optionsum; - let onshixunsmark; - if(e.target.value===1){ - optionsum=[100,200]; - onshixunsmark=100; - }else if(e.target.value===2){ - optionsum=[300,400,500,600]; - onshixunsmark=300; - }else if(e.target.value===3){ - optionsum=[700,800,900,1000] - onshixunsmark=700; - } - this.setState({ - shixunCreatePracticeGroup: e.target.value, - optionsums:optionsum, - onshixunsmarkvalue:onshixunsmark - }) - } - - shixunCreatePractice = (e) => { - this.setState({ - shixunCreatePractice: e.target.value - }) - } - - CreatePracticesend = () => { - - - this.setState({ - CreatePracticesendtype:true - }) - - if(this.props.status===2){ - this.props.showSnackbar("该实训已经发布不能新建") - this.setState({ - CreatePracticesendtype:false - }) - return - } - let {shixunCreatePractice, shixunCreatePracticeGroup, onshixunsmarkvalue, shixunsskillvaluelist,exec_time} = this.state; - if (shixunCreatePractice === undefined||shixunCreatePractice=="") { - this.setState({ - shixunCreatePracticetype: true - }) - this.props.showSnackbar("任务名称为空") - $('html').animate({ - scrollTop: 10 - }, 1000); - - this.setState({ - CreatePracticesendtype:false - }) - return - } - - if (shixunsskillvaluelist.length === 0) { - this.setState({ - shixunsskillvaluelisttype: true, - CreatePracticesendtype:false - }) - this.props.showSnackbar("技能标签为空") - return - } - if(exec_time===null||exec_time===undefined||exec_time===""){ - - this.setState({ - shixunExec_timeType:false - }) - return - } - - const exercise_editormdvalue = this.exercisememoMDRef.current.getValue().trim(); - let id = this.props.match.params.shixunId; - - let url = "/shixuns/" + id + "/challenges.json"; - - axios.post(url, { - identifier:id, - subject: shixunCreatePractice, - task_pass: exercise_editormdvalue, - difficulty: shixunCreatePracticeGroup, - score: onshixunsmarkvalue, - challenge_tag: shixunsskillvaluelist, - st: 0, - exec_time:exec_time - }).then((response) => { - if (response.data.status === 1) { - // $("html").animate({ scrollTop: 0 }) - - window.location.href=`/shixuns/${id}/challenges/${response.data.challenge_id}/editcheckpoint`; - // this.setState({ - // setopen: true, - // CreatePracticesendtype:false, - // tab2url: "/shixuns/" + id + "/challenges/"+response.data.challenge_id+"/tab=2", - // tab3url: "/shixuns/" + id + "/challenges/"+response.data.challenge_id+"/tab=3", - // }) - - } - // this.props.showSnackbar(response.data.messages); - }).catch((error) => { - console.log(error) - }); - - - - } - - onshixunsmark = (value) => { - this.setState({ - onshixunsmarkvalue: value - }) - } - - shixunsskill = (e) => { - this.setState({ - shixunsskillvalue: e.target.value - }) - } - - clickshixunsskill = () => { - - let {shixunsskillvalue, shixunsskillvaluelist} = this.state; - if (shixunsskillvalue === "") { - return - } else if (shixunsskillvalue === undefined) { - return - } - - if(shixunsskillvalue == "" || shixunsskillvalue == undefined || shixunsskillvalue == null || (shixunsskillvalue.length>0 && shixunsskillvalue.trim().length == 0)){ - message.error("输入为空,不能保存!"); - return - } - - let list = shixunsskillvaluelist; - list.push(shixunsskillvalue); - this.setState({ - shixunsskillvaluelist: list, - shixunsskillvalue: "" - }) - } - - delshixunsskilllist = (key) => { - let {shixunsskillvaluelist} = this.state; - let newshixunsskillvaluelist = shixunsskillvaluelist; - newshixunsskillvaluelist.splice(key, 1); - this.setState({ - shixunsskillvaluelist: newshixunsskillvaluelist - }) - } - - editPracticesend=()=>{ - - this.setState({ - editPracticesendtype:true - }) - - let {shixunCreatePractice, shixunCreatePracticeGroup, onshixunsmarkvalue, shixunsskillvaluelist,checkpointId,exec_time} = this.state; - - const exercise_editormdvalue = this.exercisememoMDRef.current.getValue().trim(); - - let id = this.props.match.params.shixunId; - - let url = "/shixuns/"+id+"/challenges/"+checkpointId+".json"; - - if (shixunCreatePractice === undefined||shixunCreatePractice=="") { - // this.setState({ - // shixunCreatePracticetype: true - // }) - this.props.showSnackbar("任务名称为空") - $('html').animate({ - scrollTop: 10 - }, 1000); - this.setState({ - editPracticesendtype:false - }) - return - } - - if (shixunsskillvaluelist.length === 0) { - // this.setState({ - // shixunsskillvaluelisttype: true - // }) - this.props.showSnackbar("技能标签为空") - this.setState({ - editPracticesendtype:false - }) - return - } - - if(exec_time===null||exec_time===undefined||exec_time===""){ - - this.setState({ - shixunExec_timeType:false - }) - return - } - axios.put(url, { - tab:0, - identifier:id, - id:checkpointId, - challenge:{ - subject: shixunCreatePractice, - task_pass: exercise_editormdvalue, - difficulty: shixunCreatePracticeGroup, - score: onshixunsmarkvalue, - exec_time:exec_time - }, - challenge_tag:shixunsskillvaluelist - }).then((response) => { - this.props.showSnackbar(response.data.messages); - if (response.data.status === 1) { - this.setState({ - setopen: true, - editPracticesendtype:false, - tab2url: "/shixuns/" + id + "/challenges/"+checkpointId+"/tab=2", - tab3url: "/shixuns/" + id + "/challenges/"+checkpointId+"/tab=3", - }) - // window.location.href = "/shixuns/" + id + "/challenges/"+response.data.challenge_id+"/tab=2" - } - }).catch((error) => { - console.log(error) - }); - - - } - - onshixunsmarks=()=> { - this.setState({ - marktype:true - }) - } - - onshixunsmarkss=()=> { - this.setState({ - marktype:false - }) - } - - setexec_time=(e)=>{ - this.setState({ - exec_time:e.target.value - }) - } - render() { - - let shixuntype = this.props.match.params.type; - - - let {marktype, - shixunCreatePracticetype, shixunsskillvaluelisttype, - choice_url, practice_url, go_back_url, position, task_pass_default, submit_url, setopen,checkpointId,prev_challenge,next_challenge,power, - shixunCreatePractice, shixunCreatePracticeGroup, onshixunsmarkvalue, shixunsskillvalue, shixunsskillvaluelist, tab2url, tab3url,optionsums, - CreatePracticesendtype,editPracticesendtype - } = this.state; - - let options; - if(optionsums!=undefined){ - options = optionsums.map((d, k) => { - return ( - - ) - }) - } - - return ( - -
    - - -
    - -
  • - 本关任务 -
  • - -
  • - {tab2url === "" ? 评测设置 : 评测设置} -
  • - -
  • - {tab3url === "" ? 参考答案 : 参考答案} - -
  • -
    - -
    -
    -

    任务名称

    -
    - * -
    - -
    -
    - 必填项 -
    -
    -
    -
    - - -
    - -

    过关任务

    - - - -

    -

    -
    - - -
    -

    难度系数

    -
    - - - 简单 - 中等 - 困难 - - -
    -

    奖励经验值

    -
    - * - - - -

    - 如果学员答题错误,则不能得到相应的经验值
    - 如果学员成功得到经验值,那么将同时获得等值的金币奖励,如:+10经验值、+10金币 -

    - - 必填项 -
    -
    - - -
    -

    技能标签

    -
    - * -
    - - {/*+ 添加*/} -
    学员答题正确将获得技能,否则不能获得技能
    -
    - - { - shixunsskillvaluelist===undefined?"":shixunsskillvaluelist.length === 0 ? "" : shixunsskillvaluelist.map((itme, key) => { - return ( -
  • {itme} - this.delshixunsskilllist(key)}>× -
  • - ) - }) - } - - -
    -
    - - 必填项 -
    -
    - -
    -

    服务配置

    -
    - * - -
    - -
    - 必填项 -
    -
    -
    - -
    4||this.props.identity===undefined?"none":'block'}} - > - {checkpointId===undefined?提交: - 提交} - 取消 -
    -
    -
    - ) - } -} - - diff --git a/public/react/src/tpm/challengesnew/TPMevaluation.js b/public/react/src/tpm/challengesnew/TPMevaluation.js deleted file mode 100644 index f29e9311e..000000000 --- a/public/react/src/tpm/challengesnew/TPMevaluation.js +++ /dev/null @@ -1,1213 +0,0 @@ -import React, {Component} from 'react'; - -import {Input, Select, Radio, Checkbox, Popconfirm, message, Modal,Button,Icon,Tooltip} from 'antd'; - -import {BrowserRouter as Router, Route, Link, Switch} from "react-router-dom"; - -// import "antd/dist/antd.css"; - -import axios from 'axios'; - -import { getImageUrl, toPath } from 'educoder'; - -import './css/TPMchallengesnew.css'; - -import {getUrl} from 'educoder'; - -let origin = getUrl(); - -let path = getUrl("/editormd/lib/") - -const $ = window.$; - -let timeout; - -let currentValue; - -const Option = Select.Option; - -const RadioGroup = Radio.Group; - -const { TextArea } = Input; - -function create_editorMD(id, width, high, placeholder, imageUrl, callback) { - var editorName = window.editormd(id, { - width: width, - height: high, - path: path, // "/editormd/lib/" - - syncScrolling: "single", - tex: true, - tocm: true, - emoji: true, - taskList: true, - codeFold: true, - searchReplace: true, - htmlDecode: "style,script,iframe", - sequenceDiagram: true, - autoFocus: false, - toolbarIcons: function () { - // Or return editormd.toolbarModes[name]; // full, simple, mini - // Using "||" set icons align right. - return ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "|", "testIcon", "testIcon1", '|', "image", "table", '|', "watch", "clear"] - }, - toolbarCustomIcons: { - testIcon: "
    ", - testIcon1: "
    " - }, - //这个配置在simple.html中并没有,但是为了能够提交表单,使用这个配置可以让构造出来的HTML代码直接在第二个隐藏的textarea域中,方便post提交表单。 - saveHTMLToTextarea: true, - // 用于增加自定义工具栏的功能,可以直接插入HTML标签,不使用默认的元素创建图标 - dialogMaskOpacity: 0.6, - placeholder: placeholder, - imageUpload: true, - imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp", "JPG", "JPEG", "GIF", "PNG", "BMP", "WEBP"], - imageUploadURL: imageUrl,//url - onload: function () { - // this.previewing(); - $("#" + id + " [type=\"latex\"]").bind("click", function () { - editorName.cm.replaceSelection("```latex"); - editorName.cm.replaceSelection("\n"); - editorName.cm.replaceSelection("\n"); - editorName.cm.replaceSelection("```"); - var __Cursor = editorName.cm.getDoc().getCursor(); - editorName.cm.setCursor(__Cursor.line - 1, 0); - }); - - $("#" + id + " [type=\"inline\"]").bind("click", function () { - editorName.cm.replaceSelection("`$$$$`"); - var __Cursor = editorName.cm.getDoc().getCursor(); - editorName.cm.setCursor(__Cursor.line, __Cursor.ch - 3); - editorName.cm.focus(); - }); - $("[type=\"inline\"]").attr("title", "行内公式"); - $("[type=\"latex\"]").attr("title", "多行公式"); - - window.md_elocalStorage(editorName, `exercise__${id}`, "Memochallengesnew"); - - callback && callback() - } - }); - return editorName; -} - - -export default class TPMevaluation extends Component { - constructor(props) { - super(props) - this.state = { - choice_url: undefined, - practice_url: undefined, - go_back_url: undefined, - task_pass_default: undefined, - submit_url: undefined, - value: 1, - evaluationlist:[], - shixunId:undefined, - power:false, - shixunfilepath:undefined, - evaluationvisible:false, - trees:undefined, - path:"", - main:[], - saveshixunfilepath:undefined, - selectpath:undefined, - shixunfilepathplay:undefined, - shixunfileexpectpicturepath:undefined, - shixunfilestandardpicturepath:undefined, - shixunfilepicturepath:undefined, - pathoptionvalue:-1, - showrepositoryurltiptype: false, - prev_challenge: undefined, - next_challenge: undefined, - StudentTaskPapers:false, - StudentTaskDocs:false, - selectpatharr:[], - handpathopt:false, - scorevalue:false, - markvalue:true, - scoretype:undefined, - web_route:null - } - } - - - exerciseMD(initValue, id) { - - this.contentChanged = false; - const placeholder = ""; -// amp; -// 编辑时要传memoId - const imageUrl = `/api/attachments.json`; -// 创建editorMd - - const exercise_editormd = create_editorMD(id, '100%', 400, placeholder, imageUrl, () => { - setTimeout(() => { - exercise_editormd.resize() - exercise_editormd.cm && exercise_editormd.cm.refresh() - }, 500) - - if (initValue != undefined) { - exercise_editormd.setValue(initValue) - } - exercise_editormd.cm.on("change", (_cm, changeObj) => { - console.log('....contentChanged') - this.contentChanged = true; - }) - }); - this.exercise_editormd = exercise_editormd; - window.exercise_editormd = exercise_editormd; - - } - - componentDidMount() { - let id = this.props.match.params.shixunId; - let checkpointId=this.props.match.params.checkpointId; - this.setState({ - shixunId:id, - checkpointId:checkpointId - }) - let newchoice_url= "/shixuns/"+id+"/challenges/newquestion"; - let newpractice_url= "/shixuns/"+id+"/challenges/new"; - let newgo_back_url="/shixuns/"+id+"/challenges"; - - let url = "/shixuns/" + id + "/challenges/" + checkpointId + "/edit.json?tab=1"; - axios.get(url).then((response) => { - let newprev_challenge = response.data.prev_challenge; - let next_challenge = response.data.next_challenge; - if (newprev_challenge != undefined) { - if(newprev_challenge.st===0){ - newprev_challenge = "/shixuns/" + id + "/challenges/" + newprev_challenge.id + "/editcheckpoint"; - }else{ - newprev_challenge = "/shixuns/" + id + "/challenges/" + newprev_challenge.id + "/editquestion"; - } - } - if (next_challenge != undefined) { - - if(next_challenge.st===0){ - next_challenge = "/shixuns/" + id + "/challenges/" + next_challenge.id+ "/editcheckpoint"; - }else{ - next_challenge = "/shixuns/" + id + "/challenges/" + next_challenge.id+ "/editquestion"; - } - } - let newevaluationlist=[] - if(response.data.test_sets.length===0){ - let newlist=[ - {hidden:0,input:"",output:"",score:50}, - {hidden:0,input:"",output:"",score:50} - ] - newevaluationlist=newlist - }else{ - newevaluationlist=response.data.test_sets - } - - this.setState({ - power: response.data.power, - evaluationlist:newevaluationlist, - shixunfilepath:response.data.path, - shixunfilepathplay:response.data.exec_path, - pathoptionvalue:response.data.show_type, - shixunfileexpectpicturepath:response.data.original_picture_path, - shixunfilestandardpicturepath:response.data.expect_picture_path, - shixunfilepicturepath:response.data.picture_path, - prev_challenge: newprev_challenge, - next_challenge: next_challenge, - choice_url: newchoice_url, // 导航中的新建选择题url - practice_url: newpractice_url, //string 导航中新建实践题url - go_back_url: newgo_back_url, //string 导航中的返回url - position: response.data.position, //int 关卡位置,导航栏中的第几关 - scorevalue:response.data.test_set_score, - markvalue:response.data.test_set_average, - web_route:response.data.web_route, - has_web_route:response.data.has_web_route - }) - this.evaluationoninputvalueonload(); - if(response.data.power===false){ - this.props.showSnackbar("你没有权限修改"); - } - if(response.data.answer===undefined){ - this.answerMD("", "answerMD"); - }else{ - this.answerMD(response.data.answer, "answerMD"); - } - - }).catch((error) => { - console.log(error) - }); - - } - - - setevaluationlist=(newevaluationlist)=>{ - this.setState({ - evaluationlist:newevaluationlist - }) - console.log(newevaluationlist) - } - - - addevaluationon=()=>{ - let {evaluationlist,markvalue}=this.state; - let newevaluationlist=evaluationlist; - newevaluationlist.push({hidden:0,input:"",output:"",score:0}); - newevaluationlist=this.oneditevaluationlist(newevaluationlist,markvalue); - this.setevaluationlist(newevaluationlist); - } - - del_test_array=(key)=>{ - let {evaluationlist,markvalue}=this.state; - let newevaluationlist=evaluationlist; - newevaluationlist.splice(key,1); - newevaluationlist=this.oneditevaluationlist(newevaluationlist,markvalue); - this.setevaluationlist(newevaluationlist); - } - - getfilepath=(e,shixunfilepath,type)=>{ - this.setState({ - evaluationvisible: true, - selectpath:e.target.value, - selectpatharr:[], - pathtype:type - }); - let id = this.props.match.params.shixunId; - let url ="/shixuns/"+id+"/repository.json"; - axios.post(url,{ - path: "" - }).then((response) => { - if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { - - }else{ - this.setState({ - trees:response.data.trees, - saveshixunfilepath:shixunfilepath, - path:"", - main:[], - }) - } - - }).catch((error) => { - console.log(error) - }); - } - - sendgetfilepath=(newpath,type,newpathtype)=>{ - let id = this.props.match.params.shixunId; - let{path,main,pathtype}=this.state; - let ary=main; - let paths=path; - - this.setState({ - selectpatharr:[], - }) - if(paths===""&&type==="tree"){ - newpath=newpath+"/"; - paths=""; - if(main.length===0){ - ary.push({val:"根目录/",path:""},{val:newpath,path:paths+newpath}) - }else{ - ary.push({val:newpath,path:paths+newpath}) - } - - }else if(paths!=""&&type==="tree"){ - newpath=newpath+"/"; - ary.push({val:newpath,path:paths+newpath}) - } - - - let url ="/shixuns/"+id+"/repository.json"; - if(type==="tree"){ - - axios.post(url,{ - path: paths+newpath - }).then((response) => { - if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { - - }else{ - this.setState({ - trees:response.data.trees, - path:paths+newpath, - main:ary, - // selectpath:"" - }) - } - - }).catch((error) => { - console.log(error) - }); - } - - if(pathtype===2){ - this.setState({ - selectpath: newpathtype, - }) - } - } - - goblakepath=(path,key)=>{ - let {main,selectpath,pathtype} =this.state; - let newmain=[] - for(var i=0;i<=key;i++){ - newmain.push(main[i]) - } - let id = this.props.match.params.shixunId; - let url ="/shixuns/"+id+"/repository.json"; - axios.post(url,{ - path: path - }).then((response) => { - if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { - - }else { - this.setState({ - trees: response.data.trees, - path: path, - main: newmain, - // selectpath:selectpath - }) - } - }).catch((error) => { - console.log(error) - }); - - if(pathtype===2){ - let paths = path.substring(0,path.length-1); - console.log(paths) - this.setState({ - selectpath: paths, - }) - } - - - } - - - - - - // delesavegetfilepath=(value)=>{ - // let {selectpatharr} = this.state - // let newarr =selectpatharr; - // let newselectpath=""; - // for(var i=0; i{ - let {selectpath,saveshixunfilepath,pathtype} = this.state - - if(pathtype===1){ - let newselectpath; - - if(saveshixunfilepath==="shixunfilepathplay"){ - newselectpath=value - }else{ - const type = selectpath.split(';'); - let types=false; - for(var i=0; i1&&arrtype===false){ - // for(var i=0; i{ - let {saveshixunfilepath,selectpath}=this.state; - this.setState({ - evaluationvisible: false, - [saveshixunfilepath]:selectpath - }); - } - evaluationhideModal=()=>{ - this.setState({ - evaluationvisible: false, - }); - } - handpathoptionvalue=(value)=>{ - this.setState({ - pathoptionvalue:value, - shixunfileexpectpicturepath:undefined, - shixunfilestandardpicturepath:undefined, - shixunfilepicturepath:undefined - }) - } - showrepositoryurltip=(type)=>{ - if(type===1){ - this.setState({ - showrepositoryurltiptype:true - }) - }else{ - this.setState({ - showrepositoryurltiptype:false - }) - } - } - - evaluationonChange=(e,key)=>{ - let {evaluationlist}=this.state; - let newevaluationlist=evaluationlist; - let newtype; - if(e===1){ - newtype=0; - }else{ - newtype=1; - } - // newevaluationlist[key].is_public=newtype; - // for(var i=0; i{ - let {evaluationlist,scoretype}=this.state; - - if(scoretype===key){ - this.setState({ - scoretype:undefined - }) - } - let newevaluationlist=evaluationlist; - let sum =parseInt(e.target.value); - if(isNaN(sum)){ - sum=0 - } - newevaluationlist[key].score=sum; - - this.setState({ - evaluationlist:newevaluationlist, - markvalue:false - }) - this.setevaluationlist(newevaluationlist); - } - - // 修改测试集的匹配规则 - changeEvaluationRule=(e,key)=>{ - let {evaluationlist}=this.state; - let newevaluationlist=evaluationlist; - newevaluationlist[key].match_rule=e.target.value - this.setevaluationlist(newevaluationlist); - } - - evaluationoninputvalue=(e,key,type)=>{ - $.fn.autoHeight = function(){ - function autoHeight(elem){ - elem.style.height = 'auto'; - elem.style.maxHeight = '140px'; - elem.scrollTop = 0; //防抖动 - elem.style.height = elem.scrollHeight + 'px'; - } - this.each(function(){ - autoHeight(this); - $(this).on('keyup', function(){ - autoHeight(this); - }); - }); - } - $('textarea[autoHeight]').autoHeight(); - - let {evaluationlist}=this.state; - let newevaluationlist=evaluationlist; - if(type==="sr"){ - newevaluationlist[key].input=e.target.value - }else if(type==="yq"){ - // 统一转成\r\n - newevaluationlist[key].output= e.target.value ? e.target.value.replace(/\r?\n/g, "\r\n") : e.target.value - } - this.setevaluationlist(newevaluationlist); - } - - - evaluationoninputvalueonload=()=>{ - $.fn.autoHeight = function(){ - function autoHeight(elem){ - elem.style.height = 'auto'; - elem.style.maxHeight = '140px'; - elem.scrollTop = 0; //防抖动 - elem.style.height = elem.scrollHeight + 'px'; - } - this.each(function(){ - autoHeight(this); - $(this).on('keyup', function(){ - autoHeight(this); - }); - }); - } - $('textarea[autoHeight]').autoHeight(); - } - submitarbitrationevaluation=()=>{ - let{evaluationlist,shixunfilepath,shixunfilepathplay,shixunfileexpectpicturepath,shixunfilestandardpicturepath,shixunfilepicturepath,pathoptionvalue,scorevalue,markvalue,web_route}=this.state; - - - let newscorevalue; - if(scorevalue===false){ - newscorevalue=false - }else{ - //判断占比 - newscorevalue=true - - let sum=0; - for(var i=0; i100){ - this.props.showSnackbar("测试集的评分占比不能大于100"); - this.setState({ - scoretype:i - }) - return - } - sum=sum+evaluationlist[i].score - } - - if(sum>100||sum<100){ - this.props.showSnackbar("测试集的评分占比之和必须等于100"); - return - } - - - } - if(shixunfilepath===undefined||shixunfilepath===""||shixunfilepath===null){ - this.props.showSnackbar("学员任务文件路径为空"); - this.setState({ - StudentTaskPapers:true - }) - $('html').animate({ - scrollTop: 120 - }, 1000); - return - } - - if(shixunfilepathplay===undefined||shixunfilepathplay===""||shixunfilepathplay===null){ - this.props.showSnackbar("评测执行文件路径为空"); - this.setState({ - StudentTaskDocs:true - }) - $('html').animate({ - scrollTop: 130 - }, 1000); - return - } - - if(evaluationlist.length===0){ - this.props.showSnackbar("测试集不能为空"); - return - } - let id = this.props.match.params.shixunId; - let{checkpointId}=this.state; - let url = "/shixuns/"+id+"/challenges/"+checkpointId+".json"; - let newchallenge={ - path:shixunfilepath, - exec_path:shixunfilepathplay, - show_type:pathoptionvalue, - original_picture_path:pathoptionvalue===-1?undefined:shixunfileexpectpicturepath===undefined?null:shixunfileexpectpicturepath, - expect_picture_path:pathoptionvalue===-1?undefined:shixunfilestandardpicturepath===undefined?null:shixunfilestandardpicturepath, - picture_path:pathoptionvalue===-1?undefined:shixunfilepicturepath===undefined?null:shixunfilepicturepath, - test_set_score:newscorevalue, - test_set_average:markvalue, - web_route:web_route===null?undefined:web_route - } - axios.put(url,{ - tab:1, - challenge:newchallenge, - test_set:evaluationlist - } - ).then((response) => { - this.props.showSnackbar(response.data.messages); - // if(response.data.status===1){ - // window.location.href = "/shixuns/" + id + "/challenges/"+response.data.challenge_id+"/tab=3" - // } - }).catch((error) => { - console.log(error) - }); - } - handpathoptionvalues=()=>{ - this.setState({ - handpathopt:true - }) - } - handpathoptionvaluess=()=>{ - this.setState({ - handpathopt:false - }) - } - saveselectpath=(e)=>{ - - this.setState({ - selectpath:e.target.value - }) - - } - updatepath=(e,name,type)=>{ - this.setState({ - [name]:e.target.value, - pathtype:type - }) - } - - - oneditevaluationlist=(newevaluationlist,markvalue)=>{ - - if(markvalue===true){ - if(100%newevaluationlist.length===0){ - let sum=100/newevaluationlist.length; - for(var i=0; i{ - - let {markvalue,evaluationlist}=this.state; - let newevaluationlist=evaluationlist; - - if(e.target.value===true){ - newevaluationlist=this.oneditevaluationlist(newevaluationlist,markvalue) - } - - this.setState({ - scorevalue: e.target.value, - evaluationlist:newevaluationlist - }); - - this.setevaluationlist(newevaluationlist); - } - - //均匀比例 - onChangemarkvalue=(e)=>{ - let {evaluationlist}=this.state; - - if(e.target.value===true){ - let newevaluationlist=evaluationlist; - newevaluationlist=this.oneditevaluationlist(newevaluationlist,e.target.value); - this.setevaluationlist(newevaluationlist); - } - - this.setState({ - markvalue: e.target.value, - }); - - } - - updatewebroute=(e)=>{ - this.setState({ - web_route:e.target.value - }) - } - render() { - - let { - choice_url, - practice_url, - go_back_url, - position, - evaluationlist, - shixunId, - checkpointId, - power, - shixunfileexpectpicturepath, - shixunfilestandardpicturepath, - shixunfilepicturepath, - shixunfilepath, - evaluationvisible, - trees, - path, - main, - selectpath, - shixunfilepathplay, - pathoptionvalue, - showrepositoryurltiptype, - prev_challenge, - next_challenge, - StudentTaskPapers, - StudentTaskDocs, - web_route, - scorevalue, - markvalue, - scoretype, - has_web_route - } = this.state; - - let tab1url="/shixuns/" + shixunId + "/challenges/"+checkpointId+"/editcheckpoint"; - let tab2url="/shixuns/" + shixunId + "/challenges/"+checkpointId+"/tab=2"; - let tab3url="/shixuns/" + shixunId + "/challenges/"+checkpointId+"/tab=3"; - // console.log(this.props) - const radioStyle = { - display: 'block', - height: '30px', - lineHeight: '30px', - marginLeft: '20px', - }; - return ( - -
    -
    - - - - 第{position}关 - 返回 - - {prev_challenge === undefined ? "" : - 上一关 - } - - {next_challenge === undefined ? "" : - 下一关 - } - - 4||this.props.identity===undefined||this.props.status===2||this.props.status===1? "none":'block'}} - data-tip-down="新增代码编辑类型的任务">+ 实践类型 - 4||this.props.identity===undefined||this.props.status===2||this.props.status===1?"none":'block'}} - data-tip-down="新增选择题类型的任务">+ 选择题类型 - -
    - -
    - -
  • - 本关任务 -
  • - -
  • - 评测设置 -
  • - -
  • - 参考答案 -
  • -
    - -

    - 请先上传本关任务的所有代码文件、标准图片等所有必要的文件到 - 版本库 -

    - - -
    -
    -

    学员任务文件

    -
    - * -
    - this.updatepath(e,"shixunfilepath",1)} - onClick={(e)=>this.getfilepath(e,"shixunfilepath",1)} - /> -

    该文件将直接显示给学生,需要学生在其中填写代码

    -
    -
    - 必填项 -
    -
    -
    -
    - -
    -
    - -
    - - this.saveselectpath(e)} - value={selectpath}/> -
    - - this.evaluationenter()}>确定 - this.evaluationhideModal()}>取消 -
    -
    -
    - -
    -
    -

    评测执行文件

    -
    - * -
    - this.updatepath(e,"shixunfilepathplay",1)} - onClick={(e)=>this.getfilepath(e,"shixunfilepathplay",1)} - /> -

    该文件由平台执行,用来测试平台学员代码是否正确

    -
    -
    - 必填项 -
    -
    -
    -
    - - -
    -
    -

    效果展现方式

    -
    - - this.showrepositoryurltip(1)}> -
    - -
    -

    - 图片:处理或输出图片类型的任务,请选填此项
    - 可以通过设置图片路径和学员答案文件路径,展示代码对应的图片效果

    - apk/exe:写可执行文件的任务,请选填此项
    - 可以通过设置学员答案文件路径,展示二维码以供扫码下载

    - txt:输出txt文档类型的任务,请选填此项
    - 可以通过学员答案文件路径设置,展示txt文件内容

    - html:web类型的任务,请选填此项
    - 可以通过Web路由设置,展示html效果预览页 -

    -

    this.showrepositoryurltip(2)} - >知道了 -

    -
    -
    -

    该选项用来配置学员评测本关任务时,查看效果页上需要展现的文件类型

    -
    -
    - - {pathoptionvalue===4&&web_route!=null||pathoptionvalue===4&&has_web_route===true?
    -
    -

    Web路由

    -
    -
    - this.updatewebroute(e)} - placeholder="网站类型实训,请填写Web路由地址。例:java/mypage"/> -
    -
    -
    -
    :""} - - {pathoptionvalue===1||pathoptionvalue===5||pathoptionvalue===6?
    -
    -

    待处理文件路径

    -
    -
    - this.updatepath(e,"shixunfileexpectpicturepath",2)} - onClick={(e)=>this.getfilepath(e,"shixunfileexpectpicturepath",2)} - /> -

    - 该路径下的文件将在学员评测本关任务时,作为原始文件显示在查看效果页,供学员参考;任务为文件处理时请指定该路径,并注意与程序文件所在文件夹分开。 -

    -
    -
    -
    -
    -
    :""} - - - {pathoptionvalue===1||pathoptionvalue===5||pathoptionvalue===6?
    -
    -

    标准答案文件路径

    -
    -
    - this.updatepath(e,"shixunfilestandardpicturepath",2)} - onClick={(e)=>this.getfilepath(e,"shixunfilestandardpicturepath",2)} - /> -

    - 该路径下的文件将在学员评测本关任务时,作为参考答案显示在查看效果页,供学员参考;任务输出结果为文件时请指定该路径,并注意与程序文件所在文件夹分开。 -

    -
    -
    -
    -
    -
    :""} - - - {pathoptionvalue===-1?"":
    -
    -

    学员答案文件路径

    -
    -
    - this.updatepath(e,"shixunfilepicturepath",2)} - onClick={(e)=>this.getfilepath(e,"shixunfilepicturepath",2)} - placeholder="请在版本库中指定用来保存学员代码实际输出结果的路径。例:src/step1/outputfiles"/> -

    - 学员评测本关任务时生成的文件将保存在该路径下,并作为实际输出显示在查看效果页,供学员确认;任务输出结果为文件时请指定该路径,并注意与程序文件所在文件夹分开。 -

    -
    -
    -
    -
    -
    } - - -
    -
    - {/*

    测试集

    */} -

    测试集和系统评分规则

    -

    - 得分规范: - - - 通过全部测试集 - (学员评测,仅当所有测试集都正确时,才获得一次性奖励) - - - 通过部分测试集 - (学员评测,当至少有一组测试集正确时,即可获得其对应比例的奖励) - -

    - -

    - - 系统评分占比: - - 均分比例 - 自定义比例 - - - -

    - -
    -
    - -
    - - {evaluationlist===undefined?"":evaluationlist.length===0?"":evaluationlist.map((item,key)=>{ - return( -
    -

    - * - 组{key+1} - - {/*checked={item.is_public===1?false:true}*/} - - this.editpercentage(e,key)} - value={item.score} /> - % - - - this.evaluationonChange(item.hidden,key)} checked={item.hidden===1?true:false}>隐藏 - - - - this.del_test_array(key)}> - - - -

    - - -
    - 匹配规则: - this.changeEvaluationRule(e,key)}> - 完全匹配 - 末尾匹配 - -
    -
    - ) - })} - -
    -
    - -

    - - 新增测试集 - -

    -

    温馨提示:建议公开测试集和隐藏测试集结合使用,降低作弊的几率;隐藏测试集,在“提交评测”时也将被自动检测

    -
    -
    -
    - - -
    4||this.props.identity===undefined||power===false?"none":"block"}}> - 提交 - 取消 -
    - - -
    -
    - ) - } -} - - diff --git a/public/react/src/tpm/challengesnew/TPMquestion.js b/public/react/src/tpm/challengesnew/TPMquestion.js deleted file mode 100644 index b76e17b17..000000000 --- a/public/react/src/tpm/challengesnew/TPMquestion.js +++ /dev/null @@ -1,1052 +0,0 @@ -import React, {Component} from 'react'; - -import {Input, Select, Radio, Checkbox, Popconfirm, message, Modal,Tooltip,notification} from 'antd'; - -import {BrowserRouter as Router, Route, Link, Switch} from "react-router-dom"; - -// import "antd/dist/antd.css"; - -import axios from 'axios'; - -import { getImageUrl, toPath } from 'educoder'; - -import './css/TPMchallengesnew.css'; - -import {getUrl} from 'educoder'; - -import TpmQuestionMain from './TpmQuestionMain'; - -import TpmQuestionNew from './TpmQuestionNew'; - -import TpmQuestionEdit from './TpmQuestionEdit'; - -let origin = getUrl(); - -let path = getUrl("/editormd/lib/") - -const $ = window.$; - -const Option = Select.Option; - -const RadioGroup = Radio.Group; - -var letterArr = []; -for (var i = 65, j = 0; i < 91; i++, j++) { - letterArr[j] = String.fromCharCode(i); -} - - -export default class TPMquestion extends Component { - constructor(props) { - super(props) - this.contentMdRef = React.createRef(); - this.newquestioMDMdRef = React.createRef(); - this.newquestioMDMdCont=React.createRef(); - this.neweditanswerRef=React.createRef(); - this.editanswersRef=React.createRef(); - this.state = { - choice_url: undefined, - practice_url: undefined, - go_back_url: undefined, - position: undefined, - task_pass_default: undefined, - submit_url: undefined, - questionsInputvalue:undefined, - questionaddsum:0, - questionaddarray:[], - questionaddtype:true, - activetype:"", - questionlists:[{str:"A",val:"",type:false},{str:"B",val:"",type:false},{str:"C",val:"",type:false},{str:"D",val:"",type:false}], - answeshixunsGroup: 1, - answeoptions:[10,20], - answeonshixunsmark:10, - shixunssanswerkillvalue:"", - shixunsskillanswerlist:[], - challenge_id:"", - challenge_choose_id:undefined, - questionlistss:[], - newcnttype:false, - newquestioMDvaluetype:false, - challenge_tagtype:false, - editquestionaddtype:false, - mancheckpointId:undefined, - power:false, - questionInputvaluetype:false, - questioMD:"", - standard_answer:"", - subject:"", - newquestioMDvaluetypes:false, - questionInputvaluetypes:false, - prev_challenge:undefined, - next_challenge:undefined, - newcnttypesum:1, - marktype:false, - answer:"", - sumittype:false - } - } - - - questionInputvalue=(e)=>{ - this.setState({ - questionsInputvalue: e.target.value - }) - } - - componentDidMount() { - if(this.props.status===2){ - - } - let id = this.props.match.params.shixunId; - let checkpointId=this.props.match.params.checkpointId; - - this.setState({ - mancheckpointId:id, - }) - - let newchoice_url= "/shixuns/"+id+"/challenges/newquestion"; - let newpractice_url= "/shixuns/"+id+"/challenges/new"; - let newgo_back_url="/shixuns/"+id+"/challenges"; - - if(this.props.match.params.choose_id===undefined){ - if(checkpointId===undefined){ - //新建模式 - let nurl = "/shixuns/" + id + "/challenges/new.json" - axios.get(nurl).then((response) => { - - if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { - - }else { - this.setState({ - choice_url: newchoice_url, - practice_url: newpractice_url, - go_back_url: newgo_back_url, - position: response.data.position, - task_pass_default: response.data.task_pass_default, - submit_url: response.data.submit_url, - power:true, - activetype:"first", - - }) - if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { - this.setState({ - contentMdRefval:"" - }) - } else { - - this.setState({ - contentMdRefval:response.data.task_pass_default - }) - this.contentMdRef.current.setValue(response.data.task_pass_default || '') - } - this.shixunsautoHeight() - } - - }).catch((error) => { - console.log(error) - }); - - }else{ - //编辑模式 - let url = "/shixuns/"+ id +"/challenges/"+checkpointId+"/edit.json?st=1" - axios.get(url).then((response) => { - if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { - - }else { - let newprev_challenge=response.data.prev_challenge; - let next_challenge=response.data.next_challenge; - if (newprev_challenge != undefined) { - if(newprev_challenge.st===0){ - newprev_challenge = "/shixuns/" + id + "/challenges/" + newprev_challenge.id + "/editcheckpoint"; - }else{ - newprev_challenge = "/shixuns/" + id + "/challenges/" + newprev_challenge.id + "/editquestion"; - } - } - if (next_challenge != undefined) { - if(next_challenge.st===0){ - next_challenge = "/shixuns/" + id + "/challenges/" + next_challenge.id+ "/editcheckpoint"; - }else{ - next_challenge = "/shixuns/" + id + "/challenges/" + next_challenge.id+ "/editquestion"; - } - } - this.setState({ - questionaddtype:false, - newquestionaddtype:false, - activetype:"first", - prev_challenge:newprev_challenge, - next_challenge:next_challenge, - questionsInputvalue:response.data.subject, - questionaddarray:response.data.chooses, - challenge_id:response.data.id, - mancheckpointId:checkpointId, - position: response.data.position, - choice_url: newchoice_url, - practice_url: newpractice_url, - go_back_url: newgo_back_url, - power:response.data.power, - // questioMD:response.data.task_pass, - answer:response.data.answer - - }) - - this.setState({ - contentMdRefval:response.data.task_pass - }) - // this.contentMdRef.current.setValue(response.data.task_pass || '') - if(response.data.chooses.length===0){ - // 新建选择题时,没法切回 ‘本关任务’ tab - // this.questionadd() - } - - this.shixunsautoHeight() - } - - }).catch((error) => { - console.log(error) - }); - } - - }else{ - $('html').animate({ - scrollTop:10 - }, 500); - - let{challenge_id} =this.state; - - let id = this.props.match.params.shixunId; - let url = "/shixuns/"+ id +"/challenges/"+checkpointId+"/edit.json?st=1" - axios.get(url).then((response) => { - if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { - - }else { - let newprev_challenge=response.data.prev_challenge; - let next_challenge=response.data.next_challenge; - if (newprev_challenge != undefined) { - if(newprev_challenge.st===0){ - newprev_challenge = "/shixuns/" + id + "/challenges/" + newprev_challenge.id + "/editcheckpoint"; - }else{ - newprev_challenge = "/shixuns/" + id + "/challenges/" + newprev_challenge.id + "/editquestion"; - } - } - if (next_challenge != undefined) { - if(next_challenge.st===0){ - next_challenge = "/shixuns/" + id + "/challenges/" + next_challenge.id+ "/editcheckpoint"; - }else{ - next_challenge = "/shixuns/" + id + "/challenges/" + next_challenge.id+ "/editquestion"; - } - } - this.setState({ - questionaddtype:false, - newquestionaddtype:false, - prev_challenge:newprev_challenge, - next_challenge:next_challenge, - questionsInputvalue:response.data.subject, - questionaddarray:response.data.chooses, - challenge_id:response.data.id, - mancheckpointId:checkpointId, - position: response.data.position, - choice_url: newchoice_url, - practice_url: newpractice_url, - go_back_url: newgo_back_url, - power:response.data.power, - // questioMD:response.data.task_pass, - - }) - - this.setState({ - contentMdRefval:response.data.task_pass - }) - // this.contentMdRef.current.setValue(response.data.task_pass || '') - if(response.data.chooses.length===0){ - this.questionadd() - } - this.shixunsautoHeight() - } - - }).catch((error) => { - console.log(error) - }); - - let zrl ='/shixuns/'+this.props.match.params.shixunId+'/challenges/'+this.props.match.params.checkpointId+'/edit_choose_question.json?choose_id='+this.props.match.params.choose_id; - axios.get(zrl).then((response) => { - if(response.status===200){ - let choose_contents=response.data.choose_contents; - let newchoose_contentslist=[] - for(var i=0; i { - }); - - } - } - - clickquestionsumit=()=>{ - this.setState({ - sumittype:true - }) - let checkpointId=this.props.match.params.checkpointId; - if(this.props.status===2&&checkpointId===undefined){ - this.props.showSnackbar("该实训已经发布不能新建") - this.setState({ - sumittype:false - }) - return - } - let {questionsInputvalue} =this.state; - // const exercise_editormdvalue = this.questio_editormd.getValue(); - const exercise_editormdvalue = this.contentMdRef.current.getValue().trim(); - let id = this.props.match.params.shixunId; - - if(questionsInputvalue===undefined||questionsInputvalue===null||questionsInputvalue===""){ - this.setState({ - questionInputvaluetype:true - }) - $('html').animate({ - scrollTop: 10 - }, 1000); - this.setState({ - sumittype:false - }) - return - } - - - if(exercise_editormdvalue===null ||exercise_editormdvalue===""){ - this.setState({ - questionInputvaluetypes:true - }) - $('html').animate({ - scrollTop: 500 - }, 1000); - this.setState({ - sumittype:false - }) - return - } - if(checkpointId===undefined){ - - let url = "/shixuns/" + id + "/challenges.json"; - - axios.post(url, { - identifier:id, - subject: questionsInputvalue, - task_pass: exercise_editormdvalue, - st: 1 - }).then((response) => { - this.props.showSnackbar(response.data.messages); - // if(response.data.status===1){ - // - // this.setState({ - // questionaddtype:false, - // challenge_id:response.data.challenge_id - // }) - // - // this.questionadd() - // } - window.location.href = '/shixuns/'+id+'/challenges/'+response.data.challenge_id+'/editquestion'; - - }).catch((error) => { - console.log(error) - }); - }else{ - let url ="/shixuns/"+id+"/challenges/"+checkpointId+".json"; - axios.put(url, { - tab:0, - subject: questionsInputvalue, - task_pass: exercise_editormdvalue, - }).then((response) => { - if(response.data.status===1){ - this.setState({ - questionaddtype:false, - challenge_id:response.data.challenge_id - }) - } - $('html').animate({ - scrollTop: 10 - }, 200); - this.props.showSnackbar(response.data.messages); - window.location.href = '/shixuns/'+id+'/challenges/'+response.data.challenge_id+'/editquestion'; - }).catch((error) => { - console.log(error) - }); - } - - } - - questionall=()=>{ - // this.setState({ - // activetype:"first", - // newquestionaddtype:false, - // editquestionaddtype:false, - // questionaddtype:false - // }) - - window.location.href = '/shixuns/'+this.props.match.params.shixunId+'/challenges/'+this.props.match.params.checkpointId+'/editquestion'; - } - questionadd=()=>{ - - let{questionaddarray}=this.state; - - let questionaddsums=questionaddarray.length; - - if(questionaddsums-1>9){ - this.props.showSnackbar("选择题目最大支持设置9道题") - return - } - - let questionaddarrays=questionaddarray; - - questionaddarrays.map((item,key)=>{ - if(item.choose_id===0){ - questionaddarrays.splice(key,1) - } - }) - - questionaddarrays.push({type:0,choose_id:0}); - this.setState({ - activetype:0, - questionaddarray:questionaddarrays, - questionaddtype:true, - editquestionaddtype:false, - newquestionaddtype:true, - questionlists:[{str:"A",val:"",type:false},{str:"B",val:"",type:false},{str:"C",val:"",type:false},{str:"D",val:"",type:false}], - answeshixunsGroup: 1, - answeoptions:[10,20], - answeonshixunsmark:10, - shixunssanswerkillvalue:"", - shixunsskillanswerlist:[], - contentMdRefval:"", - newquestioMDMdContval:"", - }) - - - setTimeout(() => { - this.newquestioMDMdRef.current.setValue('') - }, 1000) - setTimeout(() => { - this.newquestioMDMdCont.current.setValue('') - }, 1500) - // this.shixunsautoHeight() - } - - editquestionlists=(newquestionlists)=>{ - let newlist=newquestionlists; - let list=[] - for(var i=0; i{ - let{questionlists} = this.state; - let newquestionlists=questionlists; - let newli={str:letterArr[questionlists.length],val:"",type:false}; - newquestionlists.push(newli); - this.editquestionlists(newquestionlists); - } - - - delquestionlists=(key)=>{ - let{questionlists} = this.state; - let newquestionlists=questionlists; - newquestionlists.splice(key,1); - for(var i=0; i{ - let{questionlists} = this.state; - let newquestionlists=questionlists; - if(newquestionlists[key].type===true){ - newquestionlists[key].type=false; - }else if(newquestionlists[key].type===false){ - newquestionlists[key].type=true; - } - - this.editquestionlists(newquestionlists); - } - - onshixunGroupanswe=(e)=> { - let optionsum; - let onshixunsmark; - if(e.target.value===1){ - optionsum=[10,20]; - onshixunsmark=10; - }else if(e.target.value===2){ - optionsum=[30,40,50,60]; - onshixunsmark=30; - }else if(e.target.value===3){ - optionsum=[70,80,90,100] - onshixunsmark=70; - } - this.setState({ - answeshixunsGroup: e.target.value, - answeoptions:optionsum, - answeonshixunsmark:onshixunsmark - }) - } - shixunssanswerkill = (e) => { - this.setState({ - shixunssanswerkillvalue: e.target.value - }) - - } - clickshixunsanswerskill = () => { - - let {shixunssanswerkillvalue, shixunsskillanswerlist} = this.state; - if (shixunssanswerkillvalue === "") { - return - } else if (shixunssanswerkillvalue === undefined) { - return - } - - if(shixunssanswerkillvalue == "" || shixunssanswerkillvalue == undefined || shixunssanswerkillvalue == null || (shixunssanswerkillvalue.length>0 && shixunssanswerkillvalue.trim().length == 0)){ - message.error("输入为空,不能保存!"); - return - } - - let list = shixunsskillanswerlist; - list.push(shixunssanswerkillvalue); - this.setState({ - shixunsskillanswerlist: list, - shixunssanswerkillvalue: "" - }) - } - delshixunssnswerllist=(key)=>{ - let {shixunsskillanswerlist} = this.state; - let newshixunsskillanswerlist = shixunsskillanswerlist; - newshixunsskillanswerlist.splice(key, 1); - this.setState({ - shixunsskillanswerlist: newshixunsskillanswerlist - }) - } - onInputoquestionption=(e,key)=>{ - - $.fn.autoHeight = function(){ - function autoHeight(elem){ - elem.style.height = 'auto'; - elem.style.maxHeight = '140px'; - elem.scrollTop = 0; //防抖动 - elem.style.height = elem.scrollHeight + 'px'; - } - this.each(function(){ - autoHeight(this); - $(this).on('keyup', function(){ - autoHeight(this); - }); - }); - } - $("#"+e.target.id).autoHeight(); - - let {questionlists}=this.state; - let newquestionlists=questionlists; - newquestionlists[key].val=e.target.value; - this.editquestionlists(newquestionlists); - } - - onshixunsansweSelect=(value)=>{ - this.setState({ - answeonshixunsmark: value - }) - } - - answer_subit=(sumtype,challenge_choose_id)=>{ - $('html').animate({ - scrollTop:10 - }, 500); - - let {challenge_id,questionlists,shixunsskillanswerlist,answeonshixunsmark,answeshixunsGroup,questionaddarray} =this.state; - if(challenge_id===undefined){ - message.error("关卡id为空"); - return - } - let newquestionlists=questionlists; - let newlist=""; - let newtype=[]; - let newcnt=[]; - let list=0; - for(var i=0; i { - console.log(error) - }); - }else{ - - let newquestioMDvalue = this.newquestioMDMdRef.current.getValue().trim(); - if(newquestioMDvalue===""||newquestioMDvalue==="请输入选择题的题干内容"){ - this.setState({ - newquestioMDvaluetype:true, - }) - $('html').animate({ - scrollTop:100 - }, 200); - message.error("题干为空"); - return - } - let newnewanswerMDvalue = this.newquestioMDMdCont.current.getValue().trim(); - - if(newnewanswerMDvalue===""||newnewanswerMDvalue===" "){ - newnewanswerMDvalue=undefined - } - url="/shixuns/" + id + "/challenges/" + challenge_id + "/create_choose_question.json"; - axios.post(url, { - challenge_choose: {subject: newquestioMDvalue, answer: newnewanswerMDvalue, standard_answer:newlist , score: answeonshixunsmark, difficult: answeshixunsGroup}, - challenge_tag: shixunsskillanswerlist, - question: {cnt: newcnt}, - choice: {answer: newtype} - }).then((response) => { - - let questionaddsums=questionaddarray.length; - let questionaddarrays=questionaddarray; - questionaddarrays[questionaddsums-1].choose_id=response.data.challenge_choose_id; - if(newlist.length===1){ - questionaddarrays[questionaddsums-1].type=1; - }else if(newlist.length>1){ - questionaddarrays[questionaddsums-1].type=2; - } - this.setState({ - challenge_choose_id:response.data.challenge_choose_id, - questionaddtype:false, - editquestionaddtype:false, - newquestioMDvaluetype:false, - newquestioMDvaluetypes:false, - questionaddarray:questionaddarrays - }) - $('html').animate({ - scrollTop: 10 - }, 200); - - notification.open({ - message: '提示', - description: - '新建成功,请点击右侧加号继续添加', - }); - window.location.href=`/shixuns/${id}/challenges/${checkpointId}/editquestion/${response.data.challenge_choose_id}`; - - // this.getanswer_subitlist() - // this.gochooseid("/shixuns/"+this.props.match.params.shixunId+"/challenges/"+this.props.match.params.checkpointId+"/editquestion"+"/"+response.data.challenge_choose_id) - }).catch((error) => { - console.log(error) - }); - } - - - } - - questionlist=(key,challenge_choose_id,type)=>{ - $('html').animate({ - scrollTop:10 - }, 500); - - let{challenge_id} =this.state; - - if(challenge_choose_id===""||type===0){ - - - // this.neweditanswerRef.current.setValue('') - // this.editanswersRef.current.setValue('') - this.setState({ - activetype:challenge_choose_id, - editquestionaddtype:true, - questionaddtype:true, - newquestionaddtype:false, - questionlists:[{str:"A",val:"",type:false},{str:"B",val:"",type:false},{str:"C",val:"",type:false},{str:"D",val:"",type:false}], - answeshixunsGroup: 1, - answeoptions:[10,20], - answeonshixunsmark:10, - shixunssanswerkillvalue:"", - shixunsskillanswerlist:[], - neweditanswerRefval:'', - editanswersRefval:'' - }) - this.newquestioMDMdRef.current.setValue('') - this.newquestioMDMdCont.current.setValue('') - }else{ - let id = this.props.match.params.shixunId; - let url ='/shixuns/'+id+'/challenges/'+challenge_id+'/edit_choose_question.json?choose_id='+challenge_choose_id; - axios.get(url).then((response) => { - if(response.status===200){ - let choose_contents=response.data.choose_contents; - let newchoose_contentslist=[] - for(var i=0; i { - }); - - } - - } - - shixunsautoHeight=()=>{ - $.fn.autoHeight = function(){ - function autoHeight(elem){ - elem.style.height = 'auto'; - elem.style.maxHeight = '140px'; - elem.scrollTop = 0; //防抖动 - if(elem.scrollHeight===0){ - elem.style.height = 62 + 'px'; - }else{ - - elem.style.height = elem.scrollHeight + 'px'; - } - - } - this.each(function(){ - autoHeight(this); - $(this).on('keyup', function(){ - autoHeight(this); - }); - }); - } - $('textarea[autoHeight]').autoHeight(); - } - - gochooseid=(url)=>{ - window.location.href =url - // window.location.Reload(url) - // this.props.history.replace( url ); - // this.props.history.push( url ); - // 返回 - // this.props.history.goBack(); - } - - render() { - - let {choice_url, - practice_url, - go_back_url, - position, - answeoptions, - questionaddarray, - questionaddtype, - activetype, - newquestionaddtype, - editquestionaddtype, - challenge_choose_id, - prev_challenge, - next_challenge, - answer, - - } = this.state; - - let options; - - - options = answeoptions.map((d, k) => { - return ( - - ) - }) - - return ( - -
    - - -
    - - -
  • - 本关任务 -
  • -
    - - { - questionaddarray.length===0?"":questionaddarray.map((item,key)=>{ - return( -
  • this.questionlist(key,item.choose_id,item.type):""} - > - - { - item.choose_id!=0? - this.gochooseid("/shixuns/"+this.props.match.params.shixunId+"/challenges/"+this.props.match.params.checkpointId+"/editquestion"+"/"+item.choose_id)}> - {key+1}.{item.type===2?"多选题":item.type===1?"单选题":'选择题'} - :activetype==="first"?"":{key+1}.{item.type===2?"多选题":item.type===1?"单选题":'选择题'} - } - -
  • - ) - }) - } - - -
  • - - + - -
  • - -
    - - {/*x选择题首页*/} - {activetype==="first"?this.questionInputvalue(e)} - clickquestionsumit={(e)=>this.clickquestionsumit(e)} - - />:""} - - {/*新建*/} - - {newquestionaddtype===true? - this.selquestionlists(key)} - onInputoquestionption={(e,key)=>this.onInputoquestionption(e,key)} - delquestionlists={(key)=>this.delquestionlists(key)} - addquestionlists={(e)=>this.addquestionlists(e)} - onshixunGroupanswe={(e)=>this.onshixunGroupanswe(e)} - onshixunsansweSelect={(e)=>this.onshixunsansweSelect(e)} - shixunssanswerkill={(e)=>this.shixunssanswerkill(e)} - clickshixunsanswerskill={(e)=>this.clickshixunsanswerskill(e)} - delshixunssnswerllist={(key)=>this.delshixunssnswerllist(key)} - answer_subit={()=>this.answer_subit()} - />:""} - - - {/*修改*/} - {editquestionaddtype===true? - this.selquestionlists(key)} - onInputoquestionption={(e,key)=>this.onInputoquestionption(e,key)} - delquestionlists={(key)=>this.delquestionlists(key)} - addquestionlists={(e)=>this.addquestionlists(e)} - onshixunGroupanswe={(e)=>this.onshixunGroupanswe(e)} - onshixunsansweSelect={(e)=>this.onshixunsansweSelect(e)} - shixunssanswerkill={(e)=>this.shixunssanswerkill(e)} - clickshixunsanswerskill={(e)=>this.clickshixunsanswerskill(e)} - delshixunssnswerllist={(key)=>this.delshixunssnswerllist(key)} - answer_subit={()=>this.answer_subit("edit",challenge_choose_id)} - /> - :""} - -
    -
    - ) - } -} - diff --git a/public/react/src/tpm/challengesnew/TpmQuestionEdit.js b/public/react/src/tpm/challengesnew/TpmQuestionEdit.js deleted file mode 100644 index d0e6f98bd..000000000 --- a/public/react/src/tpm/challengesnew/TpmQuestionEdit.js +++ /dev/null @@ -1,229 +0,0 @@ -import React, {Component} from 'react'; - -import {Input, Select, Radio, Checkbox, Popconfirm, message, Modal,Tooltip} from 'antd'; - -import {BrowserRouter as Router, Route, Link, Switch} from "react-router-dom"; - -import axios from 'axios'; - -import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor'; - -const Option = Select.Option; - -const RadioGroup = Radio.Group; - -export default class TpmQuestionEdit extends Component { - constructor(props) { - super(props) - this.state = { - - } - } - - componentDidMount() { - - } - delecbtns=()=>{ - let url=`/shixuns/${this.props.match.params.shixunId}/challenges/${this.props.match.params.checkpointId}/destroy_challenge_choose.json`; - axios.delete((url), { data: { - choose_id:this.props.match.params.choose_id - }}) - .then((result)=>{ - if(result.data.status===1){ - window.location.href=`/shixuns/${this.props.match.params.shixunId}/challenges/${this.props.match.params.checkpointId}/editquestion`; - } - }) - } - - render() { - - return ( -
    - -
    -
    -

    题干

    -
    - * -
    - -
    - - -
    - 必填项 -
    - -
    -

    -

    -
    - { - this.props.questionlists===undefined||this.props.questionlists.length===0?"":this.props.questionlists.map((item,key)=>{ - return( -
  • - - - this.props.delquestionlists(key)}> - - -
  • - ) - }) - } -

    - this.props.addquestionlists()} - className="fl edu-default-btn edu-greyline-btn mb20 option_icon_add">新增选项 - - - {this.props.newcnttypesum===0?"请选择答案":"选项内容不能为空"} - -

    -
  • - - -
  • -
    -
    -
    - - -
    -
    -

    参考答案

    -
    -
    - -
    -
    - 必填项 -
    -
    -

    -

    -
    -
    - -
    -

    难度系数

    -
    - - this.props.onshixunGroupanswe(e)}> - 简单 - 中等 - 困难 - - -
    -

    奖励经验值

    -
    - * - - -

    - 如果学员答题错误,则不能得到相应的经验值
    - 如果学员成功得到经验值,那么将同时获得等值的金币奖励,如:+10经验值、+10金币 -

    - - 必填项 -
    -
    - -
    -

    技能标签

    -
    - * -
    - this.props.shixunssanswerkill(e)} - value={this.props.shixunssanswerkillvalue} - onPressEnter={(e)=>this.props.clickshixunsanswerskill(e)} - onBlur={(e)=>this.props.clickshixunsanswerskill(e)} - /> - {/*+ 添加*/} -
    学员答题正确将获得技能,否则不能获得技能 - - 必填项 - -
    -
    - - { - this.props.shixunsskillanswerlist.length === 0 ? "" : this.props.shixunsskillanswerlist.map((itme, key) => { - return ( -
  • {itme} - this.props.delshixunssnswerllist(key)}>× -
  • - ) - }) - } - - -
    - -
    - -
    -
    - - -
    4||this.props.identity===undefined||this.props.power===false?"none":"block"}}> - this.props.answer_subit()}>提交 - 取消 - - this.delecbtns()} - className="delectshixuncdbtn fr">删除 -
    - -
    - - ) - } -} - - - diff --git a/public/react/src/tpm/challengesnew/TpmQuestionMain.js b/public/react/src/tpm/challengesnew/TpmQuestionMain.js deleted file mode 100644 index 614842ab8..000000000 --- a/public/react/src/tpm/challengesnew/TpmQuestionMain.js +++ /dev/null @@ -1,84 +0,0 @@ -import React, {Component} from 'react'; - -import {BrowserRouter as Router, Route, Link, Switch} from "react-router-dom"; -import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor'; - - -export default class TpmQuestionMain extends Component { - constructor(props) { - super(props) - this.state = { - - } - } - - componentDidMount() { - - } - - - render() { - return ( -
    -
    -
    -

    任务名称

    -
    - * -
    - -
    -
    - 必填项 -
    -
    -
    -
    - - -
    -

    过关任务

    -
    - * -
    - -
    -
    - 必填项 -
    -
    -

    -

    -
    - - -
    4 || this.props.identity === undefined || this.props.power === false ? "none" : "block"}}> - 提交 - 取消 - -
    - -
    - - ) - } - } - - - diff --git a/public/react/src/tpm/challengesnew/TpmQuestionNew.js b/public/react/src/tpm/challengesnew/TpmQuestionNew.js deleted file mode 100644 index 861c4f879..000000000 --- a/public/react/src/tpm/challengesnew/TpmQuestionNew.js +++ /dev/null @@ -1,219 +0,0 @@ -import React, {Component} from 'react'; - -import {Input, Select, Radio, Checkbox, Popconfirm, message, Modal,Tooltip} from 'antd'; - -import {BrowserRouter as Router, Route, Link, Switch} from "react-router-dom"; - -import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor'; - -const Option = Select.Option; - -const RadioGroup = Radio.Group; - -export default class TpmQuestionNew extends Component { - constructor(props) { - super(props) - this.state = { - - } - } - - componentDidMount() { - - } - - - render() { - // console.log( this.props.questionlists) - return ( -
    - -
    -
    -

    题干

    -
    - * -
    - -
    - - -
    - 必填项 -
    - -
    -

    -

    - - -
    - { - this.props.questionlists===undefined||this.props.questionlists.length===0?"":this.props.questionlists.map((item,key)=>{ - return( -
  • - - - this.props.delquestionlists(key)}> - - - - - -
  • - ) - }) - } -

    - this.props.addquestionlists()} - className="fl edu-default-btn edu-greyline-btn mb20 option_icon_add">新增选项 - - - {this.props.newcnttypesum===0?"请选择答案":"选项内容不能为空"} - -

    -
  • - - -
  • -
    -
    -
    - - -
    -
    -

    参考答案

    -
    -
    - -
    -
    - 必填项 -
    -
    -

    -

    - -
    -
    - -
    -

    难度系数

    -
    - - this.props.onshixunGroupanswe(e)} - > - 简单 - 中等 - 困难 - - -
    -

    奖励经验值

    -
    - * - - - -

    - 如果学员答题错误,则不能得到相应的经验值
    - 如果学员成功得到经验值,那么将同时获得等值的金币奖励,如:+10经验值、+10金币 -

    - - 必填项 -
    -
    - -
    -

    技能标签

    -
    - * -
    - this.props.shixunssanswerkill(e)} - value={this.props.shixunssanswerkillvalue} - onPressEnter={(e)=>this.props.clickshixunsanswerskill(e)} - onBlur={(e)=>this.props.clickshixunsanswerskill(e)} - /> - {/*+ 添加*/} -
    学员答题正确将获得技能,否则不能获得技能 - - 必填项 - -
    -
    - - { - this.props.shixunsskillanswerlist.length === 0 ? "" : this.props.shixunsskillanswerlist.map((itme, key) => { - return ( -
  • {itme} - this.props.delshixunssnswerllist(key)}>× -
  • - ) - }) - } - - -
    - -
    - -
    -
    - - -
    4||this.props.identity===undefined||this.props.power===false?"none":"block"}}> - 提交 - 取消 -
    - -
    - - ) - } -} - - - diff --git a/public/react/src/tpm/challengesnew/css/TPMchallengesnew.css b/public/react/src/tpm/challengesnew/css/TPMchallengesnew.css deleted file mode 100644 index 37a65ef97..000000000 --- a/public/react/src/tpm/challengesnew/css/TPMchallengesnew.css +++ /dev/null @@ -1,269 +0,0 @@ -.CodeMirror-scroll { - overflow: auto !important; - margin-bottom: -30px; - margin-right: -30px; - padding-bottom: 30px; - height: 100%; - outline: none; - position: relative; -} -a.white-btn.orange-btn:hover { - border: 1px solid #F06200; - color: #FFF !important; -} -.flex1 a.white-btn.orange-btn:hover { - border: 1px solid #F06200; - color: #FFF !important; -} - -/*.challenge_nav li a{*/ - /*color:#000 !important;*/ -/*}*/ - -.questionli{ - width: 95%; - margin-left: 37px; -} -#directory_file{ - height:200px; - overflow-y:auto; - background:#f5f5f5; - padding:10px; -} -.directory_filepath{ - width:120px; - text-align:left; -} - -a{ - text-decoration: none; - color: #05101a; -} -.repository_url_tippostion{ - position: absolute; - left: 22%; - width: 500px; - top: 100%; -} - -.top-black-trangleft { - display: block; - border-width: 8px; - position: absolute; - top: -16px; - /* right: 4px; */ - border-style: dashed solid dashed dashed; - border-color: transparent transparent rgba(5,16,26,0.6) transparent; - font-size: 0; - line-height: 0; -} - -#exercisememoMD .CodeMirror { - margin-top: 31px !important; - height: 370px !important; - /*width: 579px !important;*/ -} - -#exercisememoMD .editormd-preview { - top: 40px !important; - height: 370px !important; - width: 578px !important; -} -#exercisememoMD{ - /*height: 700px !important;*/ -} -#questioMD{ - /*width: 95% !important;*/ - height: 417px !important; - margin-left: 0% !important; -} - - -#questioMD .CodeMirror { - /*width: 550.5px !important;*/ - margin-top: 31px !important; - height: 374px !important; -} - -#questioMD .editormd-preview { - top: 40px !important; - height: 375px !important; - width: 550px !important; -} - -#newquestioMD .CodeMirror { - /*width: 549px !important;*/ - margin-top: 31px !important; - height: 364px !important; -} - -#newquestioMD .editormd-preview { - top: 40px !important; - height: 364px !important; - width: 578px !important; -} - -#challenge_choose_answer .CodeMirror { - margin-top: 31px !important; - height: 364px !important; - /*width: 578px !important;*/ -} - - -#challenge_choose_answer .editormd-preview { - top: 40px !important; - height: 364px !important; - width: 578px !important; -} - -#neweditanswer .CodeMirror { - margin-top: 31px !important; - height: 364px !important; - /*width: 549.5px !important;*/ -} - -#neweditanswer .editormd-preview { - top: 40px !important; - height: 364px !important; - width: 551px !important; -} - -#repository_url_tip { - top: 30px !important; - left: 249px !important; - width: 292px !important; -} - -#editanswers .CodeMirror{ - /*width: 548px !important;*/ - height: 358px !important; - margin-top: 30px !important; -} -#editanswers .editormd-preview{ - width: 578px !important; - height: 358px !important; - -} -#newquestioMDs .CodeMirror{ - /*width: 510px !important;*/ - height: 358px !important; - margin-top: 30px !important; -} - -#newquestioMDs .editormd-preview{ - width: 578px !important; - height: 358px !important; -} - -.choose_names{ - width: 80px; - margin-left: 20px; -} - -#answerMD .CodeMirror{ - /*width: 569px !important;*/ - height: 600px !important; - margin-top: 30px !important; -} - -#answerMD .editormd-preview{ - width: 578px !important; - height: 600px !important; -} - -#answerMD { - height: 600px !important; -} - -.textareavalue{ - width: 100%; - padding: 5px; - - box-sizing: border-box; -} -.greyInput{ - width: 107%; -} -.greyInpus{ - width: 100%; -} - -.pdr20{ - padding-right:20px; -} - -.winput-240-40s { - background-color: #F5F5F5; -} - - -.winput-240-40s:focus{ - background-color: #fff; -} -.input-100-45{ - background-color: #F5F5F5; -} -.input-100-45:focus{ - background-color: #fff; - } - -.wind100{ - width:100% !important; -} - -.color-bule-tip { - color: #5485f7 !important; -} -.martopf4{ - margin-top:-4px; -} - -.headdfgf{ - display: block; - width: 100px; - height: 30px; - line-height: 30px; - float: left; -} - -.color979797{ - color: #979797 !important; -} - -.border-left{ - width: 0; - height: 0; - border-bottom: 6px solid transparent; - border-right: 6px solid #cccbcb; - border-top: 6px solid transparent; - position: absolute; - left: 30px; - top: 12px; -} -.border-left span{ - display: block; - width: 0; - height: 0; - border-bottom: 6px solid transparent; - border-right: 6px solid #fff; - border-top: 6px solid transparent; - position: absolute; - left: 1px; - top: -6px; - z-index: 10; -} -.fillTip{ - position: absolute; - left: 36px; - top: 2px; - width: 125px; - font-size: 12px; - display: block; - padding: 5px; - border: 1px solid #eaeaea; - border-radius: 5px; - box-sizing: border-box; - height: 32px; - line-height: 20px; - font-family: "微软雅黑","宋体"; -} \ No newline at end of file diff --git a/public/react/src/tpm/challengesnew/editorMD.js b/public/react/src/tpm/challengesnew/editorMD.js deleted file mode 100644 index 50f15b601..000000000 --- a/public/react/src/tpm/challengesnew/editorMD.js +++ /dev/null @@ -1,122 +0,0 @@ -import React, {Component} from 'react'; - -import {getUrl} from 'educoder'; - -let path = getUrl("/editormd/lib/"); - -const $ = window.$; - -function create_editorMD(id, width, high, placeholder, imageUrl, callback) { - var editorName = window.editormd(id, { - width: width, - height: high, - path: path, // "/editormd/lib/" - - syncScrolling: "single", - tex: true, - tocm: true, - emoji: true, - taskList: true, - codeFold: true, - searchReplace: true, - htmlDecode: "style,script,iframe", - sequenceDiagram: true, - autoFocus: false, - toolbarIcons: function () { - // Or return editormd.toolbarModes[name]; // full, simple, mini - // Using "||" set icons align right. - return ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "|", "testIcon", "testIcon1", '|', "image", "table", '|', "watch", "clear"] - }, - toolbarCustomIcons: { - testIcon: "
    ", - testIcon1: "
    " - }, - //这个配置在simple.html中并没有,但是为了能够提交表单,使用这个配置可以让构造出来的HTML代码直接在第二个隐藏的textarea域中,方便post提交表单。 - saveHTMLToTextarea: true, - // 用于增加自定义工具栏的功能,可以直接插入HTML标签,不使用默认的元素创建图标 - dialogMaskOpacity: 0.6, - placeholder: placeholder, - imageUpload: true, - imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp", "JPG", "JPEG", "GIF", "PNG", "BMP", "WEBP"], - imageUploadURL: imageUrl,//url - onload: function () { - // this.previewing(); - $("#" + id + " [type=\"latex\"]").bind("click", function () { - editorName.cm.replaceSelection("```latex"); - editorName.cm.replaceSelection("\n"); - editorName.cm.replaceSelection("\n"); - editorName.cm.replaceSelection("```"); - var __Cursor = editorName.cm.getDoc().getCursor(); - editorName.cm.setCursor(__Cursor.line - 1, 0); - }); - - $("#" + id + " [type=\"inline\"]").bind("click", function () { - editorName.cm.replaceSelection("`$$$$`"); - var __Cursor = editorName.cm.getDoc().getCursor(); - editorName.cm.setCursor(__Cursor.line, __Cursor.ch - 3); - editorName.cm.focus(); - }); - $("[type=\"inline\"]").attr("title", "行内公式"); - $("[type=\"latex\"]").attr("title", "多行公式"); - - window.md_elocalStorage(editorName, `MemoQuestion_${id}`, `${id}Question`); - - callback && callback() - } - }); - return editorName; -} - - -export default class TPMeditorMD extends Component { - constructor(props) { - super(props) - - } - componentDidMount() { - - - } - - questioMD=(initValue, id)=> { - - this.contentChanged = false; - const placeholder = ""; -// amp; -// 编辑时要传memoId - // const imageUrl = `/upload_with_markdown?container_id=&container_type=Memo`; - const imageUrl = `/api/attachments.json`; -// 创建editorMd - - let questio_editormd = create_editorMD(id, '100%', 400, placeholder, imageUrl, () => { - setTimeout(() => { - questio_editormd.resize() - questio_editormd.cm && questio_editormd.cm.refresh() - }, 500) - - if (initValue != undefined) { - questio_editormd.setValue(initValue) - } - questio_editormd.cm.on("change", (_cm, changeObj) => { - console.log('....contentChanged') - this.contentChanged = true; - }) - }); - this.questio_editormd = questio_editormd; - window.questio_editormd = questio_editormd; - - } - - componentWillReceiveProps(newProps) { - this.questioMD(newProps.value,newProps.id) - } - render() { - return ( -
    - -
    -
    -
    - ) - } -} diff --git a/public/react/src/tpm/component/TPMNav.js b/public/react/src/tpm/component/TPMNav.js deleted file mode 100644 index ff8f57aa5..000000000 --- a/public/react/src/tpm/component/TPMNav.js +++ /dev/null @@ -1,57 +0,0 @@ -import React, { Component } from 'react'; - -import { BrowserRouter as Router, Route, Link } from "react-router-dom"; - -class TPMNav extends Component { - - render() { - const { user, match, shixun, secret_repository } = this.props; - let isAdminOrCreator = false; - if (user) { - isAdminOrCreator = user.admin || user.manager - } - const shixunId = match.params.shixunId; - // const challengesPath = `/shixuns/${shixunId}/challenges`; - // console.log(this.props.propaedeutics) - const challengesPath = `/shixuns/${shixunId}/challenges`; - // console.log(match.path) - return ( -
    - 任务 - - { - this.props.propaedeutics===undefined?"":this.props.propaedeutics===false?"":背景知识 - } - - { this.props.identity >4||this.props.identity===undefined ?"":版本库} - {this.props.identity >4||this.props.identity===undefined ?"": secret_repository && 私密版本库} - - 合作者 - - 评论 - - 排行榜 - - {this.props.identity >2||this.props.identity===undefined?"":审核情况} - - 4||this.props.identity===undefined ? "none" : 'block'}} - >配置 -
    - ); - } -} - -export default TPMNav; diff --git a/public/react/src/tpm/component/TPMRightSection.js b/public/react/src/tpm/component/TPMRightSection.js deleted file mode 100644 index 4306fc6e3..000000000 --- a/public/react/src/tpm/component/TPMRightSection.js +++ /dev/null @@ -1,205 +0,0 @@ -import React, { Component } from 'react'; - -import { BrowserRouter as Router, Route, Link } from "react-router-dom"; - -import axios from 'axios'; - -import { getImageUrl,} from "educoder"; - -import './TPMright.css'; - -import {Icon,Tooltip} from 'antd'; - -// import "antd/dist/antd.css"; - -class TPMRightSection extends Component { - constructor(props) { - super(props) - this.state = { - - TPMRightSection:false, - clickNewsubscripttype:false - } - } - - // componentDidMount() { - // let id=this.props.match.params.shixunId; - // - // let shixunsDetailsURL=`/shixuns/`+id+`/show_right.json`; - // - // axios.get(shixunsDetailsURL).then((response)=> { - // if(response.status===200){ - // this.setState({ - // TPMRightSectionData: response.data - // }); - // } - // }).catch((error)=>{ - // console.log(error) - // }); - // } - - // shouldComponentUpdate(nextProps, nextState) { - // return nextProps.TPMRightSectionData !== this.state.TPMRightSectionData - // } - clickNewsubscript=(val)=>{ - if(val===0){ - this.setState({ - TPMRightSection:true, - clickNewsubscripttype:true - }) - }else{ - this.setState({ - TPMRightSection:false, - clickNewsubscripttype:false - }) - } - - } - render() { - let {TPMRightSection,clickNewsubscripttype}=this.state; - let {TPMRightSectionData}=this.props - - return ( -
    - { - TPMRightSectionData===undefined?"": -
    -
    -

    创建者

    -
    - - 头像 - -
    - -

    {TPMRightSectionData===undefined?"":TPMRightSectionData.creator===undefined?"":TPMRightSectionData.creator.name}

    -
    - 发布 {TPMRightSectionData.user_shixuns_count} - {/*粉丝 {TPMRightSectionData.fans_count}*/} - {/* 取消关注 */} -
    - -
    -
    -
    - { - TPMRightSectionData === undefined ? "" :TPMRightSectionData.tags===undefined?"": TPMRightSectionData.tags.length === 0 ? "" : -
    -

    技能标签 {TPMRightSectionData.tags.length}

    -
    -
    - { TPMRightSectionData.tags.map((item,key)=>{ - return( - {item.tag_name} - )}) - } -
    -
    - - -
    15&&clickNewsubscripttype===false?"newsubscript mb9 color-grey-9":"newsubscript mb9 color-grey-9 none"} - - data-tip-down="显示全部" - onClick={()=>this.clickNewsubscript(0)}>... -
    - - -
    this.clickNewsubscript(1)}> -
    - -
    - - - } - - -
    -

    所属课程

    -
    - { - TPMRightSectionData===undefined?"":TPMRightSectionData.paths===undefined?"":TPMRightSectionData.paths.map((i,k)=>{ - - return( - -
    - - - 实训 - - -
    - {i.name} -

    - - - {i.stages_count} - - - {/**/} - {/*{i.score_count}*/} - {/**/} - - - {i.members_count} - - -

    -
    - -
    - - ) - }) - } -
    -
    - - {TPMRightSectionData === undefined?"":TPMRightSectionData.paths===undefined?"":TPMRightSectionData.paths.length === 0 ? "" : - this.props.user&&this.props.user.main_site===true?
    -

    推荐实训

    -
    - { - TPMRightSectionData===undefined?"":TPMRightSectionData.recommands===undefined?"":TPMRightSectionData.recommands.map((item,key)=>{ - return( -
    - - 69?1526971094 - -
    - - {item.name} - -

    - {item.stu_num} 人学习 -

    -

    {item.level}

    -
    -
    - ) - }) - } -
    -
    :"" - } -
    - } -
    - ) - - - } -} - -export default TPMRightSection; diff --git a/public/react/src/tpm/component/TPMright.css b/public/react/src/tpm/component/TPMright.css deleted file mode 100644 index c664d75c4..000000000 --- a/public/react/src/tpm/component/TPMright.css +++ /dev/null @@ -1,79 +0,0 @@ -/*b新标签*/ -.newedu-filter-btn{ - display: block; - float: left; - padding: 0 9px; - /*height: 28px;*/ - line-height: 28px; - border-radius: 14px; - background-color: #F5F5F5; - color: #666; - margin-right: 10px; - margin-bottom: 9px; -} -.newedbox{ - /*flex-wrap: wrap;*/ - /*display: -webkit-flex; !* Safari *!*/ - /*display: flex;*/ - width: 360px; - position:relative; - overflow: hidden; -} -.newsubscript{ - position: absolute; - right: 23px; - bottom: 16px; - cursor: pointer; -} -.newsubscript:hover{ - color:deepskyblue; -} -.edu-filter-btn29BD8B{ - display: block; - float: left; - padding: 0 9px; - height: 28px; - line-height: 28px; - border-radius: 14px; - background-color: #29BD8B; - color: #FFF; - margin-right: 10px; - margin-bottom: 9px; -} -.relative{ - position:relative; -} -.newedboxheight{ - max-height: 177px; - overflow-y: hidden; -} -.newminheight{ - /*max-height: 670px;*/ - max-height: 300px; - overflow-y: auto; -} - -.delSubentry{ - font-size:7px; - font-family:MicrosoftYaHei; - font-weight:400; - color:rgba(76,172,255,1); - line-height:9px; - cursor: pointer; -} -.operationalter .delSubentry{ - font-size:15px !important; - line-height: 25px; -} -/*临时的tpi关闭按钮样式*/ -.headerRight a { - color: #1a3f5f; -} -/*实训做成弹窗a标签样式调整*/ -.-task-list-title a:link, .-task-list-title a:visited {color: #bcc6cd;} -.-task-list-title a:hover{ - color: #459be5; -} -.headerLeft .-header-right{ - height: 32px; -} \ No newline at end of file diff --git a/public/react/src/tpm/component/modal/RepositoryChooseModal.js b/public/react/src/tpm/component/modal/RepositoryChooseModal.js deleted file mode 100644 index 4b72ae2bd..000000000 --- a/public/react/src/tpm/component/modal/RepositoryChooseModal.js +++ /dev/null @@ -1,153 +0,0 @@ -// import React, { useState, useEffect, memo } from 'react'; -// import axios from 'axios' -// import { Modal, Input } from 'antd'; - -// function RepositoryChooseModal(props) { -// const [trees, setTrees] = useState([]) -// const [path, setPath] = useState('') -// const [pathArray, setPathArray] = useState([{val: "根目录/", path: ""}]) -// const [modalVisible, setModalVisible] = useState(true) - -// useEffect(() => { -// repository('') -// }, []) -// function onOk() { - -// } -// function onCancel() { - -// } -// /** -// 点nav 会传入key -// 点item 会传入 newPath - -// item => name, type type tree/leaf -// */ -// const repository=(item, key, newPath)=>{ -// let newPathArray = [] // -// // -// if (key) { -// for(var i=0; i<=key; i++){ -// newPathArray.push(pathArray[i]) -// } -// } else if (item) { -// newPathArray = pathArray.slice(0) -// newPathArray.push({val: item.name, path: pathArray[pathArray.length - 1] + "/" + item.name}) -// } - -// const path = item || key ? newPathArray[newPathArray.length - 1] : '' - -// let id = props.match.params.shixunId; -// let url ="/shixuns/"+id+"/repository.json"; -// axios.post(url,{ -// path: path -// }).then((response) => { -// if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { - -// }else{ -// setTrees(response.data.trees) -// setPath(path) -// pathArray(newPathArray) -// } - -// }).catch((error) => { -// console.log(error) -// }); -// } -// const savegetfilepath=(value)=>{ -// const state = {} -// let {selectpath,saveshixunfilepath,pathtype} = state - -// if(pathtype===1){ -// let newselectpath; - -// if(saveshixunfilepath==="shixunfilepathplay"){ -// newselectpath=value -// }else{ -// const type = selectpath.split(';'); -// let types=false; -// for(var i=0; i{ - -// } -// function sendgetfilepath() { - -// } -// return ( -// -//
    -//
    -// -//
    -// -// saveselectpath(e)} -// value={path}/> -//
    - -// onOk()}>确定 -// onCancel()}>取消 -//
    -//
    -//
    -// ) - -// } - -// export default RepositoryChooseModal \ No newline at end of file diff --git a/public/react/src/tpm/newshixuns/TPMNewshixuns/TPMNewshixuns.js b/public/react/src/tpm/newshixuns/TPMNewshixuns/TPMNewshixuns.js deleted file mode 100644 index 8b102651f..000000000 --- a/public/react/src/tpm/newshixuns/TPMNewshixuns/TPMNewshixuns.js +++ /dev/null @@ -1,19 +0,0 @@ -import React, { Component } from 'react'; - -import axios from 'axios'; - -export default class TPMNewshixuns extends Component { - constructor(props) { - super(props) - this.state = { - - } - } - render() { - return ( - - ); - } -} - - diff --git a/public/react/src/tpm/newshixuns/css/Newshixuns.css b/public/react/src/tpm/newshixuns/css/Newshixuns.css deleted file mode 100644 index e241dcf0d..000000000 --- a/public/react/src/tpm/newshixuns/css/Newshixuns.css +++ /dev/null @@ -1,397 +0,0 @@ -/* BASICS */ - -.CodeMirror { - /* Set height, width, borders, and global font properties here */ - font-family: monospace; - height: 300px; - color: black; - direction: ltr; -} - -/* PADDING */ - -.CodeMirror-lines { - padding: 4px 0; /* Vertical padding around content */ -} -.CodeMirror pre { - padding: 0 4px; /* Horizontal padding of content */ -} - -.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { - background-color: white; /* The little square between H and V scrollbars */ -} - -/* GUTTER */ - -.CodeMirror-gutters { - border-right: 1px solid #ddd; - background-color: #f7f7f7; - white-space: nowrap; -} -.CodeMirror-linenumbers {} -.CodeMirror-linenumber { - padding: 0 3px 0 5px; - min-width: 20px; - text-align: right; - color: #999; - white-space: nowrap; -} - -.CodeMirror-guttermarker { color: black; } -.CodeMirror-guttermarker-subtle { color: #999; } - -/* CURSOR */ - -.CodeMirror-cursor { - border-left: 1px solid black; - border-right: none; - width: 0; -} -/* Shown when moving in bi-directional text */ -.CodeMirror div.CodeMirror-secondarycursor { - border-left: 1px solid silver; -} -.cm-fat-cursor .CodeMirror-cursor { - width: auto; - border: 0 !important; - background: #7e7; -} -.cm-fat-cursor div.CodeMirror-cursors { - z-index: 1; -} -.cm-fat-cursor-mark { - background-color: rgba(20, 255, 20, 0.5); - -webkit-animation: blink 1.06s steps(1) infinite; - animation: blink 1.06s steps(1) infinite; -} -.cm-animate-fat-cursor { - width: auto; - border: 0; - -webkit-animation: blink 1.06s steps(1) infinite; - animation: blink 1.06s steps(1) infinite; - background-color: #7e7; -} -@-webkit-keyframes blink { - 0% {} - 50% { background-color: transparent; } - 100% {} -} -@keyframes blink { - 0% {} - 50% { background-color: transparent; } - 100% {} -} - -/* Can style cursor different in overwrite (non-insert) mode */ -.CodeMirror-overwrite .CodeMirror-cursor {} - -.cm-tab { display: inline-block; text-decoration: inherit; } - -.CodeMirror-rulers { - position: absolute; - left: 0; right: 0; top: -50px; bottom: -20px; - overflow: hidden; -} -.CodeMirror-ruler { - border-left: 1px solid #ccc; - top: 0; bottom: 0; - position: absolute; -} - -/* DEFAULT THEME */ - -.cm-s-default .cm-header {color: blue;} -.cm-s-default .cm-quote {color: #090;} -.cm-negative {color: #d44;} -.cm-positive {color: #292;} -.cm-header, .cm-strong {font-weight: bold;} -.cm-em {font-style: italic;} -.cm-link {text-decoration: underline;} -.cm-strikethrough {text-decoration: line-through;} - -.cm-s-default .cm-keyword {color: #708;} -.cm-s-default .cm-atom {color: #219;} -.cm-s-default .cm-number {color: #164;} -.cm-s-default .cm-def {color: #00f;} -.cm-s-default .cm-variable, -.cm-s-default .cm-punctuation, -.cm-s-default .cm-property, -.cm-s-default .cm-operator {} -.cm-s-default .cm-variable-2 {color: #05a;} -.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;} -.cm-s-default .cm-comment {color: #a50;} -.cm-s-default .cm-string {color: #a11;} -.cm-s-default .cm-string-2 {color: #f50;} -.cm-s-default .cm-meta {color: #555;} -.cm-s-default .cm-qualifier {color: #555;} -.cm-s-default .cm-builtin {color: #30a;} -.cm-s-default .cm-bracket {color: #997;} -.cm-s-default .cm-tag {color: #170;} -.cm-s-default .cm-attribute {color: #00c;} -.cm-s-default .cm-hr {color: #999;} -.cm-s-default .cm-link {color: #00c;} - -.cm-s-default .cm-error {color: #f00;} -.cm-invalidchar {color: #f00;} - -.CodeMirror-composing { border-bottom: 2px solid; } - -/* Default styles for common addons */ - -div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;} -div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} -.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } -.CodeMirror-activeline-background {background: #e8f2ff;} - -/* STOP */ - -/* The rest of this file contains styles related to the mechanics of - the editor. You probably shouldn't touch them. */ - -.CodeMirror { - position: relative; - overflow: hidden; - background: white; -} - -.CodeMirror-scroll { - overflow: scroll !important; /* Things will break if this is overridden */ - /* 30px is the magic margin used to hide the element's real scrollbars */ - /* See overflow: hidden in .CodeMirror */ - margin-bottom: -30px; margin-right: -30px; - padding-bottom: 30px; - height: 100%; - outline: none; /* Prevent dragging from highlighting the element */ - position: relative; -} -.CodeMirror-sizer { - position: relative; - border-right: 30px solid transparent; -} - -/* The fake, visible scrollbars. Used to force redraw during scrolling - before actual scrolling happens, thus preventing shaking and - flickering artifacts. */ -.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { - position: absolute; - z-index: 6; - display: none; -} -.CodeMirror-vscrollbar { - right: 0; top: 0; - overflow-x: hidden; - overflow-y: scroll; -} -.CodeMirror-hscrollbar { - bottom: 0; left: 0; - overflow-y: hidden; - overflow-x: scroll; -} -.CodeMirror-scrollbar-filler { - right: 0; bottom: 0; -} -.CodeMirror-gutter-filler { - left: 0; bottom: 0; -} - -.CodeMirror-gutters { - position: absolute; left: 0; top: 0; - min-height: 100%; - z-index: 3; -} -.CodeMirror-gutter { - white-space: normal; - height: 100%; - display: inline-block; - vertical-align: top; - margin-bottom: -30px; -} -.CodeMirror-gutter-wrapper { - position: absolute; - z-index: 4; - background: none !important; - border: none !important; -} -.CodeMirror-gutter-background { - position: absolute; - top: 0; bottom: 0; - z-index: 4; -} -.CodeMirror-gutter-elt { - position: absolute; - cursor: default; - z-index: 4; -} -.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } -.CodeMirror-gutter-wrapper ::selection { background-color: transparent } -.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } - -.CodeMirror-lines { - cursor: text; - min-height: 1px; /* prevents collapsing before first draw */ -} -.CodeMirror pre { - /* Reset some styles that the rest of the page might have set */ border-radius: 0; - border-width: 0; - background: transparent; - font-family: inherit; - font-size: inherit; - margin: 0; - white-space: pre; - word-wrap: normal; - line-height: inherit; - color: inherit; - z-index: 2; - position: relative; - overflow: visible; - -webkit-tap-highlight-color: transparent; - -webkit-font-variant-ligatures: contextual; - font-variant-ligatures: contextual; -} -.CodeMirror-wrap pre { - word-wrap: break-word; - white-space: pre-wrap; - word-break: normal; -} - -.CodeMirror-linebackground { - position: absolute; - left: 0; right: 0; top: 0; bottom: 0; - z-index: 0; -} - -.CodeMirror-linewidget { - position: relative; - z-index: 2; - padding: 0.1px; /* Force widget margins to stay inside of the container */ -} - -.CodeMirror-widget {} - -.CodeMirror-rtl pre { direction: rtl; } - -.CodeMirror-code { - outline: none; -} - -/* Force content-box sizing for the elements where we expect it */ -.CodeMirror-scroll, -.CodeMirror-sizer, -.CodeMirror-gutter, -.CodeMirror-gutters, -.CodeMirror-linenumber { - -webkit-box-sizing: content-box; - box-sizing: content-box; -} - -.CodeMirror-measure { - position: absolute; - width: 100%; - height: 0; - overflow: hidden; - visibility: hidden; -} - -.CodeMirror-cursor { - position: absolute; - pointer-events: none; -} -.CodeMirror-measure pre { position: static; } - -div.CodeMirror-cursors { - visibility: hidden; - position: relative; - z-index: 3; -} -div.CodeMirror-dragcursors { - visibility: visible; -} - -.CodeMirror-focused div.CodeMirror-cursors { - visibility: visible; -} - -.CodeMirror-selected { background: #d9d9d9; } -.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } -.CodeMirror-crosshair { cursor: crosshair; } -.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } -.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } -.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } - -.cm-searching { - background-color: #ffa; - background-color: rgba(255, 255, 0, .4); -} - -/* Used to force a border model for a node */ -.cm-force-border { padding-right: .1px; } - -@media print { - /* Hide the cursor when printing */ - .CodeMirror div.CodeMirror-cursors { - visibility: hidden; - } -} - -/* See issue #2901 */ -.cm-tab-wrap-hack:after { content: ''; } - -/* Help users use markselection to safely style text background */ -span.CodeMirror-selectedtext { background: none; } - - - - -.radioStyle{ - display: block; - height: 30px; - } -a.white-btn.use_scope-btn:hover{ - -} -.shixunScopeInput{ - width:218px; - height:33px; - display:block; - margin-bottom:15px; -} - -#memoMD .CodeMirror { - /*width: 576px !important;*/ - margin-top: 31px !important; - height: 364px !important; -} - -#memoMD .editormd-preview { - width: 578px !important; - top: 40px !important; - height: 364px !important; -} - -.ml36{ - margin-left: 26px; -} -#person-unit a.white-btn.use_scope-btn:hover { - border: 1px solid #F06200; - color:#FFF !important; -} - -.shixunspanred{ - margin-left: 142px; - margin-top: 5px; - margin-bottom: 5px; -} - -.ml82{ - margin-left: 82px; -} - -.ant-btn-primary.active, .ant-btn-primary:active { - color: #fff; - background-color: #096dd9; - border-color: #096dd9; -} - -.ant-btn:hover, .ant-btn:focus, .ant-btn:active, .ant-btn.active{ - background-color: #4CACFF; -} \ No newline at end of file diff --git a/public/react/src/tpm/roundedRectangle.png b/public/react/src/tpm/roundedRectangle.png deleted file mode 100755 index 0d2d0b0dc..000000000 Binary files a/public/react/src/tpm/roundedRectangle.png and /dev/null differ diff --git a/public/react/src/tpm/shixunchild/Challenges/Challenges.js b/public/react/src/tpm/shixunchild/Challenges/Challenges.js deleted file mode 100644 index 1470d45db..000000000 --- a/public/react/src/tpm/shixunchild/Challenges/Challenges.js +++ /dev/null @@ -1,676 +0,0 @@ -import React, { Component } from 'react'; - -import { Redirect } from 'react-router'; - -import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom"; - -import PropTypes from 'prop-types'; - -import classNames from 'classnames'; - -import { getImageUrl ,markdownToHTML, configShareForCustom} from 'educoder' - -import { CircularProgress } from 'material-ui/Progress'; - -import { Modal, Spin, Tooltip ,message,Icon} from 'antd'; - -import 'antd/lib/pagination/style/index.css'; - -import '../shixunchildCss/Challenges.css' - -import axios from 'axios'; - -import AccountProfile from"../../../user/AccountProfile"; - -const $ = window.$; - -class Challenges extends Component { - constructor(props) { - super(props) - this.state = { - ChallengesDataList: undefined, - operate: true, - startbtns: false, - sumid: "", - sumidtype: false, - startshixunCombattype:false, - shixunsreplace:false, - shixunsmessage:"", - hidestartshixunsreplacevalue:"", - operationstrue:false, - isSpin:false, - } - } - - ChallengesList = () => { - let id = this.props.match.params.shixunId; - let ChallengesURL = `/shixuns/` + id + `/challenges.json`; - - axios.get(ChallengesURL).then((response) => { - if (response.status === 200) { - if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { - - }else{ - configShareForCustom(this.props.shixunsDetails.name, response.data.description) - this.setState({ - ChallengesDataList: response.data, - sumidtype: false, - }); - } - } - }).catch((error) => { - console.log(error) - }); - } - - componentDidMount() { - setTimeout(this.ChallengesList(), 1000); - } - - updatamakedown = (id) => { - setTimeout(() => { - var shixunDescr = window.editormd.markdownToHTML(id, { - htmlDecode: "style,script,iframe", - taskList: true, - tex: true, - flowChart: true, - sequenceDiagram: true - }); - $("#" + id + " p:first").addClass("ReactMarkdown"); - }, 200) - } - - // 关卡的上移下移操作 - operations = (sumid, type) => { - this.setState({ - operationstrue:true - }) - let { ChallengesDataList } = this.state; - let operationUrl; - if (type === "up") { - operationUrl = "/shixuns/" + ChallengesDataList.shixun_identifier + "/challenges/" + sumid + "/index_up.json"; - } else if (type === "down") { - operationUrl = "/shixuns/" + ChallengesDataList.shixun_identifier + "/challenges/" + sumid + "/index_down.json"; - } - if (this.state.operate) { - - this.setState({ - operate: false - }); - axios.get(operationUrl).then((response) => { - if (response.status === 200) { - this.setState({ - operate: true, - operationstrue:false - }); - this.ChallengesList(); - - } - }).catch((error) => { - console.log(error); - this.setState({ - operate: true, - operationstrue:false - }); - this.ChallengesList() - }) - } - } - delOperations = (sumid) => { - this.setState({ - sumid: sumid, - sumidtype: true - }) - } - - clonedelOperationss = () => { - this.setState({ - sumidtype: false - }) - } - delOperationss = () => { - let { ChallengesDataList, sumid } = this.state; - let operationUrl = "/shixuns/" + ChallengesDataList.shixun_identifier + "/challenges/" + sumid+".json" - - if (this.state.operate) { - this.setState({ - operate: false, - sumidtype: false - }) - axios.delete(operationUrl, { - withCredentials: true - }).then((response) => { - if (response.status === 200) { - this.setState({ - operate: true, - sumidtype: false - }); - this.ChallengesList(); - } - this.ChallengesList() - }).catch((error) => { - console.log(error); - this.setState({ - operate: true, - sumidtype: false - }); - this.ChallengesList() - }) - } - } - - startgameid=(id)=>{ - - let url = "/shixuns/" + id + "/shixun_exec.json"; - axios.get(url).then((response) => { - - if (response.data.status === -2) { - this.setState({ - shixunsreplace:true, - hidestartshixunsreplacevalue:response.data.message+".json" - }) - } else if (response.data.status === -1) { - console.log(response) - }else if(response.data.status===-3){ - this.setState({ - shixunsmessage:response.data.message, - startshixunCombattype:true, - }) - } else { - window.location.href = "/tasks/" + response.data.game_identifier; - // window.location.href = path - // let path="/tasks/"+response.data.game_identifier; - // this.props.history.push(path); - } - }).catch((error) => { - - }); - - - } - - hidestartshixunsreplace=(url)=>{ - this.setState({ - isSpin:true, - }) - axios.get(url).then((response) => { - if(response.status===200){ - // let path="/shixuns/"+response.data.shixun_identifier+"/challenges"; - // this.props.history.push(path); - message.success('重置成功,正在进入实训!'); - this.startgameid(response.data.shixun_identifier); - this.setState({ - shixunsreplace:false, - isSpin:false, - }) - - // message.success('重置成功,正在进入实训!'); - // this.startshixunCombat(); - }} - ).catch((error) => { - - }); - - } - - //编辑实训题目选择题 - EditTraining=(type, ids, path)=>{ - let { ChallengesDataList } = this.state; - window.location.href = "/shixuns/" + ChallengesDataList.shixun_identifier + "/challenges/" + ids + path; - } - - //开始实战按钮 - startshixunCombat = (type, ids, id) => { - - if(this.props.checkIfLogin()===false){ - this.props.showLoginDialog() - return - } - - if(this.props.checkIfProfileCompleted()===false){ - this.setState({ - AccountProfiletype:true - }) - return - } - - // if(this.props.checkIfProfessionalCertification()===false){ - // this.setState({ - // AccountProfiletype:true - // }) - // return - // } - - - let { ChallengesDataList } = this.state; - // let id = this.props.match.params.shixunId; - this.setState({ - startbtns: true - }) - let url = "/shixuns/" + ChallengesDataList.shixun_identifier + "/shixun_exec.json?challenge_id="+id; - axios.get(url).then((response) => { - - if (response.data.status === -2) { - this.setState({ - startbtns:false, - shixunsreplace:true, - hidestartshixunsreplacevalue:response.data.message+".json" - }) - } else if (response.data.status === -1) { - this.setState({ - startbtns: false - }) - console.log(response) - }else if(response.data.status===-3){ - this.setState({ - shixunsmessage:response.data.message, - startshixunCombattype:true, - startbtns:false - }) - } else { - window.location.href = "/tasks/" + response.data.game_identifier; - // window.location.href = path - // let path="/tasks/"+response.data.game_identifier; - // this.props.history.push(path); - } - }).catch((error) => { - - }); - - - // if(path===null){ - // }else{ - // if (type > 4 || type === false) { - // window.location.href = path; - // } else { - // - // } - // } - - - } - hidestartshixunCombattype=()=>{ - this.setState({ - startshixunCombattype:false - }) - } - - hideAccountProfile=()=>{ - this.setState({ - AccountProfiletype:false - }) - } - - render() { - let { ChallengesDataList, startbtns, sumidtype ,startshixunCombattype,shixunsreplace,shixunsmessage,hidestartshixunsreplacevalue,operationstrue,AccountProfiletype} = this.state; - let { loadingContent } = this.props; - if (ChallengesDataList != undefined) { - this.updatamakedown("ReactMarkdown") - } - let id = this.props.match.params.shixunId; - const antIcon = ; - return ( - - {AccountProfiletype===true?this.hideAccountProfile()} - {...this.props} - {...this.state} - />:""} - - {loadingContent ? - : - -
    -

    - {this.props.identity < 5 && ChallengesDataList&&ChallengesDataList.shixun_status=== 0 ? - - - - 实践任务 - - - : "" - } - {this.props.identity < 5 && ChallengesDataList&&ChallengesDataList.shixun_status=== 0 ? - - - - 选择题任务 - - : "" - } -

    -

    - 简介 - - - - - - -

    - -
    -

    - {ChallengesDataList === undefined ? "" :ChallengesDataList&&ChallengesDataList.description===null?"": -

    - } -

    - - {/* - - */} -
    - -

    - 全部任务 - {this.props.identity < 5 && ChallengesDataList&&ChallengesDataList.shixun_status=== 0 ? - - - - 实践任务 - - - : "" - } - {this.props.identity < 5 && ChallengesDataList&&ChallengesDataList.shixun_status=== 0 ? - - - - 选择题任务 - - : "" - } -

    - -
    - {ChallengesDataList === undefined ?
    -
    - -

    暂时还没有相关数据哦!

    -
    -
    : ChallengesDataList.challenge_list === undefined ? -
    -
    - -

    暂时还没有相关数据哦!

    -
    -
    - : ChallengesDataList.challenge_list.length === 0 ? -
    -
    - -

    暂时还没有相关数据哦!

    -
    -
    - : ChallengesDataList.challenge_list.map((item, key) => { - - let newstatus = 2; - if(ChallengesDataList.challenge_list[key - 1]!=undefined){ - newstatus=ChallengesDataList.challenge_list[key - 1].status; - } - return ( -
    - -
    - - {item.st === 0 ? - - - - : - - - - } - - 第{key+1}关 - - {this.props.identity<5? - item.st === 1 ? - this.EditTraining(this.props.identity, item.challenge_id, "/editquestion")} - className="font-16 color05101a">{item.name} - : - this.EditTraining(this.props.identity, item.challenge_id, "/editcheckpoint")} - className="font-16 color05101a">{item.name}: this.startshixunCombat(this.props.identity, item.challenge_id, "/editcheckpoint")} - className="font-16 color05101a">{item.name} - } - - - - - - - - {item.delete_url != undefined && - - this.delOperations(item.challenge_id)} - style={{ display:this.props.user.admin===true?"block":this.props.identity < 5 && ChallengesDataList.shixun_status === 0 ? "block" : 'none' }} - className="fl ring-op-green mr25"> - - - - } - - - {item.up_url != undefined && - - this.operations(item.challenge_id, "up")} - style={{ display:this.props.user.admin===true?"block":this.props.identity < 5 && ChallengesDataList.shixun_status === 0 ? "block" : 'none' }} - className="fl ring-op-green mr25"> - - - - } - {item.down_url != undefined && - - this.operations(item.challenge_id, "down")} - style={{ display: this.props.user.admin===true?"block":this.props.identity < 5 && ChallengesDataList.shixun_status=== 0 ? "block" : 'none' }} - className="fl ring-op-green mr25"> - - - - - } - - { - item.st === 1 ? - - - - - - : - - - - - - - } - - -
    -
    - {item.passed_count} 人完成挑战 - {item.playing_count} 人正在挑战 - 完成挑战可获得经验值 {item.score} - - - {/*判断比较复杂 有排第一不能是灰色按钮*/} - {item.status === 2 ? - this.startshixunCombat(false,undefined, item.challenge_id)} - // onClick={() => this.startshixunCombat(false)} - title={"查看挑战关卡"} - >已完成 : "" - } - - { - ChallengesDataList.allow_skip === true && item.status === 1? - this.startshixunCombat(false,undefined, item.challenge_id)} - // onClick={() => this.startshixunCombat(false)} - >直接挑战 : "" - } - - - { - ChallengesDataList.allow_skip === false ? item.status === 1? - - this.startshixunCombat(false,undefined, item.challenge_id)} - style={{marginTop: '-2px'}}>直接挑战 - :"":"" - - } - - - - { - item.status === 0 ? - - this.startshixunCombat(false,undefined, item.challenge_id):""} - style={{marginTop: '-2px'}}>直接挑战 - :"" - } - - - -
    -
    - ) - })} -
    - - - - - - -
    -

    目前该实训项目尚在内测中,将于{shixunsmessage}之后开放,谢谢!

    -
    -
    - {/*取消*/} - 知道了啦 -
    - {/*

    */} - {/*知道了*/} - {/*

    */} -
    - - - -
    -

    实训已经更新了,正在为您重置!

    -
    - -
    -
    -
    - } -
    - - ) - } -} - -export default Challenges; - // { - // ChallengesDataList.allow_skip === false ? item.status === 1 && newstatus === 2 ? - // - // this.startshixunCombat(false,undefined, item.challenge_id)} - // style={{marginTop: '-2px'}}>直接挑战 - // - // - // : item.status === 1 && newstatus === 1 ? - // - // this.startshixunCombat(false,undefined, item.challenge_id)} - // style={{marginTop: '-2px'}}>直接挑战 - // : "" : "" - // - // } \ No newline at end of file diff --git a/public/react/src/tpm/shixunchild/Collaborators/Collaborators.css b/public/react/src/tpm/shixunchild/Collaborators/Collaborators.css deleted file mode 100644 index 31917086f..000000000 --- a/public/react/src/tpm/shixunchild/Collaborators/Collaborators.css +++ /dev/null @@ -1,9 +0,0 @@ -.height40 { - height: 30px; - line-height: 30px; -} - -.line27{ - line-height: 27px; - vertical-align: 1px; -} \ No newline at end of file diff --git a/public/react/src/tpm/shixunchild/Collaborators/Collaborators.js b/public/react/src/tpm/shixunchild/Collaborators/Collaborators.js deleted file mode 100644 index d67599bf1..000000000 --- a/public/react/src/tpm/shixunchild/Collaborators/Collaborators.js +++ /dev/null @@ -1,658 +0,0 @@ -import React, { Component } from 'react'; - -import { Redirect } from 'react-router'; - -import {Modal, Button, Radio, Input, Checkbox,message,Spin, Icon} from 'antd'; - -import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom"; - -import PropTypes from 'prop-types'; - -import classNames from 'classnames'; - -import { CircularProgress } from 'material-ui/Progress'; - -import { getImageUrl, toPath } from 'educoder' - -import axios from 'axios'; - -import NoneData from "../../../courses/coursesPublic/NoneData"; - -import './Collaborators.css'; - - -const $ = window.$; - -const RadioGroup = Radio.Group; - -const Search = Input.Search; - -class Collaborators extends Component { - constructor(props) { - super(props) - this.state = { - collaboratorList: [], - Collaboratorsvisible: false, - Collaboratorsvisibleadmin: false, - value: 1, - page: 1, - Searchadmin: undefined, - allChangechecked: false, - Collaboratorslist: [], - Collaboratorslisttype: false, - collaborators_deletetype: false, - collaborators_deletevalue: null, - onSearchcalue:"", - collaboratorListsum:10, - collaboratorListsumtype:true, - user_name:undefined, - school_name:undefined, - spinnings:false, - useristrue:false - } - } - componentDidMount() { - let id=this.props.match.params.shixunId; - - let collaborators=`/shixuns/`+id+`/collaborators.json`; - axios.get(collaborators).then((response)=> { - if(response.status===200){ - if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { - - }else{ - this.setState({ - collaboratorList: response.data - }); - } - - } - }).catch((error)=>{ - console.log(error) - }); - } - - updatacomponentDiddata = () => { - let id = this.props.match.params.shixunId; - - let collaborators = `/shixuns/` + id + `/collaborators.json`; - axios.get(collaborators).then((response) => { - if (response.status === 200) { - if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { - - }else{ - this.setState({ - collaboratorList: response.data - }); - } - } - }).catch((error) => { - console.log(error) - }); - } - CollaboratorsshowModal = (type) => { - - if (type === "cooperation") { - this.setState({ - Collaboratorsvisibleadmin: false, - Collaboratorslist:[], - Searchadmin:[] - }); - } else if (type === "admin") { - this.setState({ - Collaboratorsvisible: false, - Collaboratorslist:[], - Searchadmin:[] - }); - } else if (type === "collaborators_deletetype") { - this.setState({ - collaborators_deletetype: false, - }); - } - } - - showCollaboratorsvisible = (type) => { - - this.setState({ - Collaboratorslist: [], - Searchadmin:undefined, - onSearchcalue:"" - }) - let admintype = this.props.identity; - if (admintype>4) { - this.props.showSnackbar("您没有权限"); - return - } - if (type === "cooperation") { - this.setState({ - Collaboratorsvisibleadmin: true, - }); - } else if ("admin") { - let id = this.props.match.params.shixunId; - let url = "/shixuns/" + id + "/change_manager.json"; - axios.get(url).then((response) => { - if (response.status === 200) { - // this.setState({ - // Collaboratorsvisible: true - // }) - if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { - - }else{ - this.setState({ - Collaboratorsvisible: true, - Collaboratorslist: response.data - }) - } - } - }).catch((error) => { - console.log(error) - }); - - } - } - - onChange = (e) => { - this.setState({ - value: e.target.value, - }); - } - onSearchadmins=(e)=>{ - this.setState({ - onSearchcalue:e.target.value - }) - } - onSearchadmin = (value) => { - - let {collaboratorList,user_name,school_name} = this.state; - this.setState({ - // Searchadmin: undefined, - spinnings:true, - }) - // if (value === "") { - // this.setState({ - // Searchadmin: [], - // collaboratorList: collaboratorList - // }) - // } else { - // - // } - let id = this.props.match.params.shixunId; - let url = "/shixuns/" + id + "/add_collaborators.json"; - axios.get(url,{params:{ - user_name:user_name , - school_name:school_name, - }}).then((response) => { - if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { - this.setState({ - spinnings:false - }) - }else{ - let newlist = response.data.users; - for (var i = 0; i < newlist.length; i++) { - newlist[i].checked = false - } - this.setState({ - Searchadmin: newlist, - collaboratorList: collaboratorList, - spinnings:false - }) - } - - }).catch((error) => { - console.log(error) - }); - } - - selectChangenickname = (e, key) => { - - let {Searchadmin} = this.state; - let newlist = Searchadmin; - for (var i = 0; i < newlist.length; i++) { - newlist[key].checked = e.target.checked - } - - let arrlist = []; - let alltype = false; - for (var z = 0; z < newlist.length; z++) { - if (newlist[z].checked === true) { - arrlist.push(newlist[z]) - } - } - - if (Searchadmin.length === arrlist.length) { - alltype = true - } else { - alltype = false - } - - if(newlist.length===0){ - this.setState({ - Searchadmin: newlist, - allChangechecked: alltype, - }) - }else{ - this.setState({ - Searchadmin: newlist, - allChangechecked: alltype, - useristrue:false - }) - } - - - } - allChange = (e) => { - - let {Searchadmin} = this.state; - let newlist = Searchadmin; - for (var i = 0; i < newlist.length; i++) { - newlist[i].checked = e.target.checked - } - this.setState({ - Searchadmin: newlist, - allChangechecked: e.target.checked - }) - } - submit_add_collaborators_form = () => { - - let id = this.props.match.params.shixunId; - let {Searchadmin,collaboratorList} = this.state; - let newlist = Searchadmin; - let user_ids = [] - if (newlist.length === 0) { - this.setState({ - Collaboratorslisttype: true - }) - return - } - for (var i = 0; i < newlist.length; i++) { - if (newlist[i].checked === true) { - user_ids.push(newlist[i].user_id) - } - } - - for(var i=0; i { - this.updatacomponentDiddata(); - this.props.showSnackbar(response.data.message); - this.setState({ - Collaboratorsvisibleadmin: false, - Collaboratorslist:[], - Searchadmin:[] - }) - }).catch((error) => { - console.log(error) - }); - } - addadminredio = (e) => { - this.setState({ - addadminrediovalue: e - }) - - } - submit_addadminredio = () => { - - let {addadminrediovalue} = this.state; - - let id = this.props.match.params.shixunId; - - let url = "/shixuns/" + id + "/change_manager.json"; - if(addadminrediovalue===undefined){ - this.setState({ - Collaboratorsvisible: false, - Collaboratorslist:[], - Searchadmin:[] - }); - this.props.showSnackbar("所选人员为空,没有更换成功"); - this.CollaboratorsshowModal("admin") - return - } - - - axios.post(url, { - user_id: addadminrediovalue - }).then((response) => { - this.setState({ - Collaboratorsvisible: false, - Collaboratorslist:[], - Searchadmin:[] - }); - this.updatacomponentDiddata(); - this.props.showSnackbar(response.data.message); - }).catch((error) => { - console.log(error) - }); - } - - collaborators_delete = (value) => { - this.setState({ - collaborators_deletetype: true, - collaborators_deletevalue: value - }) - - } - collaborators_deletes = () => { - let {collaborators_deletevalue} = this.state; - if (collaborators_deletevalue === null) { - return - } - let id = this.props.match.params.shixunId; - let url = "/shixuns/" + id + "/collaborators_delete.json?user_id=" + collaborators_deletevalue; - axios.delete(url).then((response) => { - if (this.props.current_user.user_id == collaborators_deletevalue) { - this.props.history.push('/shixuns') - return; - } - this.props.showSnackbar(response.data.message); - this.updatacomponentDiddata(); - this.setState({ - collaborators_deletetype: false - }) - }).catch((error) => { - console.log(error) - }); - } - - loadMore=()=>{ - let {collaboratorList}=this.state; - this.setState({ - collaboratorListsum:collaboratorList.length, - collaboratorListsumtype:false - }) - } - - - contentViewScrolledit=(e)=>{ - - //滑动到底判断 - let newscrollTop=parseInt(e.currentTarget.scrollTop); - let allclientHeight=e.currentTarget.clientHeight+newscrollTop; - - if(e.currentTarget.scrollHeight-allclientHeight===0||e.currentTarget.scrollHeight-allclientHeight===1||e.currentTarget.scrollHeight-allclientHeight===-1){ - let {page,collaboratorList,user_name,school_name,Searchadmin} = this.state; - let newpage=page+1; - let newSearchadmin=Searchadmin - let id = this.props.match.params.shixunId; - let url = "/shixuns/" + id + "/add_collaborators.json"; - axios.get(url,{params:{ - user_name:user_name , - school_name:school_name, - page:newpage - }}).then((response) => { - if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { - - }else{ - let newlist = response.data.users; - for (var i = 0; i < newlist.length; i++) { - newlist[i].checked = false - newSearchadmin.push(newlist[i]) - } - - this.setState({ - Searchadmin: newSearchadmin, - collaboratorList: collaboratorList, - page:newpage - }) - } - - }).catch((error) => { - console.log(error) - }); - - } - - } - - render() { - let { - collaboratorList, - Collaboratorsvisible, - Collaboratorsvisibleadmin, - Searchadmin, - allChangechecked, - Collaboratorslist, - Collaboratorslisttype, - collaborators_deletetype, - onSearchcalue, - collaboratorListsum, - collaboratorListsumtype, - user_name, - school_name, - useristrue - } = this.state; - let {loadingContent} = this.props; - const radioStyle = { - display: 'block', - height: '30px', - lineHeight: '30px', - }; - - const antIcon = ; - - console.log(Searchadmin) - return ( - -

    - this.showCollaboratorsvisible("cooperation")} - className="edu-default-btn edu-greenback-btn fr mr20 height40" - data-remote="true"> - + 添加合作者 - - this.showCollaboratorsvisible("admin")} - style={{display:this.props.identity===1?"block":"none"}} - data-remote="true" - className="edu-default-btn edu-greenback-btn fr mr20 height40">更换管理员 -

    - - - -
    - 选择的成员将会成为新的管理员
    您将不再拥有管理员的权限,但您仍是合作团队的一员 -
    - - -
    -
      -
    • - - - { - Collaboratorslist.length === 0 ? "" : Collaboratorslist.map((item, key) => { - return ( - this.addadminredio(item.user_id)}>{item.name} - ) - }) - } - - -
    • -
    -
    - - - -
    - - {Collaboratorsvisibleadmin===true? - {/* this.onSearchadmin(value)}*/} - {/*onInput={this.onSearchadmins}*/} - {/*style={{width: '100%'}}*/} - {/*/>*/} - 姓名或手机号: - - {this.setState({user_name: e.target.value})}} - style={{ width: '215px'}} - > - 单位: - {this.setState({school_name: e.target.value})}} - style={{ width: '215px'}} - > - - - this.onSearchadmin()} - style={{ height: '30px', lineHeight: '30px', width: '70px'}} - >搜索 -

    - 姓名 - 职业 - 单位 -

    -
    - -
    -
      - {Searchadmin === undefined ?
    • - 请试试搜索一下 -
    • :Searchadmin.length === 0 ?: Searchadmin.map((item, key) => { - return ( -
    • - this.selectChangenickname(e, key)} - id={item.user_id}> - {item.nickname} - {item.identify} - {item.school_name} -
    • - ) - }) - - } -
    -
    -
    -
    -
    - - - 全选 -
    - 请至少选择一个用户 -
    -
    - - -
    - {useristrue===true?请先选择用户:""} - -
    :""} - -
    - { - collaboratorList===undefined?"":collaboratorList.map((item,key)=>{ - if(key - - 用户头像 -
    -

    - {item.user.name} - - {item.user.shixun_manager === true ? "(管理员)" : ""} -

    - -

    {item.user.identity}{item.user.school_name}

    - -

    - 发布  {item.user.user_shixuns_count} - {/*粉丝  */} - {/*{item.user.fans_count}*/} - {/**/} -

    - - {/*

    {item.user.brief_introduction}

    */} - - -
    - - {item.user.shixun_manager === true ? "" : this.collaborators_delete(item.user.user_id)}>删除} - {/*取消关注*/} -
    - - ) - } - }) - } - -
    -
    确定要删除吗?
    -
    - - -
    -
    - -
    10&&collaboratorListsumtype===true?"":"none"} - style={{textAlign:'center',borderTop:'1px solid #eee'}}> - 加载更多 -
    - -
    - - ); - } -} - -export default Collaborators; diff --git a/public/react/src/tpm/shixunchild/Propaedeutics/Propaedeu_tics.js b/public/react/src/tpm/shixunchild/Propaedeutics/Propaedeu_tics.js deleted file mode 100644 index f40e9fa63..000000000 --- a/public/react/src/tpm/shixunchild/Propaedeutics/Propaedeu_tics.js +++ /dev/null @@ -1,114 +0,0 @@ -import React, { Component } from 'react'; - -import { Redirect } from 'react-router'; - -import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom"; - -import PropTypes from 'prop-types'; - -import classNames from 'classnames'; - -import { getImageUrl, toPath } from 'educoder'; - -import { Tooltip } from 'antd'; - -import axios from 'axios'; - -import { CircularProgress } from 'material-ui/Progress'; - -const $ = window.$; - -class Propaedeutics extends Component { - constructor(props) { - super(props) - this.state={ - PropaedeuticsListcontent:undefined, - shixunId:undefined - } - } - - componentDidMount() { - let id = this.props.match.params.shixunId; - this.setState({ - shixunId:id - }) - let url="/shixuns/"+id+"/propaedeutics.json"; - axios.get(url).then((response) => { - - if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { - - }else{ - if(response.data.content!=null){ - this.setState({ - PropaedeuticsListcontent:response.data.content - }) - }else{ - this.setState({ - PropaedeuticsListcontent:"" - }) - } - } - }).catch((error) => { - console.log(error) - }); - - } - - updatamakedown=(id)=>{ - setTimeout(()=>{ - var shixunDescr = window.editormd.markdownToHTML(id, { - htmlDecode: "style,script,iframe", - taskList: true, - tex: true, - flowChart: true, - sequenceDiagram: true - }); - $("#"+id+" p:first").addClass("ReactMarkdown"); - $('#collaborators_list_info').show() - }, 200) - } - render() { - let {loadingContent} = this.props; - let {PropaedeuticsListcontent,shixunId}=this.state - - if(PropaedeuticsListcontent!=undefined){ - this.updatamakedown("ReactMarkdown") - } - - return ( - -

    - - - - -

    - { - loadingContent ? - : -
    - {PropaedeuticsListcontent===undefined?"": -

    - - {PropaedeuticsListcontent === undefined ||PropaedeuticsListcontent === ""? -

    -
    -
    - -

    暂时还没有相关数据哦!

    -
    -
    -
    - :} - -

    - } -
    - } -
    - ); - } -} - -export default Propaedeutics; diff --git a/public/react/src/tpm/shixunchild/Ranking_list/Ranking_list.js b/public/react/src/tpm/shixunchild/Ranking_list/Ranking_list.js deleted file mode 100644 index 0e5d0498b..000000000 --- a/public/react/src/tpm/shixunchild/Ranking_list/Ranking_list.js +++ /dev/null @@ -1,145 +0,0 @@ -import React, { Component } from 'react'; - -import { Redirect } from 'react-router'; - -import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom"; - -import PropTypes from 'prop-types'; - -import classNames from 'classnames'; - -import { getImageUrl, toPath } from 'educoder'; - -import { CircularProgress } from 'material-ui/Progress'; - -import axios from 'axios'; - -const $ = window.$; - -class Ranking_list extends Component { - constructor(props) { - super(props) - this.state = { - Ranking_listData:[] - } - } - - Ranking_listList = (id) => { - let Ranking_listURL = `/shixuns/` + id + `/ranking_list.json`; - axios.get(Ranking_listURL).then((response) => { - if (response.status === 200) { - if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { - - }else{ - this.setState({ - Ranking_listData: response.data - }); - } - } - - }).catch((error) => { - console.log(error) - }); - } - - componentDidMount() { - let id = this.props.match.params.shixunId; - setTimeout(this.Ranking_listList(id), 1000); - } - checkAddZone=(num)=>{ - return num<10 ? '0' + num.toString() : num - } - dateTimeFormatter=(t)=> { - if (!t) return '' - t = new Date(t).getTime() - t = new Date(t) - var year = t.getFullYear() - var month = (t.getMonth() + 1) - month = this.checkAddZone(month) - - var date = t.getDate() - date = this.checkAddZone(date) - - var hour = t.getHours() - hour = this.checkAddZone(hour) - - var min = t.getMinutes() - min = this.checkAddZone(min) - - return year + '-' + month + '-' + date + ' ' + hour + ':' + min - } - - formatSeconds=(value)=> { - var theTime = parseInt(value);// 秒 - var theTime1 = 0;// 分 - var theTime2 = 0;// 小时 - if(theTime > 60) { - theTime1 = parseInt(theTime/60); - theTime = parseInt(theTime%60); - if(theTime1 > 60) { - theTime2 = parseInt(theTime1/60); - theTime1 = parseInt(theTime1%60); - } - } - var result = ""+parseInt(theTime)+"秒"; - if(theTime1 > 0) { - result = ""+parseInt(theTime1)+"分"+result; - } - if(theTime2 > 0) { - result = ""+parseInt(theTime2)+"小时"+result; - } - return result; - } - - render() { - let { Ranking_listData } = this.state; - let { loadingContent } = this.props; - - // console.log(Ranking_listData) - return ( - - { loadingContent ? - : - -
    - {Ranking_listData===undefined||Ranking_listData.length===0? -
    - -

    我们在等你,不轻言放弃

    -
    - :Ranking_listData.map((item,key)=>{ - var keys=key+1 - return( -
    -
  • - - 2?"block":"none"}} - >{key+1} - - 头像 - - - {item.users.name} -
  • - -
  • {this.dateTimeFormatter(item.time)}通关
  • - {/*
  • */} - {/*/!*{item.accuracy} %准确率*!/*/} - {/*
  • */} -
  • {this.formatSeconds(item.use_time)}
  • -
  • +{item.gold}金币
  • -
    - ) - })} -
    - } -
    - - ); - } -} - -export default Ranking_list; diff --git a/public/react/src/tpm/shixunchild/Repository/Repository.js b/public/react/src/tpm/shixunchild/Repository/Repository.js deleted file mode 100644 index c477422e8..000000000 --- a/public/react/src/tpm/shixunchild/Repository/Repository.js +++ /dev/null @@ -1,266 +0,0 @@ -import React, { Component } from 'react'; - -import { Redirect } from 'react-router'; - -import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom"; - -import PropTypes from 'prop-types'; - -import classNames from 'classnames'; - -import axios from 'axios'; - -import { trace, trace_collapse ,getImageUrl, toPath} from "educoder"; - -import RepositoryDirectories from './RepositoryDirectories' - -import { ActionBtn , NoneData } from 'educoder' -import RepositoryCombinePath from './RepositoryCombinePath' -const $ = window.$; - -// 点击按钮复制功能 -function jsCopy(){ - var e = document.getElementById("copy_rep_content"); - e.select(); - document.execCommand("Copy"); -} -/** - 提交记录 - 使用指南 - */ -class Repository extends Component { - constructor(props) { - super(props); - this.state={ - - } - } - componentDidMount() { - } - onRepoFileClick = (item) => { - - this.props.fetchRepo(item) - - } - render() { - let { match, author, git_url, lastest_commit,repositoryLoading, commits,trees,pathArray , TPMRightSectionData } = this.props; - - if (!author) { - author = {} - } - let userauthority=false; - if(this.props.author!=undefined){ - userauthority=this.props.author.login===""||this.props.author.user_id===""||this.props.author.login===null||this.props.author.user_id===null; - } - return ( - - {/* jfinalshop/WebRoot */} - {/*
    - - - 分支 1 - - Git使用指南 -
    */} - - { repositoryLoading ?
    : - -
    -
    -
    -
    -
    - {/*
    - -
    - -
    - - - - */} - - Git使用指南 - { - this.props.current_user && (this.props.current_user.admin ==true || (TPMRightSectionData && TPMRightSectionData.creator && TPMRightSectionData.creator.login == this.props.current_user.login)) ? - !this.props.secret_repository_tab && - +添加文件 - :"" - } - - - -
    - - - { - jsCopy() - }} data-tip-down="点击复制版本库地址" - className="fl ml5"> - - - - - {/* Git使用指南 */} - - - { $('#repository_url_tip').css('display') === 'none' - ? $('#repository_url_tip').show() - : $('#repository_url_tip').hide() }} - className="fl ml6 mt1"> - - - - -
    -
    - - {this.props.secret_repository_tab && - - } - -
    -
    - - - {/* 用户、最近提交时间 */} - { - trees === undefined || trees === null ||trees.length===0? : -
    - {commits===undefined?"":commits===null||commits.length===0?"":
    - {author.name} - {commits[0].author.name} - 提交于 - - {commits===undefined?"":commits[0].time} - :{commits===undefined?"":commits[0].title} - - - - 提交记录 - -
    } - -
    -
    - {/* 当前目录位置 */} - - -
    - { trees === undefined ?"": trees === null || trees.length===0?"":trees.map((item, index) => { - return ( -
  • - - - this.onRepoFileClick(item)}> -  {item.name} - - -
  • - ) - })} -
    -
    -
    -
    - } - - {/* 当前分支的文件 */} - -
    -
    - } -
    - - ); - } -} -/* - 提交记录 -
    - { RepositoryList===undefined?"":RepositoryList.commits.map((item,key)=>{ - // {"email":"李暾","title":"2\n","id":"80cb6fc55a14bdd64a9c99913f416966238ed3de","time":"49年前"} - return( -
    -
    {item.email}
    -
    {item.title}
    -
    {item.id}
    -
    {item.time}
    -
    - ) - }) } -
    - - - -
  • - - - 1-1.py - -
  • - - - -*/ - -export default Repository; diff --git a/public/react/src/tpm/shixunchild/Repository/RepositoryAddFile.js b/public/react/src/tpm/shixunchild/Repository/RepositoryAddFile.js deleted file mode 100644 index 9ca535bb4..000000000 --- a/public/react/src/tpm/shixunchild/Repository/RepositoryAddFile.js +++ /dev/null @@ -1,198 +0,0 @@ -import React, { Component } from 'react'; -import { ActionBtn } from 'educoder' - -import { Form , Modal , Input , Breadcrumb , Button } from 'antd' - -import { Link } from 'react-router-dom' - -import axios from 'axios' - -/** - ---------------------------- START - */ -function getModeByMirrorName(mirror_name) { - let mode = 'javascript' - if (mirror_name && mirror_name.length) { - for (let i = 0; i < mirror_name.length; i++) { - let modeVal = mirrorNameModeMap[mirror_name[i]]; - if (modeVal) { - mode = modeVal; - break; - } - } - } - return mode; -} -const _extraKeys = {"Alt-/": "autocomplete"}; -function createCMOptions(mirror_name) { - let mode = getModeByMirrorName(mirror_name) - - let cmOptions = { - lineNumbers: true, - mode: mode, - theme: "railscasts", - indentUnit:4, - matchBrackets: true, - autoRefresh: true, - smartIndent: true,//智能换行 - extraKeys: _extraKeys, - autofocus: true, - styleActiveLine: true, - lint: true, - gutters: ["CodeMirror-linenumbers", "breakpoints", "CodeMirror-lint-markers"] - }; - return cmOptions; -} - -const mirrorNameModeMap = { - 'JFinal': 'text/x-java', - 'Java': 'text/x-java', - 'Kotlin': 'text/x-kotlin', - 'C/C++' : 'text/x-c++src', - 'MachineLearning': { - name: "python", - version: 3, - singleLineStringErrors: false - }, - 'Python2.7': { - name: "python", - version: 3, - singleLineStringErrors: false - }, - 'Python3.6': { - name: "python", - version: 3, - singleLineStringErrors: false - }, -} -/** - ---------------------------- END -*/ - -class RepositoryAddFile extends Component { - constructor(props) { - super(props); - } - - componentDidMount(){ - let cmOptions = createCMOptions(this.props.mirror_name) - const extend_editor = window.CodeMirror.fromTextArea(window.$('#codemirror-file-edit')[0] - , cmOptions); - - // tpi没setValue也可以 - extend_editor.setValue('') - extend_editor.refresh(); - - // 拖拽也需要用 : window.editor_CodeMirror.refresh() - window.editor_tempCodeMirror = extend_editor; - this.extend_editor = extend_editor; - } - - checkPath= (rule, value, callback) =>{ - if(!value){ - callback('文件名不能为空'); - }else if (value == "/" || value.indexOf('.') == -1 ) { - callback('请输入正确的文件路径,如:src/HelloWorld.java'); - }else{ - callback(); - } - } - - handleSubmit = () =>{ - this.props.form.validateFieldsAndScroll((err, values) => { - if(!err){ - let shixunId = this.props.match.params.shixunId; - let url = `/shixuns/${shixunId}/add_file.json` - axios.post(url,{ - path:values.path, - message:values.message, - content:this.extend_editor.getValue() - }).then((result)=>{ - if(result){ - this.props.history.push(`${result.data.url}`) - } - }).catch((error)=>{ - console.log(error); - }) - } - }) - } - render(){ - const {getFieldDecorator} = this.props.form; - let { shixunId } = this.props.match.params; - return( -
    - -

    - - 实训项目 - 版本库 - 添加新文件 - -

    -
    -
    - - {getFieldDecorator('path', { - rules: [ - { - validator:this.checkPath - }] - })( - - )} - -
    -
    -

    - -

    - -
    - -
    - - - {getFieldDecorator('message', { - rules: [{required: true, message: "请输入提交信息"}], - })( - - )} - -
    -
    - - 取消 -
    - - -
    - ) - } -} -const WrappedRepositoryAddFile = Form.create({name: 'taskRepositoryAddFile'})(RepositoryAddFile); -// RouteHOC() -export default (WrappedRepositoryAddFile); \ No newline at end of file diff --git a/public/react/src/tpm/shixunchild/Repository/RepositoryCodeEditor.js b/public/react/src/tpm/shixunchild/Repository/RepositoryCodeEditor.js deleted file mode 100644 index 51f6e35f2..000000000 --- a/public/react/src/tpm/shixunchild/Repository/RepositoryCodeEditor.js +++ /dev/null @@ -1,185 +0,0 @@ -import React, { Component } from 'react'; - -import { Redirect } from 'react-router'; - -import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom"; - -import PropTypes from 'prop-types'; - -import classNames from 'classnames'; - -import axios from 'axios'; - -import RepositoryDirectories from './RepositoryDirectories' - -import { trace_collapse } from 'educoder' - -import Popconfirm from 'antd/lib/popconfirm'; -import 'antd/lib/popconfirm/style/css'; - -import { message } from 'antd'; - -require('codemirror/lib/codemirror.css'); - -const $ = window.$; - - -/** - ---------------------------- START - */ -function getModeByMirrorName(mirror_name) { - let mode = 'javascript' - if (mirror_name && mirror_name.length) { - for (let i = 0; i < mirror_name.length; i++) { - let modeVal = mirrorNameModeMap[mirror_name[i]]; - if (modeVal) { - mode = modeVal; - break; - } - } - } - return mode; -} -const _extraKeys = {"Alt-/": "autocomplete"}; -function createCMOptions(mirror_name) { - let mode = getModeByMirrorName(mirror_name) - - let cmOptions = { - lineNumbers: true, - mode: mode, - theme: "railscasts", - indentUnit:4, - matchBrackets: true, - autoRefresh: true, - smartIndent: true,//智能换行 - extraKeys: _extraKeys, - autofocus: true, - styleActiveLine: true, - lint: true, - gutters: ["CodeMirror-linenumbers", "breakpoints", "CodeMirror-lint-markers"] - }; - return cmOptions; -} - -const mirrorNameModeMap = { - 'JFinal': 'text/x-java', - 'Java': 'text/x-java', - 'Kotlin': 'text/x-kotlin', - 'C/C++' : 'text/x-c++src', - 'MachineLearning': { - name: "python", - version: 3, - singleLineStringErrors: false - }, - 'Python2.7': { - name: "python", - version: 3, - singleLineStringErrors: false - }, - 'Python3.6': { - name: "python", - version: 3, - singleLineStringErrors: false - }, -} -/** - ---------------------------- END - */ - -class RepositoryCodeEditor extends Component { - constructor(props) { - super(props) - this.state = { - codeSaving: false - } - } - componentDidUpdate = (prevProps, prevState) => { - - if (this.props.fileContent && this.props.fileContent != prevProps.fileContent) { - // window.setTimeout(() => { - this.extend_editor.setValue(this.props.fileContent) - // }, 2000) - } - } - componentDidMount(){ - let cmOptions = createCMOptions(this.props.mirror_name) - const extend_editor = window.CodeMirror.fromTextArea(window.$('#codemirror-file-edit')[0] - , cmOptions); - - // tpi没setValue也可以 - extend_editor.setValue('') - extend_editor.refresh(); - - // 拖拽也需要用 : window.editor_CodeMirror.refresh() - window.editor_tempCodeMirror = extend_editor; - this.extend_editor = extend_editor; - } - - saveCode = () => { - const { shixunId, pathArray } = this.props; - const url = `/shixuns/${shixunId}/update_file.json` - const path = pathArray.join('/') - this.setState({ codeSaving: true }) - axios.post(url, { - secret_repository: this.props.secret_repository_tab, - content: this.extend_editor.getValue(), - // type: forTest === true ? 1 : 0, - path: path - } - ).then((response) => { - if (response.data.content) { - message.success('保存成功'); - this.setState({ codeSaving: false }) - } - }) - } - render() { - const { fileContent, match, saveCode } = this.props; - const { codeSaving } = this.state; - return ( - - -
    - - -
    -
    -
    -
    - { codeSaving ? - 保存中... - : this.saveCode(this.extend_editor.getValue())} - okText="确定" cancelText="取消"> - {/* onClick={this.saveCode} - onClick={() => saveCode(this.extend_editor.getValue())} - */} - 保存 - } -
    -
    -
    - - -
    - -
    -
    - -
    - - ); - } -} -export default RepositoryCodeEditor; diff --git a/public/react/src/tpm/shixunchild/Repository/RepositoryCombinePath.js b/public/react/src/tpm/shixunchild/Repository/RepositoryCombinePath.js deleted file mode 100644 index aba008e20..000000000 --- a/public/react/src/tpm/shixunchild/Repository/RepositoryCombinePath.js +++ /dev/null @@ -1,82 +0,0 @@ -import React, { Component } from 'react'; - -import { Redirect } from 'react-router'; - -import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom"; - -import PropTypes from 'prop-types'; - -import classNames from 'classnames'; - -import axios from 'axios'; - -import { trace_collapse, WordsBtn } from 'educoder' - -import { message, Input } from 'antd'; - - -const $ = window.$; - - -class RepositoryCombinePath extends Component { - constructor(props) { - super(props) - this.state = { - value: this.props.secret_dir_path || '', - isEdit: false, - } - } - - onSave = () => { - const { shixunId, pathArray } = this.props; - const url = `/shixuns/${shixunId}/set_secret_dir.json` - - this.setState({ codeSaving: true }) - axios.post(url, { - secret_dir_path: this.state.value - } - ).then((response) => { - if (response.data) { - message.success('保存成功'); - this.setState({isEdit: false}) - } - }) - } - onChange = (e) => { - const { value } = e.target; - this.setState({ value }) - } - onEdit = () => { - this.setState({isEdit: true}, () => { - window.$('.combinePathEditRow input')[0].focus() - }); - } - render() { - const { fileContent, match, saveCode } = this.props; - const { isEdit, value } = this.state; - return ( - -
    - - 第一版本库合并路径: - - {!isEdit && 修改} - {isEdit && 保存} -
    - - - ); - } -} -export default RepositoryCombinePath; diff --git a/public/react/src/tpm/shixunchild/Repository/RepositoryDirectories.js b/public/react/src/tpm/shixunchild/Repository/RepositoryDirectories.js deleted file mode 100644 index 7c6eca37a..000000000 --- a/public/react/src/tpm/shixunchild/Repository/RepositoryDirectories.js +++ /dev/null @@ -1,66 +0,0 @@ -import React, { Component } from 'react'; - -import { Redirect } from 'react-router'; - -import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom"; - -import PropTypes from 'prop-types'; - -import classNames from 'classnames'; - -import axios from 'axios'; - -import { trace_collapse } from 'educoder' -const $ = window.$; - - -class RepositoryDirectories extends Component { - constructor(props) { - super(props) - this.state = { - - } - } - componentDidMount() { - } - render() { - const { match, pathArray, fetchRepo - } = this.props; - let { RepositoryList } = this.state; - return ( - - - { pathArray.length !== 0 && -
    - fetchRepo(0)} - > - {match.params.shixunId} - - / - { pathArray.map((item, index) => { - // /shixuns/3ozvy5f8/repository/3ozvy5f8/master/shixun_show/src - return ( - - { this.props.nameTypeMap[item] === 'tree' || item.indexOf('.') === -1 - ? fetchRepo(index + 1)} - className="color-blue"> - {item} - : - - {item} - } - {index !== pathArray.length - 1 && /} - - ) - }) - } -
    } - -
    - - ); - } -} -export default RepositoryDirectories; diff --git a/public/react/src/tpm/shixunchild/Repository/TPMRepositoryCommits.js b/public/react/src/tpm/shixunchild/Repository/TPMRepositoryCommits.js deleted file mode 100644 index 663c5fcf3..000000000 --- a/public/react/src/tpm/shixunchild/Repository/TPMRepositoryCommits.js +++ /dev/null @@ -1,145 +0,0 @@ -import React, { Component } from 'react'; - -import { Redirect } from 'react-router'; - -import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom"; - -import PropTypes from 'prop-types'; - -import classNames from 'classnames'; - -import axios from 'axios'; - -import TPMNav from '../../component/TPMNav' -import TPMRightSection from '../../component/TPMRightSection' -import { CircularProgress } from 'material-ui/Progress'; - -import { trace_collapse } from 'educoder' -const $ = window.$; - -// 点击按钮复制功能 -function jsCopy(){ - var e = document.getElementById("copy_rep_content"); - e.select(); - document.execCommand("Copy"); -} -class TPMRepositoryCommits extends Component { - constructor(props) { - super(props) - this.state = { - RepositoryList: undefined, - } - } - componentDidMount() { - let id = this.props.match.params.shixunId; - - let collaborators=`/shixuns/`+id+`/commits.json`; - axios.post(collaborators, { - secret_repository: this.props.secret_repository_tab - }).then((response)=> { - - if(response.status===200){ - this.setState({ - RepositoryList: response.data - }); - } - trace_collapse('repo commits res', response.data) - - }).catch((error)=>{ - console.log(error) - }); - - } - render() { - const { loadingContent, creator, shixun, myshixun, recommend_shixuns, current_user, watched, - aboutFocus, user, match - } = this.props; - let { RepositoryList } = this.state; - return ( - - -
    - {/* 可能会影响到其他页面的样式,需要测试、协商 */} -
    - - { loadingContent ? - - : - -
    -
    - - 提交记录 - - {/*  35 */} - - 返回 - -
    - - -
    -
      - { RepositoryList === undefined ? "" : RepositoryList.commits.map( (item, key)=>{ - return ( -
    • - {item.email} -

      - {item.title} -

      - {item.time} - -
      -
    • ) - }) - } -
    -
    -
    - } -
    - -
    - -
    -
    - - -
    - - ); - } -} - -/** - { RepositoryList === undefined ? "" : RepositoryList.commits.map( (item, key)=>{ - // {"email":"李暾","title":"2\n","id":"80cb6fc55a14bdd64a9c99913f416966238ed3de","time":"49年前"} - return ( -
    -
    {item.email}
    -
    {item.title}
    -
    {item.id}
    -
    {item.time}
    -
    - ) - }) - */ -export default TPMRepositoryCommits; diff --git a/public/react/src/tpm/shixunchild/ShixunDiscuss/ShixunDiscuss.js b/public/react/src/tpm/shixunchild/ShixunDiscuss/ShixunDiscuss.js deleted file mode 100644 index 4e9470f89..000000000 --- a/public/react/src/tpm/shixunchild/ShixunDiscuss/ShixunDiscuss.js +++ /dev/null @@ -1,170 +0,0 @@ -import React, { Component } from 'react'; - -import { Redirect } from 'react-router'; - -import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom"; - -import PropTypes from 'prop-types'; - -import classNames from 'classnames'; - -import { getImageUrl, toPath } from 'educoder' - -import axios from 'axios'; - -const $ = window.$; - -class ShixunDiscuss extends Component { - constructor(props) { - super(props) - this.state = { - TPMRightSectionData: undefined - } - } - getshixunsDetailsList = (id) => { - let shixunsDetailsURL = `/shixuns/` + id + `/discusses.json`; - axios.get(shixunsDetailsURL).then((response) => { - if (response.status === 200) { - this.setState({ - TPMRightSectionData: response.data - }); - } - }).catch((error) => { - console.log(error) - }); - } - - componentDidMount() { - let id = this.props.match.params.shixunId; - setTimeout(this.getshixunsDetailsList(id), 1000); - } - render() { - let { TPMRightSectionData } = this.state; - - return ( -
    -
    -
    - - - { - TPMRightSectionData===undefined?"":TPMRightSectionData.map((item,key)=>{ - return( -
    -
    - - 用户头像 - -
    - -
    -
    -
    - -
    -
    - {item.user.name} - {item.time} - [第{item.round}关] -
    -
    - -
    -
    -

    {item.content}

    -
    -
    -
    -
    - - { - item.replies.map((i,k)=>{ - return( -
    -
    -
    -
    -
    - {i.user.name} - {i.time} -
    -

    - - - - - - - -

    -
    -
    -
    -
    -

    {i.content}

    -
    -
    -
    -
    -
    -
    - - ) - }) - } - - -

    - - - - - - - - - - - - - - - - - 3 - - -

    - - -
    -
    - - 0?1442652658 - -
    - -
    -
    - -
    -
    调整高度
    - 发送 -
    -
    -
    -
    -
    -
    - ) - }) - } - -
    -
    -
    - ) - } - } - - export default ShixunDiscuss; diff --git a/public/react/src/tpm/shixunchild/Shixunfork_list.js b/public/react/src/tpm/shixunchild/Shixunfork_list.js deleted file mode 100644 index f813441f1..000000000 --- a/public/react/src/tpm/shixunchild/Shixunfork_list.js +++ /dev/null @@ -1,69 +0,0 @@ -import React, { Component } from 'react'; -import { Redirect } from 'react-router'; - -import { BrowserRouter as Router, Route, Link} from "react-router-dom"; - -import { Switch } from 'antd'; - -import PropTypes from 'prop-types'; - -import classNames from 'classnames' - -import { TPMIndexHOC } from '../TPMIndexHOC' - -import { SnackbarHOC } from 'educoder' - -import ShixunCard from '.././shixuns/ShixunCard'; - -import { Pagination,Row,Col,Rate } from 'antd'; - - import './shixunchildCss/Shixunfork_list.css'; - -import 'antd/lib/rate/style/index.css'; - -const $ = window.$; - -class Shixunforklist extends Component { - constructor(props) { - super(props) - this.state = { - - } - } - - handleChange = (value) => { - console.log('Page: ', value); - // this.setState({ value }); - } - //JSX - render() { - const { match, history } = this.props - - return ( -
    - -
    -
    - Fork实训列表 - 返回 -
    - - - - - - -
    -
    -
      - -
    -
    -
    -
    -
    -
    - ); - } -} -export default SnackbarHOC() (TPMIndexHOC ( Shixunforklist )); diff --git a/public/react/src/tpm/shixunchild/shixunchildCss/Challenges.css b/public/react/src/tpm/shixunchild/shixunchildCss/Challenges.css deleted file mode 100644 index 493a95301..000000000 --- a/public/react/src/tpm/shixunchild/shixunchildCss/Challenges.css +++ /dev/null @@ -1,28 +0,0 @@ -.editormd-html-preview, .editormd-preview-container { - width: 95% !important; -} -.Finish_button{ - height: 30px; - line-height: 30px; - margin-top: -8px; -} -.startbtnModal .ant-modal-content{ - background: transparent; - box-shadow: 0 4px 12px transparent; -} - -.startbtnModal .ant-modal-content .ant-modal-body .ant-spin-spinning{ - margin-left: 45%; -} - -.color05101a{ - color:#05101a; -} - -.mtf3{ - margin-top: -3px; -} -.addshixuns{ - height: 27px; - line-height: 25px; -} \ No newline at end of file diff --git a/public/react/src/tpm/shixunchild/shixunchildCss/Shixunfork_list.css b/public/react/src/tpm/shixunchild/shixunchildCss/Shixunfork_list.css deleted file mode 100644 index d6ef5ebfe..000000000 --- a/public/react/src/tpm/shixunchild/shixunchildCss/Shixunfork_list.css +++ /dev/null @@ -1,6 +0,0 @@ -.ant-rate{ - color: #FFAA05 !important; -} -.ant-pagination-options-quick-jumper input{ - height: 22 !important; -} \ No newline at end of file diff --git a/public/react/src/tpm/shixuns/ShixunCard.js b/public/react/src/tpm/shixuns/ShixunCard.js deleted file mode 100644 index 9f62ed6b7..000000000 --- a/public/react/src/tpm/shixuns/ShixunCard.js +++ /dev/null @@ -1,200 +0,0 @@ -import React, { Component } from 'react'; -import { Redirect } from 'react-router'; - -import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom"; - -import PropTypes from 'prop-types'; - -import classNames from 'classnames'; - -import { Rating ,Pagination} from "@icedesign/base"; - -import {getImageUrl,setImagesUrl, toPath,getUrl} from 'educoder'; - -import { Spin,Icon,Tooltip ,Rate} from 'antd'; -import LoadingSpin from '../../../common/LoadingSpin'; -import './shixunCss/shixunCard.css'; - -// 引入业务组件样式 - -import axios from 'axios'; - -const $ = window.$; - -class ShixunCard extends Component { - constructor(props) { - super(props) - - this.state = { - startValue:[], - order_by:"", - page:1, - limit:16, - keyword:"", - status:0, - diff:0, - hideme:false, - tag_level:3, - tag_id:'' - } - - } - - PaginationonChange=(pageNumber)=> { - this.props.shixunsPage(pageNumber); - } - - render() { - let {middleshixundata, pagination, typepvisible, pages, totalcount} = this.props; - const MyRate = ({ defaultValue, ...rest }) => { - let myValue = defaultValue; - // console.log(myValue-Math.floor(myValue)) - // if (myValue < Math.ceil(myValue)) { - // myValue = Math.floor(myValue) + 0.5; - // } - - return ; - }; - return ( -
    - - - - { middleshixundata === undefined?"":middleshixundata.length === 0 ?
    - - -

    暂时还没有相关数据哦!

    -
    :""} - - -
    -
    -
    - {middleshixundata === undefined || middleshixundata.length === 0?" ":middleshixundata.map((item,key)=>{ - return( -
    - - { - item.tag_name === null ? "": -
    - {item.tag_name} - {/**/} -
    - } -
    - -

    非试用内容,需要授权

    -
    - - - {/**/} - - - {/*target="_blank"*/} - -
    -

    - - {item.name} - -

    - - {/*target="_blank"*/} - {/**/} -

    - - {/**/} - - - {item.score_info===null?"5分":item.score_info+"分"} -

    - -

    - - - {item.challenges_count} - - - - {/**/} - {/**/} - {/*{item.exp}*/} - {/**/} - {/**/} - - - - {item.stu_num} - - - - {item.level} -

    - -
    -
    - ) - }) - } - -
    - -
    - {/*totalcount*/} -
    - {/**/} - {/* 不加参数请求的时候,没返回总数了。加了个比较大的数字,让他可以翻页 */} - -
    - -
    - -
    -
    -
    - ) - } -} - -export default ShixunCard; diff --git a/public/react/src/tpm/shixuns/ShixunCardList.js b/public/react/src/tpm/shixuns/ShixunCardList.js deleted file mode 100644 index d95ef75fe..000000000 --- a/public/react/src/tpm/shixuns/ShixunCardList.js +++ /dev/null @@ -1,253 +0,0 @@ -import React, { Component } from 'react'; -import { Redirect } from 'react-router'; - -import { BrowserRouter as Router, Route} from "react-router-dom"; - -import { Switch ,Input,Tooltip,Icon} from 'antd'; - -import PropTypes from 'prop-types'; - -import classNames from 'classnames' - -import 'antd/lib/switch/style/index.css' - -import './shixunCss/ShixunCardList.css'; - -import { on, off } from 'educoder' - -const $ = window.$; - -const Search = Input.Search; - -class ShixunCardList extends Component { - - constructor(props) { - super(props); - this.state={ - allevent:"desc", - mine:0, - InputValue: props.keyword || "", - typemy:0, - hots:0, - news:0, - shixunid:"", - upcircle:false, - typekeyid:undefined, - } - } - - componentDidUpdate = (prevProps, prevState) => { - if (this.props.keyword != prevProps.keyword) { - this.setState({ - InputValue: this.props.keyword - }) - } - } - componentDidMount = () => { - on('searchKeywordChange', (event, data) => { - // console.log(data) - this.Input_search(data) - }) - } - componentWillUnmount = () => { - off('searchKeywordChange') - } - - - latestHot=(e,key)=>{ - - let{upcircle,typekeyid}=this.state; - - let id = e.target.id; - $("#"+id).siblings().removeClass("active"); - $("#"+id).addClass("active"); - - let type; - - // if(id==="all"){ - // type="publish_time"; - // } - if(id==="hot"){ - type="hot"; - }else if(id==="new"){ - type="new"; - - } - if(typekeyid===key){ - if(upcircle===true){ - this.setState({ - upcircle:false, - }) - this.props.Shixunsupcircles("desc") - }else if(upcircle===false){ - this.setState({ - upcircle:true, - }) - this.props.Shixunsupcircles("asc") - } - }else{ - this.setState({ - typekeyid:key - }) - } - - //allevent - this.props.ShixunsState(false,type); - } - - - onSwitchChange=(e,key)=>{ - let id=e.target.id - $("#"+id).siblings().removeClass("active"); - $("#"+id).addClass("active"); - let {typemy,upcircle,typekeyid}=this.state; - - if(typekeyid===key){ - if(upcircle===true){ - this.setState({ - upcircle:false, - }) - this.props.Shixunsupcircles("desc") - }else if(upcircle===false){ - this.setState({ - upcircle:true - }) - this.props.Shixunsupcircles("asc") - } - }else{ - this.setState({ - typekeyid:key - }) - } - - - if(typemy===0){ - this.setState({ - typemy:1 - }) - }else{ - this.setState({ - typemy:0 - }) - } - // allevent - this.props.ShixunsSwitch(); - } - //输入框搜索 - Input_search = (value) => { - this.setState({ - InputValue: value - }) - this.props.OnSearchInput(value,true); - } - - Input_searchs = (e) => { - this.setState({ - InputValue: e.target.value - }) - this.props.OnSearchInput(e.target.value,false); - } - upcircles=(val)=>{ - if(val==="asc"){ - this.setState({ - upcircle:false, - }) - this.props.Shixunsupcircles("desc") - }else if(val==="desc"){ - this.setState({ - upcircle:true - }) - this.props.Shixunsupcircles("asc") - } - } - render(){ - let {mine,InputValue,upcircle}=this.state; - return ( -
    -
    - - {/*
    this.latestHot(e,1)}>全部*/} - {/*
    */} - {/*
    this.onSwitchChange(e,2)}>我的*/} - {/*
    */} - -
    this.latestHot(e,4)}>最新 -
    - -
    this.latestHot(e,3)}>最热 -
    - - - {/*
    this.upcircles("asc")}*/} - {/*>*/} - {/**/} - {/**/} - {/*/!**!/*/} - {/**/} - {/*
    */} - {/*
    this.upcircles("desc")}*/} - {/*style={{display:upcircle===true?"none":"block"}}*/} - {/*>*/} - {/**/} - {/**/} - {/*/!**!/*/} - {/**/} - {/*
    */} - - {/*
    */} - {/* this.Input_search(value)}*/} - {/*enterButton*/} - {/*/>*/} - - {/* this.Input_search(value)} - autoComplete="off" - > */} - {/*
    */} - {/*
    */} - {/*{*/} - {/*this.props.search_tags === null ? "" : this.props.search_tags*/} - {/*}*/} - {/*
    */} - {/*/!*
    */} - {/* *!/*/} - {/**/} - {/*
    */} - {/*隐藏我的*/} - - {/*
    */} - {/**/} -
    -
    - ); - } -} - -export default ShixunCardList; diff --git a/public/react/src/tpm/shixuns/ShixunSearchBar.js b/public/react/src/tpm/shixuns/ShixunSearchBar.js deleted file mode 100644 index f9c4a7936..000000000 --- a/public/react/src/tpm/shixuns/ShixunSearchBar.js +++ /dev/null @@ -1,292 +0,0 @@ -import React, { Component } from 'react'; - -import { Select, Input,Menu, Dropdown } from 'antd'; - -import 'antd/lib/style/index.css'; - -import 'antd/lib/select/style/index.css'; - -import 'antd/lib/input/style/index.css'; - -import './shixunCss/ShixunSearchBar.css'; - -import axios from 'axios'; - -const $ = window.$; - -const Option = Select.Option; - -const Search = Input.Search; - - -class ShixunSearchBar extends Component { - - constructor(props) { - super(props) - this.state = { - status: undefined, - diff: 0, - InputValue: undefined, - shixunhoverData: [], - shixunchildValues:'', - shixunsearchAllvalue:"a", - openStatus:false, - openLevel:false - } -} - - //状态筛选 - status_search = (value) => { - let newvalue = value; - if (newvalue === "0") { - newvalue = " " - } else if (newvalue === "1") { - newvalue = 2 - } else if (newvalue === "2") { - newvalue = 1 - } else if (newvalue === "3") { - newvalue = 3 - } - - this.setState({ - status: newvalue, - openStatus:false - }) - let list = [{'type': 1}, {'value': newvalue}]; - this.props.StatusEnquiry(list); -} - - //难度筛选 -diff_search = (value) => { - this.setState({ - diff: value, - openLevel:false - }) - let list=[{'type':2},{'value':value}]; - this.props.StatusEnquiry(list); -} - - //输入框搜索 -Input_search = (value) => { - this.setState({ - InputValue: value - }) - this.props.OnSearchInput(value); -} - //查询 -shixunsearchAll = (e) => { - let{shixunsearchAllvalue}=this.state; - let id = e.target.value; - - if(shixunsearchAllvalue===id){ - return - } - if(id===0){ - id=" " - this.setState({ - InputValue: " " - }) - this.props.OnSearchInput(""); - } - let list=[{'tag_level':1},{'tag_id':id}]; - if(id!=undefined){ - this.setState({ - shixunsearchAllvalue:id, - shixunchildValues:"" - }) - this.props.Updatasearchlist(list); - } - -} - - shixunsearchall=(e)=>{ - let{shixunsearchAllvalue}=this.state; - let id = "a"; - - if(shixunsearchAllvalue===id){ - return - } - this.setState({ - shixunsearchAllvalue:"a", - shixunchildValues:"" - }) - this.props.allUpdatashixunlist(); - } - - //选择Tab页详情 - getshixunchildValue = (e) => { - - debugger - let id = e.target.name; - let newid=e.target.id; - let list=[{'tag_level':2},{'tag_id':id}]; - if(id!=undefined||newid!=undefined){ - this.setState({ - shixunsearchAllvalue:newid - }) - this.props.Updatasearchlist(list); - } - } - -getshixunchildValues = (e) => { - let id = e.target.id; - let newid=e.target.name; - let list=[{'tag_level':3},{'tag_id':id}]; - if(id!=undefined||newid!=undefined){ - this.setState({ - shixunchildValues:id, - shixunsearchAllvalue:newid - }) - this.props.Updatasearchlist(list); - } - -} - -componentDidMount() { - let hoverUrlArr = []; - let hoverUrl = `/shixuns/menus.json`; - axios.get(hoverUrl - ).then((response) => { - hoverUrlArr = response.data; - // hoverUrlArr.reverse(); - this.setState({ - shixunhoverData: hoverUrlArr - }) - }).catch((error) => { - console.log(error) - }) -} - -render() { - let {shixunhoverData, shixunchildValues, shixunsearchAllvalue, InputValue,openStatus,openLevel} = this.state; - let {typepvisible} = this.props; - // //实训首页筛选的移入和点击事件 - // $(".shaiItem").hover(function(){ - // var hei=parseInt($(".shaiAllItem").height())-2; - // $(this).find(".subshaicontent").css("top", '34px'); - // $(this).find(".subshaicontent").show(); - // },function(){ - // $(this).find(".subshaicontent").hide(); - // }); - // - // $(".shaiItem").live("click",function(){ - // $(".shaiItem").removeClass("active"); - // $(this).addClass("active"); - // $(".subshaicontent").hide(); - // }); - // - // $(".subshaicontent").live("click", function(event){ - // $(".subshaicontent").hide(); - // event.stopPropagation(); - // }); - - let overlaymenu=(item,id)=>( - - { - item.map((list,k)=>{ - return( - -
    - {list.name} -
    - { - list.tags.map((tag,e)=>{ - return( - {tag.name} - ) - }) - } -
    -
    -
    - ) - }) - } -
    - ) - - return ( -
    -
    -
    -
    - 方向: -
    -
  • 全部
  • - - { - shixunhoverData.map((item,key)=>{ - return( - -
  • - {item.name} -
  • -
    - ) - }) - } - - -
    -
    -
    - 筛选: - { - - } -
    -
  • this.diff_search(0)}>全部难度
  • -
  • this.diff_search(1)}>初级学员
  • -
  • this.diff_search(2)}>中级学员
  • -
  • this.diff_search(3)}>高级学员
  • -
  • this.diff_search(4)}>顶级学员
  • -
    - -
    -
    -
    -
    - ); -} -} - -export default ShixunSearchBar; diff --git a/public/react/src/tpm/shixuns/ShixunsIndex.js b/public/react/src/tpm/shixuns/ShixunsIndex.js deleted file mode 100644 index 15579610d..000000000 --- a/public/react/src/tpm/shixuns/ShixunsIndex.js +++ /dev/null @@ -1,422 +0,0 @@ -import React, { Component } from 'react'; - -import { Redirect } from 'react-router'; - -import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom"; - -import axios from 'axios'; - -import { Spin } from 'antd'; - -import { TPMIndexHOC } from '../TPMIndexHOC'; - -import { SnackbarHOC } from 'educoder'; - -import ShixunCardList from './ShixunCardList'; - -import ShixunSearchBar from './ShixunSearchBar'; - -import ShixunCard from './ShixunCard'; - -import UpgradeModals from '../../modals/UpgradeModals'; - -const queryString = require('query-string'); - -const $ = window.$; - -class ShixunsIndex extends Component { - constructor(props) { - super(props) - this.state={ - order_by: "new", - page:1, - limit:16, - keyword:"", - status:0, - diff:0, - tag_level: 1, - tag_id:'', - middleshixundata:[], - typepvisible:true, - pages:1, - search_tags:null, - parsedid:undefined, - newtag_level:undefined, - newpalce:undefined, - sort:"desc" - } - } - componentDidMount(){ - - const upsystem=`/users/system_update.json`; - axios.get(upsystem).then((response)=>{ - let updata=response.data; - this.setState({ - updata:updata - }) - }).catch((error)=>{ - console.log(error); - }) - - - - let _keyword; - if (window.__headSearchKeyword) { - this.setState({ keyword: window.__headSearchKeyword }) - _keyword = window.__headSearchKeyword - delete window.__headSearchKeyword - } - const parsed = queryString.parse(this.props.location.search); - if(parsed.id===undefined&&parsed.type===undefined){ - let {order_by, tag_level, tag_id, page, limit, keyword, status, diff} = this.state; - let params={ - order_by:order_by, - tag_level:tag_level, - tag_id:tag_id, - page:page, - limit:limit, - keyword: _keyword || keyword , - status:status, - diff:diff, - sort: "desc" - } - this.shixunresultend(params); - }else{ - let {order_by,page, limit, keyword, status, diff} = this.state; - let nawparsed=parsed.type; - let newpalce=parsed.palce; - if(nawparsed==="rep"){ - nawparsed=1 - } - else if(nawparsed==="sub"){ - nawparsed=2 - }else if(nawparsed==="tag"){ - nawparsed=3 - } - let params={ - order_by:order_by, - tag_level:nawparsed, - tag_id:parsed.id, - page:page, - limit:limit, - keyword: _keyword || keyword, - status:status, - diff:diff, - sort: "desc" - } - this.setState({ - parsedid:parsed.id, - newtag_level:nawparsed, - newpalce:newpalce - }) - this.shixunresultend(params); - } - - } - - allUpdatashixunlist=()=>{ - let{sort,order_by}=this.state; - - this.setState({ - tag_level: 1, - tag_id:'', - page: 1, - limit: 16, - keyword:'', - status: 0, - diff: 0, - }) - - let params={ - order_by:order_by, - tag_level: 1, - tag_id:'', - page: 1, - limit: 16, - keyword:'', - status: 0, - diff: 0, - sort:sort - } - this.shixunresultend(params) - } - Updatasearchlist=(value)=>{ - if (value[1].tag_id === " ") { - this.setState({ - keyword: "" - }) - } - this.setState({ - tag_level:value[0].tag_level, - tag_id:value[1].tag_id, - typepvisible:true - }) - - let {order_by, sort, limit, keyword, status, diff} = this.state; - - let params={ - order_by:order_by, - tag_level:value[0].tag_level, - tag_id:value[1].tag_id, - page:1, - limit:limit, - keyword:keyword, - status:status, - diff:diff, - sort:sort - } - - this.shixunresultend(params) - } - - StatusEnquiry=(key)=>{ - - let Vrl=`/shixuns.json`; - let newstatus; - let newdiff; - if(key[0].type===1){ - this.setState({ - status: key[1].value, - typepvisible:true - }) - newstatus=key[1].value; - newdiff=this.state.diff; - }else if(key[0].type===2){ - this.setState({ - diff: key[1].value, - typepvisible:true - }) - newdiff=key[1].value; - newstatus=this.state.status; - } - let params= { - order_by:this.state.order_by, - tag_level:this.state.tag_level, - tag_id:this.state.tag_id, - page:1, - limit:this.state.limit, - keyword:this.state.keyword, - status:newstatus, - diff:newdiff, - } - this.shixunresultend(params) - - } - - OnSearchInput=(value,type)=>{ - if(type===true){ - this.setState({ - keyword:value, - typepvisible:true, - pages:1 - }) - let {order_by, tag_level, tag_id, sort, limit, status, diff} = this.state; - let params= { - order_by:order_by, - tag_level:tag_level, - tag_id:tag_id, - page:1, - limit:limit, - keyword:value, - status:status, - diff:diff, - sort:sort - } - this.shixunresultend(params) - }else{ - this.setState({ - keyword:value, - pages:1 - }) - } - - - } - - ShixunsSwitch=()=>{ - //types - this.setState({ - order_by:"mine", - typepvisible:true, - pages:1, - }) - let{tag_level,tag_id,page,limit,keyword,status,diff,sort}=this.state; - let newsort=sort; - if(newsort===undefined){ - newsort="desc" - } - let params= { - order_by:"mine", - tag_level:tag_level, - tag_id:tag_id, - page:1, - limit:limit, - keyword:keyword, - status:status, - diff:diff, - sort:newsort - } - this.shixunresultend(params) - } - - - shixunsPage=(value)=>{ - this.setState({ - page:value, - typepvisible:true, - pages:value - }) - let {order_by, tag_level, tag_id, limit, keyword, status, diff,sort} = this.state; - let params= { - order_by:order_by, - tag_level:tag_level, - tag_id:tag_id, - page:value, - limit:limit, - keyword:keyword, - status:status, - diff:diff, - sort:sort - } - - let Url=`/shixuns.json`; - axios.get(Url,{ - params - }).then((response)=> { - if(response.status===200){ - this.setState({ - middleshixundata: response.data, - typepvisible:false, - }); - } - }).catch((error)=>{ - console.log(error) - }); - } - ShixunsState=(val,type)=>{ - // sort, - let {tag_level, tag_id, page, limit, keyword, status, diff,sort} = this.state; - let newsort=sort; - this.setState({ - order_by:type, - typepvisible:true, - pages:1, - // sort:sort - }) - - let params - // let vals=false - if(newsort===undefined){ - newsort="desc" - } - params= { - order_by:type, - tag_level:tag_level, - tag_id:tag_id, - page:1, - limit:limit, - keyword:keyword, - status:status, - diff:diff, - sort:newsort - } - this.shixunresultend(params) - } - - Shixunsupcircles=(sort)=>{ - console.log(sort) - this.setState({ - sort:sort - }) - let { - order_by, - tag_level, - tag_id, - limit, - keyword, - status, - diff, - } = this.state; - - - - let params= { - order_by:order_by, - tag_level:tag_level, - tag_id:tag_id, - page:1, - limit:limit, - keyword:keyword, - status:status, - diff:diff, - sort:sort - } - this.shixunresultend(params) - } - - - - - shixunresultend=(params)=>{ - let Url=`/shixuns.json`; - axios.get(Url,{ - params - }).then((response)=> { - // TODO 有keyword返回值时 显示一共有多少条记录 - if(response.status===200){ - this.setState({ - search_tags:response.data.search_tags, - middleshixundata: response.data, - typepvisible:false, - pages:1 - }); - } - }).catch((error)=>{ - console.log(error) - }); - } - render() { - let {middleshixundata, typepvisible, pages, search_tags, keyword,parsedid,newtag_level,newpalce} = this.state; - - // console.log(this.state.updata) - return ( -
    - {this.state.updata===undefined?"":} - {/**/} - - - - - - {/**/} -
    - ); - } -} - -export default SnackbarHOC() (TPMIndexHOC ( ShixunsIndex )); diff --git a/public/react/src/tpm/shixuns/css/TPMBanner.css b/public/react/src/tpm/shixuns/css/TPMBanner.css deleted file mode 100644 index fe059fccd..000000000 --- a/public/react/src/tpm/shixuns/css/TPMBanner.css +++ /dev/null @@ -1,114 +0,0 @@ -.shixunsdiffcult{ - width: 40px; - height: 21px; - overflow: hidden; - margin-left: 8px; - } - - .rateYo{ - text-align: center; - cursor: default; - width: 111px; - } - - a:link, a:visited { - color: #05101a; -} - -a:link{text-decoration:none;} - -a:visited{text-decoration:none;} - -a:hover{text-decoration:none;} - -a:active{text-decoration:none;} - - -.next-rating-overlay .next-icon{ - color: #FFA800!important; -} - -.displayblock{ - display:block; - text-align: center; - margin-bottom: 20px; -} - -.totalScore{ - justify-content: center; - align-items: center; - display: -webkit-flex; - height: 100%; -} - -.next-progress-line{ - width: 210px !important; - margin-left: 10px; - margin-top: 4px; -} - -.next-progress-line-overlay-normal{ - background-color: #FFA800 !important; -} -.next-rating-base-disabled{ - cursor: default!important; -} -/*#challenge_begin {*/ -/*!*height: 40px !important;*!*/ -/*line-height: 30px;*/ -/*}*/ -.ant-modal-title{ - font-size: 16px; - font-weight: bold !important; - color: #333; -} - -.ml60{ - margin-left:20px; -} - -.marginauto{ - margin:0 auto; -} -.margin152{ - margin-left: 152px; -} - -.margin-tp26{ - margin-top: -26px; -} -.edu-h315{ - height:315px; -} - -.height39 { - height: 39px !important; -} - -#commentsStar{ - margin-top: -7px; - width: 90px; - height: 80px; -} - -.startbtnModal .ant-modal-content{ - background: transparent; - box-shadow: 0 4px 12px transparent; -} - -.startbtnModal .ant-modal-content .ant-modal-body .ant-spin-spinning{ - margin-left: 45%; -} - -.mr51{ - margin-right:51px; -} - -.flexbannerright{ - display: flex; - justify-content: flex-end; -} - -.width360{ - width:360px; -} \ No newline at end of file diff --git a/public/react/src/tpm/shixuns/shixunCss/ShixunCardList.css b/public/react/src/tpm/shixuns/shixunCss/ShixunCardList.css deleted file mode 100644 index c806434f5..000000000 --- a/public/react/src/tpm/shixuns/shixunCss/ShixunCardList.css +++ /dev/null @@ -1,13 +0,0 @@ -#myshixuns_count{ - text-decoration:none !important; -} -#created_at{ - text-decoration:none !important; -} -.shixun_repertoire{ - cursor: pointer ; -} -.next-btn-medium:hover{ - color: #4CACFF; - border:1px solid #4CACFF; -} \ No newline at end of file diff --git a/public/react/src/tpm/shixuns/shixunCss/ShixunSearchBar.css b/public/react/src/tpm/shixuns/shixunCss/ShixunSearchBar.css deleted file mode 100644 index 9fba271ce..000000000 --- a/public/react/src/tpm/shixuns/shixunCss/ShixunSearchBar.css +++ /dev/null @@ -1,20 +0,0 @@ -.iconfontShixunSearchBar{ - z-index: 1000; - position: absolute; - right: 3px; - top: 0px; -} - -.diffSelect{ - margin-left:20px !important; - } - .ant-input-search-button{ - /*margin-right: 10px;*/ - border: 1px solid transparent; - } -.Mousebox{ - width: 800px !important; -} -.subshaicontent a{ - height:30px; -} \ No newline at end of file diff --git a/public/react/src/tpm/shixuns/shixunCss/shixunCard.css b/public/react/src/tpm/shixuns/shixunCss/shixunCard.css deleted file mode 100644 index 1ec00a26e..000000000 --- a/public/react/src/tpm/shixuns/shixunCss/shixunCard.css +++ /dev/null @@ -1,42 +0,0 @@ -.ml350 { - margin-left: 40%; -} - -.ml32 { - margin-left: 32%; -} - -.square-img{ - min-height: 210px; -} -.task-hide{ - margin-bottom: 0em; -} -.backFAFAFA{ - background:#FAFAFA; -} - -.demo { - width: 500px; - background-color: #0dcecb; - text-align: center; - padding:50px; -} -.next-loading { - margin-bottom: 5px; - width:100%; -} - -.next-rating-overlay .next-icon{ - color: #FFA800!important; -} - -.custom-pagination { - display: inline-block; - margin-left: 10px; -} - -.ml425{ - margin-left:42.5%; - margin-top:20px; -} \ No newline at end of file diff --git a/public/react/src/tpm/shixuns/shixunCss/tag2.png b/public/react/src/tpm/shixuns/shixunCss/tag2.png deleted file mode 100644 index 423d2f7e3..000000000 Binary files a/public/react/src/tpm/shixuns/shixunCss/tag2.png and /dev/null differ diff --git a/public/react/src/tpm/shixuns/shixusFunction/ShixunSearchBar.js b/public/react/src/tpm/shixuns/shixusFunction/ShixunSearchBar.js deleted file mode 100644 index 4211e3196..000000000 --- a/public/react/src/tpm/shixuns/shixusFunction/ShixunSearchBar.js +++ /dev/null @@ -1,142 +0,0 @@ -const $ = window.$; - - -$(function(){ - //实训首页筛选的移入和点击事件 - $(".shaiItem").hover(function(){ - var hei=parseInt($(".shaiAllItem").height())-2; - $(this).find(".subshaicontent").css("top",hei); - $(this).find(".subshaicontent").show(); - },function(){ - $(this).find(".subshaicontent").hide(); - }); - - $(".shaiItem").live("click",function(){ - $(".shaiItem").removeClass("active"); - $(this).addClass("active"); - $(".subshaicontent").hide(); - }); - - $(".subshaicontent").live("click", function(event){ - $(".subshaicontent").hide(); - event.stopPropagation(); - }); - - //最新、最热 - $(".bestChoose").click(function(){ - $(".bestChoose").removeClass("active"); - $(this).addClass("active"); - }) - - //实训路径选择导航条 - $(".path-nav li a").live("click",function(){ - $(".path-nav li").removeClass("active"); - $(this).parent().addClass("active"); - }) -}); - -//隐藏我的学习 - function clickControl(item, type){ - var wid=$(item).width(); - var wid1=$(".controlring").width(); - var hidden_course = 1; - if($(".controlring").css("left")=="1px"){ - $(".controlring").animate({left:parseInt(wid-wid1-1)+"px"}); - $(".controlblue").animate({width:wid+"px"}); - $("input[name='hidden_learn']").val('1'); - }else{ - $(".controlring").animate({left:"1px"}); - $(".controlblue").animate({width:"0px"}); - $("input[name='hidden_learn']").val(''); - hidden_course = 0; - } - if(type == "l_shixun"){ - $("#shixun_search_condition").submit(); - } else{ - $.get("/courses?select="+$("#select_type").val()+"&order="+$("#select_order").val()+"&hidden="+hidden_course); - } -} - -// 清空条件 - function clear_style(){ - $("#shixun_search_condition").find('input[type=hidden]').each(function() { - $(this).val(''); - }); -} - -// 精选实训的搜索 #type参数( status:实训状态; diff:实训难度; search:实训搜索; order:最新最热排序) -function filter_search(values, type){ - switch(type){ - case "status": - $("input[name='status']").val(values); - break; - case "diff": - $("input[name='diff']").val(values); - break; - case "search": - $("input[name='search']").val(values); - break; - } - $("#shixun_search_condition").submit(); -} - -// 点击实训体系名称 # type参数(rep:体系大类别; sub:体系子类别; tags 实训标签; order: 排序) -// # name参数: 列表显示使用 -// # values参数: 赋值给表单的值 -$(".shixun_repertoire").live("click", function(event){ - var type = $(this).attr("data-type"); - var name = $(this).attr("data-name"); - var values = $(this).attr("data-values"); - if(type != 'order'){ - $(".subshaicontent a").removeClass("active"); - $(".shaiItem").removeClass("active"); - $("input[name='repertoire'], input[name='sub_repertoire'], input[name='tag_repertoire']").val(''); - } - $(this).closest(".shaiItem").addClass("active"); - $(".subshaicontent").hide(); - $("#search_name").html(name); - - switch(type){ - case "rep": - $("input[name='repertoire']").val(values); - $("#shixun_search_input").val(""); - $("input[name='search']").val(""); - break; - case "sub": - $("input[name='sub_repertoire']").val(values); - break; - case "tag": - $("input[name='tag_repertoire']").val(values); - break; - case "order": - var $sort = $("input[name='sort']"); - var oldValue = $("input[name='order']").val(); - $("input[name='order']").val(values); - var newValue = $("input[name='order']").val(); - if(oldValue != newValue){ - $("input[name='sort']").val("desc"); - }else { - if($sort.val() == "desc"){ - $sort.val("asc"); - }else{ - $sort.val("desc"); - } - } - break; - } - $(this).addClass("active"); // 因为order需要判断样式因此写在switch之后 - $("#shixun_search_condition").submit(); - event.stopPropagation(); -}); - - -// 实训首页回车搜索 -$("#shixun_search_input").live("keyup", function(e){ - // 兼容FF和IE和Opera - var theEvent = e || window.event; - var code = theEvent.keyCode || theEvent.which || theEvent.charCode; - if (code == 13) { - //回车执行查询 - filter_search($(this).val(), "search"); - } -}); \ No newline at end of file diff --git a/public/stylesheets/educoder/edu-main.css b/public/stylesheets/educoder/edu-main.css index 89b62856d..84bca6a78 100644 --- a/public/stylesheets/educoder/edu-main.css +++ b/public/stylesheets/educoder/edu-main.css @@ -674,7 +674,7 @@ input.radio-width90{ width: 90px; } .ringauto{width: 20px;height: 20px;line-height: 20px;text-align: center;border-radius: 50%;background-color: #F4FAFF;margin-right:5px;} /*-------------------个人主页:右侧提示区域--------------------------*/ -.-task-sidebar{position:fixed;width:40px;height:180px;right:0;bottom:30px;z-index: 10;} +.-task-sidebar{position:fixed;width:40px;height:180px;right:0;bottom:80px;z-index: 10;} .-task-sidebar div{height: 40px;line-height: 40px;box-sizing: border-box;width:40px;background:#4CACFF;color:#fff;font-size:20px;text-align:center;margin-bottom:5px;border-radius: 4px;} .-task-sidebar div i{ color:#fff;} .-task-sidebar div i:hover{color: #fff!important;} @@ -820,4 +820,4 @@ html>body #ajax-indicator { position: fixed; } .footer_con-p{ color: #898989 !important; -} \ No newline at end of file +} diff --git a/public/stylesheets/educoder/iconfont/demo_index.html b/public/stylesheets/educoder/iconfont/demo_index.html index c88449b3f..46aed453b 100644 --- a/public/stylesheets/educoder/iconfont/demo_index.html +++ b/public/stylesheets/educoder/iconfont/demo_index.html @@ -90,6 +90,12 @@
    &#xe6bc;
    +
  • + +
    禁止
    +
    &#xe6d4;
    +
  • +
  • vs
    @@ -180,6 +186,12 @@
    &#xe609;
  • +
  • + +
    浏览
    +
    &#xe6c7;
    +
  • +
  • 路由
    @@ -204,6 +216,12 @@
    &#xe66d;
  • +
  • + +
    下箭头
    +
    &#xe700;
    +
  • +
  • 三角形-up
    @@ -450,6 +468,12 @@
    &#xe694;
  • +
  • + +
    博客园
    +
    &#xe6c6;
    +
  • +
  • base
    @@ -522,6 +546,12 @@
    &#xe67d;
  • +
  • + +
    SDK问题
    +
    &#xe7dc;
    +
  • +
  • 银行卡
    @@ -1206,6 +1236,12 @@
    &#xe68c;
  • +
  • + +
    评论
    +
    &#xe6c8;
    +
  • +
  • 工程
    @@ -1224,6 +1260,12 @@
    &#xe604;
  • +
  • + +
    上箭头-填充
    +
    &#xe733;
    +
  • +
  • 主页
    @@ -1266,6 +1308,12 @@
    &#xe6a1;
  • +
  • + +
    省略号
    +
    &#xe708;
    +
  • +
  • 排序
    @@ -1356,6 +1404,12 @@
    &#xe6b7;
  • +
  • + +
    向上 箭头
    +
    &#xe74c;
    +
  • +
  • healthmode
    @@ -1392,6 +1446,318 @@
    &#xe6c4;
  • +
  • + +
    绑定手机号
    +
    &#xe6ca;
    +
  • + +
  • + +
    标签
    +
    &#xe6ce;
    +
  • + +
  • + +
    记录
    +
    &#xe6cf;
    +
  • + +
  • + +
    +
    &#xe6d0;
    +
  • + +
  • + +
    推荐
    +
    &#xe6d1;
    +
  • + +
  • + +
    创建者
    +
    &#xe6d2;
    +
  • + +
  • + +
    创建者
    +
    &#xe6da;
    +
  • + +
  • + +
    +
    &#xe6dc;
    +
  • + +
  • + +
    标签
    +
    &#xe6dd;
    +
  • + +
  • + +
    记录
    +
    &#xe6de;
    +
  • + +
  • + +
    推荐
    +
    &#xe6df;
    +
  • + +
  • + +
    警告
    +
    &#xe6e0;
    +
  • + +
  • + +
    点赞
    +
    &#xe6e1;
    +
  • + +
  • + +
    评论
    +
    &#xe6e2;
    +
  • + +
  • + +
    对勾
    +
    &#xe6e3;
    +
  • + +
  • + +
    提示
    +
    &#xe6e4;
    +
  • + +
  • + +
    编辑_Hover
    +
    &#xe6e5;
    +
  • + +
  • + +
    上移_Hover
    +
    &#xe6e6;
    +
  • + +
  • + +
    删除_默认
    +
    &#xe6e7;
    +
  • + +
  • + +
    下移_Hover
    +
    &#xe6e8;
    +
  • + +
  • + +
    删除_Hover
    +
    &#xe6e9;
    +
  • + +
  • + +
    下移_默认
    +
    &#xe6ea;
    +
  • + +
  • + +
    编辑_默认
    +
    &#xe6eb;
    +
  • + +
  • + +
    恢复初始代码
    +
    &#xe6ec;
    +
  • + +
  • + +
    再次载入
    +
    &#xe6ed;
    +
  • + +
  • + +
    开关
    +
    &#xe6ef;
    +
  • + +
  • + +
    目录
    +
    &#xe6f0;
    +
  • + +
  • + +
    缩小
    +
    &#xe6f2;
    +
  • + +
  • + +
    扩大
    +
    &#xe6f3;
    +
  • + +
  • + +
    设置
    +
    &#xe6f4;
    +
  • + +
  • + +
    隐藏
    +
    &#xe6f5;
    +
  • + +
  • + +
    消息
    +
    &#xe6f6;
    +
  • + +
  • + +
    金币
    +
    &#xe6f7;
    +
  • + +
  • + +
    显示密码
    +
    &#xe6f9;
    +
  • + +
  • + +
    隐藏密码
    +
    &#xe6fa;
    +
  • + +
  • + +
    复制
    +
    &#xe6fb;
    +
  • + +
  • + +
    文件
    +
    &#xe6fc;
    +
  • + +
  • + +
    文件夹
    +
    &#xe6fd;
    +
  • + +
  • + +
    上传
    +
    &#xe6fe;
    +
  • + +
  • + +
    挑战
    +
    &#xe6ff;
    +
  • + +
  • + +
    完成
    +
    &#xe6cb;
    +
  • + +
  • + +
    企业账号
    +
    &#xe6cc;
    +
  • + +
  • + +
    个人账号
    +
    &#xe6cd;
    +
  • + +
  • + +
    右滑
    +
    &#xe702;
    +
  • + +
  • + +
    解锁
    +
    &#xe703;
    +
  • + +
  • + +
    +
    &#xe704;
    +
  • + +
  • + +
    加载失败
    +
    &#xe6d6;
    +
  • + +
  • + +
    搜索
    +
    &#xe706;
    +
  • + +
  • + +
    类型
    +
    &#xe6d5;
    +
  • + +
  • + +
    标签尖头
    +
    &#xe6d7;
    +
  • + +
  • + +
    笔记
    +
    &#xe70a;
    +
  • + +
  • + +
    置顶
    +
    &#xe6d9;
    +
  • +

    Unicode 引用

    @@ -1532,6 +1898,15 @@
    +
  • + +
    + 禁止 +
    +
    .icon-jinzhi +
    +
  • +
  • @@ -1667,6 +2042,15 @@
  • +
  • + +
    + 浏览 +
    +
    .icon-liulan +
    +
  • +
  • @@ -1703,6 +2087,15 @@
  • +
  • + +
    + 下箭头 +
    +
    .icon-jiantou9 +
    +
  • +
  • @@ -2072,6 +2465,15 @@
  • +
  • + +
    + 博客园 +
    +
    .icon-bokeyuan +
    +
  • +
  • @@ -2180,6 +2582,15 @@
  • +
  • + +
    + SDK问题 +
    +
    .icon-wenti +
    +
  • +
  • @@ -3206,6 +3617,15 @@
  • +
  • + +
    + 评论 +
    +
    .icon-pinglun +
    +
  • +
  • @@ -3233,6 +3653,15 @@
  • +
  • + +
    + 上箭头-填充 +
    +
    .icon-shangjiantou-tianchong +
    +
  • +
  • @@ -3296,6 +3725,15 @@
  • +
  • + +
    + 省略号 +
    +
    .icon-shenglvehao +
    +
  • +
  • @@ -3431,6 +3869,15 @@
  • +
  • + +
    + 向上 箭头 +
    +
    .icon-changyongtubiao-xianxingdaochu-zhuanqu- +
    +
  • +
  • @@ -3485,8 +3932,476 @@
  • - -
    +
  • + +
    + 绑定手机号 +
    +
    .icon-bangdingshoujihao +
    +
  • + +
  • + +
    + 标签 +
    +
    .icon-biaoqian1 +
    +
  • + +
  • + +
    + 记录 +
    +
    .icon-jilu +
    +
  • + +
  • + +
    + 书 +
    +
    .icon-shu +
    +
  • + +
  • + +
    + 推荐 +
    +
    .icon-tuijian +
    +
  • + +
  • + +
    + 创建者 +
    +
    .icon-chuangjianzhe +
    +
  • + +
  • + +
    + 创建者 +
    +
    .icon-chuangjianzhe1 +
    +
  • + +
  • + +
    + 书 +
    +
    .icon-shu1 +
    +
  • + +
  • + +
    + 标签 +
    +
    .icon-biaoqian2 +
    +
  • + +
  • + +
    + 记录 +
    +
    .icon-jilu1 +
    +
  • + +
  • + +
    + 推荐 +
    +
    .icon-tuijian1 +
    +
  • + +
  • + +
    + 警告 +
    +
    .icon-jinggao1 +
    +
  • + +
  • + +
    + 点赞 +
    +
    .icon-dianzan2 +
    +
  • + +
  • + +
    + 评论 +
    +
    .icon-pinglun1 +
    +
  • + +
  • + +
    + 对勾 +
    +
    .icon-duigou +
    +
  • + +
  • + +
    + 提示 +
    +
    .icon-tishi2 +
    +
  • + +
  • + +
    + 编辑_Hover +
    +
    .icon-bianji_Hover +
    +
  • + +
  • + +
    + 上移_Hover +
    +
    .icon-shangyi_Hover +
    +
  • + +
  • + +
    + 删除_默认 +
    +
    .icon-shanchu_moren +
    +
  • + +
  • + +
    + 下移_Hover +
    +
    .icon-xiayi_Hover +
    +
  • + +
  • + +
    + 删除_Hover +
    +
    .icon-shanchu_Hover +
    +
  • + +
  • + +
    + 下移_默认 +
    +
    .icon-xiayi_moren +
    +
  • + +
  • + +
    + 编辑_默认 +
    +
    .icon-bianji_moren +
    +
  • + +
  • + +
    + 恢复初始代码 +
    +
    .icon-huifuchushidaima +
    +
  • + +
  • + +
    + 再次载入 +
    +
    .icon-zaicizairu +
    +
  • + +
  • + +
    + 开关 +
    +
    .icon-kaiguan +
    +
  • + +
  • + +
    + 目录 +
    +
    .icon-mulu +
    +
  • + +
  • + +
    + 缩小 +
    +
    .icon-suoxiao1 +
    +
  • + +
  • + +
    + 扩大 +
    +
    .icon-kuoda +
    +
  • + +
  • + +
    + 设置 +
    +
    .icon-shezhi3 +
    +
  • + +
  • + +
    + 隐藏 +
    +
    .icon-yincang2 +
    +
  • + +
  • + +
    + 消息 +
    +
    .icon-xiaoxi11 +
    +
  • + +
  • + +
    + 金币 +
    +
    .icon-bianzu1 +
    +
  • + +
  • + +
    + 显示密码 +
    +
    .icon-xianshimima +
    +
  • + +
  • + +
    + 隐藏密码 +
    +
    .icon-yincangmima +
    +
  • + +
  • + +
    + 复制 +
    +
    .icon-fuzhi2 +
    +
  • + +
  • + +
    + 文件 +
    +
    .icon-xingzhuangjiehe +
    +
  • + +
  • + +
    + 文件夹 +
    +
    .icon-xingzhuangjiehebeifen +
    +
  • + +
  • + +
    + 上传 +
    +
    .icon-shangchuan +
    +
  • + +
  • + +
    + 挑战 +
    +
    .icon-tiaozhan +
    +
  • + +
  • + +
    + 完成 +
    +
    .icon-wancheng1 +
    +
  • + +
  • + +
    + 企业账号 +
    +
    .icon-qiyezhanghao +
    +
  • + +
  • + +
    + 个人账号 +
    +
    .icon-gerenzhanghao +
    +
  • + +
  • + +
    + 右滑 +
    +
    .icon-youhua +
    +
  • + +
  • + +
    + 解锁 +
    +
    .icon-jiesuo +
    +
  • + +
  • + +
    + 锁 +
    +
    .icon-suo1 +
    +
  • + +
  • + +
    + 加载失败 +
    +
    .icon-jiazaishibai1 +
    +
  • + +
  • + +
    + 搜索 +
    +
    .icon-bianzu11 +
    +
  • + +
  • + +
    + 类型 +
    +
    .icon-leixing +
    +
  • + +
  • + +
    + 标签尖头 +
    +
    .icon-biaoqianjiantou +
    +
  • + +
  • + +
    + 笔记 +
    +
    .icon-biji +
    +
  • + +
  • + +
    + 置顶 +
    +
    .icon-zhiding +
    +
  • + + +

    font-class 引用


    @@ -3516,1818 +4431,2306 @@
  • +
    round_close
    +
    #icon-roundclose
    +
  • + +
  • + +
    time_fill
    +
    #icon-timefill
    +
  • + +
  • + +
    round_add_fill
    +
    #icon-roundaddfill
    +
  • + +
  • + +
    粗版2_上传云端
    +
    #icon-cuban2shangchuanyunduan
    +
  • + +
  • + +
    css3
    +
    #icon-css3
    +
  • + +
  • + +
    html5
    +
    #icon-html5
    +
  • + +
  • + +
    钻石
    +
    #icon-31
    +
  • + +
  • + +
    章节
    +
    #icon-zhangjie1
    +
  • + +
  • + +
    关闭
    +
    #icon-htmal5icon19
    +
  • + +
  • + +
    社区
    +
    #icon-shequ
    +
  • + +
  • + +
    禁止
    +
    #icon-jinzhi
    +
  • + +
  • + +
    vs
    +
    #icon-vs
    +
  • + +
  • + +
    菜单
    +
    #icon-weibiaoti12
    +
  • + +
  • + +
    pdf
    +
    #icon-pdf
    +
  • + +
  • + +
    单选 选中
    +
    #icon-danxuanxuanzhong1
    +
  • + +
  • + +
    时间
    +
    #icon-shijian
    +
  • + +
  • + +
    设置
    +
    #icon-shezhi
    +
  • + +
  • + +
    坐标
    +
    #icon-xiazai18
    +
  • + +
  • + +
    礼物
    +
    #icon-liwu
    +
  • + +
  • + +
    PHP开发
    +
    #icon-phpkaifa
    +
  • + +
  • + +
    附件
    +
    #icon-fujian
    +
  • + +
  • + +
    linux
    +
    #icon-linux
    +
  • + +
  • + +
    问号
    +
    #icon-wenhao
    +
  • + +
  • + +
    坐标
    +
    #icon-zuobiao
    +
  • + +
  • + +
    expand
    +
    #icon-expand
    +
  • + +
  • + +
    重置
    +
    #icon-zhongzhi1
    +
  • + +
  • + +
    浏览
    +
    #icon-liulan
    +
  • + +
  • + +
    路由
    +
    #icon-luyou
    +
  • + +
  • + +
    点赞2
    +
    #icon-dianzan1
    +
  • + +
  • + +
    复制
    +
    #icon-fuzhi
    +
  • + +
  • + +
    点赞1
    +
    #icon-dianzan11
    +
  • + +
  • + +
    下箭头
    +
    #icon-jiantou9
    +
  • + +
  • + +
    三角形-up
    +
    #icon-sanjiaoxing-up
    +
  • + +
  • + +
    三角形-down
    +
    #icon-sanjiaoxing-down
    +
  • + +
  • + +
    认证信息
    +
    #icon-renzhengxinxi
    +
  • + +
  • + +
    关注
    +
    #icon-weibiaoti105
    +
  • + +
  • + +
    礼物
    +
    #icon-gift
    +
  • + +
  • + +
    消息
    +
    #icon-xiaoxi
    +
  • + +
  • + +
    邮件
    +
    #icon-mail
    +
  • + +
  • + +
    compress
    +
    #icon-compress
    +
  • + +
  • + +
    代码配置r
    +
    #icon-daimapeizhir
    +
  • + +
  • + +
    提示
    +
    #icon-tishi1
    +
  • + +
  • + +
    net
    +
    #icon-net
    +
  • + +
  • + +
    edit
    +
    #icon-edit
    +
  • + +
  • + +
    显示
    +
    #icon-xianshi
    +
  • + +
  • + +
    +
    #icon-suo
    +
  • + +
  • + +
    +
    #icon-default
    +
  • + +
  • + +
    消息
    +
    #icon-xiaoxi1
    +
  • + +
  • + +
    下载
    +
    #icon-xiazai1
    +
  • + +
  • + +
    撤销
    +
    #icon-chexiao
    +
  • + +
  • + +
    qq
    +
    #icon-qq
    +
  • + +
  • + +
    标签
    +
    #icon-biaoqian
    +
  • + +
  • + -
    round_close
    -
    #icon-roundclose
    +
    大数据存储
    +
    #icon-dashujucunchu
  • -
    time_fill
    -
    #icon-timefill
    +
    关注
    +
    #icon-guanzhu
  • -
    round_add_fill
    -
    #icon-roundaddfill
    +
    喇叭
    +
    #icon-laba
  • -
    粗版2_上传云端
    -
    #icon-cuban2shangchuanyunduan
    +
    mstest
    +
    #icon-mstest
  • -
    css3
    -
    #icon-css3
    +
    添加成员
    +
    #icon-tianjiachengyuan
  • -
    html5
    -
    #icon-html5
    +
    隐藏
    +
    #icon-yincang1
  • -
    钻石
    -
    #icon-31
    +
    三角形
    +
    #icon-triangle
  • -
    章节
    -
    #icon-zhangjie1
    +
    旗帜
    +
    #icon-qizhi
  • -
    关闭
    -
    #icon-htmal5icon19
    +
    CSDN
    +
    #icon-csdn
  • -
    社区
    -
    #icon-shequ
    +
    用户、角色_无数据
    +
    #icon-yonghujiaose_wushuju
  • -
    vs
    -
    #icon-vs
    +
    模板
    +
    #icon-moban
  • -
    菜单
    -
    #icon-weibiaoti12
    +
    VPN
    +
    #icon-VPN
  • -
    pdf
    -
    #icon-pdf
    +
    +
    #icon-jia
  • -
    单选 选中
    -
    #icon-danxuanxuanzhong1
    +
    银行卡
    +
    #icon-yinhangqia1
  • -
    时间
    -
    #icon-shijian
    +
    微博
    +
    #icon-weibo
  • -
    设置
    -
    #icon-shezhi
    +
    +
    #icon-kong
  • -
    坐标
    -
    #icon-xiazai18
    +
    课程
    +
    #icon-kecheng
  • -
    礼物
    -
    #icon-liwu
    +
    设置
    +
    #icon-shezhi1
  • -
    PHP开发
    -
    #icon-phpkaifa
    +
    成功
    +
    #icon-chenggong
  • -
    附件
    -
    #icon-fujian
    +
    三点
    +
    #icon-sandian
  • -
    linux
    -
    #icon-linux
    +
    gs-beixiao-icon-基本信息
    +
    #icon-jibenxinxi
  • -
    问号
    -
    #icon-wenhao
    +
    博客园
    +
    #icon-bokeyuan
  • -
    坐标
    -
    #icon-zuobiao
    +
    base
    +
    #icon-base
  • -
    expand
    -
    #icon-expand
    +
    微信
    +
    #icon-weixin2
  • +
    Stack Overflow
    +
    #icon-StackOverflow
    +
  • + +
  • +
    重置
    -
    #icon-zhongzhi1
    +
    #icon-zhongzhi2
  • -
    路由
    -
    #icon-luyou
    +
    活动
    +
    #icon-huodong
  • -
    点赞2
    -
    #icon-dianzan1
    +
    隐藏
    +
    #icon-yincang
  • -
    复制
    -
    #icon-fuzhi
    +
    电话
    +
    #icon-weibiaoti-
  • -
    点赞1
    -
    #icon-dianzan11
    +
    位置
    +
    #icon-weizhi
  • -
    三角形-up
    -
    #icon-sanjiaoxing-up
    +
    jquery
    +
    #icon-jquery
  • -
    三角形-down
    -
    #icon-sanjiaoxing-down
    +
    docker
    +
    #icon-docker
  • +
    python
    +
    #icon-python
    +
  • + +
  • + +
    php
    +
    #icon-php
    +
  • + +
  • + +
    SDK问题
    +
    #icon-wenti
    +
  • + +
  • + +
    银行卡
    +
    #icon-yinhangqia
    +
  • + +
  • + +
    linkedin
    +
    #icon-linkedin
    +
  • + +
  • + +
    文件夹
    +
    #icon-wenjianjia
    +
  • + +
  • + +
    SQL server
    +
    #icon-SQLserver
    +
  • + +
  • + +
    质量分析
    +
    #icon-zhiliangfenxi
    +
  • + +
  • + -
    认证信息
    -
    #icon-renzhengxinxi
    +
    上传图片
    +
    #icon-shangchuantupian1
  • -
    关注
    -
    #icon-weibiaoti105
    +
    登录Ip监控
    +
    #icon-dengluIpjiankong
  • -
    礼物
    -
    #icon-gift
    +
    itsm3-流程管理
    +
    #icon-itsm-liuchengguanli
  • -
    消息
    -
    #icon-xiaoxi
    +
    链接
    +
    #icon-lianjie
  • -
    邮件
    -
    #icon-mail
    +
    分支
    +
    #icon-fenzhi
  • -
    compress
    -
    #icon-compress
    +
    网址克隆
    +
    #icon-wangzhikelong
  • -
    代码配置r
    -
    #icon-daimapeizhir
    +
    下载
    +
    #icon-xiazai
  • -
    提示
    -
    #icon-tishi1
    +
    代码
    +
    #icon-daima
  • -
    net
    -
    #icon-net
    +
    提交记录
    +
    #icon-tijiaojilu
  • -
    edit
    -
    #icon-edit
    +
    选择题
    +
    #icon-xuanzeti
  • -
    显示
    -
    #icon-xianshi
    +
    编辑
    +
    #icon-bianji
  • -
    -
    #icon-suo
    +
    向上
    +
    #icon-xiangshang
  • -
    -
    #icon-default
    +
    删除掉
    +
    #icon-shanchudiao
  • -
    消息
    -
    #icon-xiaoxi1
    +
    新建
    +
    #icon-xinjian
  • -
    下载
    -
    #icon-xiazai1
    +
    上升排序
    +
    #icon-shangshengpaixu
  • -
    撤销
    -
    #icon-chexiao
    +
    奖励
    +
    #icon-jiangli
  • -
    qq
    -
    #icon-qq
    +
    删除
    +
    #icon-shanchu
  • -
    标签
    -
    #icon-biaoqian
    +
    隐藏闭眼
    +
    #icon-yincangbiyan
  • -
    大数据存储
    -
    #icon-dashujucunchu
    +
    开锁
    +
    #icon-kaisuo
  • -
    关注
    -
    #icon-guanzhu
    +
    关锁
    +
    #icon-guansuo
  • -
    喇叭
    -
    #icon-laba
    +
    版本库
    +
    #icon-banbenku
  • -
    mstest
    -
    #icon-mstest
    +
    issue
    +
    #icon-issue
  • -
    添加成员
    -
    #icon-tianjiachengyuan
    +
    上传图片
    +
    #icon-shangchuantupian
  • -
    隐藏
    -
    #icon-yincang1
    +
    测评
    +
    #icon-ceping
  • -
    三角形
    -
    #icon-triangle
    +
    tpi消息提醒
    +
    #icon-tpixiaoxitixing
  • -
    旗帜
    -
    #icon-qizhi
    +
    qq在线咨询
    +
    #icon-qqzaixianzixun
  • -
    CSDN
    -
    #icon-csdn
    +
    二维码
    +
    #icon-erweima
  • -
    用户、角色_无数据
    -
    #icon-yonghujiaose_wushuju
    +
    意见反馈
    +
    #icon-yijianfankui
  • -
    模板
    -
    #icon-moban
    +
    邮箱认证
    +
    #icon-youxiangrenzheng
  • -
    VPN
    -
    #icon-VPN
    +
    手机认证
    +
    #icon-shoujirenzheng
  • -
    -
    #icon-jia
    +
    职业认证
    +
    #icon-zhiyerenzheng
  • -
    银行卡
    -
    #icon-yinhangqia1
    +
    身份认证
    +
    #icon-shenfenrenzheng
  • -
    微博
    -
    #icon-weibo
    +
    评分
    +
    #icon-pingfen
  • -
    -
    #icon-kong
    +
    评分-线
    +
    #icon-pingfen-xian
  • -
    课程
    -
    #icon-kecheng
    +
    作业
    +
    #icon-zuoye
  • -
    设置
    -
    #icon-shezhi1
    +
    提示错误
    +
    #icon-tishicuowu
  • -
    成功
    -
    #icon-chenggong
    +
    资源
    +
    #icon-ziyuan
  • -
    三点
    -
    #icon-sandian
    +
    提示
    +
    #icon-tishi
  • -
    gs-beixiao-icon-基本信息
    -
    #icon-jibenxinxi
    +
    成员
    +
    #icon-chengyuan
  • -
    base
    -
    #icon-base
    +
    公告
    +
    #icon-gonggao
  • -
    微信
    -
    #icon-weixin2
    +
    点赞
    +
    #icon-dianzan
  • -
    Stack Overflow
    -
    #icon-StackOverflow
    +
    点赞-线
    +
    #icon-dianzan-xian
  • -
    重置
    -
    #icon-zhongzhi2
    + + +
    返回上次代码
    +
    #icon-fanhuishangcidaima
  • -
    活动
    -
    #icon-huodong
    +
    重置
    +
    #icon-zhongzhi
  • -
    隐藏
    -
    #icon-yincang
    +
    旋转
    +
    #icon-xuanzhuan
  • -
    电话
    -
    #icon-weibiaoti-
    +
    实训
    +
    #icon-shixun
  • -
    位置
    -
    #icon-weizhi
    +
    缩小
    +
    #icon-suoxiao
  • -
    jquery
    -
    #icon-jquery
    +
    下箭头
    +
    #icon-xiajiantou
  • -
    docker
    -
    #icon-docker
    +
    勾选
    +
    #icon-gouxuan
  • -
    python
    -
    #icon-python
    +
    浏览眼
    +
    #icon-liulanyan
  • -
    php
    -
    #icon-php
    +
    经验
    +
    #icon-jingyan
  • -
    银行卡
    -
    #icon-yinhangqia
    +
    消息铃铛
    +
    #icon-xiaoxilingdang
  • -
    linkedin
    -
    #icon-linkedin
    +
    实训关卡
    +
    #icon-shixunguanqia
  • -
    文件夹
    -
    #icon-wenjianjia
    +
    搜索
    +
    #icon-sousuo
  • -
    SQL server
    -
    #icon-SQLserver
    +
    发布
    +
    #icon-fabu
  • -
    质量分析
    -
    #icon-zhiliangfenxi
    +
    添加 放大
    +
    #icon-tianjiafangda
  • -
    上传图片
    -
    #icon-shangchuantupian1
    +
    向下移动
    +
    #icon-xiangxiayidong
  • -
    登录Ip监控
    -
    #icon-dengluIpjiankong
    +
    向上移动
    +
    #icon-xiangshangyidong
  • -
    itsm3-流程管理
    -
    #icon-itsm-liuchengguanli
    +
    关闭
    +
    #icon-guanbi
  • -
    链接
    -
    #icon-lianjie
    +
    毕业 [转换]
    +
    #icon-biyezhuanhuan
  • -
    分支
    -
    #icon-fenzhi
    +
    睁眼
    +
    #icon-zhengyan
  • -
    网址克隆
    -
    #icon-wangzhikelong
    +
    回复
    +
    #icon-huifu1
  • -
    下载
    -
    #icon-xiazai
    +
    文件
    +
    #icon-wenjian
  • -
    代码
    -
    #icon-daima
    +
    展开
    +
    #icon-zhankai
  • -
    提交记录
    -
    #icon-tijiaojilu
    +
    收缩
    +
    #icon-shousuo
  • -
    选择题
    -
    #icon-xuanzeti
    +
    左键头
    +
    #icon-zuojiantou
  • -
    编辑
    -
    #icon-bianji
    +
    右键头
    +
    #icon-youjiantou
  • -
    向上
    -
    #icon-xiangshang
    +
    上键头
    +
    #icon-shangjiantou
  • -
    删除掉
    -
    #icon-shanchudiao
    +
    编辑带背景
    +
    #icon-bianjidaibeijing
  • -
    新建
    -
    #icon-xinjian
    +
    播放
    +
    #icon-bofang
  • -
    上升排序
    -
    #icon-shangshengpaixu
    +
    完成
    +
    #icon-wancheng
  • -
    奖励
    -
    #icon-jiangli
    +
    分组作业
    +
    #icon-fenzuzuoye
  • -
    删除
    -
    #icon-shanchu
    +
    普通作业
    +
    #icon-putongzuoye
  • -
    隐藏闭眼
    -
    #icon-yincangbiyan
    +
    设置
    +
    #icon-shezhi2
  • -
    开锁
    -
    #icon-kaisuo
    +
    问卷
    +
    #icon-wenjuan
  • -
    关锁
    -
    #icon-guansuo
    +
    讨论
    +
    #icon-taolun
  • -
    版本库
    -
    #icon-banbenku
    +
    分班
    +
    #icon-fenban
  • -
    issue
    -
    #icon-issue
    +
    动态
    +
    #icon-dongtai
  • -
    上传图片
    -
    #icon-shangchuantupian
    +
    试卷
    +
    #icon-shijuan
  • -
    测评
    -
    #icon-ceping
    +
    菜单
    +
    #icon-caidan
  • -
    tpi消息提醒
    -
    #icon-tpixiaoxitixing
    +
    成员管理
    +
    #icon-chengyuanguanli
  • -
    qq在线咨询
    -
    #icon-qqzaixianzixun
    +
    添加
    +
    #icon-tianjia
  • -
    二维码
    -
    #icon-erweima
    +
    向下移
    +
    #icon-xiangxiayi
  • -
    意见反馈
    -
    #icon-yijianfankui
    +
    向上移
    +
    #icon-xiangshangyi
  • -
    邮箱认证
    -
    #icon-youxiangrenzheng
    +
    更多
    +
    #icon-gengduo
  • -
    手机认证
    -
    #icon-shoujirenzheng
    +
    reset
    +
    #icon-reset
  • -
    职业认证
    -
    #icon-zhiyerenzheng
    +
    学院管理员
    +
    #icon-xueyuanguanliyuan
  • -
    身份认证
    -
    #icon-shenfenrenzheng
    +
    空星
    +
    #icon-kongxing
  • -
    评分
    -
    #icon-pingfen
    +
    实星
    +
    #icon-shixing
  • -
    评分-线
    -
    #icon-pingfen-xian
    +
    下降
    +
    #icon-xiajiang
  • -
    作业
    -
    #icon-zuoye
    +
    复制
    +
    #icon-fuzhi1
  • -
    提示错误
    -
    #icon-tishicuowu
    +
    更多
    +
    #icon-gengduo1
  • -
    资源
    -
    #icon-ziyuan
    +
    20从属连接
    +
    #icon-congshulianjie
  • -
    提示
    -
    #icon-tishi
    +
    手机
    +
    #icon-shouji
  • -
    成员
    -
    #icon-chengyuan
    +
    智能监控体系
    +
    #icon-zhinengjiankongtixi
  • -
    公告
    -
    #icon-gonggao
    +
    新增提示
    +
    #icon-xinzengtishi
  • -
    点赞
    -
    #icon-dianzan
    +
    完成勾选
    +
    #icon-wanchenggouxuan
  • -
    点赞-线
    -
    #icon-dianzan-xian
    +
    java
    +
    #icon-java
  • -
    返回上次代码
    -
    #icon-fanhuishangcidaima
    +
    下降
    +
    #icon-youjiang
  • -
    重置
    -
    #icon-zhongzhi
    +
    fork
    +
    #icon-fork
  • -
    旋转
    -
    #icon-xuanzhuan
    +
    名片
    +
    #icon-mingpian
  • -
    实训
    -
    #icon-shixun
    +
    mysql
    +
    #icon-mysql
  • -
    缩小
    -
    #icon-suoxiao
    +
    客户留言
    +
    #icon-kehuliuyan
  • -
    下箭头
    -
    #icon-xiajiantou
    +
    github
    +
    #icon-github
  • -
    勾选
    -
    #icon-gouxuan
    +
    mongodb
    +
    #icon-mongodb1
  • -
    浏览眼
    -
    #icon-liulanyan
    +
    安全设置
    +
    #icon-anquanshezhi
  • -
    经验
    -
    #icon-jingyan
    +
    trustie
    +
    #icon-trustie
  • -
    消息铃铛
    -
    #icon-xiaoxilingdang
    +
    无权限
    +
    #icon-wuquanxian
  • -
    实训关卡
    -
    #icon-shixunguanqia
    +
    实训带背景
    +
    #icon-shixundaibeijing
  • -
    搜索
    -
    #icon-sousuo
    +
    评论
    +
    #icon-pinglun
  • -
    发布
    -
    #icon-fabu
    +
    工程
    +
    #icon-gongcheng
  • -
    添加 放大
    -
    #icon-tianjiafangda
    +
    警告
    +
    #icon-jinggao
  • -
    向下移动
    -
    #icon-xiangxiayidong
    +
    添加导航
    +
    #icon-tianjiadaohang
  • -
    向上移动
    -
    #icon-xiangshangyidong
    +
    上箭头-填充
    +
    #icon-shangjiantou-tianchong
  • -
    关闭
    -
    #icon-guanbi
    +
    主页
    +
    #icon-zhuye
  • -
    毕业 [转换]
    -
    #icon-biyezhuanhuan
    +
    yunweijiankong
    +
    #icon-yunweijiankong
  • -
    睁眼
    -
    #icon-zhengyan
    +
    gongyiliucheng
    +
    #icon-gongyiliucheng
  • -
    回复
    -
    #icon-huifu1
    +
    zhiliangkongzhi
    +
    #icon-zhiliangkongzhi
  • -
    文件
    -
    #icon-wenjian
    +
    shebeiguanli
    +
    #icon-shebeiguanli
  • -
    展开
    -
    #icon-zhankai
    +
    shengmingzhouqi
    +
    #icon-shengmingzhouqi
  • -
    收缩
    -
    #icon-shousuo
    +
    编辑
    +
    #icon-bianji1
  • -
    左键头
    -
    #icon-zuojiantou
    +
    省略号
    +
    #icon-shenglvehao
  • -
    右键头
    -
    #icon-youjiantou
    +
    排序
    +
    #icon-paixu1
  • -
    上键头
    -
    #icon-shangjiantou
    +
    职业认证
    +
    #icon-renzhengshangjia
  • -
    编辑带背景
    -
    #icon-bianjidaibeijing
    +
    实名认证
    +
    #icon-shenfenzhenghaomaguizheng
  • -
    播放
    -
    #icon-bofang
    +
    Page-1 (2)
    +
    #icon-Page-1
  • -
    完成
    -
    #icon-wancheng
    +
    Page-3
    +
    #icon-Page-3
  • -
    分组作业
    -
    #icon-fenzuzuoye
    +
    Page2
    +
    #icon-Page
  • -
    普通作业
    -
    #icon-putongzuoye
    +
    身份认证
    +
    #icon-yemian
  • -
    设置
    -
    #icon-shezhi2
    +
    实名认证
    +
    #icon-bianzu
  • -
    问卷
    -
    #icon-wenjuan
    +
    学习中心
    +
    #icon-xuexizhongxin
  • -
    讨论
    -
    #icon-taolun
    +
    统计
    +
    #icon-tongji
  • -
    分班
    -
    #icon-fenban
    +
    menu_3voucher
    +
    #icon-menu_voucher
  • -
    动态
    -
    #icon-dongtai
    +
    menu_3events
    +
    #icon-menu_events
  • -
    试卷
    -
    #icon-shijuan
    +
    menu_4map
    +
    #icon-menu_map
  • -
    菜单
    -
    #icon-caidan
    +
    menu_5date1
    +
    #icon-menu_date
  • -
    成员管理
    -
    #icon-chengyuanguanli
    +
    menu_people1
    +
    #icon-menu_people
  • -
    添加
    -
    #icon-tianjia
    +
    向上 箭头
    +
    #icon-changyongtubiao-xianxingdaochu-zhuanqu-
  • -
    向下移
    -
    #icon-xiangxiayi
    +
    healthmode
    +
    #icon-healthmode
  • -
    向上移
    -
    #icon-xiangshangyi
    +
    nenghaofenxi@1x
    +
    #icon-nenghaofenxix
  • -
    更多
    -
    #icon-gengduo
    +
    detection@1x
    +
    #icon-detectionx
  • -
    reset
    -
    #icon-reset
    +
    community@1x
    +
    #icon-communityx
  • -
    学院管理员
    -
    #icon-xueyuanguanliyuan
    +
    hosting@1x
    +
    #icon-hostingx2
  • -
    空星
    -
    #icon-kongxing
    +
    project@1x
    +
    #icon-projectx
  • -
    实星
    -
    #icon-shixing
    +
    绑定手机号
    +
    #icon-bangdingshoujihao
  • -
    下降
    -
    #icon-xiajiang
    +
    标签
    +
    #icon-biaoqian1
  • -
    复制
    -
    #icon-fuzhi1
    +
    记录
    +
    #icon-jilu
  • -
    更多
    -
    #icon-gengduo1
    +
    +
    #icon-shu
  • -
    20从属连接
    -
    #icon-congshulianjie
    +
    推荐
    +
    #icon-tuijian
  • -
    手机
    -
    #icon-shouji
    +
    创建者
    +
    #icon-chuangjianzhe
  • -
    智能监控体系
    -
    #icon-zhinengjiankongtixi
    +
    创建者
    +
    #icon-chuangjianzhe1
  • -
    新增提示
    -
    #icon-xinzengtishi
    +
    +
    #icon-shu1
  • -
    完成勾选
    -
    #icon-wanchenggouxuan
    +
    标签
    +
    #icon-biaoqian2
  • -
    java
    -
    #icon-java
    +
    记录
    +
    #icon-jilu1
  • -
    下降
    -
    #icon-youjiang
    +
    推荐
    +
    #icon-tuijian1
  • -
    fork
    -
    #icon-fork
    +
    警告
    +
    #icon-jinggao1
  • -
    名片
    -
    #icon-mingpian
    +
    点赞
    +
    #icon-dianzan2
  • -
    mysql
    -
    #icon-mysql
    +
    评论
    +
    #icon-pinglun1
  • -
    客户留言
    -
    #icon-kehuliuyan
    +
    对勾
    +
    #icon-duigou
  • -
    github
    -
    #icon-github
    +
    提示
    +
    #icon-tishi2
  • -
    mongodb
    -
    #icon-mongodb1
    +
    编辑_Hover
    +
    #icon-bianji_Hover
  • -
    安全设置
    -
    #icon-anquanshezhi
    +
    上移_Hover
    +
    #icon-shangyi_Hover
  • -
    trustie
    -
    #icon-trustie
    +
    删除_默认
    +
    #icon-shanchu_moren
  • -
    无权限
    -
    #icon-wuquanxian
    +
    下移_Hover
    +
    #icon-xiayi_Hover
  • -
    实训带背景
    -
    #icon-shixundaibeijing
    +
    删除_Hover
    +
    #icon-shanchu_Hover
  • -
    工程
    -
    #icon-gongcheng
    +
    下移_默认
    +
    #icon-xiayi_moren
  • -
    警告
    -
    #icon-jinggao
    +
    编辑_默认
    +
    #icon-bianji_moren
  • -
    添加导航
    -
    #icon-tianjiadaohang
    +
    恢复初始代码
    +
    #icon-huifuchushidaima
  • -
    主页
    -
    #icon-zhuye
    +
    再次载入
    +
    #icon-zaicizairu
  • -
    yunweijiankong
    -
    #icon-yunweijiankong
    +
    开关
    +
    #icon-kaiguan
  • -
    gongyiliucheng
    -
    #icon-gongyiliucheng
    +
    目录
    +
    #icon-mulu
  • -
    zhiliangkongzhi
    -
    #icon-zhiliangkongzhi
    +
    缩小
    +
    #icon-suoxiao1
  • -
    shebeiguanli
    -
    #icon-shebeiguanli
    +
    扩大
    +
    #icon-kuoda
  • -
    shengmingzhouqi
    -
    #icon-shengmingzhouqi
    +
    设置
    +
    #icon-shezhi3
  • -
    编辑
    -
    #icon-bianji1
    +
    隐藏
    +
    #icon-yincang2
  • -
    排序
    -
    #icon-paixu1
    +
    消息
    +
    #icon-xiaoxi11
  • -
    职业认证
    -
    #icon-renzhengshangjia
    +
    金币
    +
    #icon-bianzu1
  • -
    实名认证
    -
    #icon-shenfenzhenghaomaguizheng
    +
    显示密码
    +
    #icon-xianshimima
  • -
    Page-1 (2)
    -
    #icon-Page-1
    +
    隐藏密码
    +
    #icon-yincangmima
  • -
    Page-3
    -
    #icon-Page-3
    +
    复制
    +
    #icon-fuzhi2
  • -
    Page2
    -
    #icon-Page
    +
    文件
    +
    #icon-xingzhuangjiehe
  • -
    身份认证
    -
    #icon-yemian
    +
    文件夹
    +
    #icon-xingzhuangjiehebeifen
  • -
    实名认证
    -
    #icon-bianzu
    +
    上传
    +
    #icon-shangchuan
  • -
    学习中心
    -
    #icon-xuexizhongxin
    +
    挑战
    +
    #icon-tiaozhan
  • -
    统计
    -
    #icon-tongji
    +
    完成
    +
    #icon-wancheng1
  • -
    menu_3voucher
    -
    #icon-menu_voucher
    +
    企业账号
    +
    #icon-qiyezhanghao
  • -
    menu_3events
    -
    #icon-menu_events
    +
    个人账号
    +
    #icon-gerenzhanghao
  • -
    menu_4map
    -
    #icon-menu_map
    +
    右滑
    +
    #icon-youhua
  • -
    menu_5date1
    -
    #icon-menu_date
    +
    解锁
    +
    #icon-jiesuo
  • -
    menu_people1
    -
    #icon-menu_people
    +
    +
    #icon-suo1
  • -
    healthmode
    -
    #icon-healthmode
    +
    加载失败
    +
    #icon-jiazaishibai1
  • -
    nenghaofenxi@1x
    -
    #icon-nenghaofenxix
    +
    搜索
    +
    #icon-bianzu11
  • -
    detection@1x
    -
    #icon-detectionx
    +
    类型
    +
    #icon-leixing
  • -
    community@1x
    -
    #icon-communityx
    +
    标签尖头
    +
    #icon-biaoqianjiantou
  • -
    hosting@1x
    -
    #icon-hostingx2
    +
    笔记
    +
    #icon-biji
  • -
    project@1x
    -
    #icon-projectx
    +
    置顶
    +
    #icon-zhiding
  • diff --git a/public/stylesheets/educoder/iconfont/iconfont.css b/public/stylesheets/educoder/iconfont/iconfont.css index 29e19aee4..1fc157cbd 100644 --- a/public/stylesheets/educoder/iconfont/iconfont.css +++ b/public/stylesheets/educoder/iconfont/iconfont.css @@ -1,10 +1,10 @@ @font-face {font-family: "iconfont"; - src: url('iconfont.eot?t=1572859243353'); /* IE9 */ - src: url('iconfont.eot?t=1572859243353#iefix') format('embedded-opentype'), /* IE6-IE8 */ - url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'), - url('iconfont.woff?t=1572859243353') format('woff'), - url('iconfont.ttf?t=1572859243353') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ - url('iconfont.svg?t=1572859243353#iconfont') format('svg'); /* iOS 4.1- */ + src: url('iconfont.eot?t=1576634145829'); /* IE9 */ + src: url('iconfont.eot?t=1576634145829#iefix') format('embedded-opentype'), /* IE6-IE8 */ + url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'), + url('iconfont.woff?t=1576634145829') format('woff'), + url('iconfont.ttf?t=1576634145829') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ + url('iconfont.svg?t=1576634145829#iconfont') format('svg'); /* iOS 4.1- */ } .iconfont { @@ -55,6 +55,10 @@ content: "\e6bc"; } +.icon-jinzhi:before { + content: "\e6d4"; +} + .icon-vs:before { content: "\e682"; } @@ -115,6 +119,10 @@ content: "\e609"; } +.icon-liulan:before { + content: "\e6c7"; +} + .icon-luyou:before { content: "\e677"; } @@ -131,6 +139,10 @@ content: "\e66d"; } +.icon-jiantou9:before { + content: "\e700"; +} + .icon-sanjiaoxing-up:before { content: "\e78f"; } @@ -295,6 +307,10 @@ content: "\e694"; } +.icon-bokeyuan:before { + content: "\e6c6"; +} + .icon-base:before { content: "\e683"; } @@ -343,6 +359,10 @@ content: "\e67d"; } +.icon-wenti:before { + content: "\e7dc"; +} + .icon-yinhangqia:before { content: "\e675"; } @@ -799,6 +819,10 @@ content: "\e68c"; } +.icon-pinglun:before { + content: "\e6c8"; +} + .icon-gongcheng:before { content: "\e60f"; } @@ -811,6 +835,10 @@ content: "\e604"; } +.icon-shangjiantou-tianchong:before { + content: "\e733"; +} + .icon-zhuye:before { content: "\e6d3"; } @@ -839,6 +867,10 @@ content: "\e6a1"; } +.icon-shenglvehao:before { + content: "\e708"; +} + .icon-paixu1:before { content: "\e6aa"; } @@ -899,6 +931,10 @@ content: "\e6b7"; } +.icon-changyongtubiao-xianxingdaochu-zhuanqu-:before { + content: "\e74c"; +} + .icon-healthmode:before { content: "\e60e"; } @@ -923,3 +959,211 @@ content: "\e6c4"; } +.icon-bangdingshoujihao:before { + content: "\e6ca"; +} + +.icon-biaoqian1:before { + content: "\e6ce"; +} + +.icon-jilu:before { + content: "\e6cf"; +} + +.icon-shu:before { + content: "\e6d0"; +} + +.icon-tuijian:before { + content: "\e6d1"; +} + +.icon-chuangjianzhe:before { + content: "\e6d2"; +} + +.icon-chuangjianzhe1:before { + content: "\e6da"; +} + +.icon-shu1:before { + content: "\e6dc"; +} + +.icon-biaoqian2:before { + content: "\e6dd"; +} + +.icon-jilu1:before { + content: "\e6de"; +} + +.icon-tuijian1:before { + content: "\e6df"; +} + +.icon-jinggao1:before { + content: "\e6e0"; +} + +.icon-dianzan2:before { + content: "\e6e1"; +} + +.icon-pinglun1:before { + content: "\e6e2"; +} + +.icon-duigou:before { + content: "\e6e3"; +} + +.icon-tishi2:before { + content: "\e6e4"; +} + +.icon-bianji_Hover:before { + content: "\e6e5"; +} + +.icon-shangyi_Hover:before { + content: "\e6e6"; +} + +.icon-shanchu_moren:before { + content: "\e6e7"; +} + +.icon-xiayi_Hover:before { + content: "\e6e8"; +} + +.icon-shanchu_Hover:before { + content: "\e6e9"; +} + +.icon-xiayi_moren:before { + content: "\e6ea"; +} + +.icon-bianji_moren:before { + content: "\e6eb"; +} + +.icon-huifuchushidaima:before { + content: "\e6ec"; +} + +.icon-zaicizairu:before { + content: "\e6ed"; +} + +.icon-kaiguan:before { + content: "\e6ef"; +} + +.icon-mulu:before { + content: "\e6f0"; +} + +.icon-suoxiao1:before { + content: "\e6f2"; +} + +.icon-kuoda:before { + content: "\e6f3"; +} + +.icon-shezhi3:before { + content: "\e6f4"; +} + +.icon-yincang2:before { + content: "\e6f5"; +} + +.icon-xiaoxi11:before { + content: "\e6f6"; +} + +.icon-bianzu1:before { + content: "\e6f7"; +} + +.icon-xianshimima:before { + content: "\e6f9"; +} + +.icon-yincangmima:before { + content: "\e6fa"; +} + +.icon-fuzhi2:before { + content: "\e6fb"; +} + +.icon-xingzhuangjiehe:before { + content: "\e6fc"; +} + +.icon-xingzhuangjiehebeifen:before { + content: "\e6fd"; +} + +.icon-shangchuan:before { + content: "\e6fe"; +} + +.icon-tiaozhan:before { + content: "\e6ff"; +} + +.icon-wancheng1:before { + content: "\e6cb"; +} + +.icon-qiyezhanghao:before { + content: "\e6cc"; +} + +.icon-gerenzhanghao:before { + content: "\e6cd"; +} + +.icon-youhua:before { + content: "\e702"; +} + +.icon-jiesuo:before { + content: "\e703"; +} + +.icon-suo1:before { + content: "\e704"; +} + +.icon-jiazaishibai1:before { + content: "\e6d6"; +} + +.icon-bianzu11:before { + content: "\e706"; +} + +.icon-leixing:before { + content: "\e6d5"; +} + +.icon-biaoqianjiantou:before { + content: "\e6d7"; +} + +.icon-biji:before { + content: "\e70a"; +} + +.icon-zhiding:before { + content: "\e6d9"; +} + diff --git a/public/stylesheets/educoder/iconfont/iconfont.eot b/public/stylesheets/educoder/iconfont/iconfont.eot index c73a22ad3..d45ad379f 100644 Binary files a/public/stylesheets/educoder/iconfont/iconfont.eot and b/public/stylesheets/educoder/iconfont/iconfont.eot differ diff --git a/public/stylesheets/educoder/iconfont/iconfont.js b/public/stylesheets/educoder/iconfont/iconfont.js index 754daee62..289e5ea98 100644 --- a/public/stylesheets/educoder/iconfont/iconfont.js +++ b/public/stylesheets/educoder/iconfont/iconfont.js @@ -1 +1 @@ -!function(z){var c,h='',l=(c=document.getElementsByTagName("script"))[c.length-1].getAttribute("data-injectcss");if(l&&!z.__iconfont__svg__cssinject__){z.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(c){console&&console.log(c)}}!function(c){if(document.addEventListener)if(~["complete","loaded","interactive"].indexOf(document.readyState))setTimeout(c,0);else{var l=function(){document.removeEventListener("DOMContentLoaded",l,!1),c()};document.addEventListener("DOMContentLoaded",l,!1)}else document.attachEvent&&(a=c,i=z.document,t=!1,(o=function(){try{i.documentElement.doScroll("left")}catch(c){return void setTimeout(o,50)}h()})(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,h())});function h(){t||(t=!0,a())}var a,i,t,o}(function(){var c,l;(c=document.createElement("div")).innerHTML=h,h=null,(l=c.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",function(c,l){l.firstChild?function(c,l){l.parentNode.insertBefore(c,l)}(c,l.firstChild):l.appendChild(c)}(l,document.body))})}(window); \ No newline at end of file +!function(z){var c,o='',l=(c=document.getElementsByTagName("script"))[c.length-1].getAttribute("data-injectcss");if(l&&!z.__iconfont__svg__cssinject__){z.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(c){console&&console.log(c)}}!function(c){if(document.addEventListener)if(~["complete","loaded","interactive"].indexOf(document.readyState))setTimeout(c,0);else{var l=function(){document.removeEventListener("DOMContentLoaded",l,!1),c()};document.addEventListener("DOMContentLoaded",l,!1)}else document.attachEvent&&(h=c,i=z.document,t=!1,(o=function(){try{i.documentElement.doScroll("left")}catch(c){return void setTimeout(o,50)}a()})(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,a())});function a(){t||(t=!0,h())}var h,i,t,o}(function(){var c,l,a,h,i,t;(c=document.createElement("div")).innerHTML=o,o=null,(l=c.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",a=l,(h=document.body).firstChild?(i=a,(t=h.firstChild).parentNode.insertBefore(i,t)):h.appendChild(a))})}(window); \ No newline at end of file diff --git a/public/stylesheets/educoder/iconfont/iconfont.json b/public/stylesheets/educoder/iconfont/iconfont.json index 0120e0492..1b4e8c1e7 100644 --- a/public/stylesheets/educoder/iconfont/iconfont.json +++ b/public/stylesheets/educoder/iconfont/iconfont.json @@ -75,6 +75,13 @@ "unicode": "e6bc", "unicode_decimal": 59068 }, + { + "icon_id": "496400", + "name": "禁止", + "font_class": "jinzhi", + "unicode": "e6d4", + "unicode_decimal": 59092 + }, { "icon_id": "562997", "name": "vs", @@ -180,6 +187,13 @@ "unicode": "e609", "unicode_decimal": 58889 }, + { + "icon_id": "978358", + "name": "浏览", + "font_class": "liulan", + "unicode": "e6c7", + "unicode_decimal": 59079 + }, { "icon_id": "986702", "name": "路由", @@ -208,6 +222,13 @@ "unicode": "e66d", "unicode_decimal": 58989 }, + { + "icon_id": "1110411", + "name": "下箭头", + "font_class": "jiantou9", + "unicode": "e700", + "unicode_decimal": 59136 + }, { "icon_id": "1113422", "name": "三角形-up", @@ -495,6 +516,13 @@ "unicode": "e694", "unicode_decimal": 59028 }, + { + "icon_id": "3315084", + "name": "博客园", + "font_class": "bokeyuan", + "unicode": "e6c6", + "unicode_decimal": 59078 + }, { "icon_id": "3330922", "name": "base", @@ -579,6 +607,13 @@ "unicode": "e67d", "unicode_decimal": 59005 }, + { + "icon_id": "3911796", + "name": "SDK问题", + "font_class": "wenti", + "unicode": "e7dc", + "unicode_decimal": 59356 + }, { "icon_id": "4019861", "name": "银行卡", @@ -1377,6 +1412,13 @@ "unicode": "e68c", "unicode_decimal": 59020 }, + { + "icon_id": "7501072", + "name": "评论", + "font_class": "pinglun", + "unicode": "e6c8", + "unicode_decimal": 59080 + }, { "icon_id": "7587940", "name": "工程", @@ -1398,6 +1440,13 @@ "unicode": "e604", "unicode_decimal": 58884 }, + { + "icon_id": "8349119", + "name": "上箭头-填充", + "font_class": "shangjiantou-tianchong", + "unicode": "e733", + "unicode_decimal": 59187 + }, { "icon_id": "8361866", "name": "主页", @@ -1447,6 +1496,13 @@ "unicode": "e6a1", "unicode_decimal": 59041 }, + { + "icon_id": "9974429", + "name": "省略号", + "font_class": "shenglvehao", + "unicode": "e708", + "unicode_decimal": 59144 + }, { "icon_id": "9977539", "name": "排序", @@ -1552,6 +1608,13 @@ "unicode": "e6b7", "unicode_decimal": 59063 }, + { + "icon_id": "10809887", + "name": "向上 箭头", + "font_class": "changyongtubiao-xianxingdaochu-zhuanqu-", + "unicode": "e74c", + "unicode_decimal": 59212 + }, { "icon_id": "11222372", "name": "healthmode", @@ -1593,6 +1656,370 @@ "font_class": "projectx", "unicode": "e6c4", "unicode_decimal": 59076 + }, + { + "icon_id": "11965901", + "name": "绑定手机号", + "font_class": "bangdingshoujihao", + "unicode": "e6ca", + "unicode_decimal": 59082 + }, + { + "icon_id": "12028723", + "name": "标签", + "font_class": "biaoqian1", + "unicode": "e6ce", + "unicode_decimal": 59086 + }, + { + "icon_id": "12028724", + "name": "记录", + "font_class": "jilu", + "unicode": "e6cf", + "unicode_decimal": 59087 + }, + { + "icon_id": "12028725", + "name": "书", + "font_class": "shu", + "unicode": "e6d0", + "unicode_decimal": 59088 + }, + { + "icon_id": "12028726", + "name": "推荐", + "font_class": "tuijian", + "unicode": "e6d1", + "unicode_decimal": 59089 + }, + { + "icon_id": "12028727", + "name": "创建者", + "font_class": "chuangjianzhe", + "unicode": "e6d2", + "unicode_decimal": 59090 + }, + { + "icon_id": "12029022", + "name": "创建者", + "font_class": "chuangjianzhe1", + "unicode": "e6da", + "unicode_decimal": 59098 + }, + { + "icon_id": "12029023", + "name": "书", + "font_class": "shu1", + "unicode": "e6dc", + "unicode_decimal": 59100 + }, + { + "icon_id": "12029024", + "name": "标签", + "font_class": "biaoqian2", + "unicode": "e6dd", + "unicode_decimal": 59101 + }, + { + "icon_id": "12029025", + "name": "记录", + "font_class": "jilu1", + "unicode": "e6de", + "unicode_decimal": 59102 + }, + { + "icon_id": "12029026", + "name": "推荐", + "font_class": "tuijian1", + "unicode": "e6df", + "unicode_decimal": 59103 + }, + { + "icon_id": "12031268", + "name": "警告", + "font_class": "jinggao1", + "unicode": "e6e0", + "unicode_decimal": 59104 + }, + { + "icon_id": "12031648", + "name": "点赞", + "font_class": "dianzan2", + "unicode": "e6e1", + "unicode_decimal": 59105 + }, + { + "icon_id": "12031742", + "name": "评论", + "font_class": "pinglun1", + "unicode": "e6e2", + "unicode_decimal": 59106 + }, + { + "icon_id": "12033031", + "name": "对勾", + "font_class": "duigou", + "unicode": "e6e3", + "unicode_decimal": 59107 + }, + { + "icon_id": "12039315", + "name": "提示", + "font_class": "tishi2", + "unicode": "e6e4", + "unicode_decimal": 59108 + }, + { + "icon_id": "12039523", + "name": "编辑_Hover", + "font_class": "bianji_Hover", + "unicode": "e6e5", + "unicode_decimal": 59109 + }, + { + "icon_id": "12039524", + "name": "上移_Hover", + "font_class": "shangyi_Hover", + "unicode": "e6e6", + "unicode_decimal": 59110 + }, + { + "icon_id": "12039525", + "name": "删除_默认", + "font_class": "shanchu_moren", + "unicode": "e6e7", + "unicode_decimal": 59111 + }, + { + "icon_id": "12039526", + "name": "下移_Hover", + "font_class": "xiayi_Hover", + "unicode": "e6e8", + "unicode_decimal": 59112 + }, + { + "icon_id": "12039527", + "name": "删除_Hover", + "font_class": "shanchu_Hover", + "unicode": "e6e9", + "unicode_decimal": 59113 + }, + { + "icon_id": "12039528", + "name": "下移_默认", + "font_class": "xiayi_moren", + "unicode": "e6ea", + "unicode_decimal": 59114 + }, + { + "icon_id": "12039529", + "name": "编辑_默认", + "font_class": "bianji_moren", + "unicode": "e6eb", + "unicode_decimal": 59115 + }, + { + "icon_id": "12040163", + "name": "恢复初始代码", + "font_class": "huifuchushidaima", + "unicode": "e6ec", + "unicode_decimal": 59116 + }, + { + "icon_id": "12040164", + "name": "再次载入", + "font_class": "zaicizairu", + "unicode": "e6ed", + "unicode_decimal": 59117 + }, + { + "icon_id": "12040165", + "name": "开关", + "font_class": "kaiguan", + "unicode": "e6ef", + "unicode_decimal": 59119 + }, + { + "icon_id": "12040167", + "name": "目录", + "font_class": "mulu", + "unicode": "e6f0", + "unicode_decimal": 59120 + }, + { + "icon_id": "12040168", + "name": "缩小", + "font_class": "suoxiao1", + "unicode": "e6f2", + "unicode_decimal": 59122 + }, + { + "icon_id": "12040169", + "name": "扩大", + "font_class": "kuoda", + "unicode": "e6f3", + "unicode_decimal": 59123 + }, + { + "icon_id": "12040170", + "name": "设置", + "font_class": "shezhi3", + "unicode": "e6f4", + "unicode_decimal": 59124 + }, + { + "icon_id": "12053135", + "name": "隐藏", + "font_class": "yincang2", + "unicode": "e6f5", + "unicode_decimal": 59125 + }, + { + "icon_id": "12074711", + "name": "消息", + "font_class": "xiaoxi11", + "unicode": "e6f6", + "unicode_decimal": 59126 + }, + { + "icon_id": "12098941", + "name": "金币", + "font_class": "bianzu1", + "unicode": "e6f7", + "unicode_decimal": 59127 + }, + { + "icon_id": "12107631", + "name": "显示密码", + "font_class": "xianshimima", + "unicode": "e6f9", + "unicode_decimal": 59129 + }, + { + "icon_id": "12107632", + "name": "隐藏密码", + "font_class": "yincangmima", + "unicode": "e6fa", + "unicode_decimal": 59130 + }, + { + "icon_id": "12107887", + "name": "复制", + "font_class": "fuzhi2", + "unicode": "e6fb", + "unicode_decimal": 59131 + }, + { + "icon_id": "12108608", + "name": "文件", + "font_class": "xingzhuangjiehe", + "unicode": "e6fc", + "unicode_decimal": 59132 + }, + { + "icon_id": "12108609", + "name": "文件夹", + "font_class": "xingzhuangjiehebeifen", + "unicode": "e6fd", + "unicode_decimal": 59133 + }, + { + "icon_id": "12108648", + "name": "上传", + "font_class": "shangchuan", + "unicode": "e6fe", + "unicode_decimal": 59134 + }, + { + "icon_id": "12126798", + "name": "挑战", + "font_class": "tiaozhan", + "unicode": "e6ff", + "unicode_decimal": 59135 + }, + { + "icon_id": "12126824", + "name": "完成", + "font_class": "wancheng1", + "unicode": "e6cb", + "unicode_decimal": 59083 + }, + { + "icon_id": "12300755", + "name": "企业账号", + "font_class": "qiyezhanghao", + "unicode": "e6cc", + "unicode_decimal": 59084 + }, + { + "icon_id": "12300756", + "name": "个人账号", + "font_class": "gerenzhanghao", + "unicode": "e6cd", + "unicode_decimal": 59085 + }, + { + "icon_id": "12300795", + "name": "右滑", + "font_class": "youhua", + "unicode": "e702", + "unicode_decimal": 59138 + }, + { + "icon_id": "12300843", + "name": "解锁", + "font_class": "jiesuo", + "unicode": "e703", + "unicode_decimal": 59139 + }, + { + "icon_id": "12300844", + "name": "锁", + "font_class": "suo1", + "unicode": "e704", + "unicode_decimal": 59140 + }, + { + "icon_id": "12301512", + "name": "加载失败", + "font_class": "jiazaishibai1", + "unicode": "e6d6", + "unicode_decimal": 59094 + }, + { + "icon_id": "12319671", + "name": "搜索", + "font_class": "bianzu11", + "unicode": "e706", + "unicode_decimal": 59142 + }, + { + "icon_id": "12345165", + "name": "类型", + "font_class": "leixing", + "unicode": "e6d5", + "unicode_decimal": 59093 + }, + { + "icon_id": "12345541", + "name": "标签尖头", + "font_class": "biaoqianjiantou", + "unicode": "e6d7", + "unicode_decimal": 59095 + }, + { + "icon_id": "12364938", + "name": "笔记", + "font_class": "biji", + "unicode": "e70a", + "unicode_decimal": 59146 + }, + { + "icon_id": "12371179", + "name": "置顶", + "font_class": "zhiding", + "unicode": "e6d9", + "unicode_decimal": 59097 } ] } diff --git a/public/stylesheets/educoder/iconfont/iconfont.svg b/public/stylesheets/educoder/iconfont/iconfont.svg index 8a2a3508b..1a6e5fba3 100644 --- a/public/stylesheets/educoder/iconfont/iconfont.svg +++ b/public/stylesheets/educoder/iconfont/iconfont.svg @@ -50,6 +50,9 @@ Created by iconfont + + + @@ -95,6 +98,9 @@ Created by iconfont + + + @@ -107,6 +113,9 @@ Created by iconfont + + + @@ -230,6 +239,9 @@ Created by iconfont + + + @@ -266,6 +278,9 @@ Created by iconfont + + + @@ -608,6 +623,9 @@ Created by iconfont + + + @@ -617,6 +635,9 @@ Created by iconfont + + + @@ -638,6 +659,9 @@ Created by iconfont + + + @@ -683,6 +707,9 @@ Created by iconfont + + + @@ -701,6 +728,162 @@ Created by iconfont + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/stylesheets/educoder/iconfont/iconfont.ttf b/public/stylesheets/educoder/iconfont/iconfont.ttf index c343c0064..35c0ec5c2 100644 Binary files a/public/stylesheets/educoder/iconfont/iconfont.ttf and b/public/stylesheets/educoder/iconfont/iconfont.ttf differ diff --git a/public/stylesheets/educoder/iconfont/iconfont.woff b/public/stylesheets/educoder/iconfont/iconfont.woff index e6ddc4c63..9404835fc 100644 Binary files a/public/stylesheets/educoder/iconfont/iconfont.woff and b/public/stylesheets/educoder/iconfont/iconfont.woff differ diff --git a/public/stylesheets/educoder/iconfont/iconfont.woff2 b/public/stylesheets/educoder/iconfont/iconfont.woff2 index df3b0207b..440d3354d 100644 Binary files a/public/stylesheets/educoder/iconfont/iconfont.woff2 and b/public/stylesheets/educoder/iconfont/iconfont.woff2 differ diff --git a/spec/models/item_analysis_spec.rb b/spec/models/item_analysis_spec.rb new file mode 100644 index 000000000..155169289 --- /dev/null +++ b/spec/models/item_analysis_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe ItemAnalysis, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/item_bank_spec.rb b/spec/models/item_bank_spec.rb new file mode 100644 index 000000000..a8cc2cffb --- /dev/null +++ b/spec/models/item_bank_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe ItemBank, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/item_basket_spec.rb b/spec/models/item_basket_spec.rb new file mode 100644 index 000000000..4cdf7e0ea --- /dev/null +++ b/spec/models/item_basket_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe ItemBasket, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/item_choice_spec.rb b/spec/models/item_choice_spec.rb new file mode 100644 index 000000000..ff22a255e --- /dev/null +++ b/spec/models/item_choice_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe ItemChoice, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end