Merge remote-tracking branch 'origin/dev_aliyun' into dev_tj

merge from dev_aliyun
dev_forge
tangjiang 5 years ago
commit 840ed71420

@ -0,0 +1,6 @@
{
"cells": [],
"metadata": {},
"nbformat": 4,
"nbformat_minor": 2
}

@ -0,0 +1,6 @@
{
"cells": [],
"metadata": {},
"nbformat": 4,
"nbformat_minor": 2
}

@ -1,426 +0,0 @@
PATH
remote: lib/gitlab-cli
specs:
gitlab (3.2.0)
httparty
terminal-table
GEM
remote: https://gems.ruby-china.com/
specs:
aasm (5.0.5)
concurrent-ruby (~> 1.0)
actioncable (5.2.1)
actionpack (= 5.2.1)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailer (5.2.1)
actionpack (= 5.2.1)
actionview (= 5.2.1)
activejob (= 5.2.1)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (5.2.1)
actionview (= 5.2.1)
activesupport (= 5.2.1)
rack (~> 2.0)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (5.2.1)
activesupport (= 5.2.1)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.3)
active_decorator (1.2.0)
activejob (5.2.1)
activesupport (= 5.2.1)
globalid (>= 0.3.6)
activemodel (5.2.1)
activesupport (= 5.2.1)
activerecord (5.2.1)
activemodel (= 5.2.1)
activesupport (= 5.2.1)
arel (>= 9.0)
activestorage (5.2.1)
actionpack (= 5.2.1)
activerecord (= 5.2.1)
marcel (~> 0.3.1)
activesupport (5.2.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
acts-as-taggable-on (6.0.0)
activerecord (~> 5.0)
addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
archive-zip (0.11.0)
io-like (~> 0.3.0)
arel (9.0.0)
autoprefixer-rails (9.6.1)
execjs
awesome_print (1.8.0)
axlsx (3.0.0.pre)
htmlentities (~> 4.3, >= 4.3.4)
mimemagic (~> 0.3)
nokogiri (~> 1.8, >= 1.8.2)
rubyzip (~> 1.2, >= 1.2.1)
axlsx_rails (0.5.2)
actionpack (>= 3.1)
axlsx (>= 2.0.1)
bindex (0.5.0)
bootsnap (1.3.1)
msgpack (~> 1.0)
bootstrap (4.3.1)
autoprefixer-rails (>= 9.1.0)
popper_js (>= 1.14.3, < 2)
sassc-rails (>= 2.0.0)
builder (3.2.3)
bulk_insert (1.7.0)
activerecord (>= 3.2.0)
byebug (10.0.2)
capybara (3.5.1)
addressable
mini_mime (>= 0.1.3)
nokogiri (~> 1.8)
rack (>= 1.6.0)
rack-test (>= 0.6.3)
xpath (~> 3.1)
childprocess (0.9.0)
ffi (~> 1.0, >= 1.0.11)
chromedriver-helper (1.2.0)
archive-zip (~> 0.10)
nokogiri (~> 1.8)
chunky_png (1.3.10)
concurrent-ruby (1.0.5)
connection_pool (2.2.2)
crass (1.0.4)
diff-lcs (1.3)
diffy (3.3.0)
elasticsearch (7.2.0)
elasticsearch-api (= 7.2.0)
elasticsearch-transport (= 7.2.0)
elasticsearch-api (7.2.0)
multi_json
elasticsearch-transport (7.2.0)
faraday
multi_json
enumerize (2.3.1)
activesupport (>= 3.2)
erubi (1.7.1)
execjs (2.7.0)
faraday (0.15.4)
multipart-post (>= 1.2, < 3)
ffi (1.9.25)
font-awesome-sass (4.7.0)
sass (>= 3.2)
globalid (0.4.1)
activesupport (>= 4.2.0)
grape-entity (0.7.1)
activesupport (>= 4.0)
multi_json (>= 1.3.2)
hashie (3.5.7)
htmlentities (4.3.4)
httparty (0.16.2)
multi_xml (>= 0.5.2)
i18n (1.1.0)
concurrent-ruby (~> 1.0)
io-like (0.3.0)
jbuilder (2.7.0)
activesupport (>= 4.2.0)
multi_json (>= 1.2)
jquery-rails (4.3.5)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
jwt (2.1.0)
kaminari (1.1.1)
activesupport (>= 4.1.0)
kaminari-actionview (= 1.1.1)
kaminari-activerecord (= 1.1.1)
kaminari-core (= 1.1.1)
kaminari-actionview (1.1.1)
actionview
kaminari-core (= 1.1.1)
kaminari-activerecord (1.1.1)
activerecord
kaminari-core (= 1.1.1)
kaminari-core (1.1.1)
listen (3.1.5)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
ruby_dep (~> 1.2)
loofah (2.2.2)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.0)
mini_mime (>= 0.1.1)
marcel (0.3.2)
mimemagic (~> 0.3.2)
method_source (0.9.0)
mimemagic (0.3.2)
mini_mime (1.0.0)
mini_portile2 (2.3.0)
minitest (5.11.3)
msgpack (1.2.4)
multi_json (1.13.1)
multi_xml (0.6.0)
multipart-post (2.0.0)
mustermann (1.0.3)
mysql2 (0.5.2)
nio4r (2.3.1)
nokogiri (1.8.4)
mini_portile2 (~> 2.3.0)
oauth2 (1.4.1)
faraday (>= 0.8, < 0.16.0)
jwt (>= 1.0, < 3.0)
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
omniauth (1.9.0)
hashie (>= 3.4.6, < 3.7.0)
rack (>= 1.6.2, < 3)
omniauth-oauth2 (1.6.0)
oauth2 (~> 1.1)
omniauth (~> 1.9)
pdfkit (0.8.4.1)
popper_js (1.14.5)
public_suffix (4.0.1)
puma (3.12.0)
rack (2.0.5)
rack-cors (1.0.2)
rack-protection (2.0.5)
rack
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails (5.2.1)
actioncable (= 5.2.1)
actionmailer (= 5.2.1)
actionpack (= 5.2.1)
actionview (= 5.2.1)
activejob (= 5.2.1)
activemodel (= 5.2.1)
activerecord (= 5.2.1)
activestorage (= 5.2.1)
activesupport (= 5.2.1)
bundler (>= 1.3.0)
railties (= 5.2.1)
sprockets-rails (>= 2.0.0)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-html-sanitizer (1.0.4)
loofah (~> 2.2, >= 2.2.2)
rails-i18n (5.1.3)
i18n (>= 0.7, < 2)
railties (>= 5.0, < 6)
railties (5.2.1)
actionpack (= 5.2.1)
activesupport (= 5.2.1)
method_source
rake (>= 0.8.7)
thor (>= 0.19.0, < 2.0)
rake (12.3.1)
rb-fsevent (0.10.3)
rb-inotify (0.9.10)
ffi (>= 0.5.0, < 2)
rchardet (1.8.0)
redcarpet (3.4.0)
redis (4.1.0)
redis-actionpack (5.0.2)
actionpack (>= 4.0, < 6)
redis-rack (>= 1, < 3)
redis-store (>= 1.1.0, < 2)
redis-activesupport (5.0.7)
activesupport (>= 3, < 6)
redis-store (>= 1.3, < 2)
redis-rack (2.0.5)
rack (>= 1.5, < 3)
redis-store (>= 1.2, < 2)
redis-rails (5.0.2)
redis-actionpack (>= 5.0, < 6)
redis-activesupport (>= 5.0, < 6)
redis-store (>= 1.2, < 2)
redis-store (1.6.0)
redis (>= 2.2, < 5)
roo (2.8.2)
nokogiri (~> 1)
rubyzip (>= 1.2.1, < 2.0.0)
roo-xls (1.2.0)
nokogiri
roo (>= 2.0.0, < 3)
spreadsheet (> 0.9.0)
rqrcode (0.10.1)
chunky_png (~> 1.0)
rqrcode_png (0.1.5)
chunky_png
rqrcode
rspec-core (3.8.0)
rspec-support (~> 3.8.0)
rspec-expectations (3.8.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.8.0)
rspec-mocks (3.8.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.8.0)
rspec-rails (3.8.0)
actionpack (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
rspec-core (~> 3.8.0)
rspec-expectations (~> 3.8.0)
rspec-mocks (~> 3.8.0)
rspec-support (~> 3.8.0)
rspec-support (3.8.0)
ruby-ole (1.2.12.2)
ruby_dep (1.5.0)
rubyzip (1.2.1)
sass (3.5.7)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
sass-rails (5.0.7)
railties (>= 4.0.0, < 6)
sass (~> 3.1)
sprockets (>= 2.8, < 4.0)
sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3)
sassc (2.0.1)
ffi (~> 1.9)
rake
sassc-rails (2.1.2)
railties (>= 4.0.0)
sassc (>= 2.0)
sprockets (> 3.0)
sprockets-rails
tilt
searchkick (3.1.3)
activemodel (>= 4.2)
elasticsearch (>= 5)
hashie
selenium-webdriver (3.14.0)
childprocess (~> 0.5)
rubyzip (~> 1.2)
sidekiq (5.2.7)
connection_pool (~> 2.2, >= 2.2.2)
rack (>= 1.5.0)
rack-protection (>= 1.5.0)
redis (>= 3.3.5, < 5)
simple_form (4.1.0)
actionpack (>= 5.0)
activemodel (>= 5.0)
simple_xlsx_reader (1.0.4)
nokogiri
rubyzip
sinatra (2.0.5)
mustermann (~> 1.0)
rack (~> 2.0)
rack-protection (= 2.0.5)
tilt (~> 2.0)
spreadsheet (1.2.3)
ruby-ole (>= 1.0)
spring (2.0.2)
activesupport (>= 4.2)
spring-watcher-listen (2.0.1)
listen (>= 2.7, < 4.0)
spring (>= 1.2, < 3.0)
sprockets (3.7.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.2.1)
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
thor (0.20.0)
thread_safe (0.3.6)
tilt (2.0.8)
turbolinks (5.2.0)
turbolinks-source (~> 5.2)
turbolinks-source (5.2.0)
tzinfo (1.2.5)
thread_safe (~> 0.1)
uglifier (4.1.17)
execjs (>= 0.3.0, < 3)
unicode-display_width (1.4.0)
web-console (3.6.2)
actionview (>= 5.0)
activemodel (>= 5.0)
bindex (>= 0.4.0)
railties (>= 5.0)
websocket-driver (0.7.0)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.3)
wkhtmltopdf-binary (0.12.4)
xpath (3.1.0)
nokogiri (~> 1.8)
PLATFORMS
ruby
DEPENDENCIES
aasm
active_decorator
acts-as-taggable-on (~> 6.0)
awesome_print
axlsx (~> 3.0.0.pre)
axlsx_rails (~> 0.5.2)
bootsnap (>= 1.1.0)
bootstrap (~> 4.3.1)
bulk_insert
byebug
capybara (>= 2.15, < 4.0)
chromedriver-helper
diffy
enumerize
faraday (~> 0.15.4)
font-awesome-sass (= 4.7.0)
gitlab!
grape-entity (~> 0.7.1)
jbuilder (~> 2.5)
jquery-rails
kaminari (~> 1.1, >= 1.1.1)
listen (>= 3.0.5, < 3.2)
mysql2 (>= 0.4.4, < 0.6.0)
oauth2
omniauth (~> 1.9.0)
omniauth-oauth2 (~> 1.6.0)
pdfkit
puma (~> 3.11)
rack-cors
rails (~> 5.2.0)
rails-i18n (~> 5.1)
rchardet (~> 1.8)
redcarpet (~> 3.4)
redis-rails
roo-xls
rqrcode (~> 0.10.1)
rqrcode_png
rspec-rails (~> 3.8)
ruby-ole
rubyzip
sass-rails (~> 5.0)
searchkick
selenium-webdriver
sidekiq
simple_form
simple_xlsx_reader
sinatra
spreadsheet
spring
spring-watcher-listen (~> 2.0.0)
turbolinks (~> 5)
tzinfo-data
uglifier (>= 1.3.0)
web-console (>= 3.3.0)
wkhtmltopdf-binary
RUBY VERSION
ruby 2.3.7p456
BUNDLED WITH
1.17.3

@ -3,7 +3,7 @@ $(document).on('turbolinks:load', function() {
var $container = $('.edit-laboratory-setting-container');
var $form = $container.find('.edit_laboratory');
$('.logo-item-left').on("change", 'input[type="file"]', function () {
$('.logo-item-left, .banner-item-bottom').on("change", 'input[type="file"]', function () {
var $fileInput = $(this);
var file = this.files[0];
var imageType = /image.*/;

@ -85,8 +85,8 @@ $(document).on('turbolinks:load', function() {
$subjectSelect.select2({
theme: 'bootstrap4',
placeholder: '请输入课程名称/创建者检索',
multiple: true,
closeOnSelect: false,
multiple: false,
closeOnSelect: true,
ajax: {
delay: 500,
url: '/admins/laboratories/' + laboratoryId + '/subjects_for_select',
@ -132,7 +132,7 @@ $(document).on('turbolinks:load', function() {
method: 'POST',
dataType: 'json',
url: '/admins/laboratories/' + laboratoryId + '/laboratory_subjects',
data: { subject_ids: subjectIds },
data: { subject_id: subjectIds },
success: function(){
show_success_flash();
window.location.reload();

@ -0,0 +1,803 @@
$(document).on('turbolinks:load', function(){
if ($('body.cooperative-competition-settings-index-page').length > 0) {
var dateOptions = {
autoclose: true,
language: 'zh-CN',
format: 'yyyy-mm-dd',
startDate: '2017-04-01'
};
var timeOptions = {
autoclose: 1,
language: 'zh-CN',
format: 'yyyy-mm-dd hh:ii',
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);
});
};
$(".competition-start-end-date .start-date").datetimepicker(timeOptions);
$(".competition-start-end-date .end-date").datetimepicker(timeOptions);
$(".nav-setting-form .enroll_end_time").datetimepicker(timeOptions);
$(".stage-update-form .section-start-time").datetimepicker(timeOptions);
$(".stage-update-form .section-end-time").datetimepicker(timeOptions);
defineDateRangeSelect('.teaching-mode-date');
// defineTimeRangeSelect('.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 || $courseId.val().length === 0) {
$courseId.addClass('danger text-danger');
valid = false;
} else {
$courseId.removeClass('danger text-danger');
}
} else if ($("input[name='mode']:checked").val() == 3) {
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');
}
} else {
$("input[name='course_id']").removeClass('danger text-danger');
$("input[name='teach_start_time']").removeClass('danger text-danger');
$("input[name='teach_end_time']").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);
}
});
});
var selectOptions = {
theme: 'bootstrap4',
placeholder: '请输入要添加的单位名称',
multiple: true,
minimumInputLength: 1,
ajax: {
delay: 500,
url: '/api/schools/search.json',
dataType: 'json',
data: function(params){
return { keyword: params.term };
},
processResults: function(data){
return { results: data.schools }
}
},
templateResult: function (item) {
if(!item.id || item.id === '') return item.text;
return item.name || item.text;
},
templateSelection: function(item){
return item.name || item.text;
}
};
$('.sponsor-select').select2(selectOptions);
$('.allow-school-select').select2(selectOptions);
$('.manager-select').select2({
theme: 'bootstrap4',
placeholder: '请输入要添加的管理员姓名',
multiple: true,
minimumInputLength: 1,
ajax: {
delay: 500,
url: '/cooperative/users',
dataType: 'json',
data: function(params){
return { keyword: params.term };
},
processResults: function(data){
return { results: data.users }
}
},
templateResult: function (item) {
if(!item.id || item.id === '') return item.text;
return $("<div class='row px-0'><span class='col-3'>" + item.real_name + "</span><span class='col-5 font-12'>" + item.school_name + "</span><span class='col-4 font-12'>" + item.hidden_phone + "</span></div>");
},
templateSelection: function(item){
if (item.id) {
}
return item.real_name || item.text;
}
});
// 排行榜
//链接
$(".nav-setting-form").on("click",".add_linkBtn",function () {
var length=$(".nav-setting-form").find(".linkFormItem").length + 1;
var html='<div class="row mt-2 align-items-center linkFormItem">\n' +
' <div class="col-1 text-right">\n' +
' <label class="checkbox checkbox-primary mt-1">\n' +
' <input type="checkbox" name="navbar[][hidden]" value="0" hidden class="font-16" checked="checked">\n' +
' <input type="checkbox" value="0" class="font-16 module_hidden" checked="checked">\n' +
' </label>\n' +
' </div>\n' +
' <div class="col-md-label mt-1"><input type="hidden" value="md" name="navbar[][module_type]">\n' +
' <input type="text" name="navbar[][name]" value="" class="form-control" placeholder="模块名称"></div>\n' +
' <div class="col-md-1 mt-1"><input type="text" name="navbar[][position]" value="" class="form-control" placeholder="位置"></div>\n' +
' <div class="col-md-3 mt-1"><input type="text" name="navbar[][url]" value="" class="form-control" placeholder="请输入资料下载地址"></div>\n' +
' <a class="mt-1 btn btn-primary waves-effect waves-light btn-xs setBtn_s add_linkBtn" href="javascript:void(0)">+</a>\n' +
' <a class="mt-1 btn btn-icon waves-effect btn-default waves-light setBtn_s ml10 del_linkBtn" href="javascript:void(0)">×</a>\n' +
' </div>';
$(this).parents(".linkFormItem").after(html);
});
$(".nav-setting-form").on("click", ".del_linkBtn", function () {
$(this).parents(".linkFormItem").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="competition_staffs[][minimum]" value="0">\n' +
' </div>\n' +
' <span class="mt-2">~</span>\n' +
' <div class="col-1 mt-1">\n' +
' <input type="text" class="form-control" name="competition_staffs[][maximum]" value="1">\n' +
' </div>\n' +
' <span class="mt-2">人</span>\n' +
' <div class="col-2 mt-1">\n' +
' <select class="form-control" name="competition_staffs[][category]">\n' +
' <option value="student">学生</option>\n' +
' <option value="teacher">教师</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" class="mutiple-limited-radio" value="false" checked name="competition_staffs[][mutiple_limited]" type="checkbox">\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" class="mutiple-limited-radio" value="true" name="competition_staffs[][mutiple_limited]" type="checkbox">\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();
});
$('.nav-setting-form').on('click', '.module_hidden', function(){
var checkEle = $(this);
if (checkEle.is(':checked')) {
checkEle.prev().val(0);
} else {
checkEle.prev().val(1);
}
});
$('.competition-staff-settings').on('click', '.mutiple-limited-radio', function(){
var radio = $(this);
if (radio.is(':checked')) {
radio.parent().parent().siblings().find('.mutiple-limited-radio').attr('checked', false)
} else {
radio.parent().parent().siblings().find('.mutiple-limited-radio').attr('checked', true)
}
});
var $navForm = $('form.nav-setting-form');
$navForm.on('click', ".submit-btn", function () {
$navForm.find('.submit-btn').attr('disabled', 'disabled');
$navForm.find('.error').html('');
var valid = $navForm.valid();
if (!valid) return;
$.ajax({
method: 'POST',
dataType: 'json',
url: $navForm.attr('action'),
data: new FormData($navForm[0]),
processData: false,
contentType: false,
success: function (data) {
$.notify({message: '保存成功'});
// window.location.reload();
},
error: function (res) {
var data = res.responseJSON;
$navForm.find('.error').html(data.message);
},
complete: function () {
$navForm.find('.submit-btn').attr('disabled', false);
}
});
});
// 排行榜设置
//删除小阶段
$("#large_panel").on("click",".small_panel_item_del",function () {
var list = $(this).parents(".small_panel");
$(this).parents(".small_panel_item").remove();
for(var i=0;i < $(list).find(".subName").length;i++){
console.log(i);
$(list).find(".subName").eq(i).html("第"+parseInt(i+1)+"阶段");
}
});
// $('form.stage-update-form').validate({
// errorElement: 'span',
// errorClass: 'danger text-danger',
// rules: {
// stage_name: "required",
// "stage[][start_time]": "required",
// "stage[][end_time]": "required",
// "stage[][mission_count]": {
// required: true,
// min: 1
// },
// "stage[][entry]": {
// required: true,
// min: 1
// },
// score_rate: {
// required: true,
// range: [0, 100]
// }
// },
// messages: {
// "stage[][mission_count]": {
// min: ">=1"
// },
// "stage[][entry]": {
// min: ">=1"
// },
// }
// });
$('.competition-chart-setting').on('click', ".update-stage", function () {
var updateForm = $(this).parents("form");
$(this).attr('disabled', 'disabled');
updateForm.find('.error').html('');
// var valid = updateForm.valid();
var valid = true;
var $stageName = updateForm.find('input[name="stage_name"]');
if($stageName.val() === undefined || $stageName.val().length === 0){
$stageName.addClass('danger text-danger');
valid = false;
} else {
$stageName.removeClass('danger text-danger');
}
var $scoreRate = updateForm.find('input[name="score_rate"]');
if($scoreRate.val() === undefined || $scoreRate.val().length === 0){
$scoreRate.addClass('danger text-danger');
valid = false;
} else if (parseInt($scoreRate.val()) > 100 || parseInt($scoreRate.val()) < 0) {
$scoreRate.addClass('danger text-danger');
$scoreRate.after('<span class="danger text-danger">0-100之间的数值</span>');
valid = false;
} else {
$scoreRate.removeClass('danger text-danger');
$scoreRate.siblings().remove();
}
updateForm.find('input[name="stage[][start_time]"]').each(function(_, e){
var $ele = $(e);
if($ele.val() === undefined || $ele.val().length === 0){
$ele.addClass('danger text-danger');
valid = false;
} else {
$ele.removeClass('danger text-danger');
}
});
updateForm.find('input[name="stage[][end_time]"]').each(function(_, e){
var $ele = $(e);
if($ele.val() === undefined || $ele.val().length === 0){
$ele.addClass('danger text-danger');
valid = false;
} else {
$ele.removeClass('danger text-danger');
}
});
updateForm.find('input[name="stage[][mission_count]"]').each(function(i, e){
var $ele = $(e);
var $entry = updateForm.find('input[name="stage[][entry]"]').eq(i);
if($ele.val() === undefined || $ele.val().length === 0){
$ele.addClass('danger text-danger');
valid = false;
} else if (parseInt($ele.val()) < 1) {
$ele.addClass('danger text-danger');
$ele.after('<span class="danger text-danger">大于等于1</span>');
valid = false;
} else if (parseInt($ele.val()) > parseInt($entry.val())) {
$ele.addClass('danger text-danger');
$ele.after('<span class="danger text-danger">不能大于总任务数</span>');
valid = false;
} else {
$ele.removeClass('danger text-danger');
$ele.siblings().remove();
}
});
updateForm.find('input[name="stage[][entry]"]').each(function(_, e){
var $ele = $(e);
if($ele.val() === undefined || $ele.val().length === 0){
$ele.addClass('danger text-danger');
valid = false;
} else if (parseInt($ele.val()) < 1) {
$ele.addClass('danger text-danger');
$ele.after('<span class="danger text-danger">大于等于1</span>');
valid = false;
} else {
$ele.removeClass('danger text-danger');
$ele.siblings().remove();
}
});
updateForm.find('input[name="stage[][identifiers][]"]').each(function(_, e){
var $ele = $(e);
if($ele.val() === undefined || $ele.val().length === 0){
$ele.addClass('danger text-danger');
valid = false;
} else {
$ele.removeClass('danger text-danger');
}
});
if (!valid) return;
updateForm.find('input[name="stage[][mission_count]"]').each(function(_, e){
var $missionCount = $(e);
var $entryCount = $(e).parents("div.row").find('input[name="stage[][mission_count]"]');
if(parseInt($missionCount.val()) > parseInt($entryCount.val()) ){
$missionCount.addClass('danger text-danger');
$missionCount.after('<span class="danger text-danger">不能大于总任务数</span>');
valid = false;
} else {
$missionCount.removeClass('danger text-danger');
$missionCount.siblings().remove();
}
});
$.ajax({
method: 'POST',
dataType: 'json',
url: updateForm.attr('action'),
data: new FormData(updateForm[0]),
processData: false,
contentType: false,
success: function (data) {
$.notify({message: '保存成功'});
window.location.reload();
},
error: function (res) {
var data = res.responseJSON;
$navForm.find('.error').html(data.message);
},
complete: function () {
$navForm.find('.submit-btn').attr('disabled', false);
}
});
});
$(".competition-chart-stages").on("click", ".add-new-tab", function () {
if($(".new-stage-form").length > 0){
alert("请先保存上一个tab");
} else {
var count = parseInt($("#large_panel").find(".large_panel_part").length)+1;
var html = '<form class="stage-update-form new-stage-form flex-1" action="/cooperative/competitions/'+$(this).attr("data-competition-id")+'/competition_stages" accept-charset="UTF-8" data-remote="true" method="post">' +
'<div class="large_panel_part" attr_line="'+count+'"><div class="row d-flex mt-3">\n' +
' <span class="col-1 mt-2">tab标题</span>\n' +
' <div class="col-2 no_padding">\n' +
' <input type="text" class="form-control" name="stage_name"/>\n' +
' </div>\n' +
' <span class="col-1 text-right mt-2 no_padding">总排行榜占比:</span>\n' +
' <div class="col-1 no_padding">\n' +
' <input type="number" class="form-control" name="score_rate" value="100"/>\n' +
' </div><span class=" mt-2">%</span>\n' +
' <div class="flex-1">\n' +
' <a href="javascript:void(0)"class="btn btn-outline-primary export-action ml20 add-task-sub">新增子阶段</a>\n' +
' </div>\n' +
' <a href="javascript:void(0)" class="btn btn-default ml20" onclick="Del_tab(this)">删除</a>\n' +
' <a href="javascript:void(0)" class="btn btn-outline-primary update-stage export-action ml20">保存</a>\n' +
' </div>\n' +
' <div id="small_panel_'+count+'" class="small_panel">\n' +
' <div class="row d-flex small_panel_item" attr_line="sub_new_new" count="1">\n' +
' <span class="mt-2 subName mr10">第1阶段</span>\n' +
' <div class="flex-1">\n' +
' <div class="row">\n' +
' <div class="row col-6"><span class="mt-2 ml20">有效时间:</span>\n' +
' <div class="col-4 no_padding">\n' +
' <input type="text" name="stage[][start_time]" id="stage__start_time" value="" autocomplete="off" class="section-start-time form-control" placeholder="有效开始时间">\n' +
' </div>\n' +
' <span class="mt-2">~</span>\n' +
' <div class="col-4 no_padding ">\n' +
' <input type="text" name="stage[][end_time]" id="stage__end_time" value="" autocomplete="off" class="section-end-time form-control" placeholder="有效结束时间">\n' +
' </div></div>\n' +
' <div class="row col-3"><span class="col-4 text-right mt-2 no_padding">总任务数:</span>\n' +
' <div class="col-6 no_padding ">\n' +
' <input type="number" class="form-control" onchange="change_total(this)" value="3" name="stage[][entry]">\n' +
' </div></div>\n' +
' <div class="row col-3"><span class="col-4 text-right mt-2 no_padding">成绩来源:</span>\n' +
' <div class="col-6 no_padding ">\n' +
' <select class="form-control" name="stage[][score_source]">\n' +
' <option value="0">经验值</option>\n' +
' <option value="1">预测准确率</option>\n' +
' </select>\n' +
' </div></div>\n' +
' </div>\n' +
' <div class="row mt-2" id="task_Input_sub_new_new">\n' +
' <div class="col-4 row task_Input_div">\n' +
' <span class="col-4 text-right mt-3 no_padding mr10">任务1</span>\n' +
' <div class="col-6 no_padding">\n' +
' <input type="text" class="form-control mt-2" name="stage[][identifiers][]" placeholder="请填写实训ID">\n' +
' </div>\n' +
' </div>\n' +
' <div class="col-4 row task_Input_div">\n' +
' <span class="col-4 text-right mt-3 no_padding mr10">任务2</span>\n' +
' <div class="col-6 no_padding">\n' +
' <input type="text" class="form-control mt-2" name="stage[][identifiers][]" placeholder="请填写实训ID">\n' +
' </div>\n' +
' </div>\n' +
' <div class="col-4 row task_Input_div">\n' +
' <span class="col-4 text-right mt-3 no_padding mr10">任务3</span>\n' +
' <div class="col-6 no_padding">\n' +
' <input type="text" class="form-control mt-2" name="stage[][identifiers][]" placeholder="请填写实训ID">\n' +
' </div>\n' +
' </div>\n' +
' </div>\n' +
' </div>\n' +
' <span>\n' +
' <a href="javascript:void(0)" class="btn btn-default ml20 small_panel_item_del">删除</a>\n' +
' </span>\n' +
' </div>\n' +
'</div></div></form>';
$("#large_panel").append(html);
$(".stage-update-form .section-start-time").datetimepicker(timeOptions);
$(".stage-update-form .section-end-time").datetimepicker(timeOptions);
}
});
//新增子阶段
$(".competition-chart-stages").on("click", ".add-task-sub", function () {
var index = $(this).parents(".large_panel_part").attr("attr_line");
var count= 0;
console.log("sdfsf");
console.log($("#small_panel_"+index).find(".small_panel_item").length > 0);
if($("#small_panel_"+index).find(".small_panel_item").length > 0){
count = parseInt($("#small_panel_"+index).find(".small_panel_item").last().attr("count")) + 1;
console.log($("#small_panel_"+index).find(".small_panel_item").last().attr("count"));
}else{
count = 1;
}
var showCount=parseInt($("#small_panel_"+index).find(".small_panel_item").length) + 1;
var html='<div class="row d-flex small_panel_item" attr_line="sub_'+index+'_'+count+'" count="'+count+'">\n' +
' <span class="mr10 mt-2 subName">第'+showCount+'阶段</span>\n' +
' <div class="flex-1">\n' +
' <div class="row">\n' +
' <div class="row col-6"><span class="mt-2 ml20 mr10">有效时间:</span>\n' +
' <div class="col-4 no_padding ">\n' +
' <input type="text" name="stage[][start_time]" id="stage__start_time" value="" autocomplete="off" class="section-start-time form-control" placeholder="有效开始时间">\n' +
' </div>\n' +
' <span class="mt-2">~</span>\n' +
' <div class="col-4 no_padding ">\n' +
' <input type="text" name="stage[][end_time]" id="stage__end_time" value="" autocomplete="off" class="section-end-time form-control" placeholder="有效结束时间">\n' +
' </div></div>\n' +
' <div class="row col-3"><span class="col-4 text-right mt-2 no_padding mr10">总任务数:</span>\n' +
' <div class="col-6 no_padding ">\n' +
' <input type="number" class="form-control" onchange="change_total(this)" value="3" name="stage[][entry]">\n' +
' </div></div>\n' +
' <div class="row col-3"><span class="col-4 mr10 text-right mt-2 no_padding">成绩来源:</span>\n' +
' <div class="col-6 no_padding ">\n' +
' <select class="form-control" name="stage[][score_source]">\n' +
' <option value="0">经验值</option>\n' +
' <option value="1">预测准确率</option>\n' +
' </select>\n' +
' </div></div>\n' +
' </div>\n' +
' <div class="row mt-2" id="task_Input_sub_'+index+'_'+count+'">\n'+
' <div class="col-4 row task_Input_div">\n' +
' <span class="col-4 text-right mt-3 no_padding mr10">任务1</span>\n' +
' <div class="col-6 no_padding">\n' +
' <input type="text" class="form-control mt-2" name="stage[][identifiers][]" placeholder="请填写实训ID">\n' +
' </div>\n' +
' </div>\n' +
' <div class="col-4 row task_Input_div">\n' +
' <span class="col-4 text-right mt-3 no_padding mr10">任务2</span>\n' +
' <div class="col-6 no_padding">\n' +
' <input type="text" class="form-control mt-2" name="stage[][identifiers][]" placeholder="请填写实训ID">\n' +
' </div>\n' +
' </div>\n' +
' <div class="col-4 row task_Input_div">\n' +
' <span class="col-4 text-right mt-3 no_padding mr10">任务3</span>\n' +
' <div class="col-6 no_padding">\n' +
' <input type="text" class="form-control mt-2" name="stage[][identifiers][]" placeholder="请填写实训ID">\n' +
' </div>\n' +
' </div>\n' +
' </div>\n' +
' </div>\n' +
' <span>\n' +
' <a href="javascript:void(0)" class="btn btn-default ml20 small_panel_item_del">删除</a>\n' +
' </span>\n' +
' </div>';
$("#small_panel_"+index).append(html);
$(".stage-update-form .section-start-time").datetimepicker(timeOptions);
$(".stage-update-form .section-end-time").datetimepicker(timeOptions);
});
// 奖项设置
var $prizeContainer = $('#competition-prize-card');
var competitionId = $prizeContainer.data('id');
$(document).on('prize.save.success', function(){
$.ajax({
method: 'GET',
url: '/cooperative/competitions/' + competitionId + '/competition_prizes',
dataType: 'script'
})
});
$('.modal.cooperative-upload-file-modal').on('upload:success', function(e, data){
var $imageElement;
if(data.suffix === '_member'){
$imageElement = $('.prize-member-image-' + data.source_id);
} else if(data.suffix === '_team'){
$imageElement = $('.prize-team-image-' + data.source_id);
} else {
$imageElement = $('.prize-teacher-image-' + data.source_id);
}
$imageElement.attr('src', data.url);
$imageElement.show();
$imageElement.next().html('重新上传');
})
// 生成获奖记录
$prizeContainer.on('click', '.generate-prize-user-action', function(){
var $link = $(this);
var generateRequest = function(){
return $.ajax({
method: 'POST',
url: '/cooperative/competitions/' + competitionId + '/competition_prize_users',
dataType: 'json',
success: function(data){
if(data && data.status === 0){
show_success_flash();
$link.remove();
} else {
showErrorNotify(data.message);
}
},
error: function(res){
var data = res.responseJSON;
showErrorNotify(data.message);
}
})
}
customConfirm({
content: '确认生成吗?',
ok: function () {
customLoading({
ajax: generateRequest
})
}
})
});
} else {
$(document).unbind('prize.save.success');
}
});
//添加主办方或者开放范围
function addSponsor(item){
var html='<div class="sponsor_label">\n' +
' <input type="hidden" value="school_id" />\n' +
' <span>caicai</span>\n' +
' <a href="javascript:void(0)" onclick="del_sponsor(this)">×</a>\n' +
' </div>';
$(item).parents(".sponsorPanel").append(html);
}
//删除
function del_sponsor(item){
$(item).parents(".sponsor_label").remove();
}
// 小阶段修改总任务数
function change_total(item) {
var count=parseInt($(item).val());
var index = $(item).parents(".small_panel_item").attr("attr_line");
var indexLarge = $(item).parents(".large_panel_part").attr("attr_line");
console.log(indexLarge);
console.log(index);
var divCount=parseInt($("#task_Input_"+index).find(".task_Input_div").length);
var html = "";
if(count > divCount){
for(var i=0;i < count-divCount ;i++){
html+='<div class="col-4 row task_Input_div"><span class="col-4 text-right mt-3 no_padding mr10">任务'+(divCount+i+1)+'</span>\n' +
'<div class="col-6 no_padding">\n' +
'<input type="text" class="form-control mt-2" name="stage[][identifiers][]" placeholder="请填写实训ID">\n' +
'</div>\n' +
'</div>';
}
$("#task_Input_"+index).append(html);
}else{
var delCount = divCount - count ;
console.log(divCount);
console.log(count);
var _max=parseInt($("#task_Input_"+index).find(".task_Input_div:last").index());
console.log(_max);
var _get= _max - delCount;
console.log(_get);
if(count == 0){
$("#task_Input_"+index).empty();
}else{
$("#task_Input_"+index).find(".task_Input_div:gt("+_get+")").remove();
}
}
}
//删除tab
function Del_tab(item) {
$(item).parents(".large_panel_part").remove();
}
//新增tab
function addNewTab(competition_id) {
if($(".new-stage-form").length > 0){
alert("请先保存上一个tab");
} else {
var count = parseInt($("#large_panel").find(".large_panel_part").length)+1;
var html = '<form class="stage-update-form new-stage-form flex-1" action="/cooperative/competitions/'+competition_id+'/competition_stages" accept-charset="UTF-8" data-remote="true" method="post">' +
'<div class="large_panel_part" attr_line="'+count+'"><div class="row d-flex mt-3">\n' +
' <span class="col-1 mt-2">tab标题</span>\n' +
' <div class="col-2 no_padding">\n' +
' <input type="text" class="form-control" name="stage_name"/>\n' +
' </div>\n' +
' <span class="col-1 text-right mt-2 no_padding">总排行榜占比:</span>\n' +
' <div class="col-1 no_padding">\n' +
' <input type="number" class="form-control" name="score_rate" value="100"/>\n' +
' </div><span class=" mt-2">%</span>\n' +
' <div class="flex-1">\n' +
' <a href="javascript:void(0)"class="btn btn-outline-primary export-action ml20 add-task-sub">新增子阶段</a>\n' +
' </div>\n' +
' <a href="javascript:void(0)" class="btn btn-default ml20" onclick="Del_tab(this)">删除</a>\n' +
' <a href="javascript:void(0)" class="btn btn-outline-primary update-stage export-action ml20">保存</a>\n' +
' </div>\n' +
' <div id="small_panel_'+count+'" class="small_panel">\n' +
' <div class="row d-flex small_panel_item" attr_line="sub_new_new" count="1">\n' +
' <span class="mt-2 subName mr10">第1阶段</span>\n' +
' <div class="flex-1">\n' +
' <div class="row">\n' +
' <div class="row col-6"><span class="mt-2 ml20 mr10">有效时间:</span>\n' +
' <div class="col-4 no_padding ">\n' +
' <input type="text" name="stage[][start_time]" id="stage__start_time" value="" autocomplete="off" class="section-start-time form-control" placeholder="有效开始时间">\n' +
' </div>\n' +
' <span class="mt-2">~</span>\n' +
' <div class="col-4 no_padding input_middle">\n' +
' <input type="text" name="stage[][end_time]" id="stage__end_time" value="" autocomplete="off" class="section-end-time form-control" placeholder="有效结束时间">\n' +
' </div></div>\n' +
' <div class="row col-3"><span class="col-4 text-right mt-2 no_padding mr10">总任务数:</span>\n' +
' <div class="col-6 no_padding ">\n' +
' <input type="number" class="form-control" onchange="change_total(this)" value="3" name="stage[][entry]">\n' +
' </div></div>\n' +
' <div class="row col-3"><span class="col-4 text-right mt-2 no_padding mr10">成绩来源:</span>\n' +
' <div class="col-6 no_padding ">\n' +
' <select class="form-control" name="stage[][score_source]">\n' +
' <option value="0">经验值</option>\n' +
' <option value="1">预测准确率</option>\n' +
' </select>\n' +
' </div></div>\n' +
' </div>\n' +
' <div class="row mt-2" id="task_Input_sub_new_new">\n' +
' <div class="col-4 row task_Input_div">\n' +
' <span class="col-3 text-right mt-3 no_padding mr10">任务1</span>\n' +
' <div class="col-8 no_padding">\n' +
' <input type="text" class="form-control mt-2" name="stage[][identifiers][]" placeholder="请填写实训ID">\n' +
' </div>\n' +
' </div>\n' +
' <div class="col-4 row task_Input_div">\n' +
' <span class="col-3 text-right mt-3 no_padding mr10">任务2</span>\n' +
' <div class="col-8 no_padding">\n' +
' <input type="text" class="form-control mt-2" name="stage[][identifiers][]" placeholder="请填写实训ID">\n' +
' </div>\n' +
' </div>\n' +
' <div class="col-4 row task_Input_div">\n' +
' <span class="col-3 text-right no_padding mr10 mt-3">任务3</span>\n' +
' <div class="col-8 no_padding">\n' +
' <input type="text" class="form-control mt-2" name="stage[][identifiers][]" placeholder="请填写实训ID">\n' +
' </div>\n' +
' </div>\n' +
' </div>\n' +
' </div>\n' +
' <span>\n' +
' <a href="javascript:void(0)" class="btn btn-default ml20 small_panel_item_del">删除</a>\n' +
' </span>\n' +
' </div>\n' +
'</div></div></form>';
$("#large_panel").append(html);
}
}

@ -0,0 +1,148 @@
$(document).on('turbolinks:load', function() {
if ($('body.cooperative-competitions-index-page').length > 0) {
$('.modal.cooperative-upload-file-modal').on('upload:success', function(e, data){
var $imageElement = $('.competition-image-' + data.source_id);
$imageElement.attr('src', data.url);
$imageElement.show();
$imageElement.next().html('重新上传');
});
}
$(".cooperative-competition-list-form").on("change", '.competitions-hot-select', function () {
var s_value = $(this).get(0).checked ? 1 : 0;
var json = {};
json["hot"] = s_value;
$.ajax({
url: "/cooperative/competitions/hot_setting",
type: "POST",
dataType:'json',
data: json,
success: function(){
$.notify({ message: '操作成功' });
}
});
});
// ============== 新增竞赛 ===============
var $modal = $('.modal.cooperative-create-competition-modal');
var $form = $modal.find('form.cooperative-create-competition-form');
var $competitionNameInput = $form.find('input[name="competition_name"]');
$form.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
competition_name: {
required: true
}
}
});
// modal ready fire
$modal.on('show.bs.modal', function () {
$competitionNameInput.val('');
});
$modal.on('click', '.submit-btn', function(){
$form.find('.error').html('');
if ($form.valid()) {
var url = $form.data('url');
$.ajax({
method: 'POST',
dataType: 'json',
url: url,
data: $form.serialize(),
success: function(){
$.notify({ message: '创建成功' });
$modal.modal('hide');
setTimeout(function(){
window.location.reload();
}, 500);
},
error: function(res){
var data = res.responseJSON;
$form.find('.error').html(data.message);
}
});
}
});
// 导入学生
var $importScoreModal = $('.modal.cooperative-import-competition-score-modal');
var $importScoreForm = $importScoreModal.find('form.cooperative-import-competition-score-form');
var $competitionIdInput = $importScoreForm.find('input[name="competition_id"]');
$importScoreModal.on('show.bs.modal', function(event){
resetFileInputFunc($importScoreModal.find('.upload-file-input'));
$importScoreModal.find('.file-names').html('选择文件');
$importScoreModal.find('.upload-file-input').trigger('click');
var $link = $(event.relatedTarget);
var competitionId = $link.data('competition-id');
$competitionIdInput.val(competitionId);
});
$importScoreModal.on('change', '.upload-file-input', function(e){
var file = $(this)[0].files[0];
$importScoreModal.find('.file-names').html(file ? file.name : '请选择文件');
});
var importUserFormValid = function(){
if($importScoreForm.find('input[name="file"]').val() == undefined || $importScoreForm.find('input[name="file"]').val().length == 0){
$importScoreForm.find('.error').html('请选择文件');
return false;
}
return true;
};
var buildResultMessage = function(data){
var messageHtml = "<div>导入结果:成功" + data.success + "条,失败"+ data.fail.length + "条</div>";
if(data.fail.length > 0){
messageHtml += '<table class="table"><thead class="thead-light"><tr><th>数据</th><th>失败原因</th></tr></thead><tbody>';
data.fail.forEach(function(item){
messageHtml += '<tr><td>' + item.data + '</td><td>' + item.message + '</td></tr>';
});
messageHtml += '</tbody></table>'
}
return messageHtml;
};
$importScoreModal.on('click', '.submit-btn', function(){
$importScoreForm.find('.error').html('');
if (importUserFormValid()) {
$('body').mLoading({ text: '正在导入...' });
$.ajax({
method: 'POST',
dataType: 'json',
url: '/cooperative/import_competition_scores',
data: new FormData($importScoreForm[0]),
processData: false,
contentType: false,
success: function(data){
$('body').mLoading('destroy');
$importScoreModal.modal('hide');
showMessageModal(buildResultMessage(data), function(){
window.location.reload();
});
},
error: function(res){
$('body').mLoading('destroy');
var data = res.responseJSON;
$importScoreForm.find('.error').html(data.message);
}
});
}
});
});

@ -0,0 +1,9 @@
$(document).on('turbolinks:load', function() {
if($('body.cooperative-enroll-lists-index-page').length > 0){
var search_form = $(".search-form");
//导出
$(".competition-enroll-list-form").on("click","#enroll-lists-export",function () {
window.location.href = "/cooperative/competitions/"+$(this).attr("data-competition-id")+"/enroll_lists/export.xlsx?" + search_form.serialize();
});
}
});

@ -3,7 +3,7 @@ $(document).on('turbolinks:load', function() {
var $container = $('.edit-laboratory-setting-container');
var $form = $container.find('.edit_laboratory');
$('.logo-item-left').on("change", 'input[type="file"]', function () {
$('.logo-item-left, .banner-item-bottom').on("change", 'input[type="file"]', function () {
var $fileInput = $(this);
var file = this.files[0];
var imageType = /image.*/;

@ -16,7 +16,7 @@ $(document).on('turbolinks:load', function() {
minimumInputLength: 1,
ajax: {
delay: 500,
url: '/cooperative/users',
url: '/cooperative/users/for_select',
dataType: 'json',
data: function(params){
return { name: params.term };

@ -0,0 +1,2 @@
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

@ -0,0 +1,2 @@
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

@ -96,5 +96,76 @@
font-size: 14px;
}
}
.banner-item {
margin-bottom: 15px;
display: flex;
flex-direction: column;
&-img {
display: block;
width: 300px;
height: 80px;
background: #f0f0f0;
}
&-upload {
cursor: pointer;
position: absolute;
top: 0;
width: 300px;
height: 80px;
background: #F5F5F5;
border: 1px solid #E5E5E5;
&::before {
content: '';
position: absolute;
top: 27px;
left: 149px;
width: 2px;
height: 26px;
background: #E5E5E5;
}
&::after {
content: '';
position: absolute;
top: 39px;
left: 137px;
width: 26px;
height: 2px;
background: #E5E5E5;
}
}
&-top {
margin-bottom: 10px;
}
&-bottom {
position: relative;
width: 300px;
height: 80px;
&.has-img {
.banner-item-upload {
display: none;
}
&:hover {
.banner-item-upload {
display: block;
background: rgba(145, 145, 145, 0.8);
}
}
}
}
&-title {
color: #23272B;
font-size: 14px;
}
}
}
}

@ -86,8 +86,11 @@
align-items: center;
& > img {
width: 40px;
height: auto;
//width: 40px;
//height: auto;
max-width: 130px !important;
max-height: 40px !important;
overflow: hidden;
}
& > .logo-label {

@ -7,6 +7,7 @@
@import "bootstrap-datepicker.standalone";
@import "jquery.mloading";
@import "jquery-confirm.min";
@import "bootstrap-datetimepicker.min";
@import "codemirror/lib/codemirror";
@import "editormd/css/editormd.min";

@ -0,0 +1,116 @@
.cooperative-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: 20px;
}
.sponsor_label{
border:1px solid #4CACFF;
border-radius: 5px;
background-color: rgba(76,172,255,0.3);
color: #333;
padding:0px 4px;
height: 30px;
line-height: 30px;
float: left;
margin: 4px 5px;
span{
display: block;
float: left;
height: 28px;
line-height: 28px;
margin-right: 5px;
}
a{
font-size: 18px;
float: left;
height: 28px;
line-height: 28px;
}
}
.large_panel{
padding:0px 15px;
.large_panel_part{
border-top: 1px solid #eaeaea;
}
.large_panel_part:first-child{
border:none;
}
.large_panel_part >.row ,.small_panel >.row{
border-bottom: 1px solid #eaeaea;
padding:20px 0px;
}
.small_panel{
margin-left: 20px;
}
.row:last-child{
border:none;
}
.task_Input_div:nth-child(3n-2) > span.col-4{
flex: 0 0 81px;
max-width: 81px;
}
.task_Input_div:nth-child(3n-2){
flex: 0 0 50%;
max-width: 50%;
}
.task_Input_div:nth-child(3n-1){
flex: 0 0 25%;
max-width: 25%;
}
.task_Input_div:nth-child(3n){
flex: 0 0 25%;
max-width: 25%;
}
.task_Input_div:nth-child(3n) > span.col-4{
flex: 0 0 33.3%;
max-width: 33.3%;
}
.task_Input_div:nth-child(3n) > div.col-6{
flex: 0 0 50%;
max-width: 50%;
}
}
}

@ -72,5 +72,76 @@
font-size: 14px;
}
}
.banner-item {
margin-bottom: 15px;
display: flex;
flex-direction: column;
&-img {
display: block;
width: 300px;
height: 80px;
background: #f0f0f0;
}
&-upload {
cursor: pointer;
position: absolute;
top: 0;
width: 300px;
height: 80px;
background: #F5F5F5;
border: 1px solid #E5E5E5;
&::before {
content: '';
position: absolute;
top: 27px;
left: 149px;
width: 2px;
height: 26px;
background: #E5E5E5;
}
&::after {
content: '';
position: absolute;
top: 39px;
left: 137px;
width: 26px;
height: 2px;
background: #E5E5E5;
}
}
&-top {
margin-bottom: 10px;
}
&-bottom {
position: relative;
width: 300px;
height: 80px;
&.has-img {
.banner-item-upload {
display: none;
}
&:hover {
.banner-item-upload {
display: block;
background: rgba(145, 145, 145, 0.8);
}
}
}
}
&-title {
color: #23272B;
font-size: 14px;
}
}
}
}

@ -86,8 +86,11 @@
align-items: center;
& > img {
width: 40px;
height: auto;
//width: 40px;
//height: auto;
max-width: 130px !important;
max-height: 40px !important;
overflow: hidden;
}
& > .logo-label {

@ -0,0 +1,3 @@
// Place all the styles related to the HackUserLastestCodes controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

@ -0,0 +1,3 @@
// Place all the styles related to the hacks controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

@ -6,7 +6,6 @@ class Admins::BaseController < ApplicationController
layout 'admin'
skip_before_action :verify_authenticity_token
skip_before_action :setup_laboratory
before_action :require_login, :require_admin!
@ -38,4 +37,9 @@ class Admins::BaseController < ApplicationController
append_js = ERB.new(File.open(path).read).result
response.body += append_js
end
# 重写此方法,防止影响超级管理员端云上实验室功能,因为那里重写了:current_laboratory方法
def setup_laboratory
Laboratory.current = Laboratory.find_by_subdomain(request.subdomain) || Laboratory.find(1)
end
end

@ -15,6 +15,9 @@ class Admins::LaboratorySettingsController < Admins::BaseController
end
def form_params
params.permit(:identifier, :name, :nav_logo, :login_logo, :tab_logo, :footer, navbar: %i[name link hidden])
params.permit(:identifier, :name,
:nav_logo, :login_logo, :tab_logo,
:subject_banner, :course_banner, :competition_banner, :moop_cases_banner,
:footer, navbar: %i[name link hidden])
end
end

@ -9,15 +9,8 @@ class Admins::LaboratorySubjectsController < Admins::BaseController
end
def create
subject_ids = Array.wrap(params[:subject_ids])
subject_ids = Subject.where(id: subject_ids).pluck(:id)
exist_subject_id = current_laboratory.laboratory_subjects.where(subject_id: subject_ids).pluck(:subject_id)
LaboratorySubject.bulk_insert(*%i[subject_id laboratory_id created_at updated_at]) do |worker|
(subject_ids - exist_subject_id).each do |subject_id|
worker.add(subject_id: subject_id, laboratory_id: current_laboratory.id)
end
end
subject = Subject.find(params[:subject_id])
Subjects::CopySubjectService.call(subject, current_user, current_laboratory)
render_ok
end

@ -395,6 +395,50 @@ class ApplicationController < ActionController::Base
end
end
# 处理返回非0就报错的请求
def interface_post(uri, params, status, message)
begin
uid_logger_dubug("--uri_exec: params is #{params}, url is #{uri}")
uri = URI.parse(URI.encode(uri.strip))
res = Net::HTTP.post_form(uri, params).body
uid_logger_dubug("--uri_exec: .....res is #{res}")
res = JSON.parse(res)
if (res && res['code'] != 0)
tip_exception(status, message)
else
res
end
rescue Exception => e
uid_logger("--uri_exec: exception #{e.message}")
raise Educoder::TipException.new("实训平台繁忙繁忙等级84")
end
end
# json格式请求
def interface_json_post(uri, params, status, message)
begin
uid_logger_dubug("--uri_exec: params is #{params}, url is #{uri}")
uri = URI.parse(URI.encode(uri.strip))
res = Net::HTTP.start(uri.host, uri.port) do |http|
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req.body = params.to_json
http.request(req)
end
uid_logger_dubug("--uri_exec: .....res is #{res.body}")
res = JSON.parse(res.body)
if (res && res['code'] != 0)
tip_exception(status, message)
else
res
end
rescue Exception => e
uid_logger("--uri_exec: exception #{e.message}")
raise Educoder::TipException.new("服务器繁忙")
end
end
# 适用与已经用url_safe编码后回调字符串形式
def tran_base64_decode64(str)
s_size = str.size % 4

@ -9,7 +9,8 @@ class Competitions::CompetitionsController < Competitions::BaseController
def index
# 已上架 或者 即将上架
competitions = Competition.where(status: true).or(Competition.where.not(published_at: nil))
competitions = current_laboratory.competitions
competitions = competitions.where(status: true).or(competitions.where.not(published_at: nil))
competitions =
case params[:category]
@ -142,7 +143,7 @@ class Competitions::CompetitionsController < Competitions::BaseController
}
end
else
@records = @records.includes(:team_members, competition_prize_users: :competition_prize, user: :user_extension).limit(@competition.awards_count)
@records = @records.includes(:team_members, competition_prize_users: :competition_prize, user: :user_extension).limit(@competition.charts_count)
end
end
@ -169,7 +170,7 @@ class Competitions::CompetitionsController < Competitions::BaseController
def allow_visit
return if current_competition.published? || admin_or_business?
return if current_competition.nearly_published? && current_competition.manager?(current_user)
return if current_competition.manager?(current_user)
render_forbidden
end
@ -181,7 +182,7 @@ class Competitions::CompetitionsController < Competitions::BaseController
def check_manager_permission!
return if current_user.admin_or_business?
return if current_competition.nearly_published? && current_competition.manager?(current_user)
return if current_competition.manager?(current_user)
render_forbidden
end

@ -18,6 +18,7 @@ module Base::ErrorRescueHandler
render_unprocessable_entity(ex.model.errors.full_messages.join(','))
end
rescue_from ActiveRecord::RecordInvalid do |ex|
ex.backtrace.each { |msg| Rails.logger.error(msg) }
render_unprocessable_entity(ex.record.errors.full_messages.join(','))
end
end

@ -50,7 +50,7 @@ module GitHelper
raise Educoder::TipException.new("fork源路径为空,fork失败!") if original_rep_path.blank?
# 将要生成的仓库名字
new_repo_name = "#{username.try(:strip)}/#{container.try(:identifier)}#{ Time.now.strftime("%Y%m%d%H%M%S")}"
uid_logger("start fork container: repo_name is #{new_repo_name}")
# uid_logger("start fork container: repo_name is #{new_repo_name}")
GitService.fork_repository(repo_path: original_rep_path, fork_repository_path: (new_repo_name + ".git"))
container.update_attributes!(:repo_name => new_repo_name)
end

@ -17,6 +17,7 @@ class Cooperative::BaseController < ApplicationController
def current_laboratory
@_current_laboratory ||= Laboratory.find_by_subdomain(request.subdomain)
# @_current_laboratory ||= Laboratory.find 1
end
def current_setting_or_default(name)

@ -0,0 +1,51 @@
class Cooperative::CompetitionPrizeUsersController < Cooperative::BaseController
def index
@competition = current_competition
prize_users = Admins::CompetitionPrizeUserQuery.call(params.merge(competition_id: current_competition.id))
include_class = [:competition_team, :competition_prize, :approver,
user: [:process_real_name_apply, :process_professional_apply, user_extension: :school]]
@prize_users = paginate(prize_users.preload(include_class))
respond_to do |format|
format.js
format.html
format.xlsx do
@all_prize_users = prize_users
filename = "#{@competition.name}竞赛获奖人信息列表_#{Time.current.strftime('%Y%m%d%H%M%S')}.xlsx"
render xlsx: 'index', filename: filename
end
end
end
def create
Admins::CreateCompetitionPrizeUsersService.call(current_competition)
render_ok
rescue ApplicationService::Error => ex
render_error(ex.message)
end
def approve
Admins::ApproveCompetitionPrizeUserService.call(current_prize_user, current_user)
@prize_user = current_prize_user
rescue ApplicationService::Error => ex
render_js_error(ex.message, type: :notify)
end
def unapprove
Admins::UnapproveCompetitionPrizeUserService.call(current_prize_user, current_user)
@prize_user = current_prize_user
rescue ApplicationService::Error => ex
render_js_error(ex.message, type: :notify)
end
private
def current_prize_user
@_current_prize_user ||= current_competition.competition_prize_users.find(params[:id])
end
def current_competition
@_current_competition ||= current_laboratory.competitions.find(params[:competition_id])
end
end

@ -0,0 +1,43 @@
class Cooperative::CompetitionPrizesController < Cooperative::BaseController
def index
@competition = current_competition
end
def new
@prize = current_competition.competition_prizes.new
end
def create
@prize = current_competition.competition_prizes.create!(save_params)
render_ok
end
def edit
@prize = current_competition_prize
end
def update
current_competition_prize.update!(save_params)
render_ok
end
def destroy
current_competition_prize.destroy!
render_delete_success
end
private
def current_competition_prize
@_current_competition_prize ||= current_competition.competition_prizes.find(params[:id])
end
def current_competition
@_current_competition ||= current_laboratory.competitions.find(params[:competition_id])
end
def save_params
params.require(:competition_prize).permit(:name, :category, :num)
end
end

@ -0,0 +1,33 @@
class Cooperative::CompetitionSettingsController < Cooperative::BaseController
def index
@competition = current_competition
end
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
private
def current_competition
@_current_competition ||= current_laboratory.competitions.find(params[:competition_id])
end
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, sponsor_schools: [], region_schools: [], manager_ids: [])
end
def nav_form_params
params.permit(:enroll_end_time,
competition_staffs: %i[category minimum maximum mutiple_limited],
navbar: %i[module_type module_id name hidden position url])
end
end

@ -0,0 +1,194 @@
class Cooperative::CompetitionStagesController < Cooperative::BaseController
def create
Admins::CompetitionStageCreateService.call(current_competition, update_form_params)
render_ok
end
def update
Admins::CompetitionStageUpdateService.call(current_competition, update_form_params, current_stage)
render_ok
end
def destroy
current_stage.destroy!
end
def calculate_stage_score
if current_stage.max_end_time && current_stage.max_end_time < Time.now
ActiveRecord::Base.transaction do
begin
current_stage.competition_scores.destroy_all
if current_stage.competition_stage_sections.size > 0
# 是否只有老师参赛
only_teacher = current_competition.member_staff.blank?
stage_sections = current_stage.competition_stage_sections.includes(:competition_entries)
shixuns = Shixun.where(identifier: current_stage.competition_entries.pluck(:shixun_identifier).reject(&:blank?))
challenges = Challenge.where(shixun_id: shixuns.pluck(:id))
# 计算每个战队的成绩
current_competition.competition_teams.each do |team|
totoal_score = 0
total_time = 0
# 只有老师参赛则计算所有参赛队员的成绩,否则只计算学生的成绩
user_ids = only_teacher ? team.team_members.pluck(:user_id) : team.team_members.where(is_teacher: 0).pluck(:user_id)
stage_sections.each do |section|
shixun_identifiers = section.competition_entries.pluck(:shixun_identifier).reject(&:blank?)
shixun_ids = shixuns.select{ |shixun| shixun_identifiers.include?(shixun.identifier) }.map(&:id)
section_challenges = challenges.select{ |challenge| shixun_ids.include?(challenge.shixun_id) }
if section.score_source == 0
result1 = chart_stage_score user_ids, section.start_time, section.end_time, section_challenges, section_challenges.length/shixun_ids.length
else
result1 = chart_stage_rate user_ids, section.start_time, section.end_time, section_challenges, section_challenges.length/shixun_ids.length
end
score = result1[0]
time = result1[1]
totoal_score += score
total_time += time
end
# 比赛已截止且未有分数纪录 则创建
unless team.competition_scores.exists?(competition_id: current_competition.id, competition_stage_id: current_stage.id)
CompetitionScore.create!(user_id: team.user_id, competition_team_id: team.id, competition_id: current_competition.id,
competition_stage_id: current_stage.id, score: totoal_score, cost_time: total_time)
end
end
# 如果计算的是最后一个阶段,则同时计算总成绩(只有一个阶段则不需计算)
if current_stage.max_end_time == current_competition.max_stage_end_time && current_competition.competition_stages.size > 1
calculate_total_score current_competition
end
end
rescue Exception => e
uid_logger_error(e.message)
@message = "#{e.message}"
end
end
@message = "计算成功"
else
@message = "#{current_stage.name}还未结束"
end
end
def send_message
if current_stage.max_end_time && current_stage.max_end_time > Time.now
User.where(id: TeamMember.where(competition_team_id: current_competition.competition_teams.pluck(:id)).pluck(:user_id).uniq).each do |user|
name = current_competition.name + "#{current_competition.sub_title}#{stage.name}"
begin
if user.phone.present?
Educoder::Sms.send(mobile: user.phone.to_s, code: '1', send_type:'competition_start', user_name:user.show_name,
name:name, result:section.start_time.strftime('%Y-%m-%d %H:%M:%S'))
end
rescue => e
logger_error(e)
render_error("发送验证码出错")
end
end
else
render_error("#{current_stage.name}已结束")
end
end
private
def current_competition
@_current_competition ||= current_laboratory.competitions.find(params[:competition_id])
end
def current_stage
@_current_stage ||= CompetitionStage.find_by!(competition_id: params[:competition_id], id: params[:id])
end
def update_form_params
params.permit(:stage_name, :score_rate, stage: [:start_time, :end_time, :entry, :score_source, identifiers: []])
end
# challenges 每阶段的所有关卡
# s_time 阶段开始时间 e_time 阶段结束时间
# rate 关卡经验值与分数的比值
# challenge_count 每个实训的关卡数
# 对三个实训的所有关卡循环: 找到在比赛时间内通关的最低耗时
def chart_stage_score user_ids, s_time, e_time, challenges, challenge_count
total_score = 0
total_time = 0
length = challenge_count #每个实训的关卡数
for i in 1..length
score = 0
time = 0
challenges.select{|challenge| challenge.position == i}.each do |challenge|
Game.where(:challenge_id => challenge.id, :user_id => user_ids, :status => 2).select{|game| game.open_time >= s_time && game.end_time <= e_time }.each do |game|
game_score = challenge.score
cost_time = (game.end_time.to_i - s_time.to_i) > 0 ? (game.end_time.to_i - s_time.to_i) : 0
if score < game_score
score = game_score
time = cost_time
elsif score == game_score
time = cost_time > time ? time : cost_time
end
end
end
total_score += score
total_time += time
end
return [total_score, total_time]
end
def chart_stage_rate user_ids, s_time, e_time, challenges, challenge_count
# 第三阶段的得分和耗时
total_score = 0
total_time = 0
length = challenge_count #每个实训的关卡数
for i in 1..length
score3 = 0
time3 = 0
challenges.select{|challenge| challenge.position == i}.each do |challenge|
Game.where(:challenge_id => challenge.id, :user_id => user_ids, :status => 2).select{|game| game.open_time >= s_time && game.end_time <= e_time }.each do |game|
outputs = game.outputs.select{|output| !output.text_scor.nil? && output.created_at <= e_time }
if outputs.present?
outputs = outputs.sort { |a, b| b[:text_scor].to_f <=> a[:text_scor].to_f }
myshixun_score = outputs.first.text_scor.to_f
myshixun_time = outputs.first.created_at.to_i - s_time.to_i
if score3 < myshixun_score
score3 = myshixun_score
time3 = myshixun_time
elsif score3 == myshixun_score
time3 = myshixun_time > time3 ? time3 : myshixun_time
end
end
end
end
total_score += score3
total_time += time3
end
return [total_score, total_time]
end
def calculate_total_score competition
competition.competition_scores.where(competition_stage_id: 0).destroy_all
competition.competition_teams.each do |team|
total_score = 0
total_time = 0
competition.competition_stages.where("score_rate > 0").each do |stage|
stage_score = team.competition_scores.where(competition_stage_id: stage.id).take
total_score += stage_score.try(:score).to_f * stage.score_rate
total_time += stage_score.try(:cost_time).to_i
end
unless team.competition_scores.exists?(competition_id: competition.id, competition_stage_id: 0)
CompetitionScore.create!(user_id: team.user_id, competition_team_id: team.id, competition_id: competition.id,
competition_stage_id: 0, score: total_score, cost_time: total_time)
end
end
end
end

@ -0,0 +1,57 @@
class Cooperative::CompetitionsController < Cooperative::BaseController
before_action :find_competition, except: [:index]
def index
# params[:sort_by] = params[:sort_by].presence || 'created_at'
# params[:sort_direction] = params[:sort_direction].presence || 'desc'
@competitions = current_laboratory.competitions.order("created_at desc")
@params_page = params[:page] || 1
@competitions = paginate @competitions
ids = @competitions.map(&:id)
@member_count_map = TeamMember.where(competition_id: ids).group(:competition_id).count
@competition_hot = ModuleSetting.exists?(module_type: "Competition", property: "hot")
respond_to do |format|
format.js
format.html
end
end
def create
name = params[:competition_name].to_s.strip
Competition.create!(name: name)
render_ok
end
def hot_setting
if params[:hot].to_i == 1 && !ModuleSetting.exists?(module_type: "Competition", property: "hot")
ModuleSetting.create!(module_type: "Competition", property: "hot")
elsif params[:hot].to_i == 0 && ModuleSetting.exists?(module_type: "Competition", property: "hot")
ModuleSetting.where(module_type: "Competition", property: "hot").destroy_all
end
render_ok
end
def publish
@competition.update_attributes!(published_at: Time.now)
end
def unpublish
@competition.update_attributes!(published_at: nil)
end
def online_switch
if @competition.status
@competition.update_attributes!(status: false)
else
@competition.update_attributes!(status: true, online_time: Time.now)
end
end
private
def find_competition
@competition = current_laboratory.competitions.find_by(id: params[:id])
end
end

@ -0,0 +1,33 @@
class Cooperative::EnrollListsController < Cooperative::BaseController
def index
@competition = current_competition
default_sort('created_at', 'desc')
enroll_lists = Admins::CompetitionEnrollListQuery.call(@competition, params)
@params_page = params[:page] || 1
@enroll_lists = paginate enroll_lists.preload(competition_team: [:user, :teachers], user: { user_extension: :school })
@personal = @competition.personal?
respond_to do |format|
format.js
format.html
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 ||= current_laboratory.competitions.find(params[:competition_id])
end
end

@ -9,6 +9,9 @@ class Cooperative::LaboratorySettingsController < Cooperative::BaseController
end
def form_params
params.permit(:identifier, :name, :nav_logo, :login_logo, :tab_logo, :footer, navbar: %i[name link hidden])
params.permit(:identifier, :name,
:nav_logo, :login_logo, :tab_logo,
:subject_banner, :course_banner, :competition_banner, :moop_cases_banner,
:footer, navbar: %i[name link hidden])
end
end

@ -1,7 +1,13 @@
class Cooperative::UsersController < Cooperative::BaseController
def index
params[:sort_by] = params[:sort_by].presence || 'created_on'
params[:sort_direction] = params[:sort_direction].presence || 'desc'
default_sort('created_on', 'desc')
users = Admins::UserQuery.call(search_params.merge(laboratory_id: current_laboratory.id))
@users = paginate users.includes(user_extension: :school)
end
def for_select
default_sort('created_on', 'desc')
users = Admins::UserQuery.call(search_params)
@users = paginate users.includes(user_extension: :school)
@ -10,6 +16,6 @@ class Cooperative::UsersController < Cooperative::BaseController
private
def search_params
params.permit(:name, :sort_by, :sort_direction)
params.permit(:identity, :name, :keyword, :school_name, :sort_by, :sort_direction)
end
end

@ -61,12 +61,13 @@ class CoursesController < ApplicationController
@user = current_user
# 根据分类查询课堂(全部,我的,最新,最热)
@order = params[:order].present? ? params[:order] : "all"
@courses = current_laboratory.courses.not_deleted
if @order == "visits"
order_str = "courses.id = 1309 DESC, courses.visits DESC"
@courses = Course.where(is_delete: 0, is_hidden: 0)
@courses = @courses.where(is_hidden: 0)
else
order_str = "courses.id = 1309 DESC, courses.homepage_show DESC, courses.created_at desc"
@courses = Course.where(is_delete: 0, is_hidden: 0, is_end: 0)
@courses = @courses.where(is_hidden: 0, is_end: 0)
end
# 金课未开课的不显示在首页
@ -467,6 +468,7 @@ class CoursesController < ApplicationController
# @users = User.where.not(id: user_ids_of_course_members)
@users = User.where(status: User::STATUS_ACTIVE)
@users = @users.where(laboratory_id: current_laboratory.id) unless current_laboratory.main_site?
@users = @users.where("concat(users.lastname, users.firstname) like '%#{name}%'") if name.present?
# REDO:Extension
@users = @users.joins(user_extension: :school).where("schools.name like '%#{school_name}%'") if school_name.present?
@ -631,6 +633,9 @@ class CoursesController < ApplicationController
teacher_member = course_members.where(role: %i[CREATOR]).take
elsif (params[:roles].include?("PROFESSOR") || params[:roles].include?("ASSISTANT_PROFESSOR")) && !course_members.exists?(role: %i[PROFESSOR ASSISTANT_PROFESSOR])
teacher_member = CourseMember.create!(course_id: @course.id, user_id: params[:user_id], role: params[:roles].include?("PROFESSOR") ? 2 : 3)
# 如果有未审批的申请教师/助教的记录,则修改状态为已审批
apply_teacher = CourseMessage.where(course_id: @course.id, course_message_id: params[:user_id], status: 0).last
apply_teacher.update!(status: 1, apply_user_id: current_user.id)
elsif course_members.exists?(role: %i[PROFESSOR ASSISTANT_PROFESSOR])
teacher_member = course_members.where(role: %i[PROFESSOR ASSISTANT_PROFESSOR]).take
if params[:roles].include?("PROFESSOR") || params[:roles].include?("ASSISTANT_PROFESSOR")
@ -794,18 +799,15 @@ class CoursesController < ApplicationController
if approval == 1
course_message.pass!
new_teacher = CourseMember.new(course_id: @course.id, user_id: applier_user.id)
content = course_message.content.to_i
if content == 3 || content == 7
tip_exception("已存在教师/助教身份") if CourseMember.where(course_id: @course.id, user_id: applier_user.id, role: 3).any?
new_teacher.ASSISTANT_PROFESSOR!
elsif content == 2 || content == 9
tip_exception("已存在教师/助教身份") if CourseMember.where(course_id: @course.id, user_id: applier_user.id, role: 2).any?
new_teacher.PROFESSOR!
role = (content == 3 || content == 7) ? 3 : ((content == 2 || content == 9) ? 2 : nil)
if role
tip_exception("已存在教师/助教身份") if CourseMember.where(course_id: @course.id, user_id: applier_user.id, role: [1, 2 ,3]).any?
else
tip_exception("申请角色错误")
end
new_teacher = CourseMember.new(course_id: @course.id, user_id: applier_user.id, role: role)
new_teacher.save!
# 课堂管理员才有分配权限且课堂分班数大于0
@ -828,7 +830,7 @@ class CoursesController < ApplicationController
normal_status(0, "操作成功")
rescue => e
uid_logger_error(e.message)
tip_exception("操作失败")
tip_exception(e.message)
raise ActiveRecord::Rollback
end
end
@ -1083,6 +1085,7 @@ class CoursesController < ApplicationController
# REDO:Extension
@users = User.where(status: User::STATUS_ACTIVE)
@users = @users.where(laboratory_id: current_laboratory.id) unless current_laboratory.main_site?
@users = @users.where("concat(users.lastname, users.firstname) like '%#{name}%'") if name.present?
# REDO:Extension
@users = @users.joins(user_extension: :school).where("schools.name like '%#{school_name}%'") if school_name.present?
@ -1102,12 +1105,12 @@ class CoursesController < ApplicationController
# 邀请码验证
return normal_status(-1, "邀请码不能为空") if params[:invite_code].blank?
invite_code = params[:invite_code]
course = Course.find_by(invite_code: invite_code, is_delete: 0, invite_code_halt: 0)
course = Course.find_by(invite_code: invite_code, is_delete: 0, invite_code_halt: 0, laboratory_id: current_laboratory.id)
course_group = CourseGroup.find_by(invite_code: invite_code)
if course.blank?
return normal_status(-1, "邀请码无效") if course_group.blank?
course = Course.find_by(id: course_group.course_id, is_delete: 0, invite_code_halt: 0)
course = Course.find_by(id: course_group.course_id, is_delete: 0, invite_code_halt: 0, laboratory_id: current_laboratory.id)
return normal_status(-1, "邀请码无效") if course.blank?
end

@ -601,7 +601,7 @@ class ExercisesController < ApplicationController
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception("试卷提交失败")
tip_exception(e.message)
raise ActiveRecord::Rollback
end
end
@ -850,10 +850,11 @@ class ExercisesController < ApplicationController
g_course = params[:group_ids]
if g_course.present?
teacher_course_group_ids = @course.charge_group_ids(current_user)
if g_course.map(&:to_i).sort == teacher_course_group_ids.sort #开始为统一设置
all_course_group_ids = @course.course_groups.pluck(:id)
if exercise.unified_setting && g_course.map(&:to_i).sort == all_course_group_ids.sort #开始为统一设置
exercise.exercise_group_settings.destroy_all
new_ex_status = set_exercise_status(exercise.publish_time,Time.now)
exercise.update_attributes(:end_time => Time.now,:unified_setting => true,:exercise_status => new_ex_status)
exercise.update_attributes(:end_time => Time.now,:exercise_status => new_ex_status)
exercise_users = exercise.exercise_users
else
course_members_ids = course_students.course_find_by_ids("course_group_id",g_course).pluck(:user_id).uniq #该班级的全部学生
@ -1627,12 +1628,14 @@ class ExercisesController < ApplicationController
ex_and_user = user_groups_id & ex_group_settings #用户已设置的分班
unpublish_group = unpublish_group + ex_and_user - ex_ended_groups #已发布的全部班级减去截止的全部班级
else
ex_ended_groups = ex_all_group_settings.exercise_group_ended.pluck(:course_group_id).uniq
ex_and_user = user_groups_id & ex_group_settings #用户已设置的分班
unpublish_group = unpublish_group + ex_and_user
unpublish_group = unpublish_group + ex_and_user - ex_ended_groups
end
end
end
end
Rails.logger.info("#####____________unpublish_group_______#######{unpublish_group}")
unpublish_group = unpublish_group.uniq
if unpublish_group.count > 0
course_groups = CourseGroup.by_group_ids(unpublish_group)

@ -49,11 +49,12 @@ class GitsController < ApplicationController
shixunname = git_url.split("/")[1].split(".")[0]
repo_name = username + "/" + shixunname
uid_logger("git start: repo_name is #{repo_name}")
shixun = Shixun.select([:id, :user_id, :repo_name, :identifier]).where(repo_name: repo_name).first
shixun = Shixun.select([:id, :user_id, :repo_name, :identifier]).where(repo_name: repo_name, laboratory_id: nil).first
uid_logger("git start auth: shixun identifier is #{shixun.try(:identifier)}")
uid_logger("git start auth: systemuser is #{system_user.try(:login)}")
if shixun.present?
logger.info("#######{system_user.manager_of_shixun?(shixun)}")
if system_user.present? && system_user.manager_of_shixun?(shixun)
result = true
else
@ -72,7 +73,6 @@ class GitsController < ApplicationController
authenticate_or_request_with_http_basic do |username, password|
result
logger.info("1111111 git auth end: code is #{rand_code}, time is #{Time.now}")
end
end

@ -0,0 +1,170 @@
class HackUserLastestCodesController < ApplicationController
before_action :require_login, except: [:listen_result]
before_action :find_my_hack, only: [:show, :code_debug, :code_submit, :update_code, :listen_result, :result]
before_action :update_user_hack_status, only: [:code_debug, :code_submit]
before_action :require_auth_identity, only: [:update_code]
before_action :require_manager_identity, only: [:update_code]
def show
@my_hack.update_attribute(:status, 0) if @my_hack.status == 1
end
def update_code
@my_hack.update_attribute(:code, params[:code])
end
# 调试代码
def code_debug
exec_mode = "debug"
error_status = 501
error_msg = "debug_error"
oj_evaluate exec_mode, error_status, error_msg
render_ok
end
# 提交
def code_submit
exec_mode = "submit"
error_status = 502
error_msg = "submit_error"
oj_evaluate exec_mode, error_status, error_msg
render_ok
end
# 提交结果显示
def result
return if @my_hack.status == 1
end
# 接收中间件返回结果接口
# 调试模式: status 0 表示评测无错误,其他 表示错误(如编译出错,执行出错,超时等)
def listen_result
logger.info("###########listen_result#{params}")
begin
ojEvaResult = JSON.parse(params[:ojEvaResult])
testCase = ojEvaResult['testCase']
# 只有编译出错时,才正则匹配错误行数
error_line=
if params[:status] == "-4"
regular_match_error_line ojEvaResult['outPut'], @my_hack.hack.language
end
# debug 与submit 公用的参数
ds_params = {input: testCase['input'], output: testCase['output'], hack_id: @hack.id,
code: ojEvaResult['codeFileContent'], user_id: @my_hack.user_id, error_line: error_line,
status: ojEvaResult['status'], error_msg: ojEvaResult['outPut'],
execute_time: ojEvaResult['executeTime'], execute_memory: ojEvaResult['executeMem']}
ActiveRecord::Base.transaction do
# debug模式与submit模式
if ojEvaResult['execMode'] == "debug"
save_debug_data ds_params
elsif ojEvaResult['execMode'] == "submit"
save_submit_data ds_params
end
# 评测完成后,还原评测中的状态
@my_hack.update_attribute(:submit_status, 0)
end
render_ok
rescue Exception => e
logger.error("#########listen_result: #{e.message}")
end
end
private
def find_my_hack
@my_hack = HackUserLastestCode.find_by(identifier: params[:identifier])
@hack = @my_hack.hack
end
def oj_evaluate exec_mode, error_status, error_msg
request_url = "#{edu_setting('cloud_bridge')}/bridge/ojs/evaluate"
test_sets =
if exec_mode == "submit"
@hack.hack_sets.map{|set| {input: set.input, output: set.output, caseId: set.id}}
else
{input: params[:input]}
end
testCases = Base64.urlsafe_encode64(test_sets.to_json)
#codeFileContent = Base64.urlsafe_encode64(@my_hack.code)
debug_params = {execMode: exec_mode,
tpiID: @my_hack.identifier,
testCases: testCases,
platform: @my_hack.language,
codeFileContent: @my_hack.code,
timeLimit: @hack.time_limit,
sec_key: Time.now.to_i}
interface_json_post request_url, debug_params, error_status, error_msg
# 每次评测提交数增加
@hack.increment!(:submit_num)
end
# 正则错误行数
def regular_match_error_line content, language
case language
when 'Java'
content.scan(/.java.\d+/).map{|s| s.match(/\d+/)[0].to_i}.min
when 'C', 'C++'
content.scan(/\d:\d+: error/).map{|s| s.match(/\d+/)[0]}.min
when 'Python'
content.scan(/line \d+/).map{|s| s.match(/\d+/)[0].to_i}.min
end
end
# 存储debug数据
def save_debug_data debug_params
if @my_hack.hack_user_debug.present?
@my_hack.hack_user_debug.update_attributes!(debug_params)
else
@my_hack.hack_user_debug.create!(debug_params)
end
end
# 存储submit数据
def save_submit_data submit_params
# 通关
if submit_params[:status] == "0"
# 编程题已经发布,且之前未通关奖励积分
if @hack.status == 1 && !@my_hack.passed?
reward_attrs = { container_id: game.id, container_type: 'Hack', score: @hack.score }
RewardGradeService.call(@my_hack.user, reward_attrs)
RewardExperienceService.call(@my_hack.user, reward_attrs)
# 评测完成更新通过数
@hack.increment!(:pass_num)
@my_hack.update_attribute(:passed, true)
end
end
# 创建用户评测记录
logger.info("###########submit_params:#{submit_params}")
query_index = @my_hack.hack_user_codes.count +1
@my_hack.hack_user_codes.create!(submit_params.merge(query_index: query_index))
end
# 调试或提交改变状态
def update_user_hack_status
@my_hack.update_attribute(:submit_status, 1)
end
# 只有自己才能改动代码
def require_identity
if @my_hack.user_id != current_user.id
tip_exception(403, "..")
end
end
# 老师、自己、管理可以查看他人的编程题
def require_manager_identity
unless current_user.certification_teacher? || admin_or_business? || @my_hack.user_id == current_user.id
tip_exception(403, "..")
end
end
# 只有自己才能评测
def require_auth_identity
unless @my_hack.user_id == current_user.id
tip_exception(403, "..")
end
end
end

@ -0,0 +1,180 @@
class HacksController < ApplicationController
before_action :require_login, except: [:index]
before_action :require_teacher_identity, only: [:create, :edit, :update]
before_action :require_auth_identity, only: [:update, :edit, :publish]
before_action :find_hack, only: [:edit, :update, :publish, :start]
# 开启编程,如果第一次开启,创建一条记录,如果已经开启过的话,直接返回标识即可
def start
# 未发布的编程题,只能作者、或管理员访问
start_hack_auth
user_hack = @hack.hack_user_lastest_codes.mine(current_user.id)
identifier =
if user_hack.present?
user_hack.identifier
else
user_identifier = generate_identifier HackUserLastestCode, 12
user_code = {user_id: current_user.id, code: @hack.code,
identifier: user_identifier, language: @hack.language}
@hack.hack_user_lastest_codes.create!(user_code)
user_identifier
end
render_ok(data: {identifier: identifier})
end
# 首页
def index
# 筛选过滤与排序
params_filter_or_order
# 我解决的编程题数
user_codes = HackUserLastestCode.mine(current_user).passed.joins(:hack)
@simple_count = user_codes.where(hacks: {difficult: 1}).count
@medium_count = user_codes.where(hacks: {difficult: 2}).count
@diff_count = user_codes.where(hacks: {difficult: 3}).count
@pass_count = @simple_count + @medium_count + @diff_count
@hacks_count = @hacks.count("hacks.id")
@hacks = paginate @hacks
end
def create
begin
logger.info("##########{hack_params}")
hack = Hack.new(hack_params)
ActiveRecord::Base.transaction do
hack.user_id = current_user.id
hack.identifier = generate_identifier Hack, 8
hack.save!
# 创建测试集与代码
hack.hack_sets.create!(hack_sets_params)
hack.hack_codes.create!(hack_code_params)
end
render_ok({identifier: hack.identifier})
rescue Exception => e
logger.error("########create_hack_error: #{e.message}")
render_error("创建失败")
end
end
def update
begin
ActiveRecord::Base.transaction do
@hack.update_attributes!(hack_params)
set_ids = @hack.hack_sets.pluck(:id)
# 更新
param_update_sets params[:update_hack_sets], set_ids
# 新建
@hack.hack_sets.create!(hack_sets_params)
# 更新代码
@hack.hack_codes.create!(hack_code_params)
end
render_ok
rescue Exception => e
logger.error("####update_hack_error: #{e.message}")
render_error("更新失败")
end
end
# 发布功能
def publish
@hack.update_attribute(:status, 1)
render_ok
end
# 发布列表
def unpulished_list
limit = params[:limit] || 16
page = params[:page] || 1
hacks = Hack.where(user_id: current_user.id, status: 0)
@hacks_count = hacks.count
@hacks = hacks.includes(:hack_sets).page(page).per(limit)
end
def edit;end
private
# 实名认证老师,管理员与运营人员权限
def require_teacher_identity
unless current_user.certification_teacher? || admin_or_business?
tip_exception(403, "..")
end
end
# 只有自己,或者管理员才能更新
def require_auth_identity
unless @hack.user_id == current_user.id || admin_or_business?
tip_exception(403, "..")
end
end
def find_hack
@hack = Hack.find_by_identifier(params[:identifier])
end
def hack_params
params.require(:hack).permit(:name, :description, :difficult, :category, :open_or_not, :time_limit, :score)
end
def hack_sets_params
params.permit(hack_sets: [:input, :output, :position])[:hack_sets]
end
def hack_code_params
params.require(:hack_codes).permit(:code, :language)
end
def publish_params
params.require(:hack).permit(:difficult, :category, :open_or_not, :time_limit, :score)
end
def param_update_sets sets, all_sets_id
delete_set_ids = all_sets_id - sets.map{|set|set[:id]}
@hack.hack_sets.where(id: delete_set_ids).destroy_all
sets.each do |set|
if all_sets_id.include?(set[:id])
update_attrs = {input: set[:input], output: set[:output], position: set[:position]}
@hack.hack_sets.find_by!(id: set[:id]).update_attributes(update_attrs)
end
end
end
def params_filter_or_order
# 如果有来源,就不管发布公开私有
select_sql = "hacks.*, if(hacks.hack_user_lastest_codes_count=0, 0, hacks.pass_num/hacks.hack_user_lastest_codes_count) passed_rate"
if params[:come_from]
hacks = Hack.select(select_sql).mine(current_user.id)
else
hacks = Hack.select(select_sql).published.opening
end
# 搜索
if params[:search]
hacks = hacks.where("name like ?", "%#{params[:search]}%")
end
# 难度
if params[:difficult]
hacks = hacks.where(difficult: params[:difficult])
end
# 状态
if params[:status]
user_hacks = HackUserLastestCode.where(user_id: current_user.id)
if params[:status].to_i == -1
if user_hacks.present?
hacks = hacks.where.not(id: user_hacks.pluck(:hack_id))
end
else
hacks = hacks.joins(:hack_user_lastest_code).where(hack_user_lastest_code: {status: params[:status]})
end
end
# 排序
sort_by = params[:sort_by] || "hack_user_lastest_codes_count"
sort_direction = params[:sort_direction] || "desc"
@hacks = hacks.order("#{sort_by} #{sort_direction}")
end
def start_hack_auth
return true if @hack == 1
require_auth_identity
end
end

@ -532,7 +532,7 @@ class HomeworkCommonsController < ApplicationController
# 如果该发布规则 没有已发布的分班则需判断发布时间
tip_exception("发布时间不能早于等于当前时间") if setting[:publish_time] <= strf_time(Time.now) && group_settings.group_published.count == 0
tip_exception("截止时间不能早于等于当前时间") if setting[:end_time] <= strf_time(Time.now)
tip_exception("截止时间不能早于等于当前时间") if setting[:end_time] <= strf_time(Time.now) && group_settings.none_end.count > 0
tip_exception("截止时间不能早于发布时间") if setting[:publish_time] > setting[:end_time]
tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if
@course.end_date.present? && setting[:end_time] > strf_time(@course.end_date.end_of_day)

@ -7,7 +7,7 @@ class LibrariesController < ApplicationController
helper_method :current_library, :library_manageable?
def index
libraries = Library.all
libraries = current_laboratory.libraries
libraries =
if User.current&.logged? && params[:type] == 'mine'

@ -1,6 +1,6 @@
class ShixunListsController < ApplicationController
def index
@results = ShixunSearchService.call(search_params)
@results = ShixunSearchService.call(search_params, current_laboratory)
end
private

@ -38,6 +38,9 @@ class ShixunsController < ApplicationController
## 云上实验室过滤
unless current_laboratory.main_site?
@shixuns = @shixuns.joins(:laboratory_shixuns).where(laboratory_shixuns: { laboratory_id: current_laboratory.id })
else
not_shixun_ids = Shixun.joins(:laboratory_shixuns).where("laboratory_shixuns.laboratory_id != #{current_laboratory.id}")
@shixuns = @shixuns.where.not(id: not_shixun_ids)
end
## 方向
@ -486,10 +489,7 @@ class ShixunsController < ApplicationController
ShixunMirrorRepository.create(:shixun_id => @shixun.id, :mirror_repository_id => mirror)
end
end
logger.info("#########shixun_params#{shixun_params}")
@shixun.update_attributes(shixun_params)
logger.info("##########shixun_info_params: #{shixun_info_params}")
logger.info("##########params[:shixun_info][:evaluate_script]: #{params[:shixun_info][:evaluate_script]}")
@shixun.shixun_info.update_attributes(shixun_info_params)
@shixun.shixun_schools.delete_all
# scope_partment: 高校的名称
@ -916,6 +916,7 @@ class ShixunsController < ApplicationController
else
@users = User.none
end
@users = @users.where(laboratory_id: current_laboratory.id) unless current_laboratory.main_site?
page = params[:page] || 1
limit = params[:limit] || 20
@user_count = @users.count
@ -974,8 +975,14 @@ class ShixunsController < ApplicationController
end
@course_count = course_ids.length
@courses = Course.where(:id => course_ids).page(page).per(limit)
@courses = Course.where(:id => course_ids)
## 云上实验室过滤
unless current_laboratory.main_site?
@courses = @courses.where(laboratory_id: current_laboratory.id )
end
@course_count = @courses.count
@courses = @courses.page(page).per(limit)
end
# 将实训发送到课程

@ -1,6 +1,6 @@
class SubjectListsController < ApplicationController
def index
@results = SubjectSearchService.call(search_params)
@results = SubjectSearchService.call(search_params, current_laboratory)
end
private

@ -6,6 +6,7 @@ class SubjectsController < ApplicationController
before_action :allowed, only: [:update, :edit, :destroy, :publish, :cancel_publish, :cancel_has_publish,
:search_members, :add_subject_members, :statistics, :shixun_report, :school_report,
:up_member_position, :down_member_position, :update_team_title]
before_action :require_admin, only: [:copy_subject]
include ApplicationHelper
include SubjectsHelper
@ -23,18 +24,27 @@ class SubjectsController < ApplicationController
# 最热排序
if reorder == "myshixun_count"
laboratory_join = current_laboratory.main_site? ? '' : " JOIN laboratory_subjects ls ON ls.subject_id = subjects.id AND ls.laboratory_id = #{current_laboratory.id} "
if current_laboratory.main_site?
not_subject_ids = Subject.joins(:laboratory_subjects).where("laboratory_subjects.laboratory_id != #{current_laboratory.id}").pluck(:id)
not_subject_ids = not_subject_ids.length > 0 ? "(" + not_subject_ids.join(",") + ")" : "(-1)"
laboratory_join = " AND subjects.id not in #{not_subject_ids} "
else
subject_ids = Subject.joins(:laboratory_subjects).where(laboratory_subjects: { laboratory_id: current_laboratory.id }).pluck(:id)
subject_ids = subject_ids.length > 0 ? "(" + subject_ids.join(",") + ")" : "(-1)"
laboratory_join = " AND subjects.id in #{subject_ids} "
end
if select
@subjects = Subject.find_by_sql("SELECT subjects.id, subjects.user_id, subjects.name, subjects.stages_count, subjects.repertoire_id, subjects.status,
subjects.shixuns_count, subjects.excellent, sum(shixuns.myshixuns_count) AS myshixun_member_count FROM subjects join stage_shixuns
on stage_shixuns.subject_id = subjects.id join shixuns on shixuns.id = stage_shixuns.shixun_id #{laboratory_join} where
subjects.hidden = 0 AND subjects.status = 2 AND subjects.name like '%#{search}%'
on stage_shixuns.subject_id = subjects.id join shixuns on shixuns.id = stage_shixuns.shixun_id where
subjects.hidden = 0 AND subjects.status = 2 AND subjects.name like '%#{search}%' #{laboratory_join}
AND subjects.repertoire_id = #{select} GROUP BY subjects.id ORDER BY myshixun_member_count DESC")
else
@subjects = Subject.find_by_sql("SELECT subjects.id, subjects.user_id, subjects.name, subjects.stages_count, subjects.repertoire_id, subjects.status,
subjects.shixuns_count, subjects.excellent, sum(shixuns.myshixuns_count) AS myshixun_member_count FROM subjects join stage_shixuns
on stage_shixuns.subject_id = subjects.id join shixuns on shixuns.id = stage_shixuns.shixun_id #{laboratory_join} where
subjects.hidden = 0 AND subjects.status = 2 AND subjects.name like '%#{search}%'
on stage_shixuns.subject_id = subjects.id join shixuns on shixuns.id = stage_shixuns.shixun_id where
subjects.hidden = 0 AND subjects.status = 2 AND subjects.name like '%#{search}%' #{laboratory_join}
GROUP BY subjects.id ORDER BY myshixun_member_count DESC")
end
else
@ -56,6 +66,9 @@ class SubjectsController < ApplicationController
# 云上实验室过滤
unless current_laboratory.main_site?
@subjects = @subjects.joins(:laboratory_subjects).where(laboratory_subjects: { laboratory_id: current_laboratory.id })
else
not_subject_ids = Subject.joins(:laboratory_subjects).where("laboratory_subjects.laboratory_id != #{current_laboratory.id}")
@subjects = @subjects.where.not(id: not_subject_ids)
end
# 类型
@ -204,6 +217,10 @@ class SubjectsController < ApplicationController
WHERE m.course_id = c.id AND m.role in (1,2,3)
AND m.user_id=#{current_user.id} AND c.is_delete = 0 AND c.is_end = 0").map(&:id)
@courses = Course.where(id: course_ids)
## 云上实验室过滤
unless current_laboratory.main_site?
@courses = @courses.where(laboratory_id: current_laboratory.id )
end
@none_shixun_ids = ShixunSchool.where("school_id != #{current_user.user_extension.try(:school_id).to_i}").pluck(:shixun_id)
end
@ -285,6 +302,7 @@ class SubjectsController < ApplicationController
member_ids = @subject.subject_members.map(&:user_id).join(',')
condition = "%#{params[:search].strip}%".gsub(" ","")
@users = User.where("id not in (?) and status = 1 and LOWER(concat(lastname, ifnull(firstname, ''), login)) LIKE ?", member_ids, "#{condition}")
@users = @users.where(laboratory_id: current_laboratory.id) unless current_laboratory.main_site?
@users = @users.page(page).per(10)
@users = @users.includes(:user_extension)
@ -444,6 +462,13 @@ class SubjectsController < ApplicationController
end
end
# 复制实践课程
def copy
end
private
def subject_params
tip_exception("实训路径名称不能为空") if params[:name].blank?
@ -494,4 +519,6 @@ class SubjectsController < ApplicationController
end
end

@ -2,6 +2,8 @@ class Users::CoursesController < Users::BaseController
def index
courses = Users::CourseService.new(observed_user, query_params).call
courses = courses.where(laboratory_id: current_laboratory.id)
@count = courses.count
@courses = paginate(courses.includes(teacher: { user_extension: :school }), special: observed_user.is_teacher?)
end

@ -3,6 +3,7 @@ class Users::RecentContactsController < Users::BaseController
def index
contacts = observed_user.recent_contacts.distinct
contacts = contacts.where(laboratory_id: current_laboratory.id) unless current_laboratory.main_site?
@contacts = contacts.order('private_messages.created_at DESC').limit(10).includes(:user_extension)
end
end

@ -2,6 +2,14 @@ class Users::ShixunsController < Users::BaseController
def index
shixuns = Users::ShixunService.new(observed_user, query_params).call
## 云上实验室过滤
if current_laboratory.main_site?
not_shixun_ids = Shixun.joins(:laboratory_shixuns).where("laboratory_shixuns.laboratory_id != #{current_laboratory.id}")
shixuns = shixuns.where.not(id: not_shixun_ids)
else
shixuns = shixuns.joins(:laboratory_shixuns).where(laboratory_shixuns: { laboratory_id: current_laboratory.id })
end
@count = shixuns.count
@shixuns = paginate(shixuns.includes(:first_tag_repertoire), special: observed_user.is_teacher?)

@ -2,6 +2,14 @@ class Users::SubjectsController < Users::BaseController
def index
subjects = Users::SubjectService.new(observed_user, query_params).call
## 云上实验室过滤
if current_laboratory.main_site?
not_subject_ids = Subject.joins(:laboratory_subjects).where("laboratory_subjects.laboratory_id != #{current_laboratory.id}")
subjects = subjects.where.not(id: not_subject_ids)
else
subjects = subjects.joins(:laboratory_subjects).where(laboratory_subjects: { laboratory_id: current_laboratory.id })
end
@count = subjects.count
@subjects = paginate(subjects.includes(:user, :repertoire), special: observed_user.is_teacher?)
end

@ -3,6 +3,7 @@ class UsersForPrivateMessagesController < ApplicationController
def index
users = User.active.where.not(id: current_user.id)
users = users.where(laboratory_id: current_laboratory.id) unless current_laboratory.main_site?
keyword = params[:keyword].to_s.strip
if keyword.blank?

@ -1,8 +1,158 @@
class Weapps::CoursesController < Weapps::BaseController
before_action :require_wechat_login!
before_action :require_login
before_action :user_course_identity, except: [:create]
before_action :teacher_allowed, only: [:edit, :update]
before_action :teacher_or_admin_allowed, only: [:change_member_roles, :delete_course_teachers]
def create
return render_error("只有老师身份才能创建课堂") unless current_user.is_teacher?
course = Course.new(tea_id: current_user.id)
Weapps::CreateCourseService.call(course, course_params)
render_ok
rescue ApplicationService::Error => ex
render_error(ex.message)
end
def edit
@course = current_course
end
def update
Weapps::UpdateCourseService.call(current_course, update_course_params)
render_ok
end
def show
@course = current_course
@current_user = current_user
end
def shixun_homework_category
@categories = current_course.shixun_course_modules.first&.course_second_categories
end
# 教师列表
def teachers
@course = current_course
if @course.try(:id) != 1309 || current_user.admin? || current_user.try(:id) == 15582
@teacher_list = @course.course_members.joins(:user).where("course_members.role in (1, 2, 3)")
else
@teacher_list = @course.course_members.joins(:user).where("(course_members.role in (1, 3) or (course_members.user_id = #{current_user.id}
and course_members.role = 2))")
end
@teacher_list_size = @teacher_list.size
@applications_size = CourseMessage.unhandled_join_course_requests_by_course(@course).size
@teacher_list = @teacher_list.preload(user: [user_extension: :school]).order("CONVERT(CONCAT(users.lastname, users.firstname) USING gbk) COLLATE gbk_chinese_ci asc")
end
# 批量删除教师或助教
def delete_course_teachers
begin
@course = current_course
course_members = @course.course_members.where(id: params[:course_member_ids], role: %i[PROFESSOR ASSISTANT_PROFESSOR])
user_ids = course_members.pluck(:user_id)
course_members.destroy_all
CourseDeleteStudentNotifyJob.perform_later(@course.id, user_ids, current_user.id)
@course.students.where(user_id: user_ids).update_all(is_active: 1)
normal_status(0, "删除成功")
rescue => e
uid_logger_error(e.message)
tip_exception("删除失败")
end
end
def students
end
# 批量修改角色
def change_member_roles
tip_exception("请至少选择一个角色") if params[:roles].blank?
tip_exception("不能具有老师、助教两种角色") if params[:roles].include?("PROFESSOR") && params[:roles].include?("ASSISTANT_PROFESSOR")
params[:user_ids].each do |user_id|
course_members = @course.course_members.where(user_id: user_id)
tip_exception("非课堂成员不能修改角色") if course_members.blank?
ActiveRecord::Base.transaction do
# 第一次修改为教师或助教身份时直接创建数据
if params[:roles].include?("CREATOR")
teacher_member = course_members.where(role: %i[CREATOR]).take
elsif (params[:roles].include?("PROFESSOR") || params[:roles].include?("ASSISTANT_PROFESSOR")) && !course_members.exists?(role: %i[CREATOR PROFESSOR ASSISTANT_PROFESSOR])
teacher_member = CourseMember.create!(course_id: @course.id, user_id: user_id, role: params[:roles].include?("PROFESSOR") ? 2 : 3)
# 如果有未审批的申请教师/助教的记录,则修改状态为已审批
apply_teacher = CourseMessage.where(course_id: @course.id, course_message_id: user_id, status: 0).last
apply_teacher.update!(status: 1, apply_user_id: current_user.id)
elsif course_members.exists?(role: %i[PROFESSOR ASSISTANT_PROFESSOR])
teacher_member = course_members.where(role: %i[PROFESSOR ASSISTANT_PROFESSOR]).take
if params[:roles].include?("PROFESSOR") || params[:roles].include?("ASSISTANT_PROFESSOR")
# 如果之前有老师身份且老师身份要调整时只需要修改role字段
if !params[:roles].include?(teacher_member.role) && params[:roles].include?("PROFESSOR")
teacher_member.PROFESSOR!
elsif !params[:roles].include?(teacher_member.role) && params[:roles].include?("ASSISTANT_PROFESSOR")
teacher_member.ASSISTANT_PROFESSOR!
end
teacher_member.save!
else
# 不含教师的参数时删除记录
teacher_member.destroy!
# CourseDeleteStudentNotifyJob.perform_later(@course.id, [teacher_member.user_id], current_user.id)
end
end
# 学生身份的处理
student_member = course_members.where(role: %i[STUDENT]).take
# 不存在则创建学生身份
if params[:roles].include?("STUDENT") && student_member.blank?
correspond_teacher_exist = CourseMember.exists?(user_id: user_id, is_active: 1, course_id: @course.id, role: %i[CREATOR PROFESSOR ASSISTANT_PROFESSOR])
new_student = CourseMember.new(user_id: user_id, course_id: @course.id, role: 4)
new_student.is_active = 0 if correspond_teacher_exist
new_student.save!
CourseAddStudentCreateWorksJob.perform_later(@course.id, user_id)
# StudentJoinCourseNotifyJob.perform_later(current_user.id, course.id)
elsif !params[:roles].include?("STUDENT") && student_member.present?
# 删除学生身份时激活老师身份
teacher_member.update_attributes!(is_active: 1) if student_member.is_active && teacher_member.present?
student_member.destroy!
CourseDeleteStudentDeleteWorksJob.perform_later(@course.id, user_id)
# CourseDeleteStudentNotifyJob.perform_later(@course.id, [params[:user_id]], current_user.id)
elsif params[:roles].include?("STUDENT") && student_member.present? && !params[:roles].include?("PROFESSOR") && !params[:roles].include?("ASSISTANT_PROFESSOR")
# 学生身份存在且学生没有教师身份时更新is_active
student_member.update_attributes!(is_active: 1)
end
end
end
normal_status(0, "修改成功")
end
private
def course_params
params.permit(:name, :course_list_name, :credit, course_module_types: [])
end
def update_course_params
params.permit(:name, :course_list_name, :credit)
end
def current_course
@_current_course = Course.find params[:id]
end
def teacher_allowed
return render_forbidden unless @user_course_identity < Course::STUDENT
end
# 课堂教师,课堂管理员以及超级管理员的权限判断
def teacher_or_admin_allowed
unless @user_course_identity < Course::ASSISTANT_PROFESSOR
tip_exception(403, "..")
end
end
end

@ -1,5 +1,5 @@
class Weapps::HomesController < Weapps::BaseController
# before_action :require_wechat_login!
before_action :require_login
def show
# banner

@ -0,0 +1,20 @@
class Weapps::CreateCourseForm
include ActiveModel::Model
attr_accessor :course
attr_accessor :name, :course_list_name, :credit, :course_module_types
validates :name, presence: true
validates :course_list_name, presence: true
validate :course_name_prefix
validate :check_course_modules
def course_name_prefix
raise '课堂名称应以课程名称开头' unless name.index(course_list_name) && name.index(course_list_name) == 0
end
def check_course_modules
raise '请至少添加一个课堂模块' if course_module_types.blank?
end
end

@ -0,0 +1,15 @@
class Weapps::UpdateCourseForm
include ActiveModel::Model
attr_accessor :course
attr_accessor :name, :course_list_name, :credit
validates :name, presence: true
validates :course_list_name, presence: true
validate :course_name_prefix
def course_name_prefix
raise '课堂名称应以课程名称开头' unless name.index(course_list_name) && name.index(course_list_name) == 0
end
end

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

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

@ -2,8 +2,26 @@ module Util
module UUID
module_function
DCODES = %W(2 3 4 5 6 7 8 9 a b c f e f g h i j k l m n o p q r s t u v w x y z)
def time_uuid(format: '%Y%m%d%H%M%S', suffix: 8)
"#{Time.zone.now.strftime(format)}#{Random.rand(10**suffix).to_i}"
end
# 随机生成字符
def generate_identifier(container, num, pre='')
code = DCODES.sample(num).join
if container == User
while container.exists?(login: pre+code) do
code = DCODES.sample(num).join
end
else
while container.exists?(identifier: code) do
code = DCODES.sample(num).join
end
end
code
end
end
end

@ -1,4 +1,5 @@
class Competition < ApplicationRecord
belongs_to :laboratory, optional: true
has_many :competition_modules, dependent: :destroy
has_many :unhidden_competition_modules, -> { where(hidden: false) }, class_name: 'CompetitionModule'
@ -32,6 +33,7 @@ class Competition < ApplicationRecord
has_many :competition_prizes, dependent: :destroy
has_many :competition_prize_users, dependent: :destroy
before_save :set_laboratory
after_create :create_competition_modules
def mode_type
@ -147,9 +149,9 @@ class Competition < ApplicationRecord
competition_stages.map(&:max_end_time).max
end
# def awards_count
# competition_awards.pluck(:num)&.sum > 0 ? competition_awards.pluck(:num)&.sum : 20
# end
def charts_count
competition_prizes&.pluck(:num).sum.to_i > 0 ? competition_prizes&.pluck(:num).sum.to_i : awards_count
end
def manager?(user)
user && competition_managers.exists?(user_id: user.id)
@ -180,4 +182,10 @@ class Competition < ApplicationRecord
end
end
end
def set_laboratory
return unless new_record?
self.laboratory = Laboratory.current if laboratory_id.blank?
end
end

@ -7,6 +7,8 @@ class Course < ApplicationRecord
belongs_to :school, class_name: 'School', foreign_key: :school_id #定义一个方法school该方法通过school_id来调用School表
belongs_to :course_list, optional: true
belongs_to :laboratory, optional: true
# 所属实践课程
belongs_to :subject, optional: true
has_many :informs, as: :container, dependent: :destroy
@ -105,6 +107,7 @@ class Course < ApplicationRecord
validates :name, presence: true, length: { maximum: 60 }
before_save :set_laboratory
after_create :create_board_sync, :act_as_course_activity, :send_tiding
def course_member? user_id, role
@ -409,4 +412,10 @@ class Course < ApplicationRecord
else 100
end
end
def set_laboratory
return unless new_record? # 新记录才需要标记
self.laboratory = Laboratory.current if laboratory_id.blank?
end
end

@ -5,7 +5,7 @@ class CourseActivity < ApplicationRecord
belongs_to :exercise
belongs_to :poll
after_create :add_course_lead
# after_create :add_course_lead
# 发布新课导语
# 导语要放置在课程创建信息之后

@ -0,0 +1,38 @@
class Hack < ApplicationRecord
# status: 0 未发布; 1已发布
# diffcult: 难度 1简单2中等 3困难
# 编程题
validates_length_of :name, maximum: 60
# 测试集
has_many :hack_sets, ->{order("position asc")}, :dependent => :destroy
# 代码
has_many :hack_codes, :dependent => :destroy
has_many :hack_user_lastest_codes, :dependent => :destroy
belongs_to :user
scope :published, -> { where(status: 1) }
scope :opening, -> {where(open_or_not: 1)}
scope :mine, -> (author_id){ where(user_id: author_id) }
def language
if hack_codes.count == 1
hack_codes.first.language
else
hack_codes.pluck(:language).first
end
end
def code
if hack_codes.count == 1
tran_base64_decode64(hack_codes.first.code)
else
tran_base64_decode64(hack_codes.pluck(:code))
end
end
# 用于用户调试的第一个测试用例
def input_test_case
hack_sets.first&.input
end
end

@ -0,0 +1,3 @@
class HackCode < ApplicationRecord
# 编程题代码相关
end

@ -0,0 +1,4 @@
class HackSet < ApplicationRecord
# 编程题测试集
belongs_to :hack
end

@ -0,0 +1,4 @@
class HackUserCode < ApplicationRecord
# 用户编程题的信息
belongs_to :hack
end

@ -0,0 +1,4 @@
class HackUserDebug < ApplicationRecord
belongs_to :hack
belongs_to :hack_user_lastest_code
end

@ -0,0 +1,13 @@
class HackUserLastestCode < ApplicationRecord
# passed 用户之前评测是否通过
# status: 最新评测状态: -1测试用例结果不匹配; 0: 评测通过; ;2 评测超时;3 创建pod失败; 4 编译失败;5 执行失败
# submit_status: 0: 可以评测, 1评测中
# 编程题最新代码
belongs_to :hack, counter_cache: true
belongs_to :user
has_many :hack_user_codes, dependent: :destroy
has_one :hack_user_debug
scope :mine, ->(author_id){ find_by(user_id: author_id) }
scope :passed, -> {where(status: 1)}
end

@ -9,7 +9,14 @@ class Laboratory < ApplicationRecord
has_many :portal_images, dependent: :destroy
has_many :laboratory_shixuns, dependent: :destroy
# has_many :shixuns, through: :laboratory_shixuns, source: :shixun
has_many :laboratory_subjects, dependent: :destroy
# has_many :subjects, through: :laboratory_subjects, source: :subject
has_many :courses, dependent: :destroy
has_many :competitions, dependent: :destroy
has_many :libraries, dependent: :destroy
validates :identifier, uniqueness: { case_sensitive: false }, allow_nil: true

@ -15,15 +15,31 @@ class LaboratorySetting < ApplicationRecord
end
def login_logo_url
logo_url('login')
image_url('login')
end
def nav_logo_url
logo_url('nav')
image_url('nav')
end
def tab_logo_url
logo_url('tab')
image_url('tab')
end
def subject_banner_url
image_url('_subject_banner')
end
def course_banner_url
image_url('_course_banner')
end
def competition_banner_url
image_url('_competition_banner')
end
def moop_cases_banner_url
image_url('_moop_cases_banner')
end
def default_navbar
@ -32,7 +48,7 @@ class LaboratorySetting < ApplicationRecord
private
def logo_url(type)
def image_url(type)
return nil unless Util::FileManage.exists?(self, type)
Util::FileManage.source_disk_file_url(self, type)
end

@ -3,6 +3,7 @@ class Library < ApplicationRecord
belongs_to :user
belongs_to :cover, class_name: 'Attachment', foreign_key: :cover_id, optional: true
belongs_to :laboratory, optional: true
has_many :library_applies, dependent: :delete_all
has_many :library_library_tags, dependent: :delete_all
@ -34,6 +35,13 @@ class Library < ApplicationRecord
end
end
before_save :set_laboratory
private def set_laboratory
return unless new_record?
self.laboratory = Laboratory.current if laboratory_id.blank?
end
def increment_visited_count!(num = 1)
increment_column!(:visited_count, num)
end

@ -13,6 +13,7 @@ module Searchable::Course
def search_data
{
laboratory_id: laboratory_id,
name: name,
author_name: teacher&.real_name
}

@ -30,6 +30,8 @@ class User < ApplicationRecord
LOGIN_CHARS = %W(2 3 4 5 6 7 8 9 a b c f e f g h i j k l m n o p q r s t u v w x y z).freeze
belongs_to :laboratory, optional: true
has_one :user_extension, dependent: :destroy
has_many :open_users, dependent: :destroy
has_one :wechat_open_user, class_name: 'OpenUsers::Wechat'
@ -147,6 +149,9 @@ class User < ApplicationRecord
# 客户管理
has_many :partner_managers, dependent: :destroy
# OJ编程题
has_many :hacks, dependent: :destroy
has_many :hack_user_lastest_codes, dependent: :destroy
# Groups and active users
scope :active, lambda { where(status: STATUS_ACTIVE) }
@ -155,7 +160,7 @@ class User < ApplicationRecord
delegate :gender, :department_id, :school_id, :location, :location_city, :technical_title, to: :user_extension, allow_nil: true
before_save :update_hashed_password
before_save :update_hashed_password, :set_laboratory
after_create do
SyncTrustieJob.perform_later("user", 1) if allow_sync_to_trustie?
end
@ -329,6 +334,7 @@ class User < ApplicationRecord
# 实训管理员实训合作者、admin
def manager_of_shixun?(shixun)
logger.info("############id: #{id}")
shixun.shixun_members.exists?(role: [1,2], user_id: id) || admin? || business?
end
@ -704,6 +710,12 @@ class User < ApplicationRecord
raise('密码长度不能超过16位')
end
end
def set_laboratory
return unless new_record?
self.laboratory = Laboratory.current if laboratory_id.blank?
end
end

@ -6,7 +6,17 @@ class UserExtension < ApplicationRecord
belongs_to :school, optional: true
belongs_to :department, optional: true
before_save :set_laboratory_school
def identity_text
I18n.t("user.identity.#{identity}")
end
private
def set_laboratory_school
return unless new_record?
self.school_id = Laboratory.current.school_id if school_id.blank? && !Laboratory.current.main_site?
end
end

@ -12,6 +12,9 @@ class Admins::UserQuery < ApplicationQuery
def call
users = User.where(type: 'User')
# 云上实验室
users = users.where(laboratory_id: params[:laboratory_id]) if params[:laboratory_id].present?
# 状态
status = params[:status]
users = users.where(status: status) if status.present?

@ -17,7 +17,7 @@ class Admins::SaveLaboratorySettingService < ApplicationService
laboratory.save!
laboratory_setting.save!
deal_logo_file
deal_image_file
end
laboratory
@ -35,13 +35,17 @@ class Admins::SaveLaboratorySettingService < ApplicationService
end
end
def deal_logo_file
save_logo_file(params[:nav_logo], 'nav')
save_logo_file(params[:login_logo], 'login')
save_logo_file(params[:tab_logo], 'tab')
def deal_image_file
save_image_file(params[:nav_logo], 'nav')
save_image_file(params[:login_logo], 'login')
save_image_file(params[:tab_logo], 'tab')
save_image_file(params[:subject_banner], '_subject_banner')
save_image_file(params[:course_banner], '_course_banner')
save_image_file(params[:competition_banner], '_competition_banner')
save_image_file(params[:moop_cases_banner], '_moop_cases_banner')
end
def save_logo_file(file, type)
def save_image_file(file, type)
return unless file.present? && file.is_a?(ActionDispatch::Http::UploadedFile)
file_path = Util::FileManage.source_disk_filename(laboratory_setting, type)

@ -8,34 +8,57 @@ class SearchService < ApplicationService
end
def call
Searchkick.search(keyword, search_options)
# return [] if keyword.blank?
modal_name.search(keyword, search_options)
end
private
def modal_name
@_modal_name ||=
case params[:type].to_s.strip
when 'shixun' then Shixun
when 'course' then Course
when 'subject' then Subject
when 'memo' then Memo
else Shixun
end
end
def search_options
model_options = {
index_name: index_names,
model_includes: model_includes
includes: modal_name.searchable_includes
}
model_options.merge(where: { status: 2 }) if index_names == [Shixun]
model_options.merge(default_options)
end
model_options.deep_merge!(where: { status: 2 }) if modal_name == Shixun
model_options.deep_merge!(extra_options)
def index_names
@_index_names ||=
case params[:type].to_s.strip
when 'shixun' then [Shixun]
when 'course' then [Course]
when 'subject' then [Subject]
when 'memo' then [Memo]
else [Shixun, Course, Subject, Memo]
end
model_options.deep_merge!(default_options)
model_options
end
def model_includes
index_names.each_with_object({}) do |klass, obj|
obj[klass] = klass.searchable_includes
def extra_options
case params[:type].to_s.strip
when 'shixun' then
if Laboratory.current.main_site?
not_shixun_ids = Shixun.joins(:laboratory_shixuns).where("laboratory_shixuns.laboratory_id != #{Laboratory.current.id}")
shixun_ids = Shixun.where.not(id: not_shixun_ids).pluck(:id)
else
shixun_ids = Laboratory.current.shixuns.pluck(:id)
end
{ where: { id: shixun_ids } }
when 'subject' then
if Laboratory.current.main_site?
not_subject_ids = Subject.joins(:laboratory_subjects).where("laboratory_subjects.laboratory_id != #{Laboratory.current.id}")
subject_ids = Subject.where.not(id: not_subject_ids).pluck(:id)
else
subject_ids = Laboratory.current.subjects.pluck(:id)
end
{ where: { id: subject_ids } }
when 'course' then
{ where: { laboratory_id: Laboratory.current.id } }
else
{}
end
end
end

@ -1,10 +1,11 @@
class ShixunSearchService < ApplicationService
include ElasticsearchAble
attr_reader :params
attr_reader :params, :laboratory
def initialize(params)
def initialize(params, laboratory)
@params = params
@laboratory = laboratory
end
def call
@ -35,6 +36,17 @@ class ShixunSearchService < ApplicationService
@shixuns = @shixuns.where(trainee: params[:diff])
end
## 云上实验室过滤
if laboratory.main_site?
not_shixun_ids = Shixun.joins(:laboratory_shixuns).where("laboratory_shixuns.laboratory_id != #{laboratory.id}")
@shixuns = @shixuns.where.not(id: not_shixun_ids)
else
@shixuns = @shixuns.joins(:laboratory_shixuns).where(laboratory_shixuns: { laboratory_id: laboratory.id })
end
# laboratory = Laboratory.find_by_subdomain(subdomain)
# @shixuns = @shixuns.where(id: laboratory.shixuns) if laboratory
Shixun.search(keyword, search_options)
end

@ -1,10 +1,11 @@
class SubjectSearchService < ApplicationService
include ElasticsearchAble
attr_reader :params
attr_reader :params, :laboratory
def initialize(params)
def initialize(params, laboratory)
@params = params
@laboratory = laboratory
end
def call
@ -17,6 +18,17 @@ class SubjectSearchService < ApplicationService
@subjects = Subject.visible.unhidden
end
# laboratory = Laboratory.find_by_subdomain(subdomain)
# @subjects = @subjects.where(id: laboratory.subjects) if laboratory
## 云上实验室过滤
if laboratory.main_site?
not_subject_ids = Subject.joins(:laboratory_subjects).where("laboratory_subjects.laboratory_id != #{laboratory.id}")
@subjects = @subjects.where.not(id: not_subject_ids)
else
@subjects = @subjects.joins(:laboratory_subjects).where(laboratory_subjects: { laboratory_id: laboratory.id })
end
Subject.search(keyword, search_options)
end

@ -0,0 +1,217 @@
class Subjects::CopySubjectService < ApplicationService
attr_reader :subject, :to_subject, :user, :laboratory
def initialize(subject, user, laboratory=nil)
@subject = subject
@user = user
@laboratory = laboratory
subject_params = subject.attributes.dup.except('id', 'copy_subject_id', 'user_id', 'homepage_show')
@to_subject = Subject.new(subject_params)
end
def call
return if subject.blank?
ActiveRecord::Base.transaction do
copy_subject!
end
end
private
# 复制实践课程表
def copy_subject!
to_subject.copy_subject_id = subject.id
to_subject.user_id = user.id
to_subject.save!
copy_stages_data!(subject, to_subject)
copy_subject_members_data(to_subject)
laboratory.laboratory_subjects.create(subject: to_subject) if laboratory
end
# 复制章节需要的章节
def copy_stages_data!(subject, to_subject)
subject.stages.each do |stage|
to_stage = to_subject.stages.new
to_stage.attributes = stage.attributes.dup.except('id', 'subject_id', 'user_id')
to_stage.user_id = user.id
to_stage.save!
copy_stage_shixuns_data!(stage, to_stage)
end
end
# 创建实践课程关联实训表
def copy_stage_shixuns_data!(stage, to_stage)
stage.stage_shixuns.each do |stage_shixun|
to_shixun = copy_shixun_data!(stage_shixun)
to_stage_shixun = to_stage.stage_shixuns.new
to_stage_shixun.attributes = stage_shixun.attributes.dup.except('id', 'subject_id', 'stage_id', 'shixun_id')
to_stage_shixun.subject_id = to_stage.subject_id
to_stage_shixun.shixun_id = to_shixun.id
to_stage_shixun.save!
end
end
# 复制实训数据
def copy_shixun_data!(stage_shixun)
shixun = stage_shixun.shixun
to_shixun = Shixun.new
to_shixun.attributes = shixun.attributes.dup.except('id', 'user_id', 'identifier', 'homepage_show',
'use_scope', 'averge_star', 'myshixuns_count')
to_shixun.identifier = Util::UUID.generate_identifier(Shixun, 8)
to_shixun.user_id = user.id
if laboratory
to_shixun.laboratory_id = laboratory.id
end
# 复制版本库
fork_repository_name = "#{user.login}/#{to_shixun.identifier}"
GitService.fork_repository(repo_path: "#{shixun.repo_name}.git",
fork_repository_path: (fork_repository_name + ".git"))
to_shixun.repo_name = fork_repository_name
to_shixun.save!
copy_shixun_info_data!(shixun, to_shixun)
copy_shixun_mirror_repositories_data!(shixun, to_shixun)
copy_shixun_tag_repertoires_data!(shixun, to_shixun)
copy_shixun_service_configs_data!(shixun, to_shixun)
copy_challenges_data!(shixun, to_shixun)
copy_shixun_members_data!(to_shixun)
# 云上实验室
if laboratory
laboratory.laboratory_shixuns.create(shixun: to_shixun)
end
to_shixun
end
# 创建实训长字段内容
def copy_shixun_info_data!(shixun, to_shixun)
to_shixun_info = ShixunInfo.new
to_shixun_info.attributes = shixun.shixun_info.attributes.except('id', 'shixun_id')
to_shixun_info.shixun_id = to_shixun.id
to_shixun_info.save!
end
# 创建实训镜像标签
def copy_shixun_mirror_repositories_data!(shixun, to_shixun)
shixun.shixun_mirror_repositories.each do |shixun_mirror_repository|
to_shixun_mirror_repository = to_shixun.shixun_mirror_repositories.new
to_shixun_mirror_repository.attributes = shixun_mirror_repository.attributes.dup.except('id', 'shixun_id')
to_shixun_mirror_repository.shixun_id = to_shixun.id
to_shixun_mirror_repository.save!
end
end
# 创建实训tag标签
def copy_shixun_tag_repertoires_data!(shixun, to_shixun)
shixun.shixun_tag_repertoires.each do |shixun_tag_repertoire|
to_shixun_tag_repertoire = to_shixun.shixun_tag_repertoires.new
to_shixun_tag_repertoire.attributes = shixun_tag_repertoire.attributes.dup.except('id', 'shixun_id')
to_shixun_tag_repertoire.shixun_id = to_shixun.id
to_shixun_tag_repertoire.save!
end
end
# 复制实训服务配置
def copy_shixun_service_configs_data!(shixun, to_shixun)
shixun.shixun_service_configs.each do |shixun_service_config|
to_shixun_service_config = to_shixun.shixun_service_configs.new
to_shixun_service_config.attributes = shixun_service_config.attributes.dup.except('id', 'shixun_id')
to_shixun_service_config.shixun_id = to_shixun.id
to_shixun_service_config.save!
end
end
# 复制关卡信息
def copy_challenges_data!(shixun, to_shixun)
shixun.challenges.each do |challenge|
to_challenge = to_shixun.challenges.new
to_challenge.attributes = challenge.attributes.dup.except('id', 'shixun_id', 'praises_count', 'user_id', 'visits')
to_challenge.user_id = user.id
to_challenge.shixun_id = to_shixun.id
to_challenge.save!
copy_challenge_answers_data!(challenge, to_challenge)
copy_challenge_tags_data!(challenge, to_challenge)
copy_test_sets_data!(challenge, to_challenge)
copy_challenge_chooses_data!(challenge, to_challenge)
end
end
# 复制答案数据
def copy_challenge_answers_data!(challenge, to_challenge)
challenge.challenge_answers.each do |challenge_answer|
to_challenge_answer = to_challenge.challenge_answers.new
to_challenge_answer.attributes = challenge_answer.attributes.dup.except('id', 'challenge_id')
to_challenge_answer.challenge_id = to_challenge.id
to_challenge_answer.save!
end
end
# 复制关卡标签数据
def copy_challenge_tags_data!(challenge, to_challenge)
challenge.challenge_tags.each do |challenge_tag|
to_challenge_tag = to_challenge.challenge_tags.new
to_challenge_tag.attributes = challenge_tag.attributes.dup.except('id', 'challenge_id')
to_challenge_tag.challenge_id = to_challenge.id
to_challenge_tag.save!
end
end
# 复制测试集
def copy_test_sets_data!(challenge, to_challenge)
challenge.test_sets.each do |test_set|
to_test_set = to_challenge.test_sets.new
to_test_set.attributes = test_set.attributes.dup.except('id', 'challenge_id')
to_test_set.challenge_id = to_challenge.id
to_test_set.save!
end
end
# 复制选择题关卡
def copy_challenge_chooses_data!(challenge, to_challenge)
challenge.challenge_chooses.each do |challenge_choose|
to_challenge_choose = to_challenge.challenge_chooses.new
to_challenge_choose.attributes = challenge_choose.attributes.dup.except('id', 'challenge_id')
to_challenge_choose.challenge_id = to_challenge.id
to_challenge_choose.save!
copy_challenge_questions_data!(challenge_choose, to_challenge_choose)
copy_challenge_choose_tags_data!(challenge_choose, to_challenge_choose)
end
end
# 复制选择题问题
def copy_challenge_questions_data!(challenge_choose, to_challenge_choose)
challenge_choose.challenge_questions.each do |challenge_question|
to_challenge_question = to_challenge_choose.challenge_questions.new
to_challenge_question.attributes = challenge_question.attributes.dup.except('id', 'challenge_choose_id')
to_challenge_question.challenge_choose_id = to_challenge_choose.id
to_challenge_question.save!
end
end
# 复制选择题标签
def copy_challenge_choose_tags_data!(challenge_choose, to_challenge_choose)
challenge_choose.challenge_tags.each do |challenge_tag|
to_challenge_tag = to_challenge_choose.challenge_tags.new
to_challenge_tag.attributes = challenge_tag.attributes.dup.except('id', 'challenge_choose_id')
to_challenge_tag.challenge_choose_id = to_challenge_choose.id
to_challenge_tag.save!
end
end
# 创建实训成员
def copy_shixun_members_data!(to_shixun)
to_shixun.shixun_members.create!(user_id: user.id, role: 1)
end
# 创建课程成员
def copy_subject_members_data(to_subject)
to_subject.subject_members.create!(user_id: user.id, role: 1)
end
end

@ -0,0 +1,37 @@
class Weapps::CreateCourseService < ApplicationService
attr_reader :course, :params
def initialize(course, params)
@course = course
@params = params
end
def call
Weapps::CreateCourseForm.new(form_params).validate!
ActiveRecord::Base.transaction do
course.name = params[:name].to_s.strip
course.school_id = course.teacher&.school_id
course.is_public = 0
course.credit = params[:credit].blank? ? nil : params[:credit]
course_list = CourseList.find_by(name: params[:course_list_name].to_s.strip)
if course_list
course.course_list_id = course_list.id
else
new_course_list = CourseList.create!(name: params[:course_list_name].to_s.strip, user_id: course.tea_id, is_admin: 0)
course.course_list_id = new_course_list.id
end
course.save!
course.generate_invite_code
CourseMember.create!(course_id: course.id, user_id: course.tea_id, role: 1)
course.create_course_modules(params[:course_module_types])
end
end
private
def form_params
params.merge(course: course)
end
end

@ -0,0 +1,31 @@
class Weapps::UpdateCourseService < ApplicationService
attr_reader :course, :params
def initialize(course, params)
@course = course
@params = params
end
def call
Weapps::UpdateCourseForm.new(form_params).validate!
ActiveRecord::Base.transaction do
course.name = params[:name].to_s.strip
course.credit = params[:credit].blank? ? nil : params[:credit]
course_list = CourseList.find_by(name: params[:course_list_name].to_s.strip)
if course_list
course.course_list_id = course_list.id
else
new_course_list = CourseList.create!(name: params[:course_list_name].to_s.strip, user_id: course.tea_id, is_admin: 0)
course.course_list_id = new_course_list.id
end
course.save!
end
end
private
def form_params
params.merge(course: course)
end
end

@ -43,6 +43,9 @@
<%= link_to "/users/#{user.login}", class: 'identity-authentication-avatar', target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } do %>
<img src="/images/<%= url_to_avatar(user) %>" class="rounded-circle" width="40" height="40" />
<% end %>
<% if user.from_sub_site? %>
<span class="badge badge-pill badge-info">合作</span>
<% end %>
</td>
<td><%= user.real_name %></td>
<td><%= user.ID_number %></td>

@ -86,6 +86,52 @@
</div>
</div>
<div class="form-group px-2 setting-item">
<div class="setting-item-head"><h6>Banner设置</h6></div>
<div class="dropdown-divider"></div>
<div class="pl-0 py-3 row setting-item-body">
<div class="col-12 col-md-4 banner-item">
<%- subject_banner = setting.subject_banner_url -%>
<div class="banner-item-top">实践课程</div>
<div class="banner-item-bottom <%= subject_banner ? 'has-img' : '' %>">
<img class="banner-item-img subject-banner-img" src="<%= subject_banner %>" style="<%= subject_banner.present? ? '' : 'display: none' %>"/>
<%= file_field_tag(:subject_banner, accept: 'image/*', style: 'display: none', value: params[:subject_banner]) %>
<label for="subject_banner" class="banner-item-upload" data-toggle="tooltip" data-title="选择图片"></label>
</div>
</div>
<div class="col-12 col-md-4 banner-item">
<%- course_banner = setting.course_banner_url -%>
<div class="banner-item-top">翻转课堂</div>
<div class="banner-item-bottom <%= course_banner ? 'has-img' : '' %>">
<img class="banner-item-img course-banner-img" src="<%= course_banner %>" style="<%= course_banner.present? ? '' : 'display: none' %>"/>
<%= file_field_tag(:course_banner, accept: 'image/*', style: 'display: none', value: params[:course_banner]) %>
<label for="course_banner" class="banner-item-upload" data-toggle="tooltip" data-title="选择图片"></label>
</div>
</div>
<div class="col-12 col-md-4 banner-item">
<%- competition_banner = setting.competition_banner_url -%>
<div class="banner-item-top">在线竞赛</div>
<div class="banner-item-bottom <%= competition_banner ? 'has-img' : '' %>">
<img class="banner-item-img competition-banner-img" src="<%= competition_banner %>" style="<%= competition_banner.present? ? '' : 'display: none' %>"/>
<%= file_field_tag(:competition_banner, accept: 'image/*', style: 'display: none', value: params[:competition_banner]) %>
<label for="competition_banner" class="banner-item-upload" data-toggle="tooltip" data-title="选择图片"></label>
</div>
</div>
<div class="col-12 col-md-4 banner-item">
<%- moop_cases_banner = setting.moop_cases_banner_url -%>
<div class="banner-item-top">教学案例</div>
<div class="banner-item-bottom <%= moop_cases_banner ? 'has-img' : '' %>">
<img class="banner-item-img moop-cases-banner-img" src="<%= moop_cases_banner %>" style="<%= moop_cases_banner.present? ? '' : 'display: none' %>"/>
<%= file_field_tag(:moop_cases_banner, accept: 'image/*', style: 'display: none', value: params[:moop_cases_banner]) %>
<label for="moop_cases_banner" class="banner-item-upload" data-toggle="tooltip" data-title="选择图片"></label>
</div>
</div>
</div>
</div>
<div class="form-group px-2 setting-item">
<div class="setting-item-head"><h6>导航设置</h6></div>
<div class="dropdown-divider"></div>

@ -41,6 +41,9 @@
<%= link_to "/users/#{user.login}", class: 'professional-authentication-avatar', target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } do %>
<img src="/images/<%= url_to_avatar(user) %>" class="rounded-circle" width="40" height="40" />
<% end %>
<% if user.from_sub_site? %>
<span class="badge badge-pill badge-info">合作</span>
<% end %>
</td>
<td><%= user.real_name %></td>
<td><%= raw [user.school_name.presence, user.department_name.presence].compact.join('<br/>') %></td>

@ -31,6 +31,6 @@ if @competition.mode == 2
end
json.permission do
json.editable @user.admin_or_business? || (@competition.nearly_published? && @competition.manager?(@user))
json.editable @user.admin_or_business? || @competition.manager?(@user)
end

@ -30,7 +30,7 @@ json.competitions do
# end
json.permission do
json.editable current_user.admin_or_business? || (competition.nearly_published? && competition.manager?(current_user))
json.editable current_user.admin_or_business? || competition.manager?(current_user)
end
end
end

@ -12,7 +12,7 @@
<div class="col-12 custom-carousel-item custom-carousel-item-<%= image.id %>" data-id="<%= image.id %>">
<div class="border rounded relative p-3 mb-3 drag row align-items-center <%= image.online? ? '' : 'not_active' %>">
<div class="col-2 col-md-1 custom-carousel-item-no"><%= index + 1 %></div>
<div class="col-10 col-md-3 custom-carousel-item-img" data-source-id="<%= image.id %>" data-source-type="PortalImage" data-toggle="modal" data-target=".admin-upload-file-modal">
<div class="col-10 col-md-3 custom-carousel-item-img" data-source-id="<%= image.id %>" data-source-type="PortalImage" data-toggle="modal" data-target=".cooperative-upload-file-modal">
<img src="<%= Util::FileManage.exist?('PortalImage', image.id) ? Util::FileManage.disk_file_url('PortalImage', image.id) : '' %>" data-toggle="tooltip" data-title="重新上传"/>
</div>
<div class="col-10 col-md-7">

@ -0,0 +1 @@
$('.competition-prize-user-list-container .competition-prize-user-item-<%= @prize_user.id %>').html("<%= j( render partial: 'cooperative/competition_prize_users/shared/tr', locals: { prize_user: @prize_user } ) %>");

@ -0,0 +1,64 @@
<%
define_breadcrumbs do
add_breadcrumb('竞赛列表', cooperative_competitions_path)
add_breadcrumb(@competition.name)
end
%>
<div class="box search-form-container flex-column mb-0 pb-0 competition-prize-user-form">
<ul class="nav nav-tabs w-100 search-form-tabs">
<li class="nav-item">
<%= link_to '报名列表', cooperative_competition_enroll_lists_path(@competition), class: "nav-link search-form-tab" %>
</li>
<li class="nav-item">
<%= link_to '获奖证书审批', cooperative_competition_competition_prize_users_path(@competition), class: "nav-link search-form-tab active" %>
</li>
</ul>
<div class="d-flex">
<%= form_tag(cooperative_competition_competition_prize_users_path(unsafe_params), method: :get, class: 'search-form mt-3 flex-1 d-flex align-items-end', remote: true) do %>
<div class="form-group mb-0 mr-3">
<label for="status">奖项:</label>
<% prize_options = [['不限', '']] + @competition.competition_prizes.map{ |p| [p.name, p.id] } %>
<%= select_tag(:prize_id, options_for_select(prize_options), class: 'form-control') %>
</div>
<% auth_options = [['不限', ''], %w(未认证 not_authed), %w(待认证 authing), %w(已认证 authed)] %>
<div class="form-group mb-0 mr-3">
<label for="status">实名认证状态:</label>
<%= select_tag(:real_name_auth, options_for_select(auth_options), class: 'form-control') %>
</div>
<div class="form-group mb-0 mr-3">
<label for="status">职业认证状态:</label>
<%= select_tag(:professional_auth, options_for_select(auth_options), class: 'form-control') %>
</div>
<div class="form-group mb-0 mr-3">
<label for="status">职业:</label>
<%- identity_options = [['不限', ''], %w(教师 0), %w(学生 1)] %>
<%= select_tag(:identity, options_for_select(identity_options), class: 'form-control') %>
</div>
<div class="form-group mb-0 mr-3">
<label for="status">审批状态:</label>
<%- status_options = [['不限', ''], %w(未审批 pending), %w(已审批 approved)] %>
<%= select_tag(:status, options_for_select(status_options), class: 'form-control') %>
</div>
<%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '战队/学校名称检索') %>
<%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
<%= link_to '清除', cooperative_competition_competition_prize_users_path(@competition), class: "btn btn-default",'data-disable-with': '清除中...' %>
<% end %>
<div class="mt-3 d-flex align-items-end">
<%= link_to '导出', cooperative_competition_competition_prize_users_path(competition_id: @competition.id, format: :xlsx), class: 'btn btn-primary' %>
<%#= javascript_void_link '导出', class: 'btn btn-primary', 'data-url': cooperative_competition_competition_prize_users_path(competition_id: @competition.id, format: :xlsx) %>
</div>
</div>
</div>
<div class="box cooperative-list-container competition-prize-user-list-container">
<%= render(partial: 'cooperative/competition_prize_users/shared/list', locals: { prize_users: @prize_users }) %>
</div>

@ -0,0 +1 @@
$('.competition-prize-user-list-container').html("<%= j( render partial: 'cooperative/competition_prize_users/shared/list', locals: { prize_users: @prize_users } ) %>");

@ -0,0 +1,33 @@
wb = xlsx_package.workbook
wb.styles do |s|
blue_cell = s.add_style :bg_color => "FAEBDC", :sz => 10,:height => 25,:b => true, :border => { :style => :thin, :color =>"000000" },:alignment => {wrap_text: true,:horizontal => :center,:vertical => :center}
wb.add_worksheet(name: "#{@competition.name}证书审批列表") do |sheet|
sheet.add_row %w(序号 排名 奖项 战队ID 战队名称 姓名 职业 学号 学校名称 学院名称 地区 实名认证 职业认证 手机号码 队长 签领/开户行及银行卡号 审批时间 审批人), :height => 25,:style => blue_cell
@all_prize_users.each_with_index do |prize_user, index|
user = prize_user.user
data = [
index + 1,
prize_user.rank,
prize_user.competition_prize.name,
prize_user.competition_team_id,
prize_user.competition_team.name,
user.real_name,
user.identity,
user.student_id,
user.school_name,
user.department_name,
user.location,
user.auth_status,
user.pro_status,
user.phone,
prize_user.leader? ? "是" : "-",
[prize_user.extra&.[]('bank'), prize_user.extra&.[]('second_bank'), prize_user.extra&.[]('card_no')].compact.join('/'),
prize_user.approved_at&.strftime('%Y-%m-%d %H:%M'),
prize_user.approver&.real_name
]
sheet.add_row(data)
end
end
end

@ -0,0 +1,33 @@
<table class="table table-hover text-center competition-prize-user-list-table">
<thead class="thead-light">
<tr>
<th width="6%">奖项</th>
<th width="5%">排名</th>
<th width="10%" class="text-left">战队名称</th>
<th width="8%">真实姓名</th>
<th width="5%">职业</th>
<th width="8%">学号</th>
<th width="10%">单位</th>
<th width="5%">实名</th>
<th width="5%">职业</th>
<th width="5%">手机</th>
<th width="5%">队长</th>
<th width="10%">审批时间</th>
<th width="8%">审批人</th>
<th width="10%">操作</th>
</tr>
</thead>
<tbody style="overflow-wrap:break-word;">
<% if prize_users.present? %>
<% prize_users.each do |prize_user| %>
<tr class="competition-prize-user-item-<%= prize_user.id %>">
<%= render('cooperative/competition_prize_users/shared/tr', prize_user: prize_user) %>
</tr>
<% end %>
<% else %>
<%= render 'cooperative/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>
<%= render partial: 'cooperative/shared/paginate', locals: { objects: prize_users } %>

@ -0,0 +1,35 @@
<%- user = prize_user.user -%>
<td><%= prize_user.competition_prize.name %></td>
<td><%= prize_user.rank %></td>
<td class="text-left"><%= prize_user.competition_team.name %></td>
<td><%= user.real_name %></td>
<td><%= user.identity %></td>
<td><%= user.is_teacher? ? user.user_extension.technical_title : user.user_extension.student_id %></td>
<td><%= user.school_name %></td>
<td><%= display_auth_state(user.authentication?, user.process_real_name_apply.present?) %></td>
<td><%= display_auth_state(user.professional_certification?, user.process_professional_apply.present?) %></td>
<td><%= display_auth_state user.phone_binded? %></td>
<td><%= display_auth_state prize_user.leader?, error: '' %></td>
<td><%= display_text prize_user.approved_at&.strftime('%Y-%m-%d %H:%M') %></td>
<td><%= display_text prize_user.approver&.real_name %></td>
<td class="action-container">
<% if prize_user.leader? && prize_user.competition_prize.category == 'bonus' %>
<% bank_content = [prize_user.extra&.[]('bank'), prize_user.extra&.[]('second_bank'), prize_user.extra&.[]('card_no')].compact.join('<br>').presence || '无' %>
<%= javascript_void_link('查看银行账户', data: { toggle: 'tooltip', title: bank_content.html_safe, html: true, placement: 'left', trigger: 'click' }) %>
<% prize_module = prize_user.competition&.competition_modules.find_by(module_type: 'certificate') %>
<% if prize_module %>
<%= link_to('编辑', EduSetting.get("host_name").to_s + "/competitions/#{prize_user.competition&.identifier}?menu=#{prize_module&.id}&user_id=#{user.id}", target: "_blank") %>
<% end %>
<% end %>
<% if prize_user.pending? %>
<%= link_to('审批通过', approve_cooperative_competition_competition_prize_user_path(prize_user.competition, prize_user),
data: { confirm: '确认审批通过吗?' },
remote: true, method: :post, class: 'approve-action') %>
<% else %>
<%= link_to('撤销审批', unapprove_cooperative_competition_competition_prize_user_path(prize_user.competition, prize_user),
data: { confirm: '确认撤销审批吗?' },
remote: true, method: :post, class: 'approve-action') %>
<% end %>
</td>

@ -0,0 +1 @@
$('.competition-prize-user-list-container .competition-prize-user-item-<%= @prize_user.id %>').html("<%= j( render partial: 'cooperative/competition_prize_users/shared/tr', locals: { prize_user: @prize_user } ) %>");

@ -0,0 +1,2 @@
$('.cooperative-modal-container').html("<%= j( render partial: 'cooperative/competition_prizes/shared/save_competition_prize_modal', locals: { prize: @prize, title: '编辑奖项', form_method: 'PATCH' } ) %>");
$('.modal.cooperative-save-competition-prize-modal').modal('show');

@ -0,0 +1 @@
$('#competition-prize-card .competition-prize-table tbody').html("<%= j( render partial: 'cooperative/competition_settings/shared/competition_prizes', locals: { competition: @competition } ) %>");

@ -0,0 +1,2 @@
$('.cooperative-modal-container').html("<%= j( render partial: 'cooperative/competition_prizes/shared/save_competition_prize_modal', locals: { prize: @prize, title: '新建奖项', form_method: 'POST' } ) %>");
$('.modal.cooperative-save-competition-prize-modal').modal('show');

@ -0,0 +1,29 @@
<div class="modal fade cooperative-save-competition-prize-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><%= title ||= '保存奖项设置' %></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<%= simple_form_for([:cooperative, prize.competition, prize], data: { form_method: form_method }, html: { class: 'cooperative-save-competition-prize-form' }, defaults: { wrapper_html: { class: 'offset-md-1 col-md-10' } }) do |f| %>
<%= f.input :name, label: '奖项名称:', placeholder: '请输入奖项名称' %>
<%= f.input :num, as: :integer, label: '数量:', placeholder: '请输入数量', input_html: { min: 1 } %>
<%= f.input :category, label: '奖励类型:' do %>
<%= f.select :category, [%w(奖金 bonus), %w(无 unset)], {}, class: 'form-control' %>
<% end %>
<div class="error text-danger"></div>
<% end %>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary submit-btn">确认</button>
</div>
</div>
</div>
</div>

@ -0,0 +1,503 @@
<%
define_breadcrumbs do
add_breadcrumb('竞赛列表', cooperative_competitions_path)
add_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_cooperative_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 %H:%M'), autocomplete: 'off', class: 'form-control start-date mx-0 mr-2', placeholder: '竞赛开始时间' %>
<%= text_field_tag :end_time, @competition.end_time&.strftime('%Y-%m-%d %H:%M'), autocomplete: 'off', class: 'form-control end-date mx-0', placeholder: '竞赛截止时间' %>
</div>
</div>
<input type="hidden" name="mode" id="mode_1" value="1" class="form-radio-input" checked="checked">
<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 d-flex mb-2">
<div class="col-1 text-right">
管理员
</div>
<div class="col-5 text-left sponsorPanel">
<% manager_data = @competition.managers.map { |u| [u.name, u.id] } %>
<%= select_tag :manager_ids, options_for_select(manager_data, @competition.managers.map(&:id)), class: 'form-control manager-select', multiple: true %>
</div>
</div>
<div class="row align-items-center d-flex mb-2">
<div class="col-1 text-right">
主办方
</div>
<div class="col-5 text-left sponsorPanel">
<% sponsor_data = @competition.sponsor_schools.map { |s| [s.school.name, s.school.id] } %>
<%= select_tag :sponsor_schools, options_for_select(sponsor_data, @competition.sponsor_schools.map(&:school_id)), class: 'form-control sponsor-select', multiple: true %>
</div>
</div>
<div class="row align-items-center d-flex mb-2">
<div class="col-1 text-right">
开放范围
</div>
<div class="col-5 text-left sponsorPanel">
<% region_data = @competition.region_schools.map { |s| [s.school.name, s.school.id] } %>
<%= select_tag :region_schools, options_for_select(region_data, @competition.region_schools.map(&:school_id)), class: 'form-control allow-school-select', multiple: true %>
</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">
<%= form_tag(nav_setting_cooperative_competition_competition_settings_path(@competition), method: :post, class: 'nav-setting-form flex-1', remote: true) do %>
<div class="container competition-mode-container">
<% @competition.competition_modules.each do |com_module| %>
<% case com_module.module_type %>
<% when 'home' %>
<div class="mt-3" id="MD_typeFrom">
<div class="row MD_type">
<div class="col-1 text-right">
<label class="checkbox checkbox-primary mt-1">
<input type="checkbox" name="navbar[][hidden]" value="0" hidden class="font-16" checked="checked">
<input type="checkbox" disabled="disabled" class="font-16" checked="checked">
</label>
</div>
<div class="col-md-4">
<input type="hidden" value="<%= com_module.id %>" name="navbar[][module_id]">
<%= 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>
<% when 'enroll' %>
<div class="row mt-2">
<div class="col-1 text-right">
<label class="checkbox checkbox-primary mt-1">
<input type="checkbox" name="navbar[][hidden]" value="<%= com_module.hidden ? 1 : 0 %>" hidden class="font-16" checked="checked">
<%= check_box_tag('', 0, !com_module.hidden, id: nil, class: 'font-16 module_hidden') %>
</label>
</div>
<div class="col-md-8 color-blue mt-1">
<input type="hidden" value="<%= com_module.id %>" name="navbar[][module_id]">
<input type="hidden" value="<%= com_module.module_type %>" name="navbar[][module_type]">
<input type="hidden" value="报名" name="navbar[][name]">
报名
</div>
</div>
<div class="row mt-2 align-items-center">
<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">
<%= text_field_tag :enroll_end_time, @competition.enroll_end_time&.strftime('%Y-%m-%d %H:%M'), autocomplete: 'off', class: 'form-control enroll_end_time', placeholder: '报名截止时间' %>
</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">
<%= javascript_void_link '+', class: 'btn btn-primary waves-effect waves-light btn-xs setBtn_s addRequireBtn' %>
</div>
</div>
<div id="requireForm" class="competition-staff-settings">
<% @competition.competition_staffs.each do |staff| %>
<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="competition_staffs[][minimum]" value="<%= staff.minimum %>" />
</div>
<span class="mt-2">~</span>
<div class="col-1 mt-1">
<input type="text" class="form-control" name="competition_staffs[][maximum]" value="<%= staff.maximum %>" />
</div>
<span class="mt-2">人</span>
<div class="col-2 mt-1">
<select class="form-control" name="competition_staffs[][category]">
<option value="student" <%= staff.category == "student" ? "selected='selected'" : "" %>>学生</option>
<option value="teacher" <%= staff.category == "teacher" ? "selected='selected'" : "" %>>教师</option>
</select>
</div>
<div class="col-2 mt-1">
<label class="radio checkbox-primary mt-1" value="require_1_1">
<input id="require_1_<%= staff.id %>" <%= staff.mutiple_limited? ? '' : 'checked="checked"' %> class="mutiple-limited-radio" value="false" name="competition_staffs[][mutiple_limited]" type="checkbox">
<label for="require_1_<%= staff.id %>">可多次报名</label>
</label>
</div>
<div class="col-2 mt-1">
<label class="radio checkbox-primary mt-1" value="require_1_2">
<input id="require_2_<%= staff.id %>" <%= staff.mutiple_limited? ? 'checked="checked"' : '' %> class="mutiple-limited-radio" value="true" name="competition_staffs[][mutiple_limited]" type="checkbox">
<label for="require_2_<%= staff.id %>">不可多次报名</label>
</label>
<a href="javascript:void(0)" class="ml20 delRequrieBtn">
<i class="fa fa-times-circle font-20 color-grey-c"></i>
</a>
</div>
</div>
<% end %>
</div>
<% when 'inform', 'chart', 'resource', 'certificate' %>
<div class="row mt-2 new_module_div linkFormItem">
<div class="col-1 text-right">
<label class="checkbox checkbox-primary mt-1">
<input type="checkbox" name="navbar[][hidden]" value="<%= com_module.hidden ? 1 : 0 %>" hidden class="font-16" checked="checked">
<%= check_box_tag('', 0, !com_module.hidden, id: nil, class: 'font-16 module_hidden') %>
</label>
</div>
<div class="col-md-label mt-2">
<input type="hidden" value="<%= com_module.id %>" name="navbar[][module_id]">
<input type="hidden" value="<%= com_module.module_type %>" name="navbar[][module_type]">
<input type="hidden" value="<%= com_module.name %>" name="navbar[][name]">
<%= com_module.name %>
</div>
<div class="col-md-1 mt-1">
<%= text_field_tag('navbar[][position]', com_module.position, id: nil, class: 'form-control', placeholder: '位置') %>
</div>
<% if com_module.module_type == "resource" %>
<div class="col-md-3 mt-1">
<%= text_field_tag('navbar[][url]', com_module.url, id: nil, class: 'form-control', placeholder: '请输入资料下载地址') %>
</div>
<%= javascript_void_link '+', class: 'mt-1 btn btn-primary waves-effect waves-light btn-xs setBtn_s add_linkBtn' %>
<% end %>
</div>
<% else %>
<div class="row mt-2 align-items-center linkFormItem">
<div class="col-1 text-right">
<label class="checkbox checkbox-primary mt-1">
<input type="checkbox" name="navbar[][hidden]" value="<%= com_module.hidden ? 1 : 0 %>" hidden class="font-16" checked="checked">
<%= check_box_tag('', 0, !com_module.hidden, id: nil, class: 'font-16 module_hidden') %>
</label>
</div>
<div class="col-md-label mt-1">
<input type="hidden" value="<%= com_module.id %>" name="navbar[][module_id]">
<input type="hidden" value="<%= com_module.module_type %>" name="navbar[][module_type]">
<%= text_field_tag('navbar[][name]', com_module.name, id: nil, class: 'form-control', placeholder: '请输入模块名称') %>
</div>
<div class="col-md-1 mt-1">
<%= text_field_tag('navbar[][position]', com_module.position, id: nil, class: 'form-control', placeholder: '位置') %>
</div>
<div class="col-md-3 mt-1">
<%= text_field_tag('navbar[][url]', com_module.url, id: nil, class: 'form-control', placeholder: '请输入资料下载地址') %>
</div>
<%= javascript_void_link '+', class: 'mt-1 btn btn-primary waves-effect waves-light btn-xs setBtn_s add_linkBtn' %>
<%= javascript_void_link '×', class: 'mt-1 btn btn-icon waves-effect btn-default waves-light setBtn_s ml10 del_linkBtn' %>
</div>
<% end %>
<% end %>
<div class="error my-2 danger text-danger"></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>
<% end %>
</div>
</div>
<%# if @competition.mode == 1 %>
<div class="card mb-5 competition-chart-stages">
<div class="card-header d-flex justify-content-between align-items-center">
<span>排行榜设置</span>
<i class="fa fa-question-circle font-14" data-toggle="tooltip" data-html="true" data-placement="top" title="温馨提示:<br/>
需要参赛选手完成几个实训,请相应的创建几个阶段;<br/>
若多个实训只需要参赛选手选择完成1个请将其配置在同一个阶段<br/>
一个阶段多个实训时,请注意实训关卡数及各关卡的经验值设置保持相同;<br/>
同阶段的各个实训,请保持相同的成绩来源;<br/>
每阶段每个实训关卡的得分,将按照组内成员最高得分计算,总分为各阶段得分加和。"></i>
<a href="javascript:void(0)" class="btn btn-primary btn-custom waves-effect add-new-tab waves-light ml20" data-competition-id="<%= @competition.id %>">+ 新增tab</a>
<span class="flex-1 text-right color-orange">实训ID填写示例实训地址为https://www.educoder.net/shixuns/u5plmgka/challenges则填写“u5plmgka”</span>
</div>
<div class="card-body">
<div id="large_panel" class="large_panel competition-chart-setting">
<% if @competition.competition_stages.count > 0 %>
<% @competition.competition_stages.includes(competition_stage_sections: :competition_entries).each_with_index do |stage, index| %>
<%= form_tag(cooperative_competition_competition_stage_path(competition_id: @competition.id, id: stage.id), method: :put, class: 'stage-update-form flex-1', remote: true) do %>
<div class="large_panel_part" attr_line="<%= index + 1 %>">
<div class="row d-flex">
<span class="col-1 mt-2">tab标题</span>
<div class="col-2 no_padding">
<input type="text" class="form-control" name="stage_name" value="<%= stage.name %>" />
</div>
<span class="col-1 text-right mt-2 no_padding">总排行榜占比:</span>
<div class="col-1 no_padding">
<input type="number" class="form-control" name="score_rate" value="<%= (stage.score_rate * 100).to_i %>"/>
</div><span class=" mt-2">%</span>
<div class="flex-1">
<a href="javascript:void(0)" class="btn btn-outline-primary export-action ml20 add-task-sub">新增子阶段</a>
<% if stage.max_end_time && stage.max_end_time > Time.now %>
<%= agree_link '发送短信提醒', send_message_cooperative_competition_competition_stage_path(@competition, stage, element: ".send-message-#{stage.id}"),
class: 'btn btn-outline-primary ml20', 'data-confirm': '确认执行发送短信操作?' %>
<% end %>
<% if stage.max_end_time && stage.max_end_time < Time.now && Time.now < Time.at(stage.max_end_time.to_i + 30*24*3600) %>
<%= agree_link '计算成绩', calculate_stage_score_cooperative_competition_competition_stage_path(@competition, stage, element: ".calculate-score-#{stage.id}"),
class: 'btn btn-outline-primary ml20', 'data-confirm': '确认执行计算成绩操作?' %>
<% end %>
</div>
<%= delete_link '删除', cooperative_competition_competition_stage_path(competition_id: @competition.id, id: stage.id), class: 'btn btn-default delete-stage ml20' %>
<a href="javascript:void(0)" class="btn btn-outline-primary export-action update-stage ml20">保存</a>
</div>
<div id="small_panel_<%= index + 1 %>" class="small_panel">
<% stage.competition_stage_sections.each_with_index do |section, j| %>
<div class="row d-flex small_panel_item" attr_line="sub_<%= index %>_<%= j %>" count="<%= j + 1 %>">
<span class="mt-2 subName mr10">第<%= j + 1 %>阶段</span>
<div class="flex-1">
<div class="row">
<div class="row col-6">
<span class="ml20 mt-2 mr10">有效时间:</span>
<div class="col-4 no_padding">
<%= text_field_tag 'stage[][start_time]', section.start_time&.strftime('%Y-%m-%d %H:%M'), autocomplete: 'off', class: 'section-start-time form-control', placeholder: '有效开始时间' %>
</div>
<span class="mt-2">~</span>
<div class="col-4 no_padding">
<%= text_field_tag 'stage[][end_time]', section.end_time&.strftime('%Y-%m-%d %H:%M'), autocomplete: 'off', class: 'section-end-time form-control', placeholder: '有效结束时间' %>
</div>
</div>
<div class="row col-3">
<span class="col-4 text-right mt-2 no_padding mr10">总任务数:</span>
<div class="col-6 no_padding">
<input type="number" class="form-control task_all" value="<%= section.entry %>" onchange="change_total(this)" name="stage[][entry]"/>
</div>
</div>
<div class="row col-3">
<span class="col-4 text-right mt-2 no_padding mr10">成绩来源:</span>
<div class="col-6 no_padding">
<select class="form-control" name="stage[][score_source]">
<option value="0" <%= section.score_source == 0 ? "selected='selected'" : "" %>>经验值</option>
<option value="1" <%= section.score_source == 1 ? "selected='selected'" : "" %>>预测准确率</option>
</select>
</div>
</div>
</div>
<div class="row mt-2" id="task_Input_sub_<%= index %>_<%= j %>">
<% section.competition_entries.each_with_index do |entry, z| %>
<div class="col-4 row task_Input_div">
<span class="col-4 text-right mt-3 no_padding mr10">任务<%= z+1 %></span>
<div class="col-6 no_padding">
<input type="text" class="form-control mt-2" value="<%= entry.shixun_identifier %>" name="stage[][identifiers][]" placeholder="请填写实训ID">
</div>
</div>
<% end %>
</div>
</div>
<span>
<a href="javascript:void(0)" class="btn btn-default ml20 small_panel_item_del">删除</a>
</span>
</div>
<% end %>
</div>
<div class="error my-2 danger text-danger"></div>
</div>
<% end %>
<% end %>
<% else %>
<%= form_tag(cooperative_competition_competition_stages_path(competition_id: @competition.id), method: :post, class: 'stage-update-form new-stage-form flex-1', remote: true) do %>
<div class="large_panel_part" attr_line="1">
<div class="row d-flex">
<span class="col-1 mt-2">tab标题</span>
<div class="col-2 no_padding">
<input type="text" class="form-control" name="stage_name"/>
</div>
<span class="col-1 text-right mt-2 no_padding">总排行榜占比:</span>
<div class="col-1 no_padding">
<input type="number" class="form-control" name="score_rate" value="100"/>
</div><span class=" mt-2">%</span>
<div class="flex-1">
<a href="javascript:void(0)"class="btn btn-outline-primary export-action ml20 add-task-sub">新增子阶段</a>
</div>
<a href="javascript:void(0)" class="btn btn-default ml20" onclick="Del_tab(this)">删除</a>
<a href="javascript:void(0)" class="btn btn-outline-primary update-stage export-action ml20">保存</a>
</div>
<div id="small_panel_1" class="small_panel">
<div class="row d-flex small_panel_item" attr_line="sub_new_new" count="1">
<span class="mt-2 subName mr10">第1阶段</span>
<div class="flex-1">
<div class="row">
<div class="row col-6">
<span class="mt-2 ml20 mr10">有效时间:</span>
<div class="col-4 no_padding">
<%= text_field_tag 'stage[][start_time]', '', autocomplete: 'off', class: 'section-start-time form-control', placeholder: '有效开始时间' %>
</div>
<span class="mt-2">~</span>
<div class="col-4 no_padding">
<%= text_field_tag 'stage[][end_time]', '', autocomplete: 'off', class: 'section-end-time form-control', placeholder: '有效结束时间' %>
</div>
</div>
<div class="row col-3">
<span class="col-4 text-right mt-2 no_padding mr10">总任务数:</span>
<div class="col-6 no_padding">
<input type="number" class="form-control task_all" onchange="change_total(this)" value="3" name="stage[][entry]"/>
</div>
</div>
<div class="row col-3">
<span class="col-4 text-right mt-2 no_padding mr10">成绩来源:</span>
<div class="col-6 no_padding">
<select class="form-control" name="stage[][score_source]">
<option value="0">经验值</option>
<option value="1">预测准确率</option>
</select>
</div>
</div>
</div>
<div class="row mt-2" id="task_Input_sub_new_new">
<div class="col-4 row task_Input_div">
<span class="col-4 text-right mt-3 no_padding mr10">任务1</span>
<div class="col-6 no_padding">
<input type="text" class="form-control mt-2" name="stage[][identifiers][]" placeholder="请填写实训ID">
</div>
</div>
<div class="col-4 row task_Input_div">
<span class="col-4 text-right mt-3 no_padding mr10">任务2</span>
<div class="col-6 no_padding">
<input type="text" class="form-control mt-2" name="stage[][identifiers][]" placeholder="请填写实训ID">
</div>
</div>
<div class="col-4 row task_Input_div">
<span class="col-4 text-right mt-3 no_padding mr10">任务3</span>
<div class="col-6 no_padding">
<input type="text" class="form-control mt-2" name="stage[][identifiers][]" placeholder="请填写实训ID">
</div>
</div>
</div>
</div>
<span>
<a href="javascript:void(0)" class="btn btn-default ml20 small_panel_item_del">删除</a>
</span>
</div>
</div>
</div>
<% end %>
<% end %>
<div class="error my-2 danger text-danger"></div>
</div>
</div>
</div>
<div class="card mb-5" id="competition-prize-card" data-id="<%= @competition.id %>">
<div class="card-header d-flex justify-content-between align-items-center">
<span class="flex-1">奖项配置</span>
<% unless @competition.competition_prize_users.exists? %>
<%= link_to '新增奖项', new_cooperative_competition_competition_prize_path(@competition), remote: true, class: 'btn btn-primary btn-sm add-competition-prize-btn' %>
<% end %>
<% if @competition.finished? && !@competition.competition_prize_users.exists? %>
<%= javascript_void_link '生成获奖记录', class: 'btn btn-primary btn-sm ml-2 generate-prize-user-action' %>
<% end %>
</div>
<div class="card-body row pt-0">
<table class="table text-center competition-prize-table">
<thead>
<th width="8%">序号</th>
<th width="10%">奖项名称</th>
<th width="10%">数量</th>
<th width="10%">奖励类型</th>
<th width="16%">队员个人证书模板</th>
<th width="16%">团队证书模板</th>
<th width="16%">指导老师证书模板</th>
<th width="14%">操作</th>
</thead>
<tbody>
<%= render 'cooperative/competition_settings/shared/competition_prizes', competition: @competition %>
</tbody>
</table>
</div>
</div>
<div style="margin-bottom: 8.5rem;"></div>
<%# end %>
<%= render partial: 'cooperative/shared/modal/upload_file_modal', locals: { title: '上传证书模板', accept: 'image/*' } %>

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

Loading…
Cancel
Save