diff --git a/.gitignore b/.gitignore index ea08c700e..12dc596a6 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ # Ignore bundler config. /.bundle +/bundle # Ignore lock config file *.lock @@ -70,3 +71,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/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index b3a0e123a..bda4bcc6c 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' diff --git a/app/controllers/attachments_controller.rb b/app/controllers/attachments_controller.rb index a4ceeac09..bd322d59e 100644 --- a/app/controllers/attachments_controller.rb +++ b/app/controllers/attachments_controller.rb @@ -57,7 +57,6 @@ class AttachmentsController < ApplicationController @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 @@ -68,6 +67,7 @@ class AttachmentsController < ApplicationController @attachment.author_id = current_user.id @attachment.disk_directory = month_folder @attachment.cloud_url = remote_path + @attachment.disk_directory = save_path @attachment.save! else logger.info "文件已存在,id = #{@attachment.id}, filename = #{@attachment.filename}" @@ -196,4 +196,5 @@ class AttachmentsController < ApplicationController end end end + end diff --git a/app/controllers/challenges_controller.rb b/app/controllers/challenges_controller.rb index 4528b7b90..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 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/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 1325d1423..987f4873a 100644 --- a/app/controllers/myshixuns_controller.rb +++ b/app/controllers/myshixuns_controller.rb @@ -366,6 +366,31 @@ class MyshixunsController < ApplicationController 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 diff --git a/app/controllers/shixuns_controller.rb b/app/controllers/shixuns_controller.rb index caa716f16..ac26b740c 100644 --- a/app/controllers/shixuns_controller.rb +++ b/app/controllers/shixuns_controller.rb @@ -6,16 +6,17 @@ 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, :apply_public, :shixun_members_added, :change_manager, :collaborators_delete, @@ -364,73 +365,158 @@ 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) + # 镜像变动要更换服务配置 + @shixun.shixun_service_configs.where.not(mirror_repository_id: old_mirror_ids).destroy_all + @shixun.shixun_service_configs.create!(service_create_params) + service_update_params&.map do |service| + smr = @shixun.shixun_service_configs.find_by(mirror_repository_id: service[:mirror_repository_id]) + smr.update_attributes(service) + end + # 添加第二仓库(管理员权限) + if current_user.admin_or_business? + 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 + 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.page(page).per(limit) + @absolute_folder = edu_setting('shixun_folder') + end + + # 实训测试集附件 + def upload_data_sets + 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.cloud_url = remote_path + @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 + 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 +548,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 +583,8 @@ class ShixunsController < ApplicationController # @evaluate_scirpt = @shixun.evaluate_script || "无" end + + # 获取脚本内容 def get_script_contents mirrir_script = MirrorScript.find(params[:script_id]) @@ -708,112 +741,40 @@ 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("开启实战前请先在版本库中提交代码") 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("实训云平台繁忙(繁忙等级:81)") + end + end + def publish @status = 0 @position = [] @@ -1035,10 +996,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 @@ -1047,8 +1007,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 @@ -1116,4 +1074,35 @@ 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 + 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/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/models/attachment.rb b/app/models/attachment.rb index f18d9cd2a..ac051428f 100644 --- a/app/models/attachment.rb +++ b/app/models/attachment.rb @@ -33,6 +33,10 @@ class Attachment < ApplicationRecord 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 title = filename if container && container.is_a?(StudentWork) && author_id != User.current.id diff --git a/app/models/myshixun.rb b/app/models/myshixun.rb index 9b824c0d8..a1732ecc3 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 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 dc5348450..404faff29 100644 --- a/app/models/shixun.rb +++ b/app/models/shixun.rb @@ -10,6 +10,7 @@ class Shixun < ApplicationRecord # 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 @@ -55,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}%") } 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/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/jupyter_service.rb b/app/services/jupyter_service.rb new file mode 100644 index 000000000..417aa4aad --- /dev/null +++ b/app/services/jupyter_service.rb @@ -0,0 +1,188 @@ +#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}" + params = {tpiID: tpiID, identifier: shixun.identifier, :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'] + + return "https://#{res['port']}.jupyter.educoder.net/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 + params = {tpiID: tpiID, identifier: shixun.identifier, :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 + + "https://#{res['port']}.jupyter.educoder.net/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}" + + #https://47526.jupyter.educoder.net/nbconvert/notebook/data/workspace/myshixun_570461/f2ef5p798r20191210163135/01.ipynb?download=true + src_url = "https://#{jupyter_port}.jupyter.educoder.net/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 = "https://#{jupyter_port}.jupyter.educoder.net/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 + + +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..2a350fd5d --- /dev/null +++ b/app/services/shixuns/create_shixun_service.rb @@ -0,0 +1,109 @@ +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 + 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/views/challenges/index.json.jbuilder b/app/views/challenges/index.json.jbuilder index 37ce94305..c53ab9c58 100644 --- a/app/views/challenges/index.json.jbuilder +++ b/app/views/challenges/index.json.jbuilder @@ -4,8 +4,10 @@ json.description @shixun.description json.power @editable json.shixun_identifier @shixun.identifier json.shixun_status @shixun.status +json.is_jupyter @shixun.is_jupyter? json.allow_skip @shixun.task_pass + # 列表 if @challenges.present? json.challenge_list @challenges do |challenge| diff --git a/app/views/games/jupyter.json.jbuilder b/app/views/games/jupyter.json.jbuilder new file mode 100644 index 000000000..94e43a24c --- /dev/null +++ b/app/views/games/jupyter.json.jbuilder @@ -0,0 +1,7 @@ +json.user do + json.partial! 'users/user', user: current_user +end + +json.(@shixun, :id, :identifier, :status, :name) +json.myshixun_identifier @myshixun.identifier +json.tpm_modified @tpm_modified \ No newline at end of file diff --git a/app/views/games/show.json.jbuilder b/app/views/games/show.json.jbuilder index 7c9d1f22a..3c7aa3e0c 100644 --- a/app/views/games/show.json.jbuilder +++ b/app/views/games/show.json.jbuilder @@ -19,4 +19,4 @@ else json.has_answer @has_answer json.choose_test_cases @choose_test_cases json.chooses @chooses -end +end \ No newline at end of file diff --git a/app/views/shixuns/_shixun.json.jbuilder b/app/views/shixuns/_shixun.json.jbuilder index e6dbd3115..89e8d3c62 100644 --- a/app/views/shixuns/_shixun.json.jbuilder +++ b/app/views/shixuns/_shixun.json.jbuilder @@ -13,6 +13,7 @@ json.array! shixuns do |shixun| json.identifier shixun.identifier json.name shixun.name json.status shixun.status + json.is_jupyter shixun.is_jupyter? json.power (current_user.shixun_permission(shixun)) # 现在首页只显示已发布的实训 # REDO: 局部缓存 json.tag_name @tag_name_map&.fetch(shixun.id, nil) || shixun.tag_repertoires.first.try(:name) diff --git a/app/views/shixuns/_top.json.jbuilder b/app/views/shixuns/_top.json.jbuilder index 32c00cc79..8cd78dd74 100644 --- a/app/views/shixuns/_top.json.jbuilder +++ b/app/views/shixuns/_top.json.jbuilder @@ -14,6 +14,7 @@ json.stu_num shixun.myshixuns_count json.experience shixun.all_score json.diffcult diff_to_s(shixun.trainee) json.score_info shixun.shixun_preference_info # todo: 这块可以改成只显示实训的平均分,不用每次都去取每种星的分数了。 +json.is_jupyter shixun.is_jupyter # 用于是否显示导航栏中的'背景知识' json.propaedeutics shixun.propaedeutics.present? diff --git a/app/views/shixuns/get_data_sets.json.jbuilder b/app/views/shixuns/get_data_sets.json.jbuilder new file mode 100644 index 000000000..20ced2acd --- /dev/null +++ b/app/views/shixuns/get_data_sets.json.jbuilder @@ -0,0 +1,11 @@ +json.data_sets do + json.array! @data_sets do |set| + json.id set.id + json.title set.title + json.author set.author.real_name + json.created_on set.created_on + json.filesize number_to_human_size(set.filesize) + json.file_path "#{@absolute_folder}/#{set.relative_path_filename}" + end +end +json.data_sets_count @data_count \ No newline at end of file diff --git a/app/views/shixuns/jupyter_exec.json.jbuilder b/app/views/shixuns/jupyter_exec.json.jbuilder new file mode 100644 index 000000000..44e5c979a --- /dev/null +++ b/app/views/shixuns/jupyter_exec.json.jbuilder @@ -0,0 +1 @@ +json.identifier @myshixun.identifier \ No newline at end of file diff --git a/app/views/shixuns/settings.json.jbuilder b/app/views/shixuns/settings.json.jbuilder index 3441a0a06..267547431 100644 --- a/app/views/shixuns/settings.json.jbuilder +++ b/app/views/shixuns/settings.json.jbuilder @@ -2,6 +2,7 @@ json.shixun do json.status @shixun.status json.name @shixun.name json.description @shixun.description + json.is_jupyter @shixun.is_jupyter # 镜像大小类别 json.main_type @main_type @@ -41,6 +42,9 @@ json.shixun do json.(config, :cpu_limit, :lower_cpu_limit, :memory_limit, :request_limit, :mirror_repository_id) end end + + + json.jupyter_url jupyter_url(@shixun) end diff --git a/app/views/users/shixuns/shared/_shixun.json.jbuilder b/app/views/users/shixuns/shared/_shixun.json.jbuilder index 8427ead58..663522e53 100644 --- a/app/views/users/shixuns/shared/_shixun.json.jbuilder +++ b/app/views/users/shixuns/shared/_shixun.json.jbuilder @@ -6,4 +6,5 @@ json.name shixun.name json.status shixun.status json.human_status shixun.human_status json.challenges_count shixun.challenges_count -json.finished_challenges_count @finished_challenges_count_map&.fetch(shixun.id, 0) || shixun.finished_challenges_count(user) \ No newline at end of file +json.finished_challenges_count @finished_challenges_count_map&.fetch(shixun.id, 0) || shixun.finished_challenges_count(user) +json.is_jupyter shixun.is_jupyter \ No newline at end of file diff --git a/bin/bundle b/bin/bundle index b48d3a0d5..f19acf5b5 100644 --- a/bin/bundle +++ b/bin/bundle @@ -1,3 +1,3 @@ -#!/usr/bin/env ruby -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) -load Gem.bin_path('bundler', 'bundle') +#!/usr/bin/env ruby +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) +load Gem.bin_path('bundler', 'bundle') diff --git a/bin/rails b/bin/rails index a017658c5..073966023 100644 --- a/bin/rails +++ b/bin/rails @@ -1,4 +1,4 @@ -#!/usr/bin/env ruby -APP_PATH = File.expand_path('../config/application', __dir__) -require_relative '../config/boot' -require 'rails/commands' +#!/usr/bin/env ruby +APP_PATH = File.expand_path('../config/application', __dir__) +require_relative '../config/boot' +require 'rails/commands' diff --git a/bin/rake b/bin/rake index 8704afdf3..2c877f116 100644 --- a/bin/rake +++ b/bin/rake @@ -1,4 +1,6 @@ -#!/usr/bin/env ruby -require_relative '../config/boot' -require 'rake' -Rake.application.run +#!/usr/bin/env ruby +require_relative '../config/boot' + + +require 'rake' +Rake.application.run diff --git a/config/routes.rb b/config/routes.rb index acc42e7e4..3549a3f5f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -9,6 +9,8 @@ Rails.application.routes.draw do get 'auth/qq/callback', to: 'oauth/qq#create' get 'auth/failure', to: 'oauth/base#auth_failure' + + resources :edu_settings scope '/api' do get 'home/index' @@ -23,6 +25,17 @@ Rails.application.routes.draw do put 'commons/unhidden', to: 'commons#unhidden' delete 'commons/delete', to: 'commons#delete' + resources :jupyters do + collection do + get :save_with_tpi + get :save_with_tpm + get :get_info_with_tpi + get :get_info_with_tpm + get :reset_with_tpi + get :reset_with_tpm + end + end + resources :memos do member do post :sticky_or_cancel @@ -35,6 +48,9 @@ Rails.application.routes.draw do end end + + + resources :hacks, path: :problems, param: :identifier do collection do get :unpulished_list @@ -171,6 +187,7 @@ Rails.application.routes.draw do post :html_content get :open_webssh get :challenges + post :sync_code end collection do get :sigle_mul_test @@ -206,6 +223,7 @@ Rails.application.routes.draw do get :check_test_sets get :unlock_choose_answer get :get_choose_answer + get :jupyter end collection do @@ -264,6 +282,12 @@ Rails.application.routes.draw do get :shixun_exec post :review_shixun get :review_newest_record + post :update_permission_setting + post :update_learn_setting + get :get_data_sets + get :jupyter_exec + post :upload_data_sets + delete :destroy_data_sets end resources :challenges do @@ -748,7 +772,11 @@ Rails.application.routes.draw do resources :poll_bank_questions - resources :attachments + resources :attachments do + collection do + delete :destroy_files + end + end resources :schools do member do diff --git a/db/migrate/20191210070415_add_is_jupyter_for_shixuns.rb b/db/migrate/20191210070415_add_is_jupyter_for_shixuns.rb new file mode 100644 index 000000000..af2ae7ad4 --- /dev/null +++ b/db/migrate/20191210070415_add_is_jupyter_for_shixuns.rb @@ -0,0 +1,5 @@ +class AddIsJupyterForShixuns < ActiveRecord::Migration[5.2] + def change + add_column :shixuns, :is_jupyter, :boolean, :default => false + end +end diff --git a/db/migrate/20191211025413_add_index_for_shixun_secret_repositories.rb b/db/migrate/20191211025413_add_index_for_shixun_secret_repositories.rb new file mode 100644 index 000000000..650b821e1 --- /dev/null +++ b/db/migrate/20191211025413_add_index_for_shixun_secret_repositories.rb @@ -0,0 +1,17 @@ +class AddIndexForShixunSecretRepositories < ActiveRecord::Migration[5.2] + def change + shixun_ids = ShixunSecretRepository.pluck(:shixun_id).uniq + shixuns = Shixun.where(id: shixun_ids) + shixuns.find_each do |shixun| + id = shixun.shixun_secret_repository.id + shixun_secret_repositories = ShixunSecretRepository.where(shixun_id: shixun.id).where.not(id: id) + shixun_secret_repositories.destroy_all + end + + remove_index :shixun_secret_repositories, :shixun_id + add_index :shixun_secret_repositories, :shixun_id, unique: true + + + + end +end diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..16f600df4 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,35 @@ +version: '3' +services: + mysql: + image: mysql:5.7.17 + command: --sql-mode="" + restart: always + volumes: + - ./mysql_data/:/var/lib/mysql + ports: + - "3306:3306" + environment: + MYSQL_ROOT_PASSWORD: 123456789 + MYSQL_DATABASE: educoder + + redis: + image: redis:3.2 + container_name: redis + restart: always + ports: + - "6379:6379" + volumes: + - ./redis_data:/data + + web: + image: guange/educoder:latest + command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 4000 -b '0.0.0.0'" + stdin_open: true + tty: true + volumes: + - .:/app + ports: + - "4000:4000" + depends_on: + - mysql + - redis \ No newline at end of file diff --git a/educoder-2019-12-11.sql.zip b/educoder-2019-12-11.sql.zip new file mode 100644 index 000000000..410bb2788 Binary files /dev/null and b/educoder-2019-12-11.sql.zip differ diff --git a/public/react/config/webpack.config.prod.js b/public/react/config/webpack.config.prod.js index fbfbf23bc..596843f5f 100644 --- a/public/react/config/webpack.config.prod.js +++ b/public/react/config/webpack.config.prod.js @@ -326,7 +326,7 @@ module.exports = { comments: false }, compress: { - drop_debugger: true, + drop_debugger: false, drop_console: false } } diff --git a/public/react/public/css/iconfont.css b/public/react/public/css/iconfont.css index 29e19aee4..f9c2e1e68 100644 --- a/public/react/public/css/iconfont.css +++ b/public/react/public/css/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,d09GMgABAAAAAJHEAAsAAAABDAAAAJFwAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCdJAqD0giC6WYBNgIkA4cQC4NKAAQgBYRtB5UzG0vVNcptFwV0BzDiqKVSJtwYeo+DWIzh2YEWNo4F4Ngt2f9/SnIyhjAN5qxX+X4SnIkMM0dyQRblzF1DkCDZZBwylSxJEDn8SGfk+KJsPEWFib05oTsyZgZGjcDaTD3hFC8qN4wM3fvb41WjeJ3h6nNtNsVjJlbD8Zvv4H+/6vngT2/DfXljC2wb9qLR0RP+Hw73543GmDQmRhs0SjWj+At/T/wOz8+t9/+ygTFibEQJg1GxsRG9ES2MGikOgVEqIyRUdFiEBZigp6iYoIJRwB3qnY0HViBWAkHIoR/qYLJUaszgsGuDiAi40A1AFFBg1ExFzH0q8Xxwb+/sPbML9KmT9TSLBTwiVbCDJUC/kKSC1PJm4JxEYx5xVj3+ymlf97W79vZAEFgQBHY5sEgnlmQnGfI4A+gB1kW6WlcyzLBj/ETw924WzHVkroMQIWprF10Rff99khUVGmhDe1XhnYghFq2Vp/89kMcAKwDongmCMGF14FDogGfhkKuqH3gvSQDMnIOV3FxB73sDpDFw37dp/cxuKCF9f7/lcJlC/u/vXut1n8DCFhiJYCsB/F5d/55ZjWCkK1tgeyU/7yfgFoIdQUldui5tEYRtoKPzpT9f1f4BUkreAKRkZ1W2tgISLltUbGtbT7L6IX7BeX84gIodbRyJsuRkq0vSt6RsKxQ17/3dO/Ru9pE7T+58BNn1Qu2ZGO2ZSPJLfkcilZUESZCKQoAVa1dpZyYnOstumM8lP1RiBPz/qr6upPSL3PVTStnasLgtQ+YsE+97IMX3HgDqPZCKAEpHAqlfAEo+eqBkC+SXI8kN7vo/rTUCpH9ESk4ESi6UK13q5C2ePTlT6WMb1ix7xjEZli3DmDHDtActPKS3ANi0cuA8/3Mss5sty5Q4s5OSrbUd3/mbhfQ1JfpNiVKiu3MEHtCevvGA6MRpjf+ns2ylQ/vQe4QOH1CXGrHpUpR/pIWRfU6kpSct61C2A9JtQAogdNxxm2pm4Z2014zsgLybQj72huwDSglU5aXosGh64C46RmIE649pAnhLO33bZ7oP3NPgGi5NIbIs7+iwhi2p3/lLjrFVDKznfy9vcDwtom5UXNQwNr2L+ntra84oVFpQUNy+ydSO0ZV0DVKSEjIny9jClizD/+4wAFWwZAuS3giLAViQIJ3KAFjh3Zx0gH1ggKwcMlDWi8SWSM5KWyKgDLdkvPApT729rq5FsAAHBQRIt+il9M8Comfg76CZB5WFaKlRPRM42woCwAkgATFHlv1VFbshp8Kq1D/SEj5TcMGFBNN8fHb12c1nd57dfzb77H1+FdbPt5/7nieedxciC5mF7MLa5+ue33/+vbCcrJOLfj76atVr6pvDv2/PfHSQDbZdwP+rIn1pRfQtQvIgO4/4/yKP3gboa7RXGeh5WuuujqGeY7oJvuFl5qtqgYWaWWSxEKiwto7azjS+H1hO1zFEvd2n2RJniaWWWbV6uTVr61lhpabWrd8ww9aN2zft3Lxl274du3bv2X/g4KHDR44eO97JtTfedOv117Vw8y233X7nXXffe9/9DzRXxqMNPFZZdeXdUUQt00xUV1dFTTLZFFNV015NL1DOKHPM1UNng40x1nAvMUtp9bXSRn+NjdTWKwxyQx89PVXJw/0MMd47vMjrdPAGb/IWb/MV7/IeHb3PB3zIR3xMKU8+8gmf8jWf8wVfMk5ZFX3LZzTxHd/zAz/STTuvUUxtJ/aOMNMzw7RUQRfP1tBQI0/M89Bs9zxYXC8llPT044qUxOLyUPZEnP0RYWs8ehMBDCCC6EuEMJoI41VEBAOJKJ5HxNCaiKM7kUAdIomhRArPIdKYTmQwgcjiG0QOLyPymE8UTlWCBUQRC4kSmhFlLCIqWExUcQpRw6lEHWcQDZxGNHEW0cLZRBvnEB2cS3RxHtHD6UQfZxIDnE8McQExwsXEGBcSE1xETFGFmOESYo5LiQUuI5a4nFjhCmKNK4kNriK2uJrY4RpijyXEAUuJI5YRJ6wSZ6wWFywnrlgjblgr7qhHPLCCeGIl8UJT4o114oP14osN4ocZxB9bJQAbJRDbJQibJBg7JQSbJRRbJAzbJBz7JAI7JBK7JAq7JRp7JAb7JRYHJA4HJR6HJAGHJRFHJAlHJRnHJAXHJRWdSBqulXTcKBm4STJxq2ThesnGdZKDFiQXN0sebpF83CYFuF0KcacU4S4pxt1SgnulFPdJGe6XcjwgFWhOKlGGVOFRqUYDUoPHpBaVSR2qk3qUJw24QxpRhDShFmnGNNKCiaQVdUkbupJ2FCUdmEQ6MZl0YQrpxlTSg2qkF+1JH2qSfryADKAcGcQoMoQ5ZBhzyQh6kFF0JmMYTMYxhkxgLJnEcDKFl5BpzCIzKE1mUZ/MoRWZRxuygP5kEY3JEkaSZbQlK3gFWcUgsoYbZB19yAZ6kk08JVuoRLbxsOygH9nFELKH8WQf7yAHeBE5xOvIMXQgx/EGcgJvIifxFnIKbyOn8RVyBu8iZ/Eecg4dyXm8j1zAB8hFfIhcwkfIZXyMXEEpchVPyjU8Ikf4BLmOT5Eb+Bq5ic+RW/gCuY0vkTsYR+6iLLmHiuQ+vkUe4DPkIZqQR/gOeYzvkSf4AXn6/mzqGdCNvEI78hqvIR9QjHxGbfIFJ+Q79soPjKAggpkUNPCMgjaGUTBFSwp+UyEWi+mCCjHPomJMDTSLaYjmMY3QAuYJ9BkzD/3EPIT+Ymajf8A9TjIPlqqB4vx8hF78MokS/CqPkvxWjaf9HT8eh59AkdcU/93YzH9SZU8/m6dxMLtsaTvsmL3VkIolWjHDQm7oI1cO9yKEEY35OxnLRg2xJnbOKXhFP7H13IiCU5gLI4Ud03ACU8JHaG1DIpxBM6Fo7D7S3lo4T0IHGtdFXVZEs9NLTZOm8U7dDFSNAj2hCjGtC2AWZeMLNLloWAUCDk4aI8bRLGPnM6edoI+OKyvzGWvkQTt9vVbGlpzOMOPTnniHgnFjPzx9gQwbct5utVS0Sr3PFEYhGAbYXqJt9+VHzjt4f/MI9V7xx8MN8qe1Hpbkn2kK8pKsjf8jMf/XOx+fJoqReosbKfV8E/jrstaKWe2Fbe6n+WxKZ/xBqV4NVWtPIqTTaWhTjrWOMubWksxmWOfcfpqVZyRgrx5JXZVsNjYGl5zV98QZJ9hjn07f3e3YGe/6E+CkyWZVc1y3pSHG44JtXZczJUXHyj5+UcE4Fy5KmHOubfe15kjsrK1sWQ+i40cR+s6shZA7XQgRiY8fF/Ee+/xOxWdhwuFCo4v0dhgg03j74l5OaT5yFIY+pv45DGlwntkTlaPAoz9bkrke/TF2WxtjT/lUKpC8HX2Shu9oizcke/KqEfXj8zyOw78V6IFOTAQMslCM43gmLnQfga3lw1fkiRcaadim2MVun9BFKM/Xcz4IJXlRkebJt7J9QZxMsN5zOvMmTqJkAyeESTNNhU7Lh4Xw21JUaRu9eLQ38JN4Jen0TUI6CGx/8sxZC6W7z27HBOBl3kWgtem4PZ4SQvvYT2wbsE5TjPvWGYMpZLMpvb9CWKNOrZdSYS7Kgnpae6NhBV59SJyTUdEJGW/9mDMmtYDM87KcgdDGqPSGDqUffdflvUQfj9DQp7rHgsfXRahfPL3QmgoD52M3hROPFyfvpRX42oXNEZzDQEdP0bENlWgudKturwJ8MX9DvTf+9PyC20HffqHN/q5z3lmceGDX2k9ukDoO6BZ6j6UowQ/0zXir2lJEnJYOSsNTqqQgNpl5fnIC6H/ODpuA6ESCTxON5FQbt86q3kFC8BIE3rJNj7TmctVG1BDLGksg4zrBud1rRE6cEVC7aZW9H+P+8w7zq2WqZbDpwN1NjhMGnjQmg0sRGPxDTlDxNM7lqQzbqeORjQ3IDwsBvqltCBK8TYJvLriAsFNC4fpZZ51n+ZktaUGcrwMAKy9+nmlooWVDeLy/G3FTILiwmfOXZMIGrpcg5UQ4DjoiiXEnAhdw/rkSydxY0zYwX1fGCXpMOZOMuQf2LKV6nIZERhKLOJsIRHN+Mo7/UR1jSOWQHkQLtVBQ30Ksj01nmm7DlU4wPxWRZTWICRiQa2uA5DiCu+UARYRUTNnirXzEjY0e5GaTb6k8bbKZWm4QT75cqnDKvCrP8Gxsbzt3pl3GfJ94HsV81vEXJnd8ef9sZ75Hpmeozzh5ZYZg6ruMsqnaW1kTp1PKqHoyOXNk6JuqZ6RNbyxodmfty11r+exF7r/k8ReYN5s0La76qJXHtZsuctC+3x8t6ArMKRepFVCdDbm0aehH0hFWxhaeIrX516Zn6RSZ2enIuhH9T7t4K+9gN4xt7ODOfsmtT0SD9FV/MuwnL23b7NqROb7eY438o1MaaaYkEYhemboqEw3k1stdw9NSOVug2ja7lVyBA6IkkhDg4HGCtql6tINYp9kTmHiIAOUZqV+5Z2mzUiCem1xXRzsGZ35wPqLh+QAW57gyibQ5IoiWk384/k9Hf0ncQYZgyBxBapurcmYhkopfjBtkNEdlCyzGD+SJlb2s8YhVopmnyQwxErgipoOGBiN4iuoa1sIQ4Xqsf1M0NXuyUKQe7c0byhFLowINGyacTO6KRl2RtVtS4GJzziGMqGBI+ka8e41fGZOMiE+iBdv4vGuvSGRk6VPpA27Hmm7uB7SnWos+5dT8cVFkvxLtsa1MValdK6/90R5+LXuOPA6Uq1Fur9rjypqAxdKs9lTWABoS79b/flwjv9V5GkferKtSYlHbjruHYs81ap+L/pMK70mHThBlcvfld19XXomr0PlLTax88o2Tbx2+K6o43zzxDy/0pCj2isC5PfTXZU4BpDIBlGo06Yrh2Ggewehj09sMy6NuwLY5RQ4WnGnIWaOo62kUcBQEEgs6JFs7wGgIl8wKCDh2NeqT1wV6WOlS18gY4ez6SP2ZJ8aiL1PZcjST9ltlcjaFmq1Z5MO8uKBomIbRWCNyOgkA1P968mmvL3hVCE811jdjMho2K38XTDEtIKcyU+A4qmr5xvN0YHhq+VfrV0UT2xBpC+H7PRdQ4PocalzpXmlBhYxyXS+ssW3UQU4ToSjmCCownw3hTbMhLltaRM4nnJ5OTWg0wOmz4BBeMRIUjKt7h2IF5mJhIlr7s3F4sJw8tjS3ov5ZmZqxOHL34LDsOCK7aquppnRSt7mY5IjUo+thHIzfkiSXAj8iGRgI72nyM1xULCHr66JUvN4urhUEOtc7F4fbR9YY63qp1VrolVRYaPdA7tQ4rFv2+tyfB0V5Fb/llSUd90DT1Zw67eFDRuB2q0rJGOEYuh2hCOQIQZg72Bu/jZMWHA1yUGMXB4GnqYWNh7URX8Z5gSjetgBJBsMft4DKBM3xTVRCUiexERoe0tiAXPNugHkq5XfHDrS6on8HWH1kvMLB7HhVmxrqgKJoUq859YCN0e49fdSwUoARAmWEZf3TjgoWUA0EHglWKpWTCwPMRRYWyQtAG1Aucizs6gS0RG7CxfdxmakuxBNmh4ijM7k2HjzzTfFRXS1Jr7LxUNHBXBb7vlya6JYioHLVW0IhaHZdkHoMd5739uP+7R4lY0cnFNJY+DM+z/cf5fHg8sxy2tY3+juxgZQ+L4+O75Oh5lwnh9NJ3ZMApbBNL7hpLr232Ho3c6i32Y97w2ceucPN8UEWBYqiCxPHOIconl3VhOKkAG9PqhPFHb7iV7KNDKKdxdlv/hzvE8MD2TPd3M5etGs/BpvGfmNOEVBmZo4qtgf77EC0X/odGZZCRgSQkDhwcS0ArGtrpRwZSVvEGXq9nfFda82ZOjxfW8z9M+2GjTjugNQF2QsSWN3ZVx7FHsPZo66WmK5juE717xT+e40I6ZYI7ix4BDqHkRTomD4xj8iYVkDVWxY8bc7OQ9pw89Tg2UeowUM7G7eiQJUezUik81r7Jv49j/FIlvYh+/3GlYPM4n0T1ch6DylAtIaFQcMyCuxDCBvpJ5iQLC6YZyUoPorJDoLhk/32Zh4mnwUZj2COzsZ3oDJCfohNaFrgAJEe/ZrRvp/FZn3eCFAi6shdj21SahY3jQQfGvi1kE53aP7cNVihBwFYXNsgUVva46BFbL5FLfKeqgvURMPRCPZq2o/TqScfkWhMug4W0UFH6hKrsySudjJjL6sFnEBJdydmt9cQ9VZXkc7h8dofUpQ8Ntc4YTSu9KcPzYY3A7PthXO2qPG3I6OLU42kS+3xzi/TQKdgRhe4fvs+kW20XBq24kzttexcJxrDppDScYhXTVJJRhTpNHbzb1pkHLUjTFvxrhhGdF7V8MzVk7pdLq62GZf9ivnaVeNK8gLKru3Mu8DlNKtks6wI1R+j/agaPYm+kRPZcNPFdH8HRHRW+9zuuQdk7rjuf7nfdkhmV0R07Y8RlF6XyE/Phd8AWFEGXQuMbe026HoDQyroiBO9lG5NQCBkHPSfCqwKTM2Zr/g6gJwzjx7UuMgX4ozc89udfUc2HPmsxVu1jKyDBQ13s5htHe2u9l/6LNlWNiwwq+LqMFkejFJAs2r8PZSWFO1hd8PzNRIYGyOE3iDNcIMBQiYArHuFPy5fe1rVbvSdFCUTFp244e7OlCJKIs141twhYZ4AJ/gwvWqUUDixSLgszQX7Lku2+K406iQNqpl3cbuXPW9OdTXHLWMppgu5W1A5k6d/06FUxxrnkROM12VznFCSvk0JIv7+GNmTdg3uj23kaxNHVZhSFFviaEO1jB2P/CuBmK7kTD8X7cMHsvGlzekjMuR39Qj+8xWFom+lf8/1x79+/JfLx+7tFb+76835DHvb/ftW7WVvYC7tmEQTPkWkvu/8VrwIoyUyLmiyLCfy7xFfH+3bK2orUyH4mxCfjUg3YftfR9/N9oeL9q7B2T1pia7LEHJGC1oVC/AtsQsN8ZE0rLlnmGrqvLsvGs3pe/51eGfv/pvy5QCbo+1xEl4Pbh+sVSIiJxhFIlol3r6ncGinW4PjxKbzxLc5rc8tuLZrbSdYdLv9n20uzw96EtNQEuCcTCsaag0Zx1YiSIRKeHApFZXsUFeEXipG61ei3/WOpU6QgWWlpLdNs7rd79i7BhsygEpr4K0lcyVdVac2zb5rdRNjqb8e1rj8FrM/tKsQ6s3homgHPuZQtFSwfXVMzOU5j+2hGbQKr0LjkIeYRrbB03dcVYdKOcRSsMQg0HoZ17/YM/MQ5itq/0JzanFuoWfJzMcVjYjS2gga+lk4aDsFJF3WZQbDWkYiQYlIYhWb/XXVegOia9EiwmhXZ5MaTFFNcW+flItlMKiJ5Wut6BKx6UfVSmk+vOrlJTRyOYP42k+giLe2vSKuTP+iVq+MN7559pEDTNhzokg7HborgRSTjOJBWx06EukCFnicDRzt/bDuXlXDww3ydRwLo1JB747FFnMG4AWqe2n49fMAR938zUHqfEi0iSligiTw69RFBimw9LAo/2ZxOnkA/MDz8e47gTjA3zcDLEnEG8mx94nSJSODPzIGUFRfCyFrD2sHX0BnTY5p9MeW4TRktjP2Zx77if31+bn6iWhL7Rd1jTcwjHL6UGqblRsZya0j2O1sCerX1wT8d5mOi/VQDrANZQjwaEeYlso0pEmuCJJmXOJi26WVpljwrMAFCiYEDoL+aYspNjWsY8GUAohIIgBSBDZjaizaYThTcDsqPRdmiTJ9LyDagQLp0bEfyUaB7lYUzfyhtbvdj6dqfFpzPapO09lMjxELK+EdflwuUBSxEmq4dp4U2wqIAxfq70VdxLyCMKIvv2Ka++STazeyXXLa7gw3KRbZMrs+YqzFZI2gIFt8Yls3/UrT1h9pP5a2iLxMt24gOU8nuBIEPLEDiBL4668LkH+TD2afxy9kXkIEZxGj2EG0fLwTFfq8/QJ5ySI2teryShLnVrKMnU73HmB3MB0499P1kW+0vNXGnr8zjTkf93Lc7vl35uQND9snVFuteA/5LVTQh7tce7dOBSQw9kvxutjGzK87NrEc5KZo2q0TpDRFGlKBJrDgOjuIljLdRKQCMaeIGXdfQCAzQpqctN4dZaMkRoPaOPnUITcMCp4ZLZeXusxmF0FGzB/Nq6r9pSLApVGg+nfQRjIBiVTQ2cNNiZEQiLmtdmKFlyN3YGi3wF3K+GOa9n9kLwTVQqDJutOY+AWt+4eGVUkaBMkvfakzouqj3V+v6ssVRQ8FPL5JiKtGw48Yv0eP4qdAfyyjEt58lGFv/xvouEmK+NRelKtcyniZIeDMrQp+p2R7gPlZSmd+1ZuTOpGospLZjZmlETFVUt8tedXaK/KtKq5sPuqjqr//JVbExw1rVulTB3Fp2mY97cPhI84rzJsdh2gYKAHK1HX9wIaAcQiG90qlb7cFov9BshzVpFOgkSQRL9+2kV8DysFXINQjCmCE2NCTUlPj18wcsX4RvkpPkbwRYzg9jgH6cr21AhrYkcFFf4F8rcsJlTjRgIa6VGTMPIVUFElk3VAnEvMMt6GpzQwcpdu0yZfzYI/YDP3edWFQkIebRavADDaEMvxnn+Wfun9Iw1+lchjVuaXhsT6BUkkhgtMgGhoooDAV8MGt3Zp3OEqPW4KNy9GN4rGRhhQDdsuOo6fRyPpW8l0zARQriWzbhhgy0HHcME6HypFBw4nqyZ7v9mqKrlkGTva2FlU0KCkTq+VSZSeYc1LxIp2Iw0uc0sVfB1y9dJm47Dp8dk/zUtC3KEdqD5K9mjogyqehhSkup665swOXYtw5FMItlB5teIwfVGFZfwpdXhTlG2a6a2qachX0dgDe3T7zrvdZ+fwKc5FTdnRaH0Uz/n1ejWjuimmxzXEc2ywRxJNktESzwCoabGAeRsLquurkEoEPS/YTHlJO4g1nRjPFb7sB6eCvW4+lDzR2dyFYFMkY9+1KYsyYq6Wxj1ja7K02slOJOILoiKFADOOfwCUKi3QMG4xjUyZ8RzjGBV1WWI8PqY4EmlhC/m92G85VWKc1Xb7AFFEF7TFPjrwxuRR/7DB2xDUAjqgHNTiE4F4v/Hfai7nHiLs19z5c/Psq91khUljg/vapU9zkKNi4of3Qv6lfa4QmImRuNCVQ80/6JatM3EgNnox2qfGWY5L8NTkKz5ikMD8Ji8WHHy7+vCmF/5nxxMBL6SP7cYSmFJJXojwGaU5GTxHeBy6BqOeMIs7U1EC1fJM+4HtF7ms5cskovAhGmokZTf2TVotkVI8XW+fSBeCcMJcmb17TM7nYz0Aj0lFB19PGYq280QpBFOMD7Kl3ps4sR6+MByOL128BgcCf5+Uy6PR94NCYfrFil7MZUrAS+QaHDh07fvhwoTA9bk0wNqiLCS4U3wbGQx588HmWOz0HYfFRXSGPUGnsx/mcU24ePHj0yc1ENjuR6yL0MURRvig0f5379EdvvbWEixnX8FlIZyqwsVYcH6+g0dcz/+7fJYyGvQfTmvJNNph/C8LILA3PSo1myNvMCbGm+3Os+joADzVMC+ZiGnVInYL6DYpJ1UHuhyLWk8b30gSuZcYkWstEi0zWPtwigkXb67RjqRwiXByiKEdx+2bpFNdk0teF4c1h+so+apM6Fj/UOeBJxRzaPJrvKV3mb3e3dTiUwiM9NWRppcPcVVFVyUQEjV/1aFI7qssrj0qBdITjVxgPfqmJ964kD6SySkNNy8iLgJCVhTCdQnquM5u0LcVD6enoeYlYfEAiaLi7+Z3rDQGJPPhn6jFWGVFdhGihOJggR9Z7694b6CJ2A6feTT52TSzTIto3YU+yB+Z89iD36hO6O9mirsNrQklGlDaxeZ03hBIADaa6F2huTjK+LwONp2c2vudZiPNhR6WHnU3TM9LuUSG8iQIb/mKjAlFUhxcmFcq98drTlMQcCXKYdOMJUNRJAIw6VmknUGOBZ8IXJ2BKUivqVjrz+sFSDjZMSGmxMH1mf+7mBItSKsT0lA6qOsqDQueA1KPtO2gdTi12k3svZ68+eKWDyCuoM3mwQYmquu3TaE3SOB9KdVZGHifhkdEo62OCHUzD/QDWEbnF0miKOY+IrUyYQDBS4tby0wPNqXVAyFFMYsF1TZ5VN2i9eiMY+Xfwkaz2+sz/rZz4p36MqY3bPIIOLxtT35KqM6lkbpC7NbC/ysf/bpzkZv0WsMNiPgl25dVve8hwufXQXZMdfEGKPwbT4GXho17j5MPP65CoCX81ZYVK88EsvpA6HLI7XEmffPVD/dNLI7H00nv1z6u9QO5Ey7JfdzbMF88suet0GR8zsm5YXapXvmsFDdc1kwnBm4We36RjGxSU3ahuXbznJEKRZsZrTlj8MRwL6WcyMc5Q7iIAK0tAEjfEEapVo4j1x9GuUPR5ieADK45eOpKkgQbAJovDVYYvBKJREKneUXko8UosfEeaQiBK2WPqa11Kw9dMba+Al9U3SQGU1RylNEwG9UFQWiZhNWiFdFEQ4zYBlmZuJYOVsUjYx4xsHqA+FwHU0HpggYm8FdCsYltMffGArionGlsTUipoLR+4n4Y8LZuMUV9X8wSnjcpW2T6qIh8iRwVDdTkYqtEC30EesTdz58QAC1LE9pPz7KyF0tKUCjQq2l+A3IB6ml32icA0olnqz7VpF4QlK516mgRacWNFF5VLUy43F5vU6jNRxZlaWV6n1gSYx6abqytVNLIaWsncxkALm1s7D2Zel2o2qeVxy0dV+wt3Uky/AuTpn0EvcCJJL5k76CGOqCApGVNxN28G7OGgY30utiRJkjXSzffylH0H68Tg1dNPrAzvWxPJ/bflUPuRoSpuTKws3VsG+MQPv+Cxmn09LkFWe8aaRaeGwYGuiJJxrJTg7UiTkEhpSzXTtmsRyVqMKdvuSw526zkAKLQfXMsWzJVBbwrsNDmZDNJqpCXaPneoHh4g1RFfI6cLGMacGr3B+WiNn50pWgyq5WwMwEzHwe7jmAqUK6sUBytGV//JI4uodzSlEUtW+iyu4uXmcyGlq5Aosr8VnxVEy440RgGC+VBavvpi0n5lAXdk2eT8dqIsU2SugQhF7YYTVoVdKewSO4rgNNH2dWMROWk0alRZWRhaq7XVJ+XW5hw2fzc/MPNvvVo7Zv1+YdgNmUbRlqEyZzDqhuc1gL2ebRBnqu7Jvv3YbY+4aADv+DtSjSEthnRaKgxqWfXKPOVsYcyy+zvzoktGUhR4WxNM5YqzlDUywRfV0N28BfVxaMFIkRosGlA3qdP5r7fqyLY9295c9gbrqw+trptes6nMcB6mqtIWHPJ2JngSacwnkSCKLWfYW6flgewAwLiBg+f8BjFZvnd7HF55s7HocryjXEOJ7KJM9VIB4NLjsLXUKOnOshRsBknHrwZaXbnasY1MmNPf/+accw9qVQkPbRUxXqOYvOzazgsUkZfsBm9Zrj1FMLWIlSyKqUKgvRsm4XcIoq94CNtLqG4FddVh475QdTvKdPeJdRIE4owoEd3DqLTXmQod3eO4iBQ5NRRTC62bY37N6xnGQFw6mt1EIp39I9rMLRj8SUYyac/XrIAWDSeaCoL0+DMfGZNkfycHnZSb3k6mPD5DfJdVpS73wv9/ds/+UPbEr9D/nfr8Vv+biclEWEF/KoUMu4ieB2vO9GDyyFEgg1IuP/IHIBeQYY/3PGnGhyt2Z2JeTh0QnWLwzvNiwHXuXRPKe6fVlr2/ltTN4ukym66UZv3qzolxk/FsyzG4Ov3Ny9CenGkA6hsOz4zGvKLDw0DJAzDFzR99jU5EW8lWouOK+wjeWfNKWnB2xQxJFcJelc9noMuQQ9WCe7W1ivLEYobLoMnwKVRtgElQu0LjT5+NZYADc6kCpvqC/HCAMhHu+GyuFo1FOh79UuTFC2x4UeT3TcNLp4kwF/u6vfdosGkIbX2d7LSrgMwtCIPRKZwL269YM1hopqJSAGiNIxUdC3daePidrGBJmB3ju4W8ofdaN0q//hNCG3k0IJsZxZ6MHRgJRyQT/bMT9fr+JGfmKcwzkZrqo/vFeE4Fm2EUPNgyxPx1VuByJL18YbPftxycXpxFXMcjv0YERcLq2aXbdVWRduHbw3vuGO5G5VONJeMrwRGacvs9ezZeZWdmY7BgMayP9FHb3a7aP+ZQ5vYdWbxLARpuT24ZU43kCuyhrmHPmm23Mx7j9b14qrXLFfMv66wyPytEKiCxuqb2PwBB89cVPfhD1WzOe5v/l+tCNQAOodJXysWlJTkPKs9xc5On+VDqAFiBg9Y72/E+yRCPl/YLbk5lAYscnIpuoq1Y6jMayFzvbf2HFwy9pBgWw7TLLE6fic9LdAxMRMcx9WeQpw1H+RBDABKcsCXPJBj/C4Y5LNIzZFW9d0o6kAI3U9dFoKwNLJR33GsO0S2CEIRAPgphvTLL/jA5KkYXHQQYy//rUudd/cUo0Zoxt9DaeO6mOT+wV2vthabUo6mF1ulX305N11Yvvny4NNfVt2fPlas5d+1qMuKELJlVKMgBfIOc5m+Zq7QxXlYkADkCXm9UrDc1wqZhYHtrmSbAfM3yHD2A2GBuWNzWJOZEkFtz0KpaknoEqJ4A9aZr0UZSU3eXdc6XDKeWVpf4UJBVFNfPoqt4J7QUpZpIk1sEmTjZLJmuuOv9eQb7BnUwEBxq4GEIGGwe7LmXkHBLg4K8OaB0jO7ILjW2IhTq4rKSlW93ddWgQpGdLgI+NmGKA/CADM3onRFaoaDnNL1EwV4qk6bEL3xVUvqIYDCkoT1JT8B7tOq0B2/2jipaI0vXPmqKTRcxn78E4+8aqxHJKrSz6rw7G0tpGYZM9xUYSESShiNsu74T+rH6pESfamuW4SfzV47JDa+il9jVzbTQTOkgmveVumvbcg1KigiR6e3ZfrWviTC7gWjVOVQspmh0/Iik0hzi30kxoEYtckHYwIaIRspj4mZ2GkggDbxGePyr+k3/oX3fJnbxF9MXngtJ8JNWChbvSqSRT/QfNasxzeYdpv5gq6t1aUjR48bocbtFGGCt2LACC6Jj22JwI3XI8svKkQkZXfeRMicj5qhwJTBRPVQ1rqPvW6tJP8XLjTcdAJtarBvrd52O+helUyJTSyfVlKZsrWQH4LF+IQOlj7/01u3oH0WGRRdePFqen1s58fLbCuDoD7tfTDdOvvzufWx+cPH1k98n6GqdEbUjDX7k8dpfxnxN9YvBF8A8U9q/d7WigQW4ahQzWpLdKCsnZy434aQjZ/YRrmWzx3FwaZ+cwYIz2km1kBEniJzC+pyAtEv50kqyUA9+HZUOpZFSfPVg6RaAabgTO9DHfXXR86RQnSOuKSc/KaC5U4QD4DOtYYEp/F4RlZGM0gYYWJB75eKzgWJqRuiJwNkvk12Iu4mf3n3kmd68wRpr3rOHX0KNusPDk/AvW57ublzzrhb955aPPxst2Ly96T5z7FXcjn3XPhf9vPm5vjprzf3v2ppuZqiKWDfkAmlFyMSSuNpFb9GcrwXpNbVicSALYAUVVmszzdSIA9fF61gL3Pi78oZl/7fNt76g+dJzkdGgl1svP/3io1IMrh7JBLep11aaAhEsCu3Z4mqKR8mJRlYhPE3sPU8eM8LPgfkcHI8KJYryvo611zNCzM143H8bo0lmzdR96QUBUGHy6JJuiI2nakr02pFS8E6YwaB8cuOBxxXxdjs4QohHpbseCotPRTNDNhTfIiI4EreHPu31TdRIIhtcrunE8W72ytMyBQbxfz4V730SLXvlBldg3eeV72ZV9lZ5J6iPTGKH1pLk+PSVMBBJ5aJdvJtOrSsHB3JFpBnjkQSHhQbkhcanodpOd76niu55bRriH2/MXv0r3/lBWvT5NpbgO/+je8DwrvkpLAjZYLs18IhD5boHX5N19HR5w7Il+I7UW5GieQgSKke5wG9Z9rioIVBgQUAklTnWJ8o+mwQZ73hCxtflmHhfj1PCIKpfEKxxrmZ0KVENyFnHVnUnqhDIaUm2LsHtpyLsDJrJKf1iReDsEzKap+Tg9Iheo71GXMYNClScAWGpy3JzzzIuASMg152v8TMZQuIy6ZDQ/MvCvQ6t6EyO7zIgkwc6q69oGfSOfNZcHaIV5HPAGCCk2JgHXG3q5FJ2AmxGALISocEOSERr1+Jin8pmwvxCCQELn6dhBbht7grxZHH6kT+0zNUA6UUKzbpNOu/Jbvpq9rzZSOJkkb2dKL6uGZwiSDmuj4QrWd6HjeVyGJzmmXIw9rZ6OlPu6kgEI89TpsyLRSVFbsm8XuSqLvJUzJ7F+Z7pzCm9khz7Inm6CDHzsG7A1oktBasd2/6FMAMfkz35iuU6n2l2PIEO8/crEx/ILU7kZjSB68GFPYUbGQNW0KiVLzxrLHqw2JkuPg+cb8Kbz+dvoJR5HnIAb2Xo5qBrnVsdO9vZ8m495fNvd03jagp5626rbZvnrD94nt9zb/EL13oLfB17z12g8Pu+LjztvuVBej2mHNPz1aEQ3vbI5/2YZ4eySk7XQQ8GCn1K7OD1jDAmX1PP5SulmT96AAWqDb1mPQmBHdncjO+w6HODeXs9Xl3ZILZkR0p3CFlbWp7u2f5sXe1IpnSCxw6kkaJbvcM1Mae1EdeBFDTieJgcJRCdHzY/cRQajCW4DZbjQW+QrCZlr3bbD6xcs0UxFCkkDFL5W3SWFeJj2BGjZD7vlMFTuLvl0KkILyvQPN1wa3ZLCQ6pW4WH39jS7Mc4i17/mCthij4ya1AuaODxLXkbZiNSgEuEjZ0cA7cOCr/RYeF5PQe7LwG+/mNjvx65pl1zPRABmRO2Ak/uDMVkehRJXqh05y2XNNJV9YBX00B7dXU1JeuyMmgVwhsNwdwP70fB3GECf5mKbYW6vVJr85MwXRLDqlqjaLGHvXJTJfQ1BFfahrI4Dv7jnTtXe2womNxE7Ep6KImItJiCGZDm3rSxtTdsDzpNoi1NwzFMT2AHYbZiocm/dzdtM8etafs01IOXsWkrTvj6UTj5cartcQh3R0SkcTxm+quxs4bRytqjhs7+E1WbIR5oJTNu/Cx6vKqc9RMxlIdiLGoQE1oIngDx8vq+M7xJe38IKsUuuGvxIkZXE31MNwT5T7GlBFlP6alNtwEC4uWpcayO1d81igaPCeYrILe8w55EfeO4GRUP4BrEzSR3rodXJBsXknQp+Jd2MtW2ZVXqJrDjl87G0NQM7p/PjzAqFVAAp5EJOp1NS+eKeBYSoakTB+3YEsJPND5JOqQfJGlnjq12pLABE372p1KpFD1ClxVmkaoGkSTpns7CMHIzcQxPhzViDck8d31ycyjJtHJQdyYaP4qFLTC1zJIy1oN//ZESChsSayHd7IokS/NisXzQ4R2DkPudxWskY1CrPmw9iBL4t9dBB2LwQxXhBn3R5s6MRj+8dvZ2hMEkjI+9aXeXR4cvIbKJ3x+HzTs0nKL9+Mo+CC4W+aKlN+KMq+8kRfwhd3I8a0dpmC6BTziWQfN+40PlwcVEZzoRFc/uSsVNKZxq7DZpAaT0d2kEGLQEAoq5Ker6h7F7Wf7MuWvt360hVm7f5D0QupK/5PgiNvgq3vnJl56ljaPZytP15bTBkPG+Fg+OpwetOnslTRdz8GWNkjQ8ZbcXHnnB2xhYrQVbHIZA0f3eo3es1unVce7vpKyyt7Nnr16NjXxw0mjMP/y8u943m/Pm4oFG4nS3Hrq5hJ5YWhH57m661VUSxeZbN2TkvKQycZ8ojQ3yksDZTnbpIFC9Ls2XVXCwUwDemn06c0B5fBT12dQjSyHn83xNHNJMtWHRaP6lImkCe4VBKgm91rvobTM0OZPLPAkSILTs6qSoO1u21HtBE3HGBfp5La1VNUAGmzuoyILWLqJNs9aKgJzVJmqyeV2EUKum89JmOQlXI+TVNAL3I0HhvYxHFAYx8Q/uv0ZOl3AvDPrjaA2ca80GTYOYjm3CekeDIuoGNdzHQU5/0SZ1SyWRpFuEJvJIt44jOJs17lhUsZrKnN/PilKg83PenCK7Dl/kCb7b4E3XQLmdBlF1t9D0c5Hcp7sj7pZCz6KhIt05pGKhlonh2qu8nVR0s6aTe1wjvNO3o4C0q9chzvQyZ2Q5DBInalsJbLSepHuVFbDK26ucrdhF7LfdomiDdnX8fgPBhG6VpIMnHmX2oUC0p4apOKFf6zZGwoZ8JzY1hObaxspia3XzBPO5ioCoT+JEcOKXb4UMpA3t2BgCDOMRZHtGhAMT/CiqJD1tQ0lBjzogoZXIu/ZWsiEVz0yZ0l3Bz2c087XAdRq9QDtVAopZQzY5qkSCFm5lP5o+8kLTjIYylOg1JlqjtTTSZW1zGaQeolZ1zEf5Y7pkYwwFFVpAI/Sf8eLo968RunZj5wmI1pePv7Ag05M1CMdyMzxg5a4tsWYQpyovSq31iOZ44CCQTYmSFHcYOQBdpqGpCAoql4Jat59tix0UkoN8bTvnydccu5CiRBir6rSw7FYNKD+cc96b1WTqCaEaysxSYazZDiZkm87tKXZ0XMUpdes3H64Z3kfnYWtS8SraOl9SqIKDYn/O+OUw+rvCYF4/vD4hu0+k9lsO7dYbTbOy6OL1hjmZuJO1eJbSm62oPd5/USNstNR7JGjM5FXwx/R/yJunoW31S4PbYEVvTO9w7ZD9q3mYH9fxojfGmhFRBBAaZXZLFtXKPIxd60A9yyMYxVOIwJbc49CETCuN57djP1uyVIWyjpbJSoEypWzpspMXkN0GbndFEZkp3Deczo/jxx8HT5QLPD+lRrwzQH/0MrcMquDgSKVcwBDw5paB03LsIp9a5EPDtb1U01htCOghF1y9GanBcmZnEk0d64S3ByLRQnRTnU0toHoWMekpPQFGCTdvOUUVxTVXt33NQYKdVPbiVQwTAlJIZw2FHGGOcYF833GAASDa8wAoRLTvAc1zEkgzNnkISOgRFlFewbUpKFIV1BteFLaqsmir5q4qxsFAHpesrJhOmMzneywLPdwZ3VQsruPu+D5clY/tNX90xAinIQeUDWrlix3rx0dfCkfqCBpOaSLj/Su6VNr4l7Mb0oOA+mvx2nMPS4LTtJE1e/GUUyI8bg/NCMw3jc3Ua+BgVFh//pGi4fixffTkGmlmqfoM/SfUem0BRsil/sFD+Vrqn893tyXAZsGUvBv4EBT9FfjDjj2Dl/kaA3OY/2NkPsKjxsu7/gw2KNCTwXLPQEiPQLhXVxI0fPH7woYnmuM9+v39NmIjMHDBSCL6TqnZh2dN1KjXBUqRMmpaQg9GCM9A3vIL0tJuaCr6k3xbo6OX27sutWB5SdgCBRWJPJf3h+krbmf6cmPyjl9UHvcvf3zZ/7UY7l749GpA17C5/fSz/qG0odX1Tl7JpTuJSgdV4Nt0oNd/+J6hSFnyninfTAQXZQVmc9l13Hq+kIdve01b7IIJFcWYGwpcc5yGgCrK2ZSwscVvCzBjgjJpigmSig3d26jcW3umPg8kV5masa/VMhJhOA/29UIyjbjIKrmVeRRPwmdoNEXGMxQnqmORRAO6QzUslz3B1MrWoV2q6B985FbWti71Y9tYbGr9Prc5yR7USkXhsYZj3jp1A+XjIE0HmjIFIXsQmhezCO2btx5iTjqRgWBcfuNTT1zvuZ0uPoQCRmn9YYp4ugHhbLvw07dFbzg8OfPFV9q387d1Yern8NunPRdWbvgjj/QpBk7C6j/p9+2q0o/1Wgjc7Z3s3gNFuffVbeAsXuORaoWjb0JTMnVdisZKvia+Ij0o9rDBtf5ifBoTS4jUXZ7b5kJTueX9iym7iHjqGbwrStrtP9Cw2LvuXuro8p+IlStFgsgs7W3yaqGd1ygaI2XMqUXsNcWg69Orp17x5NhKlrQ7r76o+6uEms+dGOSaJ9e7keyuDFcs2uz7vRqvWss/g062K89zg43WXpuQE0/AVaMxTzaC2xhFDqtzBaOarRpSSp4iEll5vVni05ehiGY0Um7ClBr3uZgzf7YVf3kvBliGsGwbwvi7B2dXmX+5xysdJbjiNMeLw66i81CDs7DeJ1lFiF8JDxAFGmtZXTjp9s2E2+ubqejSsbP2ULpRAsOGeJu6URriwSD+wE3STTtja2JgcsHB9RGTCmABwnJIbYe0LTCBbB3V3rA1gTfM2sL3LlEEch8I9m7YCHmLD4roGJEBoC0vosbULTsxx8H3Jd4vrmd4UGmnDeBFsTOHDbJdZFE7l3Jb+RnFelE2pEkeKdkoDrYsHmtYP1b4fIcQn2nmntLJZCrJ95Gh0yH0AM5ickOSw8T8ijmZ9neQaRl3D+UZP3kaEvlRW6O2Zs+6lzXbL9Fr5S1j/BRaVlJ/UGReDU4QwNcnNOqR7lT997b6Kkwp0tSWnDRxnzlERRWnzvzFKjnwEmOapL23pd6O27PhHrUWk6Pg/KeqoNOd7I36shXVxyj/OuP93Z5Gz8F0utb1mMv/ona4blbeB2tOYdfllzqxc+tEu54wzXIHzVY0rrS/dhrzJoQnk9ZEIiyXNuKgTlYTAyILM2D1SWhjz7IzPXym0WL23MMvuz1rEIvmmaJdSecbXhQQu2U5Z+CAoHAkKf/QhABA3QxOaCJ+A1rkoDOKJR6CRQoSeJBGyDLrqrXfc/11HT/M9TH0o/UdeJNsHoBq+q//Fqvq15uEO4pjYk6j0iNTfVqCuaO8Kn7sn8mG4vACDhG8g/qFTdNyVoiL5CrEFsLh1RMc6XcmteJXCGMam0a8UxTGjtH+Z0O8kBb5b5IAik1OKYmBzV8Bj50P6Cchnd/ZRTgqxR2orJgf59Qv4lqFGOJH26yaveLm6XLtVHvmZq66YuqnjkYITR2pvUcAxUkP9cafZUYHt88wtYN7J62bGdXGfthHkml6sSvxFPn9wiHnx8Uemsni4VOdgaNJOCyGb6Rfo2s8c5KO1IYKLxLwrAhwSNTseLM2ZP/c3ZCnvV245lZCvbAl6l97o8HPH2Ub0n99HKeIwlbyUPuR9NGw7d8IjDl5K1RxMgi0ehxdRU2f1T+ivq3qF1nkLpTcyk+zCz8jVBzdZtgQO2XHHWEbbrYEPXCFVYKMM0PpYgiv2rGUI/gZuc/cB67e/Nl7nTIyw7ycz9+hOMxYgY6lZoufNrc1ZQFn7mZO7H++EcAMg5tjpeRk44afMFVlThjD6iRDeAze7WsTDsSwjiO04tncoP+atLi+jcSmMTsgDcOyAv7KfbLEBkOSbVf4QYXjhVQOyKzXvuoI4QtqQXrYME5CSKseM2ZQFRcam2PO3Okn1yZ28SSPqMpzfkjDXba5kSKy81i44x5tL6hRAbdj9f/DhLHfbggWQ+3Op4JkLYOzYUe+6mNLEx+L6c4+E1OmUdNgxb9ZjL+TCQSgQQackgcqnRnus63g6vCvYVmtmsH+ZoBoxAUrB7C/M1MZqbmdLqxT2toyDSP4W+NoK3BDmkeZqR9jWw8c5p88jfz1C3n6R0wolNYJG+GxZy/izh0EcG9DUEdoZUtbmjr1efodLc1tODF7BZiaWrBCdmfXWlZ12F9BC+AiRE0OYPTjLIlHmifTDGjk/7fnNiC8MTR/ym9Q8eXLJT2DgFspazdzRkYCbxcHAA4iRgZmmVQFdoXKOPqh8p2wFIorWRIHcbcuWQLHEfB6bWFhbL2QsLaF0hxYjs4vhqJAaGJhIIWaua42F1JAa1S6iUlwRSWUBMkzSigHWlr6OH3p3cWRcDojI5EkniGPhIyI7FNDBLibrint36+1r2m3GOunRM8lM5PNRw5z+46wCo+AD9a4zB/6glRi3WJDDxcTmUXc+rc7DI27GTirsDkIgilyvUtwcmAy+G9zcktqi6xvPLs+tetM6srS2j+uQ6ecclb+r3szuWUsqeXGQ50XUuBKEOmXOu1h6TZdaTdLj8J5iJp6UbxMPcjSVIo73xdRfPnuzo69zH7LPtrZz0jhY/wNoPpvSclAEwTFUkmWUHIjq7asyFGxojLHWmADqnFboW2oraNtiK2TbS1h+ryaoUzC0Phc733CFCERH+T7m6inrzIJk/h7lvfwpS7taWYcdC9d1LwNa9b8PN3603PEkqIzjGzyG8R3alOsKKxx5Ca/3fiOnmTgBYk03UIXYTc8Ul8tPdP9cHEUNnBbN9ExFCg+vvvXipgNWMJDwrNnHfNO19wpE2XnRPeUzhTheY1OYKBsUPi8c4Jg5Yo2gUyyXm/anb2wkfkOqXQcpSuPgCNK4JT3vdCRv9ihLqZGeMKbc0IwpO9z4zs/zuIQxZJiRR615H6/YWLZRqba11ouXF5WLeSftGqMKwPzNXeRXC4y8d2G4BbiAXjM0ebTVU3w4CDsuKkjqBNu4Ic8Pd40hl9Xx481jR/R9fkxdfUx/HjTUcdF6/kx7miEOibMONi4uNgomBu6lxs2Vy0uDjYK4+4N5QKQPRO21zjUGOAiGDe2TwA9EnUEUCUBV7mR3pCAPgR+8SRQUjIkBdQjJJAYlgs4YnPOPlNNLa8/r5mI6Y6Ci3xgYns6aKGSJ6aRlAR/utK3CXLOpSCnJ8jUFpC+kVjcY113VeU+5zYVcgIxUGN8odeESgpFylB7qZlmFRQYzaZUQhm/EktE8Drg3PYHzQqE6kUpnhJ5eFDn3kDfZVqHqB/DC5u3tBT+L1/8OCCLJ+CJRaVrbOW3eB64KItAU+mS0Ag4GWrNNoh7tQAwjEypUFXbMbVBOutkoGe0b9JE4/KlaGHmX73p4rWSMom6zk73Evgb/ANUomNile+Sb2XIYc3mLHZoaFttpNl6oWFadjg34AxjcUptgFscCPigQ+gfMepw4K2mXOFJglwm2t3bJ1yCJN0J0m+61cX8gPTbOss/za3zjMIF4SEISQxh+DPCct2TAuFgexdoM/fqXhL3NNwx6p8gyHcP07Y5oh1a1i4mvO3WYjrRhA0ry/K4SLeRPuI+bCfWvrhL1OMj/YRiAaYTs5ZaXOa/ktOqNcOL17aotW/Xl51jYbF0Rw2JdWeTghdmolAncQpxnlGv/1vl1P2WUAwWSa97sFqZ1CAzwk3d9Fu20BoOSd9x2jmuK4jKKkDSYUxNlKLDDAQBt2l3CARCFjIRjKTEpTMi06CPjCIoM9eWkWFEpAAnsARJU0eDGGi8hHoqASTCcaXbB+Oiz4QDgp4LfNtPfge6Cl+kryRQ2eRWkK+pheoX05GNYIdoDQMKMtY6WyiI5M2Nj6FNxqBXZGKyoA78mYTW4dSVULIhi6jSm9BlFeimsKm7YFNfh1zewHnoJOsy8x3mnG/iiTchnWHY/QnArXgFlVZaFuQ3BAKl0kAwJAna22ffaJ+zSrK6vL//uD+IBf6r1OVK1iyzvLKSRucgc3M6FkFH9+2f5YYnKNis2PeAn+/ePnk8eBIV15lrVZRtsamqSs38n6kG4//c8xvQISIkuicHbpLfCoTHPlOZVqZVog6WFQJAz8bxb311MAQEDo/Qq0za5nGPL0ihi+NktOaAxLienjxHYYnP5Hm3ndHRtVSV7fik+PPCpI4jxQwWd9P9C7UXcIFYIC2+wQDO0wDCBtWuS0Ijt2O6J/F7EIjLa47tLreTl44HfR7o/o7LsGTGR5mgAtJ3BG5uufhYjmx+o8LFua3AlQwWIxGAVvr16+js6iWjSAhJRhCU28/ZpolweKH3EI1P8dXn0qWdZievDaeFSZvZOlCQqpqzNc6UH0fj74rZIsWGYlouXMaRjoXZD+gTxTBhFXRmJRMpv5j0t15qLQqG0Xr6aPhdEVOiyaR3AWpFTZssSVmDTsyEVI7UJUawn3EjxHP0a9aa6zL5ppCUFuMohvDFkh+7/yZ6Pri6k01JSpzvWJ+04f8wd5rGQQyz1zr8DRbyXLraGq+Yp3DR2KQYpFRNW4G7yTh5nXPDJLB8D8I8mG6AwplznXYEk5xu1AaO1FcYbjiaKC34K29djAGbQOvU93ix5e+4fh/5ssWQCjbwv5NsNj0Q8dUiydyMIWGIqt87qlWhbpsOVT6umubiP0krGK4hF+K3rzmOD4z5ykvn8Lfd3SlPDbXGqKGlmoebMsEX3+RoXqRJdbWJjBd1DDDhfguRJtG8Y1E8mQNWhige6khGDAg3DjFeUmIczA3vF0y4oYEr/WGOaSUlRgCpBMopzLh1cWhSaCtky/cuiklNLIjAhURi9vgJlYHu5qx6mee8dMOYBSMM3VuV3OINuAhNH+LRY8Q+sXlF5lzCaIZtxiiIj7GoECP2ddY+BCeduiigF9MrnV9syM/3QGy6fGUjglBuiE0e/HxDDL4uEs7NgyIBQOmlkCDB4aORMgODSPnISCDmRzZqCL6ZYmNPD8uj+oz8KYASC99WjFFXlHz4dCwlViSKPf1UuxclEPWnedkdlLER6jah4LpVTAL12Vr47FmEFrHWDS0ccqiM9pB8uLUVzoPzIefDkNe0poPebN4MIbQjxVsHANfiN7rdDiLIaM6RihApOOy5EeQIdXE9rqHI6FzOP0AJ6PJ+j92goJ7xYxpp0gdhTz08XAwRsL7NNM0AUQrlzOCnexFugkkHXcO3lybFB0iZTVQilWQH9A6MyjmCWOHknmyjVcbWBUikusIqukrP544I5kYJECIkMgqxFCFCCB5GIeNcWIRAdNcuBX7MUDAAnt4k4txIMUAKaEI9aODr627mFQQBvHDyGwyD0xARrCuGDjc2ogoAbAqg7tU9AxRzRaACZg9BasEBYPLG3ERvv+sFpJE0y5NZaXdC21SDW4xpqTu5MM7hjuefHZCbjFryf6yojzlJwzbUcyJKee108Y5I6ocBkW6NhB/bCgcbI7lrzPJ2yMG1krkYmhGJPn1zkke+SQ4nB8SYxALOzJTi++H0zelZshEtZtH4FSAWgyuQWARdoYnSIxOIxHgPWiztCjUWXKHEzl0BsVTgeujGx0H26TfeqMBuQhD+0zmSs90/d2op3BARDRWJpIl0zQ7UB3nEH5xeFkmiDPis9CGC/RHB7oINtG1rQkjhXEqwoyDAM9hPdcyEhc32aFihuKV3ATiBEY1vDTjrGwV5WlOB1oUJO7qtuItIwZwgo1ghVFtMixEGGXlYi0gCS7CduEMXLkcH/5YRal7ejEZHoV/dxNfIHqUwcq3DrRYujLA6GJMZH4RvDm5MVaX9+/kp3gl41L2LTZhWw+nTuU2+8IM85cyqQuIzDX7upoUg1pk+MZfxX82//2bMTdCP1dTM32oWOaa7B0XWi/DpiRTDpJjpq8HRNibnOm4xY1n9/f/F/Zc7kBV7LnzfoqWXou+3iUS1TTUY3bbZDZPuAVxGOGFyFCS3DU4xOplkZicDh0ZfnX+kQOYGAANkn5urY1/R90Y6182dU0IMCAp3GyxutpcChL+xAkuhE6/XXLo32HVNWlOw9ta1o11D0Zp3+YNd9y6tKdBED+3v4NqttQU1UuDYnR9jAaGr0JAF7sf8WwwljlxFjqOgPe4+ihdbfkwq3YTbJOXz8WzhJSUvm1+/yUlSi6/tYOPY0shTl0009xfsrd5LwgeAKjIu6+DSOj/o1jp1jHagwh9PUK9fupwK5pMAOdawf3WPxVBipvhT/Gf3G8aSn3VAhZHvixLmBbj8ZbdRRobIW8jbRka3c3iRkFApRoZ2q5WM2PVNalbTOk0ZdYKeXVtcW66E1iT4tGOv4SCqdhvOu539infg0at1Tdn0h5CyVq0pVkKnqWXrNE0I4N67gBu5Ro5w5SBwuA1uJE9+zG/XQUp+ZyDNy5MaBBhuAUy4DQOpYcAWv11Hvgk4U+kkDokaMXbSwSDDbAyR7pGiIWvKBLgIu9TuoLNgC9ZlTa/rWuxK6OxqTj2G+PABcQxQzNuICXC4jk4gIu7csb9ehEb85c+o6Tsgg3ugv8Ii7f5dcQ4RywoM12lvXGJlriR8sNWLgFvhhSjA9j3eXGnVe64hEQ5nsQJOpx54ERZ5wJ+u6a94jJSxLSxiZJxL0xJrixzO9QV6kfAaOM3CM3jEscixLvy0rHOl+5vlBh6b+5YBgf/qzlUhv2sM3Vd2Lp0DA39v3Xr2bKiCv7elzOXC76yJg0lAAN2B7vhIC0TA96xoRCScG8lKCObXtSF5/fhcfCjvcBumDcs7hffDBupd2np/Nfb55u6a2XXN6FW4xzt/dL5eX5kvhVITICmQJqcGRVWpyZBbagIQXn/uMwolL6qMgsolFR3E9THqM3cWcRpxdq6K0eFRyEs8Uj7iKYZGw8aq2eU44tMX5EnO+vT5OBJKFKQv3GEKj6wi8ygmoU6qJkEE8zVC84XC8XFBqUYwkUSqcQx3XxU3/k7SO+jqu/E4bPmIXnYnW8h2ZPPYleFxzfo8qrv/UXaYNxKy+exKZWxLTH2ay9CnDg/sJNPIogTvvJmo+gyOkNBAEHrQFFSF8Shj/CxSZgZwg/g1CkbB/R8MjYhusQgajCTt9todhh9cxtMKi3akOCYdfE9C9z/WBV6QBnn6568zp3EYucGbYmIAorkwXwyL4MX5cLOLy1o3d4GfK50StFFUONbRhFThsQ3IQb2FZU3UPe6MQOLu5jxz5DIdFY1VNg+8wUu7pOM45KyIemqvaWfB+UnwXMTetP8NXTSLJHsd4x3zIhd40t/sZ28S3OZOni/gdXZcoyCz/p4yeIXoChjnJbwefZOylZK/MrK+ZBbiW0OP9qedM9yXnn8P5e1RaAOYJG2hTHpyfTCE1XzbLl1iWqoN3qPIuykvILWqirIGMlZKiBg3fGsqWJCZ5ltot3Vr7M2+y+C9R+GmAJflgvTFXfsM9nUp8yG3OuCPbuo/SgfZ+dlFSts0NyhH06SAgkFRjIEf2684JhgoIE0TlONmmwmcD4dUlOQewc/FUhCUOCpjiYSXrEulxin/AgmRDdJsO2WlXfbjp1hCe8jGDv4+yMf4FLCgKrlO13lz84twP9agW5WiKhFJuXXyP+V3/93fGn7Ked1lEmJOSYXPQLF4C5QXC1jW8C/ZHLTpvcezP6Sh/2d0nhdDCw8Pjac+/zGVY/xjgqhRuUgYjSs4whtvrGdUDr8xxUZz7ywSxtzJG4Nvozdab7Dd0/SmWsw3u5yILDe9S5Zc2rbbcY/9J8riZVwJMLbYQ9h5G6SiGLKRax6K9BTjZK5M9Fh/ZW8NUxTKVcuD8UXozMJtCoOsErGlX8xOW9OdlGDzGv/mJgvLvlj/AfMqtA4qH1CroZphwBqFEtUViXAiVDKcxDXqygRIkG5mn/c+f8nn0kXvi84C3N/XAAjGlf/9UJkg+T4jJYV68SmnU0COpIDrB+LiITHdtyXx6jWdGiV7WxgmPdEzqdaN3nMCckRj+zH40t4TQrTqsSgLlxLXZYi9dy21W64QQ/HxQMyUtC48cRK1Fo36521OvAQ36d9ZZwhltRVg8s13tQMAewl6LoWeg8pNNlwWSAsRLKTXQPlmFbqV6acGLKSH2PvrmkkqSDTSBhJVnxrtdhpw6xaEbSQXABeE53faBeOnXZ91Qc4qyjK9idXtjLBe6FkZfT1xxZG6Qb9NlHFwycEYU0GqwBhzUc2kZjR3QzkCBkwTuRy9ro2lZWhZLKwGmY2EpmFoflm4uAxonKU5NFgWCxWHxBa24hi4hyFIe6TYfs3wwJFB4jB2rAy7D/cTN0Q/pfmpkvQ83D565o4dy9B085z5Lnxn3oJeKpmkXCEOiDtLNTKik2gcatJ7AfiHZEIcoQU0HKDbuuvTREx6OZ9KyE4E7qefog9927o/MJGQ6vIuLoIYnLCaiqRGY6Njr7UoJdjLQOK4fwKdm/VdO3wMMStgsfjz3MtUwDsOv+OymHo9JYO1vYZwADsG7cM597pMx7KAmn6cVg4uU5UQq5kHYOkxehPtEj0HYs5yn8MY3BAuBlsnKDYGNwT2M56AT9AT6JPDpyefaLl6jsOOeqvy9RyGHeZJCsLxN/tKbaR+BY0OjV8bgYPfGbNX5QByOhlatPvpTciJAAUp0oJz+KD0NBBECk786yBqoFOf0zEwsJcAWUIOgeIk98gqeQWYqdcMdBLwnQN85wPWIlWg+YIgoFBAgXV9XP3PJaCzdF3bLA3KvsIPghRpjRECFYogMJIobYcshid0svcR8H2xNJC3btE6IIKOPb3mcu2py1PgAsKf65IZCmGIOdRNwY3h83XyccP4WK26I/Hx3VmGLhz/kTcGMUQXYgyqFDesfaO6R9iis+XQd1ftKdYQ3hU/xLrWBb5xhMypxnIuvqKQpccANmVy4cHIxJOOoE5U1j8F2w7Q8Vkvs0St+tFBdcHcI+4v6nVlzOxYxgqqZp2BZ5BqYXEldiEzNwGeIaHUyPogsh+GEve02AtvrzBbbU0o87b/9wbS67XuhGYNdJNq8daFysxYEvcJurKk5gn0BaRQiVkizSiYil5FX0VM9kSx7bTYUGzsYOgAPMUVdiFVEICuiMb20RHAgQePv8niIuDpQJV2mDGsJYQH4ceZ41pbO1iXG2pRjnYw3cFCIxEgYpcfytdh5L9LIyL2Xq+w8typZudp/hxLNvCMZGf2eaUFIJEYAXj8T+0TUPolcEvUKkJVUC/TIYPuu8aOtTk3N1+0TZxQK5U/SIkF0PTCtBZxRWRdZxYMwWiWSU8222w0UAsI9WthphcTupMa+FxckdbiUOOwPt0poVYs2lZl83h1ZiT7AjvvtGeWiLvqSW2qPxIIUCbQxxEvStyPW3a+a0zRMPVKbu4VelAvoSpqVZCZEeYKdXwFjh7Lw12pu0gVYJHAJ2rWZLJ7LTONm+28zUCu2QedwvWcMi2RJIYe8lQVb3G4ohGhc+NXo7zROaLaq4KtKs2AZ2LYEukQvwd3CuzTyLcZZDunGa9ldk9yDz0iOO4nUIhnSbdzLFbgMRkOvPmbaYOEAQrBcR9x5hBXomtwSlSPtysmyCetWiUHUPW4UyLDxa3S7aOTE+yWAA/opjCOghzoJ5C8PIs3/CjwXE9iU1fOFde+74T0CTuKvH5sKPb0IhH6B5CUOCGc6ebuVXoWuQJKHxzIdqeu18utOD2XXsvYDOkT1xd5vVqfHuZJIfQdigfwdMNl9jx9Hx1jZBwTPcOwUQgXA+7AGeTi6HdjMH2czrVrmy3zNz/qV6R1cvru3Omb2ZJvCf7QishT09NT5KM/ffphYZ4SnZkRzNbC5KK4GlcyENJQggmYcKOBdvQI4YggwwMu2Ni2BCqG2tbCi3AOGetbHPufQhsQFlZNCLDt2vM7kARqtkb3DYrY65RwdhFYla9WIBYh0spBICQpXgzIcC8Lweo1P5iZDjr2I0TdNxbB9aB5N9BzvfcsaiT93vNn/K8X6cKFu8+f8148n1pzjl5rz7mZLDtqcPTFrANe/OItmKIs1te7HTu+ncSVZ8Xy5pGkiwYXLUvWuwvK3VG5GOlpy/r1URY48bKyCYgwI/NH5CK1I56TmWSgbHZ0ivyb8YeMe8iYquJmMh95CYO5lMK7CKG8AYMVxcf6h8dBqnxIxsnpJsiU0VFWukF/O8gBHN+4JBNKBPULacHd46ewCwI8CCI+05wemEM6FAIFqzLiQBQoyQBcRs2d2cTfMYzvoO6iCs0y7o7x8setw9aK2wX/LiuO64XS3HcHRQbquuk2Lv427baneY/rNLV49/+ju6fBH40GFRQol0cEhY7IZK6+gSArUyI/F1+MPk17RcRkxZ0mvjqN2U95/ISyR4SgrD53RkvsRT+7Su5B91551kvOQPWFuwcVLgKewmphG7kppZKz1SnBMAERCEt0N4qspPTSKLeoP4U39RZuyTCNK4p6zvH+ko/egnRZyLjtbSQKKHlmFbgqPDEFs5UU49rjfrQtZK/Xohb7NfbRBlX0PajX3IQm4hepwQmTIl2x7lX4Y7qNocj9GNjda5ChcB55P30iVRqpFUQQGR1oJDU8yV30P8RZisyIywu4rzO0EJ2FHDXWJevFKLDYrrxERNerZh9uk+Hus/Uq7tgLccxKvQc4Uc8hNnOZhNVulmdxBYcUra93t67wck/wXxGNnax2Tzqb80wwd3v1JDZ6hT8QyFD/7t967/wLg82l26/LkvSDtjbWFoac6bv37ujE+uZ1UfE1xTnSY0FTe/mLOYERi1oBoloWEIBCRUQmQidljgCnGyJKCCUY2swboVsxvsiUEFePvNs649D+mzruea6UEGPE9Zn/UlihEQRKNux4iMwH6h3by2GoDO7aDqsRGdseA1GDOAN+7pQX7NCdmHt9W2VHJ/c30WClJD8hvmNdmx//Kz0+KuePT4uxU7lrrmXmcg7B689b2v//3AjLNf3jXnadYR8E+szDTqCfrmrKKTdpbErrvj1r3lzf9lX3rxty5/BT3kUcU/PA4NXXZZ8DM/Vt8TVfj6GuoTN85bIQnrqZnl+kNC5gS6m9O85fWimac+owCwET2xS/Fkw5vF3wa71RJH/H1f0fLuo5edlbm/5ljAk3DmpDCCK2hSwEg4C+PWDXjNwoaenl7SLk7TjrnHa6Lic35w2rX75NSWjSaK1yBIzn7zW5Obn1cFgCc2FF/KvVC94eWBhGRz/Rj6s/OH7v+dObh/3+XF8RxHlwYG2qzwJ+fZrm8L/PX717dOfMihQGP2XnxFfYUB/15DoNTuF/V5ONwgr6r361iC07ch0SyB8mbjXhbruDYNzZxjXZeoeBMD506N/nr//cvHOmIYXBLKh5YoQhDv3xulaRGv1mfo0bkKn8Yk6/rIp6womrOzhx98WMkuvfHRELGPWJyPStzZrv0uG7Wx0EBxuCAazHLVkqU77V6fjDI3dhbjuymFCPuAuLomZ5UE6bOJICPYWkYuGTtULqFhsdtdNDHXvDLg8hyYMU6OQgL9NdhlUIqW+DOOFIj+ZyfEcLBO6yfA9qI81b60stc+TK91nt2xe932o/pKU10T0O9LRl2mWnwgQAVja+LPevkJIFE+7HAiu1ihBSB29WXTR7MKd9JQuFJqpBfzup2gmC4B3mc5Ph8k/fuRJXv2kAHF2FC7nVqmrgeX3Sndg/EG9P6B95MdJPuO7vwCN7Bw4NB2BIKCMwf5meMNc1lnDIu4elJRtXEfZiaV9eIf6n6nN1qZFe9gco37WQvXHqv1x7kdTRaJmVo3zE1sEl2c0x/SJFP3KjqUmX92ZjvYIV1g9yLL3PWqce4GniiBQ2hQgWC9p36f1aee4ILSOgiUAfIkFFxFJn5sLyEiMxmouEiJA+MFlYKZq/eDt3kXaLt0JboN2+uJS1u3X4AG9twOmtrVCcdvtshbfAe3yc9vgCD/ilxk/kpyHTSQQybVIj7Ej6vjaW0UL73pkk1EzSyAQSfQEOXAV5om4v5o5VZVk9PSt6VwHf85oDYBjoQkHcLCuhB5KvUAKoaVQrNzjC2mS8NG+fyb5Fi/YZlxsN9X8iB3Cl0KfZIaNy46HizkmStKo/iEighCOo83POfw2htJOWz3CUEmPXx9T6uutDxkM7d7a0mJhQl9asqN9QhQLyc1q1J3pOXTi/igzcz8OuFCnFjSKZdp2WsOtIpUHAKfd0xUf4Q13HKTclxSnvdPkuWVR82Ve7atV6Pnpq7VIRIJlwaJO+I/hrkIFoaXcFzo35eDiCsrmhBKVhE7m9MpG9UEZLqMA6go1hApmrdLOJsIn8eTM9MBCFCgxSyAIxP4HOpweEKEdnR5RQ/+gS6HS5U17v0cBMXQKKVwO5ybuoiWRYB6eHClpuYG8TaRPx85qiQ43p8g5rzmcGbspLrokfIHWAivamxWeZ81B/o0xkRgoTZuVZ5MZn7lWKbv+gTBRGMriabFHq8tRsQCaB9Q/L5F0Quhn9T7TKjGVEQ6evS6W/ccoIK+Uqz2gjweIKbuAJhHpheVeX6WqzqFVR3jWVt2mq1JCA8zICQ84Hhj62ztMJDgmmvIDv4N4tK/sMpAZdK1vMVplFrfZbubLsQA9bWngJpvFJBHyJk3LnTmN77tjRBx7njvQbGPSPcF5MOO3t67gqsvA3zDYIMIfkhYVyyH2sKHOv6dQLTom8+OtyFlbpiU3N9f0qu9t6TbpY49aUduIPX+wqzmG4MXKYPyXTjVnXKthntV8g2mK1WQSEmkPbj7O6thO6BsD30/swSJ0pne06E6jtqHGkP9D5759ss7LtsnGUPuGtzhRBP2xW/4B+9xH5kKAve6ADfkZ9jYaKiqAoCwP/fRgoNCsrFDLYu0TlIs6DOhrJOcFeqR5QVFER//9DskgiARvbuMUEkiQmSsBXP5CUCCSOLpE31uPxqQ2VqfBAtpP9yvNdJxQIwU7iKLaT2ofdyI8hn6LEREVJVRAm70bpp0iFG92+Rgk7uQLgsgWn/n/6///VjCmG8kz7OjmMqSb8FCNHJJSWUk//Pz2tXBCKFNRrLA+7uGPNa9hHXNLA/8xnAzKYq1nClVN3+fGEMXE8yIkW4AoDJsJSD4VAReAQaBhrqe5pDPMAmhMklTrSHBvXiyGT4mIuLJNhSBpFupgrXt/oCKixsRDg57ozYaYOGkYjMNmEKdYUPtOEmLXmwiHyrKq0SiDCBnB8TYiHXtsHlT+syE5aaBokLhPMMnOCRUpB+dB5JXOWOR8b4GvpDuiuLJ2X0ISHSjzE9fVaM1nv8wuGfSoSIfUyKLUKSlpUHYvRkaISldBpnilckRMEVQhmcCi8CgpJVEpR5jGYRdVQUhWUql4moMw7Vsib+tqamDOWl2ZRozz3PqZCbsTaXLYHiGghNFGL3bRJcMr6lACI+q37RcC+hFuQSyEq0teujbKgEPLzmnWbs5VNukVfrZfYLAnwDwhU2S6xhSDg6Ef4hrATGH1H9J2sXTdA4VHWkB0pA23FJ/tA9SPcBC0Oj4+lj9M9i1PCWKUk07LhHkebaIVl7V7jFctDK2qWV4QJgEMbebzdC4WgBTU6ZxcUW3qsuMBS5N7Zjc7gu/FHbXg4zhYXHtaWPn7CmcWni+YQtsh50UcBBbkVP8t6iNuqHxZmW497y8gJXZHjQELGT0IFcMzA/0AaCYxSGED+ML+26nBbbTm6OmZE7ipLf71o5P35xzL2v27RdHq025NI9sfzMyOLXqeX7YojviMcJc4STSOILhwex4UYwSfOEo7i322tF05G5qvDI42OV7FCKlxGwlYUpI7moK5wdUJKv0UZLkyUNEo8ayIwGV8xVb5Y3Er0t3TMOWmjFDj7LSbMEh7oBgcXNxd7WvcBYVZHNUPhOTnhULOknNmIzFbvzgCrmKnndVb2XnQf4lfiH+rO9tkSDR+Ax2v+nLjQXcbWZZuxaA4DFjWp62+Qtz9UT/cbAOCxEryoCihMClxYmu+dNlHw+e8C5JxfujAw37/wZVULBG1thbmjNw10mwy816MBOCSCG4Gkz2kyON8QZos+FedkmCyUeHBXqKA5NLF5o6dDs+UP/G0d/Et35ybPxuaEUOfm0I1H5ShgcBw+FB4CPP0/n5fa3fFcQIKoIeIFWaF+IAoqKoGigi3soVu8YdAIDVFvgTFeIxg+TQNDkiEAt3oDb3AWkVB0UVE0tImhRUdaUIeglWCYd6cGt5KFpDAZl4z5jUnGE9I/RSKkUoEgiIhIAD4CNY+XS6nIerWER0W5XXp6A7SpOmU4uU4bsB8RVaYZpu4jG5tF1iV1O7qS9HCLg0radp4BnZBsAEpqN+xL1MNmiGxvf6xwCOkL6U90PHvN/hnyd2nYWJEVgM1j2n3Uap/2Pp9N6vJ2775N3uXl3pv6vNvL1Zt8UNF6oY2u+fmuje2uTfl5ja7tUSzPtSnWmJ8fnsyHj0TH/oq65LLQS/ckkxTj0JdLxD4gjPhU0lnBv/0xyIV572wf2jyMIeD/AOcjRoMjSuIgSWk4aKgkDRKVd/cJQDANJ/fROcNd1fHMlWY62gs6czpXWLF9wpaVDYaAyzA6iHz1EnEQMVDSwQgJPACAD2jgdfOuQhKNVLj7ddMx9jGQsyIy5Vo7dHub5rght//+9zTiDOLXv8jtGPqv/84gMhJ4txza7u5HPD2NXCPkp1klWFm3nLKRyaNNUv+yAKFaEmO4d0AKWB4j3egQAlCmHJNxkgn8fsbs0Qdw+5a5K4nt6Md+CYKicvmBjmy3F5YMU+xPgTgEVsB+FgimCqGKjrH1g/NJeXdONEFbvPMkFsGJm/6844zgTDwyWDBUs2VPGaIxbdkLL8c8UoD/BzPS1GjuDd+rWT6/mPTxt3rotXzEfL3n8R8Yl/d950XSzx7/9HFIsw/GVB/VaGDxEmmOl9fcQrZogP0Lvu8TQfJO+AqvE5+EAI88SNjdzXg1vrnbntG/kmkcGiku5Vr3WwJxFdWu4Wdh/dShkSZt2zURHuKJ+Ck8XiItZ5VXlDPL/fzVLHVzDisHuHy48T91CkbCU9T/uWCYy+RVVBYmVky5/I9/S5hu5jRxpglTBHBHNCDoZPCZncyBxMlndL62HaHq0yBOffJxoNkVJLpfUi+/e//t1ZZDl9PONWlT8Bu5c6fL7w+pdmBQv2TrZL9QkQ2N+i0LuLQdtMq1irP6bBWvqdbm4N4TckbcnibLHV1Z+MW1fxEcF+cULyD64Qhb21SEiJf3CUKRbYotNgp+YwzF7DCQFsfoxrnFEODCq7h+2j7cVewQ7RTadQzbT+vHjuEyOqZrAttF7cJOqPqo/fMZybiRamtz96bQltTq4HDBFgQIZFwk91fDr/qSlziB3vilOLFG33LZfCA9MOEMVCuKG7tkr4fLcsTuRpq8qLSF0bFDxxGmxS4lr9jlUowwbSWIwdjCLypNgGtUafdwLBatC6H/YOb0NHpzmDx00Ib9WIRVDX4c/wc3rp0awXZi9krQlnJpZzGdlA7FWUUHpRNzvIUxVQtt5E4MXAaMP4qGr7OZh3/9HkacRqAeOXzNjVKIjOWVS8upsVTwzQO/3JivSq9FCSEBkEYtPfvK7GXpoQyp4i70W3bnJ/4JPrtmlEqt0C0HaRvPZpRp7puU/+Jo3OEfZjrjR+m1b+tqnHuP2iR5iMvHs5nqmnti6ySbo71Oa9d/W3rnJ2OpP+ZXPsaVjfKbqmn7/lkAwHhVdu1bm8ppRzzeea0DcBE+p5G/fg5H3R2hohiGn8MfOFxHrrPAURD7zIEJ2AJnLlbABCg08dX7mVcGaCQE1EUAN4Q7FBlNoUTGHLp+MLAmHM4phORI+7jOeG7hZkhpCYaFVKs35MG4oTdK3o6ddsdXSfKAH5y6bFkqnAbXNoJTgcg3J2Uxf9VxqBo3xBjCwY2HDzfACGS3Jpg13sClJ1vkeTbiDiR21dfvFz0agsKUyjAoFIIChQklOzQHpwDX933dZB6ZQD57q0yasJ3Kq+Usrw3by5eGcVPpYsYjC0UqN1zCb66Ttd6nmniZUFfszZe21xc1uScHRXbr0HZpflCye9NWB78jAPurZjKHuI6YM1mzC7H5NHXu2F/C4MpY34sDHvlPszen2ASawJDRXU14fFybXdFm4NBt3sidhZvgWe6Qykzk3JX97KAIGPnpRucuCxGmLr7OBMvqHWNWFB7ZQdKdQDWhxxWN9KN+MY1BX9gvsa2uJ5u5/pn2P8R6AvqFqM7FghAZlaJowlDRt6MOGwqmCbhNcuCu1ewLxvesVajkLu4XGb6X/43RtQ5UpA6dtW8GribFnty8t3BVJc3VP5LncOFCXe9h03PXKWjXPJPJ11ERrO/uMrbNwIBK01ZbWyfv3UC9twH8x00SBpceLjnMHQuWe/Ls1mhqd2Wuq3PYuRHiD4OmPOGgcaKjt6yCPbn5b+gJuf6b99GcrnIvXczYeeTIul6nHVSuax5v8lV0BAu4iMqzqAEeQbv9U7Dt9GMP8Z3oQL+layzLrUTgQDb1jBYbUxCjZqoz3DLoADX7r0DQaNf6OGQVpXdBe86yVSEyjAMrZUeER1AcFSw4SJojpBDn9AGpk8BU0QggKkkU/XnLkzC34fkAIOqQABuQdIjggpoEiCkKn1ITFGVTegHgWnYnXv3U1hSHbdbe037/R54hQDod1QoPwsfbk370HtGv78OjPoAmIYD1wwzovWfd4aePzqRT88dPyw9+zHzbzN0si/o4Un0NAfdsAZf/HSVTxn/3rj124AXyHH9ePmAD8H2ZXy+7vuv+HTyPcHvVdV3/78D59YOrF0+QpYSSDwZ1pMEg7RNfSQVX4uJSDHVR2OLFNK5EQqdHo4kJCxPY3xT9VAWHYhTMKMN8G3qvs61zry09aYEfQqEbHGr2mOINYVxs6BcEg7k7XHThcIoCE6D6qQjAnN+Y+di56vKyywE37ixwaZ2O1wJt/MppFx/gMBPEZx5CVPb0VMKEYkBUHmLyJ1yDAVEuH1GtDfTOkrpWNbIEJwLX4OAw4ydL9y99cnPRs+5yPOpzbO0BdjD7MKRsaVbCDRozpDzECWIfqI39jMKXA/sjog8mEj+4Dc52kw80niN0EwONgw/UcagNGqd+AOPQSnW1apcRrca2xA/b5BrWBtdqqRkelOpVgz47+Cd5I3zpMmIjlUuXUozoorBf3ti8HLF3D7y8KXv3EgPesxexHC579tAzEYTmizfKURRZ+Rxsjg4FOF2/QMltX5EHi4DKpbjdMATKyoJC0fsM/C2OQtuioC9Hq1sR55W6wvWrBCQmAonJZnU2lsCJlI0YdJ1faax06k6FGqrwC/EbbjhHgtZ78oNmvQDwE6NEkqioGEo/JYa/8ej0ev0UKRjrOHJkARTz6AMt9wApWAtt6t/du5eRP7lf3PDIX4SfhJvBe5Ez04i9VGZmUozoonCfBs6j+Q9MH7ybVTM6zDsY6hP+FtRvOuHjc5vX88LXZ8IU/DHmdjAfk592bezw8poosD54907N7OA6sPoSA+Gjgjo2dj0lP2F0gPX6jGGzh3fvTpkPmU/dvTd1mf42EyX3uw/NhsHTDzpH0SmVq7AUTMsMREiBQ1OWJmSTCfuTK5nHyX46pc9SKg9vhCR+g119HfU6DQskpP7X/V1ghPRKS63b33EjbKa50yBcKl1sPm9byqF1KijFYIJzdzXUQ4W7uQruLyYkIsH6t4TCIMKknvqfcQV/f66K+u/tV7AY+cJr29X8rtXUQ0Q6xeu6XSvk5xFCIf117a47QSDVabGISkv1DQJc9aVR/zIE/SW4ueDf3H8X3ASuY+ZIJ6mNFn3NMmNJpTb4zdlKlXIpU+lvBPe8l+nKdJUU+xTjr3qoisUc5FjV5g0b1fhMKrOf+TTCEZRgT1gYldYsV+J2CfqQTxH9gofADaYakXiksxxJ959uIX47yEKteDOXoHBZpLnOsFi3GkdBopOB95t+TnR338uX26k0YyFljiwlv6eaGDtOb4N2jVB4lPeUuBc6SULZ9upVP15ajkDhUYG73+MuToxzLzQy3aiWQhGWxCaXPr3tz5PCxFwlZY7mRPAzoYsjGeh8+WIblQfGNcYlz5enmelHiXeH7g4TReubyZ4vLwFCee0P2vzRadflzz5H9ZgxHz6Hu5HO3dGo5c9W7T85KDA7CYKsl5g4oeFoP7/SJQEBJSUVaglBwhMSATAOkV6VRNB0EFkXngFeERD6vYjbdxA9iN7bt1+eBYUA97zp06rc64IYuRIp9jov7F79yxflky8h+bJqSC9fOAfQv3SQENe7Vu5ZZZPe01Wy+uvDvQNmBOCvAtgOFRD7I5GRI1B1Ujqx72MTIQnRIoAVxE6sBTFYsx8opbQ6DsJ2loGy55nJdOeG0r8LxrOfu8Zh4PCc3AiIUL1C4S5OsZdTDEvaQXZgPnDR+vWLYC4VxzpXadJEf2wSL3A53MyzE46mDG2+vWp9/z4mhkyJYfCkgfCxqOpojf/MV3GfbUobLUCKwgUMIoG0CPGWaHQmhTBPrCLOE74Q5gfKRqDYdJG5ytAjzgPzxudVSVuw+2j92EL73i2Y41vWjkkV/qUEXIrvH96z57D+7l7HhLaV7MM7HW2R4yIlawJp67jzMHtlm2NC7279Yh/rNqk3Bzlv8Lc8E2BZ4UaYJTgmslvzgg/I3OActDnYb+Op3o66ndpGv+Cgc9sQYPhbXo66jEN1o8SfYfnSKIgckhNCDjLchtFfILzJHtdL4CylXenZqGc8DpbmXfJ3ubAqVZRsQJQTbV9PBfhMhBhYDFsL0657LBBNc5N5WvAuJmjBpd/2ZLs/9m8cph6hBM5CVq1dILUOKf78T2F1P7uDmhWryCWzjs7FUl9Sg89bfKN5kXGncd67fcntmK7D+ueWEAF9rc6YzZDtkM3YWRYfezJAa/oBxMsxRCeQBba8d7nnsMgH0XT96FZ4G1wABkTz9cFtiJF2jogZdBDxYgzZSfDS7LgRjH/duVLmb3H1qoU/J2uFiDjr8hoPkLbJRuXkZFL5ZPJkOSmZXF5xFcSCZCiWlkxTJi8rG5hd9Z6StZ5NS6aDTosDyYENsjlnLeFIiSv1HwXienBn8ClZp7I9Nfjkn4vOWoC4Pbgt7+1BjbMaXOJoAGCpIjGNKhZDUASj0kRihxGwT3xK3C/e91eonWnkr87GGUV0lE1qkWjyGMx0Y9dP08itC8jSP7saJ7Ex8kWjolXXGzt/FfbDDxrZUNVjV73abml31YYRCKt86tSyxan5WVyaFZrebbd0EYCL++LUTGv5HIAHlw3mYmiv5h4pn3hdIwY2O6GvkTun86c7jV/NRQUkYmJz6wm3+ldrH7Ae4FcTJpgPCDrEWb1ZXVhLgLau3uoLriWnCLqEd3rv1hLL9h0IvZgDhHHWeL5/h9Z40f0hCLPJgZP39Buc/CejCsjbHWf2HIR6dFC1Y8xxPBU/zhzDOeRJhgT1SJwd2MAnaSZcXw8nznQEtxBvtj8aLloERZ8tQ1DiRdGwTksT9tQwsRW7mbYXS6jZubcCQ4VwfatPnbV822bCNYEZSDqhrWTwOuqCZIKH4k1VUHCOg1k9OCbc/veCMWl7437QqqTsO5eBYAwksOJuD1HQcjMTGEmlwzIrfZwAdwcJM2JwKDl6pMOP8dyV6jrAQDhCnOxSfzkEwStpuOPiYG8UHWEyCHOhKeElylOnKRgJHWYxEb6uFXwTKv0gk9AcZ2jvdIhn8gxGwKglQHgsUsbjCkzkdrcaFBoZjgkJwDNMQQDDE42A9PzbCXQAg7MoORd2jDGh07egHCC0ifLQWZhO/Rez7xddQAmhCATzTDgumU1xhBwollgqcHx0vLEmGayoqVmxUIGaisPwNVWVNbjw4sb4qKFhIsShkYZPkSF9iLxtQp+ZoQ8P3Z1nBsLDp5vrsj1W15JVy/38nB4YiP6w8/DhHOYsU92wIo3w9uw8TjsltXpHypN3lTPLGxrS8FOst4Q0D1bOfMdUHjpUOpB+fDCHCVz9WdMHhK8tX1q+eC1cbkYskWhLyudepVwC1hlFwp92MF8Ti1gzOroGGNEbEWuEGtdXw0Vj8+G2NjgfHKWImJfmowMDFYpIoRkZJNUm+WZS4MTyikSISkVF6i9hiToBCTXBRwYRjcAEQFWrj00IDqDlTAg4pCNogDIF+KEpULvtCKD797hd2qoU41QvLBZrrPW7Iyy+qfzwyqma3li3Q3G02HVulw8of6i+BRZpflhrUyx+1IoxyoliF7fa4Mr/5C4ldz59+pEZG3WsImnXzRHrkZuhBSHqL3LnrLso1M/MWL9j15sOTeL/uRl8aAd1zH9MGpb7ELfJvJkyN3HB/PPs8cVHoEfcHdvHtnPNZq7mP+M/SzhV7vax1yE84kI7arJg6vaimcJHX/n87ROHD/QbWX8xopMwGVOlUZQoT2dgSDAZitJ/+OD4dv5X/qPCopntwGSH/pIXi97PO/yeiZOD/XbW87wLmKcf01DMJxW6g3SbeaP+E4MTPfx500dLi19oucBR8LHvxJK2AbLj5JppHnmgre5E31UQ2d6ufTPgkimfHCIlEIcnN+JIQ5PjUu9UxMLxOSklgSKljlOZJuB2Zok5x3eO+zA5TMRtpEqXt046GgnJemShkeOkbi8ZdWrkUC8gpzyX0D8w0E/Az3JX2f5uFfC7r7tBYqg9ApKIUIDT5DN7qevWvO7O+M5uJxGIiwMiII6LFUF53WFuWTjfgGzBhPuS7uJCoLhYIAaiuDgRyDsc4eps4R3CFwaIhddM9m227afEQ2DeVwM0KYED5vUavLCbOaAjviIgmi/Him/axdf+wGBEgvYBYT3hgRYXwDHEYr0xFLQPWiECGIw3moLxwai4GIwJ38QEaytXoM2MN0arwoTAYtEq0CYYsi/rt1YDHOiWdDOp5fxy0xx+DjV8ROmJ8jCsVEwteGj3cMFUk3ejAj2DVmS/zQ1k9XW15bc7CnQIthNrpjVhW4de3PA0jO35j6V1aq7Fu+GViTYSIA4ysesYZMBQJVByoblfm1SY2+QrBTBd6xbpawZ2QmKzbkm21GzdXLRl7r6rrALb9JA8y28Jlx7CUsRerhmFR3noCaAvMdZi/wWJG3rmLh1pXUvNLJTAi5d6hlKjWbnL61LICsPgH/swFMU+BeU4whU9azeLJt/ZZrjmxYEMoYKCEWIorIemKrIqoYAnic26NN3KznbIoRln1VmhC+k1dbbo0vQaP3iOnCdRPEnQ/AVI3FzDlVgTAUcxG2xWwZs2wYmLOwIkZkHFkIpe/GwhvGwplAotXLZsYc6+dBm8EE5duhT8aAwNtxi3yLLO/KukABSClqtVpmWW5XhoeIGSvckrKWHOvsyu1KF57NMAAuCCzTxnn5RQvjxY/wnU+8/El75587rToLNxxeuy+DOqkgd3QWWO6NLF3Qnwr2/fWna6vRJHtItJl55Vw3FUSGFx3h5yEr95vUhcmyw3/ERVbE/h/GkuoAoStyn169HJxoV5EliDn98elKrjbdMQMaeYWWoTTCpdAQWhBm99/fLsRw8/gblJapSEjRhqDDctKsuGhE5LsnUstxzzpBa/v85Zu6Z6cVrgimUh4vBSlG/QoTrS+YaNOKEFhZuVxaxggQQSYgdY3du1yqLtIIVPXjP7CAfbLFd1Ib49sn+UZGCPMJibpSByfnEuA1o5v3IQlNk5o0/0CWs+ZdUjHo/ThEJEVBOH94i3isK3PoG2rG8Rot8SmiMkfPoIQ0AC+4sFyw2AWEyDomdgsFyQLU4je7BDw9reNILmQfZLW+6iAo7AAfB5Wojz7Dni+SlrUAjN5weW+PzA+ZH5fkJfOoIhkyKTIcD6Ie2hxgI8iKUGodYTHWX1+LJfy3ng060MzgFXRdwF1bXnVk0e5QnF9fH6vHPXaUYxgWl3thGIjmLjQnqYY1eW4PoSVFP1Aj2RU8jQYF67+mh6BEe6oKwgwVIyiYf98JLQRLMlV0yX+m55ikERDGRIYWawp0jxakXFOGODReXx+guW5aFH/bj3vDOZyDPfWOOu64e7qouoRoew3K0FnwtoJxZ92r8euHx0fTgqlxucNJBHjUDrP617MXkixTNK/s/gdIdPp0+LTzMSTDcTxJtRwNQBF+H0Yhl0ruWPENqwc6bB/qMIG5GOdojgfi6rvOQkSz7vX9+Ry0hdAsJDv02Q/FxGgiz9DWsMO6R8rQtJNIENBeFpS3IZNG9OMdpd22S/ewbaiDzz8ydo3Ix+uP/s7pMCr4MXRYMgA1K4NPwobtzVlHAUDkf26dj7JFxraL7ZM2hZhwpEBDluJv5NdKgC8VCaV1/NZPS1fUjifbAZtBP3Azg+4pcEgHNR56GqL0mZoqFFRXAUoazRrkDiB0HJbJqfH21YqNq4uaehRmKHrGvDJXiJix+8JyIrH2kVn3rlUbquxGrD3/c3iB1Cja6uCxqNYupmjOqtu1o2mn01IgxYpD2V6YnAxk88yjAXrjo8lvEyyCLtSs+e4MfSjRrdlQ2tL0WQR4/SCXQdRKSl1M+mAZ2FdKACRBSyFLXCz9oyEBHOStp47CxRvoKuCjCLCuApeCvEfuwF/rjvVf5+1nGUgEoznVmGapi6e4SgmcpB9Ou8QgTmrn24FY+Uka75Tz8PGgR8yj3aFt1mBQs7TyoxCE6jF4OjeVGv0HpCBgZgBNWb0Kh2Q9ZBdAjnnQCnC7Ow9s/WkpkDBtvSFV2F9IKpHQ1cLtEwaRNCzzECOWjQdOhUf0MifZHhHupxUMmsgo5Cm9BJ7MRKVsRSBPH1JQphzzl3kFmOMK91R7bjqHHLqN49plVFupPcWDxseHTGBooPwdkeOso3bD4LL4f6dE+B+g24hjo15qpv03ayzUYfk82IpxpeLjy6zAi7TbtDuw1rtHyw8LIhlRkft9WKbgadYF6BzMlsxOkAWBxhrukL4cNuiq3ErQp3lr4XuAKfhDwdXNPcnr1suGSdhfqwIFD9mh/fZBplMeM4APsguh2CTyAVlCVHmwjS+J30JowlERCnki6INE86QphLjliVih9jDOFW44a0cxGEUsYBh/okPfORkW94JW6YMe7dMDXUraiCU4WiVa85JyH+f9Tl/PP9pDmahFiz7Mh4/M7HYWasLJThOZLOR9/WsDhMDrZOGpeDwI3lDQhK09ejYHDjjLHAfHsyW09n4KdYD2hxpvr8U40dylftTixoUHX/jSJeu4EmpwFjPjocys2FIh6jylGJifoYilgtEQHgnbF6MFmOasfwasr8g6xETD/mMSQryI9MDHUA6TGm/0hfKKTMhsJuTZWo8qCbP/UkydMrJPUaUKaZBAVSE2eVv6+Pk9Q7aOkoWNSkB/YHUM2kO7B9GJTmswPTRRxBaYdwHbghPAo3hu/Aj5FO9YOzA60WoURqdIfoBb6Uydn6yvnVVpfMmlHT3RzXHJ8w2lzUKHE4ZXQOFpvzX69eNrFLSdRjW95d2cRudxRM/KM7S4BZ9jgaVhTMJdE4dO77OaGxMjtwWifr/kGPa9R0bCelj9JP6TIc+wLMrUeXE7x4v0nYS7eoty5dQneQO0jJYvnIOGwczuk0V0dGqwlqYpm1/2KaybghH+XZVpi49iAE4kGbg6vg3BwcfRlBNxT5MRxcuAFMY3+YR9tTQ0bikOQ0Q0LhUCTudFJ00QVYJJ0RWxl0JFZAp4Pu1t1F966mp0gTgQrml28HqsLh86r753MFqM4Djns14HhlhlR0YKKw/UnARmE6qr0xnZgroZBbMlrLIw2mGTOEaSF3ovaOInEQ3zy5eREz+gq6BbQ+cD18VnTQ7fA3rp491nPLarfVacL6YmjmYYf61fQrVjkGH3M7jLPG2Y6bTAtUN/z8wihBrBVlzq5LBpcWP1eK1iIwDa7xkVkAOSsSTMBdEywwuDvqcqv94ouXrndftJ+/A9rdV8aIiI7zGlOjmWuexoy96dr235Q58/eO6DkKjcJNulOKufQPH+hv2WQzQGg0QHPWF8wAeKeXUK7tyO/Qa96ZP8bLp93vzrVPsk+f92lLn98Kn5klV+Fnu8O7Z/FV5NmZaJXaQmvmV4AWwiwhmuCwUmanw6dnKQEKIRyLHm9aC3kWqF7gGBoWLQfsBMpkETWNhWVgF9mH24nbhy1hv+b3NnQzsRntjakgVmC8mN7TEiMpxZsiNZKc2cusYJ7nPczmzU+ZP1y34/+d9VZTaWmzX5BahhZCD8SKmd2Oa0JCMBikNJBcsefOi2yxIkgMxcZCE4te0uJ03Sk76ljwMGu6w7fTd4fqdsKXacls72VaMFPY2nEmuAe0/v3durp4lLLrHOPUa3Dm5eYVrX9R6AY0Q5oBfXslmW/A4/AI/KV1Wl3m6wMmHjbntHvYU0eGDCFOgG/wOYoFqEraBHMCsw83j2MCupKuYqrGgBXXwDpBU0KSjWOiXROoM25LbpgWM9nk0+Rd7lNu9FmfOmY7FPdu9q7w3j2tOAYO0qMrXXX0oMDaDTeyxwOA8kFonaIEqltcosXgaiGNSVYdzlUDLeEv4GECVA4AzAD82JuuuEAIcitdH4cIwGYiIGQAcokLIqvYVAhR+jLoU4G4IAACGxwxUfXbyyCoJM6yEVmCdRFAoixM4BdZXwAd4ywZCXCAfqJckMngl9uMOwXKBlBjychWVKRBF2Sy8Kv4AEn3MP9c/lnXw+bp2UN8laO/+bkR4aeKT+yN9mm52yD3WOUDpXNIranAWZS9/caRnbfV9NPZ2acjl7m2D8g4BOFvN96oH8ExJbf/IeKHkCbUYxs34g6X4vB/YVUS+LrZhlDJY4nfqjfnJ8W96+vQ/qhHbbZ45nhohZjKZ1NhupCNQ8Yi2UyUWqgMBj4bOqwL9ejYMv4JfvXbcQEMTEJpN06zuWT79+2rGrb/0K6q5/xm3P3WOIjbLs5ee3RTd2Vpv2u7yrbjDqZyUE5fc/D7tj77gLzaoGztD9ccBJbB0ApiiflEZNw4Au7MscRHa2hmHrV4JoMJnOqYqlO4atwprBbLdNVahnal46slbXJ5NcihHnemtEFNa5mQosHaMwF4Jk3tZMqa+ZYEPT4YK/xEwj5bozYq3julhSwuQMtzonM3cKcC9jawl1evKV2jXnjuxUWDRb5blQtHv7vi4o8W/noWeoqd2nphYRmeuFHgopslUOJzeTFncZluWy8fPltegCgpdkEMVF9MKbONEdzYYxCo891h50LZMWw7tCgKcyqShUX3MV9aYqFAhA22YzPZZarncYGk/m3IOrbYQsvQMpMQmw3jvwf1fo777qqvdPsefxARhAusIQn8/REIf6mDg1TKwDNwHJns0KhFOAb1o5oXw9/IVPREo/kj+nNhgNdYHhz+tncOe9bFrTJCG2C0F3Ml92AcA3z8Pq1XsP0k/witEna0KurpcMy9UGj8zP0T68+fXD92Bhq/Gzoco3Metxk6dILXE9YddpIHHdoc6n7I+6yg/ov64gzN8CzWtC9qPX1lmbaT7vw0dNHJsf2pzyN8Y0xVN/0NwmW8WtXQvSLFeZajwtdscYLFSA6bp79kmZk+hJYS9S0T4NkTKL/HZSyxr8NiZoHJfzgsLkzAZQJMXIqxo1Sx/N6Qoi2SF+5v8NgAUphe5D4f+rNwyunZAAJWAB+B9/Lav0rbblwkiV1blzI5p0Q3VNaE0eTV9JQkeLLh0P5a+ot8e56RMTfPfO/iNEB8RMajlmIig/wRDHJ0fYzrLu9EfFDT2CZ161WfeMfVFPMFnHrgRGLY2no+8uLamy4WEyKILWceY2JQwsm9//gVG2Ne5pe+G6XgZ9Z0TZhAtzKkdkSyDcBmakgcRKyA84kjiEVwSJrQUOwnbP4vtKha/Fltmi5ilLWTbkdxfHVmzZVrlEZDX2B6wnDwm+X4e3NDiVnuXWrZpfAw8hKq4bArBb2h5tuaycVK9/AgYdDPkAOd/S+17lY2tMKf0DmpaydKDuUszv7IGcnsi/Hq+jN67s+4cd5Yo7H+NNev2yaoKb0but9f7l+ZxTXui/vAo+jily2p+kSCmb+xQVScURAxu8JsYDKrM4bo+Kx3/Fww/2U0a78pHxaPxphpJbXIXuX1iHiX9DjUvnhRuV2CgaQtsE6rbmhusYmx0T2BTcde0Y2xtmlpaMnR5O3Oe3nlBxdK8Hyzv25ZppnGVVtBnjVcvHlTKQweR0eRKHHRO/FU9Y1ialwWdGkkI+2BZumTB5zUubdE0tu56bck4lvgKaxy43yimjzmuNbuBcSupylpVXV7EE48R2jjAEpeL5E4IffUVW+XSf0eQgPJ79Jmnc21bumv204xeWJQOAM2o/GyUAVed9Gy+zQ5R05D66lfpZDpP/dBOieddo8GRym/79OgVHXlASgz8GBP4EaUSGJuZCYRVcp4KCM0TzZjq+ChwpASUQVrj+IpZgxFQqQdiierFEnM7P7e6cTByu5f+igjwv89okrCb32kEYmasqCCQNJHhRH+6ZHxEhFKMfx1DtJum3ZEKhBOi8uArwh4gHmoDJqnelD/97LriR7Q82BTVGWa+ZyH3hybc9yLNM+eJ3kRM+v4/qcRI8Kbg9laNnFeb554AmEROfex6i34RYJ3cKuWcuvPwCLeP0njD8E8/ADxR9BKatl5IR4fCb8zeRxKnCc+IMwP/BiYN/w3HmafD+sLSAqZqioQThT6eB/xRtIdAqU+lIuv04IadNUMyhNHnavyqKjPWadOvVYE1euvI0FfjZLL5f8oOxwUjv6nEBPrX6f5S1ejxMXGEK4B/UBQwyULbUbmcWFYU2UT5snFA0AvjCQh4RDgquDZE2Z1ZwmIE/Ymbcymq52T89/N+XQ/R58hOvFhom/Lo1SfyUXPFkTogDFoLxjnG2grYspjmmKardomi/pDhEc3fyEjOlsLteWxVLPWMt728+oWfZjO/xDdpmGlZXZZeKbfom9XQ1UttjwmZCQXIjTc9MvCQPVSo1dLA9TVt90JFX5B5UiKKEcStP+hzGbuzRZvebmDAT93rqsEC/uOInUosqAgxVBHIBxdmqQ9YWFs/Yj0tEX02SyEIyavJxf8IBr/dkjapcwAPulgBnJMemtM/LHAQRwNWuOtwzVI7Zqz06/nDq7RIjVrD63xUeBGdwiUkXG7o9eMzBCA6xkxI3ZDou1bvv6CgVmd2c+PMRk2jvpSlNTYJfV4fiEYe+ypYFuKvXzQx5BXTnioQwZu76vRTbJJElwBiRXoANSmngoUzGw6lHsgeeAoAU4+9OiT6Y4jHizYfJz3XFW1rBBQnXqiOs9KDjUTnERlDEhaXkkGKghSAEtG+KvNow6ZFeyBoKge52SFfEeIRxQsPuLh8U620TpJBsl26idiZU02FemQrJ1TqViCqSA1oWE0lCcqS57L2SSD0itQtgWDpORJ6EKhLMojZIccknt4Hdl7vNCpNxpAewqdBxZLifyO3RBUmd9+UMppGuFDVCdC0+gVM5tdvUIIOFtLnjFZHjuOMGEbsvjCHlRSzy4mjEqUHwlE5Y426sDER7ImlDmmiViOhkaYiyOfy0NBrE2yG8dCHw0usGG+cdHUrHfQxhRmHtnl0WI2cBzvDMFmh2KazjgRm4lCiCokSlpe+g3kESQQkAzkE1b5HS7wkAAoqtfZWxXzGVvFM/GXbBcnWA7LNtokyqtkjTqkR9CkHapZ9hVGJvXsRNFEAZvOznvIO5tReFhqQYGbn0f+Yb8QFcfzGppKRY4RSh5HQiB+xMe7+KONHNN/9FTXo/55w84UHooLEyx/ho5SqXDnwSnJKb9+ST+3itlp1WHZYdXpAyLJz5ZN/qEBQnoMO54I2E0TWt36LlNDFVIdkzQ7YYCib8/qU/7WQvF66KfuQOgR87eGJqQ5QkIIeNj5eXjskA8ntWncclJ7q4qSEza7Rjeq0YEfJMQKonBNQymxq1pCaO6WaEtTCNJLzvrCixVprYTwxSsXZwSVy3d4WEf5J0uTc2pIHudJwgHHlvzJWWF3O9E0BJZOYGphWWGpkFhBEkLvGLfVbBYV5uXec82+5Dq+Hz440jx7jg3YlNMa4AA5Qg7AzEDvTnOgOltP7u+5kPdW0b3g1I3jkoE8oh/KGrybrvAhx3x7oTfncAxLTpzXnbtPMWvfbleox9vp8aQJkBlmQw40TGmynzQgQ2SDSU/Gw6M9CgHBPHE5U3dKN44TRx2HENA4lWTyEOhy/P6zMJN0ZnX+86bfR8Bh2GN1mFYE8frhWUG6PEp0eIaEwLQerQvHesCwP2aHGl07SxjpvWUrk922XTdJmEXX7lT7Y5zqoy111coFSvVJliVa+ThJecIA4xyLk1J1GhoZG29bvzfsYpMBiVxKIgEyqZQMChZcvm9oj49E77qSF8DrVmORFO413gpPnBl5/LrDqmS/wwkobLi5rLo38lXHpW1GpFpR2P3Pq589FfrefgqBZPgzGEpPgwIvCibc9zYgOcppLRju+jKzW9QH/lP+E/7jVoq3dm9PvXVTDnf872JT/g/Yiilke2w+J6C4QCCPCa2M4UTwZWHGUUamoVdrc39qdd1VR/4go9wkUbgAJ7JcECyjZxUNeL1eejhHYsiJHpTosqqsb3gD8uHwpdZU2YJQGTPcih1hGhlmNNrPnw0k9/C6uGzd2DjbuFhdrSX2nsCGMOWqjyxWaauM1RH4bY9G9FtZU6ySsIsOhYtS0DjDx7eObGV3nRhQfxqwv/ompw5ynPCt8q8f8FmX+pjRz5jqZM+z+3X7TWZNBqjUZSZvAU+Xx57To9a9eHGZLCSvXmKeeirASF4NwoC/pOTc8xl6LkotWipTonLpM18PS0sCwqOqA4xOpZrDfhIuVSKRoXOKzTbaEKYrL31IqUE2ey0q97uwkYQuwaoOKTDQEjRpF9uvyqWAiIusC2T8XXkdse5YN6v7mJaV9YLbYdFp0WLRbJNqZ5KW0KGbCzBJSYoF3awYQmr/dJyEQAc1v+fb2v+vv6ZrftJA09/LfO18qiGN4MkiJs4JHzXmSlpjO/3gGl3/DIjMbC203Y30qHJZ5mOt5rXKh6lZvNIg3EBoRK064D8Z6rZhNht7PJM1yzS1qKoRxI6MqHbH5fFygxJ7Ngqx0lhHI66SlVi9ed18EVRQx+ERZ1sIL2xiEL04omg0ZL1UdcxPQUv9blnYWsX6uUTZkPa7TRogKKAV/owxrXPOeHLddQfZe5ULednANVZaKxh3xP6uHYoAaJQm8yjozu32UclCAl89497djUIoCuinw/Ac7shXD/wTqigqw728ZO4xTnjoazZbPUOHZuRzb0aCPJWZsMMXS6mRa3nCvWODqCv0Tj/awJoUWVUH3BClhhpEQjjqorSeAa6Fk0YEPGzDSONWKISfmhA9iojpL5zu2SjEovldJPD3XI9GaFrEmuhHc82CqFXvnYFXN4yy4/+MzAnWdOiY6lxDCD8+7G9r6ooIXiszunG1MFzudTKnCj1Sy9jbzLbp0LKipIiRnZZQ5P/Vc1oBv4fhibUMJVmL5bnEhcdGcKQZ3vzYVo6/a7eG6ylGU/02CuIpK7TtDJdgz9acn1XceHgwFuFbQA/zLMQp/ObyMVwpjkv4kqo5lJzLrcDJ1vmsEan9Ghy+F7hlQY6yCHdcYBXaTvdgC8RQnmtg0dpQt4yw7Byeu35Hei24Xa3juJbTrCWsKHxWiQU/o82yP5tjPyqI8tPyPEZeC2NPbWstsabkSLbxQxOwcxSoXLdNYkrsDHfSqMs0xPkxOv/vnQl+xnZtHsnFkFXPDYAlNHgUvtkK+6PAb6XjFTa04pfxeYhLVqHuNd+3zfOsc4kyCzE81xA3R0ee2qE1rceOWuHrzlE8zVcjsaa2QALnWh35J6/QR7QmZqMh13u0+Ljo5pg77QqoUmuNu3N2bj5bwaCAfngODVCXj2vRXsNWpwLqzoMfHSu7/VAwR81c9bPkEcZS7oL8kfcu8yJmmS0NI4IKQExkbOBq4EfFvXyQARDFmYijmswUotZLYzHNjLv/tZnWUqiu3VQht6G4pzjUOz7t6NpNjyLRw0Mv5m3R3678CLu3pr1o5vNo/NH0IwG/pMM1azI182eq11po0fwZAQAGsNmvdUCK13csAQkA/p+R88vMfJpKO9soAwewzX/00I2Jr0R+pj4lU98P+eEEb69p8Szyc1WKl7YkDEjk/NyEmttsKdJfc0e8KAmF06P17J9wud8bhayQ/2Grx79XtscwpzcUP8w8ugTFm0UKZ+uuCU/dje/7V1EgxVmgH71en8gPppcAABxANY+DMN71cA27AXsRDJQfmDPtoAQWOkAZrERmnXQB1MBZF0EZPHUVHAW+uAZ6lgAEMCQOwMGKQRtoO6gdNMGfDtA2ITJrShfA0FtdBO0iCF0Fb4jxmjWwKbJpFTtHhIjmXM1nqoUbDcTievffaNXDeD9Zav/HMdDcubQTPF79ARlHjSeEm73E2JybUfrz98OHofdyHkZpsY7PLsbhuxd7JfUFz7X0D8XOESGiOVfzvjnVws3FY/Hk1/+NVj2MlcN+bP4/joFu/Fw+vzLED2DWYa0cGG72EsNwc96GUfp/tMPEYfQ5yHmg79ViHZ+dYHH47iWcruHieXzXg9sQzH3VuMz2zr9NGyiqphumZTuu92m+N/sAIkwo+6rc9zEXUmljnQ8x5VLVTdv1wzhd+NjDvKzbfpzX/bzffwa+1k8i9Q94y1I3OORFzFZ4xMVIS/4UL/h8jG5c/KvxepKSU+6xfIKlKdIzMrOylTm5efkFhYuKFquKl5SUlqnLKyqrqpcuW16jgWAExd5g7t8bSdEMy/GCKMmKqumGadmO6/lBGMVJmuVFWdVN2/XDOM3Luu3Hed3P+/n+/iAEIyiGEyRFMyzHC6IkK6qvp/I4aLphWg6ny+3x+mx/JNXqKMqm9hIwj9RjQ95vTQuMkWJ8UmsFfAkO2NZOgWc35B1XdQifJy72/ss3nzfL4nNawnJL5rXAf8mPd778VgeHV31zC5s7UkUgkcqLGkxzMsCT7tstTtiWWXDUEnAafW+Lo3wiWIDKb9jTXfPBDR1QA2mjETonnlin9I7sQLJFJd+6FKcB2KzzguRxysTrLJobAl6Ay6TRAH3tfHrlEIBbApmI7ZMOx7dwI3fejciLQ7bb95poW/n617tsqYnpFDPuqgfyeS39MGIIWwPUw4D0+TpjGim4Bm8Z4woNxQwc2NfsbVDJDDagfj0nE5VZbUtZ7XBXenO95mXt6ErAOwPBaau1cu00sxBrpCsPFaR9iKjiMVLFIWEK7WcFzmfiGtiWeRzpXPKYXKsTsqqD4Q9nYZtZHYEEfH/XjMOSXirgt///+fvbu77NTOyAbdGiTOpdL6tO2GYdZq9mBDLKtYvaQCsAw7WqaKlChpe0qiBglhBcP++y+ydC3f1xw7HxcseQ55I5FZM2DWyuQDF5Ii0CN0baXhXHOTVSdzimwxzfyVtmCBTVI/nO6dAQJ81ziwZ5/c9fvwYcS5N2iyNPn7SpVODExh2jDgRcHn9Ctl5/GlKhOh7/AcXQP3nSulUrsKfMZzmOaYOqxdHuDsCbHfqNmhKOJYS3ikhwrbTkNafuCkZKq5S8mD6ZLZxujk1qp4ZAsokqzA6BNBToHYAmzdoZlJuOp20hVFvRDJx2QFSEQIPzLzWvgCvkThMKQfHYwFZpjQOxPcSB6I3Emcx2f70ucG0HvNCknOF4R+qhFFQubyCzCh1nUaAzreHi++BEW3Ii1I7NuIMaXITcIH/SrEiwtO2ZPmVTnSwqMxbcEVKr3DVdaNYg7MutWXh5ZoWtBcl4VtuecQbSuwbY6Qo1khHa9Zyq2PqDuEMxDa5RVlCpNYI/qpcZRTOmGqXd2pN64Bk4a4ntWd3Dh/jVrAG2u+Qvswp8vek0iAaVVQOV7rhONBALJeFsD9MzGUHgSGjFQ9PU1RXtKpqxWmgP4DIscFnqlBotM/r1ZosD7gDzuKzDxaIC9FAxp29sm3X6Uz0ymMcAVUj1sNJKKlXO78DwpAXlMkW7vRk0fmE7zcvsUiyC1/MiiEd0XqqAM7BZBIJRyCVrIAN8wFCaO9JwCBQQuNq29r+CzCJbo5KMGDCeJsV06ErzVmdMJFgonbWcSIZNpXahzBOnIGrJUdfWqDDiyz9YHHGGg61IU+Gclw/MUPoBAWmcL1q1cIMcjqF21cjY5T2xHda1ST+Hqy86LIEqZnZpKTqt8l7YiqnKLfBVgaEWlcVRQyQs7npndiLgI72swMPs2gpbwYmchy3IHlq2AXHLqGRxOuN+Vr7HVmpA9ynGZhJD+kGKoWCwF26DwwoJgpjruO2/Fi5O9EoZIxhlKkfGyiM3ZphF+4xL8abOgfRglRwL0z/B4lN5Ov1zrk5n7AlYhtUtupsUJ2pnzkScxnNES9seWd/fBExj3Diq8YYcQ74o9zCsLzMGIj7azYAyeCwcgo+uF4M7BgML6QdhKgxGrCMJT0Utfa9McZ7WTkIkttMl1+St6QEAAAA=') 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=1576206033975'); /* IE9 */ + src: url('iconfont.eot?t=1576206033975#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=1576206033975') format('woff'), + url('iconfont.ttf?t=1576206033975') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ + url('iconfont.svg?t=1576206033975#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"; } @@ -295,6 +303,10 @@ content: "\e694"; } +.icon-bokeyuan:before { + content: "\e6c6"; +} + .icon-base:before { content: "\e683"; } @@ -799,6 +811,10 @@ content: "\e68c"; } +.icon-pinglun:before { + content: "\e6c8"; +} + .icon-gongcheng:before { content: "\e60f"; } @@ -811,6 +827,10 @@ content: "\e604"; } +.icon-shangjiantou-tianchong:before { + content: "\e733"; +} + .icon-zhuye:before { content: "\e6d3"; } @@ -839,6 +859,10 @@ content: "\e6a1"; } +.icon-shenglvehao:before { + content: "\e708"; +} + .icon-paixu1:before { content: "\e6aa"; } @@ -923,3 +947,43 @@ 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-wancheng1:before { + content: "\e6cb"; +} + +.icon-qiyezhanghao:before { + content: "\e6cc"; +} + +.icon-gerenzhanghao:before { + content: "\e6cd"; +} + +.icon-jiazaishibai1:before { + content: "\e6d6"; +} + 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 2fa9e0765..7d83757aa 100644 --- a/public/react/src/App.js +++ b/public/react/src/App.js @@ -316,6 +316,11 @@ const RecordDetail = Loadable({ loader: () => import('./modules/developer/recordDetail'), loading: Loading }); +// jupyter tpi +const JupyterTPI = Loadable({ + loader: () => import('./modules/tpm/jupyter'), + loading: Loading +}); // //个人竞赛报名 // const PersonalCompetit = Loadable({ // loader: () => import('./modules/competition/personal/PersonalCompetit.js'), @@ -358,7 +363,7 @@ class App extends Component { mydisplay:true, }) }; - + disableVideoContextMenu = () => { window.$( "body" ).on( "mousedown", "video", function(event) { if(event.which === 3) { @@ -577,14 +582,14 @@ class App extends Component { { - + return () } }> { + (props) => { return () } }> @@ -610,12 +615,21 @@ class App extends Component { + {/* jupyter */} + { + return () + } + } + /> + - {/*列表页*/} + {/*列表页 实训项目列表*/} @@ -637,8 +651,8 @@ class App extends Component { () }/> - - () } @@ -671,7 +685,7 @@ class App extends Component { (props)=>() }/> - { return () @@ -679,11 +693,11 @@ class App extends Component { } /> () } /> - () } @@ -703,7 +717,7 @@ class App extends Component { (props)=>() } /> - + @@ -825,4 +839,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 d88d5f05f..86cf39bac 100644 --- a/public/react/src/AppConfig.js +++ b/public/react/src/AppConfig.js @@ -13,10 +13,10 @@ function locationurl(list){ if (window.location.port === "3007") { } else { - window.location.replace(list) + window.location.href(list) } } -let hashTimeout +let hashTimeout // TODO 开发期多个身份切换 let debugType ="" @@ -35,7 +35,7 @@ if (isDev) { // 老师 //ebugType="teacher"; // 学生 -//debugType="student"; +// debugType="student"; window._debugType = debugType; export function initAxiosInterceptors(props) { @@ -51,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控制,避免用户通过双击等操作发出重复的请求; @@ -88,7 +89,6 @@ export function initAxiosInterceptors(props) { url = `${config.url}`; } } - if(`${config[0]}`!=`true`){ if (window.location.port === "3007") { // if (url.indexOf('.json') == -1) { @@ -109,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; @@ -224,7 +224,7 @@ export function initAxiosInterceptors(props) { return Promise.reject(error); }); // ----------------------------------------------------------------------------------- - + } 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..c9a1a2c58 100644 --- a/public/react/src/common/UrlTool.js +++ b/public/react/src/common/UrlTool.js @@ -31,7 +31,7 @@ export function getUrl(path, goTest) { // 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-newweb.educoder.net' + const local = 'https://test-jupyterweb.educoder.net' if (isDev) { return `${local}${path?path:''}` } @@ -55,6 +55,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 `${getUrl()}/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 +89,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/MyIcon.js b/public/react/src/common/components/MyIcon.js index 7c6f4bb94..aa9f29a19 100644 --- a/public/react/src/common/components/MyIcon.js +++ b/public/react/src/common/components/MyIcon.js @@ -4,12 +4,12 @@ * @Github: * @Date: 2019-12-10 09:03:48 * @LastEditors: tangjiang - * @LastEditTime: 2019-12-10 09:05:41 + * @LastEditTime: 2019-12-12 10:53:47 */ import { Icon } from 'antd'; const MyIcon = Icon.createFromIconfontCN({ - scriptUrl: '//at.alicdn.com/t/font_1535266_ss6796i6f6j.js' + scriptUrl: '//at.alicdn.com/t/font_1535266_i4ilpm93kp.js' }); export default MyIcon; diff --git a/public/react/src/common/educoder.js b/public/react/src/common/educoder.js index 004cd91c0..1eae620c1 100644 --- a/public/react/src/common/educoder.js +++ b/public/react/src/common/educoder.js @@ -3,10 +3,10 @@ // 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 + , 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/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/coursesPublic/NewShixunModel.js b/public/react/src/modules/courses/coursesPublic/NewShixunModel.js index 462df7608..a9100f2f3 100644 --- a/public/react/src/modules/courses/coursesPublic/NewShixunModel.js +++ b/public/react/src/modules/courses/coursesPublic/NewShixunModel.js @@ -494,7 +494,7 @@ class NewShixunModel extends Component{

筛选:

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

-

this.belongto("mine")}>普通实训

+

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

:"" } {/*{this.props.type==='shixuns'? */} @@ -581,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 +

+
+ :"" + ) + :"" + } +
+ 上传附件 + (单个文件150M以内) diff --git a/public/react/src/modules/courses/shixunHomework/Listofworksstudentone.js b/public/react/src/modules/courses/shixunHomework/Listofworksstudentone.js index 855f860ef..ab75b6c89 100644 --- a/public/react/src/modules/courses/shixunHomework/Listofworksstudentone.js +++ b/public/react/src/modules/courses/shixunHomework/Listofworksstudentone.js @@ -4005,7 +4005,7 @@ class Listofworksstudentone extends Component { height: 58px; } .ysltableows .ant-table-thead > tr > th, .ant-table-tbody > tr > td { - padding: 9px; + padding: 9px; } ` } diff --git a/public/react/src/modules/developer/components/userInfo/index.js b/public/react/src/modules/developer/components/userInfo/index.js index 455902e13..3508135ba 100644 --- a/public/react/src/modules/developer/components/userInfo/index.js +++ b/public/react/src/modules/developer/components/userInfo/index.js @@ -14,7 +14,7 @@ function UserInfo (props) { const {image_url, name} = props.userInfo; return (
- 用户头像 + 用户头像 {name || ''} diff --git a/public/react/src/modules/developer/index.scss b/public/react/src/modules/developer/index.scss index cc76bc4b7..ad6cc09a3 100644 --- a/public/react/src/modules/developer/index.scss +++ b/public/react/src/modules/developer/index.scss @@ -1,11 +1,14 @@ .banner-wrap{ width: 100%; height: 300px; - background-image: url(/static/media/path.e39ba7de.png); - background-color: #000a4f; - /* background-size: cover; */ - background-position: center; - background-repeat: no-repeat; + // background-image: url(/static/media/path.e39ba7de.png); + // background: #000a4f url(../../images/oj//oj_banner.jpg) none center; + // background-color: #000a4f; + // /* background-size: cover; */ + // background-position: center; + // background-repeat: no-repeat; + background: rgb(0, 1, 35) url(../../images/oj/oj_banner.jpg) no-repeat center; + background-size: cover; } .developer-list{ diff --git a/public/react/src/modules/home/shixunsHome.js b/public/react/src/modules/home/shixunsHome.js index 3a75be6eb..a495a97c3 100644 --- a/public/react/src/modules/home/shixunsHome.js +++ b/public/react/src/modules/home/shixunsHome.js @@ -334,6 +334,33 @@ class ShixunsHome extends Component { .square-Item:nth-child(4n+0) { margin-right: 25px; } + .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; + } ` } @@ -345,7 +372,13 @@ class ShixunsHome extends Component { {item.tag_name} {/**/}
- + { + item.is_jupyter===true? +
+

Jupyter

+ {/**/} +
+ :""}
diff --git a/public/react/src/modules/modals/Bottomsubmit.js b/public/react/src/modules/modals/Bottomsubmit.js new file mode 100644 index 000000000..7287f0858 --- /dev/null +++ b/public/react/src/modules/modals/Bottomsubmit.js @@ -0,0 +1,54 @@ +import React, {Component} from 'react'; +import { + Button, +} from 'antd'; + +class Bottomsubmit extends Component { + constructor(props) { + super(props) + + this.state = {} + } + + cannelfun = () => { + // window.location.href= + this.props.history.replace(this.props.url); + } + + + render() { + + return ( +
+ +
+
+ + +
+
+
+ + ); + } +} + + +export default Bottomsubmit; + + + + + + diff --git a/public/react/src/modules/modals/Modals.js b/public/react/src/modules/modals/Modals.js index 859e54784..e241f379a 100644 --- a/public/react/src/modules/modals/Modals.js +++ b/public/react/src/modules/modals/Modals.js @@ -29,6 +29,9 @@ render() { body{ overflow: hidden !important; } + .ant-modal-body { + padding: 20px 40px; + } ` } :""} diff --git a/public/react/src/modules/page/component/monaco/TPIMonaco.js b/public/react/src/modules/page/component/monaco/TPIMonaco.js index c71f13361..906765056 100644 --- a/public/react/src/modules/page/component/monaco/TPIMonaco.js +++ b/public/react/src/modules/page/component/monaco/TPIMonaco.js @@ -23,7 +23,7 @@ import { isThisSecond } from 'date-fns'; monaco.editor.defineTheme('myCoolTheme', { base: 'vs', // vs、vs-dark、hc-black inherit: true, - rules: [ + rules: [ { token: 'green', background: 'FF0000', foreground: '00FF00', fontStyle: 'italic'}, { token: 'red', foreground: 'FF0000' , fontStyle: 'bold underline'}, { background: '#121c23' }, @@ -37,7 +37,7 @@ monaco.editor.defineTheme('myCoolTheme', { // 'editor.selectionHighlightBorder': '#ffffff', // 'input.border': '#ffffff', 'editor.lineHighlightBorder': '#222c34', // .current-line - + // 'editor.selectionBackground': '#FFFF0030', // 'editor.selectionHighlightBackground' :'#0000FFFF', } @@ -60,7 +60,7 @@ function loadMonacoResouce(callback) { // https://github.com/Microsoft/monaco-editor/issues/82 // window.require = { paths: { 'vs': '../node_modules/monaco-editor/min/vs' } }; // window.require = { paths: { 'vs': `${_url_origin}${prefix}/js/monaco/vs` } }; - + // $('head').append($('') // .attr('href', `${_url_origin}${prefix}/js/monaco/vs/editor/editor.main.css`)); @@ -68,7 +68,7 @@ function loadMonacoResouce(callback) { // cache: true // }); // $.when( - + // // $.getScript( `${_url_origin}${prefix}/js/monaco/vs/language/typescript/tsMode.js` ), // // $.getScript( `${_url_origin}${prefix}/js/monaco/vs/basic-languages/javascript/javascript.js` ), // $.getScript( `${_url_origin}${prefix}/js/monaco/vs/basic-languages/python/python.js` ), @@ -79,7 +79,7 @@ function loadMonacoResouce(callback) { // $.getScript( `${_url_origin}${prefix}/js/monaco/vs/editor/editor.main.js` ), // $.Deferred(function( deferred ){ // console.log('Deferred') - + // // TODO 暂时放这里 // $( deferred.resolve ); // checkIfLoaded(callback); @@ -126,7 +126,7 @@ function checkIfLoaded (callback) { } /* - language + language javascript css less scss html typescript java ruby vb r python php perl go cpp csharp sql pgsql mysql @@ -155,7 +155,7 @@ function checkIfLoaded (callback) { version: 3, singleLineStringErrors: false }, -*/ +*/ const mirror2LanguageMap = { 'JFinal': 'java', 'Java': 'java', @@ -194,10 +194,10 @@ function getLanguageByMirrorName(mirror_name) { let notCallCodeMirrorOnChangeFlag = false; -/** - props : +/** + props : mirror_name 决定语言 - isEditablePath + isEditablePath repositoryCode codemirrorDidMount @@ -218,7 +218,7 @@ class TPIMonaco extends Component { autoCompleteSwitch: fromStore('autoCompleteSwitch', true), } } - + componentDidUpdate(prevProps, prevState, snapshot) { const { mirror_name } = this.props const editor_monaco = this.editor_monaco; @@ -234,20 +234,20 @@ class TPIMonaco extends Component { } else { editor_monaco.updateOptions({readOnly: true}) } - - } else if (editor_monaco && prevProps.codeLoading === true && this.props.codeLoading === false + + } else if (editor_monaco && prevProps.codeLoading === true && this.props.codeLoading === false ) { if (this.props.repositoryCode != editor_monaco.getValue()) { - // newProps.repositoryCode !== this.props.repositoryCode && + // newProps.repositoryCode !== this.props.repositoryCode && notCallCodeMirrorOnChangeFlag = true; - + // 重要:setState(因获取代码、重置代码等接口引起的调用)调用引起的变化才需要setValue editor_monaco.setValue(this.props.repositoryCode) } // 代码没变也需要layout,可能从命令行自动切回了代码tab editor_monaco.layout(); - + // Clears the editor's undo history. // TODO // extend_editor.clearHistory() @@ -258,7 +258,7 @@ class TPIMonaco extends Component { // 注意销毁,不然会出现不能编辑的bug this.editor_monaco && this.editor_monaco.dispose() } - + componentDidMount() { checkIfLoaded(() => { let value = [ @@ -290,7 +290,7 @@ class TPIMonaco extends Component { // http://testeduplus2.educoder.net/react/build/static/node_modules/_monaco-editor@0.15.6@monaco-editor/esm/vs/editor/common/config/commonEditorConfig.js // https://github.com/Microsoft/monaco-editor/issues/29 scrollBeyondLastLine: false, - + language: lang, // language: 'css', @@ -304,7 +304,7 @@ class TPIMonaco extends Component { window.editor_monaco = editor; this.editor_monaco = editor - + // editor.setPosition({ lineNumber: 2, column: 30 }); // editor.model.onDidChangeContent((event) => { @@ -323,27 +323,27 @@ class TPIMonaco extends Component { this.props.onRepositoryCodeUpdate && this.props.onRepositoryCodeUpdate(editor.getValue()) }) this.props.codemirrorDidMount && this.props.codemirrorDidMount() - + if(this.props.shixun && this.props.shixun.forbid_copy == true) { // 禁用粘贴 // https://github.com/Microsoft/monaco-editor/issues/100 window.editor_monaco.onDidPaste( (a, b, c) => { window.__pastePosition = a }) - window.addEventListener('paste', (a, b, c) => { - const selection = window.editor_monaco.getSelection(); - const range = new monaco.Range( - window.__pastePosition.startLineNumber || selection.endLineNumber, - window.__pastePosition.startColumn || selection.endColumn, - window.__pastePosition.endLineNumber || selection.endLineNumber, - window.__pastePosition.endColumn || selection.endColumn,); + window.addEventListener('paste', (a, b, c) => { + const selection = window.editor_monaco.getSelection(); + const range = new monaco.Range( + window.__pastePosition.startLineNumber || selection.endLineNumber, + window.__pastePosition.startColumn || selection.endColumn, + window.__pastePosition.endLineNumber || selection.endLineNumber, + window.__pastePosition.endColumn || selection.endColumn,); window.editor_monaco.executeEdits('', [{range, text: ''}] ) - }) + }) // 禁用复制 window.editor_monaco.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_C, () => null); window.editor_monaco.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_V, () => null); } - + setTimeout(() => { editor.layout(); @@ -352,23 +352,23 @@ class TPIMonaco extends Component { window.editor_monaco.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S, () => { this.props.doFileUpdateRequestOnCodeMirrorBlur(); - return false; + return false; }); - window.editor_monaco.onDidBlurEditorWidget(() => { + window.editor_monaco.onDidBlurEditorWidget(() => { this.props.onEditBlur && this.props.onEditBlur(); - }) + }) }) - // window.document.onkeydown = (e) => { + // window.document.onkeydown = (e) => { // e = window.event || e; - // if(e.keyCode== 83 && e.ctrlKey){ + // if(e.keyCode== 83 && e.ctrlKey){ // /*延迟,兼容FF浏览器 */ // // setTimeout(function(){ - // // alert('ctrl+s'); - // // },1); + // // alert('ctrl+s'); + // // },1); // this.props.doFileUpdateRequestOnCodeMirrorBlur(); - // return false; - // } + // return false; + // } // }; } onFontSizeChange = (value) => { @@ -390,7 +390,7 @@ class TPIMonaco extends Component { render() { const { repositoryCode, showSettingDrawer, settingDrawerOpen } = this.props; const { cmFontSize } = this.state; - + return ( showSettingDrawer( false )} > - { + this.setState({ + is_jupyter: e.target.value, + }); + }; render() { - + const formItemLayout = { + labelCol: { span: 4 }, + wrapperCol: { span: 14 }, + }; return( :""}
+
+ + + 普通实训 + jupyter实训 + + +

实训名称: diff --git a/public/react/src/modules/paths/PathDetail/DetailCardsEditAndAdd.js b/public/react/src/modules/paths/PathDetail/DetailCardsEditAndAdd.js index c054ac0db..9a4b82fb3 100644 --- a/public/react/src/modules/paths/PathDetail/DetailCardsEditAndAdd.js +++ b/public/react/src/modules/paths/PathDetail/DetailCardsEditAndAdd.js @@ -320,7 +320,7 @@ class DetailCardsEditAndAdd extends Component{ }) } - Getaddshixuns=(value)=>{ + Getaddshixuns=(value,is_jupyter)=>{ let { shixuns_listeditlist, shixuns_listedit, @@ -329,7 +329,8 @@ class DetailCardsEditAndAdd extends Component{ let list=shixuns_listeditlist let url='/paths/add_shixun_to_stage.json'; axios.post(url,{ - name:value + name:value, + is_jupyter:is_jupyter }).then((response) => { if(response){ if(response.data){ @@ -383,7 +384,7 @@ class DetailCardsEditAndAdd extends Component{ {this.state.Addshixunstype===true?this.Getaddshixuns(value)} + Setaddshixuns={(value,is_jupyter)=>this.Getaddshixuns(value,is_jupyter)} {...this.props} {...this.state} />:""} diff --git a/public/react/src/modules/paths/PathDetail/DetailCardsEditAndEdit.js b/public/react/src/modules/paths/PathDetail/DetailCardsEditAndEdit.js index 20d9ce9ed..350c2eb8d 100644 --- a/public/react/src/modules/paths/PathDetail/DetailCardsEditAndEdit.js +++ b/public/react/src/modules/paths/PathDetail/DetailCardsEditAndEdit.js @@ -320,7 +320,7 @@ class DetailCardsEditAndEdit extends Component{ notification.open(data); } - Getaddshixuns=(value)=>{ + Getaddshixuns=(value,is_jupyter)=>{ let { shixuns_listeditlist, shixuns_listedit, @@ -329,7 +329,8 @@ class DetailCardsEditAndEdit extends Component{ let list=shixuns_listeditlist let url='/paths/add_shixun_to_stage.json'; axios.post(url,{ - name:value + name:value, + is_jupyter:is_jupyter }).then((response) => { if(response){ if(response.data){ @@ -383,7 +384,7 @@ class DetailCardsEditAndEdit extends Component{ {this.state.Addshixunstype===true?this.Getaddshixuns(value)} + Setaddshixuns={(value,is_jupyter)=>this.Getaddshixuns(value,is_jupyter)} {...this.props} {...this.state} />:""} diff --git a/public/react/src/modules/tpm/Audit_situationComponent.js b/public/react/src/modules/tpm/Audit_situationComponent.js index 4d6c413da..3739cd6db 100644 --- a/public/react/src/modules/tpm/Audit_situationComponent.js +++ b/public/react/src/modules/tpm/Audit_situationComponent.js @@ -212,6 +212,7 @@ class Audit_situationComponent extends Component { user={user} shixun={shixun} {...this.props} + is_jupyter={this.props.is_jupyter} >

diff --git a/public/react/src/modules/tpm/TPMBanner.js b/public/react/src/modules/tpm/TPMBanner.js index b660001c6..bf33f94a8 100644 --- a/public/react/src/modules/tpm/TPMBanner.js +++ b/public/react/src/modules/tpm/TPMBanner.js @@ -1,16 +1,12 @@ -import React, { Component } from 'react'; - -import { Redirect } from 'react-router'; +import React, {Component} from 'react'; import {BrowserRouter as Router, Route, Link, Switch} from "react-router-dom"; -import PropTypes from 'prop-types'; - -import { Rating ,Progress} from "@icedesign/base"; +import {Rating, Progress} from "@icedesign/base"; -import {Modal,Input,Radio,Pagination,message,Spin,Icon,Tooltip,Rate} from 'antd'; +import {Modal, Input, Radio, Pagination, message, Spin, Icon, Tooltip, Button,Popover} from 'antd'; -import AccountProfile from"../user/AccountProfile"; +import AccountProfile from "../user/AccountProfile"; import 'antd/lib/pagination/style/index.css'; @@ -27,515 +23,693 @@ 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 + 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, + jupyterbool: false, + openknow:false, + openshowpublictype: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浏览器 } - - // 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 + } + + openknow=()=>{ + let storage=window.localStorage; + this.setState({ + openknow:false + }) + storage.setItem("shixunopenprocess",true); + } + + openshowpublic=()=>{ + let storage=window.localStorage; + this.setState({ + openshowpublictype:false + }) + storage.setItem("openopenpublictype",true); + } + + + componentDidUpdate(prevProps, prevState) { + if (prevProps != this.props) { + let shixunopenprocess=window.localStorage.shixunopenprocess; + let openopenpublictype=window.localStorage.openopenpublictype; + if(this.props.shixunsDetails.shixun_status === 0 && this.props.identity < 5){ + if(shixunopenprocess===undefined||shixunopenprocess===false){ + this.setState({ + openknow:true + }) }else{ - return -1;//不是ie浏览器 + this.setState({ + openknow:false + }) } - } - componentDidMount() { - let thiisie=this.IEVersion(); - if(thiisie!=-1){ + } + + if(this.props.shixunsDetails.shixun_status === 2 && this.props.shixunsDetails.public===0 && this.props.identity < 5){ + if(openopenpublictype===undefined||openopenpublictype===false){ this.setState({ - isIE:true + openshowpublictype:true }) - }else{ + }else{ this.setState({ - isIE:false + openshowpublictype: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, - }) - } + } + 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 = () => { + } + + 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 + 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 } - addForkvisible = () => { + // 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({ - Forkvisibletype: true, + courses_count: response.data.courses_count, + course_list: response.data.course_list }) - 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) + }); + } + + 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 = () => { - }).catch((error) => { - console.log(error) - }); + let {Senttothevcalue} = this.state; + if (Senttothevcalue === undefined) { + this.setState({ + Senttothevcaluetype: true + }) + return } - /* - * 发送至按钮 - * */ - 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"; + 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, + Modalstopval: "", + modalsMidval:undefined, + ModalsBottomval:"", + }) + } + 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: "是否确认撤销发布?", + modalsMidval:"撤销发布后,学员将无法进行练习,若您新增关", + ModalsBottomval:"卡,学员需要重新体验课程", + ModalCancel: this.ModalCancel, + ModalSave: this.ModalSave, + }) + } + + openpublic=()=>{ + let id = this.props.match.params.shixunId; + let url = `/shixuns/${id}/apply_public.json`; + axios.get(url).then((response) => { + if(response.data.status===0){ + window.location.reload() + } + }).catch((error) => { + console.log(error) + }); + } + + ModalhidenpublicSave=()=>{ + let id = this.props.match.params.shixunId; + let url = `/shixuns/${id}/cancel_apply_public.json`; + axios.get(url).then((response) => { + if(response.data.status===0){ + window.location.reload() + } + }).catch((error) => { + console.log(error) + }); + } + + hidenpublic=()=>{ + this.setState({ + Modalstype: true, + Modalstopval: "是否确认撤销公开?", + modalsMidval:" ", + ModalsBottomval:" ", + ModalCancel: this.ModalCancel, + ModalSave: this.ModalhidenpublicSave, + }) + + } + /* + * 申请发布按钮 + * */ + 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 + } + if(response.data.status===0){ + window.location.reload() + }else{ this.setState({ - Senttothetype:true + Issuevisible: true, + tag_position: response.data.tag_position, + evaluation_set_position: evaluation_set_position, + publishboxstatus: response.data.status, }) + } - 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) - }); + }).catch((error) => { + console.log(error) + }); + }; + + hiddenIssuevisible = (val) => { + this.setState({ + Issuevisible: false + }) + if (val === 0 || val === 1) { + window.location.reload() } - 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) - }); - } + } + + //重置按钮 + // 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, + }) - 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); + // message.success('重置成功,正在进入实训!'); + // this.startshixunCombat(); + } + } + ).catch((error) => { + this.setState({ + startbtn: false, + shixunsreplace: false, + isSpin: false + }) + }); - }).catch((error) => { - console.log(error) - }); + } - } - hideSenttothevcalue=()=>{ + //开始实战按钮 + startshixunCombat = (id, reset) => { + + if(this.props.shixunsDetails&&this.props.shixunsDetails.is_jupyter===true){ + if (this.props.checkIfLogin() === false) { + this.props.showLoginDialog() + return + } + + if (this.props.checkIfProfileCompleted() === false) { this.setState({ - Senttothetype:false, - Searchvalue:"", - pages:1 + AccountProfiletype: true }) + return + } + // if(this.props.checkIfProfessionalCertification()===false){ + // this.setState({ + // AccountProfiletype:true + // }) + // return + // } - } - - /* - * 撤销发布按钮 - * */ - - 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, - }) - } + let {shixunsDetails} = this.props + if (shixunsDetails.shixun_status > 1) { + this.setState({ + startbtn: true, + hidestartshixunsreplacevalue: "" + }) + } else { + this.setState({ + hidestartshixunsreplacevalue: "" + }) + } - /* - * 申请发布按钮 - * */ - 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 - } + let url = "/shixuns/" + id + "/jupyter_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({ - Issuevisible:true, - tag_position:response.data.tag_position, - evaluation_set_position:evaluation_set_position, - publishboxstatus:response.data.status, + startbtn: false, + shixunsreplace: true, + hidestartshixunsreplacevalue: response.data.message + ".json" }) - }).catch((error) => { - console.log(error) - }); - }; + // this.shixunexec(response.data.message+".json") + } else if (response.data.status === -1) { - hiddenIssuevisible=(val)=>{ - this.setState({ - Issuevisible:false - }) - if(val===0||val===1){ - window.location.reload() - } + } 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); - } - //重置按钮 - // 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) - // }); - // } + // this.context.router.history.push(path); + if (response.data.status != 401) { + window.location.href = "/tasks/" + response.data.identifier+`/jupyter`; + } - // reset_my_game - hidestartshixunsreplace=(url)=>{ + } + } + }).catch((error) => { this.setState({ - isSpin:true, + startbtn: false }) - 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 - }) - }); - - } - + }); + }else{ + if (this.props.checkIfLogin() === false) { + this.props.showLoginDialog() + return + } - //开始实战按钮 - startshixunCombat=(id, reset)=>{ + if (this.props.checkIfProfileCompleted() === false) { + this.setState({ + AccountProfiletype: true + }) + return + } - if(this.props.checkIfLogin()===false){ - this.props.showLoginDialog() - return - } + // if(this.props.checkIfProfessionalCertification()===false){ + // this.setState({ + // AccountProfiletype:true + // }) + // return + // } - if(this.props.checkIfProfileCompleted()===false){ - this.setState({ - AccountProfiletype:true - }) - return - } + let {shixunsDetails} = this.props + if (shixunsDetails.shixun_status > 1) { + this.setState({ + startbtn: true, + hidestartshixunsreplacevalue: "" + }) + } else { + this.setState({ + hidestartshixunsreplacevalue: "" + }) + } - // 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{ + 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({ - hidestartshixunsreplacevalue:"" + startbtn: false, + shixunsreplace: true, + hidestartshixunsreplacevalue: response.data.message + ".json" }) - } + // this.shixunexec(response.data.message+".json") + } else if (response.data.status === -1) { - - 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) => { + } else if (response.data.status === -3) { this.setState({ - startbtn:false + shixunsmessage: response.data.message, + startshixunCombattype: true, + startbtn: false }) - }); - } + } else { + // let path="/tasks/"+response.data.game_identifier; + // this.props.history.push(path); - tocertification=()=>{ - let{certi_url}=this.state; - this.setState({ - Forkauthentication:false - }) - window.location.href=certi_url; - } - SenttotheValue=(e)=>{ - this.setState({ - Searchvalue:e.target.value - }) - } + // this.context.router.history.push(path); + if (response.data.status != 401) { + window.location.href = "/tasks/" + response.data.game_identifier; + } - hidestartshixunCombattype=()=>{ + } + } + }).catch((error) => { this.setState({ - startshixunCombattype:false + startbtn: 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=[] + + } + + 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) { @@ -544,265 +718,275 @@ class TPMBanner extends Component { // 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 = "开始实战" - } + 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; // } - const radioStyle = { - display: 'block', - height: '30px', - lineHeight: '30px', - }; - - const antIcon = ; - 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?:""} - -
-

+ + return ; + }; + // + // console.log(this.props.shixunsDetails&&this.props.shixunsDetails.is_jupyter) + + 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.fork_from === undefined || shixunsDetails.fork_from === null ? "" : + + + } -

-
- {/**/} -
    -
  • - 学习人数 - {shixunsDetails.stu_num} -
  • - {/*
  • */} - {/*经验值*/} - {/*{shixunsDetails.experience}*/} - {/*
  • */} -
  • - 难度系数 - {shixunsDetails.diffcult} - -
  • +

    +
    + {/**/} +
      +
    • + 学习人数 + {shixunsDetails.stu_num} +
    • + {/*
    • */} + {/*经验值*/} + {/*{shixunsDetails.experience}*/} + {/*
    • */} +
    • + 难度级别 + {shixunsDetails.diffcult} + +
    - -
    this.showonMouseOver()} onMouseOut={()=>this.hideonMouseOut()}> -
    学员评分
    -
    - -
    -
    this.hideonMouseOut()}> -
    - -
    -
    -
    + { + this.props.is_jupyter===false? +
    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]}% -
    -
    -
    -
    -
    + className="font-24 color-yellow-ff lineh-20 mb10 ml20">{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] : "模拟实战"} - - - : "" + 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}之后开放,谢谢!

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

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

    */} +
    +

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

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

    */} + {/*知道了*/} + {/*

    */}
    - +
    -

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

    +

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

    -
    +
    - + { - startbtn === true ? - 开启中 : "" + startbtn === true ? + 开启中 : "" } {/*{*/} @@ -814,172 +998,211 @@ class TPMBanner extends Component { {/*}*/} {shixunsDetails.shixun_status === 0 && this.props.identity < 5 ? + +
    您编辑完成后,可以马上使用到自
    +
    己的课堂和实训课程哦
    +
    + + } + trigger="click" + placement="bottom" + visible={this.state.openknow} + > 申请发布 : "" + id="challenge_begin">发布 +
    : "" } - { - 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}, - ) - }) - } - 关尚未设置技能标签,请补充 -

    -
    : -
    -

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

    -
    - } - + { + 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 ? - 撤销发布 : "" + {shixunsDetails.shixun_status === 2 && shixunsDetails.public===0 && this.props.identity < 5 ? + +
    申请公开成功后,您的实训将会展示实训列表
    +
    平台审核通过,您将获得 1000 金币~
    +
    + + } + trigger="click" + placement="bottom" + visible={this.state.openshowpublictype} + > + +
    : "" + } + + {shixunsDetails.shixun_status === 2 && shixunsDetails.public===1 && this.props.identity < 5 ? + : "" + } + + {shixunsDetails.shixun_status === 2 && shixunsDetails.public===0 && 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"}}> - -
    - -
    -
    -
    -
    - 取消 - 确定 -
    + + +
    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 && - 已删除 - } + {shixunsDetails.shixun_status === -1 && + 已删除 + } - {this.props.identity < 8&&shixunsDetails.shixun_status != -1 ?
    - + {this.props.identity < 8 && shixunsDetails.shixun_status != -1 ? +
    + - - {Forkvisibletype===true? + {Forkvisibletype === true ? - : + :
    -

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

    -
    -
    - 取消 - 确定 -
    +

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

    +
    +
    + 取消 + 确定 +
    } @@ -1016,40 +1239,41 @@ class TPMBanner extends Component {
    -

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

    -
    -
    - 取消 - 确定 -
    +

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

    +
    +
    + 取消 + 确定 +
    {!!shixunsDetails.fork_num && - {shixunsDetails.fork_num} + {shixunsDetails.fork_num} } -
    :""} +
    : ""} + +
+
+
正在等待管理员的审核。在审核通过前,可以随时撤销发布 +
-
-
正在等待管理员的审核。在审核通过前,可以随时撤销发布
-
- - ); - } + ); + } } export default TPMBanner; diff --git a/public/react/src/modules/tpm/TPMChallenge.js b/public/react/src/modules/tpm/TPMChallenge.js index 847e8b965..5c6e1a16a 100644 --- a/public/react/src/modules/tpm/TPMChallenge.js +++ b/public/react/src/modules/tpm/TPMChallenge.js @@ -1,54 +1,63 @@ -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; +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 Challengesjupyter from './shixunchild/Challenges/Challengesjupyter' +import TPMRightSection from './component/TPMRightSection' + +import TPMNav from './component/TPMNav' + +class TPMChallenge extends Component { + constructor(props) { + super(props) + + } + + render() { + const { loadingContent, shixun, user, match,jupyterbool,is_jupyter + } = this.props; + return ( + +
+ +
+ + { + is_jupyter===true? + + : + + } + + +
+ +
+ +
+
+
+ + ); + } +} + +export default TPMChallenge; diff --git a/public/react/src/modules/tpm/TPMChallengeContainer.js b/public/react/src/modules/tpm/TPMChallengeContainer.js index a7c3c8a2b..9fc1f44f4 100644 --- a/public/react/src/modules/tpm/TPMChallengeContainer.js +++ b/public/react/src/modules/tpm/TPMChallengeContainer.js @@ -15,13 +15,16 @@ class TPMChallengeContainer extends Component { render() { const { tpmLoading } = this.props; const user = this.props.current_user; + // console.log("TPMChallengeContainerTPMChallengeContainer"); + // console.log(this.props); - return ( + return ( { tpmLoading ?
: } diff --git a/public/react/src/modules/tpm/TPMCollaborators.js b/public/react/src/modules/tpm/TPMCollaborators.js index cfab39ca5..3b1bdb181 100644 --- a/public/react/src/modules/tpm/TPMCollaborators.js +++ b/public/react/src/modules/tpm/TPMCollaborators.js @@ -1,53 +1,54 @@ -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; +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/modules/tpm/TPMCollaboratorsContainer.js b/public/react/src/modules/tpm/TPMCollaboratorsContainer.js index 80049cee9..61b9bd2ec 100644 --- a/public/react/src/modules/tpm/TPMCollaboratorsContainer.js +++ b/public/react/src/modules/tpm/TPMCollaboratorsContainer.js @@ -1,47 +1,47 @@ -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; +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/modules/tpm/TPMDataset.js b/public/react/src/modules/tpm/TPMDataset.js new file mode 100644 index 000000000..717219057 --- /dev/null +++ b/public/react/src/modules/tpm/TPMDataset.js @@ -0,0 +1,593 @@ +import React, {Component} from 'react'; +import {Redirect} from 'react-router'; +import {List, Typography, Tag, Modal, Radio, Checkbox, Table, Pagination,Upload,notification} from 'antd'; +import { NoneData } from 'educoder' + +import TPMRightSection from './component/TPMRightSection'; +import TPMNav from './component/TPMNav'; +import axios from 'axios'; +import './tpmmodel/tpmmodel.css' +import {getUploadActionUrltwo,appendFileSizeToUploadFileAll} from 'educoder'; +import moment from 'moment'; + +const confirm = Modal.confirm; + +class TPMDataset extends Component { + constructor(props) { + super(props) + this.state = { + value: undefined, + columns: [ + { + title: '文件', + dataIndex: 'title', + key: 'title', + align: 'left', + className: " font-14 wenjiantit", + width: '220px', + render: (text, record) => ( +
+ {record.title} +
+ ) + }, + { + title: '最后修改时间', + dataIndex: 'timedata', + key: 'timedata', + align: 'center', + className: "edu-txt-center font-14 zuihoushijian", + width: '150px', + render: (text, record) => ( +
+ {record.timedata} +
+ ) + }, + { + title: '最后修改人', + dataIndex: 'author', + key: 'author', + align: 'center', + className: "edu-txt-center font-14 ", + render: (text, record) => ( +
+ {record.author} +
+ ) + }, + { + title: '文件大小', + dataIndex: 'filesize', + key: 'filesize', + align: 'center', + className: "edu-txt-center font-14 ", + render: (text, record) => ( +
+ {record.filesize} +
+ ) + }, + ], + page: 1, + limit: 10, + selectedRowKeys: [], + mylistansum:30, + collaboratorList:[], + fileList:[], + fileListimgs:[], + file:null, + datalist:[], + data_sets_count:0, + selectedRowKeysdata:[], + loadingstate:false, + checked: false, + } + } + + componentDidMount() { + this.setState({ + loadingstate:true, + }) + this.getdatas() + + } + + mysonChange = (e) => { + // console.log(`全选checked = ${e.target.checked}`); + if (e.target.checked === true) { + let mydata=[]; + let datas=[]; + for(let i=0;i { + let id=this.props.match.params.shixunId; + + let collaborators=`/shixuns/${id}/get_data_sets.json`; + axios.get(collaborators,{params:{ + page:1, + limit:10, + }}).then((response)=> { + if(response.status===200){ + if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { + + }else{ + let datalists=[]; + for(let i=0;i { + this.setState({ + loadingstate:false, + }) + }, 500) + + }).catch((error)=>{ + setTimeout(() => { + this.setState({ + loadingstate:false, + }) + }, 500) + console.log(error) + }); + + } + + getdatastwo = (page,limit) => { + let id=this.props.match.params.shixunId; + + let collaborators=`/shixuns/${id}/jupyter_data_sets.json`; + axios.get(collaborators,{params:{ + page:page, + limit:limit, + }}).then((response)=> { + if(response.status===200){ + if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { + + }else{ + let datalists=[]; + for(let i=0;i { + this.setState({ + loadingstate:false, + }) + }, 500) + }).catch((error)=>{ + setTimeout(() => { + this.setState({ + loadingstate:false, + }) + }, 500) + console.log(error) + }); + + } + + + getdatasthree = (page,limit) => { + let id=this.props.match.params.shixunId; + + let collaborators=`/shixuns/${id}/jupyter_data_sets.json`; + axios.get(collaborators,{params:{ + page:page, + limit:limit, + }}).then((response)=> { + if(response.status===200){ + if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { + + }else{ + + + } + + } + + }).catch((error)=>{ + + }); + + } + + paginationonChanges = (pageNumber) => { + // //console.log('Page: '); + this.setState({ + page: pageNumber, + loadingstate:true, + }) + + this.getdatastwo(pageNumber,10); + } + + onSelectChange = (selectedRowKeys, selectedRows) => { + console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows); + this.setState( + { + selectedRowKeys + } + ); + let mydata=[]; + for(let i=0;i { + let className = 'light-row'; + if (index % 2 === 1) className = 'dark-row'; + return className; + } + handleChange = (info) => { + if(info.file.status == "done" || info.file.status == "uploading" || info.file.status === 'removed'){ + let fileList = info.fileList; + this.setState({ + fileList: appendFileSizeToUploadFileAll(fileList), + }); + if(info.file.status === 'done'){ + //done 成功就会调用这个方法 + this.getdatas(); + } + } + } + + + onAttachmentRemove = (file) => { + // debugger + 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; + } + + } + + deleteRemovedata(){ + + if(this.state.selectedRowKeysdata===undefined || this.state.selectedRowKeysdata===null ||this.state.selectedRowKeysdata.length===0){ + + this.props.showNotification(`请选择要删除的文件`); + + return + } + let id=this.props.match.params.shixunId; + + confirm({ + title: '确定要删除文件吗?', + okText: '确定', + cancelText: '取消', + // content: 'Some descriptions', + onOk: () => { + const url = `/shixuns/${id}/destroy_data_sets.json`; + axios.delete(url, + { params: { + id:this.state.selectedRowKeysdata, + }} + ) + .then((response) => { + if (response.data) { + const { status } = response.data; + if (status == 0) { + this.props.showNotification(`删除成功`); + + this.getdatas() + } + } + }) + .catch(function (error) { + console.log(error); + }); + }, + onCancel() { + console.log('Cancel'); + }, + }); + + } + deleteAttachment = (file) => { + // console.log(file); + let id=file.response ==undefined ? file.id : file.response.id + const url = `/attachements/destroy_files.json` + axios.delete(url, { + id:[id], + }) + .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() { + const {tpmLoading, shixun, user, match} = this.props; + const {columns, page, limit, selectedRowKeys,mylistansum,fileList,datalist,data_sets_count,loadingstate} = this.state; + const rowSelection = { + selectedRowKeys, + onChange: this.onSelectChange, + }; + // getCheckboxProps: record => ({ + // disabled: record.name === 'Disabled User', // Column configuration not to be checked + // name: record.name, + // }), + let id=this.props.match.params.shixunId; + const uploadProps = { + width: 600, + fileList, + data:{ + attachtype: 2, + container_id:this.props.match.params.shixunId, + container_type: "Shixun", + }, + multiple: false, + //multiple 是否支持多选 查重的时候不能多选 不然弹许多框出来 + // https://github.com/ant-design/ant-design/issues/15505 + // showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。 + // showUploadList: false, + action: `${getUploadActionUrltwo(id)}`, + showUploadList:false, + onChange: this.handleChange, + onRemove: this.onAttachmentRemove, + beforeUpload: (file) => { + //上传前的操作 + console.log('beforeUpload', file.name); + const isLt150M = file.size / 1024 / 1024 < 150; + if (!isLt150M) { + this.props.showNotification('文件大小必须小于150MB!'); + } + return isLt150M; + }, + }; + return ( + +
+ +
+ + + +
+
+ +
+ 全选 +
+ +
+ +

+ + 上传文件

+ { + data_sets_count>0? +
0 ? "deletebutomtextcode intermediatecenter mr21" : "deletebutom intermediatecenter mr21"} onClick={()=>this.deleteRemovedata()}> +

删除

+ :"" + } +
+
+
+ + {data_sets_count===0? +
+ + + + : +
+
+ + } + + { + data_sets_count>=11? +
+ +
+ :"" + } + + { data_sets_count===0? + :"" + } + + + + + + +
+ +
+ + + ); + } +} + +export default TPMDataset; diff --git a/public/react/src/modules/tpm/TPMFork_listContainer.js b/public/react/src/modules/tpm/TPMFork_listContainer.js index bbd55c2ee..12c579863 100644 --- a/public/react/src/modules/tpm/TPMFork_listContainer.js +++ b/public/react/src/modules/tpm/TPMFork_listContainer.js @@ -1,50 +1,50 @@ -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; +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/modules/tpm/TPMForklist.js b/public/react/src/modules/tpm/TPMForklist.js index 251821209..ffd31d2b7 100644 --- a/public/react/src/modules/tpm/TPMForklist.js +++ b/public/react/src/modules/tpm/TPMForklist.js @@ -1,63 +1,64 @@ -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; +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/modules/tpm/TPMIndex.css b/public/react/src/modules/tpm/TPMIndex.css index e30631e1d..e86bc2d68 100644 --- a/public/react/src/modules/tpm/TPMIndex.css +++ b/public/react/src/modules/tpm/TPMIndex.css @@ -114,7 +114,7 @@ body>.-task-title { /*-------------------个人主页:右侧提示区域--------------------------*/ -.-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 !important;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;} diff --git a/public/react/src/modules/tpm/TPMIndex.js b/public/react/src/modules/tpm/TPMIndex.js index 7fd389197..9890c2dcf 100644 --- a/public/react/src/modules/tpm/TPMIndex.js +++ b/public/react/src/modules/tpm/TPMIndex.js @@ -22,13 +22,16 @@ import TPMRepositoryCommits from './shixunchild/Repository/TPMRepositoryCommits' import TPMsettings from './TPMsettings/TPMsettings'; +//import TPMsettings from './TPMsettings/oldTPMsettings'; + 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 TPMDataset from './TPMDataset'; import '../page/tpiPage.css' +import TPMNav from "./component/TPMNav"; const $ = window.$ //任务 @@ -142,13 +145,15 @@ class TPMIndex extends Component { identity:undefined, TPMRightSectionData:undefined, PropaedeuticsList: undefined, + tpmindexjupyterbool:false, + is_jupyter:false, } } componentDidMount = () => { let id = this.props.match.params.shixunId; - console.log('props', this.props); + // console.log('props', this.props); // let collaborators = `/shixuns/` + id + `/propaedeutics.json`; // // axios.get(collaborators).then((response) => { @@ -192,7 +197,7 @@ class TPMIndex extends Component { propaedeutics:response.data.propaedeutics, status: response.data.shixun_status, secret_repository: response.data.secret_repository, - + is_jupyter:response.data.is_jupyter=== undefined||response.data.is_jupyter===null?false:response.data.is_jupyter, }); } }).catch((error) => { @@ -204,7 +209,8 @@ class TPMIndex extends Component { power: undefined, identity: undefined, status: undefined, - propaedeutics:undefined + propaedeutics:undefined, + is_jupyter:false, }); }); @@ -259,8 +265,8 @@ class TPMIndex extends Component { axios.interceptors.request.eject(this.tpmContentResponseInterceptor); this.tpmContentResponseInterceptor = null; } - - + + setLoadingContent = (isLoadingContent) => { this.setState({ loadingContent: isLoadingContent }) } @@ -270,31 +276,42 @@ class TPMIndex extends Component { // } render() { + let url = window.location.href; let flag = url.indexOf("add_file")>-1; + // console.log(this.state.is_jupyter); return (
+ {/*头部*/} { - !flag && + !flag && } - + {/*筛选*/} + {/*{*/} + {/* tpmindexjupyterbool===false?*/} + + {/* :""*/} + {/*}*/} + {/* */} + - + ( () }> ( () }> - + {/*任务*/} ( () }> @@ -304,33 +321,33 @@ class TPMIndex extends Component { }> ( () }> ( () }> - + {/* */} ( () }> ( () }> - + {/* */} ( (this.initForumState(data)} setSearchValue={this.setSearchValue} setHotLabelIndex={this.setHotLabelIndex} @@ -342,14 +359,19 @@ class TPMIndex extends Component { (props) => () }> - + {/*实训项目条目塞选*/} ( () }> + {/*合作者*/} + () + }> ( () }> @@ -400,7 +422,7 @@ class TPMIndex extends Component { }> ( () }> diff --git a/public/react/src/modules/tpm/TPMIndexHOC.js b/public/react/src/modules/tpm/TPMIndexHOC.js index 774587865..cc8e28bf7 100644 --- a/public/react/src/modules/tpm/TPMIndexHOC.js +++ b/public/react/src/modules/tpm/TPMIndexHOC.js @@ -23,7 +23,7 @@ const versionNum = '0001'; // let _url_origin = getUrl() let _url_origin=''; if(window.location.port === "3007"){ - _url_origin="http://pre-newweb.educoder.net"; + _url_origin="https://test-newweb.educoder.net"; } // let _url_origin=`https://www.educoder.net`; @@ -31,7 +31,7 @@ if(window.location.port === "3007"){ if (!window['indexHOCLoaded']) { window.indexHOCLoaded = true; //解决首屏加载问题 - + // $('head').append($('') // .attr('href', `${_url_origin}/stylesheets/educoder/antd.min.css?1525440977`)); $('head').append($('') @@ -51,7 +51,7 @@ if (!window['indexHOCLoaded']) { // 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( $('') @@ -60,7 +60,7 @@ if (!window['indexHOCLoaded']) { $("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其他页面一致 @@ -146,7 +146,7 @@ export function TPMIndexHOC(WrappedComponent) { componentWillUnmount() { window.removeEventListener('keyup', this.keyupListener) } - + componentDidMount() { // console.log("TPMIndexHOC========"); // console.log(this.props); @@ -216,7 +216,7 @@ export function TPMIndexHOC(WrappedComponent) { this.getAppdata(); } /** - 课堂权限相关方法,暂时写这里了 ----------------------------------------START + 课堂权限相关方法,暂时写这里了 ----------------------------------------START ADMIN = 0 # 超级管理员 CREATOR = 1 # 课程创建者 PROFESSOR = 2 # 课程老师 @@ -560,7 +560,7 @@ export function TPMIndexHOC(WrappedComponent) { checkIfProfessionalCertification = () => { return this.state.current_user && this.state.current_user.professional_certification } - + ShowOnlinePdf = (url) => { return axios({ @@ -642,14 +642,14 @@ export function TPMIndexHOC(WrappedComponent) { 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, @@ -660,7 +660,7 @@ export function TPMIndexHOC(WrappedComponent) { 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), @@ -671,7 +671,7 @@ export function TPMIndexHOC(WrappedComponent) { yslslowCheckresults:this.yslslowCheckresults, yslslowCheckresultsNo:this.yslslowCheckresultsNo, MdifHasAnchorJustScorll:this.MdifHasAnchorJustScorll - + }; // console.log("this.props.mygetHelmetapi"); // console.log(this.props.mygetHelmetapi); @@ -742,7 +742,7 @@ export function TPMIndexHOC(WrappedComponent) { } ` } - + - +
); } } -} \ No newline at end of file +} diff --git a/public/react/src/modules/tpm/TPMPropaedeutics.js b/public/react/src/modules/tpm/TPMPropaedeutics.js index 88a05fde7..3a6a92bb0 100644 --- a/public/react/src/modules/tpm/TPMPropaedeutics.js +++ b/public/react/src/modules/tpm/TPMPropaedeutics.js @@ -1,74 +1,75 @@ -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; +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/modules/tpm/TPMPropaedeuticsComponent.js b/public/react/src/modules/tpm/TPMPropaedeuticsComponent.js index 7c3eadb89..994bec874 100644 --- a/public/react/src/modules/tpm/TPMPropaedeuticsComponent.js +++ b/public/react/src/modules/tpm/TPMPropaedeuticsComponent.js @@ -1,39 +1,40 @@ -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 ; +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/modules/tpm/TPMRanking_list.js b/public/react/src/modules/tpm/TPMRanking_list.js index 7171692a7..b7498b876 100644 --- a/public/react/src/modules/tpm/TPMRanking_list.js +++ b/public/react/src/modules/tpm/TPMRanking_list.js @@ -1,59 +1,60 @@ -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; +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/modules/tpm/TPMRanking_listContainer.js b/public/react/src/modules/tpm/TPMRanking_listContainer.js index 98841b1ab..f07401abe 100644 --- a/public/react/src/modules/tpm/TPMRanking_listContainer.js +++ b/public/react/src/modules/tpm/TPMRanking_listContainer.js @@ -1,37 +1,40 @@ -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; +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'; +import TPMNav from "./component/TPMNav"; + +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/modules/tpm/TPMRepository.js b/public/react/src/modules/tpm/TPMRepository.js index 0f8e31258..5db8100bc 100644 --- a/public/react/src/modules/tpm/TPMRepository.js +++ b/public/react/src/modules/tpm/TPMRepository.js @@ -1,58 +1,59 @@ -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; +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/modules/tpm/TPMRepositoryComponent.js b/public/react/src/modules/tpm/TPMRepositoryComponent.js index 027f3f705..af3930b74 100644 --- a/public/react/src/modules/tpm/TPMRepositoryComponent.js +++ b/public/react/src/modules/tpm/TPMRepositoryComponent.js @@ -1,229 +1,230 @@ -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 ; +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/modules/tpm/TPMShixunDiscuss.js b/public/react/src/modules/tpm/TPMShixunDiscuss.js index 9350060cc..be084ebff 100644 --- a/public/react/src/modules/tpm/TPMShixunDiscuss.js +++ b/public/react/src/modules/tpm/TPMShixunDiscuss.js @@ -1,72 +1,73 @@ -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 ); +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/modules/tpm/TPMShixunDiscussContainer.js b/public/react/src/modules/tpm/TPMShixunDiscussContainer.js index 535840772..072cb5906 100644 --- a/public/react/src/modules/tpm/TPMShixunDiscussContainer.js +++ b/public/react/src/modules/tpm/TPMShixunDiscussContainer.js @@ -1,45 +1,45 @@ -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; +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/modules/tpm/TPMsettings/Configuration.js b/public/react/src/modules/tpm/TPMsettings/Configuration.js new file mode 100644 index 000000000..9bf457993 --- /dev/null +++ b/public/react/src/modules/tpm/TPMsettings/Configuration.js @@ -0,0 +1,385 @@ +import React, {Component} from 'react'; + +//MonacoDiffEditor 对比模式 +import { + Badge, + Select, + Radio, + Checkbox, + Modal, + DatePicker, + Button, +} from 'antd'; + +import locale from 'antd/lib/date-picker/locale/zh_CN'; + +import moment from 'moment'; + +import axios from 'axios'; + +import './css/TPMsettings.css'; + +import {handleDateStrings} from "./oldTPMsettings"; + +import Bottomsubmit from "../../modals/Bottomsubmit"; + +const $ = window.$; + +let timeout; + +let currentValue; + +const Option = Select.Option; + +const RadioGroup = Radio.Group; + +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 Shixuninformation extends Component { + constructor(props) { + super(props) + this.state = { + can_copy: false, + use_scope: 0, + opening_time: null, + opentime: false, + oldscope_partment: [], + scope_partment: [], + loading: false + } + } + + componentDidMount() { + if (this.props.data) { + if (this.props.data.shixun) { + this.setState({ + can_copy: this.props.data && this.props.data.shixun.can_copy === undefined ? false : this.props.data && this.props.data.shixun.can_copy, + use_scope: this.props.data && this.props.data.shixun.use_scope, + opening_time: this.props.data && this.props.data.shixun.opening_time, + opentime: !this.props.data && this.props.data.shixun.opening_time ? false : true, + oldscope_partment: this.props.data && this.props.data.shixun.scope_partment, + }) + } + } + 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) + }); + } + + + componentDidUpdate(prevProps, prevState) { + if (prevProps.data != this.props.data) { + if (this.props.data) { + if (this.props.data.shixun) { + this.setState({ + can_copy: this.props.data && this.props.data.shixun.can_copy === undefined ? false : this.props.data && this.props.data.shixun.can_copy, + use_scope: this.props.data && this.props.data.shixun.use_scope, + opening_time: this.props.data && this.props.data.shixun.opening_time, + opentime: !this.props.data && this.props.data.shixun.opening_time ? false : true, + oldscope_partment: this.props.data && this.props.data.shixun.scope_partment, + }) + } + } + } + } + + onChangeTimePicker = (value, dateString) => { + this.setState({ + opening_time: dateString === "" ? "" : handleDateStrings(dateString) + }) + } + + onSubmits = () => { + this.setState({ + loading: true + }) + let {can_copy, use_scope, scope_partment, opening_time} = this.state; + + let id = this.props.match.params.shixunId; + let url = `/shixuns/${id}/update_permission_setting.json`; + axios.post(url, + { + scope_partment: scope_partment, + shixun: { + can_copy: can_copy, + use_scope: use_scope, + opening_time: opening_time + } + } + ).then((response) => { + if (response.data.status === -1) { + + } else { + this.props.getdatas("3") + this.props.showNotification("权限配置保存成功!") + this.setState({ + loading: false + }) + } + }).catch((error) => { + this.setState({ + loading: false + }) + + }) + + } + CheckboxonChange = (e) => { + this.setState({ + can_copy: e.target.checked + }) + } + + SelectOpenpublic = (e) => { + this.setState({ + use_scope: e.target.value + }); + } + + shixunScopeInput = (e) => { + let {scope_partment, oldscope_partment} = this.state; + let datalist = scope_partment; + if (datalist === undefined) { + datalist = [] + } + + datalist.push(e) + + let scopetype = false; + + scope_partment.map((item, key) => { + if (item === e) { + scopetype = true + } + }) + + oldscope_partment.map((item, key) => { + if (item === e) { + scopetype = true + } + }) + + if (scopetype === false) { + this.setState({ + scope_partment: datalist + }); + } else { + this.props.showNotification("请勿指定相同的单位") + } + + } + + 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})); + } + + deleteScopeInput = (key) => { + let {scope_partment} = this.state; + let datalist = scope_partment; + datalist.splice(key, 1); + this.setState({ + scope_partment: datalist + }); + } + + setopentime = (e) => { + this.setState({ + opentime: e.target.checked + }) + } + + render() { + + let options; + if (this.state.departmentslist != undefined) { + options = this.state.departmentslist.map((d, k) => { + return ( + + ) + }) + } + + const dateFormat = 'YYYY-MM-DD HH:mm'; + + return ( +
+
+
+ 复制: + + + + +
+ +
+ {this.props.data && this.props.data.shixun.use_scope === 0 && this.props.data && this.props.data.shixun.status === 2 ? "" : +
+ 公开程度: + + + 对所有单位公开 (实训发布后,所有用户可见) + 对指定单位公开 (实训发布后,仅对下方指定单位的用户可见) + + +
+
+
+
+
+ +
+ (请通过搜索并选中单位名称进行添加) +
+
+ +
+
+ { + this.state.oldscope_partment.map((item, key) => { + return ( +
  • + +
  • + ) + }) + } + { + this.state.scope_partment === undefined ? "" : this.state.scope_partment.map((item, key) => { + + return ( +
  • + this.deleteScopeInput(key)}> + + +
  • + ) + }) + } +
    + +
    + + + + 请选择需要公开的单位 + +
    +
    + + +
    +
    } + +
    + 开启时间: + + + +
    + {this.state.opentime === false ? "" :
    + +
    } +
    + +
    + + +
    +
    + {this.props.identity < 5 ? + : ""} +
    + ); + } +} + + diff --git a/public/react/src/modules/tpm/TPMsettings/LearningSettings.js b/public/react/src/modules/tpm/TPMsettings/LearningSettings.js new file mode 100644 index 000000000..dfcdda5ba --- /dev/null +++ b/public/react/src/modules/tpm/TPMsettings/LearningSettings.js @@ -0,0 +1,348 @@ +import React, {Component} from 'react'; + +import { + Radio, + Checkbox, +} from 'antd'; + +import axios from 'axios'; + +import './css/TPMsettings.css'; + +import Bottomsubmit from "../../modals/Bottomsubmit"; + +const RadioGroup = Radio.Group; + + +export default class Shixuninformation extends Component { + constructor(props) { + super(props) + this.state = { + vnc: false, + hide_code: false, + is_secret_repository: false, + code_hidden: false, + forbid_copy: false, + test_set_permission: true, + task_pass: true, + websshshow: false, + multi_webssh: false, + opensshRadio: null, + loading: false + } + } + + + componentDidMount() { + if (this.props.data ) { + if (this.props.data.shixun) { + this.setState({ + vnc: this.props.data && this.props.data.shixun.vnc, + code_hidden: this.props.data && this.props.data.shixun.code_hidden, + forbid_copy: this.props.data && this.props.data.shixun.forbid_copy, + hide_code: this.props.data && this.props.data.shixun.hide_code, + task_pass: this.props.data && this.props.data.shixun.task_pass, + test_set_permission: this.props.data && this.props.data.shixun.test_set_permission, + is_secret_repository: this.props.data && this.props.data.shixun.is_secret_repository, + websshshow: this.props.data && this.props.data.shixun.webssh === 0 ? false : true, + multi_webssh: this.props.data && this.props.data.shixun.multi_webssh, + opensshRadio: this.props.data && this.props.data.shixun.webssh === 0 ? null : this.props.data && this.props.data.shixun.webssh, + }) + + // if(this.props.data && this.props.data.shixun.status===0){ + // this.setState({ + // task_pass:true + // }) + // } + + } + } + + } + + componentDidUpdate(prevProps, prevState) { + if (prevProps.data != this.props.data) { + if (this.props.data) { + if (this.props.data.shixun) { + this.setState({ + vnc: this.props.data && this.props.data.shixun.vnc, + code_hidden: this.props.data && this.props.data.shixun.code_hidden, + forbid_copy: this.props.data && this.props.data.shixun.forbid_copy, + hide_code: this.props.data && this.props.data.shixun.hide_code, + task_pass: this.props.data && this.props.data.shixun.task_pass, + test_set_permission: this.props.data && this.props.data.shixun.test_set_permission, + is_secret_repository: this.props.data && this.props.data.shixun.is_secret_repository, + websshshow: this.props.data && this.props.data.shixun.webssh === 0 ? false : true, + multi_webssh: this.props.data && this.props.data.shixun.multi_webssh, + opensshRadio: this.props.data && this.props.data.shixun.webssh === 0 ? null : this.props.data && this.props.data.shixun.webssh, + }) + + // if(this.props.data && this.props.data.shixun.status===0){ + // this.setState({ + // task_pass:true + // }) + // } + + } + } + } + } + + + onSubmits = () => { + this.setState({ + loading: true + }) + let id = this.props.match.params.shixunId; + let url = `/shixuns/${id}/update_permission_setting.json`; + axios.post(url, + { + shixun: { + code_hidden: this.state.code_hidden, + forbid_copy: this.state.forbid_copy, + hide_code: this.state.hide_code, + multi_webssh: this.state.multi_webssh, + task_pass: this.state.task_pass, + test_set_permission: this.state.test_set_permission, + vnc: this.state.vnc, + webssh: this.state.websshshow === false ? 0 : this.state.opensshRadio, + }, + is_secret_repository: this.state.is_secret_repository + } + ).then((response) => { + if (response.data.status === -1) { + + } else { + this.props.getdatas() + this.props.showNotification("学习页面设置保存成功!") + this.setState({ + loading: false + }) + } + }).catch((error) => { + this.setState({ + loading: false + }) + }) + } + + Checkvnc = () => { + console.log(this.state.vnc) + if (this.state.vnc === false) { + this.setState({ + hide_code: false, + is_secret_repository: false, + code_hidden: false, + forbid_copy: false, + multi_webssh: false, + websshshow: false, + }) + } + this.setState({ + vnc: !this.state.vnc + }) + } + + Checkhide_code = () => { + if (this.state.hide_code === false) { + this.setState({ + is_secret_repository: false + }) + } + this.setState({ + hide_code: !this.state.hide_code + }) + } + + Checkis_secret_repository = () => { + this.setState({ + is_secret_repository: !this.state.is_secret_repository + }) + } + + Checkcode_hidden = () => { + this.setState({ + code_hidden: !this.state.code_hidden + }) + } + + Checkforbid_copy = () => { + this.setState({ + forbid_copy: !this.state.forbid_copy + }) + } + + Checktask_pass = () => { + this.setState({ + task_pass: !this.state.task_pass + }) + } + + Checktest_set_permission = () => { + this.setState({ + test_set_permission: !this.state.test_set_permission + }) + } + + Checkwebsshshow = () => { + if (this.state.websshshow === false) { + this.setState({ + vnc: false, + opensshRadio: 1 + }) + } else { + this.setState({ + multi_webssh: false, + opensshRadio: null + }) + } + this.setState({ + websshshow: !this.state.websshshow + }) + + } + + Checkmulti_webssh = () => { + this.setState({ + multi_webssh: !this.state.multi_webssh + }) + } + + opensshRadio = (e) => { + if (e.target.value === 1) { + this.setState({ + multi_webssh: false + }) + } else { + this.setState({ + multi_webssh: true + }) + } + this.setState({ + opensshRadio: e.target.value + }); + } + + render() { + // console.log(this.props) + return ( +
    +
    + + {this.state.websshshow === true ? "" :
    + 开启图形化界面: + + + + +
    } + + {this.state.vnc === true ? "" :
    + 命令行: + + + + +
    } + + + {this.state.vnc === true ? "" : this.state.websshshow === true ?
    + + + 命令行练习窗口 (选中则给学员提供用于练习操作的命令行,命令行的操作不会对学生的实验环境造成影响) + 命令行评测窗口 (选中则给学员提供用于评测操作的命令行,命令行的操作可以对学生的实验环境产生影响) + + + + {this.state.opensshRadio === 2 ? +
    + + + + +
    +
    : ""} +
    : ""} + + {this.state.vnc === true ? "" :
    + 隐藏代码窗口: + + + + +
    } + + {this.state.vnc === true || this.state.hide_code === true ? "" :
    + 公开版本库: + + + + +
    } + + {this.state.vnc === true ? "" :
    + 隐藏代码目录: + + + + +
    } + + {this.state.vnc === true ? "" :
    + 禁用复制粘贴: + + + + +
    } + +
    + 跳关: + + + + +
    + +
    + 测试集解锁: + + + + +
    + + +
    + + {this.props.identity < 5 ? + : ""} +
    + ); + } +} + + diff --git a/public/react/src/modules/tpm/TPMsettings/Shixuninformation.js b/public/react/src/modules/tpm/TPMsettings/Shixuninformation.js new file mode 100644 index 000000000..2fdd30146 --- /dev/null +++ b/public/react/src/modules/tpm/TPMsettings/Shixuninformation.js @@ -0,0 +1,1290 @@ +import React, {Component} from 'react'; + +import MonacoEditor from 'react-monaco-editor'; + +import { + Input, + Select, + Checkbox, + Modal, + Icon, + Upload, + Button, + Tooltip, + Form +} from 'antd'; + +import axios from 'axios'; + +import TPMMDEditor from "../challengesnew/TPMMDEditor"; + +import Bottomsubmit from "../../modals/Bottomsubmit"; + +import {getUploadActionUrl} from 'educoder'; + +import './css/TPMsettings.css'; + +import '../newshixuns/css/Newshixuns.css'; + +const Option = Select.Option; + +class Shixuninformation extends Component { + constructor(props) { + super(props) + this.contentMdRef = React.createRef(); + this.state = { + NAME_COUNT: 60, + shixunmemoMDvalue: "", + testscripttiptype: false, + shixunName: '', + trainee: undefined, + choice_small_type: [], + simichecked: false, + Executivetyoe: false, + Executiveordervalue: "", + Compilecommandvalue: "", + shixun_service_configs: undefined, + fileList:[], + loading:false, + } + } + + componentDidMount() { + + } + + componentDidUpdate(prevProps, prevState) { + if (prevProps.data != this.props.data) { + + if (this.props.data ) { + if (this.props.data.shixun){ + this.setState({ + shixunName: this.props.data && this.props.data.shixun.name, + trainee: this.props.data && this.props.data.shixun.trainee, + choice_main_type: this.props.data && this.props.data.shixun.choice_main_type, + choice_small_type: this.props.data && this.props.data.shixun.choice_small_type, + choice_standard_scripts: this.props.data && this.props.data.shixun.choice_standard_scripts, + shixunmemoMDvalue: this.props.data && this.props.data.shixun.evaluate_script, + simichecked: this.props.data && this.props.data.shixun.is_secret_repository, + shixun_service_configs: this.props.data && this.props.data.shixun.shixun_service_configs, + standard_scripts: this.props.data && this.props.data.shixun.standard_scripts, + shixun_service_configlist: this.props.data && this.props.data.shixun.shixun_service_configs, + }) + + if (this.props.data && this.props.data.shixun.choice_standard_scripts === null) { + this.setState({ + choice_standard_scripts: {id: this.props.data && this.props.data.shixun.standard_scripts[0].id, value: ""}, + choice_standard_scriptssum: this.props.data && this.props.data.shixun.standard_scripts[0].id + }) + this.props.form.setFieldsValue({ + selectscripts: this.props.data && this.props.data.shixun.standard_scripts[0].id + }) + this.get_mirror_script(this.props.data && this.props.data.shixun.standard_scripts[0].id) + } else { + this.props.form.setFieldsValue({ + selectscripts: this.props.data && this.props.data.shixun.choice_standard_scripts + }) + } + + let newlist = "" + this.props.data && this.props.data.shixun.choice_small_type.map((item, key) => { + this.props.data && this.props.data.shixun.small_type.map((i, k) => { + if (item === i.id) { + newlist = newlist + `${i.description}` + } + }) + }) + this.setState({ + subvalues: newlist + }) + + this.props.data && this.props.data.shixun.main_type.map((item, key) => { + if (item.id === this.props.data && this.props.data.shixun.choice_main_type) { + this.setState({ + mainvalues: item.description, + }) + } + }) + + this.props.form.setFieldsValue({ + name: this.props.data && this.props.data.shixun.name, + trainee: this.props.data && this.props.data.shixun.trainee, + selectleft: this.props.data && this.props.data.shixun.choice_main_type, + selectright: this.props.data && this.props.data.shixun.choice_small_type, + }) + this.contentMdRef.current.setValue(this.props.data && this.props.data.shixun.description); + } + } + } + } + + getshixunmemoMDvalue = (value, e) => { + + this.setState({ + shixunmemoMDvalue: value + }) + } + + testscripttip = (val) => { + if (val === 0) { + this.setState({ + testscripttiptype: true + }) + } else if (val === 1) { + this.setState({ + testscripttiptype: false + }) + } + } + + 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: [] + }) + } + } + + + sendsure_apply = () => { + let {language, runtime, run_method} = this.state; + + if (!language || language === "") { + // this.props.showNotification(`请填写该镜像是基于什么语言`); + this.setState({ + languagewritetype: true + }) + return + } + if (!runtime || runtime === "") { + // this.props.showNotification(`请填写该镜像是基于什么语言系统环境`); + this.setState({ + systemenvironmenttype: true + }) + return; + + } + if (!run_method || run_method === "") { + // 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) { + this.setState({ + attachmentidstype: true + }) + return; + } + + 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) => { + + try { + if (response.data) { + + if (this.state.file !== undefined) { + this.setState({ + file: undefined, + deleteisnot: true, + language: "", + runtime: "", + run_method: "", + fileList: [] + }) + } else { + this.setState({ + file: undefined, + deleteisnot: true, + language: "", + runtime: "", + run_method: "", + fileList: [] + }) + } + this.props.showNotification("提交成功") + + this.sendhideModaly() + + } + } catch (e) { + + } + + }) + + } + + + setlanguage = (e) => { + this.setState({ + language: e.target.value + }) + if (e.target.value) { + this.setState({ + languagewritetype: false + }) + } + } + setruntime = (e) => { + this.setState({ + runtime: e.target.value + }) + if (e.target.value) { + this.setState({ + systemenvironmenttype: false + }) + } + + } + + setrun_method = (e) => { + this.setState({ + run_method: e.target.value + }) + if (e.target.value) { + this.setState({ + testcoderunmodetype: false + }) + } + } + + + // 附件相关 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) { + 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 + }; + }); + } + } + }) + .catch(function (error) { + console.log(error); + }); + } + + + + bigClass = (value,e) => { + this.setState({ + mainvalues:e.props.name + }) + let list = [] + list.push(this.state.choice_main_type) + this.state.choice_small_type.map((item, key) => { + list.push(item) + }) + + + let newshixun_service_configs = this.state.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) + } + }) + }) + + + + this.props.data.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 + } + } + ) + + + + + this.props.form.setFieldsValue({ + selectleft: value, + }) + + let url = `/shixuns/get_mirror_script.json?mirror_id=` + value; + axios.get(url).then((response) => { + if (response.status === 200) { + this.setState({ + choice_standard_scripts:{id: response.data[0].id, value: ""}, + choice_standard_scriptssum:response.data[0].id + }) + this.props.form.setFieldsValue({ + selectscripts:response.data[0].id + }) + this.get_mirror_script(response.data[0].id) + this.setState({ + choice_main_type: value, + standard_scripts: response.data, + shixun_service_configs: newshixun_service_configsagin, + shixun_service_configlist: newshixun_service_configsagin, + }) + + } + }).catch((error) => { + console.log(error) + }); + } + Deselectlittle = (value,e) => { + + 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, + }) + } + + showlittleClass=(value,e)=>{ + let newlist = "" + e.map((item, key) => { + if (item.props.name != "") { + newlist = newlist + `${item.props.name}` + } + }) + this.setState({ + subvalues: newlist + }) + + this.props.form.setFieldsValue({ + selectright: value, + }) + } + littleClass = (value,e) => { + let newshixun_service_configs = this.state.shixun_service_configs; + let newchoice_small_type = this.state.choice_small_type; + let list = [] + list.push(this.state.choice_main_type) + newchoice_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) + } + }) + }) + + this.props.data.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, + }) + } + + 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 = (ids) => { + let {choice_standard_scriptssum} = this.state; + let id = this.props.match.params.shixunId; + let url = `/shixuns/${id}/get_script_contents.json`; + axios.get((url),{params:{ + script_id:ids?ids:choice_standard_scriptssum + }}).then((response) => { + if (response.status === 200) { + // this.evaluate_scriptMD(response.data.content, "shixunmemoMD"); + if(ids==undefined){ + this.setState({ + standard_scriptsModal: false, + standard_scriptsModals: true, + }) + } + this.setState({ + shixunmemoMDvalue: response.data.content + }) + } + + }).catch((error) => { + console.log(error) + }) + } + + simionChange = (e) => { + this.setState({ + simichecked: e.target.checked + }) + } + + + showModal = () => { + this.setState({ + visibleTemplate: true, + }); + } + + handleCancelTemplate = (e) => { + this.setState({ + Executiveordervalue: "", + Compilecommandvalue: "", + visibleTemplate: false + }) + } + + hideModalTemplate = (e) => { + let id = this.props.match.params.shixunId; + let {Executiveordervalue, Compilecommandvalue} = this.state; + + if (Executiveordervalue === "" || Executiveordervalue === undefined) { + 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 + }) + } + + Executiveorder = (e) => { + this.setState({ + Executiveordervalue: e.target.value + }) + } + + Compilecommand = (e) => { + this.setState({ + Compilecommandvalue: e.target.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, + }) + + } + + onSubmits=()=>{ + this.setState({ + loading:true + }) + const mdContnet = this.contentMdRef.current.getValue().trim(); + let{choice_standard_scriptssum,choice_standard_scripts}=this.state; + this.props.form.validateFieldsAndScroll((err, values) => { + if (!err) { + console.log('Received values of form: ', values); + let url = `/shixuns/${this.props.match.params.shixunId}.json`; + let newshixun_service_configlist = this.state.shixun_service_configlist.map(v => { + let v1 = Object.assign({},v); + delete v1.name; + return v1 + }); + + let data={ + main_type:this.state.choice_main_type, + sub_type:this.state.choice_small_type, + is_secret_repository:this.state.simichecked, + shixun:{ + name: values.name, + trainee: this.state.trainee, + is_jupyter: this.props.shixunsDetails.is_jupyter, + mirror_script_id: this.props.shixunsDetails.is_jupyter===true?null:this.state.choice_standard_scriptssum===undefined?this.state.choice_standard_scripts:this.state.choice_standard_scriptssum, + }, + shixun_info: { + description: mdContnet, + evaluate_script: this.props.shixunsDetails.is_jupyter===true?"":this.state.shixunmemoMDvalue + }, + shixun_service_configs:newshixun_service_configlist + } + + axios.put(url, data).then((result) => { + if (result) { + if (result.data) { + this.props.getdatas("2") + if(result.data.shixun_identifier){ + this.props.showNotification("基本信息更新成功!") + this.setState({ + loading:false + }) + } + } + } + }).catch((error) => { + this.setState({ + loading:false + }) + }); + }else{ + this.setState({ + loading:false + }) + } + }); + this.setState({ + loading: false + }) + } + + Selectthestudent = (value) => { + this.setState({ + trainee: value + }) + } + render() { + + + // console.log(operateauthority) + const {getFieldDecorator} = this.props.form; + const { fileList, choice_standard_scripts, postapplyvisible, shixunmemoMDvalue} = this.state; + + // console.log("1222") + // console.log(choice_standard_scripts) + + 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`); + this.props.showNotification("文件大小必须小于50MB") + } + if (this.state.file !== undefined) { + this.setState({ + file: file + }) + } else { + this.setState({ + file: file + }) + } + return isLt150M; + }, + } + let operateauthority = this.props.identity === 1 ? true : this.props.identity < 5 && this.props.data&&this.props.data.shixun.status == 0 ? true : false; + return ( +
    +
    +
    + + {getFieldDecorator('name', { + rules: [{ + required: true, message: '请输入选题名称', + }, { + max: 60, message: '请输入名称,最大限制60个字符', + }, { + whitespace: true, message: '请勿输入空格' + }], + })( + + )} + + + + + + + + + {getFieldDecorator('trainee', { + rules: [{required: true, message: '请选择难易度'}], + })( +
    + + +
    + )} + (实训的难易程度) +
    + + +
    + + {getFieldDecorator('selectleft', { + rules: [{required: true, message: '请选择主类别'}], + })( +
    + + +
    + )} +
    +
    + + + +
    + {getFieldDecorator('selectright', { + rules: [{required: false, message: '请选择小类别'}], + })( +
    + +
    + )} + +
    + {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}`} +
    } +
    +
    +
    +
    + +
    +
    + 没有实验环境? + 申请新建 +
    + + + { this.props.shixunsDetails&&this.props.shixunsDetails.is_jupyter===true?"": + {getFieldDecorator('selectscripts', { + rules: [{required: true, message: '请选择评测脚本'}], + })( +
    + +
    + )} + + this.showModal()}>使用自定义脚本 + 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)}>知道了 +

    +
    +
    +
    } + + { this.props.shixunsDetails&&this.props.shixunsDetails.is_jupyter===true?"":
    +
    +
    +
    + +
    +
    +
    } + + + + { this.props.shixunsDetails&&this.props.shixunsDetails.is_jupyter===true?"": 私密版本库: + + {this.state.simichecked===false?"(若需要对学员隐藏部分版本库内容时,请选中;选中保存后表示启用私密版本库,请将需要对学员隐藏的文件存储在私密版本库)":"已创建的私密版本库及其内容,将在“保存”时被删除"} + } + + {this.props.identity < 3 ?
    +

    服务配置

    + {this.state.shixun_service_configs && this.state.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="请输入类别名称"/> +
    + +
    +
    +
    +
    + ) + + })} +
    : ""} + + + {postapplyvisible === true ? : ""} + + {/**/} + {/*
    */} + {/*

    已创建的私密版本库及其内容,将在“保存”时被删除

    */} + {/*

    是否确认取消选择?

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

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

    +

    是否确认执行覆盖操作

    +
    + + +
    + + + +

    评测脚本生成成功!

    + +
    + + +
    +
  • + + +

    执行命令不能为空

    +
  • + +
  • + + +
  • +
    +
    + + +
    +
  • + + +
  • +
    {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..2b389245b 100644 --- a/public/react/src/modules/tpm/TPMsettings/TPMsettings.js +++ b/public/react/src/modules/tpm/TPMsettings/TPMsettings.js @@ -1,2435 +1,232 @@ -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){ - 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) - } - }) - }) - + if(response.data){ + if (response.data.shixun&&response.data.shixun.scope_partment.length > 0) { + this.setState({ + scopetype: true + }) + } + } - 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, + data: response.data }) } - }).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 + if(key==="3"&&this.props.shixunsDetails&&this.props.shixunsDetails.is_jupyter === true){ + this.props.history.replace(`/shixuns/${this.props.match.params.shixunId}/challenges`); }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){ + if(key){ this.setState({ - scope_partmenttype: true + activeKeys:key }) - $('html').animate({ - scrollTop: 2500 - }, 1000); - this.props.showSnackbar("公开程度,指定单位为空"); - return + }else{ + this.props.history.replace(`/shixuns/${this.props.match.params.shixunId}/challenges`); } - } - // 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; + operateshixuns = (value) => { + this.setState({ + operateshixunstype: true, + delType: value }) - - } - 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})); + hideoperateshixuns = () => { + this.setState({ + operateshixunstype: false + }) } - - - shixunsclose = () => { + shixunsdel = () => { let id = this.props.match.params.shixunId; - let cul = `/shixuns/` + id + `/close.json`; - axios.post(cul).then((response) => { - if(response.data.status===1){ + 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/" + id + "/challenges"; + // window.location.href = "/shixuns"; + this.props.history.replace( "/shixuns/"); } }).catch((error) => { console.log(error) }) } - shixunsdel= () => { + shixunsclose = () => { let id = this.props.match.params.shixunId; - let cul = `/shixuns/` + id +`.json`; - - axios.delete(cul).then((response) => { - if(response.data.status===1){ + 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"; - } - }).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 - }) + // window.location.href = "/shixuns/" + id + "/challenges"; + this.props.history.replace( "/shixuns/" + id + "/challenges"); } - }).catch((error) => { console.log(error) }) } - - SelectTheCommandonChange=(e)=>{ + callback = (key) => { this.setState({ - multi_webssh:e.target.checked + activeKeys:key }) } - bigopen=()=>{ - this.setState({ - opers:true - }) - - } - - bigopens=()=>{ - this.setState({ - opers:false, - operss:false, - opersss:false, - opensmail:false - }) - - } - bigopensmal=(e)=>{ - this.setState({ - opensmail:true - }) + render() { - } - sbigopen=(e)=>{ - this.setState({ - operss:true - }) + let showtabs = this.props.shixunsDetails === undefined ? "" : this.props.shixunsDetails&&this.props.shixunsDetails.is_jupyter === 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; + // 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/component/TPMNav.js b/public/react/src/modules/tpm/component/TPMNav.js index ff8f57aa5..ac5128f26 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,6 +19,9 @@ 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:""; return (
    背景知识 } - { 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? + ( + is_teacher===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.is_jupyter === false? + 审核情况 + : + is_teacher===true? + 审核情况 + : + "" + + ) + + - {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/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..5e27dfafd --- /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 16:28:33 + */ +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..c2ec0d5ef 100644 --- a/public/react/src/modules/tpm/newshixuns/Newshixuns.js +++ b/public/react/src/modules/tpm/newshixuns/Newshixuns.js @@ -2,1351 +2,745 @@ 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) => { + 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: true + }) + } }).catch((error) => { - console.log(error) - }); - } - - setlanguagewrite = (e)=>{ - this.setState({ - languagewrite: e.target.value - }) - } - - setsystemenvironment = (e) => { - this.setState({ - systemenvironment: e.target.value - }) - } - settestcoderunmode = (e) => { - this.setState({ - testcoderunmode: e.target.value + this.setState({ + loading: true + }) }) - } - shixunname = (e) => { - this.setState({ - name: e.target.value, - shixun_nametype: false - }); - } - bigClass = (value) => { - this.setState({ - main_type: value - }) - } + } + }); + this.setState({ + loading: false + }) + }; + Selectthestudent = (value) => { + this.props.form.setFieldsValue({ + select: value, + }); + } - littleClass = (value) => { - this.setState({ - small_type: 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: [] + }) } + } - Selectthestudent = (value) => { - this.setState({ - trainee: value - }) - } - SelectTheCommand = (e) => { - this.setState({ - webssh: e.target.value, - }); + sendsure_apply = () => { + let {language, runtime, run_method} = this.state; - if (e.target.value === 2) { - this.setState({ - SelectTheCommandtype: true, - multi_webssh: false - }); - } else { - this.setState({ - SelectTheCommandtype: false, - multi_webssh: false - }); - } + if (!language || language === "") { + // this.props.showNotification(`请填写该镜像是基于什么语言`); + this.setState({ + languagewritetype: true + }) + return } - - Selectpublic = (e) => { - this.setState({ - scopetype: false, - use_scope: e.target.value, - }); - if (e.target.value === 1) { - this.setState({ - scopetype: true - }); - } + if (!runtime || runtime === "") { + // this.props.showNotification(`请填写该镜像是基于什么语言系统环境`); + this.setState({ + systemenvironmenttype: true + }) + return; } - - 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')( + + 普通实训 + 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/modules/tpm/newshixuns/oldNewshixuns.js b/public/react/src/modules/tpm/newshixuns/oldNewshixuns.js new file mode 100644 index 000000000..1eaee9ad6 --- /dev/null +++ b/public/react/src/modules/tpm/newshixuns/oldNewshixuns.js @@ -0,0 +1,1356 @@ +import React, {Component} from 'react'; + +import {TPMIndexHOC} from '../TPMIndexHOC'; + +import {SnackbarHOC,appendFileSizeToUploadFileAll, getUploadActionUrl} 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 axios from 'axios'; + +import './css/Newshixuns.css'; + +import {getUrl} from 'educoder' + +import moment from 'moment'; + +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, 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"); + + callback && callback() + } + }); + 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 + } + } + + 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]); + } + + } + }).catch((error) => { + console.log(error) + }); + + 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) + }); + } + + setlanguagewrite = (e)=>{ + this.setState({ + languagewrite: e.target.value + }) + } + + setsystemenvironment = (e) => { + this.setState({ + systemenvironment: e.target.value + }) + } + settestcoderunmode = (e) => { + this.setState({ + testcoderunmode: e.target.value + }) + + } + 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 + }) + } + + Selectthestudent = (value) => { + this.setState({ + trainee: value + }) + } + + 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, + }); + } + + adduse_scopeinput = () => { + let {scope_partment} = this.state; + let array = scope_partment; + let newarray = "" + array.push(newarray) + this.setState({ + scope_partment: array, + }); + } + + 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: "" + }); + } + + + } + + deleteScopeInput = (key) => { + let {scope_partment} = this.state; + let datalist = scope_partment; + datalist.splice(key, 1); + this.setState({ + scope_partment: datalist + }); + } + + //提交数据 + 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 + } + + 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 === "") { + this.setState({ + trainee_types: true + }) + // $('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(`请填写该镜像是基于什么语言`); + 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:[] + }) + } + } + sendsure_applyvalues = (e) => { + this.setState({ + sendsure_applyvalue: e.target.value + }) + } + yeshidemodel = () => { + this.setState({ + postapplytitle: false + }) + } + + SelectTheCommandonChange = (e) => { + this.setState({ + multi_webssh: e.target.checked + }) + } + + + bigopen = (e) => { + this.setState({ + opers: true + }) + + } + + bigopens = (e) => { + this.setState({ + opers: false, + operss: false, + opensmail: false + }) + + } + + bigopensmal = (e) => { + this.setState({ + opensmail: true + }) + + } + + sbigopen = (e) => { + this.setState({ + operss: true + }) + + } + + // sbigopens=()=>{ + // this.setState({ + // operss:false + // }) + // } + + onChangeTimePicker = (value, dateString) => { + 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); + }); + } + + + 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 + }) + } + // 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`); + // // } + // } + // }) + + + + } + 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; + }, + } + // 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}*/} + {/*/>*/} + {/*
    */} + + {/*)*/} + {/*})*/} + {/*}*/} +
    + + + + 请选择需要公开的单位 + +
    +
    +
    +
    + + +
    +

    发布信息

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

    VNC图形化

    */} + {/*
  • */} + {/**/} + {/**/} + {/*
  • */} + {/*
    */} + + +
    + + 取消 +
    + + +
    +
    +
    + + ); + } +} +const NewshixunsNew = Form.create({ name: 'newshixunsnew' })(Newshixuns); +export default SnackbarHOC()(TPMIndexHOC(NewshixunsNew)); + + + + + + 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..af88fc6a7 --- /dev/null +++ b/public/react/src/modules/tpm/shixunchild/Challenges/Challengesjupyter.js @@ -0,0 +1,329 @@ +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 LoadingSpin from '../../../../common/LoadingSpin'; +import 'antd/lib/pagination/style/index.css'; + +import '../shixunchildCss/Challenges.css' +import ReactDOM from 'react-dom'; +import axios from 'axios'; +import AccountProfile from"../../../user/AccountProfile"; +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, + } + } + + 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); + 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) => { + }) + + } + + + 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; + + + + return ( + +
    +

    + 简介 + + + + + + +

    + +
    +

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

    + } +

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

    加载实训出错,是否

    this.updatamakedowns()}>重新加载

    + +
    + + :"" + ) + :"" + } + + + + + { + this.state.jupyter_url === null || this.state.jupyter_url === undefined ? + "" + : + ( + is_teacher===true? +
    +
    +

    任务详情

    +

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

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

    应用到实训

    +
    +
    + : + "" + ) + + } + + + { + is_teacher===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..112f381ee 100644 --- a/public/react/src/modules/tpm/shixunchild/Collaborators/Collaborators.css +++ b/public/react/src/modules/tpm/shixunchild/Collaborators/Collaborators.css @@ -3,7 +3,134 @@ line-height: 30px; } +.height28 { + height: 30px; + line-height:28px; +} + .line27{ line-height: 27px; vertical-align: 1px; -} \ No newline at end of file +} +/* 中间居中 */ +.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; +} diff --git a/public/react/src/modules/tpm/shixunchild/Collaborators/Collaborators.js b/public/react/src/modules/tpm/shixunchild/Collaborators/Collaborators.js index d67599bf1..747a1cbe6 100644 --- a/public/react/src/modules/tpm/shixunchild/Collaborators/Collaborators.js +++ b/public/react/src/modules/tpm/shixunchild/Collaborators/Collaborators.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import { Redirect } from 'react-router'; -import {Modal, Button, Radio, Input, Checkbox,message,Spin, Icon} from 'antd'; +import {Modal, Button, Radio, Input, Checkbox,message,Spin, Icon,Pagination} from 'antd'; import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom"; @@ -48,7 +48,9 @@ class Collaborators extends Component { user_name:undefined, school_name:undefined, spinnings:false, - useristrue:false + useristrue:false, + mylistansum:6, + limit:20, } } componentDidMount() { @@ -434,7 +436,10 @@ class Collaborators extends Component { collaboratorListsumtype, user_name, school_name, - useristrue + useristrue, + mylistansum, + page, + limit } = this.state; let {loadingContent} = this.props; const radioStyle = { @@ -448,18 +453,32 @@ class Collaborators extends Component { 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">更换管理员 +

    +

    共{collaboratorList&&collaboratorList.length}人

    +
    + + +

    职业 单位

    + +
    @@ -584,39 +605,58 @@ class Collaborators extends Component { onClick={() => this.submit_add_collaborators_form()}>确定
    :""} - +
    { 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.name} - {/*

    {item.user.brief_introduction}

    */} +

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

    +

    +

    +

    +

    {item.user.identity}

    +

    {item.user.school_name}

    +

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

    +

    +
    + {item.user.shixun_manager === true ? "" : + + this.collaborators_delete(item.user.user_id)}> + + } +
    +

    + {/*

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

    */} + {/*

    {item.user.brief_introduction}

    */}
    - - {item.user.shixun_manager === true ? "" : this.collaborators_delete(item.user.user_id)}>删除} {/*取消关注*/}
    @@ -647,7 +687,18 @@ class Collaborators extends Component { className={collaboratorList.length>10&&collaboratorListsumtype===true?"":"none"} style={{textAlign:'center',borderTop:'1px solid #eee'}}> 加载更多 -
    + {/*{*/} + {/* mylistansum>5?*/} + {/*
    */} + {/* */} + {/*
    */} + {/* :""*/} + {/*}*/} + +
    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..10d48c120 100644 --- a/public/react/src/modules/tpm/shixunchild/shixunchildCss/Challenges.css +++ b/public/react/src/modules/tpm/shixunchild/shixunchildCss/Challenges.css @@ -25,4 +25,81 @@ .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; +} diff --git a/public/react/src/modules/tpm/shixuns/ShixunCard.js b/public/react/src/modules/tpm/shixuns/ShixunCard.js index 9f62ed6b7..045f0009b 100644 --- a/public/react/src/modules/tpm/shixuns/ShixunCard.js +++ b/public/react/src/modules/tpm/shixuns/ShixunCard.js @@ -95,6 +95,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 +132,14 @@ class ShixunCard extends Component { {/**/}
    } + { + item.is_jupyter===true? +
    +

    Jupyter

    + {/**/} +
    + :""} +
    diff --git a/public/react/src/modules/tpm/shixuns/ShixunSearchBar.js b/public/react/src/modules/tpm/shixuns/ShixunSearchBar.js index f9c4a7936..65f78502a 100644 --- a/public/react/src/modules/tpm/shixuns/ShixunSearchBar.js +++ b/public/react/src/modules/tpm/shixuns/ShixunSearchBar.js @@ -275,10 +275,10 @@ render() { }
  • 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..581301766 100644 --- a/public/react/src/modules/tpm/shixuns/ShixunsIndex.js +++ b/public/react/src/modules/tpm/shixuns/ShixunsIndex.js @@ -404,7 +404,7 @@ class ShixunsIndex extends Component { {...this.state} OnSearchInput={this.OnSearchInput.bind(this)} /> - + {/*下方图片*/} @@ -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 0f33b435c..55aa42799 100644 --- a/public/react/src/redux/actions/actionTypes.js +++ b/public/react/src/redux/actions/actionTypes.js @@ -50,6 +50,14 @@ const types = { 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' } export default types; diff --git a/public/react/src/redux/actions/index.js b/public/react/src/redux/actions/index.js index c19fa7e41..ad33061ba 100644 --- a/public/react/src/redux/actions/index.js +++ b/public/react/src/redux/actions/index.js @@ -63,6 +63,16 @@ import { getUserInfoForNew } from './user'; +import { + getJupyterTpiDataSet, + getJupyterTpiUrl, + getJupyterInfo, + syncJupyterCode, + changeGetJupyterUrlState, + saveJupyterTpi, + changeCurrentPage +} from './jupyter'; + export default { toggleTodo, getOJList, @@ -105,6 +115,14 @@ export default { restoreInitialCode, getUserInfoForNew, saveUserCodeForInterval, - saveEditorCodeForDetail + 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..5df1babf6 --- /dev/null +++ b/public/react/src/redux/actions/jupyter.js @@ -0,0 +1,152 @@ +/* + * @Description: jupyter tpi 相关内容 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-12 09:01:30 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-13 15:27:13 + */ +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); + } + }) + } +} +// 改变状态值 +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/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 620905459..9c28448a3 100644 --- a/public/react/src/redux/reducers/index.js +++ b/public/react/src/redux/reducers/index.js @@ -13,6 +13,7 @@ import ojListReducer from './ojListReducer'; import ojForUserReducer from './ojForUserReducer'; import commonReducer from './commonReducer'; import userReducer from './userReducer'; +import jupyterReducer from './jupyterReducer'; export default combineReducers({ testReducer, @@ -20,5 +21,6 @@ export default combineReducers({ ojListReducer, ojForUserReducer, commonReducer, - userReducer + 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/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..b35c499a0 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: 4px; + +} +.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/stylesheets/educoder/edu-main.css b/public/stylesheets/educoder/edu-main.css index 89b62856d..8e479aa9b 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;} diff --git a/public/stylesheets/educoder/iconfont/iconfont.css b/public/stylesheets/educoder/iconfont/iconfont.css index 29e19aee4..f9c2e1e68 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=1576206033975'); /* IE9 */ + src: url('iconfont.eot?t=1576206033975#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=1576206033975') format('woff'), + url('iconfont.ttf?t=1576206033975') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ + url('iconfont.svg?t=1576206033975#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"; } @@ -295,6 +303,10 @@ content: "\e694"; } +.icon-bokeyuan:before { + content: "\e6c6"; +} + .icon-base:before { content: "\e683"; } @@ -799,6 +811,10 @@ content: "\e68c"; } +.icon-pinglun:before { + content: "\e6c8"; +} + .icon-gongcheng:before { content: "\e60f"; } @@ -811,6 +827,10 @@ content: "\e604"; } +.icon-shangjiantou-tianchong:before { + content: "\e733"; +} + .icon-zhuye:before { content: "\e6d3"; } @@ -839,6 +859,10 @@ content: "\e6a1"; } +.icon-shenglvehao:before { + content: "\e708"; +} + .icon-paixu1:before { content: "\e6aa"; } @@ -923,3 +947,43 @@ 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-wancheng1:before { + content: "\e6cb"; +} + +.icon-qiyezhanghao:before { + content: "\e6cc"; +} + +.icon-gerenzhanghao:before { + content: "\e6cd"; +} + +.icon-jiazaishibai1:before { + content: "\e6d6"; +} +