From 5ddca68e5419a7ebf8032d394337b157c80a273e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=A0=91=E6=98=8E?= <775174143@qq.com> Date: Thu, 29 Aug 2019 10:26:26 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/stylesheets/educoder/edu-all.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/stylesheets/educoder/edu-all.css b/public/stylesheets/educoder/edu-all.css index a0d3d0aa1..e0a810d76 100644 --- a/public/stylesheets/educoder/edu-all.css +++ b/public/stylesheets/educoder/edu-all.css @@ -3763,6 +3763,6 @@ a.singlepublishtwo{ /*}*/ .topicsItem{ max-width: 1138px; - height: 110px; + max-height: 110px; overflow-y: auto; } \ No newline at end of file From 561d6b9a0bc02db1fabd19f8a77e5bcf009d3f94 Mon Sep 17 00:00:00 2001 From: p31729568 Date: Thu, 29 Aug 2019 10:28:22 +0800 Subject: [PATCH 2/5] admins: department list feature --- app/assets/javascripts/admin.js | 6 + .../javascripts/admins/departments/index.js | 173 ++++++++++ .../modals/admin-edit-department-modal.js | 34 ++ .../modals/admin-merge-department-modal.js | 110 ++++++ app/assets/stylesheets/admin.scss | 4 + .../stylesheets/admins/departments.scss | 24 ++ .../admins/department_members_controller.rb | 20 ++ .../admins/departments_controller.rb | 95 +++++ .../concerns/admins/render_helper.rb | 12 +- app/jobs/delete_department_notify_job.rb | 21 ++ app/models/department.rb | 8 + app/queries/admins/department_query.rb | 32 ++ app/queries/admins/user_query.rb | 8 +- .../admins/add_department_member_service.rb | 20 ++ .../daily_school_statistics/index.html.erb | 2 +- .../admins/department_members/create.js.erb | 4 + .../admins/department_members/destroy.js.erb | 2 + app/views/admins/departments/edit.js.erb | 2 + app/views/admins/departments/index.html.erb | 33 ++ app/views/admins/departments/index.js.erb | 1 + .../_add_department_member_modal.html.erb | 30 ++ .../shared/_create_department_modal.html.erb | 35 ++ .../shared/_department_item.html.erb | 28 ++ .../shared/_edit_department_modal.html.erb | 25 ++ .../admins/departments/shared/_list.html.erb | 28 ++ .../departments/shared/_member_users.html.erb | 12 + .../shared/_merge_department_modal.html.erb | 31 ++ app/views/admins/departments/update.js.erb | 4 + .../identity_authentications/index.html.erb | 2 +- .../index.html.erb | 2 +- app/views/admins/shared/_sidebar.html.erb | 6 + .../shixun_authorizations/index.html.erb | 2 +- .../subject_authorizations/index.html.erb | 2 +- app/views/admins/users/index.html.erb | 2 +- app/views/admins/users/index.json.jbuilder | 6 + .../users/shared/_reward_grade_modal.html.erb | 2 +- config/routes.rb | 5 + ...fest-1c370772f16743f825981ab0e5c94237.json | 2 +- ...2698a60147dfcfa42fb377bc77d8aa3701994b.js} | 323 +++++++++++++++++ ...8a60147dfcfa42fb377bc77d8aa3701994b.js.gz} | Bin 325964 -> 326890 bytes ...382665ace4475d501c2aba574cf76bbcde773.css} | 38 +- ...665ace4475d501c2aba574cf76bbcde773.css.gz} | Bin 52169 -> 52292 bytes ...1fae73f23e39cee0ed1464e007bd016a376.css.gz | Bin 89207 -> 0 bytes ...1ef75c4aea8fbccde5f7ac055031da8f7bad56.js} | 326 ++++++++++++++++++ ...75c4aea8fbccde5f7ac055031da8f7bad56.js.gz} | Bin 363116 -> 364125 bytes ...2300644d7d4ba1150394ffcbaec661ba14049.css} | 62 +++- ...00644d7d4ba1150394ffcbaec661ba14049.css.gz | Bin 0 -> 89340 bytes 47 files changed, 1563 insertions(+), 21 deletions(-) create mode 100644 app/assets/javascripts/admins/departments/index.js create mode 100644 app/assets/javascripts/admins/modals/admin-edit-department-modal.js create mode 100644 app/assets/javascripts/admins/modals/admin-merge-department-modal.js create mode 100644 app/assets/stylesheets/admins/departments.scss create mode 100644 app/controllers/admins/department_members_controller.rb create mode 100644 app/controllers/admins/departments_controller.rb create mode 100644 app/jobs/delete_department_notify_job.rb create mode 100644 app/queries/admins/department_query.rb create mode 100644 app/services/admins/add_department_member_service.rb create mode 100644 app/views/admins/department_members/create.js.erb create mode 100644 app/views/admins/department_members/destroy.js.erb create mode 100644 app/views/admins/departments/edit.js.erb create mode 100644 app/views/admins/departments/index.html.erb create mode 100644 app/views/admins/departments/index.js.erb create mode 100644 app/views/admins/departments/shared/_add_department_member_modal.html.erb create mode 100644 app/views/admins/departments/shared/_create_department_modal.html.erb create mode 100644 app/views/admins/departments/shared/_department_item.html.erb create mode 100644 app/views/admins/departments/shared/_edit_department_modal.html.erb create mode 100644 app/views/admins/departments/shared/_list.html.erb create mode 100644 app/views/admins/departments/shared/_member_users.html.erb create mode 100644 app/views/admins/departments/shared/_merge_department_modal.html.erb create mode 100644 app/views/admins/departments/update.js.erb create mode 100644 app/views/admins/users/index.json.jbuilder rename public/assets/{admin-d976e0fe3ffbf1de17a34071e36ce92b8971eb5b78bd885db0dc99c73c4d3773.js => admin-805bd1d51fc2176262c56e3d672698a60147dfcfa42fb377bc77d8aa3701994b.js} (99%) rename public/assets/{admin-d976e0fe3ffbf1de17a34071e36ce92b8971eb5b78bd885db0dc99c73c4d3773.js.gz => admin-805bd1d51fc2176262c56e3d672698a60147dfcfa42fb377bc77d8aa3701994b.js.gz} (90%) rename public/assets/{admin-c4256fab71dd84e6f6091b5a886fa3e736bbcebed51b503ea9972221022adb6b.css => admin-da73d5dfb553913851160370fde382665ace4475d501c2aba574cf76bbcde773.css} (99%) rename public/assets/{admin-c4256fab71dd84e6f6091b5a886fa3e736bbcebed51b503ea9972221022adb6b.css.gz => admin-da73d5dfb553913851160370fde382665ace4475d501c2aba574cf76bbcde773.css.gz} (63%) delete mode 100644 public/assets/application-378235e0207ed9f59fd6201c7b0971fae73f23e39cee0ed1464e007bd016a376.css.gz rename public/assets/{application-a044d1142b1e21bfda21aab2e22e08889343ad253b5847d446cee9620b2f51f0.js => application-77b7cbb373168c5147a8aaf1fb1ef75c4aea8fbccde5f7ac055031da8f7bad56.js} (99%) rename public/assets/{application-a044d1142b1e21bfda21aab2e22e08889343ad253b5847d446cee9620b2f51f0.js.gz => application-77b7cbb373168c5147a8aaf1fb1ef75c4aea8fbccde5f7ac055031da8f7bad56.js.gz} (82%) rename public/assets/{application-378235e0207ed9f59fd6201c7b0971fae73f23e39cee0ed1464e007bd016a376.css => application-9570e6d0fbc265d67e979570dde2300644d7d4ba1150394ffcbaec661ba14049.css} (99%) create mode 100644 public/assets/application-9570e6d0fbc265d67e979570dde2300644d7d4ba1150394ffcbaec661ba14049.css.gz diff --git a/app/assets/javascripts/admin.js b/app/assets/javascripts/admin.js index 7d1908547..ee85468c0 100644 --- a/app/assets/javascripts/admin.js +++ b/app/assets/javascripts/admin.js @@ -25,6 +25,12 @@ $.fn.select2.defaults.set('language', 'zh-CN'); Turbolinks.setProgressBarDelay(200); +$.notifyDefaults({ + type: 'success', + z_index: 9999, + delay: 2000 +}); + $(document).on('turbolinks:load', function(){ $('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="popover"]').popover(); diff --git a/app/assets/javascripts/admins/departments/index.js b/app/assets/javascripts/admins/departments/index.js new file mode 100644 index 000000000..eb0fc3a6a --- /dev/null +++ b/app/assets/javascripts/admins/departments/index.js @@ -0,0 +1,173 @@ +$(document).on('turbolinks:load', function() { + if ($('body.admins-departments-index-page').length > 0) { + var $searchContainer = $('.department-list-form'); + var $searchForm = $searchContainer.find('form.search-form'); + var $list = $('.department-list-container'); + + $searchContainer.on('change', '.form-check-input', function(){ + $searchForm.find('input[type="submit"]').trigger('click'); + }); + + // ============== 新建部门 =============== + var $modal = $('.modal.admin-create-department-modal'); + var $form = $modal.find('form.admin-create-department-form'); + var $departmentNameInput = $form.find('input[name="department_name"]'); + var $schoolSelect = $modal.find('.school-select'); + + $form.validate({ + errorElement: 'span', + errorClass: 'danger text-danger', + rules: { + school_id: { + required: true + }, + department_name: { + required: true + } + }, + messages: { + school_id: { + required: '请选择所属单位' + } + } + }); + + // modal ready fire + $modal.on('show.bs.modal', function () { + $departmentNameInput.val(''); + $schoolSelect.select2('val', ' '); + }); + + // ************** 学校选择 ************* + 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) { + $schoolSelect.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) { + $('#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); + } + }); + + $modal.on('click', '.submit-btn', function(){ + $form.find('.error').html(''); + + if ($form.valid()) { + var url = $form.data('url'); + + $.ajax({ + method: 'POST', + dataType: 'json', + url: url, + data: $form.serialize(), + success: function(){ + $.notify({ message: '创建成功' }); + $modal.modal('hide'); + + setTimeout(function(){ + window.location.reload(); + }, 500); + }, + error: function(res){ + var data = res.responseJSON; + $form.find('.error').html(data.message); + } + }); + } + }); + + // ============= 添加部门管理员 ============== + var $addMemberModal = $('.admin-add-department-member-modal'); + var $addMemberForm = $addMemberModal.find('.admin-add-department-member-form'); + var $memberSelect = $addMemberModal.find('.department-member-select'); + var $departmentIdInput = $addMemberForm.find('input[name="department_id"]') + + $addMemberModal.on('show.bs.modal', function(event){ + var $link = $(event.relatedTarget); + var departmentId = $link.data('department-id'); + $departmentIdInput.val(departmentId); + + $memberSelect.select2('val', ' '); + }); + + $memberSelect.select2({ + theme: 'bootstrap4', + placeholder: '请输入要添加的管理员姓名', + multiple: true, + minimumInputLength: 1, + ajax: { + delay: 500, + url: '/admins/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; + }, + templateSelection: function(item){ + if (item.id) { + } + return item.real_name || item.text; + } + }); + + $addMemberModal.on('click', '.submit-btn', function(){ + $addMemberForm.find('.error').html(''); + + var departmentId = $departmentIdInput.val(); + var memberIds = $memberSelect.val(); + if (departmentId && memberIds && memberIds.length > 0) { + $.ajax({ + method: 'POST', + dataType: 'script', + url: '/admins/departments/' + departmentId + '/department_member', + data: { user_ids: memberIds } + }); + } else { + $addMemberModal.modal('hide'); + } + }); + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/admins/modals/admin-edit-department-modal.js b/app/assets/javascripts/admins/modals/admin-edit-department-modal.js new file mode 100644 index 000000000..a1df01ba5 --- /dev/null +++ b/app/assets/javascripts/admins/modals/admin-edit-department-modal.js @@ -0,0 +1,34 @@ +$(document).on('turbolinks:load', function() { + $('.admin-modal-container').on('show.bs.modal', '.modal.admin-edit-department-modal', function(){ + var $modal = $('.modal.admin-edit-department-modal'); + var $form = $modal.find('form.admin-edit-department-form'); + + $form.validate({ + errorElement: 'span', + errorClass: 'danger text-danger', + rules: { + 'department[name]': { + required: true, + maxlength: 20 + }, + 'department[host_count]': { + digits: true + } + } + }); + + $modal.on('click', '.submit-btn', function(){ + $form.find('.error').html(''); + var url = $form.attr('action'); + + if ($form.valid()) { + $.ajax({ + method: 'PATCH', + dataType: 'script', + url: url, + data: $form.serialize() + }); + } + }); + }) +}); \ No newline at end of file diff --git a/app/assets/javascripts/admins/modals/admin-merge-department-modal.js b/app/assets/javascripts/admins/modals/admin-merge-department-modal.js new file mode 100644 index 000000000..aead3f485 --- /dev/null +++ b/app/assets/javascripts/admins/modals/admin-merge-department-modal.js @@ -0,0 +1,110 @@ +$(document).on('turbolinks:load', function() { + var $modal = $('.modal.admin-merge-department-modal'); + if ($modal.length > 0) { + var $form = $modal.find('form.admin-merge-department-form'); + var $schoolIdInput = $form.find('input[name="school_id"]'); + var $originDepartmentIdInput = $form.find('input[name="origin_department_id"]'); + var $departmentSelect = $modal.find('.department-select'); + + $form.validate({ + errorElement: 'span', + errorClass: 'danger text-danger', + rules: { + department_id: { + required: true + } + }, + messages: { + department_id: { + required: '请选择部门' + } + } + }); + + // ************** 学校选择 ************* + 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 defineDepartmentSelect = function(departments) { + $departmentSelect.empty(); + + $departmentSelect.select2({ + theme: 'bootstrap4', + placeholder: '请选择所属部门', + data: departments, + templateResult: function (item) { + if(!item.id || item.id === '') return item.text; + return item.name; + }, + templateSelection: function(item){ + if (item.id) { + $form.find('#department_id').val(item.id); + } + return item.name || item.text; + }, + matcher: matcherFunc + }); + $departmentSelect.select2('val', ' '); + }; + + // modal ready fire + $modal.on('show.bs.modal', function (event) { + var $link = $(event.relatedTarget); + + var schoolId = $link.data('schoolId'); + + $schoolIdInput.val(schoolId); + $originDepartmentIdInput.val($link.data('departmentId')); + + $.ajax({ + url: '/api/schools/' + schoolId + '/departments/for_option.json', + dataType: 'json', + type: 'GET', + success: function(data) { + defineDepartmentSelect(data.departments); + } + }); + }); + + $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); + } + }); + } + }); + } +}); \ No newline at end of file diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss index 7710423ea..e91c5ccfb 100644 --- a/app/assets/stylesheets/admin.scss +++ b/app/assets/stylesheets/admin.scss @@ -46,6 +46,10 @@ label.error { } } +input.form-control { + font-size: 14px; +} + .flex-1 { flex: 1; } diff --git a/app/assets/stylesheets/admins/departments.scss b/app/assets/stylesheets/admins/departments.scss new file mode 100644 index 000000000..7d9d078e5 --- /dev/null +++ b/app/assets/stylesheets/admins/departments.scss @@ -0,0 +1,24 @@ +.admins-departments-index-page { + .department-list-table { + .member-container { + .member-user { + display: flex; + justify-content: center; + flex-wrap: wrap; + + .member-user-item { + display: flex; + align-items: center; + height: 22px; + line-height: 22px; + padding: 2px 5px; + margin: 2px 2px; + border: 1px solid #91D5FF; + background-color: #E6F7FF; + color: #91D5FF; + border-radius: 4px; + } + } + } + } +} \ No newline at end of file diff --git a/app/controllers/admins/department_members_controller.rb b/app/controllers/admins/department_members_controller.rb new file mode 100644 index 000000000..ba483acef --- /dev/null +++ b/app/controllers/admins/department_members_controller.rb @@ -0,0 +1,20 @@ +class Admins::DepartmentMembersController < Admins::BaseController + + helper_method :current_department + + def create + Admins::AddDepartmentMemberService.call(current_department, params) + current_department.reload + end + + def destroy + @member = current_department.department_members.find_by(user_id: params[:user_id]) + @member.destroy! if @member.present? + end + + private + + def current_department + @_current_department ||= Department.find(params[:department_id]) + end +end \ No newline at end of file diff --git a/app/controllers/admins/departments_controller.rb b/app/controllers/admins/departments_controller.rb new file mode 100644 index 000000000..ed7c3d3db --- /dev/null +++ b/app/controllers/admins/departments_controller.rb @@ -0,0 +1,95 @@ +class Admins::DepartmentsController < Admins::BaseController + + helper_method :current_department + + def index + params[:sort_by] ||= 'created_at' + params[:sort_direction] ||= 'desc' + + departments = Admins::DepartmentQuery.call(params) + + @departments = paginate departments.preload(:school, :member_users) + + department_ids = @departments.map(&:id) + @users_count = UserExtension.where(department_id: department_ids).group(:department_id).count + @professional_auth_count = UserExtension.where(department_id: department_ids) + .joins(:user).where(users: { professional_certification: true }) + .group(:department_id).count + end + + def create + department_name = params[:department_name].to_s.strip + school = School.find(params[:school_id]) + + return render_error('部门名称重复') if school.departments.exists?(name: department_name) + + ActiveRecord::Base.transaction do + department = school.departments.create!(name: department_name, is_auth: 1) + ApplyAddDepartment.create!(school_id: school.id, status: 1, name: department.name, + department_id: department.id, user_id: current_user.id) + end + + render_ok + end + + def edit + end + + def update + identifier = update_params.delete(:identifier).presence + if identifier && Department.where.not(id: current_department.id).exists?(identifier: identifier) + return render_error('统计链接重复', type: :notify) + end + + current_department.update!(update_params.merge(identifier: identifier)) + end + + def destroy + ActiveRecord::Base.transaction do + current_department.apply_add_departments.update_all(status: 2) + + user_ids = current_department.user_extensions.pluck(:user_id) + if user_ids.present? + DeleteDepartmentNotifyJob.perform_later(current_department.id, 0, user_ids) + current_department.soft_delete! + else + current_department.destroy! + end + end + + render_delete_success + end + + def merge + return render_error('请选择其它部门') if params[:origin_department_id].to_s == params[:department_id].to_s + + origin_department = Department.find(params[:origin_department_id]) + to_department = Department.find(params[:department_id]) + + return render_error('部门所属单位不相同') if origin_department.school_id != to_department.school_id + + ActiveRecord::Base.transaction do + origin_department.apply_add_departments.delete_all + + origin_department.user_extensions.update_all(department_id: to_department.id) + + if to_department.identifier.blank? && origin_department.identifier.present? + to_department.update!(identifier: origin_department.identifier) + end + + origin_department.destroy! + end + + render_ok + end + + private + + def current_department + @_current_department ||= Department.find(params[:id]) + end + + def update_params + params.require(:department).permit(:name, :identifier, :host_count) + end +end \ No newline at end of file diff --git a/app/controllers/concerns/admins/render_helper.rb b/app/controllers/concerns/admins/render_helper.rb index 94b7c29cb..0f136b62d 100644 --- a/app/controllers/concerns/admins/render_helper.rb +++ b/app/controllers/concerns/admins/render_helper.rb @@ -17,9 +17,9 @@ module Admins::RenderHelper json: -> { render status: 404, json: { message: '资源未找到' } }) end - def render_unprocessable_entity(message) + def render_unprocessable_entity(message, type: :alert) render_by_format(html: -> { render 'admins/shared/422' }, - js: -> { render_js_error(message) }, + js: -> { render_js_error(message, type: type) }, json: -> { render status: 422, json: { message: message } }) end alias_method :render_error, :render_unprocessable_entity @@ -40,7 +40,11 @@ module Admins::RenderHelper end alias_method :render_success_js, :render_delete_success - def render_js_error(message) - render_js_template 'admins/shared/error', locals: { message: message } + def render_js_error(message, type: :alert) + if type == :notify + render js: "$.notify({ message: '#{message}' },{ type: 'danger', delay: 5000 });" + else + render_js_template 'admins/shared/error', locals: { message: message } + end end end \ No newline at end of file diff --git a/app/jobs/delete_department_notify_job.rb b/app/jobs/delete_department_notify_job.rb new file mode 100644 index 000000000..1da5e2e85 --- /dev/null +++ b/app/jobs/delete_department_notify_job.rb @@ -0,0 +1,21 @@ +# 删除部门 消息通知 +class DeleteDepartmentNotifyJob < ApplicationJob + queue_as :notify + + def perform(department_id, operator_id, user_ids) + department = Department.unscoped.find_by(id: department_id) + return if department.blank? || user_ids.blank? + + attrs = %i[ user_id trigger_user_id container_id container_type tiding_type status created_at updated_at] + + same_attrs = { + trigger_user_id: operator_id, container_id: department.id, container_type: 'Department', + status: 4, tiding_type: 'System' + } + Tiding.bulk_insert(*attrs) do |worker| + user_ids.each do |user_id| + worker.add same_attrs.merge(user_id: user_id) + end + end + end +end diff --git a/app/models/department.rb b/app/models/department.rb index 9c4a0908b..1923cfb33 100644 --- a/app/models/department.rb +++ b/app/models/department.rb @@ -2,6 +2,14 @@ class Department < ApplicationRecord belongs_to :school has_many :department_members, dependent: :destroy + has_many :member_users, through: :department_members, source: :user + + has_many :user_extensions, dependent: :nullify + has_many :apply_add_departments scope :without_deleted, -> { where(is_delete: false) } + + def soft_delete! + update!(is_delete: true) + end end diff --git a/app/queries/admins/department_query.rb b/app/queries/admins/department_query.rb new file mode 100644 index 000000000..b0b5d0118 --- /dev/null +++ b/app/queries/admins/department_query.rb @@ -0,0 +1,32 @@ +class Admins::DepartmentQuery < ApplicationQuery + include CustomSortable + + attr_reader :params + + sort_columns :created_at, default_by: :created_at, default_direction: :desc + + def initialize(params) + @params = params + end + + def call + departments = Department.where(is_auth: true).without_deleted + + keyword = params[:keyword].to_s.strip + if keyword.present? + departments = departments.joins(:school) + .where('schools.name LIKE :keyword OR departments.name LIKE :keyword', keyword: keyword) + end + + if params[:with_member].to_s == 'true' + subquery = DepartmentMember.where('department_id = departments.id').select('1 AS one').to_sql + departments = departments.where("EXISTS(#{subquery})") + end + + if params[:with_identifier].to_s == 'true' + departments = departments.where.not(identifier: nil).where.not(identifier: '') + end + + custom_sort(departments, 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 5a633f059..75e50fc1b 100644 --- a/app/queries/admins/user_query.rb +++ b/app/queries/admins/user_query.rb @@ -28,7 +28,13 @@ class Admins::UserQuery < ApplicationQuery keyword = params[:keyword].to_s.strip.presence if keyword sql = 'CONCAT(lastname, firstname) LIKE :keyword OR login LIKE :keyword OR mail LIKE :keyword OR phone LIKE :keyword' - users = users.where(sql, keyword: keyword) + users = users.where(sql, keyword: "%#{keyword}%") + end + + # 姓名 + name = params[:name].to_s.strip.presence + if name.present? + users = users.where('CONCAT(lastname, firstname) LIKE :name', name: "%#{name}%") end # 学校名称 diff --git a/app/services/admins/add_department_member_service.rb b/app/services/admins/add_department_member_service.rb new file mode 100644 index 000000000..f8331cf4a --- /dev/null +++ b/app/services/admins/add_department_member_service.rb @@ -0,0 +1,20 @@ +class Admins::AddDepartmentMemberService < ApplicationService + + attr_reader :department, :params + + def initialize(department, params) + @department = department + @params = params + end + + def call + columns = %i[] + DepartmentMember.bulk_insert(*columns) do |worker| + Array.wrap(params[:user_ids]).compact.each do |user_id| + next if department.department_members.exists?(user_id: user_id) + + worker.add(department_id: department.id, user_id: user_id) + end + end + end +end \ No newline at end of file diff --git a/app/views/admins/daily_school_statistics/index.html.erb b/app/views/admins/daily_school_statistics/index.html.erb index 054e06fc6..39dcba633 100644 --- a/app/views/admins/daily_school_statistics/index.html.erb +++ b/app/views/admins/daily_school_statistics/index.html.erb @@ -5,7 +5,7 @@
<%= form_tag(admins_daily_school_statistics_path, method: :get, class: 'form-inline search-form', remote: true) do %> <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: 'ID/单位名称搜索') %> - <%= submit_tag('搜索', class: 'btn btn-primary ml-3') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> <% end %> <%#= link_to '导出Excel', export_admins_daily_school_statistics_path(format: :xlsx), class: 'btn btn-outline-primary export-action' %> diff --git a/app/views/admins/department_members/create.js.erb b/app/views/admins/department_members/create.js.erb new file mode 100644 index 000000000..4355c7432 --- /dev/null +++ b/app/views/admins/department_members/create.js.erb @@ -0,0 +1,4 @@ +$('.modal.admin-add-department-member-modal').modal('hide'); +$.notify({ message: '操作成功' }); + +$('.department-list-table .department-item-<%= current_department.id %>').html("<%= j(render partial: 'admins/departments/shared/department_item', locals: { department: current_department }) %>") \ No newline at end of file diff --git a/app/views/admins/department_members/destroy.js.erb b/app/views/admins/department_members/destroy.js.erb new file mode 100644 index 000000000..d3eb3755b --- /dev/null +++ b/app/views/admins/department_members/destroy.js.erb @@ -0,0 +1,2 @@ +$.notify({ message: '操作成功' }); +$('.department-list-container .department-item-<%= current_department.id %> .member-user-item-<%= @member.user_id %>').remove(); \ No newline at end of file diff --git a/app/views/admins/departments/edit.js.erb b/app/views/admins/departments/edit.js.erb new file mode 100644 index 000000000..dc86d3ae0 --- /dev/null +++ b/app/views/admins/departments/edit.js.erb @@ -0,0 +1,2 @@ +$('.admin-modal-container').html("<%= j( render partial: 'admins/departments/shared/edit_department_modal', locals: { department: current_department } ) %>"); +$('.modal.admin-edit-department-modal').modal('show'); \ No newline at end of file diff --git a/app/views/admins/departments/index.html.erb b/app/views/admins/departments/index.html.erb new file mode 100644 index 000000000..54ca47252 --- /dev/null +++ b/app/views/admins/departments/index.html.erb @@ -0,0 +1,33 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('部门列表') %> +<% end %> + +
+ <%= form_tag(admins_departments_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %> +
+ <%= hidden_field_tag(:with_member, false) %> + <%= check_box_tag(:with_member, true, params[:with_member].to_s == 'true', class: 'form-check-input') %> + +
+ +
+ <%= hidden_field_tag(:with_identifier, false) %> + <%= check_box_tag(:with_identifier, true, params[:with_identifier].to_s == 'true', class: 'form-check-input') %> + +
+ + <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '部门/单位名称检索') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> + <% end %> + + <%= javascript_void_link '新建部门', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-create-department-modal' } %> +
+ +
+ <%= render partial: 'admins/departments/shared/list', + locals: { departments: @departments, users_count: @users_count, professional_auth_count: @professional_auth_count } %> +
+ +<%= render 'admins/departments/shared/create_department_modal' %> +<%= render 'admins/departments/shared/add_department_member_modal' %> +<%= render 'admins/departments/shared/merge_department_modal' %> \ No newline at end of file diff --git a/app/views/admins/departments/index.js.erb b/app/views/admins/departments/index.js.erb new file mode 100644 index 000000000..bd2e4b25d --- /dev/null +++ b/app/views/admins/departments/index.js.erb @@ -0,0 +1 @@ +$('.department-list-container').html("<%= j(render partial: 'admins/departments/shared/list', locals: { departments: @departments, users_count: @users_count, professional_auth_count: @professional_auth_count }) %>"); \ No newline at end of file diff --git a/app/views/admins/departments/shared/_add_department_member_modal.html.erb b/app/views/admins/departments/shared/_add_department_member_modal.html.erb new file mode 100644 index 000000000..5d2707222 --- /dev/null +++ b/app/views/admins/departments/shared/_add_department_member_modal.html.erb @@ -0,0 +1,30 @@ + \ No newline at end of file diff --git a/app/views/admins/departments/shared/_create_department_modal.html.erb b/app/views/admins/departments/shared/_create_department_modal.html.erb new file mode 100644 index 000000000..ae6605eb8 --- /dev/null +++ b/app/views/admins/departments/shared/_create_department_modal.html.erb @@ -0,0 +1,35 @@ + \ No newline at end of file diff --git a/app/views/admins/departments/shared/_department_item.html.erb b/app/views/admins/departments/shared/_department_item.html.erb new file mode 100644 index 000000000..2f39d6248 --- /dev/null +++ b/app/views/admins/departments/shared/_department_item.html.erb @@ -0,0 +1,28 @@ +<% not_list = defined?(:users_count) %> + +<%= overflow_hidden_span department.name, width: 150 %> +<%= overflow_hidden_span department.school.name, width: 150 %> + +<% if not_list %> + <%= department.user_extensions.count %> + <%= department.user_extensions.joins(:user).where(users: { professional_certification: true }).count %> +<% else %> + <%= users_count.fetch(department.id, 0) %> + <%= professional_auth_count.fetch(department.id, 0) %> +<% end %> + + + <%= render partial: 'admins/departments/shared/member_users', locals: { department: department } %> + +<%= link_to department.identifier.to_s, '#', target: '_blank' %> +<%= department.host_count %> +<%= department.created_at&.strftime('%Y-%m-%d %H:%M') %> + + <%= link_to '编辑', edit_admins_department_path(department), remote: true, class: 'action' %> + + <%= javascript_void_link '添加管理员', class: 'action', data: { department_id: department.id, toggle: 'modal', target: '.admin-add-department-member-modal' } %> + + <%= javascript_void_link '更改', class: 'action', data: { school_id: department.school_id, department_id: department.id, toggle: 'modal', target: '.admin-merge-department-modal' } %> + + <%= delete_link '删除', admins_department_path(department, element: ".department-item-#{department.id}"), class: 'delete-department-action' %> + \ No newline at end of file diff --git a/app/views/admins/departments/shared/_edit_department_modal.html.erb b/app/views/admins/departments/shared/_edit_department_modal.html.erb new file mode 100644 index 000000000..38b43bbce --- /dev/null +++ b/app/views/admins/departments/shared/_edit_department_modal.html.erb @@ -0,0 +1,25 @@ + \ No newline at end of file diff --git a/app/views/admins/departments/shared/_list.html.erb b/app/views/admins/departments/shared/_list.html.erb new file mode 100644 index 000000000..6af63d6f4 --- /dev/null +++ b/app/views/admins/departments/shared/_list.html.erb @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + <% if departments.present? %> + <% departments.each do |department| %> + + <%= render 'admins/departments/shared/department_item', department: department %> + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
部门名称单位名称用户数已职业认证部门管理员统计链接云主机数<%= sort_tag('创建时间', name: 'created_at', path: admins_departments_path) %>操作
+ +<%= render partial: 'admins/shared/paginate', locals: { objects: departments } %> \ No newline at end of file diff --git a/app/views/admins/departments/shared/_member_users.html.erb b/app/views/admins/departments/shared/_member_users.html.erb new file mode 100644 index 000000000..8d4d466db --- /dev/null +++ b/app/views/admins/departments/shared/_member_users.html.erb @@ -0,0 +1,12 @@ +
+ <% department.member_users.each do |user| %> + + <%= link_to user.real_name, "/users/#{user.login}", target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } %> + <%= link_to(admins_department_department_member_path(department, user_id: user.id), + method: :delete, remote: true, class: 'ml-1 delete-member-action', + data: { confirm: '确认删除吗?' }) do %> + + <% end %> + + <% end %> +
\ No newline at end of file diff --git a/app/views/admins/departments/shared/_merge_department_modal.html.erb b/app/views/admins/departments/shared/_merge_department_modal.html.erb new file mode 100644 index 000000000..200e75ccd --- /dev/null +++ b/app/views/admins/departments/shared/_merge_department_modal.html.erb @@ -0,0 +1,31 @@ + \ No newline at end of file diff --git a/app/views/admins/departments/update.js.erb b/app/views/admins/departments/update.js.erb new file mode 100644 index 000000000..359bac59c --- /dev/null +++ b/app/views/admins/departments/update.js.erb @@ -0,0 +1,4 @@ +$('.modal.admin-edit-department-modal').modal('hide'); +$.notify({ message: '操作成功' }); + +$('.department-list-table .department-item-<%= current_department.id %>').html("<%= j(render partial: 'admins/departments/shared/department_item', locals: { department: current_department }) %>") \ No newline at end of file diff --git a/app/views/admins/identity_authentications/index.html.erb b/app/views/admins/identity_authentications/index.html.erb index 170a8fc4a..38b7dfd63 100644 --- a/app/views/admins/identity_authentications/index.html.erb +++ b/app/views/admins/identity_authentications/index.html.erb @@ -21,7 +21,7 @@ <%= select_tag(:status, options_for_select(status_options), class: 'form-control') %>
<%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '姓名/学校/单位检索') %> - <%= submit_tag('搜索', class: 'btn btn-primary ml-3') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> <% end %> diff --git a/app/views/admins/professional_authentications/index.html.erb b/app/views/admins/professional_authentications/index.html.erb index e10d2bd80..32eaa47bd 100644 --- a/app/views/admins/professional_authentications/index.html.erb +++ b/app/views/admins/professional_authentications/index.html.erb @@ -21,7 +21,7 @@ <%= select_tag(:status, options_for_select(status_options), class: 'form-control') %> <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '姓名/学校/单位检索') %> - <%= submit_tag('搜索', class: 'btn btn-primary ml-3') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> <% end %> diff --git a/app/views/admins/shared/_sidebar.html.erb b/app/views/admins/shared/_sidebar.html.erb index 8c995ec45..238f46c76 100644 --- a/app/views/admins/shared/_sidebar.html.erb +++ b/app/views/admins/shared/_sidebar.html.erb @@ -26,6 +26,12 @@ <% end %> +
  • + <%= sidebar_item_group('#schools-submenu', '单位管理', icon: 'building') do %> +
  • <%= sidebar_item(admins_departments_path, '部门列表', icon: 'sitemap', controller: 'admins-departments') %>
  • + <% end %> + + <%#= sidebar_item_group('#course-submenu', '课堂+', icon: 'mortar-board') do %> diff --git a/app/views/admins/shixun_authorizations/index.html.erb b/app/views/admins/shixun_authorizations/index.html.erb index 743f2e1b2..3aaa1ce89 100644 --- a/app/views/admins/shixun_authorizations/index.html.erb +++ b/app/views/admins/shixun_authorizations/index.html.erb @@ -21,7 +21,7 @@ <%= select_tag(:status, options_for_select(status_options), class: 'form-control') %> <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '实训名称检索') %> - <%= submit_tag('搜索', class: 'btn btn-primary ml-3') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> <% end %> diff --git a/app/views/admins/subject_authorizations/index.html.erb b/app/views/admins/subject_authorizations/index.html.erb index 3d5539663..522278a3d 100644 --- a/app/views/admins/subject_authorizations/index.html.erb +++ b/app/views/admins/subject_authorizations/index.html.erb @@ -21,7 +21,7 @@ <%= select_tag(:status, options_for_select(status_options), class: 'form-control') %> <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '实训课程名称检索') %> - <%= submit_tag('搜索', class: 'btn btn-primary ml-3') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> <% end %> diff --git a/app/views/admins/users/index.html.erb b/app/views/admins/users/index.html.erb index 5d2af36c3..b145edd24 100644 --- a/app/views/admins/users/index.html.erb +++ b/app/views/admins/users/index.html.erb @@ -24,7 +24,7 @@ <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: 'ID/姓名/邮箱/手机号检索') %> <%= text_field_tag(:school_name, params[:school_name], class: 'form-control col-sm-2', placeholder: '学校/单位检索') %> - <%= submit_tag('搜索', class: 'btn btn-primary ml-3') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> <% end %> diff --git a/app/views/admins/users/index.json.jbuilder b/app/views/admins/users/index.json.jbuilder new file mode 100644 index 000000000..5591474a4 --- /dev/null +++ b/app/views/admins/users/index.json.jbuilder @@ -0,0 +1,6 @@ +json.count @users.total_count +json.users do + json.array! @users.each do |user| + json.extract! user, :id, :login, :real_name, :identity, :school_name + end +end \ No newline at end of file diff --git a/app/views/admins/users/shared/_reward_grade_modal.html.erb b/app/views/admins/users/shared/_reward_grade_modal.html.erb index 87c74c499..2cf741906 100644 --- a/app/views/admins/users/shared/_reward_grade_modal.html.erb +++ b/app/views/admins/users/shared/_reward_grade_modal.html.erb @@ -2,7 +2,7 @@