diff --git a/app/controllers/admins/examination_authentications_controller.rb b/app/controllers/admins/examination_authentications_controller.rb index 8045644e1..c68c062b6 100644 --- a/app/controllers/admins/examination_authentications_controller.rb +++ b/app/controllers/admins/examination_authentications_controller.rb @@ -12,7 +12,7 @@ class Admins::ExaminationAuthenticationsController < Admins::BaseController ActiveRecord::Base.transaction do exam = ExaminationBank.find current_apply.container_id current_apply.update!(status: 1) - exam.update!(public: 0) + exam.update!(public: 1) end render_success_js end diff --git a/app/controllers/admins/item_authentications_controller.rb b/app/controllers/admins/item_authentications_controller.rb index 88d833ee9..8da9b232f 100644 --- a/app/controllers/admins/item_authentications_controller.rb +++ b/app/controllers/admins/item_authentications_controller.rb @@ -16,7 +16,7 @@ class Admins::ItemAuthenticationsController < Admins::BaseController ActiveRecord::Base.transaction do item = ItemBank.find current_apply.container_id current_apply.update!(status: 1) - item.update!(public: 0) + item.update!(public: 1) end render_success_js end diff --git a/app/controllers/examination_banks_controller.rb b/app/controllers/examination_banks_controller.rb index 9ffbd6630..251cd197f 100644 --- a/app/controllers/examination_banks_controller.rb +++ b/app/controllers/examination_banks_controller.rb @@ -50,8 +50,11 @@ class ExaminationBanksController < ApplicationController end def destroy - @exam.destroy! - render_ok + ActiveRecord::Base.transaction do + ApplyAction.where(container_type: "ExaminationBank", container_id: @exam.id).destroy_all + @exam.destroy! + render_ok + end end def set_public diff --git a/app/controllers/examination_intelligent_settings_controller.rb b/app/controllers/examination_intelligent_settings_controller.rb new file mode 100644 index 000000000..f4dcc1535 --- /dev/null +++ b/app/controllers/examination_intelligent_settings_controller.rb @@ -0,0 +1,102 @@ +class ExaminationIntelligentSettingsController < ApplicationController + before_action :require_login + before_action :find_exam, only: [:exchange_one_item, :exchange_items, :save_exam] + + def optinal_items + sub_discipline_id = params[:sub_discipline_id] + tag_discipline_id = params[:tag_discipline_id] + difficulty = params[:difficulty] + source = params[:source] + + items = OptionalItemQuery.call(sub_discipline_id, tag_discipline_id, difficulty, source) + @single_question_count = items.select{ |item| item.item_type == "SINGLE" }.size + @multiple_question_count = items.select{ |item| item.item_type == "MULTIPLE" }.size + @judgement_question_count = items.select{ |item| item.item_type == "JUDGMENT" }.size + @program_question_count = items.select{ |item| item.item_type == "PROGRAM" }.size + end + + def create + ActiveRecord::Base.transaction do + exam = ExaminationIntelligentSetting.new(user: current_user) + # 保存试卷基础信息 + exam = ExaminationIntelligentSettings::SaveSettingService.call(exam, form_params) + render_ok({exam_setting_id: exam.id}) + end + rescue ApplicationService::Error => ex + render_error(ex.message) + end + + def save_exam + new_exam = ExaminationBank.new(user: current_user) + # 保存试卷基础信息 + ExaminationIntelligentSettings::SaveExaminationService.call(new_exam, save_params, @exam) + render_ok + rescue ApplicationService::Error => ex + render_error(ex.message) + end + + def exchange_one_item + item = @exam.item_baskets.find_by!(id: params[:item_id]) + exam_type_setting = @exam.examination_type_settings.find_by!(item_type: item.item_type) + + # 获取可选的题 + items = OptionalItemQuery.call(@exam.sub_discipline_id, @exam.tag_discipline_containers.pluck(:tag_discipline_id), @exam.difficulty, @exam.public) + type_items = items.select{ |t_item| t_item.item_type == item.item_type } + # 如果可选的题数小于等于设置的题数则提示无可换的题 + tip_exception("无可换的题") if type_items.size <= exam_type_setting.count + # 可选题中去掉已组卷的同题型试题 + optional_item_ids = type_items.pluck(:id) - @exam.item_baskets.where(item_type: item.item_type).pluck(:item_bank_id) + new_item = ItemBank.find optional_item_ids.sample(1).first + ActiveRecord::Base.transaction do + @exam.item_baskets << ItemBasket.new(item_bank_id: new_item.id, position: item.position, score: item.score, item_type: new_item.item_type) + item.destroy! + end + render_ok + end + + def exchange_items + exam_type_setting = @exam.examination_type_settings.find_by!(item_type: params[:item_type]) + choosed_items = @exam.item_baskets.where(item_type: params[:item_type]) + + # 获取可选的题 + items = OptionalItemQuery.call(@exam.sub_discipline_id, @exam.tag_discipline_containers.pluck(:tag_discipline_id), @exam.difficulty, @exam.public) + type_items = items.select{ |t_item| t_item.item_type == params[:item_type] } + # 如果可选的题数小于等于设置的题数则提示无可换的题 + tip_exception("无可换的题") if type_items.size <= exam_type_setting.count + # 可选题中去掉已组卷的同题型试题 + choosed_item_ids = choosed_items.pluck(:item_bank_id) + optional_item_ids = type_items.pluck(:id) - choosed_item_ids + + # 如果可选题数小于设置的题数n,则在原来的选题中随机选n个,确保换题时能选到新的题 + if optional_item_ids.size < exam_type_setting.count + absence_count = exam_type_setting.count - optional_item_ids.size + optional_item_ids = optional_item_ids + choosed_item_ids.sample(absence_count) + end + + ActiveRecord::Base.transaction do + # 取试题分数 + score = choosed_items.first&.score || (params[:item_type] == "PROGRAM" ? 10 : 5) + choosed_items.destroy_all + optional_item_ids.sample(exam_type_setting.count).each_with_index do |item_id, index| + new_item = ItemBank.find item_id + @exam.item_baskets << ItemBasket.new(item_bank_id: new_item.id, position: index+1, score: score, item_type: new_item.item_type) + end + end + render_ok + end + + private + + def find_exam + @exam = ExaminationIntelligentSetting.find_by!(id: params[:id]) + tip_exception(403,"无权限编辑") unless current_user.admin_or_business? || @exam.user_id == current_user.id + end + + def form_params + params.permit(:discipline_id, :sub_discipline_id, :difficulty, :source, tag_discipline_id: [], question_settings: %i[item_type count]) + end + + def save_params + params.permit(:name, :duration) + end +end \ No newline at end of file diff --git a/app/controllers/item_banks_controller.rb b/app/controllers/item_banks_controller.rb index 24d9c44a9..a0abffe6c 100644 --- a/app/controllers/item_banks_controller.rb +++ b/app/controllers/item_banks_controller.rb @@ -9,7 +9,14 @@ class ItemBanksController < ApplicationController @items_count = items.size @items = paginate items.includes(:item_analysis, :user, :container) exam = ExaminationBank.find_by(id: params[:exam_id]) if params[:exam_id].present? - @item_basket_ids = exam ? exam.examination_items.pluck(:item_bank_id) : current_user.item_baskets.pluck(:item_bank_id) + exam_setting = ExaminationIntelligentSetting.find_by(id: params[:exam_setting_id]) if params[:exam_setting_id].present? + @item_basket_ids = if exam + exam.examination_items.pluck(:item_bank_id) + elsif exam_setting + exam_setting.item_baskets.pluck(:item_bank_id) + else + current_user.item_baskets.pluck(:item_bank_id) + end end def create @@ -32,8 +39,11 @@ class ItemBanksController < ApplicationController end def destroy - @item.destroy! - render_ok + ActiveRecord::Base.transaction do + ApplyAction.where(container_type: "ItemBank", container_id: @item.id).destroy_all + @item.destroy! + render_ok + end end def set_public diff --git a/app/controllers/item_baskets_controller.rb b/app/controllers/item_baskets_controller.rb index 21203346b..dc5367378 100644 --- a/app/controllers/item_baskets_controller.rb +++ b/app/controllers/item_baskets_controller.rb @@ -4,7 +4,7 @@ class ItemBasketsController < ApplicationController helper_method :current_basket def index - @item_baskets = current_user.item_baskets + @item_baskets = basket_items @single_questions = @item_baskets.where(item_type: "SINGLE") @multiple_questions = @item_baskets.where(item_type: "MULTIPLE") @judgement_questions = @item_baskets.where(item_type: "JUDGMENT") @@ -22,41 +22,41 @@ class ItemBasketsController < ApplicationController end def create - ItemBaskets::SaveItemBasketService.call(current_user, create_params) + ItemBaskets::SaveItemBasketService.call(current_user, create_params, exam_setting) render_ok rescue ApplicationService::Error => ex render_error(ex.message) end def destroy - item = current_user.item_baskets.find_by!(item_bank_id: params[:id]) + item = basket_items.find_by!(item_bank_id: params[:id]) ActiveRecord::Base.transaction do - current_user.item_baskets.where(item_type: item.item_type).where("position > #{item.position}").update_all("position = position -1") + basket_items.where(item_type: item.item_type).where("position > #{item.position}").update_all("position = position -1") item.destroy! end render_ok end def delete_item_type - baskets = ItemBasket.where(item_type: params[:item_type]) + baskets = basket_items.where(item_type: params[:item_type]) baskets.destroy_all render_ok end def set_score current_basket.update_attributes!(score: params[:score]) - @questions_score = current_user.item_baskets.where(item_type: current_basket.item_type).pluck(:score).sum - @all_score = current_user.item_baskets.pluck(:score).sum + @questions_score = basket_items.where(item_type: current_basket.item_type).pluck(:score).sum + @all_score = basket_items.pluck(:score).sum end def batch_set_score - current_user.item_baskets.where(item_type: params[:item_type]).update_all(score: params[:score]) - @questions_score = current_user.item_baskets.where(item_type: params[:item_type]).pluck(:score).sum - @all_score = current_user.item_baskets.pluck(:score).sum + basket_items.where(item_type: params[:item_type]).update_all(score: params[:score]) + @questions_score = basket_items.where(item_type: params[:item_type]).pluck(:score).sum + @all_score = basket_items.pluck(:score).sum end def adjust_position - same_items = current_user.item_baskets.where(item_type: current_basket.item_type) + same_items = basket_items.where(item_type: current_basket.item_type) max_position = same_items.size tip_exception("position超出范围") unless params[:position].present? && params[:position].to_i <= max_position && params[:position].to_i >= 1 ActiveRecord::Base.transaction do @@ -79,8 +79,19 @@ class ItemBasketsController < ApplicationController params.permit(item_ids: []) end + def exam_setting + @_exam_setting = ExaminationIntelligentSetting.find_by(id: params[:exam_setting_id]) + end + + def basket_items + @_items = params[:exam_setting_id] ? exam_setting.item_baskets : current_user.item_baskets + end + def current_basket - @_current_basket = current_user.item_baskets.find_by!(id: params[:id]) + @_current_basket = ItemBasket.find_by!(id: params[:id]) + tip_exception(403, "无权限编辑") unless current_user.admin_or_business? || @_current_basket.user_id.to_i == current_user.id || + @_current_basket.examination_intelligent_setting&.user_id.to_i == current_user.id + @_current_basket end def validate_score diff --git a/app/forms/examination_intelligent_settings/save_exam_form.rb b/app/forms/examination_intelligent_settings/save_exam_form.rb new file mode 100644 index 000000000..ec4f17ad7 --- /dev/null +++ b/app/forms/examination_intelligent_settings/save_exam_form.rb @@ -0,0 +1,12 @@ +class ExaminationIntelligentSettings::SaveExamForm + include ActiveModel::Model + + attr_accessor :name, :duration + + validates :name, presence: true, length: { maximum: 60 } + validate :validate_duration + + def validate_duration + raise '时长应为大于0的整数' if duration.present? && duration.to_i < 1 + end +end \ No newline at end of file diff --git a/app/forms/examination_intelligent_settings/save_exam_setting_form.rb b/app/forms/examination_intelligent_settings/save_exam_setting_form.rb new file mode 100644 index 000000000..bbfb9eee8 --- /dev/null +++ b/app/forms/examination_intelligent_settings/save_exam_setting_form.rb @@ -0,0 +1,11 @@ +class ExaminationIntelligentSettings::SaveExamSettingForm + include ActiveModel::Model + + attr_accessor :discipline_id, :sub_discipline_id, :source, :difficulty, :tag_discipline_id, :question_settings + + validates :discipline_id, presence: true + validates :sub_discipline_id, presence: true + validates :source, presence: true + validates :difficulty, presence: true, inclusion: {in: 1..3}, numericality: { only_integer: true } + validates :question_settings, presence: true +end \ No newline at end of file diff --git a/app/forms/item_banks/save_item_form.rb b/app/forms/item_banks/save_item_form.rb index 7e788067e..d6d3a3e28 100644 --- a/app/forms/item_banks/save_item_form.rb +++ b/app/forms/item_banks/save_item_form.rb @@ -15,9 +15,9 @@ class ItemBanks::SaveItemForm return unless errors.blank? choices.each { |item| SubForm.new(item).validate! } if %W(SINGLE MULTIPLE JUDGMENT).include?(item_type) return unless errors.blank? - if [0, 2].include?(item_type) && choices.pluck(:is_answer).select{|item| item == 1}.length > 1 + if ["SINGLE", "JUDGMENT"].include?(item_type) && choices.pluck(:is_answer).select{|item| item == 1}.length > 1 raise("正确答案只能有一个") - elsif item_type == 1 && choices.pluck(:is_answer).select{|item| item == 1}.length <= 1 + elsif item_type == "MULTIPLE" && choices.pluck(:is_answer).select{|item| item == 1}.length <= 1 raise("多选题至少有两个正确答案") end end diff --git a/app/models/examination_bank.rb b/app/models/examination_bank.rb index f7b7cc0bf..824f0bb69 100644 --- a/app/models/examination_bank.rb +++ b/app/models/examination_bank.rb @@ -7,6 +7,10 @@ class ExaminationBank < ApplicationRecord has_many :examination_items, -> {order(position: :asc)}, dependent: :destroy + def apply? + !public && ApplyAction.where(container_type: "ExaminationBank", container_id: id, status: 0).exists? + end + def question_count examination_items.size end diff --git a/app/models/examination_intelligent_setting.rb b/app/models/examination_intelligent_setting.rb new file mode 100644 index 000000000..38c7fbe4b --- /dev/null +++ b/app/models/examination_intelligent_setting.rb @@ -0,0 +1,7 @@ +class ExaminationIntelligentSetting < ApplicationRecord + belongs_to :sub_discipline + belongs_to :user + has_many :examination_type_settings, dependent: :destroy + has_many :tag_discipline_containers, as: :container, dependent: :destroy + has_many :item_baskets, dependent: :destroy +end diff --git a/app/models/examination_type_setting.rb b/app/models/examination_type_setting.rb new file mode 100644 index 000000000..30f3e80b4 --- /dev/null +++ b/app/models/examination_type_setting.rb @@ -0,0 +1,4 @@ +class ExaminationTypeSetting < ApplicationRecord + enum item_type: { SINGLE: 0, MULTIPLE: 1, JUDGMENT: 2, COMPLETION: 3, SUBJECTIVE: 4, PRACTICAL: 5, PROGRAM: 6 } + belongs_to :examination_intelligent_setting +end diff --git a/app/models/item_bank.rb b/app/models/item_bank.rb index dcc0007e2..840f488d8 100644 --- a/app/models/item_bank.rb +++ b/app/models/item_bank.rb @@ -18,6 +18,10 @@ class ItemBank < ApplicationRecord item_analysis&.analysis end + def apply? + !public && ApplyAction.where(container_type: "ItemBank", container_id: id, status: 0).exists? + end + def type_string result = case item_type when "SINGLE" diff --git a/app/models/item_basket.rb b/app/models/item_basket.rb index a3ff865ba..eb849d403 100644 --- a/app/models/item_basket.rb +++ b/app/models/item_basket.rb @@ -2,13 +2,6 @@ class ItemBasket < ApplicationRecord enum item_type: { SINGLE: 0, MULTIPLE: 1, JUDGMENT: 2, COMPLETION: 3, SUBJECTIVE: 4, PRACTICAL: 5, PROGRAM: 6 } belongs_to :item_bank - belongs_to :user - - def all_score - User.current.item_baskets.map(&:score).sum - end - - def question_count - User.current.item_baskets.size - end + belongs_to :user, optional: true + belongs_to :examination_intelligent_setting, optional: true end diff --git a/app/models/user.rb b/app/models/user.rb index f1b8c3d23..2af43b6f5 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -156,6 +156,8 @@ class User < ApplicationRecord # 题库 has_many :item_banks, dependent: :destroy has_many :item_baskets, -> { order("item_baskets.position ASC") }, dependent: :destroy + has_many :examination_banks, dependent: :destroy + has_many :examination_intelligent_settings, dependent: :destroy # Groups and active users scope :active, lambda { where(status: STATUS_ACTIVE) } diff --git a/app/queries/optional_item_query.rb b/app/queries/optional_item_query.rb new file mode 100644 index 000000000..54cd0eed0 --- /dev/null +++ b/app/queries/optional_item_query.rb @@ -0,0 +1,35 @@ +class OptionalItemQuery < ApplicationQuery + attr_reader :sub_discipline_id, :tag_discipline_id, :difficulty, :source + + def initialize(sub_discipline_id, tag_discipline_id, difficulty, source) + @sub_discipline_id = sub_discipline_id + @tag_discipline_id = tag_discipline_id + @difficulty = difficulty + @source = source + end + + def call + items = ItemBank.all + if tag_discipline_id.present? && sub_discipline_id.present? + items = items.joins(:tag_discipline_containers).where(tag_discipline_containers: {tag_discipline_id: tag_discipline_id}) + hacks = Hack.joins(:tag_discipline_containers).where(tag_discipline_containers: {tag_discipline_id: tag_discipline_id}) + elsif sub_discipline_id.present? + items = items.where(sub_discipline_id: sub_discipline_id) + hacks = Hack.where(sub_discipline_id: sub_discipline_id) + end + + if hacks.present? + items = ItemBank.where(container_id: hacks.pluck(:id), container_type: "Hack").or(ItemBank.where(id: items.pluck(:id))) + end + + # 来源 + public = source.present? ? source.to_i : 1 + public = public == 2 ? [0, 1] : public + items = items.where(public: public) + + # 难度 + difficulty = difficulty ? difficulty.to_i : 1 + items = items.where(difficulty: difficulty) + items + end +end \ No newline at end of file diff --git a/app/services/examination_intelligent_settings/save_examination_service.rb b/app/services/examination_intelligent_settings/save_examination_service.rb new file mode 100644 index 000000000..d6f5e7406 --- /dev/null +++ b/app/services/examination_intelligent_settings/save_examination_service.rb @@ -0,0 +1,38 @@ +class ExaminationIntelligentSettings::SaveExaminationService < ApplicationService + attr_reader :exam, :params, :exam_setting + + def initialize(exam, params, exam_setting) + @exam = exam + @params = params + @exam_setting = exam_setting + end + + def call + ExaminationIntelligentSettings::SaveExamForm.new(params).validate! + + ActiveRecord::Base.transaction do + exam.name = params[:name].to_s.strip + exam.difficulty = exam_setting.difficulty + exam.duration = params[:duration].present? ? params[:duration].to_i : nil + exam.sub_discipline_id = exam_setting.sub_discipline_id + exam.intelligent = 1 + exam.save! + + # 知识点的创建 + exam_setting.tag_discipline_containers.each do |tag| + exam.tag_discipline_containers << TagDisciplineContainer.new(tag_discipline_id: tag.tag_discipline_id) + end + + # 试题的复制 + exam_setting.item_baskets.includes(:item_bank).each do |basket| + item = basket.item_bank + if item.present? + new_item = ExaminationItem.new + new_item.new_item(item, exam, basket.score, basket.position) + end + end + + exam_setting.destroy! + end + end +end \ No newline at end of file diff --git a/app/services/examination_intelligent_settings/save_setting_service.rb b/app/services/examination_intelligent_settings/save_setting_service.rb new file mode 100644 index 000000000..af22b3234 --- /dev/null +++ b/app/services/examination_intelligent_settings/save_setting_service.rb @@ -0,0 +1,63 @@ +class ExaminationIntelligentSettings::SaveSettingService < ApplicationService + attr_reader :exam, :params + + def initialize(exam, params) + @exam = exam + @params = params + end + + def call + ExaminationIntelligentSettings::SaveExamSettingForm.new(params).validate! + items = OptionalItemQuery.call(params[:sub_discipline_id], params[:tag_discipline_id], params[:difficulty], params[:source]) + params[:question_settings].each do |setting| + raise "超出可选题数范围" if items.select{ |item| item.item_type == setting[:item_type] }.size.to_i < setting[:count].to_i + end + + exam.difficulty = params[:difficulty] + exam.sub_discipline_id = params[:sub_discipline_id] + exam.public = params[:source].present? ? params[:source].to_i : 1 + exam.save! + + # 知识点的创建 + params[:tag_discipline_id].each do |tag_id| + exam.tag_discipline_containers << TagDisciplineContainer.new(tag_discipline_id: tag_id) + end + + # 智能选题的设置 + params[:question_settings].each do |setting| + if setting[:count].to_i > 0 + exam.examination_type_settings << ExaminationTypeSetting.new(item_type: setting[:item_type], count: setting[:count].to_i) + end + end + + # 选题 + choose_question items + + exam + end + + private + + def choose_question items + exam.examination_type_settings.each do |setting| + questions = items.select{ |item| item.item_type == setting.item_type } + questions.pluck(:id).sample(setting.count).each_with_index do |item_id, index| + item = ItemBank.find item_id + exam.item_baskets << ItemBasket.new(item_bank_id: item.id, position: index+1, score: item_score(item.item_type), item_type: item.item_type) + end + end + end + + def item_score item_type + score = + case item_type + when "SINGLE", "MULTIPLE", "JUDGMENT" + 5 + when "PROGRAM" + 10 + else + 5 + end + score + end +end \ No newline at end of file diff --git a/app/services/item_baskets/save_item_basket_service.rb b/app/services/item_baskets/save_item_basket_service.rb index cf6d3738b..debc7e2ed 100644 --- a/app/services/item_baskets/save_item_basket_service.rb +++ b/app/services/item_baskets/save_item_basket_service.rb @@ -1,9 +1,10 @@ class ItemBaskets::SaveItemBasketService < ApplicationService - attr_reader :user, :params + attr_reader :user, :params, :exam_setting - def initialize(user, params) + def initialize(user, params, exam_setting) @user = user @params = params + @exam_setting = exam_setting end def call @@ -13,9 +14,14 @@ class ItemBaskets::SaveItemBasketService < ApplicationService items = ItemBank.where(public: 1).or(ItemBank.where(user_id: user.id)) # 已选到过试题篮的不重复选用 - item_ids = params[:item_ids] - user.item_baskets.pluck(:item_bank_id) + item_ids = params[:item_ids] - basket_items.pluck(:item_bank_id) items.where(id: item_ids).each do |item| - new_item = ItemBasket.new(user_id: user.id, item_bank_id: item.id, item_type: item.item_type) + new_item = ItemBasket.new(item_bank_id: item.id, item_type: item.item_type) + if exam_setting.present? + new_item.examination_intelligent_setting_id = exam_setting.id + else + new_item.user_id = user.id + end new_item.score = item_score item.item_type new_item.position = item_position item.item_type new_item.save! @@ -25,8 +31,8 @@ class ItemBaskets::SaveItemBasketService < ApplicationService private def item_score item_type - if user.item_baskets.where(item_type: item_type).last.present? - score = user.item_baskets.where(item_type: item_type).last.score + if basket_items.where(item_type: item_type).last.present? + score = basket_items.where(item_type: item_type).last.score else score = case item_type @@ -42,6 +48,10 @@ class ItemBaskets::SaveItemBasketService < ApplicationService end def item_position item_type - user.item_baskets.where(item_type: item_type).last&.position.to_i + 1 + basket_items.where(item_type: item_type).last&.position.to_i + 1 + end + + def basket_items + exam_setting.present? ? exam_setting.item_baskets : user.item_baskets end end \ No newline at end of file diff --git a/app/views/examination_banks/index.json.jbuilder b/app/views/examination_banks/index.json.jbuilder index 75bd006fe..ae24a377c 100644 --- a/app/views/examination_banks/index.json.jbuilder +++ b/app/views/examination_banks/index.json.jbuilder @@ -1,8 +1,9 @@ json.exams @exams.each do |exam| - json.(exam, :id, :name, :difficulty, :quotes) + json.(exam, :id, :name, :difficulty, :quotes, :public) json.question_count exam.question_count json.total_score exam.total_score json.update_time exam.updated_at&.strftime("%Y-%m-%d %H:%M") + json.apply exam.apply? json.author do json.login exam.user&.login json.name exam.user&.full_name diff --git a/app/views/examination_intelligent_settings/optinal_items.json.jbuilder b/app/views/examination_intelligent_settings/optinal_items.json.jbuilder new file mode 100644 index 000000000..7d8ec7381 --- /dev/null +++ b/app/views/examination_intelligent_settings/optinal_items.json.jbuilder @@ -0,0 +1,4 @@ +json.single_question_count @single_question_count +json.multiple_question_count @multiple_question_count +json.judgement_question_count @judgement_question_count +json.program_question_count @program_question_count diff --git a/app/views/item_banks/index.json.jbuilder b/app/views/item_banks/index.json.jbuilder index 50a3e063c..c1695b866 100644 --- a/app/views/item_banks/index.json.jbuilder +++ b/app/views/item_banks/index.json.jbuilder @@ -3,6 +3,7 @@ json.items @items.each do |item| json.partial! "item_banks/item", locals: {item: item} json.update_time item.updated_at&.strftime("%Y-%m-%d %H:%M") json.choosed @item_basket_ids.include?(item.id) + json.apply item.apply? json.author do json.login item.user&.login json.name item.user&.full_name diff --git a/config/routes.rb b/config/routes.rb index 08df77eb9..a63776d44 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -96,6 +96,18 @@ Rails.application.routes.draw do end end + resources :examination_intelligent_settings do + collection do + get :optinal_items + end + + member do + post :save_exam + post :exchange_one_item + post :exchange_items + end + end + resources :hacks, path: :problems, param: :identifier do collection do get :unpulished_list diff --git a/db/migrate/20200107091630_create_examination_intelligent_settings.rb b/db/migrate/20200107091630_create_examination_intelligent_settings.rb new file mode 100644 index 000000000..f00f19c33 --- /dev/null +++ b/db/migrate/20200107091630_create_examination_intelligent_settings.rb @@ -0,0 +1,13 @@ +class CreateExaminationIntelligentSettings < ActiveRecord::Migration[5.2] + def change + create_table :examination_intelligent_settings do |t| + t.references :sub_discipline + t.integer :public, default: 1 + t.integer :difficulty, default: 1 + + t.timestamps + end + + add_index :examination_intelligent_settings, :sub_discipline_id, name: "index_on_sub_discipline_id" + end +end diff --git a/db/migrate/20200107091836_create_examination_type_settings.rb b/db/migrate/20200107091836_create_examination_type_settings.rb new file mode 100644 index 000000000..c2eb50e9e --- /dev/null +++ b/db/migrate/20200107091836_create_examination_type_settings.rb @@ -0,0 +1,13 @@ +class CreateExaminationTypeSettings < ActiveRecord::Migration[5.2] + def change + create_table :examination_type_settings do |t| + t.references :examination_intelligent_setting, index: false + t.integer :item_type + t.integer :count + + t.timestamps + end + + add_index :examination_type_settings, :examination_intelligent_setting_id, name: "index_on_examination_intelligent_setting" + end +end diff --git a/db/migrate/20200108030930_add_user_id_to_intelligent_settings.rb b/db/migrate/20200108030930_add_user_id_to_intelligent_settings.rb new file mode 100644 index 000000000..83d3b96b4 --- /dev/null +++ b/db/migrate/20200108030930_add_user_id_to_intelligent_settings.rb @@ -0,0 +1,5 @@ +class AddUserIdToIntelligentSettings < ActiveRecord::Migration[5.2] + def change + add_column :examination_intelligent_settings, :user_id, :integer, index: true + end +end diff --git a/db/migrate/20200108061139_add_intelligent_setting_id_to_item_baskets.rb b/db/migrate/20200108061139_add_intelligent_setting_id_to_item_baskets.rb new file mode 100644 index 000000000..9de17ee48 --- /dev/null +++ b/db/migrate/20200108061139_add_intelligent_setting_id_to_item_baskets.rb @@ -0,0 +1,5 @@ +class AddIntelligentSettingIdToItemBaskets < ActiveRecord::Migration[5.2] + def change + add_column :item_baskets, :examination_intelligent_setting_id, :integer, index: true + end +end diff --git a/db/migrate/20200109021528_add_intelligent_to_exam.rb b/db/migrate/20200109021528_add_intelligent_to_exam.rb new file mode 100644 index 000000000..fa4b16dd1 --- /dev/null +++ b/db/migrate/20200109021528_add_intelligent_to_exam.rb @@ -0,0 +1,5 @@ +class AddIntelligentToExam < ActiveRecord::Migration[5.2] + def change + add_column :examination_banks, :intelligent, :boolean, default: false + end +end diff --git a/public/react/src/AppConfig.js b/public/react/src/AppConfig.js index d8f642db3..535b19a94 100644 --- a/public/react/src/AppConfig.js +++ b/public/react/src/AppConfig.js @@ -42,7 +42,7 @@ if (isDev) { // 老师 //debugType="teacher"; // 学生 -debugType="student"; +//debugType="student"; function railsgettimes(proxy) { @@ -83,7 +83,7 @@ export function initAxiosInterceptors(props) { //proxy="http://47.96.87.25:48080" proxy="https://pre-newweb.educoder.net" proxy="https://test-newweb.educoder.net" - // proxy="https://test-jupyterweb.educoder.net" + //proxy="https://test-jupyterweb.educoder.net" //proxy="http://192.168.2.63:3001" // 在这里使用requestMap控制,避免用户通过双击等操作发出重复的请求; diff --git a/public/react/src/common/quillForEditor/README.md b/public/react/src/common/quillForEditor/README.md index 75b403590..c9c53b902 100644 --- a/public/react/src/common/quillForEditor/README.md +++ b/public/react/src/common/quillForEditor/README.md @@ -61,6 +61,7 @@ ### 使用 ```` + 编辑模式是放不大图片的 import QuillForEditor from 'xxx'; // 指定需要显示的工具栏信息, 不指定加载全部 diff --git a/public/react/src/modules/courses/exercise/new/SingleEditor.js b/public/react/src/modules/courses/exercise/new/SingleEditor.js index 0b1b7599a..8585e0a82 100644 --- a/public/react/src/modules/courses/exercise/new/SingleEditor.js +++ b/public/react/src/modules/courses/exercise/new/SingleEditor.js @@ -8,8 +8,9 @@ import { import TPMMDEditor from '../../../tpm/challengesnew/TPMMDEditor'; import axios from 'axios' import update from 'immutability-helper' - import {getUrl, ActionBtn, DMDEditor, ConditionToolTip} from 'educoder'; +import QuillForEditor from "../../../../common/quillForEditor"; + const { TextArea } = Input; const confirm = Modal.confirm; const $ = window.$ @@ -47,6 +48,9 @@ class SingleEditor extends Component{ question_title: this.props.question_title || '', question_type: this.props.question_type || 0, question_score: this.props.question_score || this.props.init_question_score, + choice_editor: 'md', + quill_question_title: '', + quill_default_title: '' } } addOption = () => { @@ -205,8 +209,33 @@ class SingleEditor extends Component{ toShowMode = () => { } + + // 切换编辑器 + handleChangeEditor = (e) => { + const {quill_question_title} = this.state; + const value = e.target.value + if (value === 'quill') { + const _val = quill_question_title ? JSON.parse(quill_question_title) : ''; + this.setState({ + quill_default_title: _val + }) + } + this.setState({ + choice_editor: value + }); + } + + // quill编辑器内容变化时调用此接口 + handleCtxChange = (ctx) => { + console.log('编辑器内容', ctx); + // 保存编辑器内容 + this.setState({ + quill_question_title: JSON.stringify(ctx) + }); + } + render() { - let { question_title, question_score, question_type, question_choices, standard_answers } = this.state; + let { question_title, question_score, question_type, question_choices, standard_answers, choice_editor, quill_default_title } = this.state; let { question_id, index, exerciseIsPublish, // question_title, // question_type, @@ -245,18 +274,51 @@ class SingleEditor extends Component{ max-width: 1056px; word-break:break-all; } + .editor_area{ + display: inline-block; + float: right; + // line-height: 30px; + // height: 30px; + } + .editor_txt{ + margin-right: 10px; + font-size: 12px; + color: #999; + } + .radio_style{ + display: inline-block; + vertical: center; + } `}</style> <p className="mb10 clearfix"> {/* {!question_id ? '新建' : '编辑'} */} <span className="color-blue font-16 mr20 fl">选择题</span> <span className="color-grey-9 font-12 fl">(客观题,由系统自动评分,请设置标准答案)</span> + {/* <Switch checkedChildren="MD" unCheckedChildren="Quill"></Switch> */} + {/* <div className="editor_area"> + <span className="editor_txt">切换编辑器:</span> + <Radio.Group style={{ float: 'right' }} value={choice_editor} onChange={this.handleChangeEditor}> + <Radio className="radio_style" value={'md'}>MD</Radio> + <Radio className="radio_style" value={'quill'}>Quill</Radio> + </Radio.Group> + </div> */} </p> - <TPMMDEditor mdID={qNumber} placeholder="请您输入题目" height={155} className="mb20" - initValue={question_title} onChange={(val) => this.setState({ question_title: val})} - ref="titleEditor" + {choice_editor === 'md' + ? <TPMMDEditor mdID={qNumber} placeholder="请您输入题目" height={155} className="mb20" + initValue={question_title} onChange={(val) => this.setState({ question_title: val})} + ref="titleEditor" + ></TPMMDEditor> - ></TPMMDEditor> + : <QuillForEditor + wrapStyle={{ marginBottom: '35px' }} + style={{ height: '109px' }} + options={['code', 'image', 'formula']} + placeholder="请您输入题目" + value={quill_default_title} + onContentChange={this.handleCtxChange} + ></QuillForEditor> + } {question_choices.map( (item, index) => { const bg = standard_answers[index] ? 'check-option-bg' : '' diff --git a/public/react/src/modules/question/NewMyShixunModel.js b/public/react/src/modules/question/NewMyShixunModel.js index 9db5d242f..b300156b7 100644 --- a/public/react/src/modules/question/NewMyShixunModel.js +++ b/public/react/src/modules/question/NewMyShixunModel.js @@ -34,7 +34,7 @@ class NewMyShixunModel extends Component { placement: 'right', modalsType: false, modalsTypes:false, - titilesm: "设为公开后,所有成员均可使用试题", + titilesm: "在平台审核后,所有成员均可使用试题", titiless: "是否设置为公开?", titilesms:"单选题", titbool: false, @@ -91,7 +91,7 @@ class NewMyShixunModel extends Component { page: this.state.page, per_page:10, oj_status:null, - item_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), + exam_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), }; this.getdata(data); @@ -117,7 +117,7 @@ class NewMyShixunModel extends Component { page: 1, per_page:10, oj_status:null, - item_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), + exam_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), }; this.getdata(data); } @@ -141,7 +141,7 @@ class NewMyShixunModel extends Component { page: 1, per_page:10, oj_status:null, - item_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), + exam_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), }; this.getdata(data); } @@ -156,7 +156,7 @@ class NewMyShixunModel extends Component { public: defaultActiveKey, page:1, per_page:10, - item_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), + exam_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), }; this.getdata(data); @@ -209,7 +209,7 @@ class NewMyShixunModel extends Component { page: 1, per_page:10, oj_status:null, - item_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), + exam_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), }; this.getdata(data); @@ -342,7 +342,7 @@ class NewMyShixunModel extends Component { page: pageNumber, per_page:10, oj_status:this.state.oj_status, - item_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), + exam_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), }; this.getdata(data); @@ -386,7 +386,7 @@ class NewMyShixunModel extends Component { this.setState({ modalsType: true, - titilesm: "设为公开后,所有成员均可使用试题", + titilesm: "在平台审核后,所有成员均可使用试题", titiless: "是否设置为公开?", titbool: true, timuid: id @@ -461,7 +461,7 @@ class NewMyShixunModel extends Component { page:1, per_page:10, oj_status:null, - item_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), + exam_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), }; this.getdata(data); @@ -488,7 +488,7 @@ class NewMyShixunModel extends Component { per_page:10, keywords:null, oj_status:null, - item_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), + exam_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), }; this.getdata(data); @@ -543,7 +543,7 @@ class NewMyShixunModel extends Component { page: this.state.page, per_page:10, oj_status:this.state.oj_status, - item_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), + exam_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), }; this.getdata(data); @@ -567,7 +567,7 @@ class NewMyShixunModel extends Component { keywords: this.state.keywords, page: this.state.page, per_page:10, - item_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), + exam_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), }; this.getdata(data); } @@ -593,7 +593,7 @@ class NewMyShixunModel extends Component { keywords: this.state.keywords, page: this.state.page, per_page:10, - item_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), + exam_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), }; this.getdata(data); @@ -661,7 +661,7 @@ class NewMyShixunModel extends Component { keywords: this.state.keywords, page: this.state.page, per_page:10, - item_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), + exam_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), }; this.getdatasy(data); this.getbasket_listdata(); @@ -678,34 +678,57 @@ class NewMyShixunModel extends Component { let url=""; if(this.props.exam_id===undefined){ url=`/item_baskets/${id}.json`; + axios.delete(url) + .then((result) => { + if (result.data.status == 0) { + // this.props.showNotification(`撤销成功`); + var data = { + discipline_id:this.state.discipline_id, + sub_discipline_id:this.state.sub_discipline_id, + tag_discipline_id:this.state.tag_discipline_id, + public: this.state.defaultActiveKey, + difficulty: this.state.difficulty, + item_type: this.state.item_type, + keywords: this.state.keywords, + page: this.state.page, + per_page:10, + exam_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), + }; + this.getdatasy(data); + this.getbasket_listdata(); + } + }).catch((error) => { + //console.log(error); + }) }else{ - url=`/examination_banks/${id}/revoke_item.json`; + url=`/examination_banks/${this.props.exam_id}/revoke_item.json`; + axios.delete(url,{ data: { + item_id:id===undefined?"":parseInt(id), + }}) + .then((result) => { + if (result.data.status == 0) { + // this.props.showNotification(`撤销成功`); + var data = { + discipline_id:this.state.discipline_id, + sub_discipline_id:this.state.sub_discipline_id, + tag_discipline_id:this.state.tag_discipline_id, + public: this.state.defaultActiveKey, + difficulty: this.state.difficulty, + item_type: this.state.item_type, + keywords: this.state.keywords, + page: this.state.page, + per_page:10, + exam_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), + }; + this.getdatasy(data); + this.getbasket_listdata(); + } + }).catch((error) => { + //console.log(error); + }) } - axios.delete(url,{ data: { - item_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), - }}) - .then((result) => { - if (result.data.status == 0) { - // this.props.showNotification(`撤销成功`); - var data = { - discipline_id:this.state.discipline_id, - sub_discipline_id:this.state.sub_discipline_id, - tag_discipline_id:this.state.tag_discipline_id, - public: this.state.defaultActiveKey, - difficulty: this.state.difficulty, - item_type: this.state.item_type, - keywords: this.state.keywords, - page: this.state.page, - per_page:10, - item_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), - }; - this.getdatasy(data); - this.getbasket_listdata(); - } - }).catch((error) => { - //console.log(error); - }) + } //全选试题库 selectallquestionsonthispage=()=>{ @@ -739,7 +762,7 @@ class NewMyShixunModel extends Component { } const data={ item_ids:item_idsdata, - item_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), + exam_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), } this.getitem_baskets(data); this.setState({ @@ -768,7 +791,7 @@ class NewMyShixunModel extends Component { keywords: this.state.keywords, page: this.state.page, per_page:10, - item_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), + exam_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), }; this.getdata(data); this.getbasket_listdata(); @@ -806,7 +829,7 @@ class NewMyShixunModel extends Component { page: this.state.page, per_page:10, oj_status:oj_status, - item_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), + exam_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), }; this.getdata(data); } diff --git a/public/react/src/modules/question/Paperreview_item.js b/public/react/src/modules/question/Paperreview_item.js index 9112aa248..a9747e8c2 100644 --- a/public/react/src/modules/question/Paperreview_item.js +++ b/public/react/src/modules/question/Paperreview_item.js @@ -426,8 +426,8 @@ class Paperreview_item extends Component { }}> <div className="w100s sortinxdirection"> <div className="w70s sortinxdirection"> - <p className="questionstishu lh34">题数:{this.props.all_score}</p> - <p className="ml58 questionstotal lh34">总分:{this.props.all_questions_count}</p> + <p className="questionstishu lh34">题数:{this.props.all_questions_count}</p> + <p className="ml58 questionstotal lh34">总分:{this.props.all_score}</p> </div> <div className="w30s xaxisreverseorder"> <div className="jixuxuanti xiaoshou" onClick={() => this.props.setnewmyshixunmodelbool(true)}> diff --git a/public/react/src/modules/question/Paperreview_items.js b/public/react/src/modules/question/Paperreview_items.js index 81ec320b5..8e86c514a 100644 --- a/public/react/src/modules/question/Paperreview_items.js +++ b/public/react/src/modules/question/Paperreview_items.js @@ -59,8 +59,8 @@ class Paperreview_items extends Component { let {paperreviewsingleindex,paperreviewsinglename,typenames,indexs,object,typenamesn}=this.props; // console.log(object); - console.log("Paperreview_items"); - console.log(object.item_id); + // console.log("Paperreview_items"); + // console.log(object.item_id); return ( <div> { diff --git a/public/react/src/modules/question/Paperreview_single.js b/public/react/src/modules/question/Paperreview_single.js index 5b64372a8..fdc9ff095 100644 --- a/public/react/src/modules/question/Paperreview_single.js +++ b/public/react/src/modules/question/Paperreview_single.js @@ -17,6 +17,7 @@ import Itembankstop from "./component/Itembankstop"; import NoneData from './component/NoneData'; import './questioncss/questioncom.css'; import '../tpm/newshixuns/css/Newshixuns.css'; +import QuillForEditor from "../../common/quillForEditor"; const tagArray = [ 'A.', 'B.', 'C.', 'D.', 'E.', 'F.', 'G.', 'H.', 'I.', @@ -69,13 +70,42 @@ class Paperreview_single extends Component { render() { let {questions, totalscore, total, items} = this.state; let {objectsingle, indexx, paperreviewsingleindex, indexxy,name} = this.props; + + + var itemssname=""; + try { + itemssname= JSON.parse(objectsingle.name); + }catch (e) { + } + if(itemssname===undefined){ + itemssname=objectsingle.name + } return ( <div key={indexxy} className={ "w100s borderwdswuh mb20 pd20 "} onMouseEnter={() => this.props.showparagraphs(indexxy,name)} style={{ minHeight: "114px", }}> - + <style>{ + ` + .programquill .ql-editor{ + padding: 0px; + } + + .programquill .ql-editor p{ + line-height: 28px; + + } + .programquills .ql-editor{ + padding: 0px; + } + + .programquills .ql-editor p{ + line-height: 20px; + + } + ` + }</style> {/*顶部*/} <div className="w100s sortinxdirection "> <div className=" sortinxdirection "> @@ -99,9 +129,35 @@ class Paperreview_single extends Component { ` } </style> - <div className=" lh28 listjihetixingstit markdown-body cretitlecolrlist" style={{wordBreak: "break-word"}} - dangerouslySetInnerHTML={{__html: markdownToHTML(`(${objectsingle.score}分) ` + objectsingle.name).replace(/▁/g, "▁▁▁")}}> - </div> + { + objectsingle.item_type==="PROGRAM"? + <div className="w100s sortinxdirection"> + <div className=" tites lh28 listjihetixingstit markdown-body cretitlecolrlist " style={{wordBreak: "break-word"}} + > + ({objectsingle.score}分) + </div> + <div className="ml10 lh28 listjihetixingstit markdown-body cretitlecolrlist " style={{wordBreak: "break-word"}} + dangerouslySetInnerHTML={{__html: markdownToHTML(objectsingle.name).replace(/▁/g, "▁▁▁")}}> + + </div> + </div> + : + <div className="w100s sortinxdirection"> + <div className="tites lh28 listjihetixingstit markdown-body cretitlecolrlist " style={{wordBreak: "break-word"}} + > + ({objectsingle.score}分) + </div> + <div className="ml10 lh28 listjihetixingstit cretitlecolrlist programquill" style={{wordBreak: "break-word"}} + > + <QuillForEditor + readOnly={true} + value={itemssname} + /> + + </div> + </div> + } + </div> {/*内容*/} <div className="w100s sortinxdirection"> @@ -114,7 +170,7 @@ class Paperreview_single extends Component { objectsingle === undefined || objectsingle === null ? "" : objectsingle.choices.map((object, index) => { return ( <p className={index === 1 ? "sortinxdirection ml10" : "sortinxdirection "}> - <Radio checked={object.is_answer}> + <Radio disabled={false}> {object.choice_text} </Radio> </p> @@ -126,8 +182,17 @@ class Paperreview_single extends Component { objectsingle.item_type === "PROGRAM" ? <p className="w100s listjihetixingstitssy sortinxdirection "> <p className={"sortinxdirection mt15"}> - <p style={{wordBreak: "break-word"}} - dangerouslySetInnerHTML={{__html: markdownToHTML(objectsingle.program_attr.description).replace(/▁/g, "▁▁▁")}}></p> + { + objectsingle&&objectsingle.program_attr&&objectsingle.program_attr.description? + <p className="programquill" style={{wordBreak: "break-word"}} + > + <QuillForEditor + readOnly={true} + value={JSON.parse(objectsingle.program_attr.description)} + /> + </p> + : + ""} </p> </p> : @@ -137,8 +202,21 @@ class Paperreview_single extends Component { return ( <p className={index === 0 ? "sortinxdirection" : "sortinxdirection mt15"}> {tagArray[index]} - <p style={{wordBreak: "break-word"}} - dangerouslySetInnerHTML={{__html: markdownToHTML(object.choice_text).replace(/▁/g, "▁▁▁")}}></p> + <p className="programquills" style={{wordBreak: "break-word"}}> + {object ? + object.choice_text === undefined || object.choice_text=== null || object.choice_text === "" ? + "" + : + object.choice_text.length>0? + <QuillForEditor + readOnly={true} + value={JSON.parse(object.choice_text)} + /> + :"" + : + "" + } + </p> </p> ) }) diff --git a/public/react/src/modules/question/Question.js b/public/react/src/modules/question/Question.js index 171a2f2d7..3264c52fa 100644 --- a/public/react/src/modules/question/Question.js +++ b/public/react/src/modules/question/Question.js @@ -33,7 +33,7 @@ class Question extends Component { placement: 'right', modalsType: false, modalsTypes:false, - titilesm: "设为公开后,所有成员均可使用试题", + titilesm: "在平台审核后,所有成员均可使用试题", titiless: "是否设置为公开?", titilesms:"单选题", titbool: false, @@ -66,9 +66,28 @@ class Question extends Component { oj_status:null, isVisible: false, selectionbools:false, + chakanjiexiboolindex:"无", } } + + chakanjiexibool=(index)=>{ + debugger + if(this.state.chakanjiexiboolindex===index){ + this.setState({ + chakanjiexiboolindex:"无", + }) + return + } + this.setState({ + chakanjiexiboolindex:index, + }) + } + setmychakanjiexibool=(str)=>{ + this.setState({ + chakanjiexiboolindex:str, + }) + } setdiscipline_id=(discipline_id)=>{ this.setState({ discipline_id:discipline_id, @@ -338,6 +357,8 @@ class Question extends Component { oj_status:this.state.oj_status }; this.getdata(data); + this.setmychakanjiexibool("无") + } showDrawer = () => { if(this.state.visible===true){ @@ -378,7 +399,7 @@ class Question extends Component { this.setState({ modalsType: true, - titilesm: "设为公开后,所有成员均可使用试题", + titilesm: "在平台审核后,所有成员均可使用试题", titiless: "是否设置为公开?", titbool: true, timuid: id @@ -882,6 +903,8 @@ class Question extends Component { /> {/*头部*/} <Contentpart {...this.state} {...this.props} + chakanjiexiboolindex={this.state.chakanjiexiboolindex} + chakanjiexibool={(e)=>this.chakanjiexibool(e)} getitem_basketss={(id)=>this.getitem_basketss(id)} selectallquestionsonthispage={()=>this.selectallquestionsonthispage()} getitem_baskets={(e)=>this.getitem_baskets(e)} diff --git a/public/react/src/modules/question/component/ChoquesEditor.js b/public/react/src/modules/question/component/ChoquesEditor.js index 99a713d7f..93283c307 100644 --- a/public/react/src/modules/question/component/ChoquesEditor.js +++ b/public/react/src/modules/question/component/ChoquesEditor.js @@ -10,6 +10,7 @@ import axios from 'axios' import update from 'immutability-helper' import './../questioncss/questioncom.css'; import {getUrl, ActionBtn, DMDEditor, ConditionToolTip} from 'educoder'; +import QuillForEditor from '../../../common/quillForEditor'; const { TextArea } = Input; const confirm = Modal.confirm; const $ = window.$ @@ -70,10 +71,13 @@ class ChoquesEditor extends Component{ this.state = { question_choices: _question_choices || ['', '', '', ''], standard_answers: _standard_answers || [false, false, false, false], - question_title: this.props.question_title || '', + question_title: this.props.question_title!==undefined?JSON.parse(this.props.question_title):"", question_type: this.props.question_type || 0, question_score: this.props.question_score || this.props.init_question_score, - question_titles:this.props.question_titles||'', + question_titles: this.props.question_titles!==undefined?JSON.parse(this.props.question_titles):"", + question_titlesysl:this.props.question_titlesysl||'', + question_titleysl:this.props.question_title || '', + item_banksedit:[], } } addOption = () => { @@ -122,38 +126,33 @@ class ChoquesEditor extends Component{ } onSave = () => { var editordata=[]; - const {question_title, question_score, question_type,question_titles, question_choices, standard_answers } = this.state; + const {question_title, question_score,question_titleysl,question_titlesysl, question_type,question_titles, question_choices, standard_answers } = this.state; const { question_id_to_insert_after, question_id } = this.props // TODO check const answerArray = standard_answers.map((item, index) => { return item == true ? index+1 : -1 }).filter(item => item != -1); - if(!question_title) { - this.refs['titleEditor'].showError() + if(!question_titleysl) { this.props.showNotification('请您输入题干'); return editordata; } - if(!answerArray || answerArray.length == 0) { - this.props.showNotification('请先点击选择本选择题的正确选项'); - return editordata; - } - - if(!answerArray || answerArray.length < 2) { - this.props.showNotification('多选题最小正确选项为2个'); - return editordata; - } for(let i = 0; i < question_choices.length; i++) { if (!question_choices[i]) { - this.refs[`optionEditor${i}`].showError() this.props.showNotification(`请先输入 ${tagArray[i]} 选项的内容`); return editordata; } } - - if(!question_titles) { - this.refs['titleEditor2'].showError() - this.props.showNotification('请您输入题目解析'); + if(!answerArray || answerArray.length == 0) { + this.props.showNotification('请先点击选择本选择题的正确选项'); + return editordata; + } + if(!answerArray || answerArray.length < 2) { + this.props.showNotification('多选题最小正确选项为2个'); return editordata; } + // if(!question_titlesysl) { + // this.props.showNotification('请您输入题目解析'); + // return editordata; + // } /** { "question_title":"同学朋友间常用的沟通工具是什么?", @@ -162,7 +161,7 @@ class ChoquesEditor extends Component{ "question_choices":["a答案","b答案","c答案","d答案"], "standard_answers":[1] }*/ - editordata=[question_title,answerArray,question_choices,question_titles]; + editordata=[question_titleysl,answerArray,question_choices,question_titlesysl]; // question_title, // question_type: answerArray.length > 1 ? 1 : 0, // question_score, @@ -186,8 +185,10 @@ class ChoquesEditor extends Component{ try { this.setState({ item_banksedit:this.props.item_banksedit, - question_title:this.props.item_banksedit.name, - question_titles:this.props.item_banksedit.analysis, + question_title: this.props.item_banksedit.name!==undefined?JSON.parse(this.props.item_banksedit.name):"", + question_titleysl:this.props.item_banksedit.name|| '', + question_titles: this.props.item_banksedit.analysis!==undefined?JSON.parse(this.props.item_banksedit.analysis):"", + question_titlesysl:this.props.item_banksedit.analysis||'', mychoicess:this.props.item_banksedit.choices, }) @@ -205,8 +206,10 @@ class ChoquesEditor extends Component{ if(prevProps.item_banksedit !== this.props.item_banksedit) { this.setState({ item_banksedit: this.props.item_banksedit, - question_title: this.props.item_banksedit.name, - question_titles: this.props.item_banksedit.analysis, + question_title: this.props.item_banksedit.name!==undefined?JSON.parse(this.props.item_banksedit.name):"", + question_titleysl:this.props.item_banksedit.name|| '', + question_titles: this.props.item_banksedit.analysis!==undefined?JSON.parse(this.props.item_banksedit.analysis):"", + question_titlesysl:this.props.item_banksedit.analysis||'', mychoicess: this.props.item_banksedit.choices, }) @@ -217,15 +220,34 @@ class ChoquesEditor extends Component{ standard_answers[index] = !standard_answers[index] this.setState({ standard_answers }) } - onOptionContentChange = (value, index) => { + onOptionContentChange = (value,quill,index) => { if (index >= this.state.question_choices.length) { // TODO 新建,然后删除CD选项,再输入题干,会调用到这里,且index是3 return; } + var texts; + const _text = quill.getText(); + const reg = /^[\s\S]*.*[^\s][\s\S]*$/; + if (!reg.test(_text)) { + // 处理编辑器内容为空 + texts=""; + } else { + if(_text.length>=301){ + var result = _text.substring(0,300); + texts={"ops":[{"insert":result}]}; + texts=JSON.stringify(texts); + }else { + // 提交到后台的内容需要处理一下; + value = JSON.stringify(value); + texts=value; + } + } let question_choices = this.state.question_choices.slice(0); - question_choices[index] = value; - this.setState({ question_choices }) + question_choices[index] = texts; + console.log(question_choices); + this.setState({ question_choices }); } + on_question_score_change = (e) => { this.setState({ question_score: e }) } @@ -241,6 +263,57 @@ class ChoquesEditor extends Component{ toShowMode = () => { } + + onContentChange=(value,quill)=>{ + const _text = quill.getText(); + const reg = /^[\s\S]*.*[^\s][\s\S]*$/; + if (!reg.test(_text)) { + // 处理编辑器内容为空 + this.setState({ + question_titleysl:"" + }) + } else { + // 提交到后台的内容需要处理一下; + var texts=""; + if(_text.length>=1001){ + var result = _text.substring(0,1000); + texts={"ops":[{"insert":result}]}; + texts=JSON.stringify(texts); + }else { + value = JSON.stringify(value) + texts=value; + } + this.setState({ + question_titleysl:texts + }) + } + } + onContentChanges=(value,quill)=>{ + const _text = quill.getText(); + const reg = /^[\s\S]*.*[^\s][\s\S]*$/; + if (!reg.test(_text)) { + // 处理编辑器内容为空 + this.setState({ + question_titlesysl:"" + }) + } else { + // 提交到后台的内容需要处理一下; + var texts=""; + if(_text.length>=1001){ + var result = _text.substring(0,1000); + texts={"ops":[{"insert":result}]}; + texts=JSON.stringify(texts); + }else { + value = JSON.stringify(value) + texts=value; + } + this.setState({ + question_titlesysl:texts + }) + } + } + + render() { let { question_title, question_score, question_type, question_choices, standard_answers,question_titles} = this.state; let { question_id, index, exerciseIsPublish, @@ -271,7 +344,7 @@ class ChoquesEditor extends Component{ flex:1 } .optionRow { - margin:0px!important; + /* margin:0px!important; */ /* margin-bottom: 20px!important; */ } .signleEditor .content_editorMd_show{ @@ -291,21 +364,26 @@ class ChoquesEditor extends Component{ <span className="xingtigan fl">题干:</span> </p> - <TPMMDEditor mdID={qNumber} placeholder="请您输入题干" height={155} className=" mt10" - initValue={question_title} onChange={(val) => this.setState({ question_title: val})} - ref="titleEditor" + <QuillForEditor + autoFocus={true} + imgAttrs={{width: '146px', height: '136px'}} + style={{ height: '155px'}} + placeholder="请您输入题干" + options={['code-block', 'image', 'formula']} + value={question_title} + onContentChange={this.onContentChange} - ></TPMMDEditor> + /> <div className="mb10 sortinxdirection"> {/* {!question_id ? '新建' : '编辑'} */} <span className="xingcolor font-16 fl mr4">*</span> - <span className="xingtigans fl"><span className="xingtigan">答案选项:</span>点击答案可设置正确答案</span> + <span className="xingtigans fl"><span className="xingtigan">答案选项:</span>点击选项可设置正确答案</span> </div> {question_choices.map( (item, index) => { const bg = standard_answers[index] ? 'check-option-bg' : '' - return <div className="df optionRow " > + return <div className={index>0?"df optionRow mt15": "df optionRow"} > {/* 点击设置答案 */} {/* TODO 加了tooltip后,会丢失掉span的class */} {/* <Tooltip title={standard_answers[index] ? '点击取消标准答案设置' : '点击设置为标准答案'}> */} @@ -317,13 +395,30 @@ class ChoquesEditor extends Component{ </span> {/* </Tooltip> */} <div style={{ flex: '0 0 1038px'}}> - <DMDEditor - ref={`optionEditor${index}`} - toMDMode={this.toMDMode} toShowMode={this.toShowMode} - height={166} className={'optionMdEditor'} noStorage={true} - mdID={qNumber + index} placeholder="" onChange={(value) => this.onOptionContentChange(value, index)} - initValue={item} - ></DMDEditor> + + { + item===undefined||item===null||item===""? + <QuillForEditor + autoFocus={false} + imgAttrs={{width: '146px', height: '136px'}} + style={{ height: '166px'}} + placeholder="请您输入题干" + options={['code-block', 'image', 'formula']} + value={item} + onContentChange={(value,quill) => this.onOptionContentChange(value,quill,index)} + /> + : + <QuillForEditor + autoFocus={false} + imgAttrs={{width: '146px', height: '136px'}} + style={{ height: '166px'}} + placeholder="请您输入题干" + options={['code-block', 'image', 'formula']} + value={JSON.parse(item)} + onContentChange={(value,quill) => this.onOptionContentChange(value,quill,index)} + /> + + } </div> {exerciseIsPublish || index<=2? <i className=" font-18 ml15 mr20"></i> @@ -346,32 +441,21 @@ class ChoquesEditor extends Component{ - <p className="mb10 clearfix"> + <p className="mb10 mt10 clearfix"> {/* {!question_id ? '新建' : '编辑'} */} - <span className="xingcolor font-16 fl mr4">*</span> + <span className="xingcolor font-16 fl mr4"></span> <span className="xingtigan fl">题目解析:</span> </p> - <style>{` - .optionMdEditor { - flex:1 - } - .optionRow { - margin:0px!important; - /* margin-bottom: 20px!important; */ - } - .signleEditor .content_editorMd_show{ - display: flex; - margin-top:0px!important; - border-radius:2px; - max-width: 1056px; - word-break:break-all; - } - `}</style> - <TPMMDEditor mdID={qNumber+question_choices.length} placeholder="请您输入题目解析" height={155} className=" mt10" - initValue={question_titles} onChange={(val) => this.setState({ question_titles: val})} - ref="titleEditor2" - - ></TPMMDEditor> + <div className="mt10"></div> + <QuillForEditor + autoFocus={false} + imgAttrs={{width: '146px', height: '136px'}} + style={{height: '166px' }} + placeholder="请您输入题目解析" + options={['code-block', 'image', 'formula']} + value={question_titles} + onContentChange={this.onContentChanges} + /> </div> diff --git a/public/react/src/modules/question/component/Contentpart.js b/public/react/src/modules/question/component/Contentpart.js index 2c3ea6f36..da85ce0f7 100644 --- a/public/react/src/modules/question/component/Contentpart.js +++ b/public/react/src/modules/question/component/Contentpart.js @@ -25,7 +25,6 @@ class Contentpart extends Component { this.state = { page:1, chakanjiexibool:false, - chakanjiexiboolindex:"无", } } //初始化 @@ -35,18 +34,51 @@ class Contentpart extends Component { } chakanjiexibool=(index)=>{ - debugger - if(this.state.chakanjiexiboolindex===index){ - this.setState({ - chakanjiexiboolindex:"无", - }) - return - } - this.setState({ - chakanjiexiboolindex:index, - }) + this.props.chakanjiexibool(index); } + xinzenw=(e)=>{ + var urls="?"; + if(this.props.discipline_id){ + if(urls==="?"){ + urls=urls+`discipline_id=${this.props.discipline_id}` + }else { + urls=urls+`&discipline_id=${this.props.discipline_id}` + } + } + if(this.props.sub_discipline_id){ + if(urls==="?"){ + urls=urls+`sub_discipline_id=${this.props.sub_discipline_id}` + }else { + urls=urls+`&sub_discipline_id=${this.props.sub_discipline_id}` + } + } + if(this.props.tag_discipline_id){ + if(urls==="?"){ + urls=urls+`sub_discipline_id=${this.props.tag_discipline_id}` + }else { + urls=urls+`&sub_discipline_id=${this.props.tag_discipline_id}` + } + } + if(this.props.difficulty){ + if(urls==="?"){ + urls=urls+`difficulty=${this.props.difficulty}&` + }else { + urls=urls+`&difficulty=${this.props.difficulty}` + } + } + if(this.props.item_type){ + if(urls==="?"){ + urls=urls+`item_type=${this.props.item_type}` + }else { + urls=urls+`&item_type=${this.props.item_type}` + } + + } + + + this.props.history.push("/question/newitem"+urls); + } render() { let {page}=this.state; let {defaultActiveKey,item_type,booljupyterurls}=this.props; @@ -149,7 +181,7 @@ class Contentpart extends Component { <div className="xaxisreverseorder"> { defaultActiveKey===0||defaultActiveKey==="0"? - <a href={'/question/newitem'}> + <a onClick={(e)=>this.xinzenw(e)}> <div className="newbutoon"> <p className="newbutoontes" >新增</p> </div> @@ -230,6 +262,7 @@ class Contentpart extends Component { : this.props.Contentdata.items.map((object, index) => { return ( <Listjihe {...this.state} {...this.props} + chakanjiexiboolindex={this.props.chakanjiexiboolindex} chakanjiexibool={(keindex)=>this.chakanjiexibool(keindex)} listjihe={index+1} keindex={index} diff --git a/public/react/src/modules/question/component/Headplugselections.js b/public/react/src/modules/question/component/Headplugselections.js index 3cff40cc2..206b556e9 100644 --- a/public/react/src/modules/question/component/Headplugselections.js +++ b/public/react/src/modules/question/component/Headplugselections.js @@ -153,7 +153,7 @@ class Headplugselections extends Component { { item&&item.map((list,k)=>{ return( - <Menu.Item > + <Menu.Item key={k}> <div className="mt5 subshaicontent-part" key={k}> <a style={{ height: '20px' }} className={ "mb15 shixun_repertoire color-dark intermediatecenterysls textcen "} name={list.id} id={list.id} onClick={()=>this.getshixunchildValue(list.id,id)}>{list.name}</a> </div> diff --git a/public/react/src/modules/question/component/Itembankstop.js b/public/react/src/modules/question/component/Itembankstop.js index 5192eca4e..046565356 100644 --- a/public/react/src/modules/question/component/Itembankstop.js +++ b/public/react/src/modules/question/component/Itembankstop.js @@ -19,6 +19,8 @@ import './../questioncss/questioncom.css'; import Newknledpots from './Newknledpots' const InputGroup = Input.Group; const {Option} = Select; +const queryString = require('query-string'); + const options = [ { value: '方向', @@ -41,7 +43,7 @@ const options = [ ], }, ]; - +//Comthetestpaperst试卷的 class Itembankstop extends Component { constructor(props) { super(props); @@ -53,12 +55,23 @@ class Itembankstop extends Component { knowledgepoints: [], knowledgepoints2:[], options: [], - NewknTypedel:false + NewknTypedel:false, + boolred:false, + } } + setboolred=(bool)=>{ + this.setState({ + boolred:bool + }) + + } + //初始化 componentDidMount() { + + try { this.props.getcontentMdRef(this); } catch (e) { @@ -68,7 +81,53 @@ class Itembankstop extends Component { options: this.props.disciplmy, }) + console.log("数据"); + console.log(this.props); + const parsed = queryString.parse(this.props.location.search); + console.log(parsed); + try { + if(JSON.stringify(parsed)==={}||JSON.stringify(parsed)==="{}"){ + + }else { + if(parsed.discipline_id){ + if(parsed.sub_discipline_id){ + this.setState({ + rbkc:[parseInt(parsed.discipline_id),parseInt(parsed.sub_discipline_id)] + }) + this.props.form.setFieldsValue({ + rbkc: [parseInt(parsed.discipline_id),parseInt(parsed.sub_discipline_id)], + }); + this.getdatasmyss(parseInt(parsed.discipline_id),parseInt(parsed.sub_discipline_id)); + + } + + } + + if(parsed.item_type){ + this.setState({ + rbtx:parsed.item_type, + }) + this.props.form.setFieldsValue({ + rbtx:parsed.item_type, + }); + this.props.setitem_type(parsed.item_type); + } + + if(parsed.difficulty){ + this.setState({ + rbnd:parsed.difficulty, + }) + this.props.form.setFieldsValue({ + rbnd:parsed.difficulty, + }); + } + + } + + }catch (e) { + + } } componentDidUpdate(prevProps) { @@ -79,6 +138,7 @@ class Itembankstop extends Component { }) } if(prevProps.disciplinesdata!== this.props.disciplinesdata){ + console.log("新增开始加载了") try { if(this.props.item_banksedit.discipline &&this.props.item_banksedit.sub_discipline){ const didata = this.props.disciplinesdata; @@ -110,6 +170,27 @@ class Itembankstop extends Component { knowledgepoints2: _result, }) } + + + try { + const parsed = queryString.parse(this.props.location.search); + if(JSON.stringify(parsed)==={}||JSON.stringify(parsed)==="{}"){ + + }else { + if(parsed.discipline_id){ + if(parsed.sub_discipline_id){ + this.setState({ + rbkc:[parseInt(parsed.discipline_id),parseInt(parsed.sub_discipline_id)] + }) + this.props.form.setFieldsValue({ + rbkc: [parseInt(parsed.discipline_id),parseInt(parsed.sub_discipline_id)], + }); + this.getdatasmyss(parseInt(parsed.discipline_id),parseInt(parsed.sub_discipline_id)); + } + } + } + }catch (e) { + } }catch (e) { } } @@ -125,6 +206,7 @@ class Itembankstop extends Component { this.handletag_disciplinesChange(this.props.item_banksedit.tag_disciplines); } try { + //初始化课程 this.handdisciplinesChange(this.props.item_banksedit.discipline,this.props.item_banksedit.sub_discipline); }catch (e) { @@ -175,7 +257,42 @@ class Itembankstop extends Component { } } } + getdatasmyss=(id,ids)=>{ + if(this.props.disciplinesdata){ + try { + if(id &&ids){ + var didata = this.props.disciplinesdata; + var knowledgepointsdata = []; + for (var i = 0; i < didata.length; i++) { + //方向 + if (id === didata[i].id) { + const fxdidata = didata[i].sub_disciplines; + for (var j = 0; j < fxdidata.length; j++) { + //课程 + if (ids === fxdidata[j].id) { + const zsddata = fxdidata[j].tag_disciplines; + for (var k = 0; k < zsddata.length; k++) { + //知识点 + knowledgepointsdata.push(zsddata[k]); + } + } + } + } + } + + + + this.setState({ + knowledgepoints:knowledgepointsdata, + knowledgepoints2: knowledgepointsdata, + }) + }else{ + } + }catch (e) { + } + } + } handdisciplinesChange =(name,title)=>{ this.setState({ rbkc:[name.id,title.id] @@ -413,6 +530,10 @@ class Itembankstop extends Component { } NewknTypedeldel=(bool)=>{ + if(this.state.rbkc===undefined || this.state.rbkc===null || this.state.rbkc===""){ + this.props.showNotification(`请选择课程方向`); + return; + } this.setState({ NewknTypedel:bool }) @@ -420,21 +541,34 @@ class Itembankstop extends Component { } NewknTypedeltyoedel=(value)=>{ + var knowledgepointmys= this.state.knowledgepoints; + var konwbool=null; + for(let myda of knowledgepointmys) { + if(myda.name===value){ + konwbool="yes" + break; + } + } + if(konwbool!=null){ + this.props.showNotification(`重复的知识点`); + this.setboolred(true); + return + } + + if(value===null||value===""){ this.props.showNotification(`请输入知识点`); + this.setboolred(true); return } if(value.length===0){ this.props.showNotification(`请输入知识点`); + this.setboolred(true); return } - if(this.state.rbkc===undefined || this.state.rbkc===null || this.state.rbkc===""){ - this.props.showNotification(`请选择课程方向`); - return; - } var data={ name:value, sub_discipline_id:this.state.rbkc[1] @@ -448,19 +582,37 @@ class Itembankstop extends Component { id: result.data.tag_discipline_id, name:value, } - this.state.knowledgepoints.push(leydata); - const _result =[]; - this.state.knowledgepoints.filter(item => { - if (this.state.Knowpoints.findIndex(t => t.id === item.id) === -1) { - _result.push(item); - } - }); - this.setState({ - Knowpoints: this.state.Knowpoints, - knowledgepoints: this.state.knowledgepoints, - knowledgepoints2: _result, - }) + if(this.state.Knowpoints.length>=5){ + this.state.knowledgepoints.push(leydata); + const _result =[]; + this.state.knowledgepoints.filter(item => { + if (this.state.Knowpoints.findIndex(t => t.id === item.id) === -1) { + _result.push(item); + } + }); + + this.setState({ + Knowpoints: this.state.Knowpoints, + knowledgepoints: this.state.knowledgepoints, + knowledgepoints2: _result, + }) + }else{ + this.state.Knowpoints.push(leydata); + this.state.knowledgepoints.push(leydata); + const _result =[]; + this.state.knowledgepoints.filter(item => { + if (this.state.Knowpoints.findIndex(t => t.id === item.id) === -1) { + _result.push(item); + } + }); + this.setState({ + Knowpoints: this.state.Knowpoints, + knowledgepoints: this.state.knowledgepoints, + knowledgepoints2: _result, + }) + } + } }).catch((error) => { //console.log(error); @@ -476,6 +628,11 @@ class Itembankstop extends Component { let {page, options,NewknTypedel,knowledgepoints,knowledgepoints2,Knowpoints} = this.state; const {getFieldDecorator} = this.props.form; + console.log("this.state.rbkc"); + console.log(this.state.rbkc); + console.log(options); + + return ( <div className=" clearfix educontent Contentquestionbankstyle w100s w1200fpx mt19"> @@ -512,6 +669,8 @@ class Itembankstop extends Component { { NewknTypedel? <Newknledpots {...this.state} {...this.props} + boolred={this.state.boolred} + setboolred={(bool)=>this.setboolred(bool)} NewknTypedeldel={(bool)=>this.NewknTypedeldel(bool)} NewknTypedeltyoedel={(value)=>this.NewknTypedeltyoedel(value)} ></Newknledpots> @@ -555,32 +714,34 @@ class Itembankstop extends Component { <img className=" ml22 zjzsdian xiaoshou" src={getImageUrl("images/educoder/zjzsd.png")} onClick={()=>this.NewknTypedeldel(true)}/> - <div className="sortinxdirection" style={{ - height: "33px", - lineHeight: "28px", - }}> + </div> + )} + </Form.Item> - {this.state.Knowpoints === undefined ? "" : this.state.Knowpoints.map((object, index) => { - return ( - <div key={index} className="mytags" style={{ - position: "relative", - }}> - <p className="w100s stestcen lh32">{object.name}</p> + <div className="sortinxdirection huanhan w100s mt15" style={{ + minHeight: "33px", + lineHeight: "28px", - <img className=" ml7 zjzsdian xiaoshou icondowncolorssy" onClick={() => this.deletesobject(object, index)} src={getImageUrl("images/educoder/bzucha.png")}/> + }}> - </div> - ) - })} + {this.state.Knowpoints === undefined ? "" : this.state.Knowpoints.map((object, index) => { + return ( + <div key={index} className={index===0?"mytags mb20":"mytags"} style={{ + position: "relative", + }}> + <p className="w100s stestcen lh32">{object.name}</p> + <img className=" ml7 zjzsdian xiaoshou icondowncolorssy" onClick={() => this.deletesobject(object, index)} src={getImageUrl("images/educoder/bzucha.png")}/> + </div> + ) + })} - </div> - </div> - )} - </Form.Item> + + + </div> <Form.Item label="题型" diff --git a/public/react/src/modules/question/component/JudquestionEditor.js b/public/react/src/modules/question/component/JudquestionEditor.js index ff491125c..5888efeb0 100644 --- a/public/react/src/modules/question/component/JudquestionEditor.js +++ b/public/react/src/modules/question/component/JudquestionEditor.js @@ -10,6 +10,8 @@ import axios from 'axios' import update from 'immutability-helper' import './../questioncss/questioncom.css'; import {getUrl, ActionBtn, DMDEditor, ConditionToolTip} from 'educoder'; +import QuillForEditor from '../../../common/quillForEditor'; + const { TextArea } = Input; const confirm = Modal.confirm; const $ = window.$ @@ -46,10 +48,12 @@ class JudquestionEditor extends Component{ this.state = { question_choices: _question_choices || ['', '', '', ''], standard_answers: _standard_answers || [false, false, false, false], - question_title: this.props.question_title || '', + question_title: this.props.question_title!==undefined?JSON.parse(this.props.question_title):"", question_type: this.props.question_type || 0, question_score: this.props.question_score || this.props.init_question_score, - question_titles:this.props.question_titles||'', + question_titles: this.props.question_titles!==undefined?JSON.parse(this.props.question_titles):"", + question_titlesysl:this.props.question_titlesysl||'', + question_titleysl:this.props.question_title || '', zqda:null, item_banksedit:[], mychoicess:[], @@ -99,12 +103,11 @@ class JudquestionEditor extends Component{ } onSave = () => { var editordata=[]; - const {question_title, question_score, question_type,question_titles, zqda,question_choices, standard_answers } = this.state; + const {question_title, question_score,question_titleysl,question_titlesysl, question_type,question_titles, zqda,question_choices, standard_answers } = this.state; const { question_id_to_insert_after, question_id } = this.props // TODO check const answerArray = standard_answers.map((item, index) => { return item == true ? index+1 : -1 }).filter(item => item != -1); - if(!question_title) { - this.refs['titleEditor'].showError() + if(!question_titleysl) { this.props.showNotification('请您输入题干'); return editordata; } @@ -116,12 +119,11 @@ class JudquestionEditor extends Component{ - - if(!question_titles) { - this.refs['titleEditor2'].showError() - this.props.showNotification('请您输入题目解析'); - return editordata; - } + // + // if(!question_titlesysl) { + // this.props.showNotification('请您输入题目解析'); + // return editordata; + // } /** { "question_title":"同学朋友间常用的沟通工具是什么?", @@ -132,7 +134,7 @@ class JudquestionEditor extends Component{ }*/ - editordata=[question_title,zqda,question_titles]; + editordata=[question_titleysl,zqda,question_titlesysl]; // question_title, // question_type: answerArray.length > 1 ? 1 : 0, // question_score, @@ -156,10 +158,11 @@ class JudquestionEditor extends Component{ try { this.setState({ item_banksedit:this.props.item_banksedit, - question_title:this.props.item_banksedit.name, - question_titles:this.props.item_banksedit.analysis, + question_title: this.props.item_banksedit.name!==undefined?JSON.parse(this.props.item_banksedit.name):"", + question_titles: this.props.item_banksedit.analysis!==undefined?JSON.parse(this.props.item_banksedit.analysis):"", mychoicess:this.props.item_banksedit.choices, - + question_titleysl:this.props.item_banksedit.name|| '', + question_titlesysl:this.props.item_banksedit.analysis||'', }) if(this.props.item_banksedit){ if(this.props.item_banksedit.choices){ @@ -194,9 +197,11 @@ class JudquestionEditor extends Component{ if(prevProps.item_banksedit !== this.props.item_banksedit){ this.setState({ item_banksedit:this.props.item_banksedit, - question_title:this.props.item_banksedit.name, - question_titles:this.props.item_banksedit.analysis, + question_title: this.props.item_banksedit.name!==undefined?JSON.parse(this.props.item_banksedit.name):"", + question_titles: this.props.item_banksedit.analysis!==undefined?JSON.parse(this.props.item_banksedit.analysis):"", mychoicess:this.props.item_banksedit.choices, + question_titleysl:this.props.item_banksedit.name|| '', + question_titlesysl:this.props.item_banksedit.analysis||'', }) if(this.props.item_banksedit){ @@ -275,6 +280,55 @@ class JudquestionEditor extends Component{ }) } + + onContentChange=(value,quill)=>{ + const _text = quill.getText(); + const reg = /^[\s\S]*.*[^\s][\s\S]*$/; + if (!reg.test(_text)) { + // 处理编辑器内容为空 + this.setState({ + question_titleysl:"" + }) + } else { + // 提交到后台的内容需要处理一下; + var texts=""; + if(_text.length>=1001){ + var result = _text.substring(0,1000); + texts={"ops":[{"insert":result}]}; + texts=JSON.stringify(texts); + }else { + value = JSON.stringify(value) + texts=value; + } + this.setState({ + question_titleysl:texts + }) + } + } + onContentChanges=(value,quill)=>{ + const _text = quill.getText(); + const reg = /^[\s\S]*.*[^\s][\s\S]*$/; + if (!reg.test(_text)) { + // 处理编辑器内容为空 + this.setState({ + question_titlesysl:"" + }) + } else { + // 提交到后台的内容需要处理一下; + var texts=""; + if(_text.length>=1001){ + var result = _text.substring(0,1000); + texts={"ops":[{"insert":result}]}; + texts=JSON.stringify(texts); + }else { + value =JSON.stringify(value); + texts=value; + } + this.setState({ + question_titlesysl:texts + }) + } + } render() { let { question_title, question_score, question_type, question_choices, standard_answers,question_titles} = this.state; let { question_id, index, exerciseIsPublish, @@ -325,16 +379,21 @@ class JudquestionEditor extends Component{ <span className="xingtigan fl">题干:</span> </p> - <TPMMDEditor mdID={qNumber} placeholder="请您输入题干" height={155} className=" mt10" - initValue={question_title} onChange={(val) => this.setState({ question_title: val})} - ref="titleEditor" + <QuillForEditor + autoFocus={true} + imgAttrs={{width: '146px', height: '136px'}} + style={{ height: '155px'}} + placeholder="请您输入题干" + options={['code-block', 'image', 'formula']} + value={question_title} + onContentChange={this.onContentChange} - ></TPMMDEditor> + /> <div className="mb10 sortinxdirection"> {/* {!question_id ? '新建' : '编辑'} */} <span className="xingcolor font-16 fl mr4">*</span> - <span className="xingtigans fl"><span className="xingtigan">答案选项:</span>点击答案可设置正确答案</span> + <span className="xingtigans fl"><span className="xingtigan">答案选项:</span>点击选项可设置正确答案</span> </div> <style> @@ -363,36 +422,25 @@ class JudquestionEditor extends Component{ <div> - + <div className="mt10"> <p className="mb10 clearfix"> {/* {!question_id ? '新建' : '编辑'} */} - <span className="xingcolor font-16 fl mr4">*</span> + <span className="xingcolor font-16 fl mr4"></span> <span className="xingtigan fl">题目解析:</span> </p> - <style>{` - .optionMdEditor { - flex:1 - } - .optionRow { - margin:0px!important; - /* margin-bottom: 20px!important; */ - } - .signleEditor .content_editorMd_show{ - display: flex; - margin-top:0px!important; - border-radius:2px; - max-width: 1056px; - word-break:break-all; - } - `}</style> - <TPMMDEditor mdID={qNumber+question_choices.length} placeholder="请您输入题目解析" height={155} className=" mt10" - initValue={question_titles} onChange={(val) => this.setState({ question_titles: val})} - ref="titleEditor2" - ></TPMMDEditor> + <QuillForEditor + autoFocus={false} + imgAttrs={{width: '146px', height: '136px'}} + style={{height: '166px' }} + placeholder="请您输入题目解析" + options={['code-block', 'image', 'formula']} + value={question_titles} + onContentChange={this.onContentChanges} + /> </div> - + </div> </div> ) diff --git a/public/react/src/modules/question/component/Listjihe.js b/public/react/src/modules/question/component/Listjihe.js index f796a3f45..54cc82784 100644 --- a/public/react/src/modules/question/component/Listjihe.js +++ b/public/react/src/modules/question/component/Listjihe.js @@ -1,15 +1,18 @@ import React, {Component} from "react"; import {Link, NavLink} from 'react-router-dom'; -import {WordsBtn, ActionBtn,SnackbarHOC,getImageUrl,markdownToHTML} from 'educoder'; +import {WordsBtn, ActionBtn, SnackbarHOC, getImageUrl, markdownToHTML} from 'educoder'; import axios from 'axios'; import { notification, Spin, Table, Pagination, - Radio + Radio, + Tooltip } from "antd"; import './../questioncss/questioncom.css'; +import QuillForEditor from "../../../common/quillForEditor"; + const tagArray = [ 'A.', 'B.', 'C.', 'D.', 'E.', 'F.', 'G.', 'H.', 'I.', 'J.', 'K.', 'L.', 'M.', 'N.', 'O.', 'P.', 'Q.', 'R.', @@ -20,97 +23,144 @@ const tagArrays = [ 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' ] + class Listjihe extends Component { constructor(props) { super(props); this.state = { - page:1, - name:"单选题", - nd:"简单", + page: 1, + name: "单选题", + nd: "简单", } } + //初始化 - componentDidMount(){ + componentDidMount() { } //选用 - Selectingpracticaltraining=(id)=>{ - let data={} - if(this.props.exam_id===undefined){ - data={ - item_ids:[id] + Selectingpracticaltraining = (id) => { + let data = {} + if (this.props.exam_id === undefined) { + data = { + item_ids: [id] } - }else{ - data={ - item_ids:[id], - exam_id:this.props.exam_id===undefined?"":parseInt(this.props.exam_id), + } else { + data = { + item_ids: [id], + exam_id: this.props.exam_id === undefined ? "" : parseInt(this.props.exam_id), } - } + } this.props.getitem_baskets(data); } //撤销 - Selectingpracticaltrainings=(id)=>{ + Selectingpracticaltrainings = (id) => { this.props.getitem_basketss(id); } - - - render() { - let {page,name,nd}=this.state; - let {defaultActiveKey,items,listjihe,chakanjiexiboolindex,keindex}=this.props; + let {page, name, nd} = this.state; + let {defaultActiveKey, items, listjihe, chakanjiexiboolindex, keindex} = this.props; // 编程答案 - var rightkey=null - - if(items){ - if(items.item_type){ - if(items.item_type==="PROGRAM"){ - - }else{ - if(items.item_type==="JUDGMENT") { - - - if(items.choices){ - if(items.choices.length>0){ - var arr= items.choices; - for(let data of arr) { - if(data.is_answer===true){ - rightkey=data.choice_text; - break; - } - } - } - } - }else { + var rightkey = null + var MULTIPLEkey = null; + if (items) { + if (items.item_type) { + if (items.item_type === "PROGRAM") { + + } else { + if (items.item_type === "JUDGMENT") { + //多选题 if (items.choices) { if (items.choices.length > 0) { var arr = items.choices; - for (var i = 0; i < arr.length; i++) { - if (arr[i].is_answer === true) { - rightkey = i; + for (let data of arr) { + if (data.is_answer === true) { + rightkey = data.choice_text; break; } } } } + } else { + // 单选题和判断题 + if(items.item_type === "MULTIPLE"){ + if (items.choices) { + if (items.choices.length > 0) { + var arr = items.choices; + for (var i = 0; i < arr.length; i++) { + if (arr[i].is_answer === true) { + if(MULTIPLEkey===null){ + MULTIPLEkey = tagArrays[i]; + + }else{ + MULTIPLEkey =MULTIPLEkey+ tagArrays[i]; + } + + } + } + } + } - + }else{ + if (items.choices) { + if (items.choices.length > 0) { + var arr = items.choices; + for (var i = 0; i < arr.length; i++) { + if (arr[i].is_answer === true) { + rightkey = i; + break; + } + } + } + } + } } } } } - + var itemssname=""; + try { + itemssname= JSON.parse(items.name); + }catch (e) { + } + if(itemssname===undefined){ + itemssname=items.name + } return ( - <div key={keindex} className={chakanjiexiboolindex===keindex?"w100s borderwds283 pd20 mb20 listjihecolors":"w100s borderwds pd20 mb20 listjihecolors"}> + <div key={keindex} + className={chakanjiexiboolindex === keindex ? "w100s borderwds283 pd20 mb20 listjihecolors" : "w100s borderwds pd20 mb20 listjihecolors"}> {/*顶部*/} + <style> + { + ` .markdown-body .ql-editor{ + padding-left: 0px; + } + + .markdown-body .ql-editor p{ + line-height: 0px; + + } + + .programquill .ql-editor{ + padding-left: 0px; + } + + .programquill .ql-editor p{ + line-height: 0px; + + } + ` + } + </style> <div className="w100s sortinxdirection"> <div className="listjihetixingstitsy"> { @@ -120,39 +170,89 @@ class Listjihe extends Component { <div className="listjihetixingstitsy"> . </div> - <div className="ml10 w100s listjihetixingstit markdown-body" style={{wordBreak: "break-word"}} dangerouslySetInnerHTML={{__html: markdownToHTML(items&&items.name).replace(/▁/g, "▁▁▁")}}> - </div> + { + items.item_type==="PROGRAM"? + <a href={`/problems/${items.program_attr.identifier}/edit`}> + <div className="ml10 w100s listjihetixingstit markdown-body" style={{wordBreak: "break-word"}} dangerouslySetInnerHTML={{__html: markdownToHTML(items&&items.name).replace(/▁/g, "▁▁▁")}}></div> + </a> + : + <div className="ml10 w100s listjihetixingstit markdown-body" style={{wordBreak: "break-word"}}> + { items===undefined||items===null||items===""?"": + items.name === undefined || items.name === null || items.name === "" ? + "" + : + items.name.length>0? + <QuillForEditor + readOnly={true} + value={itemssname} + /> + :"" + } + </div> + } + </div> {/*内容*/} <div className="w100s sortinxdirection "> - {items.item_type==="JUDGMENT"? - <p className="w100s listjihetixingstits sortinxdirection "> + {items.item_type === "JUDGMENT" ? + <p className="w100s listjihetixingstitsp sortinxdirection "> { - items === undefined ||items === null? "" : items.choices.map((object, index) => { + items === undefined || items === null ? "" : items.choices.map((object, index) => { return ( - <p className={index===1? "sortinxdirection ml10":"sortinxdirection " } > - <Radio disabled={false}> + <p className={index === 1 ? "sortinxdirection ml10" : "sortinxdirection "}> + <Radio disabled={false} disabled={true} > {object.choice_text} </Radio> </p> ) }) } - </p>: - items.item_type==="PROGRAM"? + </p> : + items.item_type === "PROGRAM" ? <p className="w100s listjihetixingstitssy sortinxdirection "> - <p className={"sortinxdirection mt15"} > - <p style={{wordBreak: "break-word"}} dangerouslySetInnerHTML={{__html: markdownToHTML(items.program_attr.description).replace(/▁/g, "▁▁▁")}}></p> + <p className={"sortinxdirection mt15"}> + { + items&&items.program_attr&&items.program_attr.description? + <p className="programquill" style={{wordBreak: "break-word"}} + > + <QuillForEditor + readOnly={true} + value={JSON.parse(items.program_attr.description)} + /> + + </p> + :"" + } + + + </p> </p> : - <p className="w100s listjihetixingstits verticallayout "> + <p className="w100s listjihetixingstitsp verticallayout "> { - items === undefined ||items === null? "" : items.choices.map((object, index) => { + items === undefined || items === null ? "" : items.choices.map((object, index) => { return ( - <p className={index===0?"sortinxdirection":"sortinxdirection mt15"} > - {tagArray[index]} - <p style={{wordBreak: "break-word"}} dangerouslySetInnerHTML={{__html: markdownToHTML(object.choice_text).replace(/▁/g, "▁▁▁")}}></p> + <p className={index === 0 ? "sortinxdirection " : "sortinxdirection mt15 "}> + <p className="lh26">{tagArray[index]}</p> + <p className="programquill" style={{wordBreak: "break-word"}}> + {object ? + object.choice_text === undefined || object.choice_text=== null || object.choice_text === "" ? + + "" + : + object.choice_text.length>0? + <QuillForEditor + readOnly={true} + value={JSON.parse(object.choice_text)} + /> + :"" + + + : + "" + } + </p> </p> ) }) @@ -163,91 +263,98 @@ class Listjihe extends Component { <div className="w100s sortinxdirection mt10"> - <p className="listjihetixing">难度:<span >{items.difficulty===1?"简单":items.difficulty===2?"适中":items.difficulty===3?"困难":""}</span></p> - <p className="ml30 listjihetixing">题型:<span >{items.item_type==="SINGLE"?"单选题":items.item_type==="MULTIPLE"?"多选题":items.item_type==="JUDGMENT"?"判断题":items.item_type==="PROGRAM"?"编程题":""}</span></p> + <p + className="listjihetixing">难度:<span>{items.difficulty === 1 ? "简单" : items.difficulty === 2 ? "适中" : items.difficulty === 3 ? "困难" : ""}</span> + </p> + <p + className="ml30 listjihetixing">题型:<span>{items.item_type === "SINGLE" ? "单选题" : items.item_type === "MULTIPLE" ? "多选题" : items.item_type === "JUDGMENT" ? "判断题" : items.item_type === "PROGRAM" ? "编程题" : ""}</span> + </p> </div> {/*更新时间*/} <div className="w100s sortinxdirection"> <div className="w50s listjihetixingstit sortinxdirection"> <p className="updatetimes lh30">更新时间:{items.update_time}</p> { - this.props.defaultActiveKey==="0"||this.props.defaultActiveKey===0? + this.props.defaultActiveKey === "0" || this.props.defaultActiveKey === 0 ? "" : <p className="updatetimes lh30 ml45">创建者:{items.author.name}</p> } { - items.item_type==="PROGRAM"? + items.item_type === "PROGRAM" ? <p className="updatetimes lh30 ml45">编程语言:{items.program_attr.language}</p> - :"" + : "" } { - items.item_type==="PROGRAM"? - items.program_attr.status===0? + items.item_type === "PROGRAM" ? + items.program_attr.status === 0 ? <p className="updatetimes lh30 ml45 nofabu mt5">未发布</p> - :"" - :"" + : "" + : "" } </div> <div className="w50s xaxisreverseorder"> { - items.choosed===true? - <p className="selectionss xiaoshou" onClick={()=>this.Selectingpracticaltrainings(items.id)}> + items.choosed === true ? + <p className="selectionss xiaoshou" onClick={() => this.Selectingpracticaltrainings(items.id)}> <i className="iconfont icon-jianhao font-12 lg ml7 lh30 icontianjiadaohangcolor mr5"></i> <span className="mr15 lh30">撤销</span></p> : - items.item_type==="PROGRAM"? - items.program_attr.status===0? - <p className="selectionys jinzhixiaoshou" > - <i className="iconfont icon-tianjiadaohang font-12 lg ml7 lh30 icontianjiadaohangcolor mr5"></i> - <span className="mr15 lh30">选用</span> - </p> + items.item_type === "PROGRAM" ? + items.program_attr.status === 0 ? + <Tooltip placement="top" title={"编程题未发布,不能选用!"}> + <p className="selectionys jinzhixiaoshou"> + <i className="iconfont icon-tianjiadaohang font-12 lg ml7 lh30 icontianjiadaohangcolor mr5"></i> + <span className="mr15 lh30">选用</span> + </p> + </Tooltip> + : - <p className="selection xiaoshou" onClick={()=>this.Selectingpracticaltraining(items.id)}> + <p className="selection xiaoshou" onClick={() => this.Selectingpracticaltraining(items.id)}> <i className="iconfont icon-tianjiadaohang font-12 lg ml7 lh30 icontianjiadaohangcolor mr5"></i> <span className="mr15 lh30">选用</span> </p> : - <p className="selection xiaoshou" onClick={()=>this.Selectingpracticaltraining(items.id)}> + <p className="selection xiaoshou" onClick={() => this.Selectingpracticaltraining(items.id)}> <i className="iconfont icon-tianjiadaohang font-12 lg ml7 lh30 icontianjiadaohangcolor mr5"></i> <span className="mr15 lh30">选用</span> </p> } { - defaultActiveKey===0||defaultActiveKey==="0"? + defaultActiveKey === 0 || defaultActiveKey === "0" ? <div className="xaxisreverseorder"> - <p className="viewparsings xiaoshou mr25" onClick={()=>this.props.showmodelysl(items.id)}> + <p className="viewparsings xiaoshou mr25" onClick={() => this.props.showmodelysl(items.id)}> <i className="iconfont icon-shanchu1 font-17 lg ml7 lh30 icontianjiadaohangcolors mr5"></i> <span>删除</span> </p> { - items.item_type==="PROGRAM"? + items.item_type === "PROGRAM" ? <a href={`/problems/${items.program_attr.identifier}/edit`}> - <p className="viewparsings xiaoshou mr25" > + <p className="viewparsings xiaoshou mr25"> <i className="iconfont icon-bianji2 font-17 lg ml7 lh30 icontianjiadaohangcolors mr5"></i> <span>编辑</span> </p> </a> : <a href={`/question/edit/${items.id}`}> - <p className="viewparsings xiaoshou mr25" > + <p className="viewparsings xiaoshou mr25"> <i className="iconfont icon-bianji2 font-17 lg ml7 lh30 icontianjiadaohangcolors mr5"></i> <span>编辑</span> </p> </a> } { - items.public===false? - items.item_type==="PROGRAM"? - items.program_attr.status===0? + items.public === false ? + items.item_type === "PROGRAM" ? + items.program_attr.status === 0 ? "" : - <p className="viewparsings xiaoshou mr25" onClick={()=>this.props.showmodels(items.id)}> + <p className="viewparsings xiaoshou mr25" onClick={() => this.props.showmodels(items.id)}> <i className="iconfont icon-gongkai font-17 lg ml7 lh30 icontianjiadaohangcolors mr5"></i> <span>公开</span> </p> : - <p className="viewparsings xiaoshou mr25" onClick={()=>this.props.showmodels(items.id)}> + <p className="viewparsings xiaoshou mr25" onClick={() => this.props.showmodels(items.id)}> <i className="iconfont icon-gongkai font-17 lg ml7 lh30 icontianjiadaohangcolors mr5"></i> <span>公开</span> </p> @@ -256,54 +363,73 @@ class Listjihe extends Component { } </div> - :"" + : "" } { - items.item_type==="PROGRAM"? + items.item_type === "PROGRAM" ? "" : - <p className="viewparsings xiaoshou mr25" onClick={()=>this.props.chakanjiexibool(keindex)}> + <p className="viewparsings xiaoshou mr25" onClick={() => this.props.chakanjiexibool(keindex)}> <i className="iconfont icon-jiexi font-17 lg ml7 lh30 icontianjiadaohangcolors mr5"></i> 查看解析</p> } - </div> </div> { - chakanjiexiboolindex===keindex?<div> + chakanjiexiboolindex === keindex ? <div> <div className="w100s questiontypeheng mt23"> </div> - <div className=" sortinxdirection mt15 yldxtit" > + <div className=" sortinxdirection mt15 yldxtit"> { - items.item_type==="SINGLE" || items.item_type==="MULTIPLE"? + items.item_type === "SINGLE" ? + <p className=" testfondex yldxtit" + style={{wordBreak: "break-word"}} + dangerouslySetInnerHTML={{__html: markdownToHTML("答案:" + tagArrays[rightkey]).replace(/▁/g, "▁▁▁")}} + > + </p> + : items.item_type === "MULTIPLE"? <p className=" testfondex yldxtit" - style={{wordBreak: "break-word"}} dangerouslySetInnerHTML={{__html: markdownToHTML("答案:"+tagArrays[rightkey]).replace(/▁/g, "▁▁▁")}} + style={{wordBreak: "break-word"}} + dangerouslySetInnerHTML={{__html: markdownToHTML("答案:" + MULTIPLEkey).replace(/▁/g, "▁▁▁")}} > + </p> : <p className=" testfondex yldxtit" - style={{wordBreak: "break-word"}} dangerouslySetInnerHTML={{__html: markdownToHTML("答案:"+rightkey).replace(/▁/g, "▁▁▁")}} + style={{wordBreak: "break-word"}} + dangerouslySetInnerHTML={{__html: markdownToHTML("答案:" + rightkey).replace(/▁/g, "▁▁▁")}} > </p> } </div> - { - items&&items.analysis? - "" - :"" - } - <div className=" sortinxdirection mt15 yldxtit" > - <p className=" testfondex yldxtit" - style={{wordBreak: "break-word"}} dangerouslySetInnerHTML={{__html: markdownToHTML("解析:"+items.analysis).replace(/▁/g, "▁▁▁")}} + <div className=" sortinxdirection mt15 yldxtit"> + <p className=" testfondex yldxtit programquill" + style={{wordBreak: "break-word"}} > + {items ? + items.analysis=== undefined || items.analysis=== null || items.analysis === "" ? + + "" + : + items.analysis.length>0? + <QuillForEditor + imgAttrs={{width: '60px', height: '30px'}} + readOnly={true} + value={JSON.parse(items.analysis)} + /> + : + "" + : + "" + } </p> </div> - </div>:"" + </div> : "" } @@ -313,4 +439,5 @@ class Listjihe extends Component { } } + export default Listjihe; diff --git a/public/react/src/modules/question/component/Newknledpots.js b/public/react/src/modules/question/component/Newknledpots.js index a097b7d79..4668c5e5a 100644 --- a/public/react/src/modules/question/component/Newknledpots.js +++ b/public/react/src/modules/question/component/Newknledpots.js @@ -14,10 +14,35 @@ class PaperDeletModel extends Component { } handleChange=(e)=>{ - this.setState({ - newkntypeinput: e.target.value - }) + // this.setState({ + // newkntypeinput: e.target.value + // }) + // console.log(e.target.value); + // console.log(e.target.value.length); + this.setState({ + newkntypeinput: e.target.value + }) + this.props.setboolred(false); + // + // debugger + // console.log(e); + // + // if(e.target.value.length>0){ + // if(e.target.value.length>=16){ + // var result = e.target.value.substring(0,15); + // this.setState({ + // newkntypeinput: result + // }) + // } + // } + } + mysinputOnBlur=(e)=>{ + console.log("失去焦点了"); + } + + inputOnFocus=(e)=>{ + console.log("获取焦点"); } render() { @@ -34,8 +59,8 @@ class PaperDeletModel extends Component { width="442px" > <div className="educouddiv"> - <div className={"tabeltext-alignleft mt10"}> - <Input onInput={this.handleChange} /> + <div className={this.props.boolred===true?"tabeltext-alignleft mt10 inpustred":"tabeltext-alignleft mt10"}> + <Input onInput={this.handleChange} maxLength={16} onBlur={this.mysinputOnBlur } onFocus={this.inputOnFocus }/> </div> <div className="clearfix mt30 edu-txt-center"> <a className="task-btn mr30 w80" onClick={()=>this.props.NewknTypedeldel(false)}>取消</a> diff --git a/public/react/src/modules/question/component/SingleEditor.js b/public/react/src/modules/question/component/SingleEditor.js index c111aa41b..36f9bc93a 100644 --- a/public/react/src/modules/question/component/SingleEditor.js +++ b/public/react/src/modules/question/component/SingleEditor.js @@ -75,10 +75,12 @@ class SingleEditor extends Component{ this.state = { question_choices: _question_choices || ['', '', '', ''], standard_answers: _standard_answers || [false, false, false, false], - question_title: this.props.question_title || '', + question_title: this.props.question_title!==undefined?JSON.parse(this.props.question_title):"", question_type: this.props.question_type || 0, question_score: this.props.question_score || this.props.init_question_score, - question_titles:this.props.question_titles||'', + question_titles: this.props.question_titles!==undefined?JSON.parse(this.props.question_titles):"", + question_titlesysl:this.props.question_titlesysl||'', + question_titleysl:this.props.question_title || '', item_banksedit:[], } } @@ -121,35 +123,44 @@ class SingleEditor extends Component{ } onSave = () => { var editordata=[]; - const {question_title, question_score, question_type,question_titles, question_choices, standard_answers } = this.state; + const {question_title, question_titleysl,question_score, question_type,question_titles,question_titlesysl, question_choices, standard_answers } = this.state; const { question_id_to_insert_after, question_id } = this.props // TODO check const answerArray = standard_answers.map((item, index) => { return item == true ? index+1 : -1 }).filter(item => item != -1); - if(!question_title) { - this.refs['titleEditor'].showError() - this.props.showNotification('请您输入题干'); - return editordata; - } - if(!answerArray || answerArray.length == 0) { - this.props.showNotification('请先点击选择本选择题的正确选项'); + // const _text = quill.getText(); + // const reg = /^[\s\S]*.*[^\s][\s\S]*$/; + // if (!reg.test(_text)) { + // // 处理编辑器内容为空 + // } else { + // // 提交到后台的内容需要处理一下; + // value = JSON.stringify(value) + // } + if(!question_titleysl) { + // this.refs['titleEditor'].showError() + this.props.showNotification('请您输入题干'); return editordata; } - - for(let i = 0; i < question_choices.length; i++) { if (!question_choices[i]) { - this.refs[`optionEditor${i}`].showError() + // this.refs[`optionEditor${i}`].showError() this.props.showNotification(`请先输入 ${tagArray[i]} 选项的内容`); return editordata; } } - if(!question_titles) { - this.refs['titleEditor2'].showError() - this.props.showNotification('请您输入题目解析'); + if(!answerArray || answerArray.length == 0) { + this.props.showNotification('请先点击选择本选择题的正确选项'); return editordata; } + + + + + // if(!question_titlesysl) { + // this.props.showNotification('请您输入题目解析'); + // return editordata; + // } /** { "question_title":"同学朋友间常用的沟通工具是什么?", @@ -158,7 +169,7 @@ class SingleEditor extends Component{ "question_choices":["a答案","b答案","c答案","d答案"], "standard_answers":[1] }*/ - editordata=[question_title,answerArray,question_choices,question_titles]; + editordata=[question_titleysl,answerArray,question_choices,question_titlesysl]; // question_title, // question_type: answerArray.length > 1 ? 1 : 0, // question_score, @@ -184,8 +195,10 @@ class SingleEditor extends Component{ try { this.setState({ item_banksedit:this.props.item_banksedit, - question_title:this.props.item_banksedit.name, - question_titles:this.props.item_banksedit.analysis, + question_title: this.props.item_banksedit.name!==undefined?JSON.parse(this.props.item_banksedit.name):"", + question_titleysl:this.props.item_banksedit.name|| '', + question_titles: this.props.item_banksedit.analysis!==undefined?JSON.parse(this.props.item_banksedit.analysis):"", + question_titlesysl:this.props.item_banksedit.analysis||'', mychoicess:this.props.item_banksedit.choices, }) @@ -204,8 +217,10 @@ class SingleEditor extends Component{ if(prevProps.item_banksedit !== this.props.item_banksedit) { this.setState({ item_banksedit: this.props.item_banksedit, - question_title: this.props.item_banksedit.name, - question_titles: this.props.item_banksedit.analysis, + question_title: this.props.item_banksedit.name!==undefined?JSON.parse(this.props.item_banksedit.name):"", + question_titleysl:this.props.item_banksedit.name|| '', + question_titles: this.props.item_banksedit.analysis!==undefined?JSON.parse(this.props.item_banksedit.analysis):"", + question_titlesysl:this.props.item_banksedit.analysis||'', mychoicess: this.props.item_banksedit.choices, }) @@ -229,14 +244,31 @@ class SingleEditor extends Component{ // standard_answers[index] = !standard_answers[index]; this.setState({ standard_answers }) } - onOptionContentChange = (value, index) => { + onOptionContentChange = (value,quill,index) => { if (index >= this.state.question_choices.length) { // TODO 新建,然后删除CD选项,再输入题干,会调用到这里,且index是3 return; } + var texts; + const _text = quill.getText(); + const reg = /^[\s\S]*.*[^\s][\s\S]*$/; + if (!reg.test(_text)) { + // 处理编辑器内容为空 + texts=""; + } else { + if(_text.length>=301){ + var result = _text.substring(0,300); + texts={"ops":[{"insert":result}]}; + texts=JSON.stringify(texts); + }else { + // 提交到后台的内容需要处理一下; + value = JSON.stringify(value) + texts=value; + } + } let question_choices = this.state.question_choices.slice(0); - question_choices[index] = value; - this.setState({ question_choices }) + question_choices[index] = texts; + this.setState({ question_choices }); } on_question_score_change = (e) => { this.setState({ question_score: e }) @@ -254,11 +286,61 @@ class SingleEditor extends Component{ } - onContentChange=(e)=>{ - console.log(e); + onContentChange=(value,quill)=>{ + const _text = quill.getText(); + const reg = /^[\s\S]*.*[^\s][\s\S]*$/; + if (!reg.test(_text)) { + // 处理编辑器内容为空 + this.setState({ + question_titleysl:"" + }) + } else { + // 提交到后台的内容需要处理一下; + var texts=""; + if(_text.length>=1001){ + var result = _text.substring(0,1000); + texts={"ops":[{"insert":result}]}; + texts=JSON.stringify(texts); + }else { + value = JSON.stringify(value) + texts=value; + } + this.setState({ + question_titleysl:texts + }) + } + } + onContentChanges=(value,quill)=>{ + const _text = quill.getText(); + const reg = /^[\s\S]*.*[^\s][\s\S]*$/; + if (!reg.test(_text)) { + // 处理编辑器内容为空 + this.setState({ + question_titlesysl:"" + }) + } else { + var texts=""; + if(_text.length>=1001){ + var result = _text.substring(0,1000); + texts={"ops":[{"insert":result}]}; + texts=JSON.stringify(texts); + }else { + value = JSON.stringify(value) + texts=value; + } + this.setState({ + question_titlesysl:texts + }) + } } + handleShowImage = (url) => { + console.log("点击了图片放大"); + console.log(url); + alert(url); + } + render() { - let { question_title, question_score, question_type, question_choices, standard_answers,question_titles} = this.state; + let { question_title, question_score, question_type, question_choices, standard_answers,question_titles,question_titlesysl} = this.state; let { question_id, index, exerciseIsPublish, // question_title, // question_type, @@ -279,7 +361,6 @@ class SingleEditor extends Component{ // ////console.log("xuanzheshijuan"); // ////console.log(answerTagArray); // ////console.log(!exerciseIsPublish); - return( <div className="padding20-30 signleEditor danxuano" id={qNumber}> <style>{` @@ -287,7 +368,7 @@ class SingleEditor extends Component{ flex:1 } .optionRow { - margin:0px!important; + /* margin:0px!important; */ /* margin-bottom: 20px!important; */ } .signleEditor .content_editorMd_show{ @@ -307,47 +388,64 @@ class SingleEditor extends Component{ <span className="xingtigan fl">题干:</span> </p> - <TPMMDEditor mdID={qNumber} placeholder="请您输入题干" height={155} className=" mt10" - initValue={question_title} onChange={(val) => this.setState({ question_title: val})} - ref="titleEditor" - - ></TPMMDEditor> - - {/*<QuillForEditor*/} - {/* style={{ height: '155px'}}*/} - {/* placeholder="请您输入题干"*/} - {/* value={question_title}*/} - {/* options={['code-block', 'image', 'formula']}*/} - {/* onContentChange={this.onContentChange}*/} - {/*/>*/} - - <div className="mb10 sortinxdirection"> + <QuillForEditor + autoFocus={true} + imgAttrs={{width: '146px', height: '136px'}} + style={{ height: '155px'}} + placeholder="请您输入题干" + options={['code-block', 'image', 'formula']} + value={question_title} + onContentChange={this.onContentChange} + /> + + <div className="mb10 mt10 sortinxdirection"> {/* {!question_id ? '新建' : '编辑'} */} <span className="xingcolor font-16 fl mr4">*</span> - <span className="xingtigans fl"><span className="xingtigan">答案选项:</span>点击答案可设置正确答案</span> + <span className="xingtigans fl"><span className="xingtigan">答案选项:</span>点击选项可设置正确答案</span> </div> {question_choices.map( (item, index) => { const bg = standard_answers[index] ? 'check-option-bg' : '' - return <div className="df optionRow " > + return <div className={index>0?"df optionRow mt15": "df optionRow"} key={index} > {/* 点击设置答案 */} {/* TODO 加了tooltip后,会丢失掉span的class */} {/* <Tooltip title={standard_answers[index] ? '点击取消标准答案设置' : '点击设置为标准答案'}> */} - <span class={`option-item fr mr10 color-grey select-choice ${bg} `} + <span className={`option-item fr mr10 color-grey select-choice ${bg} `} name="option_span" onClick={() => this.onOptionClick(index)} style={{flex: '0 0 38px'}}> - <ConditionToolTip title={standard_answers[index] ? '' : '点击设置为标准答案'} placement="left" condition={true}> + <ConditionToolTip key={index} title={'点击设置为标准答案'} placement="left" condition={true}> <div style={{width: '100%', height: '100%'}}>{tagArray[index]}</div> </ConditionToolTip> </span> {/* </Tooltip> */} <div style={{ flex: '0 0 1038px'}}> - <DMDEditor - ref={`optionEditor${index}`} - toMDMode={this.toMDMode} toShowMode={this.toShowMode} - height={166} className={'optionMdEditor'} noStorage={true} - mdID={qNumber + index} placeholder="" onChange={(value) => this.onOptionContentChange(value, index)} - initValue={item} - ></DMDEditor> + { + item===undefined||item===null||item===""? + <QuillForEditor + autoFocus={false} + imgAttrs={{width: '146px', height: '136px'}} + style={{ height: '166px'}} + placeholder="请您输入题干" + options={['code-block', 'image', 'formula']} + value={item} + onContentChange={(value,quill) => this.onOptionContentChange(value,quill,index)} + /> + : + <QuillForEditor + autoFocus={false} + imgAttrs={{width: '146px', height: '136px'}} + style={{ height: '166px'}} + placeholder="请您输入题干" + options={['code-block', 'image', 'formula']} + value={JSON.parse(item)} + onContentChange={(value,quill) => this.onOptionContentChange(value,quill,index)} + /> + + } + + + + + </div> {exerciseIsPublish || index<=1? <i className=" font-18 ml15 mr20"></i> @@ -366,36 +464,22 @@ class SingleEditor extends Component{ - <div> - - - - <p className="mb10 clearfix"> + <div className="mt10"> + <p className="mb10 clearfix"> {/* {!question_id ? '新建' : '编辑'} */} - <span className="xingcolor font-16 fl mr4">*</span> + <span className="xingcolor font-16 fl mr4"></span> <span className="xingtigan fl">题目解析:</span> </p> - <style>{` - .optionMdEditor { - flex:1 - } - .optionRow { - margin:0px!important; - /* margin-bottom: 20px!important; */ - } - .signleEditor .content_editorMd_show{ - display: flex; - margin-top:0px!important; - border-radius:2px; - max-width: 1056px; - word-break:break-all; - } - `}</style> - <TPMMDEditor mdID={qNumber+question_choices.length} placeholder="请您输入题目解析" height={155} className=" mt10" - initValue={question_titles} onChange={(val) => this.setState({ question_titles: val})} - ref="titleEditor2" - - ></TPMMDEditor> + <div className="mt10"></div> + <QuillForEditor + autoFocus={false} + imgAttrs={{width: '146px', height: '136px'}} + style={{height: '166px' }} + placeholder="请您输入题目解析" + options={['code-block', 'image', 'formula']} + value={question_titles} + onContentChange={this.onContentChanges} + /> </div> diff --git a/public/react/src/modules/question/comthetestpaper/Comthetestpapers.js b/public/react/src/modules/question/comthetestpaper/Comthetestpapers.js deleted file mode 100644 index 29f482471..000000000 --- a/public/react/src/modules/question/comthetestpaper/Comthetestpapers.js +++ /dev/null @@ -1,656 +0,0 @@ -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, - Radio, - Checkbox, - Form, - Input, - Select, - Cascader, - AutoComplete, - Col, Row, InputNumber, DatePicker, Button, Tag -} from "antd"; -import './../questioncss/questioncom.css'; - -const InputGroup = Input.Group; -const {Option} = Select; - -class Comthetestpapers extends Component { - constructor(props) { - super(props); - this.contentMdRef = React.createRef() - this.state = { - page: 1, - Knowpoints: [], - rbtx: undefined, - rbkc: undefined, - knowledgepoints: [], - options: [], - } - } - - //初始化 - componentDidMount() { - try { - this.props.getcontentMdRef(this); - } catch (e) { - - } - - this.setState({ - options: this.props.disciplmy, - knowledgepoints: this.props.knowledgepoints, - }) - - - } - - - handdisciplinesChange =(name,title)=>{ - this.setState({ - rbkc:[name.id,title.id] - }) - this.props.form.setFieldsValue({ - rbkc: [name.id,title.id], - }); - - if(this.props.item_banksedit.tag_disciplines.length===0){ - const didata = this.props.disciplinesdata; - const knowledgepointsdata = []; - - for (var i = 0; i < didata.length; i++) { - //方向 - if (name.id === didata[i].id) { - const fxdidata = didata[i].sub_disciplines; - for (var j = 0; j < fxdidata.length; j++) { - //课程 - if (title.id === fxdidata[j].id) { - const zsddata = fxdidata[j].tag_disciplines; - for (var k = 0; k < zsddata.length; k++) { - //知识点 - knowledgepointsdata.push(zsddata[k]); - - - } - - } - - } - } - - - } - - this.setState({ - Knowpoints: [], - knowledgepoints: knowledgepointsdata, - }) - - } - - - - - - - - - - - } - handletag_disciplinesChange = (data) => { - try { - var sju=data[data.length-1].name; - this.setState({ - rbzsd:sju, - Knowpoints:data, - }) - this.props.form.setFieldsValue({ - rbzsd: sju, - }); - }catch (e) { - - } - } - onChange = (e) => { - - } - Getdatas = () => { - return this.handleSubmits(); - } - handleSubmits = () => { - var data = []; - this.props.form.validateFields((err, values) => { - data = []; - if (!err) { - data.push({ - rbnd: parseInt(values.rbnd) - }) - data.push({ - rbtx: values.rbtx - }) - data.push({ - rbzsd: this.state.Knowpoints - }) - data.push({ - rbkc: values.rbkc - }) - data.push({ - classroom:values.classroom - }) - data.push({ - kssc:values.kssc - }) - - } - }); - - return data; - - } - handleSubmit = (e) => { - e.preventDefault(); - this.props.form.validateFields((err, values) => { - if (!err) { - ////console.log("获取的form 数据"); - ////console.log(values); - - } - - - }); - } - - handleFormLayoutChange = (value) => { - //难度塞选 - ////console.log("难度塞选"); - ////console.log(value); - this.props.form.setFieldsValue({ - rbnd: value + "", - }); - this.setState({ - rbnd: value + "", - }) - - } - handleFormkechen = (value) => { - //课程 - ////console.log("课程"); - ////console.log(value); - var valuename = undefined; - this.props.form.setFieldsValue({ - rbzsd: value, - }); - - var arr = this.state.knowledgepoints; - for (let data of arr) { - if (data.id === value) { - this.state.Knowpoints.push(data); - valuename = data.name; - } - } - - var tmp = JSON.parse(JSON.stringify(this.state.knowledgepoints)); - for (var i = 0; i < tmp.length; i++) { - if (tmp[i].id === value) { - this.state.knowledgepoints.splice(i, 1); - } - } - - this.setState({ - rbzsd: valuename, - Knowpoints: this.state.Knowpoints, - knowledgepoints: this.state.knowledgepoints, - }) - - } - - handleFormzhishidian = (value) => { - console.log("handleFormzhishidian 课程"); - console.log(value); - - //课程 - this.props.form.setFieldsValue({ - rbkc: value, - }); - this.setState({ - rbkc:value, - }) - // console.log("handleFormzhishidian"); - // console.log(this.props.disciplinesdata); - - const didata = this.props.disciplinesdata; - const knowledgepointsdata = []; - - for (var i = 0; i < didata.length; i++) { - //方向 - if (value[0] === didata[i].id) { - const fxdidata = didata[i].sub_disciplines; - for (var j = 0; j < fxdidata.length; j++) { - //课程 - if (value[1] === fxdidata[j].id) { - const zsddata = fxdidata[j].tag_disciplines; - for (var k = 0; k < zsddata.length; k++) { - //知识点 - knowledgepointsdata.push(zsddata[k]); - - - } - - } - - } - } - - - } - - this.setState({ - Knowpoints: [], - knowledgepoints: knowledgepointsdata, - }) - - this.props.form.setFieldsValue({ - rbzsd: undefined, - }); - this.setState({ - rbzsd: undefined, - }) - } - - handleFormtixing = (value) => { - //题型 - //console.log("题型"); - //console.log(value); - this.setState({ - rbtx: value + "", - }) - this.props.form.setFieldsValue({ - rbtx: value + "", - }); - this.props.setitem_type(value); - } - preventDefault = (e) => { - e.preventDefault(); - ////console.log('Clicked! But prevent default.'); - } - deletesobject = (item, index) => { - var arr = this.state.Knowpoints; - for (let data of arr) { - if (data.id === item.id) { - this.state.knowledgepoints.push(data); - } - } - - - var tmp = JSON.parse(JSON.stringify(this.state.Knowpoints)); - for (var i = 0; i < tmp.length; i++) { - if (i >= index) { - var pos = this.state.Knowpoints.indexOf(tmp[i]); - this.state.Knowpoints.splice(pos, 1); - } - } - - this.props.form.setFieldsValue({ - rbzsd: this.state.Knowpoints, - }); - - - this.setState({ - Knowpoints: this.state.Knowpoints, - }) - - if (this.state.Knowpoints.length === 0) { - this.setState({ - rbzsd: undefined, - }) - } else if (this.state.Knowpoints.length > 0) { - try { - const myknowda = this.state.Knowpoints; - this.setState({ - rbzsd: myknowda[this.state.Knowpoints.length - 1].name, - }) - } catch (e) { - - } - - } - - } - handleSearch=(value)=>{ - - - if(value!=""){ - this.props.form.setFieldsValue({ - classroom:value, - // course:value - }); - // this.Searchvalue(value) - } - - }; - - handleChange=(e)=>{ - console.log(e); - this.props.form.setFieldsValue({ - // course:value, - classroom:e.target.value, - }) - if(e.target.value){ - if(e.target.value.length>60){ - this.setState({ - bordebool:true, - }) - }else if(e.target.value.length===0){ - this.setState({ - bordebool:true, - }) - }else{ - this.setState({ - bordebool:false, - }) - } - }else{ - this.setState({ - bordebool:true - }) - - } - - }; - - render() { - let {page,options} = this.state; - const {getFieldDecorator} = this.props.form; - const optionss = this.state.searchlist && this.state.searchlist.map(d => <Option key={d.name} value={d.name}>{d.name}</Option>); - var addonAfterthree=this.props.form&&this.props.form.getFieldValue('classroom'); - var addonAfteronelens3=0; - if(addonAfterthree){ - addonAfteronelens3=String(addonAfterthree).length; - } - - return ( - - <div className=" clearfix educontent Contentquestionbankstyle w100s w1200fpx mt19"> - <style> - { - ` - .ant-form-item{ - margin-bottom: 0px !important; - - - } - .ant-form-explain{ - padding-left:0px !important; - margin-top: 3px !important; - } - .ant-select-selection{ - height: 33px !important; - } - .kechen .ant-input-group{ - width:258px !important; - } - - .zsdd .ant-input-group{ - width:258px !important; - } - - .sjmc .ant-input-group{ - width:258px !important; - } - - .kssc .ant-input-group{ - width:258px !important; - } - - .rbndclass .ant-input-group{ - width:258px !important; - } - .ant-input { - height: 33px !important; - } - - .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):focus-within { - outline: 0px solid rgba(24, 144, 255, 0.06) !important; -} - - ` - } - </style> - <div className="h12"></div> - <Form onSubmit={this.handleSubmit}> - <div className="kechen"> - <Form.Item - label="课程:" - > - {getFieldDecorator("rbkc", - { - rules: [{required: true, message: '请选择课程'}], - } - )( - <div className="sortinxdirection"> - <InputGroup compact> - <Cascader style={{width: '258px'}} value={this.state.rbkc} options={options} onChange={this.handleFormzhishidian} - placeholder="请选择..."/> - </InputGroup> - </div> - )} - </Form.Item> - </div> - - <div className="zsdd"> - <Form.Item - label="知识点:" - > - {getFieldDecorator("rbzsd" - )( - <div className="sortinxdirection"> - <InputGroup compact> - <Select style={{width: '258px'}} value={this.state.rbzsd} onChange={this.handleFormkechen} - placeholder="请选择..."> - {this.state.knowledgepoints && this.state.knowledgepoints.map((object, index) => { - return ( - <Option value={object.id}>{object.name}</Option> - ) - })} - </Select> - </InputGroup> - <div className="sortinxdirection" style={{ - height: "33px", - lineHeight: "28px", - - }}> - - {this.state.Knowpoints === undefined ? "" : this.state.Knowpoints.map((object, index) => { - return ( - <div className="mytags" style={{ - position: "relative", - }}> - <p className="w100s stestcen lh32">{object.name}</p> - <i className="iconfont icon-roundclose font-25 lg ml7 icondowncolorss" - onClick={() => this.deletesobject(object, index)}></i> - </div> - ) - })} - - </div> - </div> - )} - </Form.Item> - </div> - <style> - { - ` - .ml19{ - margin-left:19px; - } - ` - } - </style> - <div className="stud-class-set "> - <style>{ - ` - .yslzxueshis .ant-input{ - border-right: none !important; - height: 38px !important; - width: 970px !important; - } - .yslzxueshisy span .ant-input-group-addon{ - width: 65px !important; - background-color: #fafafa!important; - } - .yslzxueshisy .ant-input-group-addon{ - width: 65px !important; - background-color: #fafafa!important; - } - - ` - }</style> - <div className="sjmc"> - <Form.Item label="试卷名称:"> - {getFieldDecorator('classroom', { - rules: [{required: true, message: "不能为空"}], - })( - - <AutoComplete - onSearch={this.handleSearch} - className={"fl construction yslzxueshis "} - dataSource={optionss} - > - <Input className="yslzxueshisy " placeholder="例如:数据结构" onInput={this.handleChange} addonAfter={String(addonAfteronelens3)+"/60"} maxLength={60} /> - </AutoComplete> - )} - <div id='isclassroom'></div> - </Form.Item> - </div> - - </div> - <style> - { - ` - .kssc .ant-form-item-label{ - line-height: 38px !important; - } - - ` - } - </style> - <div className="kssc"> - - <Form.Item label="考试时长:"> - {getFieldDecorator('kssc')(<InputNumber - min={0} - step={0.1} - ></InputNumber>)} - <span className="ant-form-text"> 分钟</span> - </Form.Item> - </div> - {/*<div className="tixing">*/} - {/*<Form.Item*/} - {/* label="题型:"*/} - {/*>*/} - {/* {getFieldDecorator("rbtx",*/} - {/* {*/} - {/* rules: [{required: true, message: '请选择题型'}],*/} - {/* }*/} - {/* )(*/} - {/* <InputGroup compact>*/} - {/* <Select style={{width: '258px'}} value={this.state.rbtx} onChange={this.handleFormtixing}*/} - {/* placeholder="请选择...">*/} - {/* <Option value="SINGLE">单选题</Option>*/} - {/* <Option value="MULTIPLE">多选题</Option>*/} - {/* <Option value="JUDGMENT">判断题</Option>*/} - {/* <Option value="PROGRAM">编程题</Option>*/} - {/* </Select>*/} - {/* </InputGroup>*/} - {/* )}*/} - {/*</Form.Item>*/} - {/*</div>*/} - - <style> - { - ` - .rbndclass .ant-radio-button-wrapper{ - width:106px !important; - height:33px !important; - background:#EEEEEE; - border-radius:17px !important; - color:#333333; - text-align: center !important; - border:0px !important; - margin-right: 27px !important; - margin-top: 6px !important; - - } - .rbndclass .ant-radio-button-wrapper-checked { - width: 106px !important; - height: 33px !important; - background: #4CACFF !important; - border-radius: 17px !important; - text-align: center !important; - border:0px !important; - color: #ffffff !important; - margin-right: 27px !important; - margin-top: 6px!important; - - } - .rbndclass .ant-radio-button-wrapper:not(:first-child)::before{ - border:0px !important; - width:0px !important; - } - - .rbndclass .ant-radio-button-wrapper{ - border:0px !important; - } - .rbndclass .ant-radio-group{ - border:0px !important; - } - .rbndclass .ant-radio-group label{ - border:0px !important; - } - - .rbndclass .ant-radio-group span{ - border:0px !important; - } - - ant-radio-button-wrapper:focus-within { - outline: 0px solid #ffffff; - } - - ` - } - </style> - <div className="rbndclass"> - <Form.Item label="难度:"> - {getFieldDecorator('rbnd', - { - rules: [{required: true, message: '请选择难度'}], - } - )( - <Radio.Group value={this.state.rbnd} onChange={this.handleFormLayoutChange}> - <Radio.Button value="1">简单</Radio.Button> - <Radio.Button value="2">适中</Radio.Button> - <Radio.Button value="3">困难</Radio.Button> - </Radio.Group>, - )} - </Form.Item> - </div> - - </Form> - <div className="h20"></div> - </div> - ) - - } - - -} - -const Comthetestpaperss = Form.create({name: 'Itembankstops'})(Comthetestpapers); -export default Comthetestpaperss; diff --git a/public/react/src/modules/question/comthetestpaper/Comthetestpaperst.js b/public/react/src/modules/question/comthetestpaper/Comthetestpaperst.js index 88d5b9725..50d2bb85f 100644 --- a/public/react/src/modules/question/comthetestpaper/Comthetestpaperst.js +++ b/public/react/src/modules/question/comthetestpaper/Comthetestpaperst.js @@ -41,7 +41,7 @@ const options = [ ], }, ]; - +//Itembankstop 题库的 class Comthetestpaperst extends Component { constructor(props) { super(props); @@ -54,10 +54,16 @@ class Comthetestpaperst extends Component { knowledgepoints: [], knowledgepoints2:[], options: [], - NewknTypedel:false + NewknTypedel:false, + boolred:false, } } + setboolred=(bool)=>{ + this.setState({ + boolred:bool + }) + } //初始化 componentDidMount() { try { @@ -495,19 +501,32 @@ class Comthetestpaperst extends Component { } NewknTypedeltyoedel=(value)=>{ + var knowledgepointmys= this.state.knowledgepoints; + for(let myda of knowledgepointmys) { + if(myda.name===value){ + this.props.showNotification(`重复的知识点`); + this.setboolred(true); + break; + } + } if(value===null||value===""){ this.props.showNotification(`请输入知识点`); + this.setboolred(true); + return } if(value.length===0){ this.props.showNotification(`请输入知识点`); + this.setboolred(true); + return } if(this.state.rbkc===undefined || this.state.rbkc===null || this.state.rbkc===""){ this.props.showNotification(`请选择课程方向`); + this.setboolred(true); return; } var data={ @@ -523,19 +542,36 @@ class Comthetestpaperst extends Component { id: result.data.tag_discipline_id, name:value, } - this.state.knowledgepoints.push(leydata); - const _result =[]; - this.state.knowledgepoints.filter(item => { - if (this.state.Knowpoints.findIndex(t => t.id === item.id) === -1) { - _result.push(item); - } - }); - this.setState({ - Knowpoints: this.state.Knowpoints, - knowledgepoints: this.state.knowledgepoints, - knowledgepoints2: _result, - }) + if(this.state.Knowpoints.length>=5){ + this.state.knowledgepoints.push(leydata); + const _result =[]; + this.state.knowledgepoints.filter(item => { + if (this.state.Knowpoints.findIndex(t => t.id === item.id) === -1) { + _result.push(item); + } + }); + + this.setState({ + Knowpoints: this.state.Knowpoints, + knowledgepoints: this.state.knowledgepoints, + knowledgepoints2: _result, + }) + }else{ + this.state.Knowpoints.push(leydata); + this.state.knowledgepoints.push(leydata); + const _result =[]; + this.state.knowledgepoints.filter(item => { + if (this.state.Knowpoints.findIndex(t => t.id === item.id) === -1) { + _result.push(item); + } + }); + this.setState({ + Knowpoints: this.state.Knowpoints, + knowledgepoints: this.state.knowledgepoints, + knowledgepoints2: _result, + }) + } } }).catch((error) => { //console.log(error); @@ -609,6 +645,8 @@ class Comthetestpaperst extends Component { { NewknTypedel? <Newknledpots {...this.state} {...this.props} + boolred={this.state.boolred} + setboolred={(bool)=>this.setboolred(bool)} NewknTypedeldel={(bool)=>this.NewknTypedeldel(bool)} NewknTypedeltyoedel={(value)=>this.NewknTypedeltyoedel(value)} ></Newknledpots> @@ -656,32 +694,33 @@ class Comthetestpaperst extends Component { <img className=" ml22 zjzsdian xiaoshou" src={getImageUrl("/images/educoder/zjzsd.png")} onClick={()=>this.NewknTypedeldel(true)}/> - <div className="sortinxdirection" style={{ - height: "33px", - lineHeight: "28px", - }}> + </div> + )} + </Form.Item> + </div> + <div className="sortinxdirection huanhan w100s mt15" style={{ + height: "33px", + lineHeight: "28px", - {this.state.Knowpoints === undefined ? "" : this.state.Knowpoints.map((object, index) => { - return ( - <div key={index} className="mytags" style={{ - position: "relative", - }}> - <p className="w100s stestcen lh32">{object.name}</p> + }}> - <img className=" ml7 zjzsdian xiaoshou icondowncolorssy" onClick={() => this.deletesobject(object, index)} src={getImageUrl("/images/educoder/bzucha.png")}/> + {this.state.Knowpoints === undefined ? "" : this.state.Knowpoints.map((object, index) => { + return ( + <div key={index} className="mytags" style={{ + position: "relative", + }}> + <p className="w100s stestcen lh32">{object.name}</p> - </div> - ) - })} + <img className=" ml7 zjzsdian xiaoshou icondowncolorssy" onClick={() => this.deletesobject(object, index)} src={getImageUrl("/images/educoder/bzucha.png")}/> + + </div> + ) + })} - </div> - </div> - )} - </Form.Item> </div> <style> { diff --git a/public/react/src/modules/question/questioncss/questioncom.css b/public/react/src/modules/question/questioncss/questioncom.css index 5d37aeccd..84a663217 100644 --- a/public/react/src/modules/question/questioncss/questioncom.css +++ b/public/react/src/modules/question/questioncss/questioncom.css @@ -332,6 +332,12 @@ line-height:19px; margin-top: 19px; } +.listjihetixingstitsp{ + color: #333333; + font-size: 14px; + line-height:19px; + margin-top: 10px; +} .listjihetixingstitssy{ color: #333333; font-size: 14px; @@ -518,11 +524,11 @@ margin-top: 19px; } .mytags{ - width:106px; + min-width:106px; height:32px; border-radius:2px; border:1px solid #DDDDDD; - margin-left: 20px; + margin-right: 20px; } .lh32{ line-height: 32px; @@ -939,3 +945,28 @@ .searchwidth{ width: 347px !important; } +.lh26{ + line-height: 26px !important; +} +.tites{ + color: #888888 !important; +} +.ant-popover-inner-content{ + padding: 0px !important; +} + +.huanhan{ + flex-wrap: wrap; +} +.mb20{ + margin-bottom: 20px; +} + +.inpustred .ant-input{ + border: 1px solid #f30707; + border-radius: 5px; +} + +.mt15{ + margin-top: 15px; +} diff --git a/public/react/src/modules/testpaper/Paperlibraryeditid.js b/public/react/src/modules/testpaper/Paperlibraryeditid.js index 1b3ee95ef..efcb45496 100644 --- a/public/react/src/modules/testpaper/Paperlibraryeditid.js +++ b/public/react/src/modules/testpaper/Paperlibraryeditid.js @@ -258,17 +258,24 @@ class Paperlibraryeditid extends Component { <div className=" clearfix educontent Contentquestionbankstyle w100s w1200wuh mt19"> - + <style> + { + ` + .seeoagertitscss .jixuxuanti{ + background:#4CACFF; + } + ` + } + </style> + <div className={"seeoagertitscss"}> <Seeoagertits setnewmyshixunmodelbool={(e)=>this.setnewmyshixunmodelbool(e)} all_score={paperlibrartdata && paperlibrartdata.exam && paperlibrartdata.exam.all_questions_count} all_questions_count={paperlibrartdata && paperlibrartdata.exam && paperlibrartdata.exam.all_score} difficulty={paperlibrartdata && paperlibrartdata.exam && paperlibrartdata.exam.difficulty} > - - </Seeoagertits> - + </div> <Paperlibraryseeid_itemss {...this.state} {...this.props} diff --git a/public/react/src/modules/testpaper/component/Paperlibraryseeid_items.js b/public/react/src/modules/testpaper/component/Paperlibraryseeid_items.js index 1ab987ba0..0a6a718c3 100644 --- a/public/react/src/modules/testpaper/component/Paperlibraryseeid_items.js +++ b/public/react/src/modules/testpaper/component/Paperlibraryseeid_items.js @@ -15,6 +15,7 @@ import { } from "antd"; import '../testioncss/testioncss.css'; import '../../tpm/newshixuns/css/Newshixuns.css'; +import QuillForEditor from "../../../common/quillForEditor"; const tagArray = [ 'A.', 'B.', 'C.', 'D.', 'E.', 'F.', 'G.', 'H.', 'I.', @@ -67,15 +68,42 @@ class Paperlibraryseeid_items extends Component { render() { let {questions, totalscore, total, items} = this.state; let {objectsingle, indexx, paperreviewsingleindex, indexxy,name} = this.props; - // console.log("objectsingle"); - // console.log(objectsingle); + + + var itemssname=""; + try { + itemssname= JSON.parse(objectsingle.name); + }catch (e) { + } + if(itemssname===undefined){ + itemssname=objectsingle.name + } return ( <div key={indexxy} className={ "w100s borderwdswuh mb20 pd20 "} style={{ minHeight: "114px", }}> - + <style>{ + ` + .programquill .ql-editor{ + padding: 0px; + } + + .programquill .ql-editor p{ + line-height: 28px; + + } + .programquills .ql-editor{ + padding: 0px; + } + + .programquills .ql-editor p{ + line-height: 20px; + + } + ` + }</style> {/*顶部*/} <div className="w100s sortinxdirection "> <div className=" sortinxdirection "> @@ -100,29 +128,46 @@ class Paperlibraryseeid_items extends Component { } </style> { - this.props.typenames==="PROGRAM"? - <div className=" lh28 listjihetixingstit markdown-body cretitlecolrlist" style={{wordBreak: "break-word"}} - dangerouslySetInnerHTML={{__html: markdownToHTML( objectsingle.name).replace(/▁/g, "▁▁▁")}}> - </div> + objectsingle.item_type==="PROGRAM"? + <div className="w100s sortinxdirection"> + <div className=" tites lh28 listjihetixingstit markdown-body cretitlecolrlist " style={{wordBreak: "break-word"}} + > + ({objectsingle.score}分) + </div> + <div className="ml10 lh28 listjihetixingstit markdown-body cretitlecolrlist " style={{wordBreak: "break-word"}} + dangerouslySetInnerHTML={{__html: markdownToHTML(objectsingle.name).replace(/▁/g, "▁▁▁")}}> + + </div> + </div> : - <div className=" lh28 listjihetixingstit markdown-body cretitlecolrlist" style={{wordBreak: "break-word"}} - dangerouslySetInnerHTML={{__html: markdownToHTML(`(${objectsingle.score}分) ` + objectsingle.name).replace(/▁/g, "▁▁▁")}}> + <div className="w100s sortinxdirection"> + <div className="tites lh28 listjihetixingstit markdown-body cretitlecolrlist " style={{wordBreak: "break-word"}} + > + ({objectsingle.score}分) + </div> + <div className="ml10 lh28 listjihetixingstit cretitlecolrlist programquill" style={{wordBreak: "break-word"}} + > + <QuillForEditor + readOnly={true} + value={itemssname} + /> + + </div> </div> - } + </div> {/*内容*/} <div className="w100s sortinxdirection"> - { objectsingle.item_type === "JUDGMENT" ? <p className="w100s listjihetixingstits sortinxdirection "> { objectsingle === undefined || objectsingle === null ? "" : objectsingle.choices.map((object, index) => { return ( - <p className={index === 1 ? "sortinxdirection ml10" : "sortinxdirection "} key={index}> - <Radio checked={object.is_answer}> + <p className={index === 1 ? "sortinxdirection ml10" : "sortinxdirection "}> + <Radio disabled={false}> {object.choice_text} </Radio> </p> @@ -134,8 +179,17 @@ class Paperlibraryseeid_items extends Component { objectsingle.item_type === "PROGRAM" ? <p className="w100s listjihetixingstitssy sortinxdirection "> <p className={"sortinxdirection mt15"}> - <p style={{wordBreak: "break-word"}} - dangerouslySetInnerHTML={{__html: markdownToHTML(objectsingle.program_attr.description).replace(/▁/g, "▁▁▁")}}></p> + { + objectsingle&&objectsingle.program_attr&&objectsingle.program_attr.description? + <p className="programquill" style={{wordBreak: "break-word"}} + > + <QuillForEditor + readOnly={true} + value={JSON.parse(objectsingle.program_attr.description)} + /> + </p> + : + ""} </p> </p> : @@ -143,10 +197,23 @@ class Paperlibraryseeid_items extends Component { { objectsingle === undefined || objectsingle === null ? "" : objectsingle.choices.map((object, index) => { return ( - <p className={index === 0 ? "sortinxdirection" : "sortinxdirection mt15"} key={index}> + <p className={index === 0 ? "sortinxdirection" : "sortinxdirection mt15"}> {tagArray[index]} - <p style={{wordBreak: "break-word"}} - dangerouslySetInnerHTML={{__html: markdownToHTML(object.choice_text).replace(/▁/g, "▁▁▁")}}></p> + <p className="programquills" style={{wordBreak: "break-word"}}> + {object ? + object.choice_text === undefined || object.choice_text=== null || object.choice_text === "" ? + "" + : + object.choice_text.length>0? + <QuillForEditor + readOnly={true} + value={JSON.parse(object.choice_text)} + /> + :"" + : + "" + } + </p> </p> ) }) @@ -154,7 +221,6 @@ class Paperlibraryseeid_items extends Component { </p> } - </div> diff --git a/public/react/src/modules/testpaper/testioncss/testioncss.css b/public/react/src/modules/testpaper/testioncss/testioncss.css index a1de362d1..c22c93f8a 100644 --- a/public/react/src/modules/testpaper/testioncss/testioncss.css +++ b/public/react/src/modules/testpaper/testioncss/testioncss.css @@ -548,7 +548,7 @@ height:32px; border-radius:2px; border:1px solid #DDDDDD; - margin-left: 20px; + margin-right: 20px; } .lh32{ line-height: 32px; @@ -892,3 +892,6 @@ width: 39px; height: 44px; } +.tites{ + color: #888888 !important; +} diff --git a/public/react/src/modules/tpm/NewHeader.js b/public/react/src/modules/tpm/NewHeader.js index 64787c72b..71b597747 100644 --- a/public/react/src/modules/tpm/NewHeader.js +++ b/public/react/src/modules/tpm/NewHeader.js @@ -948,6 +948,13 @@ submittojoinclass=(value)=>{ return ( <div className="newHeaders" id="nHeader" > + <style>{ + ` + body .questionbanks .ant-popover-inner-content { + padding:0px !important; + } + ` + }</style> {isRender===true?<LoginDialog Modifyloginvalue={()=>this.Modifyloginvalue()} @@ -1039,6 +1046,17 @@ submittojoinclass=(value)=>{ ` } </style> + <li className={`pr questionbanks`}> + <Popover placement="bottom" content={contents} trigger="click" > + <div className=" sortinxdirection mr10"> + <div style={{ + color:"#fff" + }}> + 题库 + </div> + </div> + </Popover> + </li> {/*<li className={`pr questionbanks`}>*/} {/* <Popover placement="bottom" content={contents} trigger="click" >*/} {/* <div className=" sortinxdirection mr10">*/} diff --git a/spec/models/examination_intelligent_setting_spec.rb b/spec/models/examination_intelligent_setting_spec.rb new file mode 100644 index 000000000..fb82f86ca --- /dev/null +++ b/spec/models/examination_intelligent_setting_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe ExaminationIntelligentSetting, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/examination_type_setting_spec.rb b/spec/models/examination_type_setting_spec.rb new file mode 100644 index 000000000..916cb367d --- /dev/null +++ b/spec/models/examination_type_setting_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe ExaminationTypeSetting, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end