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/admins/shixun_settings_controller.rb b/app/controllers/admins/shixun_settings_controller.rb index 0ccd5725d..9202ccce6 100644 --- a/app/controllers/admins/shixun_settings_controller.rb +++ b/app/controllers/admins/shixun_settings_controller.rb @@ -92,6 +92,6 @@ class Admins::ShixunSettingsController < Admins::BaseController end def setting_params - params.permit(:use_scope,:excute_time,:close,:status,:can_copy,:webssh,:hidden,:homepage_show,:task_pass,:code_hidden,:id,tag_repertoires:[]) + params.permit(:use_scope,:excute_time,:close,:status,:can_copy,:webssh,:hidden,:homepage_show,:task_pass,:code_hidden,:page_no, :id,tag_repertoires:[]) end end 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/courses_controller.rb b/app/controllers/courses_controller.rb index 4bfc236a1..0c643d15c 100644 --- a/app/controllers/courses_controller.rb +++ b/app/controllers/courses_controller.rb @@ -12,10 +12,10 @@ class CoursesController < ApplicationController end before_action :require_login, except: [:index, :show, :students, :teachers, :board_list, :mine, :all_course_groups, - :left_banner, :top_banner, :informs, :online_learning] + :left_banner, :top_banner, :informs, :online_learning, :course_groups] before_action :check_account, only: [:new, :create, :apply_to_join_course, :join_excellent_course] before_action :check_auth, except: [:index, :show, :students, :teachers, :board_list, :mine, :all_course_groups, - :left_banner, :top_banner, :apply_to_join_course, :exit_course] + :left_banner, :top_banner, :apply_to_join_course, :exit_course, :course_groups] before_action :set_course, only: [:show, :update, :destroy, :settings, :set_invite_code_halt, :set_public_or_private, :search_teacher_candidate, :teachers, :apply_teachers, :top_banner, :left_banner, :add_teacher_popup, :add_teacher, @@ -27,7 +27,8 @@ class CoursesController < ApplicationController :attahcment_category_list,:export_member_scores_excel, :duplicate_course, :switch_to_teacher, :switch_to_assistant, :switch_to_student, :exit_course, :informs, :update_informs, :online_learning, :update_task_position, :tasks_list, - :join_excellent_course, :export_couser_info, :export_member_act_score, :new_informs, :delete_informs] + :join_excellent_course, :export_couser_info, :export_member_act_score, :new_informs, + :delete_informs, :change_member_role, :course_groups, :join_course_group] before_action :user_course_identity, except: [:join_excellent_course, :index, :create, :new, :apply_to_join_course, :search_course_list, :get_historical_course_students, :mine, :search_slim, :board_list] before_action :teacher_allowed, only: [:update, :destroy, :settings, :search_teacher_candidate, @@ -39,7 +40,7 @@ class CoursesController < ApplicationController :set_course_group, :create_group_by_importing_file, :update_task_position, :tasks_list] before_action :teacher_or_admin_allowed, only: [:graduation_group_list, :create_graduation_group, :join_graduation_group, - :change_course_teacher, :course_group_list, + :change_course_teacher, :course_group_list, :change_member_role, :teacher_application_review, :apply_teachers, :delete_course_teacher] before_action :validate_course_name, only: [:create, :update] before_action :find_board, only: :board_list @@ -340,8 +341,8 @@ class CoursesController < ApplicationController @has_graduation_design = @course.course_modules.graduation_module_not_hidden.any? - sort = params[:sort] || "desc" - @order = params[:order].to_i + sort = params[:sort] || "asc" + @order = params[:order] ? params[:order].to_i : 1 if @order.present? case @order when 1 @@ -547,6 +548,61 @@ class CoursesController < ApplicationController end end + # 修改角色 + def change_member_role + tip_exception("请至少选择一个角色") if params[:roles].blank? + tip_exception("不能具有老师、助教两种角色") if params[:roles].include?("PROFESSOR") && params[:roles].include?("ASSISTANT_PROFESSOR") + tip_exception("管理员不能切换为助教或老师") if params[:user_id].to_i == @course.tea_id && + (params[:roles].include?("PROFESSOR") || params[:roles].include?("ASSISTANT_PROFESSOR")) + + course_members = @course.course_members.where(user_id: params[:user_id]) + tip_exception("非课堂成员不能修改角色") if course_members.blank? + + ActiveRecord::Base.transaction do + # 第一次修改为教师或助教身份时直接创建数据 + if params[:roles].include?("CREATOR") + teacher_member = course_members.where(role: %i[CREATOR]).take + elsif (params[:roles].include?("PROFESSOR") || params[:roles].include?("ASSISTANT_PROFESSOR")) && !course_members.exists?(role: %i[PROFESSOR ASSISTANT_PROFESSOR]) + teacher_member = CourseMember.create!(course_id: @course.id, user_id: params[:user_id], role: params[:roles].include?("PROFESSOR") ? 2 : 3) + elsif course_members.exists?(role: %i[PROFESSOR ASSISTANT_PROFESSOR]) + teacher_member = course_members.where(role: %i[PROFESSOR ASSISTANT_PROFESSOR]).take + if params[:roles].include?("PROFESSOR") || params[:roles].include?("ASSISTANT_PROFESSOR") + # 如果之前有老师身份且老师身份要调整时,只需要修改role字段 + if !params[:roles].include?(teacher_member.role) && params[:roles].include?("PROFESSOR") + teacher_member.PROFESSOR! + elsif !params[:roles].include?(teacher_member.role) && params[:roles].include?("ASSISTANT_PROFESSOR") + teacher_member.ASSISTANT_PROFESSOR! + end + teacher_member.save! + else + # 不含教师的参数时删除记录 + teacher_member.destroy! + # CourseDeleteStudentNotifyJob.perform_later(@course.id, [teacher_member.user_id], current_user.id) + end + end + + # 学生身份的处理 + student_member = course_members.where(role: %i[STUDENT]).take + if params[:roles].include?("STUDENT") && student_member.blank? + correspond_teacher_exist = CourseMember.exists?(user_id: params[:user_id], is_active: 1, course_id: @course.id, role: %i[CREATOR PROFESSOR ASSISTANT_PROFESSOR]) + new_student = CourseMember.new(user_id: params[:user_id], course_id: @course.id, role: 4) + new_student.is_active = 0 if correspond_teacher_exist + new_student.save! + + CourseAddStudentCreateWorksJob.perform_later(@course.id, [params[:user_id]]) + # StudentJoinCourseNotifyJob.perform_later(current_user.id, course.id) + elsif !params[:roles].include?("STUDENT") && student_member.present? + # 删除学生身份时激活老师身份 + teacher_member.update_attributes!(is_active: 1) if student_member.is_active && teacher_member.present? + student_member.destroy! + CourseDeleteStudentDeleteWorksJob.perform_later(@course.id, [params[:user_id]]) + # CourseDeleteStudentNotifyJob.perform_later(@course.id, [params[:user_id]], current_user.id) + end + + normal_status(0, "修改成功") + end + end + # 教师和助教角色转换的接口 def change_course_teacher begin @@ -715,8 +771,8 @@ class CoursesController < ApplicationController # 学生列表(包括各个子分班的学生列表)及搜索 def students search = params[:search].present? ? params[:search].strip : nil - order = params[:order].present? ? params[:order].to_i : 0 - sort = params[:sort].present? ? params[:sort] : "desc" + order = params[:order].present? ? params[:order].to_i : 1 + sort = params[:sort].present? ? params[:sort] : "asc" course_group_id = params[:course_group_id].present? ? params[:course_group_id].to_i : nil @students = CourseMember.students(@course) @@ -766,6 +822,26 @@ class CoursesController < ApplicationController end end + # 分班列表 + def course_groups + @course_groups = @course.course_groups + @course_groups = @course_groups.where("name like ?", "%#{params[:search]}%") unless params[:search].blank? + @all_group_count = @course_groups.size + @teachers = @course.teachers.includes(:user, :teacher_course_groups) if @user_course_identity < Course::NORMAL + @current_group_id = @course.students.where(user_id: current_user.id).take&.course_group_id if @user_course_identity == Course::STUDENT + end + + # 学生自动加入分班 + def join_course_group + tip_exception("学生才能加入分班") if @user_course_identity != Course::STUDENT + course_group = CourseGroup.find_by!(id: params[:course_group_id], course_id: @course.id) + member = CourseMember.find_by!(user_id: current_user.id, course_id: @course.id, role: 4) + if course_group && member + member.update_attributes!(course_group_id: course_group.id) + normal_status(0, "加入成功") + end + end + # 将学生批量移动到某个分班 def transfer_to_course_group ActiveRecord::Base.transaction do diff --git a/app/controllers/exercises_controller.rb b/app/controllers/exercises_controller.rb index 30fef596d..191825cc3 100644 --- a/app/controllers/exercises_controller.rb +++ b/app/controllers/exercises_controller.rb @@ -683,12 +683,34 @@ class ExercisesController < ApplicationController end end + # 详情页的立即发布弹框 + def publish_groups + @current_user = current_user + # 可立即发布的分班:当前用户管理的分班去除已发布的分班 + group_ids = @course.charge_group_ids(@current_user) - @exercise.exercise_group_settings.exercise_group_published.pluck(:course_group_id) + @course_groups = @course.course_groups.where(id: group_ids) + @group_settings = @exercise.exercise_group_settings.where(course_group_id: group_ids) + end + #首页批量或单独 立即发布,应是跳出弹窗,设置开始时间和截止时间。 def publish - tip_exception("缺少截止时间参数") if params[:end_time].blank? - tip_exception("截止时间不能早于当前时间") if params[:end_time] <= strf_time(Time.now) - tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if - @course.end_date.present? && params[:end_time] > strf_time(@course.end_date.end_of_day) + group_ids = params[:group_ids]&.reject(&:blank?) + if params[:detail].blank? + tip_exception("缺少截止时间参数") if params[:end_time].blank? + tip_exception("截止时间不能早于当前时间") if params[:end_time] <= strf_time(Time.now) + tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if + @course.end_date.present? && params[:end_time] > strf_time(@course.end_date.end_of_day) + else + group_end_times = params[:group_end_times].reject(&:blank?).map{|time| time.to_time} + tip_exception("缺少截止时间参数") if group_end_times.blank? + tip_exception("截止时间和分班参数的个数不一致") if group_end_times.length != group_ids.length + group_end_times.each do |time| + tip_exception("分班截止时间不能早于当前时间") if time <= Time.now + tip_exception("分班截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if + @course.end_date.present? && time > @course.end_date.end_of_day + end + end + ActiveRecord::Base.transaction do begin check_ids = Exercise.where(id: params[:check_ids]) @@ -702,28 +724,30 @@ class ExercisesController < ApplicationController .exercise_group_not_published.present? ? 1 : 0 end if ex_status == 1 #如果试卷存在已发布的,或者是已截止的,那么则直接跳过 - g_course = params[:group_ids] #表示是否传入分班参数,如果传入分班的参数,那么试卷的统一设置需修改 + g_course = group_ids #表示是否传入分班参数,如果传入分班的参数,那么试卷的统一设置需修改 tiding_group_ids = g_course if g_course - user_course_groups = @course.charge_group_ids(current_user) - if g_course.map(&:to_i).sort == user_course_groups.sort # 如果是设置为全部班级,则试卷不用分组,且试卷设定为统一设置,否则则分组设置 + user_course_groups = @course.course_groups.pluck(:id) + if g_course.map(&:to_i).sort == user_course_groups.sort && + ((params[:detail] && group_end_times.min == group_end_times.max) || params[:detail].blank?) # 如果是设置为全部班级,则试卷不用分组,且试卷设定为统一设置,否则则分组设置 exercise.exercise_group_settings.destroy_all ex_unified = true - e_time = ex_end_time + e_time = params[:detail] ? group_end_times.max : ex_end_time tiding_group_ids = [] else ex_unified = false - g_course.each do |i| + g_course.each_with_index do |i, index| exercise_group_setting = exercise.exercise_group_settings.find_in_exercise_group("course_group_id",i).first #根据课堂分班的id,寻找试卷所在的班级 + group_end_time = params[:detail] ? group_end_times[index] : ex_end_time if exercise_group_setting #如果该试卷分组存在,则更新,否则新建 - exercise_group_setting.update_attributes(publish_time:Time.now,end_time:ex_end_time) + exercise_group_setting.update_attributes(publish_time: Time.now, end_time: group_end_time) else p_course_group = { :exercise_id => exercise.id, :course_group_id => i, :course_id => exercise.course.id, :publish_time => Time.now, - :end_time => ex_end_time, + :end_time => group_end_time, } new_exercise_group = exercise.exercise_group_settings.new p_course_group new_exercise_group.save @@ -954,7 +978,8 @@ class ExercisesController < ApplicationController :status => nil, :commit_status => 0, :objective_score => 0.0, - :subjective_score => -1.0 + :subjective_score => -1.0, + :commit_method => 0 } redo_exercise_users = @exercise_users.exercise_commit_users(user_ids) redo_exercise_users.update_all(redo_option) @@ -1077,22 +1102,40 @@ class ExercisesController < ApplicationController def commit_exercise ActiveRecord::Base.transaction do begin - if @user_course_identity > Course::ASSISTANT_PROFESSOR #为学生时 - objective_score = calculate_student_score(@exercise,current_user)[:total_score] - subjective_score = @answer_committed_user.subjective_score - total_score_subjective_score = subjective_score < 0.0 ? 0.0 : subjective_score - total_score = objective_score + total_score_subjective_score - commit_option = { + can_commit_exercise = false + user_left_time = nil + if @user_course_identity > Course::ASSISTANT_PROFESSOR #为学生时 + if params[:commit_method].to_i == 2 #自动提交时 + user_left_time = get_exercise_left_time(@exercise,current_user) + Rails.logger.info("######__________auto_commit_user_left_time_________################{user_left_time}") + if user_left_time.to_i <= 0 + can_commit_exercise = true + end + else + can_commit_exercise = true + end + if can_commit_exercise + objective_score = calculate_student_score(@exercise,current_user)[:total_score] + subjective_score = @answer_committed_user.subjective_score + total_score_subjective_score = subjective_score < 0.0 ? 0.0 : subjective_score + total_score = objective_score + total_score_subjective_score + commit_option = { :status => 1, :commit_status => 1, :end_at => Time.now, :objective_score => objective_score, :score => total_score, - :subjective_score => subjective_score - } - @answer_committed_user.update_attributes(commit_option) - CommitExercsieNotifyJobJob.perform_later(@exercise.id, current_user.id) - normal_status(0,"试卷提交成功!") + :subjective_score => subjective_score, + :commit_method => @answer_committed_user&.commit_method.to_i > 0 ? @answer_committed_user&.commit_method.to_i : params[:commit_method].to_i + } + @answer_committed_user.update_attributes(commit_option) + CommitExercsieNotifyJobJob.perform_later(@exercise.id, current_user.id) + normal_status(0,"试卷提交成功!") + else + normal_status(-2,"#{user_left_time.to_i}") + end + else + normal_status(-1,"提交失败,当前用户不为课堂学生!") end rescue Exception => e uid_logger_error(e.message) diff --git a/app/controllers/homework_commons_controller.rb b/app/controllers/homework_commons_controller.rb index 0ed5730a4..5d75604ee 100644 --- a/app/controllers/homework_commons_controller.rb +++ b/app/controllers/homework_commons_controller.rb @@ -1036,6 +1036,7 @@ class HomeworkCommonsController < ApplicationController # 可立即发布的分班:当前用户管理的分班去除已发布的分班 group_ids = @course.charge_group_ids(@current_user) - @homework.homework_group_settings.group_published.pluck(:course_group_id) @course_groups = @course.course_groups.where(id: group_ids) + @group_settings = @homework.homework_group_settings.where(course_group_id: group_ids) else tip_exception("没有可发布的分班") end @@ -1043,16 +1044,28 @@ class HomeworkCommonsController < ApplicationController def publish_homework tip_exception("请至少选择一个分班") if params[:group_ids].blank? && @course.course_groups.size != 0 - tip_exception("缺少截止时间参数") if params[:end_time].blank? - tip_exception("截止时间不能早于当前时间") if params[:end_time] <= strf_time(Time.now) - tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if - @course.end_date.present? && params[:end_time] > strf_time(@course.end_date.end_of_day) + group_ids = params[:group_ids]&.reject(&:blank?) + if params[:detail].blank? + tip_exception("缺少截止时间参数") if params[:end_time].blank? + tip_exception("截止时间不能早于当前时间") if params[:end_time] <= strf_time(Time.now) + tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if + @course.end_date.present? && params[:end_time] > strf_time(@course.end_date.end_of_day) + else + group_end_times = params[:group_end_times].reject(&:blank?).map{|time| time.to_time} + tip_exception("缺少截止时间参数") if group_end_times.blank? + tip_exception("截止时间和分班参数的个数不一致") if group_end_times.length != group_ids.length + group_end_times.each do |time| + tip_exception("分班截止时间不能早于当前时间") if time <= Time.now + tip_exception("分班截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if + @course.end_date.present? && time > @course.end_date.end_of_day + end + end homeworks = @course.homework_commons.where(id: params[:homework_ids]) homeworks = homeworks.includes(:homework_group_settings, :homework_detail_manual) charge_group_ids = @course.charge_group_ids(current_user) - publish_groups = charge_group_ids & params[:group_ids] if params[:group_ids] + publish_groups = charge_group_ids & group_ids if group_ids # ActiveRecord::Base.transaction do begin @@ -1062,18 +1075,26 @@ class HomeworkCommonsController < ApplicationController if !params[:group_ids].blank? # 全选即统一设置,unified_setting为true - if @course.course_groups.where(id: publish_groups).size == @course.course_groups.size + if @course.course_groups.where(id: publish_groups).size == @course.course_groups.size && + ((params[:detail] && group_end_times.min == group_end_times.max) || params[:detail].blank?) homework.homework_group_settings.destroy_all homework.unified_setting = true - homework.end_time = params[:end_time] + homework.end_time = params[:detail] ? group_end_times.max : params[:end_time] else homework.unified_setting = false # 创建作业分班设置:homework_group_setting create_homework_group_settings(homework) # 选中的分班设置的发布时间改为当前时间,截止时间改为传的截止时间参数 - homework.homework_group_settings.where(course_group_id: publish_groups).update_all(publish_time: Time.now, - end_time: params[:end_time]) + if params[:detail] + group_ids.each_with_index do |group_id, index| + homework.homework_group_settings.find_by(course_group_id: group_id)&.update_attributes!(publish_time: Time.now, + end_time: group_end_times[index]) + end + else + homework.homework_group_settings.where(course_group_id: publish_groups).update_all(publish_time: Time.now, + end_time: params[:end_time]) + end # 发消息 tiding_group_ids = publish_groups end @@ -1086,7 +1107,7 @@ class HomeworkCommonsController < ApplicationController # 截止时间的处理 if homework.end_time.nil? - homework.end_time = params[:end_time] + homework.end_time = params[:detail] ? group_end_times.max : params[:end_time] elsif homework.max_group_end_time homework.end_time = homework.max_group_end_time end @@ -1101,12 +1122,22 @@ class HomeworkCommonsController < ApplicationController create_homework_group_settings(homework) none_publish_settings = homework.homework_group_settings.where(course_group_id: publish_groups).none_published - none_publish_settings.update_all(publish_time: Time.now, end_time: params[:end_time]) + if params[:detail] + group_ids.each_with_index do |group_id, index| + none_publish_settings.find_by(course_group_id: group_id)&.update_attributes!(publish_time: Time.now, + end_time: group_end_times[index]) + end + else + none_publish_settings.update_all(publish_time: Time.now, end_time: params[:end_time]) + end + if homework.max_group_end_time homework.end_time = homework.max_group_end_time end HomeworkCommonPushNotifyJob.perform_later(homework.id, none_publish_settings.pluck(:course_group_id)) end + + if homework.end_time > Time.now && homework.homework_detail_manual.try(:comment_status) > 1 homework.homework_detail_manual.update_attribute("comment_status", 1) end diff --git a/app/controllers/polls_controller.rb b/app/controllers/polls_controller.rb index dcbded6fe..da5917e1b 100644 --- a/app/controllers/polls_controller.rb +++ b/app/controllers/polls_controller.rb @@ -2,10 +2,10 @@ class PollsController < ApplicationController # before_action :check_poll_status 问卷的发消息和定时任务没有做 before_action :require_login, :check_auth,except: [:index] before_action :find_course, except: [:show,:poll_setting,:commit_setting,:edit,:update,:start_answer,:commit_poll, - :commit_result,:poll_lists,:cancel_publish,:cancel_publish_modal,:common_header] + :commit_result,:poll_lists,:cancel_publish,:cancel_publish_modal,:common_header,:publish_groups] before_action :get_poll_and_course, only: [:show,:poll_setting,:commit_setting,:edit,:update,:start_answer, :commit_poll,:commit_result,:poll_lists,:cancel_publish, - :cancel_publish_modal,:common_header] + :cancel_publish_modal,:common_header, :publish_groups] before_action :user_course_identity before_action :is_course_teacher, except: [:index,:start_answer,:poll_setting,:commit_poll,:commit_result,:poll_lists,:common_header] #判断是否为课堂老师 before_action :check_user_status @@ -242,12 +242,35 @@ class PollsController < ApplicationController end end end + + # 详情页的立即发布弹框 + def publish_groups + @current_user = current_user + # 可立即发布的分班:当前用户管理的分班去除已发布的分班 + group_ids = @course.charge_group_ids(@current_user) - @poll.poll_group_settings.poll_group_published.pluck(:course_group_id) + @course_groups = @course.course_groups.where(id: group_ids) + @group_settings = @poll.poll_group_settings.where(course_group_id: group_ids) + end + #首页批量或单独 立即发布,应是跳出弹窗,设置开始时间和截止时间。 def publish - tip_exception("缺少截止时间参数") if params[:end_time].blank? - tip_exception("截止时间不能早于当前时间") if params[:end_time] <= strf_time(Time.now) - tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if - @course.end_date.present? && params[:end_time] > strf_time(@course.end_date.end_of_day) + group_ids = params[:group_ids]&.reject(&:blank?) + if params[:detail].blank? + tip_exception("缺少截止时间参数") if params[:end_time].blank? + tip_exception("截止时间不能早于当前时间") if params[:end_time] <= strf_time(Time.now) + tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if + @course.end_date.present? && params[:end_time] > strf_time(@course.end_date.end_of_day) + else + group_end_times = params[:group_end_times].reject(&:blank?).map{|time| time.to_time} + tip_exception("缺少截止时间参数") if group_end_times.blank? + tip_exception("截止时间和分班参数的个数不一致") if group_end_times.length != group_ids.length + group_end_times.each do |time| + tip_exception("分班截止时间不能早于当前时间") if time <= Time.now + tip_exception("分班截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if + @course.end_date.present? && time > @course.end_date.end_of_day + end + end + ActiveRecord::Base.transaction do begin check_ids = Poll.where(id: params[:check_ids]) @@ -259,26 +282,28 @@ class PollsController < ApplicationController pl_status = poll.poll_group_settings.find_in_poll_group("course_group_id",params[:group_ids]).poll_group_not_published.present? ? 1 : 0 #立即发布针对分组设置的全部未发布的班级才生效 end if pl_status == 1 #如果问卷存在已发布的,或者是已截止的,那么则直接跳过 - g_course = params[:group_ids] #表示是否传入分班参数,如果传入分班的参数,那么poll的统一设置需修改 + g_course = group_ids #表示是否传入分班参数,如果传入分班的参数,那么poll的统一设置需修改 if g_course - user_course_groups = @course.charge_group_ids(current_user) - if g_course.map(&:to_i).sort == user_course_groups.sort # 如果是设置为全部班级,则问卷不用分组,且问卷设定为统一设置,否则则分组设置 + user_course_groups = @course.course_groups.pluck(:id) + if g_course.map(&:to_i).sort == user_course_groups.sort && + ((params[:detail] && group_end_times.min == group_end_times.max) || params[:detail].blank?) # 如果是设置为全部班级,则问卷不用分组,且问卷设定为统一设置,否则则分组设置 poll.poll_group_settings.destroy_all poll_unified = true - e_time = ex_end_time + e_time = params[:detail] ? group_end_times.max : ex_end_time else poll_unified = false - g_course.each do |i| + g_course.each_with_index do |i, index| poll_group_setting = poll.poll_group_settings.find_in_poll_group("course_group_id",i).first #根据课堂分班的id,寻找问卷所在的班级 + group_end_time = params[:detail] ? group_end_times[index] : ex_end_time if poll_group_setting #如果该问卷分组存在,则更新,否则新建 - poll_group_setting.update_attributes(publish_time:Time.now,end_time:ex_end_time) + poll_group_setting.update_attributes(publish_time: Time.now, end_time: group_end_time) else p_course_group = { :poll_id => poll.id, :course_group_id => i, :course_id => poll.course.id, :publish_time => Time.now, - :end_time => ex_end_time, + :end_time => group_end_time, } new_poll_group = poll.poll_group_settings.new p_course_group new_poll_group.save diff --git a/app/controllers/question_banks_controller.rb b/app/controllers/question_banks_controller.rb index 60b9a807c..7781b5792 100644 --- a/app/controllers/question_banks_controller.rb +++ b/app/controllers/question_banks_controller.rb @@ -90,23 +90,25 @@ class QuestionBanksController < ApplicationController def send_to_course banks = @object_type.classify.constantize.where(id: params[:object_id]) course = current_user.manage_courses.find_by!(id: params[:course_id]) + task_ids = [] banks.each do |bank| case @object_type when 'HomeworkBank' # 作业 - quote_homework_bank bank, course + task = quote_homework_bank bank, course when 'ExerciseBank' if bank.container_type == 'Exercise' # 试卷 - quote_exercise_bank bank, course + task = quote_exercise_bank bank, course else # 问卷 - quote_poll_bank bank, course + task = quote_poll_bank bank, course end when 'GtaskBank' - quote_gtask_bank bank, course + task = quote_gtask_bank bank, course when 'GtopicBank' - quote_gtopic_bank bank, course + task = quote_gtopic_bank bank, course end + task_ids << task.id if task end - normal_status("发送成功") + render :json => {task_ids: task_ids, status: 0, message: "发送成功"} end def destroy 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/helpers/courses_helper.rb b/app/helpers/courses_helper.rb index d670614d5..fb7bd1a88 100644 --- a/app/helpers/courses_helper.rb +++ b/app/helpers/courses_helper.rb @@ -1,6 +1,18 @@ module CoursesHelper - # 是否有切换为学生的入口 + def member_manager group, teachers + str = "" + members = teachers.select{|teacher| teacher.teacher_course_groups.pluck(:course_group_id).include?(group.id) || teacher.teacher_course_groups.size == 0} + str = members.uniq.size == teachers.size ? "全部教师" : members.map{|member| member.user.real_name}.join("、") + str + # teachers.each do |member| + # if member.teacher_course_groups.exists?(course_group_id: group.id) || member.teacher_course_groups.size == 0 + # str << member.user.real_name + # end + # end + end + + # 是否有切换为学生的入口 def switch_student_role is_teacher, course, user is_teacher && course.course_members.where(user_id: user.id, role: %i(STUDENT)).exists? end diff --git a/app/jobs/end_exercise_calculate_job.rb b/app/jobs/end_exercise_calculate_job.rb index 39d8bb1db..b6d8e491e 100644 --- a/app/jobs/end_exercise_calculate_job.rb +++ b/app/jobs/end_exercise_calculate_job.rb @@ -19,7 +19,8 @@ class EndExerciseCalculateJob < ApplicationJob :end_at => Time.now, :objective_score => objective_score, :score => total_score, - :subjective_score => user_sub_score + :subjective_score => user_sub_score, + :commit_method => user&.commit_method.to_i > 0 ? user&.commit_method.to_i : 4 } user.update_attributes(commit_option) end diff --git a/app/libs/util.rb b/app/libs/util.rb index ae2e4b80b..72e728ab9 100644 --- a/app/libs/util.rb +++ b/app/libs/util.rb @@ -48,8 +48,8 @@ module Util return if str.blank? case type - when :phone then "#{str[0..2]}***#{str[-4..-1]}" - when :email then "#{str[0..2]}***#{str[str.rindex('@')..-1]}" + when :phone then "#{str[0..2]}***#{str[-3..-1]}" + when :email then "#{str[0]}***#{str[(str.rindex('@')-1)..-1]}" else "#{str[0..2]}***#{str[-3..-1]}" end end 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/exercise_user.rb b/app/models/exercise_user.rb index 84f042b25..0f2e8456e 100644 --- a/app/models/exercise_user.rb +++ b/app/models/exercise_user.rb @@ -1,4 +1,5 @@ class ExerciseUser < ApplicationRecord + # commit_method 0 为默认, 1为学生的手动提交,2为倒计时结束后自动提交,3为试卷定时截止的自动提交, 4为教师手动的立即截止 belongs_to :user belongs_to :exercise 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/models/shixun.rb b/app/models/shixun.rb index 4912ea15a..0cdb2e82b 100644 --- a/app/models/shixun.rb +++ b/app/models/shixun.rb @@ -1,5 +1,6 @@ class Shixun < ApplicationRecord include Searchable::Shixun + attr_accessor :page_no #管理员页面 实训配置更新状态时,需要接受page_no参数 # status: 0:编辑 1:申请发布 2:正式发布 3:关闭 -1:软删除 # hide_code: 隐藏代码窗口 diff --git a/app/models/user.rb b/app/models/user.rb index 5fdb1a713..7bfe9c36f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -242,6 +242,11 @@ class User < ApplicationRecord user_extension&.department&.name || '' end + # 课堂的所有身份 + def course_role course + course.course_members.where(user_id: id).pluck(:role) + end + # 课堂的老师(创建者、老师、助教) def teacher_of_course?(course) course.course_members.exists?(user_id: id, role: [1,2,3], is_active: 1) || admin? || business? @@ -583,6 +588,16 @@ class User < ApplicationRecord mail.present? end + # 手机号:123***123 + def hidden_phone + Util.conceal(phone, :phone).to_s + end + + # 邮箱:w***l@qq.com + def hidden_mail + Util.conceal(mail, :email).to_s + end + # 学院的url标识 def college_identifier Department.find_by_id(department_members.pluck(:department_id).first)&.identifier 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/tasks/exercise_publish_task.rb b/app/tasks/exercise_publish_task.rb index 220512664..a7b533d05 100644 --- a/app/tasks/exercise_publish_task.rb +++ b/app/tasks/exercise_publish_task.rb @@ -66,7 +66,8 @@ class ExercisePublishTask :end_at => Time.now, :objective_score => s_score, :score => total_score, - :subjective_score => subjective_score + :subjective_score => subjective_score, + :commit_method => exercise_user&.commit_method.to_i > 0 ? exercise_user&.commit_method.to_i : 3 } exercise_user.update_attributes(commit_option) end @@ -108,7 +109,8 @@ class ExercisePublishTask :end_at => Time.now, :objective_score => s_score, :score => total_score, - :subjective_score => subjective_score + :subjective_score => subjective_score, + :commit_method => exercise_user&.commit_method.to_i > 0 ? exercise_user&.commit_method.to_i : 3 } exercise_user.update_attributes(commit_option) end 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 @@ + + + + + + + + + + + + + <% 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 %> + +
单位名称域名统计链接管理员<%= sort_tag('创建时间', name: 'id', path: admins_laboratories_path) %>操作
+ +<%= 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 %> + +
+
网站域名设置
+ +
+
+
+ https:// +
+ <%= text_field_tag :identifier, @laboratory.identifier, + maxlength: 15, class: 'form-control font-16', + 'onKeyUp': 'value=value.replace(/[^\w\.\-\/]/ig,"").toLowerCase()', + style: 'text-transform:lowercase'%> +
+ <% rails_env = EduSetting.get('rails_env') %> + <%= rails_env && rails_env != 'production' ? ".#{rails_env}.educoder.net" : '.educoder.net' %> +
+
+ <%# if @laboratory.errors && @laboratory.errors.key?(:identifier) %> + + <%# end %> +
+
+ +
+
网站名称设置
+ +
+ <%= text_field_tag :name, setting.name, placeholder: '输入20个字以内的网站名称', maxlength: 20, class: 'form-control col-12 col-md-4' %> +
+
+ +
+
Logo设置
+ +
+
+ <% nav_logo_img = setting.nav_logo_url %> + +
+
网站导航logo
+
格式:PNG、JPG
+
尺寸:高度38px以内,宽等比例缩放
+
+
+ +
+ <% login_logo_img = setting.login_logo_url %> + +
+
登录页面logo
+
格式:PNG、JPG
+
尺寸:高度90px以内,宽等比例缩放
+
+
+ +
+ <% tab_logo_img = setting.tab_logo_url %> +
+ + <%= file_field_tag(:tab_logo, accept: 'image/x-icon', style: 'display: none', value: params[:tab_logo]) %> + +
+
+
浏览器导航栏logo
+
格式:ico
+
尺寸:16*16 32*32 48*48 64*64
+
+
+
+
+ +
+
导航设置
+ +
+ + + + + + + + + + <% (setting.navbar || setting.default_navbar).each do |nav| %> + + + + + + <% end %> + +
导航名称导航链接是否展示
<%= text_field_tag('navbar[][name]', nav['name'], id: nil, class: 'form-control') %><%= text_field_tag('navbar[][link]', nav['link'], id: nil, class: 'form-control') %> + <%= check_box_tag('navbar[][hidden]', 0, !nav['hidden'], id: nil, class: 'font-16') %> +
+
+
+ +
+
底部备案信息设置
+ + +
+ +
+ +
+ <%= 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/courses/course_groups.json.jbuilder b/app/views/courses/course_groups.json.jbuilder new file mode 100644 index 000000000..48a5922a6 --- /dev/null +++ b/app/views/courses/course_groups.json.jbuilder @@ -0,0 +1,11 @@ +json.course_groups @course_groups.each do |group| + json.(group, :id, :course_members_count, :name) + json.invite_code group.invite_code if @user_course_identity < Course::STUDENT + json.member_manager member_manager(group, @teachers) if @user_course_identity < Course::NORMAL +end + +if @user_course_identity == Course::STUDENT + json.current_group_id @current_group_id +end +json.none_group_member_count @course.none_group_count +json.group_count @all_group_count \ No newline at end of file diff --git a/app/views/courses/students.json.jbuilder b/app/views/courses/students.json.jbuilder index a7585672f..0b5d7fe71 100644 --- a/app/views/courses/students.json.jbuilder +++ b/app/views/courses/students.json.jbuilder @@ -1,12 +1,17 @@ json.students do json.array! @students do |student| json.user_id student.user_id - json.login student.user.try(:login) + # json.login student.user.try(:login) json.name student.user.try(:real_name) json.name_link user_path(student.user) json.student_id student.user.try(:student_id) json.course_group_name student.course_group.try(:name) json.course_member_id student.id + if @user_course_identity < Course::ASSISTANT_PROFESSOR && !params[:course_group_id].present? + json.member_roles student.user.course_role(@course) + end + json.user_phone student.user.hidden_phone + json.user_mail student.user.hidden_mail end end json.students_count @students_count diff --git a/app/views/courses/teachers.json.jbuilder b/app/views/courses/teachers.json.jbuilder index 040ffb2aa..19a92790d 100644 --- a/app/views/courses/teachers.json.jbuilder +++ b/app/views/courses/teachers.json.jbuilder @@ -16,6 +16,9 @@ json.teacher_list do end json.graduation_group teacher.graduation_group.try(:name) json.graduation_group_id teacher.graduation_group.try(:id) + if @user_course_identity < Course::ASSISTANT_PROFESSOR + json.member_roles teacher.user.course_role(@course) + end end end json.teacher_list_size @teacher_list_size diff --git a/app/views/exercises/_exercise_user.json.jbuilder b/app/views/exercises/_exercise_user.json.jbuilder index d41fb9e20..5f00bbd70 100644 --- a/app/views/exercises/_exercise_user.json.jbuilder +++ b/app/views/exercises/_exercise_user.json.jbuilder @@ -8,6 +8,7 @@ json.user_group_name ex_user_info[:user_group_name] json.student_id ex_user_info[:student_id] json.commit_status ex_user_info[:commit_status] json.end_at ex_user_info[:end_at] +json.commit_method exercise_user&.commit_method.to_i if subjective_type == 1 json.objective_score ex_user_info[:ex_object_score] json.subjective_score ex_user_info[:ex_subject_score] diff --git a/app/views/exercises/_user_exercise_info.json.jbuilder b/app/views/exercises/_user_exercise_info.json.jbuilder index 0d21cc593..bdac3a985 100644 --- a/app/views/exercises/_user_exercise_info.json.jbuilder +++ b/app/views/exercises/_user_exercise_info.json.jbuilder @@ -9,6 +9,7 @@ json.exercise_answer_user do json.user_id ex_answerer.id json.login ex_answerer.login if exercise_user.present? + json.commit_method exercise_user&.commit_method.to_i json.start_at exercise_user.start_at json.score exercise_user.score.present? ? exercise_user.score.round(1).to_s : "0.0" end diff --git a/app/views/exercises/common_header.json.jbuilder b/app/views/exercises/common_header.json.jbuilder index 5d33aca66..d43d7c3f8 100644 --- a/app/views/exercises/common_header.json.jbuilder +++ b/app/views/exercises/common_header.json.jbuilder @@ -1,6 +1,6 @@ json.course_is_end @course.is_end # true表示已结束,false表示未结束 json.extract! @exercise, :id,:exercise_name,:exercise_description,:show_statistic -json.time @user_left_time +json.time (@user_left_time.to_i / 60) json.exercise_status @ex_status diff --git a/app/views/exercises/publish_groups.json.jbuilder b/app/views/exercises/publish_groups.json.jbuilder new file mode 100644 index 000000000..72cefdd1a --- /dev/null +++ b/app/views/exercises/publish_groups.json.jbuilder @@ -0,0 +1,6 @@ +json.course_groups @course_groups do |group| + json.id group.id + json.name group.name + json.end_time @group_settings.select{|group_setting| group_setting.course_group_id == group.id}.first&.end_time +end +json.end_time @exercise.end_time \ No newline at end of file diff --git a/app/views/homework_commons/publish_groups.json.jbuilder b/app/views/homework_commons/publish_groups.json.jbuilder index 67722bb04..f3a99dbca 100644 --- a/app/views/homework_commons/publish_groups.json.jbuilder +++ b/app/views/homework_commons/publish_groups.json.jbuilder @@ -1,4 +1,6 @@ json.course_groups @course_groups do |group| json.id group.id json.name group.name -end \ No newline at end of file + json.end_time @group_settings.select{|group_setting| group_setting.course_group_id == group.id}.first&.end_time +end +json.end_time @homework.end_time \ No newline at end of file diff --git a/app/views/polls/publish_groups.json.jbuilder b/app/views/polls/publish_groups.json.jbuilder new file mode 100644 index 000000000..f63a2ea90 --- /dev/null +++ b/app/views/polls/publish_groups.json.jbuilder @@ -0,0 +1,6 @@ +json.course_groups @course_groups do |group| + json.id group.id + json.name group.name + json.end_time @group_settings.select{|group_setting| group_setting.course_group_id == group.id}.first&.end_time +end +json.end_time @poll.end_time \ No newline at end of file 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/app/views/users/accounts/show.json.jbuilder b/app/views/users/accounts/show.json.jbuilder index 6b28bda55..ec81cc6bf 100644 --- a/app/views/users/accounts/show.json.jbuilder +++ b/app/views/users/accounts/show.json.jbuilder @@ -1,5 +1,7 @@ -json.extract! observed_user, :id, :nickname, :phone, :mail, :show_realname +json.extract! observed_user, :id, :nickname, :show_realname +json.phone observed_user.hidden_phone +json.mail observed_user.hidden_mail json.avatar_url url_to_avatar(observed_user) user = ActiveDecorator::Decorator.instance.decorate(observed_user) json.name user.name 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 847afaa83..4b7addc0b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -331,6 +331,7 @@ Rails.application.routes.draw do post 'join_graduation_group' post 'set_course_group' post 'change_course_admin' + post 'change_member_role' post 'change_course_teacher' post 'delete_course_teacher' post 'teacher_application_review' @@ -369,6 +370,8 @@ Rails.application.routes.draw do post 'join_excellent_course' get 'tasks_list' post 'update_task_position' + get 'course_groups' + post 'join_course_group' end collection do @@ -584,6 +587,7 @@ Rails.application.routes.draw do post :cancel_publish #撤销发布 get :cancel_publish_modal #撤销发布的弹窗 get :common_header + get :publish_groups end resources :poll_questions,only:[:new,:create] end @@ -615,6 +619,7 @@ Rails.application.routes.draw do get :exercise_result post :cancel_exercise get :begin_commit #提交前的弹窗 + get :publish_groups end resources :exercise_questions,only:[:new,:create,:index] end @@ -819,6 +824,7 @@ Rails.application.routes.draw do end end resource :template, only: [:show] + resource :setting, only: [:show] end namespace :admins do @@ -948,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] @@ -966,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/db/migrate/20191011030441_add_commit_method_to_exercise_user.rb b/db/migrate/20191011030441_add_commit_method_to_exercise_user.rb new file mode 100644 index 000000000..060e5eb5f --- /dev/null +++ b/db/migrate/20191011030441_add_commit_method_to_exercise_user.rb @@ -0,0 +1,5 @@ +class AddCommitMethodToExerciseUser < ActiveRecord::Migration[5.2] + def change + add_column :exercise_users, :commit_method, :integer, :default => 0 + 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 diff --git a/public/react/src/common/course/WordsBtn.js b/public/react/src/common/course/WordsBtn.js index 58b000338..85a85cfb6 100644 --- a/public/react/src/common/course/WordsBtn.js +++ b/public/react/src/common/course/WordsBtn.js @@ -8,15 +8,21 @@ class WordsBtn extends Component { } render() { - let{to, href,targets}=this.props + let{to, href,targets, style2 }=this.props return( { to==undefined&&targets==undefined ? - {this.props.children}: - targets!=undefined? {this.props.children} + {this.props.children}: + targets!=undefined? {this.props.children} : - {this.props.children} + {this.props.children} } ) diff --git a/public/react/src/modules/courses/busyWork/CommonWorkSetting.js b/public/react/src/modules/courses/busyWork/CommonWorkSetting.js index 5d9a7dc16..ad25f553f 100644 --- a/public/react/src/modules/courses/busyWork/CommonWorkSetting.js +++ b/public/react/src/modules/courses/busyWork/CommonWorkSetting.js @@ -167,7 +167,12 @@ class CommonWorkSetting extends Component{ } on('commonwork_fetch_all', this.fetchAllListener) - } + + + if(this.props.isAdmin()===true){ + this.setState({startEditFlag: true}) + } + } componentWillUnmount() { off('commonwork_fetch_all', this.fetchAllListener) } diff --git a/public/react/src/modules/courses/coursesPublic/HomeworkModal.js b/public/react/src/modules/courses/coursesPublic/HomeworkModal.js index 89e747004..a001e6d86 100644 --- a/public/react/src/modules/courses/coursesPublic/HomeworkModal.js +++ b/public/react/src/modules/courses/coursesPublic/HomeworkModal.js @@ -48,7 +48,7 @@ class HomeworkModal extends Component{ if(this.props.starttimes!=undefined&&this.props.starttimes!=""){ this.setState({ - endtime:moment(moment(handleDateString(this.props.starttimes)).add(1, 'months')).format("YYYY-MM-DD HH:mm") + endtime:moment(moment(handleDateString(this.props.starttimes)).add(1, 'week')).format("YYYY-MM-DD HH:mm") }) } } @@ -70,7 +70,7 @@ class HomeworkModal extends Component{ if(this.props.starttimes!=undefined&&this.props.starttimes!=""){ this.setState({ - endtime:moment(moment(handleDateString(this.props.starttimes)).add(1, 'months')).format("YYYY-MM-DD HH:mm") + endtime:moment(moment(handleDateString(this.props.starttimes)).add(1, 'week')).format("YYYY-MM-DD HH:mm") }) } } diff --git a/public/react/src/modules/courses/css/members.css b/public/react/src/modules/courses/css/members.css index 67524d130..ab7effaa0 100644 --- a/public/react/src/modules/courses/css/members.css +++ b/public/react/src/modules/courses/css/members.css @@ -84,4 +84,8 @@ text-overflow:ellipsis; white-space:nowrap; cursor: default; +} + +.changeRolePop .ant-checkbox-group { + width: 230px !important; } \ No newline at end of file diff --git a/public/react/src/modules/courses/exercise/ExerciseReviewAndAnswer.js b/public/react/src/modules/courses/exercise/ExerciseReviewAndAnswer.js index f45de9b64..75da4f2fe 100644 --- a/public/react/src/modules/courses/exercise/ExerciseReviewAndAnswer.js +++ b/public/react/src/modules/courses/exercise/ExerciseReviewAndAnswer.js @@ -96,8 +96,8 @@ class ExerciseReviewAndAnswer extends Component{ //window.addEventListener('scroll', this.handleScroll); } - remainTime=()=>{ - let { time } = this.state; + remainTime=(time)=>{ + // let { time } = this.state; let h=moment(parseInt(time)*1000).hour()-8; let m=moment(parseInt(time)*1000).minutes(); let s=moment(parseInt(time)*1000).seconds(); @@ -127,22 +127,31 @@ class ExerciseReviewAndAnswer extends Component{ } //自动交卷 autoCommitExercise=()=>{ - let eId=this.props.match.params.Id; - let url=`/exercises/${eId}/commit_exercise.json`; - axios.post(url).then((result)=>{ - if(result){ - this.setState({ - Modalstype:true, - Modalstopval:'答题结束了,系统已自动提交试卷', - modalsBottomval:"不能再修改答题", - ModalCancel:undefined, - ModalSave:this.sureCommit, - Loadtype:true - }) - } - }).catch((error)=>{ - console.log(error); - }) + let eId=this.props.match.params.Id; + let url=`/exercises/${eId}/commit_exercise.json`; + axios.post(url,{ + commit_method:2 + }).then((result)=>{ + if(result){ + if(result.data.status===0){ + this.setState({ + Modalstype:true, + Modalstopval:'答题结束了,系统已自动提交试卷', + modalsBottomval:"不能再修改答题", + ModalCancel:undefined, + ModalSave:this.sureCommit, + Loadtype:true + }) + this.props.showNotification(`${result.data.message}`); + } + + if(result.data.status===-2){ + this.remainTime(parseInt(result.data.message)) + } + } + }).catch((error)=>{ + console.log(error); + }) } sureCommit=()=>{ @@ -235,7 +244,7 @@ class ExerciseReviewAndAnswer extends Component{ isSpin:false }) if(result.data.exercise.left_time != null){ - this.remainTime(); + this.remainTime(result.data.exercise.left_time); } } }).catch((error)=>{ @@ -485,7 +494,9 @@ class ExerciseReviewAndAnswer extends Component{ //交卷 let eId=this.props.match.params.Id; let url=`/exercises/${eId}/commit_exercise.json`; - axios.post(url).then((result)=>{ + axios.post(url,{ + commit_method:1 + }).then((result)=>{ if(result){ this.setState({ Modalstype:false, diff --git a/public/react/src/modules/courses/members/ChangeRolePop.js b/public/react/src/modules/courses/members/ChangeRolePop.js new file mode 100644 index 000000000..0f9e6f5f1 --- /dev/null +++ b/public/react/src/modules/courses/members/ChangeRolePop.js @@ -0,0 +1,79 @@ +import React, { useState, useEffect } from 'react' +import { trigger, WordsBtn } from 'educoder' +import { Input, Checkbox, Popconfirm } from "antd"; +import axios from 'axios' + +/** + 角色数组, CREATOR: 创建者, PROFESSOR: 教师, ASSISTANT_PROFESSOR: 助教, STUDENT: 学生 +*/ +function ChangeRolePop({ member_roles = [], record, courseId, onChangeRoleSuccess, showNotification, getUserId, fetchUser }) { + const [checkBoxRoles, setCheckBoxRoles] = useState(member_roles) + useEffect(() => { + setCheckBoxRoles(member_roles) + }, [member_roles]) + function onCheckBoxChange(val) { + console.log(val) + + const isTeacher = checkBoxRoles.indexOf('PROFESSOR') + const isAssitant = checkBoxRoles.indexOf('ASSISTANT_PROFESSOR') + const isTeacherNew = val.indexOf('PROFESSOR') + const isAssitantNew = val.indexOf('ASSISTANT_PROFESSOR') + if (isTeacherNew > -1 && isTeacher == -1 && isAssitantNew > -1) { + val.splice(isAssitantNew, 1) + } + if (isAssitantNew > -1 && isAssitant == -1 && isTeacherNew > -1) { + val.splice(isTeacherNew, 1) + } + + setCheckBoxRoles(val) + } + function onCancel() { + setCheckBoxRoles(member_roles) + } + const onConfirm = async () => { + if (checkBoxRoles && checkBoxRoles.length == 0) { + showNotification('请至少选择一个角色') + return; + } + const url = `/courses/${courseId}/change_member_role.json` + const response = await axios.post(url, { + roles: checkBoxRoles, + user_id: record.user_id + }) + if (response.data.status == 0) { + showNotification('保存成功') + onChangeRoleSuccess() + + trigger('updatabanner') + if (fetchUser && record.user_id == getUserId) { + fetchUser() + } + + } + console.log(response) + } + const isAdmin = checkBoxRoles.indexOf('CREATOR') != -1 + const isTeacher = checkBoxRoles.indexOf('PROFESSOR') != -1 + const isAssitant = checkBoxRoles.indexOf('ASSISTANT_PROFESSOR') != -1 + const isStudent = checkBoxRoles.indexOf('STUDENT') != -1 + return ( + + {isAdmin && 管理员} + {!isAdmin && 教师} + 助教 + 学生 + + } + > + 修改角色 + + ) +} +export default ChangeRolePop \ No newline at end of file diff --git a/public/react/src/modules/courses/members/studentsList.css b/public/react/src/modules/courses/members/studentsList.css new file mode 100644 index 000000000..ebab316a0 --- /dev/null +++ b/public/react/src/modules/courses/members/studentsList.css @@ -0,0 +1,3 @@ +.stu_table .ant-table-thead > tr > th, .stu_table .ant-table-tbody > tr > td { + padding: 14px 6px; +} \ No newline at end of file diff --git a/public/react/src/modules/courses/members/studentsList.js b/public/react/src/modules/courses/members/studentsList.js index 1b0b19d6f..3a5a55b71 100644 --- a/public/react/src/modules/courses/members/studentsList.js +++ b/public/react/src/modules/courses/members/studentsList.js @@ -1,5 +1,5 @@ import React,{ Component } from "react"; -import { Input,Checkbox,Table, Pagination, Modal,Menu ,Spin, Tooltip , Divider } from "antd"; +import { Input,Checkbox,Table, Pagination, Modal,Menu ,Spin, Tooltip , Divider, Popconfirm } from "antd"; import ClipboardJS from 'clipboard' import '../css/Courses.css' import '../css/members.css' @@ -14,6 +14,8 @@ import _ from 'lodash' import NoneData from "../coursesPublic/NoneData" import DownloadMessageysl from "../../modals/DownloadMessageysl"; import CreateGroupByImportModal from './modal/CreateGroupByImportModal' +import ChangeRolePop from './ChangeRolePop' +import "./studentsList.css" const Search =Input.Search; const TYPE_STUDENTS = 1 @@ -22,29 +24,32 @@ const TYPE_COURSE_GOURP_CHILD = 3 const buildColumns = (that,isParent) => { const { course_groups , sortedInfo } = that.state let showSorter = isParent==true + const courseId = that.props.match.params.coursesId const columns=[{ - title: '序号', + title: '序号1', dataIndex: 'id', key: 'id', align:'center', - width:"10%", + width:"8%", className:"color-grey-6", render: (id, student, index) => { return (that.state.page - 1) * 20 + index + 1 } - }, { - title: '用户id', - dataIndex: 'login', - key: 'login', - align:'center', - width:"10%", - className:"color-grey-6", - render: (login, record) => { - return 10 ? login : ''} - >{login} - } - }, { + }, + // { + // title: '用户id', + // dataIndex: 'login', + // key: 'login', + // align:'center', + // width:"10%", + // className:"color-grey-6", + // render: (login, record) => { + // return 10 ? login : ''} + // >{login} + // } + // }, + { title: '姓名', dataIndex: 'name', key: 'name', @@ -69,14 +74,45 @@ const buildColumns = (that,isParent) => { return 10 ? student_id : ''} style={{maxWidth: '160px'}} >{student_id} } - }]; + } + , { + title: '手机号', + dataIndex: 'user_phone', + key: 'user_phone', + align:'center', + width:"10%", + className:"color-grey-6", + // sorter: true, + // sortDirections: sortDirections, + // sortOrder: sortedInfo.columnKey === 'user_phone' && sortedInfo.order, + render: (user_phone, record) => { + return 10 ? user_phone : ''} + style={{maxWidth: '160px'}} >{user_phone} + } + } + , { + title: '邮箱', + dataIndex: 'user_mail', + key: 'user_mail', + align:'center', + width:"10%", + className:"color-grey-6", + // sorter: true, + // sortDirections: sortDirections, + // sortOrder: sortedInfo.columnKey === 'user_mail' && sortedInfo.order, + render: (user_mail, record) => { + return 10 ? user_mail : ''} + style={{maxWidth: '160px'}} >{user_mail} + } + } + ]; if (course_groups && course_groups.length) { columns.push({ title: '分班', dataIndex: 'course_group_name', key: 'course_group_name', align:'center', - width:"40%", + width:"25%", className:"color-grey-6", sorter:showSorter, sortDirections: sortDirections, @@ -95,14 +131,38 @@ const buildColumns = (that,isParent) => { const isAdmin = that.props.isAdmin() if (isAdmin) { columns.unshift({ - title: '', - dataIndex: 'check', - key: 'check', - render: (text, item) => { - return - }, - width:"5%" - }) + title: '', + dataIndex: 'check', + key: 'check', + render: (text, item) => { + return + }, + width:"5%" + }) + + columns.push({ + title: '操作', + key: 'action', + width: '22%', + align:'center', + render: (text, record) => { + return ( + + that.onDelete(record)} style={'grey'}>删除学生 + + + ) + }, + }) + } return columns; @@ -242,7 +302,9 @@ class studentsList extends Component{ onChange=()=>{ } - + onChangeRoleSuccess = () => { + this.fetchAll() + } componentDidMount() { this.setState({ isSpin:true @@ -451,11 +513,13 @@ class studentsList extends Component{ } } // 多选 - onDelete = () => { - const len = this.state.checkBoxValues.length - if (len == 0) { - this.props.showNotification('请先从列表选择要删除的学生') - return; + onDelete = (record) => { + if (!record) { + const len = this.state.checkBoxValues.length + if (len == 0) { + this.props.showNotification('请先从列表选择要删除的学生') + return; + } } this.props.confirm({ @@ -465,7 +529,7 @@ class studentsList extends Component{ let id = this.props.match.params.coursesId let url=`/courses/${id}/delete_from_course.json`; axios.post((url), { - students: this.state.checkBoxValues.map(item => {return {course_member_id: item} }), + students: [{course_member_id: record.course_member_id}] // this.state.checkBoxValues.map(item => {return {course_member_id: item} }), }).then((result)=>{ if (result.data.status == 0) { this.props.showNotification('删除成功') @@ -483,6 +547,20 @@ class studentsList extends Component{ addDir = () => { trigger('groupAdd', this.props.coursesids) } + addToDir = async () => { + const courseId = this.props.match.params.coursesId + const url = `/courses/${courseId}/join_course_group.json` + const course_group_id = this.props.match.params.course_group_id + + const response = await axios.post(url, { + course_group_id + }) + if (response && response.data.status == 0) { + this.props.showNotification('加入成功') + this.props.updataleftNavfun() + this.fetchAll() + } + } renameDir = () => { const course_group_id = this.props.match.params.course_group_id trigger('groupRename', { id: parseInt(course_group_id), name: this.state.course_group_name}) @@ -529,6 +607,7 @@ class studentsList extends Component{ render(){ const isAdmin = this.props.isAdmin() + const isStudent = this.props.isStudent() const isSuperAdmin = this.props.isSuperAdmin() const isCourseEnd = this.props.isCourseEnd() let { @@ -640,6 +719,8 @@ class studentsList extends Component{ { // pageType !== TYPE_STUDENTS && !isCourseEnd && isAdmin && isParent && this.addDir()}>添加分班 } + { + isStudent && !isParent && course_group_id != 0 && this.addToDir()}>加入分班 } { isAdmin && !isParent && course_group_id != 0 && this.deleteDir()}>删除分班 } { @@ -701,7 +782,7 @@ class studentsList extends Component{
    {isAdmin && 已选 {checkBoxValues.length} 个}
    - {isAdmin &&
  • 删除
  • } + {/* {isAdmin &&
  • 删除
  • } */} {isAdmin &&
  • 移动到...
      @@ -752,7 +833,7 @@ class studentsList extends Component{
  • - {!this.state.isSpin && + {students && students.length &&
    }
    diff --git a/public/react/src/modules/courses/members/teacherList.js b/public/react/src/modules/courses/members/teacherList.js index 86ae7aa0b..fe46e9bf4 100644 --- a/public/react/src/modules/courses/members/teacherList.js +++ b/public/react/src/modules/courses/members/teacherList.js @@ -1,5 +1,5 @@ import React,{ Component } from "react"; -import { Input,Checkbox,Table, Divider, Tooltip,Spin, Menu } from "antd"; +import { Input,Checkbox,Table, Divider, Tooltip,Spin, Menu, Popconfirm } from "antd"; import CourseLayoutcomponent from '../common/CourseLayoutComponent' import NoneData from "../coursesPublic/NoneData" @@ -24,6 +24,7 @@ import AddAdminModal from './modal/AddAdminModal' import CourseGroupChooserModal from './modal/CourseGroupChooserModal' import { ROLE_TEACHER_NUM, ROLE_ASSISTANT_NUM } from './common' import CourseGroupChooser from './CourseGroupChooser' +import ChangeRolePop from './ChangeRolePop' const Search = Input.Search; const ROLE_ADMIN = "管理员" @@ -38,6 +39,8 @@ function buildColumns(that) { const isAdminOrTeacher = that.props.isAdminOrTeacher() const { course_groups, filterKey } = that.state const showSorter = filterKey == '1' + const courseId = that.props.match.params.coursesId + const columns = [{ title: '序号', dataIndex: 'name', @@ -117,7 +120,7 @@ function buildColumns(that) { const hasGraduationModule = that.hasGraduationModule() if (hasGraduationModule && showSorter) { columns.push({ - title: '答辩组', + title: '所在答辩组', // width: 90, sorter: showSorter, sortDirections: sortDirections, @@ -141,6 +144,9 @@ function buildColumns(that) { width: 150, align:'center', render: (text, record) => { + const isAdmin = record.role == ROLE_ADMIN + const isTeacher = record.role == ROLE_TEACHER + const isAssitant = record.role == ROLE_TEACHER_ASSISTANT if (record.application_id) { return ( @@ -149,16 +155,46 @@ function buildColumns(that) { that.onAgree(record)} style={{color: '#4CACFF'}}>同意 ) } else { + return ( - - {record.role != ROLE_ADMIN && that.onDelete(record)} style={'grey'}>删除} - {(record.role == ROLE_TEACHER || record.role == ROLE_TEACHER_ASSISTANT || isAdminOrCreator) && record.role != ROLE_ADMIN - && } - { record.role == ROLE_TEACHER ? that.changeToAssistant(record)}>变更为助教 : '' } - { record.role == ROLE_TEACHER_ASSISTANT ? that.changeToTeacher(record)}>变更为教师 : '' } - { record.role == ROLE_ADMIN && isAdminOrCreator ? that.showChangeAdminModal(record)}>更换管理员 : '' } + + that.onDelete(record)} style={'grey'}>删除 + + {/* + 管理员 + 助教 + 学生 + + } + > + 修改角色 + */} + + + + // + // {record.role != ROLE_ADMIN && that.onDelete(record)} style={'grey'}>删除} + // {(record.role == ROLE_TEACHER || record.role == ROLE_TEACHER_ASSISTANT || isAdminOrCreator) && record.role != ROLE_ADMIN + // && } + // { record.role == ROLE_TEACHER ? that.changeToAssistant(record)}>变更为助教 : '' } + // { record.role == ROLE_TEACHER_ASSISTANT ? that.changeToTeacher(record)}>变更为教师 : '' } + // { record.role == ROLE_ADMIN && isAdminOrCreator ? that.showChangeAdminModal(record)}>更换管理员 : '' } - ) + // + ) } }, @@ -344,6 +380,9 @@ class studentsList extends Component{ console.log(error); }); } + onChangeRoleSuccess = () => { + this.fetchAll() + } fetchAll = async (argPage) => { this.setState({ isSpin:true @@ -610,6 +649,7 @@ class studentsList extends Component{ combineArray = this.state.application_list } const isAdminOrTeacher = this.props.isAdminOrTeacher() + const isAdminOrCreator = this.props.isAdminOrCreator() const isSuperAdmin = this.props.isSuperAdmin() const hasGraduationModule = this.hasGraduationModule() const coursesId = this.props.match.params.coursesId @@ -646,7 +686,7 @@ class studentsList extends Component{ {/* { isAdmin && this.addTeacher()}>添加教师 } { isAdmin && this.addStudent()}>添加学生 } */} - { isAdmin && this.showChangeAdminModal()}>更换管理员} + { isAdminOrCreator && this.showChangeAdminModal()}>更换管理员} } diff --git a/public/react/src/modules/courses/shixunHomework/ShixunhomeWorkItem.js b/public/react/src/modules/courses/shixunHomework/ShixunhomeWorkItem.js index 557e1ca89..88d143011 100644 --- a/public/react/src/modules/courses/shixunHomework/ShixunhomeWorkItem.js +++ b/public/react/src/modules/courses/shixunHomework/ShixunhomeWorkItem.js @@ -354,8 +354,8 @@ class ShixunhomeWorkItem extends Component{ {/* {discussMessage.author.name} */} { discussMessage.author && {discussMessage.author} } - {discussMessage.commit_count===undefined?"":{discussMessage.commit_count} 已交} - {discussMessage.uncommit_count===undefined?"":{discussMessage.uncommit_count} 未交} + {discussMessage.commit_count===undefined?"":已开始做题 {discussMessage.commit_count}人} + {discussMessage.uncommit_count===undefined?"":未开始做题 {discussMessage.uncommit_count}人} {/*{discussMessage.replies_count} 3 未评*/} { @@ -381,7 +381,7 @@ class ShixunhomeWorkItem extends Component{ { discussMessage && discussMessage.upper_category_name && 22 }> - { {discussMessage.upper_category_name}} + { {discussMessage.upper_category_name}} } diff --git a/public/react/src/modules/courses/shixunHomework/Trainingjobsetting.js b/public/react/src/modules/courses/shixunHomework/Trainingjobsetting.js index 142d1e074..da1408d4a 100644 --- a/public/react/src/modules/courses/shixunHomework/Trainingjobsetting.js +++ b/public/react/src/modules/courses/shixunHomework/Trainingjobsetting.js @@ -1013,18 +1013,17 @@ class Trainingjobsetting extends Component { //完成效率评分占比 onChangeeffectiveness = (e) => { if( e.target.checked === true){ - this.state.latedeductiontwo=20; this.setState({ completionefficiencyscore: e.target.checked, work_efficiencys:e.target.checked, - latedeductiontwo: 20, + latedeductiontwo: 0, }) //均分比例 if(this.state.proportion==="均分比例"){ - this.Equalproportion(20); + this.Equalproportion(0); }else if(this.state.proportion==="经验值比例"){ - this.Empiricalvalueratio(20); + this.Empiricalvalueratio(0); } @@ -1725,15 +1724,15 @@ class Trainingjobsetting extends Component { completionefficiencyscore:true, work_efficiencys:this.state.work_efficiencys, unifiedsetting:this.state.unifiedsetting, - latedeductiontwo:20, + latedeductiontwo:this.state.latedeductiontwo, }); //均分比例 try { if(this.state.proportion==="均分比例"){ - this.Equalproportion(20); + this.Equalproportion(this.state.latedeductiontwo); }else if(this.state.proportion==="经验值比例"){ - this.Empiricalvalueratio(20); + this.Empiricalvalueratio(this.state.latedeductiontwo); } }catch (e) { @@ -1838,19 +1837,19 @@ class Trainingjobsetting extends Component { flagPageEditstwo:releasetime, flagPageEditsthrees:deadline, flagPageEditsfor:endtime, - completionefficiencyscore:true, + completionefficiencyscore:false, work_efficiencys:datas.data.work_efficiency, unifiedsetting:datas.data.unified_setting, - latedeductiontwo:20, + latedeductiontwo:datas.data.eff_score, }); //均分比例 // result.data.shixun_evaluation === 0 ? "均分比例" : result.data.shixun_evaluation === 1 ? "经验值比例" : result.data.shixun_evaluation === 2 ? try { if(datas.data.shixun_evaluation === 0){ - this.Equalproportion(20); + this.Equalproportion(datas.data.eff_score); }else if(datas.data.shixun_evaluation === 1){ - this.Empiricalvalueratio(20); + this.Empiricalvalueratio(datas.data.eff_score); } }catch (e) {