diff --git a/app/assets/javascripts/admins/laboratories/edit.js b/app/assets/javascripts/admins/laboratories/edit.js
new file mode 100644
index 000000000..63b26bbe0
--- /dev/null
+++ b/app/assets/javascripts/admins/laboratories/edit.js
@@ -0,0 +1,86 @@
+$(document).on('turbolinks:load', function() {
+ if ($('body.admins-laboratory-settings-show-page, body.admins-laboratory-settings-update-page').length > 0) {
+ var $container = $('.edit-laboratory-setting-container');
+ var $form = $container.find('.edit_laboratory');
+
+ $('.logo-item-left').on("change", 'input[type="file"]', function () {
+ var $fileInput = $(this);
+ var file = this.files[0];
+ var imageType = /image.*/;
+ if (file && file.type.match(imageType)) {
+ var reader = new FileReader();
+ reader.onload = function () {
+ var $box = $fileInput.parent();
+ $box.find('img').attr('src', reader.result).css('display', 'block');
+ $box.addClass('has-img');
+ };
+ reader.readAsDataURL(file);
+ } else {
+ }
+ });
+
+ createMDEditor('laboratory-footer-editor', { height: 200, placeholder: '请输入备案信息' });
+
+ $form.validate({
+ errorElement: 'span',
+ errorClass: 'danger text-danger',
+ errorPlacement:function(error,element){
+ if(element.parent().hasClass("input-group")){
+ element.parent().after(error);
+ }else{
+ element.after(error)
+ }
+ },
+ rules: {
+ identifier: {
+ required: true,
+ checkSite: true
+ },
+ name: {
+ required: true
+ }
+ }
+ });
+ $.validator.addMethod("checkSite",function(value,element,params){
+ var checkSite = /^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$/;
+ return this.optional(element)||(checkSite.test(value + '.educoder.com'));
+ },"域名不合法!");
+
+ $form.on('click', '.submit-btn', function(){
+ $form.find('.submit-btn').attr('disabled', 'disabled');
+ $form.find('.error').html('');
+ var valid = $form.valid();
+
+ $('input[name="navbar[][name]"]').each(function(_, e){
+ var $ele = $(e);
+ if($ele.val() === undefined || $ele.val().length === 0){
+ $ele.addClass('danger text-danger');
+ valid = false;
+ } else {
+ $ele.removeClass('danger text-danger');
+ }
+ });
+
+ if(!valid) return;
+ $.ajax({
+ method: 'PATCH',
+ dataType: 'json',
+ url: $form.attr('action'),
+ data: new FormData($form[0]),
+ processData: false,
+ contentType: false,
+ success: function(data){
+ $.notify({ message: '保存成功' });
+ window.location.reload();
+ },
+ error: function(res){
+ var data = res.responseJSON;
+ $form.find('.error').html(data.message);
+ },
+ complete: function(){
+ $form.find('.submit-btn').attr('disabled', false);
+ }
+ });
+ })
+ }
+});
\ No newline at end of file
diff --git a/app/assets/javascripts/admins/laboratories/index.js b/app/assets/javascripts/admins/laboratories/index.js
new file mode 100644
index 000000000..abb7cb72d
--- /dev/null
+++ b/app/assets/javascripts/admins/laboratories/index.js
@@ -0,0 +1,164 @@
+$(document).on('turbolinks:load', function() {
+ if ($('body.admins-laboratories-index-page').length > 0) {
+ var $searchContainer = $('.laboratory-list-form');
+ var $searchForm = $searchContainer.find('form.search-form');
+ var $list = $('.laboratory-list-container');
+
+ // ============== 新建 ===============
+ var $modal = $('.modal.admin-create-laboratory-modal');
+ var $form = $modal.find('form.admin-create-laboratory-form');
+ var $schoolSelect = $modal.find('.school-select');
+
+ $form.validate({
+ errorElement: 'span',
+ errorClass: 'danger text-danger',
+ rules: {
+ school_id: {
+ required: true
+ }
+ },
+ messages: {
+ school_id: {
+ required: '请选择所属单位'
+ }
+ }
+ });
+
+ // modal ready fire
+ $modal.on('show.bs.modal', function () {
+ $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-laboratory-user-modal');
+ var $addMemberForm = $addMemberModal.find('.admin-add-laboratory-user-form');
+ var $memberSelect = $addMemberModal.find('.laboratory-user-select');
+ var $laboratoryIdInput = $addMemberForm.find('input[name="laboratory_id"]')
+
+ $addMemberModal.on('show.bs.modal', function(event){
+ var $link = $(event.relatedTarget);
+ var laboratoryId = $link.data('laboratory-id');
+ $laboratoryIdInput.val(laboratoryId);
+
+ $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 laboratoryId = $laboratoryIdInput.val();
+ var memberIds = $memberSelect.val();
+ if (laboratoryId && memberIds && memberIds.length > 0) {
+ $.ajax({
+ method: 'POST',
+ dataType: 'script',
+ url: '/admins/laboratories/' + laboratoryId + '/laboratory_user',
+ data: { user_ids: memberIds }
+ });
+ } else {
+ $addMemberModal.modal('hide');
+ }
+ });
+ }
+});
\ No newline at end of file
diff --git a/app/assets/stylesheets/admins/laboratories.scss b/app/assets/stylesheets/admins/laboratories.scss
new file mode 100644
index 000000000..ad5c8c5a8
--- /dev/null
+++ b/app/assets/stylesheets/admins/laboratories.scss
@@ -0,0 +1,99 @@
+.admins-laboratories-index-page {
+ .laboratory-list-table {
+ .member-container {
+ .laboratory-user {
+ display: flex;
+ justify-content: center;
+ flex-wrap: wrap;
+
+ .laboratory-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;
+ }
+ }
+ }
+ }
+}
+.admins-laboratory-settings-show-page, .admins-laboratory-settings-update-page {
+ .edit-laboratory-setting-container {
+ .logo-item {
+ display: flex;
+
+ &-img {
+ display: block;
+ width: 80px;
+ height: 80px;
+ }
+
+ &-upload {
+ cursor: pointer;
+ position: absolute;
+ top: 0;
+ width: 80px;
+ height: 80px;
+ background: #F5F5F5;
+ border: 1px solid #E5E5E5;
+
+ &::before {
+ content: '';
+ position: absolute;
+ top: 27px;
+ left: 39px;
+ width: 2px;
+ height: 26px;
+ background: #E5E5E5;
+ }
+
+ &::after {
+ content: '';
+ position: absolute;
+ top: 39px;
+ left: 27px;
+ width: 26px;
+ height: 2px;
+ background: #E5E5E5;
+ }
+ }
+
+ &-left {
+ position: relative;
+ width: 80px;
+ height: 80px;
+
+ &.has-img {
+ .logo-item-upload {
+ display: none;
+ }
+
+ &:hover {
+ .logo-item-upload {
+ display: block;
+ background: rgba(145, 145, 145, 0.8);
+ }
+ }
+ }
+ }
+
+ &-right {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ color: #777777;
+ font-size: 12px;
+ }
+
+ &-title {
+ color: #23272B;
+ font-size: 14px;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/controllers/admins/laboratories_controller.rb b/app/controllers/admins/laboratories_controller.rb
new file mode 100644
index 000000000..e393c6677
--- /dev/null
+++ b/app/controllers/admins/laboratories_controller.rb
@@ -0,0 +1,32 @@
+class Admins::LaboratoriesController < Admins::BaseController
+ def index
+ params[:sort_by] = params[:sort_by].presence || 'id'
+ params[:sort_direction] = params[:sort_direction].presence || 'desc'
+
+ laboratories = Admins::LaboratoryQuery.call(params)
+ @laboratories = paginate laboratories.preload(:school, :laboratory_users)
+ end
+
+ def create
+ Admins::CreateLaboratoryService.call(create_params)
+ render_ok
+ rescue Admins::CreateLaboratoryService::Error => ex
+ render_error(ex.message)
+ end
+
+ def destroy
+ current_laboratory.destroy!
+
+ render_delete_success
+ end
+
+ private
+
+ def current_laboratory
+ @_current_laboratory ||= Laboratory.find(params[:id])
+ end
+
+ def create_params
+ params.permit(:school_id)
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/admins/laboratory_settings_controller.rb b/app/controllers/admins/laboratory_settings_controller.rb
new file mode 100644
index 000000000..f9676bfd3
--- /dev/null
+++ b/app/controllers/admins/laboratory_settings_controller.rb
@@ -0,0 +1,20 @@
+class Admins::LaboratorySettingsController < Admins::BaseController
+ def show
+ @laboratory = current_laboratory
+ end
+
+ def update
+ Admins::SaveLaboratorySettingService.call(current_laboratory, form_params)
+ render_ok
+ end
+
+ private
+
+ def current_laboratory
+ @_current_laboratory ||= Laboratory.find(params[:laboratory_id])
+ end
+
+ def form_params
+ params.permit(:identifier, :name, :nav_logo, :login_logo, :tab_logo, :footer, navbar: %i[name link hidden])
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/admins/laboratory_users_controller.rb b/app/controllers/admins/laboratory_users_controller.rb
new file mode 100644
index 000000000..36e389a3e
--- /dev/null
+++ b/app/controllers/admins/laboratory_users_controller.rb
@@ -0,0 +1,19 @@
+class Admins::LaboratoryUsersController < Admins::BaseController
+ helper_method :current_laboratory
+
+ def create
+ Admins::AddLaboratoryUserService.call(current_laboratory, params.permit(user_ids: []))
+ current_laboratory.reload
+ end
+
+ def destroy
+ @laboratory_user = current_laboratory.laboratory_users.find_by(user_id: params[:user_id])
+ @laboratory_user.destroy! if @laboratory_user.present?
+ end
+
+ private
+
+ def current_laboratory
+ @_current_laboratory ||= Laboratory.find(params[:laboratory_id])
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index b539a0c68..53e1be6e7 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -7,6 +7,7 @@ class ApplicationController < ActionController::Base
include ControllerRescueHandler
include GitHelper
include LoggerHelper
+ include LaboratoryHelper
protect_from_forgery prepend: true, unless: -> { request.format.json? }
diff --git a/app/controllers/concerns/laboratory_helper.rb b/app/controllers/concerns/laboratory_helper.rb
new file mode 100644
index 000000000..fbb18b36d
--- /dev/null
+++ b/app/controllers/concerns/laboratory_helper.rb
@@ -0,0 +1,15 @@
+module LaboratoryHelper
+ extend ActiveSupport::Concern
+
+ included do
+ helper_method :default_setting
+ end
+
+ def current_laboratory
+ @_current_laboratory ||= (Laboratory.find_by_subdomain(request.subdomain) || Laboratory.find(1))
+ end
+
+ def default_setting
+ @_default_setting ||= LaboratorySetting.find_by(laboratory_id: 1)
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb
new file mode 100644
index 000000000..ce5481147
--- /dev/null
+++ b/app/controllers/settings_controller.rb
@@ -0,0 +1,5 @@
+class SettingsController < ApplicationController
+ def show
+ @laboratory = current_laboratory
+ end
+end
\ No newline at end of file
diff --git a/app/libs/util/file_manage.rb b/app/libs/util/file_manage.rb
index 822bfca4f..2f87a3e86 100644
--- a/app/libs/util/file_manage.rb
+++ b/app/libs/util/file_manage.rb
@@ -10,31 +10,35 @@ module Util::FileManage
File.join(Rails.root, "public", "images", relative_path)
end
- def disk_filename(source_type, source_id,image_file=nil)
- File.join(storage_path, "#{source_type}", "#{source_id}")
+ def disk_filename(source_type, source_id, suffix=nil)
+ File.join(storage_path, "#{source_type}", "#{source_id}#{suffix}")
end
- def exist?(source_type, source_id)
- File.exist?(disk_filename(source_type, source_id))
+ def source_disk_filename(source, suffix=nil)
+ disk_filename(source.class.name, source.id, suffix)
end
- def exists?(source)
- File.exist?(disk_filename(source.class, source.id))
+ def exist?(source_type, source_id, suffix=nil)
+ File.exist?(disk_filename(source_type, source_id, suffix))
+ end
+
+ def exists?(source, suffix=nil)
+ File.exist?(disk_filename(source.class, source.id, suffix))
end
def disk_file_url(source_type, source_id, suffix = nil)
- t = ctime(source_type, source_id)
+ t = ctime(source_type, source_id, suffix)
File.join('/images', relative_path, "#{source_type}", "#{source_id}#{suffix}") + "?t=#{t}"
end
- def source_disk_file_url(source)
- disk_file_url(source.class, source.id)
+ def source_disk_file_url(source, suffix=nil)
+ disk_file_url(source.class, source.id, suffix)
end
- def ctime(source_type, source_id)
- return nil unless exist?(source_type, source_id)
+ def ctime(source_type, source_id, suffix)
+ return nil unless exist?(source_type, source_id, suffix)
- File.ctime(disk_filename(source_type, source_id)).to_i
+ File.ctime(disk_filename(source_type, source_id, suffix)).to_i
end
def disk_auth_filename(source_type, source_id, type)
diff --git a/app/models/laboratory.rb b/app/models/laboratory.rb
new file mode 100644
index 000000000..53e66ece0
--- /dev/null
+++ b/app/models/laboratory.rb
@@ -0,0 +1,26 @@
+class Laboratory < ApplicationRecord
+ belongs_to :school, optional: true
+
+ has_many :laboratory_users, dependent: :destroy
+ has_many :users, through: :laboratory_users, source: :user
+
+ has_one :laboratory_setting, dependent: :destroy
+
+ validates :identifier, uniqueness: { case_sensitive: false }, allow_nil: true
+
+ def site
+ rails_env = EduSetting.get('rails_env')
+ suffix = rails_env && rails_env != 'production' ? ".#{rails_env}.educoder.net" : '.educoder.net'
+
+ identifier ? "#{identifier}#{suffix}" : ''
+ end
+
+ def self.find_by_subdomain(subdomain)
+ return if subdomain.blank?
+
+ rails_env = EduSetting.get('rails_env')
+ subdomain = subdomain.slice(0, subdomain.size - rails_env.size - 1) if subdomain.end_with?(rails_env) # winse.dev => winse
+
+ find_by_identifier(subdomain)
+ end
+end
\ No newline at end of file
diff --git a/app/models/laboratory_setting.rb b/app/models/laboratory_setting.rb
new file mode 100644
index 000000000..32848dca2
--- /dev/null
+++ b/app/models/laboratory_setting.rb
@@ -0,0 +1,54 @@
+class LaboratorySetting < ApplicationRecord
+ belongs_to :laboratory
+
+ serialize :config, JSON
+
+ %i[name navbar footer].each do |method_name|
+ define_method method_name do
+ config&.[](method_name.to_s)
+ end
+
+ define_method "#{method_name}=" do |value|
+ self.config ||= {}
+ config.[]=(method_name.to_s, value)
+ end
+ end
+
+ def login_logo_url
+ logo_url('login')
+ end
+
+ def nav_logo_url
+ logo_url('nav')
+ end
+
+ def tab_logo_url
+ logo_url('tab')
+ end
+
+ def default_navbar
+ self.class.default_config[:navbar]
+ end
+
+ private
+
+ def logo_url(type)
+ return nil unless Util::FileManage.exists?(self, type)
+ Util::FileManage.source_disk_file_url(self, type)
+ end
+
+ def self.default_config
+ {
+ name: nil,
+ navbar: [
+ { 'name' => '实践课程', 'link' => '/paths', 'hidden' => false },
+ { 'name' => '翻转课堂', 'link' => '/courses', 'hidden' => false },
+ { 'name' => '实现项目', 'link' => '/shixuns', 'hidden' => false },
+ { 'name' => '在线竞赛', 'link' => '/competitions', 'hidden' => false },
+ { 'name' => '教学案例', 'link' => '/moop_cases', 'hidden' => false },
+ { 'name' => '交流问答', 'link' => '/forums', 'hidden' => false },
+ ],
+ footer: nil
+ }
+ end
+end
\ No newline at end of file
diff --git a/app/models/laboratory_user.rb b/app/models/laboratory_user.rb
new file mode 100644
index 000000000..be6c0c4dd
--- /dev/null
+++ b/app/models/laboratory_user.rb
@@ -0,0 +1,4 @@
+class LaboratoryUser < ApplicationRecord
+ belongs_to :laboratory
+ belongs_to :user
+end
\ No newline at end of file
diff --git a/app/queries/admins/laboratory_query.rb b/app/queries/admins/laboratory_query.rb
new file mode 100644
index 000000000..21020216b
--- /dev/null
+++ b/app/queries/admins/laboratory_query.rb
@@ -0,0 +1,23 @@
+class Admins::LaboratoryQuery < ApplicationQuery
+ include CustomSortable
+
+ attr_reader :params
+
+ sort_columns :id, default_by: :id, default_direction: :desc
+
+ def initialize(params)
+ @params = params
+ end
+
+ def call
+ laboratories = Laboratory.all
+
+ keyword = strip_param(:keyword)
+ if keyword.present?
+ like_sql = 'schools.name LIKE :keyword OR laboratories.identifier LIKE :keyword'
+ laboratories = laboratories.joins(:school).where(like_sql, keyword: "%#{keyword}%")
+ end
+
+ custom_sort laboratories, params[:sort_by], params[:sort_direction]
+ end
+end
\ No newline at end of file
diff --git a/app/services/admins/add_laboratory_user_service.rb b/app/services/admins/add_laboratory_user_service.rb
new file mode 100644
index 000000000..16df30880
--- /dev/null
+++ b/app/services/admins/add_laboratory_user_service.rb
@@ -0,0 +1,19 @@
+class Admins::AddLaboratoryUserService < ApplicationService
+ attr_reader :laboratory, :params
+
+ def initialize(laboratory, params)
+ @laboratory = laboratory
+ @params = params
+ end
+
+ def call
+ columns = %i[]
+ LaboratoryUser.bulk_insert(*columns) do |worker|
+ Array.wrap(params[:user_ids]).compact.each do |user_id|
+ next if laboratory.laboratory_users.exists?(user_id: user_id)
+
+ worker.add(laboratory_id: laboratory.id, user_id: user_id)
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/services/admins/create_laboratory_service.rb b/app/services/admins/create_laboratory_service.rb
new file mode 100644
index 000000000..98300d5af
--- /dev/null
+++ b/app/services/admins/create_laboratory_service.rb
@@ -0,0 +1,20 @@
+class Admins::CreateLaboratoryService < ApplicationService
+ Error = Class.new(StandardError)
+
+ attr_reader :params
+
+ def initialize(params)
+ @params = params
+ end
+
+ def call
+ raise Error, '单位不能为空' if params[:school_id].blank?
+ raise Error, '该单位已存在' if Laboratory.exists?(school_id: params[:school_id])
+
+ ActiveRecord::Base.transaction do
+ laboratory = Laboratory.create!(school_id: params[:school_id])
+
+ laboratory.create_laboratory_setting!
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/services/admins/save_laboratory_setting_service.rb b/app/services/admins/save_laboratory_setting_service.rb
new file mode 100644
index 000000000..00e202cd9
--- /dev/null
+++ b/app/services/admins/save_laboratory_setting_service.rb
@@ -0,0 +1,51 @@
+class Admins::SaveLaboratorySettingService < ApplicationService
+ attr_reader :laboratory, :laboratory_setting, :params
+
+ def initialize(laboratory, params)
+ @params = params
+ @laboratory = laboratory
+ @laboratory_setting = laboratory.laboratory_setting
+ end
+
+ def call
+ ActiveRecord::Base.transaction do
+ laboratory.identifier = strip params[:identifier]
+ laboratory_setting.name = strip params[:name]
+ laboratory_setting.navbar = navbar_config
+ laboratory_setting.footer = strip params[:footer]
+
+ laboratory.save!
+ laboratory_setting.save!
+
+ deal_logo_file
+ end
+
+ laboratory
+ end
+
+ private
+
+ def navbar_config
+ params[:navbar].map do |nav|
+ hash = {}
+ hash[:name] = strip nav[:name]
+ hash[:link] = strip nav[:link]
+ hash[:hidden] = nav[:hidden].to_s == 0
+ hash
+ end
+ end
+
+ def deal_logo_file
+ save_logo_file(params[:nav_logo], 'nav')
+ save_logo_file(params[:login_logo], 'login')
+ save_logo_file(params[:tab_logo], 'tab')
+ end
+
+ def save_logo_file(file, type)
+ return unless file.present? && file.is_a?(ActionDispatch::Http::UploadedFile)
+
+ file_path = Util::FileManage.source_disk_filename(laboratory_setting, type)
+ File.delete(file_path) if File.exist?(file_path) # 删除之前的文件
+ Util.write_file(file, file_path)
+ end
+end
\ No newline at end of file
diff --git a/app/services/application_service.rb b/app/services/application_service.rb
index c6f66c098..8b3819017 100644
--- a/app/services/application_service.rb
+++ b/app/services/application_service.rb
@@ -1,3 +1,9 @@
class ApplicationService
include Callable
+
+ private
+
+ def strip(str)
+ str.to_s.strip.presence
+ end
end
\ No newline at end of file
diff --git a/app/views/admins/laboratories/index.html.erb b/app/views/admins/laboratories/index.html.erb
new file mode 100644
index 000000000..012eed792
--- /dev/null
+++ b/app/views/admins/laboratories/index.html.erb
@@ -0,0 +1,19 @@
+<% define_admin_breadcrumbs do %>
+ <% add_admin_breadcrumb('云上实验室') %>
+<% end %>
+
+
+ <%= form_tag(admins_laboratories_path(unsafe_params), method: :get, class: 'form-inline search-form flex-1', remote: true) do %>
+ <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-6 col-md-4 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-laboratory-modal' } %>
+
+
+
+ <%= render(partial: 'admins/laboratories/shared/list', locals: { laboratories: @laboratories }) %>
+
+
+<%= render 'admins/laboratories/shared/create_laboratory_modal' %>
+<%= render 'admins/laboratories/shared/add_laboratory_user_modal' %>
\ No newline at end of file
diff --git a/app/views/admins/laboratories/index.js.erb b/app/views/admins/laboratories/index.js.erb
new file mode 100644
index 000000000..dc17c6a6d
--- /dev/null
+++ b/app/views/admins/laboratories/index.js.erb
@@ -0,0 +1 @@
+$('.laboratory-list-container').html("<%= j(render partial: 'admins/laboratories/shared/list', locals: { laboratories: @laboratories }) %>");
\ No newline at end of file
diff --git a/app/views/admins/laboratories/shared/_add_laboratory_user_modal.html.erb b/app/views/admins/laboratories/shared/_add_laboratory_user_modal.html.erb
new file mode 100644
index 000000000..a13565cd6
--- /dev/null
+++ b/app/views/admins/laboratories/shared/_add_laboratory_user_modal.html.erb
@@ -0,0 +1,30 @@
+
\ No newline at end of file
diff --git a/app/views/admins/laboratories/shared/_create_laboratory_modal.html.erb b/app/views/admins/laboratories/shared/_create_laboratory_modal.html.erb
new file mode 100644
index 000000000..0a77477d3
--- /dev/null
+++ b/app/views/admins/laboratories/shared/_create_laboratory_modal.html.erb
@@ -0,0 +1,28 @@
+
\ No newline at end of file
diff --git a/app/views/admins/laboratories/shared/_laboratory_item.html.erb b/app/views/admins/laboratories/shared/_laboratory_item.html.erb
new file mode 100644
index 000000000..5dd97b549
--- /dev/null
+++ b/app/views/admins/laboratories/shared/_laboratory_item.html.erb
@@ -0,0 +1,40 @@
+<% school = laboratory.school %>
+<%= school&.name || 'EduCoder主站' %> |
+
+ <% if laboratory.identifier %>
+ <%= link_to laboratory.site, "https://#{laboratory.site}", target: '_blank' %>
+ <% else %>
+ --
+ <% end %>
+ |
+
+ <% if school && school.identifier.present? %>
+ <%= link_to school.identifier.to_s, statistics_college_path(school.identifier), target: '_blank' %>
+ <% else %>
+ --
+ <% end %>
+ |
+
+
+ <% laboratory.users.each do |user| %>
+
+ <%= link_to user.real_name, "/users/#{user.login}", target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } %>
+ <%= link_to(admins_laboratory_laboratory_user_path(laboratory, user_id: user.id),
+ method: :delete, remote: true, class: 'ml-1 delete-laboratory-user-action',
+ data: { confirm: '确认删除吗?' }) do %>
+
+ <% end %>
+
+ <% end %>
+
+ |
+<%= laboratory.created_at.strftime('%Y-%m-%d %H:%M') %> |
+
+ <%= link_to '定制', admins_laboratory_laboratory_setting_path(laboratory) %>
+
+ <% if school.present? && laboratory.id != 1 %>
+ <%= javascript_void_link '添加管理员', class: 'action', data: { laboratory_id: laboratory.id, toggle: 'modal', target: '.admin-add-laboratory-user-modal' } %>
+
+ <%= delete_link '删除', admins_laboratory_path(laboratory, element: ".laboratory-item-#{laboratory.id}"), class: 'delete-laboratory-action' %>
+ <% end %>
+ |
\ No newline at end of file
diff --git a/app/views/admins/laboratories/shared/_list.html.erb b/app/views/admins/laboratories/shared/_list.html.erb
new file mode 100644
index 000000000..33a47eed7
--- /dev/null
+++ b/app/views/admins/laboratories/shared/_list.html.erb
@@ -0,0 +1,25 @@
+
+
+
+ 单位名称 |
+ 域名 |
+ 统计链接 |
+ 管理员 |
+ <%= sort_tag('创建时间', name: 'id', path: admins_laboratories_path) %> |
+ 操作 |
+
+
+
+ <% if laboratories.present? %>
+ <% laboratories.each do |laboratory| %>
+
+ <%= render 'admins/laboratories/shared/laboratory_item', laboratory: laboratory %>
+
+ <% end %>
+ <% else %>
+ <%= render 'admins/shared/no_data_for_table' %>
+ <% end %>
+
+
+
+<%= render partial: 'admins/shared/paginate', locals: { objects: laboratories } %>
\ 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
new file mode 100644
index 000000000..120bba6cb
--- /dev/null
+++ b/app/views/admins/laboratory_settings/show.html.erb
@@ -0,0 +1,131 @@
+<% define_admin_breadcrumbs do %>
+ <% add_admin_breadcrumb('云上实验室', admins_laboratories_path) %>
+ <% add_admin_breadcrumb('单位定制') %>
+<% end %>
+
+
+ <%= simple_form_for(@laboratory, url: admins_laboratory_laboratory_setting_path(@laboratory), method: 'patch', html: { enctype: 'multipart/form-data' }) do |f| %>
+ <% setting = @laboratory.laboratory_setting %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <%= javascript_void_link '保存', class: 'btn btn-primary mr-3 px-4 submit-btn' %>
+ <%= link_to '取消', admins_laboratories_path, class: 'btn btn-secondary px-4' %>
+
+ <% end %>
+
\ No newline at end of file
diff --git a/app/views/admins/laboratory_users/create.js.erb b/app/views/admins/laboratory_users/create.js.erb
new file mode 100644
index 000000000..f43fd7887
--- /dev/null
+++ b/app/views/admins/laboratory_users/create.js.erb
@@ -0,0 +1,4 @@
+$('.modal.admin-add-laboratory-user-modal').modal('hide');
+$.notify({ message: '操作成功' });
+
+$('.laboratory-list-table .laboratory-item-<%= current_laboratory.id %>').html("<%= j(render partial: 'admins/laboratories/shared/laboratory_item', locals: { laboratory: current_laboratory }) %>")
\ No newline at end of file
diff --git a/app/views/admins/laboratory_users/destroy.js.erb b/app/views/admins/laboratory_users/destroy.js.erb
new file mode 100644
index 000000000..16ef62910
--- /dev/null
+++ b/app/views/admins/laboratory_users/destroy.js.erb
@@ -0,0 +1,2 @@
+$.notify({ message: '操作成功' });
+$('.laboratory-list-container .laboratory-item-<%= current_laboratory.id %> .laboratory-user-item-<%= @laboratory_user.user_id %>').remove();
\ No newline at end of file
diff --git a/app/views/admins/shared/_sidebar.html.erb b/app/views/admins/shared/_sidebar.html.erb
index 553820e69..145910928 100644
--- a/app/views/admins/shared/_sidebar.html.erb
+++ b/app/views/admins/shared/_sidebar.html.erb
@@ -33,23 +33,13 @@
<%= sidebar_item_group('#schools-submenu', '单位管理', icon: 'building') do %>
<%= sidebar_item(admins_schools_path, '单位列表', icon: 'university', controller: 'admins-schools') %>
<%= sidebar_item(admins_departments_path, '部门列表', icon: 'sitemap', controller: 'admins-departments') %>
+ <%= sidebar_item(admins_laboratories_path, '云上实验室', icon: 'cloud', controller: 'admins-laboratories') %>
<% end %>
-
- <%#= sidebar_item_group('#course-submenu', '课堂+', icon: 'mortar-board') do %>
-
-
-
-
- <%# end %>
-
-
<%= sidebar_item_group('#user-submenu', '用户', icon: 'user') do %>
<%= sidebar_item(admins_users_path, '用户列表', icon: 'user', controller: 'admins-users') %>
-
-
<% end %>
diff --git a/app/views/settings/show.json.jbuilder b/app/views/settings/show.json.jbuilder
new file mode 100644
index 000000000..0765e303b
--- /dev/null
+++ b/app/views/settings/show.json.jbuilder
@@ -0,0 +1,12 @@
+json.setting do
+ setting = @laboratory.laboratory_setting
+
+ json.name setting.name || default_setting.name
+ json.nav_logo_url setting.nav_logo_url || default_setting.nav_logo_url
+ json.login_logo_url setting.login_logo_url || default_setting.login_logo_url
+ json.tab_logo_url setting.tab_logo_url || default_setting.tab_logo_url
+
+ json.navbar setting.navbar || default_setting.navbar
+
+ json.footer setting.footer || default_setting.footer
+end
\ No newline at end of file
diff --git a/config/admins/sidebar.yml b/config/admins/sidebar.yml
index 30af794b7..9da34a014 100644
--- a/config/admins/sidebar.yml
+++ b/config/admins/sidebar.yml
@@ -1 +1,2 @@
-admins-mirror_scripts: 'admins-mirror_repositories'
\ No newline at end of file
+admins-mirror_scripts: 'admins-mirror_repositories'
+admins-laboratory_settings: 'admins-laboratories'
\ No newline at end of file
diff --git a/config/locales/laboratories/zh-CN.yml b/config/locales/laboratories/zh-CN.yml
new file mode 100644
index 000000000..42127f0a1
--- /dev/null
+++ b/config/locales/laboratories/zh-CN.yml
@@ -0,0 +1,7 @@
+zh-CN:
+ activerecord:
+ models:
+ laboratory: ''
+ attributes:
+ laboratory:
+ identifier: '二级域名'
\ No newline at end of file
diff --git a/config/routes.rb b/config/routes.rb
index 50b61c463..4b7addc0b 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -824,6 +824,7 @@ Rails.application.routes.draw do
end
end
resource :template, only: [:show]
+ resource :setting, only: [:show]
end
namespace :admins do
@@ -953,7 +954,7 @@ Rails.application.routes.draw do
resources :choose_mirror_repositories, only: [:new, :create]
resources :schools, only: [:index, :destroy]
resources :departments, only: [:index, :create, :edit, :update, :destroy] do
- resource :department_member, only: [:create, :update, :destroy]
+ resource :department_member, only: [:create, :destroy]
post :merge, on: :collection
end
resources :myshixuns, only: [:index]
@@ -971,6 +972,10 @@ Rails.application.routes.draw do
resources :carousels, only: [:index, :create, :update, :destroy] do
post :drag, on: :collection
end
+ resources :laboratories, only: [:index, :create, :destroy] do
+ resource :laboratory_setting, only: [:show, :update]
+ resource :laboratory_user, only: [:create, :destroy]
+ end
end
resources :colleges, only: [] do
diff --git a/db/migrate/20191010011844_create_laboratories.rb b/db/migrate/20191010011844_create_laboratories.rb
new file mode 100644
index 000000000..3dfb442f0
--- /dev/null
+++ b/db/migrate/20191010011844_create_laboratories.rb
@@ -0,0 +1,12 @@
+class CreateLaboratories < ActiveRecord::Migration[5.2]
+ def change
+ create_table :laboratories do |t|
+ t.references :school
+ t.string :identifier
+
+ t.timestamps
+
+ t.index :identifier, unique: true
+ end
+ end
+end
diff --git a/db/migrate/20191010012226_create_laboratory_users.rb b/db/migrate/20191010012226_create_laboratory_users.rb
new file mode 100644
index 000000000..1b7ae762d
--- /dev/null
+++ b/db/migrate/20191010012226_create_laboratory_users.rb
@@ -0,0 +1,8 @@
+class CreateLaboratoryUsers < ActiveRecord::Migration[5.2]
+ def change
+ create_table :laboratory_users do |t|
+ t.references :laboratory
+ t.references :user
+ end
+ end
+end
diff --git a/db/migrate/20191010063403_create_laboratory_settings.rb b/db/migrate/20191010063403_create_laboratory_settings.rb
new file mode 100644
index 000000000..7f0a5f015
--- /dev/null
+++ b/db/migrate/20191010063403_create_laboratory_settings.rb
@@ -0,0 +1,9 @@
+class CreateLaboratorySettings < ActiveRecord::Migration[5.2]
+ def change
+ create_table :laboratory_settings do |t|
+ t.references :laboratory
+
+ t.text :config
+ end
+ end
+end
diff --git a/db/migrate/20191011025619_init_edu_coder_laboratory.rb b/db/migrate/20191011025619_init_edu_coder_laboratory.rb
new file mode 100644
index 000000000..831ca3985
--- /dev/null
+++ b/db/migrate/20191011025619_init_edu_coder_laboratory.rb
@@ -0,0 +1,22 @@
+class InitEduCoderLaboratory < ActiveRecord::Migration[5.2]
+ def change
+ ActiveRecord::Base.transaction do
+ laboratory = Laboratory.create!(id: 1, identifier: 'www')
+ setting = laboratory.build_laboratory_setting
+ footer = %Q{
+
+ }
+ config = setting.class.default_config.merge(name: 'EduCoder', footer: footer)
+ setting.config = config
+ setting.save!
+ end
+ end
+end
diff --git a/public/assets/.sprockets-manifest-4627fa5586ef7fed55ca286af7c028e9.json b/public/assets/.sprockets-manifest-4627fa5586ef7fed55ca286af7c028e9.json
index 52d2a4e9b..b8b49cc9d 100644
--- a/public/assets/.sprockets-manifest-4627fa5586ef7fed55ca286af7c028e9.json
+++ b/public/assets/.sprockets-manifest-4627fa5586ef7fed55ca286af7c028e9.json
@@ -1 +1 @@
-{"files":{"admin-cd9ca8bacc973ce2dbace30c97f6c40bc08e2c2ee44972f668e738e1902c0121.js":{"logical_path":"admin.js","mtime":"2019-09-11T16:20:07+08:00","size":4350881,"digest":"cd9ca8bacc973ce2dbace30c97f6c40bc08e2c2ee44972f668e738e1902c0121","integrity":"sha256-zZyousyXPOLbrOMMl/bEC8COLC7kSXL2aOc44ZAsASE="},"admin-a1b3356efe50ff4717cf22475639b5333c5354ba03fd107c9b7a8d4ae76f47aa.css":{"logical_path":"admin.css","mtime":"2019-09-11T16:20:07+08:00","size":773445,"digest":"a1b3356efe50ff4717cf22475639b5333c5354ba03fd107c9b7a8d4ae76f47aa","integrity":"sha256-obM1bv5Q/0cXzyJHVjm1MzxTVLoD/RB8m3qNSudvR6o="},"font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot":{"logical_path":"font-awesome/fontawesome-webfont.eot","mtime":"2019-08-14T17:22:43+08:00","size":165742,"digest":"7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979","integrity":"sha256-e/yrbbmdXPvxcFygU23ceFhUMsxfpBu9etDwCQM7KXk="},"font-awesome/fontawesome-webfont-2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe.woff2":{"logical_path":"font-awesome/fontawesome-webfont.woff2","mtime":"2019-08-14T17:22:43+08:00","size":77160,"digest":"2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe","integrity":"sha256-Kt78vAQefRj88tQXh53FoJmXqmTWdbejxLbOM9oT8/4="},"font-awesome/fontawesome-webfont-ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07.woff":{"logical_path":"font-awesome/fontawesome-webfont.woff","mtime":"2019-08-14T17:22:43+08:00","size":98024,"digest":"ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07","integrity":"sha256-ugxZ3rVFD1y0Gz+TYJ7i0NmVQVh33foiPoqKdTNHTwc="},"font-awesome/fontawesome-webfont-aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8.ttf":{"logical_path":"font-awesome/fontawesome-webfont.ttf","mtime":"2019-08-14T17:22:43+08:00","size":165548,"digest":"aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8","integrity":"sha256-qljzPyOaD7AvXHpsRcBD16msmgkzNYBmlOzW1O3A1qg="},"font-awesome/fontawesome-webfont-ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4.svg":{"logical_path":"font-awesome/fontawesome-webfont.svg","mtime":"2019-08-14T17:22:43+08:00","size":444379,"digest":"ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4","integrity":"sha256-rWFXkmwWIrpOHQPUePFUE2hSS/xG9R5C/g2UX37zI+Q="},"college-18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287.js":{"logical_path":"college.js","mtime":"2019-09-26T14:40:40+08:00","size":3352744,"digest":"18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287","integrity":"sha256-GPXoQAMxY06JijWswhh4FcCWwl4Kt0q6NBrpFhZs0oc="},"college-944d4273f62c7538368b9017fdd3387b5e3bea31a87873770eb231324546d4d9.css":{"logical_path":"college.css","mtime":"2019-09-11T16:20:07+08:00","size":546841,"digest":"944d4273f62c7538368b9017fdd3387b5e3bea31a87873770eb231324546d4d9","integrity":"sha256-lE1Cc/YsdTg2i5AX/dM4e1476jGoeHN3DrIxMkVG1Nk="},"logo-7ff112568709bf97f9898fe87249b7a8f200ff1f48d537d85af87215f1870423.png":{"logical_path":"logo.png","mtime":"2019-09-03T08:55:53+08:00","size":2816,"digest":"7ff112568709bf97f9898fe87249b7a8f200ff1f48d537d85af87215f1870423","integrity":"sha256-f/ESVocJv5f5iY/ockm3qPIA/x9I1TfYWvhyFfGHBCM="},"application-9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb.js":{"logical_path":"application.js","mtime":"2019-09-26T14:40:40+08:00","size":600706,"digest":"9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb","integrity":"sha256-nPvD15JZmh0N5ce4QgnhwrLmAzbw8B4Z8FgWY5GHCPs="},"application-5eb87c6e13676d0183317debce17fade27e68c4acee28c419438da15d53c94f2.css":{"logical_path":"application.css","mtime":"2019-09-11T16:20:07+08:00","size":1844002,"digest":"5eb87c6e13676d0183317debce17fade27e68c4acee28c419438da15d53c94f2","integrity":"sha256-Xrh8bhNnbQGDMX3rzhf63ifmjErO4oxBlDjaFdU8lPI="},"admin-c9e5ebe6191548550e27514196ea125cfbb402820ec125a0c9acf99d2d378fe4.js":{"logical_path":"admin.js","mtime":"2019-09-21T15:28:08+08:00","size":4382031,"digest":"c9e5ebe6191548550e27514196ea125cfbb402820ec125a0c9acf99d2d378fe4","integrity":"sha256-yeXr5hkVSFUOJ1FBluoSXPu0AoIOwSWgyaz5nS03j+Q="},"admin-59c59f8cae8bef4a8359286c985458110c9d03ea121516595c988943f4717c38.css":{"logical_path":"admin.css","mtime":"2019-09-21T14:49:04+08:00","size":840093,"digest":"59c59f8cae8bef4a8359286c985458110c9d03ea121516595c988943f4717c38","integrity":"sha256-WcWfjK6L70qDWShsmFRYEQydA+oSFRZZXJiJQ/RxfDg="},"college-38f953d6ba5b85d3fab63cb3c2bbf0d057ccc6454d07cfaafac3b06da37b8437.css":{"logical_path":"college.css","mtime":"2019-09-16T13:56:09+08:00","size":579109,"digest":"38f953d6ba5b85d3fab63cb3c2bbf0d057ccc6454d07cfaafac3b06da37b8437","integrity":"sha256-OPlT1rpbhdP6tjyzwrvw0FfMxkVNB8+q+sOwbaN7hDc="},"application-646b1158a4e8c1f13e684d6fe9025abc75f8d3ba5256e440802c0398223374f3.css":{"logical_path":"application.css","mtime":"2019-09-21T14:49:04+08:00","size":1988767,"digest":"646b1158a4e8c1f13e684d6fe9025abc75f8d3ba5256e440802c0398223374f3","integrity":"sha256-ZGsRWKTowfE+aE1v6QJavHX407pSVuRAgCwDmCIzdPM="},"admin-a47e37c0ec7cf5f22380249776d1e82d65b6b6aa272ed7389185aa200fa40751.js":{"logical_path":"admin.js","mtime":"2019-09-25T15:33:05+08:00","size":4383107,"digest":"a47e37c0ec7cf5f22380249776d1e82d65b6b6aa272ed7389185aa200fa40751","integrity":"sha256-pH43wOx89fIjgCSXdtHoLWW2tqonLtc4kYWqIA+kB1E="},"admin-432c4eac09b036c57ff1e88d902b8aa7df81164e4b419bac557cf1366c1d3ad9.js":{"logical_path":"admin.js","mtime":"2019-09-25T15:35:20+08:00","size":4383103,"digest":"432c4eac09b036c57ff1e88d902b8aa7df81164e4b419bac557cf1366c1d3ad9","integrity":"sha256-QyxOrAmwNsV/8eiNkCuKp9+BFk5LQZusVXzxNmwdOtk="},"admin-978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a.js":{"logical_path":"admin.js","mtime":"2019-09-30T14:43:41+08:00","size":4387200,"digest":"978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a","integrity":"sha256-l45c5gf3fCaBShdPSA2nmsJGwiAYaO+EZUqgO7Zye1o="},"admin-896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7.css":{"logical_path":"admin.css","mtime":"2019-09-30T14:43:41+08:00","size":842269,"digest":"896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7","integrity":"sha256-iWKB9HMXIrDAhNuxryHQ80pbwULViv9Xs5GGSrcd3Kc="},"application-97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd.css":{"logical_path":"application.css","mtime":"2019-09-30T14:43:41+08:00","size":1993118,"digest":"97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd","integrity":"sha256-l/MT6bt9JUdmSffXIVlZz0IUgP0KN4XRlWlTv5Sh6L0="}},"assets":{"admin.js":"admin-978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a.js","admin.css":"admin-896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7.css","font-awesome/fontawesome-webfont.eot":"font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot","font-awesome/fontawesome-webfont.woff2":"font-awesome/fontawesome-webfont-2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe.woff2","font-awesome/fontawesome-webfont.woff":"font-awesome/fontawesome-webfont-ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07.woff","font-awesome/fontawesome-webfont.ttf":"font-awesome/fontawesome-webfont-aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8.ttf","font-awesome/fontawesome-webfont.svg":"font-awesome/fontawesome-webfont-ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4.svg","college.js":"college-18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287.js","college.css":"college-38f953d6ba5b85d3fab63cb3c2bbf0d057ccc6454d07cfaafac3b06da37b8437.css","logo.png":"logo-7ff112568709bf97f9898fe87249b7a8f200ff1f48d537d85af87215f1870423.png","application.js":"application-9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb.js","application.css":"application-97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd.css"}}
\ No newline at end of file
+{"files":{"admin-cd9ca8bacc973ce2dbace30c97f6c40bc08e2c2ee44972f668e738e1902c0121.js":{"logical_path":"admin.js","mtime":"2019-09-11T16:20:07+08:00","size":4350881,"digest":"cd9ca8bacc973ce2dbace30c97f6c40bc08e2c2ee44972f668e738e1902c0121","integrity":"sha256-zZyousyXPOLbrOMMl/bEC8COLC7kSXL2aOc44ZAsASE="},"admin-a1b3356efe50ff4717cf22475639b5333c5354ba03fd107c9b7a8d4ae76f47aa.css":{"logical_path":"admin.css","mtime":"2019-09-11T16:20:07+08:00","size":773445,"digest":"a1b3356efe50ff4717cf22475639b5333c5354ba03fd107c9b7a8d4ae76f47aa","integrity":"sha256-obM1bv5Q/0cXzyJHVjm1MzxTVLoD/RB8m3qNSudvR6o="},"font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot":{"logical_path":"font-awesome/fontawesome-webfont.eot","mtime":"2019-08-14T17:22:43+08:00","size":165742,"digest":"7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979","integrity":"sha256-e/yrbbmdXPvxcFygU23ceFhUMsxfpBu9etDwCQM7KXk="},"font-awesome/fontawesome-webfont-2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe.woff2":{"logical_path":"font-awesome/fontawesome-webfont.woff2","mtime":"2019-08-14T17:22:43+08:00","size":77160,"digest":"2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe","integrity":"sha256-Kt78vAQefRj88tQXh53FoJmXqmTWdbejxLbOM9oT8/4="},"font-awesome/fontawesome-webfont-ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07.woff":{"logical_path":"font-awesome/fontawesome-webfont.woff","mtime":"2019-08-14T17:22:43+08:00","size":98024,"digest":"ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07","integrity":"sha256-ugxZ3rVFD1y0Gz+TYJ7i0NmVQVh33foiPoqKdTNHTwc="},"font-awesome/fontawesome-webfont-aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8.ttf":{"logical_path":"font-awesome/fontawesome-webfont.ttf","mtime":"2019-08-14T17:22:43+08:00","size":165548,"digest":"aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8","integrity":"sha256-qljzPyOaD7AvXHpsRcBD16msmgkzNYBmlOzW1O3A1qg="},"font-awesome/fontawesome-webfont-ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4.svg":{"logical_path":"font-awesome/fontawesome-webfont.svg","mtime":"2019-08-14T17:22:43+08:00","size":444379,"digest":"ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4","integrity":"sha256-rWFXkmwWIrpOHQPUePFUE2hSS/xG9R5C/g2UX37zI+Q="},"college-18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287.js":{"logical_path":"college.js","mtime":"2019-09-26T14:40:40+08:00","size":3352744,"digest":"18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287","integrity":"sha256-GPXoQAMxY06JijWswhh4FcCWwl4Kt0q6NBrpFhZs0oc="},"college-944d4273f62c7538368b9017fdd3387b5e3bea31a87873770eb231324546d4d9.css":{"logical_path":"college.css","mtime":"2019-09-11T16:20:07+08:00","size":546841,"digest":"944d4273f62c7538368b9017fdd3387b5e3bea31a87873770eb231324546d4d9","integrity":"sha256-lE1Cc/YsdTg2i5AX/dM4e1476jGoeHN3DrIxMkVG1Nk="},"logo-7ff112568709bf97f9898fe87249b7a8f200ff1f48d537d85af87215f1870423.png":{"logical_path":"logo.png","mtime":"2019-09-03T08:55:53+08:00","size":2816,"digest":"7ff112568709bf97f9898fe87249b7a8f200ff1f48d537d85af87215f1870423","integrity":"sha256-f/ESVocJv5f5iY/ockm3qPIA/x9I1TfYWvhyFfGHBCM="},"application-9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb.js":{"logical_path":"application.js","mtime":"2019-09-26T14:40:40+08:00","size":600706,"digest":"9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb","integrity":"sha256-nPvD15JZmh0N5ce4QgnhwrLmAzbw8B4Z8FgWY5GHCPs="},"application-5eb87c6e13676d0183317debce17fade27e68c4acee28c419438da15d53c94f2.css":{"logical_path":"application.css","mtime":"2019-09-11T16:20:07+08:00","size":1844002,"digest":"5eb87c6e13676d0183317debce17fade27e68c4acee28c419438da15d53c94f2","integrity":"sha256-Xrh8bhNnbQGDMX3rzhf63ifmjErO4oxBlDjaFdU8lPI="},"admin-c9e5ebe6191548550e27514196ea125cfbb402820ec125a0c9acf99d2d378fe4.js":{"logical_path":"admin.js","mtime":"2019-09-21T15:28:08+08:00","size":4382031,"digest":"c9e5ebe6191548550e27514196ea125cfbb402820ec125a0c9acf99d2d378fe4","integrity":"sha256-yeXr5hkVSFUOJ1FBluoSXPu0AoIOwSWgyaz5nS03j+Q="},"admin-59c59f8cae8bef4a8359286c985458110c9d03ea121516595c988943f4717c38.css":{"logical_path":"admin.css","mtime":"2019-09-21T14:49:04+08:00","size":840093,"digest":"59c59f8cae8bef4a8359286c985458110c9d03ea121516595c988943f4717c38","integrity":"sha256-WcWfjK6L70qDWShsmFRYEQydA+oSFRZZXJiJQ/RxfDg="},"college-38f953d6ba5b85d3fab63cb3c2bbf0d057ccc6454d07cfaafac3b06da37b8437.css":{"logical_path":"college.css","mtime":"2019-09-16T13:56:09+08:00","size":579109,"digest":"38f953d6ba5b85d3fab63cb3c2bbf0d057ccc6454d07cfaafac3b06da37b8437","integrity":"sha256-OPlT1rpbhdP6tjyzwrvw0FfMxkVNB8+q+sOwbaN7hDc="},"application-646b1158a4e8c1f13e684d6fe9025abc75f8d3ba5256e440802c0398223374f3.css":{"logical_path":"application.css","mtime":"2019-09-21T14:49:04+08:00","size":1988767,"digest":"646b1158a4e8c1f13e684d6fe9025abc75f8d3ba5256e440802c0398223374f3","integrity":"sha256-ZGsRWKTowfE+aE1v6QJavHX407pSVuRAgCwDmCIzdPM="},"admin-a47e37c0ec7cf5f22380249776d1e82d65b6b6aa272ed7389185aa200fa40751.js":{"logical_path":"admin.js","mtime":"2019-09-25T15:33:05+08:00","size":4383107,"digest":"a47e37c0ec7cf5f22380249776d1e82d65b6b6aa272ed7389185aa200fa40751","integrity":"sha256-pH43wOx89fIjgCSXdtHoLWW2tqonLtc4kYWqIA+kB1E="},"admin-432c4eac09b036c57ff1e88d902b8aa7df81164e4b419bac557cf1366c1d3ad9.js":{"logical_path":"admin.js","mtime":"2019-09-25T15:35:20+08:00","size":4383103,"digest":"432c4eac09b036c57ff1e88d902b8aa7df81164e4b419bac557cf1366c1d3ad9","integrity":"sha256-QyxOrAmwNsV/8eiNkCuKp9+BFk5LQZusVXzxNmwdOtk="},"admin-978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a.js":{"logical_path":"admin.js","mtime":"2019-09-30T14:43:41+08:00","size":4387200,"digest":"978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a","integrity":"sha256-l45c5gf3fCaBShdPSA2nmsJGwiAYaO+EZUqgO7Zye1o="},"admin-896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7.css":{"logical_path":"admin.css","mtime":"2019-09-30T14:43:41+08:00","size":842269,"digest":"896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7","integrity":"sha256-iWKB9HMXIrDAhNuxryHQ80pbwULViv9Xs5GGSrcd3Kc="},"application-97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd.css":{"logical_path":"application.css","mtime":"2019-09-30T14:43:41+08:00","size":1993118,"digest":"97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd","integrity":"sha256-l/MT6bt9JUdmSffXIVlZz0IUgP0KN4XRlWlTv5Sh6L0="},"admin-2cdb23442fa735025385b88f2900df04fef38b61530041a6dbe375ef0f0ae888.js":{"logical_path":"admin.js","mtime":"2019-10-11T14:38:33+08:00","size":4394616,"digest":"2cdb23442fa735025385b88f2900df04fef38b61530041a6dbe375ef0f0ae888","integrity":"sha256-LNsjRC+nNQJThbiPKQDfBP7zi2FTAEGm2+N17w8K6Ig="},"admin-2c2854b9a02158ded5a809aaf7144a8630b10354ab4e56fecc4dffcc713796cc.css":{"logical_path":"admin.css","mtime":"2019-10-10T17:12:05+08:00","size":846514,"digest":"2c2854b9a02158ded5a809aaf7144a8630b10354ab4e56fecc4dffcc713796cc","integrity":"sha256-LChUuaAhWN7VqAmq9xRKhjCxA1SrTlb+zE3/zHE3lsw="},"application-50059ae929866043b47015128702fcfba53d32a2df148e64e1d961c10651c6af.css":{"logical_path":"application.css","mtime":"2019-10-10T17:12:05+08:00","size":2001607,"digest":"50059ae929866043b47015128702fcfba53d32a2df148e64e1d961c10651c6af","integrity":"sha256-UAWa6SmGYEO0cBUShwL8+6U9MqLfFI5k4dlhwQZRxq8="}},"assets":{"admin.js":"admin-2cdb23442fa735025385b88f2900df04fef38b61530041a6dbe375ef0f0ae888.js","admin.css":"admin-2c2854b9a02158ded5a809aaf7144a8630b10354ab4e56fecc4dffcc713796cc.css","font-awesome/fontawesome-webfont.eot":"font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot","font-awesome/fontawesome-webfont.woff2":"font-awesome/fontawesome-webfont-2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe.woff2","font-awesome/fontawesome-webfont.woff":"font-awesome/fontawesome-webfont-ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07.woff","font-awesome/fontawesome-webfont.ttf":"font-awesome/fontawesome-webfont-aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8.ttf","font-awesome/fontawesome-webfont.svg":"font-awesome/fontawesome-webfont-ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4.svg","college.js":"college-18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287.js","college.css":"college-38f953d6ba5b85d3fab63cb3c2bbf0d057ccc6454d07cfaafac3b06da37b8437.css","logo.png":"logo-7ff112568709bf97f9898fe87249b7a8f200ff1f48d537d85af87215f1870423.png","application.js":"application-9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb.js","application.css":"application-50059ae929866043b47015128702fcfba53d32a2df148e64e1d961c10651c6af.css"}}
\ No newline at end of file
diff --git a/public/assets/admin-896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7.css b/public/assets/admin-2c2854b9a02158ded5a809aaf7144a8630b10354ab4e56fecc4dffcc713796cc.css
similarity index 99%
rename from public/assets/admin-896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7.css
rename to public/assets/admin-2c2854b9a02158ded5a809aaf7144a8630b10354ab4e56fecc4dffcc713796cc.css
index fe1e75888..5b6bef77a 100644
--- a/public/assets/admin-896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7.css
+++ b/public/assets/admin-2c2854b9a02158ded5a809aaf7144a8630b10354ab4e56fecc4dffcc713796cc.css
@@ -25274,6 +25274,114 @@ input.form-control {
color: #6c757d;
}
+/* line 4, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratories-index-page .laboratory-list-table .member-container .laboratory-user {
+ display: -webkit-box;
+ display: flex;
+ -webkit-box-pack: center;
+ justify-content: center;
+ flex-wrap: wrap;
+}
+
+/* line 9, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratories-index-page .laboratory-list-table .member-container .laboratory-user .laboratory-user-item {
+ display: -webkit-box;
+ display: flex;
+ -webkit-box-align: center;
+ 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;
+}
+
+/* line 27, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item {
+ display: -webkit-box;
+ display: flex;
+}
+
+/* line 30, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-img, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-img {
+ display: block;
+ width: 80px;
+ height: 80px;
+}
+
+/* line 36, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-upload, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-upload {
+ cursor: pointer;
+ position: absolute;
+ top: 0;
+ width: 80px;
+ height: 80px;
+ background: #F5F5F5;
+ border: 1px solid #E5E5E5;
+}
+
+/* line 45, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-upload::before, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-upload::before {
+ content: '';
+ position: absolute;
+ top: 27px;
+ left: 39px;
+ width: 2px;
+ height: 26px;
+ background: #E5E5E5;
+}
+
+/* line 55, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-upload::after, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-upload::after {
+ content: '';
+ position: absolute;
+ top: 39px;
+ left: 27px;
+ width: 26px;
+ height: 2px;
+ background: #E5E5E5;
+}
+
+/* line 66, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-left, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-left {
+ position: relative;
+ width: 80px;
+ height: 80px;
+}
+
+/* line 72, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-left.has-img .logo-item-upload, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-left.has-img .logo-item-upload {
+ display: none;
+}
+
+/* line 77, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-left.has-img:hover .logo-item-upload, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-left.has-img:hover .logo-item-upload {
+ display: block;
+ background: rgba(145, 145, 145, 0.8);
+}
+
+/* line 85, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-right, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-right {
+ display: -webkit-box;
+ display: flex;
+ -webkit-box-orient: vertical;
+ -webkit-box-direction: normal;
+ flex-direction: column;
+ -webkit-box-pack: justify;
+ justify-content: space-between;
+ color: #777777;
+ font-size: 12px;
+}
+
+/* line 93, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-title, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-title {
+ color: #23272B;
+ font-size: 14px;
+}
+
/* line 4, app/assets/stylesheets/admins/library_applies.scss */
.admins-library-applies-index-page .library-applies-list-container span.apply-status-agreed {
color: #28a745;
diff --git a/public/assets/admin-896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7.css.gz b/public/assets/admin-2c2854b9a02158ded5a809aaf7144a8630b10354ab4e56fecc4dffcc713796cc.css.gz
similarity index 78%
rename from public/assets/admin-896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7.css.gz
rename to public/assets/admin-2c2854b9a02158ded5a809aaf7144a8630b10354ab4e56fecc4dffcc713796cc.css.gz
index 581305db5..bfac93da9 100644
Binary files a/public/assets/admin-896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7.css.gz and b/public/assets/admin-2c2854b9a02158ded5a809aaf7144a8630b10354ab4e56fecc4dffcc713796cc.css.gz differ
diff --git a/public/assets/admin-978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a.js b/public/assets/admin-2cdb23442fa735025385b88f2900df04fef38b61530041a6dbe375ef0f0ae888.js
similarity index 99%
rename from public/assets/admin-978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a.js
rename to public/assets/admin-2cdb23442fa735025385b88f2900df04fef38b61530041a6dbe375ef0f0ae888.js
index 49e1025c7..6203a3ce8 100644
--- a/public/assets/admin-978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a.js
+++ b/public/assets/admin-2cdb23442fa735025385b88f2900df04fef38b61530041a6dbe375ef0f0ae888.js
@@ -134528,6 +134528,256 @@ $(document).on('turbolinks:load', function() {
}
})
;
+$(document).on('turbolinks:load', function() {
+ if ($('body.admins-laboratory-settings-show-page, body.admins-laboratory-settings-update-page').length > 0) {
+ var $container = $('.edit-laboratory-setting-container');
+ var $form = $container.find('.edit_laboratory');
+
+ $('.logo-item-left').on("change", 'input[type="file"]', function () {
+ var $fileInput = $(this);
+ var file = this.files[0];
+ var imageType = /image.*/;
+ if (file && file.type.match(imageType)) {
+ var reader = new FileReader();
+ reader.onload = function () {
+ var $box = $fileInput.parent();
+ $box.find('img').attr('src', reader.result).css('display', 'block');
+ $box.addClass('has-img');
+ };
+ reader.readAsDataURL(file);
+ } else {
+ }
+ });
+
+ createMDEditor('laboratory-footer-editor', { height: 200, placeholder: '请输入备案信息' });
+
+ $form.validate({
+ errorElement: 'span',
+ errorClass: 'danger text-danger',
+ errorPlacement:function(error,element){
+ if(element.parent().hasClass("input-group")){
+ element.parent().after(error);
+ }else{
+ element.after(error)
+ }
+ },
+ rules: {
+ identifier: {
+ required: true,
+ checkSite: true
+ },
+ name: {
+ required: true
+ }
+ }
+ });
+ $.validator.addMethod("checkSite",function(value,element,params){
+ var checkSite = /^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$/;
+ return this.optional(element)||(checkSite.test(value + '.educoder.com'));
+ },"域名不合法!");
+
+ $form.on('click', '.submit-btn', function(){
+ $form.find('.submit-btn').attr('disabled', 'disabled');
+ $form.find('.error').html('');
+ var valid = $form.valid();
+
+ $('input[name="navbar[][name]"]').each(function(_, e){
+ var $ele = $(e);
+ if($ele.val() === undefined || $ele.val().length === 0){
+ $ele.addClass('danger text-danger');
+ valid = false;
+ } else {
+ $ele.removeClass('danger text-danger');
+ }
+ });
+
+ if(!valid) return;
+ $.ajax({
+ method: 'PATCH',
+ dataType: 'json',
+ url: $form.attr('action'),
+ data: new FormData($form[0]),
+ processData: false,
+ contentType: false,
+ success: function(data){
+ $.notify({ message: '保存成功' });
+ window.location.reload();
+ },
+ error: function(res){
+ var data = res.responseJSON;
+ $form.find('.error').html(data.message);
+ },
+ complete: function(){
+ $form.find('.submit-btn').attr('disabled', false);
+ }
+ });
+ })
+ }
+});
+$(document).on('turbolinks:load', function() {
+ if ($('body.admins-laboratories-index-page').length > 0) {
+ var $searchContainer = $('.laboratory-list-form');
+ var $searchForm = $searchContainer.find('form.search-form');
+ var $list = $('.laboratory-list-container');
+
+ // ============== 新建 ===============
+ var $modal = $('.modal.admin-create-laboratory-modal');
+ var $form = $modal.find('form.admin-create-laboratory-form');
+ var $schoolSelect = $modal.find('.school-select');
+
+ $form.validate({
+ errorElement: 'span',
+ errorClass: 'danger text-danger',
+ rules: {
+ school_id: {
+ required: true
+ }
+ },
+ messages: {
+ school_id: {
+ required: '请选择所属单位'
+ }
+ }
+ });
+
+ // modal ready fire
+ $modal.on('show.bs.modal', function () {
+ $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-laboratory-user-modal');
+ var $addMemberForm = $addMemberModal.find('.admin-add-laboratory-user-form');
+ var $memberSelect = $addMemberModal.find('.laboratory-user-select');
+ var $laboratoryIdInput = $addMemberForm.find('input[name="laboratory_id"]')
+
+ $addMemberModal.on('show.bs.modal', function(event){
+ var $link = $(event.relatedTarget);
+ var laboratoryId = $link.data('laboratory-id');
+ $laboratoryIdInput.val(laboratoryId);
+
+ $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 laboratoryId = $laboratoryIdInput.val();
+ var memberIds = $memberSelect.val();
+ if (laboratoryId && memberIds && memberIds.length > 0) {
+ $.ajax({
+ method: 'POST',
+ dataType: 'script',
+ url: '/admins/laboratories/' + laboratoryId + '/laboratory_user',
+ data: { user_ids: memberIds }
+ });
+ } else {
+ $addMemberModal.modal('hide');
+ }
+ });
+ }
+});
$(document).on('turbolinks:load', function() {
if ($('body.admins-library-applies-index-page').length > 0) {
var $searchFrom = $('.library-applies-list-form');
diff --git a/public/assets/admin-978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a.js.gz b/public/assets/admin-2cdb23442fa735025385b88f2900df04fef38b61530041a6dbe375ef0f0ae888.js.gz
similarity index 98%
rename from public/assets/admin-978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a.js.gz
rename to public/assets/admin-2cdb23442fa735025385b88f2900df04fef38b61530041a6dbe375ef0f0ae888.js.gz
index 13e36b4f6..492769a78 100644
Binary files a/public/assets/admin-978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a.js.gz and b/public/assets/admin-2cdb23442fa735025385b88f2900df04fef38b61530041a6dbe375ef0f0ae888.js.gz differ
diff --git a/public/assets/application-97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd.css b/public/assets/application-50059ae929866043b47015128702fcfba53d32a2df148e64e1d961c10651c6af.css
similarity index 99%
rename from public/assets/application-97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd.css
rename to public/assets/application-50059ae929866043b47015128702fcfba53d32a2df148e64e1d961c10651c6af.css
index c1d8ae955..f62f2f56d 100644
--- a/public/assets/application-97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd.css
+++ b/public/assets/application-50059ae929866043b47015128702fcfba53d32a2df148e64e1d961c10651c6af.css
@@ -25274,6 +25274,114 @@ input.form-control {
color: #6c757d;
}
+/* line 4, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratories-index-page .laboratory-list-table .member-container .laboratory-user {
+ display: -webkit-box;
+ display: flex;
+ -webkit-box-pack: center;
+ justify-content: center;
+ flex-wrap: wrap;
+}
+
+/* line 9, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratories-index-page .laboratory-list-table .member-container .laboratory-user .laboratory-user-item {
+ display: -webkit-box;
+ display: flex;
+ -webkit-box-align: center;
+ 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;
+}
+
+/* line 27, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item {
+ display: -webkit-box;
+ display: flex;
+}
+
+/* line 30, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-img, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-img {
+ display: block;
+ width: 80px;
+ height: 80px;
+}
+
+/* line 36, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-upload, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-upload {
+ cursor: pointer;
+ position: absolute;
+ top: 0;
+ width: 80px;
+ height: 80px;
+ background: #F5F5F5;
+ border: 1px solid #E5E5E5;
+}
+
+/* line 45, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-upload::before, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-upload::before {
+ content: '';
+ position: absolute;
+ top: 27px;
+ left: 39px;
+ width: 2px;
+ height: 26px;
+ background: #E5E5E5;
+}
+
+/* line 55, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-upload::after, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-upload::after {
+ content: '';
+ position: absolute;
+ top: 39px;
+ left: 27px;
+ width: 26px;
+ height: 2px;
+ background: #E5E5E5;
+}
+
+/* line 66, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-left, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-left {
+ position: relative;
+ width: 80px;
+ height: 80px;
+}
+
+/* line 72, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-left.has-img .logo-item-upload, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-left.has-img .logo-item-upload {
+ display: none;
+}
+
+/* line 77, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-left.has-img:hover .logo-item-upload, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-left.has-img:hover .logo-item-upload {
+ display: block;
+ background: rgba(145, 145, 145, 0.8);
+}
+
+/* line 85, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-right, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-right {
+ display: -webkit-box;
+ display: flex;
+ -webkit-box-orient: vertical;
+ -webkit-box-direction: normal;
+ flex-direction: column;
+ -webkit-box-pack: justify;
+ justify-content: space-between;
+ color: #777777;
+ font-size: 12px;
+}
+
+/* line 93, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-title, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-title {
+ color: #23272B;
+ font-size: 14px;
+}
+
/* line 4, app/assets/stylesheets/admins/library_applies.scss */
.admins-library-applies-index-page .library-applies-list-container span.apply-status-agreed {
color: #28a745;
@@ -26246,6 +26354,113 @@ input.form-control {
.admins-identity-authentications-index-page .identity-authentication-list-container span.apply-status-3 {
color: #6c757d;
}
+/* line 4, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratories-index-page .laboratory-list-table .member-container .laboratory-user {
+ display: -webkit-box;
+ display: flex;
+ -webkit-box-pack: center;
+ justify-content: center;
+ flex-wrap: wrap;
+}
+
+/* line 9, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratories-index-page .laboratory-list-table .member-container .laboratory-user .laboratory-user-item {
+ display: -webkit-box;
+ display: flex;
+ -webkit-box-align: center;
+ 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;
+}
+
+/* line 27, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item {
+ display: -webkit-box;
+ display: flex;
+}
+
+/* line 30, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-img, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-img {
+ display: block;
+ width: 80px;
+ height: 80px;
+}
+
+/* line 36, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-upload, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-upload {
+ cursor: pointer;
+ position: absolute;
+ top: 0;
+ width: 80px;
+ height: 80px;
+ background: #F5F5F5;
+ border: 1px solid #E5E5E5;
+}
+
+/* line 45, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-upload::before, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-upload::before {
+ content: '';
+ position: absolute;
+ top: 27px;
+ left: 39px;
+ width: 2px;
+ height: 26px;
+ background: #E5E5E5;
+}
+
+/* line 55, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-upload::after, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-upload::after {
+ content: '';
+ position: absolute;
+ top: 39px;
+ left: 27px;
+ width: 26px;
+ height: 2px;
+ background: #E5E5E5;
+}
+
+/* line 66, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-left, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-left {
+ position: relative;
+ width: 80px;
+ height: 80px;
+}
+
+/* line 72, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-left.has-img .logo-item-upload, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-left.has-img .logo-item-upload {
+ display: none;
+}
+
+/* line 77, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-left.has-img:hover .logo-item-upload, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-left.has-img:hover .logo-item-upload {
+ display: block;
+ background: rgba(145, 145, 145, 0.8);
+}
+
+/* line 85, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-right, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-right {
+ display: -webkit-box;
+ display: flex;
+ -webkit-box-orient: vertical;
+ -webkit-box-direction: normal;
+ flex-direction: column;
+ -webkit-box-pack: justify;
+ justify-content: space-between;
+ color: #777777;
+ font-size: 12px;
+}
+
+/* line 93, app/assets/stylesheets/admins/laboratories.scss */
+.admins-laboratory-settings-show-page .edit-laboratory-setting-container .logo-item-title, .admins-laboratory-settings-update-page .edit-laboratory-setting-container .logo-item-title {
+ color: #23272B;
+ font-size: 14px;
+}
/* line 4, app/assets/stylesheets/admins/library_applies.scss */
.admins-library-applies-index-page .library-applies-list-container span.apply-status-agreed {
color: #28a745;
diff --git a/public/assets/application-50059ae929866043b47015128702fcfba53d32a2df148e64e1d961c10651c6af.css.gz b/public/assets/application-50059ae929866043b47015128702fcfba53d32a2df148e64e1d961c10651c6af.css.gz
new file mode 100644
index 000000000..f5162f95a
Binary files /dev/null and b/public/assets/application-50059ae929866043b47015128702fcfba53d32a2df148e64e1d961c10651c6af.css.gz differ
diff --git a/public/assets/application-97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd.css.gz b/public/assets/application-97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd.css.gz
deleted file mode 100644
index 60ba5db0b..000000000
Binary files a/public/assets/application-97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd.css.gz and /dev/null differ
diff --git a/public/assets/application-9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb.js.gz b/public/assets/application-9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb.js.gz
index af63ccbfc..d17b8f444 100644
Binary files a/public/assets/application-9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb.js.gz and b/public/assets/application-9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb.js.gz differ
diff --git a/public/assets/college-18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287.js.gz b/public/assets/college-18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287.js.gz
index 3a4c01edc..d4b8b22dc 100644
Binary files a/public/assets/college-18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287.js.gz and b/public/assets/college-18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287.js.gz differ