From 97a432fa6a82c6da66391c0bc36ac1d414ceecb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=A0=91=E6=98=8E?= <775174143@qq.com> Date: Fri, 20 Dec 2019 09:11:39 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/colleges_controller.rb | 12 +- app/controllers/hacks_controller.rb | 4 +- .../homework_commons_controller.rb | 14 +- app/controllers/myshixuns_controller.rb | 2 +- app/models/curriculum.rb | 4 - app/models/curriculum_direction.rb | 4 - app/models/hack_user_lastest_code.rb | 2 +- app/models/item_bank.rb | 2 - app/models/knowledge_point.rb | 5 - app/models/knowledge_point_container.rb | 3 - .../colleges/student_shixun.json.jbuilder | 2 +- app/views/colleges/teachers.json.jbuilder | 2 +- ...1218070922_create_curriculum_directions.rb | 9 - .../20191218071017_create_curriculums.rb | 10 - .../20191218071111_create_knowledge_points.rb | 11 - ...71343_create_knowledge_point_containers.rb | 12 - ...1219120819_modify_description_for_hacks.rb | 10 + public/react/package.json | 1 + public/react/public/js/jupyter.js | 32 +- public/react/src/App.js | 13 +- public/react/src/college/College.js | 1259 +++++++++++++++++ .../college/colleagechart/Colleagechart.js | 84 ++ .../college/colleagechart/Colleagechartzu.js | 149 ++ .../src/college/colleagecss/colleage.css | 213 +++ .../common/components/comment/CommentForm.js | 116 ++ .../common/components/comment/CommentIcon.js | 32 + .../common/components/comment/CommentItem.js | 165 +++ .../common/components/comment/CommentList.js | 20 + .../src/common/components/comment/index.js | 22 + .../src/common/components/comment/index.scss | 111 ++ .../src/common/quillForEditor/ImageBlot.js | 54 + .../src/common/quillForEditor/deepEqual.js | 47 + .../react/src/common/quillForEditor/index.js | 166 +++ .../react/src/common/reactQuill/ImageBlot.js | 54 + .../react/src/common/reactQuill/ReactQuill.js | 45 + .../react/src/common/reactQuill/deepEqual.js | 47 + public/react/src/common/reactQuill/flatten.js | 26 + public/react/src/common/reactQuill/index.js | 108 ++ public/react/src/common/reactQuill/index.scss | 32 + public/react/src/common/reactQuill/lib.js | 13 + .../src/common/reactQuill/useDeepEqualMemo.js | 27 + .../src/common/reactQuill/useMountQuill.js | 148 ++ .../react/src/common/reactQuill/useQuill.js | 60 + .../src/common/reactQuill/useQuillOnChange.js | 33 + .../common/reactQuill/useQuillPlaceholder.js | 22 + .../common/reactQuill/useQuillValueSync.js | 31 + .../src/modules/developer/DeveloperHome.js | 21 +- .../components/controlSetting/index.js | 24 +- .../components/controlSetting/index.scss | 7 +- .../developer/components/execResult/index.js | 14 +- .../developer/components/initTabCtx/index.js | 7 +- .../components/initTabCtx/index.scss | 3 +- .../components/monacoSetting/index.js | 3 +- .../components/myMonacoEditor/index.js | 59 +- .../components/myMonacoEditor/index.scss | 33 +- .../modules/developer/newOrEditTask/index.js | 102 +- .../developer/newOrEditTask/index.scss | 16 +- .../leftpane/editorTab/AddTestDemo.js | 24 +- .../newOrEditTask/leftpane/editorTab/index.js | 53 +- .../leftpane/editorTab/index.scss | 5 + .../developer/newOrEditTask/leftpane/index.js | 22 - .../newOrEditTask/leftpane/prevTab/index.js | 77 +- .../newOrEditTask/rightpane/index.scss | 5 +- .../modules/developer/split_pane_resizer.scss | 6 +- .../modules/developer/studentStudy/index.js | 63 +- .../modules/developer/studentStudy/index.scss | 2 +- .../studentStudy/leftpane/comment/index.js | 13 +- .../studentStudy/leftpane/comment/index.scss | 8 + .../developer/studentStudy/leftpane/index.js | 63 +- .../studentStudy/leftpane/index.scss | 7 + .../leftpane/taskDescription/index.js | 18 +- .../developer/studentStudy/rightpane/index.js | 32 +- public/react/src/modules/tpm/TPMBanner.js | 71 +- public/react/src/modules/tpm/TPMIndex.js | 11 +- .../tpm/TPMsettings/LearningSettings.js | 6 +- .../tpm/TPMsettings/Shixuninformation.js | 13 + .../modules/tpm/challengesnew/TPManswer.js | 4 +- .../modules/tpm/challengesnew/TPManswer2.js | 4 +- .../tpm/challengesnew/TPMchallengesnew.js | 4 +- .../tpm/challengesnew/TPMevaluation.js | 4 +- .../modules/tpm/challengesnew/TPMquestion.js | 4 +- .../modules/tpm/component/TPMRightSection.js | 6 +- .../src/modules/tpm/component/TPMright.css | 5 + .../tpm/shixunchild/Challenges/Challenges.js | 42 +- .../Challenges/Challengesjupyter.js | 45 +- .../Collaborators/Collaborators.css | 9 + .../Collaborators/Collaborators.js | 11 +- .../src/modules/tpm/shixuns/ShixunCardList.js | 2 +- public/react/src/redux/actions/actionTypes.js | 5 +- public/react/src/redux/actions/common.js | 4 +- public/react/src/redux/actions/index.js | 4 +- public/react/src/redux/actions/ojForUser.js | 45 +- public/react/src/redux/actions/ojForm.js | 20 +- .../src/redux/reducers/ojForUserReducer.js | 29 +- .../react/src/redux/reducers/ojFormReducer.js | 4 +- public/react/src/services/ojService.js | 15 +- spec/models/curriculum_direction_spec.rb | 5 - spec/models/curriculum_spec.rb | 5 - spec/models/knowledge_point_container_spec.rb | 5 - spec/models/knowledge_point_spec.rb | 5 - 100 files changed, 3821 insertions(+), 491 deletions(-) delete mode 100644 app/models/curriculum.rb delete mode 100644 app/models/curriculum_direction.rb delete mode 100644 app/models/knowledge_point.rb delete mode 100644 app/models/knowledge_point_container.rb delete mode 100644 db/migrate/20191218070922_create_curriculum_directions.rb delete mode 100644 db/migrate/20191218071017_create_curriculums.rb delete mode 100644 db/migrate/20191218071111_create_knowledge_points.rb delete mode 100644 db/migrate/20191218071343_create_knowledge_point_containers.rb create mode 100644 db/migrate/20191219120819_modify_description_for_hacks.rb create mode 100644 public/react/src/college/College.js create mode 100644 public/react/src/college/colleagechart/Colleagechart.js create mode 100644 public/react/src/college/colleagechart/Colleagechartzu.js create mode 100644 public/react/src/college/colleagecss/colleage.css create mode 100644 public/react/src/common/components/comment/CommentForm.js create mode 100644 public/react/src/common/components/comment/CommentIcon.js create mode 100644 public/react/src/common/components/comment/CommentItem.js create mode 100644 public/react/src/common/components/comment/CommentList.js create mode 100644 public/react/src/common/components/comment/index.js create mode 100644 public/react/src/common/components/comment/index.scss create mode 100644 public/react/src/common/quillForEditor/ImageBlot.js create mode 100644 public/react/src/common/quillForEditor/deepEqual.js create mode 100644 public/react/src/common/quillForEditor/index.js create mode 100644 public/react/src/common/reactQuill/ImageBlot.js create mode 100644 public/react/src/common/reactQuill/ReactQuill.js create mode 100644 public/react/src/common/reactQuill/deepEqual.js create mode 100644 public/react/src/common/reactQuill/flatten.js create mode 100644 public/react/src/common/reactQuill/index.js create mode 100644 public/react/src/common/reactQuill/index.scss create mode 100644 public/react/src/common/reactQuill/lib.js create mode 100644 public/react/src/common/reactQuill/useDeepEqualMemo.js create mode 100644 public/react/src/common/reactQuill/useMountQuill.js create mode 100644 public/react/src/common/reactQuill/useQuill.js create mode 100644 public/react/src/common/reactQuill/useQuillOnChange.js create mode 100644 public/react/src/common/reactQuill/useQuillPlaceholder.js create mode 100644 public/react/src/common/reactQuill/useQuillValueSync.js create mode 100644 public/react/src/modules/developer/studentStudy/leftpane/comment/index.scss delete mode 100644 spec/models/curriculum_direction_spec.rb delete mode 100644 spec/models/curriculum_spec.rb delete mode 100644 spec/models/knowledge_point_container_spec.rb delete mode 100644 spec/models/knowledge_point_spec.rb diff --git a/app/controllers/colleges_controller.rb b/app/controllers/colleges_controller.rb index 71009d2dd..707255866 100644 --- a/app/controllers/colleges_controller.rb +++ b/app/controllers/colleges_controller.rb @@ -21,7 +21,7 @@ class CollegesController < ApplicationController # 实训总数 @shixuns_count = Shixun.visible.joins('left join user_extensions on user_extensions.user_id = shixuns.user_id') .where(user_extensions: { school_id: current_school.id }).count - render json: { teachers_count: @teachers_count, students_count: @students_count, courses_count: @courses_count, shixuns_count: @shixuns_count } + render json: { teachers_count: @teachers_count, students_count: @students_count, courses_count: @courses_count, shixuns_count: @shixuns_count, school: current_school.name } end def shixun_time @@ -44,8 +44,8 @@ class CollegesController < ApplicationController (SELECT count(c.id) FROM courses c, course_members m WHERE c.id != 1309 and m.course_id = c.id AND m.user_id=users.id AND m.role in (1,2,3) and c.school_id = #{current_school.id} AND c.is_delete = 0) as course_count FROM `users`, user_extensions ue where ue.school_id=#{current_school.id} and users.id=ue.user_id and ue.identity=0 ORDER BY publish_shixun_count desc, course_count desc, id desc LIMIT 10") # ).order("publish_shixun_count desc, experience desc").limit(10) - @teacher_count = UserExtension.where(school_id: current_school.id) - .select('SUM(IF(identity=0, 1, 0)) AS teachers_count').first.teachers_count + # @teacher_count = UserExtension.where(school_id: current_school.id) + # .select('SUM(IF(identity=0, 1, 0)) AS teachers_count').first.teachers_count @teachers = @teachers.map do |teacher| course_ids = Course.find_by_sql("SELECT c.id FROM courses c, course_members m WHERE c.id != 1309 and m.course_id = c.id AND m.role in (1,2,3) AND m.user_id=#{teacher.id} AND c.is_delete = 0 and c.school_id = #{current_school.id}") @@ -99,7 +99,7 @@ class CollegesController < ApplicationController @course_count = courses.size courses = courses.left_joins(practice_homeworks: { student_works: { myshixun: :games } }) - .select('courses.id, courses.name, courses.is_end, sum(games.evaluate_count) evaluating_count') + .select('courses.id, courses.name, courses.is_end, IFNULL(sum(games.evaluate_count), 0) evaluating_count') .group('courses.id').order('is_end asc, evaluating_count desc') @courses = paginate courses @@ -117,7 +117,7 @@ class CollegesController < ApplicationController # 学生实训 def student_shixun - @student_count = User.joins(:user_extension).where(user_extensions: { school_id: current_school.id, identity: 1 }).count + # @student_count = User.joins(:user_extension).where(user_extensions: { school_id: current_school.id, identity: 1 }).count @students = User.joins(:user_extension).where(user_extensions: { school_id: current_school.id, identity: 1 }).includes(:user_extension).order('experience desc').limit(10) student_ids = @students.map(&:id) @@ -158,7 +158,7 @@ class CollegesController < ApplicationController return true if current_user.admin_or_business? # 超级管理员|运营 return true if current_college.is_a?(Department) && current_college.member?(current_user) # 部门管理员 return true if current_user.is_teacher? && current_user.school_id == current_school.id # 学校老师 - return true if current_school.customers.exists? && current_user.partner&.partner_customers&.exists?(customer_id: current_school.customer_id) + # return true if current_school.customers.exists? && current_user.partner&.partner_customers&.exists?(customer_id: current_school.customer_id) false end diff --git a/app/controllers/hacks_controller.rb b/app/controllers/hacks_controller.rb index cab952a04..b7f6a30a2 100644 --- a/app/controllers/hacks_controller.rb +++ b/app/controllers/hacks_controller.rb @@ -9,9 +9,11 @@ class HacksController < ApplicationController def start # 未发布的编程题,只能作者、或管理员访问 start_hack_auth - user_hack = @hack.hack_user_lastest_codes.mine(current_user.id) + user_hack = @hack.hack_user_lastest_codes.where(user_id: current_user.id).first + logger.info("#user_hack: #{user_hack}") identifier = if user_hack.present? + logger.info("#####user_hack_id:#{user_hack.id}") user_hack.identifier else user_identifier = generate_identifier HackUserLastestCode, 12 diff --git a/app/controllers/homework_commons_controller.rb b/app/controllers/homework_commons_controller.rb index 8a1c07ddf..3a60dcde5 100644 --- a/app/controllers/homework_commons_controller.rb +++ b/app/controllers/homework_commons_controller.rb @@ -167,7 +167,11 @@ class HomeworkCommonsController < ApplicationController if params[:work_status].present? params_work_status = params[:work_status] work_status = params_work_status.map{|status| status.to_i} - @student_works = @student_works.where(compelete_status: work_status) + if @homework.homework_type == "practice" + @student_works = @student_works.where(compelete_status: work_status) + else + @student_works = @student_works.where(work_status: work_status) + end end # 分班情况 @@ -1497,8 +1501,12 @@ class HomeworkCommonsController < ApplicationController @user = @student_work.user tip_exception("当前用户无作品可以显示") if @student_work.nil? # 查询最新一次的查重标识query_id - group_id = @course.course_members.where(user_id: params[:user_id]).pluck(:course_group_id).first - query_id = @homework.homework_group_reviews.where(:course_group_id => group_id).last.try(:query_id) + group_id = @course.students.where(user_id: params[:user_id]).pluck(:course_group_id).first + homework_group_review = @homework.homework_group_reviews.where(:course_group_id => group_id).last || @homework.homework_group_reviews.last + query_id = homework_group_review.try(:query_id) + Rails.logger.info("##################------query_id: #{query_id}") + tip_exception(-1, "query_id有误") unless query_id.present? + results = ReviewService.query_result({user_id: params[:user_id], query_id: query_id}) @shixun = @homework.shixuns.take if results.status == 0 diff --git a/app/controllers/myshixuns_controller.rb b/app/controllers/myshixuns_controller.rb index 987f4873a..0e9ba477a 100644 --- a/app/controllers/myshixuns_controller.rb +++ b/app/controllers/myshixuns_controller.rb @@ -247,7 +247,7 @@ class MyshixunsController < ApplicationController def update_file begin @hide_code = Shixun.where(id: @myshixun.shixun_id).pluck(:hide_code).first - tip_exception("技术平台为空!") if @myshixun.mirror_name.blank? + tip_exception("实验环境不能为空,请查看实训模板的环境配置项是否正确!") if (@myshixun.mirror_name.blank? || @myshixun.mirror_name.first.to_s == "-1") path = params[:path].strip unless params[:path].blank? game_id = params[:game_id] game = Game.find(game_id) diff --git a/app/models/curriculum.rb b/app/models/curriculum.rb deleted file mode 100644 index 0b78814bc..000000000 --- a/app/models/curriculum.rb +++ /dev/null @@ -1,4 +0,0 @@ -class Curriculum < ApplicationRecord - belongs_to :curriculum_direction - has_many :knowledge_points, dependent: :destroy -end diff --git a/app/models/curriculum_direction.rb b/app/models/curriculum_direction.rb deleted file mode 100644 index 1c9211559..000000000 --- a/app/models/curriculum_direction.rb +++ /dev/null @@ -1,4 +0,0 @@ -class CurriculumDirection < ApplicationRecord - has_many :curriculums - has_many :knowledge_points -end diff --git a/app/models/hack_user_lastest_code.rb b/app/models/hack_user_lastest_code.rb index b4a707603..830f16dde 100644 --- a/app/models/hack_user_lastest_code.rb +++ b/app/models/hack_user_lastest_code.rb @@ -8,7 +8,7 @@ class HackUserLastestCode < ApplicationRecord belongs_to :user has_many :hack_user_codes, dependent: :destroy has_one :hack_user_debug - scope :mine, ->(author_id){ find_by(user_id: author_id) } + scope :mine, ->(author_id){ where(user_id: author_id).first } scope :mine_hack, ->(author_id){ where(user_id: author_id) } scope :passed, -> {where(status: 1)} diff --git a/app/models/item_bank.rb b/app/models/item_bank.rb index 32c349e34..8078a55e0 100644 --- a/app/models/item_bank.rb +++ b/app/models/item_bank.rb @@ -3,8 +3,6 @@ class ItemBank < ApplicationRecord # item_type: 0 单选 1 多选 2 判断 3 填空 4 简答 5 实训 6 编程 enum item_type: { SINGLE: 0, MULTIPLE: 1, JUDGMENT: 2, COMPLETION: 3, SUBJECTIVE: 4, PRACTICAL: 5, PROGRAM: 6 } - belongs_to :curriculum - belongs_to :curriculum_direction belongs_to :user has_one :item_analysis, dependent: :destroy diff --git a/app/models/knowledge_point.rb b/app/models/knowledge_point.rb deleted file mode 100644 index 3d75b66a1..000000000 --- a/app/models/knowledge_point.rb +++ /dev/null @@ -1,5 +0,0 @@ -class KnowledgePoint < ApplicationRecord - belongs_to :curriculum_direction - belongs_to :curriculum - has_many :knowledge_point_containers, dependent: :destroy -end diff --git a/app/models/knowledge_point_container.rb b/app/models/knowledge_point_container.rb deleted file mode 100644 index ea73d00e7..000000000 --- a/app/models/knowledge_point_container.rb +++ /dev/null @@ -1,3 +0,0 @@ -class KnowledgePointContainer < ApplicationRecord - belongs_to :knowledge_point -end diff --git a/app/views/colleges/student_shixun.json.jbuilder b/app/views/colleges/student_shixun.json.jbuilder index 431603afa..97e418148 100644 --- a/app/views/colleges/student_shixun.json.jbuilder +++ b/app/views/colleges/student_shixun.json.jbuilder @@ -7,4 +7,4 @@ json.teachers @students do |student| json.grade student.grade json.experience student.experience end -json.student_count @student_count \ No newline at end of file +# json.student_count @student_count \ No newline at end of file diff --git a/app/views/colleges/teachers.json.jbuilder b/app/views/colleges/teachers.json.jbuilder index 29b383fa4..1308f0cf4 100644 --- a/app/views/colleges/teachers.json.jbuilder +++ b/app/views/colleges/teachers.json.jbuilder @@ -8,4 +8,4 @@ json.teachers @teachers do |teacher| json.complete_rate teacher['complete_rate'] json.publish_shixun_count teacher['publish_shixun_count'] end -json.teacher_count @teacher_count \ No newline at end of file +# json.teacher_count @teacher_count \ No newline at end of file diff --git a/db/migrate/20191218070922_create_curriculum_directions.rb b/db/migrate/20191218070922_create_curriculum_directions.rb deleted file mode 100644 index 9203aa435..000000000 --- a/db/migrate/20191218070922_create_curriculum_directions.rb +++ /dev/null @@ -1,9 +0,0 @@ -class CreateCurriculumDirections < ActiveRecord::Migration[5.2] - def change - create_table :curriculum_directions do |t| - t.string :name - - t.timestamps - end - end -end diff --git a/db/migrate/20191218071017_create_curriculums.rb b/db/migrate/20191218071017_create_curriculums.rb deleted file mode 100644 index 885f2a676..000000000 --- a/db/migrate/20191218071017_create_curriculums.rb +++ /dev/null @@ -1,10 +0,0 @@ -class CreateCurriculums < ActiveRecord::Migration[5.2] - def change - create_table :curriculums do |t| - t.string :name - t.references :curriculum_direction, index: true - - t.timestamps - end - end -end diff --git a/db/migrate/20191218071111_create_knowledge_points.rb b/db/migrate/20191218071111_create_knowledge_points.rb deleted file mode 100644 index 2f748d266..000000000 --- a/db/migrate/20191218071111_create_knowledge_points.rb +++ /dev/null @@ -1,11 +0,0 @@ -class CreateKnowledgePoints < ActiveRecord::Migration[5.2] - def change - create_table :knowledge_points do |t| - t.string :name - t.references :curriculum_direction, index: true - t.references :curriculum, index: true - - t.timestamps - end - end -end diff --git a/db/migrate/20191218071343_create_knowledge_point_containers.rb b/db/migrate/20191218071343_create_knowledge_point_containers.rb deleted file mode 100644 index 0b6944ea7..000000000 --- a/db/migrate/20191218071343_create_knowledge_point_containers.rb +++ /dev/null @@ -1,12 +0,0 @@ -class CreateKnowledgePointContainers < ActiveRecord::Migration[5.2] - def change - create_table :knowledge_point_containers do |t| - t.references :knowledge_point - t.integer :container_id - t.string :container_type - - t.timestamps - end - add_index :knowledge_point_containers, [:knowledge_point_id, :container_id, :container_type], name: "container_index", unique: true - end -end diff --git a/db/migrate/20191219120819_modify_description_for_hacks.rb b/db/migrate/20191219120819_modify_description_for_hacks.rb new file mode 100644 index 000000000..3cc45fd5d --- /dev/null +++ b/db/migrate/20191219120819_modify_description_for_hacks.rb @@ -0,0 +1,10 @@ +class ModifyDescriptionForHacks < ActiveRecord::Migration[5.2] + def change + change_column :hacks, :description, :longtext + change_column :hack_codes, :code, :longtext + change_column :hack_user_lastest_codes, :code, :longtext + change_column :hack_user_codes, :code, :longtext + change_column :hack_user_debugs, :code, :longtext + + end +end diff --git a/public/react/package.json b/public/react/package.json index 66db5e6f8..144292feb 100644 --- a/public/react/package.json +++ b/public/react/package.json @@ -61,6 +61,7 @@ "prop-types": "^15.6.1", "qs": "^6.6.0", "quill": "^1.3.7", + "quill-delta-to-html": "^0.11.0", "raf": "3.4.0", "rc-form": "^2.1.7", "rc-pagination": "^1.16.2", diff --git a/public/react/public/js/jupyter.js b/public/react/public/js/jupyter.js index 8d76a9a7f..6e52088c3 100644 --- a/public/react/public/js/jupyter.js +++ b/public/react/public/js/jupyter.js @@ -1,11 +1,29 @@ -//用于嵌入到jupyter pod中的js -//guange 2019.12.18 - -window.onload=function(){ - require(["base/js/namespace"],function(Jupyter) { - Jupyter.notebook.save_checkpoint(); - }); + window.onload=function(){ + // require(["base/js/namespace"],function(Jupyter) { + // Jupyter.notebook.save_checkpoint(); + // }); + $('.navbar-nav').children().eq(7).css({'display':'none'}) + console.log($('.navbar-nav').children().eq(7)) } + +// //子目标父窗口接收子窗口发送的消息 +// let message = {type: 'open', link:'需要发送的消息'}; +// //子窗口向父窗口发送消息,消息中包含我们想跳转的链接 +// window.parent.postMessage(message,'需要发送的消息'); + + + + // //目标父窗口接收子窗口发送的消息 + // window.addEventListener('message', (e)=>{ + // let origin = event.origin || event.originalEvent.origin; + // if (origin !== '需要发送的消息') { + // return; + // }else { + // //更换iframe的src,实现iframe页面跳转 + // 执行方法 + // } + // },false); + \ No newline at end of file diff --git a/public/react/src/App.js b/public/react/src/App.js index 93b293e36..07fd13e49 100644 --- a/public/react/src/App.js +++ b/public/react/src/App.js @@ -72,6 +72,7 @@ const Otherlogin=Loadable({ loading: Loading, }) + const Otherloginstart=Loadable({ loader: () => import('./modules/login/Otherloginstart'), loading: Loading, @@ -300,6 +301,11 @@ const Developer = Loadable({ loader: () => import('./modules/developer'), loading: Loading }) +// 学院统计 +const College = Loadable({ + loader: () => import('./college/College'), + loading: Loading +}) // 开发者编辑模块 const NewOrEditTask = Loadable({ @@ -614,7 +620,10 @@ class App extends Component { {/*/>*/} - + () + }/> {/* jupyter */} + () } /> + () diff --git a/public/react/src/college/College.js b/public/react/src/college/College.js new file mode 100644 index 000000000..bc43252b7 --- /dev/null +++ b/public/react/src/college/College.js @@ -0,0 +1,1259 @@ +import React, {Component} from "react"; +import {Link, NavLink} from 'react-router-dom'; +import {WordsBtn, ActionBtn,SnackbarHOC,getImageUrl} from 'educoder'; +import axios from 'axios'; +import { + notification, + Spin, + Table, + Pagination, +} from "antd"; +import Colleagechart from './colleagechart/Colleagechart' +import Colleagechartzu from './colleagechart/Colleagechartzu' +import {TPMIndexHOC} from "../modules/tpm/TPMIndexHOC"; +import NoneData from './../modules/courses/coursesPublic/NoneData'; + +import './colleagecss/colleage.css'; +import Shixunechart from "../modules/courses/shixunHomework/shixunreport/Shixunechart"; +class College extends Component { + constructor(props) { + super(props); + // this.answerMdRef = React.createRef(); + this.state = { + coursesloading:false, + columns: [ + { + title: '名称', + dataIndex: 'name', + key: 'name', + align: 'center', + className: "edu-txt-center font-14 maxnamewidth247", + render: (text, record) => ( + { + record.name + } + ) + }, + { + title: '管理教师', + dataIndex: 'teachers', + key: 'teachers', + align: 'center', + className: "edu-txt-center font-14 maxnamewidth340", + render: (text, record) => ( + + { + record.teachers + } + + ) + }, + { + title: '评测次数', + dataIndex: 'times', + key: 'times', + align: 'center', + className: "edu-txt-center font-14 maxnamewidth175", + render: (text, record) => ( + + { + record.evaluating_count + } + + ), + }, + { + title: '学生', + key: 'student', + dataIndex: 'student', + align: 'center', + className: "edu-txt-center font-14 maxnamewidth255", + render: (text, record) => ( + + { + record.student_count + } + + ) + }, + { + title: '实训作业', + dataIndex: 'training', + key: 'training', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.shixun_work_count + } + + ) + + }, + { + title: '资源', + dataIndex: 'resources', + key: 'resources', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.attachment_count + } + + ), + }, + { + title: '帖子', + dataIndex: 'posts', + key: 'posts', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + { + record.message_count + } + ) + }, + { + title: '其它任务', + dataIndex: 'othertasks', + key: 'othertasks', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.other_work_count + } + + ) + }, + { + title: '状态', + dataIndex: 'states', + key: 'states', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.is_end? + "已结束" + : + "正在进行" + } + + ) + }, + { + title: '时间', + dataIndex: 'timemy', + key: 'timemy', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.activity_time + } + + ) + }, + ], + page:1, + limit:10, + total_users:50, + teachersloading:false, + teacherranking:[ + { + title: '排名', + dataIndex: 'ranking', + key: 'ranking', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.id + } + + ) + }, + { + title: '姓名', + dataIndex: 'name', + key: 'name', + align: 'center', + className: "edu-txt-center font-14 maxnamewidth105", + render: (text, record) => ( + { + record.name + } + + ) + }, + { + title: '管理课堂', + dataIndex: 'classroom', + key: 'classroom', + align: 'center', + className: "edu-txt-center font-14 maxnamewidth175", + render: (text, record) => ( + + { + record.course_count + } + + ), + }, + { + title: '已发布实训作业', + key: 'assignment', + dataIndex: 'assignment', + align: 'center', + className: "edu-txt-center font-14 maxnamewidth255", + render: (text, record) => ( + + { + record.shixun_work_count + } + + ) + }, + { + title: '未发布实训作业', + dataIndex: 'released', + key: 'released', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.un_shixun_work_count + } + + ) + + }, + { + title: '学生数', + dataIndex: 'studentnumber', + key: 'studentnumber', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.student_count + } + + ), + }, + { + title: '完成率', + dataIndex: 'completionrate', + key: 'completionrate', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.complete_rate+"%" + } + + ) + }, + { + title: '发布实训', + dataIndex: 'releasetraining', + key: 'releasetraining', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.publish_shixun_count + } + + ) + } + ], + studentranking:[ + { + title: '排名', + dataIndex: 'ranking', + key: 'ranking', + align: 'center', + className: "edu-txt-center font-14", + width:'100px', + render: (text, record) => ( + + { + record.id===1? + :record.id===2? + + :record.id===3? + + :record.id + } + + ) + }, + { + title: '姓名', + dataIndex: 'name', + key: 'name', + align: 'center', + className: "edu-txt-center font-14 maxnamewidth105", + width:'100px', + render: (text, record) => ( + { + record.name + } + + ) + }, + { + title: '学号', + dataIndex: 'studentid', + key: 'studentid', + align: 'center', + className: "edu-txt-center font-14 maxnamewidth175", + render: (text, record) => ( + + { + record.student_id + } + + ), + }, + { + title: '完成实训', + key: 'training', + dataIndex: 'training', + align: 'center', + className: "edu-txt-center font-14 maxnamewidth255", + render: (text, record) => ( + + { + record.shixun_count + } + + ) + }, + { + title: '在学实训', + dataIndex: 'learning', + key: 'learning', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.study_shixun_count + } + + ) + + }, + { + title: '金币', + dataIndex: 'goldcoin', + key: 'goldcoin', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.grade + } + + ), + }, + { + title: '经验值', + dataIndex: 'empirical', + key: 'empirical', + align: 'center', + className: "edu-txt-center font-14", + render: (text, record) => ( + + { + record.experience + } + + ) + }, + ], + school:"", + teachers_count:null, + students_count:null, + courses_count:null, + shixuns_count:null, + shixun_report_count:null, + shixun_time:null, + courses:null, + course_count:0, + pages:1, + limits:10, + teachers:null, + teacher_count:0, + students:null, + student_count:0, + shixun_chart_data:null, + shixun_chart_datanames:null, + studentionsnames:null, + studentionsvalues:null + } + + } + + + componentDidMount(){ + console.log("College"); + console.log(this.props.match.params.id); + this.gettop(); + this.Numberofinternshipreports(); + this.Actualcombattimeoftrainees(); + this.Classnumber(1,10); + this.Teacherranking(1,10); + this.Studentranking(1,10); + this.Onlinetraining(); + this.Hottest(); + } + //头部 + gettop=()=>{ + const id =this.props.match.params.id; + const url=`/colleges/${id}/statistics.json`; + axios.get(url).then((response) => { + if(response===null||response===undefined){ + this.setState({ + teachers_count:0, + students_count:0, + courses_count:0, + shixuns_count:0, + school:"", + }) + return + } + if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { + this.setState({ + teachers_count:0, + students_count:0, + courses_count:0, + shixuns_count:0, + school:"", + }) + }else{ + this.setState({ + teachers_count:response.data.teachers_count, + students_count:response.data.students_count, + courses_count:response.data.courses_count, + shixuns_count:response.data.shixuns_count, + school:response.data.school, + }) + } + }).catch((error) => { + console.log(error) + this.setState({ + teachers_count:0, + students_count:0, + courses_count:0, + shixuns_count:0, + school:"", + }) + }); + } + + //获取实训报告数 + Numberofinternshipreports=()=>{ + const id =this.props.match.params.id; + const url=`/colleges/${id}/shixun_report_count.json`; + axios.get(url).then((response) => { + if(response===null||response===undefined){ + this.setState({ + shixun_report_count:0, + }) + return + } + if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { + this.setState({ + shixun_report_count:0, + }) + }else{ + if (response.data.status === -1){ + this.setState({ + shixun_report_count:0, + }) + return + } + this.setState({ + shixun_report_count:response.data.shixun_report_count, + }) + } + }).catch((error) => { + console.log(error) + this.setState({ + shixun_report_count:0, + }) + }); + } + + //学员实战时间 + Actualcombattimeoftrainees=()=>{ + const id =this.props.match.params.id; + const url=`/colleges/${id}/shixun_time.json`; + axios.get(url).then((response) => { + if(response===null||response===undefined){ + this.setState({ + shixun_time:0, + }) + return + } + if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { + this.setState({ + shixun_time:0, + }) + }else{ + if (response.data.status === -1){ + this.setState({ + shixun_time:0, + }) + return + } + this.setState({ + shixun_time:response.data.shixun_time, + }) + } + }).catch((error) => { + console.log(error) + this.setState({ + shixun_time:0, + }) + }); + } + + //课堂信息 + Classnumber=(page,per_page)=>{ + const id =this.props.match.params.id; + const url=`/colleges/${id}/course_statistics.json`; + this.setState({ + coursesloading:true + }) + axios.get(url,{params:{ + page:page, + per_page:per_page, + } + }).then((response) => { + if(response===null||response===undefined){ + this.setState({ + courses:[], + course_count:0 + }) + return + } + if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { + this.setState({ + courses:[], + course_count:0 + }) + }else{ + if (response.data.status === -1){ + this.setState({ + courses:[], + course_count:0 + }) + return + } + this.setState({ + courses:response.data.courses, + course_count:response.data.course_count, + page:page, + limit:per_page + }) + } + this.setState({ + coursesloading:false + }) + }).catch((error) => { + this.setState({ + courses:[], + course_count:0, + coursesloading:false + }) + }); + } + + //教师排名 + Teacherranking=(page,per_page)=>{ + const id =this.props.match.params.id; + const url=`/colleges/${id}/teachers.json`; + this.setState({ + teachersloading:true + }) + axios.get(url,{params:{ + page:page, + per_page:per_page, + } + }).then((response) => { + if(response===null||response===undefined){ + this.setState({ + teachers:[], + teacher_count:0 + }) + return + } + if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { + this.setState({ + teachers:[], + teacher_count:0 + }) + }else{ + if (response.data.status === -1){ + this.setState({ + teachers:[], + teacher_count:0 + }) + return + } + let teachers=[]; + if(response.data.teachers){ + for(let i=0;i { + this.setState({ + teachers:[], + teacher_count:0, + teachersloading:false + }) + }); + } + //学生排名 + + Studentranking=(page,per_page)=>{ + const id =this.props.match.params.id; + const url=`/colleges/${id}/student_shixun.json`; + this.setState({ + studentsloading:true + }) + axios.get(url,{params:{ + page:page, + per_page:per_page, + } + }).then((response) => { + if(response===null||response===undefined){ + this.setState({ + students:[], + student_count:0, + }) + return + } + if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { + this.setState({ + students:[], + student_count:0, + }) + }else{ + if (response.data.status === -1){ + this.setState({ + students:[], + student_count:0, + }) + return + } + let students=[]; + if(response.data.teachers){ + for(let i=0;i { + this.setState({ + students:[], + student_count:0, + studentsloading:false + }) + }); + } + + //在线实训情况 + Onlinetraining=()=>{ + const id =this.props.match.params.id; + const url=`/colleges/${id}/shixun_chart_data.json`; + axios.get(url).then((response) => { + if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { + this.setState({ + shixun_chart_data:[], + shixun_chart_datanames:[] + }) + }else{ + if (response.data.status === -1){ + this.setState({ + shixun_chart_data:[], + shixun_chart_datanames:[] + }) + return + } + + this.setState({ + shixun_chart_data:response.data.data, + shixun_chart_datanames:response.data.names + }) + } + }).catch((error) => { + this.setState({ + shixun_chart_data:[], + shixun_chart_datanames:[] + }) + }); + } + //最热测评 + Hottest=()=>{ + const id =this.props.match.params.id; + const url=`/colleges/${id}/student_hot_evaluations.json`; + axios.get(url).then((response) => { + if (response.data.status === 403||response.data.status === 401||response.data.status === 500) { + this.setState({ + studentionsnames: [], + studentionsvalues: [] + }) + }else { + if (response.data.status === -1) { + this.setState({ + studentionsnames: [], + studentionsvalues: [] + }) + return + } + this.setState({ + studentionsnames: response.data.names, + studentionsvalues: response.data.values + }) + } + }).catch((error) => { + this.setState({ + studentionsnames: [], + studentionsvalues: [] + }) + }); + } + + table1handleChange(){ + + } + + //塞选 + paginationonChange=(pageNumber)=>{ + this.Classnumber(pageNumber,10); + } + + paginationonChanges=(pageNumber)=>{ + this.Teacherranking(pageNumber,10); + } + paginationonChangess=(pageNumber)=>{ + this.Studentranking(pageNumber,10); + } + render() { + let {columns,page,limit,total_users,teacherranking,studentranking, + teachers_count,students_count, courses_count, shixuns_count,shixun_report_count,shixun_time,courses,course_count,school,teachers, + pages,limits, teacher_count,teachersloading,coursesloading,pagess,limitss,studentsloading,students,student_count,shixun_chart_data, + shixun_chart_datanames, studentionsnames,studentionsvalues + } = this.state; + + return ( +
+
+
+
{school}
+
+
+ {/*//教师1*/} +
+ 教师 +
+
+ {teachers_count?teachers_count:0} +
+
+ +
+
+ 学生 +
+
+ {students_count?students_count:0} +
+
+ +
+
+ 课堂 +
+
+ {courses_count?courses_count:0} +
+
+ +
+
+ 共建实训 +
+
+ {shixuns_count?shixuns_count:0} +
+
+ {/*//教师2*/} + + + + +
+
+
+
+

+ 基本使用情况 +

+ {/*基本使用情况1*/} +
+

+ 教师 +

+

+ 学生 +

+

+ 课堂 +

+

+ 共建实训 +

+

+ 实习报告 +

+

+ 学员实战时间 +

+
+ + + {/*基本使用情况2*/} +
+
+ { + teachers_count? +
{teachers_count}
+ : + + } +
+
+ { + students_count? +
{students_count}
+ : + + } +
+
+ { + courses_count? +
{courses_count}
+ : + + } +
+
+ { + shixuns_count? +
{shixuns_count}
+ : + + } +
+
+ { + shixun_report_count? +
{shixun_report_count}
+ : + + } +
+
+ { + shixun_time? +
{shixun_time}
+ : + + } +
+
+ {/*基本使用情况3结束*/} +
+ + +
+

+ 课堂 +

+ { + courses===null? +
+ +
+ + : + JSON.stringify(courses) === "[]" ? + + + + : +
+
+ +
+ {courses === undefined ? "" : } + + + { + course_count>=11? +
+
+ +
+ +
+ :"" + } + + + } + + + + + +
+
+

+ 教师排名 +

+ { + teachers===null? +
+ +
+ + : + JSON.stringify(teachers) === "[]" ? + + + + : +
+ +
+ {teachers === undefined ? "" :
} + + + } + + + + {/*
*/} + {/*
*/} + {/* */} + {/*
*/} + + {/*
*/} + + +
+

+ 在线实训情况 +

+ { + shixun_chart_data===null? +
+ +
+ + : + JSON.stringify(shixun_chart_data) === "[]" ? + + + + : + + + + } + +
+
+ + + +
+
+

+ 学生排名 +

+ { + students === null ? +
+ +
+ + : + JSON.stringify(students) === "[]" ? + + + + : +
+ +
+ {students === undefined ? "" :
} + + + } + {/*
*/} + {/*
*/} + {/* */} + {/*
*/} + + {/*
*/} + + + + +
+ +
+

+ 最热评测 +

+ { + studentionsnames===null? +
+ +
+ + : + JSON.stringify(studentionsnames) === "[]" ? + + + + : + + + + } + + +
+
+ + + ) + } +} +export default SnackbarHOC() (TPMIndexHOC ( College )); + + diff --git a/public/react/src/college/colleagechart/Colleagechart.js b/public/react/src/college/colleagechart/Colleagechart.js new file mode 100644 index 000000000..fca01280a --- /dev/null +++ b/public/react/src/college/colleagechart/Colleagechart.js @@ -0,0 +1,84 @@ +import React, {Component} from "react"; +import {WordsBtn} from 'educoder'; +import {Table} from "antd"; +import {Link,Switch,Route,Redirect} from 'react-router-dom'; +const echarts = require('echarts'); + + + +function startechart(data,datanane){ + var effChart = echarts.init(document.getElementById('shixun_skill_chart')); + + var option = { + + tooltip : { + trigger: 'item', + formatter: "{d}%
" + }, + legend: { + // orient: 'vertical', + // top: 'middle', + bottom: 50, + left: 'center', + data: datanane + }, + series : [ + { + type: 'pie', + radius : '65%', + center: ['50%', '35%'], + selectedMode: 'single', + data:data, + itemStyle: { + emphasis: { + shadowBlur: 10, + shadowOffsetX: 0, + shadowColor: 'rgba(0, 0, 0, 0.5)' + } + } + } + ] + }; + effChart.setOption(option); +} +class Colleagechart extends Component { + + constructor(props) { + super(props); + this.state = { + } + } + + componentDidMount() { + startechart(this.props.data,this.props.datanane) + } + + + componentDidUpdate = (prevProps) => { + if (prevProps.data!= this.props.data) { + startechart(this.props.data,this.props.datanane) + } + } + + + render() { + let {data}=this.props; + + return ( +
+ +
+
+ + + + +
+ + ) + } +} + +export default Colleagechart; diff --git a/public/react/src/college/colleagechart/Colleagechartzu.js b/public/react/src/college/colleagechart/Colleagechartzu.js new file mode 100644 index 000000000..5c0269700 --- /dev/null +++ b/public/react/src/college/colleagechart/Colleagechartzu.js @@ -0,0 +1,149 @@ +import React, {Component} from "react"; +import {WordsBtn} from 'educoder'; +import {Table} from "antd"; +import {Link,Switch,Route,Redirect} from 'react-router-dom'; +const echarts = require('echarts'); + + + +function startechart(names, values){ + var effChart = echarts.init(document.getElementById('shixun_skill_charts')); + + var Color = ['#962e66', '#623363', '#CCCCCC', '#9A9A9A', '#FF8080', '#FF80C2', '#B980FF', '#80B9FF', '#6FE9FF', '#4DE8B4', '#F8EF63', '#FFB967']; + + var option = { + backgroundColor: '#fff', + grid: { + left: '3%', + right: '8%', + bottom: '15%', + containLabel: true + }, + + tooltip: { + show: "true", + trigger: 'item', + formatter: '{c0}', + backgroundColor: 'rgba(0,0,0,0.7)', // 背景 + padding: [8, 10], //内边距 + extraCssText: 'box-shadow: 0 0 3px rgba(255, 255, 255, 0.4);', //添加阴影 + axisPointer: { // 坐标轴指示器,坐标轴触发有效 + type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' + } + }, + xAxis: { + type: 'value', + axisTick: { + show: false + }, + axisLine: { + show: true, + lineStyle: { + color: '#CCCCCC' + } + }, + splitLine: { + show: false, + lineStyle: { + color: '#CCCCCC' + } + }, + axisLabel: { + textStyle: { + color: '#656565', + fontWeight: 'normal', + fontSize: '12' + }, + formatter: '{value}' + } + }, + yAxis: { + type: 'category', + axisLine: { + lineStyle: { + color: '#cccccc' + } + }, + splitLine: { + show: false + }, + axisTick: { + show: false + }, + splitArea: { + show: false + }, + axisLabel: { + inside: false, + textStyle: { + color: '#656565', + fontWeight: 'normal', + fontSize: '12' + } + }, + data: names + }, + series: [{ + name: '', + type: 'bar', + itemStyle: { + normal: { + show: true, + color: function(params) { + return Color[params.dataIndex] + }, + barBorderRadius: 50, + borderWidth: 0, + borderColor: '#333' + } + }, + barGap: '0%', + barCategoryGap: '50%', + data: values + } + + ] + }; + effChart.setOption(option); +} +class Colleagechartzu extends Component { + + constructor(props) { + super(props); + this.state = { + } + } + + componentDidMount() { + startechart(this.props.data,this.props.datavule) + } + + + componentDidUpdate = (prevProps) => { + if (prevProps.data!= this.props.data) { + startechart(this.props.data,this.props.datavule) + } + } + + + render() { + let {data}=this.props; + + return ( +
+ +
+
+ + + + +
+ + ) + } +} + +export default Colleagechartzu; diff --git a/public/react/src/college/colleagecss/colleage.css b/public/react/src/college/colleagecss/colleage.css new file mode 100644 index 000000000..801b86603 --- /dev/null +++ b/public/react/src/college/colleagecss/colleage.css @@ -0,0 +1,213 @@ +.yslstatistic-header { + width: 100%; + height: 240px; + background-image: url('/images/educoder/statistics.jpg'); + background-size: 100% 100%; +} +.yslborder{ + border: 1px solid; +} +.yslstatistic-header-title{ + flex: 1; + display: flex; + align-items: center; + color: #4CACFF; + font-size: 32px; +} +.yslstatistic-header-content{ + width: 100%; + display: flex; + justify-content: space-around; +} +.yslstatistic-header-item{ + margin-bottom: 22px; + display: flex; + flex-direction: column; + align-items: center; + color: #fff; +} +.yslstatistic-header-item-label{ + color: #989898; +} + +.yslstatistic-base-item-label{ + width: 217px; + text-align: center; + font-size: 16px; + height: 48px; + line-height: 48px; + color: #686868; + background: #F5F5F5; + border-top: 1px solid #EBEBEB; +} +.yslstatistic-base-item-labels{ + width: 217px; + text-align: center; + height: 100px; + line-height: 100px; + background: #ffffff; + border-top: 1px solid #EBEBEB; + border-bottom: 1px solid #EBEBEB; +} +.yslstatistic-base-item-labelsp{ + color: #000000; + font-size: 24px; +} +.yslstatistic-base-item-labelsspan{ + color: #000000; + margin-left: 5px; + font-size: 16px; +} +.jibenshiyong100{ + width: 100%; +} + +.yslstatistic-header-item-content{ + font-size: 24px; +} +/* 中间居中 */ +.intermediatecenter{ + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} +/* 简单居中 */ +.intermediatecenterysls{ + display: flex; + align-items: center; +} +.spacearound{ + display: flex; + justify-content: space-around; + +} +.spacebetween{ + display: flex; + justify-content: space-between; +} +/* 头顶部居中 */ +.topcenter{ + display: -webkit-flex; + flex-direction: column; + align-items: center; + +} + + +/* x轴正方向排序 */ +/* 一 二 三 四 五 六 七 八 */ +.sortinxdirection{ + display: flex; + flex-direction:row; +} +/* x轴反方向排序 */ +/* 八 七 六 五 四 三 二 一 */ +.xaxisreverseorder{ + display: flex; + flex-direction:row-reverse; +} +/* 垂直布局 正方向*/ +/* 一 + 二 + 三 + 四 + 五 + 六 + 七 + 八 */ +.verticallayout{ + display: flex; + flex-direction:column; +} +/* 垂直布局 反方向*/ +.reversedirection{ + display: flex; + flex-direction:column-reverse; +} + +.h4{ + font-size: 1.5rem; + font-weight: 500 !important; +} +.ysllinjibenshiyong{ + font-weight: 500; + line-height: 1.2; + padding: 2rem 1.25rem; + border-bottom: unset; + background:#fff; +} +.linjibenshiyong{ + font-weight: 500; + line-height: 1.2; + padding: 2rem 1.25rem; + border-bottom: unset; + background:#fff; + box-shadow:0px 6px 12px 0px rgba(0,0,0,0.1); + border-radius:2px; +} +.yslslinjibenshiyong{ + font-weight: 500; + line-height: 1.2; + border-bottom: unset; + box-shadow:0px 6px 12px 0px rgba(0,0,0,0.1); + border-radius:2px; +} +.yinyin{ + background: #fff; + box-shadow:0px 6px 12px 0px rgba(0,0,0,0.1); + border-radius:2px; +} +.edu-back-eeee{ + background:#EEEEEE !important; +} +.mt-4{ + margin-top: 1.5rem !important; +} + +.statistic-label{ + padding: 2rem 1.25rem; + font-size: 1.5rem; + font-weight: 400 !important; +} +.mb50{ + padding-bottom: 50px !important; +} +.mt40{ + margin-top: 40px; +} +.mb80{ + margin-bottom: 80px; +} +.task-hide{overflow:hidden; white-space: nowrap; text-overflow:ellipsis;} +a:hover{ + color:#0056b3; +} +.color-blue{ + color: #4CACFF; +} + +.color-huang{ + color:#ffc107 !important +} +.maxnamewidth105{ + max-width: 105px; + overflow:hidden; + text-overflow:ellipsis; + white-space:nowrap; + cursor: default; +} +.maxnamewidth247{ + max-width: 247px; + overflow:hidden; + text-overflow:ellipsis; + white-space:nowrap; + cursor: default; +} +.maxnamewidth340{ + max-width: 340px; + overflow:hidden; + text-overflow:ellipsis; + white-space:nowrap; + cursor: default; +} diff --git a/public/react/src/common/components/comment/CommentForm.js b/public/react/src/common/components/comment/CommentForm.js new file mode 100644 index 000000000..73e36cff9 --- /dev/null +++ b/public/react/src/common/components/comment/CommentForm.js @@ -0,0 +1,116 @@ +/* + * @Description: 评论表单 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-17 17:32:55 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-18 17:51:44 + */ +import React, { useState } from 'react'; +import { Form, Button, Input } from 'antd'; +import QuillForEditor from '../../quillForEditor'; +import { QuillDeltaToHtmlConverter } from 'quill-delta-to-html' +const FormItem = Form.Item; + +function CommentForm (props) { + + const { + commentCtxChagne, + onCancel, + onSubmit, + form + } = props; + + const { getFieldDecorator } = form; + const [ctx, setCtx] = useState(''); + + const options = [ + ['bold', 'italic', 'underline'], + [{header: [1,2,3,false]}], + ['blockquote', 'code-block'], + ['link', 'image'], + ['formula'] + ]; + // const { form: { getFieldDecorator } } = props; + const [showQuill, setShowQuill] = useState(false); + // 点击输入框 + const handleInputClick = () => { + setShowQuill(true); + } + // 取消 + const handleCancle = () => { + setShowQuill(false); + onCancel && onCancel(); + } + + // 编辑器内容变化时 + const handleContentChange = (content) => { + setCtx(content); + try { + const _html = new QuillDeltaToHtmlConverter(content.ops, {}).convert(); + // props.form.setFieldsValue({'comment': _html.replace(/<\/?[^>]*>/g, '')}); + props.form.setFieldsValue({'comment': _html}); + } catch (error) { + console.log(error); + } + } + // 发送 + const handleSubmit = (e) => { + e.preventDefault(); + props.form.validateFields((err, values) => { + if (!err) { + setShowQuill(false); + const content = ctx; + props.form.setFieldsValue({'comment': ''}); + setCtx(''); + console.log(content); + onSubmit && onSubmit(content); + } + }); + } + return ( + + + { + getFieldDecorator('comment', { + rules: [ + { required: true, message: '评论内容不能为空'} + ], + })( + + ) + } + + + + + + + + + ); +} + +export default Form.create()(CommentForm); diff --git a/public/react/src/common/components/comment/CommentIcon.js b/public/react/src/common/components/comment/CommentIcon.js new file mode 100644 index 000000000..5440e2579 --- /dev/null +++ b/public/react/src/common/components/comment/CommentIcon.js @@ -0,0 +1,32 @@ +/* + * @Description: + * @Author: tangjiang + * @Github: + * @Date: 2019-12-18 10:49:46 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-18 11:39:23 + */ +import './index.scss'; +import React from 'react'; +import { Icon } from 'antd'; +function CommentIcon ({ + type, // 图标类型 + count, // 评论数 + iconClick, + ...props +}) { + + // 点击图标 + const handleSpanClick = () => { + iconClick && iconClick(); + } + + return ( + + + { count } + + ) +} + +export default CommentIcon; diff --git a/public/react/src/common/components/comment/CommentItem.js b/public/react/src/common/components/comment/CommentItem.js new file mode 100644 index 000000000..19da645f5 --- /dev/null +++ b/public/react/src/common/components/comment/CommentItem.js @@ -0,0 +1,165 @@ +/* + * @Description: 评论单列 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-17 17:35:17 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-19 18:02:28 + */ +import './index.scss'; +import React, { useState } from 'react'; +import CommentIcon from './CommentIcon'; +import { getImageUrl, CNotificationHOC } from 'educoder' +import { Icon } from 'antd'; +import moment from 'moment'; +// import QuillForEditor from '../../quillForEditor'; +import CommentForm from './CommentForm'; + +// import {ModalConfirm} from '../ModalConfirm'; +function CommentItem ({ + options, + confirm +}) { + // 显示评论输入框 + const [showQuill, setShowQuill] = useState(false); + // 加载更多评论内容 + const [showMore, setShowMore] = useState(false); + // 箭头方向 + const [arrow, setArrow] = useState(false); + // 删除评论 + const deleteComment = () => { + console.log('删除评论...'); + confirm({ + title: '提示', + content: (

确定要删除该条回复吗?

), + onOk () { + console.log('点击了删除'); + } + }); + // ModalConfirm('提示', (

确定要删除该条回复吗?

), () => { + // console.log('点击了删除'); + // }); + } + + // 评论头像 + const commentAvatar = (url) => ( + + ); + + // 评论信息 + const commentInfo = () => ( +

+ 用户名 + {moment(new Date(), 'YYYYMMDD HHmmss').fromNow()} + +

+ ); + + // 评论内容 + const commentCtx = (ctx) => ( +

+ 这是评论内容这是评论内容这是评论内容这是评论内容这是评论内容这是评论内容这是评论内容这是评论内容这是评论内容这是评论内容 +

+ ); + + // 加载更多 + const handleOnLoadMore = () => { + if (!arrow) { + // 展开所有 + } else { + // 收起 + } + setArrow(!arrow); + }; + + // 评论追加内容 + const commentAppend = () => { + + return ( +
    +
  • + {commentAvatar()} +
    + {commentInfo()} + {commentCtx()} +
    +
  • +
  • + {commentAvatar()} +
    + {commentInfo()} + {commentCtx()} +
    +
  • +
  • + {commentAvatar()} +
    + {commentInfo()} + {commentCtx()} +
    +
  • + +
  • +

    展开其余23条评论

    +

    + +

    +
  • +
+ ); + }; + // 点击图标 + const handleIconClick = () => {} + + // 点击评论icon + const handleClickMessage = () => { + setShowQuill(true); + } + + // 点击取消 + const handleClickCancel = () => { + setShowQuill(false); + } + + // 点击保存 + const handleClickSubmit = (content) => { + // 保存并关闭 + setShowQuill(false); + console.log('获取保存内容', content); + } + + return ( +
  • + {commentAvatar()} +
    + {commentInfo()} + {commentCtx()} + + {commentAppend()} + +
    + + {/* 回复 */} + + {/* 点赞 */} + +
    + +
    + +
    +
    +
  • + ); +} + +export default CNotificationHOC() (CommentItem); diff --git a/public/react/src/common/components/comment/CommentList.js b/public/react/src/common/components/comment/CommentList.js new file mode 100644 index 000000000..9d8cde87b --- /dev/null +++ b/public/react/src/common/components/comment/CommentList.js @@ -0,0 +1,20 @@ +/* + * @Description: 评论列表页 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-17 17:34:00 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-18 11:48:09 + */ +import './index.scss'; +import React from 'react'; +import CommentItem from './CommentItem'; +function CommentList ({}) { + return ( +
      + +
    + ); +} + +export default CommentList; diff --git a/public/react/src/common/components/comment/index.js b/public/react/src/common/components/comment/index.js new file mode 100644 index 000000000..f0ecf3309 --- /dev/null +++ b/public/react/src/common/components/comment/index.js @@ -0,0 +1,22 @@ +/* + * @Description: 评论组件 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-17 17:31:33 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-18 11:47:39 + */ +import React from 'react'; +import CommentForm from './CommentForm'; +import CommentList from './CommentList'; +function Comment (props) { + + return ( + + + + + ); +} + +export default Comment; diff --git a/public/react/src/common/components/comment/index.scss b/public/react/src/common/components/comment/index.scss new file mode 100644 index 000000000..816e6da6c --- /dev/null +++ b/public/react/src/common/components/comment/index.scss @@ -0,0 +1,111 @@ +$bdColor: rgba(244,244,244,1); +$bgColor: rgba(250,250,250,1); +$lh14: 14px; +$lh22: 22px; +$fz14: 14px; +$fz12: 12px; +$ml: 20px; + +.comment_list_wrapper{ + box-sizing: border-box; + border-top: 1px solid $bdColor; + + .comment_item_area{ + display: flex; + padding: 20px 0; + box-sizing: border-box; + border-bottom: 1px solid $bdColor; + .flex-image{ + width: 48px; + height: 48px; + border-radius: 50%; + } + .item-desc{ + flex: 1; + margin-left: $ml; + } + .item-header{ + font-size: $fz14; + line-height: $lh14; + color: #333; + .item-time{ + font-size: $fz12; + line-height: $lh14; + margin-left: $ml; + } + .item-close{ + float: right; + cursor: pointer; + } + } + .item-ctx{ + line-height: $lh22; + font-size: $fz12; + color: #333; + margin-top: 10px; + } + .comment_icon_area{ + display: flex; + justify-content: flex-end; + margin-top: 10px; + + .comment-icon-margin{ + margin-left: 30px; + } + } + + .comment_item_quill{ + margin-top: 20px; + } + } + .comment_icon_count{ + cursor: pointer; + font-size: 12px; + line-height: 1.5; + + .comment_icon{ + color: #333; + } + .comment_count{ + color: #999999; + margin-left: 10px; + transition: color .3s; + } + + &:hover{ + .comment_icon, + .comment_count{ + color: #5091FF; + } + } + } + .comment_item_append_list{ + position: relative; + background-color: $bgColor; + border-radius: 5px; + padding: 0 15px 10px; + margin: 15px 0; + &::before { + position: absolute; + left: 15px; + bottom: 100%; + height: 0; + width: 0; + content: ''; + // border: 5px solid transparent; + border: 10px solid transparent; + border-bottom-color: $bgColor; + } + + .comment_item_loadmore{ + padding-top: 10px; + cursor: pointer; + .loadmore-txt, + .loadmore-icon{ + color: #999; + text-align: center; + font-size: $fz12; + } + } + } +} \ No newline at end of file diff --git a/public/react/src/common/quillForEditor/ImageBlot.js b/public/react/src/common/quillForEditor/ImageBlot.js new file mode 100644 index 000000000..091bd2c1f --- /dev/null +++ b/public/react/src/common/quillForEditor/ImageBlot.js @@ -0,0 +1,54 @@ +/* + * @Description: 重写图片 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-16 15:50:45 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-17 16:44:48 + */ +import Quill from "quill"; + +const BlockEmbed = Quill.import('blots/block/embed'); + +export default class ImageBlot extends BlockEmbed { + + static create(value) { + + const node = super.create(); + + node.setAttribute('alt', value.alt); + node.setAttribute('src', value.url); + + if (value.width) { + node.setAttribute('width', value.width); + } + if (value.height) { + node.setAttribute('height', value.height); + } + // 宽度和高度都不存在时, + if (!value.width && !value.height) { + node.setAttribute('display', 'block'); + node.setAttribute('width', '100%'); + } + // 给图片添加点击事件 + node.onclick = () => { + value.onClick && value.onClick(value.url); + } + return node; + } + + static value (node) { + + return { + alt: node.getAttribute('alt'), + url: node.getAttribute('src'), + onclick: node.onclick, + // width: node.width, + // height: node.height, + display: node.getAttribute('display') + }; + } +} + +ImageBlot.blotName = 'image'; +ImageBlot.tagName = 'img'; \ No newline at end of file diff --git a/public/react/src/common/quillForEditor/deepEqual.js b/public/react/src/common/quillForEditor/deepEqual.js new file mode 100644 index 000000000..6f2b276bf --- /dev/null +++ b/public/react/src/common/quillForEditor/deepEqual.js @@ -0,0 +1,47 @@ +function deepEqual (prev, current) { + if (prev === current) { // 基本类型比较,值,类型都相同 或者同为 null or undefined + return true; + } + + if ((!prev && current) + || (prev && !current) + || (!prev && !current) + ) { + return false; + } + + if (Array.isArray(prev)) { + if (!Array.isArray(current)) return false; + if (prev.length !== current.length) return false; + + for (let i = 0; i < prev.length; i++) { + if (!deepEqual(current[i], prev[i])) { + return false; + } + } + return true; + } + + if (typeof current === 'object') { + if (typeof prev !== 'object') return false; + const prevKeys = Object.keys(prev); + const curKeys = Object.keys(current); + + if (prevKeys.length !== curKeys.length) return false; + + prevKeys.sort(); + curKeys.sort(); + + for (let i = 0; i < prevKeys.length; i++) { + if (prevKeys[i] !== curKeys[i]) return false; + const key = prevKeys[i]; + if (!deepEqual(prev[key], current[key])) return false; + } + + return true; + } + + return false; +} + +export default deepEqual; diff --git a/public/react/src/common/quillForEditor/index.js b/public/react/src/common/quillForEditor/index.js new file mode 100644 index 000000000..6e6c01886 --- /dev/null +++ b/public/react/src/common/quillForEditor/index.js @@ -0,0 +1,166 @@ +/* + * @Description: quill 编辑器 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-18 08:49:30 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-19 16:58:50 + */ +import 'quill/dist/quill.core.css'; // 核心样式 +import 'quill/dist/quill.snow.css'; // 有工具栏 +import 'quill/dist/quill.bubble.css'; // 无工具栏 +import 'katex/dist/katex.min.css'; // katex 表达式样式 +import React, { useState, useRef, useEffect } from 'react'; +import Quill from 'quill'; +import katex from 'katex'; +import deepEqual from './deepEqual.js' +import { fetchUploadImage } from '../../services/ojService.js'; +import { getImageUrl } from 'educoder' +import ImageBlot from './ImageBlot'; + +window.Quill = Quill; +window.katex = katex; +Quill.register(ImageBlot); + +function QuillForEditor ({ + placeholder, + readOnly, + options, + value, + imgAttrs = {}, // 指定图片的宽高 + style = {}, + wrapStyle = {}, + showUploadImage, + onContentChange +}) { + // toolbar 默认值 + const defaultConfig = [ + ['bold', 'italic', 'underline'], + [{align: []}, {list: 'ordered'}, {list: 'bullet'}], // 列表 + [{script: 'sub'}, {script: 'super'}], + [{ 'color': [] }, { 'background': [] }], + [{header: [1,2,3,4,5,false]}], + ['blockquote', 'code-block'], + ['link', 'image', 'video'], + ['formula'], + ['clean'] + ]; + + const editorRef = useRef(null); + // quill 实例 + const [quill, setQuill] = useState(null); + const [selection, setSelection] = useState(null); + + // 文本内容变化时 + const handleOnChange = content => { + // console.log('编辑器内容====》》》》', content); + onContentChange && onContentChange(content); + }; + + const renderOptions = options || defaultConfig; + // quill 配置信息 + const quillOption = { + modules: { + toolbar: renderOptions + }, + readOnly, + placeholder, + theme: readOnly ? 'bubble' : 'snow' + }; + + + useEffect(() => { + const _quill = new Quill(editorRef.current, quillOption); + setQuill(_quill); + + // 处理图片上传功能 + _quill.getModule('toolbar').addHandler('image', (e) => { + const input = document.createElement('input'); + input.setAttribute('type', 'file'); + input.setAttribute('accept', 'image/*'); + input.click(); + + input.onchange = async (e) => { + const file = input.files[0]; // 获取文件信息 + const formData = new FormData(); + formData.append('file', file); + + const range = _quill.getSelection(true); + let fileUrl = ''; // 保存上传成功后图片的url + // 上传文件 + const result = await fetchUploadImage(formData); + // 获取上传图片的url + if (result.data && result.data.id) { + fileUrl = getImageUrl(`api/attachments/${result.data.id}`); + } + // 根据id获取文件路径 + const { width, height } = imgAttrs; + // console.log('上传图片的url:', fileUrl); + if (fileUrl) { + _quill.insertEmbed(range.index, 'image', { + url: fileUrl, + alt: '图片信息', + onClick: showUploadImage, + width, + height + }); + } + } + }); + }, []); + + // 设置值 + useEffect(() => { + if (!quill) return + const previous = quill.getContents() + const current = value + + if (!deepEqual(previous, current)) { + setSelection(quill.getSelection()) + if (typeof value === 'string') { + quill.clipboard.dangerouslyPasteHTML(value, 'api') + } else { + quill.setContents(value) + } + } + }, [quill, value, setQuill]); + + // 清除选择区域 + useEffect(() => { + if (quill && selection) { + quill.setSelection(selection) + setSelection(null) + } + }, [quill, selection, setSelection]); + + // 设置placeholder值 + useEffect(() => { + if (!quill || !quill.root) return; + quill.root.dataset.placeholder = placeholder; + }, [quill, placeholder]); + + // 处理内容变化 + useEffect(() => { + if (!quill) return; + if (typeof handleOnChange !== 'function') return; + let handler; + quill.on( + 'text-change', + (handler = () => { + handleOnChange(quill.getContents()); // getContents: 检索编辑器内容 + }) + ); + return () => { + quill.off('text-change', handler); + } + }, [quill, handleOnChange]); + + // 返回结果 + return ( +
    +
    +
    + ); +} + +export default QuillForEditor; diff --git a/public/react/src/common/reactQuill/ImageBlot.js b/public/react/src/common/reactQuill/ImageBlot.js new file mode 100644 index 000000000..091bd2c1f --- /dev/null +++ b/public/react/src/common/reactQuill/ImageBlot.js @@ -0,0 +1,54 @@ +/* + * @Description: 重写图片 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-16 15:50:45 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-17 16:44:48 + */ +import Quill from "quill"; + +const BlockEmbed = Quill.import('blots/block/embed'); + +export default class ImageBlot extends BlockEmbed { + + static create(value) { + + const node = super.create(); + + node.setAttribute('alt', value.alt); + node.setAttribute('src', value.url); + + if (value.width) { + node.setAttribute('width', value.width); + } + if (value.height) { + node.setAttribute('height', value.height); + } + // 宽度和高度都不存在时, + if (!value.width && !value.height) { + node.setAttribute('display', 'block'); + node.setAttribute('width', '100%'); + } + // 给图片添加点击事件 + node.onclick = () => { + value.onClick && value.onClick(value.url); + } + return node; + } + + static value (node) { + + return { + alt: node.getAttribute('alt'), + url: node.getAttribute('src'), + onclick: node.onclick, + // width: node.width, + // height: node.height, + display: node.getAttribute('display') + }; + } +} + +ImageBlot.blotName = 'image'; +ImageBlot.tagName = 'img'; \ No newline at end of file diff --git a/public/react/src/common/reactQuill/ReactQuill.js b/public/react/src/common/reactQuill/ReactQuill.js new file mode 100644 index 000000000..1b4209409 --- /dev/null +++ b/public/react/src/common/reactQuill/ReactQuill.js @@ -0,0 +1,45 @@ +/* + * @Description: + * @Author: tangjiang + * @Github: + * @Date: 2019-12-09 09:09:42 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-18 08:46:20 + */ +import 'quill/dist/quill.core.css'; // 核心样式 +import 'quill/dist/quill.snow.css'; // 有工具栏 +import 'quill/dist/quill.bubble.css'; // 无工具栏 +import 'katex/dist/katex.min.css'; // katex 表达式样式 +import React, { useState, useReducer, useEffect } from 'react'; +import useQuill from './useQuill'; + +function ReactQuill ({ + disallowColors, // 不可见时颜色 + placeholder, // 提示信息 + uploadImage, // 图片上传 + onChange, // 内容变化时 + options, // 配置信息 + value, // 显示的内容 + style, + showUploadImage // 显示上传图片 +}) { + + const [element, setElement] = useState(); // quill 渲染节点 + + useQuill({ + disallowColors, + placeholder, + uploadImage, + onChange, + options, + value, + showUploadImage, + element + }); + + return ( +
    + ); +} + +export default ReactQuill; diff --git a/public/react/src/common/reactQuill/deepEqual.js b/public/react/src/common/reactQuill/deepEqual.js new file mode 100644 index 000000000..6f2b276bf --- /dev/null +++ b/public/react/src/common/reactQuill/deepEqual.js @@ -0,0 +1,47 @@ +function deepEqual (prev, current) { + if (prev === current) { // 基本类型比较,值,类型都相同 或者同为 null or undefined + return true; + } + + if ((!prev && current) + || (prev && !current) + || (!prev && !current) + ) { + return false; + } + + if (Array.isArray(prev)) { + if (!Array.isArray(current)) return false; + if (prev.length !== current.length) return false; + + for (let i = 0; i < prev.length; i++) { + if (!deepEqual(current[i], prev[i])) { + return false; + } + } + return true; + } + + if (typeof current === 'object') { + if (typeof prev !== 'object') return false; + const prevKeys = Object.keys(prev); + const curKeys = Object.keys(current); + + if (prevKeys.length !== curKeys.length) return false; + + prevKeys.sort(); + curKeys.sort(); + + for (let i = 0; i < prevKeys.length; i++) { + if (prevKeys[i] !== curKeys[i]) return false; + const key = prevKeys[i]; + if (!deepEqual(prev[key], current[key])) return false; + } + + return true; + } + + return false; +} + +export default deepEqual; diff --git a/public/react/src/common/reactQuill/flatten.js b/public/react/src/common/reactQuill/flatten.js new file mode 100644 index 000000000..237cb543f --- /dev/null +++ b/public/react/src/common/reactQuill/flatten.js @@ -0,0 +1,26 @@ +/* + * @Description: 将多维数组转变成一维数组 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-09 09:35:01 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-16 11:36:22 + */ +function flatten (array) { + return flatten.rec(array, []); +} + +flatten.rec = function flatten (array, result) { + + for (let item of array) { + if (Array.isArray(item)) { + flatten(item, result); + } else { + result.push(item); + } + } + + return result; +} + +export default flatten; diff --git a/public/react/src/common/reactQuill/index.js b/public/react/src/common/reactQuill/index.js new file mode 100644 index 000000000..56a1a8d1f --- /dev/null +++ b/public/react/src/common/reactQuill/index.js @@ -0,0 +1,108 @@ +/* + * @Description: 入口文件 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-17 10:41:48 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-17 20:34:40 + */ +import React, { useState, useCallback, useEffect } from 'react'; +import ReactQuill from './lib'; + +function Wrapper (props) { + // 默认工具栏配置项 + const toolbarConfig = [ + ['bold', 'italic', 'underline'], + [{align: []}, {list: 'ordered'}, {list: 'bullet'}], // 列表 + [{script: 'sub'}, {script: 'super'}], + [{header: [1,2,3,4,5,false]}], + ['blockquote', 'code-block'], + ['link', 'image', 'video'], + ['formula'], + ['clean'] + ]; + + const [placeholder] = useState(props.placeholder || 'placeholder'); + const [disableBold] = useState(false); + const [value, setValue] = useState(props.value || ''); + const [toolbar, setToolbar] = useState(toolbarConfig); + const [theme, setTheme] = useState(props.theme || 'snow'); + const [readOnly] = useState(props.readOnly || false); + + const { + onContentChagne, // 当编辑器内容变化时调用该函数 + showUploadImage, // 显示上传图片, 返回url,主要用于点击图片放大 + } = props; + + // 配置信息 + const options = { + modules: { + toolbar: toolbar, + clipboard: { + matchVisual: false + } + }, + readOnly: readOnly, + theme: theme + } + // 配置信息 + useEffect (() => { + if (props.options) { + setToolbar(props.options); + } + setTheme(props.theme || 'snow'); + setValue(props.value); + }, [props]); + + // 当内容变化时 + const handleOnChange = useCallback( + contents => { + if (disableBold) { + setValue({ + ops: contents.ops.map(x => { + x = {...x}; + if (x && x.attributes && x.attributes.bold) { + x.attributes = { ...x.attributes }; + delete x.attributes.bold; + if (!Object.keys(x.attributes).length) { + delete x.attributes; + } + } + return x; + }) + }); + } else { + setValue(contents); + } + onContentChagne && onContentChagne(contents); + }, [disableBold] + ); + + // 图片上传 + const handleUploadImage = (files) => { + console.log('选择的图片信息', files); + } + + // 显示图片 + const handleShowUploadImage = (url) => { + // console.log('上传的图片url:', url); + showUploadImage && showUploadImage(url); + } + + return ( + + handleShowUploadImage(url)} + /> + + ); +} + +export default Wrapper; +// ReactDOM.render(, document.querySelector('#root')); diff --git a/public/react/src/common/reactQuill/index.scss b/public/react/src/common/reactQuill/index.scss new file mode 100644 index 000000000..b6da52bf5 --- /dev/null +++ b/public/react/src/common/reactQuill/index.scss @@ -0,0 +1,32 @@ +#quill-toolbar{ + .quill-btn{ + vertical-align: middle; + } + .quill_image{ + display: inline-block; + position: relative; + vertical-align: middle; + width: 28px; + height: 24px; + overflow: hidden; + .image_input{ + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + opacity: 0; + } + .ql-image{ + position: relative; + left: 0; + top: 0; + } + } +} + +.react_quill_area{ + .ql-toolbar:not(:last-child) { + display: none; + } +} \ No newline at end of file diff --git a/public/react/src/common/reactQuill/lib.js b/public/react/src/common/reactQuill/lib.js new file mode 100644 index 000000000..430a95bb7 --- /dev/null +++ b/public/react/src/common/reactQuill/lib.js @@ -0,0 +1,13 @@ +/* + * @Description: 导出 ReactQuill + * @Author: tangjiang + * @Github: + * @Date: 2019-12-09 09:08:24 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-16 11:37:13 + */ +import ReactQuill from './ReactQuill'; +import useQuill from './useQuill'; + +export default ReactQuill; +export { useQuill }; diff --git a/public/react/src/common/reactQuill/useDeepEqualMemo.js b/public/react/src/common/reactQuill/useDeepEqualMemo.js new file mode 100644 index 000000000..948e21781 --- /dev/null +++ b/public/react/src/common/reactQuill/useDeepEqualMemo.js @@ -0,0 +1,27 @@ +/* + * @Description: + * @Author: tangjiang + * @Github: + * @Date: 2019-12-12 19:48:55 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-16 11:38:16 + */ +import { useState, useEffect } from 'react'; +import deepEqual from './deepEqual'; + +function useDeepEqual (input) { + + const [value, setValue] = useState(input); + + useEffect(() => { + + if (!deepEqual(input, value)) { + setValue(input) + } + + }, [input, value]); + + return value; +} + +export default useDeepEqual; diff --git a/public/react/src/common/reactQuill/useMountQuill.js b/public/react/src/common/reactQuill/useMountQuill.js new file mode 100644 index 000000000..c2313c480 --- /dev/null +++ b/public/react/src/common/reactQuill/useMountQuill.js @@ -0,0 +1,148 @@ +/* + * @Description: 创建 reactQuill实例 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-09 09:31:42 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-17 20:42:05 + */ +import Quill from 'quill'; // 导入quill +import { useState, useEffect, useMemo } from 'react'; +import flatten from './flatten.js'; +import useDeepEqualMemo from './useDeepEqualMemo'; +import Katex from 'katex'; +import ImageBlot from './ImageBlot'; +import { fetchUploadImage } from '../../services/ojService.js'; +import { getImageUrl } from 'educoder' +window.katex = Katex; + +Quill.register(ImageBlot); + +function useMountQuill ({ + element, + options: passedOptions, + uploadImage, + showUploadImage, + imgAttrs = {} // 指定图片的宽高属性 +}) { + + // 是否引入 katex + const [katexLoaded, setKatexLoaded] = useState(Boolean(window.katex)) + const [quill, setQuill] = useState(null); + + const options = useDeepEqualMemo(passedOptions); + console.log('use mount quill: ', passedOptions); + + // 判断options中是否包含公式 + const requireKatex = useMemo(() => { + return flatten(options.modules.toolbar).includes('formula'); + }, [options]); + + // 加载katex + useEffect(() => { + if (!requireKatex) return; + if (katexLoaded) return; + + const interval = setInterval(() => { + if (window.katex) { + setKatexLoaded(true); + clearInterval(interval); + } + }); + + return () => { // 定义回调清除定时器 + clearInterval(interval); + } + + }, [ + setKatexLoaded, + katexLoaded, + requireKatex + ]); + + // 加载 quill + useEffect(() => { + if (!element) return; + if (requireKatex && !katexLoaded) { + element.innerHTML = ` +
    + Loading Katex... +
    + ` + } + // 清空内容 + element.innerHTML = ''; + console.log(element); + // 创建 quill 节点 + const quillNode = document.createElement('div'); + element.appendChild(quillNode); // 将quill节点追回到 element 元素中 + + const quill = new Quill(element, options); + setQuill(quill); + // 加载上传图片功能 + if (typeof uploadImage === 'function') { + quill.getModule('toolbar').addHandler('image', (e) => { + // 创建type类型输入框加载本地图片 + const input = document.createElement('input'); + input.setAttribute('type', 'file'); + input.setAttribute('accept', 'image/*'); + input.click(); + + input.onchange = async (e) => { + const file = input.files[0]; // 获取文件信息 + const formData = new FormData(); + formData.append('file', file); + + // const reader = new FileReader(); + // reader.readAsDataURL(file); + // console.log('文件信息===>>', reader); + // reader.onload = function (e) { + // debugger; + // console.log('文件信息===>>', e.target.result); + // const image = new Image(); + // image.src = e.target.result; + + // image.onload = function () { + // // file.width = + // console.log(image.width, image.height); + // } + // } + + const range = quill.getSelection(true); + let fileUrl = ''; // 保存上传成功后图片的url + // 上传文件 + const result = await fetchUploadImage(formData); + // 获取上传图片的url + if (result.data && result.data.id) { + fileUrl = getImageUrl(`api/attachments/${result.data.id}`); + } + // 根据id获取文件路径 + const { width, height } = imgAttrs; + // console.log('上传图片的url:', fileUrl); + if (fileUrl) { + quill.insertEmbed(range.index, 'image', { + url: fileUrl, + alt: '', + onClick: showUploadImage, + width, + height + }); + } + } + }); + } + + return () => { + element.innerHTML = ''; + } + }, [ + element, + options, + requireKatex, + katexLoaded, + ]); + + return quill; +} + +export default useMountQuill; diff --git a/public/react/src/common/reactQuill/useQuill.js b/public/react/src/common/reactQuill/useQuill.js new file mode 100644 index 000000000..b959dbc52 --- /dev/null +++ b/public/react/src/common/reactQuill/useQuill.js @@ -0,0 +1,60 @@ +/* + * @Description: + * @Author: tangjiang + * @Github: + * @Date: 2019-12-09 09:09:50 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-17 15:46:50 + */ +import useQuillPlaceholder from './useQuillPlaceholder'; +import useQuillValueSync from './useQuillValueSync'; +import useQuillOnChange from './useQuillOnChange'; +import useMountQuill from './useMountQuill'; +import { useEffect } from 'react'; + +function useQuill ({ + disallowColors, + placeholder, + uploadImage, + onChange, + options, + value, + element, + showUploadImage +}) { + + // 获取 quill 实例 + const quill = useMountQuill({ + element, + options, + uploadImage, + showUploadImage + }); + + useEffect(() => { + if (disallowColors && quill) { + quill.clipboard.addMatcher(Node.ELEMENT_NODE, (node, delta) => { + delta.ops = delta.ops.map(op => { + if (op.attributes && op.attributes.color) { + const { color, ...attributes } = op.attributes; + return { + ...op, + attributes + } + } + return op; + }); + return delta; + }); + } + }, [ + disallowColors, + quill + ]); + + useQuillPlaceholder(quill, placeholder); + useQuillValueSync(quill, value); + useQuillOnChange(quill, onChange); +} + +export default useQuill; diff --git a/public/react/src/common/reactQuill/useQuillOnChange.js b/public/react/src/common/reactQuill/useQuillOnChange.js new file mode 100644 index 000000000..45333a4e1 --- /dev/null +++ b/public/react/src/common/reactQuill/useQuillOnChange.js @@ -0,0 +1,33 @@ +/* + * @Description: + * @Author: tangjiang + * @Github: + * @Date: 2019-12-12 19:49:11 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-16 11:39:27 + */ +import { useEffect } from 'react'; + +function useQuillOnChange (quill, onChange) { + + useEffect(() => { + + if (!quill) return; + if (typeof onChange !== 'function') return; + + let handler; + + quill.on( + 'text-change', + (handler = () => { + onChange(quill.getContents()); // getContents: 检索编辑器内容 + }) + ); + + return () => { + quill.off('text-change', handler); + } + }, [quill, onChange]); +} + +export default useQuillOnChange; diff --git a/public/react/src/common/reactQuill/useQuillPlaceholder.js b/public/react/src/common/reactQuill/useQuillPlaceholder.js new file mode 100644 index 000000000..ccc341568 --- /dev/null +++ b/public/react/src/common/reactQuill/useQuillPlaceholder.js @@ -0,0 +1,22 @@ +/* + * @Description: + * @Author: tangjiang + * @Github: + * @Date: 2019-12-09 09:28:34 + * @LastEditors: tangjiang + * @LastEditTime: 2019-12-16 11:39:48 + */ +import { useEffect } from 'react' + +function useQuillPlaceholder ( + quill, + placeholder +) { + + useEffect(() => { + if (!quill || !quill.root) return; + quill.root.dataset.placeholder = placeholder; + }, [quill, placeholder]); +} + +export default useQuillPlaceholder; diff --git a/public/react/src/common/reactQuill/useQuillValueSync.js b/public/react/src/common/reactQuill/useQuillValueSync.js new file mode 100644 index 000000000..696d88949 --- /dev/null +++ b/public/react/src/common/reactQuill/useQuillValueSync.js @@ -0,0 +1,31 @@ +import { useEffect, useState } from 'react' +import deepEqual from './deepEqual.js' + +function useQuillValueSync(quill, value) { + const [selection, setSelection] = useState(null) + + useEffect(() => { + if (!quill) return + + const previous = quill.getContents() + const current = value + + if (!deepEqual(previous, current)) { + setSelection(quill.getSelection()) + if (typeof value === 'string') { + quill.clipboard.dangerouslyPasteHTML(value, 'api') + } else { + quill.setContents(value) + } + } + }, [quill, value, setSelection]) + + useEffect(() => { + if (quill && selection) { + quill.setSelection(selection) + setSelection(null) + } + }, [quill, selection, setSelection]) +} + +export default useQuillValueSync diff --git a/public/react/src/modules/developer/DeveloperHome.js b/public/react/src/modules/developer/DeveloperHome.js index 5f787659e..73a5c37a1 100644 --- a/public/react/src/modules/developer/DeveloperHome.js +++ b/public/react/src/modules/developer/DeveloperHome.js @@ -16,7 +16,7 @@ import MultipTags from './components/multiptags'; // import { Link } from 'react-router-dom'; import CONST from '../../constants'; import { withRouter } from 'react-router'; -import { toStore } from 'educoder'; +import { toStore, CNotificationHOC } from 'educoder'; // import MyIcon from '../../common/components/MyIcon'; const {tagBackground, diffText} = CONST; @@ -249,17 +249,26 @@ class DeveloperHome extends React.PureComponent { // 删除 handleClickDelete = (record) => { const { deleteItem } = this.props; - Modal.confirm({ - title: '删除', + this.props.confirm({ + title: '提示', content: `确定要删除${record.name}吗?`, - okText: '确定', - cancelText: '取消', onOk () { // 调用删除接口 console.log(record.identifier); deleteItem(record.identifier); } }); + // Modal.confirm({ + // title: '删除', + // content: `确定要删除${record.name}吗?`, + // okText: '确定', + // cancelText: '取消', + // onOk () { + // // 调用删除接口 + // console.log(record.identifier); + // deleteItem(record.identifier); + // } + // }); } // table条件变化时 handleTableChange = (pagination, filters, sorter) => { @@ -562,5 +571,5 @@ const mapDispatchToProps = (dispatch) => ({ export default withRouter(connect( mapStateToProps, mapDispatchToProps -)(DeveloperHome)); +)(CNotificationHOC() (DeveloperHome))); // export default DeveloperHome; diff --git a/public/react/src/modules/developer/components/controlSetting/index.js b/public/react/src/modules/developer/components/controlSetting/index.js index 587e1bee9..1d4d7834c 100644 --- a/public/react/src/modules/developer/components/controlSetting/index.js +++ b/public/react/src/modules/developer/components/controlSetting/index.js @@ -4,10 +4,10 @@ * @Github: * @Date: 2019-11-27 16:02:36 * @LastEditors: tangjiang - * @LastEditTime: 2019-12-13 17:32:33 + * @LastEditTime: 2019-12-19 19:47:32 */ import './index.scss'; -import React, { useState, useRef } from 'react'; +import React, { useState, useRef, useEffect } from 'react'; import { Tabs, Button, Icon } from 'antd'; import { connect } from 'react-redux'; import InitTabCtx from '../initTabCtx'; @@ -23,14 +23,15 @@ const ControlSetting = (props) => { submitLoading, identifier, excuteState, + showOrHideControl, commitRecordDetail, changeLoadingState, changeSubmitLoadingStatus, - showOrHideControl, + changeShowOrHideControl, // debuggerCode, // startDebuggerCode, // 外部存入 onDebuggerCode, - updateCode, + // updateCode, onSubmitForm } = props; const [defaultActiveKey, setDefaultActiveKey] = useState('1'); // 当前选中的tab @@ -44,10 +45,14 @@ const ControlSetting = (props) => { setDefaultActiveKey(key); } + useEffect(() => { + setShowTextResult(props.showOrHideControl); + }, [props]); + // 显示/隐藏tab const handleShowControl = () => { setShowTextResult(!showTextResult); - showOrHideControl(!showTextResult); + changeShowOrHideControl(!showTextResult); } // 调试代码 @@ -55,7 +60,7 @@ const ControlSetting = (props) => { // console.log(formRef.current.handleTestCodeFormSubmit); // 调出控制台界面 setShowTextResult(true); - showOrHideControl(true); + changeShowOrHideControl(true); formRef.current.handleTestCodeFormSubmit(() => { setDefaultActiveKey('2'); }); @@ -84,7 +89,7 @@ const ControlSetting = (props) => { @@ -131,19 +136,20 @@ const ControlSetting = (props) => { const mapStateToProps = (state) => { const {commonReducer, ojForUserReducer} = state; - const {loading, excuteState, submitLoading } = commonReducer; + const {loading, excuteState, submitLoading, showOrHideControl } = commonReducer; const { commitRecordDetail } = ojForUserReducer; return { loading, submitLoading, excuteState, + showOrHideControl, // identifier: user_program_identifier, commitRecordDetail // 提交详情 }; }; // changeSubmitLoadingStatus const mapDispatchToProps = (dispatch) => ({ - showOrHideControl: (flag) => dispatch(actions.showOrHideControl(flag)), + changeShowOrHideControl: (flag) => dispatch(actions.changeShowOrHideControl(flag)), changeLoadingState: (flag) => dispatch(actions.changeLoadingState(flag)), changeSubmitLoadingStatus: (flag) => dispatch(actions.changeSubmitLoadingStatus(flag)), debuggerCode: (identifier, values) => dispatch(actions.debuggerCode(identifier, values)), diff --git a/public/react/src/modules/developer/components/controlSetting/index.scss b/public/react/src/modules/developer/components/controlSetting/index.scss index 97838ce5c..31beda8a5 100644 --- a/public/react/src/modules/developer/components/controlSetting/index.scss +++ b/public/react/src/modules/developer/components/controlSetting/index.scss @@ -2,7 +2,8 @@ position: absolute; bottom: 0; width: 100%; - background:rgba(30,30,30,1); + // background: red; + // background:rgba(30,30,30,1); // height: 56px; .control_tab{ position: absolute; @@ -52,8 +53,8 @@ height: 56px; padding-right: 30px; padding-left: 10px; - // background: #000; - background:rgba(48,48,48,1); + background: rgba(18,28,36,1); + // background:rgba(48,48,48,1); } .setting_drawer{ diff --git a/public/react/src/modules/developer/components/execResult/index.js b/public/react/src/modules/developer/components/execResult/index.js index 32bbbee91..6f9341b9a 100644 --- a/public/react/src/modules/developer/components/execResult/index.js +++ b/public/react/src/modules/developer/components/execResult/index.js @@ -4,7 +4,7 @@ * @Github: * @Date: 2019-11-28 08:44:54 * @LastEditors: tangjiang - * @LastEditTime: 2019-12-10 09:24:02 + * @LastEditTime: 2019-12-19 10:44:16 */ import './index.scss'; import React, { useState, useEffect } from 'react'; @@ -38,6 +38,15 @@ function ExecResult (props) {
    ); + + const renderError = () => ( +
    + + 未知异常 + +
    + ) + const renderFinish = () => { const { error_line, @@ -60,6 +69,7 @@ function ExecResult (props) { ) } + // console.log('执行结果====》》》》', status); const excuteCtx = (state) => { if (state === 0) { return ( @@ -118,6 +128,8 @@ function ExecResult (props) { setRenderCtx(() => (readerLoaded)); } else if ('finish' === excuteState) { setRenderCtx(() => (renderFinish)); + } else if ('error' === excuteState) { + setRenderCtx(() => (renderError)) } }, [excuteState]); diff --git a/public/react/src/modules/developer/components/initTabCtx/index.js b/public/react/src/modules/developer/components/initTabCtx/index.js index 3834a3e11..3e707daa1 100644 --- a/public/react/src/modules/developer/components/initTabCtx/index.js +++ b/public/react/src/modules/developer/components/initTabCtx/index.js @@ -4,7 +4,7 @@ * @Github: * @Date: 2019-11-27 19:46:14 * @LastEditors: tangjiang - * @LastEditTime: 2019-12-13 17:38:42 + * @LastEditTime: 2019-12-19 10:47:05 */ import './index.scss'; import React, { useState, useEffect, useRef, useImperativeHandle, forwardRef } from 'react'; @@ -26,6 +26,7 @@ function InitTabCtx (props, ref) { const { inputValue, onDebuggerCode } = props; + console.log('default value', inputValue); useImperativeHandle(ref, () => ({ handleTestCodeFormSubmit: (cb) => { // console.log('父组件调用我啦~~~~~~~~~'); @@ -33,6 +34,10 @@ function InitTabCtx (props, ref) { } })); + useEffect(() => { + console.log('初始值: ========', props); + }, [props]); + // 渲染文本提示信息 const renderText = () => (请在这里添加测试用例,点击“调试代码”时将从这里读取输入来测试你的代码...); // 渲染表单信息 diff --git a/public/react/src/modules/developer/components/initTabCtx/index.scss b/public/react/src/modules/developer/components/initTabCtx/index.scss index 449db1d2a..5427aa374 100644 --- a/public/react/src/modules/developer/components/initTabCtx/index.scss +++ b/public/react/src/modules/developer/components/initTabCtx/index.scss @@ -49,7 +49,8 @@ } .input_textarea_style{ - background:rgba(30,30,30,1) !important; + // background:rgba(30,30,30,1) !important; + background:rgba(7,15,25,1) !important; color: #fff; border-color: transparent; outline: none; diff --git a/public/react/src/modules/developer/components/monacoSetting/index.js b/public/react/src/modules/developer/components/monacoSetting/index.js index a101819e0..669439577 100644 --- a/public/react/src/modules/developer/components/monacoSetting/index.js +++ b/public/react/src/modules/developer/components/monacoSetting/index.js @@ -4,10 +4,11 @@ * @Github: * @Date: 2019-11-25 17:50:33 * @LastEditors: tangjiang - * @LastEditTime: 2019-12-06 16:51:48 + * @LastEditTime: 2019-12-19 19:32:08 */ import React, { useState } from 'react'; import { fromStore, toStore } from 'educoder'; +// import { Icon } from 'antd'; // import { Select } from 'antd'; // const { Option } = Select; const SettingDrawer = (props) => { diff --git a/public/react/src/modules/developer/components/myMonacoEditor/index.js b/public/react/src/modules/developer/components/myMonacoEditor/index.js index 60f54ee0a..948d5a4f5 100644 --- a/public/react/src/modules/developer/components/myMonacoEditor/index.js +++ b/public/react/src/modules/developer/components/myMonacoEditor/index.js @@ -4,12 +4,12 @@ * @Github: * @Date: 2019-11-27 15:02:52 * @LastEditors: tangjiang - * @LastEditTime: 2019-12-13 16:16:56 + * @LastEditTime: 2019-12-19 19:36:24 */ import './index.scss'; import React, { useState, useRef, useEffect } from 'react'; -import { Drawer, Modal } from 'antd'; -import { fromStore } from 'educoder'; +import { Drawer, Modal, Icon, Badge } from 'antd'; +import { fromStore, CNotificationHOC } from 'educoder'; import { connect } from 'react-redux'; import MonacoEditor from '@monaco-editor/react'; import SettingDrawer from '../../components/monacoSetting'; @@ -19,16 +19,25 @@ import MyIcon from '../../../../common/components/MyIcon'; // import actions from '../../../../redux/actions'; const { fontSetting, opacitySetting } = CONST; +const maps = { + 'c': 'main.c', + 'c++': 'main.cc', + 'java': 'main.java', + 'pythone': 'main.py' +}; function MyMonacoEditor (props, ref) { const { + notice, language, identifier, + hadCodeUpdate, showOrHideControl, // saveUserInputCode, onCodeChange, - onRestoreInitialCode + onRestoreInitialCode, + onUpdateNotice } = props; const [showDrawer, setShowDrawer] = useState(false); // 控制配置滑框 @@ -50,7 +59,7 @@ function MyMonacoEditor (props, ref) { }, [props]); useEffect(() => { - setHeight(showOrHideControl ? 'calc(100% - 382px)' : 'calc(100% - 56px)'); + setHeight(showOrHideControl ? 'calc(100% - 378px)' : 'calc(100% - 56px)'); }, [showOrHideControl]); // 控制侧边栏设置的显示 @@ -93,28 +102,51 @@ function MyMonacoEditor (props, ref) { // 恢复初始代码 const handleRestoreCode = () => { - Modal.confirm({ + props.confirm({ + title: '提示', content: '确定要恢复代码吗?', - okText: '确定', - cancelText: '取消', onOk () { onRestoreInitialCode && onRestoreInitialCode(); } }) + // Modal.confirm({ + // content: '确定要恢复代码吗?', + // okText: '确定', + // cancelText: '取消', + // onOk () { + // onRestoreInitialCode && onRestoreInitialCode(); + // } + // }) + } + + const handleUpdateNotice = () => { + if (props.notice) { + onUpdateNotice && onUpdateNotice(); + } } const renderRestore = identifier ? ( ) : ''; + + // lex_has_save ${hadCodeUpdate} ? : '' + const _classnames = hadCodeUpdate ? `flex_strict flex_has_save` : 'flex_strict'; return (
    {/* 未保存时 ? '学员初始代码文件' : main.x */} - {identifier ? '' : '学员初始代码文件'} - {identifier ? '已保存' : ''} - {renderRestore} - {/* */} + {identifier ? language ? maps[language.toLowerCase()] : '' : '学员初始代码文件'} + {identifier ? '已保存' : ''} + + + + {renderRestore}
    @@ -161,4 +192,4 @@ const mapDispatchToProps = (dispatch) => ({ export default connect( mapStateToProps, mapDispatchToProps -)(MyMonacoEditor); +)(CNotificationHOC() (MyMonacoEditor)); diff --git a/public/react/src/modules/developer/components/myMonacoEditor/index.scss b/public/react/src/modules/developer/components/myMonacoEditor/index.scss index 7a7c4f030..61689b51e 100644 --- a/public/react/src/modules/developer/components/myMonacoEditor/index.scss +++ b/public/react/src/modules/developer/components/myMonacoEditor/index.scss @@ -1,19 +1,17 @@ .monaco_editor_area{ height: 100%; - background-color: rgba(30,30,30,1); + background-color: rgba(7,15,25,1); .code_title{ display: flex; align-items: center; - // justify-content: space-between; - // background: #000; - // background: #333333; - background-color: rgba(48,48,48,1); + background-color: rgba(18,28,36,1); color: #fff; height: 56px; padding: 0 30px; .flex_strict{ flex: 1; } + .flex_normal{ color: #E51C24; cursor: pointer; @@ -25,21 +23,21 @@ .flex_strict, .flex_normal, .code-icon{ - color: #888; + color: #666; } } } .setting_drawer{ - // .ant-drawer-body{ - // // height: calc(100vh - 120px); - // // overflow-y: auto; - // } + .ant-drawer-close{ + color: #ffffff; + } .ant-drawer-content{ top: 120px; bottom: 56px; height: calc(100vh - 176px); - background: #333333; + // background: #333333; + background: rgba(7,15,25,1); color: #fff; .setting_h2{ color: #fff; @@ -57,4 +55,17 @@ color: #fff; } } +} + +.flex_has_save{ + // animation: blink 3s line 3; + animation-name: blink; + animation-duration: .4s; + animation-iteration-count: 3; +} + +@keyframes blink{ + 50% { + color: #fff; + } } \ No newline at end of file diff --git a/public/react/src/modules/developer/newOrEditTask/index.js b/public/react/src/modules/developer/newOrEditTask/index.js index bc905fdf7..1c451c669 100644 --- a/public/react/src/modules/developer/newOrEditTask/index.js +++ b/public/react/src/modules/developer/newOrEditTask/index.js @@ -13,11 +13,11 @@ import { Button, Modal } from 'antd'; import LeftPane from './leftpane'; import RightPane from './rightpane'; import { withRouter } from 'react-router'; -import { toStore } from 'educoder'; +import { toStore, CNotificationHOC } from 'educoder'; import UserInfo from '../components/userInfo'; // import RightPane from './rightpane/index'; import actions from '../../../redux/actions'; -import {ModalConfirm} from '../../../common/components/ModalConfirm'; +// import {ModalConfirm} from '../../../common/components/ModalConfirm'; const NewOrEditTask = (props) => { const { @@ -69,10 +69,13 @@ const NewOrEditTask = (props) => { // 模拟挑战 const imitationChallenge = () => { + // 调用 start 接口, 成功后跳转到模拟页面 + startProgramQuestion(identifier, props); } // 开始挑战 const startChallenge = () => { - // 调用 start 接口, 成功后跳转到模拟页面 + // 调用 start 接口, 成功后跳转到开启实战 + // TODO startProgramQuestion(identifier, props); } @@ -82,27 +85,38 @@ const NewOrEditTask = (props) => { props.clearOJFormStore(); // 清空描述信息 toStore('oj_description', ''); - setInterval(function () { - props.history.push('/problems'); - }, 500); + props.history.push('/problems'); } // 发布 const handleClickPublish = () => { - ModalConfirm('提示', (

    发布后即可应用到自己管理的课堂
    是否确认发布?

    ), () => { - changePublishLoadingStatus(true); - handlePublish(props, 'publish'); + // ModalConfirm('提示', (

    发布后即可应用到自己管理的课堂
    是否确认发布?

    ), () => { + // changePublishLoadingStatus(true); + // handlePublish(props, 'publish'); + // }); + props.confirm({ + title: '提示', + content: (

    发布后即可应用到自己管理的课堂
    是否确认发布?

    ), + onOk () { + changePublishLoadingStatus(true); + handlePublish(props, 'publish'); + } }); - - } // 撤销发布 const handleClickCancelPublish = () => { - ModalConfirm('提示', (

    是否确认撤销发布?

    ), () => { - changePublishLoadingStatus(true); - handleCancelPublish(props, identifier); + // ModalConfirm('提示', (

    是否确认撤销发布?

    ), () => { + // changePublishLoadingStatus(true); + // handleCancelPublish(props, identifier); + // }); + props.confirm({ + title: '提示', + content: ((

    是否确认撤销发布?

    )), + onOk () { + changePublishLoadingStatus(true); + handleCancelPublish(props, identifier); + } }); - } // 取消保存/取消按钮 @@ -125,6 +139,7 @@ const NewOrEditTask = (props) => { const renderPubOrFight = () => { const pubButton = isPublish ? ( ); - // 更新 - // const updateBtn = isPublish - // ? '' - // : ( - // - // ); - return ( - - - {pubButton} - {challengeBtn} - - ) + if (isPublish) { + return ( + + {pubButton} + + {challengeBtn} + + ); + } else { + return ( + + + {pubButton} + {challengeBtn} + + ); + } + } // 渲染退出 const renderQuit = () => { return identifier ? ( ) : '' @@ -255,4 +271,4 @@ const mapDispatchToProps = (dispatch) => ({ export default withRouter(connect( mapStateToProps, mapDispatchToProps -)(NewOrEditTask)); +)(CNotificationHOC() (NewOrEditTask))); diff --git a/public/react/src/modules/developer/newOrEditTask/index.scss b/public/react/src/modules/developer/newOrEditTask/index.scss index ba0ed8503..ad668fcc8 100644 --- a/public/react/src/modules/developer/newOrEditTask/index.scss +++ b/public/react/src/modules/developer/newOrEditTask/index.scss @@ -10,9 +10,23 @@ align-items: center; justify-content: center; height: 56px; - background: #333333; + // background: #333333; + background: rgba(18,28,36,1); > button{ margin-right: 20px; } } +.quite_btn{ + position: absolute; + right: 10px; + top: 15px; + margin-left: 30px; + color: #888888; + transition: all .3s; + cursor: pointer; + &:hover{ + color: #5091FF; + } +} + diff --git a/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/AddTestDemo.js b/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/AddTestDemo.js index 4c4131fa9..8e7aa70fc 100644 --- a/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/AddTestDemo.js +++ b/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/AddTestDemo.js @@ -4,13 +4,14 @@ * @Github: * @Date: 2019-11-21 09:19:38 * @LastEditors: tangjiang - * @LastEditTime: 2019-12-13 11:58:46 + * @LastEditTime: 2019-12-19 17:54:28 */ import './index.scss'; import React, { useState } from 'react'; -import { Collapse, Icon, Input, Form, Button, Modal } from 'antd'; +import { Collapse, Icon, Input, Form, Button } from 'antd'; import { connect } from 'react-redux'; import actions from '../../../../../redux/actions'; +import { CNotificationHOC} from 'educoder'; const { Panel } = Collapse; const { TextArea } = Input; const FormItem = Form.Item; @@ -31,15 +32,22 @@ const AddTestDemo = (props) => { // console.log('点击的删除按钮') e.preventDefault(); e.stopPropagation(); - Modal.confirm({ - title: '删除', + props.confirm({ + title: '提示', content: '确定要删除当前测试用例吗?', - okText: '确定', - cancelText: '取消', onOk() { onDeleteTest(testCase); } - }) + }); + // Modal.confirm({ + // title: '删除', + // content: '确定要删除当前测试用例吗?', + // okText: '确定', + // cancelText: '取消', + // onOk() { + // onDeleteTest(testCase); + // } + // }) } // 输入框值改变时 @@ -189,4 +197,4 @@ const mapDispatchToProps = (dispatch) => ({ export default connect( mapStateToProps, mapDispatchToProps -)(Form.create()(AddTestDemo)); +)(Form.create()(CNotificationHOC()(AddTestDemo))); diff --git a/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index.js b/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index.js index 762d1d3ba..da31754bd 100644 --- a/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index.js +++ b/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index.js @@ -4,21 +4,20 @@ * @Github: * @Date: 2019-11-20 10:35:40 * @LastEditors: tangjiang - * @LastEditTime: 2019-12-13 11:39:52 + * @LastEditTime: 2019-12-19 17:23:10 */ -import 'quill/dist/quill.core.css'; -import 'quill/dist/quill.bubble.css'; -import 'quill/dist/quill.snow.css'; import './index.scss'; // import 'katex/dist/katex.css'; import React from 'react'; import { Form, Input, Select, InputNumber, Button } from 'antd'; import { connect } from 'react-redux'; import AddTestDemo from './AddTestDemo'; -import QuillEditor from '../../../quillEditor'; +// import QuillEditor from '../../../quillEditor'; import actions from '../../../../../redux/actions'; import CONST from '../../../../../constants'; -import { fromStore, toStore } from 'educoder'; // 保存和读取store值 +import { toStore } from 'educoder'; // 保存和读取store值 +// import Wrapper from '../../../../../common/reactQuill'; +import QuillForEditor from '../../../../../common/quillForEditor'; const scrollIntoView = require('scroll-into-view'); const {jcLabel} = CONST; const FormItem = Form.Item; @@ -26,9 +25,9 @@ const { Option } = Select; const maps = { language: [ { title: 'C', key: 'C' }, - // { title: 'C++', key: 'C++' }, - // { title: 'Python', key: 'Python' }, - // { title: 'Java', key: 'Java' } + { title: 'C++', key: 'C++' }, + { title: 'Python', key: 'Python' }, + { title: 'Java', key: 'Java' } ], difficult: [ { title: '简单', key: '1' }, @@ -146,9 +145,6 @@ class EditTab extends React.Component { testCasesValidate, openTestCodeIndex = [] } = this.props; - // console.log('当前位置: ', position); - // console.log('OJForm: ', ojForm); - // console.log('当前位置: ', testCases); // 表单label const myLabel = (name, subTitle) => { if (subTitle) { @@ -185,7 +181,6 @@ class EditTab extends React.Component { }; const renderTestCase = () => { return this.props.testCases.map((item, i) => { - console.log(111); return { + console.log('描述信息为: ', content); + // 保存获取的描述信息至redux中 + this.handleChangeDescription(content); + } // 编辑器配置信息 const quillConfig = [ [{ header: [1, 2, 3, 4, 5, 6, false] }], ['bold', 'italic', 'underline', 'strike'], // 切换按钮 ['blockquote', 'code-block'], // 代码块 - [{ 'list': 'ordered' }, { 'list': 'bullet' }], // 列表 + [{align: []}, { 'list': 'ordered' }, { 'list': 'bullet' }], // 列表 [{ 'script': 'sub'}, { 'script': 'super' }], [{ 'color': [] }, { 'background': [] }], // 字体颜色与背景色 - ['formula', 'image', 'video'], // 数学公式、图片、视频 + ['image', 'formula'], // 数学公式、图片、视频 ['clean'], // 清除格式 ]; + return (
    @@ -267,7 +270,7 @@ class EditTab extends React.Component { help={ojFormValidate.timeLimit.errMsg} colon={ false } > - + - +
    + +
    +
    {/* */} + {/* 添加测试用例 */} diff --git a/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index.scss b/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index.scss index f8fa3bd71..17f3eae34 100644 --- a/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index.scss +++ b/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index.scss @@ -49,6 +49,11 @@ .test_demo_ctx, .editor_form{ margin: 0 30px; + + .ant-form-explain{ + margin-top: 5px; + margin-left: -10px; + } } .test_demo_title{ display: flex; diff --git a/public/react/src/modules/developer/newOrEditTask/leftpane/index.js b/public/react/src/modules/developer/newOrEditTask/leftpane/index.js index 8b10413a9..781b82699 100644 --- a/public/react/src/modules/developer/newOrEditTask/leftpane/index.js +++ b/public/react/src/modules/developer/newOrEditTask/leftpane/index.js @@ -34,28 +34,6 @@ function LeftPane (props) { const [defaultActiveKey, setDefaultActiveKey] = useState('editor'); - // const tabArrs = [ - // { title: '编辑', key: 'editor', content: () }, - // { title: '预览', key: 'prev', content: () }, - // // { title: '提交记录', key: 'commit', content: () }, - // ]; - - // const tabs = tabArrs.map((tab) => { - // const Comp = tab.content; - // return ( - // - // { Comp } - // - // ) - // }); - - // tab切换时 - // const handleTabChange = (key) => { - // setDefaultActiveKey(key); - // } - - // 执行表单提交函数 - const renderComp = useMemo(() => { return Comp[defaultActiveKey]; }, [defaultActiveKey]); diff --git a/public/react/src/modules/developer/newOrEditTask/leftpane/prevTab/index.js b/public/react/src/modules/developer/newOrEditTask/leftpane/prevTab/index.js index c45628973..6b41d3489 100644 --- a/public/react/src/modules/developer/newOrEditTask/leftpane/prevTab/index.js +++ b/public/react/src/modules/developer/newOrEditTask/leftpane/prevTab/index.js @@ -4,74 +4,47 @@ * @Github: * @Date: 2019-11-24 10:09:55 * @LastEditors: tangjiang - * @LastEditTime: 2019-12-04 23:38:37 + * @LastEditTime: 2019-12-18 10:02:24 */ import './index.scss'; import React, { useEffect, useState, useRef } from 'react'; import { connect } from 'react-redux'; import {Empty} from 'antd'; -// import QuillEditor from '../../../quillEditor'; -const Quill = window.Quill; +// import Wrapper from '../../../../../common/reactQuill'; +import QuillForEditor from '../../../../../common/quillForEditor'; const PrevTab = (props) => { - const { - description - } = props; const prevRef = useRef(null); - const [desc, setDesc] = useState(''); - const [renderCtx, setRenderCtx] = useState(() => { - return function () { - return ''; - } - }); - - // 空内容 - const renderTxt = () => ( -
    - -
    - ); + // const [desc, setDesc] = useState(''); + const [renderCtx, setRenderCtx] = useState(() => ''); + // 渲染内容 - const renderQuill = () => ( -
    -
    - ); - useEffect(() => { - setDesc(description); - }, [description]); - - useEffect(() => { - if (description) { - setRenderCtx(() => renderQuill); - let count = 0; - let timer = setInterval(() => { - count++; - if (count >= 10 || prevRef.current) { - clearInterval(timer); - timer = null; - if (prevRef.current) { - const quillEditor = new Quill(prevRef.current, { - readOnly: true, - theme: 'bubble' - }); - quillEditor.container.firstChild.innerHTML = description; - } - } - }, 50); + if (props.description) { + setRenderCtx(() => ( +
    + +
    + )); } else { - setRenderCtx(() => renderTxt); + setRenderCtx(() => ( +
    + +
    + )); } - - }, [description]); + }, [props]); return (
    - {renderCtx()} + {renderCtx}
    ) diff --git a/public/react/src/modules/developer/newOrEditTask/rightpane/index.scss b/public/react/src/modules/developer/newOrEditTask/rightpane/index.scss index 48f5a7bb9..9c472b641 100644 --- a/public/react/src/modules/developer/newOrEditTask/rightpane/index.scss +++ b/public/react/src/modules/developer/newOrEditTask/rightpane/index.scss @@ -1,7 +1,7 @@ .right_pane_code_wrap{ position: relative; // justify-content: center; - background-color: #222; + // background-color: #222; height: 100%; // height: calc(100vh - 178px); .code-title, @@ -11,7 +11,8 @@ align-items: center; justify-content: space-between; // padding: 0 30px; - background: #000; + // background: #000; + background: rgba(18,28,36,1); color: #fff; } .code-title, diff --git a/public/react/src/modules/developer/split_pane_resizer.scss b/public/react/src/modules/developer/split_pane_resizer.scss index 498370dd6..2898d966c 100644 --- a/public/react/src/modules/developer/split_pane_resizer.scss +++ b/public/react/src/modules/developer/split_pane_resizer.scss @@ -8,7 +8,8 @@ .record_detail_header{ height: 65px; // background:rgba(34,34,34,1); - background: #1E1E1E; + // background: #1E1E1E; + background: rgba(7,15,25,1); padding:0 30px; } @@ -123,7 +124,8 @@ .split-pane-area, .split-pane-left{ .ant-tabs-nav-wrap{ - padding: 0 30px; + // padding: 0 30px; + padding: 0 20px; } .ant-tabs-bar{ margin: 0; diff --git a/public/react/src/modules/developer/studentStudy/index.js b/public/react/src/modules/developer/studentStudy/index.js index e4b296a20..15c87e64c 100644 --- a/public/react/src/modules/developer/studentStudy/index.js +++ b/public/react/src/modules/developer/studentStudy/index.js @@ -4,7 +4,7 @@ * @Github: * @Date: 2019-11-23 10:53:19 * @LastEditors: tangjiang - * @LastEditTime: 2019-12-13 17:19:15 + * @LastEditTime: 2019-12-19 19:48:20 */ import './index.scss'; import React, { useEffect, useState } from 'react'; @@ -15,10 +15,10 @@ import RightPane from './rightpane'; // import { Link } from 'react-router-dom'; // import { getImageUrl } from 'educoder' // import RightPane from '../newOrEditTask/rightpane'; -import { Icon, Modal } from 'antd'; +import { Icon } from 'antd'; import UserInfo from '../components/userInfo'; import actions from '../../../redux/actions'; -import { fromStore} from 'educoder'; +import { fromStore, CNotificationHOC} from 'educoder'; import { withRouter } from 'react-router'; function StudentStudy (props) { @@ -29,7 +29,8 @@ function StudentStudy (props) { userInfo, hack_identifier, // user_program_identifier, - restoreInitialCode + restoreInitialCode, + changeShowOrHideControl } = props; const { @@ -51,32 +52,51 @@ function StudentStudy (props) { const { hack = {} } = props; if (hack.modify_code && hasUpdate) { // 代码更改,提示是否需要更新代码 setHasUpdate(false); - Modal.confirm({ - title: '提示', - content: ( -

    - 代码文件有更新啦
    - 还未提交的代码,请自行保存 -

    - ), - okText: '立即更新', - cancelText: '稍后再说', - onOk () { - restoreInitialCode(id, '更新成功'); - } - }); + handleUpdateNotice(); } }, [props, hasUpdate, setHasUpdate]); + const handleUpdateNotice = () => { + console.log(props); + props.confirm({ + title: '提示', + content: ( +

    + 代码文件有更新啦
    + 还未提交的代码,请自行保存 +

    + ), + onOk () { + restoreInitialCode(id, '更新成功'); + } + }) + // Modal.confirm({ + // title: '提示', + // content: ( + //

    + // 代码文件有更新啦
    + // 还未提交的代码,请自行保存 + //

    + // ), + // okText: '立即更新', + // cancelText: '稍后再说', + // onOk () { + // restoreInitialCode(id, '更新成功'); + // } + // }); + } const _hack_id = hack_identifier || fromStore('hack_identifier'); // 处理编辑 const handleClickEditor = () => { + changeShowOrHideControl(false); props.saveEditorCodeForDetail(); props.history.push(`/problems/${_hack_id}/edit`); } // 处理退出 const handleClickQuit = () => { + // 将控制台关闭 + changeShowOrHideControl(false); props.saveEditorCodeForDetail(); props.history.push('/problems'); } @@ -117,7 +137,9 @@ function StudentStudy (props) {
    - +
    @@ -146,11 +168,12 @@ const mapDispatchToProps = (dispatch) => ({ saveEditorCodeForDetail: (code) => dispatch(actions.saveEditorCodeForDetail(code)), // 恢复初始代码 restoreInitialCode: (identifier, msg) => dispatch(actions.restoreInitialCode(identifier, msg)), + changeShowOrHideControl: (flag) => dispatch(actions.changeShowOrHideControl(flag)) }); export default withRouter(connect( mapStateToProps, mapDispatchToProps -)(StudentStudy)); +)(CNotificationHOC()(StudentStudy))); diff --git a/public/react/src/modules/developer/studentStudy/index.scss b/public/react/src/modules/developer/studentStudy/index.scss index bbf66aebb..68835f2ce 100644 --- a/public/react/src/modules/developer/studentStudy/index.scss +++ b/public/react/src/modules/developer/studentStudy/index.scss @@ -5,6 +5,6 @@ .right_pane_code_wrap{ position: relative; - background-color: #222; + // background-color: #222; height: 100%; } diff --git a/public/react/src/modules/developer/studentStudy/leftpane/comment/index.js b/public/react/src/modules/developer/studentStudy/leftpane/comment/index.js index 329e8fba1..ca7160117 100644 --- a/public/react/src/modules/developer/studentStudy/leftpane/comment/index.js +++ b/public/react/src/modules/developer/studentStudy/leftpane/comment/index.js @@ -4,15 +4,18 @@ * @Github: * @Date: 2019-11-27 09:49:35 * @LastEditors: tangjiang - * @LastEditTime: 2019-11-27 09:52:53 + * @LastEditTime: 2019-12-17 17:46:05 */ +import './index.scss'; import React from 'react'; - -const Comment = (props) => { +import Comment from '../../../../../common/components/comment'; +const CommentTask = (props) => { return ( -

    Comment

    +
    + +
    ) } -export default Comment; +export default CommentTask; diff --git a/public/react/src/modules/developer/studentStudy/leftpane/comment/index.scss b/public/react/src/modules/developer/studentStudy/leftpane/comment/index.scss new file mode 100644 index 000000000..ba9c132d9 --- /dev/null +++ b/public/react/src/modules/developer/studentStudy/leftpane/comment/index.scss @@ -0,0 +1,8 @@ +.task_comment_task{ + background: #fff; + padding: 20px 30px 0; + height: calc(100vh - 177px); + box-sizing: border-box; + overflow-y: auto; + border-bottom: 1px solid rgba(244,244,244,1); +} \ No newline at end of file diff --git a/public/react/src/modules/developer/studentStudy/leftpane/index.js b/public/react/src/modules/developer/studentStudy/leftpane/index.js index 69f49b9b1..347f4b4f8 100644 --- a/public/react/src/modules/developer/studentStudy/leftpane/index.js +++ b/public/react/src/modules/developer/studentStudy/leftpane/index.js @@ -4,11 +4,11 @@ * @Github: * @Date: 2019-11-23 11:33:41 * @LastEditors: tangjiang - * @LastEditTime: 2019-12-09 19:57:21 + * @LastEditTime: 2019-12-19 18:03:22 // */ import './index.scss'; import React, { useState, useEffect, useMemo } from 'react'; -import { Tabs, Divider } from 'antd'; +import { Divider } from 'antd'; import { connect } from 'react-redux'; import Comment from './comment'; import CommitRecord from './commitRecord'; @@ -19,17 +19,10 @@ import actions from '../../../../redux/actions'; const LeftPane = (props) => { - const { hack, userCodeTab, changeUserCodeTab } = props; + const { hack, userCodeTab } = props; const { pass_count, submit_count } = hack; - const [defaultActiveKey, setDefaultActiveKey] = useState('task'); + const [defaultActiveKey, setDefaultActiveKey] = useState('comment'); - console.log(pass_count, submit_count); - const tabArrs = [ - { title: '任务描述', key: 'task', content: () }, - { title: '提交记录', key: 'record', content: () }, - // { title: '评论', key: 'comment', content: () }, - ]; - const navItem = [ { title: '任务描述', @@ -38,38 +31,30 @@ const LeftPane = (props) => { { title: '提交记录', key: 'record' - } + }, + // { + // title: '评论', + // key: 'comment' + // } ]; const Comp = { task: (), - record: () + record: (), + comment: () }; useEffect(() => { + console.log('====>>>>', userCodeTab); setDefaultActiveKey(userCodeTab); }, [userCodeTab]) - // const tabs = tabArrs.map((tab) => { - // const Comp = tab.content; - // return ( - // - // { Comp } - // - // ) - // }); - - // // tab切换时 - // const handleTabChange = (key) => { - // // setDefaultActiveKey(key); - // changeUserCodeTab(key); - // } - const renderComp = useMemo(() => { return Comp[defaultActiveKey]; - }, [defaultActiveKey]); + }, [defaultActiveKey, setDefaultActiveKey]); const renderNavItem = navItem.map((item) => { + const _classes = item.key === defaultActiveKey ? 'add_editor_item active' : 'add_editor_item'; return (
  • { return ( - {/* - { tabs } - -
    -
    - - - -
    -
    - - - -
    -
    */}
      { renderNavItem }
    @@ -126,11 +96,6 @@ const LeftPane = (props) => {
  • - {/*
    - - - -
    */}
    ); diff --git a/public/react/src/modules/developer/studentStudy/leftpane/index.scss b/public/react/src/modules/developer/studentStudy/leftpane/index.scss index 9e6f019f6..1582c033f 100644 --- a/public/react/src/modules/developer/studentStudy/leftpane/index.scss +++ b/public/react/src/modules/developer/studentStudy/leftpane/index.scss @@ -13,6 +13,8 @@ bottom: 0px; height: 56px; width: 100%; + box-sizing: border-box; + border-top: 1px solid rgba(244,244,244,1); // background: pink; padding: 0 30px; // background-color: rgba(250,250,250,1); @@ -79,4 +81,9 @@ margin-right: 5px; } } +} + +.add_editor_list_area{ + box-sizing: border-box; + border-bottom: 1px solid rgba(244,244,244,1); } \ No newline at end of file diff --git a/public/react/src/modules/developer/studentStudy/leftpane/taskDescription/index.js b/public/react/src/modules/developer/studentStudy/leftpane/taskDescription/index.js index 28fe51765..981770b8c 100644 --- a/public/react/src/modules/developer/studentStudy/leftpane/taskDescription/index.js +++ b/public/react/src/modules/developer/studentStudy/leftpane/taskDescription/index.js @@ -4,14 +4,15 @@ * @Github: * @Date: 2019-11-27 09:49:30 * @LastEditors: tangjiang - * @LastEditTime: 2019-12-09 19:21:55 + * @LastEditTime: 2019-12-19 09:22:52 */ import '../index.scss'; import React from 'react'; import { Tag } from 'antd'; import { Link } from 'react-router-dom'; import { connect } from 'react-redux'; -import QuillEditor from '../../../quillEditor'; +import QuillForEditor from '../../../../../common/quillForEditor'; + import CONST from '../../../../../constants'; const {tagBackground, diffText} = CONST; @@ -36,22 +37,15 @@ const TaskDescription = (props) => {

    出题者: - {username} + {username}

    -
    - {/* */} - {/*
    */} ) } diff --git a/public/react/src/modules/developer/studentStudy/rightpane/index.js b/public/react/src/modules/developer/studentStudy/rightpane/index.js index 669a8a693..af46d0aef 100644 --- a/public/react/src/modules/developer/studentStudy/rightpane/index.js +++ b/public/react/src/modules/developer/studentStudy/rightpane/index.js @@ -4,7 +4,7 @@ * @Github: * @Date: 2019-11-27 14:59:51 * @LastEditors: tangjiang - * @LastEditTime: 2019-12-13 16:50:40 + * @LastEditTime: 2019-12-19 19:13:05 */ import React, { useState, useEffect } from 'react'; import {connect} from 'react-redux'; @@ -20,8 +20,11 @@ const RightPane = (props) => { submitUserCode, input, hack, + notice, updateCode, + hadCodeUpdate, editor_code, + updateNotice, saveUserInputCode, restoreInitialCode, saveUserCodeForInterval @@ -29,6 +32,7 @@ const RightPane = (props) => { const [editorCode, setEditorCode] = useState(''); + let initFlag = true; useEffect(() => { if (editor_code) { setEditorCode(editor_code); @@ -48,7 +52,12 @@ const RightPane = (props) => { // 代码块内容变化时 const handleCodeChange = (code) => { // 保存用户提交的代码块 - console.log(code); + setEditorCode(code); + // 第一次回填代码内容时不更新; + if (initFlag) { + initFlag = false; + return; + } if (!timer) { timer = setInterval(() => { clearInterval(timer); @@ -69,13 +78,21 @@ const RightPane = (props) => { restoreInitialCode(identifier, '恢复初始代码成功'); } + // 更新代码 + const handleUpdateNotice = () => { + updateNotice && updateNotice(); + }; + return (
    { const mapStateToProps = (state) => { - const {user_program_identifier, hack, userTestInput, editor_code} = state.ojForUserReducer; + const { + user_program_identifier, + hack, + userTestInput, + editor_code, + notice, + hadCodeUpdate + } = state.ojForUserReducer; // const { language, code } = hack; return { hack, + notice, + hadCodeUpdate, editor_code, input: userTestInput, submitInput: hack.input, diff --git a/public/react/src/modules/tpm/TPMBanner.js b/public/react/src/modules/tpm/TPMBanner.js index b498c4d22..672fff455 100644 --- a/public/react/src/modules/tpm/TPMBanner.js +++ b/public/react/src/modules/tpm/TPMBanner.js @@ -58,15 +58,6 @@ class TPMBanner extends Component { } } - // star_info:[0, 0, 0, 0, 0, 0], - // star_infos:[0, 0, 0, 0, 0, 0], - // shixunsDetails:{}, - // shixunId: undefined, - // componentWillReceiveProps(newProps, newContext){ - // this.setState({ - // shixunsDetails: newProps.shixunsDetails - // }); - // } IEVersion = () => { var userAgent = navigator.userAgent; //取得浏览器的userAgent字符串 @@ -118,31 +109,50 @@ class TPMBanner extends Component { if (prevProps != this.props) { let shixunopenprocess=window.localStorage.shixunopenprocess; let openopenpublictype=window.localStorage.openopenpublictype; - if(this.props.shixunsDetails&&this.props.shixunsDetails.shixun_status === 0 && this.props.identity < 5){ - if(shixunopenprocess===undefined||shixunopenprocess===false){ - this.setState({ - openknow:true - }) - }else{ - this.setState({ - openknow:false - }) + if(this.props.status===0&&this.props.openknows===false){ + + if(this.props.shixunsDetails&&this.props.shixunsDetails.shixun_status === 0 && this.props.identity < 5){ + if(shixunopenprocess===undefined||shixunopenprocess===false){ + this.setState({ + openknow:true + }) + }else{ + this.setState({ + openknow:false + }) + } } + }else{ + this.setState({ + openknow:false + }) } - if(this.props.shixunsDetails&&this.props.shixunsDetails.shixun_status === 2 && this.props.shixunsDetails&&this.props.shixunsDetails.public===0 && this.props.identity < 5){ - if(openopenpublictype===undefined||openopenpublictype===false){ - this.setState({ - openshowpublictype:true - }) - }else{ - this.setState({ - openshowpublictype:false - }) + + + if(this.props.public===0&&this.props.status>1&&this.props.openknows===false){ + if(this.props.shixunsDetails&&this.props.shixunsDetails.shixun_status === 2 && this.props.shixunsDetails&&this.props.shixunsDetails.public===0 && this.props.identity < 5){ + if(openopenpublictype===undefined||openopenpublictype===false){ + this.setState({ + openshowpublictype:true + }) + }else{ + this.setState({ + openshowpublictype:false + }) + } } + }else{ + this.setState({ + openshowpublictype:false + }) } + + } } + + componentDidMount() { let thiisie = this.IEVersion(); @@ -155,6 +165,7 @@ class TPMBanner extends Component { isIE: false }) } + } /* @@ -784,7 +795,8 @@ class TPMBanner extends Component { }; // // console.log(this.props.shixunsDetails&&this.props.shixunsDetails.is_jupyter) - + // console.log(this.props) + // console.log(this.state) return ( shixunsDetails === undefined ? "" : @@ -796,6 +808,9 @@ class TPMBanner extends Component { height: 180px !important; padding-top:35px !important; } + .ant-popover{ + z-index:1000 !important; + } ` } diff --git a/public/react/src/modules/tpm/TPMIndex.js b/public/react/src/modules/tpm/TPMIndex.js index d79bae95b..c44d8ce51 100644 --- a/public/react/src/modules/tpm/TPMIndex.js +++ b/public/react/src/modules/tpm/TPMIndex.js @@ -207,6 +207,7 @@ class TPMIndex extends Component { propaedeutics:response.data.propaedeutics, status: response.data.shixun_status, secret_repository: response.data.secret_repository, + public:response.data.public, is_jupyter:response.data.is_jupyter=== undefined||response.data.is_jupyter===null?false:response.data.is_jupyter, }); @@ -295,10 +296,6 @@ class TPMIndex extends Component { // this.getnavdatas() } - // componentDidUpdate=()=>{ - // this.getnavdatas() - // } - setLoadingContent = (isLoadingContent) => { this.setState({ loadingContent: isLoadingContent }) } @@ -307,9 +304,6 @@ class TPMIndex extends Component { getnavdatas=()=>{ let selectedKeys; const {location} = this.props; - - console.log(location.pathname) - if(location.pathname.indexOf('/challenges')!=-1){ selectedKeys="1" }else if(location.pathname.indexOf('/propaedeutics')!=-1){ @@ -385,6 +379,9 @@ class TPMIndex extends Component { margin:0 40px 0 0; padding:0px; } + .ant-popover{ + z-index:1000 !important; + } ` } diff --git a/public/react/src/modules/tpm/TPMsettings/LearningSettings.js b/public/react/src/modules/tpm/TPMsettings/LearningSettings.js index 681cbc956..a98f68223 100644 --- a/public/react/src/modules/tpm/TPMsettings/LearningSettings.js +++ b/public/react/src/modules/tpm/TPMsettings/LearningSettings.js @@ -215,9 +215,9 @@ export default class Shixuninformation extends Component { multi_webssh: false }) } else { - this.setState({ - multi_webssh: true - }) + // this.setState({ + // multi_webssh: true + // }) } this.setState({ opensshRadio: e.target.value diff --git a/public/react/src/modules/tpm/TPMsettings/Shixuninformation.js b/public/react/src/modules/tpm/TPMsettings/Shixuninformation.js index 200880019..66e1da8dd 100644 --- a/public/react/src/modules/tpm/TPMsettings/Shixuninformation.js +++ b/public/react/src/modules/tpm/TPMsettings/Shixuninformation.js @@ -50,6 +50,14 @@ class Shixuninformation extends Component { } componentDidMount() { + let query=this.props.location.search + const types = query.split('?edit=') + if(types[1]==="1"){ + let anchorElement = document.getElementById("newcourseContentMD"); + if(anchorElement){ + this.scrollToAnchor("newcourseContentMD"); + } + } } @@ -113,6 +121,7 @@ class Shixuninformation extends Component { selectright: this.props.data && this.props.data.shixun.choice_small_type, }) this.contentMdRef.current.setValue(this.props.data && this.props.data.shixun.description); + } } } @@ -807,16 +816,20 @@ class Shixuninformation extends Component { )} + + + diff --git a/public/react/src/modules/tpm/challengesnew/TPManswer.js b/public/react/src/modules/tpm/challengesnew/TPManswer.js index 9c9a23a12..9187e09b0 100644 --- a/public/react/src/modules/tpm/challengesnew/TPManswer.js +++ b/public/react/src/modules/tpm/challengesnew/TPManswer.js @@ -302,11 +302,11 @@ export default class TPManswer extends Component { className="color-grey-6 fr font-15 mt3">返回 {prev_challenge === undefined ? "" : - 上一关 + 上一关 } {next_challenge === undefined ? "" : - 下一关 + 下一关 } 返回 {prev_challenge === undefined ? "" : - 上一关 + 上一关 } {next_challenge === undefined ? "" : - 下一关 + 下一关 } 返回 { next_challenge===undefined?"": - 下一关 + 下一关 } { prev_challenge===undefined?"": - 上一关 + 上一关 } diff --git a/public/react/src/modules/tpm/challengesnew/TPMevaluation.js b/public/react/src/modules/tpm/challengesnew/TPMevaluation.js index d70f22a0a..34eb1a075 100644 --- a/public/react/src/modules/tpm/challengesnew/TPMevaluation.js +++ b/public/react/src/modules/tpm/challengesnew/TPMevaluation.js @@ -824,11 +824,11 @@ export default class TPMevaluation extends Component { className="color-grey-6 fr font-15 mt3">返回 {prev_challenge === undefined ? "" : - 上一关 + 上一关 } {next_challenge === undefined ? "" : - 下一关 + 下一关 } 返回 { prev_challenge===undefined?"": - 上一关 + 上一关 } { next_challenge===undefined?"": - 下一关 + 下一关 } { @@ -97,11 +99,11 @@ class TPMRightSection extends Component {
    - {TPMRightSectionData&&TPMRightSectionData.complete_count!=null?
    + {this.props&&this.props.status>1?
    学习统计 - 已完成 {TPMRightSectionData&&TPMRightSectionData.complete_count} 个 / 共 {TPMRightSectionData&&TPMRightSectionData.challenge_count} 关 + 已完成 {TPMRightSectionData&&TPMRightSectionData.complete_count===null?0:TPMRightSectionData&&TPMRightSectionData.complete_count} 个 / 共 {TPMRightSectionData&&TPMRightSectionData.challenge_count} 关
    diff --git a/public/react/src/modules/tpm/component/TPMright.css b/public/react/src/modules/tpm/component/TPMright.css index a75160bf5..0d6b306fd 100644 --- a/public/react/src/modules/tpm/component/TPMright.css +++ b/public/react/src/modules/tpm/component/TPMright.css @@ -119,9 +119,14 @@ .padd252020px{ padding: 25px 20px 15px; } + .rightjinengs{ height: 35px; margin-top: 20px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; } .borderbottomf4{ diff --git a/public/react/src/modules/tpm/shixunchild/Challenges/Challenges.js b/public/react/src/modules/tpm/shixunchild/Challenges/Challenges.js index 307823ee8..548e7224a 100644 --- a/public/react/src/modules/tpm/shixunchild/Challenges/Challenges.js +++ b/public/react/src/modules/tpm/shixunchild/Challenges/Challenges.js @@ -34,7 +34,8 @@ class Challenges extends Component { operationstrue:false, isSpin:false, boxoffsetHeigh:0, - opentitletype:true + opentitletype:true, + isopentitletype:"Less", } } @@ -52,6 +53,13 @@ class Challenges extends Component { ChallengesDataList: response.data, sumidtype: false, }); + + if(response.data.description=== ""||response.data.description===null||response.data.description===undefined){ + this.setState({ + isopentitletype:"Less", + }) + } + } } }).catch((error) => { @@ -71,17 +79,19 @@ class Challenges extends Component { let box=document.getElementById("shixunchallengesid"); if(box){ boxoffsetHeigh=box.offsetHeight - if(boxoffsetHeigh<260){ + if(boxoffsetHeigh<300){ this.setState({ - opentitletype:false, + isopentitletype:"Less", boxoffsetHeigh:boxoffsetHeigh }) }else{ this.setState({ + opentitletype:true, + isopentitletype:"greater", boxoffsetHeigh:boxoffsetHeigh }) } - + console.log(boxoffsetHeigh) } } @@ -322,7 +332,8 @@ class Challenges extends Component { opentitle=()=>{ this.setState({ - opentitletype:!this.state.opentitletype + opentitletype:!this.state.opentitletype, + }) } @@ -334,6 +345,7 @@ class Challenges extends Component { } let id = this.props.match.params.shixunId; const antIcon = ; + return ( {AccountProfiletype===true?简介 {this.props.identity < 5 && ChallengesDataList&&ChallengesDataList.shixun_status < 3 ? - +
    {/**/} 编辑 - :""} + :""} {this.props.user && this.props.user.main_site === true ? this.props.identity < 5?实训制作指南 : "":""} @@ -407,7 +419,7 @@ class Challenges extends Component { { ` #shixunchallengesid{ - max-height: 260px; + max-height: 300px; overflow: hidden; } ` @@ -427,14 +439,22 @@ class Challenges extends Component {
    -

    + {ChallengesDataList === undefined || ChallengesDataList&&ChallengesDataList.description=== ""||ChallengesDataList&&ChallengesDataList.description===null||ChallengesDataList&&ChallengesDataList.description===undefined? +

    + {this.props.identity < 5?:} +

    暂时还没有相关数据哦!

    +
    + :

    {ChallengesDataList === undefined ? "" :ChallengesDataList&&ChallengesDataList.description===null?"":

    } -

    +

    }
    - {this.state.opentitletype===true?this.opentitle()} className={"pointer Breadcrumbfont color-grey-9 "}> + + {this.state.isopentitletype==="Less"?"":this.state.opentitletype===true?this.opentitle()} className={"pointer Breadcrumbfont color-grey-9 "}> 阅读全文 :this.opentitle()} className={"pointer Breadcrumbfont color-grey-9 "}> 收起全文 diff --git a/public/react/src/modules/tpm/shixunchild/Challenges/Challengesjupyter.js b/public/react/src/modules/tpm/shixunchild/Challenges/Challengesjupyter.js index fcdbed408..e04ad17c9 100644 --- a/public/react/src/modules/tpm/shixunchild/Challenges/Challengesjupyter.js +++ b/public/react/src/modules/tpm/shixunchild/Challenges/Challengesjupyter.js @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import { Link } from "react-router-dom"; -import { markdownToHTML, configShareForCustom} from 'educoder' +import { markdownToHTML, configShareForCustom,getImageUrl} from 'educoder' import { Divider, Tooltip } from 'antd'; import LoadingSpin from '../../../../common/LoadingSpin'; import 'antd/lib/pagination/style/index.css'; @@ -23,7 +23,8 @@ class Challengesjupyter extends Component { booljupyterurls:false, loading:false, boxoffsetHeigh:0, - opentitletype:true + opentitletype:true, + isopentitletype:"Less", } } @@ -41,6 +42,11 @@ class Challengesjupyter extends Component { ChallengesDataList: response.data, sumidtype: false, }); + if(response.data.description=== ""||response.data.description===null||response.data.description===undefined){ + this.setState({ + isopentitletype:"Less", + }) + } } } }).catch((error) => { @@ -55,13 +61,15 @@ class Challengesjupyter extends Component { let box=document.getElementById("shixunchallengesid"); if(box){ boxoffsetHeigh=box.offsetHeight - if(boxoffsetHeigh<260){ + if(boxoffsetHeigh<300){ this.setState({ - opentitletype:false, + isopentitletype:"Less", boxoffsetHeigh:boxoffsetHeigh }) }else{ this.setState({ + isopentitletype:"greater", + opentitletype:true, boxoffsetHeigh:boxoffsetHeigh }) } @@ -219,17 +227,17 @@ class Challengesjupyter extends Component {
    简介 - + 编辑 - +
    {this.state.opentitletype===true?