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

dev_sync_trustie
SylorHuang 5 years ago
commit 097f20bebf

@ -11,6 +11,7 @@
//= require select2
//= require jquery.cxselect
//= require bootstrap-datepicker
//= require bootstrap-datetimepicker
//= require bootstrap.viewer
//= require jquery.mloading
//= require jquery-confirm.min

@ -0,0 +1,200 @@
$(document).on('turbolinks:load', function(){
if ($('body.admins-competition-settings-index-page').length > 0) {
var dateOptions = {
autoclose: true,
language: 'zh-CN',
format: 'yyyy-mm-dd',
startDate: '2017-04-01'
};
var timeOptions = {
autoclose: true,
language: 'zh-CN',
format: 'yyyy-mm-dd hh:ii:ss',
minuteStep: 30
};
var defineDateRangeSelect = function (element) {
var options = $.extend({inputs: $(element).find('.start-date, .end-date')}, dateOptions);
$(element).datepicker(options);
$(element).find('.start-date').datepicker().on('changeDate', function (e) {
$(element).find('.end-date').datepicker('setStartDate', e.date);
});
};
// var defineTimeRangeSelect = function (element) {
// var options = $.extend({inputs: $(element).find('.start-date, .end-date')}, timeOptions);
// $(element).datetimepicker(options);
//
// $(element).find('.start-date').datetimepicker().on('changeDate', function (e) {
// $(element).find('.end-date').datetimepicker('setStartDate', e.date);
// });
// };
defineDateRangeSelect('.teaching-mode-date');
defineDateRangeSelect('.competition-start-end-date');
var $basicForm = $('form.basic-setting-form');
$basicForm.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
name: "required",
subTitle: "required",
startTime: "required",
endTime: "required",
mode: "required",
identifier: "required"
}
});
// 保存按钮
$basicForm.on('click', ".submit-btn", function () {
$basicForm.find('.submit-btn').attr('disabled', 'disabled');
$basicForm.find('.error').html('');
var valid = $basicForm.valid();
if ($("input[name='mode']:checked").val() == 2) {
var $courseId = $("input[name='course_id'");
if ($courseId.val() === undefined || $course_id.val().length === 0) {
$courseId.addClass('danger text-danger');
valid = false;
} else {
$courseId.removeClass('danger text-danger');
}
} else if ($("input[name='mode']:checked").val() == 4) {
var $techStartTime = $("input[name='teach_start_time'");
var $techEndTime = $("input[name='teach_end_time'");
if ($techStartTime.val() === undefined || $techStartTime.val().length === 0) {
$techStartTime.addClass('danger text-danger');
valid = false;
} else {
$techStartTime.removeClass('danger text-danger');
}
if ($techEndTime.val() === undefined || $techEndTime.val().length === 0) {
$techEndTime.addClass('danger text-danger');
valid = false;
} else {
$techEndTime.removeClass('danger text-danger');
}
}
if (!valid) return;
$.ajax({
method: 'POST',
dataType: 'json',
url: $basicForm.attr('action'),
data: new FormData($basicForm[0]),
processData: false,
contentType: false,
success: function (data) {
$.notify({message: '保存成功'});
// window.location.reload();
},
error: function (res) {
var data = res.responseJSON;
$basicForm.find('.error').html(data.message);
},
complete: function () {
$basicForm.find('.submit-btn').attr('disabled', false);
}
});
});
}
});
$(function () {
//MD编辑
$("#MD_typeFrom").on("click",".add_MD_type",function () {
var length=$(".MD_type").find(".add_MD_type").length + 1;
var html='<div class="row MD_type mt-2">\n' +
' <div class="col-1 text-right">\n' +
' <label class="checkbox checkbox-primary mt-1">\n' +
' <input id="checkbox_MD_'+length+'" type="checkbox" name="checkbox_MD_'+length+'">\n' +
' <label for="checkbox_MD_'+length+'">&nbsp;</label>\n' +
' </label>\n' +
' </div>\n' +
' <div class="col-md-4"><input type="text" class="form-control" name="MD_type_value_'+length+'" placeholder="导航名称"></div>\n' +
' <div class="col-md-1"><input type="text" class="form-control" name="MD_type_index_'+length+'" /></div>\n' +
' <button class="btn btn-primary waves-effect waves-light btn-xs setBtn_s add_MD_type">+</button>\n' +
' <button class="btn btn-icon waves-effect btn-default waves-light setBtn_s ml10 del_MD_type">×</button>\n' +
' </div>';
$("#MD_typeFrom").append(html);
})
$("#MD_typeFrom").on("click",".del_MD_type",function () {
$(this).parents(".MD_type").remove();
})
//链接
$("#linkForm").on("click",".add_linkBtn",function () {
var length=$("#linkForm").find(".linkFormItem").length + 1;
var html='<div class="row mt-2 lineFromItem">\n' +
' <div class="col-1 text-right">\n' +
' <label class="checkbox checkbox-primary mt-1">\n' +
' <input id="link_'+length+'" type="checkbox" name="link_'+length+'" />\n' +
' <label for="link_'+length+'">&nbsp;</label>\n' +
' </label>\n' +
' </div>\n' +
' <div class="col-md-label mt-1"><input type="text" name="link_name_'+length+'" class="form-control"></div>\n' +
' <div class="col-md-1 mt-1"><input type="text" name="link_index_'+length+'" class="form-control"></div>\n' +
' <div class="col-md-3 mt-1"><input type="text" name="link_info_'+length+'" class="form-control"></div>\n' +
' <button class="mt-1 btn btn-primary waves-effect waves-light btn-xs setBtn_s add_linkBtn">+</button>\n' +
' <button class="mt-1 btn btn-icon waves-effect btn-default waves-light setBtn_s ml10 del_linkBtn">×</button>\n' +
' </div>'
$("#linkForm").append(html)
})
$("#linkForm").on("click",".del_linkBtn",function () {
$(this).parents(".lineFromItem").remove();
})
//有关报名要求
$("#addRequireBtn").on("click",function () {
var length=$("#requireForm").find(".requireForm_item").length + 1;
var html='<div class="row mt-2 mb-4 requireForm_item">\n' +
' <div class="col-1 text-right">&nbsp;&nbsp;</div>\n' +
' <div class="col-1 text-left mt-1">\n' +
' <input type="text" class="form-control" name="min_'+length+'" />\n' +
' </div>\n' +
' <span class="mt-2">~</span>\n' +
' <div class="col-1 mt-1">\n' +
' <input type="text" class="form-control" name="max_'+length+'" />\n' +
' </div>\n' +
' <span class="mt-2">人</span>\n' +
' <div class="col-2 mt-1">\n' +
' <select class="form-control" name="choice_'+length+'" >\n' +
' <option>不限</option>\n' +
' <option>教师</option>\n' +
' <option>学生</option>\n' +
' <option>专业人士</option>\n' +
' </select>\n' +
' </div>\n' +
' <div class="col-2 mt-1">\n' +
' <label class="radio checkbox-primary mt-1" value="require_'+length+'_1">\n' +
' <input id="require_'+length+'_1" name="require_1" type="radio">\n' +
' <label for="require_'+length+'_1">可多次报名</label>\n' +
' </label>\n' +
' </div>\n' +
' <div class="col-2 mt-1">\n' +
' <label class="radio checkbox-primary mt-1" value="require_'+length+'_2">\n' +
' <input id="require_'+length+'_2" name="require_1" type="radio">\n' +
' <label for="require_'+length+'_2">不可多次报名</label>\n' +
' </label>\n' +
' <a href="javascript:void(0)" class="ml20 delRequrieBtn">\n' +
' <i class="fa fa-times-circle font-20 color-grey-c"></i>\n' +
' </a>\n' +
' </div>\n' +
' </div>';
$("#requireForm").append(html);
})
$("#requireForm").on("click",".delRequrieBtn",function () {
$(this).parents(".requireForm_item").remove();
})
})

@ -3,7 +3,7 @@ $(document).on('turbolinks:load', function() {
let search_form = $(".search-form");
//导出
$(".competition-enroll-list-form").on("click","#enroll-lists-export",function () {
window.location.href = "/admins/competitions/"+$(this).attr("data-competition-id")+"/enroll_lists.xls?" + search_form.serialize();
window.location.href = "/admins/competitions/"+$(this).attr("data-competition-id")+"/enroll_lists/export.xlsx?" + search_form.serialize();
});
}
});

File diff suppressed because it is too large Load Diff

@ -0,0 +1,42 @@
.admins-competition-settings-index-page {
.competition-mode-container {
.row {
height: 35px;
}
.des-row {
height: auto;
}
.form-control {
font-size: 14px;
}
//.mode-input {
// input {
// width: 40%;
// }
//}
}
.col-md-label{
-webkit-box-flex: 0;
flex: 0 0 10%;
max-width: 10%;
min-width: 30px;
padding-right: 15px;
padding-left: 15px;
position: relative;
}
.col-md-label-s{
-webkit-box-flex: 0;
flex: 0 0 30px;
padding-right: 15px;
padding-left: 15px;
position: relative;
}
.setBtn_s{
height: 35px;
line-height: 5px;
}
}

@ -34,16 +34,20 @@ input.form-control {
.font-14 { font-size: 14px !important; }
.font-16 { font-size: 16px !important; }
.font-18 { font-size: 18px !important; }
.font-20 { font-size: 20px !important; }
.font-24 { font-size: 24px !important; }
.padding10-5 { padding: 10px 5px;}
.width100 { width: 100%;}
.mb10 { margin-bottom: 10px ;}
.mt10 { margin-top: 10px ;}
.mr10{ margin-right: 10px; }
.ml10{ margin-left: 10px; }.ml20{ margin-left: 20px; }
.textarea-width-100{width:100%; resize: none; border: 1px solid #ccc;}
.padding10{padding: 10px;}
.padding5-10{padding: 5px 10px;}
.position-r{position:relative;}
.color-grey-c{color:#ccc}
.color-blue{color:#4CACFF}
.inline-block{display:inline-block;}
.hide{display: none;}
.show{display: block;}

@ -73,15 +73,21 @@ class AccountsController < ApplicationController
def login
@user = User.try_to_login(params[:login], params[:password])
if @user
# user is already in local database
return normal_status(-2, "违反平台使用规范,账号已被锁定") if @user.locked?
return normal_status(-2, "错误的账号或密码") unless @user.check_password?(params[:password].to_s)
else
return normal_status(-2, "错误的账号或密码") if @user.blank?
# user is already in local database
return normal_status(-2, "违反平台使用规范,账号已被锁定") if @user.locked?
login_control = LimitForbidControl::UserLogin.new(@user)
return normal_status(-2, "登录密码出错已达上限将锁定密码1小时") if login_control.forbid?
password_ok = @user.check_password?(params[:password].to_s)
unless password_ok
login_control.increment!
return normal_status(-2, "错误的账号或密码")
end
successful_authentication(@user)
login_control.clear # 重置每日密码错误次数
session[:user_id] = @user.id
end

@ -1,10 +1,15 @@
class Admins::CompetitionSettingsController < Admins::BaseController
def show
def index
@competition = current_competition
end
def update
Admins::SaveLaboratorySettingService.call(current_competition, form_params)
def basic_setting
Admins::CompetitionBasicSettingService.call(current_competition, basic_form_params)
render_ok
end
def nav_setting
Admins::CompetitionNavSettingService.call(current_competition, nav_form_params)
render_ok
end
@ -14,7 +19,11 @@ class Admins::CompetitionSettingsController < Admins::BaseController
@_current_competition ||= Competition.find(params[:competition_id])
end
def form_params
params.permit(:identifier, :name, :nav_logo, :login_logo, :tab_logo, :footer, navbar: %i[name link hidden])
def basic_form_params
params.permit(:identifier, :name, :sub_title, :start_time, :end_time, :mode, :identifier, :bonus, :awards_count, :description, :course_id, :teach_start_time, :teach_end_time)
end
def nav_form_params
params.permit(:enroll_end_time, competition_staffs: %i[category minimum maximum mutiple_limited], nav_module: %i[module_type name hidden position url])
end
end

@ -0,0 +1,83 @@
class Admins::CompetitionStagesController < Admins::BaseController
def create
if @competition.competition_stages.exists?(name: params[:stage_name])
render_error "已存在同名的阶段"
else
@competition.competition_stages << CompetitionStage.new(name: params[:stage_name])
render_ok
end
end
def update
current_stage.update_attributes!(name: params[:stage_name], score_rate: params[:score_rate])
render_ok
end
def destroy
current_stage.destroy!
render_ok
end
def create_stage_section
ActiveRecord::Base.transaction do
new_section = CompetitionStageSection.create!(competition_id: current_stage.competition_id, competition_stage_id: current_stage.id,
start_time: params[:new_start_time], end_time: params[:new_end_time],
entry: params[:entry], mission_count: params[:mission_count], score_source: params[:score_source])
unless params[:shixun_identifiers].blank?
params[:shixun_identifiers].each do |identifier|
new_section.competition_entries << CompetitionEntry.new(competition_stage_id: current_stage.id, shixun_identifier: identifier)
end
end
render_ok
end
end
def update_stage_section
ActiveRecord::Base.transaction do
section = current_stage.competition_stage_sections.find_by!(id: params[:section_id])
if section.present?
section_entries = section.competition_entries
if !params[:shixun_identifiers]
section_entries.destroy_all
else
if params[:shixun_identifiers].length < section_entries.size
section_entries[params[:shixun_identifiers].length, section_entries.size - 1].each(&:destroy)
end
params[:shixun_identifiers].each_with_index do |identifier, index|
entry = section_entries[index]
if entry.present?
entry.update_attributes!(shixun_identifier: identifier)
else
section.competition_entries << CompetitionEntry.new(competition_stage_id: current_stage.id, shixun_identifier: identifier)
end
end
end
section.update_attributes!(start_time: params[:new_start_time], end_time: params[:new_end_time],
entry: params[:entry], mission_count: params[:mission_count], score_source: params[:score_source])
render_ok
end
end
end
def destroy_stage_section
section = current_stage.competition_stage_sections.find_by!(id: params[:section_id])
section.destroy!
render_ok
end
def calculate_stage_score
end
private
def current_competition
@_current_competition ||= Competition.find(params[:competition_id])
end
def current_stage
@_current_stage ||= CompetitionStage.find_by!(competition_id: params[:competition_id], id: params[:stage_id])
end
end

@ -2,8 +2,7 @@ class Admins::EnrollListsController < Admins::BaseController
def index
@competition = current_competition
params[:sort_by] = params[:sort_by].presence || 'created_at'
params[:sort_direction] = params[:sort_direction].presence || 'desc'
default_sort('created_at', 'desc')
enroll_lists = Admins::CompetitionEnrollListQuery.call(@competition, params)
@params_page = params[:page] || 1
@ -13,10 +12,23 @@ class Admins::EnrollListsController < Admins::BaseController
respond_to do |format|
format.js
format.html
format.xls
format.xls{
filename = "#{@competition.name}竞赛报名列表_#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}.xls"
send_data(shixun_list_xls(shixuns), :type => 'application/octet-stream', :filename => filename_for_content_disposition(filename))
}
end
end
def export
default_sort('created_at', 'desc')
@enroll_lists = Admins::CompetitionEnrollListQuery.call(current_competition, params)
@enroll_lists = @enroll_lists.preload(competition_team: [:user, :teachers], user: { user_extension: :school })
@competition_scores = current_competition.competition_scores.where(competition_stage_id: 0).order("score desc, cost_time desc").pluck(:competition_team_id)
@personal = current_competition.personal?
filename = ["#{current_competition.name}竞赛报名列表", Time.zone.now.strftime('%Y-%m-%d%H:%M:%S')].join('-') << '.xlsx'
render xlsx: 'export', filename: filename
end
private
def current_competition
@_current_competition ||= Competition.find(params[:competition_id])

@ -86,8 +86,18 @@ class ApplicationController < ActionController::Base
when 8, 3, 5
# 邮箱类型的发送
sigle_para = {email: value}
# 60s内不能重复发送
send_email_limit_cache_key = "send_email_60_second_limit:#{value}"
tip_exception(-1, '请勿频繁操作') if Rails.cache.exist?(send_email_limit_cache_key)
# 短时间内不能大量发送
send_email_control = LimitForbidControl::SendEmailCode.new(value)
tip_exception(-1, '邮件发送太频繁,请稍后再试') if send_email_control.forbid?
begin
UserMailer.register_email(value, code).deliver_now
Rails.cache.write(send_email_limit_cache_key, 1, expires_in: 1.minute)
send_email_control.increment!
# Mailer.run.email_register(code, value)
rescue Exception => e
logger_error(e)
@ -112,6 +122,8 @@ class ApplicationController < ActionController::Base
"验证码发送次数超过频率"
when 43
"一天内同一手机号发送次数超过限制"
when 53
"手机号接收超过频率限制"
end
end

@ -60,7 +60,12 @@ class Competitions::CompetitionTeamsController < Competitions::BaseController
def index
@personal = current_competition.personal?
admin_or_business? ? all_competition_teams : user_competition_teams
if admin_or_business?
all_competition_teams
user_competition_teams
else
user_competition_teams
end
end
def create
@ -113,8 +118,8 @@ class Competitions::CompetitionTeamsController < Competitions::BaseController
teams = teams.joins(users: { user_extension: :school }).where('schools.name LIKE ?', "%#{keyword}%")
end
@count = teams.count
@teams = paginate(teams.includes(:user, users: { user_extension: :school }))
@all_count = teams.count
@all_teams = paginate(teams.includes(:user, users: { user_extension: :school }))
end
def user_competition_teams

@ -34,6 +34,7 @@ class Competitions::CompetitionsController < Competitions::BaseController
def update
@competition.update_attributes!(introduction: params[:introduction])
Attachment.associate_container(params[:attachment_ids], @competition.id, @competition.class) if params[:attachment_ids]
normal_status("更新成功")
end
@ -106,13 +107,8 @@ class Competitions::CompetitionsController < Competitions::BaseController
@stage = @competition.competition_stages.find_by(id: params[:stage_id])
end
if @competition.identifier == "gcc-annotation-2018"
@records = @competition.competition_teams.joins(:competition_scores)
.select("competition_teams.*, score, cost_time").order("score desc, cost_time desc")
else
@records = @competition.competition_teams.joins(:competition_scores).where(competition_scores: {competition_stage_id: @stage&.id.to_i})
.select("competition_teams.*, score, cost_time").order("score desc, cost_time desc")
end
@records = @competition.competition_teams.joins(:competition_scores).where(competition_scores: {competition_stage_id: @stage&.id.to_i})
.select("competition_teams.*, score, cost_time").order("score desc, cost_time desc")
current_team_ids = @competition.team_members.where(user_id: current_user.id).pluck(:competition_team_id).uniq
@user_ranks = @records.select{|com_team| current_team_ids.include?(com_team.id)}

@ -7,22 +7,20 @@ class DiscussesController < ApplicationController
def index
page = params[:page].to_i
offset = page * LIMIT
@manger = @container.has_manager?(current_user) || current_user.is_certification_teacher
# 总数,分页使用
if current_user.admin?
if @manger
@disscuss_count = Discuss.where(:dis_id => @container.id, :dis_type => @container.class.to_s, :root_id => nil).count
disscusses = Discuss.where(:dis_id => @container.id, :dis_type => @container.class.to_s,
:root_id => nil)
@discusses = disscusses.limit(LIMIT).joins("left join games on discusses.challenge_id = games.challenge_id and discusses.user_id = games.user_id")
.select("discusses.*, games.identifier").includes(:user, :praise_treads).offset(offset)
else
disscusses = Discuss.where("dis_id = :dis_id and dis_type = :dis_type and root_id is null and
(discusses.hidden = :hidden or discusses.user_id = :user_id)",
{dis_id: @container.id, dis_type: @container.class.to_s, hidden: false, user_id: current_user.id})
@disscuss_count = disscusses.count("discusses.id")
end
@manger = @container.has_manager?(current_user)
if @manger
@discusses = disscusses.limit(LIMIT).joins("left join games on discusses.challenge_id = games.challenge_id and discusses.user_id = games.user_id")
.select("discusses.*, games.identifier").includes(:user, :praise_treads).offset(offset)
else
@discusses = disscusses.limit(LIMIT).includes(:user, :praise_treads).offset(offset)
end

@ -439,6 +439,7 @@ class HomeworkCommonsController < ApplicationController
def settings
@user = current_user
@work = @homework.user_work(current_user.id) if @user_course_identity == Course::STUDENT
@course_groups = @course.course_groups.where(id: @course.charge_group_ids(@user))
end
def update_settings

@ -264,11 +264,13 @@ class MyshixunsController < ApplicationController
unless @hide_code || (@myshixun.shixun&.vnc_evaluate && params[:evaluate].present?)
# 远程版本库文件内容
last_content = GitService.file_content(repo_path: @repo_path, path: path)["content"]
content = if @myshixun.mirror_name.select {|a| a.include?("MachineLearning") || a.include?("Python")}.present? && params[:content].present?
params[:content].gsub(/\t/, ' ').gsub(/ /, ' ') # 这个不是空格在windows机器上带来的问题
else
params[:content]
end
content =
if python_file?(path)
params[:content].gsub(/\t/, ' ').gsub(/ /, ' ')
else
params[:content]
end
uid_logger_dubug("###11222333####{content}")
uid_logger_dubug("###222333####{last_content}")
@ -374,4 +376,9 @@ class MyshixunsController < ApplicationController
@repo_path = @myshixun.try(:repo_path)
@path = params[:path]
end
def python_file?(path)
false if path.blank?
path.to_s.split(".").last.downcase == "py"
end
end

@ -11,6 +11,10 @@ class SchoolsController < ApplicationController
end
def for_option
render_ok(schools: School.select(:id, :name).as_json)
schools = School.all
keyword = params[:keyword].to_s.strip
schools = schools.where('name LIKE ?', "%#{keyword}%") if keyword
render_ok(schools: schools.select(:id, :name).as_json)
end
end

@ -0,0 +1,38 @@
class Weapps::CheckAccountsController < Weapps::BaseController
def create
params[:type] == 'register' ? check_can_register : check_can_bind
end
private
def check_can_bind
if params[:login] =~ /^[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/
user = User.find_by(mail: params[:login])
return render_error('该邮箱尚未注册') if user.blank?
elsif params[:login] =~ /^1\d{10}$/
user = User.find_by(phone: params[:login])
return render_error('该手机号尚未注册') if user.blank?
else
user = User.find_by(login: params[:login])
return render_error('该账号尚未注册') if user.blank?
end
return render_error('该账号已经绑定') if user.wechat_open_user.present?
render_ok
end
def check_can_register
if params[:login] =~ /^[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/
user = User.find_by(mail: params[:login])
return render_error('该邮箱已注册') if user.present?
elsif params[:login] =~ /^1\d{10}$/
user = User.find_by(phone: params[:login])
return render_error('该手机号已注册') if user.present?
else
return render_error('请输入正确的邮箱或手机号')
end
render_ok
end
end

@ -1,8 +1,10 @@
class Weapps::CodeSessionsController < Weapps::BaseController
def create
logged = false
return render_error('code不能为空') if params[:code].blank?
reset_session
logged = false
result = Wechat::Weapp.jscode2session(params[:code])
# 能根据 code 拿到 unionid
@ -28,7 +30,7 @@ class Weapps::CodeSessionsController < Weapps::BaseController
set_session_openid(result['openid'])
set_weapp_session_key(result['session_key']) # weapp session_key写入缓存 后续解密需要
render_ok(openid: result['openid'], logged: logged)
render_ok(openid: result['openid'], logged: logged) unless logged
rescue Wechat::Error => ex
render_error(ex.message)
end

@ -19,6 +19,5 @@ class Weapps::SessionsController < Weapps::BaseController
OpenUsers::Wechat.create!(user: user, uid: session_unionid) if user.wechat_open_user.blank?
successful_authentication(user)
render_ok
end
end

@ -73,10 +73,10 @@ class Competitions::SaveTeamForm
# 竞赛是否限制了职业
def check_creator_identity_enrollable
if user.is_teacher? && competition.teacher_enroll_forbidden?
if creator.is_teacher? && competition.teacher_enroll_forbidden?
errors.add(:creator, :teacher_enroll_forbidden)
return false
elsif !user.is_teacher? && competition.member_enroll_forbidden?
elsif !creator.is_teacher? && competition.member_enroll_forbidden?
errors.add(:creator, :member_enroll_forbidden)
return false
end
@ -86,9 +86,9 @@ class Competitions::SaveTeamForm
# 创建者是否能多次报名
def check_creator_multiple_enrollable
return unless competition.enrolled?(user)
return unless competition.enrolled?(creator)
if (user.is_teacher? && competition.teacher_multiple_limited?) || (!user.is_teacher? && competition.member_multiple_limited?)
if (creator.is_teacher? && competition.teacher_multiple_limited?) || (!creator.is_teacher? && competition.member_multiple_limited?)
errors.add(:creator, :enrolled)
return false
end

@ -15,6 +15,17 @@ module ApplicationHelper
EduSetting.get(name)
end
# xss共计问题
def content_safe content
tags = %w(
a abbr b bdo blockquote br caption cite code col colgroup dd del dfn dl
dt em figcaption figure h1 h2 h3 h4 h5 h6 hgroup i img ins kbd li mark
ol p pre q rp rt ruby s samp small strike strong sub sup table tbody td
tfoot th thead time tr u ul var wbr div span
)
sanitize content, tags: tags
end
def graduation_navigation graduation
graduation.class.to_s == "GraduationTopic" ? "毕设选题" : "毕设任务"
end

@ -25,7 +25,7 @@ class CourseAddStudentCreateWorksJob < ApplicationJob
student_ids.each do |user_id|
same_attrs = {user_id: user_id}
course.homework_commons.where(homework_type: %i[normal group practice]).each do |homework|
next if homework.student_works.where(user_id: user_id).any?
next if StudentWork.where(user_id: user_id, homework_common_id: homework.id).any?
worker.add same_attrs.merge(homework_common_id: homework.id)
end
end
@ -36,7 +36,7 @@ class CourseAddStudentCreateWorksJob < ApplicationJob
student_ids.each do |user_id|
same_attrs = {user_id: user_id}
course.exercises.each do |exercise|
next if exercise.exercise_users.where(user_id: user_id).any?
next if ExerciseUser.where(user_id: user_id, exercise_id: exercise.id).any?
worker.add same_attrs.merge(exercise_id: exercise.id)
end
end
@ -47,7 +47,7 @@ class CourseAddStudentCreateWorksJob < ApplicationJob
student_ids.each do |user_id|
same_attrs = {user_id: user_id}
course.polls.each do |poll|
next if poll.poll_users.where(user_id: user_id).any?
next if PollUser.where(user_id: user_id, poll_id: poll.id).any?
worker.add same_attrs.merge(poll_id: poll.id)
end
end
@ -58,7 +58,7 @@ class CourseAddStudentCreateWorksJob < ApplicationJob
student_ids.each do |user_id|
same_attrs = {user_id: user_id, course_id: course.id}
course.graduation_tasks.each do |task|
next if task.graduation_works.where(user_id: user_id).any?
next if GraduationWork.where(user_id: user_id, graduation_task_id: task.id).any?
worker.add same_attrs.merge(graduation_task_id: task.id)
end
end

@ -0,0 +1,2 @@
module LimitForbidControl
end

@ -0,0 +1,56 @@
class LimitForbidControl::Base
def initialize
end
def cache_key
raise 'Please overwrite method :cache_Key'
end
def forbid_cache_key
"#{cache_key}:forbid"
end
def allow_times
5
end
def cumulative_expires
1.days
end
def forbid_expires
1.hours
end
def forbid?
Rails.cache.read(forbid_cache_key)
end
def increment!
value = Rails.cache.read(cache_key)
value = value.to_i + 1
# 锁定
if value >= allow_times.to_i
Rails.cache.write(forbid_cache_key, true, expires_in: forbid_expires)
Rails.cache.delete(cache_key)
else
Rails.cache.write(cache_key, value, expires_in: cumulative_expires)
end
end
def clear
Rails.cache.delete(forbid_cache_key)
Rails.cache.delete(cache_key)
end
private
def redis_cache?
Rails.cache.is_a?(ActiveSupport::Cache::RedisStore)
end
def day
Time.current.strftime('%Y%m%d')
end
end

@ -0,0 +1,25 @@
class LimitForbidControl::SendEmailCode < LimitForbidControl::Base
attr_reader :email
def initialize(email)
super()
@email = email
end
def allow_times
EduSetting.get('daily_send_email_code_times').presence || 5
end
def forbid_expires
num = EduSetting.get('daily_send_email_code_forbid_time').presence.to_i
num.zero? ? 10.minutes : num.to_i.hours
end
def cumulative_expires
1.hours
end
def cache_key
@_cache_key ||= "limit_forbid_control:#{day}:send_email_code:#{email}"
end
end

@ -0,0 +1,25 @@
class LimitForbidControl::UserLogin < LimitForbidControl::Base
attr_reader :user
def initialize(user)
super()
@user = user
end
def allow_times
EduSetting.get('daily_error_password_times').presence || 5
end
def forbid_expires
num = EduSetting.get('daily_error_password_forbid_time').presence.to_i
num.zero? ? 1.hours : num.to_i.hours
end
def cumulative_expires
1.days
end
def cache_key
@_cache_key ||= "limit_forbid_control:#{day}:user_login:#{user.id}"
end
end

@ -65,4 +65,19 @@ module Util
else "#{str[0..2]}***#{str[-3..-1]}"
end
end
def display_cost_time(time)
time = time.to_i
return if time.zero? || time < 60
day = time / (24 * 60 * 60)
hour = (time % (24 * 60 * 60)) / (60 * 60)
minute = (time % (60 * 60)) / 60
str = ''
str += "#{day}" unless day.zero?
str += "#{hour}小时" unless hour.zero?
str += "#{minute}" unless minute.zero?
str
end
end

@ -28,9 +28,9 @@ class Competition < ApplicationRecord
def mode_type
case mode
when 1
"课堂"
when 2
"实训"
when 2
"课堂"
when 3
"教学"
when 4
@ -40,6 +40,17 @@ class Competition < ApplicationRecord
end
end
def competition_status
if !status
com_status = "nearly_published"
elsif end_time > Time.now
com_status = "progressing"
else
com_status = "ended"
end
com_status
end
def teacher_staff_num
teacher_staff ? "#{teacher_staff.minimum}~#{teacher_staff.maximum}" : "--"
end
@ -94,16 +105,31 @@ class Competition < ApplicationRecord
GROUP BY competition_stage_id order by competition_stage_id")
end
def awards_count
competition_awards.pluck(:num)&.sum > 0 ? competition_awards.pluck(:num)&.sum : 20
def all_module_types
%w[home enroll inform chart resource]
end
# def awards_count
# competition_awards.pluck(:num)&.sum > 0 ? competition_awards.pluck(:num)&.sum : 20
# end
private
def get_module_name type
case type
when 'home' then '首页'
when 'enroll' then '报名'
when 'inform' then '通知公告'
when 'chart' then '排行榜'
when 'resource' then '资料下载'
else ''
end
end
def create_competition_modules
CompetitionModule.bulk_insert(*%i[competition_id name position created_at updated_at]) do |worker|
%w(首页 报名 通知公告 排行榜 资料下载).each_with_index do |name, index|
worker.add(competition_id: id, name: name, position: index + 1)
CompetitionModule.bulk_insert(*%i[competition_id module_type name position created_at updated_at]) do |worker|
all_module_types.each_with_index do |type, index|
worker.add(competition_id: id, module_type: type, name: get_module_name(type), position: index + 1, )
end
end
end

@ -6,16 +6,16 @@ class CompetitionModule < ApplicationRecord
has_one :competition_module_md_content, dependent: :destroy
def module_url
case name
when "首页", "赛制介绍"
case module_type
when "home"
"/competitions/#{competition.identifier}"
when "通知公告"
when "inform"
"/competitions/#{competition.identifier}/informs?status=1"
when "参赛手册"
when "manual"
"/competitions/#{competition.identifier}/informs?status=2"
when "排行榜"
when "chart"
"/competitions/#{competition.identifier}/charts"
when "报名"
when "enroll"
"/competitions/#{competition.identifier}/competition_teams"
else
url || "/competitions/#{competition.identifier}/md_content?md_content_id=#{competition_module_md_content&.id}"

@ -28,7 +28,7 @@ class CompetitionTeam < ApplicationRecord
while self.class.exists?(invite_code: code)
code = CODE_CHARS.sample(6).join
end
self.code = code
self.invite_code = code
code
end

@ -83,6 +83,11 @@ class Myshixun < ApplicationRecord
self.games.select{|game| game.status == 2}.size
end
# 查看答案的关卡数
def view_answer_count
self.games.select{|game| game.status == 2 && game.answer_open != 0}.size
end
# 通关时间
def passed_time
self.status == 1 ? self.games.select{|game| game.status == 2}.map(&:end_time).max : "--"

@ -36,8 +36,8 @@ class Admins::UserStatisticQuery < ApplicationQuery
study_myshixun = Myshixun.where(user_id: ids)
finish_myshixun = Myshixun.where(user_id: ids, status: 1)
study_challenge = Game.joins(:myshixun).where(myshixuns: { user_id: ids }).where(status: [0, 1, 2])
finish_challenge = Game.joins(:myshixun).where(myshixuns: { user_id: ids }).where(status: 2)
study_challenge = Game.where(user_id: ids).where(status: [0, 1, 2])
finish_challenge = Game.where(user_id: ids).where(status: 2)
if time_range.present?
study_myshixun = study_myshixun.where(updated_at: time_range)
@ -50,6 +50,8 @@ class Admins::UserStatisticQuery < ApplicationQuery
finish_myshixun_map = finish_myshixun.group(:user_id).count
study_challenge_map = study_challenge.group(:user_id).count
finish_challenge_map = finish_challenge.group(:user_id).count
evaluate_count_map = study_challenge.group(:user_id).sum(:evaluate_count)
cost_time_map = study_challenge.group(:user_id).sum(:cost_time)
users.each do |user|
user._extra_data = {
@ -57,6 +59,8 @@ class Admins::UserStatisticQuery < ApplicationQuery
finish_shixun_count: finish_myshixun_map.fetch(user.id, 0),
study_challenge_count: study_challenge_map.fetch(user.id, 0),
finish_challenge_count: finish_challenge_map.fetch(user.id, 0),
evaluate_count: evaluate_count_map.fetch(user.id, 0),
cost_time: cost_time_map.fetch(user.id, 0),
}
end
@ -102,12 +106,12 @@ class Admins::UserStatisticQuery < ApplicationQuery
when 'finish_challenge_count' then
users =
if time_range.present?
users.joins('LEFT JOIN myshixuns ON myshixuns.user_id = users.id')
.joins("LEFT JOIN games ON games.myshixun_id = myshixuns.id "\
users#.joins('LEFT JOIN myshixuns ON myshixuns.user_id = users.id')
.joins("LEFT JOIN games ON games.user_id = users.id "\
"AND games.status = 2 AND games.updated_at BETWEEN '#{time_range.min}' AND '#{time_range.max}'")
else
users.joins('LEFT JOIN myshixuns ON myshixuns.user_id = users.id')
.joins("LEFT JOIN games ON games.myshixun_id = myshixuns.id AND games.status = 2")
users#.joins('LEFT JOIN myshixuns ON myshixuns.user_id = users.id')
.joins("LEFT JOIN games ON games.user_id = users.id AND games.status = 2")
end
users.select("#{base_query_column}, COUNT(*) finish_challenge_count")

@ -0,0 +1,39 @@
class Admins::CompetitionBasicSettingService < ApplicationService
attr_reader :competition, :params
def initialize(competition, params)
@params = params
@competition = competition
end
def call
ActiveRecord::Base.transaction do
competition.name = strip params[:name]
competition.sub_title = strip params[:sub_title]
competition.start_time = params[:start_time]
competition.end_time = params[:end_time]
competition.mode = params[:mode]
competition.identifier = strip params[:identifier]
competition.bonus = params[:bonus]
competition.awards_count = params[:awards_count]
competition.description = strip params[:description]
competition.save!
if competition.mode == 1 || competition.mode == 4
competition.competition_mode_setting.destroy
else
setting = competition.competition_mode_setting || CompetitionModeSetting.create!(competition_id: competition.id)
if competition.mode == 2
setting.course_id = params[:course_id]
elsif competition.mode == 3
setting.start_time = params[:teach_start_time]
setting.end_time = params[:teach_end_time]
end
setting.save!
end
competition
end
end
end

@ -0,0 +1,36 @@
class Admins::CompetitionNavSettingService < ApplicationService
attr_reader :competition, :params
def initialize(competition, params)
@params = params
@competition = competition
end
def call
ActiveRecord::Base.transaction do
competition.competition_modules.where(module_type: 'md').destroy_all
# hidden_module_type = competition.all_module_types - params[:module_type]
# competition.competition_modules.where(module_type: hidden_module_type).update_all(hidden: 1)
params[:nav_module].each do |nav|
module_type = nav["module_type"]
if competition.all_module_types.include?(module_type)
com_module = competition.competition_modules.find_by(module_type: module_type)
else
com_module = CompetitionModule.create!(competition_id: competition.id, module_type: 'md')
end
com_module.update_attributes!(hidden: nav["hidden"] ? 0 : 1, position: nav["position"], name: nav["name"], url: nav["url"])
end
if params[:competition_staffs].present?
competition.competition_staffs.delete_all
params[:competition_staffs].each_with_index do |staff_params, index|
competition.competition_staffs.create!(staff_params.merge(position: index + 1))
end
end
competition
end
end
end

@ -36,7 +36,7 @@ class Admins::ImportCourseMemberService < ApplicationService
member = course.course_members.find_by(user_id: user.id, role: data.role.to_i)
# 如果已是课堂成员且是学生身份and不在指定的分班则移动到该分班
if member.present? && member.role == :STUDENT && course_group && member.course_group_id != course_group&.id
if member.present? && member.role == 'STUDENT' && course_group && member.course_group_id != course_group&.id
member.update!(course_group_id: course_group&.id)
elsif member.blank?
course.course_members.create!(user_id: user.id, role: data.role.to_i, course_group_id: course_group&.id)

@ -20,8 +20,8 @@ class Competitions::CreatePersonalTeamService < ApplicationService
raise Error, '您已报名该竞赛' if enrolled && multiple_limited
ActiveRecord::Base.transaction do
team = competition.competition_teams.create!(name: user.show_name, user_id: user.id)
team.team_members.create!(competition_id: competition, user_id: user.id, role: 1, is_teacher: is_teacher)
team = competition.competition_teams.create!(name: user.real_name, user_id: user.id)
team.team_members.create!(competition_id: competition.id, user_id: user.id, role: 1, is_teacher: is_teacher)
end
end
end

@ -32,7 +32,7 @@ class Competitions::SaveTeamService < ApplicationService
private
def update_teacher_team_members!
teacher_ids = Array.wrap(params[:teacher_ids]).map(:to_i)
teacher_ids = Array.wrap(params[:teacher_ids]).map(&:to_i)
old_teacher_ids = team.team_members.where(role: 3).pluck(:user_id)
destroy_teacher_ids = old_teacher_ids - teacher_ids
@ -49,7 +49,7 @@ class Competitions::SaveTeamService < ApplicationService
end
def update_member_team_members!
member_ids = Array.wrap(params[:member_ids]).map(:to_i)
member_ids = Array.wrap(params[:member_ids]).map(&:to_i)
old_member_ids = team.team_members.where(role: 2).pluck(:user_id)
destroy_member_ids = old_member_ids - member_ids

@ -14,6 +14,7 @@ class CreateAddDepartmentApplyService < ApplicationService
school = School.find_by(id: params[:school_id])
raise Error, '学校/单位不存在' if school.blank?
raise Error, '部门已存在' if school.departments.exists?(name: name)
department = Department.new
department.name = name

@ -16,7 +16,7 @@ class Oauth::CreateOrFindQqAccountService < ApplicationService
if user.blank? || !user.logged?
new_user = true
# 新用户
login = User.generate_login('q')
login = User.generate_login('Q')
@user = User.new(login: login, nickname: params.dig('info', 'nickname'), type: 'User', status: User::STATUS_ACTIVE)
end
@ -26,6 +26,9 @@ class Oauth::CreateOrFindQqAccountService < ApplicationService
gender = params.dig('extra', 'raw_info', 'gender') == '女' ? 1 : 0
user.create_user_extension!(gender: gender)
# 下载头像
avatar_path = Util::FileManage.source_disk_filename(user)
Util.download_file(params.dig('info', 'figureurl_qq_1'), avatar_path)
end
new_open_user = OpenUsers::QQ.create!(user: user, uid: params['uid'], extra: params.dig('extra', 'raw_info'))

@ -0,0 +1,284 @@
<%
define_admin_breadcrumbs do
add_admin_breadcrumb('竞赛列表', admins_competitions_path)
add_admin_breadcrumb(@competition.name)
end
%>
<div class="card mb-5">
<div class="card-header d-flex justify-content-between align-items-center">
<span class="flex-1">基础设置</span>
</div>
<div class="card-body row">
<%= form_tag(basic_setting_admins_competition_competition_settings_path(@competition), method: :post, class: 'basic-setting-form flex-1', remote: true) do %>
<div class="container competition-mode-container">
<div class="row align-items-center mb-1">
<div class="col-1 text-right">
主标题
</div>
<div class="col-5 text-left">
<%= text_field_tag(:name, @competition.name, autocomplete: 'off', class: 'form-control', placeholder: '竞赛标题') %>
</div>
</div>
<div class="row align-items-center mb-1">
<div class="col-1 text-right">
副标题
</div>
<div class="col-5 text-left">
<%= text_field_tag(:sub_title, @competition.sub_title, autocomplete: 'off', class: 'form-control', placeholder: '竞赛副标题') %>
</div>
</div>
<div class="row align-items-center mb-1">
<div class="col-1 text-right">
起止时间
</div>
<div class="col-5 competition-start-end-date d-flex">
<%= text_field_tag :start_time, @competition.start_time&.strftime('%Y-%m-%d'), autocomplete: 'off', class: 'form-control start-date mx-0 mr-2', placeholder: '竞赛开始时间' %>
<%= text_field_tag :end_time, @competition.end_time&.strftime('%Y-%m-%d'), autocomplete: 'off', class: 'form-control end-date mx-0', placeholder: '竞赛截止时间' %>
</div>
</div>
<div class="row align-items-center mb-1">
<div class="col-1 text-right">
竞赛模式
</div>
<div class="col-5 text-left">
<%= radio_button_tag(:mode, 1, @competition.mode == 1, class: 'form-radio-input') %>
<label class="form-radio-label mb-0" for="mode_1">实训模式(参赛者报名参赛,挑战实训,系统评审)</label>
</div>
</div>
<div class="row align-items-center mb-1">
<div class="col-1 text-right">
</div>
<div class="col-5 text-left">
<%= radio_button_tag(:mode, 2, @competition.mode == 2, class: 'form-radio-input') %>
<label class="form-radio-label mb-0" for="mode_2">课堂模式(参赛者加入课堂,提交作品,评委评审)</label>
</div>
<div class="col-6">
<%= text_field_tag(:course_id, @competition.competition_mode_setting&.course_id, autocomplete: 'off', class: 'form-control', placeholder: '课堂id') %>
</div>
</div>
<div class="row align-items-center mb-1">
<div class="col-1 text-right">
</div>
<div class="col-5 text-left">
<%= radio_button_tag(:mode, 3, @competition.mode == 3, class: 'form-radio-input') %>
<label class="form-radio-label mb-0" for="mode_3">教学模式(参赛者报名参赛,统计战队课堂和实训数据)</label>
</div>
<div class="col-6 teaching-mode-date d-flex">
<%= text_field_tag :teach_start_time, @competition.competition_mode_setting&.start_time, autocomplete: 'off', class: 'form-control start-date mx-0 mr-2', placeholder: '统计数据的开始时间' %>
<%= text_field_tag :teach_end_time, @competition.competition_mode_setting&.end_time, autocomplete: 'off', class: 'form-control end-date mx-0', placeholder: '统计数据的结束时间' %>
</div>
</div>
<div class="row align-items-center mb-2">
<div class="col-1 text-right">
</div>
<div class="col-5 text-left">
<%= radio_button_tag(:mode, 4, @competition.mode == 4, class: 'form-radio-input') %>
<label class="form-radio-label mb-0" for="mode_4">托管模式(参赛者报名参赛,在其他平台完成任务)</label>
</div>
</div>
<div class="row align-items-center mb-2">
<div class="col-1 text-right">
URL
</div>
<div class="col-5 text-left mode-input">
<%= text_field_tag(:identifier, @competition.identifier, autocomplete: 'off', class: 'form-control', placeholder: '请输入url赛事网址') %>
</div>
</div>
<div class="row align-items-center mb-2">
<div class="col-1 text-right">
主办方
</div>
<div class="col-5 text-left">
</div>
</div>
<div class="row align-items-center mb-2">
<div class="col-1 text-right">
奖金
</div>
<div class="col-5 text-left input-group">
<div class="input-group-prepend">
<div class="input-group-text">¥</div>
</div>
<%= number_field_tag(:bonus, @competition.bonus, autocomplete: 'off', step: 1, min: 0, class: 'form-control', placeholder: '请输入总奖金额') %>
</div>
</div>
<div class="row align-items-center mb-2">
<div class="col-1 text-right">
奖项数
</div>
<div class="col-5 text-left">
<%= number_field_tag(:awards_count, @competition.awards_count, autocomplete: 'off', step: 1, min: 0, class: 'form-control', placeholder: '请输入奖项数') %>
</div>
</div>
<div class="row des-row align-items-center mb-2">
<div class="col-1 text-right">
描述
</div>
<div class="col-11 text-left">
<%= text_area_tag(:description, @competition.description, class: 'form-control', placeholder: '请输入赛事简介') %>
</div>
</div>
<div class="error my-2 danger text-danger"></div>
<div class="row des-row align-items-center mb-2">
<div class="col-1 text-right">
</div>
<div class="col-5 text-left">
<%= javascript_void_link '保存', class: 'btn btn-primary submit-btn' %>
</div>
</div>
</div>
<% end %>
</div>
</div>
<div class="card mb-5">
<div class="card-header d-flex justify-content-between align-items-center">
<span class="flex-1">导航设置</span>
</div>
<div class="card-body row">
<div class="container competition-mode-container">
<% @competition.competition_modules.each do |com_module| %>
<% case com_module.module_type %>
<% when 'home' %>
<div id="MD_typeFrom">
<div class="row MD_type">
<div class="col-1 text-right">
<label class="checkbox checkbox-primary mt-1">
<%= check_box_tag('navbar[][hidden]', 0, !com_module.hidden, id: nil, class: 'font-16') %>
</label>
</div>
<div class="col-md-4">
<%= text_field_tag('navbar[][name]', com_module.name, id: nil, class: 'form-control', placeholder: '首页') %>
<input type="hidden" value="<%= com_module.module_type %>" name="navbar[][module_type]">
</div>
<div class="col-md-1">
<%= text_field_tag('navbar[][position]', com_module.position, id: nil, class: 'form-control', placeholder: '位置') %>
</div>
</div>
</div>
<% end %>
<% end %>
<div class="row mt-2">
<div class="col-1 text-right">
<label class="checkbox checkbox-primary mt-1">
<input id="checkbox2" type="checkbox">
<label for="checkbox2">&nbsp;</label>
</label>
</div>
<div class="col-md-8 color-blue mt-1">
报名
</div>
</div>
<div class="row mt-2">
<div class="col-1 text-right">&nbsp;&nbsp;</div>
<div class="col-1 text-left" style="max-width: 120px;flex: 0 0 120px;">
报名截止时间
</div>
<div class="col-md-3"><input type="text" class="form-control" /></div>
</div>
<div class="row mt-2">
<div class="col-1 text-right">&nbsp;&nbsp;</div>
<div class="col-1 text-left mt-1">
报名要求
</div>
<div class="col-md-3">
<button class="btn btn-primary waves-effect waves-light btn-xs setBtn_s" id="addRequireBtn">+</button>
</div>
</div>
<div id="requireForm">
<div class="row mt-2 mb-4 requireForm_item">
<div class="col-1 text-right">&nbsp;&nbsp;</div>
<div class="col-1 text-left mt-1">
<input type="text" class="form-control" name="min_1" />
</div>
<span class="mt-2">~</span>
<div class="col-1 mt-1">
<input type="text" class="form-control" name="max_1" />
</div>
<span class="mt-2">人</span>
<div class="col-2 mt-1">
<select class="form-control" name="choice_1" >
<option>不限</option>
<option>教师</option>
<option>学生</option>
<option>专业人士</option>
</select>
</div>
<div class="col-2 mt-1">
<label class="radio checkbox-primary mt-1" value="require_1_1">
<input id="require_1_1" name="require_1" type="radio">
<label for="require_1_1">可多次报名</label>
</label>
</div>
<div class="col-2 mt-1">
<label class="radio checkbox-primary mt-1" value="require_1_2">
<input id="require_1_2" name="require_1" type="radio">
<label for="require_1_2">不可多次报名</label>
</label>
</div>
</div>
</div>
<div class="row mt-2">
<div class="col-1 text-right">
<label class="checkbox checkbox-primary mt-1">
<input id="checkbox2" type="checkbox">
<label for="checkbox2">&nbsp;</label>
</label>
</div>
<div class="col-md-label mt-2">排行榜</div>
<div class="col-md-1 mt-1"><input type="text" class="form-control"></div>
</div>
<div id="linkForm">
<div class="row mt-2 linkFormItem">
<div class="col-1 text-right">
<label class="checkbox checkbox-primary mt-1">
<input id="link_1" type="checkbox" name="link_1" />
<label for="link_1">&nbsp;</label>
</label>
</div>
<div class="col-md-label mt-2">资料下载</div>
<div class="col-md-1 mt-1"><input type="text" name="link_index_1" class="form-control"></div>
<div class="col-md-3 mt-1"><input type="text" name="link_info_1" class="form-control"></div>
<button class="mt-1 btn btn-primary waves-effect waves-light btn-xs setBtn_s add_linkBtn">+</button>
</div>
</div>
<div class="row mt-2">
<div class="col-1 text-right">
<label class="checkbox checkbox-primary mt-1">
<input id="checkbox2" type="checkbox">
<label for="checkbox2">&nbsp;</label>
</label>
</div>
<div class="col-md-label mt-2">获奖证书</div>
</div>
<div class="row mt-2 mb-4">
<div class="col-1 text-right">
</div>
<div class="col-md-label mt-2"><%= javascript_void_link '保存', class: 'btn btn-primary submit-btn' %></div>
</div>
</div>
</div>
</div>

@ -1,16 +0,0 @@
<%
define_admin_breadcrumbs do
add_admin_breadcrumb('竞赛列表', admins_competitions_path)
add_admin_breadcrumb(@competition.name)
end
%>
<div class="card mb-5">
<div class="card-header d-flex justify-content-between align-items-center">
<span class="flex-1">基础设置</span>
</div>
<div class="card-body row">
</div>
</div>

@ -0,0 +1,29 @@
wb = xlsx_package.workbook
wb.add_worksheet(name: '报名列表') do |sheet|
sheet.add_row %w(序号 战队ID 战队名称 创建者 指导老师 队员姓名 职业 手机号 邮箱 学号 实名认证 职业认证 队员学校 地区 报名时间 排名)
@enroll_lists.each_with_index do |member, index|
team = member.competition_team
member_user = member.user
rank = @competition_scores.length > 0 ? @competition_scores.index(member.competition_team_id).to_i + 1 : "--"
data = [
index + 1,
member.competition_team_id,
@personal ? "--" : team.name,
team.user.real_name,
@personal ? "--" : team.teachers_info,
member_user.real_name,
member_user.identity,
member_user.phone,
member_user.mail,
member_user.student_id,
member_user.authentication ? "√" : "",
member_user.professional_certification ? "√" : "",
member_user.school_name,
member_user.school_province,
member.created_at.strftime('%Y-%m-%d %H:%M'),
rank
]
sheet.add_row(data)
end
end

@ -24,7 +24,7 @@
<%= link_to "清除", admins_competition_enroll_lists_path(@competition), class: "btn btn-default",'data-disable-with': '清除中...' %>
<% end %>
<a href="javascript:void(0)" class="btn btn-primary mt-3" id="enroll-lists-export" data-competition-id="<%= @competition.id %>" data-disable-with = '导出中...'>导出</a>
<a href="javascript:void(0)" class="btn btn-primary mt-3" id="enroll-lists-export" data-competition-id="<%= @competition.id %>">导出</a>
</div>
</div>

@ -17,14 +17,10 @@
<% myshixuns.each do |myshixun| %>
<tr class="myshixun-item-<%= myshixun.id %>">
<td><%= myshixun.id %></td>
<td>
<td><%= myshixun.identifier %></td>
<td class="text-left">
<% current_task = myshixun.last_executable_task || myshixun.last_task %>
<%= link_to "/myshixuns/#{myshixun.identifier}/stages/#{current_task.identifier}", target: '_blank' do %>
<%= myshixun.identifier %>
<% end %>
</td>
<td class="text-left">
<%= link_to "/shixuns/#{myshixun.shixun.identifier}", target: '_blank' do %>
<%= overflow_hidden_span myshixun.shixun.name, width: 280 %>
<% end %>
</td>

@ -1,6 +1,6 @@
wb = xlsx_package.workbook
wb.add_worksheet(name: '用户实训情况') do |sheet|
sheet.add_row %w(姓名 单位部门 学习关卡数 完成关卡数 学习实训数 完成实训数)
sheet.add_row %w(姓名 单位部门 学习关卡数 完成关卡数 学习实训数 完成实训数 评测次数 实战时间)
@users.each do |user|
data = [
@ -9,7 +9,9 @@ wb.add_worksheet(name: '用户实训情况') do |sheet|
user.display_extra_data(:study_challenge_count),
user.display_extra_data(:finish_challenge_count),
user.display_extra_data(:study_shixun_count),
user.display_extra_data(:finish_shixun_count)
user.display_extra_data(:finish_shixun_count),
user.display_extra_data(:evaluate_count),
Util.display_cost_time(user.display_extra_data(:cost_time)),
]
sheet.add_row(data)
end

@ -2,11 +2,13 @@
<thead class="thead-light">
<tr>
<th width="14%" class="text-left">姓名</th>
<th width="38%" class="text-left">单位部门</th>
<th width="12%"><%= sort_tag('学习关卡数', name: 'study_challenge_count', path: admins_user_statistics_path) %></th>
<th width="12%"><%= sort_tag('完成关卡数', name: 'finish_challenge_count', path: admins_user_statistics_path) %></th>
<th width="12%"><%= sort_tag('学习实训数', name: 'study_shixun_count', path: admins_user_statistics_path) %></th>
<th width="12%"><%= sort_tag('完成实训数', name: 'finish_shixun_count', path: admins_user_statistics_path) %></th>
<th width="22%" class="text-left">单位部门</th>
<th width="10%">学习关卡数<%#= sort_tag('学习关卡数', name: 'study_challenge_count', path: admins_user_statistics_path) %></th>
<th width="10%">完成关卡数<%#= sort_tag('完成关卡数', name: 'finish_challenge_count', path: admins_user_statistics_path) %></th>
<th width="10%"><%= sort_tag('学习实训数', name: 'study_shixun_count', path: admins_user_statistics_path) %></th>
<th width="10%"><%= sort_tag('完成实训数', name: 'finish_shixun_count', path: admins_user_statistics_path) %></th>
<th width="10%">评测次数</th>
<th width="14%">实战时间</th>
</tr>
</thead>
<tbody>
@ -23,6 +25,8 @@
<td><%= user.display_extra_data(:finish_challenge_count) %></td>
<td><%= user.display_extra_data(:study_shixun_count) %></td>
<td><%= user.display_extra_data(:finish_shixun_count) %></td>
<td><%= user.display_extra_data(:evaluate_count) %></td>
<td><%= Util.display_cost_time(user.display_extra_data(:cost_time)) || '--' %></td>
</tr>
<% end %>
<% else %>

@ -21,7 +21,7 @@
</div>
</div>
<%= f.input :link, as: :url, label: '跳转地址', placeholder: '请输入跳转地址' %>
<%= f.input :link, label: '跳转地址', placeholder: '请输入跳转地址' %>
<div class="error text-danger"></div>
<% end %>

@ -21,7 +21,7 @@
</div>
</div>
<%= f.input :link, as: :url, label: '跳转地址', placeholder: '请输入跳转地址' %>
<%= f.input :link, label: '跳转地址', placeholder: '请输入跳转地址' %>
<div class="error text-danger"></div>
<% end %>

@ -1,12 +1,11 @@
json.count @count
json.count @all_count
json.personal @personal
json.competition_teams do
json.array! @teams.each do |team|
json.array! @all_teams&.each do |team|
json.extract! team, :id, :name, :invite_code
json.team_type team.en_team_type
json.school_name team.user.school_name
json.manage_permission current_user.id == team.user_id
json.created_at team.created_at.strftime("%Y-%m-%d %H:%M")
json.creator do
json.partial! 'users/user_simple', user: team.user
@ -25,3 +24,30 @@ json.competition_teams do
end
end
end
json.my_teams @teams.each do |team|
json.extract! team, :id, :name, :invite_code
json.team_type team.en_team_type
json.school_name team.user.school_name
json.created_at team.created_at.strftime("%Y-%m-%d %H:%M")
json.manage_permission current_user.id == team.user_id
json.creator do
json.partial! 'users/user_simple', user: team.user
json.role team.team_members.find(&:creator?).en_role
end
json.team_members do
json.array! team.team_members.each do |member|
json.partial! 'users/user_simple', user: member.user
json.user_id member.user_id
json.role member.en_role
json.identity member.user.identity
json.school_name member.user.school_name
json.student_id member.user.student_id
end
end
end

@ -9,15 +9,17 @@ json.enroll_end_time @competition.enroll_end_time&.strftime("%Y-%m-%d %H:%M:%S")
json.published @competition.published?
json.nearly_published @competition.published_at.present?
json.competition_status @competition.competition_status
json.competition_modules @competition_modules do |com_module|
json.(com_module, :name, :position)
json.(com_module, :id, :name, :position, :module_type)
json.module_url com_module.module_url
json.has_url com_module.url.present?
end
json.stages
if @competition.mode == 1
if @competition.mode == 2
json.course_id @competition.competition_mode_setting&.course_id
json.member_of_course @user.member_of_course?(@competition.competition_mode_setting&.course)
end

@ -3,6 +3,7 @@ json.competitions do
json.array! @competitions.each do |competition|
json.extract! competition, :id, :identifier, :name, :sub_title, :bonus, :description, :mode
json.competition_status competition.competition_status
json.visits_count competition.visits
member_count = @member_count_map&.fetch(competition.id, 0) || competition.team_members.count
json.member_count member_count.zero? ? 268 : member_count

@ -2,7 +2,7 @@ json.author do
json.partial! 'users/user', user: discuss.user
end
json.id discuss.id
json.content discuss.content
json.content content_safe(discuss.content)
json.time time_from_now(discuss.created_at)
json.position discuss.position
json.shixun_id discuss.dis_id

@ -7,7 +7,7 @@ json.partial! "student_btn_check", locals: {identity: @user_course_identity, hom
json.(@homework, :unified_setting, :publish_time, :end_time, :late_penalty, :allow_late, :late_time, :work_public,
:score_open, :answer_public)
json.group_settings @course.course_groups do |group|
json.group_settings @course_groups do |group|
json.group_id group.id
json.group_name group.name
json.publish_time group_homework_setting(@homework, group.id).try(:publish_time)

@ -41,6 +41,7 @@ elsif @user_course_identity == Course::STUDENT
json.efficiency work_score_format(@work.efficiency, true, @score_open)
json.eff_score work_score_format(@work.eff_score, true, @score_open)
json.complete_count @work.myshixun.try(:passed_count)
json.view_answer_count @work.myshixun.try(:view_answer_count)
else
json.(@work, :id, :work_status, :update_time, :ultimate_score)
@ -95,6 +96,7 @@ if @homework.homework_type == "practice"
json.cost_time work.myshixun.try(:total_spend_time)
json.complete_count work.myshixun.try(:passed_count)
json.view_answer_count work.myshixun.try(:view_answer_count)
json.user_login work.user.try(:login)
json.user_name work.user.try(:real_name)
json.student_id work.user.try(:student_id)

@ -3,7 +3,7 @@ json.memo do
json.forum_id memo.forum_id
json.subject memo.subject
json.is_md memo.is_md
json.content memo.content
json.content content_safe(memo.content)
json.sticky memo.sticky
json.reward memo.reward
json.viewed_count memo.viewed_count

@ -1,5 +1,5 @@
json.id memo.id
json.content memo.content
json.content content_safe(memo.content)
json.time time_from_now(memo.created_at)
json.user_id memo.author_id
json.image_url url_to_avatar(memo.author)
@ -15,7 +15,7 @@ json.admin @user.admin? || @user.business?
json.children do
json.array! memo.children_of_reply do |child|
json.id child.id
json.content child.content
json.content content_safe(child.content)
json.time time_from_now(child.created_at)
json.image_url url_to_avatar(child.author)
json.username child.author.full_name

@ -1 +1 @@
json.content content
json.content content_safe(content)

@ -1,6 +1,6 @@
json.partial! "messages/message_simple", message: message
json.partial! "commons/like", message: message
json.content message.message_detail.try(:content)
json.content content_safe(message.message_detail.try(:content))
json.author do
json.partial! "users/user_simple", user: message.author
end

@ -4,6 +4,7 @@ json.login @user.login
json.user_id @user.id
json.image_url url_to_avatar(@user)
json.admin @user.admin?
json.business @user.business?
json.is_teacher @user.user_extension&.teacher?
json.user_identity @user.identity
json.tidding_count 0

@ -0,0 +1,3 @@
json.user do
json.partial! 'weapps/shared/user', locals: { user: current_user }
end

@ -0,0 +1,3 @@
json.user do
json.partial! 'weapps/shared/user', locals: { user: current_user }
end

@ -0,0 +1,14 @@
json.username user.full_name
json.real_name user.real_name
json.login user.login
json.user_id user.id
json.image_url url_to_avatar(user)
json.admin user.admin?
json.business user.business?
json.is_teacher user.user_extension&.teacher?
json.user_identity user.identity
json.tidding_count 0
json.user_phone_binded user.phone.present?
json.phone user.phone
json.profile_completed user.profile_completed?
json.professional_certification user.professional_certification

@ -0,0 +1,418 @@
/*!
* Datetimepicker for Bootstrap
*
* Copyright 2012 Stefan Petre
* Improvements by Andrew Rowls
* Licensed under the Apache License v2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
*/
.datetimepicker {
padding: 4px;
margin-top: 1px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
direction: ltr;
}
.datetimepicker-inline {
width: 220px;
}
.datetimepicker.datetimepicker-rtl {
direction: rtl;
}
.datetimepicker.datetimepicker-rtl table tr td span {
float: right;
}
.datetimepicker-dropdown, .datetimepicker-dropdown-left {
top: 0;
left: 0;
}
[class*=" datetimepicker-dropdown"]:before {
content: '';
display: inline-block;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-bottom: 7px solid #cccccc;
border-bottom-color: rgba(0, 0, 0, 0.2);
position: absolute;
}
[class*=" datetimepicker-dropdown"]:after {
content: '';
display: inline-block;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid #ffffff;
position: absolute;
}
[class*=" datetimepicker-dropdown-top"]:before {
content: '';
display: inline-block;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-top: 7px solid #cccccc;
border-top-color: rgba(0, 0, 0, 0.2);
border-bottom: 0;
}
[class*=" datetimepicker-dropdown-top"]:after {
content: '';
display: inline-block;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 6px solid #ffffff;
border-bottom: 0;
}
.datetimepicker-dropdown-bottom-left:before {
top: -7px;
right: 6px;
}
.datetimepicker-dropdown-bottom-left:after {
top: -6px;
right: 7px;
}
.datetimepicker-dropdown-bottom-right:before {
top: -7px;
left: 6px;
}
.datetimepicker-dropdown-bottom-right:after {
top: -6px;
left: 7px;
}
.datetimepicker-dropdown-top-left:before {
bottom: -7px;
right: 6px;
}
.datetimepicker-dropdown-top-left:after {
bottom: -6px;
right: 7px;
}
.datetimepicker-dropdown-top-right:before {
bottom: -7px;
left: 6px;
}
.datetimepicker-dropdown-top-right:after {
bottom: -6px;
left: 7px;
}
.datetimepicker > div {
display: none;
}
.datetimepicker.minutes div.datetimepicker-minutes {
display: block;
}
.datetimepicker.hours div.datetimepicker-hours {
display: block;
}
.datetimepicker.days div.datetimepicker-days {
display: block;
}
.datetimepicker.months div.datetimepicker-months {
display: block;
}
.datetimepicker.years div.datetimepicker-years {
display: block;
}
.datetimepicker table {
margin: 0;
}
.datetimepicker td,
.datetimepicker th {
text-align: center;
width: 20px;
height: 20px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
border: none;
}
.table-striped .datetimepicker table tr td,
.table-striped .datetimepicker table tr th {
background-color: transparent;
}
.datetimepicker table tr td.minute:hover {
background: #eeeeee;
cursor: pointer;
}
.datetimepicker table tr td.hour:hover {
background: #eeeeee;
cursor: pointer;
}
.datetimepicker table tr td.day:hover {
background: #eeeeee;
cursor: pointer;
}
.datetimepicker table tr td.old,
.datetimepicker table tr td.new {
color: #999999;
}
.datetimepicker table tr td.disabled,
.datetimepicker table tr td.disabled:hover {
background: none;
color: #999999;
cursor: default;
}
.datetimepicker table tr td.today,
.datetimepicker table tr td.today:hover,
.datetimepicker table tr td.today.disabled,
.datetimepicker table tr td.today.disabled:hover {
background-color: #fde19a;
background-image: -moz-linear-gradient(top, #fdd49a, #fdf59a);
background-image: -ms-linear-gradient(top, #fdd49a, #fdf59a);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fdd49a), to(#fdf59a));
background-image: -webkit-linear-gradient(top, #fdd49a, #fdf59a);
background-image: -o-linear-gradient(top, #fdd49a, #fdf59a);
background-image: linear-gradient(to bottom, #fdd49a, #fdf59a);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a', endColorstr='#fdf59a', GradientType=0);
border-color: #fdf59a #fdf59a #fbed50;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
}
.datetimepicker table tr td.today:hover,
.datetimepicker table tr td.today:hover:hover,
.datetimepicker table tr td.today.disabled:hover,
.datetimepicker table tr td.today.disabled:hover:hover,
.datetimepicker table tr td.today:active,
.datetimepicker table tr td.today:hover:active,
.datetimepicker table tr td.today.disabled:active,
.datetimepicker table tr td.today.disabled:hover:active,
.datetimepicker table tr td.today.active,
.datetimepicker table tr td.today:hover.active,
.datetimepicker table tr td.today.disabled.active,
.datetimepicker table tr td.today.disabled:hover.active,
.datetimepicker table tr td.today.disabled,
.datetimepicker table tr td.today:hover.disabled,
.datetimepicker table tr td.today.disabled.disabled,
.datetimepicker table tr td.today.disabled:hover.disabled,
.datetimepicker table tr td.today[disabled],
.datetimepicker table tr td.today:hover[disabled],
.datetimepicker table tr td.today.disabled[disabled],
.datetimepicker table tr td.today.disabled:hover[disabled] {
background-color: #fdf59a;
}
.datetimepicker table tr td.today:active,
.datetimepicker table tr td.today:hover:active,
.datetimepicker table tr td.today.disabled:active,
.datetimepicker table tr td.today.disabled:hover:active,
.datetimepicker table tr td.today.active,
.datetimepicker table tr td.today:hover.active,
.datetimepicker table tr td.today.disabled.active,
.datetimepicker table tr td.today.disabled:hover.active {
background-color: #fbf069;
}
.datetimepicker table tr td.active,
.datetimepicker table tr td.active:hover,
.datetimepicker table tr td.active.disabled,
.datetimepicker table tr td.active.disabled:hover {
background-color: #006dcc;
background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
background-image: -o-linear-gradient(top, #0088cc, #0044cc);
background-image: linear-gradient(to bottom, #0088cc, #0044cc);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
border-color: #0044cc #0044cc #002a80;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
color: #ffffff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.datetimepicker table tr td.active:hover,
.datetimepicker table tr td.active:hover:hover,
.datetimepicker table tr td.active.disabled:hover,
.datetimepicker table tr td.active.disabled:hover:hover,
.datetimepicker table tr td.active:active,
.datetimepicker table tr td.active:hover:active,
.datetimepicker table tr td.active.disabled:active,
.datetimepicker table tr td.active.disabled:hover:active,
.datetimepicker table tr td.active.active,
.datetimepicker table tr td.active:hover.active,
.datetimepicker table tr td.active.disabled.active,
.datetimepicker table tr td.active.disabled:hover.active,
.datetimepicker table tr td.active.disabled,
.datetimepicker table tr td.active:hover.disabled,
.datetimepicker table tr td.active.disabled.disabled,
.datetimepicker table tr td.active.disabled:hover.disabled,
.datetimepicker table tr td.active[disabled],
.datetimepicker table tr td.active:hover[disabled],
.datetimepicker table tr td.active.disabled[disabled],
.datetimepicker table tr td.active.disabled:hover[disabled] {
background-color: #0044cc;
}
.datetimepicker table tr td.active:active,
.datetimepicker table tr td.active:hover:active,
.datetimepicker table tr td.active.disabled:active,
.datetimepicker table tr td.active.disabled:hover:active,
.datetimepicker table tr td.active.active,
.datetimepicker table tr td.active:hover.active,
.datetimepicker table tr td.active.disabled.active,
.datetimepicker table tr td.active.disabled:hover.active {
background-color: #003399;
}
.datetimepicker table tr td span {
display: block;
width: 23%;
height: 54px;
line-height: 54px;
float: left;
margin: 1%;
cursor: pointer;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.datetimepicker .datetimepicker-hours span {
height: 26px;
line-height: 26px;
}
.datetimepicker .datetimepicker-hours table tr td span.hour_am,
.datetimepicker .datetimepicker-hours table tr td span.hour_pm {
width: 14.6%;
}
.datetimepicker .datetimepicker-hours fieldset legend,
.datetimepicker .datetimepicker-minutes fieldset legend {
margin-bottom: inherit;
line-height: 30px;
}
.datetimepicker .datetimepicker-minutes span {
height: 26px;
line-height: 26px;
}
.datetimepicker table tr td span:hover {
background: #eeeeee;
}
.datetimepicker table tr td span.disabled,
.datetimepicker table tr td span.disabled:hover {
background: none;
color: #999999;
cursor: default;
}
.datetimepicker table tr td span.active,
.datetimepicker table tr td span.active:hover,
.datetimepicker table tr td span.active.disabled,
.datetimepicker table tr td span.active.disabled:hover {
background-color: #006dcc;
background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
background-image: -o-linear-gradient(top, #0088cc, #0044cc);
background-image: linear-gradient(to bottom, #0088cc, #0044cc);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
border-color: #0044cc #0044cc #002a80;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
color: #ffffff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.datetimepicker table tr td span.active:hover,
.datetimepicker table tr td span.active:hover:hover,
.datetimepicker table tr td span.active.disabled:hover,
.datetimepicker table tr td span.active.disabled:hover:hover,
.datetimepicker table tr td span.active:active,
.datetimepicker table tr td span.active:hover:active,
.datetimepicker table tr td span.active.disabled:active,
.datetimepicker table tr td span.active.disabled:hover:active,
.datetimepicker table tr td span.active.active,
.datetimepicker table tr td span.active:hover.active,
.datetimepicker table tr td span.active.disabled.active,
.datetimepicker table tr td span.active.disabled:hover.active,
.datetimepicker table tr td span.active.disabled,
.datetimepicker table tr td span.active:hover.disabled,
.datetimepicker table tr td span.active.disabled.disabled,
.datetimepicker table tr td span.active.disabled:hover.disabled,
.datetimepicker table tr td span.active[disabled],
.datetimepicker table tr td span.active:hover[disabled],
.datetimepicker table tr td span.active.disabled[disabled],
.datetimepicker table tr td span.active.disabled:hover[disabled] {
background-color: #0044cc;
}
.datetimepicker table tr td span.active:active,
.datetimepicker table tr td span.active:hover:active,
.datetimepicker table tr td span.active.disabled:active,
.datetimepicker table tr td span.active.disabled:hover:active,
.datetimepicker table tr td span.active.active,
.datetimepicker table tr td span.active:hover.active,
.datetimepicker table tr td span.active.disabled.active,
.datetimepicker table tr td span.active.disabled:hover.active {
background-color: #003399;
}
.datetimepicker table tr td span.old {
color: #999999;
}
.datetimepicker th.switch {
width: 145px;
}
.datetimepicker th span.glyphicon {
pointer-events: none;
}
.datetimepicker thead tr:first-child th,
.datetimepicker tfoot th {
cursor: pointer;
}
.datetimepicker thead tr:first-child th:hover,
.datetimepicker tfoot th:hover {
background: #eeeeee;
}
.input-append.date .add-on i,
.input-prepend.date .add-on i,
.input-group.date .input-group-addon span {
cursor: pointer;
width: 14px;
height: 14px;
}

@ -787,7 +787,7 @@ Rails.application.routes.draw do
resources :competitions, only: [:index, :show, :update] do
resources :competition_modules, only: [:index, :show, :update]
resource :competition_staff
resources :competition_teams, only: [:index, :show] do
resources :competition_teams, only: [:index, :show, :create, :update] do
post :join, on: :collection
post :leave, on: :member
get :course_detail, on: :member
@ -854,6 +854,7 @@ Rails.application.routes.draw do
resource :register, only: [:create]
resource :code_session, only: [:create]
resource :verify, only: [:create]
resource :check_account, only: [:create]
resources :searchs, only: [:index]
end
@ -1022,8 +1023,21 @@ Rails.application.routes.draw do
post :hot_setting
end
resources :competition_settings, only: [:index, :update]
resources :enroll_lists, only: [:index]
resources :competition_settings, only: [:index] do
post :basic_setting, on: :collection
end
resources :enroll_lists, only: [:index] do
get :export, on: :collection
end
resources :competition_stages, only: [:create, :update, :destroy] do
collection do
post :create_stage_section
post :update_stage_section
delete :destroy_stage_section
end
end
end
resources :weapp_carousels, only: [:index, :create, :update, :destroy] do

@ -1,6 +1,6 @@
class AddIsInvalidToStudentWorksScores < ActiveRecord::Migration[5.2]
def change
# add_column :student_works_scores, :is_invalid, :boolean, default: false
add_column :student_works_scores, :is_invalid, :boolean, default: false
StudentWorksScore.where("score is not null").order("id desc").find_each do |score|
unless score.is_invalid

@ -0,0 +1,7 @@
class MigrateCompetitionScoreStageId < ActiveRecord::Migration[5.2]
def change
change_column_default :competition_scores, :competition_stage_id, 0
CompetitionScore.where("competition_stage_id is null").update_all(competition_stage_id: 0)
end
end

@ -0,0 +1,6 @@
class MigrateCompetitionModeDefault < ActiveRecord::Migration[5.2]
def change
change_column_default :competitions, :mode, from: 0, to: 1
Competition.all.update_all(mode: 1)
end
end

@ -0,0 +1,5 @@
class AddAwardsCountToCompetition < ActiveRecord::Migration[5.2]
def change
add_column :competitions, :awards_count, :integer, default: 0
end
end

@ -0,0 +1,28 @@
class MigrateCompetitionModuleType < ActiveRecord::Migration[5.2]
def change
add_column :competition_modules, :module_type, :string
Competition.all.each do |competition|
competition.competition_modules.each do |com_module|
mod_type = ""
case com_module.name
when '首页'
mod_type = "home"
when '报名'
mod_type = "enroll"
when '通知公告'
mod_type = "inform"
when '参赛手册'
mod_type = "manual"
when '排行榜'
mod_type = "chart"
when '资料下载 '
mod_type = "resource"
else
mod_type = "md"
end
com_module.update_attributes!(module_type: mod_type)
end
end
end
end

@ -0,0 +1,10 @@
class AddColumnToStageSections < ActiveRecord::Migration[5.2]
def change
def change
add_column :competition_stage_sections, :mission_count, :integer, default: 0
add_column :competition_stage_sections, :score_source, :integer, default: 0
add_column :competition_entries, :shixun_identifier, :string
end
end
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

@ -9,20 +9,20 @@ delete require.cache[require.resolve('./paths')];
const NODE_ENV = process.env.NODE_ENV;
if (!NODE_ENV) {
throw new Error(
'The NODE_ENV environment variable is required but was not specified.'
);
throw new Error(
'The NODE_ENV environment variable is required but was not specified.'
);
}
// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
var dotenvFiles = [
`${paths.dotenv}.${NODE_ENV}.local`,
`${paths.dotenv}.${NODE_ENV}`,
// Don't include `.env.local` for `test` environment
// since normally you expect tests to produce the same
// results for everyone
NODE_ENV !== 'test' && `${paths.dotenv}.local`,
paths.dotenv,
`${paths.dotenv}.${NODE_ENV}.local`,
`${paths.dotenv}.${NODE_ENV}`,
// Don't include `.env.local` for `test` environment
// since normally you expect tests to produce the same
// results for everyone
NODE_ENV !== 'test' && `${paths.dotenv}.local`,
paths.dotenv,
].filter(Boolean);
// Load environment variables from .env* files. Suppress warnings using silent
@ -31,13 +31,13 @@ var dotenvFiles = [
// https://github.com/motdotla/dotenv
// https://github.com/motdotla/dotenv-expand
dotenvFiles.forEach(dotenvFile => {
if (fs.existsSync(dotenvFile)) {
require('dotenv-expand')(
require('dotenv').config({
path: dotenvFile,
})
);
}
if (fs.existsSync(dotenvFile)) {
require('dotenv-expand')(
require('dotenv').config({
path: dotenvFile,
})
);
}
});
// We support resolving modules according to `NODE_PATH`.
@ -51,43 +51,43 @@ dotenvFiles.forEach(dotenvFile => {
// We also resolve them to make sure all tools using them work consistently.
const appDirectory = fs.realpathSync(process.cwd());
process.env.NODE_PATH = (process.env.NODE_PATH || '')
.split(path.delimiter)
.filter(folder => folder && !path.isAbsolute(folder))
.map(folder => path.resolve(appDirectory, folder))
.join(path.delimiter);
.split(path.delimiter)
.filter(folder => folder && !path.isAbsolute(folder))
.map(folder => path.resolve(appDirectory, folder))
.join(path.delimiter);
// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
// injected into the application via DefinePlugin in Webpack configuration.
const REACT_APP = /^REACT_APP_/i;
function getClientEnvironment(publicUrl) {
const raw = Object.keys(process.env)
.filter(key => REACT_APP.test(key))
.reduce(
(env, key) => {
env[key] = process.env[key];
return env;
},
{
// Useful for determining whether were running in production mode.
// Most importantly, it switches React into the correct mode.
NODE_ENV: process.env.NODE_ENV || 'development',
// Useful for resolving the correct path to static assets in `public`.
// For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
// This should only be used as an escape hatch. Normally you would put
// images into the `src` and `import` them in code to get their paths.
PUBLIC_URL: '/react/build/.',
}
);
// Stringify all values so we can feed into Webpack DefinePlugin
const stringified = {
'process.env': Object.keys(raw).reduce((env, key) => {
env[key] = JSON.stringify(raw[key]);
return env;
}, {}),
};
const raw = Object.keys(process.env)
.filter(key => REACT_APP.test(key))
.reduce(
(env, key) => {
env[key] = process.env[key];
return env;
},
{
// Useful for determining whether were running in production mode.
// Most importantly, it switches React into the correct mode.
NODE_ENV: process.env.NODE_ENV || 'development',
// Useful for resolving the correct path to static assets in `public`.
// For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
// This should only be used as an escape hatch. Normally you would put
// images into the `src` and `import` them in code to get their paths.
PUBLIC_URL: '/react/build/.',
}
);
// Stringify all values so we can feed into Webpack DefinePlugin
const stringified = {
'process.env': Object.keys(raw).reduce((env, key) => {
env[key] = JSON.stringify(raw[key]);
return env;
}, {}),
};
return { raw, stringified };
return { raw, stringified };
}
module.exports = getClientEnvironment;

@ -32,7 +32,7 @@ module.exports = {
// See the discussion in https://github.com/facebookincubator/create-react-app/issues/343.s
// devtool: "cheap-module-eval-source-map",
// 开启调试
//devtool: "source-map", // 开启调试
devtool: "source-map", // 开启调试
// These are the "entry points" to our application.
// This means they will be the "root" imports that are included in JS bundle.
// The first two entry points enable "hot" CSS and auto-refreshes for JS.

@ -33,7 +33,7 @@ const env = getClientEnvironment(publicUrl);
// Assert this just to be safe.
// Development builds of React are slow and not intended for production.
if (env.stringified['process.env'].NODE_ENV !== '"production"') {
throw new Error('Production builds must have NODE_ENV=production.');
throw new Error('Production builds must have NODE_ENV=production.');
}
// Note: defined here because it will be used more than once.
@ -44,9 +44,9 @@ const cssFilename = './static/css/[name].[contenthash:8].css';
// However, our output is structured with css, js and media folders.
// To have this structure working with relative paths, we have to use custom options.
const extractTextPluginOptions = shouldUseRelativeAssetPaths
? // Making sure that the publicPath goes back to to build folder.
{ publicPath: Array(cssFilename.split('/').length).join('../') }
: {};
? // Making sure that the publicPath goes back to to build folder.
{ publicPath: Array(cssFilename.split('/').length).join('../') }
: {};
// This is the production configuration.
// It compiles slowly and is focused on producing a fast and minimal bundle.
@ -54,332 +54,331 @@ const extractTextPluginOptions = shouldUseRelativeAssetPaths
// 上线用的
// console.log('publicPath ', publicPath)
module.exports = {
// externals: {
// 'react': 'window.React'
// },
// Don't attempt to continue if there are any errors.
bail: true,
// We generate sourcemaps in production. This is slow but gives good results.
// You can exclude the *.map files from the build during deployment.
// devtool: shouldUseSourceMap ? 'nosources-source-map' : false, //正式版
devtool: shouldUseSourceMap ? 'source-map' : false,//测试版
// In production, we only want to load the polyfills and the app code.
entry: [require.resolve('./polyfills'), paths.appIndexJs],
output: {
// The build folder.
path: paths.appBuild,
// Generated JS file names (with nested folders).
// There will be one main bundle, and one file per asynchronous chunk.
// We don't currently advertise code splitting but Webpack supports it.
filename: './static/js/[name].[chunkhash:8].js',
chunkFilename: './static/js/[name].[chunkhash:8].chunk.js',
// We inferred the "public path" (such as / or /my-project) from homepage.
// cdn
// publicPath: 'https://shixun.educoder.net/react/build/', //publicPath, https://cdn.educoder.net
// publicPath: 'https://cdn-testeduplus2.educoder.net/react/build/', //publicPath, https://cdn.educoder.net
publicPath: '/react/build/', //publicPath, https://cdn.educoder.net
// Point sourcemap entries to original disk location (format as URL on Windows)
devtoolModuleFilenameTemplate: info =>
path
.relative(paths.appSrc, info.absoluteResourcePath)
.replace(/\\/g, '/'),
},
resolve: {
// This allows you to set a fallback for where Webpack should look for modules.
// We placed these paths second because we want `node_modules` to "win"
// if there are any conflicts. This matches Node resolution mechanism.
// https://github.com/facebookincubator/create-react-app/issues/253
modules: ['node_modules', paths.appNodeModules].concat(
// It is guaranteed to exist because we tweak it in `env.js`
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
),
// These are the reasonable defaults supported by the Node ecosystem.
// We also include JSX as a common component filename extension to support
// some tools, although we do not recommend using it, see:
// https://github.com/facebookincubator/create-react-app/issues/290
// `web` extension prefixes have been added for better support
// for React Native Web.
extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'],
alias: {
"educoder": __dirname + "/../src/common/educoder.js",
// Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
'react-native': 'react-native-web',
},
plugins: [
// Prevents users from importing files from outside of src/ (or node_modules/).
// This often causes confusion because we only process files within src/ with babel.
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
// please link the files into your node_modules/ and let module-resolution kick in.
// Make sure your source files are compiled, as they will not be processed in any way.
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
],
},
module: {
strictExportPresence: true,
rules: [
// TODO: Disable require.ensure as it's not a standard language feature.
// We are waiting for https://github.com/facebookincubator/create-react-app/issues/2176.
// { parser: { requireEnsure: false } },
// externals: {
// 'react': 'window.React'
// },
// Don't attempt to continue if there are any errors.
bail: true,
// We generate sourcemaps in production. This is slow but gives good results.
// You can exclude the *.map files from the build during deployment.
// devtool: shouldUseSourceMap ? 'nosources-source-map' : false, //正式版
devtool:false,//测试版
// In production, we only want to load the polyfills and the app code.
entry: [require.resolve('./polyfills'), paths.appIndexJs],
output: {
// The build folder.
path: paths.appBuild,
// Generated JS file names (with nested folders).
// There will be one main bundle, and one file per asynchronous chunk.
// We don't currently advertise code splitting but Webpack supports it.
filename: './static/js/[name].[chunkhash:8].js',
chunkFilename: './static/js/[name].[chunkhash:8].chunk.js',
// We inferred the "public path" (such as / or /my-project) from homepage.
// cdn
// publicPath: 'https://shixun.educoder.net/react/build/', //publicPath, https://cdn.educoder.net
// publicPath: 'https://cdn-testeduplus2.educoder.net/react/build/', //publicPath, https://cdn.educoder.net
publicPath: '/react/build/', //publicPath, https://cdn.educoder.net
// First, run the linter.
// It's important to do this before Babel processes the JS.
{
test: /\.(js|jsx|mjs)$/,
enforce: 'pre',
use: [
{
options: {
formatter: eslintFormatter,
eslintPath: require.resolve('eslint'),
// Point sourcemap entries to original disk location (format as URL on Windows)
devtoolModuleFilenameTemplate: info =>
path
.relative(paths.appSrc, info.absoluteResourcePath)
.replace(/\\/g, '/'),
},
resolve: {
// This allows you to set a fallback for where Webpack should look for modules.
// We placed these paths second because we want `node_modules` to "win"
// if there are any conflicts. This matches Node resolution mechanism.
// https://github.com/facebookincubator/create-react-app/issues/253
modules: ['node_modules', paths.appNodeModules].concat(
// It is guaranteed to exist because we tweak it in `env.js`
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
),
// These are the reasonable defaults supported by the Node ecosystem.
// We also include JSX as a common component filename extension to support
// some tools, although we do not recommend using it, see:
// https://github.com/facebookincubator/create-react-app/issues/290
// `web` extension prefixes have been added for better support
// for React Native Web.
extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'],
alias: {
"educoder": __dirname + "/../src/common/educoder.js",
// Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
'react-native': 'react-native-web',
},
plugins: [
// Prevents users from importing files from outside of src/ (or node_modules/).
// This often causes confusion because we only process files within src/ with babel.
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
// please link the files into your node_modules/ and let module-resolution kick in.
// Make sure your source files are compiled, as they will not be processed in any way.
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
],
},
module: {
strictExportPresence: true,
rules: [
// TODO: Disable require.ensure as it's not a standard language feature.
// We are waiting for https://github.com/facebookincubator/create-react-app/issues/2176.
// { parser: { requireEnsure: false } },
},
loader: require.resolve('eslint-loader'),
},
],
include: paths.appSrc,
},
{
// "oneOf" will traverse all following loaders until one will
// match the requirements. When no loader matches it will fall
// back to the "file" loader at the end of the loader list.
oneOf: [
// "url" loader works just like "file" loader but it also embeds
// assets smaller than specified size as data URLs to avoid requests.
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve('url-loader'),
options: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]',
},
},
// Process JS with Babel.
{
test: /\.(js|jsx|mjs)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
compact: true,
},
},
// The notation here is somewhat confusing.
// "postcss" loader applies autoprefixer to our CSS.
// "css" loader resolves paths in CSS and adds assets as dependencies.
// "style" loader normally turns CSS into JS modules injecting <style>,
// but unlike in development configuration, we do something different.
// `ExtractTextPlugin` first applies the "postcss" and "css" loaders
// (second argument), then grabs the result CSS and puts it into a
// separate file in our build process. This way we actually ship
// a single CSS file in production instead of JS code injecting <style>
// tags. If you use code splitting, however, any async bundles will still
// use the "style" loader inside the async code so CSS from them won't be
// in the main CSS file.
{
test: /\.css$/,
loader: ExtractTextPlugin.extract(
Object.assign(
{
fallback: {
loader: require.resolve('style-loader'),
options: {
hmr: false,
},
},
use: [
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
minimize: true,
sourceMap: shouldUseSourceMap,
},
},
{
loader: require.resolve('postcss-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebookincubator/create-react-app/issues/2677
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9', // React doesn't support IE8 anyway
],
flexbox: 'no-2009',
}),
],
},
},
],
},
extractTextPluginOptions
)
),
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
},
{
test: /\.scss$/,
use: [
require.resolve("style-loader"),
{
loader: require.resolve("css-loader"),
options: {
importLoaders: 1,
minimize: true,
sourceMap: shouldUseSourceMap,
},
},
{
loader: require.resolve("sass-loader")
}
],
},
// "file" loader makes sure assets end up in the `build` folder.
// When you `import` an asset, you get its filename.
// This loader doesn't use a "test" so it will catch all modules
// that fall through the other loaders.
{
loader: require.resolve('file-loader'),
// Exclude `js` files to keep "css" loader working as it injects
// it's runtime that would otherwise processed through "file" loader.
// Also exclude `html` and `json` extensions so they get processed
// by webpacks internal loaders.
exclude: [/\.(js|jsx|mjs)$/, /\.html$/, /\.json$/],
options: {
name: 'static/media/[name].[hash:8].[ext]',
},
},
// ** STOP ** Are you adding a new loader?
// Make sure to add the new loader(s) before the "file" loader.
],
},
],
},
plugins: [
// Makes some environment variables available in index.html.
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
// In production, it will be an empty string unless you specify "homepage"
// in `package.json`, in which case it will be the pathname of that URL.
new InterpolateHtmlPlugin(env.raw),
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),
// Makes some environment variables available to the JS code, for example:
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
// It is absolutely essential that NODE_ENV was set to production here.
// Otherwise React will be compiled in the very slow development mode.
new webpack.DefinePlugin(env.stringified),
// Minify the code.
// new webpack.optimize.UglifyJsPlugin({
// compress: {
// warnings: false,
// // Disabled because of an issue with Uglify breaking seemingly valid code:
// // https://github.com/facebookincubator/create-react-app/issues/2376
// // Pending further investigation:
// // https://github.com/mishoo/UglifyJS2/issues/2011
// comparisons: false,
// },
// mangle: {
// safari10: true,
// },
// output: {
// comments: false,
// // Turned on because emoji and regex is not minified properly using default
// // https://github.com/facebookincubator/create-react-app/issues/2488
// ascii_only: true,
// },
// sourceMap: shouldUseSourceMap,
// }),
//正式版上线后打开去掉debuger和console
new ParallelUglifyPlugin({
cacheDir: '.cache/',
uglifyJS:{
output: {
comments: false
},
warnings: false,
compress: {
drop_debugger: true,
// First, run the linter.
// It's important to do this before Babel processes the JS.
{
test: /\.(js|jsx|mjs)$/,
enforce: 'pre',
use: [
{
options: {
formatter: eslintFormatter,
eslintPath: require.resolve('eslint'),
},
loader: require.resolve('eslint-loader'),
},
],
include: paths.appSrc,
},
{
// "oneOf" will traverse all following loaders until one will
// match the requirements. When no loader matches it will fall
// back to the "file" loader at the end of the loader list.
oneOf: [
// "url" loader works just like "file" loader but it also embeds
// assets smaller than specified size as data URLs to avoid requests.
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve('url-loader'),
options: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]',
},
},
// Process JS with Babel.
{
test: /\.(js|jsx|mjs)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
compact: true,
},
},
// The notation here is somewhat confusing.
// "postcss" loader applies autoprefixer to our CSS.
// "css" loader resolves paths in CSS and adds assets as dependencies.
// "style" loader normally turns CSS into JS modules injecting <style>,
// but unlike in development configuration, we do something different.
// `ExtractTextPlugin` first applies the "postcss" and "css" loaders
// (second argument), then grabs the result CSS and puts it into a
// separate file in our build process. This way we actually ship
// a single CSS file in production instead of JS code injecting <style>
// tags. If you use code splitting, however, any async bundles will still
// use the "style" loader inside the async code so CSS from them won't be
// in the main CSS file.
{
test: /\.css$/,
loader: ExtractTextPlugin.extract(
Object.assign(
{
fallback: {
loader: require.resolve('style-loader'),
options: {
hmr: false,
},
},
use: [
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
minimize: true,
sourceMap: shouldUseSourceMap,
},
},
{
loader: require.resolve('postcss-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebookincubator/create-react-app/issues/2677
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9', // React doesn't support IE8 anyway
],
flexbox: 'no-2009',
}),
],
},
},
],
},
extractTextPluginOptions
)
),
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
},
{
test: /\.scss$/,
use: [
require.resolve("style-loader"),
{
loader: require.resolve("css-loader"),
options: {
importLoaders: 1,
minimize: true,
sourceMap: shouldUseSourceMap,
},
},
{
loader: require.resolve("sass-loader")
}
],
},
// "file" loader makes sure assets end up in the `build` folder.
// When you `import` an asset, you get its filename.
// This loader doesn't use a "test" so it will catch all modules
// that fall through the other loaders.
{
loader: require.resolve('file-loader'),
// Exclude `js` files to keep "css" loader working as it injects
// it's runtime that would otherwise processed through "file" loader.
// Also exclude `html` and `json` extensions so they get processed
// by webpacks internal loaders.
exclude: [/\.(js|jsx|mjs)$/, /\.html$/, /\.json$/],
options: {
name: 'static/media/[name].[hash:8].[ext]',
},
},
// ** STOP ** Are you adding a new loader?
// Make sure to add the new loader(s) before the "file" loader.
],
},
],
},
plugins: [
// Makes some environment variables available in index.html.
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
// In production, it will be an empty string unless you specify "homepage"
// in `package.json`, in which case it will be the pathname of that URL.
new InterpolateHtmlPlugin(env.raw),
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),
// Makes some environment variables available to the JS code, for example:
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
// It is absolutely essential that NODE_ENV was set to production here.
// Otherwise React will be compiled in the very slow development mode.
new webpack.DefinePlugin(env.stringified),
// Minify the code.
// new webpack.optimize.UglifyJsPlugin({
// compress: {
// warnings: false,
// // Disabled because of an issue with Uglify breaking seemingly valid code:
// // https://github.com/facebookincubator/create-react-app/issues/2376
// // Pending further investigation:
// // https://github.com/mishoo/UglifyJS2/issues/2011
// comparisons: false,
// },
// mangle: {
// safari10: true,
// },
// output: {
// comments: false,
// // Turned on because emoji and regex is not minified properly using default
// // https://github.com/facebookincubator/create-react-app/issues/2488
// ascii_only: true,
// },
// sourceMap: shouldUseSourceMap,
// }),
//正式版上线后打开去掉debuger和console
new ParallelUglifyPlugin({
cacheDir: '.cache/',
uglifyJS:{
output: {
comments: false
},
compress: {
drop_debugger: true,
drop_console: true
}
}
}),
// Note: this won't work without ExtractTextPlugin.extract(..) in `loaders`.
new ExtractTextPlugin({
filename: cssFilename,
}),
// Generate a manifest file which contains a mapping of all asset filenames
// to their corresponding output file so that tools can pick it up without
// having to parse `index.html`.
new ManifestPlugin({
fileName: 'asset-manifest.json',
}),
// Generate a service worker script that will precache, and keep up to date,
// the HTML & assets that are part of the Webpack build.
new SWPrecacheWebpackPlugin({
// By default, a cache-busting query parameter is appended to requests
// used to populate the caches, to ensure the responses are fresh.
// If a URL is already hashed by Webpack, then there is no concern
// about it being stale, and the cache-busting can be skipped.
dontCacheBustUrlsMatching: /\.\w{8}\./,
filename: 'service-worker.js',
logger(message) {
if (message.indexOf('Total precache size is') === 0) {
// This message occurs for every build and is a bit too noisy.
return;
}
if (message.indexOf('Skipping static resource') === 0) {
// This message obscures real errors so we ignore it.
// https://github.com/facebookincubator/create-react-app/issues/2612
return;
}
// console.log(message);
},
minify: true,
// For unknown URLs, fallback to the index page
navigateFallback: publicUrl + '/index.html',
// Ignores URLs starting from /__ (useful for Firebase):
// https://github.com/facebookincubator/create-react-app/issues/2237#issuecomment-302693219
navigateFallbackWhitelist: [/^(?!\/__).*/],
// Don't precache sourcemaps (they're large) and build asset manifest:
staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/],
}),
// Moment.js is an extremely popular library that bundles large locale files
// by default due to how Webpack interprets its code. This is a practical
// solution that requires the user to opt into importing specific locales.
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
// You can remove this if you don't use Moment.js:
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
],
// Some libraries import Node modules but don't use them in the browser.
// Tell Webpack to provide empty mocks for them so importing them works.
node: {
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty',
},
}
}
}),
// Note: this won't work without ExtractTextPlugin.extract(..) in `loaders`.
new ExtractTextPlugin({
filename: cssFilename,
}),
// Generate a manifest file which contains a mapping of all asset filenames
// to their corresponding output file so that tools can pick it up without
// having to parse `index.html`.
new ManifestPlugin({
fileName: 'asset-manifest.json',
}),
// Generate a service worker script that will precache, and keep up to date,
// the HTML & assets that are part of the Webpack build.
new SWPrecacheWebpackPlugin({
// By default, a cache-busting query parameter is appended to requests
// used to populate the caches, to ensure the responses are fresh.
// If a URL is already hashed by Webpack, then there is no concern
// about it being stale, and the cache-busting can be skipped.
dontCacheBustUrlsMatching: /\.\w{8}\./,
filename: 'service-worker.js',
logger(message) {
if (message.indexOf('Total precache size is') === 0) {
// This message occurs for every build and is a bit too noisy.
return;
}
if (message.indexOf('Skipping static resource') === 0) {
// This message obscures real errors so we ignore it.
// https://github.com/facebookincubator/create-react-app/issues/2612
return;
}
// console.log(message);
},
minify: true,
// For unknown URLs, fallback to the index page
navigateFallback: publicUrl + '/index.html',
// Ignores URLs starting from /__ (useful for Firebase):
// https://github.com/facebookincubator/create-react-app/issues/2237#issuecomment-302693219
navigateFallbackWhitelist: [/^(?!\/__).*/],
// Don't precache sourcemaps (they're large) and build asset manifest:
staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/],
}),
// Moment.js is an extremely popular library that bundles large locale files
// by default due to how Webpack interprets its code. This is a practical
// solution that requires the user to opt into importing specific locales.
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
// You can remove this if you don't use Moment.js:
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
],
// Some libraries import Node modules but don't use them in the browser.
// Tell Webpack to provide empty mocks for them so importing them works.
node: {
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty',
},
};

@ -161,6 +161,7 @@
"devDependencies": {
"@babel/runtime": "7.0.0-beta.51",
"babel-plugin-import": "^1.11.0",
"compression-webpack-plugin": "^1.1.12",
"concat": "^1.0.3",
"happypack": "^5.0.1",
"node-sass": "^4.12.0",

@ -1079,8 +1079,14 @@
<div class="name">下降</div>
<div class="code-name">&amp;#xe669;</div>
</li>
<li class="dib">
<li class="dib">
<span class="icon iconfont">&#xe800;</span>
<div class="name">复制</div>
<div class="code-name">&amp;#xe800;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe7f9;</span>
<div class="name">更多</div>
<div class="code-name">&amp;#xe7f9;</div>
@ -1337,7 +1343,31 @@
<div class="name">nenghaofenxi@1x</div>
<div class="code-name">&amp;#xe6be;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe6c1;</span>
<div class="name">detection@1x</div>
<div class="code-name">&amp;#xe6c1;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe6c2;</span>
<div class="name">community@1x</div>
<div class="code-name">&amp;#xe6c2;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe6c3;</span>
<div class="name">hosting@1x</div>
<div class="code-name">&amp;#xe6c3;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe6c4;</span>
<div class="name">project@1x</div>
<div class="code-name">&amp;#xe6c4;</div>
</li>
</ul>
<div class="article markdown">
<h2 id="unicode-">Unicode 引用</h2>
@ -2962,8 +2992,17 @@
<div class="code-name">.icon-xiajiang
</div>
</li>
<li class="dib">
<li class="dib">
<span class="icon iconfont icon-fuzhi1"></span>
<div class="name">
复制
</div>
<div class="code-name">.icon-fuzhi1
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-gengduo1"></span>
<div class="name">
更多
@ -3349,7 +3388,43 @@
<div class="code-name">.icon-nenghaofenxix
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-detectionx"></span>
<div class="name">
detection@1x
</div>
<div class="code-name">.icon-detectionx
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-communityx"></span>
<div class="name">
community@1x
</div>
<div class="code-name">.icon-communityx
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-hostingx2"></span>
<div class="name">
hosting@1x
</div>
<div class="code-name">.icon-hostingx2
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-projectx"></span>
<div class="name">
project@1x
</div>
<div class="code-name">.icon-projectx
</div>
</li>
</ul>
<div class="article markdown">
<h2 id="font-class-">font-class 引用</h2>
@ -4778,8 +4853,16 @@
<div class="name">下降</div>
<div class="code-name">#icon-xiajiang</div>
</li>
<li class="dib">
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-fuzhi1"></use>
</svg>
<div class="name">复制</div>
<div class="code-name">#icon-fuzhi1</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-gengduo1"></use>
</svg>
@ -5122,6 +5205,38 @@
<div class="name">nenghaofenxi@1x</div>
<div class="code-name">#icon-nenghaofenxix</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-detectionx"></use>
</svg>
<div class="name">detection@1x</div>
<div class="code-name">#icon-detectionx</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-communityx"></use>
</svg>
<div class="name">community@1x</div>
<div class="code-name">#icon-communityx</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-hostingx2"></use>
</svg>
<div class="name">hosting@1x</div>
<div class="code-name">#icon-hostingx2</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-projectx"></use>
</svg>
<div class="name">project@1x</div>
<div class="code-name">#icon-projectx</div>
</li>
</ul>
<div class="article markdown">

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1230,6 +1230,13 @@
"unicode": "e669",
"unicode_decimal": 58985
},
{
"icon_id": "5255211",
"name": "复制",
"font_class": "fuzhi1",
"unicode": "e800",
"unicode_decimal": 59392
},
{
"icon_id": "5291605",
"name": "更多",
@ -1530,6 +1537,34 @@
"font_class": "nenghaofenxix",
"unicode": "e6be",
"unicode_decimal": 59070
},
{
"icon_id": "11408531",
"name": "detection@1x",
"font_class": "detectionx",
"unicode": "e6c1",
"unicode_decimal": 59073
},
{
"icon_id": "11409495",
"name": "community@1x",
"font_class": "communityx",
"unicode": "e6c2",
"unicode_decimal": 59074
},
{
"icon_id": "11409771",
"name": "hosting@1x",
"font_class": "hostingx2",
"unicode": "e6c3",
"unicode_decimal": 59075
},
{
"icon_id": "11410432",
"name": "project@1x",
"font_class": "projectx",
"unicode": "e6c4",
"unicode_decimal": 59076
}
]
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 294 KiB

After

Width:  |  Height:  |  Size: 303 KiB

@ -76,10 +76,10 @@ const Otherloginstart=Loadable({
loading: Loading,
})
const TestIndex = Loadable({
loader: () => import('./modules/test'),
loading: Loading,
})
// const TestIndex = Loadable({
// loader: () => import('./modules/test'),
// loading: Loading,
// })
const IndexWrapperComponent = Loadable({
loader: () => import('./modules/page/IndexWrapper'),
@ -91,23 +91,23 @@ const CommentComponent = Loadable({
loading: Loading,
})
const TestMaterialDesignComponent = Loadable({
loader: () => import('./modules/test/md/TestMaterialDesign'),
loading: Loading,
})
const TestCodeMirrorComponent = Loadable({
loader: () => import('./modules/test/codemirror/TestCodeMirror'),
loading: Loading,
})
// const TestMaterialDesignComponent = Loadable({
// loader: () => import('./modules/test/md/TestMaterialDesign'),
// loading: Loading,
// })
// const TestCodeMirrorComponent = Loadable({
// loader: () => import('./modules/test/codemirror/TestCodeMirror'),
// loading: Loading,
// })
const TestComponent = Loadable({
loader: () => import('./modules/test/TestRC'),
loading: Loading,
})
const TestUrlQueryComponent = Loadable({
loader: () => import('./modules/test/urlquery/TestUrlQuery'),
loading: Loading,
})
// const TestComponent = Loadable({
// loader: () => import('./modules/test/TestRC'),
// loading: Loading,
// })
// const TestUrlQueryComponent = Loadable({
// loader: () => import('./modules/test/urlquery/TestUrlQuery'),
// loading: Loading,
// })
const TPMIndexComponent = Loadable({
loader: () => import('./modules/tpm/TPMIndex'),
@ -254,10 +254,10 @@ const Interestpage = Loadable({
})
//众包创新
const ProjectPackages=Loadable({
loader: () => import('./modules/projectPackages/ProjectPackageIndex'),
loading: Loading,
})
// const ProjectPackages=Loadable({
// loader: () => import('./modules/projectPackages/ProjectPackageIndex'),
// loading: Loading,
// })
//竞赛
const NewCompetitions=Loadable({
@ -285,6 +285,12 @@ const Ecs = Loadable({
loading: Loading,
})
// //个人竞赛报名
// const PersonalCompetit = Loadable({
// loader: () => import('./modules/competition/personal/PersonalCompetit.js'),
// loading: Loading,
// });
class App extends Component {
constructor(props) {
super(props)
@ -477,10 +483,17 @@ class App extends Component {
return (<Topicbank {...this.props} {...props} {...this.state} />)
}
}></Route>
{/*众包创新*/}
<Route path={"/crowdsourcing"} component={ProjectPackages}/>
{/*/!*众包创新*!/*/}
{/*<Route path={"/crowdsourcing"} component={ProjectPackages}/>*/}
{/*竞赛*/}
<Route path={"/newcompetitions"} component={NewCompetitions}/>
<Route path={"/newcompetitions"}
render={
(props) => {
return (<NewCompetitions {...this.props} {...props} {...this.state} />)
}
}></Route>
{/*认证*/}
<Route path="/account" component={AccountPage}/>
@ -531,7 +544,12 @@ class App extends Component {
return (<BanksIndex {...this.props} {...props} {...this.state} />)
}
}></Route>
{/*<Route*/}
{/*path="/personalcompetit"*/}
{/*render={*/}
{/*(props) => (<PersonalCompetit {...this.props} {...props} {...this.state}></PersonalCompetit>)*/}
{/*}*/}
{/*/>*/}
<Route
path="/changepassword"
render={
@ -583,11 +601,18 @@ class App extends Component {
>
</Route>
<Route path="/comment" component={CommentComponent}/>
<Route path="/testMaterial" component={TestMaterialDesignComponent}/>
<Route path="/test" component={TestIndex}/>
<Route path="/testCodeMirror" component={TestCodeMirrorComponent}/>
<Route path="/testRCComponent" component={TestComponent}/>
<Route path="/testUrlQuery" component={TestUrlQueryComponent}/>
{/*<Route path="/testMaterial" component={TestMaterialDesignComponent}/>*/}
{/*<Route path="/test" component={TestIndex}/>*/}
{/*<Route path="/testCodeMirror" component={TestCodeMirrorComponent}/>*/}
{/*<Route path="/testRCComponent" component={TestComponent}/>*/}
{/*<Route path="/testUrlQuery" component={TestUrlQueryComponent}/>*/}
{/*<Route*/}
{/*path="/registration"*/}
{/*render={*/}
{/*(props) => (<Registration {...this.props} {...props} {...this.state}></Registration>)*/}
{/*}*/}
{/*/>*/}
<Route path="/messages"
render={
(props)=>(<Messagerouting {...this.props} {...props} {...this.state}></Messagerouting>)
@ -601,6 +626,7 @@ class App extends Component {
render={
(props)=>(<Ecs {...this.props} {...props} {...this.state}></Ecs>)
}/>
<Route exact path="/"
// component={ShixunsHome}
render={

@ -71,3 +71,18 @@ export function toPath(path) {
export function getTaskUrlById(id) {
return `/tasks/${id}`
}
export function htmlEncode(str) {
var s = "";
if (str.length === 0) {
return "";
}
s = str.replace(/&/g, "&amp;");
s = s.replace(/</g, "&lt;");
s = s.replace(/>/g, "&gt;");
s = s.replace(/ /g, "&nbsp;");
s = s.replace(/\'/g, "&#39;");//IE下不支持实体名称
s = s.replace(/\"/g, "&quot;");
return s;
}

@ -4,7 +4,7 @@ import { from } from '_array-flatten@2.1.2@array-flatten';
export { getImageUrl as getImageUrl, getUrl as getUrl, getUrl2 as getUrl2, setImagesUrl as setImagesUrl
, getUploadActionUrl as getUploadActionUrl, getUploadActionUrlOfAuth as getUploadActionUrlOfAuth
, getTaskUrlById as getTaskUrlById, TEST_HOST } from './UrlTool';
, getTaskUrlById as getTaskUrlById, TEST_HOST ,htmlEncode as htmlEncode } from './UrlTool';
export { default as queryString } from './UrlTool2';
export { SnackbarHOC as SnackbarHOC } from './SnackbarHOC';

@ -0,0 +1,127 @@
import React, {Component} from 'react';
import competition from './comcss/competition.css';
import {getImageUrl} from 'educoder';
// 团队竞赛报名大图
class CompetitionMaxImg extends React.Component {
constructor(props) {
super(props)
this.state = {
GetenrollmentAPI: undefined
}
}
componentDidMount() {
}
componentDidUpdate = (prevProps) => {
if (prevProps.GetenrollmentAPI != this.props.GetenrollmentAPI) {
// ////console.log("团队竞赛报名大图componentDidUpdate");
// ////console.log(this.props);
// ////console.log(this.props.GetenrollmentAPI);
this.setState({
GetenrollmentAPI: this.props.GetenrollmentAPI,
})
}
}
render() {
let {type, pint} = this.props;
return (
<div>
<style>
{
`
`
}
</style>
{
type === 1 || type === 2 ?
<div className="registrationback"
style={{"background": `url(${getImageUrl(`images/educoder/competitions/tipregistit.jpg`)})`}}
>
<p className="registrationbackp1">Educoder竞赛平台</p>
<p className="registrationbackp2">Educoder是一个面向计算机类的互联网IT教育和实战平台</p>
<p className="registrationbackp3">提供企业级工程实训以实现工程化专业教学的自动化和智能化</p>
<div className="registrationbackp2button">
<div className="registbut1">
<p onClick={() => this.props.Jointheteam()}>加入战队</p>
</div>
<div className="registbut2">
<p onClick={() => this.props.Createateam()}>创建战队</p>
</div>
</div>
</div>
:
type === 3 ?
<div className="registrationback"
style={{"background": `url(${getImageUrl(`images/educoder/competitions/tipregistit.jpg`)})`}}>
<p className="registrationbackp1">Educoder竞赛平台</p>
<p className="registrationbackp4">高校智能课堂与综合实训平台</p>
<div className="registrationbackp2button2">
<div className="registbut11">
<p onClick={() => this.props.Jointheteam()}>加入战队</p>
</div>
<div className="registbut22">
<p onClick={() => this.props.Createateam()}>创建战队</p>
</div>
</div>
</div>
:
type === 4 || type === 5 ?
<div className="registrationback1"
style={{"background": `url(${getImageUrl(`images/educoder/competitions/tipregistit.jpg`)})`}}>
<p className="registrationbackp11">Educoder竞赛平台</p>
<p className="registrationbackp22">高校智能课堂与综合实训平台</p>
<div className="registrationbackp2button3">
<div className="registbut111">
<p onClick={() => this.props.Jointheteam()}>加入战队</p>
</div>
<div className="registbut222">
<p onClick={() => this.props.Createateam()}>创建战队</p>
</div>
</div>
</div>
: type === 6 ?
<div className="registrationback"
style={{"background": `url(${getImageUrl(`images/educoder/competitions/tipregistit.jpg`)})`}}>
<p className="registrationbackp1">Educoder竞赛平台</p>
<p className="registrationbackp2">Educoder是一个面向计算机类的互联网IT教育和实战平台</p>
<p className="registrationbackp3">提供企业级工程实训以实现工程化专业教学的自动化和智能化</p>
<div className="registrationbackp2button">
{
pint === 1 ?
<div className="registbut1" onClick={() => this.props.Personalregistration()}>
<p onClick={() => this.props.Personalregistration()}>立即报名</p>
</div>
: pint === 2 ?
<div className="personreg1">
<p>已报名</p>
</div>
:
<div className="personreg1">
<p>报名已截止</p>
</div>
}
</div>
</div>
:
<div className="registrationback"
style={{"background": `url(${getImageUrl(`images/educoder/competitions/tipregistit.jpg`)})`}}>
</div>
}
</div>
)
}
}
export default CompetitionMaxImg;

@ -0,0 +1,50 @@
import React, {Component} from 'react';
import {
BrowserRouter as Router,
Route,
Switch
} from 'react-router-dom';
import axios from 'axios';
import moment from 'moment';
import competition from './comcss/competition.css';
import {Checkbox, Table, Pagination, Menu, Icon} from "antd";
import {getImageUrl} from 'educoder';
// 团队竞赛报名无报名子组件团队 在线竞赛 > 全国高校计算机大赛-项目挑战>
class RegisListview extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<div className="reglistviewdivs">
<div className="reglistviewdivss " style={{
width: "100%",
display: "flex",
justifyContent: " space-around",
alignItems: "center",
background: `url(${getImageUrl(`images/educoder/competitions/Rectanglex.png`)})`,
height: "50px",
backgroundPosition: "center",
backgroundSize: "110% 100%",
}}>
<p className=" "
style={{fontSize: "16px", color: "#05101A", width: "79px", textAlign: "center"}}>创建者</p>
<p className=" "
style={{fontSize: "16px", color: "#05101A", width: "160px", textAlign: "center"}}>战队名称</p>
<p className=" "
style={{fontSize: "16px", color: "#05101A", width: "487px", textAlign: "center"}}>战队成员</p>
<p className=" "
style={{fontSize: "16px", color: "#05101A", width: "134px", textAlign: "center"}}>学校</p>
<p className=" "
style={{fontSize: "16px", color: "#05101A", width: "151px", textAlign: "center"}}>时间</p>
</div>
</div>
)
}
}
export default RegisListview;

@ -0,0 +1,144 @@
import React, {Component} from 'react';
import {
BrowserRouter as Router,
Route,
Switch
} from 'react-router-dom';
import axios from 'axios';
import moment from 'moment';
import {SnackbarHOC, WordsBtn, getImageUrl} from 'educoder';
import {TPMIndexHOC} from '../tpm/TPMIndexHOC';
import competition from './comcss/competition.css';
import {Button} from 'antd';
// 团队竞赛报名无报名子组件团队 竞赛报名-已创建战队
class RegisListviewdata extends React.Component {
constructor(props) {
super(props)
this.state = {
item: undefined
}
}
componentDidMount() {
////console.log("RegisListviewdata");
////console.log(this.props.item)
this.setState({
item: this.props.item
})
}
render() {
const {item} = this.props;
return (
<div>
{
item !== undefined ?
<div className={"yslborderbottom"}>
<div className="regitem22">
<div className="regitemimg1 ">
<img className="regitemimg2" src={getImageUrl("images/" + item.creator.image_url)}>
</img>
<p style={{
color: "#999999", fontSize: "14px",
width: "78px",
textAlign: "center"
}}>{item.creator.name}</p>
</div>
<div style={{
marginTop: "29px",
marginLeft: "37px"
}}>
<p className="maxnamewidth160" style={{
color: "#05101A",
fontSize: "16px",
width: "160px",
textAlign: "center"
}}>{item.name}</p>
</div>
<div style={{
marginLeft: "37px",
display: "flex",
flexDirection: "initial",
width: "487px"
}}>
{
item && item.team_members.map((item, index) => {
return (
index === 0 ?
<img className="regitemimgs" src={getImageUrl("images/" + item.image_url)}>
</img>
: index === 1 ?
<img className="regitemimgs2" src={getImageUrl("images/" + item.image_url)}>
</img>
: index === 2 ?
<img className="regitemimgs2" src={getImageUrl("images/" + item.image_url)}>
</img>
: index === 3 ?
<img className="regitemimgs2" src={getImageUrl("images/" + item.image_url)}>
</img>
: index === 4 ?
<img className="regitemimgs2" src={getImageUrl("images/" + item.image_url)}>
</img>
: index === 5 ?
<div>
<img className="regitemimgs2" src={getImageUrl("images/" + item.image_url)}>
</img>
<img className="regitemimgs22"
src={getImageUrl(`images/educoder/competitions/pexjiazai.png`)}>
</img>
</div>
: ""
)
})
}
</div>
<div style={{
marginLeft: "41px",
width: "134px",
marginTop: "29px",
}}>
<p className="maxnamewidth134" style={{
color: "#05101A",
fontSize: "16px",
textAlign: "center"
}}>{item.school_name}</p>
</div>
<div style={{
marginLeft: "37px",
width: "151px",
marginTop: "29px",
}}>
<p style={{
color: "#999999",
fontSize: "16px",
textAlign: "center"
}}>{item.created_at}</p>
</div>
</div>
</div>
: ""
}
</div>
)
}
}
export default RegisListviewdata;

@ -0,0 +1,34 @@
import React, {Component} from 'react';
import competition from './comcss/competition.css';
import {getImageUrl} from 'educoder';
// 团队竞赛报名无报名子组件
class RegisNodata extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<div className="bootom">
<div className="bootomimg" style={{
height: "80px",
width: "125px",
marginTop: "107px",
background: `url(${getImageUrl(`images/educoder/competitions/Noentry.jpg`)})`,
backgroundPosition: "center",
backgroundSize: "110% 100%",
}}>
</div>
<p className="bootomtext">暂无战队参与报名哦赶紧来成为第一个挑战的吧~</p>
</div>
)
}
}
export default RegisNodata;

@ -0,0 +1,797 @@
import React, {Component} from 'react';
import {
BrowserRouter as Router,
Route,
Switch
} from 'react-router-dom';
import axios from 'axios';
import moment from 'moment';
import {SnackbarHOC, WordsBtn} from 'educoder';
import {TPMIndexHOC} from '../tpm/TPMIndexHOC';
import competition from './comcss/competition.css';
import {Button, Pagination, message, Spin, Breadcrumb} from 'antd';
import Registrationitem from './Registrationitem';
import RegisNodata from './RegisNodata';
import CompetitionMaxImg from './CompetitionMaxImg';
import RegistrationSearch from './RegistrationSearch';
import RegisListview from './RegisListview';
import RegisListviewdata from './RegisListviewdata';
import PersonModal from './competmodal/PersonModal';
import MessagePersonModal from './competmodal/MessagePersonModal';
import PersonalModalteam from './competmodal/PersonalModalteam';
import PersonalCompetititem from './personal/PersonalCompetititem';
import ExittheteamModel from './competmodal/ExittheteamModel';
// 团队竞赛报名无报名
class Registration extends React.Component {
/***
*"personal": false, // 是否为个人赛
*"enroll_ended": false, // 报名是否截止
*"enrolled: false, // 是否已经报名
*"teacher_staff": { // 为空表示不支持老师报名
*"member_staff": { // 为空表示不支持学生报名
* personal// 是否是个人赛
* **/
constructor(props) {
super(props)
this.state = {
loadingstate: false,
pages: 1,
limit: 20,
type: 7,
tmodalsType: false,
tmodalsTypes: false,
Newtit: true,
keyword: "",
page: 1,
per_page: 20,
data: [],
competition_teams: [],
count: 0,
GetenrollmentAPI: undefined,
personal: false,
enroll_ended: false,
enrolled: false,
teacher_staff: null,
member_staff: null,
messagePer: "提示",
messagePerbool: false,
intpermessages: "确认",
messageexit: "提示",
messageexitol: false,
exitintpermessages: "是否确认退出战队?",
itemid: undefined,
itemiddata: [],
pint: 0,
}
}
componentDidMount() {
console.log(this.props);
// //////console.log("componentDidMount Registration");
// //// //////console.log("调用子组件 ");
// //////console.log(this.props.isAdmin());
// //// //////console.log(this.props.isAdmin())
try {
const {keyword, page, per_page} = this.state;
this.Getdata(keyword, page, per_page, this.props.user.admin);
this.GetenrollmentAPI();
} catch (e) {
// const {keyword, page, per_page} = this.state;
// this.Getdata(keyword, page, per_page, this.props.isAdmin());
// this.GetenrollmentAPI();
}
}
componentDidUpdate = (prevProps) => {
if (prevProps.user != this.props.user) {
console.log("componentDidUpdate");
console.log(this.props);
////console.log("Registration");
////console.log("componentDidUpdate");
////console.log(this.props.user.admin);
const {keyword, page, per_page} = this.state;
this.Getdata(keyword, page, per_page, this.props.user.admin);
this.GetenrollmentAPI();
}
}
//获取报名配置API
GetenrollmentAPI = () => {
const url = `/competitions/${this.props.match.params.identifier}/competition_staff.json`;
axios.get((url)).then((result) => {
if (result) {
if (result.data) {
//// //////console.log("获取报名配置API");
//// //////console.log(result);
this.setState({
GetenrollmentAPI: result.data,
personal: result.data.personal,
enroll_ended: result.data.enroll_ended,
enrolled: result.data.enrolled,
teacher_staff: result.data.teacher_staff,
member_staff: result.data.member_staff,
})
if (result.data.enroll_ended === true) {
this.setState({
pint: 0
})
} else if (result.data.enrolled === true) {
this.setState({
pint: 2
})
} else if (result.data.enrolled === false) {
this.setState({
pint: 1
})
}
}
}
}).catch((error) => {
//// //////console.log(error);
})
}
Getdata = (keyword, page, per_page, admin) => {
//搜索关键字 keyword
//页数 page
//分页 per_page
const datas = {
keyword: keyword,
page: page,
per_page: per_page,
};
let url = `/competitions/${this.props.match.params.identifier}/competition_teams.json`;
axios.get((url), {params: datas}).then((result) => {
if (result) {
if (result.data) {
//// //////console.log(result);\
if (result.data.personal === false) {
//不是个人赛
if (result.data.my_teams.length === 0) {
// 没有创建数据的
if (admin === true) {
//管理员
this.setState({
type: 4,
count: result.data.count,
data: result.data.my_teams,
competition_teams: result.data.competition_teams,
personal: result.data.personal,
})
} else {
//普通账号
this.setState({
type: 1,
count: result.data.count,
data: result.data.my_teams,
competition_teams: result.data.competition_teams,
personal: result.data.personal,
})
}
} else {
//有数据的
if (admin === true) {
if (result.data.my_teams[0].manage_permission === true) {
this.setState({
type: 5,
data: result.data.my_teams,
count: result.data.count,
competition_teams: result.data.competition_teams,
personal: result.data.personal,
})
} else {
this.setState({
type: 4,
data: result.data.my_teams,
count: result.data.count,
competition_teams: result.data.competition_teams,
personal: result.data.personal,
})
}
} else {
if (result.data.my_teams[0].manage_permission === true) {
//普通账号true 为创建了竞赛
this.setState({
type: 2,
data: result.data.my_teams,
count: result.data.count,
personal: result.data.personal,
})
} else {
//普通账号true 加入了竞赛
this.setState({
type: 3,
data: result.data.my_teams,
count: result.data.count,
personal: result.data.personal,
})
}
}
}
} else {
this.setState({
type: 6,
data: result.data.my_teams,
count: result.data.count,
competition_teams: result.data.competition_teams,
personal: result.data.personal,
})
}
}
}
this.setState({
loadingstate: false,
})
}).catch((error) => {
if (admin === true) {
//管理员
this.setState({
type: 4,
count: 0,
competition_teams: [],
data: [],
loadingstate: false,
})
} else {
//普通账号
this.setState({
type: 1,
count: 0,
competition_teams: [],
data: [],
loadingstate: false,
})
}
})
}
Getdatatype5 = (keyword, page, per_page, admin) => {
//搜索关键字 keyword
//页数 page
//分页 per_page
const datas = {
keyword: keyword,
page: page,
per_page: per_page,
};
let url = `/competitions/${this.props.match.params.identifier}/competition_teams.json`;
axios.get((url), {params: datas}).then((result) => {
this.setState({
loadingstate: false,
})
if (result) {
if (result.data) {
//// //////console.log(result);
if (result.data.personal === false) {
//不是个人赛
////console.log("Getdatatype5");
////console.log(result.data.my_teams.length);
if (result.data.my_teams.length === 0) {
// 没有创建数据的
//管理员
////console.log("a");
////console.log(this.state.competition_teams);
////console.log(result.data.competition_teams);
this.setState({
type: 4,
count: result.data.count,
competition_teams: result.data.competition_teams,
data: result.data.my_teams,
personal: result.data.personal,
})
} else {
//有数据的
////console.log("b");
if (result.data.my_teams[0].manage_permission === true) {
this.setState({
type: 5,
data: result.data.my_teams,
count: result.data.count,
competition_teams: result.data.competition_teams,
personal: result.data.personal,
})
} else {
////console.log("c");
this.setState({
type: 4,
data: result.data.my_teams,
count: result.data.count,
competition_teams: result.data.competition_teams,
personal: result.data.personal,
})
}
}
} else {
//团队赛
//////console.log("d");
this.setState({
type: 6,
data: result.data.my_teams,
count: result.data.count,
competition_teams: result.data.competition_teams,
personal: result.data.personal,
})
}
}
}
}).catch((error) => {
////console.log("k");
////console.log(error);
////console.log("报错了");
if (admin === true) {
//管理员
this.setState({
count: 0,
competition_teams: [],
data: [],
loadingstate: false,
})
} else {
//普通账号
this.setState({
count: 0,
competition_teams: [],
data: [],
loadingstate: false,
})
}
})
}
//团队竞赛翻页
paginationonChangestwo = (pageNumber) => {
this.setState({
pages: pageNumber,
loadingstate: true,
})
const {keyword, per_page} = this.state;
this.Getdatatype5(keyword, pageNumber, per_page, this.props.user.admin);
};
/**
* 加入战队
* */
Jointheteam = () => {
if (this.state.enrolled === true) {
//已经报名
this.setState({
messagePerbool: true,
intpermessages: "您已报名,无需重复报"
})
return;
}
if (this.state.enroll_ended === true) {
//报名截止
this.setState({
messagePerbool: true,
intpermessages: "报名已截止,无需报名"
})
return
}
if (this.props.user.admin === true) {
//老师
if (this.state.teacher_staff === null) {
//禁止老师
this.setState({
messagePerbool: true,
intpermessages: "已禁止老师报名"
})
return;
}
this.setState({
tmodalsTypes: true
})
} else {
//学生
if (this.state.member_staff === null) {
//禁止学生
this.setState({
messagePerbool: true,
intpermessages: "已禁止学生报名"
})
return;
}
this.setState({
tmodalsTypes: true
})
}
// this.setState({
// tmodalsTypes: true
// })
}
/**
* 创建战队
**/
Createateam = () => {
//
if (this.state.enrolled === true) {
//已经报名
this.setState({
messagePerbool: true,
intpermessages: "您已报名,无需重复报"
})
return;
}
if (this.state.enroll_ended === true) {
//报名截止
this.setState({
messagePerbool: true,
intpermessages: "报名已截止,无需报名"
})
return
}
if (this.props.user.admin === true) {
//老师
if (this.state.teacher_staff === null) {
//禁止老师
this.setState({
messagePerbool: true,
intpermessages: "已禁止老师报名"
})
return;
}
this.setState({
tmodalsType: true,
Newtit: true,
})
} else {
//学生
if (this.state.member_staff === null) {
//禁止学生
this.setState({
messagePerbool: true,
intpermessages: "已禁止学生报名"
})
return;
}
this.setState({
tmodalsType: true,
Newtit: true,
})
}
}
//编辑战队
Createateamedit = (data) => {
this.setState({
tmodalsType: true,
Newtit: false,
itemiddata: data
})
}
Tmoconfirm = (bool) => {
//boolfalse 取消 true 确认
this.setState({
tmodalsTypes: false
})
if (bool) {
//确认
} else {
//取消
}
}
//创建战队确认
Tmoconfirm1 = (bool) => {
//boolfalse 取消 true 确认
this.setState({
tmodalsType: false
})
if (bool) {
//确认
this.Refreshteam();
} else {
//取消
}
}
//自定义弹框按钮
messagePerboolbuton = () => {
this.setState({
messagePerbool: false
})
}
//显示退出战队弹框
Exittheteamshow = (itemid, bool) => {
if (bool === true) {
this.setState({
messageexitol: true,
itemid: itemid,
exitintpermessages: "是否确认删除战队",
})
} else {
this.setState({
messageexitol: true,
itemid: itemid,
exitintpermessages: "是否确认退出战队",
})
}
};
//刷新战队
Refreshteam = () => {
const {keyword, page, per_page} = this.state;
this.Getdata(keyword, page, per_page, this.props.user.admin);
this.GetenrollmentAPI();
}
//退出战队
Exittheteam = (bool) => {
// //////console.log(this.state.itemid);
if (bool) {
this.setState({
messageexitol: true
})
let url = `/competitions/${"gcc-task-2020"}/competition_teams/${this.state.itemid}/leave.json`;
axios.post(url).then((response) => {
if (response) {
if (response.data) {
//////console.log("退出战队");
//////console.log(response);
this.Refreshteam();
this.setState({
messageexitol: false
})
}
}
}).catch((error) => {
//////console.log(error)
});
} else {
this.setState({
messageexitol: false
})
}
}
//搜索战队
RegistrationSearchvalue = (value) => {
////console.log("RegistrationSearchvalue");
////console.log(this.props.user.admin);
this.setState({
pages: 1,
limit: 20,
})
this.Getdatatype5(value, 1, 20, this.props.user.admin);
}
//个人竞赛
// /competitions/:identifier/competition_teams.json
Personalregistration = () => {
let {teacher_staff, member_staff, data, enroll_ended, enrolled} = this.state;
if (enroll_ended === true) {
//已截止
this.props.showNotification(`报名已截止`);
return;
}
if (enrolled === true) {
this.props.showNotification(`你已经报名,不能重复报名!`);
return;
}
const url = `/competitions/${this.props.match.params.identifier}/competition_teams.json`;
axios.post(url).then((response) => {
if (response) {
if (response.data) {
this.props.showNotification(`报名成功,预祝您夺得桂冠!`);
this.Refreshteam();
}
}
}).catch((error) => {
});
}
render() {
const {page, pages, limit, type, tmodalsType, tmodalsTypes, data, count, competition_teams, Newtit, itemiddata, messagePerbool, messageexitol, GetenrollmentAPI, loadingstate, pint} = this.state;
return (
<div className="newMain clearfix ">
<div className={"educontent mb20 persmstyle"} style={{width: "1200px", marginTop: "26px"}}>
<style>
{
`
html, body{
overflow: hidden;
height: 100%;
}
`
}
</style>
{
messagePerbool === true ?
<MessagePersonModal messagePer={this.state.messagePer} {...this.props} {...this.state}
messagePerboolbuton={() => this.messagePerboolbuton()}
GetenrollmentAPI={GetenrollmentAPI}></MessagePersonModal>
: ""
}
{/*编辑创建战队*/}
{
tmodalsType === true ?
<PersonModal modalsType={tmodalsType} {...this.props} {...this.state} Newtit={Newtit}
itemiddata={itemiddata} GetenrollmentAPI={GetenrollmentAPI}
Tmoconfirm1={(bool) => this.Tmoconfirm1(bool)}></PersonModal>
:
""
}
{
tmodalsTypes === true ?
<PersonalModalteam tmodalsTypes={tmodalsTypes} {...this.props} {...this.state}
GetenrollmentAPI={GetenrollmentAPI}
Tmoconfirm={(bool) => this.Tmoconfirm(bool)}></PersonalModalteam>
: ""
}
{
messageexitol === true ?
<ExittheteamModel {...this.props} {...this.state} GetenrollmentAPI={GetenrollmentAPI}
Exittheteam={(bool) => this.Exittheteam(bool)}></ExittheteamModel>
: ""
}
{/*<div className="educontent mb20 ">*/}
{/* <p className="clearfix mb20 mt10">*/}
{/* <a className="btn colorgrey fl hovercolorblue ">在线竞赛</a>*/}
{/* <span className="color-grey-9 fl ml3 mr3">&gt;</span>*/}
{/* <a*/}
{/* className=" btn colorgrey fl hovercolorblue ">全国高校计算机大赛-项目挑战</a>*/}
{/* <span className="color-grey-9 fl ml3 mr3">&gt;</span>*/}
{/* <WordsBtn className="fl">报名</WordsBtn>*/}
{/* </p>*/}
{/*</div>*/}
<div style={{marginBottom: '12px'}}>
<Breadcrumb separator=">">
<Breadcrumb.Item href="/newcompetitions">在线竞赛</Breadcrumb.Item>
<Breadcrumb.Item href="">全国高校计算机大赛</Breadcrumb.Item>
<Breadcrumb.Item href="">报名</Breadcrumb.Item>
</Breadcrumb>
</div>
{/*大图*/}
<CompetitionMaxImg type={type} Jointheteam={() => this.Jointheteam()}
pint={pint}
{...this.props} {...this.state}
Createateam={() => this.Createateam()}
Personalregistration={() => this.Personalregistration()}
></CompetitionMaxImg>
{/*大图结尾*/}
{/*没数据*/}
{
pint === 1 || pint === 3 ?
<div style={{
marginTop: "22px"
}}>
<p>参赛总人数<span style={{color: "#459BE5"}}>{data === null || data === undefined ? 0 : data.length}</span><span
style={{marginLeft: "5px"}}></span>
</p>
</div>
: ""}
{/*列表*/}
{
type === 6 ?
<div
style={{
marginTop: "31px"
}}
>
<Spin spinning={loadingstate}>
{
data && data.map((item, index) => {
return (
<Registrationitem key={index} item={item}></Registrationitem>
)
})
}
</Spin>
</div>
: ""}
{
type === 1 ?
<RegisNodata></RegisNodata>
:
""
}
{/*普通账号出现单人 战队弹框*/}
{
type === 2 || type === 3 || type === 5 ?
<PersonalCompetititem type={type} data={data}
Exittheteamshow={(itemid) => this.Exittheteamshow(itemid)}
Createateamedit={(itemid) => this.Createateamedit(itemid)}></PersonalCompetititem>
: ""
}
{
type === 4 || type === 5 ?
<RegistrationSearch count={count}
RegistrationSearchvalue={(value) => this.RegistrationSearchvalue(value)}></RegistrationSearch>
: ""
}
{/*<Registrationitem></Registrationitem>*/}
{
type === 4 || type === 5 ?
<RegisListview></RegisListview>
:
""
}
{type === 4 || type === 5 ?
<Spin spinning={loadingstate}>
{
competition_teams && competition_teams.map((item, index) => {
return (
<RegisListviewdata key={index} item={item}></RegisListviewdata>
)
})
}
</Spin>
:
""
}
{
type === 4 || type === 5 ?
(
count < 20 ? <div style={{
height: "20px",
minHeight: "20px"
}}></div> :
<div className="edu-txt-center ysyslxh mt56 " style={{marginBottom: "192px",}}>
<Pagination showQuickJumper current={pages}
onChange={this.paginationonChangestwo} pageSize={limit}
total={count}></Pagination>
</div>
)
: <div style={{
height: "20px",
minHeight: "20px"
}}></div>
}
</div>
</div>
)
}
}
export default Registration;

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

Loading…
Cancel
Save