diff --git a/app/assets/javascripts/admins/laboratory_shixuns/index.js b/app/assets/javascripts/admins/laboratory_shixuns/index.js
index f7f41c4d8..5fd4c2d72 100644
--- a/app/assets/javascripts/admins/laboratory_shixuns/index.js
+++ b/app/assets/javascripts/admins/laboratory_shixuns/index.js
@@ -63,16 +63,23 @@ $(document).on('turbolinks:load', function() {
       theme: 'bootstrap4',
       placeholder: '请输入实训名称/创建者检索',
       multiple: true,
-      minimumInputLength: 1,
+      closeOnSelect: false,
       ajax: {
         delay: 500,
         url: '/admins/laboratories/' + laboratoryId + '/shixuns_for_select',
         dataType: 'json',
         data: function(params){
-          return { keyword: params.term };
+          return { keyword: params.term, page: params.page || 1, per_page: 20 };
         },
-        processResults: function(data){
-          return { results: data.shixuns }
+        processResults: function(data, params){
+          params.page = params.page || 1;
+
+          return {
+            results: data.shixuns,
+            pagination: {
+              more: (params.page * 20) < data.count
+            }
+          };
         }
       },
       templateResult: function (item) {
diff --git a/app/assets/javascripts/admins/laboratory_subjects/index.js b/app/assets/javascripts/admins/laboratory_subjects/index.js
index fda0d075f..b5fa3bffb 100644
--- a/app/assets/javascripts/admins/laboratory_subjects/index.js
+++ b/app/assets/javascripts/admins/laboratory_subjects/index.js
@@ -7,6 +7,7 @@ $(document).on('turbolinks:load', function() {
     $searchForm.find('.school-select').select2({
       theme: 'bootstrap4',
       placeholder: '请选择创建者单位',
+      allowClear: true,
       minimumInputLength: 1,
       ajax: {
         delay: 500,
@@ -85,16 +86,23 @@ $(document).on('turbolinks:load', function() {
       theme: 'bootstrap4',
       placeholder: '请输入课程名称/创建者检索',
       multiple: true,
-      minimumInputLength: 1,
+      closeOnSelect: false,
       ajax: {
         delay: 500,
         url: '/admins/laboratories/' + laboratoryId + '/subjects_for_select',
         dataType: 'json',
         data: function(params){
-          return { keyword: params.term };
+          return { keyword: params.term, page: params.page || 1, per_page: 20 }
         },
-        processResults: function(data){
-          return { results: data.subjects }
+        processResults: function(data, params){
+          params.page = params.page || 1;
+
+          return {
+            results: data.subjects,
+            pagination: {
+              more: (params.page * 20) < data.count
+            }
+          };
         }
       },
       templateResult: function (item) {
diff --git a/app/assets/javascripts/cooperative/laboratory_shixuns/index.js b/app/assets/javascripts/cooperative/laboratory_shixuns/index.js
new file mode 100644
index 000000000..ef6018a8a
--- /dev/null
+++ b/app/assets/javascripts/cooperative/laboratory_shixuns/index.js
@@ -0,0 +1,60 @@
+$(document).on('turbolinks:load', function() {
+  if ($('body.cooperative-laboratory-shixuns-index-page').length > 0) {
+    var $searchForm = $('.laboratory-shixun-list-form .search-form');
+
+    $searchForm.find('select#tag_id').select2({
+      placeholder: "请选择",
+      allowClear: true
+    });
+
+    // 上传图片
+    $('.modal.cooperative-upload-file-modal').on('upload:success', function (e, data) {
+      var $imageElement = $('.shixun-image-' + data.source_id);
+      if($imageElement.length === 0) return;
+      $imageElement.attr('src', data.url);
+      $imageElement.show();
+      $imageElement.next().html('重新上传');
+    });
+
+    // 定义状态切换监听事件
+    var defineStatusChangeFunc = function (doElement, undoElement, url, callback) {
+      $('.laboratory-shixun-list-container').on('click', doElement, function () {
+        var $doAction = $(this);
+        var $undoAction = $doAction.siblings(undoElement);
+
+        var laboratoryShixunId = $doAction.data('id');
+        customConfirm({
+          content: '确认进行该操作吗?',
+          ok: function () {
+            $.ajax({
+              url: '/cooperative/laboratory_shixuns/' + laboratoryShixunId + url,
+              method: 'POST',
+              dataType: 'json',
+              success: function () {
+                show_success_flash();
+                $doAction.hide();
+                $undoAction.show();
+                if (callback && typeof callback === "function") {
+                  callback(laboratoryShixunId, url);
+                }
+              }
+            });
+          }
+        });
+      });
+    }
+
+    // 首页展示与取消首页展示
+    var homepageShowCallback = function (laboratoryShixunId, url) {
+      var $laboratoryShixunItem = $('.laboratory-shixun-list-container').find('.laboratory-shixun-item-' + laboratoryShixunId);
+
+      if (url === '/homepage') {
+        $laboratoryShixunItem.find('.homepage-badge').show();
+      } else {
+        $laboratoryShixunItem.find('.homepage-badge').hide();
+      }
+    }
+    defineStatusChangeFunc('.homepage-show-action', '.homepage-hide-action', '/homepage', homepageShowCallback);
+    defineStatusChangeFunc('.homepage-hide-action', '.homepage-show-action', '/cancel_homepage', homepageShowCallback);
+  }
+})
\ No newline at end of file
diff --git a/app/assets/javascripts/cooperative/laboratory_subjects/index.js b/app/assets/javascripts/cooperative/laboratory_subjects/index.js
new file mode 100644
index 000000000..639c5d5df
--- /dev/null
+++ b/app/assets/javascripts/cooperative/laboratory_subjects/index.js
@@ -0,0 +1,83 @@
+$(document).on('turbolinks:load', function() {
+  if ($('body.cooperative-laboratory-subjects-index-page').length > 0) {
+    var $searchForm = $('.laboratory-subject-list-form .search-form');
+
+    // ************** 学校选择 *************
+    $searchForm.find('.school-select').select2({
+      theme: 'bootstrap4',
+      placeholder: '请选择创建者单位',
+      allowClear: true,
+      minimumInputLength: 1,
+      ajax: {
+        delay: 500,
+        url: '/api/schools/search.json',
+        dataType: 'json',
+        data: function (params) {
+          return {keyword: params.term};
+        },
+        processResults: function (data) {
+          return {results: data.schools}
+        }
+      },
+      templateResult: function (item) {
+        if (!item.id || item.id === '') return item.text;
+        return item.name;
+      },
+      templateSelection: function (item) {
+        if (item.id) {
+        }
+        return item.name || item.text;
+      }
+    });
+
+    // 上传图片
+    $('.modal.cooperative-upload-file-modal').on('upload:success', function (e, data) {
+      var $imageElement = $('.subject-image-' + data.source_id);
+      if($imageElement.length === 0) return;
+      $imageElement.attr('src', data.url);
+      $imageElement.show();
+      $imageElement.next().html('重新上传');
+    });
+
+    // 定义状态切换监听事件
+    var defineStatusChangeFunc = function (doElement, undoElement, url, callback) {
+      $('.laboratory-subject-list-container').on('click', doElement, function () {
+        var $doAction = $(this);
+        var $undoAction = $doAction.siblings(undoElement);
+
+        var laboratorySubjectId = $doAction.data('id');
+        customConfirm({
+          content: '确认进行该操作吗?',
+          ok: function () {
+            $.ajax({
+              url: '/cooperative/laboratory_subjects/' + laboratorySubjectId + url,
+              method: 'POST',
+              dataType: 'json',
+              success: function () {
+                show_success_flash();
+                $doAction.hide();
+                $undoAction.show();
+                if (callback && typeof callback === "function") {
+                  callback(laboratorySubjectId, url);
+                }
+              }
+            });
+          }
+        });
+      });
+    }
+
+    // 首页展示与取消首页展示
+    var homepageShowCallback = function (laboratoryShixunId, url) {
+      var $laboratoryShixunItem = $('.laboratory-subject-list-container').find('.laboratory-subject-item-' + laboratoryShixunId);
+
+      if (url === '/homepage') {
+        $laboratoryShixunItem.find('.homepage-badge').show();
+      } else {
+        $laboratoryShixunItem.find('.homepage-badge').hide();
+      }
+    }
+    defineStatusChangeFunc('.homepage-show-action', '.homepage-hide-action', '/homepage', homepageShowCallback);
+    defineStatusChangeFunc('.homepage-hide-action', '.homepage-show-action', '/cancel_homepage', homepageShowCallback);
+  }
+})
\ No newline at end of file
diff --git a/app/assets/javascripts/cooperative/modals/cooperative-edit-subject-modal.js b/app/assets/javascripts/cooperative/modals/cooperative-edit-subject-modal.js
new file mode 100644
index 000000000..414d04ed2
--- /dev/null
+++ b/app/assets/javascripts/cooperative/modals/cooperative-edit-subject-modal.js
@@ -0,0 +1,18 @@
+$(document).on('turbolinks:load', function () {
+	$('.cooperative-modal-container').on('show.bs.modal', '.modal.cooperative-edit-subject-modal', function () {
+		var $modal = $('.modal.cooperative-edit-subject-modal');
+		var $form = $modal.find('form.cooperative-edit-subject-form');
+
+		$modal.on('click', '.submit-btn', function () {
+			$form.find('.error').html('');
+			var url = $form.attr('action');
+
+			$.ajax({
+				method: 'PATCH',
+				dataType: 'script',
+				url: url,
+				data: $form.serialize()
+			});
+		});
+	})
+});
diff --git a/app/assets/javascripts/cooperative/modals/upload-file-modal.js b/app/assets/javascripts/cooperative/modals/upload-file-modal.js
index 835ccd383..302c534b6 100644
--- a/app/assets/javascripts/cooperative/modals/upload-file-modal.js
+++ b/app/assets/javascripts/cooperative/modals/upload-file-modal.js
@@ -42,7 +42,7 @@ $(document).on('turbolinks:load', function() {
         $.ajax({
           method: 'POST',
           dataType: 'json',
-          url: '/cooperatives/files?' + formDataString,
+          url: '/cooperative/files?' + formDataString,
           data: new FormData($form[0]),
           processData: false,
           contentType: false,
diff --git a/app/assets/stylesheets/admins/common.scss b/app/assets/stylesheets/admins/common.scss
index aabe6085c..3437c3049 100644
--- a/app/assets/stylesheets/admins/common.scss
+++ b/app/assets/stylesheets/admins/common.scss
@@ -47,6 +47,12 @@
     }
   }
 
+  .image-preview-container {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+  }
+
   .action-container {
     & > .action {
       padding: 0 3px;
diff --git a/app/assets/stylesheets/cooperative/common.scss b/app/assets/stylesheets/cooperative/common.scss
index 488dd4caa..8e04e2328 100644
--- a/app/assets/stylesheets/cooperative/common.scss
+++ b/app/assets/stylesheets/cooperative/common.scss
@@ -47,6 +47,12 @@
     }
   }
 
+  .image-preview-container {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+  }
+
   .action-container {
     & > .action {
       padding: 0 3px;
diff --git a/app/controllers/admins/base_controller.rb b/app/controllers/admins/base_controller.rb
index e2e3babae..ad56afbf7 100644
--- a/app/controllers/admins/base_controller.rb
+++ b/app/controllers/admins/base_controller.rb
@@ -6,6 +6,7 @@ class Admins::BaseController < ApplicationController
   layout 'admin'
 
   skip_before_action :verify_authenticity_token
+  skip_before_action :setup_laboratory
 
   before_action :require_login, :require_admin!
 
diff --git a/app/controllers/admins/competition_prize_users_controller.rb b/app/controllers/admins/competition_prize_users_controller.rb
index 50d11e211..34dbc1f76 100644
--- a/app/controllers/admins/competition_prize_users_controller.rb
+++ b/app/controllers/admins/competition_prize_users_controller.rb
@@ -6,6 +6,16 @@ class Admins::CompetitionPrizeUsersController < Admins::BaseController
     include_class = [:competition_team, :competition_prize, :approver,
                      user: [:process_real_name_apply, :process_professional_apply, user_extension: :school]]
     @prize_users = paginate(prize_users.preload(include_class))
+
+    respond_to do |format|
+      format.js
+      format.html
+      format.xlsx do
+        @all_prize_users = prize_users
+        filename = "#{@competition.name}竞赛获奖人信息列表_#{Time.current.strftime('%Y%m%d%H%M%S')}.xlsx"
+        render xlsx: 'index', filename: filename
+      end
+    end
   end
 
   def create
diff --git a/app/controllers/admins/enroll_lists_controller.rb b/app/controllers/admins/enroll_lists_controller.rb
index fb7cec9b0..f9fac9316 100644
--- a/app/controllers/admins/enroll_lists_controller.rb
+++ b/app/controllers/admins/enroll_lists_controller.rb
@@ -12,10 +12,6 @@ class Admins::EnrollListsController < Admins::BaseController
     respond_to do |format|
       format.js
       format.html
-      format.xls{
-        filename = "#{@competition.name}竞赛报名列表_#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}.xls"
-        send_data(shixun_list_xls(shixuns), :type => 'application/octet-stream', :filename => filename_for_content_disposition(filename))
-      }
     end
   end
 
diff --git a/app/controllers/admins/laboratories_controller.rb b/app/controllers/admins/laboratories_controller.rb
index 3bc9383cc..716275468 100644
--- a/app/controllers/admins/laboratories_controller.rb
+++ b/app/controllers/admins/laboratories_controller.rb
@@ -1,7 +1,6 @@
 class Admins::LaboratoriesController < Admins::BaseController
   def index
-    params[:sort_by] = params[:sort_by].presence || 'id'
-    params[:sort_direction] = params[:sort_direction].presence || 'desc'
+    default_sort('id', 'desc')
 
     laboratories = Admins::LaboratoryQuery.call(params)
     @laboratories = paginate laboratories.preload(:school, :laboratory_users)
@@ -27,10 +26,12 @@ class Admins::LaboratoriesController < Admins::BaseController
 
     keyword = params[:keyword].to_s.strip
     if keyword.present?
-      like_sql = 'shixuns.name LIKE :keyword OR CONCAT(users.lastname, users.firstname) LIKE :keyword'
-      shixuns = shixuns.joins(:user).where(like_sql, keyword: "%#{keyword}%")
+      like_sql = 'shixuns.name LIKE :keyword OR CONCAT(users.lastname, users.firstname) LIKE :keyword '\
+                 'OR mirror_repositories.name LIKE :keyword'
+      shixuns = shixuns.joins(:user, :mirror_repositories).where(like_sql, keyword: "%#{keyword}%")
     end
 
+    @count = shixuns.count
     @shixuns = paginate(shixuns.includes(:user))
   end
 
@@ -45,6 +46,7 @@ class Admins::LaboratoriesController < Admins::BaseController
       subjects = subjects.joins(:user).where(like_sql, keyword: "%#{keyword}%")
     end
 
+    @count = subjects.count
     @subjects = paginate(subjects.includes(:user))
   end
 
diff --git a/app/controllers/concerns/base/render_helper.rb b/app/controllers/concerns/base/render_helper.rb
index e0aa49ac1..4d246c6cc 100644
--- a/app/controllers/concerns/base/render_helper.rb
+++ b/app/controllers/concerns/base/render_helper.rb
@@ -8,6 +8,7 @@ module Base::RenderHelper
 
   def render_forbidden
     render_by_format(html: -> { current_user&.business? ? render('shared/403') : redirect_to('/403') },
+                     js: -> { render_js_error(I18n.t('error.forbidden'), type: :notify) },
                      json: -> { render status: 403, json: { messages: I18n.t('error.forbidden') } } )
   end
 
diff --git a/app/controllers/cooperative/files_controller.rb b/app/controllers/cooperative/files_controller.rb
index 56710a968..20c0b0d5f 100644
--- a/app/controllers/cooperative/files_controller.rb
+++ b/app/controllers/cooperative/files_controller.rb
@@ -1,5 +1,5 @@
 class Cooperative::FilesController < Cooperative::BaseController
-  before_action :convert_file!, only: [:create]
+  before_action :convert_file!, :check_permission!, only: [:create]
 
   def create
     File.delete(file_path) if File.exist?(file_path) # 删除之前的文件
@@ -29,6 +29,22 @@ class Cooperative::FilesController < Cooperative::BaseController
     render_error(ex.message)
   end
 
+  def check_permission!
+    permission =
+      case params[:source_type].to_s
+      when '' then false
+      when 'Shixun' then
+        current_laboratory.laboratory_shixuns.exists?(ownership: true, shixun_id: params[:source_id])
+      when 'Subject' then
+        current_laboratory.laboratory_subjects.exists?(ownership: true, subject_id: params[:source_id])
+      else true
+      end
+
+    return if permission
+
+    render_forbidden
+  end
+
   def file_path
     @_file_path ||= begin
       case params[:source_type].to_s
diff --git a/app/controllers/cooperative/laboratory_shixuns_controller.rb b/app/controllers/cooperative/laboratory_shixuns_controller.rb
new file mode 100644
index 000000000..e428416da
--- /dev/null
+++ b/app/controllers/cooperative/laboratory_shixuns_controller.rb
@@ -0,0 +1,39 @@
+class Cooperative::LaboratoryShixunsController < Cooperative::BaseController
+  before_action :check_shixun_ownership!, only: [:edit, :update]
+
+  helper_method :current_laboratory_shixun
+
+  def index
+    laboratory_shixuns = Admins::LaboratoryShixunQuery.call(current_laboratory, params)
+    @laboratory_shixuns = paginate laboratory_shixuns.includes(shixun: %i[tag_repertoires user])
+  end
+
+  def edit
+  end
+
+  def update
+
+  end
+
+  def homepage
+    current_laboratory_shixun.update!(homepage: true)
+    render_ok
+  end
+
+  def cancel_homepage
+    current_laboratory_shixun.update!(homepage: false)
+    render_ok
+  end
+
+  private
+
+  def current_laboratory_shixun
+    @_current_laboratory_shixun ||= current_laboratory.laboratory_shixuns.find(params[:id])
+  end
+
+  def check_shixun_ownership!
+    return if current_laboratory_shixun.ownership?
+
+    render_forbidden
+  end
+end
\ No newline at end of file
diff --git a/app/controllers/cooperative/laboratory_subjects_controller.rb b/app/controllers/cooperative/laboratory_subjects_controller.rb
new file mode 100644
index 000000000..77088c223
--- /dev/null
+++ b/app/controllers/cooperative/laboratory_subjects_controller.rb
@@ -0,0 +1,46 @@
+class Cooperative::LaboratorySubjectsController < Cooperative::BaseController
+  before_action :check_subject_ownership!, only: [:edit, :update]
+
+  helper_method :current_laboratory_subject
+
+  def index
+    laboratory_subjects = Admins::LaboratorySubjectQuery.call(current_laboratory, params)
+
+    includes_tables = { subject: [:repertoire, :subject_level_system, user: {user_extension: :school}] }
+    @laboratory_subjects = paginate(laboratory_subjects.includes(includes_tables))
+  end
+
+  def edit
+    @laboratory_subject = current_laboratory_subject
+  end
+
+  def update
+    current_laboratory_subject.subject.update!(update_params)
+  end
+
+  def homepage
+    current_laboratory_subject.update!(homepage: true)
+    render_ok
+  end
+
+  def cancel_homepage
+    current_laboratory_subject.update!(homepage: false)
+    render_ok
+  end
+
+  private
+
+  def current_laboratory_subject
+    @_current_laboratory_subject ||= current_laboratory.laboratory_subjects.find(params[:id])
+  end
+
+  def check_subject_ownership!
+    return if current_laboratory_subject.ownership?
+
+    render_forbidden
+  end
+
+  def update_params
+    params.require(:laboratory_subject).permit(:repertoire_id, :subject_level_system_id)
+  end
+end
\ No newline at end of file
diff --git a/app/controllers/courses_controller.rb b/app/controllers/courses_controller.rb
index 5dd001f5a..dd8c7c5c0 100644
--- a/app/controllers/courses_controller.rb
+++ b/app/controllers/courses_controller.rb
@@ -1141,7 +1141,7 @@ class CoursesController < ApplicationController
             # 如果在该课堂已经存在学生身份,且邀请码为分班邀请码,则将其直接加入分班
             existing_student.update_attributes(course_group_id: course_group.id) if course_group.present?
           else
-            correspond_teacher_exist = current_user.teacher_of_course? course
+            correspond_teacher_exist = current_user.none_admin_teacher_of_course? course
             new_student = CourseMember.new(user_id: current_user.id, course_id: course.id, role: 4)
             new_student.is_active = 0 if correspond_teacher_exist
 
diff --git a/app/controllers/exercises_controller.rb b/app/controllers/exercises_controller.rb
index cb32f7bb3..681684295 100644
--- a/app/controllers/exercises_controller.rb
+++ b/app/controllers/exercises_controller.rb
@@ -514,6 +514,9 @@ class ExercisesController < ApplicationController
             if exercise_group.present? && (exercise_group.first.publish_time < Time.now) && (exercise_publish_time != exercise_group.first.publish_time)
               error_count += 1
             end
+            if exercise_group.present? && (exercise_group.first.publish_time < Time.now && exercise_group.first.end_time > Time.now) && (exercise_end_time < Time.now)
+              error_count += 1
+            end
             if error_count == 0
               common_group = exercise_groups_ids & course_id #传入的班级与问卷已存在的班级的交集,即表示已有分班的
               new_group_ids = course_id - common_group  #新传入的班级id
@@ -529,12 +532,12 @@ class ExercisesController < ApplicationController
                   if the_group_setting_status == 2
                     ex_group_params = {
                       :publish_time => the_group_setting.publish_time,
-                      :end_time => exercise_end_time
+                      :end_time => exercise_end_time < Time.now ? the_group_setting.end_time : exercise_end_time
                     }
                   elsif the_group_setting_status == 3
                     ex_group_params = {
                       :publish_time => the_group_setting.publish_time,
-                      :end_time => the_group_setting.end_time
+                      :end_time => exercise_end_time
                     }
                   end
                   the_group_setting.update_attributes!(ex_group_params)
@@ -558,7 +561,7 @@ class ExercisesController < ApplicationController
 
           if error_count > 0
             error_count == 0
-            normal_status(-1,"已发布/已截止的试卷不允许修改时间")
+            normal_status(-1,"试卷发布/截止时间不能小于当前时间")
           else
             # 未发布的分班设置才能删除
             if old_exercise_groups.size > 0
@@ -609,13 +612,27 @@ class ExercisesController < ApplicationController
   def adjust_score
     exercise_user = @exercise.exercise_users.find_by!(user_id: params[:user_id])
     tip_exception("已提交的作品请去评阅页进行调分") if exercise_user.commit_status == 1
-    tip_exception("分数不能为空") if params[:score].blank?
-    tip_exception("分数不能超过0-#{@exercise.question_scores}") if params[:score].to_f < 0 || params[:score].to_f.round(1) > @exercise.question_scores.round(1)
+    if @exercise.subjective_score > 0
+      tip_exception("主观题成绩不能为空") if params[:subject_score].blank?
+      tip_exception("主观题成绩不能小于零") if params[:subject_score].to_f < 0
+      tip_exception("主观题成绩不能大于总分值:#{@exercise.subjective_score}分") if params[:subject_score].to_f.round(1) > @exercise.subjective_score.round(1)
+    end
+
+    if @exercise.objective_score > 0
+      tip_exception("客观题成绩不能为空") if params[:objective_score].blank?
+      tip_exception("客观题成绩不能小于零") if params[:objective_score].to_f < 0
+      tip_exception("客观题成绩不能大于总分值:#{@exercise.objective_score}分") if params[:objective_score].to_f.round(1) > @exercise.objective_score.round(1)
+    end
 
     ActiveRecord::Base.transaction do
       start_at_time = exercise_user.start_at || Time.now
-      exercise_user.update_attributes!(start_at: start_at_time, end_at: Time.now, status: 1, commit_status: 1, score: params[:score].to_f.round(2), commit_method: 5)
-      ExerciseUserScore.create!(exercise_id: @exercise.id, exercise_user_id: exercise_user.id, score: params[:score], comment: params[:comment])
+      subjective_score = @exercise.subjective_score > 0 ? params[:subject_score].to_f.round(2) : 0
+      objective_score = @exercise.objective_score > 0 ? params[:objective_score].to_f.round(2) : 0
+      score = subjective_score + objective_score
+      exercise_user.update_attributes!(start_at: start_at_time, end_at: Time.now, status: 1, commit_status: 1, score: score,
+                                       subjective_score: subjective_score, objective_score: objective_score, commit_method: 5)
+      ExerciseUserScore.create!(exercise_id: @exercise.id, exercise_user_id: exercise_user.id,
+                                subjective_score: subjective_score, objective_score: objective_score)
       normal_status("操作成功")
     end
   end
diff --git a/app/controllers/graduation_tasks_controller.rb b/app/controllers/graduation_tasks_controller.rb
index 864eac3f5..e8ad221be 100644
--- a/app/controllers/graduation_tasks_controller.rb
+++ b/app/controllers/graduation_tasks_controller.rb
@@ -149,6 +149,7 @@ class GraduationTasksController < ApplicationController
         else
           respond_to do |format|
             format.xlsx{
+              set_export_cookies
               graduation_work_to_xlsx(@work_excel,@task,current_user)
               task_export_name_ = "#{current_user.real_name}_#{@course.name}_#{@task.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
               render xlsx: "#{task_export_name_.strip}",template: "graduation_tasks/tasks_list.xlsx.axlsx",locals: {table_columns:@head_cells_column, task_users:@task_cells_column}
diff --git a/app/controllers/graduation_works_controller.rb b/app/controllers/graduation_works_controller.rb
index d50885313..67f21e85c 100644
--- a/app/controllers/graduation_works_controller.rb
+++ b/app/controllers/graduation_works_controller.rb
@@ -379,8 +379,9 @@ class GraduationWorksController < ApplicationController
   end
 
   def adjust_score
-    tip_exception("分数不能为空") if params[:score].blank?
-    tip_exception("分数不能超过0-100") if params[:score].to_f < 0 || params[:score].to_f > 100
+    tip_exception("成绩不能为空") if params[:score].blank?
+    tip_exception("成绩不能小于零") if params[:score].to_f < 0
+    tip_exception("成绩不能大于100") if params[:score].to_f.round(1) > 100
     ActiveRecord::Base.transaction do
       begin
         # 分数不为空的历史评阅都置为失效
diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb
index 48c942e65..ba07d42c9 100644
--- a/app/controllers/home_controller.rb
+++ b/app/controllers/home_controller.rb
@@ -26,12 +26,27 @@ class HomeController < ApplicationController
       @rep_list << {rep_id: rep.id, rep_name: rep.name, sub_rep_list: sub_rep_list}
     end
 
-    @shixuns = Shixun.where(homepage_show: 1).includes(:tag_repertoires, :challenges).limit(8)
+    shixuns = current_laboratory.shixuns
+    subjects = current_laboratory.subjects
+
+    if current_laboratory.main_site?
+      shixuns = shixuns.where(homepage_show: true)
+      subjects = subjects.where(homepage_show: true)
+    else
+      shixuns = shixuns.where(laboratory_shixuns: { homepage: true })
+      subjects = subjects.where(laboratory_subjects: { homepage: true })
+    end
+
+    @shixuns = shixuns.includes(:tag_repertoires, :challenges).limit(8)
+    @subjects = subjects.includes(:repertoire, :shixuns).limit(8)
 
-    @subjects = Subject.where(homepage_show: 1).includes(:shixuns, :repertoire).limit(8)
+    @main_shixuns = Shixun.where(homepage_show: true).includes(:tag_repertoires, :challenges).limit(8)
+    @main_subjects = Subject.where(homepage_show: true).includes(:shixuns, :repertoire).limit(8)
 
-    @tea_users = User.where(homepage_teacher: 1).includes(:user_extension).limit(10).order("experience desc")
-    @stu_users = User.where(is_test: 0).includes(:user_extension).where(user_extensions: {identity: 1}).limit(10).order("experience desc")
+    if current_laboratory.main_site?
+      @tea_users = User.where(homepage_teacher: 1).includes(:user_extension).limit(10).order("experience desc")
+      @stu_users = User.where(is_test: 0).includes(:user_extension).where(user_extensions: {identity: 1}).limit(10).order("experience desc")
+    end
   end
 
   def search
diff --git a/app/controllers/homework_commons_controller.rb b/app/controllers/homework_commons_controller.rb
index 6d627df33..4bf80765c 100644
--- a/app/controllers/homework_commons_controller.rb
+++ b/app/controllers/homework_commons_controller.rb
@@ -160,7 +160,7 @@ class HomeworkCommonsController < ApplicationController
 
         # 作品状态 0: 未提交, 1 按时提交, 2 延迟提交
         if params[:work_status].present?
-          params_work_status = request.get? ? params[:work_status].split(",") : params[:work_status]
+          params_work_status = params[:work_status]
           work_status = params_work_status.map{|status| status.to_i}
           all_student_works = @student_works.left_joins(:myshixun)
           @student_works = all_student_works.where(work_status: work_status)
@@ -171,7 +171,7 @@ class HomeworkCommonsController < ApplicationController
 
         # 分班情况
         unless params[:course_group].blank?
-          group_ids = request.get? ? params[:course_group].split(",") : params[:course_group]
+          group_ids = params[:course_group]
           group_user_ids = @course.students.where(course_group_id: group_ids).pluck(:user_id)
           # 有分组只可能是老师身份查看列表
           @student_works = @student_works.where(user_id: group_user_ids)
@@ -179,9 +179,9 @@ class HomeworkCommonsController < ApplicationController
 
         if @homework.homework_type == "group" && !params[:member_work].blank?
           if params[:member_work].to_i == 1
-            @student_works = @student_works.where("user_id = commit_user_id")
+            @student_works = @student_works.where("student_works.user_id = commit_user_id")
           elsif params[:member_work].to_i == 0
-            @student_works = @student_works.where("user_id != commit_user_id")
+            @student_works = @student_works.where("student_works.user_id != commit_user_id")
           end
         end
 
diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb
index c1e08d440..57a5c7fad 100644
--- a/app/controllers/messages_controller.rb
+++ b/app/controllers/messages_controller.rb
@@ -103,14 +103,19 @@ class MessagesController < ApplicationController
     return normal_status(403, "您没有权限进行该操作") if current_user != @message.author && !current_user.teacher_of_course?(@message.board.course)
 
     begin
-      h = {is_md: true}
+      board = @message.board&.course&.boards.find_by!(id: params[:select_board_id])
+
+      email_notify = @message.email_notify ? 1 : @message.board&.course.email_notify && params[:email_notify]
+      send_email = !@message.email_notify && email_notify
+      h = {is_md: true, email_notify: email_notify, board_id: board&.id}
       m_params = message_params.merge(h)
       @message.update_attributes(m_params)
       Attachment.associate_container(params[:attachment_ids], @message.id, @message.class.name)
       @message.update_content(params[:content])
+      notify_course_students(@message, @message.board&.course) if send_email
     rescue Exception => e
       uid_logger_error(e.message)
-      tip_exception("修改失败")
+      tip_exception(e.message)
       raise ActiveRecord::Rollback
     end
   end
@@ -123,6 +128,7 @@ class MessagesController < ApplicationController
       @message.author = current_user
       @message.board_id = params[:select_board_id]
       @message.message_detail_attributes = {content: params[:content]}
+      @message.email_notify = @board.course.email_notify && params[:email_notify] ? 1 : 0
       @message.save!
       Attachment.associate_container(params[:attachment_ids], @message.id, @message.class.name)
       if @board.course.email_notify && params[:email_notify]
@@ -189,7 +195,7 @@ class MessagesController < ApplicationController
 
   private
   def validate_sort_type
-    normal_status(2, "参数sort_tyope暂时只支持 'time', 'hot'两种") if params.has_key?(:sort_type) && !SORT_TYPE.include?(params[:sort_type].strip)
+    normal_status(2, "参数sort_type暂时只支持 'time', 'hot'两种") if params.has_key?(:sort_type) && !SORT_TYPE.include?(params[:sort_type].strip)
   end
 
   def find_message
@@ -207,7 +213,7 @@ class MessagesController < ApplicationController
 
   def notify_course_students message, course
     course.students.includes(:user).each do |student|
-      UserMailer.course_message_email(student&.user&.mail, message.id).deliver_now if student&.user&.mail
+      UserMailer.course_message_email(student&.user&.mail, message.id).deliver_later if student&.user&.mail
     end
   end
 end
diff --git a/app/controllers/shixuns_controller.rb b/app/controllers/shixuns_controller.rb
index e2b78ee4b..cfe6141ee 100644
--- a/app/controllers/shixuns_controller.rb
+++ b/app/controllers/shixuns_controller.rb
@@ -33,7 +33,12 @@ class ShixunsController < ApplicationController
           current_user.my_shixuns
         else
           Shixun.unhidden
-        end
+				end
+
+		## 云上实验室过滤
+		unless current_laboratory.main_site?
+			@shixuns = @shixuns.joins(:laboratory_shixuns).where(laboratory_shixuns: { laboratory_id: current_laboratory.id })
+		end
 
 		## 方向
 		if params[:tag_level].present? && params[:tag_id].present?
diff --git a/app/controllers/student_works_controller.rb b/app/controllers/student_works_controller.rb
index c45bca268..060656a88 100644
--- a/app/controllers/student_works_controller.rb
+++ b/app/controllers/student_works_controller.rb
@@ -524,19 +524,23 @@ class StudentWorksController < ApplicationController
     @echart_data = student_efficiency(@homework, @work)
     @myself_eff = @echart_data[:efficiency_list].find { |item| item.last == @user.id }
     @myself_consume = @echart_data[:consume_list].find { |item| item.last == @user.id }
-
     filename_ = "#{@use&.student_id}_#{@use&.real_name}_#{@shixun&.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
     filename = Base64.urlsafe_encode64(filename_.strip)
     stylesheets = %w(shixun_work/shixun_work.css shared/codemirror.css)
-    render pdf: 'shixun_work/shixun_work', filename: filename, stylesheets: stylesheets, disposition: 'inline', type:"pdf_attachment.content_type",stream:false
+    if params[:export].present? && params[:export]
+      normal_status(0,"正在下载中")
+    else
+      set_export_cookies
+      render pdf: 'shixun_work/shixun_work', filename: filename, stylesheets: stylesheets, disposition: 'inline', type:"pdf_attachment.content_type",stream:false
+    end
+    # render pdf: 'shixun_work/shixun_work', filename: filename, stylesheets: stylesheets, disposition: 'inline', type:"pdf_attachment.content_type",stream:false
   end
 
   # 作品调分
   def adjust_score
-    tip_exception("分数不能为空") if params[:score].blank?
-    tip_exception("分数不能超过0-100") if @homework.homework_type != "practice" && (params[:score].to_f < 0 || params[:score].to_f.round(1) > 100.round(1))
-    tip_exception("已提交的作品请去评阅页进行调分") if @homework.homework_type == "practice" && @work.work_status > 0
-    tip_exception("分数不能超过总分值#{@homework.total_score}") if @homework.homework_type == "practice" && (params[:score].to_f < 0 || params[:score].to_f.round(1) > @homework.total_score.round(1))
+    tip_exception("成绩不能为空") if params[:score].blank?
+    tip_exception("成绩不能小于零") if params[:score].to_f < 0
+    tip_exception("成绩不能大于100") if params[:score].to_f.round(1) > 100
     ActiveRecord::Base.transaction do
       begin
         # 分数不为空的历史评阅都置为失效
diff --git a/app/controllers/subjects_controller.rb b/app/controllers/subjects_controller.rb
index 9c6980b69..b76db8a20 100644
--- a/app/controllers/subjects_controller.rb
+++ b/app/controllers/subjects_controller.rb
@@ -23,16 +23,17 @@ class SubjectsController < ApplicationController
 
     # 最热排序
     if reorder == "myshixun_count"
+      laboratory_join = current_laboratory.main_site? ? '' : " JOIN laboratory_subjects ls ON ls.subject_id = subjects.id AND ls.laboratory_id = #{current_laboratory.id} "
       if select
         @subjects = Subject.find_by_sql("SELECT subjects.id, subjects.user_id, subjects.name, subjects.stages_count, subjects.repertoire_id, subjects.status,
                       subjects.shixuns_count, subjects.excellent, sum(shixuns.myshixuns_count) AS myshixun_member_count FROM subjects join stage_shixuns
-                      on stage_shixuns.subject_id = subjects.id join shixuns on shixuns.id = stage_shixuns.shixun_id where
+                      on stage_shixuns.subject_id = subjects.id join shixuns on shixuns.id = stage_shixuns.shixun_id #{laboratory_join} where
                       subjects.hidden = 0 AND subjects.status = 2 AND subjects.name like '%#{search}%'
                       AND subjects.repertoire_id = #{select} GROUP BY subjects.id ORDER BY myshixun_member_count DESC")
       else
         @subjects = Subject.find_by_sql("SELECT subjects.id, subjects.user_id, subjects.name, subjects.stages_count, subjects.repertoire_id, subjects.status,
                       subjects.shixuns_count, subjects.excellent, sum(shixuns.myshixuns_count) AS myshixun_member_count FROM subjects join stage_shixuns
-                      on stage_shixuns.subject_id = subjects.id join shixuns on shixuns.id = stage_shixuns.shixun_id where
+                      on stage_shixuns.subject_id = subjects.id join shixuns on shixuns.id = stage_shixuns.shixun_id #{laboratory_join} where
                       subjects.hidden = 0 AND subjects.status = 2 AND subjects.name like '%#{search}%'
                       GROUP BY subjects.id ORDER BY myshixun_member_count DESC")
       end
@@ -52,6 +53,11 @@ class SubjectsController < ApplicationController
         @subjects = Subject.visible.unhidden
       end
 
+      # 云上实验室过滤
+      unless current_laboratory.main_site?
+        @subjects = @subjects.joins(:laboratory_subjects).where(laboratory_subjects: { laboratory_id: current_laboratory.id })
+      end
+
       # 类型
       if select
         @subjects = @subjects.where(repertoire_id: select)
diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb
index 5cd9d787c..6e957b4df 100644
--- a/app/mailers/user_mailer.rb
+++ b/app/mailers/user_mailer.rb
@@ -12,6 +12,6 @@ class UserMailer < ApplicationMailer
   def course_message_email(mail, message_id)
     @message = Message.find_by(id: message_id)
     @course = @message&.board&.course
-    mail(to: mail, subject: '课堂通知') if @message.present? && @course.present?
+    mail(to: mail, subject: '课堂发布了新的帖子') if @message.present? && @course.present?
   end
 end
diff --git a/app/models/exercise.rb b/app/models/exercise.rb
index 7413853e0..9bc9bd1fb 100644
--- a/app/models/exercise.rb
+++ b/app/models/exercise.rb
@@ -41,6 +41,14 @@ class Exercise < ApplicationRecord
     exercise_questions.pluck(:question_score).sum
   end
 
+  def subjective_score
+    exercise_questions.where(question_type: [4]).pluck(:question_score).sum
+  end
+
+  def objective_score
+    exercise_questions.where(question_type: [0, 1, 2, 3, 5]).pluck(:question_score).sum
+  end
+
   def create_exercise_list
     str = ""
     # TODO: 一次性为所有学生创建数据是否存在问题?
diff --git a/app/models/laboratory.rb b/app/models/laboratory.rb
index ff8f89c5e..672294b63 100644
--- a/app/models/laboratory.rb
+++ b/app/models/laboratory.rb
@@ -38,4 +38,17 @@ class Laboratory < ApplicationRecord
   def self.current
     Thread.current[:current_laboratory] ||= Laboratory.find(1)
   end
+
+  def shixuns
+    main_site? ? Shixun.all : Shixun.joins(:laboratory_shixuns).where(laboratory_shixuns: { laboratory_id: id })
+  end
+
+  def subjects
+    main_site? ? Subject.all : Subject.joins(:laboratory_subjects).where(laboratory_subjects: { laboratory_id: id })
+  end
+
+  # 是否为主站
+  def main_site?
+    id == 1
+  end
 end
\ No newline at end of file
diff --git a/app/models/laboratory_subject.rb b/app/models/laboratory_subject.rb
index fa5862712..e56cd94bd 100644
--- a/app/models/laboratory_subject.rb
+++ b/app/models/laboratory_subject.rb
@@ -1,4 +1,6 @@
 class LaboratorySubject < ApplicationRecord
   belongs_to :laboratory
   belongs_to :subject
+
+  delegate :repertoire_id, :subject_level_system_id, :student_count, to: :subject
 end
\ No newline at end of file
diff --git a/app/models/user.rb b/app/models/user.rb
index 85b9ef551..abab82927 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -235,6 +235,28 @@ class User < ApplicationRecord
     end
   end
 
+  # 实名认证状态
+  def auth_status
+    status = if authentication
+              "已认证"
+            elsif process_real_name_apply.present?
+              "待审核"
+            else
+              "未认证"
+            end
+  end
+
+  # 职业认证状态
+  def pro_status
+    status = if professional_certification
+               "已认证"
+             elsif process_professional_apply.present?
+               "待审核"
+             else
+               "未认证"
+             end
+  end
+
   # 判断当前用户是否通过职业认证
   def pro_certification?
     professional_certification
@@ -265,6 +287,11 @@ class User < ApplicationRecord
     course.course_members.exists?(user_id: id, role: [1,2,3], is_active: 1) || admin? || business?
   end
 
+  # 课堂的老师(创建者、老师、助教),不考虑超管和运营人员
+  def none_admin_teacher_of_course?(course)
+    course.course_members.exists?(user_id: id, role: [1,2,3], is_active: 1)
+  end
+
   # 课堂的老师(创建者、老师、助教),不用考虑当前身份
   def teacher_of_course_non_active?(course)
     course.course_members.exists?(user_id: id, role: [1,2,3])
diff --git a/app/services/duplicate_course_service.rb b/app/services/duplicate_course_service.rb
index acff0a726..fa57a8901 100644
--- a/app/services/duplicate_course_service.rb
+++ b/app/services/duplicate_course_service.rb
@@ -94,8 +94,8 @@ class DuplicateCourseService < ApplicationService
       exercise = course.exercises.create!(attrs.merge(user_id: user.id))
 
       origin_exercise.exercise_questions.find_each do |origin_question|
-        question_attrs = origin_question.as_json(only: %i[question_title question_type question_number question_score])
-        question_attrs[:question_type] ||= 1
+        question_attrs = origin_question.as_json(only: %i[question_title question_type question_number question_score shixun_name shixun_id is_ordered level])
+        # question_attrs[:question_type] ||= 1
         question = exercise.exercise_questions.create!(question_attrs)
 
         exercise_choice_map = {}
@@ -103,15 +103,20 @@ class DuplicateCourseService < ApplicationService
           choice_attrs = { choice_position: index + 1, choice_text: origin_choice.choice_text }
           choice = question.exercise_choices.create!(choice_attrs)
 
-          exercise_choice_map[origin_choice.id] = choice.id
+          # exercise_choice_map[origin_choice.id] = choice.id 标准答案中存的是choice_position, 直接取原题的exercise_choice_id就行
         end
 
         origin_question.exercise_standard_answers.find_each do |origin_answer|
           question.exercise_standard_answers.create!(
-            exercise_choice_id: exercise_choice_map[origin_answer.exercise_choice_id],
+            exercise_choice_id: origin_answer.exercise_choice_id,
             answer_text: origin_answer.answer_text
           )
         end
+
+        origin_question.exercise_shixun_challenges.each_with_index do |sc, index|
+          question.exercise_shixun_challenges.create!({position: index+1, challenge_id: sc.challenge_id,
+                                                            shixun_id: sc.shixun_id, question_score: sc.question_score})
+        end
       end
 
       origin_exercise.exercise_bank.increment!(:quotes) if exercise.exercise_bank
diff --git a/app/views/admins/competition_prize_users/index.html.erb b/app/views/admins/competition_prize_users/index.html.erb
index dbb86d066..735d94d2e 100644
--- a/app/views/admins/competition_prize_users/index.html.erb
+++ b/app/views/admins/competition_prize_users/index.html.erb
@@ -50,6 +50,12 @@
       <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
       <%= link_to '清除', admins_competition_competition_prize_users_path(@competition), class: "btn btn-default",'data-disable-with': '清除中...' %>
     <% end %>
+
+    <div class="mt-3 d-flex align-items-end">
+      <%= link_to '导出', admins_competition_competition_prize_users_path(competition_id: @competition.id, format: :xlsx), class: 'btn btn-primary' %>
+      <%#= javascript_void_link '导出', class: 'btn btn-primary', 'data-url': admins_competition_competition_prize_users_path(competition_id: @competition.id, format: :xlsx) %>
+    </div>
+
   </div>
 </div>
 
diff --git a/app/views/admins/competition_prize_users/index.xlsx.axlsx b/app/views/admins/competition_prize_users/index.xlsx.axlsx
new file mode 100644
index 000000000..63e23a214
--- /dev/null
+++ b/app/views/admins/competition_prize_users/index.xlsx.axlsx
@@ -0,0 +1,33 @@
+wb = xlsx_package.workbook
+
+wb.styles do |s|
+  blue_cell = s.add_style :bg_color => "FAEBDC", :sz => 10,:height => 25,:b => true, :border => { :style => :thin, :color =>"000000" },:alignment => {wrap_text: true,:horizontal => :center,:vertical => :center}
+  wb.add_worksheet(name: "#{@competition.name}证书审批列表") do |sheet|
+    sheet.add_row %w(序号 排名 奖项 战队ID 战队名称 姓名 职业 学号 学校名称 学院名称 地区 实名认证 职业认证 手机号码 队长 签领/开户行及银行卡号 审批时间 审批人), :height => 25,:style => blue_cell
+
+    @all_prize_users.each_with_index do |prize_user, index|
+      user = prize_user.user
+      data = [
+        index + 1,
+        prize_user.rank,
+        prize_user.competition_prize.name,
+        prize_user.competition_team_id,
+        prize_user.competition_team.name,
+        user.real_name,
+        user.identity,
+        user.student_id,
+        user.school_name,
+        user.department_name,
+        user.location,
+        user.auth_status,
+        user.pro_status,
+        user.phone,
+        prize_user.leader? ? "是" : "-",
+        [prize_user.extra&.[]('bank'), prize_user.extra&.[]('second_bank'), prize_user.extra&.[]('card_no')].compact.join('/'),
+        prize_user.approved_at&.strftime('%Y-%m-%d %H:%M'),
+        prize_user.approver&.real_name
+      ]
+      sheet.add_row(data)
+    end
+  end
+end
diff --git a/app/views/admins/laboratories/shixuns_for_select.json.jbuilder b/app/views/admins/laboratories/shixuns_for_select.json.jbuilder
index de4f14cf8..872391bb6 100644
--- a/app/views/admins/laboratories/shixuns_for_select.json.jbuilder
+++ b/app/views/admins/laboratories/shixuns_for_select.json.jbuilder
@@ -1,3 +1,4 @@
+json.count @count
 json.shixuns do
   json.array! @shixuns do |shixun|
     json.extract! shixun, :id, :name, :status
diff --git a/app/views/admins/laboratories/subjects_for_select.json.jbuilder b/app/views/admins/laboratories/subjects_for_select.json.jbuilder
index c35f0255b..2395eb30c 100644
--- a/app/views/admins/laboratories/subjects_for_select.json.jbuilder
+++ b/app/views/admins/laboratories/subjects_for_select.json.jbuilder
@@ -1,3 +1,4 @@
+json.count @count
 json.subjects do
   json.array! @subjects do |subject|
     json.extract! subject, :id, :name, :status
diff --git a/app/views/admins/laboratory_shixuns/shared/_list.html.erb b/app/views/admins/laboratory_shixuns/shared/_list.html.erb
index 8463e2d49..e1244c472 100644
--- a/app/views/admins/laboratory_shixuns/shared/_list.html.erb
+++ b/app/views/admins/laboratory_shixuns/shared/_list.html.erb
@@ -7,8 +7,8 @@
       <th width="10%">封面</th>
       <th width="8%">创建者</th>
       <th width="8%">状态</th>
-      <th width="10%">执行时间</th>
-      <th width="14%">操作</th>
+      <th width="8%">执行时间</th>
+      <th width="16%">操作</th>
     </tr>
   </thead>
   <tbody>
diff --git a/app/views/admins/shared/_sidebar.html.erb b/app/views/admins/shared/_sidebar.html.erb
index a1dab9afe..f78b52ac7 100644
--- a/app/views/admins/shared/_sidebar.html.erb
+++ b/app/views/admins/shared/_sidebar.html.erb
@@ -1,10 +1,10 @@
 <% sidebar_collapse = request.cookies['admin_sidebar_collapse'].to_s == 'true' %>
 <nav id="sidebar" class="<%= sidebar_collapse ? 'active' : '' %>" data-current-controller="<%= admin_sidebar_controller %>">
   <div class="sidebar-header">
-    <div class="sidebar-header-logo">
+    <a href="/" class="sidebar-header-logo" data-toggle="tooltip" data-title="返回主站" >
       <img class="rounded-circle" src="/images/<%= url_to_avatar(current_user) %>" />
       <span class="logo-label">后台管理</span>
-    </div>
+    </a>
     <div id="sidebarCollapse" class="navbar-btn <%= sidebar_collapse ? 'active' : '' %>">
       <i class="fa fa-chevron-left fold" data-toggle="tooltip" data-placement="right" data-boundary="window" title="收起"></i>
       <i class="fa fa-bars unfold" data-toggle="tooltip" data-placement="right" data-boundary="window" title="展开"></i>
diff --git a/app/views/cooperative/laboratory_shixuns/index.html.erb b/app/views/cooperative/laboratory_shixuns/index.html.erb
new file mode 100644
index 000000000..85781a85a
--- /dev/null
+++ b/app/views/cooperative/laboratory_shixuns/index.html.erb
@@ -0,0 +1,42 @@
+<% define_breadcrumbs do %>
+  <% add_breadcrumb('实训项目') %>
+<% end %>
+
+
+<div class="box search-form-container laboratory-shixun-list-form">
+  <%= form_tag(cooperative_laboratory_shixuns_path, method: :get, class: 'form-inline search-form', remote: true) do %>
+    <div class="form-group mr-1">
+      <label for="status">状态:</label>
+      <% status_options = [['全部', ''], ['编辑中', 0], ['审核中', 1], ['已发布', 2], ['已关闭', 3]] %>
+      <%= select_tag(:status, options_for_select(status_options), class: 'form-control') %>
+    </div>
+
+    <div class="form-group mr-4">
+      <label for="status">技术平台:</label>
+      <%= select_tag(:tag_id, options_for_select(MirrorRepository.pluck(:type_name,:id).unshift(['']), params[:tag_id]), class: 'form-control') %>
+    </div>
+
+    <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-12 col-md-2 mr-3', placeholder: '创建者/实训名称检索') %>
+
+    <div class="form-check mr-2">
+      <%= hidden_field_tag(:homepage, false, id:'') %>
+      <%= check_box_tag(:homepage, true, params[:homepage].to_s == 'true', class: 'form-check-input') %>
+      <label class="form-check-label" for="homepage">只看首页展示</label>
+    </div>
+
+    <div class="form-check mr-2">
+      <%= hidden_field_tag(:ownership, false, id:'') %>
+      <%= check_box_tag(:ownership, true, params[:ownership].to_s == 'true', class: 'form-check-input') %>
+      <label class="form-check-label" for="ownership">只看自建</label>
+    </div>
+
+    <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
+    <%= link_to '清空', cooperative_laboratory_shixuns_path, class: 'btn btn-default','data-disable-with': '清空中...' %>
+  <% end %>
+</div>
+
+<div class="box laboratory-shixun-list-container">
+  <%= render partial: 'cooperative/laboratory_shixuns/shared/list', locals: { laboratory_shixuns: @laboratory_shixuns } %>
+</div>
+
+<%= render(partial: 'cooperative/shared/modal/upload_file_modal', locals: { title: '上传封面', accept: 'image/*' }) %>
\ No newline at end of file
diff --git a/app/views/cooperative/laboratory_shixuns/index.js.erb b/app/views/cooperative/laboratory_shixuns/index.js.erb
new file mode 100644
index 000000000..927609f6c
--- /dev/null
+++ b/app/views/cooperative/laboratory_shixuns/index.js.erb
@@ -0,0 +1 @@
+$('.laboratory-shixun-list-container').html("<%= j(render partial: 'cooperative/laboratory_shixuns/shared/list', locals: { laboratory_shixuns: @laboratory_shixuns }) %>");
\ No newline at end of file
diff --git a/app/views/cooperative/laboratory_shixuns/shared/_list.html.erb b/app/views/cooperative/laboratory_shixuns/shared/_list.html.erb
new file mode 100644
index 000000000..1501e9f3b
--- /dev/null
+++ b/app/views/cooperative/laboratory_shixuns/shared/_list.html.erb
@@ -0,0 +1,27 @@
+<table class="table text-center laboratory-shixun-list-table">
+  <thead class="thead-light">
+    <tr>
+      <th width="28%" class="text-left">实训名称</th>
+      <th width="12%">技术平台</th>
+      <th width="14%" class="text-left">技术体系</th>
+      <th width="10%">封面</th>
+      <th width="8%">创建者</th>
+      <th width="8%">状态</th>
+      <th width="8%">执行时间</th>
+      <th width="16%">操作</th>
+    </tr>
+  </thead>
+  <tbody>
+    <% if laboratory_shixuns.present? %>
+      <% laboratory_shixuns.each do |laboratory_shixun| %>
+        <tr class="laboratory-shixun-item-<%= laboratory_shixun.id %>">
+          <%= render partial: 'cooperative/laboratory_shixuns/shared/td', locals: { laboratory_shixun: laboratory_shixun } %>
+        </tr>
+      <% end %>
+    <% else %>
+      <%= render 'cooperative/shared/no_data_for_table' %>
+    <% end %>
+  </tbody>
+</table>
+
+<%= render partial: 'cooperative/shared/paginate', locals: { objects: laboratory_shixuns } %>
\ No newline at end of file
diff --git a/app/views/cooperative/laboratory_shixuns/shared/_td.html.erb b/app/views/cooperative/laboratory_shixuns/shared/_td.html.erb
new file mode 100644
index 000000000..e7f87057d
--- /dev/null
+++ b/app/views/cooperative/laboratory_shixuns/shared/_td.html.erb
@@ -0,0 +1,32 @@
+<%- shixun = laboratory_shixun.shixun -%>
+
+<td class="text-left">
+  <%= link_to "/shixuns/#{shixun.identifier}", target: '_blank' do %>
+    <%= shixun.name %>
+    <span class="badge badge-pill badge-success homepage-badge" style="<%= laboratory_shixun.homepage? ? '' : 'display:none' %>">首页</span>
+    <span class="badge badge-pill badge-info ownership-badge" style="<%= laboratory_shixun.ownership ? '' : 'display:none' %>">自建</span>
+  <% end %>
+</td>
+<td><%= shixun.shixun_main_name %></td>
+<td class="text-left">
+  <% shixun.tag_repertoires.each do |tag| %>
+    <span class="badge badge-secondary"><%= tag.name %></span>
+  <% end %>
+</td>
+<td class="image-preview-container">
+  <% imageExists = Util::FileManage.exists?(shixun) %>
+  <%= image_tag(imageExists ? Util::FileManage.source_disk_file_url(shixun) : '', height: 40, class: "preview-image shixun-image-#{shixun.id}", data: { toggle: 'tooltip', title: '点击预览' }, style: imageExists ? '' : 'display:none') %>
+  <% if laboratory_shixun.ownership? %>
+    <%= javascript_void_link imageExists ? '重新上传' : '上传图片', class: 'action upload-shixun-image-action', data: { source_id: shixun.id, source_type: 'Shixun', toggle: 'modal', target: '.cooperative-upload-file-modal' } %>
+  <% end %>
+</td>
+<td><%= link_to shixun.user&.real_name, "/users/#{shixun.user&.login}", target:'_blank' %></td>
+<td><span class="<%= shixun_status_class(shixun) %>"><%= t("shixun.status.#{shixun.status}") %></span></td>
+<td><%= shixun.excute_time %></td>
+<td class="action-container">
+  <% if laboratory_shixun.ownership? %>
+    <%#= link_to('修改', cooperative_laboratory_subjects_path(laboratory_shixun)) %>
+  <% end %>
+  <%= javascript_void_link('首页展示', class: 'action homepage-show-action', data: { id: laboratory_shixun.id }, style: laboratory_shixun.homepage? ? 'display:none' : '') %>
+  <%= javascript_void_link('取消首页展示', class: 'action homepage-hide-action', data: { id: laboratory_shixun.id }, style: laboratory_shixun.homepage? ? '' : 'display:none') %>
+</td>
\ No newline at end of file
diff --git a/app/views/cooperative/laboratory_subjects/edit.js.erb b/app/views/cooperative/laboratory_subjects/edit.js.erb
new file mode 100644
index 000000000..146879309
--- /dev/null
+++ b/app/views/cooperative/laboratory_subjects/edit.js.erb
@@ -0,0 +1,2 @@
+$('.cooperative-modal-container').html("<%= j( render partial: 'cooperative/laboratory_subjects/shared/edit_subject_modal', locals: { laboratory_subject: @laboratory_subject } ) %>");
+$('.modal.cooperative-edit-subject-modal').modal('show');
\ No newline at end of file
diff --git a/app/views/cooperative/laboratory_subjects/index.html.erb b/app/views/cooperative/laboratory_subjects/index.html.erb
new file mode 100644
index 000000000..3ed30b4f2
--- /dev/null
+++ b/app/views/cooperative/laboratory_subjects/index.html.erb
@@ -0,0 +1,42 @@
+<% define_breadcrumbs do %>
+  <% add_breadcrumb('实践课程') %>
+<% end %>
+
+
+<div class="box search-form-container laboratory-subject-list-form">
+  <%= form_tag(cooperative_laboratory_subjects_path, method: :get, class: 'form-inline search-form', remote: true) do %>
+    <div class="form-group mr-1">
+      <label for="status">状态:</label>
+      <% status_options = [['全部', ''], ['编辑中', 0], ['审核中', 1], ['已发布', 2]] %>
+      <%= select_tag(:status, options_for_select(status_options), class: 'form-control') %>
+    </div>
+
+    <div class="form-group col-12 col-md-3">
+      <label for="school_name">单位:</label>
+      <%= select_tag :school_id, options_for_select([''], params[:school_id]), class: 'form-control school-select flex-1' %>
+    </div>
+
+    <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-12 col-md-2 mr-3', placeholder: '创建者/课程名称检索') %>
+
+    <div class="form-check mr-2">
+      <%= hidden_field_tag(:homepage, false, id:'') %>
+      <%= check_box_tag(:homepage, true, params[:homepage].to_s == 'true', class: 'form-check-input') %>
+      <label class="form-check-label" for="homepage">只看首页展示</label>
+    </div>
+
+    <div class="form-check mr-2">
+      <%= hidden_field_tag(:ownership, false, id:'') %>
+      <%= check_box_tag(:ownership, true, params[:ownership].to_s == 'true', class: 'form-check-input') %>
+      <label class="form-check-label" for="ownership">只看自建</label>
+    </div>
+
+    <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
+    <%= link_to '清空', cooperative_laboratory_subjects_path(current_laboratory), class: 'btn btn-default','data-disable-with': '清空中...' %>
+  <% end %>
+</div>
+
+<div class="box laboratory-subject-list-container">
+  <%= render partial: 'cooperative/laboratory_subjects/shared/list', locals: { laboratory_subjects: @laboratory_subjects } %>
+</div>
+
+<%= render(partial: 'cooperative/shared/modal/upload_file_modal', locals: { title: '上传封面', accept: 'image/*' }) %>
\ No newline at end of file
diff --git a/app/views/cooperative/laboratory_subjects/index.js.erb b/app/views/cooperative/laboratory_subjects/index.js.erb
new file mode 100644
index 000000000..557182485
--- /dev/null
+++ b/app/views/cooperative/laboratory_subjects/index.js.erb
@@ -0,0 +1 @@
+$('.laboratory-subject-list-container').html("<%= j(render partial: 'cooperative/laboratory_subjects/shared/list', locals: { laboratory_subjects: @laboratory_subjects }) %>");
\ No newline at end of file
diff --git a/app/views/cooperative/laboratory_subjects/shared/_edit_subject_modal.html.erb b/app/views/cooperative/laboratory_subjects/shared/_edit_subject_modal.html.erb
new file mode 100644
index 000000000..02ff4acbc
--- /dev/null
+++ b/app/views/cooperative/laboratory_subjects/shared/_edit_subject_modal.html.erb
@@ -0,0 +1,31 @@
+<div class="modal fade cooperative-edit-subject-modal" tabindex="-1" role="dialog" aria-hidden="true">
+  <div class="modal-dialog modal-dialog-centered" role="document">
+    <div class="modal-content">
+      <div class="modal-header">
+        <h5 class="modal-title">编辑课程信息</h5>
+        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+          <span aria-hidden="true">&times;</span>
+        </button>
+      </div>
+      <div class="modal-body">
+        <%= simple_form_for([:cooperative, laboratory_subject], html: { class: 'cooperative-edit-subject-form' }, defaults: { wrapper_html: { class: 'offset-md-1 col-md-10' } }) do |f| %>
+          <%= f.input :repertoire_id, label: '技术体系:' do %>
+            <% repertoire_options = Repertoire.order('CONVERT(name USING gbk) COLLATE gbk_chinese_ci ASC').map{|r| [r.name, r.id]} %>
+            <%= f.select :repertoire_id, [['请选择', '']] + repertoire_options, {}, class: 'form-control' %>
+          <% end %>
+
+          <%= f.input :subject_level_system_id, label: '等级体系:' do %>
+            <% level_options = SubjectLevelSystem.all.map{|r| [r.name, r.id]} %>
+            <%= f.select :subject_level_system_id, [['请选择', '']] + level_options, {}, class: 'form-control' %>
+          <% end %>
+
+          <div class="error text-danger"></div>
+        <% end %>
+      </div>
+      <div class="modal-footer">
+        <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
+        <button type="button" class="btn btn-primary submit-btn">确认</button>
+      </div>
+    </div>
+  </div>
+</div>
\ No newline at end of file
diff --git a/app/views/cooperative/laboratory_subjects/shared/_list.html.erb b/app/views/cooperative/laboratory_subjects/shared/_list.html.erb
new file mode 100644
index 000000000..2b972a959
--- /dev/null
+++ b/app/views/cooperative/laboratory_subjects/shared/_list.html.erb
@@ -0,0 +1,27 @@
+<table class="table text-center laboratory-subject-list-table">
+  <thead class="thead-light">
+    <tr>
+      <th width="28%" class="text-left">课程名称</th>
+      <th width="12%">技术体系</th>
+      <th width="10%">等级体系</th>
+      <th width="10%">封面</th>
+      <th width="8%">创建者</th>
+      <th width="10%">单位</th>
+      <th width="8%">状态</th>
+      <th width="14%">操作</th>
+    </tr>
+  </thead>
+  <tbody>
+    <% if laboratory_subjects.present? %>
+      <% laboratory_subjects.each do |laboratory_subject| %>
+        <tr class="laboratory-subject-item-<%= laboratory_subject.id %>">
+          <%= render partial: 'cooperative/laboratory_subjects/shared/td', locals: { laboratory_subject: laboratory_subject } %>
+        </tr>
+      <% end %>
+    <% else %>
+      <%= render 'cooperative/shared/no_data_for_table' %>
+    <% end %>
+  </tbody>
+</table>
+
+<%= render partial: 'cooperative/shared/paginate', locals: { objects: laboratory_subjects } %>
\ No newline at end of file
diff --git a/app/views/cooperative/laboratory_subjects/shared/_td.html.erb b/app/views/cooperative/laboratory_subjects/shared/_td.html.erb
new file mode 100644
index 000000000..301bbd6a7
--- /dev/null
+++ b/app/views/cooperative/laboratory_subjects/shared/_td.html.erb
@@ -0,0 +1,28 @@
+<%- subject = laboratory_subject.subject -%>
+
+<td class="text-left">
+  <%= link_to(subject.name, "/paths/#{subject.id}", target: '_blank') %>
+  <span class="badge badge-pill badge-success homepage-badge" style="<%= laboratory_subject.homepage? ? '' : 'display:none' %>">首页</span>
+  <span class="badge badge-pill badge-success ownership-badge" style="<%= laboratory_subject.ownership? ? '' : 'display:none' %>">自建</span>
+</td>
+<td><%= display_text subject.repertoire&.name %></td>
+<td><%= display_text subject.subject_level_system&.name %></td>
+<td class="image-preview-container">
+  <% image_exists = Util::FileManage.exists?(subject) %>
+  <%= image_tag(image_exists ? Util::FileManage.source_disk_file_url(subject) : '', height: 40, class: "w-100 preview-image subject-image-#{subject.id}", style: image_exists ? '' : 'display:none') %>
+  <% if laboratory_subject.ownership? %>
+    <%= javascript_void_link image_exists ? '重新上传' : '上传图片', class: 'action upload-subject-image-action', data: { source_id: subject.id, source_type: 'Subject', toggle: 'modal', target: '.cooperative-upload-file-modal' } %>
+  <% end %>
+</td>
+<td><%= link_to subject.user&.real_name, "/users/#{subject.user&.login}", target:'_blank' %></td>
+<td><%= subject.user.school_name %></td>
+<td><%= display_subject_status(subject) %></td>
+
+<td class="action-container">
+  <% if laboratory_subject.ownership? %>
+    <%= link_to('修改', edit_cooperative_laboratory_subject_path(laboratory_subject), remote: true) %>
+  <% end %>
+
+  <%= javascript_void_link('首页展示', class: 'action homepage-show-action', data: { id: laboratory_subject.id }, style: laboratory_subject.homepage? ? 'display:none' : '') %>
+  <%= javascript_void_link('取消首页展示', class: 'action homepage-hide-action', data: { id: laboratory_subject.id }, style: laboratory_subject.homepage? ? '' : 'display:none') %>
+</td>
\ No newline at end of file
diff --git a/app/views/cooperative/laboratory_subjects/update.js.erb b/app/views/cooperative/laboratory_subjects/update.js.erb
new file mode 100644
index 000000000..3b98e6e97
--- /dev/null
+++ b/app/views/cooperative/laboratory_subjects/update.js.erb
@@ -0,0 +1,3 @@
+$('.laboratory-subject-list-container .laboratory-subject-item-<%= current_laboratory_subject.id %>').html("<%= j(render partial: 'cooperative/laboratory_subjects/shared/td', locals: { laboratory_subject: current_laboratory_subject }) %>");
+$('.cooperative-modal-container .modal.cooperative-edit-subject-modal').modal('hide');
+show_success_flash();
diff --git a/app/views/cooperative/shared/_sidebar.html.erb b/app/views/cooperative/shared/_sidebar.html.erb
index 40a6b2de4..81ff12b0a 100644
--- a/app/views/cooperative/shared/_sidebar.html.erb
+++ b/app/views/cooperative/shared/_sidebar.html.erb
@@ -17,6 +17,8 @@
     <li><%= sidebar_item(edit_cooperative_laboratory_setting_path, '网站设置', icon: 'cogs', controller: 'cooperative-laboratory_settings') %></li>
     <li><%= sidebar_item(cooperative_carousels_path, '轮播图设置', icon: 'image', controller: 'cooperative-carousels') %></li>
     <li><%= sidebar_item(cooperative_laboratory_users_path, '管理员列表', icon: 'user', controller: 'cooperative-laboratory_users') %></li>
+    <li><%= sidebar_item(cooperative_laboratory_shixuns_path, '实训项目', icon: 'window-restore', controller: 'cooperative-laboratory_shixuns') %></li>
+    <li><%= sidebar_item(cooperative_laboratory_subjects_path, '实践课程', icon: 'th-list', controller: 'cooperative-laboratory_subjects') %></li>
     <li><%= sidebar_item('/', '返回主页', icon: 'sign-out', controller: 'root') %></li>
   </ul>
 </nav>
\ No newline at end of file
diff --git a/app/views/cooperative/shared/modal/_upload_file_modal.html.erb b/app/views/cooperative/shared/modal/_upload_file_modal.html.erb
index 8cd22a362..587002403 100644
--- a/app/views/cooperative/shared/modal/_upload_file_modal.html.erb
+++ b/app/views/cooperative/shared/modal/_upload_file_modal.html.erb
@@ -11,12 +11,13 @@
         <form class="cooperative-upload-file-form" enctype="multipart/form-data">
           <%= hidden_field_tag(:source_type, nil) %>
           <%= hidden_field_tag(:source_id, nil) %>
+          <%= hidden_field_tag(:suffix, nil) %>
           <div class="input-group">
             <div class="input-group-prepend">
               <span class="input-group-text">文件</span>
             </div>
             <div class="custom-file">
-              <input type="file" name="file" class="upload-file-input" id="upload-file-input">
+              <input type="file" name="file" class="upload-file-input" accept="<%= accept ||= '*' %>" id="upload-file-input">
               <label class="custom-file-label file-names" for="upload-file-input">选择文件</label>
             </div>
           </div>
diff --git a/app/views/exercises/exercise_lists.json.jbuilder b/app/views/exercises/exercise_lists.json.jbuilder
index 26723c894..8230ef7c7 100644
--- a/app/views/exercises/exercise_lists.json.jbuilder
+++ b/app/views/exercises/exercise_lists.json.jbuilder
@@ -16,6 +16,8 @@ json.exercise_types do
   json.subjective @subjective_type  #是否包含主观题,1为包括,0为不包括
   json.exercise_end_time ((@ex_user_end_time.nil? || @ex_user_end_time < Time.now) ? "--" : how_much_time(@ex_user_end_time))
   json.groups_count @c_group_counts
+  json.subjective_score @exercise.subjective_score
+  json.objective_score @exercise.objective_score
 end
 
 if @exercise_current_user_status == 0  #当为老师的时候
diff --git a/app/views/graduation_topics/_graduation_comments.json.jbuilder b/app/views/graduation_topics/_graduation_comments.json.jbuilder
index 03002e82e..e52395fbf 100644
--- a/app/views/graduation_topics/_graduation_comments.json.jbuilder
+++ b/app/views/graduation_topics/_graduation_comments.json.jbuilder
@@ -3,8 +3,8 @@ json.author do
 end
 
 json.id message.id
-# json.content content_safe(message.contents_show(identity))
-json.content message.contents_show(identity)
+json.content content_safe(message.contents_show(identity))
+# json.content message.contents_show(identity)
 json.time time_from_now(message.created_at)
 json.hidden message.hidden
 # 主贴与子贴不一致
diff --git a/app/views/home/index.json.jbuilder b/app/views/home/index.json.jbuilder
index aa29ae7ff..5bbd1b799 100644
--- a/app/views/home/index.json.jbuilder
+++ b/app/views/home/index.json.jbuilder
@@ -3,17 +3,19 @@ json.images_url @images_url
 json.reps @rep_list
 
 json.shixuns do
-  json.partial! 'shixuns/shixun', locals: {shixuns: @shixuns}
+  json.partial! 'shixuns/shixun', locals: {shixuns: @shixuns.present? ? @shixuns : @main_shixuns}
 end
 
 json.subjects do
-  json.partial! 'subjects/subject', locals: {subjects: @subjects}
+  json.partial! 'subjects/subject', locals: {subjects: @subjects.present? ? @subjects : @main_subjects}
 end
 
-json.teachers do
-  json.partial! 'users/user_small', users: @tea_users
-end
+if current_laboratory.main_site?
+  json.teachers do
+    json.partial! 'users/user_small', users: @tea_users
+  end
 
-json.students do
-  json.partial! 'users/user_small', users: @stu_users
+  json.students do
+    json.partial! 'users/user_small', users: @stu_users
+  end
 end
diff --git a/app/views/messages/_message.json.jbuilder b/app/views/messages/_message.json.jbuilder
index c3ab6c6e3..17e2c39a8 100644
--- a/app/views/messages/_message.json.jbuilder
+++ b/app/views/messages/_message.json.jbuilder
@@ -1,2 +1,2 @@
 json.extract! message, :id, :parent_id, :subject, :created_on, :total_replies_count, :total_praises_count,
-              :is_md, :praises_count, :visits, :sticky, :is_hidden, :is_public
+              :is_md, :praises_count, :visits, :sticky, :is_hidden, :is_public, :email_notify
diff --git a/app/views/user_mailer/course_message_email.html.erb b/app/views/user_mailer/course_message_email.html.erb
index 66c75414b..b3e57b5a2 100644
--- a/app/views/user_mailer/course_message_email.html.erb
+++ b/app/views/user_mailer/course_message_email.html.erb
@@ -1,7 +1,7 @@
 <html>
 <head>
   <meta charset="utf-8">
-  <title><%= @course.name %>通知</title>
+  <title><%= @course.name %> 发布了新的帖子</title>
   <style type="text/css">
     /* 验证链接页面 */
     body,h1,h2,h3,h4,h5,h6,hr,p,blockquote,dl,dt,dd,ul,ol,li,pre,form,fieldset,legend,button,input,textarea,th,td{ margin:0; padding:0;}
@@ -36,22 +36,16 @@
       <p style="color:#333; font-size:16px; margin-bottom:15px;font-weight: bold">
         您好!
       </p>
-      <p style="color:#333;">
-        您正在注册Educoder,请在10分钟内在注册页输入此验证码,并进行下一步操作。
-        如非你本人操作,请忽略此邮件。
+      <p style="color:#333; ">
+        您参与的课堂:<%= @course.name %>,有新的帖子发布了:
+        <a href="https://www.educoder.net/courses/<%= @course.id %>/boards/<%= @message.board_id %>/messages/<%= @message.id %>" style="font-weight: normal; color:#ff7500;"><%= @message.subject %></a>
       </p>
-      <div  style="text-align: center;">
-        <div style="display:block; height: 45px; line-height:45px;padding:0 30px; width:100px; font-size: 20px;  font-weight: bold; background:#ffd9d9; color:#e72c37; margin:30px auto;">
-          <p><%= @code %></p>
-        </div>
+      <div style="text-align: center; margin-top:40px;">
         <span style="font-weight: normal;color:#666;">
             此邮件为系统所发,请勿直接回复。<br/>
     要解决问题或了解您的帐户详情,您可以访问 <a href="https://www.educoder.net/help?index=5" style="font-weight: normal; color:#ff7500;">帮助中心</a>。
             </span>
       </div>
-      <p style="color:#666; margin-top:30px;">
-        如果您并未发过此请求,则可能是因为其他用户在注册时误输了您的邮件地址,而使您收到了这封邮件,那么您可以放心的忽略此邮件,无需进一步采取任何操作。
-      </p>
     </div>
     <div style="padding:20px; color:#333; line-height: 1.9;background:#46484c;border:1px solid #ddd; border-top:none; width: 558px;">
       <a href="https:///www.educoder.net/"  style="font-weight: normal; color:#fff;">www.educoder.net</a>
diff --git a/config/admins/sidebar.yml b/config/admins/sidebar.yml
index 8ff86a70a..2c82d8b2d 100644
--- a/config/admins/sidebar.yml
+++ b/config/admins/sidebar.yml
@@ -2,6 +2,7 @@ admins-mirror_scripts: 'admins-mirror_repositories'
 admins-laboratory_settings: 'admins-laboratories'
 admins-carousels: 'admins-laboratories'
 admins-laboratory_shixuns: 'admins-laboratories'
+admins-laboratory_subjects: 'admins-laboratories'
 admins-competition_settings: 'admins-competitions'
 admins-enroll_lists: 'admins-competitions'
 admins-competition_prize_users: 'admins-competitions'
diff --git a/config/routes.rb b/config/routes.rb
index 7ceb28128..e111ed7ba 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1121,12 +1121,25 @@ Rails.application.routes.draw do
   namespace :cooperative do
     # get '/', to: 'dashboards#show'
     get '/', to: 'laboratory_settings#edit'
+    resources :files, only: [:create]
     resources :users, only: [:index]
     resources :laboratory_users, only: [:index, :create, :destroy]
     resource :laboratory_setting, only: [:edit, :update]
     resources :carousels, only: [:index, :create, :update, :destroy] do
       post :drag, on: :collection
     end
+    resources :laboratory_shixuns, only: [:index, :edit, :update] do
+      member do
+        post :homepage
+        post :cancel_homepage
+      end
+    end
+    resources :laboratory_subjects, only: [:index, :edit, :update] do
+      member do
+        post :homepage
+        post :cancel_homepage
+      end
+    end
   end
 
   resources :colleges, only: [] do
diff --git a/db/migrate/20191107062552_add_email_notify_to_messages.rb b/db/migrate/20191107062552_add_email_notify_to_messages.rb
new file mode 100644
index 000000000..2ec6f20e4
--- /dev/null
+++ b/db/migrate/20191107062552_add_email_notify_to_messages.rb
@@ -0,0 +1,5 @@
+class AddEmailNotifyToMessages < ActiveRecord::Migration[5.2]
+  def change
+    add_column :messages, :email_notify, :boolean, default: 0
+  end
+end
diff --git a/db/migrate/20191107093428_add_column_to_exercise_user_score.rb b/db/migrate/20191107093428_add_column_to_exercise_user_score.rb
new file mode 100644
index 000000000..892252987
--- /dev/null
+++ b/db/migrate/20191107093428_add_column_to_exercise_user_score.rb
@@ -0,0 +1,6 @@
+class AddColumnToExerciseUserScore < ActiveRecord::Migration[5.2]
+  def change
+    add_column :exercise_user_scores, :subjective_score, :float, default: 0
+    add_column :exercise_user_scores, :objective_score, :float, default: 0
+  end
+end
diff --git a/public/assets/.sprockets-manifest-4627fa5586ef7fed55ca286af7c028e9.json b/public/assets/.sprockets-manifest-4627fa5586ef7fed55ca286af7c028e9.json
index cdde54661..7e2761c2d 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-11-06T11:34:24+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-11-06T11:34:24+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="},"admin-992cde09b6d17f00a49576ae2d9f1ced127244ba401ef5b7d677cab9741688d2.js":{"logical_path":"admin.js","mtime":"2019-10-16T16:11:32+08:00","size":4394790,"digest":"992cde09b6d17f00a49576ae2d9f1ced127244ba401ef5b7d677cab9741688d2","integrity":"sha256-mSzeCbbRfwCklXauLZ8c7RJyRLpAHvW31nfKuXQWiNI="},"admin-84f2a7791e275d6f820514370b3f968176b994b9dd7b8c3ba8bf48336b03f257.css":{"logical_path":"admin.css","mtime":"2019-10-16T19:25:40+08:00","size":846676,"digest":"84f2a7791e275d6f820514370b3f968176b994b9dd7b8c3ba8bf48336b03f257","integrity":"sha256-hPKneR4nXW+CBRQ3Cz+WgXa5lLnde4w7qL9IM2sD8lc="},"application-ef6bab84852baaf69a91fe6af875b6e1b118c55b4c7d165665c488fac80c4997.css":{"logical_path":"application.css","mtime":"2019-10-16T19:25:40+08:00","size":2001931,"digest":"ef6bab84852baaf69a91fe6af875b6e1b118c55b4c7d165665c488fac80c4997","integrity":"sha256-72urhIUrqvaakf5q+HW24bEYxVtMfRZWZcSI+sgMSZc="},"admin-c99030d305662f740aa84b6c925a1adbbaadaa07fd74e2655e64d44b4b97fc4a.js":{"logical_path":"admin.js","mtime":"2019-10-17T09:44:58+08:00","size":4394897,"digest":"c99030d305662f740aa84b6c925a1adbbaadaa07fd74e2655e64d44b4b97fc4a","integrity":"sha256-yZAw0wVmL3QKqEtskloa27qtqgf9dOJlXmTUS0uX/Eo="},"admin-534bde871d67f4d6fc8da611917d78be4066fc7593ba53ee92aa17068a199d6d.css":{"logical_path":"admin.css","mtime":"2019-10-17T10:22:41+08:00","size":846699,"digest":"534bde871d67f4d6fc8da611917d78be4066fc7593ba53ee92aa17068a199d6d","integrity":"sha256-U0vehx1n9Nb8jaYRkX14vkBm/HWTulPukqoXBooZnW0="},"cooperative-04cd6a60d41220d38ee45ce40b1d004e1d0bcd87c132fb1a7bab6144c1deb8d7.js":{"logical_path":"cooperative.js","mtime":"2019-10-17T10:17:56+08:00","size":4330072,"digest":"04cd6a60d41220d38ee45ce40b1d004e1d0bcd87c132fb1a7bab6144c1deb8d7","integrity":"sha256-BM1qYNQSINOO5FzkCx0ATh0LzYfBMvsae6thRMHeuNc="},"cooperative-a345bbfd8e38b70c9285ecc1747012ffcde429187983e2aea5657abb56b9b4f3.css":{"logical_path":"cooperative.css","mtime":"2019-10-17T10:21:41+08:00","size":830628,"digest":"a345bbfd8e38b70c9285ecc1747012ffcde429187983e2aea5657abb56b9b4f3","integrity":"sha256-o0W7/Y44twyShezBdHAS/83kKRh5g+KupWV6u1a5tPM="},"application-0e417478d56f42467e857cd186b29cbbc0d6c7c6e85c8a6f42f39ac618943de8.css":{"logical_path":"application.css","mtime":"2019-09-03T08:55:53+08:00","size":442932,"digest":"0e417478d56f42467e857cd186b29cbbc0d6c7c6e85c8a6f42f39ac618943de8","integrity":"sha256-DkF0eNVvQkZ+hXzRhrKcu8DWx8boXIpvQvOaxhiUPeg="},"cooperative-149f47b8675d60a8014ccff50f00f932ff69e2be286ffb74343bc4a3effb135b.js":{"logical_path":"cooperative.js","mtime":"2019-10-17T14:03:03+08:00","size":4338033,"digest":"149f47b8675d60a8014ccff50f00f932ff69e2be286ffb74343bc4a3effb135b","integrity":"sha256-FJ9HuGddYKgBTM/1DwD5Mv9p4r4ob/t0NDvEo+/7E1s="},"cooperative-6273b766d6ef11dd56174d868bab55e7f17af17546c888d2ba0dd0a6bcda76c8.css":{"logical_path":"cooperative.css","mtime":"2019-10-17T11:13:07+08:00","size":832914,"digest":"6273b766d6ef11dd56174d868bab55e7f17af17546c888d2ba0dd0a6bcda76c8","integrity":"sha256-YnO3ZtbvEd1WF02Gi6tV5/F68XVGyIjSug3Qprzadsg="},"admin-82f66cc80b5649c6530a562567f28fe8d05f7bc3b8221e0695b2216255c52ba6.js":{"logical_path":"admin.js","mtime":"2019-10-21T13:51:43+08:00","size":4397012,"digest":"82f66cc80b5649c6530a562567f28fe8d05f7bc3b8221e0695b2216255c52ba6","integrity":"sha256-gvZsyAtWScZTClYlZ/KP6NBfe8O4Ih4GlbIhYlXFK6Y="},"admin-1b5728d94f6bccfbcef452a760d94c3b6f31966bc65d7f89be077fc2ea512bec.js":{"logical_path":"admin.js","mtime":"2019-10-21T16:41:06+08:00","size":4397437,"digest":"1b5728d94f6bccfbcef452a760d94c3b6f31966bc65d7f89be077fc2ea512bec","integrity":"sha256-G1co2U9rzPvO9FKnYNlMO28xlmvGXX+Jvgd/wupRK+w="},"admin-c8c127fefa5eca98bca19832c246619318164e8f242635c07033e2423cc18a6f.js":{"logical_path":"admin.js","mtime":"2019-10-22T09:53:29+08:00","size":4408150,"digest":"c8c127fefa5eca98bca19832c246619318164e8f242635c07033e2423cc18a6f","integrity":"sha256-yMEn/vpeypi8oZgywkZhkxgWTo8kJjXAcDPiQjzBim8="},"admin-60d200c1fcdf61a60537d29ccf4479c6b1e5e904208870a63b8ee677c96b347e.css":{"logical_path":"admin.css","mtime":"2019-10-22T09:43:20+08:00","size":851150,"digest":"60d200c1fcdf61a60537d29ccf4479c6b1e5e904208870a63b8ee677c96b347e","integrity":"sha256-YNIAwfzfYaYFN9Kcz0R5xrHl6QQgiHCmO47md8lrNH4="},"cooperative-9fb7ac4ad44081fafd5ad2a3a1bfb7f4329ac96f28bc6446d1ff52b1e2e71286.js":{"logical_path":"cooperative.js","mtime":"2019-10-22T09:55:26+08:00","size":4338142,"digest":"9fb7ac4ad44081fafd5ad2a3a1bfb7f4329ac96f28bc6446d1ff52b1e2e71286","integrity":"sha256-n7esStRAgfr9WtKjob+39DKayW8ovGRG0f9SseLnEoY="},"admin-a11066081d60365ddf25d5867560d1ccdd3197dbe82a5b6e969cc940e3429ff1.js":{"logical_path":"admin.js","mtime":"2019-10-24T14:16:30+08:00","size":4524252,"digest":"a11066081d60365ddf25d5867560d1ccdd3197dbe82a5b6e969cc940e3429ff1","integrity":"sha256-oRBmCB1gNl3fJdWGdWDRzN0xl9voKltulpzJQONCn/E="},"admin-7ce3dd717f7d12fcbc64caf14200230a1e68db439be0ba1879077599ff2c32c6.css":{"logical_path":"admin.css","mtime":"2019-10-24T10:10:08+08:00","size":852772,"digest":"7ce3dd717f7d12fcbc64caf14200230a1e68db439be0ba1879077599ff2c32c6","integrity":"sha256-fOPdcX99Evy8ZMrxQgAjCh5o20Ob4LoYeQd1mf8sMsY="},"college-93904c65d52c125aec0a463b9fd98bedda0018b78707f806be22685cca5d3747.css":{"logical_path":"college.css","mtime":"2019-10-24T10:10:08+08:00","size":579546,"digest":"93904c65d52c125aec0a463b9fd98bedda0018b78707f806be22685cca5d3747","integrity":"sha256-k5BMZdUsElrsCkY7n9mL7doAGLeHB/gGviJoXMpdN0c="},"cooperative-84c79d26a36aff5b496551b6d21b1bfb726b1bbc4153435a366115e96c204e06.js":{"logical_path":"cooperative.js","mtime":"2019-10-24T14:17:15+08:00","size":4338225,"digest":"84c79d26a36aff5b496551b6d21b1bfb726b1bbc4153435a366115e96c204e06","integrity":"sha256-hMedJqNq/1tJZVG20hsb+3JrG7xBU0NaNmEV6WwgTgY="},"cooperative-10a9ee5177e196572573ccea460e133c748072e223fdb473d05ee72c991fbbe3.css":{"logical_path":"cooperative.css","mtime":"2019-10-24T10:10:08+08:00","size":833351,"digest":"10a9ee5177e196572573ccea460e133c748072e223fdb473d05ee72c991fbbe3","integrity":"sha256-EKnuUXfhllclc8zqRg4TPHSAcuIj/bRz0F7nLJkfu+M="},"admin-441d8f3722e5f73e5748aaeb6f517101474cb1eb48a99f119e561f08b9e9dc60.js":{"logical_path":"admin.js","mtime":"2019-10-24T16:08:56+08:00","size":4525031,"digest":"441d8f3722e5f73e5748aaeb6f517101474cb1eb48a99f119e561f08b9e9dc60","integrity":"sha256-RB2PNyLl9z5XSKrrb1FxAUdMsetIqZ8RnlYfCLnp3GA="},"admin-76c52986591f274f639ad48dfbb480a1aeeec7647b6fa28fa541e78a064b6316.css":{"logical_path":"admin.css","mtime":"2019-10-24T15:25:17+08:00","size":867945,"digest":"76c52986591f274f639ad48dfbb480a1aeeec7647b6fa28fa541e78a064b6316","integrity":"sha256-dsUphlkfJ09jmtSN+7SAoa7ux2R7b6KPpUHnigZLYxY="},"cooperative-6c4c663b6b5071535bab2b76cc5e05ab5682665857763a76bf4f01afef51be5a.js":{"logical_path":"cooperative.js","mtime":"2019-10-24T17:56:20+08:00","size":4339039,"digest":"6c4c663b6b5071535bab2b76cc5e05ab5682665857763a76bf4f01afef51be5a","integrity":"sha256-bExmO2tQcVNbqyt2zF4Fq1aCZlhXdjp2v08Br+9Rvlo="},"admin-c63acadd431434979db50540a0bf7e65c75e1de0d1b449919f2cce89a0548d43.js":{"logical_path":"admin.js","mtime":"2019-10-24T18:12:33+08:00","size":4533182,"digest":"c63acadd431434979db50540a0bf7e65c75e1de0d1b449919f2cce89a0548d43","integrity":"sha256-xjrK3UMUNJedtQVAoL9+ZcdeHeDRtEmRnyzOiaBUjUM="},"admin-bd832b9a35eb3743dde9218beab61f9bcde1508767ad68dbedb1c89a4bb65c3a.css":{"logical_path":"admin.css","mtime":"2019-10-24T17:56:20+08:00","size":861450,"digest":"bd832b9a35eb3743dde9218beab61f9bcde1508767ad68dbedb1c89a4bb65c3a","integrity":"sha256-vYMrmjXrN0Pd6SGL6rYfm83hUIdnrWjb7bHImku2XDo="},"college-fa202780f3e7f96cb9b5916c6f0d7dd9e03cb746864bbd2dd491ed001c30ad8f.css":{"logical_path":"college.css","mtime":"2019-10-24T17:56:20+08:00","size":571936,"digest":"fa202780f3e7f96cb9b5916c6f0d7dd9e03cb746864bbd2dd491ed001c30ad8f","integrity":"sha256-+iAngPPn+Wy5tZFsbw192eA8t0aGS70t1JHtABwwrY8="},"cooperative-4f233e8963b0bd80bc56b71c209d31464d314240ac8d686806baf99511c53ad0.css":{"logical_path":"cooperative.css","mtime":"2019-10-24T17:56:20+08:00","size":825741,"digest":"4f233e8963b0bd80bc56b71c209d31464d314240ac8d686806baf99511c53ad0","integrity":"sha256-TyM+iWOwvYC8VrccIJ0xRk0xQkCsjWhoBrr5lRHFOtA="},"application-8c9d6bb61c50908f584b3070c79aeb95f25c1166d39e07da5e95438b39ca0de9.css":{"logical_path":"application.css","mtime":"2019-10-23T14:16:56+08:00","size":436995,"digest":"8c9d6bb61c50908f584b3070c79aeb95f25c1166d39e07da5e95438b39ca0de9","integrity":"sha256-jJ1rthxQkI9YSzBwx5rrlfJcEWbTngfaXpVDiznKDek="},"admin-bf2bd889f02d15c4913aa260497d72afeb26d701aac49a4ef6a75619af030152.js":{"logical_path":"admin.js","mtime":"2019-10-25T10:12:17+08:00","size":4533673,"digest":"bf2bd889f02d15c4913aa260497d72afeb26d701aac49a4ef6a75619af030152","integrity":"sha256-vyvYifAtFcSROqJgSX1yr+sm1wGqxJpO9qdWGa8DAVI="},"admin-46e564d29ffae5c71ae9b5e36dc0bd5de57b10f396eb2005bfb9cf51e7744cdd.css":{"logical_path":"admin.css","mtime":"2019-10-25T10:12:17+08:00","size":870355,"digest":"46e564d29ffae5c71ae9b5e36dc0bd5de57b10f396eb2005bfb9cf51e7744cdd","integrity":"sha256-RuVk0p/65cca6bXjbcC9XeV7EPOW6yAFv7nPUed0TN0="},"college-2299e05f5e9b640e333ece624d4ab18a678fdabff0bc18b69a9c2e3de49cba8e.css":{"logical_path":"college.css","mtime":"2019-10-25T10:12:17+08:00","size":580077,"digest":"2299e05f5e9b640e333ece624d4ab18a678fdabff0bc18b69a9c2e3de49cba8e","integrity":"sha256-IpngX16bZA4zPs5iTUqximeP2r/wvBi2mpwuPeScuo4="},"cooperative-f1ac8f14ad6ade8d1f79ca49ea9c79be77d49aae9d2705ca672e78444481700d.js":{"logical_path":"cooperative.js","mtime":"2019-10-25T11:01:38+08:00","size":4409145,"digest":"f1ac8f14ad6ade8d1f79ca49ea9c79be77d49aae9d2705ca672e78444481700d","integrity":"sha256-8ayPFK1q3o0fecpJ6px5vnfUmq6dJwXKZy54RESBcA0="},"cooperative-8057adee2454dbc9d648305faf9ede9824f40d3bd0184e816e8035bb7f1e730b.css":{"logical_path":"cooperative.css","mtime":"2019-10-25T10:12:17+08:00","size":833882,"digest":"8057adee2454dbc9d648305faf9ede9824f40d3bd0184e816e8035bb7f1e730b","integrity":"sha256-gFet7iRU28nWSDBfr57emCT0DTvQGE6BboA1u38ecws="},"admin-6f9bb9720e7e5040ae559a8fae11553313f77552a76416b3a9fe77198471964d.js":{"logical_path":"admin.js","mtime":"2019-10-25T17:00:09+08:00","size":4554537,"digest":"6f9bb9720e7e5040ae559a8fae11553313f77552a76416b3a9fe77198471964d","integrity":"sha256-b5u5cg5+UECuVZqPrhFVMxP3dVKnZBazqf53GYRxlk0="},"admin-ab3e0f7240ae4df8d1585c8d5e99df41edd3305ecc2abadcf8820796e1d9fc65.css":{"logical_path":"admin.css","mtime":"2019-10-25T09:55:22+08:00","size":862288,"digest":"ab3e0f7240ae4df8d1585c8d5e99df41edd3305ecc2abadcf8820796e1d9fc65","integrity":"sha256-qz4PckCuTfjRWFyNXpnfQe3TMF7MKrrc+IIHluHZ/GU="},"college-2fdfc5431b46ad4a454a25386dbcbc390466886f76b85fdb7e3f75018196a870.css":{"logical_path":"college.css","mtime":"2019-10-25T09:28:47+08:00","size":572010,"digest":"2fdfc5431b46ad4a454a25386dbcbc390466886f76b85fdb7e3f75018196a870","integrity":"sha256-L9/FQxtGrUpFSiU4bby8OQRmiG92uF/bfj91AYGWqHA="},"cooperative-47d516a0904d0633e82c1de39a6ec4c9e6de0a37813843e01d4bacf97e8b2ebf.css":{"logical_path":"cooperative.css","mtime":"2019-10-25T09:28:47+08:00","size":825815,"digest":"47d516a0904d0633e82c1de39a6ec4c9e6de0a37813843e01d4bacf97e8b2ebf","integrity":"sha256-R9UWoJBNBjPoLB3jmm7EyebeCjeBOEPgHUus+X6LLr8="},"admin-839af7c0d2917a8f8019d0376ea17cec050ef4d19d98c6c10de91f5d2bc81adf.js":{"logical_path":"admin.js","mtime":"2019-10-26T11:25:08+08:00","size":4554543,"digest":"839af7c0d2917a8f8019d0376ea17cec050ef4d19d98c6c10de91f5d2bc81adf","integrity":"sha256-g5r3wNKReo+AGdA3bqF87AUO9NGdmMbBDekfXSvIGt8="},"admin-52d692608c620ae47717a2ac88377e55b9b58af0acdf3f777814a1fd47b6594b.js":{"logical_path":"admin.js","mtime":"2019-10-26T17:16:18+08:00","size":4553202,"digest":"52d692608c620ae47717a2ac88377e55b9b58af0acdf3f777814a1fd47b6594b","integrity":"sha256-UtaSYIxiCuR3F6KsiDd+Vbm1ivCs3z93eBSh/Ue2WUs="},"admin-b58555ae641bbaa61e3af0ddd2756acc3c5de9023c737815c72132cc67c9a403.js":{"logical_path":"admin.js","mtime":"2019-10-27T13:06:02+08:00","size":4553607,"digest":"b58555ae641bbaa61e3af0ddd2756acc3c5de9023c737815c72132cc67c9a403","integrity":"sha256-tYVVrmQbuqYeOvDd0nVqzDxd6QI8c3gVxyEyzGfJpAM="},"admin-b95c48ea51f392ce7c4024d0d8b2c42dfe48528a1898ec3f7818e0cda2a4f224.js":{"logical_path":"admin.js","mtime":"2019-10-28T10:46:29+08:00","size":4554008,"digest":"b95c48ea51f392ce7c4024d0d8b2c42dfe48528a1898ec3f7818e0cda2a4f224","integrity":"sha256-uVxI6lHzks58QCTQ2LLELf5IUooYmOw/eBjgzaKk8iQ="},"admin-1f2e5a2a28462df8bcddfbdbfbebdc36a761ca7a94962ae71d37fb8c06fc5f94.js":{"logical_path":"admin.js","mtime":"2019-10-29T08:57:11+08:00","size":4554938,"digest":"1f2e5a2a28462df8bcddfbdbfbebdc36a761ca7a94962ae71d37fb8c06fc5f94","integrity":"sha256-Hy5aKihGLfi83fvb++vcNqdhynqUlirnHTf7jAb8X5Q="},"admin-cb3d4541758ef2bcbfe16f518d48f85097d0547a587de222d2fc13dbdd474b4b.js":{"logical_path":"admin.js","mtime":"2019-10-29T14:06:13+08:00","size":4556541,"digest":"cb3d4541758ef2bcbfe16f518d48f85097d0547a587de222d2fc13dbdd474b4b","integrity":"sha256-yz1FQXWO8ry/4W9RjUj4UJfQVHpYfeIi0vwT291HS0s="},"admin-6a76c25b6691b4f436608be28606d90c907ba8f033f5f47c6c20d7bf11251cb6.css":{"logical_path":"admin.css","mtime":"2019-10-29T14:22:47+08:00","size":871031,"digest":"6a76c25b6691b4f436608be28606d90c907ba8f033f5f47c6c20d7bf11251cb6","integrity":"sha256-anbCW2aRtPQ2YIvihgbZDJB7qPAz9fR8bCDXvxElHLY="},"admin-ba909dfe0de4d216bedb3c743144321e4023837568abab1d3ee9a28b2faa5925.js":{"logical_path":"admin.js","mtime":"2019-10-29T14:43:01+08:00","size":4556622,"digest":"ba909dfe0de4d216bedb3c743144321e4023837568abab1d3ee9a28b2faa5925","integrity":"sha256-upCd/g3k0ha+2zx0MUQyHkAjg3Voq6sdPumiiy+qWSU="},"admin-e975e2039206e9ae2b6a072fee083cf39b8e04f2318f67bfbf1923fe208456b3.js":{"logical_path":"admin.js","mtime":"2019-10-29T15:50:27+08:00","size":4559454,"digest":"e975e2039206e9ae2b6a072fee083cf39b8e04f2318f67bfbf1923fe208456b3","integrity":"sha256-6XXiA5IG6a4ragcv7gg885uOBPIxj2e/vxkj/iCEVrM="},"cooperative-a309d245cd0b0b9c653db471c53ec090e49ba7ad885879ffa02a11b6efd79d74.js":{"logical_path":"cooperative.js","mtime":"2019-10-29T15:50:27+08:00","size":4409163,"digest":"a309d245cd0b0b9c653db471c53ec090e49ba7ad885879ffa02a11b6efd79d74","integrity":"sha256-ownSRc0LC5xlPbRxxT7AkOSbp62IWHn/oCoRtu/XnXQ="},"admin-5d791c4f4a14e1586cfa44776ae262b3c1494e1c0fb0e00c330f0cb9d30fd7ba.js":{"logical_path":"admin.js","mtime":"2019-11-01T08:41:10+08:00","size":4563272,"digest":"5d791c4f4a14e1586cfa44776ae262b3c1494e1c0fb0e00c330f0cb9d30fd7ba","integrity":"sha256-XXkcT0oU4Vhs+kR3auJis8FJThwPsOAMMw8MudMP17o="},"admin-70dc0e7136a8f54139e4167c00f3fde9ccc92b404b01b37ac6064913806e3f6e.css":{"logical_path":"admin.css","mtime":"2019-10-31T10:05:33+08:00","size":872438,"digest":"70dc0e7136a8f54139e4167c00f3fde9ccc92b404b01b37ac6064913806e3f6e","integrity":"sha256-cNwOcTao9UE55BZ8APP96czJK0BLAbN6xgZJE4BuP24="},"cooperative-4fe879591997da39d38e94f6f5eb3b688aa827fa42cb8fd73d21bc96ed880236.js":{"logical_path":"cooperative.js","mtime":"2019-11-06T11:34:24+08:00","size":4409560,"digest":"4fe879591997da39d38e94f6f5eb3b688aa827fa42cb8fd73d21bc96ed880236","integrity":"sha256-T+h5WRmX2jnTjpT29es7aIqoJ/pCy4/XPSG8lu2IAjY="},"admin-22af0d0f8c16da312c9355540590a8b6de54729793a1bef2782aa4f9469a77ad.js":{"logical_path":"admin.js","mtime":"2019-11-01T18:45:10+08:00","size":4563272,"digest":"22af0d0f8c16da312c9355540590a8b6de54729793a1bef2782aa4f9469a77ad","integrity":"sha256-Iq8ND4wW2jEsk1VUBZCott5UcpeTob7yeCqk+Uaad60="},"admin-4c1fcf5a570db989682ef483c1016234a3b7614e0a8d42c040eed220298fef8c.css":{"logical_path":"admin.css","mtime":"2019-11-01T18:45:10+08:00","size":864367,"digest":"4c1fcf5a570db989682ef483c1016234a3b7614e0a8d42c040eed220298fef8c","integrity":"sha256-TB/PWlcNuYloLvSDwQFiNKO3YU4KjULAQO7SICmP74w="},"admin-1a7f715f91fa17b0018650fa22b43778aad419157b8fdb9d67a02ea11760349b.js":{"logical_path":"admin.js","mtime":"2019-11-05T16:10:50+08:00","size":4565998,"digest":"1a7f715f91fa17b0018650fa22b43778aad419157b8fdb9d67a02ea11760349b","integrity":"sha256-Gn9xX5H6F7ABhlD6IrQ3eKrUGRV7j9udZ6AuoRdgNJs="},"admin-5902a288ce59247a683d5719189fc6260be1e38181872e710a7643ad60caaccc.css":{"logical_path":"admin.css","mtime":"2019-11-02T08:33:50+08:00","size":872434,"digest":"5902a288ce59247a683d5719189fc6260be1e38181872e710a7643ad60caaccc","integrity":"sha256-WQKiiM5ZJHpoPVcZGJ/GJgvh44GBhy5xCnZDrWDKrMw="},"admin-8ceaca69cdf7194b7a94e49b16ac5fa272f80e86493e5da27ab5a729de22c07a.js":{"logical_path":"admin.js","mtime":"2019-11-07T10:59:11+08:00","size":4575131,"digest":"8ceaca69cdf7194b7a94e49b16ac5fa272f80e86493e5da27ab5a729de22c07a","integrity":"sha256-jOrKac33GUt6lOSbFqxfonL4DoZJPl2ierWnKd4iwHo="}},"assets":{"admin.js":"admin-8ceaca69cdf7194b7a94e49b16ac5fa272f80e86493e5da27ab5a729de22c07a.js","admin.css":"admin-5902a288ce59247a683d5719189fc6260be1e38181872e710a7643ad60caaccc.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-2299e05f5e9b640e333ece624d4ab18a678fdabff0bc18b69a9c2e3de49cba8e.css","logo.png":"logo-7ff112568709bf97f9898fe87249b7a8f200ff1f48d537d85af87215f1870423.png","application.js":"application-9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb.js","application.css":"application-0e417478d56f42467e857cd186b29cbbc0d6c7c6e85c8a6f42f39ac618943de8.css","cooperative.js":"cooperative-4fe879591997da39d38e94f6f5eb3b688aa827fa42cb8fd73d21bc96ed880236.js","cooperative.css":"cooperative-8057adee2454dbc9d648305faf9ede9824f40d3bd0184e816e8035bb7f1e730b.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-11-06T11:34:24+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-11-06T11:34:24+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="},"admin-992cde09b6d17f00a49576ae2d9f1ced127244ba401ef5b7d677cab9741688d2.js":{"logical_path":"admin.js","mtime":"2019-10-16T16:11:32+08:00","size":4394790,"digest":"992cde09b6d17f00a49576ae2d9f1ced127244ba401ef5b7d677cab9741688d2","integrity":"sha256-mSzeCbbRfwCklXauLZ8c7RJyRLpAHvW31nfKuXQWiNI="},"admin-84f2a7791e275d6f820514370b3f968176b994b9dd7b8c3ba8bf48336b03f257.css":{"logical_path":"admin.css","mtime":"2019-10-16T19:25:40+08:00","size":846676,"digest":"84f2a7791e275d6f820514370b3f968176b994b9dd7b8c3ba8bf48336b03f257","integrity":"sha256-hPKneR4nXW+CBRQ3Cz+WgXa5lLnde4w7qL9IM2sD8lc="},"application-ef6bab84852baaf69a91fe6af875b6e1b118c55b4c7d165665c488fac80c4997.css":{"logical_path":"application.css","mtime":"2019-10-16T19:25:40+08:00","size":2001931,"digest":"ef6bab84852baaf69a91fe6af875b6e1b118c55b4c7d165665c488fac80c4997","integrity":"sha256-72urhIUrqvaakf5q+HW24bEYxVtMfRZWZcSI+sgMSZc="},"admin-c99030d305662f740aa84b6c925a1adbbaadaa07fd74e2655e64d44b4b97fc4a.js":{"logical_path":"admin.js","mtime":"2019-10-17T09:44:58+08:00","size":4394897,"digest":"c99030d305662f740aa84b6c925a1adbbaadaa07fd74e2655e64d44b4b97fc4a","integrity":"sha256-yZAw0wVmL3QKqEtskloa27qtqgf9dOJlXmTUS0uX/Eo="},"admin-534bde871d67f4d6fc8da611917d78be4066fc7593ba53ee92aa17068a199d6d.css":{"logical_path":"admin.css","mtime":"2019-10-17T10:22:41+08:00","size":846699,"digest":"534bde871d67f4d6fc8da611917d78be4066fc7593ba53ee92aa17068a199d6d","integrity":"sha256-U0vehx1n9Nb8jaYRkX14vkBm/HWTulPukqoXBooZnW0="},"cooperative-04cd6a60d41220d38ee45ce40b1d004e1d0bcd87c132fb1a7bab6144c1deb8d7.js":{"logical_path":"cooperative.js","mtime":"2019-10-17T10:17:56+08:00","size":4330072,"digest":"04cd6a60d41220d38ee45ce40b1d004e1d0bcd87c132fb1a7bab6144c1deb8d7","integrity":"sha256-BM1qYNQSINOO5FzkCx0ATh0LzYfBMvsae6thRMHeuNc="},"cooperative-a345bbfd8e38b70c9285ecc1747012ffcde429187983e2aea5657abb56b9b4f3.css":{"logical_path":"cooperative.css","mtime":"2019-10-17T10:21:41+08:00","size":830628,"digest":"a345bbfd8e38b70c9285ecc1747012ffcde429187983e2aea5657abb56b9b4f3","integrity":"sha256-o0W7/Y44twyShezBdHAS/83kKRh5g+KupWV6u1a5tPM="},"application-0e417478d56f42467e857cd186b29cbbc0d6c7c6e85c8a6f42f39ac618943de8.css":{"logical_path":"application.css","mtime":"2019-09-03T08:55:53+08:00","size":442932,"digest":"0e417478d56f42467e857cd186b29cbbc0d6c7c6e85c8a6f42f39ac618943de8","integrity":"sha256-DkF0eNVvQkZ+hXzRhrKcu8DWx8boXIpvQvOaxhiUPeg="},"cooperative-149f47b8675d60a8014ccff50f00f932ff69e2be286ffb74343bc4a3effb135b.js":{"logical_path":"cooperative.js","mtime":"2019-10-17T14:03:03+08:00","size":4338033,"digest":"149f47b8675d60a8014ccff50f00f932ff69e2be286ffb74343bc4a3effb135b","integrity":"sha256-FJ9HuGddYKgBTM/1DwD5Mv9p4r4ob/t0NDvEo+/7E1s="},"cooperative-6273b766d6ef11dd56174d868bab55e7f17af17546c888d2ba0dd0a6bcda76c8.css":{"logical_path":"cooperative.css","mtime":"2019-10-17T11:13:07+08:00","size":832914,"digest":"6273b766d6ef11dd56174d868bab55e7f17af17546c888d2ba0dd0a6bcda76c8","integrity":"sha256-YnO3ZtbvEd1WF02Gi6tV5/F68XVGyIjSug3Qprzadsg="},"admin-82f66cc80b5649c6530a562567f28fe8d05f7bc3b8221e0695b2216255c52ba6.js":{"logical_path":"admin.js","mtime":"2019-10-21T13:51:43+08:00","size":4397012,"digest":"82f66cc80b5649c6530a562567f28fe8d05f7bc3b8221e0695b2216255c52ba6","integrity":"sha256-gvZsyAtWScZTClYlZ/KP6NBfe8O4Ih4GlbIhYlXFK6Y="},"admin-1b5728d94f6bccfbcef452a760d94c3b6f31966bc65d7f89be077fc2ea512bec.js":{"logical_path":"admin.js","mtime":"2019-10-21T16:41:06+08:00","size":4397437,"digest":"1b5728d94f6bccfbcef452a760d94c3b6f31966bc65d7f89be077fc2ea512bec","integrity":"sha256-G1co2U9rzPvO9FKnYNlMO28xlmvGXX+Jvgd/wupRK+w="},"admin-c8c127fefa5eca98bca19832c246619318164e8f242635c07033e2423cc18a6f.js":{"logical_path":"admin.js","mtime":"2019-10-22T09:53:29+08:00","size":4408150,"digest":"c8c127fefa5eca98bca19832c246619318164e8f242635c07033e2423cc18a6f","integrity":"sha256-yMEn/vpeypi8oZgywkZhkxgWTo8kJjXAcDPiQjzBim8="},"admin-60d200c1fcdf61a60537d29ccf4479c6b1e5e904208870a63b8ee677c96b347e.css":{"logical_path":"admin.css","mtime":"2019-10-22T09:43:20+08:00","size":851150,"digest":"60d200c1fcdf61a60537d29ccf4479c6b1e5e904208870a63b8ee677c96b347e","integrity":"sha256-YNIAwfzfYaYFN9Kcz0R5xrHl6QQgiHCmO47md8lrNH4="},"cooperative-9fb7ac4ad44081fafd5ad2a3a1bfb7f4329ac96f28bc6446d1ff52b1e2e71286.js":{"logical_path":"cooperative.js","mtime":"2019-10-22T09:55:26+08:00","size":4338142,"digest":"9fb7ac4ad44081fafd5ad2a3a1bfb7f4329ac96f28bc6446d1ff52b1e2e71286","integrity":"sha256-n7esStRAgfr9WtKjob+39DKayW8ovGRG0f9SseLnEoY="},"admin-a11066081d60365ddf25d5867560d1ccdd3197dbe82a5b6e969cc940e3429ff1.js":{"logical_path":"admin.js","mtime":"2019-10-24T14:16:30+08:00","size":4524252,"digest":"a11066081d60365ddf25d5867560d1ccdd3197dbe82a5b6e969cc940e3429ff1","integrity":"sha256-oRBmCB1gNl3fJdWGdWDRzN0xl9voKltulpzJQONCn/E="},"admin-7ce3dd717f7d12fcbc64caf14200230a1e68db439be0ba1879077599ff2c32c6.css":{"logical_path":"admin.css","mtime":"2019-10-24T10:10:08+08:00","size":852772,"digest":"7ce3dd717f7d12fcbc64caf14200230a1e68db439be0ba1879077599ff2c32c6","integrity":"sha256-fOPdcX99Evy8ZMrxQgAjCh5o20Ob4LoYeQd1mf8sMsY="},"college-93904c65d52c125aec0a463b9fd98bedda0018b78707f806be22685cca5d3747.css":{"logical_path":"college.css","mtime":"2019-10-24T10:10:08+08:00","size":579546,"digest":"93904c65d52c125aec0a463b9fd98bedda0018b78707f806be22685cca5d3747","integrity":"sha256-k5BMZdUsElrsCkY7n9mL7doAGLeHB/gGviJoXMpdN0c="},"cooperative-84c79d26a36aff5b496551b6d21b1bfb726b1bbc4153435a366115e96c204e06.js":{"logical_path":"cooperative.js","mtime":"2019-10-24T14:17:15+08:00","size":4338225,"digest":"84c79d26a36aff5b496551b6d21b1bfb726b1bbc4153435a366115e96c204e06","integrity":"sha256-hMedJqNq/1tJZVG20hsb+3JrG7xBU0NaNmEV6WwgTgY="},"cooperative-10a9ee5177e196572573ccea460e133c748072e223fdb473d05ee72c991fbbe3.css":{"logical_path":"cooperative.css","mtime":"2019-10-24T10:10:08+08:00","size":833351,"digest":"10a9ee5177e196572573ccea460e133c748072e223fdb473d05ee72c991fbbe3","integrity":"sha256-EKnuUXfhllclc8zqRg4TPHSAcuIj/bRz0F7nLJkfu+M="},"admin-441d8f3722e5f73e5748aaeb6f517101474cb1eb48a99f119e561f08b9e9dc60.js":{"logical_path":"admin.js","mtime":"2019-10-24T16:08:56+08:00","size":4525031,"digest":"441d8f3722e5f73e5748aaeb6f517101474cb1eb48a99f119e561f08b9e9dc60","integrity":"sha256-RB2PNyLl9z5XSKrrb1FxAUdMsetIqZ8RnlYfCLnp3GA="},"admin-76c52986591f274f639ad48dfbb480a1aeeec7647b6fa28fa541e78a064b6316.css":{"logical_path":"admin.css","mtime":"2019-10-24T15:25:17+08:00","size":867945,"digest":"76c52986591f274f639ad48dfbb480a1aeeec7647b6fa28fa541e78a064b6316","integrity":"sha256-dsUphlkfJ09jmtSN+7SAoa7ux2R7b6KPpUHnigZLYxY="},"cooperative-6c4c663b6b5071535bab2b76cc5e05ab5682665857763a76bf4f01afef51be5a.js":{"logical_path":"cooperative.js","mtime":"2019-10-24T17:56:20+08:00","size":4339039,"digest":"6c4c663b6b5071535bab2b76cc5e05ab5682665857763a76bf4f01afef51be5a","integrity":"sha256-bExmO2tQcVNbqyt2zF4Fq1aCZlhXdjp2v08Br+9Rvlo="},"admin-c63acadd431434979db50540a0bf7e65c75e1de0d1b449919f2cce89a0548d43.js":{"logical_path":"admin.js","mtime":"2019-10-24T18:12:33+08:00","size":4533182,"digest":"c63acadd431434979db50540a0bf7e65c75e1de0d1b449919f2cce89a0548d43","integrity":"sha256-xjrK3UMUNJedtQVAoL9+ZcdeHeDRtEmRnyzOiaBUjUM="},"admin-bd832b9a35eb3743dde9218beab61f9bcde1508767ad68dbedb1c89a4bb65c3a.css":{"logical_path":"admin.css","mtime":"2019-10-24T17:56:20+08:00","size":861450,"digest":"bd832b9a35eb3743dde9218beab61f9bcde1508767ad68dbedb1c89a4bb65c3a","integrity":"sha256-vYMrmjXrN0Pd6SGL6rYfm83hUIdnrWjb7bHImku2XDo="},"college-fa202780f3e7f96cb9b5916c6f0d7dd9e03cb746864bbd2dd491ed001c30ad8f.css":{"logical_path":"college.css","mtime":"2019-10-24T17:56:20+08:00","size":571936,"digest":"fa202780f3e7f96cb9b5916c6f0d7dd9e03cb746864bbd2dd491ed001c30ad8f","integrity":"sha256-+iAngPPn+Wy5tZFsbw192eA8t0aGS70t1JHtABwwrY8="},"cooperative-4f233e8963b0bd80bc56b71c209d31464d314240ac8d686806baf99511c53ad0.css":{"logical_path":"cooperative.css","mtime":"2019-10-24T17:56:20+08:00","size":825741,"digest":"4f233e8963b0bd80bc56b71c209d31464d314240ac8d686806baf99511c53ad0","integrity":"sha256-TyM+iWOwvYC8VrccIJ0xRk0xQkCsjWhoBrr5lRHFOtA="},"application-8c9d6bb61c50908f584b3070c79aeb95f25c1166d39e07da5e95438b39ca0de9.css":{"logical_path":"application.css","mtime":"2019-10-23T14:16:56+08:00","size":436995,"digest":"8c9d6bb61c50908f584b3070c79aeb95f25c1166d39e07da5e95438b39ca0de9","integrity":"sha256-jJ1rthxQkI9YSzBwx5rrlfJcEWbTngfaXpVDiznKDek="},"admin-bf2bd889f02d15c4913aa260497d72afeb26d701aac49a4ef6a75619af030152.js":{"logical_path":"admin.js","mtime":"2019-10-25T10:12:17+08:00","size":4533673,"digest":"bf2bd889f02d15c4913aa260497d72afeb26d701aac49a4ef6a75619af030152","integrity":"sha256-vyvYifAtFcSROqJgSX1yr+sm1wGqxJpO9qdWGa8DAVI="},"admin-46e564d29ffae5c71ae9b5e36dc0bd5de57b10f396eb2005bfb9cf51e7744cdd.css":{"logical_path":"admin.css","mtime":"2019-10-25T10:12:17+08:00","size":870355,"digest":"46e564d29ffae5c71ae9b5e36dc0bd5de57b10f396eb2005bfb9cf51e7744cdd","integrity":"sha256-RuVk0p/65cca6bXjbcC9XeV7EPOW6yAFv7nPUed0TN0="},"college-2299e05f5e9b640e333ece624d4ab18a678fdabff0bc18b69a9c2e3de49cba8e.css":{"logical_path":"college.css","mtime":"2019-10-25T10:12:17+08:00","size":580077,"digest":"2299e05f5e9b640e333ece624d4ab18a678fdabff0bc18b69a9c2e3de49cba8e","integrity":"sha256-IpngX16bZA4zPs5iTUqximeP2r/wvBi2mpwuPeScuo4="},"cooperative-f1ac8f14ad6ade8d1f79ca49ea9c79be77d49aae9d2705ca672e78444481700d.js":{"logical_path":"cooperative.js","mtime":"2019-10-25T11:01:38+08:00","size":4409145,"digest":"f1ac8f14ad6ade8d1f79ca49ea9c79be77d49aae9d2705ca672e78444481700d","integrity":"sha256-8ayPFK1q3o0fecpJ6px5vnfUmq6dJwXKZy54RESBcA0="},"cooperative-8057adee2454dbc9d648305faf9ede9824f40d3bd0184e816e8035bb7f1e730b.css":{"logical_path":"cooperative.css","mtime":"2019-10-25T10:12:17+08:00","size":833882,"digest":"8057adee2454dbc9d648305faf9ede9824f40d3bd0184e816e8035bb7f1e730b","integrity":"sha256-gFet7iRU28nWSDBfr57emCT0DTvQGE6BboA1u38ecws="},"admin-6f9bb9720e7e5040ae559a8fae11553313f77552a76416b3a9fe77198471964d.js":{"logical_path":"admin.js","mtime":"2019-10-25T17:00:09+08:00","size":4554537,"digest":"6f9bb9720e7e5040ae559a8fae11553313f77552a76416b3a9fe77198471964d","integrity":"sha256-b5u5cg5+UECuVZqPrhFVMxP3dVKnZBazqf53GYRxlk0="},"admin-ab3e0f7240ae4df8d1585c8d5e99df41edd3305ecc2abadcf8820796e1d9fc65.css":{"logical_path":"admin.css","mtime":"2019-10-25T09:55:22+08:00","size":862288,"digest":"ab3e0f7240ae4df8d1585c8d5e99df41edd3305ecc2abadcf8820796e1d9fc65","integrity":"sha256-qz4PckCuTfjRWFyNXpnfQe3TMF7MKrrc+IIHluHZ/GU="},"college-2fdfc5431b46ad4a454a25386dbcbc390466886f76b85fdb7e3f75018196a870.css":{"logical_path":"college.css","mtime":"2019-10-25T09:28:47+08:00","size":572010,"digest":"2fdfc5431b46ad4a454a25386dbcbc390466886f76b85fdb7e3f75018196a870","integrity":"sha256-L9/FQxtGrUpFSiU4bby8OQRmiG92uF/bfj91AYGWqHA="},"cooperative-47d516a0904d0633e82c1de39a6ec4c9e6de0a37813843e01d4bacf97e8b2ebf.css":{"logical_path":"cooperative.css","mtime":"2019-10-25T09:28:47+08:00","size":825815,"digest":"47d516a0904d0633e82c1de39a6ec4c9e6de0a37813843e01d4bacf97e8b2ebf","integrity":"sha256-R9UWoJBNBjPoLB3jmm7EyebeCjeBOEPgHUus+X6LLr8="},"admin-839af7c0d2917a8f8019d0376ea17cec050ef4d19d98c6c10de91f5d2bc81adf.js":{"logical_path":"admin.js","mtime":"2019-10-26T11:25:08+08:00","size":4554543,"digest":"839af7c0d2917a8f8019d0376ea17cec050ef4d19d98c6c10de91f5d2bc81adf","integrity":"sha256-g5r3wNKReo+AGdA3bqF87AUO9NGdmMbBDekfXSvIGt8="},"admin-52d692608c620ae47717a2ac88377e55b9b58af0acdf3f777814a1fd47b6594b.js":{"logical_path":"admin.js","mtime":"2019-10-26T17:16:18+08:00","size":4553202,"digest":"52d692608c620ae47717a2ac88377e55b9b58af0acdf3f777814a1fd47b6594b","integrity":"sha256-UtaSYIxiCuR3F6KsiDd+Vbm1ivCs3z93eBSh/Ue2WUs="},"admin-b58555ae641bbaa61e3af0ddd2756acc3c5de9023c737815c72132cc67c9a403.js":{"logical_path":"admin.js","mtime":"2019-10-27T13:06:02+08:00","size":4553607,"digest":"b58555ae641bbaa61e3af0ddd2756acc3c5de9023c737815c72132cc67c9a403","integrity":"sha256-tYVVrmQbuqYeOvDd0nVqzDxd6QI8c3gVxyEyzGfJpAM="},"admin-b95c48ea51f392ce7c4024d0d8b2c42dfe48528a1898ec3f7818e0cda2a4f224.js":{"logical_path":"admin.js","mtime":"2019-10-28T10:46:29+08:00","size":4554008,"digest":"b95c48ea51f392ce7c4024d0d8b2c42dfe48528a1898ec3f7818e0cda2a4f224","integrity":"sha256-uVxI6lHzks58QCTQ2LLELf5IUooYmOw/eBjgzaKk8iQ="},"admin-1f2e5a2a28462df8bcddfbdbfbebdc36a761ca7a94962ae71d37fb8c06fc5f94.js":{"logical_path":"admin.js","mtime":"2019-10-29T08:57:11+08:00","size":4554938,"digest":"1f2e5a2a28462df8bcddfbdbfbebdc36a761ca7a94962ae71d37fb8c06fc5f94","integrity":"sha256-Hy5aKihGLfi83fvb++vcNqdhynqUlirnHTf7jAb8X5Q="},"admin-cb3d4541758ef2bcbfe16f518d48f85097d0547a587de222d2fc13dbdd474b4b.js":{"logical_path":"admin.js","mtime":"2019-10-29T14:06:13+08:00","size":4556541,"digest":"cb3d4541758ef2bcbfe16f518d48f85097d0547a587de222d2fc13dbdd474b4b","integrity":"sha256-yz1FQXWO8ry/4W9RjUj4UJfQVHpYfeIi0vwT291HS0s="},"admin-6a76c25b6691b4f436608be28606d90c907ba8f033f5f47c6c20d7bf11251cb6.css":{"logical_path":"admin.css","mtime":"2019-10-29T14:22:47+08:00","size":871031,"digest":"6a76c25b6691b4f436608be28606d90c907ba8f033f5f47c6c20d7bf11251cb6","integrity":"sha256-anbCW2aRtPQ2YIvihgbZDJB7qPAz9fR8bCDXvxElHLY="},"admin-ba909dfe0de4d216bedb3c743144321e4023837568abab1d3ee9a28b2faa5925.js":{"logical_path":"admin.js","mtime":"2019-10-29T14:43:01+08:00","size":4556622,"digest":"ba909dfe0de4d216bedb3c743144321e4023837568abab1d3ee9a28b2faa5925","integrity":"sha256-upCd/g3k0ha+2zx0MUQyHkAjg3Voq6sdPumiiy+qWSU="},"admin-e975e2039206e9ae2b6a072fee083cf39b8e04f2318f67bfbf1923fe208456b3.js":{"logical_path":"admin.js","mtime":"2019-10-29T15:50:27+08:00","size":4559454,"digest":"e975e2039206e9ae2b6a072fee083cf39b8e04f2318f67bfbf1923fe208456b3","integrity":"sha256-6XXiA5IG6a4ragcv7gg885uOBPIxj2e/vxkj/iCEVrM="},"cooperative-a309d245cd0b0b9c653db471c53ec090e49ba7ad885879ffa02a11b6efd79d74.js":{"logical_path":"cooperative.js","mtime":"2019-10-29T15:50:27+08:00","size":4409163,"digest":"a309d245cd0b0b9c653db471c53ec090e49ba7ad885879ffa02a11b6efd79d74","integrity":"sha256-ownSRc0LC5xlPbRxxT7AkOSbp62IWHn/oCoRtu/XnXQ="},"admin-5d791c4f4a14e1586cfa44776ae262b3c1494e1c0fb0e00c330f0cb9d30fd7ba.js":{"logical_path":"admin.js","mtime":"2019-11-01T08:41:10+08:00","size":4563272,"digest":"5d791c4f4a14e1586cfa44776ae262b3c1494e1c0fb0e00c330f0cb9d30fd7ba","integrity":"sha256-XXkcT0oU4Vhs+kR3auJis8FJThwPsOAMMw8MudMP17o="},"admin-70dc0e7136a8f54139e4167c00f3fde9ccc92b404b01b37ac6064913806e3f6e.css":{"logical_path":"admin.css","mtime":"2019-10-31T10:05:33+08:00","size":872438,"digest":"70dc0e7136a8f54139e4167c00f3fde9ccc92b404b01b37ac6064913806e3f6e","integrity":"sha256-cNwOcTao9UE55BZ8APP96czJK0BLAbN6xgZJE4BuP24="},"cooperative-4fe879591997da39d38e94f6f5eb3b688aa827fa42cb8fd73d21bc96ed880236.js":{"logical_path":"cooperative.js","mtime":"2019-11-06T11:34:24+08:00","size":4409560,"digest":"4fe879591997da39d38e94f6f5eb3b688aa827fa42cb8fd73d21bc96ed880236","integrity":"sha256-T+h5WRmX2jnTjpT29es7aIqoJ/pCy4/XPSG8lu2IAjY="},"admin-22af0d0f8c16da312c9355540590a8b6de54729793a1bef2782aa4f9469a77ad.js":{"logical_path":"admin.js","mtime":"2019-11-01T18:45:10+08:00","size":4563272,"digest":"22af0d0f8c16da312c9355540590a8b6de54729793a1bef2782aa4f9469a77ad","integrity":"sha256-Iq8ND4wW2jEsk1VUBZCott5UcpeTob7yeCqk+Uaad60="},"admin-4c1fcf5a570db989682ef483c1016234a3b7614e0a8d42c040eed220298fef8c.css":{"logical_path":"admin.css","mtime":"2019-11-01T18:45:10+08:00","size":864367,"digest":"4c1fcf5a570db989682ef483c1016234a3b7614e0a8d42c040eed220298fef8c","integrity":"sha256-TB/PWlcNuYloLvSDwQFiNKO3YU4KjULAQO7SICmP74w="},"admin-1a7f715f91fa17b0018650fa22b43778aad419157b8fdb9d67a02ea11760349b.js":{"logical_path":"admin.js","mtime":"2019-11-05T16:10:50+08:00","size":4565998,"digest":"1a7f715f91fa17b0018650fa22b43778aad419157b8fdb9d67a02ea11760349b","integrity":"sha256-Gn9xX5H6F7ABhlD6IrQ3eKrUGRV7j9udZ6AuoRdgNJs="},"admin-5902a288ce59247a683d5719189fc6260be1e38181872e710a7643ad60caaccc.css":{"logical_path":"admin.css","mtime":"2019-11-02T08:33:50+08:00","size":872434,"digest":"5902a288ce59247a683d5719189fc6260be1e38181872e710a7643ad60caaccc","integrity":"sha256-WQKiiM5ZJHpoPVcZGJ/GJgvh44GBhy5xCnZDrWDKrMw="},"admin-8ceaca69cdf7194b7a94e49b16ac5fa272f80e86493e5da27ab5a729de22c07a.js":{"logical_path":"admin.js","mtime":"2019-11-07T10:59:11+08:00","size":4575131,"digest":"8ceaca69cdf7194b7a94e49b16ac5fa272f80e86493e5da27ab5a729de22c07a","integrity":"sha256-jOrKac33GUt6lOSbFqxfonL4DoZJPl2ierWnKd4iwHo="},"admin-80cc0a53298c5abd849f03d8b8514d3263ef67fce38f83c05b1da0d30d3bb235.js":{"logical_path":"admin.js","mtime":"2019-11-07T17:20:35+08:00","size":4575538,"digest":"80cc0a53298c5abd849f03d8b8514d3263ef67fce38f83c05b1da0d30d3bb235","integrity":"sha256-gMwKUymMWr2EnwPYuFFNMmPvZ/zjj4PAWx2g0w07sjU="},"admin-62cbd1f755bfb83be19051bd099d6c58bf760d144f2eb890a06d98e694506dd4.js":{"logical_path":"admin.js","mtime":"2019-11-08T09:49:05+08:00","size":4575562,"digest":"62cbd1f755bfb83be19051bd099d6c58bf760d144f2eb890a06d98e694506dd4","integrity":"sha256-YsvR91W/uDvhkFG9CZ1sWL92DRRPLriQoG2Y5pRQbdQ="},"admin-a43c5a60b3ce25fd4322aa44414813d41aad3f6f94966f9b691f2f13b41277df.css":{"logical_path":"admin.css","mtime":"2019-11-08T10:05:27+08:00","size":872744,"digest":"a43c5a60b3ce25fd4322aa44414813d41aad3f6f94966f9b691f2f13b41277df","integrity":"sha256-pDxaYLPOJf1DIqpEQUgT1BqtP2+Ulm+baR8vE7QSd98="},"cooperative-874fcb0a0f1072b2245fbd18f00e0eb0e6a9e2e5b995008688b47693c3495699.js":{"logical_path":"cooperative.js","mtime":"2019-11-08T10:32:25+08:00","size":4415335,"digest":"874fcb0a0f1072b2245fbd18f00e0eb0e6a9e2e5b995008688b47693c3495699","integrity":"sha256-h0/LCg8QcrIkX70Y8A4OsOap4uW5lQCGiLR2k8NJVpk="},"cooperative-57330e03998e7ea29c4342718d18b36cc396a4a466c9b8c4ad371e44159f14d6.css":{"logical_path":"cooperative.css","mtime":"2019-11-08T10:05:46+08:00","size":834203,"digest":"57330e03998e7ea29c4342718d18b36cc396a4a466c9b8c4ad371e44159f14d6","integrity":"sha256-VzMOA5mOfqKcQ0JxjRizbMOWpKRmybjErTceRBWfFNY="}},"assets":{"admin.js":"admin-62cbd1f755bfb83be19051bd099d6c58bf760d144f2eb890a06d98e694506dd4.js","admin.css":"admin-a43c5a60b3ce25fd4322aa44414813d41aad3f6f94966f9b691f2f13b41277df.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-2299e05f5e9b640e333ece624d4ab18a678fdabff0bc18b69a9c2e3de49cba8e.css","logo.png":"logo-7ff112568709bf97f9898fe87249b7a8f200ff1f48d537d85af87215f1870423.png","application.js":"application-9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb.js","application.css":"application-0e417478d56f42467e857cd186b29cbbc0d6c7c6e85c8a6f42f39ac618943de8.css","cooperative.js":"cooperative-874fcb0a0f1072b2245fbd18f00e0eb0e6a9e2e5b995008688b47693c3495699.js","cooperative.css":"cooperative-57330e03998e7ea29c4342718d18b36cc396a4a466c9b8c4ad371e44159f14d6.css"}}
\ No newline at end of file
diff --git a/public/assets/admin-8ceaca69cdf7194b7a94e49b16ac5fa272f80e86493e5da27ab5a729de22c07a.js b/public/assets/admin-62cbd1f755bfb83be19051bd099d6c58bf760d144f2eb890a06d98e694506dd4.js
similarity index 99%
rename from public/assets/admin-8ceaca69cdf7194b7a94e49b16ac5fa272f80e86493e5da27ab5a729de22c07a.js
rename to public/assets/admin-62cbd1f755bfb83be19051bd099d6c58bf760d144f2eb890a06d98e694506dd4.js
index c7c0497f4..84af99c3f 100644
--- a/public/assets/admin-8ceaca69cdf7194b7a94e49b16ac5fa272f80e86493e5da27ab5a729de22c07a.js
+++ b/public/assets/admin-62cbd1f755bfb83be19051bd099d6c58bf760d144f2eb890a06d98e694506dd4.js
@@ -137758,16 +137758,23 @@ $(document).on('turbolinks:load', function() {
       theme: 'bootstrap4',
       placeholder: '请输入实训名称/创建者检索',
       multiple: true,
-      minimumInputLength: 1,
+      closeOnSelect: false,
       ajax: {
         delay: 500,
         url: '/admins/laboratories/' + laboratoryId + '/shixuns_for_select',
         dataType: 'json',
         data: function(params){
-          return { keyword: params.term };
+          return { keyword: params.term, page: params.page || 1, per_page: 20 };
         },
-        processResults: function(data){
-          return { results: data.shixuns }
+        processResults: function(data, params){
+          params.page = params.page || 1;
+
+          return {
+            results: data.shixuns,
+            pagination: {
+              more: (params.page * 20) < data.count
+            }
+          };
         }
       },
       templateResult: function (item) {
@@ -137822,6 +137829,7 @@ $(document).on('turbolinks:load', function() {
     $searchForm.find('.school-select').select2({
       theme: 'bootstrap4',
       placeholder: '请选择创建者单位',
+      allowClear: true,
       minimumInputLength: 1,
       ajax: {
         delay: 500,
@@ -137900,16 +137908,23 @@ $(document).on('turbolinks:load', function() {
       theme: 'bootstrap4',
       placeholder: '请输入课程名称/创建者检索',
       multiple: true,
-      minimumInputLength: 1,
+      closeOnSelect: false,
       ajax: {
         delay: 500,
         url: '/admins/laboratories/' + laboratoryId + '/subjects_for_select',
         dataType: 'json',
         data: function(params){
-          return { keyword: params.term };
+          return { keyword: params.term, page: params.page || 1, per_page: 20 }
         },
-        processResults: function(data){
-          return { results: data.subjects }
+        processResults: function(data, params){
+          params.page = params.page || 1;
+
+          return {
+            results: data.subjects,
+            pagination: {
+              more: (params.page * 20) < data.count
+            }
+          };
         }
       },
       templateResult: function (item) {
diff --git a/public/assets/admin-8ceaca69cdf7194b7a94e49b16ac5fa272f80e86493e5da27ab5a729de22c07a.js.gz b/public/assets/admin-62cbd1f755bfb83be19051bd099d6c58bf760d144f2eb890a06d98e694506dd4.js.gz
similarity index 97%
rename from public/assets/admin-8ceaca69cdf7194b7a94e49b16ac5fa272f80e86493e5da27ab5a729de22c07a.js.gz
rename to public/assets/admin-62cbd1f755bfb83be19051bd099d6c58bf760d144f2eb890a06d98e694506dd4.js.gz
index adcf11567..ff59a98b5 100644
Binary files a/public/assets/admin-8ceaca69cdf7194b7a94e49b16ac5fa272f80e86493e5da27ab5a729de22c07a.js.gz and b/public/assets/admin-62cbd1f755bfb83be19051bd099d6c58bf760d144f2eb890a06d98e694506dd4.js.gz differ
diff --git a/public/assets/admin-5902a288ce59247a683d5719189fc6260be1e38181872e710a7643ad60caaccc.css b/public/assets/admin-a43c5a60b3ce25fd4322aa44414813d41aad3f6f94966f9b691f2f13b41277df.css
similarity index 99%
rename from public/assets/admin-5902a288ce59247a683d5719189fc6260be1e38181872e710a7643ad60caaccc.css
rename to public/assets/admin-a43c5a60b3ce25fd4322aa44414813d41aad3f6f94966f9b691f2f13b41277df.css
index 2d96ecdae..bbbf2c50a 100644
--- a/public/assets/admin-5902a288ce59247a683d5719189fc6260be1e38181872e710a7643ad60caaccc.css
+++ b/public/assets/admin-a43c5a60b3ce25fd4322aa44414813d41aad3f6f94966f9b691f2f13b41277df.css
@@ -25487,17 +25487,28 @@ input.form-control {
   height: 300px;
 }
 
-/* line 51, app/assets/stylesheets/admins/common.scss */
+/* line 50, app/assets/stylesheets/admins/common.scss */
+.admin-body-container .image-preview-container {
+  display: -webkit-box;
+  display: flex;
+  -webkit-box-orient: vertical;
+  -webkit-box-direction: normal;
+          flex-direction: column;
+  -webkit-box-align: center;
+          align-items: center;
+}
+
+/* line 57, app/assets/stylesheets/admins/common.scss */
 .admin-body-container .action-container > .action {
   padding: 0 3px;
 }
 
-/* line 56, app/assets/stylesheets/admins/common.scss */
+/* line 62, app/assets/stylesheets/admins/common.scss */
 .admin-body-container .action-container .more-action-dropdown .dropdown-item {
   font-size: 14px;
 }
 
-/* line 63, app/assets/stylesheets/admins/common.scss */
+/* line 69, app/assets/stylesheets/admins/common.scss */
 .admin-body-container .paginate-container {
   margin-top: 20px;
   display: -webkit-box;
@@ -25511,68 +25522,68 @@ input.form-control {
           align-items: center;
 }
 
-/* line 70, app/assets/stylesheets/admins/common.scss */
+/* line 76, app/assets/stylesheets/admins/common.scss */
 .admin-body-container .paginate-container .paginate-total {
   margin-bottom: 10px;
   color: darkgrey;
 }
 
-/* line 75, app/assets/stylesheets/admins/common.scss */
+/* line 81, app/assets/stylesheets/admins/common.scss */
 .admin-body-container .paginate-container .pagination {
   margin-bottom: 0px;
 }
 
-/* line 81, app/assets/stylesheets/admins/common.scss */
+/* line 87, app/assets/stylesheets/admins/common.scss */
 .admin-body-container .search-form-container {
   display: -webkit-box;
   display: flex;
   margin-bottom: 20px;
 }
 
-/* line 85, app/assets/stylesheets/admins/common.scss */
+/* line 91, app/assets/stylesheets/admins/common.scss */
 .admin-body-container .search-form-container .search-form {
   -webkit-box-flex: 1;
           flex: 1;
 }
 
-/* line 88, app/assets/stylesheets/admins/common.scss */
+/* line 94, app/assets/stylesheets/admins/common.scss */
 .admin-body-container .search-form-container .search-form * {
   font-size: 14px;
 }
 
-/* line 90, app/assets/stylesheets/admins/common.scss */
+/* line 96, app/assets/stylesheets/admins/common.scss */
 .admin-body-container .search-form-container .search-form select, .admin-body-container .search-form-container .search-form input {
   margin-right: 10px;
   font-size: 14px;
 }
 
-/* line 97, app/assets/stylesheets/admins/common.scss */
+/* line 103, app/assets/stylesheets/admins/common.scss */
 .admin-body-container .global-error {
   color: grey;
   min-height: 300px;
 }
 
-/* line 101, app/assets/stylesheets/admins/common.scss */
+/* line 107, app/assets/stylesheets/admins/common.scss */
 .admin-body-container .global-error-code {
   font-size: 80px;
 }
 
-/* line 105, app/assets/stylesheets/admins/common.scss */
+/* line 111, app/assets/stylesheets/admins/common.scss */
 .admin-body-container .global-error-text {
   font-size: 24px;
 }
 
-/* line 111, app/assets/stylesheets/admins/common.scss */
+/* line 117, app/assets/stylesheets/admins/common.scss */
 .admin-body-container .nav-tabs .nav-link {
   padding: 0.5rem 2rem;
 }
 
-/* line 116, app/assets/stylesheets/admins/common.scss */
+/* line 122, app/assets/stylesheets/admins/common.scss */
 .admin-body-container .CodeMirror {
   border: 1px solid #ced4da;
 }
 
-/* line 120, app/assets/stylesheets/admins/common.scss */
+/* line 126, app/assets/stylesheets/admins/common.scss */
 .admin-body-container .batch-action-container {
   margin-bottom: -15px;
   padding: 10px 20px 0;
diff --git a/public/assets/admin-5902a288ce59247a683d5719189fc6260be1e38181872e710a7643ad60caaccc.css.gz b/public/assets/admin-a43c5a60b3ce25fd4322aa44414813d41aad3f6f94966f9b691f2f13b41277df.css.gz
similarity index 78%
rename from public/assets/admin-5902a288ce59247a683d5719189fc6260be1e38181872e710a7643ad60caaccc.css.gz
rename to public/assets/admin-a43c5a60b3ce25fd4322aa44414813d41aad3f6f94966f9b691f2f13b41277df.css.gz
index d40b4868f..20f375d4d 100644
Binary files a/public/assets/admin-5902a288ce59247a683d5719189fc6260be1e38181872e710a7643ad60caaccc.css.gz and b/public/assets/admin-a43c5a60b3ce25fd4322aa44414813d41aad3f6f94966f9b691f2f13b41277df.css.gz differ
diff --git a/public/assets/cooperative-8057adee2454dbc9d648305faf9ede9824f40d3bd0184e816e8035bb7f1e730b.css b/public/assets/cooperative-57330e03998e7ea29c4342718d18b36cc396a4a466c9b8c4ad371e44159f14d6.css
similarity index 99%
rename from public/assets/cooperative-8057adee2454dbc9d648305faf9ede9824f40d3bd0184e816e8035bb7f1e730b.css
rename to public/assets/cooperative-57330e03998e7ea29c4342718d18b36cc396a4a466c9b8c4ad371e44159f14d6.css
index f8ac15614..0a221d856 100644
--- a/public/assets/cooperative-8057adee2454dbc9d648305faf9ede9824f40d3bd0184e816e8035bb7f1e730b.css
+++ b/public/assets/cooperative-57330e03998e7ea29c4342718d18b36cc396a4a466c9b8c4ad371e44159f14d6.css
@@ -25047,17 +25047,28 @@ input.form-control {
   height: 300px;
 }
 
-/* line 51, app/assets/stylesheets/cooperative/common.scss */
+/* line 50, app/assets/stylesheets/cooperative/common.scss */
+.cooperative-body-container .image-preview-container {
+  display: -webkit-box;
+  display: flex;
+  -webkit-box-orient: vertical;
+  -webkit-box-direction: normal;
+          flex-direction: column;
+  -webkit-box-align: center;
+          align-items: center;
+}
+
+/* line 57, app/assets/stylesheets/cooperative/common.scss */
 .cooperative-body-container .action-container > .action {
   padding: 0 3px;
 }
 
-/* line 56, app/assets/stylesheets/cooperative/common.scss */
+/* line 62, app/assets/stylesheets/cooperative/common.scss */
 .cooperative-body-container .action-container .more-action-dropdown .dropdown-item {
   font-size: 14px;
 }
 
-/* line 63, app/assets/stylesheets/cooperative/common.scss */
+/* line 69, app/assets/stylesheets/cooperative/common.scss */
 .cooperative-body-container .paginate-container {
   margin-top: 20px;
   display: -webkit-box;
@@ -25071,68 +25082,68 @@ input.form-control {
           align-items: center;
 }
 
-/* line 70, app/assets/stylesheets/cooperative/common.scss */
+/* line 76, app/assets/stylesheets/cooperative/common.scss */
 .cooperative-body-container .paginate-container .paginate-total {
   margin-bottom: 10px;
   color: darkgrey;
 }
 
-/* line 75, app/assets/stylesheets/cooperative/common.scss */
+/* line 81, app/assets/stylesheets/cooperative/common.scss */
 .cooperative-body-container .paginate-container .pagination {
   margin-bottom: 0px;
 }
 
-/* line 81, app/assets/stylesheets/cooperative/common.scss */
+/* line 87, app/assets/stylesheets/cooperative/common.scss */
 .cooperative-body-container .search-form-container {
   display: -webkit-box;
   display: flex;
   margin-bottom: 20px;
 }
 
-/* line 85, app/assets/stylesheets/cooperative/common.scss */
+/* line 91, app/assets/stylesheets/cooperative/common.scss */
 .cooperative-body-container .search-form-container .search-form {
   -webkit-box-flex: 1;
           flex: 1;
 }
 
-/* line 88, app/assets/stylesheets/cooperative/common.scss */
+/* line 94, app/assets/stylesheets/cooperative/common.scss */
 .cooperative-body-container .search-form-container .search-form * {
   font-size: 14px;
 }
 
-/* line 90, app/assets/stylesheets/cooperative/common.scss */
+/* line 96, app/assets/stylesheets/cooperative/common.scss */
 .cooperative-body-container .search-form-container .search-form select, .cooperative-body-container .search-form-container .search-form input {
   margin-right: 10px;
   font-size: 14px;
 }
 
-/* line 97, app/assets/stylesheets/cooperative/common.scss */
+/* line 103, app/assets/stylesheets/cooperative/common.scss */
 .cooperative-body-container .global-error {
   color: grey;
   min-height: 300px;
 }
 
-/* line 101, app/assets/stylesheets/cooperative/common.scss */
+/* line 107, app/assets/stylesheets/cooperative/common.scss */
 .cooperative-body-container .global-error-code {
   font-size: 80px;
 }
 
-/* line 105, app/assets/stylesheets/cooperative/common.scss */
+/* line 111, app/assets/stylesheets/cooperative/common.scss */
 .cooperative-body-container .global-error-text {
   font-size: 24px;
 }
 
-/* line 111, app/assets/stylesheets/cooperative/common.scss */
+/* line 117, app/assets/stylesheets/cooperative/common.scss */
 .cooperative-body-container .nav-tabs .nav-link {
   padding: 0.5rem 2rem;
 }
 
-/* line 116, app/assets/stylesheets/cooperative/common.scss */
+/* line 122, app/assets/stylesheets/cooperative/common.scss */
 .cooperative-body-container .CodeMirror {
   border: 1px solid #ced4da;
 }
 
-/* line 120, app/assets/stylesheets/cooperative/common.scss */
+/* line 126, app/assets/stylesheets/cooperative/common.scss */
 .cooperative-body-container .batch-action-container {
   margin-bottom: -15px;
   padding: 10px 20px 0;
diff --git a/public/assets/cooperative-8057adee2454dbc9d648305faf9ede9824f40d3bd0184e816e8035bb7f1e730b.css.gz b/public/assets/cooperative-57330e03998e7ea29c4342718d18b36cc396a4a466c9b8c4ad371e44159f14d6.css.gz
similarity index 79%
rename from public/assets/cooperative-8057adee2454dbc9d648305faf9ede9824f40d3bd0184e816e8035bb7f1e730b.css.gz
rename to public/assets/cooperative-57330e03998e7ea29c4342718d18b36cc396a4a466c9b8c4ad371e44159f14d6.css.gz
index acb04f26f..d4ed16582 100644
Binary files a/public/assets/cooperative-8057adee2454dbc9d648305faf9ede9824f40d3bd0184e816e8035bb7f1e730b.css.gz and b/public/assets/cooperative-57330e03998e7ea29c4342718d18b36cc396a4a466c9b8c4ad371e44159f14d6.css.gz differ
diff --git a/public/assets/cooperative-4fe879591997da39d38e94f6f5eb3b688aa827fa42cb8fd73d21bc96ed880236.js b/public/assets/cooperative-874fcb0a0f1072b2245fbd18f00e0eb0e6a9e2e5b995008688b47693c3495699.js
similarity index 99%
rename from public/assets/cooperative-4fe879591997da39d38e94f6f5eb3b688aa827fa42cb8fd73d21bc96ed880236.js
rename to public/assets/cooperative-874fcb0a0f1072b2245fbd18f00e0eb0e6a9e2e5b995008688b47693c3495699.js
index cf5015df2..65e2ac5d7 100644
--- a/public/assets/cooperative-4fe879591997da39d38e94f6f5eb3b688aa827fa42cb8fd73d21bc96ed880236.js
+++ b/public/assets/cooperative-874fcb0a0f1072b2245fbd18f00e0eb0e6a9e2e5b995008688b47693c3495699.js
@@ -136047,6 +136047,151 @@ $(document).on('turbolinks:load', function() {
     })
   }
 });
+$(document).on('turbolinks:load', function() {
+  if ($('body.cooperative-laboratory-shixuns-index-page').length > 0) {
+    var $searchForm = $('.laboratory-shixun-list-form .search-form');
+
+    $searchForm.find('select#tag_id').select2({
+      placeholder: "请选择",
+      allowClear: true
+    });
+
+    // 上传图片
+    $('.modal.cooperative-upload-file-modal').on('upload:success', function (e, data) {
+      var $imageElement = $('.shixun-image-' + data.source_id);
+      if($imageElement.length === 0) return;
+      $imageElement.attr('src', data.url);
+      $imageElement.show();
+      $imageElement.next().html('重新上传');
+    });
+
+    // 定义状态切换监听事件
+    var defineStatusChangeFunc = function (doElement, undoElement, url, callback) {
+      $('.laboratory-shixun-list-container').on('click', doElement, function () {
+        var $doAction = $(this);
+        var $undoAction = $doAction.siblings(undoElement);
+
+        var laboratoryShixunId = $doAction.data('id');
+        customConfirm({
+          content: '确认进行该操作吗?',
+          ok: function () {
+            $.ajax({
+              url: '/cooperative/laboratory_shixuns/' + laboratoryShixunId + url,
+              method: 'POST',
+              dataType: 'json',
+              success: function () {
+                show_success_flash();
+                $doAction.hide();
+                $undoAction.show();
+                if (callback && typeof callback === "function") {
+                  callback(laboratoryShixunId, url);
+                }
+              }
+            });
+          }
+        });
+      });
+    }
+
+    // 首页展示与取消首页展示
+    var homepageShowCallback = function (laboratoryShixunId, url) {
+      var $laboratoryShixunItem = $('.laboratory-shixun-list-container').find('.laboratory-shixun-item-' + laboratoryShixunId);
+
+      if (url === '/homepage') {
+        $laboratoryShixunItem.find('.homepage-badge').show();
+      } else {
+        $laboratoryShixunItem.find('.homepage-badge').hide();
+      }
+    }
+    defineStatusChangeFunc('.homepage-show-action', '.homepage-hide-action', '/homepage', homepageShowCallback);
+    defineStatusChangeFunc('.homepage-hide-action', '.homepage-show-action', '/cancel_homepage', homepageShowCallback);
+  }
+})
+;
+$(document).on('turbolinks:load', function() {
+  if ($('body.cooperative-laboratory-subjects-index-page').length > 0) {
+    var $searchForm = $('.laboratory-subject-list-form .search-form');
+
+    // ************** 学校选择 *************
+    $searchForm.find('.school-select').select2({
+      theme: 'bootstrap4',
+      placeholder: '请选择创建者单位',
+      allowClear: true,
+      minimumInputLength: 1,
+      ajax: {
+        delay: 500,
+        url: '/api/schools/search.json',
+        dataType: 'json',
+        data: function (params) {
+          return {keyword: params.term};
+        },
+        processResults: function (data) {
+          return {results: data.schools}
+        }
+      },
+      templateResult: function (item) {
+        if (!item.id || item.id === '') return item.text;
+        return item.name;
+      },
+      templateSelection: function (item) {
+        if (item.id) {
+        }
+        return item.name || item.text;
+      }
+    });
+
+    // 上传图片
+    $('.modal.cooperative-upload-file-modal').on('upload:success', function (e, data) {
+      var $imageElement = $('.subject-image-' + data.source_id);
+      if($imageElement.length === 0) return;
+      $imageElement.attr('src', data.url);
+      $imageElement.show();
+      $imageElement.next().html('重新上传');
+    });
+
+    // 定义状态切换监听事件
+    var defineStatusChangeFunc = function (doElement, undoElement, url, callback) {
+      $('.laboratory-subject-list-container').on('click', doElement, function () {
+        var $doAction = $(this);
+        var $undoAction = $doAction.siblings(undoElement);
+
+        var laboratorySubjectId = $doAction.data('id');
+        customConfirm({
+          content: '确认进行该操作吗?',
+          ok: function () {
+            $.ajax({
+              url: '/cooperative/laboratory_subjects/' + laboratorySubjectId + url,
+              method: 'POST',
+              dataType: 'json',
+              success: function () {
+                show_success_flash();
+                $doAction.hide();
+                $undoAction.show();
+                if (callback && typeof callback === "function") {
+                  callback(laboratorySubjectId, url);
+                }
+              }
+            });
+          }
+        });
+      });
+    }
+
+    // 首页展示与取消首页展示
+    var homepageShowCallback = function (laboratoryShixunId, url) {
+      var $laboratoryShixunItem = $('.laboratory-subject-list-container').find('.laboratory-subject-item-' + laboratoryShixunId);
+
+      if (url === '/homepage') {
+        $laboratoryShixunItem.find('.homepage-badge').show();
+      } else {
+        $laboratoryShixunItem.find('.homepage-badge').hide();
+      }
+    }
+    defineStatusChangeFunc('.homepage-show-action', '.homepage-hide-action', '/homepage', homepageShowCallback);
+    defineStatusChangeFunc('.homepage-hide-action', '.homepage-show-action', '/cancel_homepage', homepageShowCallback);
+  }
+})
+;
 $(document).on('turbolinks:load', function() {
   if ($('body.cooperative-laboratory-users-index-page').length > 0) {
     // ============= 添加管理员 ==============
@@ -136109,6 +136254,24 @@ $(document).on('turbolinks:load', function() {
     });
   }
 });
+$(document).on('turbolinks:load', function () {
+	$('.cooperative-modal-container').on('show.bs.modal', '.modal.cooperative-edit-subject-modal', function () {
+		var $modal = $('.modal.cooperative-edit-subject-modal');
+		var $form = $modal.find('form.cooperative-edit-subject-form');
+
+		$modal.on('click', '.submit-btn', function () {
+			$form.find('.error').html('');
+			var url = $form.attr('action');
+
+			$.ajax({
+				method: 'PATCH',
+				dataType: 'script',
+				url: url,
+				data: $form.serialize()
+			});
+		});
+	})
+});
 $(document).on('turbolinks:load', function() {
   var $modal = $('.modal.cooperative-upload-file-modal');
   if ($modal.length > 0) {
@@ -136153,7 +136316,7 @@ $(document).on('turbolinks:load', function() {
         $.ajax({
           method: 'POST',
           dataType: 'json',
-          url: '/cooperatives/files?' + formDataString,
+          url: '/cooperative/files?' + formDataString,
           data: new FormData($form[0]),
           processData: false,
           contentType: false,
diff --git a/public/assets/cooperative-4fe879591997da39d38e94f6f5eb3b688aa827fa42cb8fd73d21bc96ed880236.js.gz b/public/assets/cooperative-874fcb0a0f1072b2245fbd18f00e0eb0e6a9e2e5b995008688b47693c3495699.js.gz
similarity index 98%
rename from public/assets/cooperative-4fe879591997da39d38e94f6f5eb3b688aa827fa42cb8fd73d21bc96ed880236.js.gz
rename to public/assets/cooperative-874fcb0a0f1072b2245fbd18f00e0eb0e6a9e2e5b995008688b47693c3495699.js.gz
index b034ad16d..8ae176f36 100644
Binary files a/public/assets/cooperative-4fe879591997da39d38e94f6f5eb3b688aa827fa42cb8fd73d21bc96ed880236.js.gz and b/public/assets/cooperative-874fcb0a0f1072b2245fbd18f00e0eb0e6a9e2e5b995008688b47693c3495699.js.gz differ
diff --git a/public/images/educoder/competitions/heikesong.jpg b/public/images/educoder/competitions/heikesong.jpg
new file mode 100644
index 000000000..8c0109413
Binary files /dev/null and b/public/images/educoder/competitions/heikesong.jpg differ
diff --git a/public/react/config/webpack.config.dev.js b/public/react/config/webpack.config.dev.js
index 743b3685c..f335f1705 100644
--- a/public/react/config/webpack.config.dev.js
+++ b/public/react/config/webpack.config.dev.js
@@ -32,7 +32,7 @@ module.exports = {
   // See the discussion in https://github.com/facebookincubator/create-react-app/issues/343.s
 	// devtool: "cheap-module-eval-source-map",
   // 开启调试
-	// devtool: "source-map",  // 开启调试
+	devtool: "source-map",  // 开启调试
   // These are the "entry points" to our application.
   // This means they will be the "root" imports that are included in JS bundle.
   // The first two entry points enable "hot" CSS and auto-refreshes for JS.
diff --git a/public/react/src/modules/courses/boards/BoardsNew.js b/public/react/src/modules/courses/boards/BoardsNew.js
index 67daa9189..018d16853 100644
--- a/public/react/src/modules/courses/boards/BoardsNew.js
+++ b/public/react/src/modules/courses/boards/BoardsNew.js
@@ -3,7 +3,7 @@ import React,{ Component } from "react";
 import {
   Form, Input, InputNumber, Switch, Radio,
   Slider, Button, Upload, Icon, Rate, Checkbox, message,
-  Row, Col, Select, Modal, Divider
+  Row, Col, Select, Modal, Divider,Tooltip
 } from 'antd';
 import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor';
 import axios from 'axios'
@@ -29,7 +29,9 @@ class BoardsNew extends Component{
       fileList: [],
       boards: [],
       title_num: 0,
-			email_notify:false
+			email_notify:false,
+			isemail_notify:false,
+			isemail_notifys:false
     }
   }
   addSuccess = () => {
@@ -98,7 +100,7 @@ class BoardsNew extends Component{
                 }
               })
           
-              this.setState({ fileList: _fileList, board_name: data.board_name, title_num: parseInt(data.subject.length) })
+              this.setState({ fileList: _fileList, board_name: data.board_name, title_num: parseInt(data.subject.length) ,isemail_notifys:response.data.data.email_notify})
             }
           }
         })
@@ -136,6 +138,7 @@ class BoardsNew extends Component{
             select_board_id: values.select_board_id,
             content: values.content,
             sticky: values.sticky,
+						email_notify:this.state.isemail_notify,
             attachment_ids,
           })
             .then((response) => {
@@ -160,7 +163,7 @@ class BoardsNew extends Component{
           
           axios.post(url, {
             ...values,
-						email_notify:this.state.email_notify,
+						email_notify:this.state.isemail_notify,
             course_id: cid,
             attachment_ids,
           })
@@ -253,7 +256,7 @@ class BoardsNew extends Component{
 
 	setemailchange=(e)=>{
   	this.setState({
-			email_notify:e.target.checked
+			isemail_notify:e.target.checked
 		})
 	}
   render() {
@@ -299,6 +302,7 @@ class BoardsNew extends Component{
     const isCourseEnd = this.props.isCourseEnd();
 		document.title=this.props.coursedata&&this.props.coursedata.name;
 
+		// console.log(this.state)
     return(
         <div className="newMain ">
           <AddDirModal {...this.props}
@@ -338,13 +342,17 @@ class BoardsNew extends Component{
             {/* notRequired */}
             <Form {...formItemLayout} onSubmit={this.handleSubmit}>
               <div className="formBlock" style={{paddingBottom: '0px', position: 'relative'}}>
+								{this.state.boardsdata&&this.state.boardsdata.email_notify===true?this.props.isAdminOrTeacher()===true?<Tooltip placement="bottom" title={this.state.isemail_notifys===true?"邮件只能发送一次":""}><span className={"setemail fr mr70 setemailposition"}>
+										<Checkbox onChange={this.setemailchange} checked={this.state.isemail_notifys===true?this.state.isemail_notifys:this.state.isemail_notify} disabled={this.state.isemail_notifys}>发送邮件提醒</Checkbox>
+								</span></Tooltip>:"":""}
+
                 {  isAdmin && 
                     <React.Fragment>
                       {getFieldDecorator('sticky', {
                         valuePropName: 'checked',
                       })(
                         isAdmin && <Checkbox style={{ right: '22px',
-                          top: '28px',
+                          top: '17px',
                           position: 'absolute'
                         }}>置顶</Checkbox>
                       )}
@@ -411,9 +419,7 @@ class BoardsNew extends Component{
                     </Select>
                   )}
                 </Form.Item>
-								{this.state.boardsdata&&this.state.boardsdata.email_notify===true?this.props.isAdminOrTeacher()===true?this.isEdit ?"":<span className={"setemail"}>
-										<Checkbox onChange={this.setemailchange} checked={this.state.email_notify}>发送邮件提醒</Checkbox>
-								</span>:"":""}
+
 
 
                 {/* { isAdmin && <Form.Item
diff --git a/public/react/src/modules/courses/busyWork/CommonWorkSetting.js b/public/react/src/modules/courses/busyWork/CommonWorkSetting.js
index 1bc806190..1e3ced6d3 100644
--- a/public/react/src/modules/courses/busyWork/CommonWorkSetting.js
+++ b/public/react/src/modules/courses/busyWork/CommonWorkSetting.js
@@ -724,22 +724,28 @@ class CommonWorkSetting extends Component{
     let course_id=this.props.match.params.coursesId;
     const url = `/homework_commons/${workId}/update_settings.json`
     // comments
+    const temp_publish_time_date = new Date(temp_publish_time)
+    const temp_end_time_date = new Date(temp_end_time)
+    const late_time_date = new Date(late_time)
+    const evaluation_start_date = new Date(evaluation_start)
+    const evaluation_end_date = new Date(evaluation_end)
+    const appeal_time_date = new Date(appeal_time)
     axios.post(url,{
       course_id ,
       unified_setting: unified_setting,      // 统一设置
       group_settings: group_settings_param,
-      publish_time: temp_end_time ? new Date(temp_publish_time.replace(/-/g, '/')) : temp_end_time,      // 发布
-      end_time: temp_end_time ? new Date(temp_end_time.replace(/-/g, '/')) : temp_end_time,              // 截止
+      publish_time: temp_publish_time ? !isNaN(temp_publish_time_date.getTime()) ? temp_publish_time_date : new Date(temp_publish_time.replace(/-/g, '/')) : temp_publish_time,      // 发布
+      end_time: temp_end_time ? !isNaN(temp_end_time_date.getTime()) ? temp_end_time_date : new Date(temp_end_time.replace(/-/g, '/')) : temp_end_time,              // 截止
       late_penalty: late_penalty,           // 迟交扣分
       allow_late: allow_late,       // 是否允许补交
-      late_time: late_time ? new Date(late_time.replace(/-/g, '/')) : late_time,         // 补交截止时间
+      late_time: late_time ? !isNaN(late_time_date.getTime()) ? late_time_date : new Date(late_time.replace(/-/g, '/')) : late_time,         // 补交截止时间
       anonymous_comment: anonymous_comment,   // true: 启用匿评 false:未启用匿评
-      evaluation_start: evaluation_start ? new Date(evaluation_start.replace(/-/g, '/')) : evaluation_start, //匿评开始时间
-      evaluation_end: evaluation_end ? new Date(evaluation_end.replace(/-/g, '/')) : evaluation_end,
+      evaluation_start: evaluation_start ? !isNaN(evaluation_start_date.getTime()) ? evaluation_start_date : new Date(evaluation_start.replace(/-/g, '/')) : evaluation_start, //匿评开始时间
+      evaluation_end: evaluation_end ? !isNaN(evaluation_end_date.getTime()) ? evaluation_end_date : new Date(evaluation_end.replace(/-/g, '/')) : evaluation_end,
       evaluation_num: evaluation_num,     //	匿评数
       absence_penalty: absence_penalty,   // 匿评扣分
       anonymous_appeal: anonymous_appeal, // true: 启用匿评申诉, false:未启用
-      appeal_time: appeal_time ? new Date(appeal_time.replace(/-/g, '/')) : appeal_time,   // 申诉结束时间
+      appeal_time: appeal_time ? !isNaN(appeal_time_date.getTime()) ? appeal_time_date : new Date(appeal_time.replace(/-/g, '/')) : appeal_time,   // 申诉结束时间
       appeal_penalty: appeal_penalty, //	违规匿评扣分
       ta_mode: ta_mode,  // 1:普通模式 0:复审模式
       final_mode: final_mode,  // true: 单项评分优先, false: 多项评分配比
diff --git a/public/react/src/modules/courses/common/formCommon.css b/public/react/src/modules/courses/common/formCommon.css
index 5b30a338c..94ee5b099 100644
--- a/public/react/src/modules/courses/common/formCommon.css
+++ b/public/react/src/modules/courses/common/formCommon.css
@@ -49,4 +49,10 @@
   display: inline;
   margin-left: 10px;
 }
-/* errorInline ----------- */
\ No newline at end of file
+/* errorInline ----------- */
+
+.setemailposition{
+    position: absolute;
+    right: 40px;
+    top: 10px;
+}
\ No newline at end of file
diff --git a/public/react/src/modules/courses/coursesPublic/ModulationModal.js b/public/react/src/modules/courses/coursesPublic/ModulationModal.js
index 4a679b515..56ccee894 100644
--- a/public/react/src/modules/courses/coursesPublic/ModulationModal.js
+++ b/public/react/src/modules/courses/coursesPublic/ModulationModal.js
@@ -1,5 +1,5 @@
 import React,{ Component } from "react";
-import {Modal, Checkbox, Upload, Button, Icon, message, Input, Form} from "antd";
+import {Modal, Checkbox, Upload, Button, Icon, message, Input, Form, InputNumber} from "antd";
 import { WordNumberTextarea } from 'educoder';
 import './Newshixunmodel.css'
 
@@ -17,6 +17,7 @@ class ModulationModal extends Component{
 
 
   Saves=()=>{
+		console.log("Saves=()");
     let {textareaval,Inputsval}=this.state;
 		// if(textareaval===""||textareaval===undefined){
 		//   this.setState({
@@ -32,7 +33,7 @@ class ModulationModal extends Component{
 			this.setState({
 				Inputsval: "",
 				Inputsvaltype: true,
-				Inputsvaltest: "请输入分数",
+				Inputsvaltest: "请填写分数",
 			})
       return
     }
@@ -40,10 +41,51 @@ class ModulationModal extends Component{
 		if (this.state.Inputsvaltype === true) {
 			return;
 		}
+
+		if (Inputsval === undefined || Inputsval === null || Inputsval === "") {
+			this.setState({
+				borredszf: "ml10  color-grey-9 bor-reds ",
+				Inputsval: "",
+				Inputsvaltype: true,
+				Inputsvaltest: "成绩不能为空",
+			})
+			return
+		}
+		var re = /^[0-9]+.?[0-9]*$/; //判断字符串是否为数字 //判断正整数 /^[1-9]+[0-9]*]*$/
+		var nubmer = Inputsval;
+		if (!re.test(nubmer)) {
+			this.setState({
+				borredszf: "ml10  color-grey-9 bor-reds ",
+				Inputsval: Inputsval,
+				Inputsvaltype: true,
+				Inputsvaltest: "请输入0-100的分数",
+			})
+			return;
+		}
+		if (0 > parseFloat(Inputsval)) {
+			this.setState({
+				borredszf: "ml10  color-grey-9 bor-reds ",
+				Inputsval: Inputsval,
+				Inputsvaltype: true,
+				Inputsvaltest: "成绩不能小于零",
+			})
+			return;
+		} else if (parseFloat(Inputsval) > 100) {
+			this.setState({
+				borredszf: "ml10  color-grey-9 bor-reds ",
+				Inputsval: Inputsval,
+				Inputsvaltype: true,
+				Inputsvaltest: "成绩不能大于100",
+			})
+			return;
+		}
+
+
 		this.setState({
 			Inputsvaltype: false,
 			Inputsvaltest: "",
 		})
+		console.log(Inputsval);
      this.props.Saves(textareaval,Inputsval)
 
   }
@@ -55,31 +97,13 @@ class ModulationModal extends Component{
   }
 
   setInputs=(e)=>{
-		debugger
-    var value=parseInt(e.target.value)
+		console.log("setInputs");
+		console.log(e);
 
-    if(isNaN(value)){
-			value = 0;
-			this.setState({
-				Inputsval: value,
-				Inputsvaltype: true,
-				Inputsvaltest: "请输入分数",
-			})
-    }else{
-      if(value<0||value>100){
-				value = 0;
-				this.setState({
-					Inputsval: value,
-					Inputsvaltype: true,
-					Inputsvaltest: "请输入0-100的分数",
-				})
-
-      }
-    }
-    this.setState({
-			Inputsval: value,
+		this.setState({
+			Inputsval: e,
 			Inputsvaltype: false,
-    })
+		})
   }
   render(){
 		let {textareaval, Inputsval, textareavaltype, Inputsvaltype, Inputsvaltest} = this.state;
@@ -100,65 +124,90 @@ class ModulationModal extends Component{
 						alignItems: "center",
 					}}>
 						<div style={{
-							marginTop: " 27px",
 							display: "flex",
 							flexDirection: "initial",
-						}}>
 
+						}}>
+							<p className=" mt3 font-14 " style={{color: "#666666"}}>该学生的最终成绩将不会按照评分规则进行计算</p>
+						</div>
 
 
+						<div style={{
+							marginTop: " 27px",
+							display: "flex",
+							flexDirection: "initial",
+							width: "100%",
+						}}>
              <span style={{
-							 width: "70px",
 							 textAlign: "center",
-							 lineHeight: " 40px",
+							 lineHeight: "40px",
+							 marginLeft: "16px",
 						 }}><span style={{
 							 textAlign: "center",
 							 lineHeight: " 40px",
 							 color: " #f5222d",
-						 }}>*</span>调分:</span>
-							<Input
-								className={Inputsvaltype === true ? "borerinput" : ""}
+						 }}>*</span>成绩:</span>
+							<style>
+								{
+									`
+									.myinputnumbers .ant-input-number-input{
+									line-height: 40px;
+                   height: 35px;
+									}
+									
+									`
+								}
+							</style>
+							{Inputsvaltype === true ?
+								<style>
+									{
+										`
+										.ant-input:hover {
+												border: 1px solid #DD1717!important;
+								    }
+								    .ant-input:focus {
+								    border: 1px solid #DD1717!important;
+								    }
+								    }
+										`
+									}
+								</style>
+								:
+								""
+
+							} <InputNumber
+							className={Inputsvaltype === true ? "borerinput myinputnumbers  bor-reds" : "myinputnumbers"}
+							style={{
+														 width: "120px",
+														 height: "40px",
+													 }}
+							placeholder="请填写分数"
+							onChange={(e) => this.setInputs(e)}
+							value={Inputsval === undefined || Inputsval === null ? "" : Inputsval}/>
+							<span
 								style={{
-								width: "335px",
-								height: "40px",
-							}}
-								placeholder="请填写分数"
-								value={Inputsval}
-								onInput={this.setInputs}
-								suffix={
-											 <span
-												 style={{
-													 textAlign: "center",
-													 lineHeight: " 40px",
-												 }}
-											 >分</span>
-										 }
-							/>
+									textAlign: "center",
+									lineHeight: " 40px",
+									marginLeft: "10px",
+								}}
+							>分</span>
 						</div>
 						{
 							Inputsvaltype === true ?
-								<p style={{color: "#DD1717", width: "268px"}}>{Inputsvaltest}</p>
+								<p style={{color: "#DD1717", width: "77%", marginLeft: "1px", marginTop: "10px",}}>{Inputsvaltest}</p>
 								: ""
 						}
 
-						<div style={{
-							display: "flex",
-							flexDirection: "initial",
 
-						}}>
-							<span style={{width: "70px"}}></span>
-							<p className=" mt3 font-14 " style={{color: "#666666"}}>调分后该学生的最终成绩将不会按照评分规则进行计算</p>
-						</div>
 
 						<div style={{
 							display: "flex",
 							flexDirection: "initial",
 							marginTop: "10px;",
 						}}>
-							<span style={{width: "70px", marginTop: "24px"}}>调分原因:</span>
 							<WordNumberTextarea
-								style={{width: "335px"}}
-								placeholder={"请输入调分原因(选填)"}
+								style={{width: "100%"}}
+								placeholder={"请填写您对作品调分的原因(选填)"}
 								onInput={(e) => this.settextarea(e)}
 								value={textareaval}
 								maxlength={100}
@@ -166,16 +215,19 @@ class ModulationModal extends Component{
 						</div>
 
 						<div style={{
-							marginTop: "27px",
-							width: " 336px",
+							marginTop: "15px",
+							width: "82%",
 							marginLeft: "70px",
 							marginBottom: "29px",
+							display: "flex",
+							flexDirection: "row-reverse",
 						}}>
-							<a className="task-btn color-white mr30" style={{width: "72px",}}
+							<a className="task-btn task-btn-orange " style={{width: "72px", borderRadius: "5px"}}
+								 onClick={this.Saves}>{this.props.Savesname || '确认'}</a>
+							<a className="task-btn color-white mr30" style={{width: "72px", borderRadius: "5px"}}
 								 onClick={this.props.Cancel}>{this.props.Cancelname || '取消'}</a>
-							<a className="task-btn task-btn-orange" style={{width: "72px",}}
-								 onClick={this.Saves}>{this.props.Savesname || '保存'}</a>
-            </div>
+
+						</div>
 
           </div>
 
diff --git a/public/react/src/modules/courses/coursesPublic/ModulationModal_exercise.js b/public/react/src/modules/courses/coursesPublic/ModulationModal_exercise.js
new file mode 100644
index 000000000..538b32773
--- /dev/null
+++ b/public/react/src/modules/courses/coursesPublic/ModulationModal_exercise.js
@@ -0,0 +1,176 @@
+import React, {Component} from "react";
+import {Modal, Checkbox, Upload, Button, Icon, message, Input, Form} from "antd";
+import {WordNumberTextarea} from 'educoder';
+import './Newshixunmodel.css'
+
+//调分
+class ModulationModal_exercise extends Component {
+	constructor(props) {
+		super(props);
+		this.state = {
+			score: 0,
+			subjective_questions: 0,
+			objective_questions: 0,
+
+
+		}
+		//因为主观题加客观题的和是总分
+	}
+
+	componentDidMount = () => {
+
+
+	}
+
+	Saves = () => {
+		let {textareaval, subjective_questions, objective_questions, score} = this.state;
+
+		// this.props.Saves(textareaval, Inputsval)
+
+	}
+
+	settextarea = (e) => {
+		this.setState({
+			textareaval: e.target.value
+		})
+	}
+
+	setInputs = (e) => {
+
+
+	}
+
+	render() {
+		let {Inputsvaltype, subjective_questions, objective_questions, score} = this.state;
+		return (
+			<div>
+				<Modal
+					keyboard={false}
+					className={"HomeworkModal"}
+					title={this.props.modalname || '评阅'}
+					visible={this.props.visible}
+					closable={false}
+					footer={null}
+					destroyOnClose={true}
+				>
+					<div className="clearfix" style={{
+						display: "-webkit-flex",
+						flexDirection: "column",
+						alignItems: "center",
+					}}>
+
+						<div className="mexertwo">
+							<p className="mexeheigth2">主观题成绩:</p>
+							<Input
+								className={Inputsvaltype === true ? "borerinput myinputnumbers  bor-reds" : "myinputnumbers"}
+								style={{
+									width: "120px",
+									height: "40px",
+								}}
+								placeholder="请填写主观题成绩"
+								onChange={(e) => this.setInputs(e)}
+								value={subjective_questions === undefined || subjective_questions === null ? "" : subjective_questions}/>
+							<p className="mexeheigth">分 ,</p>
+							<p className="mexeheigth"><span>总分:</span><span>45.0 </span><span>分</span></p>
+						</div>
+
+						<div className="mexertwo">
+
+							<p className="mexeheigth2">客观题成绩:</p>
+							<Input
+								className={Inputsvaltype === true ? "borerinput myinputnumbers  bor-reds" : "myinputnumbers"}
+								style={{
+									width: "120px",
+									height: "40px",
+								}}
+								placeholder="请填写客观题成绩"
+								onChange={(e) => this.setInputs(e)}
+								value={objective_questions === undefined || objective_questions === null ? "" : objective_questions}/>
+							<p className="mexeheigth">分 ,</p>
+							<p className="mexeheigth"><span>总分:</span><span>45.0 </span><span>分</span></p>
+
+						</div>
+
+						<div className="mexertwo">
+							<p className="mexeheigth2">最终成绩:</p>
+							<Input
+								className={Inputsvaltype === true ? "borerinput myinputnumbers  bor-reds" : "myinputnumbers"}
+								style={{
+									width: "120px",
+									height: "40px",
+								}}
+								placeholder="请填写最终成绩"
+								onChange={(e) => this.setInputs(e)}
+								value={score === undefined || score === null ? "" : score}/>
+							<p className="mexeheigth"> 分 ,</p>
+							<p className="mexeheigth"><span>总分:</span><span>45.0 </span><span>分</span></p>
+
+						</div>
+
+
+						<div className="minbuttionte">
+							<a className="task-btn color-white mr30" style={{width: "72px",}}
+								 onClick={this.props.Cancel}>{this.props.Cancelname || '取消'}</a>
+							<a className="task-btn task-btn-orange" style={{width: "72px",}}
+								 onClick={this.Saves}>{this.props.Savesname || '保存'}</a>
+						</div>
+
+					</div>
+
+
+				</Modal>
+			</div>
+		)
+	}
+}
+
+export default ModulationModal_exercise;
+// <div className="task-popup-content">
+//   <p className="task-popup-text-center font-16 mb20">
+//
+//     <span className={"color-dark-21"}>该学生的最终成绩将不会按照评分规则进行计算</span>
+//
+//   </p>
+//
+//
+//   <div className="clearfix">
+//     {/*<textarea*/}
+//     {/*className="winput-100-150"*/}
+//     {/*placeholder="请填写您对作品调分的原因"*/}
+//     {/*value={textareaval}*/}
+//     {/*onInput={this.settextarea}*/}
+//     {/*></textarea>*/}
+//
+//     <WordNumberTextarea
+//       placeholder={"请填写您对作品调分的原因"}
+//       onInput={(e)=>this.settextarea(e)}
+//       value={textareaval}
+//       maxlength={100}
+//     />
+//
+//     {/*<li style={{height:"20px",lineHeight:"20px"}}><span className={textareavaltype===true?"color-red":"none"}>原因不能为空</span></li>*/}
+//     <div style={{height:"20px",lineHeight:"20px"}}></div>
+//   </div>
+//
+//   <style>
+//     {
+//
+//       `
+// 									.pdl10{
+// 									 padding-left:10px;
+// 									}
+// 									`
+//     }
+//   </style>
+//
+//   <li className={"pdl10"}>
+//
+//   </li>
+//   <li style={{height:"20px",lineHeight:"20px"}}><span className={Inputsvaltype===true?"color-red":"none"}>分数不能为空</span></li>
+//   <div className="clearfix edu-txt-center">
+//     <a  className="task-btn color-white mr30" onClick={this.props.Cancel}>{this.props.Cancelname || '取消'}</a>
+//     <a className="task-btn task-btn-orange" onClick={this.Saves}>{this.props.Savesname || '保存'}</a>
+{/*  </div>*/
+}
+{/*</div>*/
+}
diff --git a/public/react/src/modules/courses/coursesPublic/Newshixunmodel.css b/public/react/src/modules/courses/coursesPublic/Newshixunmodel.css
index e31766c3f..34721fa21 100644
--- a/public/react/src/modules/courses/coursesPublic/Newshixunmodel.css
+++ b/public/react/src/modules/courses/coursesPublic/Newshixunmodel.css
@@ -305,8 +305,27 @@
     margin: 10px 10px 0px 10px;
     padding: 10px 10px 5px 10px;
     backgroud: rgba(234, 234, 234, 1);
-    width: 335px;
+    width: 530px;
+    margin-left: 10px;
+    margin-top: 25px;
+    height: 214px !important;
+}
 
+.WordNumbernote .WordNumberTextarea {
+    outline: none;
+    appearance: none;
+    -webkit-appearance: none;
+    -moz-appearance: none;
+    background-color: white;
+    text-shadow: none;
+    -webkit-writing-mode: horizontal-tb !important;
+    -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+    resize: none;
+    border: none;
+    width: 100%;
+    height: 169px !important;
+    border: none;
+    display: block;
 }
 
 .WordNumberTextarea-count {
@@ -325,3 +344,30 @@
     border: 1px solid #eee !important;
 }
 
+
+.mexertwo {
+    display: flex;
+    flex-direction: initial;
+}
+
+.mexeheigth {
+    line-height: 40px;
+}
+
+.mexeheigth2 {
+    line-height: 40px;
+    width: 74px;
+}
+
+.minbuttionte {
+    /* display: flex; */
+    margin-top: 27px;
+    width: 100%;
+    /* align-items: center; */
+    margin-bottom: 17px;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    flex-direction: initial;
+}
diff --git a/public/react/src/modules/courses/exercise/ExerciseNewCommon.js b/public/react/src/modules/courses/exercise/ExerciseNewCommon.js
index e69c44974..6d7c72b13 100644
--- a/public/react/src/modules/courses/exercise/ExerciseNewCommon.js
+++ b/public/react/src/modules/courses/exercise/ExerciseNewCommon.js
@@ -120,9 +120,9 @@ class ExerciseNewCommon extends Component{
   componentDidMount = () => {
     this.fetchExercise()
   }
-  handleSubmit = (e) => {
-    
-  }
+  // handleSubmit = (e) => {
+  //
+  // }
   onSaveExercise = () => {
     const { exercise_name, exercise_description } = this.state;
     const exercise_id = this.props.match.params.Id
@@ -472,7 +472,9 @@ class ExerciseNewCommon extends Component{
               ></div>
               
             </div>}
-            {this.state.editMode && <Form {...formItemLayout} onSubmit={this.handleSubmit}>
+            {this.state.editMode && <Form {...formItemLayout}
+																					// onSubmit={this.handleSubmit}
+						>
               <div className="formBlock" style={{paddingBottom: '2px',borderBottom:"none"}}>
                 <Form.Item
                   label="试卷标题"
diff --git a/public/react/src/modules/courses/exercise/Studentshavecompletedthelist.js b/public/react/src/modules/courses/exercise/Studentshavecompletedthelist.js
index 90c76ee68..d6a99f5d1 100644
--- a/public/react/src/modules/courses/exercise/Studentshavecompletedthelist.js
+++ b/public/react/src/modules/courses/exercise/Studentshavecompletedthelist.js
@@ -24,6 +24,7 @@ import {getImageUrl, toPath, sortDirections} from 'educoder';
 import CheckBoxGroup from "../../page/component/CheckBoxGroup";
 import NoneData from '../../../modules/courses/coursesPublic/NoneData'
 import ModulationModal from "../coursesPublic/ModulationModal";
+import ModulationModal_exercise from "../coursesPublic/ModulationModal_exercise";
 const Search = Input.Search;
 const RadioGroup = Radio.Group;
 const CheckboxGroup = Checkbox.Group;
@@ -1030,10 +1031,11 @@ class Studentshavecompletedthelist extends Component {
 					render: (text, record) => (
 						<span>
 							{record.finalscore==="--"?
-								<span className="color-blue" style={{textAlign: "center", cursor: "pointer"}}
-											onClick={() => this.Adjustment(record)}
-								>调分</span>
-									:
+
+
+								<span style={{textAlign: "center", color: '#999999'}}
+								>--</span>
+								:
 									<a style={{textAlign: "center"}} className="color-blue"
 										 target="_blank"
 										 href={`/courses/${this.props.match.params.coursesId}/exercises/${this.props.match.params.Id}/users/${record.myid}`}>{record.finalscore}</a>
@@ -1227,9 +1229,8 @@ class Studentshavecompletedthelist extends Component {
 					render: (text, record) => (
 						<span>
 							{record.finalscore==="--"?
-								<span className="color-blue" style={{textAlign: "center", cursor: "pointer"}}
-											onClick={() => this.Adjustment(record)}
-								>调分</span>
+								<span style={{textAlign: "center", color: '#999999'}}
+								>--</span>
 									:
 									<a style={{textAlign: "center"}} className="color-blue"
 										 target="_blank"
@@ -1243,6 +1244,15 @@ class Studentshavecompletedthelist extends Component {
 			exercise_status:0,
 			order_type: "desc",
 			exeuserid: 0,
+			subjective: 0,
+			objective_score: 0,
+			subjective_score: 0,
+		}
+		{/*<a style={{textAlign: "center"}} className="color-blue"*/
+		}
+		{/*	 target="_blank"*/
+		}
+		{/*	 onClick={() => this.Adjustment(record.user_id)}>评阅</a>*/
 		}
 		// //console.log("Studentshavecompletedthelist");
 		// //console.log(props.current_status);
@@ -1799,6 +1809,9 @@ class Studentshavecompletedthelist extends Component {
 						course_groups: response.data.course_groups,
 						mylistansum:response.data.exercise_types.answer_users+response.data.exercise_types.unanswer_users,
 						loadingstate: false,
+						subjective: response.data.exercise_types.subjective,
+						objective_score: response.data.exercise_types.objective_score,
+						subjective_score: response.data.exercise_types.subjective_score,
 						columnsys: arr,
 					})
 				} else {
@@ -1831,6 +1844,9 @@ class Studentshavecompletedthelist extends Component {
 						exercise_users: response.data.exercise_users,
 						course_groups: response.data.course_groups,
 						mylistansum:response.data.exercise_types.answer_users+response.data.exercise_types.unanswer_users,
+						subjective: response.data.exercise_types.subjective,
+						objective_score: response.data.exercise_types.objective_score,
+						subjective_score: response.data.exercise_types.subjective_score,
 						columnsys: arr,
 					})
 				}
@@ -1868,6 +1884,9 @@ class Studentshavecompletedthelist extends Component {
 						course_groups: response.data.course_groups,
 						mylistansum:response.data.exercise_types.answer_users+response.data.exercise_types.unanswer_users,
 						loadingstate: false,
+						subjective: response.data.exercise_types.subjective,
+						objective_score: response.data.exercise_types.objective_score,
+						subjective_score: response.data.exercise_types.subjective_score,
 						columnsys:	arr,
 					})
 				} else {
@@ -1900,6 +1919,9 @@ class Studentshavecompletedthelist extends Component {
 						course_groups: response.data.course_groups,
 						mylistansum:response.data.exercise_types.answer_users+response.data.exercise_types.unanswer_users,
 						loadingstate: false,
+						subjective: response.data.exercise_types.subjective,
+						objective_score: response.data.exercise_types.objective_score,
+						subjective_score: response.data.exercise_types.subjective_score,
 						columnsys: arr,
 					})
 				}
@@ -1932,6 +1954,9 @@ class Studentshavecompletedthelist extends Component {
 						course_groups: response.data.course_groups,
 						mylistansum:response.data.exercise_types.answer_users+response.data.exercise_types.unanswer_users,
 						columnsys: arr,
+						subjective: response.data.exercise_types.subjective,
+						objective_score: response.data.exercise_types.objective_score,
+						subjective_score: response.data.exercise_types.subjective_score,
 					})
 				} else {
 					var arr =[];
@@ -1957,6 +1982,9 @@ class Studentshavecompletedthelist extends Component {
 						course_groups: response.data.course_groups,
 						mylistansum:response.data.exercise_types.answer_users+response.data.exercise_types.unanswer_users,
 						columnsys:arr,
+						subjective: response.data.exercise_types.subjective,
+						objective_score: response.data.exercise_types.objective_score,
+						subjective_score: response.data.exercise_types.subjective_score,
 					})
 				}
 			} else {
@@ -1973,6 +2001,9 @@ class Studentshavecompletedthelist extends Component {
 						mylistansum:response.data.exercise_types.answer_users+response.data.exercise_types.unanswer_users,
 						loadingstate: false,
 						columnsys: this.state.columnsystwo,
+						subjective: response.data.exercise_types.subjective,
+						objective_score: response.data.exercise_types.objective_score,
+						subjective_score: response.data.exercise_types.subjective_score,
 					})
 				} else {
 					var arr =[];
@@ -1999,6 +2030,9 @@ class Studentshavecompletedthelist extends Component {
 						mylistansum:response.data.exercise_types.answer_users+response.data.exercise_types.unanswer_users,
 						loadingstate: false,
 						columnsys: arr,
+						subjective: response.data.exercise_types.subjective,
+						objective_score: response.data.exercise_types.objective_score,
+						subjective_score: response.data.exercise_types.subjective_score,
 					})
 				}
 
@@ -2586,9 +2620,9 @@ class Studentshavecompletedthelist extends Component {
 		var exercise_id = this.props.match.params.Id;
 		let url = `/exercises/${exercise_id}/adjust_score.json`;
 		axios.post(url, {
-				score: n,
 				user_id: this.state.exeuserid,
-				comment: v,
+				subjective_score: n,
+				objective_score: v,
 			})
 			.then((response) => {
 				if (response.data.status == '0') {
@@ -2639,6 +2673,13 @@ class Studentshavecompletedthelist extends Component {
 										Cancel={() => this.Adjustments()}
 										Saves={(value, num) => this.Testpapergrading(value, num)}
 									/> : ""}
+									{/*{*/}
+									{/*	testpapergradingboll === true ? <ModulationModal_exercise*/}
+									{/*		visible={testpapergradingboll}*/}
+									{/*		Cancel={() => this.Adjustments()}*/}
+									{/*		Saves={(value, num) => this.Testpapergrading(value, num)}*/}
+									{/*	/> : ""*/}
+									{/*}*/}
 									<div className="edu-back-white" >
 										<ul className="clearfix" style={{padding: '10px 30px 10px 30px'}}>
 
diff --git a/public/react/src/modules/courses/exercise/Testpapersettinghomepage.js b/public/react/src/modules/courses/exercise/Testpapersettinghomepage.js
index b670be86e..1398e18c1 100644
--- a/public/react/src/modules/courses/exercise/Testpapersettinghomepage.js
+++ b/public/react/src/modules/courses/exercise/Testpapersettinghomepage.js
@@ -292,7 +292,7 @@ class Testpapersettinghomepage extends Component{
         const isStudent = this.props.isStudent();
             // TODO
 
-			// console.log(Commonheadofthetestpaper.exercise_status);
+	  //console.log(Commonheadofthetestpaper.exercise_status);
 			document.title=this.props.coursedata&&this.props.coursedata.name;
         return(
             <div className="newMain clearfix ">
@@ -411,7 +411,7 @@ class Testpapersettinghomepage extends Component{
                                 </div>
 
                                 {
-                                    isAdmin  === true &&Commonheadofthetestpaper && Commonheadofthetestpaper.user_permission.user_commit_counts>0?
+                                    isAdmin  === true &&Commonheadofthetestpaper && Commonheadofthetestpaper.user_permission.user_commit_counts>0&&Commonheadofthetestpaper.exercise_status===2?
                                       <a className="fr color-blue font-16 mt20 mr20" onClick={this.Ecerciseacallagain}>打回重做</a>:""
                                 }
 
@@ -443,7 +443,7 @@ class Testpapersettinghomepage extends Component{
 																		<a  className="fr color-blue font-16 mt20" onClick={()=>this.setgameexercise(`/courses/${this.props.match.params.coursesId}/exercises/${this.props.match.params.Id}/users/${this.props.current_user.login}`)}>开始答题</a>:
                                   <Link className="fr color-blue font-16 mt20"
                                         to={`/courses/${this.props.match.params.coursesId}/exercises/${this.props.match.params.Id}/users/${this.props.current_user.login}`}>
-																		{exercise_status===2?"":exercise_status===3?"":exercise_status===4?"":start_Value[exercise_status]}
+																		{exercise_status===2?start_Value[Commonheadofthetestpaper&&Commonheadofthetestpaper.user_permission.current_status]:exercise_status===3?"":exercise_status===4?"":start_Value[Commonheadofthetestpaper&&Commonheadofthetestpaper.user_permission.current_status]}
 																	</Link>
                                   :""}
 
diff --git a/public/react/src/modules/courses/gradinforms/Bullsubdirectory.js b/public/react/src/modules/courses/gradinforms/Bullsubdirectory.js
index db4f89bfc..729308852 100644
--- a/public/react/src/modules/courses/gradinforms/Bullsubdirectory.js
+++ b/public/react/src/modules/courses/gradinforms/Bullsubdirectory.js
@@ -211,6 +211,59 @@ class Bullsubdirectory extends Component{
 		});
 	}
 
+	//上移
+	Moveupward = (id) => {
+		let url = `/courses/${this.props.match.params.coursesId}/inform_up.json`;
+		axios.post(url, {
+			inform_id: id
+		}).then((response) => {
+			if (response) {
+				if (response.data) {
+					if (response.data.status === 0) {
+						this.props.showNotification(`上移成功`);
+						this.props.getinputdata();
+					} else {
+						this.props.showNotification(`上移失败`);
+					}
+				} else {
+					this.props.showNotification(`上移失败`);
+				}
+			} else {
+				this.props.showNotification(`上移失败`);
+			}
+		}).catch((error) => {
+			console.log(error)
+		});
+	}
+	//下移
+	Movedown = (id) => {
+		let url = `/courses/${this.props.match.params.coursesId}/inform_down.json`;
+		axios.post(url, {
+			inform_id: id
+		}).then((response) => {
+			if (response) {
+				if (response.data) {
+					if (response.data.status === 0) {
+						this.props.showNotification(`下移成功`);
+						this.props.getinputdata();
+					} else {
+						this.props.showNotification(`下移失败`);
+					}
+				} else {
+					this.props.showNotification(`下移失败`);
+				}
+			} else {
+				this.props.showNotification(`下移失败`);
+			}
+		}).catch((error) => {
+			console.log(error)
+		});
+	}
+
+
+
+
+
 
 	render(){
 		let{description,whethertoeditysl,addonAfter,eduintits,informs,isSpinysl} =this.state;
@@ -218,7 +271,7 @@ class Bullsubdirectory extends Component{
 		const {getFieldDecorator} = this.props.form;
 		// console.log("Bullsubdirectory");
 		// console.log(this.props.isAdmin());
-		// console.log(this.props.yslbool);
+    //  console.log(this.props);
 		return(
 			<React.Fragment  >
 				<div >
@@ -269,12 +322,48 @@ class Bullsubdirectory extends Component{
 																			:""
 																	}
 																</span>
+																	{
+																		this.props.length - 1 === this.props.index ? "" :
+																			this.props.isAdmin() === true ?
+																				(this.props.yslbool === false ?
+																						<a className="fr yslbianji mr30"
+																							 style={{
+																								 lineHeight: "31px",
+																							 }}
+																							 onClick={() => this.Movedown(this.props.id)}
+																						><Tooltip
+																							title="下移"><i
+																							style={{color: "#4CACFF"}}
+																							className=" font-18 iconfont icon-xiangxiayi"></i></Tooltip></a>
+																						:
+																						""
+																				)
+																				: ""
+																	}
+																	{
+																		this.props.index === 0 ? "" :
+																			this.props.isAdmin() === true ?
+																				(this.props.yslbool === false ?
+																						<a className="fr yslbianji mr30"
+																							 style={{
+																								 lineHeight: "31px",
+																							 }}
+																							 onClick={() => this.Moveupward(this.props.id)}
+																						><Tooltip
+																							title="上移"><i
+																							style={{color: "#4CACFF"}}
+																							className=" font-18 iconfont icon-xiangshangyi"></i></Tooltip></a>
+																						:
+																						""
+																				)
+																				: ""
+																	}
 																</div>
 																<div className="yslclear"></div>
 															</div>
 															<div id="MakedownHTML"className="markdown-body fonttext yslmtopcg yslminHeigth markdownysltext"   dangerouslySetInnerHTML={{__html: markdownToHTML(mydescription).replace(/▁/g, "▁▁▁")}}/>
 														</div>
-														<div className="bor-bottom-greyE mr25 ml25"></div>
+														{parseInt(this.props&&this.props.informs.length)===parseInt(this.props&&this.props.index+1)?"":<div className="bor-bottom-greyE mr25 ml25"></div>}
 													</div>
 													:
 													<div  className="edu-back-white ">
diff --git a/public/react/src/modules/courses/gradinforms/Eduinforms.js b/public/react/src/modules/courses/gradinforms/Eduinforms.js
index 38190afaf..4cb42eff9 100644
--- a/public/react/src/modules/courses/gradinforms/Eduinforms.js
+++ b/public/react/src/modules/courses/gradinforms/Eduinforms.js
@@ -491,9 +491,12 @@ class Eduinforms extends Component{
 														:
 
 														<div  className="edu-back-white ">
+															{/*公告栏底部*/}
 															{ informs&&informs.map((item, index) => {
 																return (
-																	<Bullsubdirectory {...this.state} {...this.props}  key={index} yslbool={yslbool} id={item.id} myname={item.name} mydescription={item.description}
+																	<Bullsubdirectory {...this.state} {...this.props} key={index} index={index}
+																										length={informs.length} yslbool={yslbool} id={item.id}
+																										myname={item.name} mydescription={item.description}
 																										getyslbooltrue={()=>this.getyslbooltrue()}
 																										getyslboolfalse={()=>this.getyslboolfalse()}
 																										getinputdata={()=>this.getinputdata()} ></Bullsubdirectory>
@@ -524,4 +527,4 @@ export default Eduinformss;
 {/*<div key={index} className="bor-bottom-greyE" >*/}
 {/*	{item.name===""?"":item.name===undefined?"":item.name===null?"":<div className="ysltitbt"><span >{item.name}</span></div>}*/}
 {/*	<div id="MakedownHTML" key={index} className={"markdown-body fonttext yslmtopcg yslminHeigth markdownysltext"}   dangerouslySetInnerHTML={{__html: markdownToHTML(item.description).replace(/▁/g, "▁▁▁")}}/>*/}
-{/*</div>*/}
\ No newline at end of file
+{/*</div>*/}
diff --git a/public/react/src/modules/courses/poll/PollDetailTabSecond.js b/public/react/src/modules/courses/poll/PollDetailTabSecond.js
index f52e77dd8..c4a743ac6 100644
--- a/public/react/src/modules/courses/poll/PollDetailTabSecond.js
+++ b/public/react/src/modules/courses/poll/PollDetailTabSecond.js
@@ -1,5 +1,5 @@
 import React,{ Component } from "react";
-import { Pagination} from "antd";
+import { Pagination , Spin } from "antd";
 import NoneData from "../coursesPublic/NoneData"
 
 import '../css/members.css'
@@ -16,10 +16,14 @@ class PollDetailTabSecond extends Component{
       page:1,
       limit:10,
       questions:undefined,
-      questionsInfo:undefined
+      questionsInfo:undefined,
+      isSpin:false
     }
   }
   getInfo=(page)=>{
+    this.setState({
+      isSpin:true
+    })
     let pollId=this.props.match.params.pollId;
     let url=`/polls/${pollId}/commit_result.json?page=${page}`;
     axios.get(url).then((result)=>{
@@ -27,7 +31,8 @@ class PollDetailTabSecond extends Component{
         this.setState({
 					page: page,
           questions:result.data.questions,
-          questionsInfo:result.data.question_types
+          questionsInfo:result.data.question_types,
+          isSpin:false
         })
       }
     }).catch((error)=>{
@@ -56,11 +61,12 @@ class PollDetailTabSecond extends Component{
   }
 
   render(){
-		let {page, limit, questions, questionsInfo} = this.state;
+		let {page, limit, questions, questionsInfo , isSpin} = this.state;
     return(
+      <Spin size="large" spinning={ isSpin }>
       <div>
         {
-          questions && questions.length>0?questions.map((item,key)=>{
+          questions && questions.length>0 && questions.map((item,key)=>{
             return(
             <div className="edu-back-white mb10">
               <div className="pt20 pl30 pr30 pb10">
@@ -90,8 +96,9 @@ class PollDetailTabSecond extends Component{
                 <p className="countHeader">
                   {
                     item.question.question_type==3?
-                    <ul className="clearfix">
-                      <span>文本答案</span>  
+                    <ul className="clearfix df">
+                      <span style={{width:"6%"}}>编号</span>
+                      <span style={{width:"50%",textAlign:"left"}}>文本答案</span>    
                     </ul>
                     :
                     <ul className="clearfix">
@@ -105,19 +112,22 @@ class PollDetailTabSecond extends Component{
                 {/* 主观题 */}
                 {
                   item.question.question_type == 3 && item.question.vote_text &&
-                  <div className="countBody">  
-                    <ul className="clearfix">
-                      <span className="color-grey-3 break-word" style={{width:"100%"}}>
-                        {
-                          item.question.vote_text.map((txt,t)=>{
-                            return(
-                              <li>{t+1}.{txt}</li>
-                            )
-                          })
-                        }
-                      </span>
-                    </ul>
-                  </div>
+                  <React.Fragment>
+                    {
+                      item.question.vote_text.map((txt,t)=>{
+                        return(
+                          <div className="countBody">  
+                            <ul className="clearfix df">
+                              <span style={{width:"6%"}} className="pl8">{t+1}</span>
+                              <span className="color-grey-3 break-word edu-txt-left flex1">
+                                <li>{txt}</li>
+                              </span>
+                            </ul>
+                          </div>
+                        )
+                      })
+                    }
+                  </React.Fragment>
                 }
                 {/* 单选和多选 */}
                 {
@@ -166,7 +176,10 @@ class PollDetailTabSecond extends Component{
               </div>
             </div> 
             )
-          }):<NoneData></NoneData>
+          })
+        }
+        {
+          questions && questions.length == 0 && <NoneData></NoneData>
         }
         {
 					questionsInfo && questionsInfo.q_counts > limit &&
@@ -177,6 +190,7 @@ class PollDetailTabSecond extends Component{
         }
         
       </div>
+      </Spin>
     )
   }
 }
diff --git a/public/react/src/modules/courses/poll/PollDetailTabThird.js b/public/react/src/modules/courses/poll/PollDetailTabThird.js
index 32d35818d..1e03adb5a 100644
--- a/public/react/src/modules/courses/poll/PollDetailTabThird.js
+++ b/public/react/src/modules/courses/poll/PollDetailTabThird.js
@@ -1,5 +1,5 @@
 import React,{ Component } from "react";
-
+import { Spin } from 'antd';
 import '../css/members.css'
 import '../css/busyWork.css'
 import './pollStyle.css'
@@ -12,10 +12,14 @@ class PollDetailTabThird extends Component{
   constructor(props){
     super(props);
     this.state={
-      pollDetail:undefined
+      pollDetail:undefined,
+      isSpin:false
     }
   }
   getPollInfo=()=>{
+    this.setState({
+      isSpin:true
+    })
     let pollId=this.props.match.params.pollId;
     let url=`/polls/${pollId}.json`;
     axios.get(url).then((result)=>{
@@ -23,12 +27,14 @@ class PollDetailTabThird extends Component{
 				if (result.data.status === 401) {
 					//未登入
 					this.setState({
-						pollDetail: undefined
+            pollDetail: undefined,
+            isSpin:false
 					})
 					return
 				}
         this.setState({
-          pollDetail:result.data
+          pollDetail:result.data,
+          isSpin:false
         })
       }
     }).catch((error)=>{
@@ -39,10 +45,12 @@ class PollDetailTabThird extends Component{
     this.getPollInfo();
   }
   render(){
-    let {pollDetail}=this.state;
+    let {pollDetail , isSpin}=this.state;
     return(
       <div>
-        <PollDetailTabThirdInfo {...this.props} {...this.state} pollDetail = {pollDetail}></PollDetailTabThirdInfo>
+        <Spin size="large" spinning={isSpin}>
+          <PollDetailTabThirdInfo {...this.props} {...this.state} pollDetail = {pollDetail}></PollDetailTabThirdInfo>
+        </Spin>
       </div>
     )
   }
diff --git a/public/react/src/modules/courses/shixunHomework/Listofworksstudentone.js b/public/react/src/modules/courses/shixunHomework/Listofworksstudentone.js
index ad3d28167..e43be32ea 100644
--- a/public/react/src/modules/courses/shixunHomework/Listofworksstudentone.js
+++ b/public/react/src/modules/courses/shixunHomework/Listofworksstudentone.js
@@ -43,6 +43,7 @@ const {Option} = Select;
 //GraduationTaskssetting.js
 
 //作品列表(学生)
+let allow_lates=false;
 class Listofworksstudentone extends Component {
 	//unifiedsetting 统一设置
 	//allowreplenishment 允许补交
@@ -288,7 +289,11 @@ class Listofworksstudentone extends Component {
 					),
 				},
 				{
-					title: '实战耗时',
+					title:<span>实训总耗时<Tooltip placement="top" title={<pre>
+						计算规则:<br/>
+						学员离开实训学习界面停止计时;<br/>
+						评测首次通过之后,停止计时<br/>
+					</pre>}><img src={getImageUrl("images/educoder/problem.png")}/></Tooltip></span>,
 					dataIndex: 'cost_time',
 					key: 'cost_time',
 					align: 'center',
@@ -383,7 +388,19 @@ class Listofworksstudentone extends Component {
 					)
 				},
 				{
-					title: '效率分',
+					title: <span>效率分<Tooltip placement="top" title={allow_lates===true?<pre>
+							补交结束时,系统根据学生在课堂成员中的<br/>
+						  效率表现自动评分。计算规则:<br/>
+							学生工作效率= log(实训总得分/实训总耗时)<br/>
+							学生效率分 = 学生工作效率 / 课堂学生最高<br/>
+						  工作效率 * 分值<br/>
+					</pre>:<pre>
+					作业截止时,系统根据学生在课堂成员中的<br/>
+					效率表现自动评分。计算规则:<br/>
+					学生工作效率= log(实训总得分/实训总耗时)<br/>
+					学生效率分 = 学生工作效率 / 课堂学生最高<br/>
+					工作效率 * 分值<br/>
+					</pre>}><img src={getImageUrl("images/educoder/problem.png")}/></Tooltip></span>,
 					dataIndex: 'efficiencyscore',
 					key: 'efficiencyscore',
 					align: 'center',
@@ -645,7 +662,11 @@ class Listofworksstudentone extends Component {
 					),
 				},
 				{
-					title: '实战耗时',
+					title:<span>实训总耗时<Tooltip placement="top" title={<pre>
+						计算规则:<br/>
+						学员离开实训学习界面停止计时;<br/>
+						评测首次通过之后,停止计时<br/>
+					</pre>}><img src={getImageUrl("images/educoder/problem.png")}/></Tooltip></span>,
 					dataIndex: 'cost_time',
 					key: 'cost_time',
 					align: 'center',
@@ -736,7 +757,19 @@ class Listofworksstudentone extends Component {
 					)
 				},
 				{
-					title: '效率分',
+					title:  <span>效率分<Tooltip placement="top" title={allow_lates===true?<pre>
+							补交结束时,系统根据学生在课堂成员中的<br/>
+						  效率表现自动评分。计算规则:<br/>
+							学生工作效率= log(实训总得分/实训总耗时)<br/>
+							学生效率分 = 学生工作效率 / 课堂学生最高<br/>
+						  工作效率 * 分值<br/>
+					</pre>:<pre>
+					作业截止时,系统根据学生在课堂成员中的<br/>
+					效率表现自动评分。计算规则:<br/>
+					学生工作效率= log(实训总得分/实训总耗时)<br/>
+					学生效率分 = 学生工作效率 / 课堂学生最高<br/>
+					工作效率 * 分值<br/>
+					</pre>}><img src={getImageUrl("images/educoder/problem.png")}/></Tooltip></span>,
 					dataIndex: 'efficiencyscore',
 					key: 'efficiencyscore',
 					align: 'center',
@@ -953,7 +986,11 @@ class Listofworksstudentone extends Component {
 
 				},
 				{
-					title: '实战耗时',
+					title:<span>实训总耗时<Tooltip placement="top" title={<pre>
+						计算规则:<br/>
+						学员离开实训学习界面停止计时;<br/>
+						评测首次通过之后,停止计时<br/>
+					</pre>}><img src={getImageUrl("images/educoder/problem.png")}/></Tooltip></span>,
 					dataIndex: 'cost_time',
 					key: 'cost_time',
 					align: 'center',
@@ -1007,18 +1044,11 @@ class Listofworksstudentone extends Component {
 					className: 'font-14',
 					render: (text, record) => (
 						<span>
-
-										<Tooltip placement="bottom" title={<div>
-											<div>已通过{record.completion}关,共{this.state.challenges_count}关</div>
-											<div>完成任务评测之前查看了参考答案:{record.view_answer_count}关</div>
-										</div>}>
 											<span style={{
 												color: '#07111B',
 												"text-align": "center"
 											}}>{record.completion + "/" + this.state.challenges_count} </span>
-										</Tooltip>
-
-        </span>
+       			 </span>
 					)
 				},
 				{
@@ -1043,7 +1073,19 @@ class Listofworksstudentone extends Component {
 					)
 				},
 				{
-					title: '效率分',
+					title:  <span>效率分<Tooltip placement="top" title={allow_lates===true?<pre>
+							补交结束时,系统根据学生在课堂成员中的<br/>
+						  效率表现自动评分。计算规则:<br/>
+							学生工作效率= log(实训总得分/实训总耗时)<br/>
+							学生效率分 = 学生工作效率 / 课堂学生最高<br/>
+						  工作效率 * 分值<br/>
+					</pre>:<pre>
+					作业截止时,系统根据学生在课堂成员中的<br/>
+					效率表现自动评分。计算规则:<br/>
+					学生工作效率= log(实训总得分/实训总耗时)<br/>
+					学生效率分 = 学生工作效率 / 课堂学生最高<br/>
+					工作效率 * 分值<br/>
+					</pre>}><img src={getImageUrl("images/educoder/problem.png")}/></Tooltip></span>,
 					dataIndex: 'efficiencyscore',
 					key: 'efficiencyscore',
 					align: 'center',
@@ -1053,22 +1095,14 @@ class Listofworksstudentone extends Component {
             {
 							record.efficiencyscore && record.efficiencyscore === "--" ? (
 									this.state.allow_late && this.state.allow_late === false ?
-										<Tooltip placement="bottom" title={<div>
-											<div>作业截止时,系统根据学生在课堂成员中的效率表现自动评分</div>
-										</div>}>
 											<span style={{color: "#9A9A9A"}}>
 												--
 											</span>
-										</Tooltip>
 										:
 										this.state.allow_late && this.state.allow_late === true ?
-											<Tooltip placement="bottom" title={<div>
-												<div>补交结束时,系统根据学生在课堂成员中的效率表现自动评分</div>
-											</div>}>
 												<span style={{color: "#9A9A9A"}}>
 													--
 												</span>
-											</Tooltip>
 											:
 											<span style={{color: "#9A9A9A"}}>
 												--
@@ -1104,7 +1138,7 @@ class Listofworksstudentone extends Component {
 									{
 										record.ultimate_score === true ?
 											<Tooltip placement="bottom" title={<div>
-												<div>{record.user_name}{record.user_login}</div>
+												{/*<div>{record.user_name}{record.user_login}</div>*/}
 												<div>{record.finalscore === "--" ? <span>最终调整成绩:0分</span> :
 													<span>最终调整成绩:{record.finalscore}分</span>}</div>
 											</div>}>
@@ -1130,16 +1164,21 @@ class Listofworksstudentone extends Component {
 											</Tooltip>
 											:
 											<Tooltip placement="bottom" title={<div>
-												<div>{record.user_name}{record.user_login}</div>
-												<div>完成任务评测之前查看了参考答案:{record.view_answer_count}关</div>
+												{/*<div>{record.user_name}{record.user_login}</div>*/}
+
 												<div>{record.levelscore === "--" ? <span>关卡得分:0分</span> :
 													<span>关卡得分:{record.levelscore}分</span>}</div>
+
 												<div>{record.efficiencyscore === "--" ? <span>效率评分:0分</span> :
 													<span>效率评分:{record.efficiencyscore}分</span>}</div>
+
 												<div>{record.late_penalty === "--" ? <span>迟交扣分:0分</span> :
 													<span>迟交扣分:{record.late_penalty}分</span>}</div>
-												<div>{record.finalscore === "--" ? <span>当前成绩:0分</span> :
-													<span>当前成绩:{record.finalscore}分</span>}</div>
+
+												{record.view_answer_count===null?"":<div>查看参考答案:{record.view_answer_count}关</div>}
+
+												<div>{record.finalscore === "--" ? <span>最终成绩:0分</span> :
+													<span>最终成绩:{record.finalscore}分</span>}</div>
 											</div>}>
 												{
 													record.finalscore && record.finalscore === "--" ?
@@ -1177,11 +1216,11 @@ class Listofworksstudentone extends Component {
 						record.submitstate === "未提交" ?
 							<a style={{textAlign: "center"}} className="color-blue"
 								 onMouseDown={(e) => this.Viewstudenttraininginformationtysl2(e, record)}
-								 onClick={() => this.Viewstudenttraininginformationt(record)}>查看</a> :
+								 onClick={() => this.Viewstudenttraininginformationt(record)}>评阅</a> :
 							<span>
                        <a style={{textAlign: "center"}} className="color-blue"
 													onMouseDown={(e) => this.Viewstudenttraininginformationtysl2(e, record)}
-													onClick={() => this.Viewstudenttraininginformationt(record)}>查看</a>
+													onClick={() => this.Viewstudenttraininginformationt(record)}>评阅</a>
         </span>
 					)
 				},
@@ -1304,7 +1343,11 @@ class Listofworksstudentone extends Component {
 				// 	),
 				// },
 				{
-					title: '实战耗时',
+					title:<span>实训总耗时<Tooltip placement="top" title={<pre>
+						计算规则:<br/>
+						学员离开实训学习界面停止计时;<br/>
+						评测首次通过之后,停止计时<br/>
+					</pre>}><img src={getImageUrl("images/educoder/problem.png")}/></Tooltip></span>,
 					dataIndex: 'cost_time',
 					key: 'cost_time',
 					align: 'center',
@@ -1341,18 +1384,11 @@ class Listofworksstudentone extends Component {
 					className: 'font-14',
 					render: (text, record) => (
 						<span>
-
-										<Tooltip placement="bottom" title={<div>
-											<div>已通过{record.completion}关,共{this.state.challenges_count}关</div>
-											<div>完成任务评测之前查看了参考答案:{record.view_answer_count}关</div>
-										</div>}>
-											<span style={{
-												color: '#07111B',
-												textAlign: "center"
-											}}>{record.completion + "/" + this.state.challenges_count} </span>
-										</Tooltip>
-
-        </span>
+									<span style={{
+										color: '#07111B',
+										textAlign: "center"
+									}}>{record.completion + "/" + this.state.challenges_count} </span>
+        		</span>
 					)
 				},
 				{
@@ -1377,7 +1413,19 @@ class Listofworksstudentone extends Component {
 					)
 				},
 				{
-					title: '效率分',
+					title:  <span>效率分<Tooltip placement="top" title={allow_lates===true?<pre>
+							补交结束时,系统根据学生在课堂成员中的<br/>
+						  效率表现自动评分。计算规则:<br/>
+							学生工作效率= log(实训总得分/实训总耗时)<br/>
+							学生效率分 = 学生工作效率 / 课堂学生最高<br/>
+						  工作效率 * 分值<br/>
+					</pre>:<pre>
+					作业截止时,系统根据学生在课堂成员中的<br/>
+					效率表现自动评分。计算规则:<br/>
+					学生工作效率= log(实训总得分/实训总耗时)<br/>
+					学生效率分 = 学生工作效率 / 课堂学生最高<br/>
+					工作效率 * 分值<br/>
+					</pre>}><img src={getImageUrl("images/educoder/problem.png")}/></Tooltip></span>,
 					dataIndex: 'efficiencyscore',
 					key: 'efficiencyscore',
 					align: 'center',
@@ -1387,22 +1435,14 @@ class Listofworksstudentone extends Component {
             {
 							record.efficiencyscore && record.efficiencyscore === "--" ? (
 									this.state.allow_late && this.state.allow_late === false ?
-										<Tooltip placement="bottom" title={<div>
-											<div>作业截止时,系统根据学生在课堂成员中的效率表现自动评分</div>
-										</div>}>
 											<span style={{color: "#9A9A9A"}}>
 												--
 											</span>
-										</Tooltip>
 										:
 										this.state.allow_late && this.state.allow_late === true ?
-											<Tooltip placement="bottom" title={<div>
-												<div>补交结束时,系统根据学生在课堂成员中的效率表现自动评分</div>
-											</div>}>
 												<span style={{color: "#9A9A9A"}}>
 													--
 												</span>
-											</Tooltip>
 											:
 											<span style={{color: "#9A9A9A"}}>
 												--
@@ -1438,7 +1478,7 @@ class Listofworksstudentone extends Component {
 									{
 										record.ultimate_score === true ?
 											<Tooltip placement="bottom" title={<div>
-												<div>{record.user_name}{record.user_login}</div>
+												{/*<div>{record.user_name}{record.user_login}</div>*/}
 												<div>{record.finalscore === "--" ? <span>最终调整成绩:0分</span> :
 													<span>最终调整成绩:{record.finalscore}分</span>}</div>
 											</div>}>
@@ -1464,16 +1504,21 @@ class Listofworksstudentone extends Component {
 											</Tooltip>
 											:
 											<Tooltip placement="bottom" title={<div>
-												<div>{record.user_name}{record.user_login}</div>
-												<div>完成任务评测之前查看了参考答案:{record.view_answer_count}关</div>
+												{/*<div>{record.user_name}{record.user_login}</div>*/}
+
 												<div>{record.levelscore === "--" ? <span>关卡得分:0分</span> :
 													<span>关卡得分:{record.levelscore}分</span>}</div>
+
 												<div>{record.efficiencyscore === "--" ? <span>效率评分:0分</span> :
 													<span>效率评分:{record.efficiencyscore}分</span>}</div>
+
 												<div>{record.late_penalty === "--" ? <span>迟交扣分:0分</span> :
 													<span>迟交扣分:{record.late_penalty}分</span>}</div>
-												<div>{record.finalscore === "--" ? <span>当前成绩:0分</span> :
-													<span>当前成绩:{record.finalscore}分</span>}</div>
+
+												{record.view_answer_count===null?"":<div>查看参考答案:{record.view_answer_count}关</div>}
+
+												<div>{record.finalscore === "--" ? <span>最终成绩:0分</span> :
+													<span>最终成绩:{record.finalscore}分</span>}</div>
 											</div>}>
 												{
 													record.finalscore && record.finalscore === "--" ?
@@ -1511,11 +1556,11 @@ class Listofworksstudentone extends Component {
 						record.submitstate === "未提交" ?
 							<a style={{textAlign: "center"}} className="color-blue"
 								 onMouseDown={(e) => this.Viewstudenttraininginformationtysl2(e, record)}
-								 onClick={() => this.Viewstudenttraininginformationt(record)}>{record.operating}</a> :
+								 onClick={() => this.Viewstudenttraininginformationt(record)}>评阅</a> :
 							<span>
                        <a style={{textAlign: "center"}} className="color-blue"
 													onMouseDown={(e) => this.Viewstudenttraininginformationtysl2(e, record)}
-													onClick={() => this.Viewstudenttraininginformationt(record)}>{record.operating}</a>
+													onClick={() => this.Viewstudenttraininginformationt(record)}>评阅</a>
         </span>
 					)
 				},
@@ -1711,6 +1756,7 @@ class Listofworksstudentone extends Component {
 				homework_status: result.data.homework_status,
 				update_score: result.data.update_score
 			});
+			allow_lates=result.data.allow_late;
 			this.seacthdatat(result.data, result.data.student_works, result.data.work_efficiency, result.data.course_group_info, 1);
 
 			if (result.data.student_works === undefined || result.data.student_works === null || JSON.stringify(result.data.student_works) === "[]") {
@@ -2419,6 +2465,7 @@ class Listofworksstudentone extends Component {
 					challenges_count: result.data.challenges_count,
 					homework_status: result.data.homework_status,
 				});
+				allow_lates=result.data.allow_late
 				this.seacthdatat(result.data, result.data.student_works, result.data.work_efficiency, result.data.course_group_info, page);
 				this.props.Getdataback(result, result.data);
 				// }
diff --git a/public/react/src/modules/courses/shixunHomework/ShixunWorkReport.js b/public/react/src/modules/courses/shixunHomework/ShixunWorkReport.js
index 2a2ed9a9e..737ed4c00 100644
--- a/public/react/src/modules/courses/shixunHomework/ShixunWorkReport.js
+++ b/public/react/src/modules/courses/shixunHomework/ShixunWorkReport.js
@@ -72,10 +72,10 @@ class ShixunWorkReport extends Component {
 					})
 				}
 			}else {
-				this.props.slowDownload(url)
+				// this.props.slowDownload(url)
 
-			// 	this.props.showNotification(`正在下载中`);
-		    // window.open("/api"+url+'?export=true', '_blank');
+				this.props.showNotification(`正在下载中`);
+		    	window.open("/api"+url+'?export=true', '_blank');
 				this.setState({ isspinning: false })
 			}
 		}).catch((error) => {
diff --git a/public/react/src/modules/home/shixunsHome.js b/public/react/src/modules/home/shixunsHome.js
index 8cdee3554..40bc8c69d 100644
--- a/public/react/src/modules/home/shixunsHome.js
+++ b/public/react/src/modules/home/shixunsHome.js
@@ -394,6 +394,7 @@ class ShixunsHome extends Component {
 
 
                             {/*导师排行榜*/}
+                            { homedatalist !== undefined && homedatalist.teachers !== undefined && (
                             <div className="pt60 pb60 mb30 mentor-ranking">
                                 <div className="educontent">
                                     <div className="edu-txt-center">
@@ -470,8 +471,10 @@ class ShixunsHome extends Component {
 
                                 </div>
                             </div>
+                            )}
 
                             {/*程序员排行榜*/}
+                            { homedatalist !== undefined && homedatalist.students !== undefined && (
                             <div className="pt60 pb60 mb30 pro-ranking">
                                 <div className="educontent">
                                     <div className="edu-txt-center">
@@ -544,6 +547,7 @@ class ShixunsHome extends Component {
                                 </div>
                                 </div>
                             </div>
+                            )}
                         </div>
 									</Spin>
             </div>
diff --git a/public/react/src/modules/login/LoginDialog.css b/public/react/src/modules/login/LoginDialog.css
index 37e2f48b6..833a17e3d 100644
--- a/public/react/src/modules/login/LoginDialog.css
+++ b/public/react/src/modules/login/LoginDialog.css
@@ -151,3 +151,11 @@
 .textcenter{
     text-align: center;
 }
+
+.zindextest {
+    z-index: 1000 !important;
+}
+
+.MuiModal-root-15 {
+    z-index: 1000 !important;
+}
diff --git a/public/react/src/modules/login/LoginDialog.js b/public/react/src/modules/login/LoginDialog.js
index 1fa52670e..cf78d97e0 100644
--- a/public/react/src/modules/login/LoginDialog.js
+++ b/public/react/src/modules/login/LoginDialog.js
@@ -476,10 +476,28 @@ class LoginDialog extends Component {
                 if (response.data.status === 402) {
                     // window.location.href = response.data.url;
                 }else if (response.data.status === -2) {
-									notification.open({
-										message: '提示',
-										description:response.data.message,
-									});
+									if (response.data.message === "登录密码出错已达上限,账号已被锁定, 请10分钟后重新登录或找回密码") {
+										const messge = (
+											<div>
+												<p>
+													登录密码出错已达上限,账号已被锁定;
+												</p>
+												<p className="mt10">
+													请10分钟后重新登录或<a href={'/changepassword'} style={{
+													textDecoration: "underline",
+													color: "#4CACFF",
+												}}>找回密码</a>
+												</p>
+											</div>
+										)
+										this.openNotifications(messge);
+									} else {
+										notification.open({
+											message: '提示',
+											description: response.data.message,
+                      duration: 5,
+										});
+									}
 								}else{
 
 
@@ -529,6 +547,17 @@ class LoginDialog extends Component {
 			weixinlogin:true
 		})
 	}
+	openNotifications = (btn) => {
+		// type  1 成功提示绿色 2提醒颜色黄色 3错误提示红色
+		notification.open({
+			message: "提示",
+			description: btn,
+      duration: 5,
+			onClick: () => {
+
+			},
+		});
+	}
 	openqqlogin=()=>{
 		this.setState({
 			qqlogin:true
@@ -543,13 +572,17 @@ class LoginDialog extends Component {
 			`https://xui.ptlogin2.qq.com/cgi-bin/xlogin?appid=716027609&pt_3rd_aid=101508858&daid=383&pt_skey_valid=0&style=35&s_url=http%3A%2F%2Fconnect.qq.com&refer_cgi=authorize&which=&client_id=101508858&response_type=code&scope=get_user_info&redirect_uri=https%3a%2f%2ftest-newweb.educoder.net%2fotherloginqq&response_type=code`
 		)
 	}
-	
+
+  getTContainer = () => {
+    return document.body;
+  }
 	hideweixinlogin=()=>{
 		this.setState({
 			weixinlogin:false,
 			qqlogin:false
 		})
 	}
+
     render() {
       let{qqlogin,login,isGoing,isGoingValue,disabled,bottonclass,Phonenumberisnotco,
         dialogBox, isRender,weixinlogin}=this.state;
@@ -560,9 +593,10 @@ class LoginDialog extends Component {
       // console.log(this.props)
     	return (
 			<Dialog open={true} id="DialogID"
+              className="zindextest"
               style={{ display: isRender==false? 'none' : ''}}
-							disableEscapeKeyDown={true}
-							disableBackdropClick={true}
+              disableEscapeKeyDown={true}
+              disableBackdropClick={true}
               onClose={() => this.handleDialogClose()}
             >
         <Notcompletedysl
@@ -605,27 +639,37 @@ class LoginDialog extends Component {
 
                           <input name="back_url" type="hidden" value={this.back_url}></input>
                           <input
-                          type="text"
-                          className="input-100-45 mt20"
-                          id="name_loggin_input"
-                          ref="loginPassText"
-                          onInput={this.loginChange}
-                          onBlur={(e) => this.inputOnBlur(e, 1)}
-                          value={this.state.loginValue}
-                          name="username"
-                          placeholder="请输入有效的手机号/邮箱号" ></input>
+														type="text"
+														className="input-100-45 mt20"
+														id="name_loggin_input"
+														ref="loginPassText"
+														onInput={this.loginChange}
+														onBlur={(e) => this.inputOnBlur(e, 1)}
+														onPressEnter={disabled === false ?
+															this.loginEDU : () => {
+															}
+															// console.log(1)
+														}
+														value={this.state.loginValue}
+														name="username"
+														placeholder="请输入有效的手机号/邮箱号" ></input>
 
                           <div style={{height: '25px'}}><p className="color-orange edu-txt-left none" id="username_error_notice"
                             style={{display: Phonenumberisnotco===undefined?'none':'block'}}>{Phonenumberisnotco}</p></div>
 
                           <div>
                             <input type="password" id="password_loggin_input"
-                              name="password"
-                              ref="passwordText"
-                              onInput={this.passwordChange}
-                              onKeyDown={this.onKeydowns}
-                              className="input-100-45 mt5"
-                              placeholder="密码" >
+																	 name="password"
+																	 ref="passwordText"
+																	 onInput={this.passwordChange}
+																	 onKeyDown={this.onKeydowns}
+																	 className="input-100-45 mt5"
+																	 onPressEnter={disabled === false ?
+																		 this.loginEDU : () => {
+																		 }
+																		 // console.log(1)
+																	 }
+																	 placeholder="密码" >
                             </input>
                             <div style={{height: '25px'}}>
                               <p className="color-orange edu-txt-left none" id="password_error_notice">
@@ -662,7 +706,8 @@ class LoginDialog extends Component {
                             </span>
 
                             <span className="fr">
-                              <a onClick={(url)=>this.getloginurl("/changepassword")} className="mr3 color-grey-9">忘记密码</a><em className="vertical-line"></em>
+                              <a onClick={(url) => this.getloginurl("/changepassword")}
+																 className="mr3 color-grey-9">找回密码</a><em className="vertical-line"></em>
                               <a onClick={(url)=>this.getloginurl("/register")} className="color-grey-9">注册</a>
                             </span>
 
diff --git a/public/react/src/modules/osshackathon/Osshackathon.css b/public/react/src/modules/osshackathon/Osshackathon.css
index 8966ed800..7fecb7d9c 100644
--- a/public/react/src/modules/osshackathon/Osshackathon.css
+++ b/public/react/src/modules/osshackathon/Osshackathon.css
@@ -4,10 +4,15 @@
     -ms-flex-direction: column;
     flex-direction: column;
 }
+
 .textright{
     text-align: right;
 }
 
+.textcenter{
+    text-align: center;
+}
+
 .Osshackathonfont{
     width: 80px;
     height: 28px;
@@ -26,7 +31,7 @@
 }
 .OsshackathonCard{
     width:1200px;
-    height:150px;
+    min-height:150px;
     background:rgba(248,248,248,1);
     border:1px solid rgba(235,235,235,1);
 }
@@ -37,6 +42,9 @@
     font-weight:400;
     color:rgba(5,16,26,1);
     line-height:24px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
 }
 
 .ant-input::-webkit-input-placeholder{
@@ -66,4 +74,85 @@
 
 .Searchant-btn-primary .ant-input-group .ant-input{
   height:42px;
+}
+
+.Breadcrumbfont{
+    color:#4CACFF;
+}
+
+.ant-breadcrumb-separator{
+    color:#BCBCBC;
+}
+
+.minheight50px{
+    min-height: 50px;
+}
+
+.borderDEDEDE{
+    border-left:1px solid #DEDEDE;
+}
+
+.pl80pt6{
+    padding-top: 6px;
+    padding-left: 80px;
+}
+
+.OsshackprimaryButton{
+    width: 100%;
+    height: 45px;
+}
+
+.OsshackprimaryButtonsyle{
+    background: rgba(255,255,255,1) !important;
+    border: 1px solid rgba(76,172,255,1);
+    color: rgba(76,172,255,1);
+    line-height: 16px;
+    border-radius: unset;
+    box-shadow:none;
+}
+
+.h45input input{
+    height:45px;
+}
+
+.width14bai{
+    width: 14%;
+}
+
+.Osshackprimaryfonttop{
+    font-size: 20px;
+    color: rgba(5,16,26,1);
+    text-align: center;
+}
+
+.Osshackathonmodelinput{
+    width: 97% !important;
+    margin-left: 7px;
+}
+.Osshackathonmodelinput .ant-input::-webkit-input-placeholder{
+    color: #999;
+    font-size: 14px;
+}
+
+.Osshackathonmodelinput .ant-input::-moz-placeholder{color: #cccccc;font-size:14px;}
+.Osshackathonmodelinput .ant-input::-webkit-input-placeholder{color: #cccccc;font-size:14px;}
+.Osshackathonmodelinput .ant-input:-ms-input-placeholder{color: #cccccc;font-size:14px;}
+.Osshackathonmodelinput .ant-input-group-addon {
+    color: #adadad !important;
+}
+
+.iscursor{
+    cursor: pointer;
+}
+
+.issignup:hover, .issignup:focus {
+    color: #fff !important;
+    background-color: #40a9ff;
+    border-color: #40a9ff;
+}
+
+.Osshackathon-btn-primary .ant-btn-primary:hover, .Osshackathon-btn-primary .ant-btn-primary:focus {
+    color: #fff !important;
+    background-color: #40a9ff;
+    border-color: #40a9ff;
 }
\ No newline at end of file
diff --git a/public/react/src/modules/osshackathon/Osshackathon.js b/public/react/src/modules/osshackathon/Osshackathon.js
index 6b8cb3bf3..9e09aeeec 100644
--- a/public/react/src/modules/osshackathon/Osshackathon.js
+++ b/public/react/src/modules/osshackathon/Osshackathon.js
@@ -1,10 +1,12 @@
 import React, {Component} from 'react';
 import axios from 'axios';
 import {SnackbarHOC, WordsBtn,getImageUrl,markdownToHTML} from 'educoder';
-import {Row, Col,Input,Divider,Card,Button} from 'antd';
+import {Row, Col,Input,Divider,Card,Button,Pagination,Breadcrumb,Icon,Spin} from 'antd';
 import { TPMIndexHOC } from '../tpm/TPMIndexHOC';
 import { CNotificationHOC } from '../courses/common/CNotificationHOC';
+import Osshackathonmd from './Osshackathonmd';
 import './Osshackathon.css';
+import Osshackathonmodel from "./Osshackathonmodel";
 const { Search } = Input;
 class Osshackathon extends Component {
 
@@ -14,16 +16,22 @@ class Osshackathon extends Component {
 			page:1,
 			limit:10,
 			search:undefined,
-			data:undefined
+			data:undefined,
+			hackathonedit:false,
+			Osshackathonmodeltype:false,
+			spinning:false,
 		}
 	}
 
 	componentDidMount() {
-		this.getosshackathon();
-
+		this.getosshackathonlist();
+		window.document.title = '竞赛报名';
 	}
-	getosshackathon=()=>{
-		let {page,limit,search}=this.state;
+
+	getosshackathon=(page,limit,search)=>{
+		this.setState({
+			spinning:true
+		})
 		let url=`/osshackathon.json`;
 		axios.get(url,{params:{
 				page:page,
@@ -31,24 +39,166 @@ class Osshackathon extends Component {
 				search:search,
 			}}).then((result)=>{
 			if(result.status==200){
-				console.log(result)
 				this.setState({
-						data:result.data
+						data:result.data,
+				  	spinning:false
+				})
+			}else{
+				this.setState({
+					spinning:false
 				})
 			}
 		}).catch((error)=>{
-			console.log(error);
+			this.setState({
+				spinning:true
+			})
 		})
 	}
 
+
+	getosshackathonlist=()=>{
+		let {page,limit,search}=this.state;
+		this.setState({
+			page:1,
+			search:undefined
+		})
+		this.getosshackathon(1,limit,undefined)
+	}
+
+
 	componentDidUpdate = (prevProps) => {
 
 
 	}
 
+	PaginationTask=(pageNumber)=>{
+    this.setState({
+			page:pageNumber
+		})
+		let {page,limit,search}=this.state;
+		this.getosshackathon(pageNumber,limit,search)
+	}
+
+	hackathonedit=(id)=>{
+		//管理员编辑title
+	 this.setState({
+		 hackathonedit:true
+	 })
+	}
+
+	hidehackathonedit=()=>{
+		this.setState({
+			hackathonedit:false
+		})
+	}
+
+	Signupentry=(id)=>{
+	//	用户报名
+		if(this.props.checkIfLogin()===false){
+			this.props.showLoginDialog()
+			return
+		}
+		if(this.props.checkIfProfileCompleted()===false){
+			this.props.showProfileCompleteDialog()
+			return
+		}
+
+		this.props.confirm({
+			content: `是否确认报名?`,
+			onOk: () => {
+				this.Signupentrys(id)
+			}
+		})
+
+
+	}
+	Signupentrys=(id)=>{
+		let url=`/osshackathon/${id}/entry.json`;
+		axios.post(url
+		).then((response) => {
+			if(response.data.status===0){
+				this.getosshackathonlist()
+				this.props.showNotification(`报名成功,预祝您夺得桂冠`);
+			}
+		}).catch((error) => {
+			console.log(error)
+		})
+	}
+
+	editSignupentry=(id,name,description)=>{
+		//	管理员编辑项目
+	  this.setState({
+			Osshackathonmodeltype:true
+		})
+		if(id===undefined){
+   		this.setState({
+				modelid:undefined,
+				modelname:undefined,
+				modeldescription:undefined
+			})
+		}else{
+     this.setState({
+			  modelid:id,
+				modelname:name,
+			  modeldescription:description
+		 })
+		}
+	}
+
+	hideeditSignupentry=(id)=>{
+		//	管理员取消项目
+		this.setState({
+			Osshackathonmodeltype:false
+		})
+	}
+
+
+	delSignupentry=(id)=>{
+		//	管理员删除项目
+		this.props.confirm({
+			content: `是否确认删除该项目?`,
+			onOk: () => {
+				this.delSignupentrys(id)
+			}
+		})
+	}
+	delSignupentrys=(id)=>{
+		let url=`/osshackathon/${id}.json`;
+		axios.delete(url)
+			.then((response) => {
+				if (response.data.status == 0) {
+					// {"status":1,"message":"删除成功"}
+					this.getosshackathonlist();
+					this.props.showNotification(`删除成功`);
+				}
+			})
+			.catch(function (error) {
+				console.log(error);
+			});
+
+	}
+
+
+	onsearchvalue=(value)=>{
+		this.setState({
+			search:value
+		})
+		if(value.length>300){
+			this.props.showNotification(`搜索字数大于300个字`);
+		}
+		let {page,limit,search}=this.state;
+		this.getosshackathon(page,limit,value)
+	}
+
+	onsetsearchvalue=(e)=>{
+
+		this.setState({
+			search:e.target.value
+		})
+	}
 	render() {
-		let{data}=this.state;
-		console.log(this.state.data)
+		let {page,data,hackathonedit}=this.state;
+
 
 		return (
 			<div className="newMain clearfix  newMainybot">
@@ -56,8 +206,13 @@ class Osshackathon extends Component {
 					{
 						`
 						.ant-btn-primary{
-					background: #4CACFF;
-					border-color: #4CACFF;
+							background: #4CACFF;
+							border-color: #4CACFF;
+						}
+						.ant-btn-primary:hover, .ant-btn-primary:focus {
+								color: #4CACFF;
+								background-color: #40a9ff;
+								border-color: #40a9ff;
 						}
 						`
 					}
@@ -66,18 +221,20 @@ class Osshackathon extends Component {
 				<div className={"educontent  mb20 persmstyle"} style={{width: "1200px", marginTop: "26px"}}>
 
 					<div className="registrationback"
-							 style={{"background": `url(${getImageUrl(`images/educoder/competitions/tipregistit.jpg`)})`,"height":"360px"}}
+							 style={{"background": `url(${getImageUrl(`images/educoder/competitions/heikesong.jpg`)})`,"height":"360px"}}
 					></div>
-
-					<Row className={"mt20"}>
+					<Spin spinning={this.state.spinning}>
+					{this.props.user&&this.props.user.admin===true?<Row className={"mt20"}>
 
 						<Col span={6}>
 							<Search
-						  className={"Searchant-btn-primary"}
+						  className={"Osshackathon-btn-primary"}
 							placeholder="请输入项目名称进行搜索"
 							enterButton="搜索"
 							size="large"
-							onSearch={value => console.log(value)}
+							value={this.state.search}
+							onInput={(e)=>this.onsetsearchvalue(e)}
+							onSearch={value => this.onsearchvalue(value)}
 							/>
 						</Col>
 
@@ -87,17 +244,17 @@ class Osshackathon extends Component {
 								</div>
 						</Col>
 
-					</Row>
+					</Row>:""}
 
-					<Row className={"mt20"}>
+					{hackathonedit===true?"":<Row className={"mt20"}>
 						<Col span={6} className={"Osshackathonfont"}>
 							{data&&data.hackathon.name}
 						</Col>
-						<Col span={3} className={"fr textright"}>
-							<Button type="primary">编辑</Button>
-						</Col>
-					</Row>
-					<style>
+						{this.props.user&&this.props.user.admin===true?<Col span={3} className={"fr textright"}>
+							<Button type="primary" className={"OsshackprimaryButtonsyle"} onClick={()=>this.hackathonedit(data&&data.hackathon.id)}>编辑</Button>
+						</Col>:""}
+					</Row>}
+					{hackathonedit===true?"":	<style>
 						{
 							`
 							.ant-divider-horizontal{
@@ -105,59 +262,126 @@ class Osshackathon extends Component {
 							}
 							`
 						}
-					</style>
+					</style>}
 
-					<Divider />
+					{hackathonedit===true?"":<Divider />}
 
-					<p className={"Osshackathonfontlist mb30"}>
+					{hackathonedit===true?"":<p className={"Osshackathonfontlist mb30"}>
 						{data&&data.hackathon.description===null?"":<div className={"markdown-body"}
 									dangerouslySetInnerHTML={{__html: markdownToHTML(data&&data.hackathon.description).replace(/▁/g, "▁▁▁")}}></div>}
-					</p>
+					</p>}
+
+					{hackathonedit===true?<Osshackathonmd
+						getosshackathon={()=>this.getosshackathonlist()}
+						hidehackathonedit={()=>this.hidehackathonedit()}
+						{...this.props}
+						{...this.state}
+					/>:""}
+						{this.state.Osshackathonmodeltype===true?<Osshackathonmodel
+						getosshackathon={()=>this.getosshackathonlist()}
+						hideeditSignupentry={()=>this.hideeditSignupentry()}
+						{...this.props}
+						{...this.state}
+					/>:""}
+					{this.props.user&&this.props.user.admin===true?<Row className={"mb20"}>
+						<Col span={8}></Col>
+						<Col span={8}><Button type="primary" className={"OsshackprimaryButton OsshackprimaryButtonsyle"} onClick={()=>this.editSignupentry()}><Icon type="plus" />新建项目</Button></Col>
+						<Col span={8}></Col>
+					</Row>:""}
+					{/*学生身份*/}
 					{
-						data&&data.hacks.length==0?"":data&&data.hacks.map((item,key)=>{
+						this.props.user&&this.props.user.admin===false?data&&data.hacks.length==0?"":data&&data.hacks.map((item,key)=>{
 							return(
-								<span></span>
+								<Card className={"OsshackathonCard mb20"} key={key}>
+									<Row>
+										<Col span={20} className={"OsshackathonCardtitle"}>
+											{item.name}
+										</Col>
+
+										<Col span={6} className={"fr textright"}>
+											{item.entry_info===true?<Button type="primary fr mr20" disabled>
+												已报名
+											</Button>:<Button type="primary fr issignup" onClick={()=>this.Signupentry(item.id)}>立即报名</Button>}
+										</Col>
+									</Row>
+									<p className={"mt20"}>{item.description}</p>
+								</Card>
 							)
-						})
+						}):""
 					}
-					{/*学生身份*/}
-					<Card className={"OsshackathonCard mb20"}>
-
-						<Row>
-							<Col span={6} className={"OsshackathonCardtitle"}>
-								大赛介绍
-							</Col>
-							<Col span={6} className={"fr textright"}>
-								<Button type="primary fr ">立即报名</Button>
-								<Button type="primary fr mr20" disabled>
-										已报名
-								</Button>
-							</Col>
-						</Row>
-
-						<p>Card content</p>
-						<p>Card content</p>
-					</Card>
+
+
 
 					{/*教师身份*/}
+					{this.props.user&&this.props.user.admin===true?<style>
+						{
+							`
+							.ant-col-pull-6 {
+									right: 17%;
+							}
+							.ant-col-18 {
+									width: 82%;
+							}
+							.CompetitionsIndexbottomvalue{
+   							  text-align: center;
+							}
+							`
+						}
+					</style>:""}
+					{
+						this.props.user&&this.props.user.admin===true?data&&data.hacks.length==0?"":data&&data.hacks.map((item,key)=>{
+							return(
+								<Card className={"OsshackathonCard mb20"}>
+									<Row>
+										<Col span={20} className={"OsshackathonCardtitle"}>
+											{item.name}
+										</Col>
+										<Col span={4} className={"fr textcenter width14bai"}>
+											<Breadcrumb separator="|">
+												<Breadcrumb.Item className={"Breadcrumbfont iscursor"} onClick={()=>this.editSignupentry(item.id,item.name,item.description)}>编辑</Breadcrumb.Item>
+												<Breadcrumb.Item className={"Breadcrumbfont iscursor"} onClick={()=>this.delSignupentry(item.id)}>删除</Breadcrumb.Item>
+											</Breadcrumb>
+										</Col>
+									</Row>
+
+									<Row className={"mt20"}>
+										<Col span={4} push={20} className={"minheight50px borderDEDEDE"}>
+											<div className={"pl80pt6"}>
+												<Row gutter={16}>
+													<Col className="gutter-row" span={15}>
+														<div className="gutter-box Osshackprimaryfonttop">{item.hack_users_count}</div>
+													</Col>
+												</Row>
+
+												<Row gutter={16}>
+													<Col className="gutter-row" span={15}>
+														<div className="gutter-box CompetitionsIndexbottomvalue">报名数</div>
+													</Col>
+												</Row>
+											</div>
+										</Col>
+										<Col span={18} pull={6} className={"minheight50px ml5"}>
+											{item.description}
+										</Col>
+									</Row>
 
-					<Card className={"OsshackathonCard"}>
-						<Row>
-							<Col span={6} className={"OsshackathonCardtitle"}>
-								大赛介绍
-							</Col>
-							<Col span={6} className={"fr textright"}>
-								<Button type="primary fr ">立即报名</Button>
-								<Button type="primary fr mr20" disabled>
-									已报名
-								</Button>
-							</Col>
-						</Row>
-						<p>Card content</p>
-						<p>Card content</p>
-					</Card>
+
+								</Card>
+							)}):""
+					}
 
 
+					{data&&data.hacks_count>10?data&&data.hacks.length===0?"":<div className="mb40 edu-txt-center padding20-30" >
+						<Pagination
+							showQuickJumper
+							defaultCurrent={1}
+							pageSize={10}
+							total={data&&data.hacks_count}
+							current={page}
+							onChange={this.PaginationTask}
+						/>
+					</div>:""}
+					</Spin>
 				</div>
 
 			</div>
diff --git a/public/react/src/modules/osshackathon/Osshackathonmd.js b/public/react/src/modules/osshackathon/Osshackathonmd.js
new file mode 100644
index 000000000..e9a888ab1
--- /dev/null
+++ b/public/react/src/modules/osshackathon/Osshackathonmd.js
@@ -0,0 +1,111 @@
+import React, { Component } from 'react';
+import {Button, Card,  Row, Col ,Upload,Icon,message,Tabs,Form,Input} from 'antd';
+import axios from 'axios';
+import {getImageUrl,getUrl} from 'educoder';
+import TPMMDEditor from '../tpm/challengesnew/TPMMDEditor';
+class Osshackathonmd extends Component{
+	constructor(props) {
+		super(props)
+		this.contentMdRef = React.createRef();
+		this.state={
+			title_num: 0,
+			title_value: undefined
+		}
+	}
+	componentDidUpdate =(prevState)=>{
+		// if(prevState!=this.props){
+		// 	let url=`/osshackathon/edit_hackathon.json`;
+		// 	axios.get(url).then((result)=>{
+		// 		if(result.status==200){
+		// 			this.setState({
+		// 				title_value:result.data.name
+		// 			})
+		// 			this.contentMdRef.current.setValue(result.data.description);
+		// 		}
+		// 	})
+		// }
+	}
+	componentDidMount(){
+		let url=`/osshackathon/edit_hackathon.json`;
+		axios.get(url).then((result)=>{
+			if(result.status==200){
+				this.setState({
+					title_value:result.data.name
+				})
+				this.contentMdRef.current.setValue(result.data.description);
+			}
+		})
+	}
+
+
+	// 输入title
+	changeTitle = (e) => {
+		// title_num: 60 - parseInt(e.target.value.length),
+		this.setState({
+			title_num: e.target.value.length,
+			title_value: e.target.value
+		})
+
+	}
+	handleSubmit = () => {
+		let {title_value}=this.state;
+		const mdContnet = this.contentMdRef.current.getValue().trim();
+		// if(mdContnet.length>10000){
+		// 	this.props.showNotification("内容超过10000个字");
+		// 	return
+		// }
+
+		let url=`/osshackathon/update_hackathon.json`;
+		axios.post(url,{
+			name:title_value,
+			description:mdContnet,
+			}
+		).then((response) => {
+     if(response.data.status===0){
+     	 this.props.getosshackathon()
+			 this.props.hidehackathonedit()
+			 this.props.showNotification(`提交成功`);
+		 }
+		}).catch((error) => {
+			console.log(error)
+		})
+
+	}
+	render() {
+
+
+		// console.log(this.props.tabkey)
+		// console.log(chart_rules)
+
+		return (
+			<div className={"mt20"}>
+				<Form>
+					<Form.Item label="标题">
+						<Input placeholder="请输入标题"
+									 value={this.state.title_value}
+									  onInput={this.changeTitle}
+									 className="searchView searchViewAfter h45input" style={{"width": "100%"}} maxLength="60"
+									 addonAfter={String(this.state.title_value===undefined?0:this.state.title_value.length)+"/60"}
+						/>
+					</Form.Item>
+
+					<Form.Item label="描述">
+						<TPMMDEditor ref={this.contentMdRef} placeholder="请输入描述" mdID={'courseContentMD'} refreshTimeout={1500}
+												 className="courseMessageMD" initValue={this.state.description}></TPMMDEditor>
+					</Form.Item>
+				</Form>
+
+
+
+				<div className="clearfix mt30 mb30">
+					<div className={"fr"}>
+						<Button type="primary"  onClick={this.handleSubmit}  className="defalutSubmitbtn fl mr20">提交</Button>
+						<a className="defalutCancelbtn fl" onClick={() => this.props.hidehackathonedit()}>取消</ a>
+					</div>
+				</div>
+			</div>
+
+		)
+	}
+}
+export default Osshackathonmd;
\ No newline at end of file
diff --git a/public/react/src/modules/osshackathon/Osshackathonmodel.js b/public/react/src/modules/osshackathon/Osshackathonmodel.js
new file mode 100644
index 000000000..f9d170695
--- /dev/null
+++ b/public/react/src/modules/osshackathon/Osshackathonmodel.js
@@ -0,0 +1,217 @@
+import React, { Component } from 'react';
+import {Button, Card,  Row, Col ,Upload,Icon,message,Tabs,Form,Input,Modal} from 'antd';
+import axios from 'axios';
+import {getImageUrl,getUrl,WordNumberTextarea} from 'educoder';
+
+class Osshackathonmodel extends Component{
+	constructor(props) {
+		super(props)
+		this.state={
+			title_num: 0,
+			title_value: undefined,
+			Textarea_comment:undefined
+		}
+	}
+	componentDidUpdate =(prevState)=>{
+		// if(prevState!=this.props){
+		// 	let name=this.props&&this.props.modelname;
+		// 	let mdvalue=this.props&&this.props.modeldescription;
+		// 	this.setState({
+		// 		title_value:name,
+		// 		Textarea_comment:mdvalue
+		// 	})
+		// }
+	}
+	componentDidMount(){
+		if(this.props.modelid===undefined){
+			this.setState({
+				title_value:undefined,
+				Textarea_comment:undefined
+			})
+		}else{
+			let url=`/osshackathon/${this.props.modelid}/edit.json`;
+			axios.get(url).then((result)=>{
+				if(result.status==200){
+					this.setState({
+						title_value:result.data.name,
+						Textarea_comment:result.data.description
+					})
+				}
+			})
+		}
+	}
+
+	handleSubmit = () => {
+		let {title_value,Textarea_comment}=this.state;
+		// if(mdContnet.length>10000){
+		// 	this.props.showNotification("内容超过10000个字");
+		// 	return
+		// }
+		//
+
+		if(this.props.modelid===undefined){
+			let url=`/osshackathon.json`;
+			axios.post(url,{
+					name:title_value,
+					description:Textarea_comment,
+				}
+			).then((response) => {
+				if(response.data.status===0){
+
+					this.props.getosshackathon()
+					this.props.hideeditSignupentry()
+					this.props.showNotification(`提交成功`);
+				}
+			}).catch((error) => {
+				console.log(error)
+			})
+		}else{
+			let url=`/osshackathon/${this.props.modelid}.json`
+			axios.put(url,{
+					name:title_value,
+					description:Textarea_comment,
+				}
+			).then((response) => {
+				if(response.data.status===0){
+
+					this.props.getosshackathon()
+					this.props.hideeditSignupentry()
+					this.props.showNotification(`提交成功`);
+				}
+			}).catch((error) => {
+				console.log(error)
+			})
+		}
+
+	}
+
+	changeTitle=(e)=>{
+		this.setState({
+			title_value:e.target.value,
+			title_num:e.target.value.length,
+		})
+	}
+
+	Textarea_comment=(e)=>{
+		this.setState({
+			Textarea_comment:e.target.value,
+		})
+	}
+	render() {
+		let {textareavaltype}=this.state;
+		// console.log(this.props.tabkey)
+    // console.log(this.props.Osshackathonmodeltype)
+
+		return (
+			<div>
+
+				<style>
+					{
+						`
+								@media (max-width: 2000px) {
+											.WordNumberTextarea{
+											  height: 130px !important;
+											}
+									}
+
+								 @media (max-width: 1350px) {
+										.HomeworkModal{
+ 										  top:10px !important;
+ 										}
+ 											.WordNumberTextarea{
+											  height: 80px !important;
+											}
+									}
+
+	 								@media (max-width: 1250px) {
+ 										.HomeworkModal{
+ 										  top:0px !important;
+ 										}
+
+ 										.WordNumberTextarea{
+											  height: 40px !important;
+									  }
+									}
+
+									`
+					}
+				</style>
+				<Modal
+					keyboard={false}
+					className={"HomeworkModal"}
+					title={this.props.modelid===undefined?"新建项目":"编辑项目"}
+					visible={this.props.Osshackathonmodeltype}
+					closable={false}
+					footer={null}
+					destroyOnClose={true}
+				>
+
+					<div className={"pd015"}>
+						<style>
+							{
+								`
+								.pd015{
+								    padding: 0px 15px 15px 15px;
+								}
+								.font{
+									font-size: 14px;
+									font-weight: 400;
+									color: rgba(5,16,26,1);
+								}
+								.newfont{
+								    height: 16px;
+										font-size: 16px;
+										font-weight: 400;
+										color: rgba(5,16,26,1);
+										line-height: 16px;
+										margin-bottom: 5px;
+								}
+								.Osshackathonmodelinput .ant-input, .ant-input .ant-input-suffix{
+										background: #fff !important;
+								}
+								.Osshackathonmodelinput .ant-input-group-wrapper{
+								    width:510px !important;
+   								  margin-left: 10px;
+								}
+								`
+							}
+						</style>
+						<div className="clearfix">
+							<p className={"font mt10 mb10 ml10"}>
+									名称
+							</p>
+
+							<Input placeholder="请输入项目名称"
+										 value={this.state.title_value}
+										 onInput={(e)=>this.changeTitle(e)}
+										 className={"Osshackathonmodelinput"}
+									   style={{"width": "100%"}} maxLength="60"
+										 addonAfter={String(this.state.title_value===undefined?0:this.state.title_value.length)+"/60"}
+							/>
+
+							<p className={"font mt10 mb10 ml10"}>
+									描述
+							</p>
+
+							<WordNumberTextarea
+								placeholder={"请输入项目描述"}
+								onInput={(e)=>this.Textarea_comment(e)}
+								value={this.state.Textarea_comment}
+								maxlength={500}
+							/>
+
+							<li style={{height:"20px",lineHeight:"20px"}} className={textareavaltype===true?"color-red mt20 mb10":"none"}><span>评阅内容至少有一个不为空</span></li>
+						</div>
+
+						<div className={textareavaltype===false?"mt20 clearfix edu-txt-center":"clearfix edu-txt-center mt20"}>
+							<a  className="task-btn color-white mr30" onClick={()=>this.props.hideeditSignupentry()}>取消</a>
+							<a className="task-btn task-btn-orange" onClick={()=>this.handleSubmit()}>确定</a>
+						</div>
+					</div>
+				</Modal>
+			</div>
+
+		)
+	}
+}
+export default Osshackathonmodel;
\ No newline at end of file
diff --git a/public/react/src/modules/page/main/LeftView.js b/public/react/src/modules/page/main/LeftView.js
index 2f311690f..a78a1b1df 100644
--- a/public/react/src/modules/page/main/LeftView.js
+++ b/public/react/src/modules/page/main/LeftView.js
@@ -26,6 +26,7 @@ import './leftView.css'
 
 import CodeEvaluateMultiLevelAnswerUnlock from './CodeEvaluateMultiLevelAnswerUnlock'
 import MUIDialogStyleUtil from '../component/MUIDialogStyleUtil'
+import moment from 'moment';
 // http://danilowoz.com/create-react-content-loader/
 const MyLoader = () => (
 	<ContentLoader
@@ -35,16 +36,16 @@ const MyLoader = () => (
 		primaryColor={"#000000"}
 		secondaryColor={"#ecebeb"}
 	>
-		<rect x="0" y="10" rx="3" ry="3" width="320" height="6.4" /> 
-		<rect x="0" y="35" rx="3" ry="3" width="85" height="10" /> 
-		<rect x="0" y="60" rx="3" ry="3" width="350" height="6.4" /> 
-		<rect x="0" y="80" rx="3" ry="3" width="350" height="6.4" /> 
-		<rect x="0" y="100" rx="3" ry="3" width="350" height="6.4" /> 
-		<rect x="0" y="120" rx="3" ry="3" width="101" height="6.4" /> 
-
-		<rect x="0" y="145" rx="3" ry="3" width="65" height="10" /> 
-		<rect x="0" y="170" rx="3" ry="3" width="350" height="6.4" /> 
-		<rect x="0" y="190" rx="3" ry="3" width="350" height="6.4" /> 
+		<rect x="0" y="10" rx="3" ry="3" width="320" height="6.4" />
+		<rect x="0" y="35" rx="3" ry="3" width="85" height="10" />
+		<rect x="0" y="60" rx="3" ry="3" width="350" height="6.4" />
+		<rect x="0" y="80" rx="3" ry="3" width="350" height="6.4" />
+		<rect x="0" y="100" rx="3" ry="3" width="350" height="6.4" />
+		<rect x="0" y="120" rx="3" ry="3" width="101" height="6.4" />
+
+		<rect x="0" y="145" rx="3" ry="3" width="65" height="10" />
+		<rect x="0" y="170" rx="3" ry="3" width="350" height="6.4" />
+		<rect x="0" y="190" rx="3" ry="3" width="350" height="6.4" />
 		<rect x="0" y="210" rx="3" ry="3" width="201" height="6.4" />
 	</ContentLoader>
 )
@@ -59,7 +60,7 @@ const styles = MUIDialogStyleUtil.getTwoButtonStyle(
 				width: '36px',
 				height: '36px',
 			}
-		} 
+		}
 	}
 )
 
@@ -86,7 +87,7 @@ class LeftView extends Component {
 	   //      style={{marginRight: '6px'}}
 	   //    />,
 	   //    <Button
-	   //    	variant="raised" 
+	   //    	variant="raised"
 	   //      label="确定"
 	   //      primary={true}
 	   //      onClick={handleDialogReadAnswer}
@@ -97,14 +98,14 @@ class LeftView extends Component {
 			// isMultiLevelAnswer = true
 			const is_teacher = user.is_teacher
 
-		 	let contentText = is_teacher ? 
+		 	let contentText = is_teacher ?
 				<React.Fragment>
 				<p>{`已经过职业认证的教师可以免金币查看答案哟~`}</p>
 				<p>{`将扣除${challenge.score}点金币,是否确认查看答案`}</p>
-				<p><a onClick={()=>this.goToCertification()} 
+				<p><a onClick={()=>this.goToCertification()}
 						style={{textDecoration: 'underline', color: '#4CACFF'}}>立即认证</a></p>
 				</React.Fragment>
-				: 
+				:
 				<React.Fragment>
 				<p>{`先查看参考答案,再通过评测的学生,实训作业有可能是零分哦~`}</p>
 				<p>{`将扣除${challenge.score}点金币,是否确认查看答案`}</p>
@@ -112,13 +113,13 @@ class LeftView extends Component {
 
 			// 多级别解锁
 			if (isMultiLevelAnswer) {
-				// power === 0 && 
-				contentText =  ( is_teacher ) ? 
+				// power === 0 &&
+				contentText =  ( is_teacher ) ?
            		<React.Fragment>
 	            	<p>{`已经过职业认证的教师可以免金币查看答案哟~`}</p>
 					<p><a style={{textDecoration: 'underline'}} onClick={()=>this.goToCertification()}
 							style={{ color: '#1890ff', 'margin-top': '6px', display: 'inline-block'}}>立即认证</a></p>
-					<CodeEvaluateMultiLevelAnswerUnlock 
+					<CodeEvaluateMultiLevelAnswerUnlock
 						ref="answerUnlock" lockedAnswers={lockedAnswers} unlockedAnswers={unlockedAnswers}
 						challenge={challenge}
 					>
@@ -128,7 +129,7 @@ class LeftView extends Component {
             <React.Fragment>
 				<p>{`先查看参考答案,再通过评测的学生,实训作业将被扣分`}</p>
 				{/* { MultiLevelUnlockTable } */}
-				<CodeEvaluateMultiLevelAnswerUnlock 
+				<CodeEvaluateMultiLevelAnswerUnlock
 					ref="answerUnlock" lockedAnswers={lockedAnswers} unlockedAnswers={unlockedAnswers}
 					challenge={challenge}
 				>
@@ -136,13 +137,13 @@ class LeftView extends Component {
             </React.Fragment>;
 			}
 
-			/** 
-				
+			/**
+
 				{ is_teacher ? <Button size="small" variant="raised" style={{ marginRight: '20px'}}
 						onClick={()=>this.goToCertification()} color="primary">
 					{ '立即认证' }
 				</Button> : ''}
-			*/ 
+			*/
 
 	    return (
 	    	<React.Fragment>
@@ -150,7 +151,7 @@ class LeftView extends Component {
 		          	open={dialogOpen}
 								disableEscapeKeyDown={true}
 		          	onClose={handleDialogClose}
-		        >	
+		        >
 			       	<DialogTitle id="alert-dialog-title">{"提示"}</DialogTitle>
 							<DialogContent id="dialog-content" >
 								<DialogContentText id="alert-dialog-description" style={{textAlign: 'center'}}>
@@ -159,32 +160,32 @@ class LeftView extends Component {
 							</DialogContent>
 		          {/* http://localhost:3000/account/professional_certification */}
 							<DialogActions id="dialog-actions">
-								
+
 								<Button onClick={handleDialogClose} color="primary" className={`${classes.button} ${classes.buttonGray}`}>
 									取消
 								</Button>
 								{/* variant={ is_teacher ? "flat" : "raised"}  */}
 								<Button size="medium" variant={"raised"}
 										className={`${classes.button} `}
-										onClick={() => handleDialogReadAnswer(this.refs.answerUnlock ? this.refs.answerUnlock.getSelectedId() : '')} 
+										onClick={() => handleDialogReadAnswer(this.refs.answerUnlock ? this.refs.answerUnlock.getSelectedId() : '')}
 										color="primary" autoFocus>
 									{ is_teacher ? '继续查看' : '确定'}
 								</Button>
 							</DialogActions>
 
 		        </Dialog>
-	    	
+
 		      	<div className="-fit -layout-v">
-		      		
+
 			      	<div className="-layout-v -flex -bg-white -task-ml80">
-			      		
+
 			      		{/*新界面关卡名称显示、关卡金币显示*/}
 			      		<div id="task_name_section" className="task_name_section">
-			      			
+
 			      			{ loading ? "" :
 			      			<React.Fragment>
 			      				<Tooltip title={ "点击查看全部任务" } disableFocusListener={true}>
-					      			<IconButton color="default" mini={''}  aria-label="edit" className={classes.iconButton} 
+					      			<IconButton color="default" mini={''}  aria-label="edit" className={classes.iconButton}
 						      			onClick={onDrawerButtonClick}>
 								        <i className={ "fa font-18 fa-list-ul" }></i>
 								    </IconButton>
@@ -223,15 +224,15 @@ class LeftView extends Component {
 					    	<div id="tab_con_1" className="tab-info" style={ tabIndex === 0 ? {display: 'block'} : {display: 'none'}  }>
 					    		<div className="fit -scroll">
 					    			<div className="-layout-v -fit">
-					    					{ loading ? 
-					    						<div className="-flex -scroll task-padding16 panel-box-sizing new_li break_word markdown-body editormd-html-preview" 
+					    					{ loading ?
+					    						<div className="-flex -scroll task-padding16 panel-box-sizing new_li break_word markdown-body editormd-html-preview"
 					    						unselectable="on">
 					    							<CircularProgress size={40} thickness={3} className="circularProgress"
-					    								style={{ marginLeft: 'auto', marginRight: 'auto', marginTop: '40% !important', display: 'block' }}/>		    		
+					    								style={{ marginLeft: 'auto', marginRight: 'auto', marginTop: '40% !important', display: 'block' }}/>
 					    						</div> : ""
 					    					}
-					    					
-				    						<div className="-flex -scroll task-padding16 panel-box-sizing new_li break_word markdown-body editormd-html-preview" 
+
+				    						<div className="-flex -scroll task-padding16 panel-box-sizing new_li break_word markdown-body editormd-html-preview"
 					    						unselectable="on" id="game_task_pass" style={loading ? {display:'none'} : {}}>
 				    						</div>
 					    			</div>
@@ -242,7 +243,7 @@ class LeftView extends Component {
 					    		<div className="fit -scroll">
 						            <div className="-layout-v -fit">
 						              	<div className="-flex -scroll task-padding16 panel-box-sizing new_li markdown-body editormd-html-preview" unselectable="on" id="game_ready_knowledge">
-											
+
 						              	</div>
 					    			</div>
 					    		</div>
@@ -253,8 +254,8 @@ class LeftView extends Component {
 								  	<div className="-layout-v -fit" style={{ overflowY: 'scroll' }}>
 								  		{/*  只读markdown的写法  markdownToHTML 这个接口生成的markdown没有setMarkdown接口
 								  		<textarea style={{display:'none'}} id="editorMd_contents" value={gameAnswer}></textarea>*/}
-									    
-									    
+
+
 											{ (!unlockedAnswers || unlockedAnswers.length === 0) && (!lockedAnswers || lockedAnswers.length === 0) &&
 									    <div className="-flex -scroll task-padding16 panel-box-sizing new_li" id="game_answer_show"
 									    		style={{ display: st === 0 ? 'block' : 'none' }}>
@@ -283,8 +284,10 @@ class LeftView extends Component {
 												}
 												.multiLevelAnswer .status{
 													color: #CDCDCD;
-													flex: 0 0 45px;
-												}
+                        }
+                        .multiLevelAnswer .lock-time{
+                          margin-right: 15px;
+                        }
 												.markdown-body ol, .markdown-body ul {
 													padding-left: 2.5em;
 												}
@@ -292,13 +295,17 @@ class LeftView extends Component {
 
 											<div className="multiLevelAnswer">
 												{ unlockedAnswers && unlockedAnswers.map((item, index) => {
+                          const {name, contents, view_time} = item;
 													return <div className="anwserSection">
 														<div className="df">
 															<div className="level">级别{index + 1}:</div>
-															<div className="name">{item.name}</div>
-															<div className="status">已解锁</div>
+															<div className="name">{name}</div>
+                              <div className="status">
+                                <span className="lock-time">{view_time ? moment(view_time).format('YYYY-MM-DD HH:mm') : ''}</span>
+                                已解锁
+                              </div>
 														</div>
-														<div className="contents markdown-body" dangerouslySetInnerHTML={{__html: markdownToHTML(item.contents)}}>
+														<div className="contents markdown-body" dangerouslySetInnerHTML={{__html: markdownToHTML(contents)}}>
 														</div>
 													</div>
 												})}
@@ -308,19 +315,19 @@ class LeftView extends Component {
 														<div className="df">
 															<div className="level">级别{index + 1 + (unlockedAnswers ? unlockedAnswers.length : 0)}:</div>
 															<div className="name">{item.name}</div>
-															<div 
+															<div
 																className="status" onClick={ () => { this.props.showUnlockAnswerDialog(item) } }
 																style={{ color: '#4CACFF', cursor: 'pointer' }}
 															>解锁</div>
 														</div>
 													</div>
 												})}
-											
+
 											</div>
-											
 
-									    { st === 1 ? 
-									    <div className="-flex -scroll task-padding16 panel-box-sizing new_li" id="game_answer_show_choose" 
+
+									    { st === 1 ?
+									    <div className="-flex -scroll task-padding16 panel-box-sizing new_li" id="game_answer_show_choose"
 									    		>
 									      	<ChooseAnswerView gameAnswer={gameAnswer}></ChooseAnswerView>
 									    </div>
@@ -338,8 +345,8 @@ class LeftView extends Component {
 								</div>
 					    	</div>
 					    	<div id="tab_con_4" className="commentTab tab-info commentsDelegateParent" style={ tabIndex === 3 ? {display: 'block'} : {display: 'none'} }>
-					    		{ loadingComments ? 
-							    	<CircularProgress size={40} thickness={3} style={{ marginLeft: 'auto', marginRight: 'auto', marginTop: '40%', display: 'block' }}/> : 
+					    		{ loadingComments ?
+							    	<CircularProgress size={40} thickness={3} style={{ marginLeft: 'auto', marginRight: 'auto', marginTop: '40%', display: 'block' }}/> :
 						    		<CommentContainer {...this.props}></CommentContainer>
 						    	}
 					    	</div>
diff --git a/public/react/src/modules/page/taskList/TaskList.css b/public/react/src/modules/page/taskList/TaskList.css
index 46dfd5076..5d70558ab 100644
--- a/public/react/src/modules/page/taskList/TaskList.css
+++ b/public/react/src/modules/page/taskList/TaskList.css
@@ -1,92 +1,123 @@
-/*------------------------------- TaskList Start */ 
-#all_task_index {
-	color: #fff;
-	background: #111C24;
-}	
-	.panel-list:last-child {
-	    margin-bottom: 50px;
-	}
-	.-task-list-title a, .-task-list-title a:link, .-task-list-title a:visited {
-		color: #BCC6CD;
-		
-	}
-	.-task-list-inner {
-		background: none;
-		border-bottom: 1px solid #222C34;
-    	padding-bottom: 16px;
-	}
-		#all_task_index .fa-play-circle {
-			color: white;
-		}
-		#all_task_index .panel-list {
-		    position: relative;
-		}
-		#all_task_index .current .currentSelected {
-			border-left: 3px solid #4CACFF;
-		    height: 22px;
-		    width: 0px;
-		    position: absolute;
-		    top: 8px;
-		}
-
-	#all_task_index .grades {
-		color: #747A7F;
-	}
-	.-task-list-title {
-		color: #BCC6CD;
-	}
-	#all_task_index .positive.info-partly span {
-		color: #45E660;
-	}
-	#all_task_index .negative.info-partly span {
-		color: #FF954C;
-	}
-	#all_task_index .notFinish .info-partly span {
-		color: #747A7F !important;
-	}
-
-	#all_task_index #shixun_exp_118 span {
-		/*color: #45E660;*/
-	}
-	#all_task_index #shixun_grade_118 span {
-		/*color: #FF954C;*/
-	}
-	
-	#all_task_index #shixun_tag_118 span {
-		color: #FFF;
-	}
-
-
-.rateRow {
-	margin-left: 20px;
-	font-size: 13px;
-	margin-top: 6px;
-}	
-	.rateRow .rc-rate {
-		margin-top: -5px;
-   		margin-left: 3px;
-	}
-	.rateRow .rc-rate-star {
-		color: #9A9A9A;
-	}
-	.rateRow .starTip {
-	    color: #666666;
-    	font-size: 13px;
-	}
-	.rateRow .starNumber {
-        color: #FFA800;
-    	margin-left: 4px;
-	}
-	.rateRow .unstar .starNumber {
-		cursor: pointer;
-	}
-	.rateRow .unstar .starNumber:hover {
-		text-decoration: underline;
-	}
-	.rateRow .stared .starNumber {
-
-	}
-	.rateRow .rc-rate-star-half .rc-rate-star-first, .rateRow .rc-rate-star-full .rc-rate-star-second {
-	    color: #FFA800;
-	}
-/*------------------------------- TaskList End */ 
\ No newline at end of file
+/*------------------------------- TaskList Start */
+#all_task_index {
+	color: #fff;
+	background: #111C24;
+}
+	.panel-list:last-child {
+	    margin-bottom: 50px;
+	}
+	.-task-list-title a, .-task-list-title a:link, .-task-list-title a:visited {
+		color: #BCC6CD;
+
+	}
+	.-task-list-inner {
+		background: none;
+		border-bottom: 1px solid #222C34;
+    	padding-bottom: 16px;
+	}
+		#all_task_index .fa-play-circle {
+			color: white;
+		}
+		#all_task_index .panel-list {
+		    position: relative;
+		}
+		#all_task_index .current .currentSelected {
+			border-left: 3px solid #4CACFF;
+		    height: 22px;
+		    width: 0px;
+		    position: absolute;
+		    top: 8px;
+		}
+
+	#all_task_index .grades {
+		color: #747A7F;
+	}
+	.-task-list-title {
+		color: #BCC6CD;
+	}
+	#all_task_index .positive.info-partly span {
+		color: #45E660;
+	}
+	#all_task_index .negative.info-partly span {
+		color: #FF954C;
+	}
+	#all_task_index .notFinish .info-partly span {
+		color: #747A7F !important;
+	}
+
+	#all_task_index #shixun_exp_118 span {
+		/*color: #45E660;*/
+	}
+	#all_task_index #shixun_grade_118 span {
+		/*color: #FF954C;*/
+	}
+
+	#all_task_index #shixun_tag_118 span {
+		color: #FFF;
+	}
+
+
+.rateRow {
+	margin-left: 20px;
+	font-size: 13px;
+	margin-top: 6px;
+}
+	.rateRow .rc-rate {
+		margin-top: -5px;
+   		margin-left: 3px;
+	}
+	.rateRow .rc-rate-star {
+		color: #9A9A9A;
+	}
+	.rateRow .starTip {
+	    color: #666666;
+    	font-size: 13px;
+	}
+	.rateRow .starNumber {
+        color: #FFA800;
+    	margin-left: 4px;
+	}
+	.rateRow .unstar .starNumber {
+		cursor: pointer;
+	}
+	.rateRow .unstar .starNumber:hover {
+		text-decoration: underline;
+	}
+	.rateRow .stared .starNumber {
+
+	}
+	.rateRow .rc-rate-star-half .rc-rate-star-first, .rateRow .rc-rate-star-full .rc-rate-star-second {
+	    color: #FFA800;
+  }
+
+  .tip-info-wrap{
+    background-color: #111C24;
+    padding: 20px 0px;
+  }
+  .tip-info-wrap .tip-info{
+    display: flex;
+    background: rgba(40, 47, 53, 1);
+    border: 1px solid rgba(112,112,112,1);
+    border-radius: 100px;
+    width: 375px;
+    height: 52px;
+    font-size: 12px;
+    align-items: center;
+    align-items: space-around;
+    margin: 0 auto;
+    padding: 0 20px;
+  }
+
+  .finish-wrap{
+    display: flex;
+    padding: 0 0px 0 20px;
+    margin-top: 10px;
+  }
+  .finish-wrap .finish-time{
+    flex: 1;
+  }
+  .finish-time .time-title{
+    color: #747A7F;
+    margin-right: 5px;
+  }
+/*------------------------------- TaskList End */
diff --git a/public/react/src/modules/page/taskList/TaskList.js b/public/react/src/modules/page/taskList/TaskList.js
index d3efba167..824512291 100644
--- a/public/react/src/modules/page/taskList/TaskList.js
+++ b/public/react/src/modules/page/taskList/TaskList.js
@@ -1,124 +1,140 @@
-import React, { Component } from 'react';
-
-import { BrowserRouter as Router, Route, Link } from "react-router-dom";
-import { CircularProgress } from 'material-ui/Progress';
-
-import Rate from 'rc-rate';
-
-import 'rc-rate/assets/index.css';
-import './TaskList.css'
-
-import classNames  from 'classnames'
-class TaskList extends Component {
-  	
-  	onChange() {
-
-  	}
-  	renderTasks() {
-  		const { challenges, challenge, shixun, onChallengesDrawerClose, myshixun_manager } = this.props;
-  		const currentChallenge = challenge;
-  		const taskArray = challenges.map( (challenge, index) => {
-  			challenge.experience = challenge.get_experience
-  			challenge.gold = challenge.get_gold
-  			challenge.subject = challenge.name
-
-  			const showExp = (challenge.experience > 0 && challenge.status === 2) ? '+' + challenge.experience : challenge.experience
-  			const showGold = (challenge.gold > 0 && challenge.status === 2) ? '+' + challenge.gold : challenge.gold
-  			return (
-  				<div className={classNames("panel-list", {'current': (currentChallenge.position-1) === index})} key={index}>
-  					<div className="currentSelected"></div>
-		          	<div className=" clearfix -task-list-inner" id="game_status_118">
-		            	
-		            	{/* 允许跳关的设置 */}
-		                <h4 className=" -task-list-title fl">
-		                	{shixun.status<2 || challenge.status === 2 || (challenges[index-1] && challenges[index-1].status === 2) 
-		                		|| shixun.task_pass || myshixun_manager === true ? 
-		                	<Link to={`/tasks/${challenge.identifier}`} onClick={onChallengesDrawerClose}>{index+1}. {challenge.subject}</Link>
-		                	:
-		                	<span>{index+1}. {challenge.subject}</span>}
-		                </h4>
-		                
-		                <a className="fr">
-		                	{challenge.status === 2 ? 
-		                	<i data-tip-down="已完成" className="fa fa-check-circle color-light-green fr font-16 mt5 -text-danger w20_center"></i>
-		                	: 
-		                	<i data-tip-down="待完成" className="fa fa-check-circle fr font-16 mt5 color-light-grey w20_center"></i>
-		                	}
-		                </a>
-
-		                
-		            	<div className="cl"></div>
-			            <div style={{display: 'flex'}} className={`grades with80 ml20 ${challenge.status === 2 ? '' : 'notFinish'}`}>
-		                    <span className={`font-12 mr15 info-partly ${(challenge.status === 2 && challenge.experience > 0) ? 'positive ' : 'negative'}`} id="shixun_exp_118">
-		                    	经验值<span className="ml5">
-		                    		{ showExp }
-		                    	</span>
-		                    </span>
-		                    <span className={`font-12 mr15 info-partly ${(challenge.status === 2 && challenge.experience > 0) ? 'positive ' : 'negative'}`} id="shixun_grade_118">
-		                    	金币<span className="ml5">
-		                    		{/*因为关卡金币和经验值是一样的,所以这里可以直接用经验值*/}
-		                    		{ showGold }
-		                    		
-		                    	</span>
-		                    </span>
-		                    <span className=" font-12 mr15 info-partly" id="shixun_tag_118">
-		                    	{ challenge.tag_count ? 
-		                    		<React.Fragment>
-		                    			技能标签<span className="ml5">{challenge.tag_count || '无'}</span>
-		                    		</React.Fragment>
-		                     		: '' }
-		                    </span>
-			            </div>
-
-			            { shixun.status >= 2 && <div className="rateRow">
-			            	{/* 已完成、未评分 */}
-			            	{challenge.status === 2 && challenge.star === 0? 
-		            		<div className="unstar">
-						    	<span className="starTip">给个评分吧:</span>
-				            	<Rate
-							      defaultValue={0}
-							      onChange={(value) => this.props.onStarChange(challenge, index, value)}
-							    />
-							    <span className="starNumber" onClick={()=>this.props.saveChallengeStar(challenge, index)}>评价</span>
-						    </div>
-						    : 
-						    challenge.status === 2 && challenge.star > 0 ? 
-						    <div className="stared">
-						    	<span className="starTip">已评分:</span>
-								<Rate
-							      defaultValue={challenge.star}
-							      disabled 
-							    />
-							    <span className="starNumber">{challenge.star}分</span>
-						    </div>
-						    : ''}
-			            </div> }
-		          	</div>
-		        </div>
-  			)
-  		})
-		return taskArray;
-  	}
-
-  	render() {
-  		const { taskListLoading } = this.props;
-	    return (
-	    	<div className="page--over">
-			    <div className="col-width-3 -scroll" style={{height: '100%'}} id="all_task_index">
-				    { taskListLoading ?
-				    	<CircularProgress size={40} thickness={3} style={{ marginLeft: 'auto', marginRight: 'auto', marginTop: '40%', display: 'block' }}/> :
-			    		this.renderTasks()
-			    	}
-			  	</div>
-			</div>
-	    );
-  	}
-  	/*
-  		<a className="fr"><i data-tip-down="待完成" className="fa fa-play-circle color-light-green fr font-18 mt5 -text-danger w20_center"></i></a>
-		<div className="-task-list-header clearfix">
-				    	<h3 className="fl">全部任务</h3>
-			    	</div>
-  	*/
-}
-
-export default TaskList;
+import React, { Component } from 'react';
+
+import { BrowserRouter as Router, Route, Link } from "react-router-dom";
+import { CircularProgress } from 'material-ui/Progress';
+import moment from 'moment';
+import Rate from 'rc-rate';
+
+import 'rc-rate/assets/index.css';
+import './TaskList.css'
+
+import classNames  from 'classnames'
+class TaskList extends Component {
+
+  	onChange() {
+
+  	}
+  	renderTasks() {
+  		const { challenges, challenge, shixun, onChallengesDrawerClose, myshixun_manager } = this.props;
+      const currentChallenge = challenge;
+  		const taskArray = challenges.map( (challenge, index) => {
+  			challenge.experience = challenge.get_experience
+  			challenge.gold = challenge.get_gold
+  			challenge.subject = challenge.name
+        const {finished_time, view_answer_time} = challenge;
+  			const showExp = (challenge.experience > 0 && challenge.status === 2) ? '+' + challenge.experience : challenge.experience
+  			const showGold = (challenge.gold > 0 && challenge.status === 2) ? '+' + challenge.gold : challenge.gold
+  			return (
+  				<div className={classNames("panel-list", {'current': (currentChallenge.position-1) === index})} key={index}>
+  					<div className="currentSelected"></div>
+		          	<div className=" clearfix -task-list-inner" id="game_status_118">
+
+		            	{/* 允许跳关的设置 */}
+		                <h4 className=" -task-list-title fl">
+		                	{shixun.status<2 || challenge.status === 2 || (challenges[index-1] && challenges[index-1].status === 2)
+		                		|| shixun.task_pass || myshixun_manager === true ?
+		                	<Link to={`/tasks/${challenge.identifier}`} onClick={onChallengesDrawerClose}>{index+1}. {challenge.subject}</Link>
+		                	:
+		                	<span>{index+1}. {challenge.subject}</span>}
+		                </h4>
+
+		                <a className="fr">
+		                	{challenge.status === 2 ?
+		                	<i data-tip-down="已完成" className="fa fa-check-circle color-light-green fr font-16 mt5 -text-danger w20_center"></i>
+		                	:
+		                	<i data-tip-down="待完成" className="fa fa-check-circle fr font-16 mt5 color-light-grey w20_center"></i>
+		                	}
+		                </a>
+
+
+		            	<div className="cl"></div>
+			            <div style={{display: 'flex'}} className={`grades with80 ml20 ${challenge.status === 2 ? '' : 'notFinish'}`}>
+		                    <span className={`font-12 mr15 info-partly ${(challenge.status === 2 && challenge.experience > 0) ? 'positive ' : 'negative'}`} id="shixun_exp_118">
+		                    	经验值<span className="ml5">
+		                    		{ showExp }
+		                    	</span>
+		                    </span>
+		                    <span className={`font-12 mr15 info-partly ${(challenge.status === 2 && challenge.experience > 0) ? 'positive ' : 'negative'}`} id="shixun_grade_118">
+		                    	金币<span className="ml5">
+		                    		{/*因为关卡金币和经验值是一样的,所以这里可以直接用经验值*/}
+		                    		{ showGold }
+
+		                    	</span>
+		                    </span>
+		                    <span className=" font-12 mr15 info-partly" id="shixun_tag_118">
+		                    	{ challenge.tag_count ?
+		                    		<React.Fragment>
+		                    			技能标签<span className="ml5">{challenge.tag_count || '无'}</span>
+		                    		</React.Fragment>
+		                     		: '' }
+		                    </span>
+			            </div>
+
+                  <div className="finish-wrap">
+                    <p class="finish-time font-12">
+                      <span class="time-title">完成时间</span>
+                      { finished_time ? moment(finished_time).format('YYYY-MM-DD HH:mm') : '--' }
+                    </p>
+                    <p class="finish-time font-12">
+                      <span class="time-title">查看答案时间</span>
+                      { view_answer_time ? moment(view_answer_time).format('YYYY-MM-DD HH:mm') : '--' }                    </p>
+                  </div>
+			            { shixun.status >= 2 && <div className="rateRow">
+			            	{/* 已完成、未评分 */}
+			            	{challenge.status === 2 && challenge.star === 0?
+		            		<div className="unstar">
+						    	<span className="starTip">给个评分吧:</span>
+				            	<Rate
+							      defaultValue={0}
+							      onChange={(value) => this.props.onStarChange(challenge, index, value)}
+							    />
+							    <span className="starNumber" onClick={()=>this.props.saveChallengeStar(challenge, index)}>评价</span>
+						    </div>
+						    :
+						    challenge.status === 2 && challenge.star > 0 ?
+						    <div className="stared">
+						    	<span className="starTip">已评分:</span>
+								<Rate
+							      defaultValue={challenge.star}
+							      disabled
+							    />
+							    <span className="starNumber">{challenge.star}分</span>
+						    </div>
+						    : ''}
+			            </div> }
+		          	</div>
+		        </div>
+  			)
+  		})
+		return taskArray;
+  	}
+
+  	render() {
+  		const { taskListLoading } = this.props;
+	    return (
+        <div className="page--over" style={{ width: '420px'}}>
+          {/** 增加提示信息 */}
+          <div className="tip-info-wrap">
+            <p className="tip-info">
+              <span><span style={{ color: '#FFBD4C'}}>温馨提示: </span> 若查看答案时间早于关卡任务完成时间,将影响课堂实训作业的成绩。</span>
+            </p>
+          </div>
+
+			    <div className="col-width-3 -scroll" style={{height: 'calc( 100% - 100px )', width: '420px'}} id="all_task_index">
+				    { taskListLoading ?
+				    	<CircularProgress size={40} thickness={3} style={{ marginLeft: 'auto', marginRight: 'auto', marginTop: '40%', display: 'block' }}/> :
+			    		this.renderTasks()
+			    	}
+			  	</div>
+			</div>
+	    );
+  	}
+  	/*
+  		<a className="fr"><i data-tip-down="待完成" className="fa fa-play-circle color-light-green fr font-18 mt5 -text-danger w20_center"></i></a>
+		<div className="-task-list-header clearfix">
+				    	<h3 className="fl">全部任务</h3>
+			    	</div>
+  	*/
+}
+
+export default TaskList;
diff --git a/public/react/src/modules/tpm/NewHeader.js b/public/react/src/modules/tpm/NewHeader.js
index c9dc518b0..376d08221 100644
--- a/public/react/src/modules/tpm/NewHeader.js
+++ b/public/react/src/modules/tpm/NewHeader.js
@@ -698,7 +698,7 @@ submittojoinclass=(value)=>{
 		}
 		document.head.appendChild(link);
 	}
-	
+
 	getAppdata=()=>{
 		let url = "/setting.json";
 		axios.get(url).then((response) => {
@@ -1226,15 +1226,15 @@ submittojoinclass=(value)=>{
                   this.props.Headertop && this.props.Headertop.laboratory_user &&
                   <li><a href="/admins">后台管理</a></li>
                 }
-                
+
                 <li><a href={`/account/profile`}>账号管理</a></li>
                 {/*<li><a onClick={()=>this.educoderlogin()} >登入测试接口</a></li>*/}
                 {/*<li><a onClick={()=>this.trialapplications()} >试用申请</a> </li>*/}
                 {/*<li><Link to={`/interest`}>兴趣页</Link></li>*/}
 
-                <li className="bor-top-greyE">
-                  {/*<a href={this.props.Headertop===undefined?"":this.props.Headertop.logout_url}>退出</a>*/}
-                  {/*<a  onClick={()=>this.educoderlogin()}>退出</a>*/}
+								<li className="bor-top-greyE">
+
+									{/*<a onClick={()=>this.educoderlogin()} >登录</a>*/}
                   <a  onClick={()=>this.educoderloginysl()}>退出</a>
 
                 </li>
diff --git a/public/react/src/modules/user/LoginRegisterComponent.js b/public/react/src/modules/user/LoginRegisterComponent.js
index 9a11ac91a..41954a3fb 100644
--- a/public/react/src/modules/user/LoginRegisterComponent.js
+++ b/public/react/src/modules/user/LoginRegisterComponent.js
@@ -184,7 +184,17 @@ class LoginRegisterComponent extends Component {
             message: "提示",
             description: messge,
             onClick: () => {
-                console.log('Notification Clicked!');
+							console.log('Notification Clicked12312313123!');
+            },
+        });
+    }
+    openNotifications = (btn) => {
+        // type  1 成功提示绿色 2提醒颜色黄色 3错误提示红色
+        notification.open({
+            message: "提示",
+            description: btn,
+            onClick: () => {
+
             },
         });
     }
@@ -369,8 +379,23 @@ class LoginRegisterComponent extends Component {
                     })
 
                     return;
-                }
-                else {
+                } else if (response.data.message === "登录密码出错已达上限,账号已被锁定, 请10分钟后重新登录或找回密码") {
+                    const messge = (
+                      <div>
+                          <p>
+                              登录密码出错已达上限,账号已被锁定;
+                          </p>
+                          <p className="mt10">
+                              请10分钟后重新登录或<a href={'/changepassword'} style={{
+                              textDecoration: "underline",
+                              color: "#4CACFF",
+                          }}>找回密码</a>
+                          </p>
+                      </div>
+                    )
+                    this.openNotifications(messge);
+                    return;
+                } else {
                     this.openNotification(response.data.message);
                     return;
                 }
@@ -1012,7 +1037,9 @@ class LoginRegisterComponent extends Component {
                                  name="username"
                                  className={Phonenumberisnotco && Phonenumberisnotco !== "" ?" color-grey-9 loginInputzhucheyslass bor-reds":" color-grey-9 loginInputzhuche"}
                                  onBlur={(e) => this.inputOnBlur(e, 1)}
-                                 style={{marginTop: '30px', height: '38px'}}></Input>
+                                 style={{marginTop: '30px', height: '38px'}}
+                                 onPressEnter={() => this.postLogin()}
+                          ></Input>
 
                           {
                               Phonenumberisnotco && Phonenumberisnotco != "" ?
@@ -1022,8 +1049,9 @@ class LoginRegisterComponent extends Component {
                                 : <div style={{height:"25px"}}></div>
                           }
 
-                          <Input type="password" name="password" id="password"   value={this.state.password}
+                          <Input type="password" name="password" id="password" value={this.state.password}
                                  onChange={this.passwordonChange}
+                                 onPressEnter={() => this.postLogin()}
                                  className={Phonenumberisnotcodmm && Phonenumberisnotcodmm !== "" ?" color-grey-9 loginInputzhucheyslass bor-reds":" color-grey-9 loginInputzhuche"}
                                  placeholder="密码"></Input>
                           {
diff --git a/public/react/src/modules/user/usersInfo/InfosCourse.js b/public/react/src/modules/user/usersInfo/InfosCourse.js
index 7104edc59..85ae6b9c5 100644
--- a/public/react/src/modules/user/usersInfo/InfosCourse.js
+++ b/public/react/src/modules/user/usersInfo/InfosCourse.js
@@ -200,7 +200,7 @@ class InfosCourse extends Component{
               
               .white-panel li.active {
                   border-radius: 24px;
-                  border: 0px solid #4CACFF;
+                  border: none !important;
                   color: #4CACFF;
               }
               
diff --git a/public/react/src/modules/user/usersInfo/InfosPackage.js b/public/react/src/modules/user/usersInfo/InfosPackage.js
index effbdbc20..ad2c1c0ab 100644
--- a/public/react/src/modules/user/usersInfo/InfosPackage.js
+++ b/public/react/src/modules/user/usersInfo/InfosPackage.js
@@ -190,7 +190,7 @@ class InfosPackage extends Component{
               }
               .white-panel li.active {
                   border-radius: 24px;
-                  border: 0px solid #4CACFF;
+                  border: none !important;
                   color: #4CACFF;
               }
               
diff --git a/public/react/src/modules/user/usersInfo/InfosPath.js b/public/react/src/modules/user/usersInfo/InfosPath.js
index 968d61083..d127ff87b 100644
--- a/public/react/src/modules/user/usersInfo/InfosPath.js
+++ b/public/react/src/modules/user/usersInfo/InfosPath.js
@@ -202,7 +202,7 @@ class InfosPath extends Component{
               
               .white-panel li.active {
                   border-radius: 24px;
-                  border: 0px solid #4CACFF;
+                  border: none !important;
                   color: #4CACFF;
               }
               
diff --git a/public/react/src/modules/user/usersInfo/InfosProject.js b/public/react/src/modules/user/usersInfo/InfosProject.js
index cd2e14265..102a26724 100644
--- a/public/react/src/modules/user/usersInfo/InfosProject.js
+++ b/public/react/src/modules/user/usersInfo/InfosProject.js
@@ -203,7 +203,7 @@ class InfosProject extends Component{
               
               .white-panel li.active {
                   border-radius: 24px;
-                  border: 0px solid #4CACFF;
+                  border: none !important;
                   color: #4CACFF;
               }
               
diff --git a/public/react/src/modules/user/usersInfo/InfosShixun.js b/public/react/src/modules/user/usersInfo/InfosShixun.js
index f6455121e..0a63c14a0 100644
--- a/public/react/src/modules/user/usersInfo/InfosShixun.js
+++ b/public/react/src/modules/user/usersInfo/InfosShixun.js
@@ -203,7 +203,7 @@ class InfosShixun extends Component{
               
               .white-panel li.active {
                   border-radius: 24px;
-                  border: 0px solid #4CACFF;
+                  border: none !important;     
                   color: #4CACFF;
               }
               
diff --git a/public/react/src/modules/user/usersInfo/video/InfosVideo.js b/public/react/src/modules/user/usersInfo/video/InfosVideo.js
index 323706b01..a3678e56e 100644
--- a/public/react/src/modules/user/usersInfo/video/InfosVideo.js
+++ b/public/react/src/modules/user/usersInfo/video/InfosVideo.js
@@ -282,7 +282,7 @@ function InfoVideo (props) {
                 }
                .white-panel li.active {
                   border-radius: 24px;
-                  border: 0px solid #4CACFF;
+                  border: none !important;
                   color: #4CACFF;
               }
                    .whitepanelysllisyt {