diff --git a/Gemfile b/Gemfile index 7cda93671..28df2328c 100644 --- a/Gemfile +++ b/Gemfile @@ -95,3 +95,4 @@ gem 'bulk_insert' gem 'searchkick' gem 'aasm' +gem 'enumerize' diff --git a/Gemfile.lock b/Gemfile.lock index a33cd2ef6..3e5945c70 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -106,6 +106,8 @@ GEM elasticsearch-transport (7.2.0) faraday multi_json + enumerize (2.3.1) + activesupport (>= 3.2) erubi (1.7.1) execjs (2.7.0) faraday (0.15.4) @@ -366,6 +368,7 @@ DEPENDENCIES byebug capybara (>= 2.15, < 4.0) chromedriver-helper + enumerize faraday (~> 0.15.4) font-awesome-sass (= 4.7.0) gitlab! diff --git a/app/assets/javascripts/admin.js b/app/assets/javascripts/admin.js index e7a4d28ff..2dcd7cd17 100644 --- a/app/assets/javascripts/admin.js +++ b/app/assets/javascripts/admin.js @@ -13,10 +13,14 @@ //= require bootstrap-datepicker //= require bootstrap.viewer //= require jquery.mloading +//= require common //= require echarts -//= require lib/codemirror -//= require mode/shell/shell +//= require codemirror/lib/codemirror +//= require codemirror/mode/shell/shell +//= require editormd/editormd +//= require editormd/languages/zh-tw +//= require dragula/dragula //= require_tree ./i18n //= require_tree ./admins @@ -41,7 +45,7 @@ $.notifyDefaults({ }); $(document).on('turbolinks:load', function(){ - $('[data-toggle="tooltip"]').tooltip(); + $('[data-toggle="tooltip"]').tooltip({ trigger : 'hover' }); $('[data-toggle="popover"]').popover(); // 图片查看大图 diff --git a/app/assets/javascripts/admins/about.js b/app/assets/javascripts/admins/about.js new file mode 100644 index 000000000..d706e653c --- /dev/null +++ b/app/assets/javascripts/admins/about.js @@ -0,0 +1,5 @@ +$(document).on('turbolinks:load', function() { + if ($('body.admins-abouts-edit-page, body.admins-abouts-update-page').length > 0) { + createMDEditor('about-us-editor', {}); + } +}) \ No newline at end of file diff --git a/app/assets/javascripts/admins/agreement.js b/app/assets/javascripts/admins/agreement.js new file mode 100644 index 000000000..387d4b337 --- /dev/null +++ b/app/assets/javascripts/admins/agreement.js @@ -0,0 +1,5 @@ +$(document).on('turbolinks:load', function() { + if ($('body.admins-agreements-edit-page, body.admins-agreements-update-page').length > 0) { + createMDEditor('agreement-editor', {}); + } +}) \ No newline at end of file diff --git a/app/assets/javascripts/admins/cooperatives.js b/app/assets/javascripts/admins/cooperatives.js new file mode 100644 index 000000000..f650e00e3 --- /dev/null +++ b/app/assets/javascripts/admins/cooperatives.js @@ -0,0 +1,96 @@ +$(document).on('turbolinks:load', function() { + if ($('body.admins-cooperatives-index-page').length > 0) { + // ------------ 保存链接 ----------- + $('.coo-img-card').on('click', '.save-url-btn', function(){ + var $link = $(this); + var cooId = $link.data('id'); + var url = $('.coo-img-item-' + cooId).find('.url-input').val(); + $link.attr('disabled', true); + + $.ajax({ + url: '/admins/cooperatives/' + cooId, + method: 'PATCH', + dataType: 'json', + data: { url: url }, + success: function(data){ + $.notify({ message: '保存成功' }); + }, + error: ajaxErrorNotifyHandler, + complete: function(){ + $link.removeAttr('disabled'); + } + }) + }); + + // ------------ 拖拽 ------------- + var onDropFunc = function(el, _target, _source, sibling){ + var moveId = $(el).data('id'); + var insertId = $(sibling).data('id') || ''; + + $.ajax({ + url: '/admins/cooperatives/drag', + method: 'POST', + dataType: 'json', + data: { move_id: moveId, after_id: insertId }, + success: function(data){ + }, + error: function(res){ + var data = res.responseJSON; + $.notify({message: '移动失败,原因:' + data.message}, {type: 'danger'}); + } + }) + }; + var ele1 = document.getElementById('coo-img-container-alliance_coop'); + dragula([ele1], { mirrorContainer: ele1 }).on('drop', onDropFunc); + var ele2 = document.getElementById('coo-img-container-com_coop'); + dragula([ele2], { mirrorContainer: ele2 }).on('drop', onDropFunc); + var ele3 = document.getElementById('coo-img-container-edu_coop'); + dragula([ele3], { mirrorContainer: ele3 }).on('drop', onDropFunc); + + + // ----------- 新增 -------------- + var $createModal = $('.modal.admin-add-cooperative-modal'); + var $createForm = $createModal.find('form.admin-add-cooperative-form'); + + $createForm.validate({ + errorElement: 'span', + errorClass: 'danger text-danger', + rules: { + "coo_img[image]": { + required: true + } + } + }); + + $createModal.on('show.bs.modal', function(event){ + resetFileInputFunc($createModal.find('.img-file-input')); + $createModal.find('.file-names').html('选择文件'); + + var $link = $(event.relatedTarget); + var imgType = $link.data('imgType'); + $createForm.find('input[name="coo_img[img_type]"]').val(imgType); + }); + + $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 $cooImgItem = $('.coo-img-item-' + data.source_id); + $.post('/admins/cooperatives/'+ data.source_id + '/replace_image_url'); + $cooImgItem.find('.coo-img-item-img img').attr('src', data.url); + }) + } +}) \ No newline at end of file diff --git a/app/assets/javascripts/admins/help-center.js b/app/assets/javascripts/admins/help-center.js new file mode 100644 index 000000000..5fd6055df --- /dev/null +++ b/app/assets/javascripts/admins/help-center.js @@ -0,0 +1,5 @@ +$(document).on('turbolinks:load', function() { + if ($('body.admins-help-centers-edit-page, body.admins-help-centers-update-page').length > 0) { + createMDEditor('help-center-editor', {}); + } +}) \ No newline at end of file diff --git a/app/assets/javascripts/admins/users/index.js b/app/assets/javascripts/admins/users/index.js index 1ac936df5..c7b8e5e6a 100644 --- a/app/assets/javascripts/admins/users/index.js +++ b/app/assets/javascripts/admins/users/index.js @@ -122,10 +122,6 @@ $(document).on('turbolinks:load', function(){ // 导入学生 var $importUserModal = $('.modal.admin-import-user-modal'); var $importUserForm = $importUserModal.find('form.admin-import-user-form') - var resetFileInputFunc = function(file){ - file.after(file.clone().val("")); - file.remove(); - } $importUserModal.on('show.bs.modal', function(){ resetFileInputFunc($importUserModal.find('.upload-file-input')); diff --git a/app/assets/javascripts/common.js b/app/assets/javascripts/common.js new file mode 100644 index 000000000..fa7bef244 --- /dev/null +++ b/app/assets/javascripts/common.js @@ -0,0 +1,44 @@ +function createMDEditor(element, opts){ + var defaults = { + path: '/editormd/lib/', + syncScrolling: "single", + tex: true, + tocm: true, + emoji: true, + taskList: true, + codeFold: true, + searchReplace: true, + htmlDecode: "style,script,iframe", + sequenceDiagram: true, + autoFocus: false, + toolbarIcons: function () { + // Or return editormd.toolbarModes[name]; // full, simple, mini + // Using "||" set icons align right. + return ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "|", "image", "table", '|', "watch", "clear"] + }, + //这个配置在simple.html中并没有,但是为了能够提交表单,使用这个配置可以让构造出来的HTML代码直接在第二个隐藏的textarea域中,方便post提交表单。 + saveHTMLToTextarea: true, + dialogMaskOpacity: 0.6, + imageUpload: true, + imageFormats: ["jpg", "jpeg", "gif", "png", "bmp", "webp", "JPG", "JPEG", "GIF", "PNG", "BMP", "WEBP"], + imageUploadURL: '/api/attachments.json' + } + var options = $.extend({}, defaults, opts); + + return editormd(element, options); +} + +function ajaxErrorNotifyHandler(res) { + var message = ''; + if(res.status !== 500){ + message = res.responseJSON.message; + } else { + message = '系统错误'; + } + return $.notify({message: message}, {type: 'danger'}); +} + +function resetFileInputFunc(file){ + file.after(file.clone().val("")); + file.remove(); +} \ No newline at end of file diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss index 30fd635a6..efa12b32e 100644 --- a/app/assets/stylesheets/admin.scss +++ b/app/assets/stylesheets/admin.scss @@ -6,7 +6,10 @@ @import "bootstrap-datepicker"; @import "bootstrap-datepicker.standalone"; @import "jquery.mloading"; -@import "lib/codemirror"; + +@import "codemirror/lib/codemirror"; +@import "editormd/css/editormd.min"; +@import "dragula/dragula"; @import "common"; @import "admins/*"; diff --git a/app/assets/stylesheets/admins/cooperatives.scss b/app/assets/stylesheets/admins/cooperatives.scss new file mode 100644 index 000000000..530ed2316 --- /dev/null +++ b/app/assets/stylesheets/admins/cooperatives.scss @@ -0,0 +1,35 @@ +.admins-cooperatives-index-page { + .coo-img-card { + .coo-img-item { + & > .drag { + cursor: move; + background: #fff; + box-shadow: 1px 2px 5px 3px #f0f0f0; + } + + &-img { + cursor: pointer; + width: 100%; + height: 40px; + margin-bottom: 10px; + + & > img { + width: 100%; + height: 40px; + } + } + + .delete-btn { + position: absolute; + top: 3px; + right: 20px; + color: red; + cursor: pointer; + } + + .save-url-btn { + cursor: pointer; + } + } + } +} \ No newline at end of file diff --git a/app/controllers/admins/abouts_controller.rb b/app/controllers/admins/abouts_controller.rb new file mode 100644 index 000000000..1f5838cbb --- /dev/null +++ b/app/controllers/admins/abouts_controller.rb @@ -0,0 +1,18 @@ +class Admins::AboutsController < Admins::BaseController + def edit + current_doc + end + + def update + current_doc.update!(about_us: params[:about_us]) + + flash[:success] = '保存成功' + redirect_to edit_admins_about_path + end + + private + + def current_doc + @doc ||= Help.first || Help.create + end +end \ No newline at end of file diff --git a/app/controllers/admins/agreements_controller.rb b/app/controllers/admins/agreements_controller.rb new file mode 100644 index 000000000..2a0d56b8a --- /dev/null +++ b/app/controllers/admins/agreements_controller.rb @@ -0,0 +1,18 @@ +class Admins::AgreementsController < Admins::BaseController + def edit + current_doc + end + + def update + current_doc.update!(agreement: params[:agreement]) + + flash[:success] = '保存成功' + redirect_to edit_admins_agreement_path + end + + private + + def current_doc + @doc ||= Help.first || Help.create + end +end \ No newline at end of file diff --git a/app/controllers/admins/contact_us_controller.rb b/app/controllers/admins/contact_us_controller.rb new file mode 100644 index 000000000..5c6a7b6e2 --- /dev/null +++ b/app/controllers/admins/contact_us_controller.rb @@ -0,0 +1,28 @@ +class Admins::ContactUsController < Admins::BaseController + def edit + @cooperations = Cooperation.all.group(:user_type) + @help = Help.first + end + + def update + cooperation = Cooperation.find(params[:id]) + cooperation.update!(update_cooperation_params) + + flash[:success] = '保存成功' + redirect_to edit_admins_contact_us_path + end + + def update_address + help = Help.first || Help.create + help.update!(status: params.dig('help', 'status')) + + flash[:success] = '保存成功' + redirect_to edit_admins_contact_us_path + end + + private + + def update_cooperation_params + params.require(:cooperation).permit(:name, :qq, :mail) + end +end \ No newline at end of file diff --git a/app/controllers/admins/cooperatives_controller.rb b/app/controllers/admins/cooperatives_controller.rb new file mode 100644 index 000000000..4c114003e --- /dev/null +++ b/app/controllers/admins/cooperatives_controller.rb @@ -0,0 +1,84 @@ +class Admins::CooperativesController < Admins::BaseController + before_action :convert_file!, only: [:create] + + def index + @data = { 'alliance_coop' => [], 'com_coop' => [], 'edu_coop' => [] } + @data = @data.merge CooImg.all.group_by(&:img_type) + end + + def create + position = CooImg.where(img_type: create_params[:img_type]).count + 1 + + ActiveRecord::Base.transaction do + coo = CooImg.create!(create_params.merge(position: position)) + + file_path = Util::FileManage.disk_filename('CooImg', coo.id) + File.delete(file_path) if File.exist?(file_path) # 删除之前的文件 + Util.write_file(@file, file_path) + + coo.update!(url_states: Util::FileManage.disk_file_url('CooImg', coo.id)) + end + + flash[:success] = '保存成功' + redirect_to admins_cooperatives_path + end + + def update + current_coo.update!(src_states: params[:url]) + render_ok + end + + def destroy + ActiveRecord::Base.transaction do + current_coo.destroy! + # 前移 + CooImg.where(img_type: current_coo.img_type).where('position > ?', current_coo.position) + .update_all('position = position - 1') + + file_path = Util::FileManage.disk_filename('CooImg', current_coo.id) + File.delete(file_path) if File.exist?(file_path) + end + render_delete_success + end + + def drag + move = CooImg.find_by(id: params[:move_id]) + after = CooImg.find_by(id: params[:after_id]) + + Admins::DragCooperativeService.call(move, after) + render_ok + rescue Admins::DragCooperativeService::Error => e + render_error(e.message) + end + + def replace_image_url + current_coo.update!(url_states: Util::FileManage.disk_file_url('CooImg', current_coo.id)) + render_ok + end + + private + + def current_coo + @_current_coo ||= CooImg.find(params[:id]) + end + + def create_params + params.require(:coo_img).permit(:img_type, :src_states) + end + + def convert_file! + max_size = 10 * 1024 * 1024 # 10M + file = params.dig('coo_img', '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/files_controller.rb b/app/controllers/admins/files_controller.rb index 3c799ceba..b269f8e27 100644 --- a/app/controllers/admins/files_controller.rb +++ b/app/controllers/admins/files_controller.rb @@ -6,7 +6,7 @@ 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) + 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('上传失败') @@ -33,22 +33,14 @@ class Admins::FilesController < Admins::BaseController @_file_path ||= begin case params[:source_type].to_s when 'Shixun' then - disk_filename('Shixun', params[:source_id]) + Util::FileManage.disk_filename('Shixun', params[:source_id]) else - 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) end end end - def disk_filename(type, id) - File.join(storage_path, type.to_s, id.to_s) - end - - def storage_path - @_storage_path ||= File.join(Rails.root, 'public', 'images', 'avatars') - end - def file_url - File.join('/images/avatars/', 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) end end \ No newline at end of file diff --git a/app/controllers/admins/help_centers_controller.rb b/app/controllers/admins/help_centers_controller.rb new file mode 100644 index 000000000..5e8fa5a94 --- /dev/null +++ b/app/controllers/admins/help_centers_controller.rb @@ -0,0 +1,18 @@ +class Admins::HelpCentersController < Admins::BaseController + def edit + current_doc + end + + def update + current_doc.update!(help_center: params[:help_center]) + + flash[:success] = '保存成功' + redirect_to edit_admins_help_center_path + end + + private + + def current_doc + @doc ||= Help.first || Help.create + end +end \ No newline at end of file diff --git a/app/controllers/question_banks_controller.rb b/app/controllers/question_banks_controller.rb index f09a53dbe..e7be8b395 100644 --- a/app/controllers/question_banks_controller.rb +++ b/app/controllers/question_banks_controller.rb @@ -88,7 +88,7 @@ class QuestionBanksController < ApplicationController end def send_to_course - banks = object_banks + banks = @object_type.classify.constantize.where(id: params[:object_id]) course = current_user.manage_courses.find_by!(id: params[:course_id]) banks.each do |bank| case @object_type diff --git a/app/controllers/student_works_controller.rb b/app/controllers/student_works_controller.rb index 99a02106c..7eef670fc 100644 --- a/app/controllers/student_works_controller.rb +++ b/app/controllers/student_works_controller.rb @@ -8,12 +8,15 @@ class StudentWorksController < ApplicationController before_action :find_work, only: [:shixun_work_report, :adjust_review_score, :shixun_work, :commit_des, :update_des, :adjust_score, :show, :adjust_score, :supply_attachments, :revise_attachment, :comment_list, :add_score, :add_score_reply, :destroy_score, :appeal_anonymous_score, - :deal_appeal_score, :cancel_appeal, :edit, :update, :export_shixun_work_report] + :deal_appeal_score, :cancel_appeal, :edit, :update, :export_shixun_work_report, + :shixun_work_comment, :destroy_work_comment] before_action :user_course_identity before_action :allow_add_score, only: [:add_score] before_action :homework_publish - before_action :teacher_allowed, only: [:adjust_score, :adjust_review_score, :deal_appeal_score] + before_action :teacher_allowed, only: [:adjust_score, :adjust_review_score, :deal_appeal_score, :shixun_work_comment, + :destroy_work_comment] + before_action :course_student, only: [:new, :commit_des, :update_des, :create, :edit, :update, :search_member_list, :relate_project, :cancel_relate_project, :relate_project, :delete_work] @@ -455,6 +458,7 @@ class StudentWorksController < ApplicationController @shixun = @homework.shixuns.take # 提示: 这里如果includes outputs表的话: sum(:evaluate_count)会出现错误 @games = @work.myshixun.games.joins(:challenge).reorder("challenges.position asc") if @work.myshixun + @comment = @work.student_works_scores.shixun_comment.first # 用户最大评测次数 if @games @@ -468,6 +472,23 @@ class StudentWorksController < ApplicationController @echart_data = student_efficiency(@homework, @work) end + # 实训作品的评阅 + def shixun_work_comment + tip_exception("评阅不能为空") if params[:comment].blank? + tip_exception("缺少is_hidden参数") if params[:is_hidden].blank? || ![true, false].include?(params[:is_hidden]) + comment = @work.student_works_scores.shixun_comment.first || StudentWorksScore.new(student_work_id: @work.id, user_id: current_user.id) + comment.comment = params[:comment] + comment.is_hidden = params[:is_hidden] + comment.save! + normal_status("评阅成功") + end + + # 删除实训作品评阅 + def destroy_work_comment + @work.student_works_scores.shixun_comment.first.destroy! if @work.student_works_scores.shixun_comment.first.present? + normal_status("删除成功") + end + def export_shixun_work_report @user = @work.user @shixun = @homework.shixuns.take diff --git a/app/helpers/admins/base_helper.rb b/app/helpers/admins/base_helper.rb index c655be2e7..0da80f5f6 100644 --- a/app/helpers/admins/base_helper.rb +++ b/app/helpers/admins/base_helper.rb @@ -18,7 +18,7 @@ module Admins::BaseHelper def sidebar_item(url, text, **opts) content = link_to url, 'data-controller': opts[:controller] do - content_tag(:i, '', class: "fa fa-#{opts[:icon]}", 'data-toggle': 'tooltip', 'data-placement': 'right', 'data-boundary': 'window', title: text) + + content_tag(:i, '', class: "fa fa-#{opts[:icon]} fa-fw", 'data-toggle': 'tooltip', 'data-placement': 'right', 'data-boundary': 'window', title: text) + content_tag(:span, text) end @@ -87,13 +87,17 @@ module Admins::BaseHelper raw link_to(name, url, { method: :post, remote: true, class: klass, 'data-confirm': '确认审核通过?'}.merge(opts)) end - def delete_link(name, url, **opts) + def delete_link(name, url, **opts, &block) klass = ['action delete-action', opts.delete(:class)].compact.join(' ') refresh_url_data = "refresh_url=#{CGI::escape(request.fullpath)}" url = url + (url.index('?') ? '&' : '?') + refresh_url_data - raw link_to(name, url, { method: :delete, remote: true, class: klass, 'data-confirm': '确认删除?'}.merge(opts)) + if block_given? + raw link_to(url, { method: :delete, remote: true, class: klass, 'data-confirm': '确认删除?'}.merge(opts), &block) + else + raw link_to(name, url, { method: :delete, remote: true, class: klass, 'data-confirm': '确认删除?'}.merge(opts)) + end end def unsafe_params diff --git a/app/libs/util/file_manage.rb b/app/libs/util/file_manage.rb index 620fd7f01..f592ca4e7 100644 --- a/app/libs/util/file_manage.rb +++ b/app/libs/util/file_manage.rb @@ -10,10 +10,18 @@ module Util::FileManage File.join(Rails.root, "public", "images", relative_path) end - def disk_filename(source_type,source_id,image_file=nil) + def disk_filename(source_type, source_id,image_file=nil) File.join(storage_path, "#{source_type}", "#{source_id}") end + def exist?(source_type, source_id) + File.exist?(disk_filename(source_type, source_id)) + end + + def disk_file_url(source_type, source_id) + File.join('/images', relative_path, "#{source_type}", "#{source_id}") + end + def disk_auth_filename(source_type, source_id, type) File.join(storage_path, "#{source_type}", "#{source_id}#{type}") end diff --git a/app/models/coo_img.rb b/app/models/coo_img.rb new file mode 100644 index 000000000..0766c0727 --- /dev/null +++ b/app/models/coo_img.rb @@ -0,0 +1,5 @@ +class CooImg < ApplicationRecord + extend Enumerize + + enumerize :img_type, in: %i[com_coop edu_coop alliance_coop] +end \ No newline at end of file diff --git a/app/models/cooperation.rb b/app/models/cooperation.rb new file mode 100644 index 000000000..f0eb30cf6 --- /dev/null +++ b/app/models/cooperation.rb @@ -0,0 +1,9 @@ +class Cooperation < ApplicationRecord + def user_type_text + case user_type.to_i + when 1 then '高校合作' + when 2 then '企业合作' + when 3 then '实训投稿' + end + end +end \ No newline at end of file diff --git a/app/models/help.rb b/app/models/help.rb new file mode 100644 index 000000000..2b8bf5813 --- /dev/null +++ b/app/models/help.rb @@ -0,0 +1,3 @@ +class Help < ApplicationRecord + +end \ No newline at end of file diff --git a/app/models/student_works_score.rb b/app/models/student_works_score.rb index 19043f7f8..86d393f93 100644 --- a/app/models/student_works_score.rb +++ b/app/models/student_works_score.rb @@ -9,6 +9,9 @@ belongs_to :student_work validates :comment, length: { maximum: 2000 } + scope :shixun_comment, lambda { where(is_ultimate: 0) } + + def show_name identity, user identity < Course::STUDENT || self.user == user || self.reviewer_role != 3 end diff --git a/app/services/admins/drag_cooperative_service.rb b/app/services/admins/drag_cooperative_service.rb new file mode 100644 index 000000000..241b7eb11 --- /dev/null +++ b/app/services/admins/drag_cooperative_service.rb @@ -0,0 +1,35 @@ +class Admins::DragCooperativeService < ApplicationService + Error = Class.new(StandardError) + + attr_reader :move, :after + + def initialize(move, after) + @move = move + @after = after # 移动后下一个位置的元素 + end + + def call + return if move.position + 1 == after&.position # 未移动 + raise Error, '未知错误' if after && move.img_type != after.img_type + + coo_imgs = CooImg.where(img_type: move.img_type) + + ActiveRecord::Base.transaction do + if after.blank? # 移动至末尾 + total = coo_imgs.count + + coo_imgs.where('position > ?', move.position).update_all('position = position - 1') + move.update!(position: total) + return + end + + if move.position > after.position # 前移 + coo_imgs.where('position >= ? AND position < ?', after.position, move.position).update_all('position = position + 1') + move.update!(position: after.position) + else # 后移 + coo_imgs.where('position > ? AND position <= ?', move.position, after.position).update_all('position = position - 1') + move.update!(position: after.position) + end + end + end +end \ No newline at end of file diff --git a/app/services/admins/update_user_service.rb b/app/services/admins/update_user_service.rb index 9531d3718..c4755a1ae 100644 --- a/app/services/admins/update_user_service.rb +++ b/app/services/admins/update_user_service.rb @@ -11,7 +11,7 @@ class Admins::UpdateUserService < ApplicationService def call user.assign_attributes(user_attributes) user.firstname = '' - user.password = password if params[:password].present? + user.password = params[:password] if params[:password].present? if params[:identity].to_s == 'student' params[:technical_title] = nil diff --git a/app/services/users/question_bank_service.rb b/app/services/users/question_bank_service.rb index 7e640c6a5..66dff2117 100644 --- a/app/services/users/question_bank_service.rb +++ b/app/services/users/question_bank_service.rb @@ -85,7 +85,7 @@ class Users::QuestionBankService def custom_sort(relations, sort_by, sort_direction) case sort_by when 'updated_at' then - relations.order(updated_at: sort_direction) + relations.order("updated_at #{sort_direction}, id desc") when 'name' then relations.order("CONVERT(name USING gbk) COLLATE gbk_chinese_ci #{sort_direction}") when 'contributor' then diff --git a/app/views/admins/abouts/edit.html.erb b/app/views/admins/abouts/edit.html.erb new file mode 100644 index 000000000..4e75940c2 --- /dev/null +++ b/app/views/admins/abouts/edit.html.erb @@ -0,0 +1,15 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('关于我们') %> +<% end %> + +
" + this.lang.description + "
", + "", + "", + "" + this.atLink(this.emoji(text)) + "
\n" ); + }; + + markedRenderer.code = function (code, lang, escaped) { + + if (lang === "seq" || lang === "sequence") + { + return "" + code + "
"; + } + else + { + + return marked.Renderer.prototype.code.apply(this, arguments); + } + }; + + markedRenderer.tablecell = function(content, flags) { + var type = (flags.header) ? "th" : "td"; + var tag = (flags.align) ? "<" + type +" style=\"text-align:" + flags.align + "\">" : "<" + type + ">"; + + return tag + this.atLink(this.emoji(content)) + "" + type + ">\n"; + }; + + markedRenderer.listitem = function(text) { + if (settings.taskList && /^\s*\[[x\s]\]\s*/.test(text)) + { + text = text.replace(/^\s*\[\s\]\s*/, " ") + .replace(/^\s*\[x\]\s*/, " "); + + return "数据 | 失败原因 |
---|---|
' + item.data + ' | ' + item.message + ' |
+
" + this.lang.description + "
", + "", + "", + "" + this.atLink(this.emoji(text)) + "
\n" ); + }; + + markedRenderer.code = function (code, lang, escaped) { + + if (lang === "seq" || lang === "sequence") + { + return "" + code + "
"; + } + else + { + + return marked.Renderer.prototype.code.apply(this, arguments); + } + }; + + markedRenderer.tablecell = function(content, flags) { + var type = (flags.header) ? "th" : "td"; + var tag = (flags.align) ? "<" + type +" style=\"text-align:" + flags.align + "\">" : "<" + type + ">"; + + return tag + this.atLink(this.emoji(content)) + "" + type + ">\n"; + }; + + markedRenderer.listitem = function(text) { + if (settings.taskList && /^\s*\[[x\s]\]\s*/.test(text)) + { + text = text.replace(/^\s*\[\s\]\s*/, " ") + .replace(/^\s*\[x\]\s*/, " "); + + return "