diff --git a/app/assets/javascripts/admins/courses/index.js b/app/assets/javascripts/admins/courses/index.js
new file mode 100644
index 000000000..336d8c7c9
--- /dev/null
+++ b/app/assets/javascripts/admins/courses/index.js
@@ -0,0 +1,57 @@
+$(document).on('turbolinks:load', function() {
+ if ($('body.admins-courses-index-page').length > 0) {
+ let searchContainer = $(".course-list-form");
+ let searchForm = $("form.search-form",searchContainer);
+
+ searchContainer.on('change', '.course-homepage-show', function(){
+ searchForm.find('input[type="submit"]').trigger('click');
+ });
+
+ //导出
+ searchContainer.on('click', "#course-export", function () {
+ window.location.href = "/admins/courses.xlsx?" + searchForm.serialize();
+ });
+
+ $(".course-list-container").on("change", '.course-setting-form', function () {
+ var s_id = $(this).attr("data-id");
+ var s_value = $(this).val();
+ var s_name = $(this).attr("name");
+ var json = {};
+ json[s_name] = s_value;
+ $.ajax({
+ url: "/admins/courses/" + s_id,
+ type: "PUT",
+ dataType:'script',
+ data: json
+ });
+ });
+
+ // ************** 学校选择 *************
+ searchForm.find('.school-select').select2({
+ theme: 'bootstrap4',
+ placeholder: '请选择单位',
+ 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;
+ }
+ });
+ }
+});
+
diff --git a/app/assets/javascripts/admins/modals/admin-merge-course-list-modal.js b/app/assets/javascripts/admins/modals/admin-merge-course-list-modal.js
new file mode 100644
index 000000000..e08277024
--- /dev/null
+++ b/app/assets/javascripts/admins/modals/admin-merge-course-list-modal.js
@@ -0,0 +1,60 @@
+$(document).on('turbolinks:load', function() {
+ var $modal = $('.modal.admin-merge-course-list-modal');
+ if ($modal.length > 0) {
+ var $form = $modal.find('form.admin-merge-course-list-form');
+ var $originCourseListIdInput = $form.find('input[name="origin_course_list_id"]');
+
+ $form.validate({
+ errorElement: 'span',
+ errorClass: 'danger text-danger',
+ rules: {
+ course_list_name: {
+ required: true
+ }
+ },
+ messages: {
+ course_list_name: {
+ required: '请输入课程名称'
+ }
+ }
+ });
+
+ // modal ready fire
+ $modal.on('show.bs.modal', function (event) {
+ var $link = $(event.relatedTarget);
+
+ var couresListId = $link.data('courseListId');
+ var url = $link.data('url');
+
+ $originCourseListIdInput.val(couresListId);
+ $form.data('url', url);
+ });
+
+ $modal.on('click', '.submit-btn', function(){
+ $form.find('.error').html('');
+
+ if ($form.valid()) {
+ var url = $form.data('url');
+
+ $.ajax({
+ method: 'POST',
+ dataType: 'json',
+ url: url,
+ data: $form.serialize(),
+ success: function(){
+ $.notify({ message: '操作成功' });
+ $modal.modal('hide');
+
+ setTimeout(function(){
+ window.location.reload();
+ }, 500);
+ },
+ error: function(res){
+ var data = res.responseJSON;
+ $form.find('.error').html(data.message);
+ }
+ });
+ }
+ });
+ }
+});
\ No newline at end of file
diff --git a/app/controllers/admins/course_lists_controller.rb b/app/controllers/admins/course_lists_controller.rb
new file mode 100644
index 000000000..83ac92c61
--- /dev/null
+++ b/app/controllers/admins/course_lists_controller.rb
@@ -0,0 +1,35 @@
+class Admins::CourseListsController < Admins::BaseController
+
+ def index
+ course_lists = Admins::CourseListQuery.call(params)
+ @course_lists = paginate course_lists.preload(:courses, :user)
+ @params_page = params[:page] || 1
+ respond_to do |format|
+ format.js
+ format.html
+ end
+ end
+
+ def destroy
+ CourseList.find(params[:id]).destroy!
+
+ render_delete_success
+ end
+
+ def merge
+ origin_course_list = CourseList.find_by!(id: params[:origin_course_list_id])
+ o_courselist = CourseList.find_by(name: params[:course_list_name])
+ if o_courselist
+ origin_course_list.courses.each do |course|
+ course.update!(name: course.name.sub(origin_course_list.name, params[:course_list_name]), course_list_id: o_courselist.id)
+ end
+ origin_course_list.destroy
+ else
+ origin_course_list.courses.each do |course|
+ course.update!(name: course.name.sub(origin_course_list.name, params[:course_list_name]))
+ end
+ origin_course_list.update!(name: params[:course_list_name])
+ end
+ render_ok
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/admins/courses_controller.rb b/app/controllers/admins/courses_controller.rb
index e69de29bb..80f48fab2 100644
--- a/app/controllers/admins/courses_controller.rb
+++ b/app/controllers/admins/courses_controller.rb
@@ -0,0 +1,49 @@
+class Admins::CoursesController < Admins::BaseController
+ before_action :find_course, except: [:index]
+
+ def index
+ default_sort('created_at', 'desc')
+
+ courses = Admins::CourseQuery.call(params)
+ @ended_courses = courses.where(is_end: 1).size
+ @processed_courses = courses.where(is_end: 0).size
+ @courses = paginate courses.includes(:school, :students, :attachments, :homework_commons, teacher: :user_extension)
+
+ respond_to do |format|
+ format.js
+ format.html
+ format.xlsx do
+ @courses = courses.includes(:school, :students, :attachments, :homework_commons, :course_acts, teacher: :user_extension)
+ filename = "课堂列表_#{Time.current.strftime('%Y%m%d%H%M%S')}.xlsx"
+ render xlsx: 'index', filename: filename
+ end
+ end
+ end
+
+ def destroy
+ if @course.is_delete == 0
+ @course.delete!
+ Tiding.create!(user_id: current_user.id, trigger_user_id: current_user.id, container_id: @course.id,
+ container_type: 'DeleteCourse', tiding_type: 'System', belong_container: @course, extra: @course.name)
+ end
+ end
+
+ def update
+ if @course.update_attributes(setting_params)
+ render_ok
+ else
+ redirect_to admins_courses_path
+ flash[:danger] = "更新失败"
+ end
+ end
+
+ private
+
+ def find_course
+ @course = Course.find_by!(id: params[:id])
+ end
+
+ def setting_params
+ params.permit(:homepage_show, :email_notify)
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/admins/laboratory_subjects_controller.rb b/app/controllers/admins/laboratory_subjects_controller.rb
index c09697c7e..827645839 100644
--- a/app/controllers/admins/laboratory_subjects_controller.rb
+++ b/app/controllers/admins/laboratory_subjects_controller.rb
@@ -19,9 +19,9 @@ class Admins::LaboratorySubjectsController < Admins::BaseController
ActiveRecord::Base.transaction do
current_subject = current_laboratory_subject.subject
- current_subject.shixuns.each do |shixun|
- shixun.destroy!
- end
+ # 实训软删除,并解除与子站的关联
+ current_laboratory.laboratory_shixuns.where(shixun_id: current_subject.shixuns).destroy_all
+ current_subject.shixuns.update_all(status: -1)
current_subject.destroy!
render_delete_success
diff --git a/app/controllers/admins/projects_controller.rb b/app/controllers/admins/projects_controller.rb
new file mode 100644
index 000000000..53d94fd9b
--- /dev/null
+++ b/app/controllers/admins/projects_controller.rb
@@ -0,0 +1,25 @@
+class Admins::ProjectsController < Admins::BaseController
+
+ def index
+ default_sort('created_at', 'desc')
+
+ search = params[:search].to_s.strip
+ projects = Project.where("name like ?", "%#{search}%")
+ @projects = paginate projects.includes(:owner, :members, :issues, :versions, :attachments, :project_score)
+ end
+
+ def destroy
+ project = Project.find_by!(id: params[:id])
+ ActiveRecord::Base.transaction do
+ g = Gitlab.client
+ g.delete_project(project.gpid)
+ # 删除Trustie版本库记录
+ repoisitory = Repository.where(project_id: project.id, type: "Repository::Gitlab").first
+ repoisitory.destroy!
+ Tiding.where(container_id: project.id, container_type: ["JoinProject", "DealProject", "ReporterJoinProject", "ManagerJoinProject"]).destroy_all
+ project.destroy!
+ render_delete_success
+ end
+ end
+
+end
\ No newline at end of file
diff --git a/app/controllers/admins/shixun_recycles_controller.rb b/app/controllers/admins/shixun_recycles_controller.rb
new file mode 100644
index 000000000..5a0582d4e
--- /dev/null
+++ b/app/controllers/admins/shixun_recycles_controller.rb
@@ -0,0 +1,22 @@
+class Admins::ShixunRecyclesController < Admins::BaseController
+
+ def index
+ sort_by = params[:sort_by].presence || 'created_at'
+ sort_direction = params[:sort_direction].presence || 'desc'
+ search = params[:search].to_s.strip
+ shixuns = Shixun.where(status: -1).where("name like ?", "%#{search}%").order("#{sort_by} #{sort_direction}")
+ @shixuns = paginate shixuns.preload(:user, :laboratory)
+ end
+
+ def destroy
+ Shixun.find(params[:id]).destroy!
+
+ render_delete_success
+ end
+
+ def resume
+ Shixun.find(params[:id]).update!(status: 0)
+ render_delete_success
+ end
+
+end
diff --git a/app/controllers/admins/shixuns_controller.rb b/app/controllers/admins/shixuns_controller.rb
index a4aa8a044..e2d2830ad 100644
--- a/app/controllers/admins/shixuns_controller.rb
+++ b/app/controllers/admins/shixuns_controller.rb
@@ -1,7 +1,7 @@
class Admins::ShixunsController < Admins::BaseController
def index
- params[:sort_by] = params[:sort_by].presence || 'created_on'
+ params[:sort_by] = params[:sort_by].presence || 'created_at'
params[:sort_direction] = params[:sort_direction].presence || 'desc'
shixuns = Admins::ShixunQuery.call(params)
@editing_shixuns = shixuns.where(status:0).size
@@ -23,7 +23,7 @@ class Admins::ShixunsController < Admins::BaseController
end
def destroy
- Shixun.find(params[:id]).destroy!
+ Shixun.find(params[:id]).update!(status: -1)
render_delete_success
end
diff --git a/app/controllers/files_controller.rb b/app/controllers/files_controller.rb
index 117475894..0299fdf7d 100644
--- a/app/controllers/files_controller.rb
+++ b/app/controllers/files_controller.rb
@@ -24,26 +24,53 @@ class FilesController < ApplicationController
get_category(@course, @course_second_category_id)
@total_count = @attachments.size
- @publish_count = @attachments.published.size
- @unpublish_count = @total_count - @publish_count
- @attachments = @attachments.by_keywords(params[:search])
- @attachments =
- case @user.course_identity(@course)
- when 5
- @attachments.published
- when 6, 7
- @attachments.publiced.published
+ if @user.course_identity(@course) == 5
+ member = @course.course_members.find_by(user_id: current_user.id, is_active: 1)
+ if member.try(:course_group_id).to_i == 0
+ @attachments = @attachments.published.unified_setting
else
- @attachments
+ not_atta_ids = @course.attachment_group_settings.none_published.where("course_group_id = #{member.try(:course_group_id)}").pluck(:attachment_id)
+
+ @attachments = @attachments.where.not(id: not_atta_ids).published
end
+ elsif @user.course_identity(@course) > 5
+ @attachments = @attachments.publiced.published
+ end
+
+ @publish_count = @attachments.published.size
+ @unpublish_count = @total_count - @publish_count
+ @attachments = @attachments.by_keywords(params[:search])
@attachments = @attachments.page(@page).per(@page_size)
end
def bulk_publish
return normal_status(403, "您没有权限进行操作") if current_user.course_identity(@course) >= 5
- @course.attachments.by_ids(@attachment_ids).unpublish.update_all(is_publish: 1, publish_time: Time.now)
+ tip_exception("请至少选择一个分班") if params[:group_ids].blank? && @course.course_groups.size != 0
+
+ attachments = @course.attachments.by_ids(@attachment_ids)
+
+ ActiveRecord::Base.transaction do
+ # 有分班设置时
+ if @course.course_group_module? && @course.course_groups_count != 0 && params[:group_ids]
+ group_ids = params[:group_ids]&.reject(&:blank?)
+ charge_group_ids = @course.charge_group_ids(current_user)
+ publish_groups = charge_group_ids & group_ids if group_ids
+
+ attachments.each do |atta|
+ if atta.published? && !atta.unified_setting || !atta.published?
+ create_atta_group_settings atta
+ atta.update_all(unified_setting: 0) if atta.unified_setting
+ none_publish_settings = atta.attachment_group_settings.where(course_group_id: publish_groups).none_published
+ none_publish_settings.update_all(publish_time: Time.now)
+ end
+ end
+ end
+
+ # 未发布的资源更新状态
+ attachments.where(is_publish: 0).update_all(is_publish: 1, publish_time: Time.now)
+ end
render_ok
end
@@ -153,6 +180,10 @@ class FilesController < ApplicationController
attachment.is_publish = @atta_is_publish
attachment.delay_publish = @atta_delay_publish
attachment.publish_time = @atta_publish_time
+ attachment.unified_setting = @unified_setting
+ if @unified_setting == 0
+ attachment_group_setting attachment, params[:group_settings]
+ end
# attachment.set_publish_time(publish_time) if is_unified_setting
# attachment.set_course_group_publish_time(@course, course_group_publish_times) if @course.course_groups.size > 0 && !is_unified_setting && publish_time.blank?
attachment.save!
@@ -195,6 +226,10 @@ class FilesController < ApplicationController
attach_copied_obj.is_publish = @atta_is_publish
attach_copied_obj.delay_publish = @atta_delay_publish
attach_copied_obj.publish_time = @atta_publish_time
+ attach_copied_obj.unified_setting = @unified_setting
+ if @unified_setting == 0
+ attachment_group_setting attach_copied_obj, params[:group_settings]
+ end
attach_copied_obj.course_second_category_id = course_second_category_id
attach_copied_obj.copy_from = ori.copy_from.nil? ? ori.id : ori.copy_from
if attach_copied_obj.attachtype == nil
@@ -234,6 +269,12 @@ class FilesController < ApplicationController
@old_attachment.is_publish = @atta_is_publish
@old_attachment.delay_publish = @atta_delay_publish
@old_attachment.publish_time = @atta_publish_time
+ @old_attachment.unified_setting = @unified_setting
+ if @unified_setting == 0
+ attachment_group_setting @old_attachment, params[:group_settings]
+ else
+ @old_attachment.attachment_group_settings.destroy_all
+ end
if params[:description] && !params[:description].strip.blank? && params[:description] != @old_attachment.description
@old_attachment.description = params[:description]
@@ -319,9 +360,40 @@ class FilesController < ApplicationController
def publish_params
tip_exception("缺少发布参数") if params[:delay_publish].blank?
- tip_exception("缺少延期发布的时间参数") if params[:delay_publish].to_i == 1 && params[:publish_time].blank?
- @atta_is_publish = params[:delay_publish].to_i == 1 && params[:publish_time].to_time > Time.now ? 0 : 1
+ @unified_setting = 1
+ if params[:delay_publish].to_i == 1 && @course.course_group_module? && @course.course_groups_count != 0
+ tip_exception("分班发布设置不能为空") if params[:group_settings].blank?
+ min_publish_time = params[:group_settings].pluck(:publish_time).reject(&:blank?).min
+ max_publish_time = params[:group_settings].pluck(:publish_time).reject(&:blank?).max
+ tip_exception("分班发布设置不能为空") if min_publish_time.blank?
+
+ # 分班设置中的时间一样且包含所有分班 则按统一设置处理,否则是非统一设置
+ @unified_setting = 0 unless min_publish_time == max_publish_time && params[:group_settings].pluck(:group_id).flatten.sort == @course.course_groups.pluck(:id).sort
+ elsif params[:delay_publish].to_i == 1
+ tip_exception("缺少延期发布的时间参数") if params[:publish_time].blank?
+ min_publish_time = params[:publish_time]
+ end
+ @atta_is_publish = params[:delay_publish].to_i == 1 && min_publish_time.to_time > Time.now ? 0 : 1
@atta_delay_publish = params[:delay_publish].to_i
- @atta_publish_time = params[:delay_publish].to_i == 1 && params[:publish_time] ? params[:publish_time] : Time.now
+ @atta_publish_time = params[:delay_publish].to_i == 1 ? min_publish_time : Time.now
+ end
+
+ def create_atta_group_settings atta
+ if atta.attachment_group_settings.size != @course.course_groups.size
+ @course.course_groups.where.not(id: atta.attachment_group_settings.pluck(:course_group_id)).each do |group|
+ atta.attachment_group_settings << AttachmentGroupSetting.new(course_group_id: group.id, course_id: @course.id,
+ publish_time: atta.publish_time)
+ end
+ end
+ end
+
+ def attachment_group_setting attachment, group_setting
+ create_atta_group_settings attachment
+ group_setting.each do |setting|
+ tip_exception("分班id不能为空") if setting[:group_id].length == 0
+ tip_exception("发布时间不能为空") if setting[:publish_time].blank?
+ AttachmentGroupSetting.where(attachment_id: attachment.id, course_group_id: setting[:group_id]).
+ update_all(publish_time: setting[:publish_time])
+ end
end
end
diff --git a/app/controllers/homework_commons_controller.rb b/app/controllers/homework_commons_controller.rb
index 3a3d61e6d..a94eed83f 100644
--- a/app/controllers/homework_commons_controller.rb
+++ b/app/controllers/homework_commons_controller.rb
@@ -13,7 +13,8 @@ class HomeworkCommonsController < ApplicationController
:reference_answer, :publish_groups, :end_groups, :alter_name, :update_explanation,
:update_score, :update_student_score]
before_action :user_course_identity
- before_action :homework_publish, only: [:show, :works_list, :code_review_results, :show_comment, :settings, :reference_answer, :update_student_score]
+ before_action :homework_publish, only: [:show, :works_list, :code_review_results, :show_comment, :settings, :reference_answer,
+ :update_student_score]
before_action :teacher_allowed, only: [:new, :edit, :create, :update, :shixuns, :subjects, :create_shixun_homework,
:publish_homework, :end_homework, :set_public, :choose_category, :move_to_category,
:choose_category, :create_subject_homework, :multi_destroy, :group_list, :homework_code_repeat,
diff --git a/app/models/attachment.rb b/app/models/attachment.rb
index 8b7034ab9..37884e40b 100644
--- a/app/models/attachment.rb
+++ b/app/models/attachment.rb
@@ -23,6 +23,7 @@ class Attachment < ApplicationRecord
scope :mine, -> (author_id) { where(author_id: author_id) }
scope :simple_columns, -> { select(:id, :filename, :filesize, :created_on, :cloud_url, :author_id, :content_type) }
scope :search_by_container, -> (ids) {where(container_id: ids)}
+ scope :unified_setting, -> {where("unified_setting = ? ", 1)}
validates_length_of :description, maximum: 100
diff --git a/app/models/attachment_group_setting.rb b/app/models/attachment_group_setting.rb
index 67240d88f..3fefe5ceb 100644
--- a/app/models/attachment_group_setting.rb
+++ b/app/models/attachment_group_setting.rb
@@ -3,4 +3,6 @@ class AttachmentGroupSetting < ActiveRecord::Base
belongs_to :course_group
belongs_to :course
+ scope :none_published, -> {where("attachment_group_settings.publish_time IS NULL OR attachment_group_settings.publish_time > ?", Time.now)}
+
end
diff --git a/app/models/course.rb b/app/models/course.rb
index 9e252b02f..2f561bba7 100644
--- a/app/models/course.rb
+++ b/app/models/course.rb
@@ -31,6 +31,7 @@ class Course < ApplicationRecord
has_many :graduation_groups, dependent: :destroy
has_many :course_members, dependent: :destroy
+ has_many :students, -> { course_students }, class_name: 'CourseMember'
has_many :teacher_course_members, -> { teachers_and_admin }, class_name: 'CourseMember'
has_many :teacher_users, through: :teacher_course_members, source: :user
has_many :course_messages, dependent: :destroy
@@ -114,6 +115,10 @@ class Course < ApplicationRecord
course_members.where(user_id: user_id, role: role).exists?
end
+ def course_group_module?
+ course_modules.exists?(module_type: "course_group", hidden: 0)
+ end
+
# 作业对应的子目录/父目录名称
def category_info type
course_module = course_modules.find_by(module_type: type)
@@ -234,11 +239,6 @@ class Course < ApplicationRecord
course_members.where(role: %i[CREATOR PROFESSOR])
end
- # 课堂学生
- def students
- course_members.where(role: %i[STUDENT])
- end
-
# 更新课程的访问人数
def update_visits(new_visits)
update_attributes(visits: new_visits)
@@ -367,6 +367,23 @@ class Course < ApplicationRecord
count = course_challeng_count == 0 ? 0 : ((my_challenge_count.to_f / course_challeng_count).round(2) * 100).to_i
end
+ # 课堂实训作业的评测次数
+ def evaluate_count
+ course_user_ids = students.pluck(:user_id)
+ shixun_ids = homework_commons.joins(:homework_commons_shixun).where(homework_type: 4).pluck(:shixun_id)
+ return 0 if shixun_ids.blank?
+ Game.joins(:challenge).where(challenges: {shixun_id: shixun_ids}, games: {user_id: course_user_ids}).sum(:evaluate_count)
+ end
+
+ def max_activity_time
+ course_acts.pluck(:updated_at).max
+ end
+
+ # 课堂作业数
+ def course_homework_count type
+ homework_commons.select{|homework| homework.homework_type == type}.size
+ end
+
private
#创建课程后,给该用户发送消息
diff --git a/app/models/course_list.rb b/app/models/course_list.rb
index 080c05ae2..cd622f20a 100644
--- a/app/models/course_list.rb
+++ b/app/models/course_list.rb
@@ -5,4 +5,5 @@ class CourseList < ApplicationRecord
has_many :exercise_banks
has_many :gtask_banks
has_many :gtopic_banks
+ belongs_to :user
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 2e0a8be64..af59f58de 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -7,6 +7,9 @@ class Project < ApplicationRecord
has_many :issues
has_many :user_grades, dependent: :destroy
+ has_many :attachments, as: :container, dependent: :destroy
+ has_one :project_score, dependent: :destroy
+ has_many :versions, -> { order("versions.effective_date DESC, versions.name DESC") }, dependent: :destroy
after_create do
SyncTrustieJob.perform_later("project", 1) if allow_sync_to_trustie?
diff --git a/app/models/shixun.rb b/app/models/shixun.rb
index e8aa1b186..0f9842739 100644
--- a/app/models/shixun.rb
+++ b/app/models/shixun.rb
@@ -53,6 +53,7 @@ class Shixun < ApplicationRecord
has_many :shixun_reviews, -> {order("shixun_reviews.created_at desc")}, :dependent => :destroy
has_many :laboratory_shixuns, dependent: :destroy
+ belongs_to :laboratory, optional: true
scope :search_by_name, ->(keyword) { where("name like ? or description like ? ",
"%#{keyword}%", "%#{keyword}%") }
diff --git a/app/models/version.rb b/app/models/version.rb
new file mode 100644
index 000000000..c278ff0a9
--- /dev/null
+++ b/app/models/version.rb
@@ -0,0 +1,3 @@
+class Version < ApplicationRecord
+ belongs_to :project
+end
diff --git a/app/queries/admins/course_list_query.rb b/app/queries/admins/course_list_query.rb
new file mode 100644
index 000000000..24eedaf1b
--- /dev/null
+++ b/app/queries/admins/course_list_query.rb
@@ -0,0 +1,30 @@
+class Admins::CourseListQuery < ApplicationQuery
+ include CustomSortable
+
+ attr_reader :params
+
+ sort_columns :created_at, default_by: :created_at, default_direction: :desc
+
+ def initialize(params)
+ @params = params
+ end
+
+ def call
+ course_lists = CourseList.all
+
+ # 关键字模糊查询
+ keyword = params[:keyword].to_s.strip
+ if keyword.present?
+ search_type = params[:search_type] || "0"
+ case search_type
+ when "0"
+ course_lists = course_lists.joins(:user)
+ .where('CONCAT(lastname, firstname) like :keyword', keyword: "%#{keyword}%")
+ when "1"
+ course_lists = course_lists.where('name like :keyword', keyword: "%#{keyword}%")
+ end
+ end
+
+ custom_sort(course_lists, params[:sort_by], params[:sort_direction])
+ end
+end
\ No newline at end of file
diff --git a/app/queries/admins/course_query.rb b/app/queries/admins/course_query.rb
new file mode 100644
index 000000000..e883650d0
--- /dev/null
+++ b/app/queries/admins/course_query.rb
@@ -0,0 +1,44 @@
+class Admins::CourseQuery < ApplicationQuery
+ include CustomSortable
+
+ attr_reader :params
+
+ sort_columns :created_at, default_by: :created_at, default_direction: :desc, default_table: 'courses'
+
+ def initialize(params)
+ @params = params
+ end
+
+ def call
+ courses = Course.all
+
+ courses = courses.where(id: params[:id]) if params[:id].present?
+
+ # 状态过滤
+ status =
+ case params[:status].to_s.strip
+ when 'processing' then 0
+ when 'ended' then 1
+ end
+ courses = courses.where(is_end: status) if status
+
+ # 单位
+ if params[:school_id].present?
+ courses = courses.where(school_id: params[:school_id])
+ end
+
+ # 首页展示
+ if params[:homepage_show].present? && params[:homepage_show].to_s == 'true'
+ courses = courses.where(homepage_show: true)
+ end
+
+ # 关键字
+ keyword = params[:keyword].to_s.strip
+ if keyword
+ sql = 'CONCAT(lastname, firstname) LIKE :keyword OR courses.name LIKE :keyword OR course_lists.name LIKE :keyword'
+ courses = courses.joins(:teacher, :course_list).where(sql, keyword: "%#{keyword}%")
+ end
+
+ custom_sort(courses, params[:sort_by], params[:sort_direction])
+ end
+end
\ No newline at end of file
diff --git a/app/views/admins/course_lists/index.html.erb b/app/views/admins/course_lists/index.html.erb
new file mode 100644
index 000000000..cd814ed8a
--- /dev/null
+++ b/app/views/admins/course_lists/index.html.erb
@@ -0,0 +1,22 @@
+<% define_admin_breadcrumbs do %>
+ <% add_admin_breadcrumb('课程列表') %>
+<% end %>
+
+
+
+
+ <%= render partial: 'admins/course_lists/shared/list', locals: { courses: @course_lists } %>
+
+
+<%= render 'admins/course_lists/shared/merge_course_list_modal' %>
\ No newline at end of file
diff --git a/app/views/admins/course_lists/index.js.erb b/app/views/admins/course_lists/index.js.erb
new file mode 100644
index 000000000..e4bfead7d
--- /dev/null
+++ b/app/views/admins/course_lists/index.js.erb
@@ -0,0 +1 @@
+$(".course-list-list-container").html("<%= j render partial: 'admins/course_lists/shared/list', locals: { courses: @course_lists }%>");
\ No newline at end of file
diff --git a/app/views/admins/course_lists/shared/_list.html.erb b/app/views/admins/course_lists/shared/_list.html.erb
new file mode 100644
index 000000000..228385b3e
--- /dev/null
+++ b/app/views/admins/course_lists/shared/_list.html.erb
@@ -0,0 +1,37 @@
+
+
+ 序号
+ ID
+ 课程名称
+ 课堂数
+ 创建者
+ <%= sort_tag('创建时间', name: 'created_at', path: admins_course_lists_path) %>
+ 操作
+
+
+ <% if courses.present? %>
+ <% courses.each_with_index do |course_list,index| %>
+
+ <%= list_index_no(@params_page.to_i, index) %>
+ <%= course_list.id %>
+ <%= course_list.name %>
+ <% course_count = course_list.courses.size %>
+ <%= course_count %>
+ <%= link_to course_list.user.try(:real_name),"/users/#{course_list.user.try(:login)}",target:'_blank' %>
+ <%= format_time course_list.created_at %>
+
+ <% if course_count == 0 %>
+ <%= delete_link '删除', admins_course_list_path(course_list, element: ".course-list-item-#{course_list.id}"), class: 'delete-department-action' %>
+ <% end %>
+ <%= javascript_void_link '修改', class: 'action', data: { course_list_id: course_list.id,
+ toggle: 'modal', target: '.admin-merge-course-list-modal', url: merge_admins_course_lists_path } %>
+
+
+ <% end %>
+ <% else %>
+ <%= render 'admins/shared/no_data_for_table' %>
+ <% end %>
+
+
+
+<%= render partial: 'admins/shared/paginate', locals: { objects: courses } %>
\ No newline at end of file
diff --git a/app/views/admins/course_lists/shared/_merge_course_list_modal.html.erb b/app/views/admins/course_lists/shared/_merge_course_list_modal.html.erb
new file mode 100644
index 000000000..4858f5372
--- /dev/null
+++ b/app/views/admins/course_lists/shared/_merge_course_list_modal.html.erb
@@ -0,0 +1,29 @@
+
\ No newline at end of file
diff --git a/app/views/admins/courses/destroy.js.erb b/app/views/admins/courses/destroy.js.erb
new file mode 100644
index 000000000..811038193
--- /dev/null
+++ b/app/views/admins/courses/destroy.js.erb
@@ -0,0 +1,2 @@
+alert("删除成功");
+$(".course-item-<%= @course.id %>").find(".delete-course-action").remove();
\ No newline at end of file
diff --git a/app/views/admins/courses/index.html.erb b/app/views/admins/courses/index.html.erb
new file mode 100644
index 000000000..84ea98223
--- /dev/null
+++ b/app/views/admins/courses/index.html.erb
@@ -0,0 +1,34 @@
+<% define_admin_breadcrumbs do %>
+ <% add_admin_breadcrumb('课堂列表') %>
+<% end %>
+
+
+
+
+ <%= render partial: 'admins/courses/shared/list', locals: { courses: @courses } %>
+
\ No newline at end of file
diff --git a/app/views/admins/courses/index.js.erb b/app/views/admins/courses/index.js.erb
new file mode 100644
index 000000000..7073c2a81
--- /dev/null
+++ b/app/views/admins/courses/index.js.erb
@@ -0,0 +1 @@
+$('.course-list-container').html("<%= j( render partial: 'admins/courses/shared/list', locals: { courses: @courses } ) %>");
\ No newline at end of file
diff --git a/app/views/admins/courses/index.xlsx.axlsx b/app/views/admins/courses/index.xlsx.axlsx
new file mode 100644
index 000000000..7cab54482
--- /dev/null
+++ b/app/views/admins/courses/index.xlsx.axlsx
@@ -0,0 +1,29 @@
+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: "课堂列表") do |sheet|
+ sheet.add_row %w(ID 课堂名称 成员 资源 普通作业 分组作业 实训作业 试卷 评测次数 私有 状态 单位 创建者 创建时间 动态时间), :height => 25,:style => blue_cell
+
+ @courses.each do |course|
+ data = [
+ course.id,
+ course.name,
+ course.course_members_count,
+ get_attachment_count(course, 0),
+ course.course_homework_count(1),
+ course.course_homework_count(3),
+ course.course_homework_count(4),
+ course.exercises_count,
+ course.evaluate_count,
+ course.is_public == 1 ? "--" : "√",
+ course.is_end ? "已结束" : "正在进行",
+ course.school&.name,
+ course.teacher&.real_name,
+ course.created_at&.strftime('%Y-%m-%d %H:%M'),
+ course.max_activity_time ? course.max_activity_time&.strftime('%Y-%m-%d %H:%M') : "--"
+ ]
+ sheet.add_row(data)
+ end
+ end
+end
diff --git a/app/views/admins/courses/shared/_list.html.erb b/app/views/admins/courses/shared/_list.html.erb
new file mode 100644
index 000000000..31661d8c5
--- /dev/null
+++ b/app/views/admins/courses/shared/_list.html.erb
@@ -0,0 +1,62 @@
+
+
+
+ ID
+ 课堂名称
+ 成员
+ 资源
+ 普通作业
+ 分组作业
+ 实训作业
+ 试卷
+ 评测次数
+ 私有
+ 状态
+ 单位
+ 创建者
+ <%= sort_tag('创建时间', name: 'created_at', path: admins_courses_path) %>
+ 首页
+ 邮件通知
+ 操作
+
+
+
+ <% if courses.present? %>
+ <% courses.each do |course| %>
+
+ <%= course.id %>
+
+ <%= link_to(course.name, "/courses/#{course.id}", target: '_blank') %>
+
+ <%= course.course_members_count %>
+ <%= get_attachment_count(course, 0) %>
+ <%= course.course_homework_count(1) %>
+ <%= course.course_homework_count(3) %>
+ <%= course.course_homework_count(4) %>
+ <%= course.exercises_count %>
+ <%= course.evaluate_count %>
+ <%= course.is_public == 1 ? "--" : "√" %>
+ <%= course.is_end ? "已结束" : "正在进行" %>
+ <%= course.school&.name %>
+ <%= course.teacher&.real_name %>
+ <%= course.created_at&.strftime('%Y-%m-%d %H:%M') %>
+
+ <%= check_box_tag :homepage_show,!course.homepage_show,course.homepage_show,remote:true,data:{id:course.id},class:"course-setting-form" %>
+
+
+ <%= check_box_tag :email_notify,!course.email_notify,course.email_notify,remote:true,data:{id:course.id},class:"course-setting-form" %>
+
+
+ <% if course.is_delete == 0 %>
+ <%= delete_link '删除', admins_course_path(course, element: ".course-item-#{course.id}"), class: 'delete-course-action' %>
+ <% end %>
+
+
+ <% end %>
+ <% else %>
+ <%= render 'admins/shared/no_data_for_table' %>
+ <% end %>
+
+
+
+<%= render partial: 'admins/shared/paginate', locals: { objects: courses } %>
\ No newline at end of file
diff --git a/app/views/admins/laboratory_subjects/shared/_add_laboratory_subject_modal.html.erb b/app/views/admins/laboratory_subjects/shared/_add_laboratory_subject_modal.html.erb
index a6fb89186..63ab3fdf1 100644
--- a/app/views/admins/laboratory_subjects/shared/_add_laboratory_subject_modal.html.erb
+++ b/app/views/admins/laboratory_subjects/shared/_add_laboratory_subject_modal.html.erb
@@ -21,7 +21,7 @@
diff --git a/app/views/admins/projects/destroy.js.erb b/app/views/admins/projects/destroy.js.erb
new file mode 100644
index 000000000..811038193
--- /dev/null
+++ b/app/views/admins/projects/destroy.js.erb
@@ -0,0 +1,2 @@
+alert("删除成功");
+$(".course-item-<%= @course.id %>").find(".delete-course-action").remove();
\ No newline at end of file
diff --git a/app/views/admins/projects/index.html.erb b/app/views/admins/projects/index.html.erb
new file mode 100644
index 000000000..c2cdd3946
--- /dev/null
+++ b/app/views/admins/projects/index.html.erb
@@ -0,0 +1,15 @@
+<% define_admin_breadcrumbs do %>
+ <% add_admin_breadcrumb('项目列表') %>
+<% end %>
+
+
+ <%= form_tag(admins_projects_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %>
+ <%= text_field_tag(:search, params[:search], class: 'form-control col-12 col-md-2 mr-3', placeholder: '项目名称检索') %>
+ <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
+
+ <% end %>
+
+
+
+ <%= render partial: 'admins/projects/shared/list', locals: { projects: @projects } %>
+
\ No newline at end of file
diff --git a/app/views/admins/projects/index.js.erb b/app/views/admins/projects/index.js.erb
new file mode 100644
index 000000000..ceaabb423
--- /dev/null
+++ b/app/views/admins/projects/index.js.erb
@@ -0,0 +1 @@
+$('.project-list-container').html("<%= j( render partial: 'admins/projects/shared/list', locals: { projects: @projects } ) %>");
\ No newline at end of file
diff --git a/app/views/admins/projects/shared/_list.html.erb b/app/views/admins/projects/shared/_list.html.erb
new file mode 100644
index 000000000..8a84a72d0
--- /dev/null
+++ b/app/views/admins/projects/shared/_list.html.erb
@@ -0,0 +1,48 @@
+
+
+
+ ID
+ 项目名称
+ 公开
+ issue
+ 资源
+ 版本库
+ PullRequest
+ 里程碑
+ 成员
+ 管理员
+ <%= sort_tag('创建时间', name: 'created_at', path: admins_projects_path) %>
+ 操作
+
+
+
+ <% if projects.present? %>
+ <% projects.each do |project| %>
+
+ <%= project.id %>
+
+ <%= link_to(project.name, "/projects/#{project.id}", target: '_blank') %>
+
+ <%= project.is_public ? '√' : '' %>
+ <%= project.issues.size %>
+ <%= project.attachments.size %>
+ <%= project.project_score.try(:changeset_num).to_i %>
+ <%= project.project_score.try(:pull_request_num).to_i %>
+ <%= project.versions.size %>
+ <%= project.members.size %>
+
+ <%= project.owner ? link_to(project.owner&.real_name, "/users/#{project.owner&.login}", target: '_blank') : "" %>
+
+ <%= project.created_on&.strftime('%Y-%m-%d %H:%M') %>
+
+ <%= delete_link '删除', admins_project_path(project, element: ".project-item-#{project.id}"), class: 'delete-project-action' %>
+
+
+ <% end %>
+ <% else %>
+ <%= render 'admins/shared/no_data_for_table' %>
+ <% end %>
+
+
+
+<%= render partial: 'admins/shared/paginate', locals: { objects: projects } %>
\ No newline at end of file
diff --git a/app/views/admins/shared/_sidebar.html.erb b/app/views/admins/shared/_sidebar.html.erb
index 7b1f5db7a..df344fd1a 100644
--- a/app/views/admins/shared/_sidebar.html.erb
+++ b/app/views/admins/shared/_sidebar.html.erb
@@ -27,13 +27,23 @@
<%= sidebar_item(admins_shixun_settings_path, '实训配置', icon: 'cog', controller: 'admins-shixun_settings') %>
<%= sidebar_item(admins_mirror_repositories_path, '镜像管理', icon: 'cubes', controller: 'admins-mirror_repositories') %>
<%= sidebar_item(admins_myshixuns_path, '学员实训列表', icon: 'server', controller: 'admins-myshixuns') %>
- <% end %>
+ <%= sidebar_item(admins_shixun_recycles_path, '实训回收站', icon: 'recycle', controller: 'admins-myshixuns') %>
+ <% end %>
<%= sidebar_item_group('#subject-submenu', '实践课程', icon: 'th-list') do %>
- <%= sidebar_item(admins_subjects_path, '课程列表', icon: 'cog', controller: 'admins-subjects') %>
- <% end %>
+ <%= sidebar_item(admins_subjects_path, '课程列表', icon: 'cog', controller: 'admins-subjects') %>
+ <% end %>
+
+
+
+ <%= sidebar_item_group('#course-submenu', '课堂管理', icon: 'book') do %>
+ <%= sidebar_item(admins_course_lists_path, '课程列表', icon: 'list', controller: 'admins-course_lists') %>
+ <%= sidebar_item(admins_courses_path, '课堂列表', icon: 'clone', controller: 'admins-courses') %>
+
+ <%= sidebar_item(admins_projects_path, '项目列表', icon: 'database', controller: 'admins-projects') %>
+ <% end %>
diff --git a/app/views/admins/shixun_recycles/index.html.erb b/app/views/admins/shixun_recycles/index.html.erb
new file mode 100644
index 000000000..eb6947a06
--- /dev/null
+++ b/app/views/admins/shixun_recycles/index.html.erb
@@ -0,0 +1,15 @@
+<% define_admin_breadcrumbs do %>
+ <% add_admin_breadcrumb('实训回收站') %>
+<% end %>
+
+
+ <%= form_tag(admins_shixun_recycles_path, method: :get, class: 'form-inline search-form',id:"shixun-recycles-search-form",remote:true) do %>
+ <%= text_field_tag(:search, params[:search], class: 'form-control col-sm-2 ml-3', placeholder: '输入名称关键字搜索') %>
+ <%= submit_tag('搜索', class: 'btn btn-primary ml-3','data-disable-with': '搜索中...') %>
+ <%= link_to "清除",admins_shixun_recycles_path,class: "btn btn-default",id:"shixun-recycles-clear-search",'data-disable-with': '清除中...' %>
+ <% end %>
+
+
+
+ <%= render partial: 'admins/shixun_recycles/shared/list', locals: { shixuns: @shixuns } %>
+
diff --git a/app/views/admins/shixun_recycles/index.js.erb b/app/views/admins/shixun_recycles/index.js.erb
new file mode 100644
index 000000000..9255dde34
--- /dev/null
+++ b/app/views/admins/shixun_recycles/index.js.erb
@@ -0,0 +1 @@
+$(".shixun-recycles-list-container").html("<%= j render partial: "admins/shixun_recycles/shared/list",locals: {shixuns: @shixuns} %>")
\ No newline at end of file
diff --git a/app/views/admins/shixun_recycles/shared/_list.html.erb b/app/views/admins/shixun_recycles/shared/_list.html.erb
new file mode 100644
index 000000000..60d2742e9
--- /dev/null
+++ b/app/views/admins/shixun_recycles/shared/_list.html.erb
@@ -0,0 +1,31 @@
+
+
+ ID
+ 实训名称
+ 子站源
+ 创建者
+ <%= sort_tag('创建于', name: 'created_at', path: admins_shixun_recycles_path) %>
+ 操作
+
+
+ <% if shixuns.present? %>
+ <% shixuns.each do |shixun| %>
+
+ <%= shixun.identifier %>
+ <%= link_to overflow_hidden_span(shixun.name), "/shixuns/#{shixun.identifier}", :target => "_blank", :title => shixun.name %>
+ <%= shixun.laboratory&.school&.name %>
+ <%= link_to shixun.user.try(:real_name),"/users/#{shixun.user.try(:login)}",target:'_blank' %>
+ <%= format_time shixun.created_at %>
+
+ <%= delete_link '删除', admins_shixun_recycle_path(shixun, element: ".shixun-recycle-item-#{shixun.id}"), class: 'delete-shixun-recyle-action' %>
+ <%= link_to('恢复', resume_admins_shixun_recycle_path(shixun), :method => :post, :remote => true, :data => { confirm: "您确定要恢复吗?" } ) %>
+
+
+ <% end %>
+ <% else %>
+ <%= render 'admins/shared/no_data_for_table' %>
+ <% end %>
+
+
+
+<%= render partial: 'admins/shared/paginate', locals: { objects: shixuns } %>
\ No newline at end of file
diff --git a/app/views/admins/shixuns/shared/_list.html.erb b/app/views/admins/shixuns/shared/_list.html.erb
index 7e96eb0fa..7503d8fd2 100644
--- a/app/views/admins/shixuns/shared/_list.html.erb
+++ b/app/views/admins/shixuns/shared/_list.html.erb
@@ -9,14 +9,14 @@
选择
状态
创建者
- <%= sort_tag('创建于', name: 'created_on', path: admins_shixuns_path) %>
+ <%= sort_tag('创建于', name: 'created_at', path: admins_shixuns_path) %>
单测
操作
<% if shixuns.present? %>
<% shixuns.each_with_index do |shixun,index| %>
-
+
<%= list_index_no(@params_page.to_i, index) %>
<%= shixun.identifier %>
<%= link_to overflow_hidden_span(shixun.name), "/shixuns/#{shixun.identifier}", :target => "_blank", :title => shixun.name %>
@@ -33,7 +33,7 @@
<%= shixun.challenges.where(:st => 0).size %>
<%= shixun.challenges.where(:st => 1).size %>
<%= shixun_authentication_status shixun %>
- <%= link_to shixun.owner.try(:real_name),"/users/#{shixun.owner.try(:login)}",target:'_blank' %>
+ <%= link_to shixun.user.try(:real_name),"/users/#{shixun.user.try(:login)}",target:'_blank' %>
<%= format_time shixun.created_at %>
class="ml-3 mr5 magic-checkbox" id="join_teacher_homepage_<%= shixun.id %>">
@@ -41,7 +41,7 @@
<% if shixun.status == 0 %>
- <%= link_to(l(:button_delete), admins_shixun_path(shixun), :method => :delete, :data => { confirm: "您确定要删除吗?" } ) %>
+ <%= delete_link l(:button_delete), admins_shixun_path(shixun, element: ".shixun-item-#{shixun.id}"), class: 'delete-shixun-action' %>
<% end %>
diff --git a/app/views/files/show.json.jbuilder b/app/views/files/show.json.jbuilder
index 71359ccfd..941e66619 100644
--- a/app/views/files/show.json.jbuilder
+++ b/app/views/files/show.json.jbuilder
@@ -1,3 +1,3 @@
json.partial! 'attachments/attachment', attachment: @file
-# json.partial! "files/course_groups", attachment_group_settings: @file.attachment_group_settings
+json.partial! "files/course_groups", attachment_group_settings: @file.attachment_group_settings
json.partial! "attachment_histories/list", attachment_histories: @attachment_histories
\ No newline at end of file
diff --git a/config/routes.rb b/config/routes.rb
index aa4aad8ec..ad4cd56d5 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1049,6 +1049,9 @@ Rails.application.routes.draw do
resources :shixuns, only: [:index,:destroy]
resources :shixun_settings, only: [:index,:update]
resources :shixun_feedback_messages, only: [:index]
+ resources :shixun_recycles, only: [:index, :destroy] do
+ post :resume, on: :member
+ end
resources :department_applies,only: [:index,:destroy] do
collection do
post :merge
@@ -1173,6 +1176,14 @@ Rails.application.routes.draw do
resources :partners, only: [:index, :create, :destroy] do
resources :customers, only: [:index, :create, :destroy]
end
+
+ resources :course_lists, only: [:index, :destroy] do
+ post :merge, on: :collection
+ end
+
+ resources :courses, only: [:index, :destroy, :update]
+
+ resources :projects, only: [:index, :destroy]
end
namespace :cooperative do
diff --git a/public/react/config/webpack.config.dev.js b/public/react/config/webpack.config.dev.js
index 4acbb35a7..86938e51d 100644
--- a/public/react/config/webpack.config.dev.js
+++ b/public/react/config/webpack.config.dev.js
@@ -267,7 +267,7 @@ module.exports = {
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
// You can remove this if you don't use Moment.js:
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
- new MonacoWebpackPlugin(),
+ new MonacoWebpackPlugin(),
],
// Some libraries import Node modules but don't use them in the browser.
// Tell Webpack to provide empty mocks for them so importing them works.
diff --git a/public/react/config/webpack.config.prod.js b/public/react/config/webpack.config.prod.js
index 1cc105517..fbfbf23bc 100644
--- a/public/react/config/webpack.config.prod.js
+++ b/public/react/config/webpack.config.prod.js
@@ -11,6 +11,8 @@ const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
const eslintFormatter = require('react-dev-utils/eslintFormatter');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
+// const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
+// const TerserPlugin = require('terser-webpack-plugin');
const paths = require('./paths');
const getClientEnvironment = require('./env');
@@ -54,6 +56,10 @@ const extractTextPluginOptions = shouldUseRelativeAssetPaths
// 上线用的
// console.log('publicPath ', publicPath)
module.exports = {
+ // optimization: {
+ // minimize: true,
+ // minimizer: [new TerserPlugin()],
+ // },
// externals: {
// 'react': 'window.React'
// },
@@ -371,6 +377,7 @@ module.exports = {
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
// You can remove this if you don't use Moment.js:
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
+ // new MonacoWebpackPlugin(),
],
// Some libraries import Node modules but don't use them in the browser.
// Tell Webpack to provide empty mocks for them so importing them works.
diff --git a/public/react/index.js b/public/react/index.js
new file mode 100644
index 000000000..cb8cb4f94
--- /dev/null
+++ b/public/react/index.js
@@ -0,0 +1,46 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+
+import './index.css';
+import './indexPlus.css';
+import App from './App';
+
+// 加之前main.js 18.1MB
+// import { message } from 'antd';
+import message from 'antd/lib/message';
+import 'antd/lib/message/style/css';
+
+import { AppContainer } from 'react-hot-loader';
+
+import registerServiceWorker from './registerServiceWorker';
+
+import { configureUrlQuery } from 'react-url-query';
+
+import history from './history';
+
+// link the history used in our app to url-query so it can update the URL with it.
+configureUrlQuery({ history });
+// ----------------------------------------------------------------------------------- 请求配置
+
+window.__useKindEditor = false;
+
+
+const render = (Component) => {
+ ReactDOM.render(
+
+
+ ,
+ document.getElementById('root')
+ );
+}
+
+
+// ReactDOM.render(
+// ,
+// document.getElementById('root'));
+// registerServiceWorker();
+
+render(App);
+if (module.hot) {
+ module.hot.accept('./App', () => { render(App) });
+}
diff --git a/public/react/package-lock.json b/public/react/package-lock.json
index 631da270e..d66fbf62a 100644
--- a/public/react/package-lock.json
+++ b/public/react/package-lock.json
@@ -84,7 +84,6 @@
"@icedesign/base": {
"version": "0.2.8",
"resolved": "https://registry.npm.taobao.org/@icedesign/base/download/@icedesign/base-0.2.8.tgz",
- "integrity": "sha1-hmlSY+17gnKJB3sbgoy446sqzAk=",
"requires": {
"async-validator": "^1.6.7",
"classnames": "^2.2.3",
@@ -138,8 +137,7 @@
},
"@types/tapable": {
"version": "1.0.4",
- "resolved": "https://registry.npm.taobao.org/@types/tapable/download/@types/tapable-1.0.4.tgz",
- "integrity": "sha1-tP/H3Je0mMlps2CkHu4kf4JhY3A="
+ "resolved": "https://registry.npm.taobao.org/@types/tapable/download/@types/tapable-1.0.4.tgz"
},
"@types/uglify-js": {
"version": "3.0.4",
@@ -2779,8 +2777,7 @@
},
"cropperjs": {
"version": "0.7.2",
- "resolved": "https://registry.npm.taobao.org/cropperjs/download/cropperjs-0.7.2.tgz",
- "integrity": "sha1-atinHbAGKbqULZzt5lKyeXXp50o="
+ "resolved": "https://registry.npm.taobao.org/cropperjs/download/cropperjs-0.7.2.tgz"
},
"cross-spawn": {
"version": "5.1.0",
@@ -10456,6 +10453,11 @@
"resolved": "http://registry.npm.taobao.org/requires-port/download/requires-port-1.0.0.tgz",
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
},
+ "reqwest": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/reqwest/-/reqwest-2.0.5.tgz",
+ "integrity": "sha1-APsVrEkYxBnKgrQ/JMeIguZgOaE="
+ },
"resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "http://registry.npm.taobao.org/resize-observer-polyfill/download/resize-observer-polyfill-1.5.1.tgz",
@@ -10834,86 +10836,6 @@
"resolved": "http://registry.npm.taobao.org/shellwords/download/shellwords-0.1.1.tgz",
"integrity": "sha1-1rkYHBpI05cyTISHHvvPxz/AZUs="
},
- "showdown": {
- "version": "1.9.0",
- "resolved": "http://registry.npm.taobao.org/showdown/download/showdown-1.9.0.tgz",
- "integrity": "sha1-1J0qC22yG3wulu+FX3s7KijvRvQ=",
- "requires": {
- "yargs": "^10.0.3"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "3.0.0",
- "resolved": "http://registry.npm.taobao.org/ansi-regex/download/ansi-regex-3.0.0.tgz",
- "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
- },
- "camelcase": {
- "version": "4.1.0",
- "resolved": "http://registry.npm.taobao.org/camelcase/download/camelcase-4.1.0.tgz",
- "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
- },
- "cliui": {
- "version": "4.1.0",
- "resolved": "http://registry.npm.taobao.org/cliui/download/cliui-4.1.0.tgz",
- "integrity": "sha1-NIQi2+gtgAswIu709qwQvy5NG0k=",
- "requires": {
- "string-width": "^2.1.1",
- "strip-ansi": "^4.0.0",
- "wrap-ansi": "^2.0.0"
- }
- },
- "os-locale": {
- "version": "2.1.0",
- "resolved": "http://registry.npm.taobao.org/os-locale/download/os-locale-2.1.0.tgz",
- "integrity": "sha1-QrwpAKa1uL0XN2yOiCtlr8zyS/I=",
- "requires": {
- "execa": "^0.7.0",
- "lcid": "^1.0.0",
- "mem": "^1.1.0"
- }
- },
- "strip-ansi": {
- "version": "4.0.0",
- "resolved": "http://registry.npm.taobao.org/strip-ansi/download/strip-ansi-4.0.0.tgz",
- "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
- "requires": {
- "ansi-regex": "^3.0.0"
- }
- },
- "which-module": {
- "version": "2.0.0",
- "resolved": "http://registry.npm.taobao.org/which-module/download/which-module-2.0.0.tgz",
- "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
- },
- "yargs": {
- "version": "10.1.2",
- "resolved": "http://registry.npm.taobao.org/yargs/download/yargs-10.1.2.tgz",
- "integrity": "sha1-RU0HTCsWpRpD4vt4B+T53mnMtcU=",
- "requires": {
- "cliui": "^4.0.0",
- "decamelize": "^1.1.1",
- "find-up": "^2.1.0",
- "get-caller-file": "^1.0.1",
- "os-locale": "^2.0.0",
- "require-directory": "^2.1.1",
- "require-main-filename": "^1.0.1",
- "set-blocking": "^2.0.0",
- "string-width": "^2.0.0",
- "which-module": "^2.0.0",
- "y18n": "^3.2.1",
- "yargs-parser": "^8.1.0"
- }
- },
- "yargs-parser": {
- "version": "8.1.0",
- "resolved": "http://registry.npm.taobao.org/yargs-parser/download/yargs-parser-8.1.0.tgz",
- "integrity": "sha1-8TdqM7Ziml0GN4KUTacyYx6WaVA=",
- "requires": {
- "camelcase": "^4.1.0"
- }
- }
- }
- },
"signal-exit": {
"version": "3.0.2",
"resolved": "http://registry.npm.taobao.org/signal-exit/download/signal-exit-3.0.2.tgz",
diff --git a/public/react/package.json b/public/react/package.json
index e43132877..b06c70e5f 100644
--- a/public/react/package.json
+++ b/public/react/package.json
@@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"@icedesign/base": "^0.2.5",
+ "@monaco-editor/react": "^2.3.0",
"@novnc/novnc": "^1.1.0",
"antd": "^3.23.2",
"array-flatten": "^2.1.2",
@@ -28,6 +29,7 @@
"dotenv": "4.0.0",
"dotenv-expand": "4.2.0",
"echarts": "^4.2.0-rc.2",
+ "editor.md": "^1.5.0",
"eslint": "4.10.0",
"eslint-config-react-app": "^2.1.0",
"eslint-loader": "1.9.0",
@@ -42,6 +44,8 @@
"immutability-helper": "^2.6.6",
"install": "^0.12.2",
"jest": "20.0.4",
+ "js-base64": "^2.5.1",
+ "katex": "^0.11.1",
"lodash": "^4.17.5",
"loglevel": "^1.6.1",
"material-ui": "^1.0.0-beta.40",
@@ -49,12 +53,14 @@
"monaco-editor": "^0.15.6",
"monaco-editor-webpack-plugin": "^1.7.0",
"npm": "^6.10.1",
+ "numeral": "^2.0.6",
"object-assign": "4.1.1",
"postcss-flexbugs-fixes": "3.2.0",
"postcss-loader": "2.0.8",
"promise": "8.0.1",
"prop-types": "^15.6.1",
"qs": "^6.6.0",
+ "quill": "^1.3.7",
"raf": "3.4.0",
"rc-form": "^2.1.7",
"rc-pagination": "^1.16.2",
@@ -77,11 +83,12 @@
"react-redux": "5.0.7",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
- "react-split-pane": "^0.1.87",
+ "react-split-pane": "^0.1.89",
"react-url-query": "^1.4.0",
"redux": "^4.0.0",
"redux-thunk": "2.3.0",
"rsuite": "^4.0.1",
+ "sass-loader": "7.3.1",
"store": "^2.0.12",
"style-loader": "0.19.0",
"styled-components": "^4.1.3",
@@ -167,7 +174,7 @@
"concat": "^1.0.3",
"happypack": "^5.0.1",
"node-sass": "^4.12.0",
- "sass-loader": "^7.3.1",
+ "reqwest": "^2.0.5",
"webpack-bundle-analyzer": "^3.0.3",
"webpack-parallel-uglify-plugin": "^1.1.0"
}
diff --git a/public/react/public/index.html b/public/react/public/index.html
index c79d4d148..aac86e776 100755
--- a/public/react/public/index.html
+++ b/public/react/public/index.html
@@ -13,6 +13,7 @@
+
+