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">×</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 {