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

dev_aliyun_beta
SylorHuang 6 years ago
commit a6f83cf601

@ -13,6 +13,9 @@
//= require bootstrap-datepicker
//= require bootstrap.viewer
//= require lib/codemirror
//= require mode/shell/shell
//= require_tree ./i18n
//= require_tree ./admins
@ -32,11 +35,19 @@ $(document).on('turbolinks:load', function(){
// flash alert提示框自动关闭
if($('.admin-alert-container .alert').length > 0){
setTimeout(function(){
$('.admin-alert-container .alert').alert('close');
$('.admin-alert-container .alert:not(.alert-danger)').alert('close');
}, 2000);
setTimeout(function(){
$('.admin-alert-container .alert.alert-danger').alert('close');
}, 5000);
}
});
$(document).on("turbolinks:before-cache", function () {
$('[data-toggle="tooltip"]').tooltip('hide');
$('[data-toggle="popover"]').popover('hide');
});
// var progressBar = new Turbolinks.ProgressBar();
// $(document).on('ajax:send', function(event){

@ -0,0 +1,19 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-mirror-repositories-edit-page, body.admins-mirror-repositories-update-page').length > 0) {
var $form = $('form.edit-mirror');
$form.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
"mirror_repository[type_name]": {
required: true
}
}
});
$form.submit(function(e){
if(!$form.valid()){ e.preventDefault(); }
});
}
});

@ -0,0 +1,4 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-mirror-repositories-index-page').length > 0) {
}
});

@ -0,0 +1,33 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-mirror-scripts-edit-page, body.admins-mirror-scripts-update-page, body.admins-mirror-scripts-new-page, body.admins-mirror-scripts-create-page').length > 0) {
var $form = $('form.script-form');
// codemirror编辑器
var scriptEditor = CodeMirror.fromTextArea(document.getElementById('mirror_script_script'), {
lineNumbers: true,
mode: 'shell',
theme: "default",
indentUnit: 4, //代码缩进为一个tab的距离
matchBrackets: true,
autoRefresh: true,
smartIndent: true,//智能换行
styleActiveLine: true,
lint: true
});
scriptEditor.setSize('auto', '600px');
$form.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
"mirror_script[script_type]": {
required: true
}
}
});
$form.submit(function(e){
if(!$form.valid()){ e.preventDefault(); }
});
}
});

@ -0,0 +1,32 @@
$(document).on('turbolinks:load', function() {
$('.admin-modal-container').on('show.bs.modal', '.modal.admin-choose-mirror-modal', function(){
var $modal = $('.modal.admin-choose-mirror-modal');
var $form = $modal.find('form.admin-choose-mirror-form');
var validateForm = function(){
var checkedValue = $form.find('input[name="mirror_number"]:checked').val();
if(checkedValue == undefined){
$modal.find('.error').html('必须选择一种镜像保存!');
return false;
}
return true;
}
$modal.on('click', '.submit-btn', function(){
$form.find('.error').html('');
var url = $form.attr('action');
if (validateForm()) {
$.ajax({
method: 'POST',
dataType: 'script',
url: url,
data: $form.serialize(),
}).done(function(){
$modal.modal('hide');
});
}
});
})
});

@ -0,0 +1,89 @@
$(document).on('turbolinks:load', function() {
var $modal = $('.modal.admin-replace-mirror-modal');
if ($modal.length > 0) {
var $form = $modal.find('form.admin-replace-mirror-form');
var $mirrorIdInput = $modal.find('.modal-body input[name="mirror_id"]');
var $mirrorSelect = $modal.find('.new-mirror-select');
var setMirror = function(id, name){
$mirrorIdInput.val(id);
$form.find('.mirror-id-container').html(id);
$form.find('.mirror-name-container').html(name);
}
$form.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
new_mirror_id: {
required: true
},
},
messages: {
new_mirror_id: {
required: '请选择新镜像'
}
}
});
// modal ready fire
$modal.on('show.bs.modal', function (event) {
var $link = $(event.relatedTarget);
var mirrorId = $link.data('id');
var mirrorName = $link.data('name');
setMirror(mirrorId, mirrorName);
$mirrorSelect.select2('val', ' ');
});
$modal.on('hide.bs.modal', function () {
setMirror('', '');
$mirrorSelect.select2('val', ' ');
$('#new_mirror_id-error').remove();
});
$mirrorSelect.select2({
theme: 'bootstrap4',
placeholder: '输入要合并的镜像名',
minimumInputLength: 1,
ajax: {
url: '/admins/mirror_repositories/for_select',
dataType: 'json',
data: function(params){
return { keyword: params.term };
},
processResults: function(data){
return { results: data.mirrors }
}
},
templateResult: function (item) {
if(!item.id || item.id === '') return item.text;
return item.name;
},
templateSelection: function(item){
if (item.id) {
$('#new_mirror_id-error').remove();
$('#new_mirror_id').val(item.id);
}
return item.name || item.text;
}
});
$modal.on('click', '.submit-btn', function(){
$form.find('.error').html('');
if ($form.valid()) {
var url = $form.data('url');
$.ajax({
method: 'POST',
dataType: 'script',
url: url,
data: $form.serialize(),
}).done(function(){
$modal.modal('hide');
});
}
});
}
});

@ -5,6 +5,7 @@
@import "select2-bootstrap4.min";
@import "bootstrap-datepicker";
@import "bootstrap-datepicker.standalone";
@import "lib/codemirror";
@import "admins/*";
@ -47,4 +48,9 @@ label.error {
.flex-1 {
flex: 1;
}
}
.font-12 { font-size: 12px !important; }
.font-14 { font-size: 14px !important; }
.font-16 { font-size: 16px !important; }
.font-18 { font-size: 18px !important; }

@ -106,5 +106,9 @@
padding: 0.5rem 2rem;
}
}
.CodeMirror {
border: 1px solid #ced4da;
}
}

@ -0,0 +1,11 @@
class Admins::ChooseMirrorRepositoriesController < Admins::BaseController
def new
@mirror = MirrorRepository.find(params[:mirror_id])
@new_mirror = MirrorOperationRecord.where(mirror_repository_id: @mirror.id, status: 1, user_id: -1).first
end
def create
mirror = MirrorRepository.find(params[:mirror_id])
Admins::ChooseMirrorService.call(mirror, current_user, params[:mirror_number])
end
end

@ -0,0 +1,97 @@
class Admins::MirrorRepositoriesController < Admins::BaseController
before_action :check_shixun_mirrors!, only: [:index]
def index
mirrors = MirrorRepository.all
mirrors = mirrors.reorder(status: :desc, main_type: :desc, type_name: :asc)
@mirrors = paginate mirrors.includes(:mirror_scripts)
@error_mirror_names = MirrorRepository.where(status: 5).pluck(:name)
end
def new
@mirror = MirrorRepository.new
end
def create
@mirror = MirrorRepository.new
Admins::SaveMirrorRepositoryService.call(@mirror, current_user, form_params)
flash[:success] = '保存成功'
redirect_to edit_admins_mirror_repository_path(@mirror)
rescue ActiveRecord::RecordInvalid
flash.now[:danger] = '保存失败'
render 'new'
rescue Admins::SaveMirrorRepositoryService::Error => ex
flash.now[:danger] = ex.message
render 'new'
end
def edit
@mirror = current_mirror
end
def update
@mirror = current_mirror
Admins::SaveMirrorRepositoryService.call(current_mirror, current_user, form_params)
flash[:success] = '保存成功'
redirect_to edit_admins_mirror_repository_path(current_mirror)
rescue ActiveRecord::RecordInvalid
flash.now[:danger] = '保存失败'
render 'edit'
rescue Admins::SaveMirrorRepositoryService::Error => ex
flash.now[:danger] = ex.message
render 'edit'
end
def destroy
return render_js_error('该状态下不允许删除') unless current_mirror.deletable?
current_mirror.destroy!
render_delete_success
end
def for_select
mirrors = MirrorRepository.all
keyword = params[:keyword].to_s.strip
mirrors = mirrors.where('name LIKE ?', "%#{keyword}%") if keyword.present?
@mirrors = paginate mirrors
render_ok(count: @mirrors.total_count, mirrors: @mirrors.as_json(only: %i[id name]))
end
def merge
origin_mirror = MirrorRepository.find(params[:mirror_id])
mirror = MirrorRepository.find(params[:new_mirror_id])
ActiveRecord::Base.transaction do
origin_mirror.update!(name: mirror.name, mirrorID: mirror.mirrorID)
mirror.destroy!
end
end
private
def current_mirror
@_current_mirror ||= MirrorRepository.find(params[:id])
end
def form_params
columns = %i[type_name main_type time_limit resource_limit cpu_limit memory_limit description status]
params.require(:mirror_repository).permit(*columns)
end
def check_shixun_mirrors!
return
return unless request.format.html?
Admins::CheckShixunMirrorsService.call
rescue Admins::CheckShixunMirrorsService::Error => e
internal_server_error(e.message)
end
end

@ -0,0 +1,59 @@
class Admins::MirrorScriptsController < Admins::BaseController
helper_method :current_mirror
def index
scripts = current_mirror.mirror_scripts.order(updated_at: :desc)
@scripts = paginate scripts
end
def new
@script = current_mirror.mirror_scripts.new
end
def create
@script = current_mirror.mirror_scripts.new(form_params)
if @script.save
flash[:success] = '保存成功'
redirect_to edit_admins_mirror_repository_mirror_script_path(current_mirror, @script)
else
flash[:danger] = '保存失败'
render 'new'
end
end
def edit
@script = current_script
end
def update
@script = current_script
if @script.update(form_params)
flash[:success] = '保存成功'
redirect_to edit_admins_mirror_repository_mirror_script_path(current_mirror, @script)
else
flash[:danger] = '保存失败'
render 'edit'
end
end
def destroy
current_script.destroy!
render_delete_success
end
private
def current_script
@_current_script ||= current_mirror.mirror_scripts.find(params[:id])
end
def current_mirror
@_current_mirror ||= MirrorRepository.find(params[:mirror_repository_id])
end
def form_params
params.require(:mirror_script).permit(:script_type, :description, :script)
end
end

@ -416,7 +416,7 @@ class ApplicationController < ActionController::Base
# 实训主类别列表,自带描述
def shixun_main_type
list = []
mirrors = MirrorRepository.select([:id, :type_name]).published_main_mirror
mirrors = MirrorRepository.select([:id, :type_name, :description]).published_main_mirror
mirrors.try(:each) do |mirror|
list << {id: mirror.id, type_name: mirror.type_name, description: mirror.try(:description)}
end

@ -74,6 +74,7 @@ class ChallengesController < ApplicationController
ChallengeTag.create(:name => tag, :challenge_choose_id => @challenge_choose.id, :challenge_id => @challenge.id)
end
end
@challenge.update_column(:score, @challenge.challenge_chooses.sum(:score))
end
rescue Exception => e
raise ActiveRecord::Rollback
@ -95,6 +96,7 @@ class ChallengesController < ApplicationController
@challenge.update_column(:modify_time, Time.now)
end
@challenge_choose.update_attributes(chooce_params)
@challenge.update_column(:score, @challenge.challenge_chooses.sum(:score))
# 单选多选题的更新
category = @challenge_choose.standard_answer.length > 1 ? 2 : 1
@challenge_choose.update_column(:category, category)
@ -208,6 +210,9 @@ class ChallengesController < ApplicationController
# 测试集的
@shixun.myshixuns.update_all(:system_tip => 0)
end
if params[:challenge][:show_type].to_i == -1
@challenge.update_attributes(picture_path: nil, web_route: nil, expect_picture_path: nil, original_picture_path: nil)
end
# 关卡评测执行文件如果被修改,需要修改脚本内容
script = modify_shixun_script @shixun, @shixun.evaluate_script
@shixun.shixun_info.update_column(:evaluate_script, script)
@ -229,18 +234,21 @@ class ChallengesController < ApplicationController
# {...}, ...]
#}
def crud_answer
raise '参考答案不能为空' if params[:challenge_answer].empty?
raise '占比之和必须为100%' if params[:challenge_answer].map{|a| a[:score]}.sum != 100
ActiveRecord::Base.transaction do
@challenge.challenge_answers.destroy_all if @challenge.challenge_answers
params[:challenge_answer].each_with_index do |answer, index|
# 内容为空不保存
next if answer[:contents].blank?
ChallengeAnswer.create(name: answer[:name], contents: answer[:contents],
level: index+1, score: answer[:score], challenge_id: @challenge.id)
if @challenge.challenge_answers && params[:challenge_answer].blank?
@challenge.challenge_answers.destroy_all
else
raise '参考答案不能为空' if params[:challenge_answer].empty?
raise '占比之和必须为100%' if params[:challenge_answer].map{|a| a[:score]}.sum != 100
ActiveRecord::Base.transaction do
@challenge.challenge_answers.destroy_all if @challenge.challenge_answers
params[:challenge_answer].each_with_index do |answer, index|
# 内容为空不保存
next if answer[:contents].blank?
ChallengeAnswer.create(name: answer[:name], contents: answer[:contents],
level: index+1, score: answer[:score], challenge_id: @challenge.id)
end
end
end
end
# 查看参考答案接口

@ -1,36 +1,34 @@
module Admins::RenderHelper
extend ActiveSupport::Concern
def render_by_format(hash)
format = request.format.symbol
hash.key?(format) ? hash[format].call : hash[:html].call
end
def render_forbidden
respond_to do |format|
format.html { redirect_to '/403' }
format.json { super }
end
render_by_format(html: -> { current_user&.business? ? render('admins/shared/403') : redirect_to('/403') },
json: -> { render status: 403, json: { messages: I18n.t('error.forbidden') } } )
end
def render_not_found
respond_to do |format|
format.html { render 'admins/shared/404' }
format.js { render_js_error('资源未找到') }
format.json { render status: 404, json: { message: '资源未找到' } }
end
render_by_format(html: -> { render 'admins/shared/404' },
js: -> { render_js_error('资源未找到') },
json: -> { render status: 404, json: { message: '资源未找到' } })
end
def render_unprocessable_entity(message)
respond_to do |format|
format.html { render 'admins/shared/422' }
format.js { render_js_error(message) }
format.json { render status: 422, json: { message: message } }
end
render_by_format(html: -> { render 'admins/shared/422' },
js: -> { render_js_error(message) },
json: -> { render status: 422, json: { message: message } })
end
alias_method :render_error, :render_unprocessable_entity
def internal_server_error
respond_to do |format|
format.html { render 'admins/shared/500' }
format.js { render_js_error('系统错误') }
format.json { render status: 500, json: { message: '系统错误' } }
end
def internal_server_error(message = '系统错误')
@message = message
render_by_format(html: -> { render 'admins/shared/500' },
js: -> { render_js_error(message) },
json: -> { render status: 500, json: { message: message } })
end
def render_js_template(template, **opts)

@ -171,17 +171,18 @@ class FilesController < ApplicationController
begin
attachment_ids.each do |attachment_id|
ori = Attachment.find_by_id(attachment_id)
@course.attachments.each do |att|
@exist = false
if att.id == ori.id || (!att.copy_from.nil? && !ori.copy_from.nil? && att.copy_from == ori.copy_from) || att.copy_from == ori.id || att.id == ori.copy_from
att.created_on = Time.now
att.save
@exist = true
break
end
end
next if @exist
# 同一个资源可以多次发送到课堂
# @course.attachments.each do |att|
# @exist = false
# if att.id == ori.id || (!att.copy_from.nil? && !ori.copy_from.nil? && att.copy_from == ori.copy_from) || att.copy_from == ori.id || att.id == ori.copy_from
# att.created_on = Time.now
# att.save
# @exist = true
# break
# end
# end
#
# next if @exist
attach_copied_obj = ori.copy
attach_copied_obj.container = @course
attach_copied_obj.created_on = Time.now

@ -739,7 +739,7 @@ class GamesController < ApplicationController
experience = 0
game_status = @game.status
had_done = @game.had_done
game_challenge = Challenge.select([:id, :score, :position, :shixun_id, :web_route]).find(@game.challenge_id)
game_challenge = Challenge.select([:id, :score, :position, :shixun_id, :web_route, :show_type]).find(@game.challenge_id)
if params[:resubmit].blank? # 非重新评测
if game_status == 2 # 通关
@ -765,9 +765,10 @@ class GamesController < ApplicationController
testset_detail max_query_index.to_i, game_challenge
# 处理生成图片类型文件
picture = (@game.picture_path.nil? ? 0 : @game.id)
picture = (game_challenge.show_type.to_i == -1 || @game.picture_path.nil?) ? 0 : @game.id
# 针对web类型的实训
web_route = game_challenge.try(:web_route)
mirror_name = @shixun.mirror_name
e_record = EvaluateRecord.where(:identifier => sec_key).first

@ -274,7 +274,7 @@ class GraduationWorksController < ApplicationController
@is_author = @work.user_id == current_user.id
@work_members = @task.task_type == 1 ? [] : @task.graduation_works.where.not(user_id: @work.user_id).
where(group_id: @work.group_id).includes(:user)
@attachments = @work.attachments.where.not(attachtype: 7)
@attachments = @work.attachments.where("attachtype != 7 or attachtype is null")
end
def comment_list

@ -92,6 +92,7 @@ class StudentWorksController < ApplicationController
student_work.commit_time = Time.now
student_work.update_time = Time.now
student_work.commit_user_id = current_user.id
student_work.update_user_id = current_user.id
student_work.group_id = @homework.homework_type == "group" ? @homework.max_group_id : 0
#提交作品时,计算是否迟交
@ -112,7 +113,7 @@ class StudentWorksController < ApplicationController
homework_common_id: @homework.id, project_id: student_work.project_id,
late_penalty: student_work.late_penalty, work_status: student_work.work_status,
commit_time: Time.now, update_time: Time.now, group_id: student_work.group_id,
commit_user_id: current_user.id)
commit_user_id: current_user.id, update_user_id: current_user.id)
stu_work.save!
student_work.attachments.each do |attachment|
att = attachment.copy
@ -156,6 +157,7 @@ class StudentWorksController < ApplicationController
begin
@work.description = params[:description]
@work.update_time = Time.now
@work.update_user_id = current_user.id
# @work.commit_user_id = current_user.id
if @work.save!
Attachment.associate_container(params[:attachment_ids], @work.id, @work.class)
@ -172,7 +174,7 @@ class StudentWorksController < ApplicationController
# 原成员更新描述、更新时间以及附件
@homework.student_works.where(group_id: @work.group_id, user_id: (work_user_ids & params_user_ids)).each do |work|
# work.update_attributes(update_time: Time.now, description: @work.description, commit_user_id: current_user.id)
work.update_attributes(update_time: Time.now, description: @work.description)
work.update_attributes(update_time: Time.now, description: @work.description, update_user_id: current_user.id)
work.attachments.destroy_all
@work.attachments.each do |attachment|
att = attachment.copy
@ -192,7 +194,7 @@ class StudentWorksController < ApplicationController
@homework.student_works.where(group_id: @work.group_id, user_id: delete_user_ids).
update_all(work_status: 0, description: nil, late_penalty: 0, commit_time: nil, update_time: nil,
final_score: nil, teacher_score: nil, student_score: nil, teaching_asistant_score: nil,
work_score: nil, project_id: 0, group_id: 0, commit_user_id: nil)
work_score: nil, project_id: 0, group_id: 0, commit_user_id: nil, update_user_id: nil)
# 新增加的成员
(params_user_ids - work_user_ids).each do |user_id|
@ -200,7 +202,7 @@ class StudentWorksController < ApplicationController
stu_work.update_attributes(user_id: user_id, description: @work.description, homework_common_id: @homework.id,
project_id: @work.project_id, late_penalty: @work.late_penalty,
work_status: @work.work_status, commit_time: Time.now, update_time: Time.now,
group_id: @work.group_id, commit_user_id: @work.commit_user_id)
group_id: @work.group_id, commit_user_id: @work.commit_user_id, update_user_id: current_user.id)
@work.attachments.each do |attachment|
att = attachment.copy
att.author_id = attachment.author_id

@ -101,8 +101,9 @@ class SubjectsController < ApplicationController
@tags = ChallengeTag.where(challenge_id: challenge_ids).pluck(:name).uniq
# 用户获取的实训标签
# @user_tags = @subject.shixuns.map(&:user_tags_name).flatten.uniq
@user_tags = user_shixun_tags challenge_ids, @user.id
@my_subject_progress = @subject.my_subject_progress
# 用户进展
user_subject_progress(challenge_ids)
end
def new
@ -269,7 +270,7 @@ class SubjectsController < ApplicationController
page = params[:page] || 1
member_ids = @subject.subject_members.map(&:user_id).join(',')
condition = "%#{params[:search].strip}%".gsub(" ","")
@users = User.where("id not in (?) and status = 1 and LOWER(concat(lastname, firstname, login, mail)) LIKE ?", member_ids, "#{condition}")
@users = User.where("id not in (?) and status = 1 and LOWER(concat(lastname, ifnull(firstname, ''), login)) LIKE ?", member_ids, "#{condition}")
@users = @users.page(page).per(10)
@users = @users.includes(:user_extension)
@ -436,4 +437,33 @@ class SubjectsController < ApplicationController
tip_exception("403", "")
end
end
# 用户进展和获取的标签
def user_subject_progress challenge_ids
pass_games = Game.select(:id, :cost_time, :challenge_id).where(status: 2, user_id: current_user.id, challenge_id: challenge_ids) if current_user.logged?
@all_score = Challenge.where(id: challenge_ids).sum(:score)
# 如果没有通关的,没必要再继续统计了
if pass_games.blank?
@my_score = 0
@learned = 0
@time = 0
@user_tags = []
else
pass_challenge_ids = pass_games.map(&:challenge_id).uniq # 按道理是不用去重的,但是历史数据与重复
subject_challenge_count = @subject.shixuns.sum(:challenges_count)
# 用户通关获得的标签
@user_tags = ChallengeTag.where(challenge_id: pass_challenge_ids).pluck(:name)
# 用户学习进度
@learned =
subject_challenge_count == 0 ? 0 :
((pass_challenge_ids.size.to_f / subject_challenge_count).round(2) * 100).to_i
# 用户通关分数
@my_score = Challenge.where(id: pass_challenge_ids).pluck(:score).sum
@time = pass_games.map(&:cost_time).sum
end
end
end

@ -0,0 +1,23 @@
module Admins::MirrorRepositoriesHelper
def mirror_type_tag(mirror)
case mirror.main_type
when '1' then '<i class="fa fa-star text-success font-16" aria-hidden="true" data-toggle="tooltip" data-title="主类别"></i>'.html_safe
when '0' then '<i class="fa fa-star text-secondary font-16" aria-hidden="true" data-toggle="tooltip" data-title="子类别"></i>'.html_safe
end
end
def mirror_status_tag(mirror)
case mirror.status
when 0
'<i class="fa fa-check-circle text-secondary font-16" data-toggle="tooltip" data-title="未发布"></i>'.html_safe
when 1
'<i class="fa fa-check-circle text-success font-16" data-toggle="tooltip" data-title="已发布"></i>'.html_safe
when 2, 3
'<i class="fa fa-exclamation-circle text-danger font-16" data-toggle="tooltip" data-title="被修改"></i>'.html_safe
when 4
'<i class="fa fa-times-circle text-danger font-18" data-toggle="tooltip" data-title="被删除"></i>'.html_safe
when 5
'<i class="fa fa-exclamation-circle text-warning font-16" data-toggle="tooltip" data-title="子节点异常"></i>'.html_safe
end
end
end

@ -48,7 +48,7 @@ module HomeworkCommonsHelper
end
when 2, 5, 6
status << "评阅中"
time = "评阅剩余时间:" + (course.end_date.present? ? how_much_time(course.end_date.end_of_day) : "")
time = course.end_date.present? ? ("评阅剩余时间:" + how_much_time(course.end_date.end_of_day)) : ""
time_status = 5
end

@ -40,16 +40,18 @@ class Challenge < ApplicationRecord
## 选择题总分
def choose_score
self.challenge_chooses.pluck(:score).sum
self.score
#self.challenge_chooses.pluck(:score).sum
end
# 关卡总分
def all_score
if self.st == 1
self.choose_score
else
self.score
end
self.score
# if self.st == 1
# self.choose_score
# else
# self.score
# end
end
# 开启挑战

@ -31,7 +31,7 @@ class Game < ApplicationRecord
# 根据得分比例来算实际得分(试卷、实训作业)
def real_score score
((final_score < 0 ? 0 : final_score).to_f / challenge.all_score) * score
((final_score < 0 ? 0 : final_score).to_f / challenge.score) * score
end
# 判断实训是否全部通关

@ -0,0 +1,7 @@
# status: 0 创建镜像; 1 修改镜像ID 2 修改镜像name 3 删除镜像 4.从主节点同步镜像到子节点(子节点发生异常), 5. 修改镜像别名, 6. 修改镜像的状态
# user_id: -1时证明是非人为因素造成中间层异常导致
class MirrorOperationRecord < ActiveRecord::Base
default_scope { order(created_at: :desc) }
belongs_to :mirror_repository
end

@ -8,4 +8,8 @@ class MirrorRepository < ApplicationRecord
scope :published_mirror, -> { where(status: 1) }
scope :published_main_mirror, -> { published_mirror.where(main_type: 1) }
scope :published_small_mirror, -> { published_mirror.where(main_type: 0) }
def deletable?
status != 1 && !shixun_mirror_repositories.exists?
end
end

@ -192,11 +192,7 @@ class Shixun < ApplicationRecord
# 实训关卡的总分(由于大部分是实践题,因此没关联查choose表)
# 提前加载问题由于选择题比较少所以几乎不会触发选择题的查询所以没必要提前载入choose_score
def all_score
sum = 0
challenges.each do |challenge|
sum += challenge.st == 0 ? challenge.score : challenge.choose_score
end
sum
self.challenges.pluck(:score).sum
end
### fork 数量

@ -2,6 +2,7 @@ class StudentWork < ApplicationRecord
#学生提交作品表 #work_status :0 未提交 1 已提交 2 迟交
belongs_to :user
belongs_to :commit_user, class_name: 'User', foreign_key: :commit_user_id, optional: true
belongs_to :update_user, class_name: 'User', foreign_key: :update_user_id, optional: true
belongs_to :homework_common
belongs_to :myshixun, optional: true
has_many :student_works_evaluation_distributions, dependent: :destroy

@ -0,0 +1,89 @@
class Admins::CheckShixunMirrorsService < ApplicationService
Error = Class.new(StandardError)
def call
bridge_images
ActiveRecord::Base.transaction do
check_sync_mirrors!
check_mirrors!
end
end
private
def mirrors
bridge_images['images']
end
def sync_mirrors
bridge_images['imagesNotSync']
end
def check_mirrors!
return if mirrors.blank?
image_names = []
mirrors.each do |data|
mirror = JSON.parse(data)
name_repository = MirrorRepository.find_by(name: mirror['imageName'])
id_repository = MirrorRepository.find_by(mirrorID: mirror['imageID'])
image_names << mirror['imageName']
if name_repository.blank? && id_repository.present? # 镜像名称被修改
id_repository.update_column(:status, 2)
MirrorOperationRecord.create!(mirror_repository_id: id_repository.id, mirror_id: mirror['imageID'],
mirror_name: mirror['imageName'], status: 2, user_id: -1)
elsif name_repository.blank? # 镜像不存在、创建镜像
new_repository = MirrorRepository.create!(mirrorID: mirror['imageID'], name: mirror['imageName'])
MirrorOperationRecord.create!(mirror_repository_id: new_repository.id, mirror_id: mirror['imageID'],
mirror_name: mirror['imageName'], status: 0, user_id: -1)
elsif name_repository.mirrorID != mirror['imageID'] # 镜像ID被修改
name_repository.update_column(:status, 2)
MirrorOperationRecord.create!(mirror_repository_id: name_repository.id, mirror_id: mirror['imageID'],
mirror_name: mirror['imageName'], status: 1, user_id: -1)
end
end
# 判断中间层镜像是否被删除
MirrorRepository.find_each do |mirror|
next if mirror&.name.blank? || image_names.index(mirror.name)
mirror.update_column(:status, 4)
MirrorOperationRecord.create!(mirror_repository_id: mirror.id, mirror_id: mirror&.mirrorID,
mirror_name: mirror.name, status: 3, user_id: -1)
end
end
def check_sync_mirrors!
return if sync_mirrors.blank?
sync_mirrors.each do |data|
mirror = JSON.parse(data)
repository = MirrorRepository.find_by(name: mirror['imageName'])
next if repository.blank? || repository.status != 1
repository.update_column(:status, 5)
MirrorOperationRecord.create!(mirror_repository_id: repository.id, mirror_id: mirror['imageID'],
mirror_name: mirror['imageName'], status: 4, user_id: -1)
end
end
def bridge_images
@_bridge_images ||= begin
url = EduSetting.get('cloud_bridge')
res = Faraday.get(url)
raise Error, '拉取镜像信息异常' if res && res['code'].nonzero?
res
rescue => e
Rails.logger.error("get response failed ! #{e.message}")
raise Error, '实训云平台繁忙繁忙等级84'
end
end
end

@ -0,0 +1,21 @@
class Admins::ChooseMirrorService < ApplicationService
attr_reader :mirror, :user, :number
def initialize(mirror, user, mirror_number)
@mirror = mirror
@user = user
@number = mirror_number
end
def call
if mirror.mirrorID == number
mirror.update_column(:status, 1)
return
end
old_number = mirror.mirrorID
mirror.update!(mirrorID: number, status: 1)
MirrorOperationRecord.create!(mirror_repository_id: mirror.id, mirror_id: number, mirror_name: mirror.name,
status: 1, user_id: user.id, old_tag: old_number, new_tag: mirror.mirrorID)
end
end

@ -0,0 +1,37 @@
class Admins::SaveMirrorRepositoryService < ApplicationService
Error = Class.new(StandardError)
attr_reader :mirror, :user, :params
def initialize(mirror, user, params)
@mirror = mirror
@user = user
@params = params
end
def call
mirror.assign_attributes(params)
raise Error, '镜像别名重复' if MirrorRepository.where.not(id: mirror.id).exists?(type_name: params[:type_name])
ActiveRecord::Base.transaction do
record_operation! if mirror.persisted?
mirror.save!
end
end
private
def record_operation!
if mirror.type_name_changed?
MirrorOperationRecord.create!(mirror_repository_id: mirror.id, status: 5,
user_id: user.id, old_tag: mirror.type_name_in_database,
new_tag: mirror.type_name)
elsif mirror.status_changed?
MirrorOperationRecord.create!(mirror_repository_id: mirror.id, status: 5,
user_id: user.id, old_tag: mirror.status_in_database,
new_tag: mirror.status)
end
end
end

@ -0,0 +1,5 @@
$.notify({ message: '操作成功' },{ type: 'success' });
setTimeout(function(){
window.location.reload();
}, 500)

@ -0,0 +1,2 @@
$('.admin-modal-container').html("<%= j( render partial: 'admins/mirror_repositories/shared/choose_mirror_modal', locals: { mirror: @mirror, new_mirror: @new_mirror } ) %>");
$('.modal.admin-choose-mirror-modal').modal('show');

@ -0,0 +1,8 @@
<%
define_admin_breadcrumbs do
add_admin_breadcrumb('镜像管理', admins_mirror_repositories_path)
add_admin_breadcrumb('镜像详情')
end
%>
<%= render partial: 'admins/mirror_repositories/shared/form', locals: { mirror: @mirror, form_action: 'update' } %>

@ -0,0 +1,23 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('镜像管理') %>
<% end %>
<div class="box search-form-container mirror-repository-list-form">
<form class="flex-1"></form>
<%= link_to '新建', new_admins_mirror_repository_path, class: 'btn btn-primary' %>
</div>
<% if @error_mirror_names.present? %>
<div class="box pb-0">
以下镜像异常:
<% @error_mirror_names.each do |mirror_name| %>
<span class="ml-2 text-danger"><%= mirror_name %></span>
<% end %>
</div>
<% end %>
<div class="box mirror-repository-list-container">
<%= render partial: 'admins/mirror_repositories/shared/list', locals: { mirrors: @mirrors } %>
</div>
<%= render 'admins/mirror_repositories/shared/replace_mirror_modal' %>

@ -0,0 +1 @@
$('.mirror-repository-list-container').html("<%= j( render partial: 'admins/mirror_repositories/shared/list', locals: { mirrors: @mirrors } ) %>");

@ -0,0 +1,5 @@
$.notify({ message: '操作成功' },{ type: 'success' });
setTimeout(function(){
window.location.reload();
}, 500)

@ -0,0 +1,8 @@
<%
define_admin_breadcrumbs do
add_admin_breadcrumb('镜像管理', admins_mirror_repositories_path)
add_admin_breadcrumb('新建镜像')
end
%>
<%= render partial: 'admins/mirror_repositories/shared/form', locals: { mirror: @mirror, form_action: 'create' } %>

@ -0,0 +1,42 @@
<div class="modal fade admin-choose-mirror-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">更新镜像仓库</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<%= form_tag(admins_choose_mirror_repositories_path(mirror_id: mirror.id), method: :post, class: 'admin-choose-mirror-form') do %>
<div class="form-group row">
<div class="col-md-2"></div>
<div class="col-md-5">ID</div>
<div class="col-md-5">名称</div>
</div>
<div class="form-group row">
<div class="col-md-2">旧镜像</div>
<div class="col-md-5 form-check">
<input class="form-check-input" type="radio" name="mirror_number" id="old-mirror-check" value="<%= mirror.mirrorID %>">
<label class="form-check-label" for="old-mirror-check"><%= mirror.mirrorID %></label>
</div>
<div class="col-md-5"><%= mirror.name %></div>
</div>
<div class="form-group row">
<div class="col-md-2">新镜像</div>
<div class="col-md-5 form-check">
<input class="form-check-input" type="radio" name="mirror_number" id="new-mirror-check" value="<%= new_mirror.mirror_id %>">
<label class="form-check-label" for="new-mirror-check"><%= new_mirror.mirror_id %></label>
</div>
<div class="col-md-5"><%= new_mirror.mirror_name %></div>
</div>
<div class="mt-2 error text-danger"></div>
<% end %>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary submit-btn">确认</button>
</div>
</div>
</div>
</div>

@ -0,0 +1,42 @@
<div class="box edit-mirror-repository-container">
<%= simple_form_for([:admins, mirror], url: { action: form_action }, html: { class: 'edit-mirror col-md-12' }, defaults: { wrapper_html: { class: 'col-md-4' } }) do |f| %>
<% unless mirror.new_record? %>
<div class="row">
<%= f.input :mirrorID, label: '镜像ID', input_html: { readonly: true, class: 'form-control-plaintext' } %>
<%= f.input :name, label: '镜像名称', input_html: { readonly: true, class: 'form-control-plaintext' } %>
</div>
<% end %>
<div class="row">
<%= f.input :type_name, as: :string, label: '镜像别名 *' %>
<div class="form-group select optional col-md-4">
<%= f.label :main_type, label: '类别' %>
<%= f.select :main_type, [['主类别', 1],['小类别', 0]], {}, class: 'form-control optional' %>
</div>
</div>
<div class="row">
<%= f.input :time_limit, as: :integer, label: '评测时限(S)' %>
<%= f.input :resource_limit, as: :integer, label: '磁盘限制(K)' %>
</div>
<div class="row">
<%= f.input :cpu_limit, as: :integer, label: 'CPU限制(核)' %>
<%= f.input :memory_limit, as: :integer, label: '内存限制(M)' %>
</div>
<div class="row">
<%= f.input :description, as: :text, label: '描述', wrapper_html: { class: 'col-md-8' } %>
</div>
<div class="row">
<%= f.input :status, as: :radio_buttons, label: '状态', collection: [%w(未发布 0), %w(已发布 1)], wrapper_html: { class: 'col-md-4' } %>
</div>
<div class="row">
<%= f.button :submit, value: '保存', class: 'btn-primary mr-3 px-4', 'data-disable-with': '保存中...' %>
<%= link_to '取消', admins_mirror_repositories_path, class: 'btn btn-secondary px-4' %>
</div>
<% end %>
</div>

@ -0,0 +1,54 @@
<table class="table table-hover text-center mirror-repository-list-table">
<thead class="thead-light">
<tr>
<th width="6%">ID</th>
<th width="6%">类别</th>
<th width="12%" class="text-left">镜像别名</th>
<th width="16%" class="text-left">镜像名称</th>
<th width="22%" class="text-left">镜像描述</th>
<th width="14%">修改时间</th>
<th width="6%">脚本</th>
<th width="6%">状态</th>
<th width="12%">操作</th>
</tr>
</thead>
<tbody>
<% if mirrors.present? %>
<% mirrors.each do |mirror| %>
<tr class="mirror-repository-item-<%= mirror.id %>">
<td><%= mirror.id %></td>
<td><%= mirror_type_tag(mirror) %></td>
<td class="text-left"><%= display_text(mirror.type_name) %></td>
<td class="text-left"><%= overflow_hidden_span mirror.name, width: 150 %></td>
<td class="text-left"><%= overflow_hidden_span mirror.description, width: 240 %></td>
<td><%= mirror.updated_at.strftime('%Y-%m-%d %H:%M') %></td>
<td>
<% if mirror.main_type == "1" %>
<%= link_to admins_mirror_repository_mirror_scripts_path(mirror) do %>
<i class="fa fa-file-text <%= mirror.mirror_scripts.blank? ? 'text-danger' : 'text-success' %>" aria-hidden="true" data-toggle="tooltip" data-title="查看脚本"></i>
<% end %>
<% end %>
</td>
<td><%= mirror_status_tag mirror %></td>
<td class="action-container">
<%= link_to '编辑', edit_admins_mirror_repository_path(mirror), class: 'action edit-action' %>
<% if mirror.status == 2 %>
<%= link_to '同步', new_admins_choose_mirror_repository_path(mirror_id: mirror.id), remote: true, class: 'action sync-action' %>
<% end %>
<%= javascript_void_link '替换', class: 'action replace-action', data: { toggle: 'modal', target: '.admin-replace-mirror-modal', id: mirror.id, name: mirror.name } %>
<% if mirror.deletable? %>
<%= delete_link '删除', admins_mirror_repository_path(mirror, element: ".mirror-repository-item-#{mirror.id}"), class: 'delete-mirror-repository-action' %>
<% end %>
</td>
</tr>
<% end %>
<% else %>
<%= render 'admins/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>
<%= render partial: 'admins/shared/paginate', locals: { objects: mirrors } %>

@ -0,0 +1,33 @@
<div class="modal fade admin-replace-mirror-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">镜像替换</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form class="admin-replace-mirror-form" data-url="<%= merge_admins_mirror_repositories_path %>">
<%= hidden_field_tag(:mirror_id, nil) %>
<div class="form-group d-flex">
<div>被替换镜像:</div>
<div class="ml-2">ID<span class="mx-1 text-info mirror-id-container"></span></div>
<div class="ml-2">名称:<span class="mx-1 text-info mirror-name-container"></span></div>
</div>
<div class="form-group d-flex">
<label for="new_mirror_id" class="col-form-label">选择新镜像:</label>
<div class="d-flex flex-column-reverse w-75">
<select id="new_mirror_id" name="new_mirror_id" class="form-control new-mirror-select"></select>
</div>
</div>
<div class="error text-danger"></div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary submit-btn">确认</button>
</div>
</div>
</div>
</div>

@ -0,0 +1,8 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('镜像管理', admins_mirror_repositories_path) %>
<% add_admin_breadcrumb(current_mirror.type_name, edit_admins_mirror_repository_path(current_mirror)) %>
<% add_admin_breadcrumb('脚本列表', admins_mirror_repository_mirror_scripts_path(current_mirror)) %>
<% add_admin_breadcrumb('编辑脚本') %>
<% end %>
<%= render partial: 'admins/mirror_scripts/shared/form', locals: { mirror: current_mirror, script: @script, form_action: 'update' } %>

@ -0,0 +1,14 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('镜像管理', admins_mirror_repositories_path) %>
<% add_admin_breadcrumb(current_mirror.type_name, edit_admins_mirror_repository_path(current_mirror)) %>
<% add_admin_breadcrumb('脚本列表') %>
<% end %>
<div class="box search-form-container mirror-script-list-form">
<form class="flex-1"></form>
<%= link_to '新建', new_admins_mirror_repository_mirror_script_path(current_mirror), class: 'btn btn-primary' %>
</div>
<div class="box mirror-script-list-container">
<%= render partial: 'admins/mirror_scripts/shared/list', locals: { mirror: current_mirror, scripts: @scripts } %>
</div>

@ -0,0 +1,8 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('镜像管理', admins_mirror_repositories_path) %>
<% add_admin_breadcrumb(current_mirror.type_name, edit_admins_mirror_repository_path(current_mirror)) %>
<% add_admin_breadcrumb('脚本列表', admins_mirror_repository_mirror_scripts_path(current_mirror)) %>
<% add_admin_breadcrumb('新建脚本') %>
<% end %>
<%= render partial: 'admins/mirror_scripts/shared/form', locals: { mirror: current_mirror, script: @script, form_action: 'create' } %>

@ -0,0 +1,12 @@
<div class="box edit-mirror-script-container">
<%= simple_form_for([:admins, mirror, script], url: { action: form_action }, html: { class: 'script-form' }) do |f| %>
<%= f.input :script_type, label: '名称', input_html: { class: 'col-md-6' } %>
<%= f.input :description, as: :text, label: '说明', input_html: { class: 'col-md-6' } %>
<%= f.input :script, as: :text, label: '评测脚本' %>
<div class="form-row">
<%= f.button :submit, value: '保存', class: 'btn-primary mr-3 px-4', 'data-disable-with': '保存中...' %>
<%= link_to '取消', admins_mirror_repository_mirror_scripts_path(mirror), class: 'btn btn-secondary px-4' %>
</div>
<% end %>
</div>

@ -0,0 +1,32 @@
<table class="table table-hover text-center mirror-script-list-table">
<thead class="thead-light">
<tr>
<th width="10%">ID</th>
<th width="20%" class="text-left">名称</th>
<th width="34%" class="text-left">说明</th>
<th width="16%">更新时间</th>
<th width="20%">操作</th>
</tr>
</thead>
<tbody>
<% if scripts.present? %>
<% scripts.each do |script| %>
<tr class="mirror-script-item-<%= script.id %>">
<td><%= script.id %></td>
<td class="text-left"><%= overflow_hidden_span script.script_type, width: 200 %></td>
<td class="text-left"><%= overflow_hidden_span script.description, width: 400 %></td>
<td><%= script.updated_at.strftime('%Y-%m-%d %H:%M') %></td>
<td class="action-container">
<%= link_to '编辑', edit_admins_mirror_repository_mirror_script_path(mirror, script), class: 'action edit-action' %>
<%= delete_link '删除', admins_mirror_repository_mirror_script_path(mirror, script, element: ".mirror-script-item-#{script.id}"), class: 'delete-mirror-script-action' %>
</td>
</tr>
<% end %>
<% else %>
<%= render 'admins/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>
<%= render partial: 'admins/shared/paginate', locals: { objects: scripts } %>

@ -0,0 +1,6 @@
<div class="d-flex flex-column align-items-center justify-content-center global-error">
<div class="global-error-code">
<span>403</span>
</div>
<div class="global-error-text">未授权</div>
</div>

@ -2,5 +2,5 @@
<div class="global-error-code">
<span>500</span>
</div>
<div class="global-error-text">系统错误</div>
<div class="global-error-text"><%= @message %></div>
</div>

@ -1,5 +1,5 @@
<div class="paginate-container">
<% if objects.size.nonzero? %>
<% if objects && objects.size.nonzero? %>
<div class="paginate-total"><%= page_entries_info objects %></div>
<% end %>
<%= paginate objects, views_prefix: 'admins', remote: true %>

@ -20,6 +20,12 @@
<% end %>
</li>
<li>
<%= sidebar_item_group('#shixun-submenu', '实训管理', icon: 'window-restore') do %>
<li><%= sidebar_item(admins_mirror_repositories_path, '镜像管理', icon: 'cubes', controller: 'admins-mirror_repositories') %></li>
<% end %>
</li>
<!-- <li>-->
<%#= sidebar_item_group('#course-submenu', '课堂+', icon: 'mortar-board') do %>
<!-- <li><%#= sidebar_item('#', '课程列表', icon: 'calendar', controller: '') %></li>-->

@ -131,7 +131,7 @@
<div class="form-row mt-4">
<%= f.button :submit, value: '保存', class: 'btn-primary mr-3 px-4' %>
<%= link_to '取消', 'javascript:history.go(-1)', class: 'btn btn-secondary px-4' %>
<%= link_to '取消', admins_users_path, class: 'btn btn-secondary px-4' %>
</div>
<% end %>
</div>

@ -1,2 +1,2 @@
json.status 1
json.message "创建参考答案成功"
json.message "操作成功"

@ -13,7 +13,7 @@ if @challenges.present?
json.position challenge.position
json.st challenge.st
json.name challenge.subject
json.score challenge.all_score
json.score challenge.score
json.passed_count challenge.user_passed_count
json.playing_count challenge.playing_count
json.name_url shixun_challenge_path(challenge, shixun_identifier: @shixun.identifier)

@ -5,7 +5,8 @@ json.is_evaluation @is_evaluation
json.author_name @is_evaluation ? "匿名" : @work.user.real_name
json.is_leader_work @work.user_id == @work.commit_user_id if @homework.homework_type == "group"
json.is_author @is_author
json.update_user_name @is_evaluation ? "匿名" : @work.commit_user.try(:real_name)
json.commit_user_name @is_evaluation ? "匿名" : @work.commit_user.try(:real_name)
json.update_user_name @is_evaluation ? "匿名" : @work.update_user.try(:real_name)
json.update_atta @homework.late_duration && @is_author

@ -14,8 +14,8 @@ end
# 我的进展
json.progress do
json.my_score @subject.my_subject_score
json.all_score @subject.all_score
json.learned @subject.my_subject_progress
json.time @subject.my_consume_time
json.my_score @my_score
json.all_score @all_score
json.learned @learned
json.time @time
end

@ -1 +1 @@
admins-users: admins-users
admins-mirror_scripts: 'admins-mirror_repositories'

@ -7,8 +7,10 @@ Rails.application.config.assets.version = '1.0'
# Rails.application.config.assets.paths << Emoji.images_path
# Add Yarn node_modules folder to the asset load path.
Rails.application.config.assets.paths << Rails.root.join('node_modules')
Rails.application.config.assets.paths << Rails.root.join('vendor/assets')
# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in the app/assets
# folder are already added.
Rails.application.config.assets.precompile += %w( admin.js admin.css )

@ -786,6 +786,15 @@ Rails.application.routes.draw do
post :refuse
end
end
resources :mirror_repositories, only: [:index, :new, :create, :edit, :update, :destroy] do
collection do
post :merge
get :for_select
end
resources :mirror_scripts, only: [:index, :new, :create, :edit, :update, :destroy]
end
resources :choose_mirror_repositories, only: [:new, :create]
end
#git 认证回调

@ -0,0 +1,10 @@
class ModifyChallnegeScoreForChoose < ActiveRecord::Migration[5.2]
def change
challenges = Challenge.where(st: 1)
challenges.find_each do |c|
puts(c.id)
score = c.challenge_chooses.sum(:score)
c.update_column(:score, score)
end
end
end

@ -0,0 +1,6 @@
class AddUpdateUserIdToStudentWorks < ActiveRecord::Migration[5.2]
def change
add_column :student_works, :update_user_id, :integer
StudentWork.update_all("update_user_id = commit_user_id")
end
end

File diff suppressed because one or more lines are too long

@ -17816,6 +17816,607 @@ form.was-validated select:valid ~ .select2-container--bootstrap4 .select2-select
.datepicker.datepicker-inline td {
padding: 4px 5px;
}
/* BASICS */
/* line 3, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror {
/* Set height, width, borders, and global font properties here */
font-family: monospace;
height: 300px;
color: black;
direction: ltr;
}
/* PADDING */
/* line 13, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-lines {
padding: 4px 0;
/* Vertical padding around content */
}
/* line 16, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror pre.CodeMirror-line,
.CodeMirror pre.CodeMirror-line-like {
padding: 0 4px;
/* Horizontal padding of content */
}
/* line 21, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
background-color: white;
/* The little square between H and V scrollbars */
}
/* GUTTER */
/* line 27, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutters {
border-right: 1px solid #ddd;
background-color: #f7f7f7;
white-space: nowrap;
}
/* line 33, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-linenumber {
padding: 0 3px 0 5px;
min-width: 20px;
text-align: right;
color: #999;
white-space: nowrap;
}
/* line 41, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-guttermarker {
color: black;
}
/* line 42, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-guttermarker-subtle {
color: #999;
}
/* CURSOR */
/* line 46, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-cursor {
border-left: 1px solid black;
border-right: none;
width: 0;
}
/* Shown when moving in bi-directional text */
/* line 52, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror div.CodeMirror-secondarycursor {
border-left: 1px solid silver;
}
/* line 55, vendor/assets/codemirror/lib/codemirror.css */
.cm-fat-cursor .CodeMirror-cursor {
width: auto;
border: 0 !important;
background: #7e7;
}
/* line 60, vendor/assets/codemirror/lib/codemirror.css */
.cm-fat-cursor div.CodeMirror-cursors {
z-index: 1;
}
/* line 63, vendor/assets/codemirror/lib/codemirror.css */
.cm-fat-cursor-mark {
background-color: rgba(20, 255, 20, 0.5);
-webkit-animation: blink 1.06s steps(1) infinite;
animation: blink 1.06s steps(1) infinite;
}
/* line 69, vendor/assets/codemirror/lib/codemirror.css */
.cm-animate-fat-cursor {
width: auto;
border: 0;
-webkit-animation: blink 1.06s steps(1) infinite;
animation: blink 1.06s steps(1) infinite;
background-color: #7e7;
}
@-webkit-keyframes blink {
0% {
}
50% {
background-color: transparent;
}
100% {
}
}
@keyframes blink {
0% {
}
50% {
background-color: transparent;
}
100% {
}
}
/* Can style cursor different in overwrite (non-insert) mode */
/* line 96, vendor/assets/codemirror/lib/codemirror.css */
.cm-tab {
display: inline-block;
text-decoration: inherit;
}
/* line 98, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-rulers {
position: absolute;
left: 0;
right: 0;
top: -50px;
bottom: 0;
overflow: hidden;
}
/* line 103, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-ruler {
border-left: 1px solid #ccc;
top: 0;
bottom: 0;
position: absolute;
}
/* DEFAULT THEME */
/* line 111, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-header {
color: blue;
}
/* line 112, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-quote {
color: #090;
}
/* line 113, vendor/assets/codemirror/lib/codemirror.css */
.cm-negative {
color: #d44;
}
/* line 114, vendor/assets/codemirror/lib/codemirror.css */
.cm-positive {
color: #292;
}
/* line 115, vendor/assets/codemirror/lib/codemirror.css */
.cm-header, .cm-strong {
font-weight: bold;
}
/* line 116, vendor/assets/codemirror/lib/codemirror.css */
.cm-em {
font-style: italic;
}
/* line 117, vendor/assets/codemirror/lib/codemirror.css */
.cm-link {
text-decoration: underline;
}
/* line 118, vendor/assets/codemirror/lib/codemirror.css */
.cm-strikethrough {
text-decoration: line-through;
}
/* line 120, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-keyword {
color: #708;
}
/* line 121, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-atom {
color: #219;
}
/* line 122, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-number {
color: #164;
}
/* line 123, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-def {
color: #00f;
}
/* line 128, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-variable-2 {
color: #05a;
}
/* line 129, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-variable-3, .cm-s-default .cm-type {
color: #085;
}
/* line 130, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-comment {
color: #a50;
}
/* line 131, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-string {
color: #a11;
}
/* line 132, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-string-2 {
color: #f50;
}
/* line 133, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-meta {
color: #555;
}
/* line 134, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-qualifier {
color: #555;
}
/* line 135, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-builtin {
color: #30a;
}
/* line 136, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-bracket {
color: #997;
}
/* line 137, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-tag {
color: #170;
}
/* line 138, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-attribute {
color: #00c;
}
/* line 139, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-hr {
color: #999;
}
/* line 140, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-link {
color: #00c;
}
/* line 142, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-error {
color: #f00;
}
/* line 143, vendor/assets/codemirror/lib/codemirror.css */
.cm-invalidchar {
color: #f00;
}
/* line 145, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-composing {
border-bottom: 2px solid;
}
/* Default styles for common addons */
/* line 149, vendor/assets/codemirror/lib/codemirror.css */
div.CodeMirror span.CodeMirror-matchingbracket {
color: #0b0;
}
/* line 150, vendor/assets/codemirror/lib/codemirror.css */
div.CodeMirror span.CodeMirror-nonmatchingbracket {
color: #a22;
}
/* line 151, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-matchingtag {
background: rgba(255, 150, 0, 0.3);
}
/* line 152, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-activeline-background {
background: #e8f2ff;
}
/* STOP */
/* The rest of this file contains styles related to the mechanics of
the editor. You probably shouldn't touch them. */
/* line 159, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror {
position: relative;
overflow: hidden;
background: white;
}
/* line 165, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-scroll {
overflow: scroll !important;
/* Things will break if this is overridden */
/* 30px is the magic margin used to hide the element's real scrollbars */
/* See overflow: hidden in .CodeMirror */
margin-bottom: -30px;
margin-right: -30px;
padding-bottom: 30px;
height: 100%;
outline: none;
/* Prevent dragging from highlighting the element */
position: relative;
}
/* line 175, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-sizer {
position: relative;
border-right: 30px solid transparent;
}
/* The fake, visible scrollbars. Used to force redraw during scrolling
before actual scrolling happens, thus preventing shaking and
flickering artifacts. */
/* line 183, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
position: absolute;
z-index: 6;
display: none;
}
/* line 188, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-vscrollbar {
right: 0;
top: 0;
overflow-x: hidden;
overflow-y: scroll;
}
/* line 193, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-hscrollbar {
bottom: 0;
left: 0;
overflow-y: hidden;
overflow-x: scroll;
}
/* line 198, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-scrollbar-filler {
right: 0;
bottom: 0;
}
/* line 201, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter-filler {
left: 0;
bottom: 0;
}
/* line 205, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutters {
position: absolute;
left: 0;
top: 0;
min-height: 100%;
z-index: 3;
}
/* line 210, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter {
white-space: normal;
height: 100%;
display: inline-block;
vertical-align: top;
margin-bottom: -30px;
}
/* line 217, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter-wrapper {
position: absolute;
z-index: 4;
background: none !important;
border: none !important;
}
/* line 223, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter-background {
position: absolute;
top: 0;
bottom: 0;
z-index: 4;
}
/* line 228, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter-elt {
position: absolute;
cursor: default;
z-index: 4;
}
/* line 233, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter-wrapper ::-moz-selection {
background-color: transparent;
}
.CodeMirror-gutter-wrapper ::selection {
background-color: transparent;
}
/* line 234, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter-wrapper ::-moz-selection {
background-color: transparent;
}
/* line 236, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-lines {
cursor: text;
min-height: 1px;
/* prevents collapsing before first draw */
}
/* line 240, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror pre.CodeMirror-line,
.CodeMirror pre.CodeMirror-line-like {
/* Reset some styles that the rest of the page might have set */
border-radius: 0;
border-width: 0;
background: transparent;
font-family: inherit;
font-size: inherit;
margin: 0;
white-space: pre;
word-wrap: normal;
line-height: inherit;
color: inherit;
z-index: 2;
position: relative;
overflow: visible;
-webkit-tap-highlight-color: transparent;
-webkit-font-variant-ligatures: contextual;
font-variant-ligatures: contextual;
}
/* line 260, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-wrap pre.CodeMirror-line,
.CodeMirror-wrap pre.CodeMirror-line-like {
word-wrap: break-word;
white-space: pre-wrap;
word-break: normal;
}
/* line 267, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-linebackground {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 0;
}
/* line 273, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-linewidget {
position: relative;
z-index: 2;
padding: 0.1px;
/* Force widget margins to stay inside of the container */
}
/* line 281, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-rtl pre {
direction: rtl;
}
/* line 283, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-code {
outline: none;
}
/* Force content-box sizing for the elements where we expect it */
/* line 288, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-scroll,
.CodeMirror-sizer,
.CodeMirror-gutter,
.CodeMirror-gutters,
.CodeMirror-linenumber {
box-sizing: content-box;
}
/* line 297, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-measure {
position: absolute;
width: 100%;
height: 0;
overflow: hidden;
visibility: hidden;
}
/* line 305, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-cursor {
position: absolute;
pointer-events: none;
}
/* line 309, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-measure pre {
position: static;
}
/* line 311, vendor/assets/codemirror/lib/codemirror.css */
div.CodeMirror-cursors {
visibility: hidden;
position: relative;
z-index: 3;
}
/* line 316, vendor/assets/codemirror/lib/codemirror.css */
div.CodeMirror-dragcursors {
visibility: visible;
}
/* line 320, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-focused div.CodeMirror-cursors {
visibility: visible;
}
/* line 324, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-selected {
background: #d9d9d9;
}
/* line 325, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-focused .CodeMirror-selected {
background: #d7d4f0;
}
/* line 326, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-crosshair {
cursor: crosshair;
}
/* line 327, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection {
background: #d7d4f0;
}
.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection {
background: #d7d4f0;
}
/* line 328, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection {
background: #d7d4f0;
}
/* line 330, vendor/assets/codemirror/lib/codemirror.css */
.cm-searching {
background-color: #ffa;
background-color: rgba(255, 255, 0, 0.4);
}
/* Used to force a border model for a node */
/* line 336, vendor/assets/codemirror/lib/codemirror.css */
.cm-force-border {
padding-right: .1px;
}
@media print {
/* Hide the cursor when printing */
/* line 340, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror div.CodeMirror-cursors {
visibility: hidden;
}
}
/* See issue #2901 */
/* line 346, vendor/assets/codemirror/lib/codemirror.css */
.cm-tab-wrap-hack:after {
content: '';
}
/* Help users use markselection to safely style text background */
/* line 349, vendor/assets/codemirror/lib/codemirror.css */
span.CodeMirror-selectedtext {
background: none;
}
/* line 1, app/assets/stylesheets/admins/common.scss */
.admin-body-container {
padding: 20px;
@ -17952,6 +18553,11 @@ form.was-validated select:valid ~ .select2-container--bootstrap4 .select2-select
padding: 0.5rem 2rem;
}
/* line 110, app/assets/stylesheets/admins/common.scss */
.admin-body-container .CodeMirror {
border: 1px solid #ced4da;
}
/* line 2, app/assets/stylesheets/admins/daily_school_statistics.scss */
.admins-daily-school-statistics-index-page .daily-school-statistic-list-container {
text-align: center;
@ -18359,7 +18965,7 @@ form.was-validated select:valid ~ .select2-container--bootstrap4 .select2-select
text-align: center;
}
/* line 11, app/assets/stylesheets/admin.scss */
/* line 12, app/assets/stylesheets/admin.scss */
body {
width: 100vw;
height: 100vh;
@ -18373,33 +18979,53 @@ body {
background: #efefef;
}
/* line 23, app/assets/stylesheets/admin.scss */
/* line 24, app/assets/stylesheets/admin.scss */
a:hover {
text-decoration: unset;
}
/* line 28, app/assets/stylesheets/admin.scss */
/* line 29, app/assets/stylesheets/admin.scss */
textarea.danger, input.danger {
border-color: #dc3545 !important;
}
/* line 32, app/assets/stylesheets/admin.scss */
/* line 33, app/assets/stylesheets/admin.scss */
label.error {
color: #dc3545 !important;
}
/* line 38, app/assets/stylesheets/admin.scss */
/* line 39, app/assets/stylesheets/admin.scss */
.simple_form .form-group .collection_radio_buttons {
margin-bottom: 0px;
}
/* line 42, app/assets/stylesheets/admin.scss */
/* line 43, app/assets/stylesheets/admin.scss */
.simple_form .form-group .form-check-inline {
height: calc(1.5em + 0.75rem + 2px);
}
/* line 48, app/assets/stylesheets/admin.scss */
/* line 49, app/assets/stylesheets/admin.scss */
.flex-1 {
-webkit-box-flex: 1;
flex: 1;
}
/* line 53, app/assets/stylesheets/admin.scss */
.font-12 {
font-size: 12px !important;
}
/* line 54, app/assets/stylesheets/admin.scss */
.font-14 {
font-size: 14px !important;
}
/* line 55, app/assets/stylesheets/admin.scss */
.font-16 {
font-size: 16px !important;
}
/* line 56, app/assets/stylesheets/admin.scss */
.font-18 {
font-size: 18px !important;
}

@ -17816,6 +17816,607 @@ form.was-validated select:valid ~ .select2-container--bootstrap4 .select2-select
.datepicker.datepicker-inline td {
padding: 4px 5px;
}
/* BASICS */
/* line 3, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror {
/* Set height, width, borders, and global font properties here */
font-family: monospace;
height: 300px;
color: black;
direction: ltr;
}
/* PADDING */
/* line 13, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-lines {
padding: 4px 0;
/* Vertical padding around content */
}
/* line 16, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror pre.CodeMirror-line,
.CodeMirror pre.CodeMirror-line-like {
padding: 0 4px;
/* Horizontal padding of content */
}
/* line 21, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
background-color: white;
/* The little square between H and V scrollbars */
}
/* GUTTER */
/* line 27, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutters {
border-right: 1px solid #ddd;
background-color: #f7f7f7;
white-space: nowrap;
}
/* line 33, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-linenumber {
padding: 0 3px 0 5px;
min-width: 20px;
text-align: right;
color: #999;
white-space: nowrap;
}
/* line 41, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-guttermarker {
color: black;
}
/* line 42, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-guttermarker-subtle {
color: #999;
}
/* CURSOR */
/* line 46, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-cursor {
border-left: 1px solid black;
border-right: none;
width: 0;
}
/* Shown when moving in bi-directional text */
/* line 52, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror div.CodeMirror-secondarycursor {
border-left: 1px solid silver;
}
/* line 55, vendor/assets/codemirror/lib/codemirror.css */
.cm-fat-cursor .CodeMirror-cursor {
width: auto;
border: 0 !important;
background: #7e7;
}
/* line 60, vendor/assets/codemirror/lib/codemirror.css */
.cm-fat-cursor div.CodeMirror-cursors {
z-index: 1;
}
/* line 63, vendor/assets/codemirror/lib/codemirror.css */
.cm-fat-cursor-mark {
background-color: rgba(20, 255, 20, 0.5);
-webkit-animation: blink 1.06s steps(1) infinite;
animation: blink 1.06s steps(1) infinite;
}
/* line 69, vendor/assets/codemirror/lib/codemirror.css */
.cm-animate-fat-cursor {
width: auto;
border: 0;
-webkit-animation: blink 1.06s steps(1) infinite;
animation: blink 1.06s steps(1) infinite;
background-color: #7e7;
}
@-webkit-keyframes blink {
0% {
}
50% {
background-color: transparent;
}
100% {
}
}
@keyframes blink {
0% {
}
50% {
background-color: transparent;
}
100% {
}
}
/* Can style cursor different in overwrite (non-insert) mode */
/* line 96, vendor/assets/codemirror/lib/codemirror.css */
.cm-tab {
display: inline-block;
text-decoration: inherit;
}
/* line 98, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-rulers {
position: absolute;
left: 0;
right: 0;
top: -50px;
bottom: 0;
overflow: hidden;
}
/* line 103, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-ruler {
border-left: 1px solid #ccc;
top: 0;
bottom: 0;
position: absolute;
}
/* DEFAULT THEME */
/* line 111, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-header {
color: blue;
}
/* line 112, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-quote {
color: #090;
}
/* line 113, vendor/assets/codemirror/lib/codemirror.css */
.cm-negative {
color: #d44;
}
/* line 114, vendor/assets/codemirror/lib/codemirror.css */
.cm-positive {
color: #292;
}
/* line 115, vendor/assets/codemirror/lib/codemirror.css */
.cm-header, .cm-strong {
font-weight: bold;
}
/* line 116, vendor/assets/codemirror/lib/codemirror.css */
.cm-em {
font-style: italic;
}
/* line 117, vendor/assets/codemirror/lib/codemirror.css */
.cm-link {
text-decoration: underline;
}
/* line 118, vendor/assets/codemirror/lib/codemirror.css */
.cm-strikethrough {
text-decoration: line-through;
}
/* line 120, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-keyword {
color: #708;
}
/* line 121, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-atom {
color: #219;
}
/* line 122, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-number {
color: #164;
}
/* line 123, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-def {
color: #00f;
}
/* line 128, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-variable-2 {
color: #05a;
}
/* line 129, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-variable-3, .cm-s-default .cm-type {
color: #085;
}
/* line 130, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-comment {
color: #a50;
}
/* line 131, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-string {
color: #a11;
}
/* line 132, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-string-2 {
color: #f50;
}
/* line 133, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-meta {
color: #555;
}
/* line 134, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-qualifier {
color: #555;
}
/* line 135, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-builtin {
color: #30a;
}
/* line 136, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-bracket {
color: #997;
}
/* line 137, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-tag {
color: #170;
}
/* line 138, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-attribute {
color: #00c;
}
/* line 139, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-hr {
color: #999;
}
/* line 140, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-link {
color: #00c;
}
/* line 142, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-error {
color: #f00;
}
/* line 143, vendor/assets/codemirror/lib/codemirror.css */
.cm-invalidchar {
color: #f00;
}
/* line 145, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-composing {
border-bottom: 2px solid;
}
/* Default styles for common addons */
/* line 149, vendor/assets/codemirror/lib/codemirror.css */
div.CodeMirror span.CodeMirror-matchingbracket {
color: #0b0;
}
/* line 150, vendor/assets/codemirror/lib/codemirror.css */
div.CodeMirror span.CodeMirror-nonmatchingbracket {
color: #a22;
}
/* line 151, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-matchingtag {
background: rgba(255, 150, 0, 0.3);
}
/* line 152, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-activeline-background {
background: #e8f2ff;
}
/* STOP */
/* The rest of this file contains styles related to the mechanics of
the editor. You probably shouldn't touch them. */
/* line 159, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror {
position: relative;
overflow: hidden;
background: white;
}
/* line 165, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-scroll {
overflow: scroll !important;
/* Things will break if this is overridden */
/* 30px is the magic margin used to hide the element's real scrollbars */
/* See overflow: hidden in .CodeMirror */
margin-bottom: -30px;
margin-right: -30px;
padding-bottom: 30px;
height: 100%;
outline: none;
/* Prevent dragging from highlighting the element */
position: relative;
}
/* line 175, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-sizer {
position: relative;
border-right: 30px solid transparent;
}
/* The fake, visible scrollbars. Used to force redraw during scrolling
before actual scrolling happens, thus preventing shaking and
flickering artifacts. */
/* line 183, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
position: absolute;
z-index: 6;
display: none;
}
/* line 188, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-vscrollbar {
right: 0;
top: 0;
overflow-x: hidden;
overflow-y: scroll;
}
/* line 193, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-hscrollbar {
bottom: 0;
left: 0;
overflow-y: hidden;
overflow-x: scroll;
}
/* line 198, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-scrollbar-filler {
right: 0;
bottom: 0;
}
/* line 201, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter-filler {
left: 0;
bottom: 0;
}
/* line 205, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutters {
position: absolute;
left: 0;
top: 0;
min-height: 100%;
z-index: 3;
}
/* line 210, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter {
white-space: normal;
height: 100%;
display: inline-block;
vertical-align: top;
margin-bottom: -30px;
}
/* line 217, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter-wrapper {
position: absolute;
z-index: 4;
background: none !important;
border: none !important;
}
/* line 223, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter-background {
position: absolute;
top: 0;
bottom: 0;
z-index: 4;
}
/* line 228, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter-elt {
position: absolute;
cursor: default;
z-index: 4;
}
/* line 233, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter-wrapper ::-moz-selection {
background-color: transparent;
}
.CodeMirror-gutter-wrapper ::selection {
background-color: transparent;
}
/* line 234, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter-wrapper ::-moz-selection {
background-color: transparent;
}
/* line 236, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-lines {
cursor: text;
min-height: 1px;
/* prevents collapsing before first draw */
}
/* line 240, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror pre.CodeMirror-line,
.CodeMirror pre.CodeMirror-line-like {
/* Reset some styles that the rest of the page might have set */
border-radius: 0;
border-width: 0;
background: transparent;
font-family: inherit;
font-size: inherit;
margin: 0;
white-space: pre;
word-wrap: normal;
line-height: inherit;
color: inherit;
z-index: 2;
position: relative;
overflow: visible;
-webkit-tap-highlight-color: transparent;
-webkit-font-variant-ligatures: contextual;
font-variant-ligatures: contextual;
}
/* line 260, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-wrap pre.CodeMirror-line,
.CodeMirror-wrap pre.CodeMirror-line-like {
word-wrap: break-word;
white-space: pre-wrap;
word-break: normal;
}
/* line 267, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-linebackground {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 0;
}
/* line 273, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-linewidget {
position: relative;
z-index: 2;
padding: 0.1px;
/* Force widget margins to stay inside of the container */
}
/* line 281, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-rtl pre {
direction: rtl;
}
/* line 283, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-code {
outline: none;
}
/* Force content-box sizing for the elements where we expect it */
/* line 288, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-scroll,
.CodeMirror-sizer,
.CodeMirror-gutter,
.CodeMirror-gutters,
.CodeMirror-linenumber {
box-sizing: content-box;
}
/* line 297, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-measure {
position: absolute;
width: 100%;
height: 0;
overflow: hidden;
visibility: hidden;
}
/* line 305, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-cursor {
position: absolute;
pointer-events: none;
}
/* line 309, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-measure pre {
position: static;
}
/* line 311, vendor/assets/codemirror/lib/codemirror.css */
div.CodeMirror-cursors {
visibility: hidden;
position: relative;
z-index: 3;
}
/* line 316, vendor/assets/codemirror/lib/codemirror.css */
div.CodeMirror-dragcursors {
visibility: visible;
}
/* line 320, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-focused div.CodeMirror-cursors {
visibility: visible;
}
/* line 324, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-selected {
background: #d9d9d9;
}
/* line 325, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-focused .CodeMirror-selected {
background: #d7d4f0;
}
/* line 326, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-crosshair {
cursor: crosshair;
}
/* line 327, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection {
background: #d7d4f0;
}
.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection {
background: #d7d4f0;
}
/* line 328, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection {
background: #d7d4f0;
}
/* line 330, vendor/assets/codemirror/lib/codemirror.css */
.cm-searching {
background-color: #ffa;
background-color: rgba(255, 255, 0, 0.4);
}
/* Used to force a border model for a node */
/* line 336, vendor/assets/codemirror/lib/codemirror.css */
.cm-force-border {
padding-right: .1px;
}
@media print {
/* Hide the cursor when printing */
/* line 340, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror div.CodeMirror-cursors {
visibility: hidden;
}
}
/* See issue #2901 */
/* line 346, vendor/assets/codemirror/lib/codemirror.css */
.cm-tab-wrap-hack:after {
content: '';
}
/* Help users use markselection to safely style text background */
/* line 349, vendor/assets/codemirror/lib/codemirror.css */
span.CodeMirror-selectedtext {
background: none;
}
/* line 1, app/assets/stylesheets/admins/common.scss */
.admin-body-container {
padding: 20px;
@ -17952,6 +18553,11 @@ form.was-validated select:valid ~ .select2-container--bootstrap4 .select2-select
padding: 0.5rem 2rem;
}
/* line 110, app/assets/stylesheets/admins/common.scss */
.admin-body-container .CodeMirror {
border: 1px solid #ced4da;
}
/* line 2, app/assets/stylesheets/admins/daily_school_statistics.scss */
.admins-daily-school-statistics-index-page .daily-school-statistic-list-container {
text-align: center;
@ -18359,7 +18965,7 @@ form.was-validated select:valid ~ .select2-container--bootstrap4 .select2-select
text-align: center;
}
/* line 11, app/assets/stylesheets/admin.scss */
/* line 12, app/assets/stylesheets/admin.scss */
body {
width: 100vw;
height: 100vh;
@ -18373,36 +18979,56 @@ body {
background: #efefef;
}
/* line 23, app/assets/stylesheets/admin.scss */
/* line 24, app/assets/stylesheets/admin.scss */
a:hover {
text-decoration: unset;
}
/* line 28, app/assets/stylesheets/admin.scss */
/* line 29, app/assets/stylesheets/admin.scss */
textarea.danger, input.danger {
border-color: #dc3545 !important;
}
/* line 32, app/assets/stylesheets/admin.scss */
/* line 33, app/assets/stylesheets/admin.scss */
label.error {
color: #dc3545 !important;
}
/* line 38, app/assets/stylesheets/admin.scss */
/* line 39, app/assets/stylesheets/admin.scss */
.simple_form .form-group .collection_radio_buttons {
margin-bottom: 0px;
}
/* line 42, app/assets/stylesheets/admin.scss */
/* line 43, app/assets/stylesheets/admin.scss */
.simple_form .form-group .form-check-inline {
height: calc(1.5em + 0.75rem + 2px);
}
/* line 48, app/assets/stylesheets/admin.scss */
/* line 49, app/assets/stylesheets/admin.scss */
.flex-1 {
-webkit-box-flex: 1;
flex: 1;
}
/* line 53, app/assets/stylesheets/admin.scss */
.font-12 {
font-size: 12px !important;
}
/* line 54, app/assets/stylesheets/admin.scss */
.font-14 {
font-size: 14px !important;
}
/* line 55, app/assets/stylesheets/admin.scss */
.font-16 {
font-size: 16px !important;
}
/* line 56, app/assets/stylesheets/admin.scss */
.font-18 {
font-size: 18px !important;
}
@charset "UTF-8";
/* line 1, app/assets/stylesheets/admins/common.scss */
.admin-body-container {
@ -18539,6 +19165,11 @@ label.error {
.admin-body-container .nav-tabs .nav-link {
padding: 0.5rem 2rem;
}
/* line 110, app/assets/stylesheets/admins/common.scss */
.admin-body-container .CodeMirror {
border: 1px solid #ced4da;
}
/* line 2, app/assets/stylesheets/admins/daily_school_statistics.scss */
.admins-daily-school-statistics-index-page .daily-school-statistic-list-container {
text-align: center;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 137 KiB

@ -446,7 +446,7 @@ table.text-file{}
/*-------------------------------实训路径-------------------------------*/
.path-head{width: 100%;height: 300px;background-image: url("/images/educoder/path.png");
background-color: #131b39;
background-color: #000a4f;
/*background-size: cover;*/
background-position: center;
background-repeat: no-repeat;

@ -207,7 +207,7 @@ function generateNewIndexJsp() {
if (window.location.host == 'pre-newweb.educoder.net') {
_host = 'https://testali-cdn.educoder.net/react/build/'
} else if (window.location.host == 'www.educoder.net') {
_host = 'https://ali-newweb.educoder.net/react/build/'
_host = 'https://ali-cdn.educoder.net/react/build/'
}
document.write('<script type="text/javascript" src="' + _host + 'js/js_min_all.js"><\\/script>');
document.write('<script type="text/javascript" src="' + _host + 'static/js/main.${matchResult[1]}.js"><\\/script>');

@ -1,10 +1,10 @@
const isDev = window.location.port == 3007;
export const TEST_HOST = "http://pre-newweb.educoder.net"
export const TEST_HOST = "https://pre-newweb.educoder.net"
export function getImageUrl(path) {
// https://www.educoder.net
// https://testbdweb.trustie.net
// const local = 'http://localhost:3000'
const local = 'http://pre-newweb.educoder.net'
const local = 'https://pre-newweb.educoder.net'
if (isDev) {
return `${local}/${path}`
}
@ -12,7 +12,7 @@ export function getImageUrl(path) {
}
export function setImagesUrl(path){
const local = 'http://pre-newweb.educoder.net'
const local = 'https://pre-newweb.educoder.net'
let firstStr=path.substr(0,1);
// console.log(firstStr);
if(firstStr=="/"){
@ -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 = 'http://pre-newweb.educoder.net'
const local = 'https://pre-newweb.educoder.net'
if (isDev) {
return `${local}${path?path:''}`
}

@ -119,20 +119,21 @@ class Fileslists extends Component{
}).then((response) => {
if(response!=undefined){
if(response.data&&response.data){
let list=response.data.course_modules;
let course_second_categoriess;
list.map((item,key)=>{
course_second_categoriess=item.course_second_categories
})
this.setState({
course_modules:response.data,
has_course_groups:response.data.has_course_groups,
course_second_categories:course_second_categoriess
})
}
if(response.data&&response.data) {
if (response.data.status != 401) {
let list = response.data.course_modules;
let course_second_categoriess;
list.map((item, key) => {
course_second_categoriess = item.course_second_categories
})
this.setState({
course_modules: response.data,
has_course_groups: response.data.has_course_groups,
course_second_categories: course_second_categoriess
})
}
}
}

@ -162,7 +162,7 @@ class CommonWorkAppraise extends Component{
let {course_name, homework_name, search, page, loadingstate, homework_status, reference_answer,
attachments, homework_id, project_info, work_members, is_evaluation,
description, update_user_name, update_time, commit_time, author_name,
description, update_user_name, commit_user_name, update_time, commit_time, author_name,
revise_attachments, revise_reason, atta_update_user, atta_update_time, atta_update_user_login,
Modalstype,Modalstopval,ModalCancel,ModalSave,loadtype, is_leader_work
} =this.state;
@ -237,7 +237,7 @@ class CommonWorkAppraise extends Component{
{ commit_time &&
<React.Fragment>
<span className={"color9B9B fr mr30"}>提交</span>
<span className={"fr font-14 mr10 ml10"}>{author_name}</span>
<span className={"fr font-14 mr10 ml10"}>{commit_user_name}</span>
<span className={" color9B9B fr"}>
{moment(commit_time).format('YYYY-MM-DD HH:mm')==="Invalid date"?"":moment(commit_time).format('YYYY-MM-DD HH:mm')}
</span>
@ -271,15 +271,19 @@ class CommonWorkAppraise extends Component{
{work_members.map((item, index) => {
return <React.Fragment>
{isAdmin ?
<a className={`color-blue ${index == 0 ? '' : 'ml10'}`} href="javascript:void(0)"
<a className={`color-blue ${index == 0 ? '' : 'ml12'}`} href="javascript:void(0)"
onClick={() => this.props.toWorkDetailPage(this.props.match.params, null, item.work_id)}
>
{item.user_name}
</a> : <span className={`${index == 0 ? '' : 'ml10'}`} >{item.user_name}</span>}
</a> : <span className={`${index == 0 ? '' : 'ml12'}`} >{item.user_name}</span>}
{item.is_leader && <LeaderIcon small={true} ></LeaderIcon>}
</React.Fragment>
})}
</div>
{isAdmin && <div className="font-12 color-grey-9">
<span >温馨提示</span>
点击其他组员的姓名可以快速评阅TA的作品
</div>}
</div>
</div>

@ -430,7 +430,7 @@ class CommonWorkList extends Component{
}
_getRequestParams() {
const { search, arg_work_status, arg_teacher_comment, arg_course_group, order, page } = this.state
const { search, arg_work_status, arg_teacher_comment, arg_course_group, order, page, arg_member_work } = this.state
return {
page,
search,
@ -441,6 +441,7 @@ class CommonWorkList extends Component{
limit: PAGE_SIZE,
b_order: orderMap[order],
group_id:arg_course_group,
member_work: arg_member_work
}
}
fetchData = () => {
@ -488,6 +489,11 @@ class CommonWorkList extends Component{
this.fetchList()
})
}
memberWorkChange = (values, isAllChecked) => {
this.setState({arg_member_work: isAllChecked ? '' : values[0], page: 1}, () => {
this.fetchList()
})
}
funorder = (order) => {
this.setState({ order }, () => {
this.fetchList()
@ -557,7 +563,7 @@ class CommonWorkList extends Component{
late_penalty, absence_penalty, appeal_penalty
,end_immediately ,publish_immediately
, homework_id, visible, work_group, project_info
, homework_id, visible, work_group, project_info, is_leader
} =this.state;
@ -581,10 +587,21 @@ class CommonWorkList extends Component{
return { label: `${item.name}(${item.count})`, value: item.id }
})
// 1:组长, 0:组员,“” 不限
const member_works = [{
name: '组长', id: 1
}, {
name: '组员', id: 0
}]
const options_member_work = member_works.map((item) => {
return { label: `${item.name}`, value: item.id }
})
const isAdmin = this.props.isAdmin()
const isStudent = this.props.isStudent()
const isAdminOrStudent = this.props.isAdminOrStudent()
const isGroup = this.props.isGroup();
// work_group
let StudentData;
@ -593,7 +610,7 @@ class CommonWorkList extends Component{
}else{
StudentData=isStudent ? [{ id, user_name, user_login, student_id, group_name, work_status, update_time, teacher_score, teaching_asistant_score, student_score,
ultimate_score, work_score, student_comment_count, appeal_all_count, appeal_deal_count,
late_penalty, absence_penalty, appeal_penalty, project_info,
late_penalty, absence_penalty, appeal_penalty, project_info, is_leader,
work_group, isMine: true }] : []
}
const columns = buildColumns(this, student_works, StudentData)
@ -678,6 +695,7 @@ class CommonWorkList extends Component{
<CheckAllGroup options={options_teacher_comment} label={'你的评阅:'} onChange={this.teacherCommentOptionChange}></CheckAllGroup>
<CheckAllGroup options={options_status} label={'作品状态:'} onChange={this.statusOptionChange}></CheckAllGroup>
{isGroup && <CheckAllGroup options={options_member_work} label={'组内角色:'} onChange={this.memberWorkChange}></CheckAllGroup>}
{options_course_group.length > 1 && <CheckAllGroup options={options_course_group} label={'分班情况:'} onChange={this.courseGroupOptionChange} checkboxGroupStyle={{width: '980px'}}></CheckAllGroup>}

@ -1,12 +1,14 @@
import React,{Component} from "React";
import { Form, Select, Input, Button,Checkbox,Upload,Icon,message,Modal,Tooltip} from "antd";
import {Link} from 'react-router-dom';
import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor';
import { WordsBtn, getUploadActionUrl, appendFileSizeToUploadFile, appendFileSizeToUploadFileAll } from 'educoder';
import axios from 'axios';
import Modals from '../../modals/Modals';
import _ from 'lodash'
import { WordsBtn, getUploadActionUrl, appendFileSizeToUploadFile, appendFileSizeToUploadFileAll, ConditionToolTip } from 'educoder';
import Modals from '../../modals/Modals';
import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor';
import LeaderIcon from './common/LeaderIcon'
const Search = Input.Search;
const CheckboxGroup = Checkbox.Group;
@ -722,21 +724,25 @@ render(){
<div className={"ml20"} style={{width:"100%"}}>
<div className={"members fl"}
<div className={"members workPost fl"}
onScroll={this.contentViewScroll}>
<CheckboxGroup value={task_status} onChange={this.funtaskstatus} style={{ paddingTop: '4px'}}>
{members.map((item,key)=>{
return(
<div key={key} style={{
width: '375px',
// width: '375px',
height: '30px'
}}>
<Checkbox value={item.user_id} key={item.user_id} checked={parseInt(task_status[key])===item.user_id?true:false}
disabled={item.commit_status || item.user_id == this.props.current_user.user_id ===true?true:false} className="fl "
></Checkbox>
<Tooltip placement="bottom" title={item.user_nam}><div className={"fl ml5 fonthidden"} style={{width: '70px'}}>{item.user_name}</div></Tooltip>
<div className={"fl ml20 color-grey-9"}>{item.group_name}</div>
<div className={"fl ml20 color-grey-9"}>{item.student_id}</div>
<div className={"fl ml20 color-grey-9 overflowHidden1"} style={{ maxWidth: '147px' }}
title={item.group_name && item.group_name.length > 9 ? item.group_name : ''}
>{item.group_name}</div>
<div className={"fl ml20 color-grey-9 overflowHidden1"} style={{ maxWidth: '99px' }}
title={item.student_id && item.student_id.length > 12 ? item.student_id : ''}
>{item.student_id}</div>
<div className={"fl ml20"}>{item.commit_status===true?<span className={"color-orange"}>已提交</span> :""}</div>
</div>
)
@ -753,22 +759,38 @@ render(){
text-overflow:ellipsis;
white-space:nowrap
}
.members .leaderIcon {
}
.workPost.members {
width: 452px;
}
`}</style>
<div className={"members fl"}>
<div className={"members workPost fl"}>
{selectmemberslist&&selectmemberslist.map((item,key)=>{
const _is_leader = (item.is_leader || !this.isEdit && key==0)
return(
<div key={key} style={{
width: '375px',
// width: '375px',
height: '30px',
display:item.user_name===undefined?"none":""
}}>
<Tooltip placement="bottom" title={item.user_nam}>
<div className={"fl ml5 fonthidden"} style={{width: '50px'}}>{item.user_name}</div>
</Tooltip>
<div className={"fl ml40 color-grey-9"}>{item.group_name}</div>
<div className={"fl ml40 color-grey-9"}>{item.student_id}</div>
{item.user_id != this.props.current_user.user_id ?<div className={"fr"}><i className={"iconfont icon-shanchudiao fl "} style={{marginTop:'-4px'}} onClick={()=>this.delecttask_status(item.user_id)}></i></div>:""}
<ConditionToolTip placement="bottom" title={'组长'} condition={_is_leader && item.user_name.length > 5}>
<div className={"fl ml5 fonthidden"} style={{width: '98px', color: `${item.user_name.length > 5 && _is_leader ? '#4CACFF' : 'inherit'}`}}>
{item.user_name}{ item.user_name.length <= 5 && _is_leader && <LeaderIcon className="leaderIcon" small={true}></LeaderIcon>}
</div>
</ConditionToolTip>
<div className={"fl ml20 color-grey-9 overflowHidden1"} style={{ maxWidth: '147px' }}
title={item.group_name && item.group_name.length > 9 ? item.group_name : ''}
>{item.group_name}</div>
<div className={"fl ml20 color-grey-9 overflowHidden1"} style={{ maxWidth: '99px' }}
title={item.student_id && item.student_id.length > 12 ? item.student_id : ''}
>{item.student_id}</div>
{item.user_id != this.props.current_user.user_id ?
<div className={"fr"}><i className={"iconfont icon-shanchudiao fl "}
style={{marginTop:'-4px'}} onClick={()=>this.delecttask_status(item.user_id)}></i></div>:""}
</div>
)
})}

@ -2,17 +2,20 @@ import React,{Component} from "React";
export default function LeaderIcon(props = {}) {
let icon = null;
const { className, style } = props;
const _className = `font-8 blueFull Actionbtn ${className}`
if (props.small) {
icon = <div className="font-8 blueFull Actionbtn" style={{
icon = <div className={_className} style={{
height: '14px',
'line-height': '14px',
width: '24px',
padding: 0,
// width: '24px',
transform: 'scale(0.833)',
padding: '0px 5px',
'margin-top': '-2px',
'margin-left': '2px',
'vertical-align': 'middle', }}>组长</div>
} else {
icon = <div className="font-8 blueFull Actionbtn" style={{ height: '16px', 'line-height': '16px', width: '30px'}}>组长</div>
icon = <div className={_className} style={{ height: '16px', 'line-height': '16px', transform: 'scale(0.833)'}}>组长</div>
}
return icon

@ -31,15 +31,16 @@ class WorkDetailPageHeader extends Component{
}
goback = () => {
// let workId=this.props.match.params.workId;
//
// if ( window.location.pathname.indexOf('appraise') == -1) {
// let category_id= this.props.category.category_id;
// this.props.toListPage(this.props.match.params, category_id)
// } else {
// this.props.toWorkListPage(this.props.match.params, this.props.match.params.category_id)
// }
this.props.history.goBack()
let workId=this.props.match.params.workId;
if ( window.location.pathname.indexOf('appraise') == -1) {
let category_id= this.props.category.category_id;
this.props.toListPage(this.props.match.params, category_id)
} else {
this.props.toWorkListPage(this.props.match.params, workId)
// this.props.match.params.category_id
}
// this.props.history.goBack()
}
// 补交附件
Cancelvisible=()=>{

@ -711,8 +711,12 @@ class GraduationTaskssettinglist extends Component{
title: '姓名',
dataIndex: 'name',
key: 'name',
className:'edu-txt-center'
className:'edu-txt-center',
render: (text, record) => (
<span>
<div style={{color:'#9A9A9A'}} className={"studentname"} title={record.name}>{record.name}</div>
</span>
),
}, {
title: '学号',
dataIndex: 'stduynumber',
@ -720,7 +724,7 @@ class GraduationTaskssettinglist extends Component{
className:'edu-txt-center',
render: (text, record) => (
<span>
<a style={{color:'#9A9A9A'}}>{record.stduynumber}</a>
<div style={{color:'#9A9A9A'}} className={"studentnumber"} title={record.stduynumber}>{record.stduynumber}</div>
</span>
),
}, {
@ -935,6 +939,7 @@ class GraduationTaskssettinglist extends Component{
if(taskslistdata&&taskslistdata.have_grouping===false){
columns.some((item,key)=> {
if (item.title === "分组") {
columns.splice(key, 1)
@ -961,10 +966,38 @@ class GraduationTaskssettinglist extends Component{
.linbox{
height: 26px;
}
.ant-table-tbody>tr>td, .ant-table-thead>tr>th{
padding: 16px 10px
}
`
}
</style>
{taskslistdata&&taskslistdata.have_grouping===true||
taskslistdata&&taskslistdata.have_project===true||
taskslistdata&&taskslistdata.cross_comment===true?<style>
{
`
.studentnumber{
text-overflow: ellipsis;
white-space: nowrap;
width: 105px;
display: block;
overflow: hidden;
margin: 0px auto;
cursor: default;
}
.studentname{
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 90px;
margin: 0px auto;
}
`
}</style>:""}
{/*提示*/}
<Modals
modalsType={Modalstype}
@ -1207,22 +1240,25 @@ class GraduationTaskssettinglist extends Component{
{taskslistdata.search_assistants&&taskslistdata.search_assistants.course_group_info.length!=0?<li className="clearfix">
<span className="fl mr10 color-grey-8">分班情况</span>
<div className="fl mr25">
<a id="graduation_comment_no_limit" className={course_group===null||course_group===undefined?"pl10 pr10 check_on":"pl10 pr10 "} onClick={()=>this.groupgroup([])}>不限</a>
</div>
<div className="fl groupList" style={{maxWidth: '945px'}}>
<CheckboxGroup value={course_group} onChange={(e)=>this.groupgroup(e,taskslistdata.search_assistants&&taskslistdata.search_assistants.course_group_info.length)} style={{width: '1000px', paddingTop: '4px'}}>
{taskslistdata.search_assistants&&taskslistdata.search_assistants.course_group_info.map((item,key)=>{
return(
<span key={key} className={"mt10"}>
<Checkbox value={item.course_group_id} key={item.course_group_id} className="fl ">{item.group_group_name}
<span>({item.count})</span>
</Checkbox>
</span>
)
})}
</CheckboxGroup>
</div>
<CheckboxGroup value={course_group} onChange={(e)=>this.groupgroup(e,taskslistdata.search_assistants&&taskslistdata.search_assistants.course_group_info.length)} style={{width: '1000px', paddingTop: '4px'}}>
<span className="fl mr25">
<a id="graduation_comment_no_limit" className={course_group===null||course_group===undefined?"pl10 pr10 check_on":"pl10 pr10 "} onClick={()=>this.groupgroup([])}>不限</a>
</span>
{taskslistdata.search_assistants&&taskslistdata.search_assistants.course_group_info.map((item,key)=>{
return(
<span key={key} className={"mt10"}>
<Checkbox value={item.course_group_id} key={item.course_group_id} className="fl ">{item.group_group_name}
<span>({item.count})</span>
</Checkbox>
</span>
)
})}
</CheckboxGroup>
</li>:""}

@ -45,6 +45,7 @@ class CreateGroupByImportModal extends Component{
.then((response) => {
if (response.data.status == 0) {
this.props.showNotification(response.data.message)
this.props.createGroupImportSuccess && this.props.createGroupImportSuccess()
this.setVisible(false)
} else {

@ -234,6 +234,9 @@ class studentsList extends Component{
off('updateNavSuccess', this.updateNavSuccess)
}
}
createGroupImportSuccess = () => {
this.props.updataleftNavfun()
}
updateNavSuccess = () => {
this.fetchCourseGroups()
}
@ -556,7 +559,9 @@ class studentsList extends Component{
firstRowRight={
<React.Fragment>
{ isSuperAdmin && <React.Fragment>
<CreateGroupByImportModal ref="createGroupByImportModal" {...this.props}></CreateGroupByImportModal>
<CreateGroupByImportModal ref="createGroupByImportModal" {...this.props}
createGroupImportSuccess={this.createGroupImportSuccess}
></CreateGroupByImportModal>
<WordsBtn style="blue" className="mr30" onClick={()=> this.refs['createGroupByImportModal'].setVisible(true)}>导入创建分班</WordsBtn>
</React.Fragment> }
{ isAdmin && isParent && <WordsBtn style="blue" className="mr30" onClick={()=>this.addDir()}>添加分班</WordsBtn> }

@ -42,6 +42,7 @@ function buildColumns(that) {
title: '序号',
dataIndex: 'name',
key: 'index',
width: 78,
render: (content, item, index) => {
return index + 1
// return item.isApply == true ? '' : <a href="javascript:;">{(that.state.page - 1) * 20 + index + 1
@ -74,6 +75,7 @@ function buildColumns(that) {
dataIndex: 'role',
key: 'role',
sorter: showSorter,
width: 86,
// 'ascend' | 'descend'
defaultSortOrder: 'ascend',
sortDirections: sortDirections,
@ -160,16 +162,16 @@ function buildColumns(that) {
},
})
}
if(isAdminOrTeacher && hasGraduationModule) {
columns.unshift({
title: '',
dataIndex: 'course_member_id',
key: 'course_member_id',
render: (content, item, index) => {
return content ? <Checkbox value={content}></Checkbox> : ''
}
})
}
// if(isAdminOrTeacher && hasGraduationModule) {
// columns.unshift({
// title: '',
// dataIndex: 'course_member_id',
// key: 'course_member_id',
// render: (content, item, index) => {
// return content ? <Checkbox value={content}></Checkbox> : ''
// }
// })
// }
return columns
}

@ -45,10 +45,11 @@ class CoursesNew extends Component {
fetching:false,
boolxinjian:false,
checkboxgroup:undefined,
addonAfteronelenone:0,
addonAfteronelentwo:0,
checkbofrup:[{module_type:"shixun_homework",module_name:"实训作业"},{module_type:"common_homework",module_name:"普通作业"},{module_type:"group_homework",module_name:"分组作业"}
,{module_type:"exercise",module_name:"试卷"},{module_type:"poll",module_name:"问卷"},{module_type:"graduation",module_name:"毕业设计"}
,{module_type:"board",module_name:"讨论"},{module_type:"attachment",module_name:"资源"},{module_type:"course_group",module_name:"分班"}],
checkbofrups:[],
}
}
componentDidMount() {
@ -66,8 +67,8 @@ class CoursesNew extends Component {
this.props.form.setFieldsValue({
course: data.course_list_name,
classroom: data.name,
period: data.class_period,
credit: data.credit,
period: data.class_period===undefined?'':data.class_period===null?'':data.class_period===null?'':data.class_period==="null"?'':data.class_period+"",
credit: data.credit===undefined?'':data.credit===null?'':data.credit===null?'':data.credit==="null"?'':data.credit+"",
checkboxgroup: data.course_module_types,
Realnamecertification: data.authentication,
Professionalcertification:data.professional_certification,
@ -81,7 +82,9 @@ class CoursesNew extends Component {
is_public: data.is_public === 1 ? true : false,
Realnamecertification: data.authentication,
Professionalcertification:data.professional_certification,
// checkbofrups:data.course_modules,
addonAfteronelenone: data.class_period===undefined?'':data.class_period===null?'':data.class_period===null?'':data.class_period==="null"?'':data.class_period,
addonAfteronelentwo:data.credit===undefined?'':data.credit===null?'':data.credit===null?'':data.credit==="null"?'':data.credit,
});
// try {
// if(data.course_modules===undefined||data.course_modules.length===0){
@ -94,6 +97,9 @@ class CoursesNew extends Component {
// checkbofrups:this.state.checkbofrup,
// });
// }
this.handleSearchschool(data.school);
}).catch((error) => {
console.log(error);
@ -469,10 +475,20 @@ class CoursesNew extends Component {
const optionschool = this.state.searchlistscholl===undefined?"":this.state.searchlistscholl===null?"":this.state.searchlistscholl==="[]"?"":this.state.searchlistscholl.map(z => <Option key={z} value={z}>{z}</Option>);
// console.log(this.props.current_user.user_school)
// form合并了
console.log("获取到的数据");
console.log(this.state);
console.log(this.props);
console.log(this.props.current_user);
// console.log("获取到的数据");
// console.log(this.state);
// console.log(this.props);
// console.log(this.props.current_user);
var addonAfterone=this.props.form&&this.props.form.getFieldValue('period');
var addonAfteronelen=0;
if(addonAfterone){
addonAfteronelen=String(addonAfterone).length;
}
var addonAftertwo=this.props.form&&this.props.form.getFieldValue('credit');
var addonAfteronelens=0;
if(addonAftertwo){
addonAfteronelens=String(addonAftertwo).length;
}
return (
<React.Fragment>
@ -630,6 +646,23 @@ class CoursesNew extends Component {
}
`}
</style>
<style>{
`
.yslzxueshi .ant-input{
border-right: none !important;
height: 40px !important;
width: 236px !important
}
.yslzxueshi .ant-input-group {
width: 280px !important;
}
.yslzxueshi .ant-input-group-addon{
width: 44px !important;
background-color: #fafafa!important;
}
`
}</style>
<Form.Item
label="总学时"
hasFeedback
@ -637,15 +670,15 @@ class CoursesNew extends Component {
{getFieldDecorator("period",
{
rules:[{
required:false,
pattern: new RegExp(/^[0-9]\d*$/, "g"),
message: ''
}],
getValueFromEvent: (event) => {
return event.target.value.replace(/\D/g,'')
}}
pattern: new RegExp(/^[0-9]+([.]{1}[0-9]+){0,1}$/, "g"),
message: '必须是数值'
},
{
max:5,
message: '不能超过5个字符',
}]}
)(
<Input id="period" className="greyInput " placeholder="例如30"/>
<Input id="period" className="yslzxueshi " placeholder="例如30" addonAfter={String(addonAfteronelen)+"/5"} maxLength={5}/>
)}
</Form.Item>
@ -656,15 +689,16 @@ class CoursesNew extends Component {
{getFieldDecorator("credit",
{
rules:[{
required:false,
pattern: new RegExp(/^[0-9]\d*$/, "g"),
message: ''
}],
getValueFromEvent: (event) => {
return event.target.value.replace(/\D/g,'')
}}
pattern: new RegExp(/^[0-9]+([.]{1}[0-9]+){0,1}$/, "g"),
message: '必须是数值'
},
{
max:5,
message: '不能超过5个字符',
}
]}
)(
<Input id="credit" className={"greyInput "} placeholder="例如3"/>
<Input id="credit" className="yslzxueshi" placeholder="例如3" addonAfter={String(addonAfteronelen)+"/5"} maxLength={5}/>
)}
</Form.Item>
<Form.Item
@ -696,7 +730,6 @@ class CoursesNew extends Component {
label="课堂模块"
hasFeedback
>
{getFieldDecorator("checkboxgroup", {
initialValue: [
"shixun_homework", "common_homework", "group_homework", "exercise", "attachment", "course_group",

@ -27,7 +27,9 @@ function disabledDateTime() {
disabledMinutes: () => range(1, 30).concat(range(31, 60)),
};
}
function disabledDate(current) {
return current && current < moment().endOf('day').subtract(1, 'days');
}
// function disabledDate(current) {
// console.log(current);
// return current && current < moment().endOf('day').subtract(1, 'days');
@ -49,13 +51,15 @@ class Goldsubject extends Component {
fetching:false,
subject_id:"",
start_date:"",
addonAfteronelenone:"",
addonAfteronelentwo:"",
Whethertocreateanewclassroom:true,
checkbofrup:[
{module_type:"announcement",module_name:"公告栏"},{module_type:"online_learning",module_name:"在线学习"}
,{module_type:"shixun_homework",module_name:"实训作业"},{module_type:"common_homework",module_name:"普通作业"}
,{module_type:"exercise",module_name:"试卷"},{module_type:"poll",module_name:"问卷"}
,{module_type:"attachment",module_name:"资源"},{module_type:"board",module_name:"讨论"},{module_type:"course_group",module_name:"分班"},],
checkbofrups:[],
}
}
// disabledEndDate= endValue => {
@ -121,8 +125,8 @@ class Goldsubject extends Component {
this.props.form.setFieldsValue({
course: data.course_list_name,
classroom: data.name,
period: data.class_period,
credit: data.credit,
period: data.class_period===undefined?'':data.class_period===null?'':data.class_period===null?'':data.class_period==="null"?'':data.class_period+"",
credit: data.credit===undefined?'':data.credit===null?'':data.credit===null?'':data.credit==="null"?'':data.credit+"",
checkboxgroup: data.course_module_types,
Realnamecertification: data.authentication,
Professionalcertification:data.professional_certification,
@ -140,22 +144,13 @@ class Goldsubject extends Component {
Professionalcertification:data.professional_certification,
name: data.name,
class_period: data.class_period,
credit: parseFloat(data.credit),
addonAfteronelenone: data.class_period===undefined?'':data.class_period===null?'':data.class_period===null?'':data.class_period==="null"?'':data.class_period,
credit: parseFloat(data.credit),
addonAfteronelentwo:data.credit===undefined?'':data.credit===null?'':data.credit===null?'':data.credit==="null"?'':data.credit,
course_module_types: data.course_module_types,
school:data.school,
Whethertocreateanewclassroom:false,
});
// try {
// if(data.course_modules===undefined||data.course_modules.length===0){
// this.setState({
// checkbofrups:this.state.checkbofrup,
// });
// }
// }catch (e) {
// this.setState({
// checkbofrups:this.state.checkbofrup,
// });
// }
this.handleSearchschool(data.school);
}).catch((error) => {
console.log(error);
@ -607,7 +602,7 @@ class Goldsubject extends Component {
this.applyForAddOrgForm.setVisible(true)
}
render() {
let {datatime,datatimetwo,school,searchlistscholl,Whethertocreateanewclassroom} = this.state;
let {datatime,datatimetwo,school,searchlistscholl,Whethertocreateanewclassroom,addonAfteronelenone,addonAfteronelentwo} = this.state;
const {getFieldDecorator} = this.props.form;
const propsWithoutForm = Object.assign({}, this.props)
delete propsWithoutForm.form
@ -619,6 +614,18 @@ class Goldsubject extends Component {
// console.log(this.state);
// console.log(this.props);
// console.log(this.props.current_user);
var addonAfterone=this.props.form&&this.props.form.getFieldValue('period');
var addonAfteronelen=0;
if(addonAfterone){
addonAfteronelen=String(addonAfterone).length;
}
var addonAftertwo=this.props.form&&this.props.form.getFieldValue('credit');
var addonAfteronelens=0;
if(addonAftertwo){
addonAfteronelens=String(addonAftertwo).length;
}
console.log(addonAfteronelenone);
console.log(addonAfteronelentwo);
return (
<React.Fragment>
@ -786,6 +793,23 @@ class Goldsubject extends Component {
}
`}
</style>
<style>{
`
.yslzxueshi .ant-input{
border-right: none !important;
height: 40px !important;
width: 236px !important
}
.yslzxueshi .ant-input-group {
width: 280px !important;
}
.yslzxueshi .ant-input-group-addon{
width: 44px !important;
background-color: #fafafa!important;
}
`
}</style>
<Form.Item
label="总学时"
hasFeedback
@ -793,18 +817,17 @@ class Goldsubject extends Component {
{getFieldDecorator("period",
{
rules:[{
required:false,
pattern: new RegExp(/^[0-9]\d*$/, "g"),
message: ''
}],
getValueFromEvent: (event) => {
return event.target.value.replace(/\D/g,'')
}}
pattern: new RegExp(/^[0-9]+([.]{1}[0-9]+){0,1}$/, "g"),
message: '必须是数值'
},
{
max:5,
message: '不能超过5个字符',
}]}
)(
<Input id="period" className="greyInput " placeholder="例如30"/>
<Input id="period" placeholder="例如30" className="yslzxueshi" addonAfter={String(addonAfteronelen)+"/5"} maxLength={5}/>
)}
</Form.Item>
<Form.Item
label="学分"
hasFeedback
@ -812,15 +835,16 @@ class Goldsubject extends Component {
{getFieldDecorator("credit",
{
rules:[{
required:false,
pattern: new RegExp(/^[0-9]\d*$/, "g"),
message: ''
}],
getValueFromEvent: (event) => {
return event.target.value.replace(/\D/g,'')
}}
pattern: new RegExp(/^[0-9]+([.]{1}[0-9]+){0,1}$/, "g"),
message: '必须是数值'
},
{
max:5,
message: '不能超过5个字符',
}
]}
)(
<Input id="credit" className={"greyInput "} placeholder="例如3"/>
<Input id="credit" placeholder="例如3" className="yslzxueshi" addonAfter={String(addonAfteronelens)+"/5"} maxLength={5}/>
)}
</Form.Item>
<Form.Item
@ -850,7 +874,8 @@ class Goldsubject extends Component {
label="结束时间"
>
{getFieldDecorator("endtime", {
rules: [{type: 'object',required: true, message: "结束时间不能为空"}],
rules: [{type: 'object',
required: true, message: "结束时间不能为空"}],
})(
<span className="fl mt5">
<DatePicker

@ -1210,7 +1210,7 @@ class ShixunHomework extends Component{
</div>
{
datas===undefined?'' :datas.homeworks.length===0? <NoneData></NoneData>:""
datas===undefined?"":datas.homeworks && datas.homeworks.length===0? <NoneData></NoneData>:""
}
</Spin>

@ -37,13 +37,18 @@ class MemoDetailMDEditor extends Component {
const placeholder = '我要回复...'
// const imageUrl = `/upload_with_markdown?container_id=${this.props.memo.id}&container_type=Memo`;
const imageUrl = `/api/attachments.json`;
if (this.isMDInited) {
return;
}
this.isMDInited = true
// 执行太快了,样式不正常
window.__tt = 400;
setTimeout(() => {
console.log('create_editorMD_4comment')
var commentMDEditor = window.create_editorMD_4comment("memo_comment_editorMd", '', this.props.height || 240, placeholder, imageUrl, () => {
// commentMDEditor.focus()
this.isMDInited = true
this.initDrag()
commentMDEditor.cm.on("change", (_cm, changeObj) => {
@ -119,7 +124,7 @@ class MemoDetailMDEditor extends Component {
this.initMDEditor()
} else {
setTimeout(() => {
this.commentMDEditor.focus()
this.commentMDEditor && this.commentMDEditor.focus()
}, 10)
}
}

File diff suppressed because it is too large Load Diff

@ -882,11 +882,11 @@ class MainContentContainer extends Component {
}
render() {
const { challenge, output_sets, time_limit } = this.props;
const { challenge, output_sets, time_limit, real_time_limit } = this.props;
// TODO 这里直接写死了game_status在超时时也会返回这句话
let msg = (
<div>
{`代码评测超时!本关限定时间为:${time_limit}s`}<br></br>
{`代码评测超时!本关限定时间为:${real_time_limit}s`}<br></br>
请检查代码是否存在死循环或其他耗时操作
</div>)

@ -116,20 +116,40 @@ class TPICodeSetting extends Component {
<div className="ide-settings--item-value">代码补全</div>
</div> */}
<div className="-layout-h -center -justify-between">
<div className="ide-settings--item-key">CTRL + S</div>
<div className="ide-settings--item-value">保存代码</div>
<div className="ide-settings--item-key">保存代码</div>
<div className="ide-settings--item-value">Ctrl + S</div>
</div>
<div className="-layout-h -center -justify-between">
<div className="ide-settings--item-key">唤出快捷键列表</div>
<div className="ide-settings--item-value">F1 / Alt + F1</div>
</div>
<div className="-layout-h -center -justify-between">
<div className="ide-settings--item-key">左右缩进</div>
<div className="ide-settings--item-value">Ctrl + ]/[</div>
</div>
<div className="-layout-h -center -justify-between">
<div className="ide-settings--item-key">跳到匹配的括号</div>
<div className="ide-settings--item-value">Ctrl + Shift + \</div>
</div>
<div className="-layout-h -center -justify-between">
<div className="ide-settings--item-key">转到行首</div>
<div className="ide-settings--item-value">Home</div>
</div>
<div className="-layout-h -center -justify-between">
<div className="ide-settings--item-key">转到行尾</div>
<div className="ide-settings--item-value">End</div>
</div>
</div>
<h3 className="ide-settings--section -light ">
学员使用说明
关卡配置信息
{/* <a href="https://github.com/Microsoft/monaco-editor/wiki/Monaco-Editor-Accessibility-Guide"></a> */}
</h3>
<div className="-padding-24 ">
<div className="-padding-24 " style={{ marginBottom: '40px' }}>
<Tooltip title={ task_pass ? "允许学员跳关挑战实训" : "禁止学员跳关挑战实训"} disableFocusListener={true}>
<div className="-layout-h -center -justify-between">
<div className="ide-settings--item-key">跳关</div>
<div className="ide-settings--item-value">{ task_pass ? 'on' : 'off'}</div>
<div className="ide-settings--item-value">{ task_pass ? '允许' : '不允许'}</div>
</div>
</Tooltip>
@ -137,7 +157,7 @@ class TPICodeSetting extends Component {
: "禁止学员通过金币解锁查看测试集内容"} disableFocusListener={true}>
<div className="-layout-h -center -justify-between">
<div className="ide-settings--item-key">测试集解锁</div>
<div className="ide-settings--item-value">{ test_set_permission ? 'on' : 'off'}</div>
<div className="ide-settings--item-value">{ test_set_permission ? '允许' : '不允许'}</div>
</div>
</Tooltip>
@ -145,15 +165,15 @@ class TPICodeSetting extends Component {
: "启用页面复制和粘贴功能"} disableFocusListener={true}>
<div className="-layout-h -center -justify-between">
<div className="ide-settings--item-key">禁止代码复制粘贴</div>
<div className="ide-settings--item-value">{ forbid_copy ? 'on' : 'off'}</div>
<div className="ide-settings--item-key">代码复制粘贴</div>
<div className="ide-settings--item-value">{ !forbid_copy ? '允许' : '不允许'}</div>
</div>
</Tooltip>
</div>
<h3 className="ide-settings--section -light ">
{/* <a href="https://github.com/Microsoft/monaco-editor/wiki/Monaco-Editor-Accessibility-Guide"></a> */}
{/* <h3 className="ide-settings--section -light ">
说明
{/* <a href="https://github.com/Microsoft/monaco-editor/wiki/Monaco-Editor-Accessibility-Guide"></a> */}
</h3>
<div className="-padding-24 " style={{ marginBottom: '75px' }}>
<div className="-layout-h -center -justify-between">
@ -161,7 +181,7 @@ class TPICodeSetting extends Component {
本编辑器和Visual Studio Code的快捷键一致使用F1或Alt+F1 (Internet Explorer)可以唤出快捷键列表
</div>
</div>
</div>
</div> */}
</div>

@ -53,17 +53,17 @@ class ActionView extends Component {
// request
window._tpiWidthResize = () => {
const _w = window.$('#actionView').width();
if (_w < 446) {
window.$('#time-consuming').hide()
// window.$('#time-consuming').hide()
} else if (_w < 746) {
// 文字放出来之前是 580
window.$('#time-consuming').show()
window.$('.time_limit').hide()
} else {
window.$('#time-consuming').show()
window.$('.time_limit').show()
}
// if (_w < 446) {
// window.$('#time-consuming').hide()
// // window.$('#time-consuming').hide()
// } else if (_w < 746) {
// // 文字放出来之前是 580
// window.$('#time-consuming').show()
// window.$('.time_limit').hide()
// } else {
// window.$('#time-consuming').show()
// window.$('.time_limit').show()
// }
}
}
@ -77,6 +77,9 @@ class ActionView extends Component {
return (
<div className="-flex -layout-h" id="game_operate_action">
<style>{`
#game_operate_action {
width: 100%;
}
.time_limit {
margin-right: 0px;
}
@ -89,11 +92,19 @@ class ActionView extends Component {
position: relative;
top: 3px;
}
#time-consuming {
flex: auto;
overflow: hidden;
white-space: nowrap;
}
#game_operate_action .act_btn {
flex: 0 0 90px;
}
`}</style>
<span className="mt10 -flex c_grey ml15" id="time-consuming">
{!!time_limit &&
<span className="time_limit">{`本关最大执行时间:${real_time_limit}`}
<span className="spliter"></span>
{!gameBuilding && record && <span className="spliter"></span>}
</span>}
{!gameBuilding && record ?
// <Tooltip title={ "本次评测耗时(编译、运行总时间)" }></Tooltip>
@ -105,7 +116,11 @@ class ActionView extends Component {
{/*将第一个按钮改为visibility方式隐藏不然加载时测评按钮会出现没有垂直居中的情况*/}
<Tooltip title={ "倒计时为0时服务将被关闭" }>
<Button size="small" className={classes.button + ' actionViewfirstButton'} onClick={()=>this.showWebDisplay(challenge)}
style={{ visibility: challenge.showWebDisplayButton ? '': 'hidden'}}
style={{ visibility: challenge.showWebDisplayButton ? '': 'hidden',
minWidth: challenge.showWebDisplayButton ? '': '1px',
width: challenge.showWebDisplayButton ? '': '1px',
flex: `0 0 ${challenge.showWebDisplayButton ? '110px': '1px'}`
}}
id="showWebDisplayButton"
// style={{ display: challenge.showWebDisplayButton ? 'flex': 'none'}}
>
@ -116,7 +131,7 @@ class ActionView extends Component {
{
!gameBuilding &&
(game && !!game.prev_game) ?
<Link to={`/tasks/${game.prev_game}`} className={classes.buttonText}>
<Link to={`/tasks/${game.prev_game}`} className={classes.buttonText + ' act_btn'}>
<Button size="small" className={classes.button}>
上一关
</Button>
@ -126,7 +141,7 @@ class ActionView extends Component {
{/*未发布的都能跳转*/}
{ !gameBuilding &&
((game && (game.status === 2 || shixun.status < 2) || shixun && shixun.task_pass ) && !!game.next_game) ?
<Link to={`/tasks/${game.next_game}`} className={classes.buttonText}>
<Link to={`/tasks/${game.next_game}`} className={classes.buttonText + ' act_btn'}>
<Button size="small" className={classes.button}>
下一关
</Button>
@ -135,11 +150,11 @@ class ActionView extends Component {
<div id="code_test">
<div id="code_test" className="act_btn">
{
st === 1 && game.status === 2 ?
<Tooltip title={ "已通关的选择题任务无法再次测评" }>
<a href="javascript:void(0)" className="shixun-task-btn mr15 gray"
<a href="javascript:void(0)" className="shixun-task-btn mr15 gray "
>
<i className="fa fa-play-circle font-16"></i>
测评
@ -148,7 +163,7 @@ class ActionView extends Component {
:
gameBuilding ?
<a href="javascript:void(0)" className="shixun-task-btn mr15 gray"
<a href="javascript:void(0)" className="shixun-task-btn mr15 gray "
>
<i className="fa fa-play-circle font-16"></i>
测评

@ -3,7 +3,7 @@ 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} from 'antd';
import {Input, Select, Radio, Checkbox, Popconfirm, message, Modal,Icon,DatePicker,Breadcrumb,Upload,Button,notification, Tooltip} from 'antd';
// import "antd/dist/antd.css";
@ -1607,9 +1607,12 @@ export default class TPMsettings extends Component {
{
settingsData === undefined ? "" : settingsData.shixun.main_type.map((item, key) => {
return(
<Option value={item.id} key={key} title={item.description} >{item.type_name}</Option>
return (
<Option value={item.id} key={key} >
<Tooltip placement="right" title={item.description} >
{item.type_name}
</Tooltip>
</Option>
)
})
}

@ -120,7 +120,9 @@ export default class TPManswer extends Component {
let urlAnswer = `/shixuns/${id}/challenges/${checkpointId}/answer.json`;
axios.get(urlAnswer).then((response) => {
if (response.data) {
if (response.data.status === 401) {
} else if (response.data) {
this.setState({ answers: response.data })
}
})
@ -158,7 +160,7 @@ export default class TPManswer extends Component {
if (!isValidate) {
return;
}
if (totalScore != 100) {
if (answersParams.length != 0 && totalScore != 100) {
this.props.showSnackbar("请先保证占比和为100%");
return;
}

@ -646,7 +646,7 @@ export default class TPMevaluation extends Component {
let newchallenge={
path:shixunfilepath,
exec_path:shixunfilepathplay,
show_type:pathoptionvalue===-1?undefined:pathoptionvalue,
show_type:pathoptionvalue,
original_picture_path:pathoptionvalue===-1?undefined:shixunfileexpectpicturepath===undefined?null:shixunfileexpectpicturepath,
expect_picture_path:pathoptionvalue===-1?undefined:shixunfilestandardpicturepath===undefined?null:shixunfilestandardpicturepath,
picture_path:pathoptionvalue===-1?undefined:shixunfilepicturepath===undefined?null:shixunfilepicturepath,

@ -411,9 +411,7 @@ export default class TPMquestion extends Component {
window.location.href = '/shixuns/'+this.props.match.params.shixunId+'/challenges/'+this.props.match.params.checkpointId+'/editquestion';
}
questionadd=()=>{
$('html').animate({
scrollTop: 10
}, 200);
let{questionaddarray}=this.state;
let questionaddsums=questionaddarray.length;
@ -431,15 +429,13 @@ export default class TPMquestion extends Component {
}
})
questionaddarrays.push({type:0,choose_id:0});
this.setState({
activetype:0,
questionaddarray:questionaddarrays,
questionaddtype:true,
editquestionaddtype:false,
newquestionaddtype:true,
editquestionaddtype:false,
questionlists:[{str:"A",val:"",type:false},{str:"B",val:"",type:false},{str:"C",val:"",type:false},{str:"D",val:"",type:false}],
answeshixunsGroup: 1,
answeoptions:[10,20],
@ -447,17 +443,17 @@ export default class TPMquestion extends Component {
shixunssanswerkillvalue:"",
shixunsskillanswerlist:[],
contentMdRefval:"",
newquestioMDMdContval:""
newquestioMDMdContval:"",
})
// setTimeout(() => {
// this.newquestioMDMdCont.current.setValue('')
// this.newquestioMDMdRef.current.setValue('')
//
// }, 2000)
this.shixunsautoHeight()
setTimeout(() => {
this.newquestioMDMdRef.current.setValue('')
}, 1000)
setTimeout(() => {
this.newquestioMDMdCont.current.setValue('')
}, 1500)
// this.shixunsautoHeight()
}
editquestionlists=(newquestionlists)=>{
@ -592,6 +588,10 @@ export default class TPMquestion extends Component {
}
answer_subit=(sumtype,challenge_choose_id)=>{
$('html').animate({
scrollTop:10
}, 500);
let {challenge_id,questionlists,shixunsskillanswerlist,answeonshixunsmark,answeshixunsGroup,questionaddarray} =this.state;
if(challenge_id===undefined){
message.error("关卡id为空");
@ -679,6 +679,7 @@ export default class TPMquestion extends Component {
let id = this.props.match.params.shixunId;
let checkpointId=this.props.match.params.checkpointId
let url;
if(sumtype==="edit"){
let newquestioMDvalue = this.neweditanswerRef.current.getValue().trim();
@ -781,7 +782,9 @@ export default class TPMquestion extends Component {
description:
'新建成功,请点击右侧加号继续添加',
});
// this.getanswer_subitlist()
window.location.href=`/shixuns/${id}/challenges/${checkpointId}/editquestion/${response.data.challenge_choose_id}`;
// this.getanswer_subitlist()
// this.gochooseid("/shixuns/"+this.props.match.params.shixunId+"/challenges/"+this.props.match.params.checkpointId+"/editquestion"+"/"+response.data.challenge_choose_id)
}).catch((error) => {
console.log(error)
@ -817,7 +820,8 @@ export default class TPMquestion extends Component {
neweditanswerRefval:'',
editanswersRefval:''
})
this.newquestioMDMdRef.current.setValue('')
this.newquestioMDMdCont.current.setValue('')
}else{
let id = this.props.match.params.shixunId;
let url ='/shixuns/'+id+'/challenges/'+challenge_id+'/edit_choose_question.json?choose_id='+challenge_choose_id;
@ -920,6 +924,7 @@ export default class TPMquestion extends Component {
<Option key={d} id={k}>{d}</Option>
)
})
return (
<React.Fragment>
<div className="educontent mt30 mb30">

@ -4,6 +4,8 @@ import {Input, Select, Radio, Checkbox, Popconfirm, message, Modal,Tooltip} from
import {BrowserRouter as Router, Route, Link, Switch} from "react-router-dom";
import axios from 'axios';
import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor';
const Option = Select.Option;
@ -21,7 +23,17 @@ export default class TpmQuestionEdit extends Component {
componentDidMount() {
}
delecbtns=()=>{
let url=`/shixuns/${this.props.match.params.shixunId}/challenges/${this.props.match.params.checkpointId}/destroy_challenge_choose.json`;
axios.delete((url), { data: {
choose_id:this.props.match.params.choose_id
}})
.then((result)=>{
if(result.data.status===1){
window.location.href=`/shixuns/${this.props.match.params.shixunId}/challenges/${this.props.match.params.checkpointId}/editquestion`;
}
})
}
render() {
@ -202,6 +214,9 @@ export default class TpmQuestionEdit extends Component {
onClick={()=>this.props.answer_subit()}>提交</a>
<a href={this.props.go_back_url}
className="defalutCancelbtn fl">取消</a>
<a onClick={()=>this.delecbtns()}
className="delectshixuncdbtn fr">删除</a>
</div>
</div>

@ -35,7 +35,7 @@ export default class TpmQuestionNew extends Component {
<span className="mr30 color-orange pt10">*</span>
<div className="flex1 mr20">
<TPMMDEditor ref={this.props.newquestioMDMdRef} placeholder="请输入选择题的题干内容" mdID={'newquestioMDid'} refreshTimeout={1500}
watch={true} className="courseMessageMD" initValue={this.props.contentMdRefval}></TPMMDEditor>
needRecreate={true} watch={true} className="courseMessageMD" initValue={this.props.contentMdRefval}></TPMMDEditor>
</div>
@ -108,7 +108,7 @@ export default class TpmQuestionNew extends Component {
<div className={"df"}>
<div className={"flex1 mr20"}>
<TPMMDEditor ref={this.props.newquestioMDMdCont} placeholder="请输入各个选项的具体解析或其他相关信息" mdID={'newquestioMDMdConts'} refreshTimeout={1500}
watch={true} className="courseMessageMD" initValue={this.props.newquestioMDMdContval}></TPMMDEditor>
needRecreate={true} watch={true} className="courseMessageMD" initValue={this.props.newquestioMDMdContval}></TPMMDEditor>
</div>
<div className={"choose_names"} style={{display:this.props.newquestioMDvaluetypes===true?"block":"none"}}>
<span className="color-orange mt8 fl" id="choose_name" style={{display: 'inline'}}><i className="fa fa-exclamation-circle mr3"></i></span>

@ -4,7 +4,7 @@ import {TPMIndexHOC} from '../TPMIndexHOC';
import {SnackbarHOC,appendFileSizeToUploadFileAll} from 'educoder';
import {Input, Select, Radio, Checkbox, Modal, Icon, DatePicker,Upload,Button,message,Form,notification} from 'antd';
import {Input, Select, Radio, Checkbox, Modal, Icon, DatePicker,Upload,Button,message,Form,notification,Tooltip} from 'antd';
// import "antd/dist/antd.css";
@ -993,9 +993,11 @@ class Newshixuns extends Component {
{
newshixunlist === undefined ? "" : newshixunlist.main_type.map((item, key) => {
return (
<Option value={item.id} key={key}>{item.type_name}</Option>
<Option value={item.id} key={key} >
<Tooltip placement="right" title={item.description} >
{item.type_name}
</Tooltip>
</Option>
)
})
}

@ -1,122 +1,124 @@
import React, { Component } from 'react';
import {Link} from 'react-router-dom';
import {Tooltip,Menu} from 'antd';
import {getImageUrl} from 'educoder';
import "./usersInfo.css"
import "../../courses/css/members.css"
import "../../courses/css/Courses.css"
import { LinkAfterLogin } from 'educoder'
class InfosBanner extends Component{
constructor(props){
super(props);
}
render(){
let {
data ,
id,
login,
moduleName,
current_user,
}=this.props;
let is_current=this.props.is_current;
let {username}= this.props.match.params;
let {pathname}=this.props.location;
moduleName=pathname.split("/")[3];
return(
<div className="bannerPanel mb60">
<div className="educontent">
<div className="clearfix color-white mb25">
<p className="myPhoto mr20 fl"><img alt="头像" src={data && `${getImageUrl('images/'+data.avatar_url)}`}/></p>
<div className="fl">
<p className="clearfix mt20">
<span className="username task-hide" style={{"maxWidth":'370px'}}>{data && data.name}</span>
{
data && is_current == false && data.identity =="学生" ? "" :
<span className="userpost"><label>{data && data.identity}</label></span>
}
</p>
<p className="mt15">
<Tooltip placement='bottom' title={ data && data.professional_certification ?"已职业认证":"未职业认证"}>
<i className={ data && data.professional_certification ? "iconfont icon-shenfenzhenghaomaguizheng font-18 user-colorgrey-green mr20 ml2":"iconfont icon-shenfenzhenghaomaguizheng font-18 user-colorgrey-B8 mr20 ml2"}></i>
</Tooltip>
<Tooltip placement='bottom' title={ data && data.authentication ?"已实名认证":"未实名认证"}>
<i className={ data && data.authentication ? "iconfont icon-renzhengshangjia font-18 user-colorgrey-green":"iconfont icon-renzhengshangjia font-18 user-colorgrey-B8"}></i>
</Tooltip>
</p>
</div>
<div className="fr">
<div class="fl headtab mt20">
<span>{is_current ? "我":"TA"}的经验值</span>
<a style={{"cursor":"default"}}>{data && data.experience}</a>
</div>
<div class="fl headtab mt20 pr leftTransform pl20">
<span>{is_current ? "我":"TA"}的金币</span>
<a style={{"cursor":"default"}}>{data && data.grade}</a>
</div>
{
is_current ?
<span className="fl mt35 ml60">
{
data && data.attendance_signed ?
<span className="user_default_btn user_grey_btn font-18">已签到</span>
:
<a herf="javascript:void(0);" onClick={this.props.signFor} className="user_default_btn user_yellow_btn fl font-18">签到</a>
}
</span>
:
<span className="fl mt35 ml60">
<LinkAfterLogin
{...this.props}
{...this.state}
className="user_default_btn user_yellow_btn fl font-18"
to={`/messages/${login}/message_detail?target_ids=${id}`}
>
私信
</LinkAfterLogin>
</span>
}
</div>
</div>
<div className="userNav">
<li className={`${moduleName == 'courses' ||moduleName == undefined ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'courses'})}
to={`/users/${username}/courses`}>翻转课堂</Link>
</li>
<li className={`${moduleName == 'shixuns' ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'shixuns'})}
to={`/users/${username}/shixuns`}>开发社区</Link>
</li>
<li className={`${moduleName == 'paths' ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'paths'})}
to={`/users/${username}/paths`}>实践课程</Link>
</li>
<li className={`${moduleName == 'projects' ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'projects'})}
to={`/users/${username}/projects`}>项目</Link>
</li>
<li className={`${moduleName == 'package' ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'package'})}
to={`/users/${username}/package`}>众包</Link>
</li>
{((is_current && current_user && current_user.is_teacher ) || current_user && current_user.admin)
&& <li className={`${moduleName == 'videos' ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'videos'})}
to={`/users/${username}/videos`}>视频</Link>
</li>}
</div>
</div>
</div>
)
}
}
import React, { Component } from 'react';
import {Link} from 'react-router-dom';
import {Tooltip,Menu} from 'antd';
import {getImageUrl} from 'educoder';
import "./usersInfo.css"
import "../../courses/css/members.css"
import "../../courses/css/Courses.css"
import { LinkAfterLogin } from 'educoder'
class InfosBanner extends Component{
constructor(props){
super(props);
}
render(){
let {
data ,
id,
login,
moduleName,
current_user,
}=this.props;
let is_current=this.props.is_current;
let {username}= this.props.match.params;
let {pathname}=this.props.location;
moduleName=pathname.split("/")[3];
return(
<div className="bannerPanel mb60">
<div className="educontent">
<div className="clearfix color-white mb25">
<p className="myPhoto mr20 fl"><img alt="头像" src={data && `${getImageUrl('images/'+data.avatar_url)}`}/></p>
<div className="fl">
<p className="clearfix mt20">
<span className="username task-hide" style={{"maxWidth":'370px'}}>{data && data.name}</span>
{
data && is_current == false && data.identity =="学生" ? "" :
<span className="userpost"><label>{data && data.identity}</label></span>
}
</p>
<p className="mt15">
<Tooltip placement='bottom' title={ data && data.professional_certification ?"已职业认证":"未职业认证"}>
<i className={ data && data.professional_certification ? "iconfont icon-shenfenzhenghaomaguizheng font-18 user-colorgrey-green mr20 ml2":"iconfont icon-shenfenzhenghaomaguizheng font-18 user-colorgrey-B8 mr20 ml2"}></i>
</Tooltip>
<Tooltip placement='bottom' title={ data && data.authentication ?"已实名认证":"未实名认证"}>
<i className={ data && data.authentication ? "iconfont icon-renzhengshangjia font-18 user-colorgrey-green":"iconfont icon-renzhengshangjia font-18 user-colorgrey-B8"}></i>
</Tooltip>
</p>
</div>
<div className="fr">
<div class="fl headtab mt20">
<span>{is_current ? "我":"TA"}的经验值</span>
<a style={{"cursor":"default"}}>{data && data.experience}</a>
</div>
<div class="fl headtab mt20 pr leftTransform pl20">
<span>{is_current ? "我":"TA"}的金币</span>
<a style={{"cursor":"default"}}>{data && data.grade}</a>
</div>
{
is_current ?
<span className="fl mt35 ml60">
{
data && data.attendance_signed ?
<span className="user_default_btn user_grey_btn font-18">已签到</span>
:
<a herf="javascript:void(0);" onClick={this.props.signFor} className="user_default_btn user_yellow_btn fl font-18">签到</a>
}
</span>
:
<span className="fl mt35 ml60">
<LinkAfterLogin
{...this.props}
{...this.state}
className="user_default_btn user_yellow_btn fl font-18"
to={`/messages/${login}/message_detail?target_ids=${id}`}
>
私信
</LinkAfterLogin>
</span>
}
</div>
</div>
<div className="userNav">
<li className={`${moduleName == 'courses' ||moduleName == undefined ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'courses'})}
to={`/users/${username}/courses`}>翻转课堂</Link>
</li>
<li className={`${moduleName == 'shixuns' ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'shixuns'})}
to={`/users/${username}/shixuns`}>实训项目</Link>
</li>
<li className={`${moduleName == 'paths' ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'paths'})}
to={`/users/${username}/paths`}>实践课程</Link>
</li>
<li className={`${moduleName == 'projects' ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'projects'})}
to={`/users/${username}/projects`}>开发项目</Link>
</li>
<li className={`${moduleName == 'package' ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'package'})}
to={`/users/${username}/package`}>众包</Link>
</li>
{((is_current && current_user && current_user.is_teacher ) || current_user && current_user.admin)
&& <li className={`${moduleName == 'videos' ? 'active' : '' }`}>
<Link
onClick={() => this.setState({moduleName: 'videos'})}
to={`/users/${username}/videos`}>视频</Link>
</li>}
</div>
</div>
</div>
)
}
}
export default InfosBanner;

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save