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

dev_sync_trustie
杨树林 5 years ago
commit 07a8062ac9

@ -1,6 +1,13 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-carousels-index-page').length > 0) {
var laboratoryId = $('#carousels-container').data('laboratoryId');
var resetNo = function(){
$('#carousels-container .custom-carousel-item-no').each(function(index, ele){
$(ele).html(index + 1);
})
}
// 删除后
$(document).on('delete_success', resetNo);
// ------------ 保存链接 -----------
$('.carousels-card').on('click', '.save-data-btn', function(){
@ -67,9 +74,7 @@ $(document).on('turbolinks:load', function() {
dataType: 'json',
data: { move_id: moveId, after_id: insertId },
success: function(data){
$('#carousels-container .custom-carousel-item-no').each(function(index, ele){
$(ele).html(index + 1);
})
resetNo();
},
error: function(res){
var data = res.responseJSON;

@ -0,0 +1,73 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-competitions-index-page').length > 0) {
$('.modal.admin-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('重新上传');
});
}
$(".admin-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: "/admins/competitions/hot_setting",
type: "POST",
dataType:'json',
data: json,
success: function(){
$.notify({ message: '操作成功' });
}
});
});
// ============== 新增竞赛 ===============
var $modal = $('.modal.admin-create-competition-modal');
var $form = $modal.find('form.admin-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);
}
});
}
});
});

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

@ -4,14 +4,17 @@ $(document).on('turbolinks:load', function() {
var $form = $modal.find('form.admin-upload-file-form')
var $sourceIdInput = $modal.find('input[name="source_id"]');
var $sourceTypeInput = $modal.find('input[name="source_type"]');
var $suffixInput = $modal.find('input[name="suffix"]');
$modal.on('show.bs.modal', function(event){
var $link = $(event.relatedTarget);
var sourceId = $link.data('sourceId');
var sourceType = $link.data('sourceType');
var suffix = $link.data('suffix');
$sourceIdInput.val(sourceId);
$sourceTypeInput.val(sourceType);
if(suffix != undefined){ $suffixInput.val(suffix); }
$modal.find('.upload-file-input').trigger('click');
});
@ -48,6 +51,7 @@ $(document).on('turbolinks:load', function() {
contentType: false,
success: function(data){
$.notify({ message: '上传成功' });
$modal.find('.file-names').html('');
$modal.trigger('upload:success', data);
$modal.modal('hide');
},

@ -34,10 +34,17 @@ $(document).on('turbolinks:load', function() {
});
$('.modal.admin-upload-file-modal').on('upload:success', function(e, data){
if(data.suffix == '_weapp'){
var $imageElement = $('.shixun-weapp-image-' + data.source_id);
$imageElement.attr('src', data.url);
$imageElement.show();
$imageElement.next().html('重新上传');
} else {
var $imageElement = $('.shixun-image-' + data.source_id);
$imageElement.attr('src', data.url);
$imageElement.show();
$imageElement.next().html('重新上传');
}
})
}
});

@ -0,0 +1,73 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-user-statistics-index-page').length > 0) {
var $form = $('.user-statistic-list-form');
// ************** 学校选择 *************
var matcherFunc = function(params, data){
if ($.trim(params.term) === '') {
return data;
}
if (typeof data.text === 'undefined') {
return null;
}
if (data.name && data.name.indexOf(params.term) > -1) {
var modifiedData = $.extend({}, data, true);
return modifiedData;
}
// Return `null` if the term should not be displayed
return null;
}
var defineSchoolSelect = function (schools) {
$form.find('.school-select').select2({
theme: 'bootstrap4',
placeholder: '选择学校/单位',
minimumInputLength: 1,
data: schools,
templateResult: function (item) {
if(!item.id || item.id === '') return item.text;
return item.name;
},
templateSelection: function(item){
if (item.id) {
$form.find('#school_id').val(item.id);
}
return item.name || item.text;
},
matcher: matcherFunc
});
};
// 初始化学校选择器
$.ajax({
url: '/api/schools/for_option.json',
dataType: 'json',
type: 'GET',
success: function(data) {
defineSchoolSelect(data.schools);
}
});
// 清空
$form.on('click', '.clear-btn', function(){
$form.find('select[name="date"]').val('');
$form.find('.school-select').val('').trigger('change');
$form.find('input[type="submit"]').trigger('click');
})
// 导出
$('.export-action').on('click', function(){
var form = $(".user-statistic-list-form .search-form")
var exportLink = $(this);
var date = form.find("select[name='date']").val();
var schoolId = form.find('input[name="school_id"]').val();
var url = exportLink.data("url").split('?')[0] + "?date=" + date + "&school_id=" + schoolId;
window.open(url);
});
}
});

@ -0,0 +1,124 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-weapp-adverts-index-page').length > 0) {
var resetNo = function(){
$('#adverts-container .advert-item-no').each(function(index, ele){
$(ele).html(index + 1);
})
}
// ------------ 保存链接 -----------
$('.adverts-card').on('click', '.save-data-btn', function(){
var $link = $(this);
var id = $link.data('id');
var link = $('.advert-item-' + id).find('.link-input').val();
$link.attr('disabled', true);
$.ajax({
url: '/admins/weapp_adverts/' + id,
method: 'PATCH',
dataType: 'json',
data: { link: link },
success: function(data){
$.notify({ message: '操作成功' });
},
error: ajaxErrorNotifyHandler,
complete: function(){
$link.removeAttr('disabled');
}
})
});
// -------------- 是否在首页展示 --------------
$('.adverts-card').on('change', '.online-check-box', function(){
var $checkbox = $(this);
var id = $checkbox.data('id');
var checked = $checkbox.is(':checked');
$checkbox.attr('disabled', true);
$.ajax({
url: '/admins/weapp_adverts/' + id,
method: 'PATCH',
dataType: 'json',
data: { online: checked },
success: function(data){
$.notify({ message: '保存成功' });
var box = $('.advert-item-' + id).find('.drag');
if(checked){
box.removeClass('not_active');
}else{
box.addClass('not_active');
}
},
error: ajaxErrorNotifyHandler,
complete: function(){
$checkbox.removeAttr('disabled');
}
})
});
// ------------ 拖拽 -------------
var onDropFunc = function(el, _target, _source, sibling){
var moveId = $(el).data('id');
var insertId = $(sibling).data('id') || '';
$.ajax({
url: '/admins/weapp_adverts/drag',
method: 'POST',
dataType: 'json',
data: { move_id: moveId, after_id: insertId },
success: function(data){
resetNo();
},
error: function(res){
var data = res.responseJSON;
$.notify({message: '移动失败,原因:' + data.message}, {type: 'danger'});
}
})
};
var ele1 = document.getElementById('adverts-container');
dragula([ele1], { mirrorContainer: ele1 }).on('drop', onDropFunc);
// ----------- 新增 --------------
var $createModal = $('.modal.admin-add-weapp-advert-modal');
var $createForm = $createModal.find('form.admin-add-weapp-advert-form');
$createForm.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
"weapp_settings_advert[image]": {
required: true
}
}
});
$createModal.on('show.bs.modal', function(event){
resetFileInputFunc($createModal.find('.img-file-input'));
$createModal.find('.file-names').html('选择文件');
});
$createModal.on('click', '.submit-btn', function() {
$createForm.find('.error').html('');
if ($createForm.valid()) {
$createForm.submit();
} else {
$createForm.find('.error').html('请选择图片');
}
});
$createModal.on('change', '.img-file-input', function(){
var file = $(this)[0].files[0];
$createModal.find('.file-names').html(file ? file.name : '请选择文件');
})
// -------------- 重新上传图片 --------------
//replace_image_url
$('.modal.admin-upload-file-modal').on('upload:success', function(e, data){
var $advertItem = $('.advert-item-' + data.source_id);
$advertItem.find('.advert-item-img img').attr('src', data.url);
})
// 删除后
$(document).on('delete_success', resetNo)
}
})

@ -0,0 +1,123 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-weapp-carousels-index-page').length > 0) {
var resetNo = function(){
$('#carousels-container .custom-carousel-item-no').each(function(index, ele){
$(ele).html(index + 1);
})
}
// ------------ 保存链接 -----------
$('.carousels-card').on('click', '.save-data-btn', function(){
var $link = $(this);
var id = $link.data('id');
var link = $('.custom-carousel-item-' + id).find('.link-input').val();
$link.attr('disabled', true);
$.ajax({
url: '/admins/weapp_carousels/' + id,
method: 'PATCH',
dataType: 'json',
data: { link: link },
success: function(data){
$.notify({ message: '操作成功' });
},
error: ajaxErrorNotifyHandler,
complete: function(){
$link.removeAttr('disabled');
}
})
});
// -------------- 是否在首页展示 --------------
$('.carousels-card').on('change', '.online-check-box', function(){
var $checkbox = $(this);
var id = $checkbox.data('id');
var checked = $checkbox.is(':checked');
$checkbox.attr('disabled', true);
$.ajax({
url: '/admins/weapp_carousels/' + id,
method: 'PATCH',
dataType: 'json',
data: { online: checked },
success: function(data){
$.notify({ message: '保存成功' });
var box = $('.custom-carousel-item-' + id).find('.drag');
if(checked){
box.removeClass('not_active');
}else{
box.addClass('not_active');
}
},
error: ajaxErrorNotifyHandler,
complete: function(){
$checkbox.removeAttr('disabled');
}
})
});
// ------------ 拖拽 -------------
var onDropFunc = function(el, _target, _source, sibling){
var moveId = $(el).data('id');
var insertId = $(sibling).data('id') || '';
$.ajax({
url: '/admins/weapp_carousels/drag',
method: 'POST',
dataType: 'json',
data: { move_id: moveId, after_id: insertId },
success: function(data){
resetNo();
},
error: function(res){
var data = res.responseJSON;
$.notify({message: '移动失败,原因:' + data.message}, {type: 'danger'});
}
})
};
var ele1 = document.getElementById('carousels-container');
dragula([ele1], { mirrorContainer: ele1 }).on('drop', onDropFunc);
// ----------- 新增 --------------
var $createModal = $('.modal.admin-add-weapp-carousel-modal');
var $createForm = $createModal.find('form.admin-add-weapp-carousel-form');
$createForm.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
"weapp_settings_carousel[image]": {
required: true
}
}
});
$createModal.on('show.bs.modal', function(event){
resetFileInputFunc($createModal.find('.img-file-input'));
$createModal.find('.file-names').html('选择文件');
});
$createModal.on('click', '.submit-btn', function() {
$createForm.find('.error').html('');
if ($createForm.valid()) {
$createForm.submit();
} else {
$createForm.find('.error').html('请选择图片');
}
});
$createModal.on('change', '.img-file-input', function(){
var file = $(this)[0].files[0];
$createModal.find('.file-names').html(file ? file.name : '请选择文件');
})
// -------------- 重新上传图片 --------------
//replace_image_url
$('.modal.admin-upload-file-modal').on('upload:success', function(e, data){
var $carouselItem = $('.custom-carousel-item-' + data.source_id);
$carouselItem.find('.custom-carousel-item-img img').attr('src', data.url);
})
// 删除后
$(document).on('delete_success', resetNo)
}
})

@ -0,0 +1,130 @@
$(document).on('turbolinks:load', function() {
if ($('body.cooperative-carousels-index-page').length > 0) {
var resetNo = function(){
$('#carousels-container .custom-carousel-item-no').each(function(index, ele){
$(ele).html(index + 1);
})
}
// 删除后
$(document).on('delete_success', resetNo);
// ------------ 保存链接 -----------
$('.carousels-card').on('click', '.save-data-btn', function(){
var $link = $(this);
var id = $link.data('id');
var link = $('.custom-carousel-item-' + id).find('.link-input').val();
var name = $('.custom-carousel-item-' + id).find('.name-input').val();
if(!name || name.length == 0){
$.notify({ message: '名称不能为空' },{ type: 'danger' });
return;
}
$link.attr('disabled', true);
$.ajax({
url: '/cooperative/carousels/' + id,
method: 'PATCH',
dataType: 'json',
data: { link: link, name: name },
success: function(data){
$.notify({ message: '操作成功' });
},
error: ajaxErrorNotifyHandler,
complete: function(){
$link.removeAttr('disabled');
}
})
});
// -------------- 是否在首页展示 --------------
$('.carousels-card').on('change', '.online-check-box', function(){
var $checkbox = $(this);
var id = $checkbox.data('id');
var checked = $checkbox.is(':checked');
$checkbox.attr('disabled', true);
$.ajax({
url: '/cooperative/carousels/' + id,
method: 'PATCH',
dataType: 'json',
data: { status: checked },
success: function(data){
$.notify({ message: '保存成功' });
var box = $('.custom-carousel-item-' + id).find('.drag');
if(checked){
box.removeClass('not_active');
}else{
box.addClass('not_active');
}
},
error: ajaxErrorNotifyHandler,
complete: function(){
$checkbox.removeAttr('disabled');
}
})
});
// ------------ 拖拽 -------------
var onDropFunc = function(el, _target, _source, sibling){
var moveId = $(el).data('id');
var insertId = $(sibling).data('id') || '';
$.ajax({
url: '/cooperative/carousels/drag',
method: 'POST',
dataType: 'json',
data: { move_id: moveId, after_id: insertId },
success: function(data){
resetNo();
},
error: function(res){
var data = res.responseJSON;
$.notify({message: '移动失败,原因:' + data.message}, {type: 'danger'});
}
})
};
var ele1 = document.getElementById('carousels-container');
dragula([ele1], { mirrorContainer: ele1 }).on('drop', onDropFunc);
// ----------- 新增 --------------
var $createModal = $('.modal.cooperative-add-carousel-modal');
var $createForm = $createModal.find('form.cooperative-add-carousel-form');
$createForm.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
"portal_image[image]": {
required: true
},
"portal_image[name]": {
required: true
},
}
});
$createModal.on('show.bs.modal', function(event){
resetFileInputFunc($createModal.find('.img-file-input'));
$createModal.find('.file-names').html('选择文件');
});
$createModal.on('click', '.submit-btn', function() {
$createForm.find('.error').html('');
if ($createForm.valid()) {
$createForm.submit();
} else {
$createForm.find('.error').html('请选择图片');
}
});
$createModal.on('change', '.img-file-input', function(){
var file = $(this)[0].files[0];
$createModal.find('.file-names').html(file ? file.name : '请选择文件');
})
// -------------- 重新上传图片 --------------
//replace_image_url
$('.modal.cooperative-upload-file-modal').on('upload:success', function(e, data){
var $carouselItem = $('.custom-carousel-item-' + data.source_id);
$carouselItem.find('.custom-carousel-item-img img').attr('src', data.url);
})
}
})

@ -0,0 +1,62 @@
$(document).on('turbolinks:load', function() {
if ($('body.cooperative-laboratory-users-index-page').length > 0) {
// ============= 添加管理员 ==============
var $addMemberModal = $('.cooperative-add-laboratory-user-modal');
var $addMemberForm = $addMemberModal.find('.cooperative-add-laboratory-user-form');
var $memberSelect = $addMemberModal.find('.laboratory-user-select');
$addMemberModal.on('show.bs.modal', function(event){
$memberSelect.select2('val', ' ');
});
$memberSelect.select2({
theme: 'bootstrap4',
placeholder: '请输入要添加的管理员姓名',
multiple: true,
minimumInputLength: 1,
ajax: {
delay: 500,
url: '/cooperative/users',
dataType: 'json',
data: function(params){
return { name: params.term };
},
processResults: function(data){
return { results: data.users }
}
},
templateResult: function (item) {
if(!item.id || item.id === '') return item.text;
return item.real_name + "--" + item.identity;
},
templateSelection: function(item){
if (item.id) {
}
return item.real_name || item.text;
}
});
$addMemberModal.on('click', '.submit-btn', function(){
$addMemberForm.find('.error').html('');
var memberIds = $memberSelect.val();
if (memberIds && memberIds.length > 0) {
$.ajax({
method: 'POST',
dataType: 'json',
url: '/cooperative/laboratory_users',
data: { user_ids: memberIds },
success: function(data){
if(data && data.status == 0){
show_success_flash();
$addMemberModal.modal('hide');
window.location.reload();
}
}
});
} else {
$addMemberModal.modal('hide');
}
});
}
});

@ -0,0 +1,62 @@
$(document).on('turbolinks:load', function() {
var $modal = $('.modal.cooperative-upload-file-modal');
if ($modal.length > 0) {
var $form = $modal.find('form.cooperative-upload-file-form')
var $sourceIdInput = $modal.find('input[name="source_id"]');
var $sourceTypeInput = $modal.find('input[name="source_type"]');
$modal.on('show.bs.modal', function(event){
var $link = $(event.relatedTarget);
var sourceId = $link.data('sourceId');
var sourceType = $link.data('sourceType');
$sourceIdInput.val(sourceId);
$sourceTypeInput.val(sourceType);
$modal.find('.upload-file-input').trigger('click');
});
$modal.find('.upload-file-input').on('change', function(e){
var file = $(this)[0].files[0];
if(file){
$modal.find('.file-names').html(file.name);
$modal.find('.submit-btn').trigger('click');
}
})
var formValid = function(){
if($form.find('input[name="file"]').val() == undefined || $form.find('input[name="file"]').val().length == 0){
$form.find('.error').html('请选择文件');
return false;
}
return true;
};
$modal.on('click', '.submit-btn', function(){
$form.find('.error').html('');
if (formValid()) {
var formDataString = $form.serialize();
$.ajax({
method: 'POST',
dataType: 'json',
url: '/cooperatives/files?' + formDataString,
data: new FormData($form[0]),
processData: false,
contentType: false,
success: function(data){
$.notify({ message: '上传成功' });
$modal.trigger('upload:success', data);
$modal.modal('hide');
},
error: function(res){
var data = res.responseJSON;
$form.find('.error').html(data.message);
}
});
}
});
}
});

@ -0,0 +1,60 @@
.admins-weapp-adverts-index-page {
.adverts-card {
.advert-item {
& > .drag {
cursor: move;
background: #fff;
box-shadow: 1px 2px 5px 3px #f0f0f0;
}
&-no {
font-size: 28px;
text-align: center;
}
&-img {
cursor: pointer;
width: 100%;
height: 60px;
& > img {
display: block;
width: 100%;
height: 60px;
background: #F5F5F5;
}
}
.not_active {
background: #F0F0F0;
}
.delete-btn {
font-size: 20px;
color: red;
cursor: pointer;
}
.save-url-btn {
cursor: pointer;
}
.operate-box {
display: flex;
justify-content: space-between;
align-items: center;
}
.online-check-box {
font-size: 20px;
}
.name-input {
flex: 1;
}
.link-input {
flex: 3;
}
}
}
}

@ -0,0 +1,60 @@
.admins-weapp-carousels-index-page {
.carousels-card {
.custom-carousel-item {
& > .drag {
cursor: move;
background: #fff;
box-shadow: 1px 2px 5px 3px #f0f0f0;
}
&-no {
font-size: 28px;
text-align: center;
}
&-img {
cursor: pointer;
width: 100%;
height: 60px;
& > img {
display: block;
width: 100%;
height: 60px;
background: #F5F5F5;
}
}
.not_active {
background: #F0F0F0;
}
.delete-btn {
font-size: 20px;
color: red;
cursor: pointer;
}
.save-url-btn {
cursor: pointer;
}
.operate-box {
display: flex;
justify-content: space-between;
align-items: center;
}
.online-check-box {
font-size: 20px;
}
.name-input {
flex: 1;
}
.link-input {
flex: 3;
}
}
}
}

@ -0,0 +1,60 @@
.cooperative-carousels-index-page {
.carousels-card {
.custom-carousel-item {
& > .drag {
cursor: move;
background: #fff;
box-shadow: 1px 2px 5px 3px #f0f0f0;
}
&-no {
font-size: 28px;
text-align: center;
}
&-img {
cursor: pointer;
width: 100%;
height: 60px;
& > img {
display: block;
width: 100%;
height: 60px;
background: #F5F5F5;
}
}
.not_active {
background: #F0F0F0;
}
.delete-btn {
font-size: 20px;
color: red;
cursor: pointer;
}
.save-url-btn {
cursor: pointer;
}
.operate-box {
display: flex;
justify-content: space-between;
align-items: center;
}
.online-check-box {
font-size: 20px;
}
.name-input {
flex: 1;
}
.link-input {
flex: 3;
}
}
}
}

@ -11,18 +11,34 @@ class Admins::CompetitionsController < Admins::BaseController
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)
@competition.update_attributes!(published_at: Time.now)
end
def unpublish
@competition.update_attributes!(:published_at, nil)
@competition.update_attributes!(published_at: nil)
end
def online_switch
@ -33,10 +49,6 @@ class Admins::CompetitionsController < Admins::BaseController
end
end
def enroll_list
end
private
def find_competition

@ -0,0 +1,35 @@
class Admins::EnrollListsController < Admins::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
format.xls{
filename = "#{@competition.name}竞赛报名列表_#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}.xls"
send_data(shixun_list_xls(shixuns), :type => 'application/octet-stream', :filename => filename_for_content_disposition(filename))
}
end
end
def export
default_sort('created_at', 'desc')
@enroll_lists = Admins::CompetitionEnrollListQuery.call(current_competition, params)
filename = ["#{current_competition.name}竞赛报名列表", Time.zone.now.strftime('%Y-%m-%d%H:%M:%S')].join('-') << '.xlsx'
render xlsx: 'export', filename: filename
end
private
def current_competition
@_current_competition ||= Competition.find(params[:competition_id])
end
end

@ -6,7 +6,12 @@ class Admins::FilesController < Admins::BaseController
Util.write_file(@file, file_path)
render_ok(source_id: params[:source_id], source_type: params[:source_type].to_s, url: file_url + "?t=#{Random.rand}")
render_ok(
source_id: params[:source_id],
source_type: params[:source_type].to_s,
suffix: params[:suffix].presence,
url: file_url
)
rescue StandardError => ex
logger_error(ex)
render_error('上传失败')
@ -33,14 +38,14 @@ class Admins::FilesController < Admins::BaseController
@_file_path ||= begin
case params[:source_type].to_s
when 'Shixun' then
Util::FileManage.disk_filename('Shixun', params[:source_id])
Util::FileManage.disk_filename('Shixun', params[:source_id], params[:suffix].presence)
else
Util::FileManage.disk_filename(params[:source_type].to_s, params[:source_id].to_s)
Util::FileManage.disk_filename(params[:source_type].to_s, params[:source_id].to_s, params[:suffix].presence)
end
end
end
def file_url
Util::FileManage.disk_file_url(params[:source_type].to_s, params[:source_id].to_s)
Util::FileManage.disk_file_url(params[:source_type].to_s, params[:source_id].to_s, params[:suffix].presence)
end
end

@ -26,7 +26,7 @@ class Admins::ShixunAuthorizationsController < Admins::BaseController
@applies = paginate applies.includes(user: :user_extension)
shixun_ids = @applies.map(&:container_id)
@shixun_map = Shixun.where(id: shixun_ids).each_with_object({}) { |s, h| h[s.id] = s }
@shixun_map = Shixun.where(id: shixun_ids).includes(:shixun_reviews).each_with_object({}) { |s, h| h[s.id] = s }
end
def agree

@ -0,0 +1,19 @@
class Admins::UserStatisticsController < Admins::BaseController
def index
default_sort('finish_shixun_count', 'desc')
total_count, users = Admins::UserStatisticQuery.call(params)
@users = paginate users, total_count: total_count
end
def export
default_sort('finish_shixun_count', 'desc')
params[:per_page] = 10000
_count, @users = Admins::UserStatisticQuery.call(params)
filename = ['用户实训情况', Time.zone.now.strftime('%Y%m%d%H%M%S')].join('-') << '.xlsx'
render xlsx: 'export', filename: filename
end
end

@ -0,0 +1,79 @@
class Admins::WeappAdvertsController < Admins::BaseController
before_action :convert_file!, only: [:create]
def index
@adverts = WeappSettings::Advert.all
end
def create
position = WeappSettings::Advert.count + 1
ActiveRecord::Base.transaction do
advert = WeappSettings::Advert.create!(create_params.merge(position: position))
file_path = Util::FileManage.source_disk_filename(advert)
File.delete(file_path) if File.exist?(file_path) # 删除之前的文件
Util.write_file(@file, file_path)
end
flash[:success] = '保存成功'
redirect_to admins_weapp_adverts_path
end
def update
current_advert.update!(update_params)
render_ok
end
def destroy
ActiveRecord::Base.transaction do
current_advert.destroy!
# 前移
WeappSettings::Advert.where('position > ?', current_advert.position)
.update_all('position = position - 1')
file_path = Util::FileManage.source_disk_filename(current_advert)
File.delete(file_path) if File.exist?(file_path)
end
render_delete_success
end
def drag
move = WeappSettings::Advert.find_by(id: params[:move_id])
after = WeappSettings::Advert.find_by(id: params[:after_id])
Admins::DragWeappAdvertService.call(move, after)
render_ok
rescue ApplicationService::Error => e
render_error(e.message)
end
private
def current_advert
@_current_advert ||= WeappSettings::Advert.find(params[:id])
end
def create_params
params.require(:weapp_settings_advert).permit(:link)
end
def update_params
params.permit(:link, :online)
end
def convert_file!
max_size = 10 * 1024 * 1024 # 10M
file = params.dig('weapp_settings_advert', 'image')
if file.class == ActionDispatch::Http::UploadedFile
@file = file
render_error('请上传文件') if @file.size.zero?
render_error('文件大小超过限制') if @file.size > max_size
else
file = file.to_s.strip
return render_error('请上传正确的图片') if file.blank?
@file = Util.convert_base64_image(file, max_size: max_size)
end
rescue Base64ImageConverter::Error => ex
render_error(ex.message)
end
end

@ -0,0 +1,80 @@
class Admins::WeappCarouselsController < Admins::BaseController
before_action :convert_file!, only: [:create]
def index
@carousels = WeappSettings::Carousel.all
end
def create
position = WeappSettings::Carousel.count + 1
ActiveRecord::Base.transaction do
carousel = WeappSettings::Carousel.create!(create_params.merge(position: position))
file_path = Util::FileManage.source_disk_filename(carousel)
File.delete(file_path) if File.exist?(file_path) # 删除之前的文件
Util.write_file(@file, file_path)
end
flash[:success] = '保存成功'
redirect_to admins_weapp_carousels_path
end
def update
current_carousel.update!(update_params)
render_ok
end
def destroy
ActiveRecord::Base.transaction do
current_carousel.destroy!
# 前移
WeappSettings::Carousel.where('position > ?', current_carousel.position)
.update_all('position = position - 1')
file_path = Util::FileManage.source_disk_filename(current_carousel)
File.delete(file_path) if File.exist?(file_path)
end
render_delete_success
end
def drag
move = WeappSettings::Carousel.find_by(id: params[:move_id])
after = WeappSettings::Carousel.find_by(id: params[:after_id])
Admins::DragWeappCarouselService.call(move, after)
render_ok
rescue ApplicationService::Error => e
render_error(e.message)
end
private
def current_carousel
@_current_carousel ||= WeappSettings::Carousel.find(params[:id])
end
def create_params
params.require(:weapp_settings_carousel).permit(:link)
end
def update_params
params.permit(:link, :online)
end
def convert_file!
max_size = 10 * 1024 * 1024 # 10M
file = params.dig('weapp_settings_carousel', 'image')
if file.class == ActionDispatch::Http::UploadedFile
@file = file
render_error('请上传文件') if @file.size.zero?
render_error('文件大小超过限制') if @file.size > max_size
else
file = file.to_s.strip
return render_error('请上传正确的图片') if file.blank?
@file = Util.convert_base64_image(file, max_size: max_size)
end
rescue Base64ImageConverter::Error => ex
render_error(ex.message)
end
end

@ -112,6 +112,8 @@ class ApplicationController < ActionController::Base
"验证码发送次数超过频率"
when 43
"一天内同一手机号发送次数超过限制"
when 53
"手机号接收超过频率限制"
end
end
@ -338,9 +340,9 @@ class ApplicationController < ActionController::Base
# 如果代码窗口是隐藏的,则不用保存代码
return if myshixun.shixun.hide_code || myshixun.shixun.vnc
file_content = git_fle_content myshixun.repo_path, path
unless file_content.present?
raise("获取文件代码异常")
end
#unless file_content.present?
# raise("获取文件代码异常")
#end
logger.info("#######game_id:#{game_id}, file_content:#{file_content}")
game_code = GameCode.where(:game_id => game_id, :path => path).first
if game_code.nil?
@ -353,10 +355,10 @@ class ApplicationController < ActionController::Base
# Post请求
def uri_post(uri, params)
begin
uid_logger("--uri_exec: params is #{params}, url is #{uri}")
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
logger.info("--uri_exec: .....res is #{res}")
uid_logger_dubug("--uri_exec: .....res is #{res}")
JSON.parse(res)
rescue Exception => e
uid_logger_error("--uri_exec: exception #{e.message}")
@ -367,10 +369,10 @@ class ApplicationController < ActionController::Base
# 处理返回非0就报错的请求
def interface_post(uri, params, status, message)
begin
uid_logger("--uri_exec: params is #{params}, url is #{uri}")
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
logger.info("--uri_exec: .....res is #{res}")
uid_logger_dubug("--uri_exec: .....res is #{res}")
res = JSON.parse(res)
if (res && res['code'] != 0)
tip_exception(status, message)
@ -623,4 +625,13 @@ class ApplicationController < ActionController::Base
end
user
end
# 记录热门搜索关键字
def record_search_keyword
keyword = params[:keyword].to_s.strip
return if keyword.blank? || keyword.size <= 1
return unless HotSearchKeyword.available?
HotSearchKeyword.add(keyword)
end
end

@ -1,5 +1,65 @@
class Competitions::CompetitionTeamsController < Competitions::BaseController
before_action :tech_mode, only: [:shixun_detail, :course_detail]
def shixun_detail
start_time = current_competition.competition_mode_setting&.start_time || current_competition.start_time
end_time = current_competition.competition_mode_setting&.end_time || current_competition.end_time
team_user_ids = @team.team_members.pluck(:user_id)
shixuns = Shixun.where(user_id: team_user_ids, status: 2)
.where('shixuns.created_at > ? && shixuns.created_at <= ?', start_time, end_time)
shixuns = shixuns.joins('left join shixuns forked_shixuns on forked_shixuns.fork_from = shixuns.id and forked_shixuns.status = 2')
shixuns = shixuns.select('shixuns.id, shixuns.identifier, shixuns.user_id, shixuns.myshixuns_count, shixuns.name, shixuns.fork_from, sum(forked_shixuns.myshixuns_count) forked_myshixun_count')
@shixuns = shixuns.group('shixuns.id').order('shixuns.myshixuns_count desc').includes(:user)
shixun_ids = @shixuns.map(&:id)
@myshixun_count_map = get_valid_myshixun_count(shixun_ids)
@original_myshixun_count_map = @myshixun_count_map.clone
# forked shixun valid myshixun count
forked_shixun_map = Shixun.where(status: 2, fork_from: shixun_ids).select('id, fork_from')
forked_shixun_map = forked_shixun_map.each_with_object({}) { |sx, obj| obj[sx.id] = sx.fork_from }
@forked_myshixun_count_map = get_valid_myshixun_count(forked_shixun_map.keys)
@forked_myshixun_count_map.each { |k, v| @myshixun_count_map[forked_shixun_map[k]] += v }
@course_count_map = get_valid_course_count(shixun_ids)
@forked_map = get_valid_course_count(forked_shixun_map.keys)
@forked_course_count_map = {}
@forked_map.each do |forked_id, course_count|
@forked_course_count_map[forked_shixun_map[forked_id]] ||= 0
@forked_course_count_map[forked_shixun_map[forked_id]] += course_count
end
@forked_shixun_map = forked_shixun_map
end
def course_detail
start_time = current_competition.competition_mode_setting&.start_time || current_competition.start_time
end_time = current_competition.competition_mode_setting&.end_time || current_competition.end_time
@team_user_ids = @team.team_members.pluck(:user_id)
student_count_subquery = CourseMember.where('course_id = courses.id AND role = 4').select('count(*)').to_sql
subquery = StudentWork.where('homework_common_id = hcs.id')
.select('sum(compelete_status !=0 ) as finish, count(*) as total')
.having('total != 0 and finish >= (total / 2)').to_sql
course_ids = Course.where('courses.created_at > ?', start_time)
.where('courses.created_at <= ?', end_time)
.where("(#{student_count_subquery}) >= 3")
.where("exists(select 1 from homework_commons hcs where hcs.course_id = courses.id and hcs.publish_time is not null and hcs.publish_time < NOW() and hcs.homework_type = 4 and exists(#{subquery}))")
.joins('join course_members on course_members.course_id = courses.id and course_members.role in (1,2,3)')
.where(course_members: { user_id: @team_user_ids }).pluck(:id)
courses = Course.where(id: course_ids).joins(:practice_homeworks).where('homework_commons.publish_time < now()')
@courses = courses.select('courses.id, courses.name, courses.members_count, count(*) shixun_homework_count')
.group('courses.id').order('shixun_homework_count desc').having('shixun_homework_count > 0')
course_ids = @courses.map(&:id)
@course_myshixun_map = Myshixun.joins(student_works: :homework_common)
.where(homework_commons: { course_id: course_ids })
.where('exists(select 1 from games where games.myshixun_id = myshixuns.id and games.status = 2)')
.group('homework_commons.course_id').count
@course_shixun_count_map = get_valid_shixun_count(course_ids)
end
def index
@personal = current_competition.personal?
admin_or_business? ? all_competition_teams : user_competition_teams
end
@ -67,4 +127,37 @@ class Competitions::CompetitionTeamsController < Competitions::BaseController
def save_params
params.permit(:name, teacher_ids: [], member_ids: [])
end
def tech_mode
# render_not_found if current_competition.mode != 3
@team = current_competition.competition_teams.find_by(id: params[:id])
end
def get_valid_myshixun_count(ids)
Myshixun.where(shixun_id: ids)
.where('exists(select 1 from games where games.myshixun_id = myshixuns.id and games.status = 2)')
.group('shixun_id').count
end
def get_valid_course_count(ids)
percentage_sql = StudentWork.where('homework_common_id = homework_commons.id and homework_commons.publish_time is not null and homework_commons.publish_time < NOW()')
.select('sum(compelete_status !=0 ) as finish, count(*) as total')
.having('total != 0 and finish >= (total / 2)').to_sql
Course.joins(practice_homeworks: :homework_commons_shixun)
.where('shixun_id in (?)', ids)
.where("exists (#{percentage_sql})")
.group('shixun_id').count
end
def get_valid_shixun_count(ids)
percentage_sql = StudentWork.where('homework_common_id = homework_commons.id and homework_commons.publish_time is not null and homework_commons.publish_time < NOW()')
.select('sum(compelete_status !=0 ) as finish, count(*) as total')
.having('total != 0 and finish >= (total / 2)').to_sql
Shixun.joins(homework_commons_shixuns: :homework_common)
.where(homework_commons: { homework_type: 4 })
.where('course_id in (?)', ids)
.where("exists (#{percentage_sql})")
.group('course_id').count
end
end

@ -1,6 +1,10 @@
class Competitions::CompetitionsController < Competitions::BaseController
include CompetitionsHelper
skip_before_action :require_login
before_action :allow_visit, except: [:index]
before_action :require_admin, only: [:update, :update_inform]
before_action :chart_visible, only: [:charts, :chart_rules]
def index
# 已上架 或者 即将上架
@ -25,10 +29,108 @@ class Competitions::CompetitionsController < Competitions::BaseController
end
def show
@competition = current_competition
end
def update
@competition.update_attributes!(introduction: params[:introduction])
normal_status("更新成功")
end
def common_header
@competition = current_competition
@competition_modules = @competition.unhidden_competition_modules
@user = current_user
end
def informs
status = params[:status] || 1
@informs = current_competition.informs.where(status: status)
end
def update_inform
tip_exception("标题和内容不能为空") if params[:name].blank? || params[:description].blank?
tip_exception("status参数有误") if params[:status].blank? || ![1, 2].include?(params[:status].to_i)
ActiveRecord::Base.transaction do
if params[:inform_id]
inform = current_competition.informs.find_by!(id: params[:inform_id])
inform.update_attributes!(name: params[:name], description: params[:description])
else
inform = current_competition.informs.create!(name: params[:name], description: params[:description], status: params[:status])
end
Attachment.associate_container(params[:attachment_ids], inform.id, inform.class) if params[:attachment_ids]
normal_status("更新成功")
end
end
def md_content
@md_content = CompetitionModuleMdContent.find_by!(id: params[:md_content_id])
end
def update_md_content
tip_exception("标题和内容不能为空") if params[:name].blank? || params[:content].blank?
ActiveRecord::Base.transaction do
if params[:md_content_id]
md_content = CompetitionModuleMdContent.find_by!(id: params[:md_content_id])
md_content.update_attributes!(name: params[:name], content: params[:content])
else
md_content = CompetitionModuleMdContent.create!(name: params[:name], content: params[:content])
end
Attachment.associate_container(params[:attachment_ids], md_content.id, md_content.class) if params[:attachment_ids]
normal_status("更新成功")
end
end
def chart_rules
@competition = current_competition
@stages = @competition.competition_stages
@rule_contents = @competition.chart_rules
end
def update_chart_rules
tip_exception("内容不能为空") if params[:content].blank?
@competition = current_competition
@stage = @competition.competition_stages.find_by!(id: params[:stage_id]) if params[:stage_id]
chart_rule = @competition.chart_rules.where(competition_stage_id: @stage&.id).first
if chart_rule
chart_rule.update_attributes!(content: params[:content])
else
@competition.chart_rules.create!(competition_stage_id: @stage&.id, content: params[:content])
end
normal_status("更新成功")
end
def charts
@competition = current_competition
if params[:stage_id]
@stage = @competition.competition_stages.find_by(id: params[:stage_id])
end
if @competition.identifier == "gcc-annotation-2018"
@records = @competition.competition_teams.joins(:competition_scores)
.select("competition_teams.*, score, cost_time").order("score desc, cost_time desc")
else
@records = @competition.competition_teams.joins(:competition_scores).where(competition_scores: {competition_stage_id: @stage&.id.to_i})
.select("competition_teams.*, score, cost_time").order("score desc, cost_time desc")
end
current_team_ids = @competition.team_members.where(user_id: current_user.id).pluck(:competition_team_id).uniq
@user_ranks = @records.select{|com_team| current_team_ids.include?(com_team.id)}
@records = @records.where("score > 0")
if params[:format] == "xlsx"
@records = @records.includes(user: [user_extension: :school], team_members: :user)
respond_to do |format|
format.xlsx{
set_export_cookies
chart_to_xlsx(@records, @competition)
exercise_export_name = "#{@competition.name}比赛成绩"
render xlsx: "#{exercise_export_name.strip}",template: "competitions/competitions/chart_list.xlsx.axlsx",locals:
{table_columns: @competition_head_cells, chart_lists: @competition_cells_column}
}
end
else
@records = @records.includes(:user, :team_members).limit(@competition.awards_count)
end
end
private
@ -38,9 +140,60 @@ class Competitions::CompetitionsController < Competitions::BaseController
end
def allow_visit
unless current_competition.published? || admin_or_business?
render_forbidden
return
render_forbidden unless current_competition.published? || admin_or_business?
end
def chart_visible
chart_module = current_competition.competition_modules.find_by(name: "排行榜")
render_forbidden unless (chart_module.present? && !chart_module.hidden) || admin_or_business?
end
# 竞赛成绩导出
def chart_to_xlsx records, competition
@competition_head_cells = []
@competition_cells_column = []
max_staff = competition.competition_staffs.sum(:maximum)
if max_staff < 2 # 个人赛
@competition_head_cells = %w(序号 姓名 学校 学号)
else # 战队赛
@competition_head_cells = %w(序号 战队名 指导老师 队员 学校)
end
statistic_stages = competition.competition_stages.where("score_rate > 0")
statistic_stages.each do |stage|
@competition_head_cells += ["#{stage.name}得分", "#{stage.name}用时"]
end
@competition_head_cells += ["总得分", "总用时"] if statistic_stages.size > 1
competition_scores = competition.competition_scores
records.each_with_index do |record, index|
row_cells_column = []
row_cells_column << index + 1
record_user = record.user
if max_staff < 2
row_cells_column << record_user.real_name
row_cells_column << record_user.school_name
row_cells_column << record_user.student_id
else
row_cells_column << record.name
row_cells_column << record.teachers_name
row_cells_column << record.members_name
row_cells_column << chart_school_str(record.team_members.select{|member| !member.is_teacher}.pluck(:user_id))
end
statistic_stages.each do |stage|
stage_score = competition_scores.select{|score| score.competition_stage_id == stage.id && score.competition_team_id == record.id}.first
row_cells_column << stage_score&.score.to_f.round(2)
row_cells_column << com_spend_time(stage_score&.cost_time.to_i)
end
if statistic_stages.size > 1
row_cells_column << record&.score.to_f.round(2)
row_cells_column << com_spend_time(record&.cost_time.to_i)
end
@competition_cells_column.push(row_cells_column)
end
end
end

@ -1,6 +1,11 @@
module Base::PaginateHelper
extend ActiveSupport::Concern
def default_sort(sort_by, direction)
params[:sort_by] = params[:sort_by].presence || sort_by
params[:sort_direction] = params[:sort_direction].presence || direction
end
def offset
(page - 1) * per_page
end

@ -9,6 +9,12 @@ module LoggerHelper
Rails.logger.info("##:#{current_user.try(:id)} --#{message}")
end
# debug日志
def uid_logger_dubug(message)
Rails.logger.info("##dubug-#{current_user.try(:id)} --#{message}")
end
# 以用户id开始的日志定义
def uid_logger_error(message)
Rails.logger.error("##:#{current_user.try(:id)} --#{message}")

@ -26,7 +26,7 @@ class Cooperative::BaseController < ApplicationController
def laboratory_exist!
return if current_laboratory.present?
redirect_to '/nopage'
redirect_to '/403'
end
def require_login
@ -48,7 +48,7 @@ class Cooperative::BaseController < ApplicationController
return if request.format.symbol != :js
return if response.content_type != 'text/javascript'
path = Rails.root.join('app/views/shared/after_render_js_hook.js.erb')
path = Rails.root.join('app/views/cooperative/shared/after_render_js_hook.js.erb')
return unless File.exists?(path)
append_js = ERB.new(File.open(path).read).result

@ -0,0 +1,80 @@
class Cooperative::CarouselsController < Cooperative::BaseController
before_action :convert_file!, only: [:create]
def index
@images = current_laboratory.portal_images.order(position: :asc)
end
def create
position = current_laboratory.portal_images.count + 1
ActiveRecord::Base.transaction do
image = current_laboratory.portal_images.create!(create_params.merge(position: position))
file_path = Util::FileManage.disk_filename('PortalImage', image.id)
File.delete(file_path) if File.exist?(file_path) # 删除之前的文件
Util.write_file(@file, file_path)
end
flash[:success] = '保存成功'
redirect_to cooperative_carousels_path(current_laboratory)
end
def update
current_image.update!(update_params)
render_ok
end
def destroy
ActiveRecord::Base.transaction do
current_image.destroy!
# 前移
current_laboratory.portal_images.where('position > ?', current_image.position)
.update_all('position = position - 1')
file_path = Util::FileManage.disk_filename('PortalImage', current_image.id)
File.delete(file_path) if File.exist?(file_path)
end
render_delete_success
end
def drag
move = current_laboratory.portal_images.find_by(id: params[:move_id])
after = current_laboratory.portal_images.find_by(id: params[:after_id])
Admins::DragPortalImageService.call(current_laboratory, move, after)
render_ok
rescue Admins::DragPortalImageService::Error => e
render_error(e.message)
end
private
def current_image
@_current_image ||= current_laboratory.portal_images.find(params[:id])
end
def create_params
params.require(:portal_image).permit(:name, :link)
end
def update_params
params.permit(:name, :link, :status)
end
def convert_file!
max_size = 10 * 1024 * 1024 # 10M
file = params.dig('portal_image', 'image')
if file.class == ActionDispatch::Http::UploadedFile
@file = file
render_error('请上传文件') if @file.size.zero?
render_error('文件大小超过限制') if @file.size > max_size
else
file = file.to_s.strip
return render_error('请上传正确的图片') if file.blank?
@file = Util.convert_base64_image(file, max_size: max_size)
end
rescue Base64ImageConverter::Error => ex
render_error(ex.message)
end
end

@ -0,0 +1,46 @@
class Cooperative::FilesController < Cooperative::BaseController
before_action :convert_file!, only: [:create]
def create
File.delete(file_path) if File.exist?(file_path) # 删除之前的文件
Util.write_file(@file, file_path)
render_ok(source_id: params[:source_id], source_type: params[:source_type].to_s, url: file_url + "?t=#{Random.rand}")
rescue StandardError => ex
logger_error(ex)
render_error('上传失败')
end
private
def convert_file!
max_size = 10 * 1024 * 1024 # 10M
if params[:file].class == ActionDispatch::Http::UploadedFile
@file = params[:file]
render_error('请上传文件') if @file.size.zero?
render_error('文件大小超过限制') if @file.size > max_size
else
file = params[:file].to_s.strip
return render_error('请上传正确的图片') if file.blank?
@file = Util.convert_base64_image(file, max_size: max_size)
end
rescue Base64ImageConverter::Error => ex
render_error(ex.message)
end
def file_path
@_file_path ||= begin
case params[:source_type].to_s
when 'Shixun' then
Util::FileManage.disk_filename('Shixun', params[:source_id])
else
Util::FileManage.disk_filename(params[:source_type].to_s, params[:source_id].to_s)
end
end
end
def file_url
Util::FileManage.disk_file_url(params[:source_type].to_s, params[:source_id].to_s)
end
end

@ -0,0 +1,24 @@
class Cooperative::LaboratoryUsersController < Cooperative::BaseController
def index
laboratory_users = current_laboratory.laboratory_users
@laboratory_users = paginate laboratory_users.includes(user: { user_extension: [:school, :department] })
end
def create
Admins::AddLaboratoryUserService.call(current_laboratory, params.permit(user_ids: []))
render_ok
end
def destroy
return render_error('不能删除自己', type: :notify) if current_laboratory_user.user_id == current_user.id
current_laboratory_user.destroy!
render_delete_success
end
private
def current_laboratory_user
@_current_laboratory_user ||= current_laboratory.laboratory_users.find(params[:id])
end
end

@ -0,0 +1,10 @@
class Cooperative::UsersController < Cooperative::BaseController
def index
params[:sort_by] = params[:sort_by].presence || 'created_on'
params[:sort_direction] = params[:sort_direction].presence || 'desc'
params[:school_id] = current_laboratory.school_id
users = Admins::UserQuery.call(params)
@users = paginate users.includes(user_extension: :school)
end
end

@ -1129,7 +1129,7 @@ class ExercisesController < ApplicationController
:subjective_score => subjective_score,
:commit_method => @answer_committed_user&.commit_method.to_i > 0 ? @answer_committed_user&.commit_method.to_i : params[:commit_method].to_i
}
@answer_committed_user.update_attributes(commit_option)
@answer_committed_user.update_attributes!(commit_option)
CommitExercsieNotifyJobJob.perform_later(@exercise.id, current_user.id)
normal_status(0,"试卷提交成功!")
else

@ -55,7 +55,7 @@ class GamesController < ApplicationController
# 选择题和编程题公共部分
@base_date = {st: @st, discusses_count: discusses_count, game_count: game_count, myshixun: @myshixun,
challenge: game_challenge.attributes.except("answer"), game: @game.try(:attributes), shixun: @shixun.try(:attributes),
challenge: game_challenge.attributes.except("answer"), game: @game.try(:attributes), shixun: @shixun.attributes.except("vnc", "vnc_evaluate"),
record_onsume_time: record_onsume_time, prev_game: prev_game, next_game: next_game,
praise_count: praise_count, user_praise: user_praise, time_limit: time_limit,
tomcat_url: edu_setting('cloud_tomcat_php'), is_teacher: is_teacher,

@ -1058,8 +1058,8 @@ class HomeworkCommonsController < ApplicationController
tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if
@course.end_date.present? && params[:end_time] > strf_time(@course.end_date.end_of_day)
else
tip_exception("缺少分班截止时间参数") if params[:group_end_times].blank?
group_end_times = params[:group_end_times].reject(&:blank?).map{|time| time.to_time}
tip_exception("缺少截止时间参数") if group_end_times.blank?
tip_exception("截止时间和分班参数的个数不一致") if group_end_times.length != group_ids.length
group_end_times.each do |time|
tip_exception("分班截止时间不能早于当前时间") if time <= Time.now

@ -0,0 +1,7 @@
class HotKeywordsController < ApplicationController
def index
keywords = []
keywords = HotSearchKeyword.hot(8) if HotSearchKeyword.available?
render_ok(keywords: keywords)
end
end

@ -1,5 +1,5 @@
class MainController < ApplicationController
def index
render file: 'public/react/build/index', formats: [:html]
render file: 'public/react/build/index.html', :layout => false
end
end

@ -90,7 +90,7 @@ class MyshixunsController < ApplicationController
ActiveRecord::Base.transaction do
begin
t1 = Time.now
Rails.logger.info("@@@222222#{params[:jsonTestDetails]}")
uid_logger_dubug("@@@222222#{params[:jsonTestDetails]}")
jsonTestDetails = JSON.parse(params[:jsonTestDetails])
timeCost = JSON.parse(params[:timeCost])
brige_end_time = Time.parse(timeCost['evaluateEnd']) if timeCost['evaluateEnd'].present?
@ -99,7 +99,7 @@ class MyshixunsController < ApplicationController
game_id = jsonTestDetails['buildID']
sec_key = jsonTestDetails['sec_key']
logger.info("training_task_status start#1**#{game_id}**** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")
uid_logger_dubug("training_task_status start-#{game_id}-1#{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")
resubmit = jsonTestDetails['resubmit']
outPut = tran_base64_decode64(jsonTestDetails['outPut'])
@ -116,17 +116,14 @@ class MyshixunsController < ApplicationController
pics = params[:tpiRepoPath]
game.update_column(:picture_path, pics)
end
logger.info("training_task_status start#2**#{game_id}**** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")
max_query_index = game.outputs ? (game.outputs.first.try(:query_index).to_i + 1) : 1
test_set_score = 0
unless jenkins_testsets.blank?
jenkins_testsets.each_with_index do |j_test_set, i|
logger.info("j_test_set: ############## #{j_test_set}")
actual_output = tran_base64_decode64(j_test_set['output'])
#ts_time += j_test_set['testSetTime'].to_i
# is_public = test_sets.where(:position => j_test_set['caseId']).first.try(:is_public)
logger.info "actual_output:################################################# #{actual_output}"
ts_time = format("%.2f", j_test_set['testSetTime'].to_f/1000000000).to_f if j_test_set['testSetTime']
ts_mem = format("%.2f", j_test_set['testSetMem'].to_f/1024/1024).to_f if j_test_set['testSetMem']
@ -139,15 +136,12 @@ class MyshixunsController < ApplicationController
end
end
end
uid_logger("#############status: #{status}")
record = EvaluateRecord.where(:identifier => sec_key).first
logger.info("training_task_status start#3**#{game_id}**** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")
answer_deduction_percentage = (100 - game.answer_deduction) / 100.to_f # 查看答案后剩余分数的百分比.
# answer_deduction是查看答案的扣分比例
# status0表示评测成功
if status == "0"
if resubmit.present?
uid_logger("#############resubmitdaiao: #{resubmit}")
game.update_attributes!(:retry_status => 2, :resubmit_identifier => resubmit)
challenge.path.split("").each do |path|
game_passed_code(path.try(:strip), myshixun, game_id)
@ -205,7 +199,6 @@ class MyshixunsController < ApplicationController
:pod_execute => timeCost['execute'], :test_cases => test_cases_time,
:brige => timeCost['evaluateAllTime'], :return_back => return_back_time)
end
uid_logger("training_task_status start#4**#{game_id}**** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")
sucess_status
# rescue Exception => e
# tip_exception(e.message)
@ -265,10 +258,10 @@ class MyshixunsController < ApplicationController
@sec_key = generate_identifier(EvaluateRecord, 12)
record = EvaluateRecord.create!(:user_id => current_user.id, :shixun_id => @myshixun.shixun_id, :game_id => game_id,
:identifier => @sec_key, :exec_time => exec_time)
uid_logger("-- game build: file update #{@sec_key}, record id is #{record.id}, time is **** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")
uid_logger_dubug("-- game build: file update #{@sec_key}, record id is #{record.id}, time is **** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")
end
# 隐藏代码文件 和 VNC的都不需要走版本库
unless @hide_code || @myshixun.shixun&.vnc_evaluate
unless @hide_code || (@myshixun.shixun&.vnc_evaluate && params[:evaluate].present?)
# 远程版本库文件内容
last_content = GitService.file_content(repo_path: @repo_path, path: path)["content"]
content = if @myshixun.mirror_name.select {|a| a.include?("MachineLearning") || a.include?("Python")}.present? && params[:content].present?
@ -276,8 +269,8 @@ class MyshixunsController < ApplicationController
else
params[:content]
end
Rails.logger.info("###11222333####{content}")
Rails.logger.info("###222333####{last_content}")
uid_logger_dubug("###11222333####{content}")
uid_logger_dubug("###222333####{last_content}")
if content != last_content
@content_modified = 1
@ -285,8 +278,8 @@ class MyshixunsController < ApplicationController
author_name = current_user.real_name
author_email = current_user.git_mail
message = params[:evaluate] == 0 ? "System automatically submitted" : "User submitted"
uid_logger("112233#{author_name}")
uid_logger("112233#{author_email}")
uid_logger_dubug("112233#{author_name}")
uid_logger_dubug("112233#{author_email}")
@content = GitService.update_file(repo_path: @repo_path,
file_path: path,
message: message,

@ -16,9 +16,9 @@ class PollVotesController < ApplicationController
# 当前用户的当前答案,如果已存在,当再次点击的时候,取消答案,即删除该答案
current_vote_text = nil
if user_votes.find_vote_text.present?
current_vote_text = user_votes.find_vote_text.first
end
# if user_votes.find_vote_text.present?
# current_vote_text = user_votes.find_vote_text.first
# end
vote_answer_params = {
:user_id => current_user.id,
@ -36,7 +36,6 @@ class PollVotesController < ApplicationController
else
if question_answer_text.present?
current_user_answer.update_attribute("vote_text", question_answer_text)
end
end
@ -48,7 +47,8 @@ class PollVotesController < ApplicationController
if question_answer_ids.present?
if question_answer_text.present? #有文字输入,但是不存在其他选项的
ques_vote_id = question_answer_ids.map(&:to_i).max
if current_vote_text.present? #已有其他输入文字的选项
if user_votes.find_vote_text.present?
current_vote_text = user_votes.find_vote_text.first
current_vote_text.update_attribute("vote_text", question_answer_text)
else
answer_option = {
@ -59,6 +59,11 @@ class PollVotesController < ApplicationController
}
PollVote.create(answer_option)
end
# if current_vote_text.present? #已有其他输入文字的选项
# current_vote_text.update_attribute("vote_text", question_answer_text)
# else
#
# end
end
ea_ids = user_votes.pluck(:poll_answer_id)
@ -85,12 +90,14 @@ class PollVotesController < ApplicationController
user_votes.destroy_all
end
else #主观题的输入
if current_vote_text.present?
if question_answer_text.present?
# current_vote_text = user_votes.find_vote_text
if user_votes.present?
user_votes.first.update_attribute("vote_text", question_answer_text)
else
user_votes.destroy_all
end
# if question_answer_text.present?
# user_votes.first.update_attribute("vote_text", question_answer_text)
# else
# user_votes.destroy_all
# end
else
PollVote.create(vote_answer_params)
end

@ -900,12 +900,30 @@ class PollsController < ApplicationController
# 判断是否已经回答还是新建的回答
@poll_questions.each do |q|
ques_vote = q.poll_votes.find_current_vote("user_id",@poll_current_user_id)
if ques_vote.present?
ques_type = q.question_type
if ques_type != 3 #非简答题时
if ques_vote.exists?
ques_status = 1
question_answered += 1
else
ques_status = 0
end
else
if ques_vote.find_vote_text.first.present?
ques_status = 1
question_answered += 1
else
ques_status = 0
end
end
# if ques_vote.present?
# ques_status = 1
# question_answered += 1
# else
# ques_status = 0
# end
answer_status = {
:ques_id => q.id,
:ques_number => q.question_number,

@ -1,9 +1,12 @@
class SearchsController < ApplicationController
after_action :record_search_keyword, only: [:index]
def index
@results = SearchService.call(search_params)
end
private
def search_params
params.permit(:keyword, :type, :page, :per_page)
end

@ -14,7 +14,7 @@ class ShixunsController < ApplicationController
before_action :shixun_access_allowed, except: [:index, :new, :create, :menus, :get_recommend_shixuns,
:propaedeutics, :departments, :apply_shixun_mirror,
:get_mirror_script, :download_file, :shixun_list]
:get_mirror_script, :download_file, :shixun_list, :review_shixuns]
before_action :find_repo_name, only: [:repository, :commits, :file_content, :update_file, :shixun_exec, :copy, :add_file]
before_action :allowed, only: [:update, :close, :update_propaedeutics, :settings, :publish,
@ -232,10 +232,12 @@ class ShixunsController < ApplicationController
# 同步私密版本库
if @shixun.shixun_secret_repository
repo_name = "#{current_user.login}/secret_#{@shixun.identifier}"
# 源仓库的的私密版本库地址
repo_name = @shixun.shixun_secret_repository.repo_name
# 新生成的地址
fork_repository_name = "#{current_user.login}/secret_#{@new_shixun.identifier}"
ShixunSecretRepository.create!(shixun_id: @new_shixun.id,
repo_name: "#{repo_name}",
repo_name: "#{fork_repository_name}",
secret_dir_path: @shixun.shixun_secret_repository.secret_dir_path)
GitService.fork_repository(repo_path: "#{repo_name}.git", fork_repository_path: (fork_repository_name + ".git"))
end
@ -262,6 +264,11 @@ class ShixunsController < ApplicationController
:mirror_repository_id => config.mirror_repository_id)
end
# 同步高校限制
@shixun.shixun_schools.each do |school|
ShixunSchool.create!(shixun_id: @new_shixun.id, school_id: school.school_id)
end
# fork版本库
logger.info("###########fork_repo_path: ######{@repo_path}")
project_fork(@new_shixun, @repo_path, current_user.login)
@ -330,7 +337,10 @@ class ShixunsController < ApplicationController
end
rescue Exception => e
uid_logger_error("copy shixun failed ##{e.message}")
g.delete_project(gshixungshixun.id) if gshixun.try(:id).present? # 异常后,如果已经创建了版本库需要删除该版本库
# 删除版本库
# 删除私密版本库
GitService.delete_repository(repo_path: "#{fork_repository_name}.git") if @new_shixun.shixun_secret_repository&.repo_name
GitService.delete_repository(repo_path: @new_shixun.repo_path) if @new_shixun.repo_path
tip_exception("实训Fork失败")
raise ActiveRecord::Rollback
end
@ -986,6 +996,21 @@ class ShixunsController < ApplicationController
@shixun.update_column(:status, 0)
end
# 创建实训审核
def review_shixun
validate_review_shixun_params
# 没有记录就创建记录, 如果有记录就
@shixun.shixun_reviews.create!(user_id: current_user.id, status: params[:status],
review_type: params[:review_type], evaluate_content: params[:evaluate_content])
normal_status("审核完成")
end
# 实训审核最新记录
def review_newest_record
@content_record = @shixun.shixun_reviews.where(review_type: "Content").first
@perfer_record = @shixun.shixun_reviews.where(review_type: "Performance").first
end
private
def shixun_params
raise("实训名称不能为空") if params[:shixun][:name].blank?
@ -994,6 +1019,11 @@ private
:hide_code, :forbid_copy, :vnc_evaluate, :code_edit_permission)
end
def validate_review_shixun_params
tip_exception("只有平台管理员或运营人员才能审核") if !admin_or_business?
tip_exception("审核类型参数不对") unless ["Content", "Performance"].include?(params[:review_type])
end
def shixun_info_params
raise("实训描述不能为空") if params[:shixun_info][:description].blank?
raise("评测脚本不能为空") if params[:shixun_info][:evaluate_script].blank?

@ -1,4 +1,4 @@
class Users::OpenUsers < Users::BaseAccountController
class Users::OpenUsersController < Users::BaseAccountController
def destroy
current_open_users.destroy!

@ -3,6 +3,7 @@ class Weapps::BaseController < ApplicationController
private
def require_wechat_login!
Rails.logger.info("[Weapp] unionid: #{session_unionid}, openid: #{session_openid}")
return if session_unionid.present?
render_error('请先进行微信授权')
@ -21,6 +22,7 @@ class Weapps::BaseController < ApplicationController
end
def set_session_openid(openid)
Rails.logger.info("[Weapp] set session openid: #{openid}")
session[:openid] = openid
end
@ -29,6 +31,7 @@ class Weapps::BaseController < ApplicationController
end
def set_session_unionid(unionid)
Rails.logger.info("[Weapp] set session unionid: #{unionid}")
session[:unionid] = unionid
end
end

@ -5,22 +5,31 @@ class Weapps::CodeSessionsController < Weapps::BaseController
result = Wechat::Weapp.jscode2session(params[:code])
set_session_openid(result['openid'])
set_weapp_session_key(result['session_key']) # weapp session_key写入缓存 后续解密需要
# 已授权,绑定过账号
open_user = ::OpenUser::Wechat.find_by(uid: result['unionid'])
# 能根据 code 拿到 unionid
open_user = OpenUsers::Wechat.find_by(uid: result['unionid'])
if open_user.present? && open_user.user
set_session_unionid(result['unionid'])
successful_authentication(open_user.user)
set_session_unionid(result['unionid'])
logged = true
else
# 新用户
# 根据 code没拿到 unionid
user_info = Wechat::Weapp.decrypt(result['session_key'], params[:encrypted_data], params[:iv])
# 老用户,已绑定
open_user = OpenUsers::Wechat.find_by(uid: user_info['unionId'])
if open_user.present? && open_user.user
successful_authentication(open_user.user)
logged = true
end
set_session_unionid(user_info['unionId'])
end
set_session_openid(result['openid'])
set_weapp_session_key(result['session_key']) # weapp session_key写入缓存 后续解密需要
render_ok(openid: result['openid'], logged: logged)
rescue Wechat::Error => ex
render_error(ex.message)
end
end

@ -1,12 +1,15 @@
class Weapps::HomesController < Weapps::BaseController
def show
# banner图
@images = PortalImage.where(status: true).order(position: :asc)
# banner
@carousels = WeappSettings::Carousel.only_online
# 广告
@advert = WeappSettings::Advert.only_online.first
# 热门实训
@shixuns = Shixun.where(homepage_show: true).includes(:tag_repertoires, :challenges).limit(4)
# 热门实践课程
@subjects = Subject.where(homepage_show: true).includes(:shixuns, :repertoire).limit(4)
end
end

@ -0,0 +1,13 @@
class Weapps::SearchsController < Weapps::BaseController
after_action :record_search_keyword, only: [:index]
def index
@results = Weapps::SearchQuery.call(search_params)
end
private
def search_params
params.permit(:keyword, :type, :page, :per_page)
end
end

@ -142,6 +142,14 @@ module ApplicationHelper
end
end
# 主页banner图
def banner_img(source_type)
if File.exist?(disk_filename(source_type, "banner"))
ctime = File.ctime(disk_filename(source_type, "banner")).to_i
File.join("images/avatars", ["#{source_type}", "banner"]) + "?t=#{ctime}"
end
end
def disk_filename(source_type,source_id,image_file=nil)
File.join(storage_path, "#{source_type}", "#{source_id}")
end

@ -0,0 +1,54 @@
module CompetitionsHelper
def chart_school_str user_ids
chart_school_name = ""
chart_school_name = School.where(id: UserExtension.where(user_id: user_ids).pluck(:school_id).uniq).pluck(:name).join("")
end
# 耗时:小时、分、秒 00:00:00
# 小于1秒钟则不显示
def com_spend_time time
hour = time / (60*60)
min = time % (60*60) / 60
sec = time % (60*60) % 60
hour_str = "00"
min_str = "00"
sec_str = "00"
if hour >= 1 && hour < 10
hour_str = "0#{hour}"
elsif hour >= 10
hour_str = "#{hour}"
end
if min >= 1 && min < 10
min_str = "0#{min}"
elsif min >= 10
min_str = "#{min}"
end
if sec >= 1 && sec < 10
sec_str = "0#{sec}"
elsif sec >= 10
sec_str = "#{sec}"
end
time = "#{hour_str} : #{min_str} : #{sec_str}"
return time
end
def chart_stages competition
stages = []
statistic_stages = competition.competition_stages.where("rate > 0")
if competition.end_time && competition.end_time < Time.now && statistic_stages.size > 1
stages << {id: nil, name: "总排行榜", rate: 1.0, start_time: competition.start_time, end_time: competition.end_time}
end
statistic_stages.each do |stage|
if stage.max_end_time && stage.max_end_time < Time.now
stages << {id: stage.id, name: "#{stage.name}排行榜", rate: stage.score_rate, start_time: stage.min_start_time, end_time: stage.max_end_time}
end
end
stages = stages.sort { |a, b| b[:end_time] <=> a[:end_time] } if stages.size > 0
return stages
end
end

@ -414,7 +414,7 @@ module ExercisesHelper
score2 = 0.0 #填空题
score5 = 0.0 #实训题
ques_stand = [] #问题是否正确
exercise_questions = exercise.exercise_questions.includes(:exercise_answers,:exercise_shixun_answers,:exercise_standard_answers,:exercise_shixun_challenges)
exercise_questions = exercise.exercise_questions.includes(:exercise_standard_answers,:exercise_shixun_challenges)
exercise_questions&.each do |q|
begin
if q.question_type != 5

@ -13,7 +13,7 @@ module PollsHelper
end
def poll_votes_count(votes,user_ids)
votes.find_current_vote("user_id",user_ids.uniq).size
votes.find_current_vote("user_id",user_ids.uniq).reject(&:blank?).size
end
#公用tab页的相关信息

@ -3,6 +3,7 @@ class HotSearchKeyword
class << self
def add(keyword)
return if keyword.blank?
Rails.logger.info("[Hot Keyword] #{keyword} score increment ~")
Rails.cache.data.zincrby(redis_key, 1, keyword)
end

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

@ -53,13 +53,13 @@ class Wechat::Client
private
def request(method, url, **params)
Rails.logger.error("[wechat] request: #{method} #{url} #{params.except(:secret).inspect}")
Rails.logger.info("[wechat] request: #{method} #{url} #{params.except(:secret).inspect}")
client = Faraday.new(url: BASE_SITE)
response = client.public_send(method, url, params)
result = JSON.parse(response.body)
Rails.logger.error("[wechat] response:#{response.status} #{result.inspect}")
Rails.logger.info("[wechat] response:#{response.status} #{result.inspect}")
if response.status != 200
raise Wechat::Error.parse(result)

@ -34,7 +34,7 @@ class Wechat::Weapp
data = cipher.update(encrypted_data) << cipher.final
result = JSON.parse(data[0...-data.last.ord])
raise Wechat::Error, '解密错误' if result.dig('watermark', 'appid') != appid
raise Wechat::Error.new(-1, '解密错误') if result.dig('watermark', 'appid') != appid
result
end

@ -1,9 +1,15 @@
class ApplicationRecord < ActiveRecord::Base
include NumberDisplayHelper
attr_accessor :_extra_data
self.abstract_class = true
def format_time(time)
time.present? ? time.strftime('%Y-%m-%d %H:%M') : ''
end
def display_extra_data(key)
_extra_data&.[](key)
end
end

@ -0,0 +1,6 @@
class ChartRule < ApplicationRecord
belongs_to :competition
belongs_to :competition_stage, optional: true
validates :content, length: { maximum: 1000 }
end

@ -9,12 +9,19 @@ class Competition < ApplicationRecord
has_many :competition_teams, dependent: :destroy
has_many :team_members, dependent: :destroy
has_many :chart_rules, dependent: :destroy
has_many :competition_scores, dependent: :destroy
has_many :competition_staffs, dependent: :destroy
has_one :teacher_staff, -> { where(category: :teacher) }, class_name: 'CompetitionStaff'
has_one :member_staff, -> { where.not(category: :teacher) }, class_name: 'CompetitionStaff'
has_one :competition_mode_setting, dependent: :destroy
has_many :informs, as: :container, dependent: :destroy
has_many :attachments, as: :container, dependent: :destroy
has_many :attachments, as: :container
has_many :competition_awards, dependent: :destroy
after_create :create_competition_modules
@ -48,7 +55,7 @@ class Competition < ApplicationRecord
# 是否为个人赛
def personal?
competition_staffs.maximum(:maximum) == 1
competition_staffs.maximum(:maximum) == 1 || max_num == 1
end
# 报名是否结束
@ -73,18 +80,28 @@ class Competition < ApplicationRecord
# 老师是否能多次报名
def teacher_multiple_limited?
teacher_staff.mutiple_limited?
teacher_staff&.mutiple_limited?
end
# 队员是否能多次报名
def member_multiple_limited?
member_staff.mutiple_limited?
member_staff&.mutiple_limited?
end
def max_min_stage_time
CompetitionStageSection.find_by_sql("SELECT MAX(end_time) as max_end_time, MIN(start_time) as min_start_time,
competition_stage_id FROM competition_stage_sections WHERE competition_id = #{id}
GROUP BY competition_stage_id order by competition_stage_id")
end
def awards_count
competition_awards.pluck(:num)&.sum > 0 ? competition_awards.pluck(:num)&.sum : 20
end
private
def create_competition_modules
CompetitionModule.bulk_insert(*%i[competition_id name position]) do |worker|
CompetitionModule.bulk_insert(*%i[competition_id name position created_at updated_at]) do |worker|
%w(首页 报名 通知公告 排行榜 资料下载).each_with_index do |name, index|
worker.add(competition_id: id, name: name, position: index + 1)
end

@ -0,0 +1,3 @@
class CompetitionAward < ApplicationRecord
belongs_to :competition
end

@ -1,3 +1,4 @@
class CompetitionModeSetting < ApplicationRecord
belongs_to :course
belongs_to :course, optional: true
belongs_to :competition
end

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

@ -0,0 +1,6 @@
class CompetitionScore < ApplicationRecord
belongs_to :user
belongs_to :competition
belongs_to :competition_stage, optional: true
belongs_to :competition_team, optional: true
end

@ -3,5 +3,15 @@ class CompetitionStage < ApplicationRecord
has_many :competition_stage_sections, dependent: :destroy
has_many :competition_entries, dependent: :destroy
has_many :competition_scores, dependent: :destroy
has_one :chart_rule, dependent: :destroy
def min_start_time
competition_stage_sections.where("start_time is not null").pluck(:start_time).min
end
def max_end_time
competition_stage_sections.where("end_time is not null").pluck(:end_time).max
end
end

@ -7,6 +7,7 @@ class CompetitionTeam < ApplicationRecord
has_many :team_members, dependent: :destroy
has_many :users, through: :team_members, source: :user
has_many :competition_scores, dependent: :destroy
has_many :members, -> { without_teachers }, class_name: 'TeamMember'
has_many :teachers, -> { only_teachers }, class_name: 'TeamMember'
@ -30,4 +31,21 @@ class CompetitionTeam < ApplicationRecord
self.code = code
code
end
def teachers_info
info = ""
teachers.each do |teacher|
teacher_info = "#{teacher.user.real_name}/#{teacher.user.school_name}"
info += info == "" ? teacher_info : "#{teacher_info}"
end
info
end
def teachers_name
teachers.map{|teacher| teacher.user.real_name}.join(",")
end
def members_name
members.map{|member| member.user.real_name}.join(",")
end
end

@ -3,4 +3,6 @@ class Inform < ApplicationRecord
validates :name, length: { maximum: 60 }
validates :description, length: { maximum: 5000 }
has_many :attachments, as: :container, dependent: :destroy
end

@ -43,7 +43,7 @@ class LaboratorySetting < ApplicationRecord
navbar: [
{ 'name' => '实践课程', 'link' => '/paths', 'hidden' => false },
{ 'name' => '翻转课堂', 'link' => '/courses', 'hidden' => false },
{ 'name' => '实项目', 'link' => '/shixuns', 'hidden' => false },
{ 'name' => '实项目', 'link' => '/shixuns', 'hidden' => false },
{ 'name' => '在线竞赛', 'link' => '/competitions', 'hidden' => false },
{ 'name' => '教学案例', 'link' => '/moop_cases', 'hidden' => false },
{ 'name' => '交流问答', 'link' => '/forums', 'hidden' => false },

@ -0,0 +1,2 @@
class ModuleSetting < ApplicationRecord
end

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

@ -4,6 +4,6 @@ class OpenUsers::Wechat < OpenUser
end
def en_type
'qq'
'wechat'
end
end

@ -27,6 +27,7 @@ class Shixun < ApplicationRecord
has_one :first_shixun_tag_repertoire, class_name: 'ShixunTagRepertoire'
has_one :first_tag_repertoire, through: :first_shixun_tag_repertoire, source: :tag_repertoire
has_many :homework_commons_shixuns, class_name: 'HomeworkCommonsShixun'
#实训的关卡
has_many :exercise_shixun_challenges, :dependent => :destroy
@ -47,6 +48,9 @@ class Shixun < ApplicationRecord
has_many :shixun_service_configs, :dependent => :destroy
has_many :tidings, as: :container, dependent: :destroy
# 实训审核记录
has_many :shixun_reviews, -> {order("shixun_reviews.created_at desc")}, :dependent => :destroy
scope :search_by_name, ->(keyword) { where("name like ? or description like ? ",
"%#{keyword}%", "%#{keyword}%") }

@ -0,0 +1,4 @@
class ShixunReview < ApplicationRecord
belongs_to :user
belongs_to :shixun
end

@ -237,6 +237,11 @@ class User < ApplicationRecord
professional_certification
end
# 学校所在的地区
def school_province
user_extension&.school&.province || ''
end
# 用户的学校名称
def school_name
user_extension&.school&.name || ''

@ -0,0 +1,3 @@
class WeappSetting < ApplicationRecord
scope :only_online, -> { where(online: true) }
end

@ -0,0 +1,2 @@
class WeappSettings::Advert < WeappSetting
end

@ -0,0 +1,3 @@
class WeappSettings::Carousel < WeappSetting
default_scope { order(position: :asc) }
end

@ -0,0 +1,40 @@
class Admins::CompetitionEnrollListQuery < ApplicationQuery
include CustomSortable
attr_reader :competition, :params
sort_columns :created_at, :competition_team_id, default_by: :created_at, default_direction: :desc
def initialize(competition, params)
@competition = competition
@params = params
end
def call
members = competition.team_members
only_teacher = competition.competition_staffs.count == 1 && competition.competition_staffs.first.category == 'teacher'
members = members.where("team_members.is_teacher = 0") unless only_teacher # 只有老师报名时才显示老师,此时老师作为队员
school = params[:school].to_s.strip
if school.present?
school_ids = School.where("schools.name like ?", "%#{school}%").pluck(:id)
school_ids = school_ids.size == 0 ? "(-1)" : "(" + school_ids.join(",") + ")"
members = members.joins(user: :user_extension).where("user_extensions.school_id in #{school_ids}")
end
location = params[:location].to_s.strip
if location.present?
members = members.joins(user: { user_extension: :school }).where("schools.province like ?", "%#{location}%")
end
# 关键字模糊查询
keyword = params[:keyword].to_s.strip
if keyword.present?
members = members.joins(:competition_team)
.where('competition_teams.name LIKE :keyword', keyword: "%#{keyword}%")
end
custom_sort(members, params[:sort_by], params[:sort_direction])
end
end

@ -37,6 +37,9 @@ class Admins::UserQuery < ApplicationQuery
users = users.where('CONCAT(lastname, firstname) LIKE :name', name: "%#{name}%")
end
# 单位ID
users = users.joins(:user_extension).where(user_extensions: { school_id: params[:school_id] }) if params[:school_id].present?
# 学校名称
school_name = params[:school_name].to_s.strip.presence
users = users.joins(user_extension: :school).where('schools.name LIKE ?', "%#{school_name}%") if school_name

@ -0,0 +1,142 @@
class Admins::UserStatisticQuery < ApplicationQuery
include CustomSortable
attr_reader :params
sort_columns :study_challenge_count, :finish_challenge_count, :study_shixun_count, :finish_shixun_count,
default_by: :finish_shixun_count, default_direction: :desc
def initialize(params)
@params = params
end
def call
users = User.where(type: 'User').group(:id)
users = users.joins(:user_extension).where(user_extensions: { school_id: params[:school_id] }) if params[:school_id].present?
total = users.count.count
# 根据排序字段进行查询
users = query_by_sort_column(users, params[:sort_by])
users = custom_sort(users, params[:sort_by], params[:sort_direction])
users = users.includes(user_extension: [:school, :department])
users = users.limit(page_size).offset(offset).to_a
# 查询并组装其它数据
users = package_other_data(users)
[total, users]
end
private
def package_other_data(users)
ids = users.map(&:id)
study_myshixun = Myshixun.where(user_id: ids)
finish_myshixun = Myshixun.where(user_id: ids, status: 1)
study_challenge = Game.joins(:myshixun).where(myshixuns: { user_id: ids }).where(status: [0, 1, 2])
finish_challenge = Game.joins(:myshixun).where(myshixuns: { user_id: ids }).where(status: 2)
if time_range.present?
study_myshixun = study_myshixun.where(updated_at: time_range)
finish_myshixun = finish_myshixun.where(updated_at: time_range)
study_challenge = study_challenge.where(updated_at: time_range)
finish_challenge = finish_challenge.where(updated_at: time_range)
end
study_myshixun_map = study_myshixun.group(:user_id).count
finish_myshixun_map = finish_myshixun.group(:user_id).count
study_challenge_map = study_challenge.group(:user_id).count
finish_challenge_map = finish_challenge.group(:user_id).count
evaluate_count_map = study_challenge.group(:user_id).sum(:evaluate_count)
cost_time_map = study_challenge.group(:user_id).sum(:cost_time)
users.each do |user|
user._extra_data = {
study_shixun_count: study_myshixun_map.fetch(user.id, 0),
finish_shixun_count: finish_myshixun_map.fetch(user.id, 0),
study_challenge_count: study_challenge_map.fetch(user.id, 0),
finish_challenge_count: finish_challenge_map.fetch(user.id, 0),
evaluate_count: evaluate_count_map.fetch(user.id, 0),
cost_time: cost_time_map.fetch(user.id, 0),
}
end
users
end
def query_by_sort_column(users, sort_by_column)
base_query_column = 'users.*'
case sort_by_column.to_s
when 'study_shixun_count' then
users =
if time_range.present?
users.joins("LEFT JOIN myshixuns ON myshixuns.user_id = users.id "\
"AND myshixuns.updated_at BETWEEN '#{time_range.min}' AND '#{time_range.max}'")
else
users.left_joins(:myshixuns)
end
users.select("#{base_query_column}, COUNT(*) study_shixun_count")
when 'finish_shixun_count' then
users =
if time_range.present?
users.joins("LEFT JOIN myshixuns ON myshixuns.user_id = users.id AND myshixuns.status = 1 AND "\
"myshixuns.updated_at BETWEEN '#{time_range.min}' AND '#{time_range.max}'")
else
users.joins('LEFT JOIN myshixuns ON myshixuns.user_id = users.id AND myshixuns.status = 1')
end
users.select("#{base_query_column}, COUNT(*) finish_shixun_count")
when 'study_challenge_count' then
users =
if time_range.present?
users.joins('LEFT JOIN myshixuns ON myshixuns.user_id = users.id')
.joins("LEFT JOIN games ON games.myshixun_id = myshixuns.id "\
"AND games.status IN (0,1,2) AND games.updated_at BETWEEN '#{time_range.min}' AND '#{time_range.max}'")
else
users.joins('LEFT JOIN myshixuns ON myshixuns.user_id = users.id')
.joins("LEFT JOIN games ON games.myshixun_id = myshixuns.id AND games.status IN (0,1,2)")
end
users.select("#{base_query_column}, COUNT(*) study_challenge_count")
when 'finish_challenge_count' then
users =
if time_range.present?
users.joins('LEFT JOIN myshixuns ON myshixuns.user_id = users.id')
.joins("LEFT JOIN games ON games.myshixun_id = myshixuns.id "\
"AND games.status = 2 AND games.updated_at BETWEEN '#{time_range.min}' AND '#{time_range.max}'")
else
users.joins('LEFT JOIN myshixuns ON myshixuns.user_id = users.id')
.joins("LEFT JOIN games ON games.myshixun_id = myshixuns.id AND games.status = 2")
end
users.select("#{base_query_column}, COUNT(*) finish_challenge_count")
else
users
end
end
def time_range
@_time_range ||= begin
case params[:date]
when 'weekly' then 1.weeks.ago..Time.now
when 'monthly' then 1.months.ago..Time.now
when 'quarterly' then 3.months.ago..Time.now
when 'yearly' then 1.years.ago..Time.now
else ''
end
end
end
def page_size
params[:per_page].to_i.zero? ? 20 : params[:per_page].to_i
end
def offset
(params[:page].to_i.zero? ? 0 : params[:page].to_i - 1) * page_size
end
end

@ -0,0 +1,37 @@
class Weapps::SearchQuery < ApplicationQuery
include ElasticsearchAble
attr_reader :params
def initialize(params)
@params = params
end
def call
modal_name.search(keyword, search_options)
end
private
def search_options
hash = {
fields: [:name],
page: page,
per_page: per_page
}
hash.merge(where: { status: 2 }) if modal_name == Shixun
hash
end
def modal_name
@_modal_name ||= begin
case params[:type].to_s
when 'subject' then Subject
when 'shixun' then Shixun
when 'course' then Course
else Subject
end
end
end
end

@ -0,0 +1,32 @@
class Admins::DragWeappAdvertService < ApplicationService
attr_reader :move, :after
def initialize(move, after)
@move = move
@after = after # 移动后下一个位置的元素
end
def call
return if move.position + 1 == after&.position # 未移动
carousels = WeappSettings::Advert.all
ActiveRecord::Base.transaction do
if after.blank? || move.id == after.id # 移动至末尾
total = carousels.count
carousels.where('position > ?', move.position).update_all('position = position - 1')
move.update!(position: total)
return
end
if move.position > after.position # 前移
carousels.where('position >= ? AND position < ?', after.position, move.position).update_all('position = position + 1')
move.update!(position: after.position)
else # 后移
carousels.where('position > ? AND position < ?', move.position, after.position).update_all('position = position - 1')
move.update!(position: after.position - 1)
end
end
end
end

@ -0,0 +1,32 @@
class Admins::DragWeappCarouselService < ApplicationService
attr_reader :move, :after
def initialize(move, after)
@move = move
@after = after # 移动后下一个位置的元素
end
def call
return if move.position + 1 == after&.position # 未移动
carousels = WeappSettings::Carousel.all
ActiveRecord::Base.transaction do
if after.blank? || move.id == after.id # 移动至末尾
total = carousels.count
carousels.where('position > ?', move.position).update_all('position = position - 1')
move.update!(position: total)
return
end
if move.position > after.position # 前移
carousels.where('position >= ? AND position < ?', after.position, move.position).update_all('position = position + 1')
move.update!(position: after.position)
else # 后移
carousels.where('position > ? AND position < ?', move.position, after.position).update_all('position = position - 1')
move.update!(position: after.position - 1)
end
end
end
end

@ -69,7 +69,7 @@ class ExercisePublishTask
:subjective_score => subjective_score,
:commit_method => exercise_user&.commit_method.to_i > 0 ? exercise_user&.commit_method.to_i : 3
}
exercise_user.update_attributes(commit_option)
exercise_user.update_attributes!(commit_option)
end
rescue Exception => e
Rails.logger.info("rescue errors ___________________________#{e}")

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

@ -2,8 +2,32 @@
<% add_admin_breadcrumb('竞赛列表', admins_competitions_path) %>
<% end %>
<div class="box mb-5 admin-competition-list-form">
<div class="d-flex align-items-center w-100">
<div class="flex-1 d-flex align-items-center">
<div class="mr-5">
<% imageExists = File.exist?(disk_filename("Competition", "banner")) %>
<% imageUrl = imageExists ? '/' + banner_img("Competition") + "?#{Time.now.to_i}" : '' %>
<span class="mr-3">竞赛主页banner</span>
<%= image_tag(imageUrl, width: 150, height: 50, class: "preview-image competition-image-banner mr-1", data: { toggle: 'tooltip', title: '点击预览' }) %>
<%= javascript_void_link imageExists ? '重新上传' : '上传图片', class: 'action upload-competition-image-action', data: { source_id: "banner", source_type: 'Competition', toggle: 'modal', target: '.admin-upload-file-modal' } %>
</div>
<div class="mr-5">
<label for="hot">
<%= check_box_tag :hot,(@competition_hot ? 1 : 0),@competition_hot,remote:true,data:{toggle:"tooltip",placement:"top"},class:"competitions-hot-select"%>
<span class="only_view">hot标识勾选则"在线竞赛"显示hot标识</span>
</label>
</div>
</div>
<%= javascript_void_link '新增', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-create-competition-modal' } %>
</div>
</div>
<div class="box competitions-list-container">
<%= render partial: 'admins/competitions/shared/list', locals: { competitions: @competitions } %>
</div>
<%= render 'admins/competitions/shared/create_competition_modal' %>
<%= render partial: 'admins/shared/modal/upload_file_modal', locals: { title: '上传图片' } %>

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

@ -0,0 +1,4 @@
var page_no = $("#competition-item-<%= @competition.id %>").children(":first").html();
$("#competition-item-<%= @competition.id %>").html("<%= j render partial: "admins/competitions/shared/td", locals: {competition: @competition, page_no: 1} %>");
$("#competition-item-<%= @competition.id %>").children(":first").html(page_no);
show_success_flash();

@ -0,0 +1,4 @@
var page_no = $("#competition-item-<%= @competition.id %>").children(":first").html();
$("#competition-item-<%= @competition.id %>").html("<%= j render partial: "admins/competitions/shared/td", locals: {competition: @competition, page_no: 1} %>");
$("#competition-item-<%= @competition.id %>").children(":first").html(page_no);
show_success_flash();

@ -0,0 +1,28 @@
<div class="modal fade admin-create-competition-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">新增竞赛</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form class="admin-create-competition-form" data-url="<%= admins_competitions_path %>">
<div class="form-group d-flex">
<label for="new_mirror_id" class="col-form-label">竞赛名称:</label>
<div class="w-75 d-flex flex-column">
<%= text_field_tag(:competition_name, nil, class: 'form-control', placeholder: '请输入竞赛名称') %>
</div>
</div>
<div class="error text-danger"></div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary submit-btn">确认</button>
</div>
</div>
</div>
</div>

@ -18,7 +18,7 @@
<% competitions.each_with_index do |competition, index| %>
<tr id="competition-item-<%= competition.id %>">
<% page_no = list_index_no(@params_page.to_i, index) %>
<%= render partial: "admins/competitions/shared/td",locals: {competition: competition, page_no: page_no} %>
<%= render partial: "admins/competitions/shared/td", locals: {competition: competition, page_no: page_no} %>
</tr>
<% end %>
<% else %>

@ -1,13 +1,13 @@
<td><%= page_no %></td>
<td class="text-left">
<span><%= link_to competition.name, enroll_list_admins_competition_path(competition), :target => "_blank", :title => competition.name %></span>
<span><%= link_to competition.name, admins_competition_enroll_lists_path(competition), :title => competition.name %></span>
</td>
<td><%= competition.sub_title %></td>
<td><%= competition.mode_type %></td>
<td><%= @member_count_map&.fetch(competition.id, 0) || competition.team_members.count %></td>
<td><%= competition.teacher_staff_num %></td>
<td><%= competition.member_staff_num %></td>
<td class="shixun-setting-image">
<td class="competition-setting-image">
<% imageExists = File.exist?(disk_filename("Competition", competition.id)) %>
<% imageUrl = imageExists ? '/' + url_to_avatar(competition) + "?#{Time.now.to_i}" : '' %>
<%= image_tag(imageUrl, width: 60, height: 40, class: "preview-image competition-image-#{competition.id}", data: { toggle: 'tooltip', title: '点击预览' }, style: imageExists ? '' : 'display:none') %>
@ -15,7 +15,7 @@
</td>
<td><%= competition.created_at.strftime('%Y-%m-%d %H:%M') %></td>
<td class="action-container">
<%= link_to '配置', admins_competition_competition_setting_path(competition), class: 'action edit-action' %>
<%= link_to '配置', admins_competition_competition_settings_path(competition), class: 'action edit-action' %>
<% if !competition.status? && competition.published_at.blank? %>
<%= link_to '发布', publish_admins_competition_path(competition), class: 'action publish-action', method: :post, remote: true %>

@ -0,0 +1,4 @@
var page_no = $("#competition-item-<%= @competition.id %>").children(":first").html();
$("#competition-item-<%= @competition.id %>").html("<%= j render partial: "admins/competitions/shared/td", locals: {competition: @competition, page_no: 1} %>");
$("#competition-item-<%= @competition.id %>").children(":first").html(page_no);
show_success_flash();

@ -0,0 +1,42 @@
<table class="table text-center shixun-settings-list-table">
<thead class="thead-light">
<tr>
<th width="4%" class="text-left">序号</th>
<th width="6%"><%= sort_tag('战队ID', name: 'competition_team_id', path: admins_competition_enroll_lists_path(@competition)) %></th>
<th width="12%">战队名称</th>
<th width="10%">创建者</th>
<th width="10%">队员姓名</th>
<th width="6%">职业</th>
<th width="12%">学号</th>
<th width="10%">队员学校</th>
<th width="6%">地区</th>
<th width="16%">指导老师</th>
<th width="8%"><%= sort_tag('报名时间', name: 'created_at', path: admins_competition_enroll_lists_path(@competition)) %></th>
</tr>
</thead>
<tbody>
<% if enroll_lists.present? %>
<% enroll_lists.each_with_index do |member, index| %>
<tr id="competition-team-member-<%= member.id %>">
<% team = member.competition_team %>
<% page_no = list_index_no(@params_page.to_i, index) %>
<td><%= page_no %></td>
<td><%= member.competition_team_id %></td>
<td><%= @personal ? "--" : team.name %></td>
<td><%= team.user.real_name %></td>
<td><%= member.user.real_name %></td>
<td><%= member.user.identity %></td>
<td><%= member.user.student_id %></td>
<td><%= member.user.school_name %></td>
<td><%= member.user.school_province %></td>
<td><%= @personal ? "--" : team.teachers_info %></td>
<td><%= member.created_at.strftime('%Y-%m-%d %H:%M') %></td>
</tr>
<% end %>
<% else %>
<%= render 'admins/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>
<%= render partial: 'admins/shared/paginate', locals: { objects: enroll_lists } %>

@ -0,0 +1,33 @@
<%
define_admin_breadcrumbs do
add_admin_breadcrumb('竞赛列表', admins_competitions_path)
add_admin_breadcrumb(@competition.name)
end
%>
<div class="box search-form-container flex-column mb-0 pb-0 competition-enroll-list-form">
<ul class="nav nav-tabs w-100 search-form-tabs">
<li class="nav-item">
<%= link_to '报名列表', admins_competition_enroll_lists_path(@competition), class: "nav-link search-form-tab active" %>
</li>
<li class="nav-item">
<%= link_to '获奖证书审批', admins_competition_enroll_lists_path(@competition), class: "nav-link search-form-tab" %>
</li>
</ul>
<div class="d-flex">
<%= form_tag(admins_competition_enroll_lists_path(unsafe_params), method: :get, class: 'form-inline search-form mt-3 flex-1 d-flex', remote: true) do %>
<%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '战队名称检索') %>
<%= text_field_tag(:school, params[:school], class: 'form-control col-sm-2 ml-3', placeholder: '队员学校名称检索') %>
<%= text_field_tag(:location, params[:location], class: 'form-control col-sm-2 ml-3', placeholder: '队员地区检索') %>
<%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
<%= link_to "清除", admins_competition_enroll_lists_path(@competition), class: "btn btn-default",'data-disable-with': '清除中...' %>
<% end %>
<a href="javascript:void(0)" class="btn btn-primary mt-3" id="enroll-lists-export" data-competition-id="<%= @competition.id %>" data-disable-with = '导出中...'>导出</a>
</div>
</div>
<div class="box competition-enroll-list-container">
<%= render(partial: 'admins/enroll_lists/list', locals: { enroll_lists: @enroll_lists }) %>
</div>

@ -0,0 +1 @@
$('.competition-enroll-list-container').html("<%= j( render(partial: 'admins/enroll_lists/list', locals: { enroll_lists: @enroll_lists }) ) %>");

@ -17,7 +17,7 @@
</div>
<%= text_field_tag :identifier, @laboratory.identifier,
maxlength: 15, class: 'form-control font-16',
'onKeyUp': 'value=value.replace(/[^\w\.\-\/]/ig,"").toLowerCase()',
'onKeyUp': 'value=value.replace(/[^\w\-\/]/ig,"").toLowerCase()',
style: 'text-transform:lowercase'%>
<div class="input-group-append">
<% rails_env = EduSetting.get('rails_env') %>

@ -39,6 +39,7 @@
<li>
<%= sidebar_item_group('#user-submenu', '用户', icon: 'user') do %>
<li><%= sidebar_item(admins_users_path, '用户列表', icon: 'user', controller: 'admins-users') %></li>
<li><%= sidebar_item(admins_user_statistics_path, '用户实训情况', icon: 'area-chart', controller: 'admins-user_statistics') %></li>
<% end %>
</li>
@ -77,6 +78,14 @@
<li><%= sidebar_item(edit_admins_help_center_path, '帮助中心', icon: 'question-circle-o', controller: 'admins-help_centers') %></li>
<% end %>
</li>
<li>
<%= sidebar_item_group('#weapp-setting-submenu', '小程序设置', icon: 'id-badge') do %>
<li><%= sidebar_item(admins_weapp_carousels_path, '轮播图', icon: 'image', controller: 'admins-weapp_carousels') %></li>
<li><%= sidebar_item(admins_weapp_adverts_path, '广告栏', icon: 'paper-plane', controller: 'admins-weapp_adverts') %></li>
<% end %>
</li>
<li><%= sidebar_item('/', '返回主站', icon: 'sign-out', controller: 'root') %></li>
</ul>
</nav>

@ -25,3 +25,4 @@ if (!notRefresh) {
} else {
deleteRow.remove();
}
$(document).trigger('delete_success');

@ -11,6 +11,7 @@
<form class="admin-upload-file-form" enctype="multipart/form-data">
<%= hidden_field_tag(:source_type, nil) %>
<%= hidden_field_tag(:source_id, nil) %>
<%= hidden_field_tag(:suffix, nil) %>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">文件</span>

@ -4,15 +4,16 @@
<thead class="thead-light">
<tr>
<th width="8%">头像</th>
<th width="14%">创建者</th>
<th width="8%">创建者</th>
<th width="28%" class="text-left">实训名称</th>
<th width="8%">审核情况</th>
<th width="12%">任务数</th>
<th width="16%">时间</th>
<% if is_processed %>
<th width="14%">拒绝原因</th>
<th width="12%">拒绝原因</th>
<th width="8%">状态</th>
<% else %>
<th width="22%">操作</th>
<th width="20%">操作</th>
<% end %>
</tr>
</thead>
@ -21,6 +22,8 @@
<% applies.each do |apply| %>
<% user = apply.user %>
<% shixun = shixun_map[apply.container_id] %>
<% content_review = shixun.shixun_reviews.select{|sr| sr.review_type == 'Content'}.first %>
<% perference_review = shixun.shixun_reviews.select{|sr| sr.review_type == 'Performance'}.first %>
<tr class="shixun-authorization-item shixun-authorization-<%= apply.id %>">
<td>
<%= link_to "/users/#{user.login}", class: 'shixun-authorization-avatar', target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } do %>
@ -33,6 +36,10 @@
<%= overflow_hidden_span shixun.name, width: 300 %>
<% end %>
</td>
<td>
<%= check_box_tag :content, content_review&.status, content_review&.status.to_i == 1, class:"shixun-setting-form" ,title:"内容审核", disabled: "disabled"%>
<%= check_box_tag :perference, perference_review&.status, perference_review&.status.to_i == 1, class:"shixun-setting-form" ,title:"性能审核", disabled: "disabled"%>
</td>
<td><%= shixun.challenges_count %></td>
<td><%= apply.updated_at.strftime('%Y-%m-%d %H:%M') %></td>

@ -1,12 +1,12 @@
<table class="table text-center shixun-settings-list-table">
<thead class="thead-light">
<th width="4%">序号</th>
<th width="8%">ID</th>
<th width="12%" class="text-left">实训名称</th>
<th width="8%">技术平台</th>
<th width="8%">权限</th>
<th width="15%">技术体系</th>
<th width="12%">上传图片</th>
<th width="8%">上传图片</th>
<th width="8%">小程序封面</th>
<th width="5%">创建者</th>
<th width="5%">关闭</th>
<th width="4%">复制</th>

@ -1,4 +1,3 @@
<td class="shixun-line-no"><%= page_no %></td>
<td><%= shixun.identifier %></td>
<td class="text-left">
<span>
@ -21,6 +20,13 @@
<%= image_tag(imageUrl, width: 60, height: 40, class: "preview-image shixun-image-#{shixun.id}", data: { toggle: 'tooltip', title: '点击预览' }, style: imageExists ? '' : 'display:none') %>
<%= javascript_void_link imageExists ? '重新上传' : '上传图片', class: 'action upload-shixun-image-action', data: { source_id: shixun.id, source_type: 'Shixun', toggle: 'modal', target: '.admin-upload-file-modal' } %>
</td>
<td class="shixun-setting-weapp-image">
<% weappImageExists = Util::FileManage.exists?(shixun, '_weapp') %>
<% imageUrl = weappImageExists ? Util::FileManage.source_disk_file_url(shixun, '_weapp') : '' %>
<%= image_tag(imageUrl, width: 60, height: 40, class: "preview-image shixun-weapp-image-#{shixun.id}", data: { toggle: 'tooltip', title: '点击预览' }, style: weappImageExists ? '' : 'display:none') %>
<%= raw '<br/>' if weappImageExists %>
<%= javascript_void_link weappImageExists ? '重新上传' : '上传图片', class: 'action upload-shixun-weapp-image-action', data: { source_id: shixun.id, source_type: 'Shixun', suffix: '_weapp', toggle: 'modal', target: '.admin-upload-file-modal' } %>
</td>
<td><%= link_to shixun.owner.try(:real_name),"/users/#{shixun.owner.login}",target:'_blank' %></td>
<td>
<% if shixun.status.to_i < 3 %>

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

Loading…
Cancel
Save