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

merge code
chromesetting
tangjiang 5 years ago
commit f774e99d70

@ -8,6 +8,9 @@ class Admins::ShixunsController < Admins::BaseController
@pending_shixuns = shixuns.where(status:1).size @pending_shixuns = shixuns.where(status:1).size
@processed_shixuns = shixuns.where(status:2).size @processed_shixuns = shixuns.where(status:2).size
@closed_shixuns = shixuns.where(status:3).size @closed_shixuns = shixuns.where(status:3).size
@none_public_shixuns = shixuns.where(public:0).size
@pending_public_shixuns = shixuns.where(public:1).size
@processed_pubic_shixuns = shixuns.where(public:2).size
@shixuns_type_check = MirrorRepository.pluck(:type_name,:id) @shixuns_type_check = MirrorRepository.pluck(:type_name,:id)
@params_page = params[:page] || 1 @params_page = params[:page] || 1
@shixuns = paginate shixuns.preload(:user,:challenges) @shixuns = paginate shixuns.preload(:user,:challenges)

@ -29,7 +29,7 @@ class CoursesController < ApplicationController
:informs, :update_informs, :online_learning, :update_task_position, :tasks_list, :informs, :update_informs, :online_learning, :update_task_position, :tasks_list,
:join_excellent_course, :export_couser_info, :export_member_act_score, :new_informs, :join_excellent_course, :export_couser_info, :export_member_act_score, :new_informs,
:delete_informs, :change_member_role, :course_groups, :join_course_group, :statistics, :delete_informs, :change_member_role, :course_groups, :join_course_group, :statistics,
:work_score, :act_score] :work_score, :act_score, :calculate_all_shixun_scores]
before_action :user_course_identity, except: [:join_excellent_course, :index, :create, :new, :apply_to_join_course, before_action :user_course_identity, except: [:join_excellent_course, :index, :create, :new, :apply_to_join_course,
:search_course_list, :get_historical_course_students, :mine, :search_slim, :board_list] :search_course_list, :get_historical_course_students, :mine, :search_slim, :board_list]
before_action :teacher_allowed, only: [:update, :destroy, :settings, :search_teacher_candidate, before_action :teacher_allowed, only: [:update, :destroy, :settings, :search_teacher_candidate,
@ -48,7 +48,7 @@ class CoursesController < ApplicationController
before_action :validate_page_size, only: :mine before_action :validate_page_size, only: :mine
before_action :course_tasks, only: [:tasks_list, :update_task_position] before_action :course_tasks, only: [:tasks_list, :update_task_position]
before_action :validate_inform_params, only: [:update_informs, :new_informs] before_action :validate_inform_params, only: [:update_informs, :new_informs]
before_action :course_member_allowed, only: [:statistics, :work_score, :act_score] before_action :course_member_allowed, only: [:statistics, :work_score, :act_score, :calculate_all_shixun_scores]
if RUBY_PLATFORM =~ /linux/ if RUBY_PLATFORM =~ /linux/
require 'simple_xlsx_reader' require 'simple_xlsx_reader'
@ -1334,6 +1334,16 @@ class CoursesController < ApplicationController
end end
end end
# 计算课堂所有已发布的实训作业成绩
def calculate_all_shixun_scores
tip_exception(-1, "课堂已结束") if @course.is_end
shixun_homeworks = @course.homework_commons.homework_published.where(homework_type: 4)
shixun_homeworks.includes(:homework_challenge_settings, :published_settings, :homework_commons_shixun).each do |homework|
homework.update_homework_work_score
end
normal_status(0, "更新成功")
end
def search_slim def search_slim
courses = current_user.manage_courses.not_deleted.processing courses = current_user.manage_courses.not_deleted.processing

@ -1,8 +1,8 @@
class HacksController < ApplicationController class HacksController < ApplicationController
before_action :require_login, except: [:index] before_action :require_login, except: [:index]
before_action :find_hack, only: [:edit, :update, :publish, :start, :update_set, :delete_set, :destroy] before_action :find_hack, only: [:edit, :update, :publish, :start, :update_set, :delete_set, :destroy, :cancel_publish]
before_action :require_teacher_identity, only: [:create] before_action :require_teacher_identity, only: [:create]
before_action :require_auth_identity, only: [:update, :edit, :publish, :update_set, :delete_set, :destroy] before_action :require_auth_identity, only: [:update, :edit, :publish, :update_set, :delete_set, :destroy, :cancel_publish]
# 开启编程,如果第一次开启,创建一条记录,如果已经开启过的话,直接返回标识即可 # 开启编程,如果第一次开启,创建一条记录,如果已经开启过的话,直接返回标识即可
@ -99,6 +99,12 @@ class HacksController < ApplicationController
render_ok render_ok
end end
# 取消发布
def cancel_publish
@hack.update_attribute(:status, 0)
render_ok
end
# 发布列表 # 发布列表
def unpulished_list def unpulished_list
limit = params[:limit] || 16 limit = params[:limit] || 16

@ -217,7 +217,8 @@ class MyshixunsController < ApplicationController
shixun_tomcat = edu_setting('tomcat_webssh') shixun_tomcat = edu_setting('tomcat_webssh')
uri = "#{shixun_tomcat}/bridge/webssh/getConnectInfo" uri = "#{shixun_tomcat}/bridge/webssh/getConnectInfo"
# 由于中间层采用混合云的方式因为local参数表示在有文件生成的实训是在本地生成还是在其他云端生成评测文件 # 由于中间层采用混合云的方式因为local参数表示在有文件生成的实训是在本地生成还是在其他云端生成评测文件
params = {tpiID:@myshixun.id, podType:@myshixun.shixun.try(:webssh), local: @myshixun.shixun.show_type != -1, local = @myshixun.shixun.challenges.where.not(show_type: -1).count == 0
params = {tpiID:@myshixun.id, podType:@myshixun.shixun.try(:webssh), local: local,
containers:(Base64.urlsafe_encode64(shixun_container_limit @myshixun.shixun))} containers:(Base64.urlsafe_encode64(shixun_container_limit @myshixun.shixun))}
res = uri_post uri, params res = uri_post uri, params
if res && res['code'].to_i != 0 if res && res['code'].to_i != 0

@ -20,14 +20,14 @@ class ShixunsController < ApplicationController
before_action :allowed, only: [:update, :close, :update_propaedeutics, :settings, :publish, before_action :allowed, only: [:update, :close, :update_propaedeutics, :settings, :publish,
:shixun_members_added, :change_manager, :collaborators_delete, :shixun_members_added, :change_manager, :collaborators_delete,
:cancel_publish, :add_collaborators, :add_file] :cancel_apply_public, :cancel_publish, :add_collaborators, :add_file]
before_action :portion_allowed, only: [:copy] before_action :portion_allowed, only: [:copy]
before_action :special_allowed, only: [:send_to_course, :search_user_courses] before_action :special_allowed, only: [:send_to_course, :search_user_courses]
## 获取课程列表 ## 获取课程列表
def index def index
@shixuns = current_laboratory.shixuns.unhidden @shixuns = current_laboratory.shixuns.unhidden.publiced
## 方向 ## 方向
if params[:tag_level].present? && params[:tag_id].present? if params[:tag_level].present? && params[:tag_id].present?
@ -365,12 +365,7 @@ class ShixunsController < ApplicationController
end end
def create def create
begin @shixun = CreateShixunService.call(current_user, shixun_params, params)
@shixun = CreateShixunService.call(current_user, shixun_params, params)
rescue => e
logger_error("shixun_create_error: #{e.message}")
tip_exception("创建实训失败!")
end
end end
# 保存jupyter到版本库 # 保存jupyter到版本库
@ -730,37 +725,41 @@ class ShixunsController < ApplicationController
@status = 0 @status = 0
@position = [] @position = []
begin begin
if @shixun.challenges.count == 0 unless @shixun.is_jupyter?
@status = 4 if @shixun.challenges.count == 0
else @status = 4
@shixun.challenges.each do |challenge| else
if challenge.challenge_tags.count == 0 @shixun.challenges.each do |challenge|
@status = 3 if challenge.challenge_tags.count == 0
@position << challenge.position @status = 3
@position << challenge.position
end
end end
end unfinish_challenge = @shixun.challenges.where(:st => 0, :path => nil)
unfinish_challenge = @shixun.challenges.where(:st => 0, :path => nil) if unfinish_challenge.count > 0 && !@shixun.is_choice_type?
if unfinish_challenge.count > 0 && !@shixun.is_choice_type? @status = 2
@status = 2 @pos = []
@pos = [] unfinish_challenge.each do |challenge|
unfinish_challenge.each do |challenge| @pos << challenge.position
@pos << challenge.position end
end end
end end
end end
if @status == 0 if @status == 0
@shixun.update_attributes!(:status => 1) ActiveRecord::Base.transaction do
apply = ApplyAction.where(:container_type => "ApplyShixun", :container_id => @shixun.id).order("created_at desc").first @shixun.update_attributes!(:status => 2)
if apply && apply.status == 0 apply = ApplyAction.where(:container_type => "ApplyShixun", :container_id => @shixun.id).order("created_at desc").first
@status = 0 if apply && apply.status == 0
else @status = 0
ApplyAction.create(:container_type => "ApplyShixun", :container_id => @shixun.id, :user_id => current_user.id, :status => 0) else
#begin ApplyAction.create(:container_type => "ApplyShixun", :container_id => @shixun.id, :user_id => current_user.id, :status => 0)
# status = Trustie::Sms.send(mobile: '18711011226', send_type:'publish_shixun' , name: '管理员') #begin
#rescue => e # status = Trustie::Sms.send(mobile: '18711011226', send_type:'publish_shixun' , name: '管理员')
# Rails.logger.error "发送验证码出错: #{e}" #rescue => e
#end # Rails.logger.error "发送验证码出错: #{e}"
@status = 1 #end
@status = 1
end
end end
end end
rescue Exception => e rescue Exception => e
@ -905,14 +904,24 @@ class ShixunsController < ApplicationController
:disposition => 'attachment' #inline can open in browser :disposition => 'attachment' #inline can open in browser
end end
# 撤销申请公开
def cancel_apply_public
tip_exception("实训已经公开,无法撤销") if @shixun.public == 2
ActiveRecord::Base.transaction do
apply = ApplyAction.where(:container_type => "ApplyShixun", :container_id => @shixun.id).order("created_at desc").first
if apply && apply.status == 0
apply.update_attributes!(status: 3)
apply.tidings&.destroy_all
end
@shixun.update_column(:public, 0)
end
normal_status(0, "成功撤销申请")
end
# 撤销发布 # 撤销发布
def cancel_publish def cancel_publish
tip_exception("实训已经发布,无法撤销") if @shixun.status == 2 tip_exception("请先撤销申请公开,再撤销发布") if @shixun.public == 1
apply = ApplyAction.where(:container_type => "ApplyShixun", :container_id => @shixun.id).order("created_at desc").first tip_exception("实训已经公开,无法撤销") if @shixun.public == 2
if apply && apply.status == 0
apply.update_attribute(:status, 3)
apply.tidings.destroy_all
end
@shixun.update_column(:status, 0) @shixun.update_column(:status, 0)
end end

@ -27,6 +27,17 @@ module ShixunsHelper
end end
end end
def shixun_public_status shixun
case shixun.try(:public)
when 0,nil
"未公开"
when 1
"待审核"
when 2
"已公开"
end
end
# 已完成实训所获得的经验值 # 已完成实训所获得的经验值
def myshixun_exp myshixun def myshixun_exp myshixun
score = 0 score = 0

@ -119,6 +119,12 @@ class Game < ApplicationRecord
# self.outputs.pluck(:query_index).first # self.outputs.pluck(:query_index).first
#end #end
# 是否查看了答案(通关的是否在通关前看的答案)
def view_answer
answer_exists = Grade.exists?("container_type = 'Answer' and container_id = #{id} and created_at < '#{end_time}'")
answer_open != 0 ? (status == 2 ? answer_exists : true) : false
end
# 用户关卡得分 # 用户关卡得分
def get_user_final_score def get_user_final_score

@ -88,9 +88,10 @@ class Myshixun < ApplicationRecord
self.games.select{|game| game.status == 2}.size self.games.select{|game| game.status == 2}.size
end end
# 查看答案的关卡数 # 查看答案的关卡数,只统计通关前看的关卡
def view_answer_count def view_answer_count
self.games.select{|game| game.status == 2 && game.answer_open != 0}.size answer_ids = user.grades.joins("join games on grades.container_id = games.id").where("container_type = 'Answer' and games.status=2 and games.end_time > grades.created_at").pluck(:container_id)
self.games.select{|game| game.status == 2 && game.answer_open != 0 && answer_ids.include?(game.id)}.size
end end
# 通关时间 # 通关时间

@ -3,6 +3,7 @@ class Shixun < ApplicationRecord
attr_accessor :page_no #管理员页面 实训配置更新状态时需要接受page_no参数 attr_accessor :page_no #管理员页面 实训配置更新状态时需要接受page_no参数
# status: 0编辑 1申请发布 2正式发布 3关闭 -1软删除 # status: 0编辑 1申请发布 2正式发布 3关闭 -1软删除
# public: 0未公开 1申请公开 2公开
# hide_code 隐藏代码窗口 # hide_code 隐藏代码窗口
# code_hidden: 隐藏代码目录 # code_hidden: 隐藏代码目录
# task_pass: 跳关 # task_pass: 跳关
@ -79,6 +80,7 @@ class Shixun < ApplicationRecord
scope :published_closed, lambda{ where(status: [2, 3]) } scope :published_closed, lambda{ where(status: [2, 3]) }
scope :none_closed, lambda{ where(status: [0, 1, 2]) } scope :none_closed, lambda{ where(status: [0, 1, 2]) }
scope :unhidden, lambda{ where(hidden: 0, status: 2) } scope :unhidden, lambda{ where(hidden: 0, status: 2) }
scope :publiced, lambda{ where(public: 2) }
scope :field_for_recommend, lambda{ select([:id, :name, :identifier, :myshixuns_count]) } scope :field_for_recommend, lambda{ select([:id, :name, :identifier, :myshixuns_count]) }
scope :find_by_ids,lambda{|k| where(id:k)} scope :find_by_ids,lambda{|k| where(id:k)}

@ -1,8 +1,6 @@
class ShixunInfo < ApplicationRecord class ShixunInfo < ApplicationRecord
belongs_to :shixun belongs_to :shixun
validates_uniqueness_of :shixun_id validates_uniqueness_of :shixun_id
validates_presence_of :shixun_id, :description
after_commit :create_diff_record after_commit :create_diff_record
private private

@ -21,7 +21,17 @@ class Admins::ShixunQuery < ApplicationQuery
[0,1,2,3] [0,1,2,3]
end end
public =
case params[:public]
when "editing" then [0]
when "pending" then [1]
when "processed" then [2]
else
[0,1,2]
end
all_shixuns = all_shixuns.where(status: status) if status.present? all_shixuns = all_shixuns.where(status: status) if status.present?
all_shixuns = all_shixuns.where(public: public) if public.present?
if params[:tag].present? if params[:tag].present?
all_shixuns = all_shixuns.joins(:mirror_repositories).where("mirror_repositories.id = ?",params[:tag].to_i) all_shixuns = all_shixuns.joins(:mirror_repositories).where("mirror_repositories.id = ?",params[:tag].to_i)

@ -10,7 +10,7 @@ class Admins::ShixunAuths::AgreeApplyService < ApplicationService
def call def call
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
apply.update!(status: 1, dealer_id: user.id) apply.update!(status: 1, dealer_id: user.id)
shixun.update!(status: 2, publish_time: Time.now) shixun.update!(public: 2, publish_time: Time.now)
# 奖励金币、经验 # 奖励金币、经验
reward_grade_and_experience! reward_grade_and_experience!

@ -10,7 +10,7 @@ class Admins::ShixunAuths::RefuseApplyService < ApplicationService
def call def call
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
shixun.update!(status: 0) shixun.update!(public: 0)
apply.update!(status: 2, reason: reason, dealer_id: user.id) apply.update!(status: 2, reason: reason, dealer_id: user.id)
deal_tiding! deal_tiding!

@ -30,7 +30,6 @@ class SearchService < ApplicationService
model_options = { model_options = {
includes: modal_name.searchable_includes includes: modal_name.searchable_includes
} }
model_options.deep_merge!(where: { status: 2 }) if modal_name == Shixun
model_options.deep_merge!(extra_options) model_options.deep_merge!(extra_options)
model_options.deep_merge!(default_options) model_options.deep_merge!(default_options)
@ -40,7 +39,7 @@ class SearchService < ApplicationService
def extra_options def extra_options
case params[:type].to_s.strip case params[:type].to_s.strip
when 'shixun' then when 'shixun' then
{ where: { id: Laboratory.current.shixuns.pluck(:id) } } { where: { id: Laboratory.current.shixuns.where(public: 2, status: 2, fork_from: nil).or(Laboratory.current.shixuns.where(status: 2, id: User.current.shixuns)).pluck(:id) } }
when 'subject' then when 'subject' then
{ where: { id: Laboratory.current.subjects.pluck(:id) } } { where: { id: Laboratory.current.subjects.pluck(:id) } }
when 'course' then when 'course' then

@ -25,7 +25,7 @@ class ShixunSearchService < ApplicationService
else else
none_shixun_ids = ShixunSchool.where("school_id != #{User.current.school_id}").pluck(:shixun_id) none_shixun_ids = ShixunSchool.where("school_id != #{User.current.school_id}").pluck(:shixun_id)
@shixuns = @shixuns.where.not(id: none_shixun_ids).where(hidden: 0) @shixuns = @shixuns.where.not(id: none_shixun_ids).where(hidden: 0, status: 2, public: 2).or(@shixuns.where(id: current_user.shixuns))
end end
end end

@ -14,34 +14,39 @@ class CreateShixunService < ApplicationService
shixun.user_id = user.id shixun.user_id = user.id
main_mirror = MirrorRepository.find params[:main_type] main_mirror = MirrorRepository.find params[:main_type]
sub_mirrors = MirrorRepository.where(id: params[:sub_type]) sub_mirrors = MirrorRepository.where(id: params[:sub_type])
ActiveRecord::Base.transaction do begin
shixun.save! ActiveRecord::Base.transaction do
# 获取脚本内容 shixun.save!
shixun_script = get_shixun_script(shixun, main_mirror, sub_mirrors) # 获取脚本内容
# 创建额外信息 shixun_script = get_shixun_script(shixun, main_mirror, sub_mirrors)
ShixunInfo.create!(shixun_id: shixun.id, evaluate_script: shixun_script, description: params[:description]) # 创建额外信息
# 创建合作者 ShixunInfo.create!(shixun_id: shixun.id, evaluate_script: shixun_script, description: params[:description])
shixun.shixun_members.create!(user_id: user.id, role: 1) # 创建合作者
# 创建镜像 shixun.shixun_members.create!(user_id: user.id, role: 1)
ShixunMirrorRepository.create!(:shixun_id => shixun.id, :mirror_repository_id => main_mirror.id) # 创建镜像
# 创建主服务配置 ShixunMirrorRepository.create!(:shixun_id => shixun.id, :mirror_repository_id => main_mirror.id)
ShixunServiceConfig.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) 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? name = sub.name #查看镜像是否有名称,如果没有名称就不用服务配置
end 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) repo_path = repo_namespace(user.login, shixun.identifier)
shixun.update_column(:repo_name, repo_path.split(".")[0]) 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) if !Laboratory.current.main_site?
Laboratory.current.laboratory_shixuns.create!(shixun: shixun, ownership: true)
end
return shixun
end end
return shixun rescue => e
Rails.logger.error("shixun_create_error: #{e.message}")
raise("创建实训失败!")
end end
end end

@ -4,25 +4,31 @@
<div class="box search-form-container shixuns-list-form"> <div class="box search-form-container shixuns-list-form">
<%= form_tag(admins_shixuns_path, method: :get, class: 'form-inline search-form',id:"shixuns-search-form",remote:true) do %> <%= form_tag(admins_shixuns_path, method: :get, class: 'form-inline search-form',id:"shixuns-search-form",remote:true) do %>
<div class="form-group mr-2"> <div class="form-group">
<label for="status">状态:</label> <label for="status">状态:</label>
<% status_options = [['全部', ''], ["编辑中(#{@editing_shixuns})", "editing"], ["待审核(#{@pending_shixuns})", 'pending'], ["已发布(#{@processed_shixuns})", 'processed'],["已关闭(#{@closed_shixuns})",'closed']] %> <% status_options = [['全部', ''], ["编辑中(#{@editing_shixuns})", "editing"], ["待审核(#{@pending_shixuns})", 'pending'], ["已发布(#{@processed_shixuns})", 'processed'],["已关闭(#{@closed_shixuns})",'closed']] %>
<%= select_tag(:status, options_for_select(status_options), class: 'form-control') %> <%= select_tag(:status, options_for_select(status_options), class: 'form-control') %>
</div> </div>
<div class="form-group">
<label for="status">公开:</label>
<% public_options = [['全部', ''], ["未公开(#{@none_public_shixuns})", "editing"], ["待审核(#{@pending_public_shixuns})", 'pending'], ["已公开(#{@processed_pubic_shixuns})", 'processed']] %>
<%= select_tag(:public, options_for_select(public_options), class: 'form-control') %>
</div>
<div class="form-group mr-2"> <div class="form-group mr-2">
<label for="tag-choosed">技术平台:</label> <label for="tag-choosed">技术平台:</label>
<%= select_tag(:tag, options_for_select(@shixuns_type_check.unshift(["",nil])), class: 'form-control',id:"tag-choosed") %> <%= select_tag(:tag, options_for_select(@shixuns_type_check.unshift(["",nil])), class: 'form-control',id:"tag-choosed") %>
</div> </div>
<div class="form-group ml-3"> <div class="form-group">
<label>搜索类型:</label> <label>搜索类型:</label>
<% auto_trial_options = [['创建者姓名', 0], ['实训名称', 1], ['学校名称', 2]] %> <% auto_trial_options = [['创建者姓名', 0], ['实训名称', 1], ['学校名称', 2]] %>
<%= select_tag(:search_type, options_for_select(auto_trial_options), class: 'form-control') %> <%= select_tag(:search_type, options_for_select(auto_trial_options), class: 'form-control') %>
</div> </div>
<%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '输入关键字搜索') %> <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2', placeholder: '输入关键字搜索') %>
<%= submit_tag('搜索', class: 'btn btn-primary ml-3','data-disable-with': '搜索中...') %> <%= submit_tag('搜索', class: 'btn btn-primary','data-disable-with': '搜索中...') %>
<%= link_to "清除",admins_shixuns_path,class: "btn btn-default",id:"shixuns-clear-search",'data-disable-with': '清除中...' %> <%= link_to "清除", admins_shixuns_path,class: "btn btn-default",id:"shixuns-clear-search",'data-disable-with': '清除中...' %>
<% end %> <% end %>
<a href="javascript:void(0)" class="btn btn-primary" id="shixuns-export" data-disable-with = '导出中...'>导出</a> <a href="javascript:void(0)" class="btn btn-primary" id="shixuns-export" data-disable-with = '导出中...'>导出</a>
</div> </div>

@ -2,14 +2,15 @@
<thead class="thead-light"> <thead class="thead-light">
<th width="4%">序号</th> <th width="4%">序号</th>
<th width="8%">ID</th> <th width="8%">ID</th>
<th width="28%" class="text-left">实训名称</th> <th width="24%" class="text-left">实训名称</th>
<th width="8%">技术平台</th> <th width="8%">技术平台</th>
<th width="5%">Fork源</th> <th width="5%">Fork源</th>
<th width="5%">实践</th> <th width="5%">实践</th>
<th width="5%">选择</th> <th width="5%">选择</th>
<th width="6%">状态</th> <th width="6%">状态</th>
<th width="6%">公开</th>
<th width="7%">创建者</th> <th width="7%">创建者</th>
<th width="13%"><%= sort_tag('创建于', name: 'created_at', path: admins_shixuns_path) %></th> <th width="11%"><%= sort_tag('创建于', name: 'created_at', path: admins_shixuns_path) %></th>
<th width="5%">单测</th> <th width="5%">单测</th>
<th width="6%">操作</th> <th width="6%">操作</th>
</thead> </thead>
@ -33,6 +34,7 @@
<td><%= shixun.challenges.where(:st => 0).size %></td> <td><%= shixun.challenges.where(:st => 0).size %></td>
<td><%= shixun.challenges.where(:st => 1).size %></td> <td><%= shixun.challenges.where(:st => 1).size %></td>
<td class="shixuns-status-<%= shixun.status %>"><%= shixun_authentication_status shixun %></td> <td class="shixuns-status-<%= shixun.status %>"><%= shixun_authentication_status shixun %></td>
<td class="shixuns-status-<%= shixun.public %>"><%= shixun_public_status shixun %></td>
<td><%= link_to shixun.user.try(:real_name),"/users/#{shixun.user.try(:login)}",target:'_blank' %></td> <td><%= link_to shixun.user.try(:real_name),"/users/#{shixun.user.try(:login)}",target:'_blank' %></td>
<td><%= format_time shixun.created_at %></td> <td><%= format_time shixun.created_at %></td>
<td class="homepage_teacher"> <td class="homepage_teacher">

@ -1,5 +1,5 @@
json.hack do json.hack do
json.(@hack, :name, :difficult, :time_limit, :description, :score, :identifier) json.(@hack, :name, :difficult, :time_limit, :description, :score, :identifier, :status)
json.language @hack.language json.language @hack.language
json.username @hack.user.real_name json.username @hack.user.real_name
json.code @my_hack.code json.code @my_hack.code

@ -8,6 +8,6 @@ end
json.hacks_count @hacks_count json.hacks_count @hacks_count
json.hacks_list do json.hacks_list do
json.array! @hacks do |hack| json.array! @hacks do |hack|
json.(hack,:identifier, :name , :hack_user_lastest_codes_count, :difficult, :passed_rate, :category) json.(hack,:identifier, :name , :hack_user_lastest_codes_count, :difficult, :passed_rate, :category, :open_or_not, :status)
end end
end end

@ -17,3 +17,5 @@ json.score_info shixun.shixun_preference_info # todo: 这块可以改成只
json.is_jupyter shixun.is_jupyter json.is_jupyter shixun.is_jupyter
# 用于是否显示导航栏中的'背景知识' # 用于是否显示导航栏中的'背景知识'
json.propaedeutics shixun.propaedeutics.present? json.propaedeutics shixun.propaedeutics.present?
json.public shixun.public

@ -42,7 +42,7 @@ if @shixun
json.challenge_comment challenge_comment&.comment json.challenge_comment challenge_comment&.comment
json.challenge_comment_hidden @user_course_identity < Course::STUDENT ? challenge_comment&.hidden_comment : nil json.challenge_comment_hidden @user_course_identity < Course::STUDENT ? challenge_comment&.hidden_comment : nil
json.comment_id challenge_comment&.id json.comment_id challenge_comment&.id
json.view_answer game ? game.answer_open != 0 : 0 json.view_answer game ? game.view_answer : false
end end
end end

@ -55,6 +55,7 @@ Rails.application.routes.draw do
end end
member do member do
post :publish post :publish
post :cancel_publish
get :start get :start
post :update_set post :update_set
delete :delete_set delete :delete_set
@ -273,6 +274,7 @@ Rails.application.routes.draw do
post :send_to_course post :send_to_course
delete :collaborators_delete delete :collaborators_delete
get :cancel_publish get :cancel_publish
get :cancel_apply_public
get :publish get :publish
get :shixun_exec get :shixun_exec
post :review_shixun post :review_shixun
@ -440,6 +442,7 @@ Rails.application.routes.draw do
get 'statistics' get 'statistics'
post :inform_up post :inform_up
post :inform_down post :inform_down
get :calculate_all_shixun_scores
end end
collection do collection do

@ -0,0 +1,5 @@
class AddPublicStatusToShixun < ActiveRecord::Migration[5.2]
def change
add_column :shixuns, :public, :integer, default: 0
end
end

@ -0,0 +1,9 @@
class MigrateShixunStatus < ActiveRecord::Migration[5.2]
def change
# 平台上所有已发布且未隐藏的实训都设为公开
Shixun.unhidden.update_all(public: 2)
# 所有已申请发布的实训状态都改为已发布,申请发布改为申请公开
Shixun.where(status: 1, id: ApplyAction.where(container_type: 'ApplyShixun', status: 0).pluck(:container_id)).update_all(status: 2, public: 1)
end
end

File diff suppressed because one or more lines are too long

@ -109,15 +109,15 @@ export function initAxiosInterceptors(props) {
} }
// //
// console.log(config); // console.log(config);
if (config.method === "post") { // if (config.method === "post") {
if (requestMap[config.url] === true) { // 避免重复的请求 导致页面f5刷新 也会被阻止 显示这个方法会影响到定制信息 // if (requestMap[config.url] === true) { // 避免重复的请求 导致页面f5刷新 也会被阻止 显示这个方法会影响到定制信息
// console.log(config); // // console.log(config);
// console.log(JSON.parse(config)); // // console.log(JSON.parse(config));
// console.log(config.url); // // console.log(config.url);
// console.log("被阻止了是重复请求================================="); // // console.log("被阻止了是重复请求=================================");
return false; // return false;
} // }
} // }
// 非file_update请求 // 非file_update请求
if (config.url.indexOf('update_file') === -1) { if (config.url.indexOf('update_file') === -1) {
requestMap[config.url] = true; requestMap[config.url] = true;

@ -19,7 +19,8 @@ class NewShixunModel extends Component{
order:'desc', order:'desc',
diff:0, diff:0,
limit:15, limit:15,
sort:"myshixuns_count" sort:"myshixuns_count",
belongtoindex:0,
} }
} }
componentDidMount() { componentDidMount() {
@ -161,7 +162,17 @@ class NewShixunModel extends Component{
}) })
let{status,order,diff,limit,sort}=this.state; let{status,order,diff,limit,sort}=this.state;
if(this.props.type==='shixuns'){ if(this.props.type==='shixuns'){
this.getdatalist(1,value,status,undefined,order,diff,limit) this.getdatalist(1,value,status,undefined,order,diff,limit);
if(value==="all"){
this.setState({
belongtoindex:0
})
}else{
this.setState({
belongtoindex:1
})
}
}else{ }else{
this.getdatalist(1,value,undefined,undefined,order,undefined,limit,undefined,sort) this.getdatalist(1,value,undefined,undefined,order,undefined,limit,undefined,sort)
} }
@ -322,6 +333,7 @@ class NewShixunModel extends Component{
this.getdatalist(page,type,status,keyword,order,diff,limit) this.getdatalist(page,type,status,keyword,order,diff,limit)
} }
updatepathlist=(sorts,orders)=>{ updatepathlist=(sorts,orders)=>{
let{page,type,keyword,order,diff,limit,status,sort}=this.state; let{page,type,keyword,order,diff,limit,status,sort}=this.state;
let seartorders; let seartorders;
@ -352,7 +364,7 @@ class NewShixunModel extends Component{
} }
render() { render() {
let {diff,Grouplist,status,shixun_list,shixuns_count,page,type,order,sort}=this.state; let {diff,Grouplist,status,shixun_list,shixuns_count,page,type,order,sort,belongtoindex}=this.state;
// let {visible,patheditarry}=this.props; // let {visible,patheditarry}=this.props;
// console.log(Grouplist) // console.log(Grouplist)
// console.log(allGrouplist) // console.log(allGrouplist)
@ -446,7 +458,7 @@ class NewShixunModel extends Component{
<Spin spinning={this.state.isspinning}> <Spin spinning={this.state.isspinning}>
<div className={"clearfix educontent pr mb60shixun"}> <div className={"clearfix educontent pr mb60shixun"}>
<div className={"square-list clearfix"}> <div className={"square-list clearfix verticallayout"}>
<div className="newshixunheadersear"> <div className="newshixunheadersear">
<div style={{height:"53px"}}></div> <div style={{height:"53px"}}></div>
@ -472,7 +484,36 @@ class NewShixunModel extends Component{
onSearch={ (value)=>this.setdatafuns(value)} /> onSearch={ (value)=>this.setdatafuns(value)} />
</div> </div>
<div className="clearfix font-12 mt30">
{this.props.type==='shixuns'?
<div className="clearfix sortinxdirection mt30 intermediatecenterysls">
<p className="nandu">筛选</p>
<p className={belongtoindex===0?"clickbutstwo ml13":"clickbutstwos ml13"} onClick={()=>this.belongto("all")}>全部实训</p>
<p className={belongtoindex===1?"clickbutstwo ml20":"clickbutstwos ml20"} onClick={()=>this.belongto("mine")}>普通实训</p>
</div>:""
}
{/*{this.props.type==='shixuns'? <Dropdown overlay={menus}>*/}
{/* <a className="ant-dropdown-link color-grey-6">*/}
{/* {diff===0?"难度":diff===1?"初级":diff===2?"中级":diff===3?"高级":diff===4?"顶级":""}<Icon type="down" className={"color-grey-6"}/>*/}
{/* </a>*/}
{/*</Dropdown>:""}*/}
{this.props.type==='shixuns'?
<div className="clearfix sortinxdirection mt20 intermediatecenterysls">
<p className="nandu">难度</p>
<p className={diff===0?"clickbuts ml13":"clickbutst ml13"} onClick={()=>this.DropdownClick(0)}>全部</p>
<p className={diff===1?"clickbuts ml30":"clickbutst ml30"} onClick={()=>this.DropdownClick(1)}>初级</p>
<p className={diff===2?"clickbuts ml30":"clickbutst ml30"} onClick={()=>this.DropdownClick(2)}>中级</p>
<p className={diff===3?"clickbuts ml30":"clickbutst ml30"} onClick={()=>this.DropdownClick(3)}>高级</p>
<p className={diff===4?"clickbuts ml30":"clickbutst ml30"} onClick={()=>this.DropdownClick(4)}>顶级</p>
</div>:""
}
<div className={this.props.type==='shixuns'?"clearfix font-12 mt20":"clearfix font-12 mt30"}>
<div className="font-12 ml5 fl"> <div className="font-12 ml5 fl">
@ -509,17 +550,18 @@ class NewShixunModel extends Component{
</a> </a>
</Dropdown>:"":""} </Dropdown>:"":""}
{this.props.type==='shixuns'? <Dropdown overlay={menus}>
<a className="ant-dropdown-link color-grey-6">
{diff===0?"难度":diff===1?"初级":diff===2?"中级":diff===3?"高级":diff===4?"顶级":""}<Icon type="down" className={"color-grey-6"}/>
</a>
</Dropdown>:""}
</div> </div>
<div className="font-12 alltopiscright ml25 fr"> <div className="font-12 alltopiscright ml25 fl">
{/*<span className={"fr pointer color-grey-3"} onClick={()=>this.props.hideNewShixunModelType()}>返回</span>*/}
<span className={type==="mine"?"fr topcsactive pointer color-grey-3 color-blue":"fr pointer color-grey-3"} onClick={()=>this.belongto("mine")}>我的{this.props.type==='shixuns'?'实训':"课程"}</span> {this.props.type==='shixuns'?"":
<span className={type==="all"?"fr mr30 topcsactive pointer color-grey-3 color-blue":"fr mr30 pointer color-grey-3"} onClick={()=>this.belongto("all")}>全部{this.props.type==='shixuns'?'实训':"课程"}</span> <span className={type==="mine"?"fr topcsactive pointer color-grey-3 color-blue":"fr pointer color-grey-3"} onClick={()=>this.belongto("mine")}>我的课程</span>
}
{this.props.type==='shixuns'?"":
<span className={type==="all"?"fr mr30 topcsactive pointer color-grey-3 color-blue":"fr mr30 pointer color-grey-3"} onClick={()=>this.belongto("all")}>全部课程</span>
}
</div> </div>
</div> </div>

@ -407,3 +407,110 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
/* 中间居中 */
.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;
}
.nandu{
width: 42px;
height: 19px;
font-size: 14px;
color: #000000;
line-height: 19px;
margin-left: 6px;
}
.clickbuts{
text-align: center;
width: 60px;
height: 32px;
background: #4CACFF;
border-radius: 16px;
line-height: 30px;
color: #FFFFFF;
cursor:pointer;
}
.clickbutst{
height:19px;
font-size:14px;
color:#505050;
line-height:19px;
cursor:pointer;
}
.clickbutstwo{
text-align: center;
width: 85px;
height: 32px;
background: #4CACFF;
border-radius: 16px;
line-height: 30px;
color: #FFFFFF;
cursor:pointer;
}
.clickbutstwos{
height:19px;
font-size:14px;
color:#505050;
line-height:19px;
cursor:pointer;
}

@ -483,8 +483,8 @@ class GraduationTasksnew extends Component {
</style> </style>
<Upload {...uploadProps} className="upload_1 ml5"> <Upload {...uploadProps} className="upload_1 ml5">
<Button className="uploadBtn"> <Button className="uploadBtn">
<Icon type="upload"/> 上传附件 <Icon type="upload"/> 上传附件
</Button> </Button>
(单个文件150M以内) (单个文件150M以内)
</Upload> </Upload>

@ -1,15 +1,16 @@
import React, {Component} from 'react'; import React, {Component} from 'react';
import {
Button,
} from 'antd';
class Bottomsubmit extends Component { class Bottomsubmit extends Component {
constructor(props) { constructor(props) {
super(props) super(props)
this.state = { this.state = {}
}
} }
cannelfun=()=>{ cannelfun = () => {
// window.location.href= // window.location.href=
this.props.history.replace(this.props.url); this.props.history.replace(this.props.url);
} }
@ -30,8 +31,11 @@ class Bottomsubmit extends Component {
</style> </style>
<div className="clearfix bor-bottom-greyE edu-back-white orderingbox newshixunbottombtn"> <div className="clearfix bor-bottom-greyE edu-back-white orderingbox newshixunbottombtn">
<div className=" edu-txt-center padding13-30"> <div className=" edu-txt-center padding13-30">
<button type="button" className="ant-btn mr20 newshixunmode backgroundFFF" onClick={()=>this.cannelfun()}><span> </span></button> <button type="button" className="ant-btn mr20 newshixunmode backgroundFFF" onClick={() => this.cannelfun()}>
<button type="button" className="ant-btn newshixunmode mr40 ant-btn-primary" type="primary" htmlType="submit" onClick={()=>this.props.onSubmits()}><span> </span></button> <span> </span></button>
<Button type="button" className="ant-btn newshixunmode mr40 ant-btn-primary" type="primary"
htmlType="submit" onClick={() => this.props.onSubmits()}
loading={this.props.loadings}><span>{this.props.bottomvalue===undefined?"确 定":this.props.bottomvalue}</span></Button>
</div> </div>
</div> </div>
</div> </div>
@ -41,7 +45,6 @@ class Bottomsubmit extends Component {
} }
export default Bottomsubmit; export default Bottomsubmit;

@ -311,6 +311,7 @@ class TPMBanner extends Component {
console.log(error) console.log(error)
}); });
} }
cancel_publish = () => { cancel_publish = () => {
this.setState({ this.setState({
Modalstype: true, Modalstype: true,
@ -971,7 +972,7 @@ class TPMBanner extends Component {
</Modal> </Modal>
{shixunsDetails.shixun_status === 1 && this.props.identity < 5 ? {shixunsDetails.shixun_status === 2 && shixunsDetails.public===0 && this.props.identity < 5 ?
<a onClick={this.cancel_publish} className="fr user_default_btn user_blue_btn mr20 font-18 height39" <a onClick={this.cancel_publish} className="fr user_default_btn user_blue_btn mr20 font-18 height39"
id="challenge_begin">撤销发布</a> : "" id="challenge_begin">撤销发布</a> : ""
} }

@ -7,7 +7,7 @@ import TPMRightSection from './component/TPMRightSection';
import TPMNav from './component/TPMNav'; import TPMNav from './component/TPMNav';
import axios from 'axios'; import axios from 'axios';
import './tpmmodel/tpmmodel.css' import './tpmmodel/tpmmodel.css'
import {getUploadActionUrl} from 'educoder'; import {getUploadActionUrl,appendFileSizeToUploadFileAll} from 'educoder';
import moment from 'moment'; import moment from 'moment';
const confirm = Modal.confirm; const confirm = Modal.confirm;
@ -20,8 +20,8 @@ class TPMDataset extends Component {
columns: [ columns: [
{ {
title: '文件', title: '文件',
dataIndex: 'number', dataIndex: 'title',
key: 'number', key: 'title',
align: 'left', align: 'left',
className: " font-14 wenjiantit", className: " font-14 wenjiantit",
width: '220px', width: '220px',
@ -33,8 +33,8 @@ class TPMDataset extends Component {
}, },
{ {
title: '最后修改时间', title: '最后修改时间',
dataIndex: 'number', dataIndex: 'timedata',
key: 'number', key: 'timedata',
align: 'center', align: 'center',
className: "edu-txt-center font-14 zuihoushijian", className: "edu-txt-center font-14 zuihoushijian",
width: '150px', width: '150px',
@ -46,8 +46,8 @@ class TPMDataset extends Component {
}, },
{ {
title: '最后修改人', title: '最后修改人',
dataIndex: 'number', dataIndex: 'author',
key: 'number', key: 'author',
align: 'center', align: 'center',
className: "edu-txt-center font-14 ", className: "edu-txt-center font-14 ",
render: (text, record) => ( render: (text, record) => (
@ -58,8 +58,8 @@ class TPMDataset extends Component {
}, },
{ {
title: '文件大小', title: '文件大小',
dataIndex: 'number', dataIndex: 'filesize',
key: 'number', key: 'filesize',
align: 'center', align: 'center',
className: "edu-txt-center font-14 ", className: "edu-txt-center font-14 ",
render: (text, record) => ( render: (text, record) => (
@ -260,38 +260,26 @@ class TPMDataset extends Component {
if (index % 2 === 1) className = 'dark-row'; if (index % 2 === 1) className = 'dark-row';
return className; return className;
} }
// 附件相关 START
handleChange = (info) => { handleChange = (info) => {
if(info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') { if(info.file.status == "done" || info.file.status == "uploading" || info.file.status === 'removed'){
let {fileList} = this.state; let fileList = info.fileList;
if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') { // for(var list of fileList ){
console.log("handleChange1fileLists"); // console.log(list)
// if(fileList.length===0){ // }
let fileLists = info.fileList; this.setState({
// console.log(fileLists); fileList: appendFileSizeToUploadFileAll(fileList),
this.setState({ });
// fileList:appendFileSizeToUploadFileAll(fileList),
fileList: fileLists,
deleteisnot: false
});
}
if(info.file.status === 'done'){ if(info.file.status === 'done'){
//done 成功就会调用这个方法 //done 成功就会调用这个方法
this.getdatas(); this.getdatas();
// this.props.showNotification(`上传文件成功`); // this.props.showNotification(`上传文件成功`);
}else if(info.file.status === 'removed'){
// this.props.showNotification(`上传文件失败`);
}else if(info.file.status === 'uploading'){
// this.props.showNotification(`正在上传文件中`);
} }
} }
} }
onAttachmentRemove = (file) => { onAttachmentRemove = (file) => {
// debugger // debugger
if(!file.percent || file.percent == 100){ if(!file.percent || file.percent == 100){
@ -403,47 +391,27 @@ class TPMDataset extends Component {
width: 600, width: 600,
fileList, fileList,
data:{ data:{
attachtype: 2, attachtype: 2,
container_id:this.props.match.params.shixunId, container_id:this.props.match.params.shixunId,
container_type: "Shixun", container_type: "Shixun",
}, },
multiple: true, multiple: true,
// https://github.com/ant-design/ant-design/issues/15505 // https://github.com/ant-design/ant-design/issues/15505
// showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。 // showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。
// showUploadList: false, // showUploadList: false,
action: `${getUploadActionUrl()}`, action: `${getUploadActionUrl()}`,
onChange: this.handleChange, onChange: this.handleChange,
onRemove: this.onAttachmentRemove, onRemove: this.onAttachmentRemove,
beforeUpload: (file, fileList) => { beforeUpload: (file) => {
console.log('beforeUpload', file.name);
if (this.state.fileList.length >= 1) { const isLt150M = file.size / 1024 / 1024 < 150;
return false
}
// console.log('beforeUpload', file.name);
const isLt150M = file.size / 1024 / 1024 < 50;
if (!isLt150M) { if (!isLt150M) {
// this.props.showNotification(`文件大小必须小于50MB`); this.props.showNotification('文件大小必须小于150MB!');
notification.open(
{
message: '提示',
description:
'文件大小必须小于50MB',
}
)
}
if(this.state.file !== undefined){
console.log("763")
this.setState({
file:file
})
}else {
this.setState({
file:file
})
} }
return isLt150M; return isLt150M;
}, },
} };
return ( return (
<React.Fragment> <React.Fragment>
<div className="tpmComment educontent clearfix mt30 mb80"> <div className="tpmComment educontent clearfix mt30 mb80">
@ -460,13 +428,10 @@ class TPMDataset extends Component {
<div className="padding20 edu-back-white mt20 " style={{minHeight: '463px'}}> <div className="padding20 edu-back-white mt20 " style={{minHeight: '463px'}}>
<div className="sortinxdirection"> <div className="sortinxdirection">
{
data_sets_count>0?
<div className="tpmwidth"> <div className="tpmwidth">
<Checkbox checked={this.state.checked} onChange={this.mysonChange}>全选</Checkbox> <Checkbox checked={this.state.checked} onChange={this.mysonChange}>全选</Checkbox>
</div> </div>
:""
}
<div className="tpmwidth xaxisreverseorder"> <div className="tpmwidth xaxisreverseorder">
<style> <style>
@ -478,7 +443,7 @@ class TPMDataset extends Component {
` `
} }
</style> </style>
<div className="deletebuttom intermediatecenter "> <Upload {...uploadProps}><p className="deletebuttomtest"> <div className="deletebuttom intermediatecenter "> <Upload {...uploadProps}><p className="deletebuttomtest" type="upload">
上传文件</p> </Upload></div> 上传文件</p> </Upload></div>
{ {

@ -11,8 +11,6 @@ import {
Button, Button,
} from 'antd'; } from 'antd';
// import "antd/dist/antd.css";
import locale from 'antd/lib/date-picker/locale/zh_CN'; import locale from 'antd/lib/date-picker/locale/zh_CN';
import moment from 'moment'; import moment from 'moment';
@ -21,8 +19,8 @@ import axios from 'axios';
import './css/TPMsettings.css'; import './css/TPMsettings.css';
import {getImageUrl, toPath, getUrl, appendFileSizeToUploadFileAll, getUploadActionUrl} from 'educoder';
import {handleDateStrings} from "./oldTPMsettings"; import {handleDateStrings} from "./oldTPMsettings";
import Bottomsubmit from "../../modals/Bottomsubmit"; import Bottomsubmit from "../../modals/Bottomsubmit";
const $ = window.$; const $ = window.$;
@ -34,7 +32,6 @@ let currentValue;
const Option = Select.Option; const Option = Select.Option;
const RadioGroup = Radio.Group; const RadioGroup = Radio.Group;
const confirm = Modal.confirm;
function range(start, end) { function range(start, end) {
const result = []; const result = [];
@ -61,24 +58,25 @@ export default class Shixuninformation extends Component {
constructor(props) { constructor(props) {
super(props) super(props)
this.state = { this.state = {
can_copy:false, can_copy: false,
use_scope:0, use_scope: 0,
opening_time:null, opening_time: null,
opentime:false, opentime: false,
oldscope_partment:[], oldscope_partment: [],
scope_partment:[] scope_partment: [],
loading: false
} }
} }
componentDidMount() { componentDidMount() {
if (this.props.data) { if (this.props.data&&this.props.data.shixun) {
this.setState({ this.setState({
can_copy: this.props.data && this.props.data.shixun.can_copy === undefined ? false :this.props.data.shixun.can_copy, can_copy:this.props.data.shixun && this.props.data.shixun.can_copy === undefined ? false : this.props.data.shixun&&this.props.data.shixun.can_copy,
use_scope:this.props.data && this.props.data.shixun.use_scope, use_scope: this.props.data.shixun && this.props.data.shixun.use_scope,
opening_time: this.props.data && this.props.data.shixun.opening_time, opening_time: this.props.data.shixun && this.props.data.shixun.opening_time,
opentime:!this.props.data && this.props.data.shixun.opening_time?false:true, opentime: !this.props.data.shixun && this.props.data.shixun.opening_time ? false : true,
oldscope_partment:this.props.data&&this.props.data.shixun.scope_partment, oldscope_partment:this.props.data.shixun && this.props.data.shixun.scope_partment,
}) })
} }
@ -102,11 +100,11 @@ export default class Shixuninformation extends Component {
if (this.props.data) { if (this.props.data) {
this.setState({ this.setState({
can_copy: this.props.data && this.props.data.shixun.can_copy === undefined ? false :this.props.data.shixun.can_copy, can_copy: this.props.data.shixun && this.props.data.shixun.can_copy === undefined ? false : this.props.data.shixun&&this.props.data.shixun.can_copy,
use_scope:this.props.data && this.props.data.shixun.use_scope, use_scope: this.props.data.shixun&& this.props.data.shixun.use_scope,
opening_time: this.props.data && this.props.data.shixun.opening_time, opening_time: this.props.data.shixun && this.props.data.shixun.opening_time,
opentime:!this.props.data && this.props.data.shixun.opening_time?false:true, opentime: !this.props.data.shixun && this.props.data.shixun.opening_time ? false : true,
oldscope_partment:this.props.data&&this.props.data.shixun.scope_partment, oldscope_partment: this.props.data.shixun && this.props.data.shixun.scope_partment,
}) })
} }
@ -115,75 +113,85 @@ export default class Shixuninformation extends Component {
onChangeTimePicker = (value, dateString) => { onChangeTimePicker = (value, dateString) => {
this.setState({ this.setState({
opening_time: dateString === "" ? "" :handleDateStrings(dateString) opening_time: dateString === "" ? "" : handleDateStrings(dateString)
}) })
} }
onSubmits = () => { onSubmits = () => {
let {can_copy,use_scope,scope_partment,opening_time }=this.state; this.setState({
loading: true
})
let {can_copy, use_scope, scope_partment, opening_time} = this.state;
let id = this.props.match.params.shixunId; let id = this.props.match.params.shixunId;
let url=`/shixuns/${id}/update_permission_setting.json`; let url = `/shixuns/${id}/update_permission_setting.json`;
axios.post(url, axios.post(url,
{ {
scope_partment:scope_partment, scope_partment: scope_partment,
shixun:{ shixun: {
can_copy: can_copy, can_copy: can_copy,
use_scope:use_scope, use_scope: use_scope,
opening_time:opening_time opening_time: opening_time
}
} }
}
).then((response) => { ).then((response) => {
if(response.data.status===-1){ if (response.data.status === -1) {
}else{ } else {
this.props.getdatas() this.props.getdatas("3")
this.props.showNotification("保存成功") this.props.showNotification("权限配置保存成功!")
this.setState({
loading: false
})
} }
}).catch((error) => { }).catch((error) => {
console.log(error) this.setState({
loading: false
})
}) })
} }
CheckboxonChange=(e)=>{ CheckboxonChange = (e) => {
this.setState({ this.setState({
can_copy:e.target.checked can_copy: e.target.checked
}) })
} }
SelectOpenpublic=(e)=>{
SelectOpenpublic = (e) => {
this.setState({ this.setState({
use_scope: e.target.value use_scope: e.target.value
}); });
} }
shixunScopeInput = (e) => { shixunScopeInput = (e) => {
let {scope_partment,oldscope_partment} = this.state; let {scope_partment, oldscope_partment} = this.state;
let datalist = scope_partment; let datalist = scope_partment;
if (datalist===undefined) { if (datalist === undefined) {
datalist=[] datalist = []
} }
datalist.push(e) datalist.push(e)
let scopetype=false; let scopetype = false;
scope_partment.map((item,key)=>{ scope_partment.map((item, key) => {
if(item===e){ if (item === e) {
scopetype=true scopetype = true
} }
}) })
oldscope_partment.map((item,key)=>{ oldscope_partment.map((item, key) => {
if(item===e){ if (item === e) {
scopetype=true scopetype = true
} }
}) })
if(scopetype===false){ if (scopetype === false) {
this.setState({ this.setState({
scope_partment: datalist scope_partment: datalist
}); });
}else{ } else {
this.props.showNotification("请勿指定相同的单位") this.props.showNotification("请勿指定相同的单位")
} }
@ -221,11 +229,12 @@ export default class Shixuninformation extends Component {
}); });
} }
setopentime=(e)=>{ setopentime = (e) => {
this.setState({ this.setState({
opentime:e.target.checked opentime: e.target.checked
}) })
} }
render() { render() {
let options; let options;
@ -241,7 +250,7 @@ export default class Shixuninformation extends Component {
return ( return (
<div> <div>
<div className="educontent mb200 edu-back-white padding10-20 pdb30"> <div className="educontent mb200 edu-back-white padding10-20 pdb30 mb50">
<div className="clearfix ml40"> <div className="clearfix ml40">
<span className="color-grey-6 mt5 fl font-16 ml20" style={{minWidth: '45px'}}>复制:</span> <span className="color-grey-6 mt5 fl font-16 ml20" style={{minWidth: '45px'}}>复制:</span>
<span className="fl mt8 ml13"> <span className="fl mt8 ml13">
@ -253,9 +262,10 @@ export default class Shixuninformation extends Component {
</div> </div>
<div className="edu-back-white mb10 ml30 mt20"> <div className="edu-back-white mb10 ml30 mt20">
{this.props.data && this.props.data.shixun.use_scope === 0 &&this.props.data && this.props.data.shixun.status === 2?"": <div> {this.props.data.shixun && this.props.data.shixun.use_scope === 0 && this.props.data.shixun && this.props.data.shixun.status === 2 ? "" :
<span className="color-grey-6 mt5 fl font-16" style={{minWidth: '45px'}}>公开程度:</span> <div>
<span className="fl mt8 ml20"> <span className="color-grey-6 mt5 fl font-16" style={{minWidth: '45px'}}>公开程度:</span>
<span className="fl mt8 ml20">
<RadioGroup onChange={this.SelectOpenpublic} value={this.state.use_scope}> <RadioGroup onChange={this.SelectOpenpublic} value={this.state.use_scope}>
<Radio className="radioStyle" value={0}><span>对所有单位公开</span> <span <Radio className="radioStyle" value={0}><span>对所有单位公开</span> <span
className="color-grey-9">实训发布后所有用户可见</span></Radio> className="color-grey-9">实训发布后所有用户可见</span></Radio>
@ -264,7 +274,7 @@ export default class Shixuninformation extends Component {
</RadioGroup> </RadioGroup>
<div className="clearfix" id="unit-all" <div className="clearfix" id="unit-all"
style={{display:this.state.use_scope === 0 ? 'none' : 'block'}}> style={{display: this.state.use_scope === 0 ? 'none' : 'block'}}>
<div className="fl ml25"> <div className="fl ml25">
<div className="fl" id="unit-input-part" style={{width: '100%'}}> <div className="fl" id="unit-input-part" style={{width: '100%'}}>
<div id="person-unit" className="fl pr mr10"> <div id="person-unit" className="fl pr mr10">
@ -292,12 +302,12 @@ export default class Shixuninformation extends Component {
<div style={{width: '100%'}}> <div style={{width: '100%'}}>
<div className="mt20 clearfix" id="task_tag_content"> <div className="mt20 clearfix" id="task_tag_content">
{ {
this.state.oldscope_partment.map((item,key)=>{ this.state.oldscope_partment.map((item, key) => {
return ( return (
<li key={key} className={"fl mr20"}> <li key={key} className={"fl mr20"}>
<Button type="primary" ghost className={"Permanentban "}> <Button type="primary" ghost className={"Permanentban "}>
{item} {item}
</Button> </Button>
</li> </li>
) )
}) })
@ -307,9 +317,9 @@ export default class Shixuninformation extends Component {
return ( return (
<li key={key} className={"fl mr20"}> <li key={key} className={"fl mr20"}>
<Badge count={"x"} onClick={(key) => this.deleteScopeInput(key)}> <Badge count={"x"} onClick={(key) => this.deleteScopeInput(key)}>
<Button type="primary" ghost className={"Permanentban "}> <Button type="primary" ghost className={"Permanentban "}>
{item} {item}
</Button> </Button>
</Badge> </Badge>
</li> </li>
@ -331,31 +341,32 @@ export default class Shixuninformation extends Component {
</span> </span>
</div>} </div>}
<div className="clearfix mt20"> <div className="clearfix mt20">
<span className="color-grey-6 mt5 fl font-16" style={{minWidth: '45px'}}>开启时间:</span> <span className="color-grey-6 mt5 fl font-16" style={{minWidth: '45px'}}>开启时间:</span>
<span className="fl mt8 ml20"> <span className="fl mt8 ml20">
<Checkbox <Checkbox
checked={this.state.opentime=== undefined ? false : this.state.opentime} checked={this.state.opentime === undefined ? false : this.state.opentime}
onChange={this.setopentime}></Checkbox> onChange={this.setopentime}></Checkbox>
<label style={{top: '6px'}} className="color-grey-9 ml10">选中则学员在指定的开启时间后才能开启学习不选中则学员在实训发布后能立即开启学习</label> <label style={{top: '6px'}}
className="color-grey-9 ml10">选中则学员在指定的开启时间后才能开启学习不选中则学员在实训发布后能立即开启学习</label>
<div className={"both"}></div> <div className={"both"}></div>
{this.state.opentime===false?"":<div className="mt20 ml25"> {this.state.opentime === false ? "" : <div className="mt20 ml25">
<DatePicker <DatePicker
showToday={false} showToday={false}
showTime={{format: 'HH:mm'}} showTime={{format: 'HH:mm'}}
format="YYYY-MM-DD HH:mm" format="YYYY-MM-DD HH:mm"
width={178} width={178}
locale={locale} locale={locale}
disabledTime={disabledDateTime} disabledTime={disabledDateTime}
disabledDate={disabledDate} disabledDate={disabledDate}
placeholder="请输入开启时间" placeholder="请输入开启时间"
value={this.state.opening_time === null ||this.state.opening_time === "" ? "" : moment(this.state.opening_time, dateFormat)} value={this.state.opening_time === null || this.state.opening_time === "" ? "" : moment(this.state.opening_time, dateFormat)}
onChange={this.onChangeTimePicker} onChange={this.onChangeTimePicker}
dropdownClassName="hideDisable" dropdownClassName="hideDisable"
/> />
</div>} </div>}
</span> </span>
</div> </div>
@ -365,7 +376,7 @@ export default class Shixuninformation extends Component {
</div> </div>
{this.props.identity < 5 ? {this.props.identity < 5 ?
<Bottomsubmit {...this.props} {...this.state} url={`/shixuns/${this.props.match.params.shixunId}/challenges`} <Bottomsubmit {...this.props} {...this.state} url={`/shixuns/${this.props.match.params.shixunId}/challenges`}
onSubmits={this.onSubmits}/> : ""} onSubmits={this.onSubmits} loadings={this.state.loading} bottomvalue={ this.props.shixunsDetails&&this.props.shixunsDetails.is_jupyter === true?"确 定":"下一步"} /> : ""}
</div> </div>
); );
} }

@ -1,29 +1,14 @@
import React, {Component} from 'react'; import React, {Component} from 'react';
import { import {
Input,
Select,
Radio, Radio,
Checkbox, Checkbox,
Popconfirm,
message,
Modal,
Icon,
DatePicker,
Breadcrumb,
Upload,
Button,
notification,
Tooltip,
Tabs
} from 'antd'; } from 'antd';
import axios from 'axios'; import axios from 'axios';
import './css/TPMsettings.css'; import './css/TPMsettings.css';
import {getImageUrl, toPath, getUrl, appendFileSizeToUploadFileAll, getUploadActionUrl} from 'educoder';
import Bottomsubmit from "../../modals/Bottomsubmit"; import Bottomsubmit from "../../modals/Bottomsubmit";
const RadioGroup = Radio.Group; const RadioGroup = Radio.Group;
@ -32,144 +17,317 @@ const RadioGroup = Radio.Group;
export default class Shixuninformation extends Component { export default class Shixuninformation extends Component {
constructor(props) { constructor(props) {
super(props) super(props)
this.state = {} 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() { componentDidMount() {
if (this.props.data) { if (this.props.data&&this.props.data.shixun) {
this.setState({ this.setState({
vnc: this.props.data && this.props.data.shixun.vnc, vnc: this.props.data && this.props.data.shixun.vnc,
use_scope: this.props.data && this.props.data.shixun.use_scope, code_hidden: this.props.data && this.props.data.shixun.code_hidden,
opening_time: this.props.data && this.props.data.shixun.opening_time, forbid_copy: this.props.data && this.props.data.shixun.forbid_copy,
opentime: !this.props.data && this.props.data.shixun.opening_time ? false : true, hide_code: this.props.data && this.props.data.shixun.hide_code,
oldscope_partment: this.props.data && this.props.data.shixun.scope_partment, 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) {
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
// })
// }
}
} }
let departmentsUrl = `/shixuns/departments.json`; }
axios.get(departmentsUrl).then((response) => {
if (response.status === 200) {
if (response.data.message === undefined) { onSubmits = () => {
this.setState({ this.setState({
departmentslist: response.data.shools_name 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) => { }).catch((error) => {
console.log(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() { render() {
console.log(this.props) console.log(this.props)
return ( return (
<div> <div>
<div className="educontent mb200 edu-back-white padding10-20 pdb30"> <div className="educontent mb200 edu-back-white padding10-20 pdb30 mb50">
<div className="clearfix mb20"> {this.state.websshshow === true ? "" : <div className="clearfix mb20">
<span className="color-grey-6 mt5 fl font-16" style={{minWidth: '45px'}}>开启图形化界面</span> <span className="color-grey-6 mt5 fl font-16" style={{minWidth: '45px'}}>开启图形化界面</span>
<span className="fl mt8"> <span className="fl mt8">
<Checkbox <Checkbox
checked={this.state.can_copy} checked={this.state.vnc}
onChange={this.CheckboxonChange}></Checkbox> onChange={this.Checkvnc}></Checkbox>
<label style={{top: '6px'}} className="color-grey-9 ml10">选中则给学员的实践任务启动Ubuntu系统的图形化界面</label> <label style={{top: '6px'}} className="color-grey-9 ml10">选中则给学员的实践任务启动Ubuntu系统的图形化界面</label>
</span> </span>
</div> </div>}
<div className="clearfix mb20"> {this.state.vnc === true ? "" : <div className="clearfix mb20">
<span className="color-grey-6 mt5 fl font-16 ml64" style={{minWidth: '45px'}}>命令行</span> <span className="color-grey-6 mt5 fl font-16 ml64" style={{minWidth: '45px'}}>命令行</span>
<span className="fl mt8"> <span className="fl mt8">
<Checkbox <Checkbox
checked={this.state.can_copy} checked={this.state.websshshow}
onChange={this.CheckboxonChange}></Checkbox> onChange={this.Checkwebsshshow}></Checkbox>
<label style={{top: '6px'}} className="color-grey-9 ml10">选中则给学员的实践任务提供命令行窗口</label> <label style={{top: '6px'}} className="color-grey-9 ml10">选中则给学员的实践任务提供命令行窗口</label>
</span> </span>
</div> </div>}
<div> {this.state.vnc === true ? "" : this.state.websshshow === true ? <div>
<span className="fl ml160"> <span className="fl ml160">
<RadioGroup onChange={this.SelectOpenpublic} value={this.state.use_scope}> <RadioGroup onChange={this.opensshRadio} value={this.state.opensshRadio}>
<Radio className="radioStyle font-14" value={0}><span>命令行练习窗口</span> <span <Radio className="radioStyle font-14" value={1}><span>命令行练习窗口</span> <span
className="color-grey-9">选中则给学员提供用于练习操作的命令行命令行的操作不会对学生的实验环境造成影响</span></Radio> className="color-grey-9">选中则给学员提供用于练习操作的命令行命令行的操作不会对学生的实验环境造成影响</span></Radio>
<Radio className="radioStyle font-14" value={1}><span>命令行评测窗口</span> <span <Radio className="radioStyle font-14" value={2}><span>命令行评测窗口</span> <span
className="color-grey-9">选中则给学员提供用于评测操作的命令行命令行的操作可以对学生的实验环境产生影响</span></Radio> className="color-grey-9">选中则给学员提供用于评测操作的命令行命令行的操作可以对学生的实验环境产生影响</span></Radio>
</RadioGroup> </RadioGroup>
</span> </span>
<span className="fl ml180"> {this.state.opensshRadio === 2 ? <span className="fl ml180">
<div className="clearfix mb20"> <div className="clearfix mb20">
<span className="fl mt8 ml5"> <span className="fl mt8 ml5">
<Checkbox <Checkbox
checked={this.state.can_copy} checked={this.state.multi_webssh}
onChange={this.CheckboxonChange}></Checkbox> onChange={this.Checkmulti_webssh}></Checkbox>
<label style={{top: '6px'}} className="color-grey-9 ml10"> <label style={{top: '6px'}} className="color-grey-9 ml10">
<span className="color-grey-6 font-14" style={{minWidth: '45px'}}>多个命令行窗口</span> <span className="color-grey-6 font-14" style={{minWidth: '45px'}}>多个命令行窗口</span>
选中则允许学员同时开启多个命令行窗口</label> 选中则允许学员同时开启多个命令行窗口</label>
</span> </span>
</div> </div>
</span> </span> : ""}
</div> </div> : ""}
<div className="clearfix mb20"> {this.state.vnc === true ? "" : <div className="clearfix mb20">
<span className="color-grey-6 mt5 fl font-16 ml17" style={{minWidth: '45px'}}>隐藏代码窗口</span> <span className="color-grey-6 mt5 fl font-16 ml17" style={{minWidth: '45px'}}>隐藏代码窗口</span>
<span className="fl mt8"> <span className="fl mt8">
<Checkbox <Checkbox
checked={this.state.can_copy} checked={this.state.hide_code}
onChange={this.CheckboxonChange}></Checkbox> onChange={this.Checkhide_code}></Checkbox>
<label style={{top: '6px'}} className="color-grey-9 ml10">选中则学员页面不显示代码窗口</label> <label style={{top: '6px'}} className="color-grey-9 ml10">选中则学员页面不显示代码窗口</label>
</span> </span>
</div> </div>}
<div className="clearfix mb20"> {this.state.vnc === true || this.state.hide_code === true ? "" : <div className="clearfix mb20">
<span className="color-grey-6 mt5 fl font-16 ml33" style={{minWidth: '45px'}}>公开版本库</span> <span className="color-grey-6 mt5 fl font-16 ml33" style={{minWidth: '45px'}}>公开版本库</span>
<span className="fl mt8"> <span className="fl mt8">
<Checkbox <Checkbox
checked={this.state.can_copy} checked={this.state.is_secret_repository}
onChange={this.CheckboxonChange}></Checkbox> onChange={this.Checkis_secret_repository}></Checkbox>
<label style={{top: '6px'}} className="color-grey-9 ml10">选中则允许学员修改版本库中的全部文件</label> <label style={{top: '6px'}} className="color-grey-9 ml10">选中则允许学员修改版本库中的全部文件</label>
</span> </span>
</div> </div>}
<div className="clearfix mb20"> {this.state.vnc === true ? "" : <div className="clearfix mb20">
<span className="color-grey-6 mt5 fl font-16 ml20" style={{minWidth: '45px'}}>隐藏代码目录</span> <span className="color-grey-6 mt5 fl font-16 ml17" style={{minWidth: '45px'}}>隐藏代码目录</span>
<span className="fl mt8"> <span className="fl mt8">
<Checkbox <Checkbox
checked={this.state.can_copy} checked={this.state.code_hidden}
onChange={this.CheckboxonChange}></Checkbox> onChange={this.Checkcode_hidden}></Checkbox>
<label style={{top: '6px'}} className="color-grey-9 ml10">选中则学员页面不显示版本库目录</label> <label style={{top: '6px'}} className="color-grey-9 ml10">选中则学员页面不显示版本库目录</label>
</span> </span>
</div> </div>}
<div className="clearfix mb20"> {this.state.vnc === true ? "" : <div className="clearfix mb20">
<span className="color-grey-6 mt5 fl font-16 ml20" style={{minWidth: '45px'}}>禁用复制粘贴</span> <span className="color-grey-6 mt5 fl font-16 ml17" style={{minWidth: '45px'}}>禁用复制粘贴</span>
<span className="fl mt8"> <span className="fl mt8">
<Checkbox <Checkbox
checked={this.state.can_copy} checked={this.state.forbid_copy}
onChange={this.CheckboxonChange}></Checkbox> onChange={this.Checkforbid_copy}></Checkbox>
<label style={{top: '6px'}} className="color-grey-9 ml10">选中则学员页面不允许使用复制和粘贴功能</label> <label style={{top: '6px'}} className="color-grey-9 ml10">选中则学员页面不允许使用复制和粘贴功能</label>
</span> </span>
</div> </div>}
<div className="clearfix mb20"> <div className="clearfix mb20">
<span className="color-grey-6 mt5 fl font-16 ml84" style={{minWidth: '45px'}}>跳关</span> <span className="color-grey-6 mt5 fl font-16 ml80" style={{minWidth: '45px'}}>跳关</span>
<span className="fl mt8"> <span className="fl mt8">
<Checkbox <Checkbox
checked={this.state.can_copy} checked={this.state.task_pass}
onChange={this.CheckboxonChange}></Checkbox> onChange={this.Checktask_pass}></Checkbox>
<label style={{top: '6px'}} className="color-grey-9 ml10">选中则允许学员跳关学习实训关卡任务</label> <label style={{top: '6px'}} className="color-grey-9 ml10">选中则允许学员跳关学习实训关卡任务</label>
</span> </span>
</div> </div>
<div className="clearfix mb20"> <div className="clearfix mb20">
<span className="color-grey-6 mt5 fl font-16 ml36s" style={{minWidth: '45px'}}>测试集解锁</span> <span className="color-grey-6 mt5 fl font-16 ml32s" style={{minWidth: '45px'}}>测试集解锁</span>
<span className="fl mt8"> <span className="fl mt8">
<Checkbox <Checkbox
checked={this.state.can_copy} checked={this.state.test_set_permission}
onChange={this.CheckboxonChange}></Checkbox> onChange={this.Checktest_set_permission}></Checkbox>
<label style={{top: '6px'}} className="color-grey-9 ml10">选中则允许学员允许学员通过金币解锁查看隐藏测试集的内容</label> <label style={{top: '6px'}} className="color-grey-9 ml10">选中则允许学员允许学员通过金币解锁查看隐藏测试集的内容</label>
</span> </span>
</div> </div>
@ -179,7 +337,7 @@ export default class Shixuninformation extends Component {
{this.props.identity < 5 ? {this.props.identity < 5 ?
<Bottomsubmit {...this.props} {...this.state} url={`/shixuns/${this.props.match.params.shixunId}/challenges`} <Bottomsubmit {...this.props} {...this.state} url={`/shixuns/${this.props.match.params.shixunId}/challenges`}
onSubmits={this.onSubmits}/> : ""} onSubmits={this.onSubmits} loadings={this.state.loading}/> : ""}
</div> </div>
); );
} }

@ -44,7 +44,8 @@ class Shixuninformation extends Component {
Executiveordervalue: "", Executiveordervalue: "",
Compilecommandvalue: "", Compilecommandvalue: "",
shixun_service_configs: undefined, shixun_service_configs: undefined,
fileList:[] fileList:[],
loading:false,
} }
} }
@ -54,33 +55,37 @@ class Shixuninformation extends Component {
componentDidUpdate(prevProps, prevState) { componentDidUpdate(prevProps, prevState) {
if (prevProps.data != this.props.data) { if (prevProps.data != this.props.data) {
if (this.props.data) { if (this.props.data&&this.props.data.shixun) {
this.setState({ this.setState({
shixunName: this.props.data.shixun.name, shixunName: this.props.data.shixun&&this.props.data.shixun.name,
trainee: this.props.data.shixun.trainee, trainee:this.props.data.shixun&&this.props.data.shixun.trainee,
choice_main_type: this.props.data.shixun.choice_main_type, choice_main_type: this.props.data.shixun&&this.props.data.shixun.choice_main_type,
choice_small_type: this.props.data.shixun.choice_small_type, choice_small_type: this.props.data.shixun&&this.props.data.shixun.choice_small_type,
choice_standard_scripts: this.props.data.shixun.choice_standard_scripts, choice_standard_scripts:this.props.data.shixun&&this.props.data.shixun.choice_standard_scripts,
shixunmemoMDvalue: this.props.data.shixun.evaluate_script, shixunmemoMDvalue: this.props.data.shixun&&this.props.data.shixun.evaluate_script,
simichecked: this.props.data.shixun.is_secret_repository, simichecked: this.props.data.shixun&&this.props.data.shixun.is_secret_repository,
shixun_service_configs: this.props.data.shixun.shixun_service_configs, shixun_service_configs: this.props.data.shixun&&this.props.data.shixun.shixun_service_configs,
standard_scripts:this.props.data.shixun.standard_scripts, standard_scripts:this.props.data.shixun&&this.props.data.shixun.standard_scripts,
shixun_service_configlist:this.props.data.shixun.shixun_service_configs, shixun_service_configlist:this.props.data.shixun&&this.props.data.shixun.shixun_service_configs,
}) })
if(this.props.data.shixun.choice_standard_scripts===null){ if(this.props.data.shixun&&this.props.data.shixun.choice_standard_scripts===null){
this.setState({ this.setState({
choice_standard_scripts:{id: this.props.data.shixun.standard_scripts[0].id, value: ""}, choice_standard_scripts:{id: this.props.data.shixun&&this.props.data.shixun.standard_scripts[0].id, value: ""},
choice_standard_scriptssum:this.props.data.shixun.standard_scripts[0].id choice_standard_scriptssum:this.props.data.shixun&&this.props.data.shixun.standard_scripts[0].id
}) })
this.props.form.setFieldsValue({ this.props.form.setFieldsValue({
selectscripts:this.props.data.shixun.standard_scripts[0].id selectscripts:this.props.data.shixun&&this.props.data.shixun.standard_scripts[0].id
})
this.get_mirror_script(this.props.data.shixun&&this.props.data.shixun.standard_scripts[0].id)
}else{
this.props.form.setFieldsValue({
selectscripts:this.props.data.shixun&&this.props.data.shixun.choice_standard_scripts
}) })
this.get_mirror_script(this.props.data.shixun.standard_scripts[0].id)
} }
let newlist = "" let newlist = ""
this.props.data.shixun.choice_small_type.map((item, key) => { this.props.data.shixun&&this.props.data.shixun.choice_small_type.map((item, key) => {
this.props.data.shixun.small_type.map((i,k)=>{ this.props.data.shixun.small_type.map((i,k)=>{
if (item===i.id) { if (item===i.id) {
newlist = newlist + `${i.description}` newlist = newlist + `${i.description}`
@ -91,8 +96,8 @@ class Shixuninformation extends Component {
subvalues: newlist subvalues: newlist
}) })
this.props.data.shixun.main_type.map((item,key)=>{ this.props.data.shixun&&this.props.data.shixun.main_type.map((item,key)=>{
if(item.id===this.props.data.shixun.choice_main_type){ if(item.id===this.props.data.shixun&&this.props.data.shixun.choice_main_type){
this.setState({ this.setState({
mainvalues:item.description, mainvalues:item.description,
}) })
@ -100,12 +105,12 @@ class Shixuninformation extends Component {
}) })
this.props.form.setFieldsValue({ this.props.form.setFieldsValue({
name: this.props.data.shixun.name, name:this.props.data.shixun&&this.props.data.shixun.name,
trainee: this.props.data.shixun.trainee, trainee: this.props.data.shixun&&this.props.data.shixun.trainee,
selectleft: this.props.data.shixun.choice_main_type, selectleft: this.props.data.shixun&&this.props.data.shixun.choice_main_type,
selectright:this.props.data.shixun.choice_small_type, selectright:this.props.data.shixun&&this.props.data.shixun.choice_small_type,
}) })
this.contentMdRef.current.setValue(this.props.data.shixun.description); this.contentMdRef.current.setValue(this.props.data.shixun&&this.props.data.shixun.description);
} }
} }
} }
@ -641,6 +646,9 @@ class Shixuninformation extends Component {
} }
onSubmits=()=>{ onSubmits=()=>{
this.setState({
loading:true
})
const mdContnet = this.contentMdRef.current.getValue().trim(); const mdContnet = this.contentMdRef.current.getValue().trim();
let{choice_standard_scriptssum,choice_standard_scripts}=this.state; let{choice_standard_scriptssum,choice_standard_scripts}=this.state;
this.props.form.validateFieldsAndScroll((err, values) => { this.props.form.validateFieldsAndScroll((err, values) => {
@ -673,18 +681,29 @@ class Shixuninformation extends Component {
axios.put(url, data).then((result) => { axios.put(url, data).then((result) => {
if (result) { if (result) {
if (result.data) { if (result.data) {
this.props.getdatas() this.props.getdatas("2")
if(result.data.shixun_identifier){ if(result.data.shixun_identifier){
this.props.showNotification("基本信息更新成功") this.props.showNotification("基本信息更新成功!")
this.setState({
loading:false
})
} }
} }
} }
}).catch((error) => { }).catch((error) => {
// ////console.log(error) this.setState({
loading:false
})
}); });
}else{
this.setState({
loading:false
})
} }
}); });
this.setState({
loading: false
})
} }
Selectthestudent = (value) => { Selectthestudent = (value) => {
@ -822,7 +841,7 @@ class Shixuninformation extends Component {
> >
{ {
this.props.data === undefined ? "" : this.props.data.shixun.main_type.map((item, key) => { this.props.data === undefined ? "" : this.props.data.shixun&&this.props.data.shixun.main_type.map((item, key) => {
return ( return (
<Option value={item.id} key={key} name={item.description}> <Option value={item.id} key={key} name={item.description}>
<Tooltip placement="right" title={item.description === "" ? "无描述" : item.description}> <Tooltip placement="right" title={item.description === "" ? "无描述" : item.description}>
@ -851,7 +870,7 @@ class Shixuninformation extends Component {
<div className=" fl pr mr20"> <div className=" fl pr mr20">
<Select placeholder="请选择小类别" <Select placeholder="请选择小类别"
mode="multiple" mode="multiple"
value={this.state.choice_small_type.length === 0 || this.state.choice_small_type[0] === "" || this.state.choice_small_type === [] ? undefined : this.state.choice_small_type} value={!this.state.choice_small_type?undefined:this.state.choice_small_type.length === 0 || this.state.choice_small_type[0] === "" || this.state.choice_small_type === [] ? undefined : this.state.choice_small_type}
style={{width: 200}} style={{width: 200}}
disabled={operateauthority ? false : true} disabled={operateauthority ? false : true}
onDeselect={operateauthority ? this.Deselectlittle : ""} onDeselect={operateauthority ? this.Deselectlittle : ""}
@ -860,7 +879,7 @@ class Shixuninformation extends Component {
defaultOpen={false} defaultOpen={false}
> >
{ {
this.props.data === undefined ? "" : this.props.data.shixun.small_type.map((item, key) => { this.props.data === undefined ? "" : this.props.data.shixun&&this.props.data.shixun.small_type.map((item, key) => {
return ( return (
<Option value={item.id} key={key} name={item.description}> <Option value={item.id} key={key} name={item.description}>
<Tooltip placement="right" title={item.description === "" ? "无描述" : item.description}> <Tooltip placement="right" title={item.description === "" ? "无描述" : item.description}>
@ -997,7 +1016,8 @@ class Shixuninformation extends Component {
{ this.props.shixunsDetails&&this.props.shixunsDetails.is_jupyter===true?"": <span className="ant-form-text mt20">私密版本库 { this.props.shixunsDetails&&this.props.shixunsDetails.is_jupyter===true?"": <span className="ant-form-text mt20">私密版本库
<Checkbox onChange={this.simionChange} <Checkbox onChange={this.simionChange}
value={this.state.simichecked}>若需要对学员隐藏部分版本库内容时请选中选中保存后表示启用私密版本库请将需要对学员隐藏的文件存储在私密版本库</Checkbox> value={this.state.simichecked}>
{this.state.simichecked===false?"(若需要对学员隐藏部分版本库内容时,请选中;选中保存后表示启用私密版本库,请将需要对学员隐藏的文件存储在私密版本库)":"已创建的私密版本库及其内容,将在“保存”时被删除"}</Checkbox>
</span>} </span>}
{this.props.identity < 3 ? <div className="edu-back-white padding40-20 mb20"> {this.props.identity < 3 ? <div className="edu-back-white padding40-20 mb20">
@ -1064,6 +1084,24 @@ class Shixuninformation extends Component {
} }
</style> : ""} </style> : ""}
{/*<Modal*/}
{/* keyboard={false}*/}
{/* title="提示"*/}
{/* visible={this.state.simicheckedtype}*/}
{/* closable={false}*/}
{/* footer={null}*/}
{/*>*/}
{/* <div className="task-popup-content">*/}
{/* <p className="task-popup-text-center font-16">已创建的私密版本库及其内容,将在“保存”时被删除</p>*/}
{/* <p className="task-popup-text-center font-16">是否确认取消选择?</p>*/}
{/* </div>*/}
{/* <div className="task-popup-submit clearfix mt10">*/}
{/* <a onClick={() => this.hidesimichecked()} className="task-btn fl">取消</a>*/}
{/* <a className="task-btn task-btn-orange fr" onClick={() => this.getsimichecked()}>确定</a>*/}
{/* </div>*/}
{/*</Modal>*/}
<Modal <Modal
keyboard={false} keyboard={false}
title="提示" title="提示"
@ -1082,6 +1120,7 @@ class Shixuninformation extends Component {
</div> </div>
</Modal> </Modal>
<Modal <Modal
keyboard={false} keyboard={false}
title="提示" title="提示"
@ -1215,7 +1254,7 @@ class Shixuninformation extends Component {
</div> </div>
{this.props.identity < 5 ? {this.props.identity < 5 ?
<Bottomsubmit {...this.props} {...this.state} url={`/shixuns/${this.props.match.params.shixunId}/challenges`} <Bottomsubmit {...this.props} {...this.state} url={`/shixuns/${this.props.match.params.shixunId}/challenges`}
onSubmits={this.onSubmits}/> : ""} onSubmits={this.onSubmits} loadings={this.state.loading} bottomvalue={"下一步"} /> : ""}
</div> </div>
); );

@ -24,42 +24,51 @@ const {TabPane} = Tabs;
export default class TPMsettings extends Component { export default class TPMsettings extends Component {
constructor(props) { constructor(props) {
super(props) super(props)
this.state = {} this.state = {
activeKeys:"1"
}
} }
componentDidMount() { componentDidMount() {
this.getdatas() this.getdatas("1")
} }
getdatas=()=>{ getdatas = (key) => {
let id = this.props.match.params.shixunId; let id = this.props.match.params.shixunId;
let Url = `/shixuns/` + id + `/settings.json`; let Url = `/shixuns/` + id + `/settings.json`;
axios.get(Url).then((response) => { axios.get(Url).then((response) => {
// alert(response.data.shixun.choice_standard_scripts) // alert(response.data.shixun.choice_standard_scripts)
if (response.status === 200) { if (response.status === 200) {
this.setState({ this.setState({
data: response.data data: response.data
}) })
if (response.data.shixun.multi_webssh === true) {
this.setState({
SelectTheCommandtype: true
})
} else {
this.setState({
SelectTheCommandtype: false
})
}
if (response.data.shixun.scope_partment.length > 0) { if (response.data.shixun.scope_partment.length > 0) {
this.setState({ this.setState({
scopetype: true scopetype: true
}) })
} }
} }
}); });
if(key==="3"&&this.props.shixunsDetails&&this.props.shixunsDetails.is_jupyter === true){
this.props.history.replace(`/shixuns/${this.props.match.params.shixunId}/challenges`);
}else{
if(key){
this.setState({
activeKeys:key
})
}else{
this.props.history.replace(`/shixuns/${this.props.match.params.shixunId}/challenges`);
}
}
} }
operateshixuns = (value) => { operateshixuns = (value) => {
@ -111,9 +120,22 @@ export default class TPMsettings extends Component {
}) })
} }
callback = (key) => {
this.setState({
activeKeys:key
})
}
render() { render() {
let showtabs= this.props.shixunsDetails === undefined ?"":this.props.shixunsDetails.is_jupyter===true?"":"学习页面设置" let showtabs = this.props.shixunsDetails === undefined ? "" : this.props.shixunsDetails.is_jupyter === true ? "" : "学习页面设置"
// 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 ( return (
<div> <div>
@ -130,10 +152,14 @@ export default class TPMsettings extends Component {
.ant-tabs-nav{ .ant-tabs-nav{
margin-left:20px; margin-left:20px;
} }
.ant-tabs-nav .ant-tabs-tab {
font-size: 16px;
}
` `
} }
</style> </style>
<Tabs animated={false} tabBarExtraContent={
<Tabs activeKey={this.state.activeKeys} onChange={this.callback} animated={false} tabBarExtraContent={
<div className={"mb20 mr20"}> <div className={"mb20 mr20"}>
{ {
@ -161,22 +187,24 @@ export default class TPMsettings extends Component {
<TopShixuninformation <TopShixuninformation
{...this.state} {...this.state}
{...this.props} {...this.props}
getdatas={()=>this.getdatas()} getdatas={(key) => this.getdatas(key)}
/> />
</TabPane> </TabPane>
<TabPane tab="权限配置" key="2"> <TabPane tab="权限配置" key="2">
<Configuration <Configuration
{...this.state} {...this.state}
{...this.props} {...this.props}
getdatas={()=>this.getdatas()} getdatas={(key) => this.getdatas(key)}
/> />
</TabPane> </TabPane>
{/*{ this.props.shixunsDetails===undefined?"":this.props.shixunsDetails.is_jupyter===true?"":<TabPane tab={showtabs} key="3">*/} {this.props.shixunsDetails === undefined ? "" : this.props.shixunsDetails.is_jupyter === false ?
{/* <LearningSettings*/} <TabPane tab={showtabs} key="3">
{/* {...this.state}*/} <LearningSettings
{/* {...this.props}*/} {...this.state}
{/* />*/} {...this.props}
{/*</TabPane>}*/} getdatas={(key) => this.getdatas(key)}
/>
</TabPane>:"" }
</Tabs> </Tabs>
<Modal <Modal
keyboard={false} keyboard={false}

@ -136,12 +136,12 @@ a.newuse_scope-btn {
display: inline-block; display: inline-block;
} }
.ml84{ .ml81{
margin-left:84px; margin-left:81px;
} }
.ml36s{ .ml32s{
margin-left: 36px; margin-left: 32px;
} }
.ml64{ .ml64{

@ -88,6 +88,9 @@ class Newshixuns extends Component {
}; };
handleSubmit = (e) => { handleSubmit = (e) => {
this.setState({
loading: true
})
const mdContnet = this.contentMdRef.current.getValue().trim(); const mdContnet = this.contentMdRef.current.getValue().trim();
this.props.form.validateFieldsAndScroll((err, values) => { this.props.form.validateFieldsAndScroll((err, values) => {
if (!err) { if (!err) {
@ -110,19 +113,21 @@ class Newshixuns extends Component {
// window.open("/shixuns/"+response.data.shixun_identifier+"/challenges"); // window.open("/shixuns/"+response.data.shixun_identifier+"/challenges");
} else { } else {
this.setState({ this.setState({
bottonloading: false loading: true
}) })
} }
}).catch((error) => { }).catch((error) => {
console.log(error)
this.setState({ this.setState({
bottonloading: false loading: true
}) })
}) })
} }
}); });
this.setState({
loading: false
})
}; };
Selectthestudent = (value) => { Selectthestudent = (value) => {
this.props.form.setFieldsValue({ this.props.form.setFieldsValue({
@ -460,7 +465,7 @@ class Newshixuns extends Component {
> >
{getFieldDecorator('name', { {getFieldDecorator('name', {
rules: [{ rules: [{
required: true, message: '请输入选题名称', required: true, message: '请输入名称',
}, { }, {
max: 60, message: '请输入名称最大限制60个字符', max: 60, message: '请输入名称最大限制60个字符',
}, { }, {
@ -703,7 +708,7 @@ class Newshixuns extends Component {
</div> </div>
</div> </div>
</div> </div>
<Bottomsubmit {...this.props} {...this.state} url={"/shixuns"} onSubmits={() => this.handleSubmit()}/> <Bottomsubmit {...this.props} {...this.state} url={"/shixuns"} onSubmits={() => this.handleSubmit()} loadings={this.state.loading}/>
</div> </div>
); );

@ -79,7 +79,7 @@ class Challengesjupyter extends Component {
setTimeout(() => { setTimeout(() => {
this.setState({ this.setState({
jupyter_url:response.data.url, jupyter_url:response.data.url,
jupyter_port:response.data.port, jupyter_port:response.data.port,
booljupyterurls:true, booljupyterurls:true,
}) })
@ -176,6 +176,7 @@ class Challengesjupyter extends Component {
} }
}).catch((error) => { }).catch((error) => {
}) })
} }
@ -211,9 +212,9 @@ class Challengesjupyter extends Component {
booljupyterurls===true? booljupyterurls===true?
( (
this.state.jupyter_url === null? this.state.jupyter_url === null?
<div className="mt50"> <div className="mt50 intermediatecenter">
{/*<span className="icon iconfont icon-jiazaishibai1"></span>*/}
<p className="intermediatecenter sortinxdirection"><p className="colorbluetest">加载实训出错是否</p><p className="colorbluetwo" onClick={()=>this.updatamakedowns()}></p></p> <p className="intermediatecenter sortinxdirection mt5"><p className="colorbluetest">加载实训出错是否</p><p className="colorbluetwo" onClick={()=>this.updatamakedowns()}></p></p>
</div> </div>
@ -243,6 +244,8 @@ class Challengesjupyter extends Component {
display: flex; display: flex;
flex-direction:row-reverse; flex-direction:row-reverse;
} }
;
}
` `
} }
</style> </style>
@ -255,7 +258,7 @@ class Challengesjupyter extends Component {
<div className="sortinxdirection mt60"> <div className="sortinxdirection mt60">
<div className="renwuxiangssi sortinxdirection"> <div className="renwuxiangssi sortinxdirection">
<div><p className="renwuxiangqdiv">任务详情</p></div> <div><p className="renwuxiangqdiv">任务详情</p></div>
<div><p className="renwuxiangqdivtest ml24">请将实训题目写在下方</p></div> <div><p className="renwuxiangqdivtest ml24 shixunbingbaocun">请将实训题目写在下方并保存</p></div>
</div> </div>
<div className="renwuxiangssit xaxisreverseorder"> <div className="renwuxiangssit xaxisreverseorder">
<div className="challenbaocun" onClick={() => this.modifyjupyter(this.state)}><p <div className="challenbaocun" onClick={() => this.modifyjupyter(this.state)}><p
@ -286,6 +289,10 @@ class Challengesjupyter extends Component {
#header{ #header{
visibility:hidden; visibility:hidden;
} }
` `
} }
</style> </style>

@ -75,3 +75,13 @@
font-size: 12px; font-size: 12px;
cursor:default cursor:default
} }
.shixunbingbaocun{
font-size:14px;
color:#888888;
}
.intermediatecenter{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save