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 @@
+
+
+
+ 单位名称
+ 域名
+ 统计链接
+ 管理员
+ <%= sort_tag('创建时间', name: 'id', path: admins_laboratories_path) %>
+ 操作
+
+
+
+ <% if laboratories.present? %>
+ <% laboratories.each do |laboratory| %>
+
+ <%= render 'admins/laboratories/shared/laboratory_item', laboratory: laboratory %>
+
+ <% end %>
+ <% else %>
+ <%= render 'admins/shared/no_data_for_table' %>
+ <% end %>
+
+
+
+<%= render partial: 'admins/shared/paginate', locals: { objects: laboratories } %>
\ No newline at end of file
diff --git a/app/views/admins/laboratory_settings/show.html.erb b/app/views/admins/laboratory_settings/show.html.erb
new file mode 100644
index 000000000..120bba6cb
--- /dev/null
+++ b/app/views/admins/laboratory_settings/show.html.erb
@@ -0,0 +1,131 @@
+<% define_admin_breadcrumbs do %>
+ <% add_admin_breadcrumb('云上实验室', admins_laboratories_path) %>
+ <% add_admin_breadcrumb('单位定制') %>
+<% end %>
+
+
+ <%= simple_form_for(@laboratory, url: admins_laboratory_laboratory_setting_path(@laboratory), method: 'patch', html: { enctype: 'multipart/form-data' }) do |f| %>
+ <% setting = @laboratory.laboratory_setting %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <%= javascript_void_link '保存', class: 'btn btn-primary mr-3 px-4 submit-btn' %>
+ <%= link_to '取消', admins_laboratories_path, class: 'btn btn-secondary px-4' %>
+
+ <% end %>
+
\ No newline at end of file
diff --git a/app/views/admins/laboratory_users/create.js.erb b/app/views/admins/laboratory_users/create.js.erb
new file mode 100644
index 000000000..f43fd7887
--- /dev/null
+++ b/app/views/admins/laboratory_users/create.js.erb
@@ -0,0 +1,4 @@
+$('.modal.admin-add-laboratory-user-modal').modal('hide');
+$.notify({ message: '操作成功' });
+
+$('.laboratory-list-table .laboratory-item-<%= current_laboratory.id %>').html("<%= j(render partial: 'admins/laboratories/shared/laboratory_item', locals: { laboratory: current_laboratory }) %>")
\ No newline at end of file
diff --git a/app/views/admins/laboratory_users/destroy.js.erb b/app/views/admins/laboratory_users/destroy.js.erb
new file mode 100644
index 000000000..16ef62910
--- /dev/null
+++ b/app/views/admins/laboratory_users/destroy.js.erb
@@ -0,0 +1,2 @@
+$.notify({ message: '操作成功' });
+$('.laboratory-list-container .laboratory-item-<%= current_laboratory.id %> .laboratory-user-item-<%= @laboratory_user.user_id %>').remove();
\ No newline at end of file
diff --git a/app/views/admins/shared/_sidebar.html.erb b/app/views/admins/shared/_sidebar.html.erb
index 553820e69..145910928 100644
--- a/app/views/admins/shared/_sidebar.html.erb
+++ b/app/views/admins/shared/_sidebar.html.erb
@@ -33,23 +33,13 @@
<%= sidebar_item_group('#schools-submenu', '单位管理', icon: 'building') do %>
<%= sidebar_item(admins_schools_path, '单位列表', icon: 'university', controller: 'admins-schools') %>
<%= sidebar_item(admins_departments_path, '部门列表', icon: 'sitemap', controller: 'admins-departments') %>
+ <%= sidebar_item(admins_laboratories_path, '云上实验室', icon: 'cloud', controller: 'admins-laboratories') %>
<% end %>
-
- <%#= sidebar_item_group('#course-submenu', '课堂+', icon: 'mortar-board') do %>
-
-
-
-
- <%# end %>
-
-
<%= sidebar_item_group('#user-submenu', '用户', icon: 'user') do %>
<%= sidebar_item(admins_users_path, '用户列表', icon: 'user', controller: 'admins-users') %>
-
-
<% end %>
diff --git a/app/views/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) {