diff --git a/app/assets/javascripts/admins/carousels/index.js b/app/assets/javascripts/admins/carousels/index.js index fe665a35f..31ce3000d 100644 --- a/app/assets/javascripts/admins/carousels/index.js +++ b/app/assets/javascripts/admins/carousels/index.js @@ -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; diff --git a/app/assets/javascripts/admins/competitions/index.js b/app/assets/javascripts/admins/competitions/index.js new file mode 100644 index 000000000..c476dbf24 --- /dev/null +++ b/app/assets/javascripts/admins/competitions/index.js @@ -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); + } + }); + } + }); +}); + diff --git a/app/assets/javascripts/admins/enroll_lists/index.js b/app/assets/javascripts/admins/enroll_lists/index.js new file mode 100644 index 000000000..04bd95070 --- /dev/null +++ b/app/assets/javascripts/admins/enroll_lists/index.js @@ -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(); + }); + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/admins/modals/admin-upload-file-modal.js b/app/assets/javascripts/admins/modals/admin-upload-file-modal.js index cf1333381..a53173959 100644 --- a/app/assets/javascripts/admins/modals/admin-upload-file-modal.js +++ b/app/assets/javascripts/admins/modals/admin-upload-file-modal.js @@ -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'); }, diff --git a/app/assets/javascripts/admins/shixun_settings/index.js b/app/assets/javascripts/admins/shixun_settings/index.js index f99574673..8b3eee505 100644 --- a/app/assets/javascripts/admins/shixun_settings/index.js +++ b/app/assets/javascripts/admins/shixun_settings/index.js @@ -34,10 +34,17 @@ $(document).on('turbolinks:load', function() { }); $('.modal.admin-upload-file-modal').on('upload:success', function(e, data){ - var $imageElement = $('.shixun-image-' + data.source_id); - $imageElement.attr('src', data.url); - $imageElement.show(); - $imageElement.next().html('重新上传'); + 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('重新上传'); + } }) } }); diff --git a/app/assets/javascripts/admins/user_statistics/index.js b/app/assets/javascripts/admins/user_statistics/index.js new file mode 100644 index 000000000..3ffb288ac --- /dev/null +++ b/app/assets/javascripts/admins/user_statistics/index.js @@ -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); + }); + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/admins/weapp_adverts/index.js b/app/assets/javascripts/admins/weapp_adverts/index.js new file mode 100644 index 000000000..74e06fbab --- /dev/null +++ b/app/assets/javascripts/admins/weapp_adverts/index.js @@ -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) + } +}) \ No newline at end of file diff --git a/app/assets/javascripts/admins/weapp_carousels/index.js b/app/assets/javascripts/admins/weapp_carousels/index.js new file mode 100644 index 000000000..2fc63f61d --- /dev/null +++ b/app/assets/javascripts/admins/weapp_carousels/index.js @@ -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) + } +}) \ No newline at end of file diff --git a/app/assets/javascripts/cooperative/carousels/index.js b/app/assets/javascripts/cooperative/carousels/index.js new file mode 100644 index 000000000..42c87f46f --- /dev/null +++ b/app/assets/javascripts/cooperative/carousels/index.js @@ -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); + }) + } +}) \ No newline at end of file diff --git a/app/assets/javascripts/cooperative/laboratory_users/index.js b/app/assets/javascripts/cooperative/laboratory_users/index.js new file mode 100644 index 000000000..cd705d8d0 --- /dev/null +++ b/app/assets/javascripts/cooperative/laboratory_users/index.js @@ -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'); + } + }); + } +}); diff --git a/app/assets/javascripts/cooperative/modals/upload-file-modal.js b/app/assets/javascripts/cooperative/modals/upload-file-modal.js new file mode 100644 index 000000000..835ccd383 --- /dev/null +++ b/app/assets/javascripts/cooperative/modals/upload-file-modal.js @@ -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); + } + }); + } + }); + } +}); \ No newline at end of file diff --git a/app/assets/stylesheets/admins/weapp_adverts.scss b/app/assets/stylesheets/admins/weapp_adverts.scss new file mode 100644 index 000000000..d6bfa48ea --- /dev/null +++ b/app/assets/stylesheets/admins/weapp_adverts.scss @@ -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; + } + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/admins/weapp_carousels.scss b/app/assets/stylesheets/admins/weapp_carousels.scss new file mode 100644 index 000000000..4ecf35da0 --- /dev/null +++ b/app/assets/stylesheets/admins/weapp_carousels.scss @@ -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; + } + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/cooperative/carousels.scss b/app/assets/stylesheets/cooperative/carousels.scss new file mode 100644 index 000000000..092e02fe8 --- /dev/null +++ b/app/assets/stylesheets/cooperative/carousels.scss @@ -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; + } + } + } +} \ No newline at end of file diff --git a/app/controllers/admins/competitions_controller.rb b/app/controllers/admins/competitions_controller.rb index 3b9b63243..2e2dbd4e2 100644 --- a/app/controllers/admins/competitions_controller.rb +++ b/app/controllers/admins/competitions_controller.rb @@ -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 diff --git a/app/controllers/admins/enroll_lists_controller.rb b/app/controllers/admins/enroll_lists_controller.rb new file mode 100644 index 000000000..135ef55ae --- /dev/null +++ b/app/controllers/admins/enroll_lists_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/admins/files_controller.rb b/app/controllers/admins/files_controller.rb index b269f8e27..cb8b66119 100644 --- a/app/controllers/admins/files_controller.rb +++ b/app/controllers/admins/files_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/admins/shixun_authorizations_controller.rb b/app/controllers/admins/shixun_authorizations_controller.rb index 31bd5faf7..14d43b00a 100644 --- a/app/controllers/admins/shixun_authorizations_controller.rb +++ b/app/controllers/admins/shixun_authorizations_controller.rb @@ -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 diff --git a/app/controllers/admins/user_statistics_controller.rb b/app/controllers/admins/user_statistics_controller.rb new file mode 100644 index 000000000..71dc3096a --- /dev/null +++ b/app/controllers/admins/user_statistics_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/admins/weapp_adverts_controller.rb b/app/controllers/admins/weapp_adverts_controller.rb new file mode 100644 index 000000000..873036ae7 --- /dev/null +++ b/app/controllers/admins/weapp_adverts_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/admins/weapp_carousels_controller.rb b/app/controllers/admins/weapp_carousels_controller.rb new file mode 100644 index 000000000..9f22efdbe --- /dev/null +++ b/app/controllers/admins/weapp_carousels_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 5c93b08b9..76ffe23d4 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -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 diff --git a/app/controllers/competitions/competition_teams_controller.rb b/app/controllers/competitions/competition_teams_controller.rb index e03810b61..6a4dbfd34 100644 --- a/app/controllers/competitions/competition_teams_controller.rb +++ b/app/controllers/competitions/competition_teams_controller.rb @@ -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 diff --git a/app/controllers/competitions/competitions_controller.rb b/app/controllers/competitions/competitions_controller.rb index 77159fdaf..a17564737 100644 --- a/app/controllers/competitions/competitions_controller.rb +++ b/app/controllers/competitions/competitions_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/concerns/base/paginate_helper.rb b/app/controllers/concerns/base/paginate_helper.rb index a10d7eb2e..7a81da2b9 100644 --- a/app/controllers/concerns/base/paginate_helper.rb +++ b/app/controllers/concerns/base/paginate_helper.rb @@ -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 diff --git a/app/controllers/concerns/logger_helper.rb b/app/controllers/concerns/logger_helper.rb index 44d0448ce..af368a007 100644 --- a/app/controllers/concerns/logger_helper.rb +++ b/app/controllers/concerns/logger_helper.rb @@ -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}") diff --git a/app/controllers/cooperative/base_controller.rb b/app/controllers/cooperative/base_controller.rb index e91f52b91..556cd7aa9 100644 --- a/app/controllers/cooperative/base_controller.rb +++ b/app/controllers/cooperative/base_controller.rb @@ -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 diff --git a/app/controllers/cooperative/carousels_controller.rb b/app/controllers/cooperative/carousels_controller.rb new file mode 100644 index 000000000..c958f9859 --- /dev/null +++ b/app/controllers/cooperative/carousels_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/cooperative/files_controller.rb b/app/controllers/cooperative/files_controller.rb new file mode 100644 index 000000000..56710a968 --- /dev/null +++ b/app/controllers/cooperative/files_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/cooperative/laboratory_users_controller.rb b/app/controllers/cooperative/laboratory_users_controller.rb new file mode 100644 index 000000000..5cfcd5f41 --- /dev/null +++ b/app/controllers/cooperative/laboratory_users_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/cooperative/users_controller.rb b/app/controllers/cooperative/users_controller.rb new file mode 100644 index 000000000..8d6e5f52a --- /dev/null +++ b/app/controllers/cooperative/users_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/exercises_controller.rb b/app/controllers/exercises_controller.rb index d71b72618..99356946f 100644 --- a/app/controllers/exercises_controller.rb +++ b/app/controllers/exercises_controller.rb @@ -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 diff --git a/app/controllers/games_controller.rb b/app/controllers/games_controller.rb index 8a3999410..78f665b3d 100644 --- a/app/controllers/games_controller.rb +++ b/app/controllers/games_controller.rb @@ -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, diff --git a/app/controllers/homework_commons_controller.rb b/app/controllers/homework_commons_controller.rb index 60cf2d6c5..168d05299 100644 --- a/app/controllers/homework_commons_controller.rb +++ b/app/controllers/homework_commons_controller.rb @@ -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 diff --git a/app/controllers/hot_keywords_controller.rb b/app/controllers/hot_keywords_controller.rb new file mode 100644 index 000000000..e0c536e42 --- /dev/null +++ b/app/controllers/hot_keywords_controller.rb @@ -0,0 +1,7 @@ +class HotKeywordsController < ApplicationController + def index + keywords = [] + keywords = HotSearchKeyword.hot(8) if HotSearchKeyword.available? + render_ok(keywords: keywords) + end +end \ No newline at end of file diff --git a/app/controllers/main_controller.rb b/app/controllers/main_controller.rb index 869ba27f6..0e2628c3e 100644 --- a/app/controllers/main_controller.rb +++ b/app/controllers/main_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/myshixuns_controller.rb b/app/controllers/myshixuns_controller.rb index 74f02f1ab..4e587513a 100644 --- a/app/controllers/myshixuns_controller.rb +++ b/app/controllers/myshixuns_controller.rb @@ -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是查看答案的扣分比例 # status:0表示评测成功 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, diff --git a/app/controllers/poll_votes_controller.rb b/app/controllers/poll_votes_controller.rb index b1191d8ea..4a15ae301 100644 --- a/app/controllers/poll_votes_controller.rb +++ b/app/controllers/poll_votes_controller.rb @@ -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,17 +47,23 @@ 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 = { - :user_id => current_user.id, - :poll_question_id => @poll_question.id, - :poll_answer_id => ques_vote_id, - :vote_text => question_answer_text + :user_id => current_user.id, + :poll_question_id => @poll_question.id, + :poll_answer_id => ques_vote_id, + :vote_text => question_answer_text } 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? - user_votes.first.update_attribute("vote_text", question_answer_text) - else - user_votes.destroy_all - end + # current_vote_text = user_votes.find_vote_text + if user_votes.present? + user_votes.first.update_attribute("vote_text", question_answer_text) + # 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 diff --git a/app/controllers/polls_controller.rb b/app/controllers/polls_controller.rb index cb507fdcb..bbd2a06fa 100644 --- a/app/controllers/polls_controller.rb +++ b/app/controllers/polls_controller.rb @@ -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_status = 1 - question_answered += 1 + 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 - ques_status = 0 + 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, diff --git a/app/controllers/searchs_controller.rb b/app/controllers/searchs_controller.rb index 1ea1c5d05..130e8a5cd 100644 --- a/app/controllers/searchs_controller.rb +++ b/app/controllers/searchs_controller.rb @@ -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 diff --git a/app/controllers/shixuns_controller.rb b/app/controllers/shixuns_controller.rb index d0bd4fb0d..05c3500ee 100644 --- a/app/controllers/shixuns_controller.rb +++ b/app/controllers/shixuns_controller.rb @@ -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? diff --git a/app/controllers/users/open_users_controller.rb b/app/controllers/users/open_users_controller.rb index 76413ec02..49bf27a72 100644 --- a/app/controllers/users/open_users_controller.rb +++ b/app/controllers/users/open_users_controller.rb @@ -1,4 +1,4 @@ -class Users::OpenUsers < Users::BaseAccountController +class Users::OpenUsersController < Users::BaseAccountController def destroy current_open_users.destroy! diff --git a/app/controllers/weapps/base_controller.rb b/app/controllers/weapps/base_controller.rb index fadf10fb6..a6002a941 100644 --- a/app/controllers/weapps/base_controller.rb +++ b/app/controllers/weapps/base_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/weapps/code_sessions_controller.rb b/app/controllers/weapps/code_sessions_controller.rb index ca0fa5820..687605fc4 100644 --- a/app/controllers/weapps/code_sessions_controller.rb +++ b/app/controllers/weapps/code_sessions_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/weapps/homes_controller.rb b/app/controllers/weapps/homes_controller.rb index efb80b898..9646f6108 100644 --- a/app/controllers/weapps/homes_controller.rb +++ b/app/controllers/weapps/homes_controller.rb @@ -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 \ No newline at end of file diff --git a/app/controllers/weapps/searchs_controller.rb b/app/controllers/weapps/searchs_controller.rb new file mode 100644 index 000000000..b4960e7b1 --- /dev/null +++ b/app/controllers/weapps/searchs_controller.rb @@ -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 \ No newline at end of file diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index dbdcaea40..1d684350c 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -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 diff --git a/app/helpers/competitions_helper.rb b/app/helpers/competitions_helper.rb new file mode 100644 index 000000000..9e3e7f62a --- /dev/null +++ b/app/helpers/competitions_helper.rb @@ -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 \ No newline at end of file diff --git a/app/helpers/exercises_helper.rb b/app/helpers/exercises_helper.rb index 395d67913..eadbfa05c 100644 --- a/app/helpers/exercises_helper.rb +++ b/app/helpers/exercises_helper.rb @@ -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 diff --git a/app/helpers/polls_helper.rb b/app/helpers/polls_helper.rb index 52e90f559..03210fccd 100644 --- a/app/helpers/polls_helper.rb +++ b/app/helpers/polls_helper.rb @@ -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页的相关信息 diff --git a/app/libs/hot_search_keyword.rb b/app/libs/hot_search_keyword.rb index f026142cb..f79d1641e 100644 --- a/app/libs/hot_search_keyword.rb +++ b/app/libs/hot_search_keyword.rb @@ -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 diff --git a/app/libs/util.rb b/app/libs/util.rb index 84f14a6c0..38b5c9af5 100644 --- a/app/libs/util.rb +++ b/app/libs/util.rb @@ -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 \ No newline at end of file diff --git a/app/libs/wechat/client.rb b/app/libs/wechat/client.rb index 5ac0a28f2..43d5b0732 100644 --- a/app/libs/wechat/client.rb +++ b/app/libs/wechat/client.rb @@ -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) diff --git a/app/libs/wechat/weapp.rb b/app/libs/wechat/weapp.rb index 755c9351a..9684206cd 100644 --- a/app/libs/wechat/weapp.rb +++ b/app/libs/wechat/weapp.rb @@ -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 diff --git a/app/models/application_record.rb b/app/models/application_record.rb index a876440c3..ded4119cd 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -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 diff --git a/app/models/chart_rule.rb b/app/models/chart_rule.rb new file mode 100644 index 000000000..de8fceaf1 --- /dev/null +++ b/app/models/chart_rule.rb @@ -0,0 +1,6 @@ +class ChartRule < ApplicationRecord + belongs_to :competition + belongs_to :competition_stage, optional: true + + validates :content, length: { maximum: 1000 } +end diff --git a/app/models/competition.rb b/app/models/competition.rb index 9055adefc..8b9d59ed2 100644 --- a/app/models/competition.rb +++ b/app/models/competition.rb @@ -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 diff --git a/app/models/competition_award.rb b/app/models/competition_award.rb new file mode 100644 index 000000000..715d82ee2 --- /dev/null +++ b/app/models/competition_award.rb @@ -0,0 +1,3 @@ +class CompetitionAward < ApplicationRecord + belongs_to :competition +end diff --git a/app/models/competition_mode_setting.rb b/app/models/competition_mode_setting.rb index b6bafa7c3..e8d6c17d7 100644 --- a/app/models/competition_mode_setting.rb +++ b/app/models/competition_mode_setting.rb @@ -1,3 +1,4 @@ class CompetitionModeSetting < ApplicationRecord - belongs_to :course + belongs_to :course, optional: true + belongs_to :competition end diff --git a/app/models/competition_module.rb b/app/models/competition_module.rb index be73bf3c1..9579533da 100644 --- a/app/models/competition_module.rb +++ b/app/models/competition_module.rb @@ -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 diff --git a/app/models/competition_score.rb b/app/models/competition_score.rb new file mode 100644 index 000000000..ce7bad427 --- /dev/null +++ b/app/models/competition_score.rb @@ -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 diff --git a/app/models/competition_stage.rb b/app/models/competition_stage.rb index 60d4b1644..dec777b39 100644 --- a/app/models/competition_stage.rb +++ b/app/models/competition_stage.rb @@ -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 \ No newline at end of file diff --git a/app/models/competition_team.rb b/app/models/competition_team.rb index 625b29421..6c2b99859 100644 --- a/app/models/competition_team.rb +++ b/app/models/competition_team.rb @@ -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 \ No newline at end of file diff --git a/app/models/inform.rb b/app/models/inform.rb index 5caf80c5f..d486b6f11 100644 --- a/app/models/inform.rb +++ b/app/models/inform.rb @@ -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 diff --git a/app/models/laboratory_setting.rb b/app/models/laboratory_setting.rb index 32848dca2..ca62f5c91 100644 --- a/app/models/laboratory_setting.rb +++ b/app/models/laboratory_setting.rb @@ -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 }, diff --git a/app/models/module_setting.rb b/app/models/module_setting.rb new file mode 100644 index 000000000..36fc45e6f --- /dev/null +++ b/app/models/module_setting.rb @@ -0,0 +1,2 @@ +class ModuleSetting < ApplicationRecord +end diff --git a/app/models/myshixun.rb b/app/models/myshixun.rb index 006bbf26d..54dcf9011 100644 --- a/app/models/myshixun.rb +++ b/app/models/myshixun.rb @@ -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 : "--" diff --git a/app/models/open_users/wechat.rb b/app/models/open_users/wechat.rb index aadc9e78c..9e7e032b4 100644 --- a/app/models/open_users/wechat.rb +++ b/app/models/open_users/wechat.rb @@ -4,6 +4,6 @@ class OpenUsers::Wechat < OpenUser end def en_type - 'qq' + 'wechat' end end \ No newline at end of file diff --git a/app/models/shixun.rb b/app/models/shixun.rb index 0cdb2e82b..38df5f0d4 100644 --- a/app/models/shixun.rb +++ b/app/models/shixun.rb @@ -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}%") } diff --git a/app/models/shixun_review.rb b/app/models/shixun_review.rb new file mode 100644 index 000000000..a6325e208 --- /dev/null +++ b/app/models/shixun_review.rb @@ -0,0 +1,4 @@ +class ShixunReview < ApplicationRecord + belongs_to :user + belongs_to :shixun +end diff --git a/app/models/user.rb b/app/models/user.rb index 103f9e0b5..135b85e25 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -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 || '' diff --git a/app/models/weapp_setting.rb b/app/models/weapp_setting.rb new file mode 100644 index 000000000..a4ef86eb1 --- /dev/null +++ b/app/models/weapp_setting.rb @@ -0,0 +1,3 @@ +class WeappSetting < ApplicationRecord + scope :only_online, -> { where(online: true) } +end \ No newline at end of file diff --git a/app/models/weapp_settings/advert.rb b/app/models/weapp_settings/advert.rb new file mode 100644 index 000000000..564e85565 --- /dev/null +++ b/app/models/weapp_settings/advert.rb @@ -0,0 +1,2 @@ +class WeappSettings::Advert < WeappSetting +end \ No newline at end of file diff --git a/app/models/weapp_settings/carousel.rb b/app/models/weapp_settings/carousel.rb new file mode 100644 index 000000000..71445fd3f --- /dev/null +++ b/app/models/weapp_settings/carousel.rb @@ -0,0 +1,3 @@ +class WeappSettings::Carousel < WeappSetting + default_scope { order(position: :asc) } +end \ No newline at end of file diff --git a/app/queries/admins/competition_enroll_list_query.rb b/app/queries/admins/competition_enroll_list_query.rb new file mode 100644 index 000000000..64f5dc7f8 --- /dev/null +++ b/app/queries/admins/competition_enroll_list_query.rb @@ -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 \ No newline at end of file diff --git a/app/queries/admins/user_query.rb b/app/queries/admins/user_query.rb index 75e50fc1b..749ba2110 100644 --- a/app/queries/admins/user_query.rb +++ b/app/queries/admins/user_query.rb @@ -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 diff --git a/app/queries/admins/user_statistic_query.rb b/app/queries/admins/user_statistic_query.rb new file mode 100644 index 000000000..d05656b3e --- /dev/null +++ b/app/queries/admins/user_statistic_query.rb @@ -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 diff --git a/app/queries/weapps/search_query.rb b/app/queries/weapps/search_query.rb new file mode 100644 index 000000000..665480073 --- /dev/null +++ b/app/queries/weapps/search_query.rb @@ -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 \ No newline at end of file diff --git a/app/services/admins/drag_weapp_advert_service.rb b/app/services/admins/drag_weapp_advert_service.rb new file mode 100644 index 000000000..b041c9d18 --- /dev/null +++ b/app/services/admins/drag_weapp_advert_service.rb @@ -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 \ No newline at end of file diff --git a/app/services/admins/drag_weapp_carousel_service.rb b/app/services/admins/drag_weapp_carousel_service.rb new file mode 100644 index 000000000..f0b3832b2 --- /dev/null +++ b/app/services/admins/drag_weapp_carousel_service.rb @@ -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 \ No newline at end of file diff --git a/app/tasks/exercise_publish_task.rb b/app/tasks/exercise_publish_task.rb index a7b533d05..4e34320ea 100644 --- a/app/tasks/exercise_publish_task.rb +++ b/app/tasks/exercise_publish_task.rb @@ -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}") diff --git a/app/views/admins/competition_settings/show.html.erb b/app/views/admins/competition_settings/show.html.erb index e69de29bb..6272687e4 100644 --- a/app/views/admins/competition_settings/show.html.erb +++ b/app/views/admins/competition_settings/show.html.erb @@ -0,0 +1,16 @@ +<% + define_admin_breadcrumbs do + add_admin_breadcrumb('竞赛列表', admins_competitions_path) + add_admin_breadcrumb(@competition.name) + end +%> + +
+
+ 基础设置 +
+
+ +
+
+ diff --git a/app/views/admins/competitions/index.html.erb b/app/views/admins/competitions/index.html.erb index b97e26f94..41e061fc1 100644 --- a/app/views/admins/competitions/index.html.erb +++ b/app/views/admins/competitions/index.html.erb @@ -2,8 +2,32 @@ <% add_admin_breadcrumb('竞赛列表', admins_competitions_path) %> <% end %> +
+
+
+
+ <% imageExists = File.exist?(disk_filename("Competition", "banner")) %> + <% imageUrl = imageExists ? '/' + banner_img("Competition") + "?#{Time.now.to_i}" : '' %> + 竞赛主页banner + <%= 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' } %> +
+ +
+ +
+
+ + <%= javascript_void_link '新增', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-create-competition-modal' } %> +
+
+
<%= render partial: 'admins/competitions/shared/list', locals: { competitions: @competitions } %>
+<%= render 'admins/competitions/shared/create_competition_modal' %> <%= render partial: 'admins/shared/modal/upload_file_modal', locals: { title: '上传图片' } %> \ No newline at end of file diff --git a/app/views/admins/competitions/index.js.erb b/app/views/admins/competitions/index.js.erb new file mode 100644 index 000000000..e0985ce5e --- /dev/null +++ b/app/views/admins/competitions/index.js.erb @@ -0,0 +1 @@ +$('.competitions-list-container').html("<%= j( render partial: 'admins/competitions/shared/list', locals: { competitions: @competitions } ) %>"); \ No newline at end of file diff --git a/app/views/admins/competitions/online_switch.js.erb b/app/views/admins/competitions/online_switch.js.erb index e69de29bb..f67122e46 100644 --- a/app/views/admins/competitions/online_switch.js.erb +++ b/app/views/admins/competitions/online_switch.js.erb @@ -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(); \ No newline at end of file diff --git a/app/views/admins/competitions/publish.js.erb b/app/views/admins/competitions/publish.js.erb index e69de29bb..f67122e46 100644 --- a/app/views/admins/competitions/publish.js.erb +++ b/app/views/admins/competitions/publish.js.erb @@ -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(); \ No newline at end of file diff --git a/app/views/admins/competitions/shared/_create_competition_modal.html.erb b/app/views/admins/competitions/shared/_create_competition_modal.html.erb new file mode 100644 index 000000000..94d12ba32 --- /dev/null +++ b/app/views/admins/competitions/shared/_create_competition_modal.html.erb @@ -0,0 +1,28 @@ + \ No newline at end of file diff --git a/app/views/admins/competitions/shared/_list.html.erb b/app/views/admins/competitions/shared/_list.html.erb index 8f215d54c..6e9620a39 100644 --- a/app/views/admins/competitions/shared/_list.html.erb +++ b/app/views/admins/competitions/shared/_list.html.erb @@ -18,7 +18,7 @@ <% competitions.each_with_index do |competition, index| %> <% 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} %> <% end %> <% else %> diff --git a/app/views/admins/competitions/shared/_td.html.erb b/app/views/admins/competitions/shared/_td.html.erb index d05974c86..394a41d79 100644 --- a/app/views/admins/competitions/shared/_td.html.erb +++ b/app/views/admins/competitions/shared/_td.html.erb @@ -1,13 +1,13 @@ <%= page_no %> - <%= link_to competition.name, enroll_list_admins_competition_path(competition), :target => "_blank", :title => competition.name %> + <%= link_to competition.name, admins_competition_enroll_lists_path(competition), :title => competition.name %> <%= competition.sub_title %> <%= competition.mode_type %> <%= @member_count_map&.fetch(competition.id, 0) || competition.team_members.count %> <%= competition.teacher_staff_num %> <%= competition.member_staff_num %> - + <% 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 @@ <%= competition.created_at.strftime('%Y-%m-%d %H:%M') %> - <%= 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 %> diff --git a/app/views/admins/competitions/unpublish.js.erb b/app/views/admins/competitions/unpublish.js.erb index e69de29bb..f67122e46 100644 --- a/app/views/admins/competitions/unpublish.js.erb +++ b/app/views/admins/competitions/unpublish.js.erb @@ -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(); \ No newline at end of file diff --git a/app/views/admins/enroll_lists/_list.html.erb b/app/views/admins/enroll_lists/_list.html.erb new file mode 100644 index 000000000..2b86a3507 --- /dev/null +++ b/app/views/admins/enroll_lists/_list.html.erb @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + <% if enroll_lists.present? %> + <% enroll_lists.each_with_index do |member, index| %> + + <% team = member.competition_team %> + <% page_no = list_index_no(@params_page.to_i, index) %> + + + + + + + + + + + + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
序号<%= sort_tag('战队ID', name: 'competition_team_id', path: admins_competition_enroll_lists_path(@competition)) %>战队名称创建者队员姓名职业学号队员学校地区指导老师<%= sort_tag('报名时间', name: 'created_at', path: admins_competition_enroll_lists_path(@competition)) %>
<%= page_no %><%= member.competition_team_id %><%= @personal ? "--" : team.name %><%= team.user.real_name %><%= member.user.real_name %><%= member.user.identity %><%= member.user.student_id %><%= member.user.school_name %><%= member.user.school_province %><%= @personal ? "--" : team.teachers_info %><%= member.created_at.strftime('%Y-%m-%d %H:%M') %>
+ +<%= render partial: 'admins/shared/paginate', locals: { objects: enroll_lists } %> \ No newline at end of file diff --git a/app/views/admins/enroll_lists/index.html.erb b/app/views/admins/enroll_lists/index.html.erb new file mode 100644 index 000000000..95787d54c --- /dev/null +++ b/app/views/admins/enroll_lists/index.html.erb @@ -0,0 +1,33 @@ +<% + define_admin_breadcrumbs do + add_admin_breadcrumb('竞赛列表', admins_competitions_path) + add_admin_breadcrumb(@competition.name) + end +%> + +
+ + +
+ <%= 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 %> + + 导出 +
+
+ +
+ <%= render(partial: 'admins/enroll_lists/list', locals: { enroll_lists: @enroll_lists }) %> +
\ No newline at end of file diff --git a/app/views/admins/enroll_lists/index.js.erb b/app/views/admins/enroll_lists/index.js.erb new file mode 100644 index 000000000..18edd97f2 --- /dev/null +++ b/app/views/admins/enroll_lists/index.js.erb @@ -0,0 +1 @@ +$('.competition-enroll-list-container').html("<%= j( render(partial: 'admins/enroll_lists/list', locals: { enroll_lists: @enroll_lists }) ) %>"); \ No newline at end of file diff --git a/app/views/admins/laboratory_settings/show.html.erb b/app/views/admins/laboratory_settings/show.html.erb index 120bba6cb..a30ec8b71 100644 --- a/app/views/admins/laboratory_settings/show.html.erb +++ b/app/views/admins/laboratory_settings/show.html.erb @@ -17,7 +17,7 @@ <%= 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'%>
<% rails_env = EduSetting.get('rails_env') %> diff --git a/app/views/admins/shared/_sidebar.html.erb b/app/views/admins/shared/_sidebar.html.erb index 694e9d1ff..87b1a7f67 100644 --- a/app/views/admins/shared/_sidebar.html.erb +++ b/app/views/admins/shared/_sidebar.html.erb @@ -39,6 +39,7 @@
  • <%= sidebar_item_group('#user-submenu', '用户', icon: 'user') do %>
  • <%= sidebar_item(admins_users_path, '用户列表', icon: 'user', controller: 'admins-users') %>
  • +
  • <%= sidebar_item(admins_user_statistics_path, '用户实训情况', icon: 'area-chart', controller: 'admins-user_statistics') %>
  • <% end %> @@ -77,6 +78,14 @@
  • <%= sidebar_item(edit_admins_help_center_path, '帮助中心', icon: 'question-circle-o', controller: 'admins-help_centers') %>
  • <% end %> + +
  • + <%= sidebar_item_group('#weapp-setting-submenu', '小程序设置', icon: 'id-badge') do %> +
  • <%= sidebar_item(admins_weapp_carousels_path, '轮播图', icon: 'image', controller: 'admins-weapp_carousels') %>
  • +
  • <%= sidebar_item(admins_weapp_adverts_path, '广告栏', icon: 'paper-plane', controller: 'admins-weapp_adverts') %>
  • + <% end %> + +
  • <%= sidebar_item('/', '返回主站', icon: 'sign-out', controller: 'root') %>
  • \ No newline at end of file diff --git a/app/views/admins/shared/delete.js.erb b/app/views/admins/shared/delete.js.erb index 0da435fd7..d4989f392 100644 --- a/app/views/admins/shared/delete.js.erb +++ b/app/views/admins/shared/delete.js.erb @@ -24,4 +24,5 @@ if (!notRefresh) { } } else { deleteRow.remove(); -} \ No newline at end of file +} +$(document).trigger('delete_success'); \ No newline at end of file diff --git a/app/views/admins/shared/modal/_upload_file_modal.html.erb b/app/views/admins/shared/modal/_upload_file_modal.html.erb index 036f1122a..a32058cd3 100644 --- a/app/views/admins/shared/modal/_upload_file_modal.html.erb +++ b/app/views/admins/shared/modal/_upload_file_modal.html.erb @@ -11,6 +11,7 @@
    <%= hidden_field_tag(:source_type, nil) %> <%= hidden_field_tag(:source_id, nil) %> + <%= hidden_field_tag(:suffix, nil) %>
    文件 diff --git a/app/views/admins/shixun_authorizations/shared/_list.html.erb b/app/views/admins/shixun_authorizations/shared/_list.html.erb index 042e4096e..c1dec5f45 100644 --- a/app/views/admins/shixun_authorizations/shared/_list.html.erb +++ b/app/views/admins/shixun_authorizations/shared/_list.html.erb @@ -4,15 +4,16 @@ 头像 - 创建者 + 创建者 实训名称 + 审核情况 任务数 时间 <% if is_processed %> - 拒绝原因 + 拒绝原因 状态 <% else %> - 操作 + 操作 <% end %> @@ -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 %> <%= 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 %> + + <%= 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"%> + <%= shixun.challenges_count %> <%= apply.updated_at.strftime('%Y-%m-%d %H:%M') %> diff --git a/app/views/admins/shixun_settings/shared/_list.html.erb b/app/views/admins/shixun_settings/shared/_list.html.erb index 0235e5ef8..7591fe323 100644 --- a/app/views/admins/shixun_settings/shared/_list.html.erb +++ b/app/views/admins/shixun_settings/shared/_list.html.erb @@ -1,12 +1,12 @@ - - + + diff --git a/app/views/admins/shixun_settings/shared/_td.html.erb b/app/views/admins/shixun_settings/shared/_td.html.erb index f72dc8a38..ddbbdff02 100644 --- a/app/views/admins/shixun_settings/shared/_td.html.erb +++ b/app/views/admins/shixun_settings/shared/_td.html.erb @@ -1,4 +1,3 @@ - ++
    奖金
    + + +
    浏览数
    + + +
    报名数
    + + + + + +
    ¥4500
    + + +
    351
    + + +
    351
    + + + + + } + > + {item.name}{item.sub_title===null?"":{ + item.sub_title + }} + } + /> + {item.description} + + )} + /> + +
    15 ? 'block':'none' + // // } + // } + > + + + +
    + + { + datas===undefined?"":datas && datas.length===0? :"" + } + + + + + + + + ) + } +} +export default CompetitionsIndex; \ No newline at end of file diff --git a/public/react/src/modules/competitions/competitimain/Competitionsindex.css b/public/react/src/modules/competitions/competitimain/Competitionsindex.css new file mode 100644 index 000000000..608bc05fd --- /dev/null +++ b/public/react/src/modules/competitions/competitimain/Competitionsindex.css @@ -0,0 +1,109 @@ +.courses-head{ + width: 100%; + height: 300px; + background-image: url(./courses.jpg); + background-color: #081C4B; + background-size: cover; + background-position: center; + background-repeat: no-repeat; +} + +.competitionstitle{ + height:50px !important; + border-radius: 6px; + background: #fff; + display: flex; + justify-content: center; +} + +.competitionstitle2{ + height:50px !important; + margin-left: 30px !important; + background: #fff; + width: 1200px; +} + + +.ant-menu-horizontal { + border-bottom:none !important; +} + + +.competitionsvalue{ + font-size: 16px; + font-family: PingFangSC-Medium,PingFangSC; + font-weight: 500; +} + +.competitionmr50 { + margin-right: 50px !important; +} + +.CompetitionsIndex .ant-list-item{ + background: #fff !important; + margin-top: 20px; + border: none !important; +} + +.CompetitionsIndex .ant-list-item{ + padding:25px; +} + +.CompetitionsIndex .ant-list-item-meta-title{ + height:28px; + font-size:28px; + font-family:PingFangSC-Regular,PingFangSC; + font-weight:400; + color:rgba(5,16,26,1); + line-height:28px; +} + +.CompetitionsIndex .ant-list-vertical .ant-list-item-meta{ + margin-bottom: 20px !important; +} + +.CompetitionsIndex .ant-list-vertical .ant-list-item-action { + margin-top: 20px; + margin-left: auto; +} + +.CompetitionsIndex .ant-list-item-action-split{ + display: none !important; +} + +.CompetitionsIndexdadels{ + font-family: PingFangSC-Regular,PingFangSC; + font-weight: 400; + color: #777777; + margin-bottom: 14px; + margin-right: 20px; +} + +.CompetitionsIndexbottomvalue{ + font-size: 24px; + font-family: ArialMT; + color: rgba(5,16,26,1); +} + +.CompetitionsIndex .gutter-row{ + margin-right:20px; +} + +.pt50{ + padding-top: 50px; +} + +.competitionstitles{ + max-width: 789px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + display: inline-block; + margin-right: 15px; +} + +.competitionsrelative{ + position: absolute; + /*top: 28px;*/ +} + diff --git a/public/react/src/modules/competitions/competitimain/courses.jpg b/public/react/src/modules/competitions/competitimain/courses.jpg new file mode 100644 index 000000000..9447b74be Binary files /dev/null and b/public/react/src/modules/competitions/competitimain/courses.jpg differ diff --git a/public/react/src/modules/courses/css/members.css b/public/react/src/modules/courses/css/members.css index ab7effaa0..22e83a93e 100644 --- a/public/react/src/modules/courses/css/members.css +++ b/public/react/src/modules/courses/css/members.css @@ -1,6 +1,6 @@ .studentList_operation_ul{ color: #999; - font-size: 12px; + font-size: 14px; float: right; margin-top: 2px; } diff --git a/public/react/src/modules/courses/members/CourseGroupListTable.js b/public/react/src/modules/courses/members/CourseGroupListTable.js index 656ae67bc..88d0598b1 100644 --- a/public/react/src/modules/courses/members/CourseGroupListTable.js +++ b/public/react/src/modules/courses/members/CourseGroupListTable.js @@ -92,8 +92,9 @@ function CourseGroupListTable(props) { width:"27%", className:"color-grey-6", render: (member_manager, record, index) => { + // 加title 文本太长会出现卡死 https://www.trustie.net/issues/24950 + // title={record.subStringOfMember_manager ? member_manager : ''} return {record.subStringOfMember_manager || member_manager} } }) diff --git a/public/react/src/modules/courses/new/CoursesNew.js b/public/react/src/modules/courses/new/CoursesNew.js index b2b42dad3..76e5d28f8 100644 --- a/public/react/src/modules/courses/new/CoursesNew.js +++ b/public/react/src/modules/courses/new/CoursesNew.js @@ -266,6 +266,9 @@ class CoursesNew extends Component { }) } }).catch((error) => { + this.setState({ + bottonloading:false + }) console.log(error) }) }else{ @@ -339,6 +342,9 @@ class CoursesNew extends Component { }) } }).catch((error) => { + this.setState({ + bottonloading:false + }) console.log(error) }) }else{ diff --git a/public/react/src/modules/courses/poll/PollDetailTabSecond.js b/public/react/src/modules/courses/poll/PollDetailTabSecond.js index cebae4153..bf8d8b5f8 100644 --- a/public/react/src/modules/courses/poll/PollDetailTabSecond.js +++ b/public/react/src/modules/courses/poll/PollDetailTabSecond.js @@ -25,6 +25,7 @@ class PollDetailTabSecond extends Component{ axios.get(url).then((result)=>{ if(result){ this.setState({ + page: page, questions:result.data.questions, questionsInfo:result.data.question_types }) @@ -55,7 +56,7 @@ class PollDetailTabSecond extends Component{ } render(){ - let{page,limit,questions,question_types}=this.state; + let {page, limit, questions, questionsInfo} = this.state; return(
    { @@ -160,9 +161,10 @@ class PollDetailTabSecond extends Component{ }): } { - question_types && question_types.q_counts>limit && + questionsInfo && questionsInfo.q_counts > limit &&
    - +
    } diff --git a/public/react/src/modules/courses/shixunHomework/Listofworksstudentone.js b/public/react/src/modules/courses/shixunHomework/Listofworksstudentone.js index 7f18089ae..9d3d94561 100644 --- a/public/react/src/modules/courses/shixunHomework/Listofworksstudentone.js +++ b/public/react/src/modules/courses/shixunHomework/Listofworksstudentone.js @@ -987,6 +987,7 @@ class Listofworksstudentone extends Component {
    已通过{record.completion}关,共{this.state.challenges_count}关
    +
    完成任务评测之前查看了参考答案:{record.view_answer_count}关
    }> {record.completion+"/"+this.state.challenges_count} @@ -1100,6 +1101,7 @@ class Listofworksstudentone extends Component { :
    {record.user_name}{record.user_login}
    +
    完成任务评测之前查看了参考答案:{record.view_answer_count}关
    {record.levelscore === "--"?关卡得分:0分 :关卡得分:{record.levelscore}分}
    {record.efficiencyscore === "--"?效率评分:0分 :效率评分:{record.efficiencyscore}分}
    {record.late_penalty === "--"?迟交扣分:0分 :迟交扣分:{record.late_penalty}分}
    @@ -1293,6 +1295,7 @@ class Listofworksstudentone extends Component {
    已通过{record.completion}关,共{this.state.challenges_count}关
    +
    完成任务评测之前查看了参考答案:{record.view_answer_count}关
    }> {record.completion+"/"+this.state.challenges_count}
    @@ -1406,6 +1409,7 @@ class Listofworksstudentone extends Component { :
    {record.user_name}{record.user_login}
    +
    完成任务评测之前查看了参考答案:{record.view_answer_count}关
    {record.levelscore === "--"?关卡得分:0分 :关卡得分:{record.levelscore}分}
    {record.efficiencyscore === "--"?效率评分:0分 :效率评分:{record.efficiencyscore}分}
    {record.late_penalty === "--"?迟交扣分:0分 :迟交扣分:{record.late_penalty}分}
    @@ -1795,6 +1799,7 @@ class Listofworksstudentone extends Component { updatetime: timedata === "Invalid date" ? "--" : timedata, completion: teacherdata.complete_count === null ? "0" :teacherdata.complete_count === undefined ? "0": teacherdata.complete_count, levelscore: teacherdata.final_score, + view_answer_count: teacherdata.view_answer_count, efficiencyscore: teacherdata.eff_score, finalscore: teacherdata.work_score, operating: "查看", @@ -1838,6 +1843,7 @@ class Listofworksstudentone extends Component { levelscore: student_works[i].final_score, efficiencyscore: student_works[i].eff_score==="0.0"?"--":student_works[i].eff_score==="0"?"--":student_works[i].eff_score, finalscore:student_works[i].work_score, + view_answer_count: student_works[i].view_answer_count, operating: "查看", late_penalty: student_works[i].late_penalty=== null?"0":student_works[i].late_penalty === undefined?"0":student_works[i].late_penalty, ultimate_score:student_works[i].ultimate_score, @@ -1979,6 +1985,7 @@ class Listofworksstudentone extends Component { completion: teacherdata.complete_count === null ? "0" :teacherdata.complete_count === undefined ? "0": teacherdata.complete_count, levelscore: teacherdata.final_score, efficiencyscore: teacherdata.eff_score, + view_answer_count: teacherdata.view_answer_count, finalscore: teacherdata.work_score, operating: "查看", late_penalty: teacherdata.late_penalty=== null?"0":teacherdata.late_penalty === undefined?"0":teacherdata.late_penalty, @@ -2327,6 +2334,7 @@ class Listofworksstudentone extends Component { levelscore: student_works[i].final_score, efficiencyscore: student_works[i].eff_score==="0.0"?"--":student_works[i].eff_score==="0"?"--":student_works[i].eff_score, finalscore: student_works[i].work_score, + view_answer_count: student_works[i].view_answer_count, operating: "查看", late_penalty: student_works[i].late_penalty=== null?"0":student_works[i].late_penalty === undefined?"0":student_works[i].late_penalty, ultimate_score:student_works[i].ultimate_score, diff --git a/public/react/src/modules/courses/shixunHomework/shixunreport/OfficialAcademicTranscript.js b/public/react/src/modules/courses/shixunHomework/shixunreport/OfficialAcademicTranscript.js index be99c7035..d115eef45 100644 --- a/public/react/src/modules/courses/shixunHomework/shixunreport/OfficialAcademicTranscript.js +++ b/public/react/src/modules/courses/shixunHomework/shixunreport/OfficialAcademicTranscript.js @@ -1,95 +1,97 @@ import React, {Component} from "react"; import {WordsBtn} from 'educoder'; -import {Table,InputNumber,Tooltip} from "antd"; -import {Link,Switch,Route,Redirect} from 'react-router-dom'; +import {Table, InputNumber, Tooltip} from "antd"; +import {Link, Switch, Route, Redirect} from 'react-router-dom'; import axios from 'axios'; + class OfficialAcademicTranscript extends Component { constructor(props) { super(props); this.state = { - loadingstate:true, - datas:undefined, - customsids:undefined + loadingstate: true, + datas: undefined, + customsids: undefined } } componentDidMount() { } - myjumptopic=(e)=>{ + + myjumptopic = (e) => { console.log("获取到值"); console.log(e); this.props.jumptopic(e); } - editgame_scores=(e,id,maxsum)=>{ - let{datas}=this.state; - let newdatas=datas; - let score=e.target.value; + editgame_scores = (e, id, maxsum) => { + let {datas} = this.state; + let newdatas = datas; + let score = e.target.value; - if(score!=null&&score!=undefined&&score!=""){ - if(score<0){ + if (score != null && score != undefined && score != "") { + if (score < 0) { this.props.showNotification("不能小于0"); this.setState({ - customsids:id + customsids: id }) - }else if(score>maxsum){ + } else if (score > maxsum) { this.props.showNotification(`不能大于关卡分值${maxsum}`); this.setState({ - customsids:id + customsids: id }) - }else{ - let work_id=this.props.data.work_id; - let url=`/student_works/${work_id}/adjust_review_score.json` - axios.post(url,{ - type:"report", - score:score, - challenge_id:id - }).then((result)=>{ - if(result.data.status===0){ + } else { + let work_id = this.props.data.work_id; + let url = `/student_works/${work_id}/adjust_review_score.json` + axios.post(url, { + type: "report", + score: score, + challenge_id: id + }).then((result) => { + if (result.data.status === 0) { // this.props.getdatalist() this.props.showNotification(result.data.message); - this.props.setupdalist(result.data.challenge_score,result.data.overall_appraisal,result.data.work_score) - newdatas.map((item,key)=>{ - if(item.challenge_id.id===id){ - item.game_scores.game_score=score + this.props.setupdalist(result.data.challenge_score, result.data.overall_appraisal, result.data.work_score) + newdatas.map((item, key) => { + if (item.challenge_id.id === id) { + item.game_scores.game_score = score } }) this.setState({ - datas:newdatas, - customsids:undefined + datas: newdatas, + customsids: undefined }) - }else{ + } else { this.props.showNotification(result.data.message); } - }).catch((error)=>{ + }).catch((error) => { }) } - }else{ + } else { this.props.showNotification("调分为空将不会修改之前的分数"); } } render() { - let {customsids}=this.state; - let {data}=this.props; + let {customsids} = this.state; + let {data} = this.props; - let datas=[]; - if(data!=undefined){ - data.stage_list===undefined?"":data.stage_list.forEach((item,key)=>{ + let datas = []; + if (data != undefined) { + data.stage_list === undefined ? "" : data.stage_list.forEach((item, key) => { datas.push({ - customs: key+1, - taskname:{name:item.name,complete_status:item.complete_status}, - openingtime:item.open_time, + customs: key + 1, + taskname: {name: item.name, complete_status: item.complete_status}, + openingtime: item.open_time, evaluating: item.evaluate_count, - finishtime:item.finished_time, - elapsedtime:item.time_consuming, - empvalue:{myself:item.myself_experience,experience:item.experience}, - game_scores:{game_score:item.game_score,game_score_full:item.game_score_full}, - challenge_id:{id:item.challenge_id}, + finishtime: item.finished_time, + elapsedtime: item.time_consuming, + empvalue: {myself: item.myself_experience, experience: item.experience}, + game_scores: {game_score: item.game_score, game_score_full: item.game_score_full}, + challenge_id: {id: item.challenge_id}, challenge_comment: item.challenge_comment, challenge_comment_hidden: item.challenge_comment_hidden, // adjustmentminute:asdasd @@ -97,7 +99,7 @@ class OfficialAcademicTranscript extends Component { }) } - let columns=[{ + let columns = [{ title: '关卡', dataIndex: 'customs', key: 'customs', @@ -110,13 +112,17 @@ class OfficialAcademicTranscript extends Component { title: '任务名称', dataIndex: 'taskname', key: 'taskname', - className:"TaskForms", + className: "TaskForms", render: (text, record) => ( - this.myjumptopic("id"+record.customs)} title={record.taskname.name.length>15?record.taskname.name:""} > + this.myjumptopic("id" + record.customs)} + title={record.taskname.name.length > 15 ? record.taskname.name : ""}> {record.taskname.name} - {record.taskname.complete_status===2?延时:record.taskname.complete_status===3?延时:""} + {record.taskname.complete_status === 2 ? + 延时 : record.taskname.complete_status === 3 ? + 延时 : ""} ), @@ -158,79 +164,101 @@ class OfficialAcademicTranscript extends Component { {record.elapsedtime} ), - }, { - title: '经验值', - key: 'empvalue', - dataIndex: 'empvalue', + }, + { + title: '查看答案', + dataIndex: 'view_answer', + key: 'view_answer', + className: "edu-txt-center", + render: (text, record) => { + return ( + {record.view_answer === true ? + 学生在完成任务评测之前查是否看了参考答案 + + }>已查看 : + 学生在完成任务评测之前查是否看了参考答案 + + }>未查看 } + ) + } + }, + { + title: '经验值', + key: 'empvalue', + dataIndex: 'empvalue', - render: (text, record) => ( - - {record.empvalue.myself}/{record.empvalue.experience} + render: (text, record) => ( + + {record.empvalue.myself}/{record.empvalue.experience} - ), - },{ - title: '关卡得分', - key: 'game_scores', - dataIndex: 'game_scores', - render: (text, record) => ( - + ), + }, { + title: '关卡得分', + key: 'game_scores', + dataIndex: 'game_scores', + render: (text, record) => ( + 关卡得分:{record.game_scores.game_score}/关卡满分:{record.game_scores.game_score_full} }> - {record.game_scores.game_score}/{record.game_scores.game_score_full} + {record.game_scores.game_score}/{record.game_scores.game_score_full} - ), - },{ - title: '调分', - key: 'adjustmentminute', - dataIndex: 'adjustmentminute', + ), + }, { + title: '调分', + key: 'adjustmentminute', + dataIndex: 'adjustmentminute', - render: (text, record) => ( - - this.editgame_scores(e,record.challenge_id.id,record.game_scores.game_score_full)} - // min={0} max={record.game_scores.game_score_full} + render: (text, record) => ( + + this.editgame_scores(e, record.challenge_id.id, record.game_scores.game_score_full)} + // min={0} max={record.game_scores.game_score_full} /> - {/*查看*/} + {/*查看*/} - ), - },{ - title: '操作', - key: 'operation', - dataIndex: 'operation', + ), + }, { + title: '操作', + key: 'operation', + dataIndex: 'operation', - render: (text, record) => ( - + render: (text, record) => ( + this.props.showAppraiseModal("child",record.challenge_id.id,record.challenge_comment,record.challenge_comment_hidden)} + onClick={() => this.props.showAppraiseModal("child", record.challenge_id.id, record.challenge_comment, record.challenge_comment_hidden)} >评阅 - ), - }]; - + ), + }]; - if(this.props.isAdmin()===false){ - columns.some((item,key)=> { - if (item.title === "调分") { - columns.splice(key, 1) - return true - } - } - ) - columns.some((item,key)=> { - if (item.title === "操作") { - columns.splice(key, 1) - return true - } - } - ) - } + if (this.props.isAdmin() === false) { + columns.some((item, key) => { + if (item.title === "调分") { + columns.splice(key, 1) + return true + } + } + ) + columns.some((item, key) => { + if (item.title === "操作") { + columns.splice(key, 1) + return true + } + } + ) + } return (
    {/*{data===undefined?"":""}*/} @@ -301,10 +329,10 @@ class OfficialAcademicTranscript extends Component { } `} - {datas===undefined?"":
    序号 ID 实训名称 技术平台 权限 技术体系上传图片上传图片小程序封面 创建者 关闭 复制<%= page_no %> <%= shixun.identifier %> @@ -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' } %> + <% 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 '
    ' 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' } %> +
    <%= link_to shixun.owner.try(:real_name),"/users/#{shixun.owner.login}",target:'_blank' %> <% if shixun.status.to_i < 3 %> diff --git a/app/views/admins/user_statistics/export.xlsx.axlsx b/app/views/admins/user_statistics/export.xlsx.axlsx new file mode 100644 index 000000000..1511b6ea3 --- /dev/null +++ b/app/views/admins/user_statistics/export.xlsx.axlsx @@ -0,0 +1,18 @@ +wb = xlsx_package.workbook +wb.add_worksheet(name: '用户实训情况') do |sheet| + sheet.add_row %w(姓名 单位部门 学习关卡数 完成关卡数 学习实训数 完成实训数 评测次数 实战时间) + + @users.each do |user| + data = [ + user.real_name, + [user.school_name.presence, user.department_name.presence].compact.join(' - '), + user.display_extra_data(:study_challenge_count), + user.display_extra_data(:finish_challenge_count), + user.display_extra_data(:study_shixun_count), + user.display_extra_data(:finish_shixun_count), + user.display_extra_data(:evaluate_count), + Util.display_cost_time(user.display_extra_data(:cost_time)), + ] + sheet.add_row(data) + end +end \ No newline at end of file diff --git a/app/views/admins/user_statistics/index.html.erb b/app/views/admins/user_statistics/index.html.erb new file mode 100644 index 000000000..31f968eab --- /dev/null +++ b/app/views/admins/user_statistics/index.html.erb @@ -0,0 +1,27 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('用户实训情况') %> +<% end %> + +
    + <%= form_tag(admins_user_statistics_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %> +
    + + <% data_arrs = [['不限', ''], ['最近一周', 'weekly'], ['最近一个月', 'monthly'], ['最近三个月', 'quarterly'], ['最近一年', 'yearly']] %> + <%= select_tag(:date, options_for_select(data_arrs, params[:date]), class: 'form-control') %> +
    + +
    + + <%= hidden_field_tag(:school_id, params[:school_id]) %> + <%= select_tag :school_name, options_for_select([''], params[:school_id]), class: 'form-control school-select flex-1' %> +
    + + <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> + + <% end %> + <%= javascript_void_link '导出', class: 'btn btn-outline-primary export-action', 'data-url': export_admins_user_statistics_path(format: :xlsx) %> +
    + +
    + <%= render partial: 'admins/user_statistics/shared/list', locals: { users: @users } %> +
    \ No newline at end of file diff --git a/app/views/admins/user_statistics/index.js.erb b/app/views/admins/user_statistics/index.js.erb new file mode 100644 index 000000000..c3d2cc9de --- /dev/null +++ b/app/views/admins/user_statistics/index.js.erb @@ -0,0 +1 @@ +$('.user-statistic-list-container').html("<%= j( render partial: 'admins/user_statistics/shared/list', locals: { users: @users } ) %>"); \ No newline at end of file diff --git a/app/views/admins/user_statistics/shared/_list.html.erb b/app/views/admins/user_statistics/shared/_list.html.erb new file mode 100644 index 000000000..44076812d --- /dev/null +++ b/app/views/admins/user_statistics/shared/_list.html.erb @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + <% if users.present? %> + <% users.each do |user| %> + + + + + + + + + + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
    姓名单位部门<%= sort_tag('学习关卡数', name: 'study_challenge_count', path: admins_user_statistics_path) %><%= sort_tag('完成关卡数', name: 'finish_challenge_count', path: admins_user_statistics_path) %><%= sort_tag('学习实训数', name: 'study_shixun_count', path: admins_user_statistics_path) %><%= sort_tag('完成实训数', name: 'finish_shixun_count', path: admins_user_statistics_path) %>评测次数实战时间
    + <%= link_to "/users/#{user.login}", target: '_blank' do %> + <%= overflow_hidden_span user.real_name, width: 100 %> + <% end %> + <%= display_text [user.school_name.presence, user.department_name.presence].compact.join(' - ') %><%= user.display_extra_data(:study_challenge_count) %><%= user.display_extra_data(:finish_challenge_count) %><%= user.display_extra_data(:study_shixun_count) %><%= user.display_extra_data(:finish_shixun_count) %><%= user.display_extra_data(:evaluate_count) %><%= Util.display_cost_time(user.display_extra_data(:cost_time)) || '--' %>
    + +<%= render partial: 'admins/shared/paginate', locals: { objects: users } %> \ No newline at end of file diff --git a/app/views/admins/weapp_adverts/index.html.erb b/app/views/admins/weapp_adverts/index.html.erb new file mode 100644 index 000000000..c560fdb8a --- /dev/null +++ b/app/views/admins/weapp_adverts/index.html.erb @@ -0,0 +1,42 @@ +<% + define_admin_breadcrumbs do + add_admin_breadcrumb('广告栏') + end +%> + +
    +
    + 广告栏 + <%= javascript_void_link '添加', class: 'btn btn-primary btn-sm add-btn', data: { toggle: 'modal', target: '.admin-add-weapp-advert-modal' } %> +
    +
    + <% @adverts.each_with_index do |advert, index| %> + + <% end %> +
    +
    + + +<%= render partial: 'admins/weapp_adverts/shared/add_weapp_advert_modal' %> +<%= render partial: 'admins/shared/modal/upload_file_modal' %> \ No newline at end of file diff --git a/app/views/admins/weapp_adverts/shared/_add_weapp_advert_modal.html.erb b/app/views/admins/weapp_adverts/shared/_add_weapp_advert_modal.html.erb new file mode 100644 index 000000000..9909084b8 --- /dev/null +++ b/app/views/admins/weapp_adverts/shared/_add_weapp_advert_modal.html.erb @@ -0,0 +1,35 @@ + \ No newline at end of file diff --git a/app/views/admins/weapp_carousels/index.html.erb b/app/views/admins/weapp_carousels/index.html.erb new file mode 100644 index 000000000..ec6cfa57c --- /dev/null +++ b/app/views/admins/weapp_carousels/index.html.erb @@ -0,0 +1,42 @@ +<% + define_admin_breadcrumbs do + add_admin_breadcrumb('轮播图') + end +%> + +
    +
    + 顶部轮播图(拖动排序) + <%= javascript_void_link '添加', class: 'btn btn-primary btn-sm add-btn', data: { toggle: 'modal', target: '.admin-add-weapp-carousel-modal' } %> +
    +
    + <% @carousels.each_with_index do |carousel, index| %> + + <% end %> +
    +
    + + +<%= render partial: 'admins/weapp_carousels/shared/add_weapp_carousel_modal' %> +<%= render partial: 'admins/shared/modal/upload_file_modal' %> \ No newline at end of file diff --git a/app/views/admins/weapp_carousels/shared/_add_weapp_carousel_modal.html.erb b/app/views/admins/weapp_carousels/shared/_add_weapp_carousel_modal.html.erb new file mode 100644 index 000000000..767ae61ae --- /dev/null +++ b/app/views/admins/weapp_carousels/shared/_add_weapp_carousel_modal.html.erb @@ -0,0 +1,35 @@ + \ No newline at end of file diff --git a/app/views/competitions/competition_teams/course_detail.json.jbuilder b/app/views/competitions/competition_teams/course_detail.json.jbuilder new file mode 100644 index 000000000..ce37d9568 --- /dev/null +++ b/app/views/competitions/competition_teams/course_detail.json.jbuilder @@ -0,0 +1,28 @@ +total_students_count = 0 +total_shixun_homework_count = 0 +total_course_score = 0 + +json.courses @courses.each do |course| + students_count = course.students.count + total_students_count += students_count + total_shixun_homework_count += course['shixun_homework_count'].to_i + + score = 500 + 5 * @course_shixun_count_map.fetch(course.id, 0) * @course_myshixun_map.fetch(course.id, 0) + total_course_score += score + + teacher = course.teachers.where(user_id: @team_user_ids).first.user + json.creator teacher&.real_name + json.creator_login teacher&.login + json.course_name course.name + json.course_id course.id + json.students_count students_count + json.shixun_homework_count course['shixun_homework_count'] + json.valid_count @course_myshixun_map.fetch(course.id, 0) + json.score score +end + +json.total_course_count @courses.size +json.total_students_count total_students_count +json.total_shixun_homework_count total_shixun_homework_count +json.total_valid_count @course_myshixun_map.values.reduce(:+) +json.total_course_score total_course_score \ No newline at end of file diff --git a/app/views/competitions/competition_teams/index.json.jbuilder b/app/views/competitions/competition_teams/index.json.jbuilder index b641b28e6..d44fcdf1c 100644 --- a/app/views/competitions/competition_teams/index.json.jbuilder +++ b/app/views/competitions/competition_teams/index.json.jbuilder @@ -1,4 +1,5 @@ json.count @count +json.personal @personal json.competition_teams do json.array! @teams.each do |team| json.extract! team, :id, :name, :invite_code diff --git a/app/views/competitions/competition_teams/shixun_detail.json.jbuilder b/app/views/competitions/competition_teams/shixun_detail.json.jbuilder new file mode 100644 index 000000000..abeb8c85c --- /dev/null +++ b/app/views/competitions/competition_teams/shixun_detail.json.jbuilder @@ -0,0 +1,41 @@ +total_myshixun_count = 0 +total_forked_myshixun_count = 0 +total_shixun_score = 0 + +json.shixuns @shixuns.each do |shixun| + total_myshixun_count += shixun.myshixuns_count + total_forked_myshixun_count += shixun['forked_myshixun_count'].to_i + + valid_course_count = @course_count_map.fetch(shixun.id, 0) + valid_student_count = @original_myshixun_count_map.fetch(shixun.id, 0) + score = + if shixun.fork_from.blank? + 500 + 50 * valid_course_count + 10 * valid_student_count + else + 100 + 10 * valid_course_count + 5 * valid_student_count + end + + @forked_shixun_map.each do |shixun_id, fork_from_id| + next if fork_from_id != shixun.id + + score += 100 + 10 * @forked_map.fetch(shixun_id, 0) + 5 * @forked_myshixun_count_map.fetch(shixun_id, 0) + end + + total_shixun_score += score + + json.creator shixun.user.real_name + json.creator_login shixun.user.login + json.shixun_name shixun.name + json.shixun_identifier shixun.identifier + json.forked shixun.fork_from.present? + json.myshixuns_count shixun.myshixuns_count + json.forked_myshixun_count shixun['forked_myshixun_count'].to_i + json.valid_count @myshixun_count_map.fetch(shixun.id, 0) + json.score score +end + +json.shixun_count @shixuns.size +json.total_myshixun_count total_myshixun_count +json.total_forked_myshixun_count total_forked_myshixun_count +json.total_valid_count @myshixun_count_map.values.reduce(:+) +json.total_shixun_score total_shixun_score \ No newline at end of file diff --git a/app/views/competitions/competitions/chart_list.xlsx.axlsx b/app/views/competitions/competitions/chart_list.xlsx.axlsx new file mode 100644 index 000000000..2186b6ffd --- /dev/null +++ b/app/views/competitions/competitions/chart_list.xlsx.axlsx @@ -0,0 +1,18 @@ + +wb = xlsx_package.workbook +# wb.use_autowidth = false +wb.styles do |s| + sz_all = s.add_style :border => { :style => :thin, :color =>"000000" },:alignment => {:horizontal => :center} + blue_cell = s.add_style :bg_color => "FAEBDC", :sz => 10,:height => 20,:b => true, :border => { :style => :thin, :color =>"000000" },:alignment => {:horizontal => :center} + wb.add_worksheet(:name => "比赛成绩") do |sheet| + sheet.sheet_view.show_grid_lines = false + sheet.add_row table_columns, :style => blue_cell + if chart_lists.count > 0 + chart_lists.each do |user| + sheet.add_row user, :height => 20,:style => sz_all + end #each_widh_index + end + sheet.column_widths *([20]*sheet.column_info.count) + sheet.column_info.first.width = 12 + end #add_worksheet +end \ No newline at end of file diff --git a/app/views/competitions/competitions/chart_rules.json.jbuilder b/app/views/competitions/competitions/chart_rules.json.jbuilder new file mode 100644 index 000000000..bf1d04689 --- /dev/null +++ b/app/views/competitions/competitions/chart_rules.json.jbuilder @@ -0,0 +1,5 @@ +json.rule_contents @rule_contents.each do |rule| + json.(rule, :id, :content, :competition_stage_id) +end + +json.stages chart_stages @competition \ No newline at end of file diff --git a/app/views/competitions/competitions/charts.json.jbuilder b/app/views/competitions/competitions/charts.json.jbuilder new file mode 100644 index 000000000..f59f377f7 --- /dev/null +++ b/app/views/competitions/competitions/charts.json.jbuilder @@ -0,0 +1,21 @@ +json.user_ranks @user_ranks.each do |user_rank| + rank = @records.map(&:id).index(user_rank.id) + rank = rank.present? ? (rank+1) : 0 + json.rank rank == 0 ? "--" : user_rank.rank + json.team_name user_rank.name + json.user_name user_rank.user.real_name + json.cost_time rank == 0 && user_rank.cost_time ? "--" : com_spend_time(user_rank.cost_time) + json.score rank == 0 ? "--" : user_rank.score.round(2) +end + +json.teams @records.each do |record| + record_user = record.user + json.team_name record.name + json.record_user_name record_user.real_name + json.user_image url_to_avatar(record_user) + json.user_login record_user.login + school_name = chart_school_str record.team_members.select{|member| !member.is_teacher}.pluck(:user_id) + json.school_name school_name + json.score record&.score&.round(2) + json.spend_time record.cost_time ? com_spend_time(record.cost_time) : "--" +end \ No newline at end of file diff --git a/app/views/competitions/competitions/common_header.json.jbuilder b/app/views/competitions/competitions/common_header.json.jbuilder new file mode 100644 index 000000000..882d1d1c0 --- /dev/null +++ b/app/views/competitions/competitions/common_header.json.jbuilder @@ -0,0 +1,24 @@ +json.extract! @competition, :id, :name, :sub_title, :identifier, :bonus, :mode +json.visits_count @competition.visits +member_count = @competition.team_members.count +json.member_count member_count.zero? ? 268 : member_count + +json.start_time @competition.start_time&.strftime("%Y-%m-%d") +json.end_time @competition.end_time&.strftime("%Y-%m-%d") +json.enroll_end_time @competition.enroll_end_time&.strftime("%Y-%m-%d %H:%M:%S") + +json.published @competition.published? +json.nearly_published @competition.published_at.present? + +json.competition_modules @competition_modules do |com_module| + json.(com_module, :name, :position) + json.module_url com_module.module_url +end + +json.stages + +if @competition.mode == 1 + json.course_id @competition.competition_mode_setting&.course_id + json.member_of_course @user.member_of_course?(@competition.competition_mode_setting&.course) +end + diff --git a/app/views/admins/competitions/enroll_list.html.erb b/app/views/competitions/competitions/enroll.json.jbuilder similarity index 100% rename from app/views/admins/competitions/enroll_list.html.erb rename to app/views/competitions/competitions/enroll.json.jbuilder diff --git a/app/views/competitions/competitions/index.json.jbuilder b/app/views/competitions/competitions/index.json.jbuilder index 4b8390a7d..8e99d754d 100644 --- a/app/views/competitions/competitions/index.json.jbuilder +++ b/app/views/competitions/competitions/index.json.jbuilder @@ -1,7 +1,7 @@ json.count @count json.competitions do json.array! @competitions.each do |competition| - json.extract! competition, :id, :identifier, :name, :sub_title, :bonus, :description + json.extract! competition, :id, :identifier, :name, :sub_title, :bonus, :description, :mode json.visits_count competition.visits member_count = @member_count_map&.fetch(competition.id, 0) || competition.team_members.count @@ -12,18 +12,18 @@ json.competitions do json.nearly_published competition.published_at.present? json.single_stage (@stage_count_map&.fetch(competition.id, 0) || competition.competition_stages.count) == 1 - json.start_time competition.display_start_time - json.end_time competition.display_end_time - json.enroll_end_time competition.display_enroll_end_time + json.start_time competition.start_time&.strftime("%Y-%m-%d") + json.end_time competition.end_time&.strftime("%Y-%m-%d") + json.enroll_end_time competition.enroll_end_time&.strftime("%Y-%m-%d %H:%M:%S") - section = competition.current_stage_section - if section - json.current_stage do - - json.name section.competition_stage.name - json.start_time section.display_start_time - json.end_time section.display_end_time - end - end + # section = competition.current_stage_section + # if section + # json.current_stage do + # + # json.name section.competition_stage.name + # json.start_time section.display_start_time + # json.end_time section.display_end_time + # end + # end end end \ No newline at end of file diff --git a/app/views/competitions/competitions/informs.json.jbuilder b/app/views/competitions/competitions/informs.json.jbuilder new file mode 100644 index 000000000..1b40c93e7 --- /dev/null +++ b/app/views/competitions/competitions/informs.json.jbuilder @@ -0,0 +1,6 @@ +json.informs @informs.each do |inform| + json.(inform, :id, :name, :description) + json.attachments inform.attachments do |atta| + json.partial! "attachments/attachment_simple", locals: {attachment: atta} + end +end \ No newline at end of file diff --git a/app/views/competitions/competitions/md_content.json.jbuilder b/app/views/competitions/competitions/md_content.json.jbuilder new file mode 100644 index 000000000..45325cecb --- /dev/null +++ b/app/views/competitions/competitions/md_content.json.jbuilder @@ -0,0 +1,4 @@ +json.(@md_content, :id, :name, :content) +json.attachments @md_content.attachments do |atta| + json.partial! "attachments/attachment_simple", locals: {attachment: atta} +end \ No newline at end of file diff --git a/app/views/competitions/competitions/show.json.jbuilder b/app/views/competitions/competitions/show.json.jbuilder index 18216ee92..f96949398 100644 --- a/app/views/competitions/competitions/show.json.jbuilder +++ b/app/views/competitions/competitions/show.json.jbuilder @@ -1,36 +1,2 @@ -competition = current_competition -json.extract! competition, :id, :name, :sub_title, :identifier - -json.start_time competition.display_start_time -json.end_time competition.display_end_time -json.enroll_end_time competition.display_enroll_end_time - -json.images do - json.array! competition.attachments, partial: 'attachments/attachment_simple', as: :attachment -end - -json.competition_stages do - stages = competition.competition_stages.includes(competition_stage_sections: :competition_entries) - json.array! stages.each do |stage| - json.extract! stage, :id, :name - - json.sections do - json.array! stage.competition_stage_sections.each do |section| - json.extract! section, :id, :name - - decorator_section = ActiveDecorator::Decorator.instance.decorate(section) - json.start_time decorator_section.display_start_time - json.end_time decorator_section.display_end_time - - is_start = section.start_time > Time.now - json.entries do - json.array! section.competition_entries.each do |entry| - json.extract! entry, :id, :name - - json.url is_start ? entry.url : '' - end - end - end - end - end -end \ No newline at end of file +json.extract! @competition, :id, :introduction +json.image_url url_to_avatar(@competition) \ No newline at end of file diff --git a/app/views/cooperative/carousels/index.html.erb b/app/views/cooperative/carousels/index.html.erb new file mode 100644 index 000000000..4b7c0bcc5 --- /dev/null +++ b/app/views/cooperative/carousels/index.html.erb @@ -0,0 +1,41 @@ +<% define_breadcrumbs do %> + <% add_breadcrumb('轮播图设置') %> +<% end %> + +
    +
    + 首页轮播图(拖动排序) + <%= javascript_void_link '添加', class: 'btn btn-primary btn-sm add-btn', data: { toggle: 'modal', target: '.cooperative-add-carousel-modal' } %> +
    +
    + <% @images.each_with_index do |image, index| %> + + <% end %> +
    +
    + + +<%= render partial: 'cooperative/carousels/shared/add_carousel_modal', locals: { laboratory_id: current_laboratory } %> +<%= render partial: 'cooperative/shared/modal/upload_file_modal' %> \ No newline at end of file diff --git a/app/views/cooperative/carousels/shared/_add_carousel_modal.html.erb b/app/views/cooperative/carousels/shared/_add_carousel_modal.html.erb new file mode 100644 index 000000000..687649c9b --- /dev/null +++ b/app/views/cooperative/carousels/shared/_add_carousel_modal.html.erb @@ -0,0 +1,36 @@ + \ No newline at end of file diff --git a/app/views/cooperative/laboratory_settings/edit.html.erb b/app/views/cooperative/laboratory_settings/edit.html.erb index c070ee014..a0d389acf 100644 --- a/app/views/cooperative/laboratory_settings/edit.html.erb +++ b/app/views/cooperative/laboratory_settings/edit.html.erb @@ -16,7 +16,7 @@ <%= 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'%>
    <% rails_env = EduSetting.get('rails_env') %> diff --git a/app/views/cooperative/laboratory_users/index.html.erb b/app/views/cooperative/laboratory_users/index.html.erb new file mode 100644 index 000000000..ae1345043 --- /dev/null +++ b/app/views/cooperative/laboratory_users/index.html.erb @@ -0,0 +1,14 @@ +<% define_breadcrumbs do %> + <% add_breadcrumb('管理员列表', cooperative_laboratory_users_path) %> +<% end %> + +
    +
    + <%= javascript_void_link '添加管理员', class: 'btn btn-primary btn-sm', data: { toggle: 'modal', target: '.cooperative-add-laboratory-user-modal'} %> +
    + +
    + <%= render partial: 'cooperative/laboratory_users/shared/list', locals: { laboratory_users: @laboratory_users } %> +
    + +<%= render 'cooperative/laboratory_users/shared/add_laboratory_user_modal' %> \ No newline at end of file diff --git a/app/views/cooperative/laboratory_users/index.js.erb b/app/views/cooperative/laboratory_users/index.js.erb new file mode 100644 index 000000000..d5d9e6e99 --- /dev/null +++ b/app/views/cooperative/laboratory_users/index.js.erb @@ -0,0 +1 @@ +$('.laboratory-user-list-container').html("<%= j(render partial: 'cooperative/laboratory_users/shared/list', locals: { laboratory_users: @laboratory_users }) %>"); \ No newline at end of file diff --git a/app/views/cooperative/laboratory_users/shared/_add_laboratory_user_modal.html.erb b/app/views/cooperative/laboratory_users/shared/_add_laboratory_user_modal.html.erb new file mode 100644 index 000000000..dabb3feb3 --- /dev/null +++ b/app/views/cooperative/laboratory_users/shared/_add_laboratory_user_modal.html.erb @@ -0,0 +1,28 @@ + \ No newline at end of file diff --git a/app/views/cooperative/laboratory_users/shared/_list.html.erb b/app/views/cooperative/laboratory_users/shared/_list.html.erb new file mode 100644 index 000000000..23823ef5e --- /dev/null +++ b/app/views/cooperative/laboratory_users/shared/_list.html.erb @@ -0,0 +1,43 @@ + + + + + + + + + + + + + <% if laboratory_users.present? %> + <% laboratory_users.each do |laboratory_user| %> + <% user = laboratory_user.user %> + + + + + + + + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
    头像真实姓名邮件地址手机号码单位部门操作
    + <%= link_to "/users/#{user.login}", target: '_blank' do %> + + <% end %> + + <%= link_to "/users/#{user.login}", target: '_blank' do %> + <%= overflow_hidden_span user.real_name, width: 100 %> + <% end %> + <%= overflow_hidden_span display_text(user.mail), width: 150 %><%= overflow_hidden_span display_text(user.phone), width: 100 %><%= [user.school_name.presence, user.department_name.presence].compact.join('-') %> + <% if current_user.id != laboratory_user.user_id %> + <%= delete_link '删除', cooperative_laboratory_user_path(laboratory_user, element: ".laboratory-user-item-#{laboratory_user.id}"), class: 'delete-laboratory-user-action' %> + <% end %> +
    + +<%= render partial: 'cooperative/shared/paginate', locals: { objects: laboratory_users } %> \ No newline at end of file diff --git a/app/views/cooperative/shared/_sidebar.html.erb b/app/views/cooperative/shared/_sidebar.html.erb index a39a84e3a..40a6b2de4 100644 --- a/app/views/cooperative/shared/_sidebar.html.erb +++ b/app/views/cooperative/shared/_sidebar.html.erb @@ -3,7 +3,7 @@
    } diff --git a/public/react/src/modules/login/Otherloginstart.js b/public/react/src/modules/login/Otherloginstart.js index 9b38ea2b9..fc5d46bbc 100644 --- a/public/react/src/modules/login/Otherloginstart.js +++ b/public/react/src/modules/login/Otherloginstart.js @@ -10,6 +10,7 @@ class Otherloginstart extends Component { let query=this.props.location.search; const type = query.split('?code='); const types = type[1].split('&state='); + console.log(types) let codeurl = `/auth/wechat/callback.json` axios.get(codeurl,{params:{ code:types[0] @@ -20,7 +21,12 @@ class Otherloginstart extends Component { window.location.href="/otherlogin" }else{ // this.getinfo() - window.location.href="/" + if(types[1]==="account"){ + window.location.href="/account/binding" + }else{ + window.location.href="/" + } + } } } diff --git a/public/react/src/modules/page/VNCContainer.js b/public/react/src/modules/page/VNCContainer.js index 6ef10888a..4a591f4ac 100644 --- a/public/react/src/modules/page/VNCContainer.js +++ b/public/react/src/modules/page/VNCContainer.js @@ -24,7 +24,7 @@ class VNCContainer extends Component { repositoryCode: '', displayKey: 1, vnc_reseting: false, - + saving: false, } } componentDidMount() { @@ -40,27 +40,61 @@ class VNCContainer extends Component { getSecondDrawerWidth = () => { return $('#game_right_contents').width() - firstDrawerWidth } + onEditBlur = () => { + console.log('blurblur') + this.doFileUpdateRequestOnCodeMirrorBlur() + } + doFileUpdateRequestOnCodeMirrorBlur = () => { + if (!this.currentPath) { + console.error('未找到文件path') + return; + } + const { myshixun, game } = this.props + var url = `/myshixuns/${myshixun.identifier}/update_file.json` + const codeContent = window.editor_monaco.getValue() + + this.setState({saving: true}) + axios.post(url, { + content: codeContent, + // 评测的时候传1,其它情况不用传,主要是为了区分是用户自己提交还是自动提交 + // evaluate: 0, + game_id : game.id, + path: this.currentPath + } + ).then(res => { + this.setState({saving: false}) + }).catch(e => { + this.setState({saving: false}) + console.error('update_file error') + }) + } renderSecondDrawerChildren = () => { - const { readingCodeLoading, repositoryCode } = this.state; + const { readingCodeLoading, repositoryCode, saving } = this.state; + const { shixun } = this.props const height = $(window).height() - 130 const isEditablePath = false; return ( - +
    + {/* (isEditablePath ? 'none' : 'block') */}
    + style={{ backgroundImage: `url('${notEditablePathImg}')`, display: (shixun.code_edit_permission ? 'none' : 'block') }}>
    ); } fetchReadRepositoryCode = (path) => { + this.currentPath = path; const status = 1 const fetchRepoCodeUrl = `/tasks/${this.props.game.identifier}/rep_content.json?path=${path}&status=${status}` this.setState({ readingCodeLoading: true }); diff --git a/public/react/src/modules/page/component/monaco/TPIMonaco.js b/public/react/src/modules/page/component/monaco/TPIMonaco.js index 0d5eb2d34..b814ecb44 100644 --- a/public/react/src/modules/page/component/monaco/TPIMonaco.js +++ b/public/react/src/modules/page/component/monaco/TPIMonaco.js @@ -347,6 +347,9 @@ class TPIMonaco extends Component { this.props.doFileUpdateRequestOnCodeMirrorBlur(); return false; }); + window.editor_monaco.onDidBlurEditorWidget(() => { + this.props.onEditBlur && this.props.onEditBlur(); + }) }) // window.document.onkeydown = (e) => { diff --git a/public/react/src/modules/projectPackages/ProjectPackageIndex.js b/public/react/src/modules/projectPackages/ProjectPackageIndex.js index 29d4efa17..097e012e0 100644 --- a/public/react/src/modules/projectPackages/ProjectPackageIndex.js +++ b/public/react/src/modules/projectPackages/ProjectPackageIndex.js @@ -36,8 +36,7 @@ class ProjectPackageIndex extends Component { } render() { -console.log(this.props) - console.log(this.state) + return (
    diff --git a/public/react/src/modules/tpm/Audit_situationComponent.js b/public/react/src/modules/tpm/Audit_situationComponent.js new file mode 100644 index 000000000..4d6c413da --- /dev/null +++ b/public/react/src/modules/tpm/Audit_situationComponent.js @@ -0,0 +1,260 @@ +import React, { Component } from 'react'; +import { Redirect } from 'react-router'; +import { List,Typography,Tag,Modal,Radio} from 'antd'; + +import TPMRightSection from './component/TPMRightSection'; +import TPMNav from './component/TPMNav'; +import axios from 'axios'; + +class Audit_situationComponent extends Component { + constructor(props) { + super(props) + this.state = { + datas:undefined, + value:undefined, + } + } + + componentDidMount() { + this.getdatas() + + } + + + getdatas=()=>{ + + let url=`/shixuns/${this.props.match.params.shixunId}/review_newest_record.json`; + axios.get(url).then((response) => { + + if(response.data===undefined||JSON.stringify(response.data) == "{}"||response.data===null){ + this.setState({ + datas:[ + { + name: '内容审核情况', + id:"Content", + }, + { + name: '性能审核情况', + id:"Performance", + }, + ] + }) + }else{ + let newlist=[] + if(response.data.content_info!=undefined&&response.data.perference_info===undefined){ + let arr=[ + { + name: '内容审核情况', + id:"Content", + status:response.data.content_info.status, + username:response.data.content_info.username, + time:response.data.content_info.time, + }, + { + name: '性能审核情况', + id:"Performance", + }, + ] + newlist=arr + } + + if(response.data.content_info===undefined&&response.data.perference_info!=undefined){ + let arr=[ + { + name: '内容审核情况', + id:"Content", + }, + { + name: '性能审核情况', + id:"Performance", + status:response.data.perference_info.status, + username:response.data.perference_info.username, + time:response.data.perference_info.time, + }, + ] + newlist=arr + } + + if(response.data.content_info!=undefined&&response.data.perference_info!=undefined){ + let arr=[ + { + name: '内容审核情况', + id:"Content", + status:response.data.content_info.status, + username:response.data.content_info.username, + time:response.data.content_info.time, + }, + { + name: '性能审核情况', + id:"Performance", + status:response.data.perference_info.status, + username:response.data.perference_info.username, + time:response.data.perference_info.time, + }, + ] + newlist=arr + } + + this.setState({ + datas:newlist + }) + + } + }).catch((error) => { + console.log(error) + }); + } + + showModal = (id,status) => { + debugger + this.setState({ + visible: true, + editid:id, + value:status + }); + }; + + handleOk=(id,editid)=>{ + let url = `/shixuns/${this.props.match.params.shixunId}/review_shixun.json`; + axios.post(url, { + status: id===undefined?1:id, + review_type: editid, + }).then((response) => { + if(response.data.status===0){ + this.props.showNotification(response.data.message); + this.setState({ + visible: false, + }); + this.getdatas() + } + }).catch((error) => { + console.log(error) + }); + }; + + handleCancel = e => { + this.setState({ + visible: false, + }); + }; + + onChange = e => { + this.setState({ + value: e.target.value, + }); + }; + render() { + const { tpmLoading, shixun, user, match } = this.props; + let {value,editid,datas}=this.state; + + console.log(this.props) + return ( + + + {this.state.visible===true? +
    + + + + 已完成 + 未完成 + + + + +
    + +
    :""} + + + + { tpmLoading ?
    : + +
    + +
    + + + +
    + {this.props.identity >2||this.props.identity===undefined?"": ( + this.showModal(item.id,item.status)} key={key}> + + , + ]} + > + +
    {item.name}
    + {item.status===undefined?"":item.status===1?已完成:未完成} +
    } + description={ +
    + {item.time===undefined?"":审核时间: {item.time}} + {item.username===undefined?"":审核人: {item.username}} +
    + } + /> + + )} + />} +
    + +
    + +
    + +
    +
    + + } + + ); + } +} + +export default Audit_situationComponent; diff --git a/public/react/src/modules/tpm/NewHeader.js b/public/react/src/modules/tpm/NewHeader.js index d8e88ae48..eaf3204f2 100644 --- a/public/react/src/modules/tpm/NewHeader.js +++ b/public/react/src/modules/tpm/NewHeader.js @@ -657,76 +657,72 @@ submittojoinclass=(value)=>{ headtypesonClickbool:bool, }) } + //获取数据为空的时候 + gettablogourlnull = () => { + this.setState({ + mygetHelmetapi2: undefined + }); + document.title = "EduCoder"; + var link = document.createElement('link'), + oldLink = document.getElementById('dynamic-favicon'); + link.id = 'dynamic-favicon'; + link.rel = 'shortcut icon'; + link.href = "/react/build/./favicon.ico"; + if (oldLink) { + document.head.removeChild(oldLink); + } + document.head.appendChild(link); + }; + + //获取数据的时候 + gettablogourldata = (response) => { + document.title = response.data.setting.name; + var link = document.createElement('link'), + oldLink = document.getElementById('dynamic-favicon'); + link.id = 'dynamic-favicon'; + link.rel = 'shortcut icon'; + link.href = '/' + response.data.setting.tab_logo_url; + if (oldLink) { + document.head.removeChild(oldLink); + } + document.head.appendChild(link); + } + getAppdata=()=>{ - // console.log("开始刷新数据了") let url = "/setting.json"; axios.get(url).then((response) => { - // console.log("axios.get"); - // console.log(response); - + // console.log("app.js开始请求/setting.json"); + // console.log("获取当前定制信息"); if(response){ if(response.data){ this.setState({ mygetHelmetapi2:response.data.setting }); - document.title = response.data.setting.name; - var link = document.createElement('link'), - oldLink = document.getElementById('dynamic-favicon'); - link.id = 'dynamic-favicon'; - link.rel = 'shortcut icon'; - link.href = '/'+response.data.setting.tab_logo_url; - if (oldLink) { - document.head.removeChild(oldLink); - } - document.head.appendChild(link); - }else { - this.setState({ - mygetHelmetapi2: undefined - }); - document.title = "EduCoder"; - var link = document.createElement('link'), - oldLink = document.getElementById('dynamic-favicon'); - link.id = 'dynamic-favicon'; - link.rel = 'shortcut icon'; - link.href = "/react/build/./favicon.ico"; - if (oldLink) { - document.head.removeChild(oldLink); + try { + if (response.data.setting.tab_logo_url) { + this.gettablogourldata(response); + } else { + this.gettablogourlnull(); + } + } catch (e) { + this.gettablogourlnull(); } - document.head.appendChild(link); - } - }else{ - this.setState({ - mygetHelmetapi2: undefined - }); - document.title = "EduCoder"; - var link = document.createElement('link'), - oldLink = document.getElementById('dynamic-favicon'); - link.id = 'dynamic-favicon'; - link.rel = 'shortcut icon'; - link.href = "/react/build/./favicon.ico"; - if (oldLink) { - document.head.removeChild(oldLink); + + } else { + + this.gettablogourlnull(); + } - document.head.appendChild(link); + + } else { + this.gettablogourlnull(); + } }).catch((error) => { - console.log("开始刷新定制数据了但报错了"); - console.log(error); - this.setState({ - mygetHelmetapi2: undefined - }); - document.title = "EduCoder"; - var link = document.createElement('link'), - oldLink = document.getElementById('dynamic-favicon'); - link.id = 'dynamic-favicon'; - link.rel = 'shortcut icon'; - link.href = "/react/build/./favicon.ico"; - if (oldLink) { - document.head.removeChild(oldLink); - } - document.head.appendChild(link); + this.gettablogourlnull(); + }); }; render() { @@ -1215,6 +1211,10 @@ submittojoinclass=(value)=>{ this.props.Headertop && this.props.Headertop.college_identifier &&
  • 学院统计
  • } + { + this.props.Headertop && this.props.Headertop.laboratory_user && +
  • 后台管理
  • + }
  • 账号管理
  • {/*
  • this.educoderlogin()} >登入测试接口
  • */} diff --git a/public/react/src/modules/tpm/TPMIndex.js b/public/react/src/modules/tpm/TPMIndex.js index 67779af8d..9b3308e04 100644 --- a/public/react/src/modules/tpm/TPMIndex.js +++ b/public/react/src/modules/tpm/TPMIndex.js @@ -22,10 +22,11 @@ import TPMRepositoryCommits from './shixunchild/Repository/TPMRepositoryCommits' import TPMsettings from './TPMsettings/TPMsettings'; -import TPMChallengeComponent from './TPMChallengeContainer' -import TPMPropaedeuticsComponent from './TPMPropaedeuticsComponent' -import TPMRanking_listComponent from './TPMRanking_listContainer' -import TPMCollaboratorsComponent from './TPMCollaboratorsContainer' +import TPMChallengeComponent from './TPMChallengeContainer'; +import TPMPropaedeuticsComponent from './TPMPropaedeuticsComponent'; +import TPMRanking_listComponent from './TPMRanking_listContainer'; +import TPMCollaboratorsComponent from './TPMCollaboratorsContainer'; +import Audit_situationComponent from './Audit_situationComponent'; import '../page/tpiPage.css' @@ -347,6 +348,11 @@ class TPMIndex extends Component { />) }> + () + }> + () diff --git a/public/react/src/modules/tpm/TPMIndexHOC.js b/public/react/src/modules/tpm/TPMIndexHOC.js index b5f35e1ad..3ee678a9a 100644 --- a/public/react/src/modules/tpm/TPMIndexHOC.js +++ b/public/react/src/modules/tpm/TPMIndexHOC.js @@ -283,74 +283,72 @@ export function TPMIndexHOC(WrappedComponent) { // // } + //获取数据为空的时候 + gettablogourlnull = () => { + this.setState({ + mygetHelmetapi: undefined + }); + document.title = "EduCoder"; + var link = document.createElement('link'), + oldLink = document.getElementById('dynamic-favicon'); + link.id = 'dynamic-favicon'; + link.rel = 'shortcut icon'; + link.href = "/react/build/./favicon.ico"; + if (oldLink) { + document.head.removeChild(oldLink); + } + document.head.appendChild(link); + }; + + //获取数据的时候 + gettablogourldata = (response) => { + document.title = response.data.setting.name; + var link = document.createElement('link'), + oldLink = document.getElementById('dynamic-favicon'); + link.id = 'dynamic-favicon'; + link.rel = 'shortcut icon'; + link.href = '/' + response.data.setting.tab_logo_url; + if (oldLink) { + document.head.removeChild(oldLink); + } + document.head.appendChild(link); + } //获取当前定制信息 - //获取当前定制信息 - getAppdata=()=>{ + getAppdata = () => { let url = "/setting.json"; axios.get(url).then((response) => { // console.log("app.js开始请求/setting.json"); // console.log("获取当前定制信息"); - if(response){ - if(response.data){ + if (response) { + if (response.data) { this.setState({ - mygetHelmetapi:response.data.setting + mygetHelmetapi: response.data.setting }); - document.title = response.data.setting.name; - var link = document.createElement('link'), - oldLink = document.getElementById('dynamic-favicon'); - link.id = 'dynamic-favicon'; - link.rel = 'shortcut icon'; - link.href = '/'+response.data.setting.tab_logo_url; - if (oldLink) { - document.head.removeChild(oldLink); + try { + if (response.data.setting.tab_logo_url) { + this.gettablogourldata(response); + } else { + this.gettablogourlnull(); + } + } catch (e) { + this.gettablogourlnull(); } - document.head.appendChild(link); - }else { - this.setState({ - mygetHelmetapi: undefined - }); - document.title = "EduCoder"; - var link = document.createElement('link'), - oldLink = document.getElementById('dynamic-favicon'); - link.id = 'dynamic-favicon'; - link.rel = 'shortcut icon'; - link.href = "/react/build/./favicon.ico"; - if (oldLink) { - document.head.removeChild(oldLink); - } - document.head.appendChild(link); - } - }else{ - this.setState({ - mygetHelmetapi: undefined - }); - document.title = "EduCoder"; - var link = document.createElement('link'), - oldLink = document.getElementById('dynamic-favicon'); - link.id = 'dynamic-favicon'; - link.rel = 'shortcut icon'; - link.href = "/react/build/./favicon.ico"; - if (oldLink) { - document.head.removeChild(oldLink); + + } else { + + this.gettablogourlnull(); + } - document.head.appendChild(link); + + } else { + this.gettablogourlnull(); + } }).catch((error) => { - this.setState({ - mygetHelmetapi: undefined - }); - document.title = "EduCoder"; - var link = document.createElement('link'), - oldLink = document.getElementById('dynamic-favicon'); - link.id = 'dynamic-favicon'; - link.rel = 'shortcut icon'; - link.href = "/react/build/./favicon.ico"; - if (oldLink) { - document.head.removeChild(oldLink); - } - document.head.appendChild(link); + this.gettablogourlnull(); + }); }; /** diff --git a/public/react/src/modules/tpm/challengesnew/TPMquestion.js b/public/react/src/modules/tpm/challengesnew/TPMquestion.js index 8fc2b1133..b76e17b17 100644 --- a/public/react/src/modules/tpm/challengesnew/TPMquestion.js +++ b/public/react/src/modules/tpm/challengesnew/TPMquestion.js @@ -698,7 +698,7 @@ export default class TPMquestion extends Component { let newnewanswerMDvalue = this.editanswersRef.current.getValue().trim(); console.log(newnewanswerMDvalue) if(newnewanswerMDvalue===""||newnewanswerMDvalue===" "){ - newnewanswerMDvalue=newlist + newnewanswerMDvalue=undefined } url="/shixuns/" + id + "/challenges/" + challenge_id + "/update_choose_question.json?choose_id="+challenge_choose_id; @@ -747,7 +747,7 @@ export default class TPMquestion extends Component { let newnewanswerMDvalue = this.newquestioMDMdCont.current.getValue().trim(); if(newnewanswerMDvalue===""||newnewanswerMDvalue===" "){ - newnewanswerMDvalue=newlist + newnewanswerMDvalue=undefined } url="/shixuns/" + id + "/challenges/" + challenge_id + "/create_choose_question.json"; axios.post(url, { diff --git a/public/react/src/modules/tpm/component/TPMNav.js b/public/react/src/modules/tpm/component/TPMNav.js index ce74be13b..ff8f57aa5 100644 --- a/public/react/src/modules/tpm/component/TPMNav.js +++ b/public/react/src/modules/tpm/component/TPMNav.js @@ -28,11 +28,9 @@ class TPMNav extends Component { >背景知识 } - 4||this.props.identity===undefined ? "none" : 'block'}} - className={`${match.url.indexOf('/repository') != -1 ? 'active' : ''} fl mr40`}>版本库 - {secret_repository && 4||this.props.identity===undefined ? "none" : 'block'}} + { this.props.identity >4||this.props.identity===undefined ?"":版本库} + {this.props.identity >4||this.props.identity===undefined ?"": secret_repository && 私密版本库} 评论 排行榜 - {/* target="_blank"*/} + className={`${match.url.indexOf('ranking_list') != -1 ? 'active' : ''} fl mr40`}>排行榜 + + {this.props.identity >2||this.props.identity===undefined?"":审核情况} + 4||this.props.identity===undefined ? "none" : 'block'}} diff --git a/public/react/src/modules/user/AccountPage.js b/public/react/src/modules/user/AccountPage.js index 7268bd7bb..42a323dfd 100644 --- a/public/react/src/modules/user/AccountPage.js +++ b/public/react/src/modules/user/AccountPage.js @@ -25,11 +25,16 @@ const AccountCertification= Loadable({ loader: () => import('./account/AccountCertification'), loading: Loading, }) + const AccountSecure= Loadable({ loader: () => import('./account/AccountSecure'), loading: Loading, }) +const AccountBinding= Loadable({ + loader: () => import('./account/AccountBinding'), + loading: Loading, +}) class AccountPage extends Component { constructor (props) { super(props) @@ -93,40 +98,49 @@ class AccountPage extends Component { padding-bottom: 30px; margin-bottom:30px; margin-right:20px; - height:418px; + height: 460px; } `}
    - () + } + > + + () } > - () - } - > - - () } > - () } > - () + } + > + + () + (props) => (this.getBasicInfo()} {...this.props} {...props} {...this.state} {...common} />) } > + +
    diff --git a/public/react/src/modules/user/account/AccountBinding.js b/public/react/src/modules/user/account/AccountBinding.js new file mode 100644 index 000000000..b36095fcb --- /dev/null +++ b/public/react/src/modules/user/account/AccountBinding.js @@ -0,0 +1,215 @@ +import React, { Component } from 'react'; + +import { SnackbarHOC, getImageUrl, WordsBtn } from 'educoder'; +import { Form, Button, Input ,Modal } from 'antd' +import Modals from "../../modals/Modals"; +import '../../courses/css/Courses.css' +import './common.css' +import axios from 'axios' + +class AccountSecure extends Component { + constructor (props) { + super(props) + this.state = { + Modalstype:false, + } + } + + + showModal=()=>{ + this.setState({ + visible: true, + }); + }; + + handleOk=(e)=> { + this.setState({ + visible: false, + }); + }; + + handleCancel=()=>{ + this.setState({ + visible: false, + }); + }; + + Cancelundologins=()=>{ + this.setState({ + Modalstype:false, + ModalCancel:this.Cancelundologin, + ModalSave:this.Saveundologin, + }) + } + + Saveundologin=(id)=>{ + let {basicInfo}=this.props; + let url=`/users/accounts/${basicInfo.id}/open_users/${id}.json`; + axios.delete(url).then((result)=>{ + if(result.data.status===0){ + this.props.showNotification('解绑成功'); + this.Cancelundologins() + this.props.getBasicInfo() + } + }).catch((error)=>{ + console.log(error) + }) + } + + undologin=(type,id)=>{ + this.setState({ + Modalstype:true, + Modalstopval:type==="wechat"?"是否确定解绑微信账号?":"是否确定解绑QQ账号?", + ModalCancel:this.Cancelundologins, + ModalSave:()=>this.Saveundologin(id), + }) + } + + render() { + let {basicInfo}=this.props; + + return ( +
    +
    + {/*提示*/} + + +
    绑定登录账号
    + +
    + +
    +
    * 我们确保你所提供的信息均处于严格保密状态,不会泄露
    +
    + ); + } +} +const WrappedAccountSecure = Form.create({ name: 'AccountSecure' })(AccountSecure); + +export default WrappedAccountSecure; diff --git a/public/react/src/modules/user/account/AccountNav.js b/public/react/src/modules/user/account/AccountNav.js index 8379ba886..b4065a016 100644 --- a/public/react/src/modules/user/account/AccountNav.js +++ b/public/react/src/modules/user/account/AccountNav.js @@ -12,6 +12,9 @@ class AccountNav extends Component { toSecure = () => { this.props.history.push(`/account/secure`) } + isBinding = () => { + this.props.history.push(`/account/binding`) + } render() { let { basicInfo, current_user } = this.props // console.log(this.props); @@ -19,6 +22,7 @@ class AccountNav extends Component { const isBasic = path.indexOf('profile') != -1 || path == "/account" const isCertification = path.indexOf('certification') != -1 const isSecure = path.indexOf('secure') != -1 + const isBinding = path.indexOf('binding') != -1 return (
    ")}catch(c){console&&console.log(c)}}!function(c){if(document.addEventListener)if(~["complete","loaded","interactive"].indexOf(document.readyState))setTimeout(c,0);else{var l=function(){document.removeEventListener("DOMContentLoaded",l,!1),c()};document.addEventListener("DOMContentLoaded",l,!1)}else document.attachEvent&&(i=c,a=z.document,t=!1,(o=function(){try{a.documentElement.doScroll("left")}catch(c){return void setTimeout(o,50)}h()})(),a.onreadystatechange=function(){"complete"==a.readyState&&(a.onreadystatechange=null,h())});function h(){t||(t=!0,i())}var i,a,t,o}(function(){var c,l;(c=document.createElement("div")).innerHTML=h,h=null,(l=c.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",function(c,l){l.firstChild?function(c,l){l.parentNode.insertBefore(c,l)}(c,l.firstChild):l.appendChild(c)}(l,document.body))})}(window); \ No newline at end of file +!function(z){var c,h='',l=(c=document.getElementsByTagName("script"))[c.length-1].getAttribute("data-injectcss");if(l&&!z.__iconfont__svg__cssinject__){z.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(c){console&&console.log(c)}}!function(c){if(document.addEventListener)if(~["complete","loaded","interactive"].indexOf(document.readyState))setTimeout(c,0);else{var l=function(){document.removeEventListener("DOMContentLoaded",l,!1),c()};document.addEventListener("DOMContentLoaded",l,!1)}else document.attachEvent&&(a=c,i=z.document,t=!1,(o=function(){try{i.documentElement.doScroll("left")}catch(c){return void setTimeout(o,50)}h()})(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,h())});function h(){t||(t=!0,a())}var a,i,t,o}(function(){var c,l;(c=document.createElement("div")).innerHTML=h,h=null,(l=c.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",function(c,l){l.firstChild?function(c,l){l.parentNode.insertBefore(c,l)}(c,l.firstChild):l.appendChild(c)}(l,document.body))})}(window); \ No newline at end of file diff --git a/public/stylesheets/educoder/iconfont/iconfont.json b/public/stylesheets/educoder/iconfont/iconfont.json new file mode 100644 index 000000000..1a21d0726 --- /dev/null +++ b/public/stylesheets/educoder/iconfont/iconfont.json @@ -0,0 +1,1535 @@ +{ + "id": "653600", + "name": "educode", + "font_family": "iconfont", + "css_prefix_text": "icon-", + "description": "", + "glyphs": [ + { + "icon_id": "29944", + "name": "round_close", + "font_class": "roundclose", + "unicode": "e673", + "unicode_decimal": 58995 + }, + { + "icon_id": "29949", + "name": "time_fill", + "font_class": "timefill", + "unicode": "e679", + "unicode_decimal": 59001 + }, + { + "icon_id": "38744", + "name": "round_add_fill", + "font_class": "roundaddfill", + "unicode": "e6d8", + "unicode_decimal": 59096 + }, + { + "icon_id": "209734", + "name": "粗版2_上传云端", + "font_class": "cuban2shangchuanyunduan", + "unicode": "e86d", + "unicode_decimal": 59501 + }, + { + "icon_id": "348426", + "name": "css3", + "font_class": "css3", + "unicode": "ea8b", + "unicode_decimal": 60043 + }, + { + "icon_id": "348560", + "name": "html5", + "font_class": "html5", + "unicode": "eb10", + "unicode_decimal": 60176 + }, + { + "icon_id": "392172", + "name": "钻石", + "font_class": "31", + "unicode": "e6b2", + "unicode_decimal": 59058 + }, + { + "icon_id": "394097", + "name": "章节", + "font_class": "zhangjie1", + "unicode": "e68e", + "unicode_decimal": 59022 + }, + { + "icon_id": "400088", + "name": "关闭", + "font_class": "htmal5icon19", + "unicode": "e66b", + "unicode_decimal": 58987 + }, + { + "icon_id": "425651", + "name": "社区", + "font_class": "shequ", + "unicode": "e6bc", + "unicode_decimal": 59068 + }, + { + "icon_id": "562997", + "name": "vs", + "font_class": "vs", + "unicode": "e682", + "unicode_decimal": 59010 + }, + { + "icon_id": "593484", + "name": "菜单", + "font_class": "weibiaoti12", + "unicode": "e671", + "unicode_decimal": 58993 + }, + { + "icon_id": "622431", + "name": "pdf", + "font_class": "pdf", + "unicode": "e740", + "unicode_decimal": 59200 + }, + { + "icon_id": "695129", + "name": "单选 选中", + "font_class": "danxuanxuanzhong1", + "unicode": "e6bd", + "unicode_decimal": 59069 + }, + { + "icon_id": "712283", + "name": "时间", + "font_class": "shijian", + "unicode": "e69c", + "unicode_decimal": 59036 + }, + { + "icon_id": "715974", + "name": "设置", + "font_class": "shezhi", + "unicode": "e66f", + "unicode_decimal": 58991 + }, + { + "icon_id": "720976", + "name": "坐标", + "font_class": "xiazai18", + "unicode": "e627", + "unicode_decimal": 58919 + }, + { + "icon_id": "736502", + "name": "礼物", + "font_class": "liwu", + "unicode": "e611", + "unicode_decimal": 58897 + }, + { + "icon_id": "776044", + "name": "PHP开发", + "font_class": "phpkaifa", + "unicode": "e67e", + "unicode_decimal": 59006 + }, + { + "icon_id": "826201", + "name": "附件", + "font_class": "fujian", + "unicode": "e670", + "unicode_decimal": 58992 + }, + { + "icon_id": "836716", + "name": "linux", + "font_class": "linux", + "unicode": "e765", + "unicode_decimal": 59237 + }, + { + "icon_id": "845852", + "name": "问号", + "font_class": "wenhao", + "unicode": "e680", + "unicode_decimal": 59008 + }, + { + "icon_id": "913780", + "name": "坐标", + "font_class": "zuobiao", + "unicode": "e674", + "unicode_decimal": 58996 + }, + { + "icon_id": "929277", + "name": "expand", + "font_class": "expand", + "unicode": "e6db", + "unicode_decimal": 59099 + }, + { + "icon_id": "959394", + "name": "重置", + "font_class": "zhongzhi1", + "unicode": "e609", + "unicode_decimal": 58889 + }, + { + "icon_id": "986702", + "name": "路由", + "font_class": "luyou", + "unicode": "e677", + "unicode_decimal": 58999 + }, + { + "icon_id": "1004630", + "name": "点赞2", + "font_class": "dianzan1", + "unicode": "e639", + "unicode_decimal": 58937 + }, + { + "icon_id": "1025289", + "name": "复制", + "font_class": "fuzhi", + "unicode": "e68f", + "unicode_decimal": 59023 + }, + { + "icon_id": "1071052", + "name": "点赞1", + "font_class": "dianzan11", + "unicode": "e66d", + "unicode_decimal": 58989 + }, + { + "icon_id": "1113422", + "name": "三角形-up", + "font_class": "sanjiaoxing-up", + "unicode": "e78f", + "unicode_decimal": 59279 + }, + { + "icon_id": "1113424", + "name": "三角形-down", + "font_class": "sanjiaoxing-down", + "unicode": "e791", + "unicode_decimal": 59281 + }, + { + "icon_id": "1118044", + "name": "认证信息", + "font_class": "renzhengxinxi", + "unicode": "e693", + "unicode_decimal": 59027 + }, + { + "icon_id": "1221889", + "name": "礼物", + "font_class": "gift", + "unicode": "e63c", + "unicode_decimal": 58940 + }, + { + "icon_id": "1240507", + "name": "消息", + "font_class": "xiaoxi", + "unicode": "e614", + "unicode_decimal": 58900 + }, + { + "icon_id": "1241465", + "name": "邮件", + "font_class": "mail", + "unicode": "e66a", + "unicode_decimal": 58986 + }, + { + "icon_id": "1261493", + "name": "compress", + "font_class": "compress", + "unicode": "e65e", + "unicode_decimal": 58974 + }, + { + "icon_id": "1283976", + "name": "代码配置r", + "font_class": "daimapeizhir", + "unicode": "e727", + "unicode_decimal": 59175 + }, + { + "icon_id": "1301376", + "name": "提示", + "font_class": "tishi1", + "unicode": "e690", + "unicode_decimal": 59024 + }, + { + "icon_id": "1328121", + "name": "net", + "font_class": "net", + "unicode": "e607", + "unicode_decimal": 58887 + }, + { + "icon_id": "1471592", + "name": "edit", + "font_class": "edit", + "unicode": "e691", + "unicode_decimal": 59025 + }, + { + "icon_id": "1472537", + "name": "显示", + "font_class": "xianshi", + "unicode": "e695", + "unicode_decimal": 59029 + }, + { + "icon_id": "1606757", + "name": "锁", + "font_class": "suo", + "unicode": "e6c9", + "unicode_decimal": 59081 + }, + { + "icon_id": "1679994", + "name": "减", + "font_class": "default", + "unicode": "e67f", + "unicode_decimal": 59007 + }, + { + "icon_id": "1703520", + "name": "消息", + "font_class": "xiaoxi1", + "unicode": "e6a4", + "unicode_decimal": 59044 + }, + { + "icon_id": "1770008", + "name": "下载", + "font_class": "xiazai1", + "unicode": "e6ac", + "unicode_decimal": 59052 + }, + { + "icon_id": "1770896", + "name": "撤销", + "font_class": "chexiao", + "unicode": "e657", + "unicode_decimal": 58967 + }, + { + "icon_id": "1792712", + "name": "qq", + "font_class": "qq", + "unicode": "e687", + "unicode_decimal": 59015 + }, + { + "icon_id": "1820861", + "name": "标签", + "font_class": "biaoqian", + "unicode": "e74f", + "unicode_decimal": 59215 + }, + { + "icon_id": "1881547", + "name": "大数据存储", + "font_class": "dashujucunchu", + "unicode": "e678", + "unicode_decimal": 59000 + }, + { + "icon_id": "1941293", + "name": "喇叭", + "font_class": "laba", + "unicode": "e608", + "unicode_decimal": 58888 + }, + { + "icon_id": "1966078", + "name": "mstest", + "font_class": "mstest", + "unicode": "e686", + "unicode_decimal": 59014 + }, + { + "icon_id": "2065962", + "name": "添加成员", + "font_class": "tianjiachengyuan", + "unicode": "e69a", + "unicode_decimal": 59034 + }, + { + "icon_id": "2323314", + "name": "隐藏", + "font_class": "yincang1", + "unicode": "e9b5", + "unicode_decimal": 59829 + }, + { + "icon_id": "2365578", + "name": "三角形", + "font_class": "triangle", + "unicode": "e600", + "unicode_decimal": 58880 + }, + { + "icon_id": "2367774", + "name": "旗帜", + "font_class": "qizhi", + "unicode": "e699", + "unicode_decimal": 59033 + }, + { + "icon_id": "2405900", + "name": "CSDN", + "font_class": "csdn", + "unicode": "e602", + "unicode_decimal": 58882 + }, + { + "icon_id": "2423301", + "name": "用户、角色_无数据", + "font_class": "yonghujiaose_wushuju", + "unicode": "e69e", + "unicode_decimal": 59038 + }, + { + "icon_id": "2471377", + "name": "模板", + "font_class": "moban", + "unicode": "e692", + "unicode_decimal": 59026 + }, + { + "icon_id": "2584358", + "name": "VPN", + "font_class": "VPN", + "unicode": "e601", + "unicode_decimal": 58881 + }, + { + "icon_id": "2629328", + "name": "加 ", + "font_class": "jia", + "unicode": "e605", + "unicode_decimal": 58885 + }, + { + "icon_id": "2668434", + "name": "银行卡", + "font_class": "yinhangqia1", + "unicode": "e697", + "unicode_decimal": 59031 + }, + { + "icon_id": "2709989", + "name": "微博", + "font_class": "weibo", + "unicode": "e688", + "unicode_decimal": 59016 + }, + { + "icon_id": "2800857", + "name": "无", + "font_class": "kong", + "unicode": "e69f", + "unicode_decimal": 59039 + }, + { + "icon_id": "2959158", + "name": "课程", + "font_class": "kecheng", + "unicode": "e60a", + "unicode_decimal": 58890 + }, + { + "icon_id": "3182660", + "name": "设置", + "font_class": "shezhi1", + "unicode": "e71d", + "unicode_decimal": 59165 + }, + { + "icon_id": "3216521", + "name": "成功", + "font_class": "chenggong", + "unicode": "e68b", + "unicode_decimal": 59019 + }, + { + "icon_id": "3250316", + "name": "三点", + "font_class": "sandian", + "unicode": "e6f8", + "unicode_decimal": 59128 + }, + { + "icon_id": "3295983", + "name": "gs-beixiao-icon-基本信息", + "font_class": "jibenxinxi", + "unicode": "e694", + "unicode_decimal": 59028 + }, + { + "icon_id": "3330922", + "name": "base", + "font_class": "base", + "unicode": "e683", + "unicode_decimal": 59011 + }, + { + "icon_id": "3471155", + "name": "微信", + "font_class": "weixin2", + "unicode": "e603", + "unicode_decimal": 58883 + }, + { + "icon_id": "3580617", + "name": "Stack Overflow", + "font_class": "StackOverflow", + "unicode": "e689", + "unicode_decimal": 59017 + }, + { + "icon_id": "3592482", + "name": "重置", + "font_class": "zhongzhi2", + "unicode": "e68a", + "unicode_decimal": 59018 + }, + { + "icon_id": "3634968", + "name": "活动", + "font_class": "huodong", + "unicode": "e6bb", + "unicode_decimal": 59067 + }, + { + "icon_id": "3738942", + "name": "隐藏", + "font_class": "yincang", + "unicode": "e6a0", + "unicode_decimal": 59040 + }, + { + "icon_id": "3764006", + "name": "电话", + "font_class": "weibiaoti-", + "unicode": "e60d", + "unicode_decimal": 58893 + }, + { + "icon_id": "3866700", + "name": "位置", + "font_class": "weizhi", + "unicode": "e676", + "unicode_decimal": 58998 + }, + { + "icon_id": "3876414", + "name": "jquery", + "font_class": "jquery", + "unicode": "e67a", + "unicode_decimal": 59002 + }, + { + "icon_id": "3876424", + "name": "docker", + "font_class": "docker", + "unicode": "e67b", + "unicode_decimal": 59003 + }, + { + "icon_id": "3876458", + "name": "python", + "font_class": "python", + "unicode": "e67c", + "unicode_decimal": 59004 + }, + { + "icon_id": "3876486", + "name": "php", + "font_class": "php", + "unicode": "e67d", + "unicode_decimal": 59005 + }, + { + "icon_id": "4019861", + "name": "银行卡", + "font_class": "yinhangqia", + "unicode": "e675", + "unicode_decimal": 58997 + }, + { + "icon_id": "4058832", + "name": "linkedin", + "font_class": "linkedin", + "unicode": "e60c", + "unicode_decimal": 58892 + }, + { + "icon_id": "4187234", + "name": "文件夹", + "font_class": "wenjianjia", + "unicode": "e640", + "unicode_decimal": 58944 + }, + { + "icon_id": "4239271", + "name": "SQL server", + "font_class": "SQLserver", + "unicode": "e705", + "unicode_decimal": 59141 + }, + { + "icon_id": "4320360", + "name": "质量分析", + "font_class": "zhiliangfenxi", + "unicode": "e68d", + "unicode_decimal": 59021 + }, + { + "icon_id": "4333787", + "name": "上传图片", + "font_class": "shangchuantupian1", + "unicode": "e7fd", + "unicode_decimal": 59389 + }, + { + "icon_id": "4479935", + "name": "登录Ip监控", + "font_class": "dengluIpjiankong", + "unicode": "e684", + "unicode_decimal": 59012 + }, + { + "icon_id": "4489857", + "name": "itsm3-流程管理", + "font_class": "itsm-liuchengguanli", + "unicode": "e685", + "unicode_decimal": 59013 + }, + { + "icon_id": "4553727", + "name": "链接", + "font_class": "lianjie", + "unicode": "e7db", + "unicode_decimal": 59355 + }, + { + "icon_id": "4734937", + "name": "分支", + "font_class": "fenzhi", + "unicode": "e610", + "unicode_decimal": 58896 + }, + { + "icon_id": "4734938", + "name": "网址克隆", + "font_class": "wangzhikelong", + "unicode": "e612", + "unicode_decimal": 58898 + }, + { + "icon_id": "4734939", + "name": "下载", + "font_class": "xiazai", + "unicode": "e613", + "unicode_decimal": 58899 + }, + { + "icon_id": "4734940", + "name": "代码", + "font_class": "daima", + "unicode": "e615", + "unicode_decimal": 58901 + }, + { + "icon_id": "4734941", + "name": "提交记录", + "font_class": "tijiaojilu", + "unicode": "e616", + "unicode_decimal": 58902 + }, + { + "icon_id": "4734942", + "name": "选择题", + "font_class": "xuanzeti", + "unicode": "e617", + "unicode_decimal": 58903 + }, + { + "icon_id": "4734943", + "name": "编辑", + "font_class": "bianji", + "unicode": "e618", + "unicode_decimal": 58904 + }, + { + "icon_id": "4734944", + "name": "向上", + "font_class": "xiangshang", + "unicode": "e61a", + "unicode_decimal": 58906 + }, + { + "icon_id": "4734945", + "name": "删除掉", + "font_class": "shanchudiao", + "unicode": "e620", + "unicode_decimal": 58912 + }, + { + "icon_id": "4734946", + "name": "新建", + "font_class": "xinjian", + "unicode": "e619", + "unicode_decimal": 58905 + }, + { + "icon_id": "4734947", + "name": "上升排序", + "font_class": "shangshengpaixu", + "unicode": "e621", + "unicode_decimal": 58913 + }, + { + "icon_id": "4734948", + "name": "奖励", + "font_class": "jiangli", + "unicode": "e61b", + "unicode_decimal": 58907 + }, + { + "icon_id": "4734949", + "name": "删除", + "font_class": "shanchu", + "unicode": "e61c", + "unicode_decimal": 58908 + }, + { + "icon_id": "4734950", + "name": "隐藏闭眼", + "font_class": "yincangbiyan", + "unicode": "e61d", + "unicode_decimal": 58909 + }, + { + "icon_id": "4734951", + "name": "开锁", + "font_class": "kaisuo", + "unicode": "e61e", + "unicode_decimal": 58910 + }, + { + "icon_id": "4734952", + "name": "关锁", + "font_class": "guansuo", + "unicode": "e61f", + "unicode_decimal": 58911 + }, + { + "icon_id": "4734953", + "name": "版本库", + "font_class": "banbenku", + "unicode": "e622", + "unicode_decimal": 58914 + }, + { + "icon_id": "4734954", + "name": "issue", + "font_class": "issue", + "unicode": "e623", + "unicode_decimal": 58915 + }, + { + "icon_id": "4734955", + "name": "上传图片", + "font_class": "shangchuantupian", + "unicode": "e625", + "unicode_decimal": 58917 + }, + { + "icon_id": "4734956", + "name": "测评", + "font_class": "ceping", + "unicode": "e626", + "unicode_decimal": 58918 + }, + { + "icon_id": "4734957", + "name": "tpi消息提醒", + "font_class": "tpixiaoxitixing", + "unicode": "e624", + "unicode_decimal": 58916 + }, + { + "icon_id": "4734958", + "name": "qq在线咨询", + "font_class": "qqzaixianzixun", + "unicode": "e628", + "unicode_decimal": 58920 + }, + { + "icon_id": "4734959", + "name": "二维码", + "font_class": "erweima", + "unicode": "e629", + "unicode_decimal": 58921 + }, + { + "icon_id": "4734960", + "name": "意见反馈", + "font_class": "yijianfankui", + "unicode": "e62a", + "unicode_decimal": 58922 + }, + { + "icon_id": "4734961", + "name": "邮箱认证", + "font_class": "youxiangrenzheng", + "unicode": "e62b", + "unicode_decimal": 58923 + }, + { + "icon_id": "4734962", + "name": "手机认证", + "font_class": "shoujirenzheng", + "unicode": "e62c", + "unicode_decimal": 58924 + }, + { + "icon_id": "4734963", + "name": "职业认证", + "font_class": "zhiyerenzheng", + "unicode": "e62d", + "unicode_decimal": 58925 + }, + { + "icon_id": "4734964", + "name": "身份认证", + "font_class": "shenfenrenzheng", + "unicode": "e62e", + "unicode_decimal": 58926 + }, + { + "icon_id": "4734965", + "name": "评分", + "font_class": "pingfen", + "unicode": "e62f", + "unicode_decimal": 58927 + }, + { + "icon_id": "4734966", + "name": "评分-线", + "font_class": "pingfen-xian", + "unicode": "e630", + "unicode_decimal": 58928 + }, + { + "icon_id": "4734967", + "name": "作业", + "font_class": "zuoye", + "unicode": "e631", + "unicode_decimal": 58929 + }, + { + "icon_id": "4734968", + "name": "提示错误", + "font_class": "tishicuowu", + "unicode": "e632", + "unicode_decimal": 58930 + }, + { + "icon_id": "4734969", + "name": "资源", + "font_class": "ziyuan", + "unicode": "e633", + "unicode_decimal": 58931 + }, + { + "icon_id": "4734970", + "name": "提示", + "font_class": "tishi", + "unicode": "e636", + "unicode_decimal": 58934 + }, + { + "icon_id": "4734971", + "name": "成员", + "font_class": "chengyuan", + "unicode": "e63a", + "unicode_decimal": 58938 + }, + { + "icon_id": "4734972", + "name": "公告", + "font_class": "gonggao", + "unicode": "e63b", + "unicode_decimal": 58939 + }, + { + "icon_id": "4734973", + "name": "点赞", + "font_class": "dianzan", + "unicode": "e634", + "unicode_decimal": 58932 + }, + { + "icon_id": "4734974", + "name": "点赞-线", + "font_class": "dianzan-xian", + "unicode": "e635", + "unicode_decimal": 58933 + }, + { + "icon_id": "4734976", + "name": "返回上次代码", + "font_class": "fanhuishangcidaima", + "unicode": "e637", + "unicode_decimal": 58935 + }, + { + "icon_id": "4734977", + "name": "重置", + "font_class": "zhongzhi", + "unicode": "e638", + "unicode_decimal": 58936 + }, + { + "icon_id": "4734978", + "name": "旋转", + "font_class": "xuanzhuan", + "unicode": "e63d", + "unicode_decimal": 58941 + }, + { + "icon_id": "4734979", + "name": "实训", + "font_class": "shixun", + "unicode": "e63e", + "unicode_decimal": 58942 + }, + { + "icon_id": "4734981", + "name": "缩小", + "font_class": "suoxiao", + "unicode": "e63f", + "unicode_decimal": 58943 + }, + { + "icon_id": "4734982", + "name": "下箭头", + "font_class": "xiajiantou", + "unicode": "e642", + "unicode_decimal": 58946 + }, + { + "icon_id": "4734983", + "name": "勾选", + "font_class": "gouxuan", + "unicode": "e644", + "unicode_decimal": 58948 + }, + { + "icon_id": "4734984", + "name": "浏览眼", + "font_class": "liulanyan", + "unicode": "e646", + "unicode_decimal": 58950 + }, + { + "icon_id": "4734985", + "name": "经验", + "font_class": "jingyan", + "unicode": "e647", + "unicode_decimal": 58951 + }, + { + "icon_id": "4734986", + "name": "消息铃铛", + "font_class": "xiaoxilingdang", + "unicode": "e641", + "unicode_decimal": 58945 + }, + { + "icon_id": "4734987", + "name": "实训关卡", + "font_class": "shixunguanqia", + "unicode": "e648", + "unicode_decimal": 58952 + }, + { + "icon_id": "4734988", + "name": "搜索", + "font_class": "sousuo", + "unicode": "e643", + "unicode_decimal": 58947 + }, + { + "icon_id": "4734989", + "name": "发布", + "font_class": "fabu", + "unicode": "e64a", + "unicode_decimal": 58954 + }, + { + "icon_id": "4734990", + "name": "添加 放大", + "font_class": "tianjiafangda", + "unicode": "e645", + "unicode_decimal": 58949 + }, + { + "icon_id": "4734991", + "name": "向下移动", + "font_class": "xiangxiayidong", + "unicode": "e64b", + "unicode_decimal": 58955 + }, + { + "icon_id": "4734992", + "name": "向上移动", + "font_class": "xiangshangyidong", + "unicode": "e64c", + "unicode_decimal": 58956 + }, + { + "icon_id": "4734994", + "name": "关闭", + "font_class": "guanbi", + "unicode": "e64d", + "unicode_decimal": 58957 + }, + { + "icon_id": "4736792", + "name": "毕业 [转换]", + "font_class": "biyezhuanhuan", + "unicode": "e6f1", + "unicode_decimal": 59121 + }, + { + "icon_id": "4738423", + "name": "睁眼", + "font_class": "zhengyan", + "unicode": "e649", + "unicode_decimal": 58953 + }, + { + "icon_id": "4769329", + "name": "回复", + "font_class": "huifu1", + "unicode": "e64e", + "unicode_decimal": 58958 + }, + { + "icon_id": "4779517", + "name": "文件", + "font_class": "wenjian", + "unicode": "e64f", + "unicode_decimal": 58959 + }, + { + "icon_id": "4780701", + "name": "展开", + "font_class": "zhankai", + "unicode": "e650", + "unicode_decimal": 58960 + }, + { + "icon_id": "4780702", + "name": "收缩", + "font_class": "shousuo", + "unicode": "e651", + "unicode_decimal": 58961 + }, + { + "icon_id": "4807118", + "name": "左键头", + "font_class": "zuojiantou", + "unicode": "e652", + "unicode_decimal": 58962 + }, + { + "icon_id": "4807119", + "name": "右键头", + "font_class": "youjiantou", + "unicode": "e653", + "unicode_decimal": 58963 + }, + { + "icon_id": "4807120", + "name": "上键头", + "font_class": "shangjiantou", + "unicode": "e654", + "unicode_decimal": 58964 + }, + { + "icon_id": "4835183", + "name": "编辑带背景", + "font_class": "bianjidaibeijing", + "unicode": "e655", + "unicode_decimal": 58965 + }, + { + "icon_id": "4835184", + "name": "播放", + "font_class": "bofang", + "unicode": "e656", + "unicode_decimal": 58966 + }, + { + "icon_id": "4835185", + "name": "完成", + "font_class": "wancheng", + "unicode": "e658", + "unicode_decimal": 58968 + }, + { + "icon_id": "5030348", + "name": "分组作业", + "font_class": "fenzuzuoye", + "unicode": "e65d", + "unicode_decimal": 58973 + }, + { + "icon_id": "5030654", + "name": "普通作业", + "font_class": "putongzuoye", + "unicode": "e65c", + "unicode_decimal": 58972 + }, + { + "icon_id": "5037778", + "name": "设置", + "font_class": "shezhi2", + "unicode": "e698", + "unicode_decimal": 59032 + }, + { + "icon_id": "5044767", + "name": "问卷", + "font_class": "wenjuan", + "unicode": "e659", + "unicode_decimal": 58969 + }, + { + "icon_id": "5044768", + "name": "讨论", + "font_class": "taolun", + "unicode": "e65a", + "unicode_decimal": 58970 + }, + { + "icon_id": "5044770", + "name": "分班", + "font_class": "fenban", + "unicode": "e65f", + "unicode_decimal": 58975 + }, + { + "icon_id": "5044775", + "name": "动态", + "font_class": "dongtai", + "unicode": "e660", + "unicode_decimal": 58976 + }, + { + "icon_id": "5045030", + "name": "试卷", + "font_class": "shijuan", + "unicode": "e65b", + "unicode_decimal": 58971 + }, + { + "icon_id": "5051277", + "name": "菜单", + "font_class": "caidan", + "unicode": "e661", + "unicode_decimal": 58977 + }, + { + "icon_id": "5052642", + "name": "成员管理", + "font_class": "chengyuanguanli", + "unicode": "e662", + "unicode_decimal": 58978 + }, + { + "icon_id": "5057140", + "name": "添加", + "font_class": "tianjia", + "unicode": "e672", + "unicode_decimal": 58994 + }, + { + "icon_id": "5068519", + "name": "向下移", + "font_class": "xiangxiayi", + "unicode": "e663", + "unicode_decimal": 58979 + }, + { + "icon_id": "5068520", + "name": "向上移", + "font_class": "xiangshangyi", + "unicode": "e664", + "unicode_decimal": 58980 + }, + { + "icon_id": "5111347", + "name": "更多", + "font_class": "gengduo", + "unicode": "e665", + "unicode_decimal": 58981 + }, + { + "icon_id": "5127482", + "name": "reset", + "font_class": "reset", + "unicode": "e7fe", + "unicode_decimal": 59390 + }, + { + "icon_id": "5148158", + "name": "学院管理员", + "font_class": "xueyuanguanliyuan", + "unicode": "e666", + "unicode_decimal": 58982 + }, + { + "icon_id": "5196483", + "name": "空星", + "font_class": "kongxing", + "unicode": "e667", + "unicode_decimal": 58983 + }, + { + "icon_id": "5196487", + "name": "实星", + "font_class": "shixing", + "unicode": "e668", + "unicode_decimal": 58984 + }, + { + "icon_id": "5242367", + "name": "下降", + "font_class": "xiajiang", + "unicode": "e669", + "unicode_decimal": 58985 + }, + { + "icon_id": "5291605", + "name": "更多", + "font_class": "gengduo1", + "unicode": "e7f9", + "unicode_decimal": 59385 + }, + { + "icon_id": "5379378", + "name": "20从属连接", + "font_class": "congshulianjie", + "unicode": "e6ee", + "unicode_decimal": 59118 + }, + { + "icon_id": "5383113", + "name": "手机", + "font_class": "shouji", + "unicode": "e69d", + "unicode_decimal": 59037 + }, + { + "icon_id": "5430942", + "name": "智能监控体系", + "font_class": "zhinengjiankongtixi", + "unicode": "e6b4", + "unicode_decimal": 59060 + }, + { + "icon_id": "5480167", + "name": "新增提示", + "font_class": "xinzengtishi", + "unicode": "e66c", + "unicode_decimal": 58988 + }, + { + "icon_id": "5480235", + "name": "完成勾选", + "font_class": "wanchenggouxuan", + "unicode": "e66e", + "unicode_decimal": 58990 + }, + { + "icon_id": "5634728", + "name": "java", + "font_class": "java", + "unicode": "f1d7", + "unicode_decimal": 61911 + }, + { + "icon_id": "5669524", + "name": "下降", + "font_class": "youjiang", + "unicode": "e792", + "unicode_decimal": 59282 + }, + { + "icon_id": "5872571", + "name": "fork", + "font_class": "fork", + "unicode": "e6b3", + "unicode_decimal": 59059 + }, + { + "icon_id": "5921996", + "name": "名片", + "font_class": "mingpian", + "unicode": "e69b", + "unicode_decimal": 59035 + }, + { + "icon_id": "5961392", + "name": "mysql", + "font_class": "mysql", + "unicode": "ec6d", + "unicode_decimal": 60525 + }, + { + "icon_id": "6018156", + "name": "客户留言", + "font_class": "kehuliuyan", + "unicode": "e71a", + "unicode_decimal": 59162 + }, + { + "icon_id": "6252982", + "name": "github", + "font_class": "github", + "unicode": "e763", + "unicode_decimal": 59235 + }, + { + "icon_id": "6359951", + "name": "mongodb", + "font_class": "mongodb1", + "unicode": "e60b", + "unicode_decimal": 58891 + }, + { + "icon_id": "6576426", + "name": "安全设置", + "font_class": "anquanshezhi", + "unicode": "e606", + "unicode_decimal": 58886 + }, + { + "icon_id": "6879322", + "name": "trustie", + "font_class": "trustie", + "unicode": "e681", + "unicode_decimal": 59009 + }, + { + "icon_id": "6897671", + "name": "无权限", + "font_class": "wuquanxian", + "unicode": "e6a2", + "unicode_decimal": 59042 + }, + { + "icon_id": "7395703", + "name": "实训带背景", + "font_class": "shixundaibeijing", + "unicode": "e68c", + "unicode_decimal": 59020 + }, + { + "icon_id": "7587940", + "name": "工程", + "font_class": "gongcheng", + "unicode": "e60f", + "unicode_decimal": 58895 + }, + { + "icon_id": "7637642", + "name": "警告", + "font_class": "jinggao", + "unicode": "e696", + "unicode_decimal": 59030 + }, + { + "icon_id": "7913929", + "name": "添加导航", + "font_class": "tianjiadaohang", + "unicode": "e604", + "unicode_decimal": 58884 + }, + { + "icon_id": "9219273", + "name": "yunweijiankong", + "font_class": "yunweijiankong", + "unicode": "e6a3", + "unicode_decimal": 59043 + }, + { + "icon_id": "9238635", + "name": "gongyiliucheng", + "font_class": "gongyiliucheng", + "unicode": "e6a5", + "unicode_decimal": 59045 + }, + { + "icon_id": "9247438", + "name": "zhiliangkongzhi", + "font_class": "zhiliangkongzhi", + "unicode": "e6a6", + "unicode_decimal": 59046 + }, + { + "icon_id": "9247680", + "name": "shebeiguanli", + "font_class": "shebeiguanli", + "unicode": "e6a7", + "unicode_decimal": 59047 + }, + { + "icon_id": "9255551", + "name": "shengmingzhouqi", + "font_class": "shengmingzhouqi", + "unicode": "e6a8", + "unicode_decimal": 59048 + }, + { + "icon_id": "9751215", + "name": "编辑", + "font_class": "bianji1", + "unicode": "e6a1", + "unicode_decimal": 59041 + }, + { + "icon_id": "9977539", + "name": "排序", + "font_class": "paixu1", + "unicode": "e6aa", + "unicode_decimal": 59050 + }, + { + "icon_id": "10371563", + "name": "职业认证", + "font_class": "renzhengshangjia", + "unicode": "e6ab", + "unicode_decimal": 59051 + }, + { + "icon_id": "10371564", + "name": "实名认证", + "font_class": "shenfenzhenghaomaguizheng", + "unicode": "e6ad", + "unicode_decimal": 59053 + }, + { + "icon_id": "10383001", + "name": "Page-1 (2)", + "font_class": "Page-1", + "unicode": "e6ae", + "unicode_decimal": 59054 + }, + { + "icon_id": "10383218", + "name": "Page-3", + "font_class": "Page-3", + "unicode": "e6af", + "unicode_decimal": 59055 + }, + { + "icon_id": "10383219", + "name": "Page2", + "font_class": "Page", + "unicode": "e6b0", + "unicode_decimal": 59056 + }, + { + "icon_id": "10440637", + "name": "身份认证", + "font_class": "yemian", + "unicode": "e6b1", + "unicode_decimal": 59057 + }, + { + "icon_id": "10440717", + "name": "实名认证", + "font_class": "bianzu", + "unicode": "e6b5", + "unicode_decimal": 59061 + }, + { + "icon_id": "10484724", + "name": "学习中心", + "font_class": "xuexizhongxin", + "unicode": "e6b6", + "unicode_decimal": 59062 + }, + { + "icon_id": "10610051", + "name": "menu_3voucher", + "font_class": "menu_voucher", + "unicode": "e6b8", + "unicode_decimal": 59064 + }, + { + "icon_id": "10610055", + "name": "menu_3events", + "font_class": "menu_events", + "unicode": "e6b9", + "unicode_decimal": 59065 + }, + { + "icon_id": "10610061", + "name": "menu_4map", + "font_class": "menu_map", + "unicode": "e6ba", + "unicode_decimal": 59066 + }, + { + "icon_id": "10610561", + "name": "menu_5date1", + "font_class": "menu_date", + "unicode": "e6a9", + "unicode_decimal": 59049 + }, + { + "icon_id": "10610574", + "name": "menu_people1", + "font_class": "menu_people", + "unicode": "e6b7", + "unicode_decimal": 59063 + }, + { + "icon_id": "11222372", + "name": "healthmode", + "font_class": "healthmode", + "unicode": "e60e", + "unicode_decimal": 58894 + }, + { + "icon_id": "11230822", + "name": "nenghaofenxi@1x", + "font_class": "nenghaofenxix", + "unicode": "e6be", + "unicode_decimal": 59070 + } + ] +} diff --git a/public/stylesheets/educoder/iconfont/iconfont.svg b/public/stylesheets/educoder/iconfont/iconfont.svg index a8e71e424..e9aa3e2f9 100644 --- a/public/stylesheets/educoder/iconfont/iconfont.svg +++ b/public/stylesheets/educoder/iconfont/iconfont.svg @@ -1,536 +1,680 @@ - - - - - -Created by iconfont - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + +Created by iconfont + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/stylesheets/educoder/iconfont/iconfont.ttf b/public/stylesheets/educoder/iconfont/iconfont.ttf index 092b6b955..bf316a765 100644 Binary files a/public/stylesheets/educoder/iconfont/iconfont.ttf and b/public/stylesheets/educoder/iconfont/iconfont.ttf differ diff --git a/public/stylesheets/educoder/iconfont/iconfont.woff b/public/stylesheets/educoder/iconfont/iconfont.woff index 0d49fe209..b06206850 100644 Binary files a/public/stylesheets/educoder/iconfont/iconfont.woff and b/public/stylesheets/educoder/iconfont/iconfont.woff differ diff --git a/public/stylesheets/educoder/iconfont/iconfont.woff2 b/public/stylesheets/educoder/iconfont/iconfont.woff2 index 2bf8563c8..374af4f2a 100644 Binary files a/public/stylesheets/educoder/iconfont/iconfont.woff2 and b/public/stylesheets/educoder/iconfont/iconfont.woff2 differ diff --git a/spec/models/chart_rule_spec.rb b/spec/models/chart_rule_spec.rb new file mode 100644 index 000000000..b3059c94d --- /dev/null +++ b/spec/models/chart_rule_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe ChartRule, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/competition_award_spec.rb b/spec/models/competition_award_spec.rb new file mode 100644 index 000000000..dac3bddcf --- /dev/null +++ b/spec/models/competition_award_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe CompetitionAward, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/competition_score_spec.rb b/spec/models/competition_score_spec.rb new file mode 100644 index 000000000..d0f1372b1 --- /dev/null +++ b/spec/models/competition_score_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe CompetitionScore, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/module_setting_spec.rb b/spec/models/module_setting_spec.rb new file mode 100644 index 000000000..ede893338 --- /dev/null +++ b/spec/models/module_setting_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe ModuleSetting, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/shixun_review_spec.rb b/spec/models/shixun_review_spec.rb new file mode 100644 index 000000000..0bb62bbee --- /dev/null +++ b/spec/models/shixun_review_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe ShixunReview, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end