diff --git a/app/controllers/admins/customers_controller.rb b/app/controllers/admins/customers_controller.rb index 80b01757b..8235bdb80 100644 --- a/app/controllers/admins/customers_controller.rb +++ b/app/controllers/admins/customers_controller.rb @@ -1,4 +1,5 @@ class Admins::CustomersController < Admins::BaseController + # skip_before_action :check_sign helper_method :current_partner def index diff --git a/app/controllers/examination_banks_controller.rb b/app/controllers/examination_banks_controller.rb index 98dfc57bb..e5e9ca4ca 100644 --- a/app/controllers/examination_banks_controller.rb +++ b/app/controllers/examination_banks_controller.rb @@ -11,7 +11,11 @@ class ExaminationBanksController < ApplicationController end def show - + @items = @exam.examination_items + @single_questions = @items.where(item_type: "SINGLE") + @multiple_questions = @items.where(item_type: "MULTIPLE") + @judgement_questions = @items.where(item_type: "JUDGMENT") + @program_questions = @items.where(item_type: "PROGRAM") end def create @@ -26,12 +30,16 @@ class ExaminationBanksController < ApplicationController if item.present? new_item = ExaminationItem.new(examination_bank: exam, item_bank: item, name: item.name, item_type: item.item_type, difficulty: item.difficulty, score: basket.score, position: basket.position) - if new_item.save! - item.increment!(:quotes) - if item.item_choices.present? - item.item_choices.each do |choice| - new_item.examination_item_choices << ExaminationItemChoice.new(choice_text: choice.choice_text, is_answer: choice.is_answer) - end + new_item.save! + item.increment!(:quotes) + + if item.item_type == "PROGRAM" + new_hack = item.container.fork + new_item.update_attributes!(container: new_hack) + else + new_item.examination_item_analysis.create!(analysis: item.analysis) + item.item_choices.each do |choice| + new_item.examination_item_choices << ExaminationItemChoice.new(choice_text: choice.choice_text, is_answer: choice.is_answer) end end end diff --git a/app/controllers/partners_controller.rb b/app/controllers/partners_controller.rb index 7875e1780..dfa1b2017 100644 --- a/app/controllers/partners_controller.rb +++ b/app/controllers/partners_controller.rb @@ -1,4 +1,5 @@ class PartnersController < ApplicationController + skip_before_action :check_sign include Base::PaginateHelper include Admins::RenderHelper diff --git a/app/controllers/shixuns_controller.rb b/app/controllers/shixuns_controller.rb index 3de5970b6..e84e83c71 100644 --- a/app/controllers/shixuns_controller.rb +++ b/app/controllers/shixuns_controller.rb @@ -18,9 +18,9 @@ class ShixunsController < ApplicationController before_action :find_repo_name, only: [:repository, :commits, :file_content, :update_file, :shixun_exec, :copy, :add_file, :jupyter_exec] - before_action :allowed, only: [:update, :close, :update_propaedeutics, :settings, :publish, :apply_public, + before_action :allowed, only: [:update, :close, :update_propaedeutics, :settings, :publish, :apply_public, :upload_git_folder, :shixun_members_added, :change_manager, :collaborators_delete, :upload_git_file, - :cancel_apply_public, :cancel_publish, :add_collaborators, :add_file] + :cancel_apply_public, :cancel_publish, :add_collaborators, :add_file, :delete_git_file] before_action :portion_allowed, only: [:copy] before_action :special_allowed, only: [:send_to_course, :search_user_courses] @@ -896,6 +896,15 @@ class ShixunsController < ApplicationController render_ok end + def upload_git_folder + render_ok + end + + def delete_git_file + + render_ok + end + def add_collaborators member_ids = "(" + @shixun.shixun_members.map(&:user_id).join(',') + ")" user_name = "%#{params[:user_name].to_s.strip}%" diff --git a/app/models/examination_item.rb b/app/models/examination_item.rb index b9da32ab5..0cc515cae 100644 --- a/app/models/examination_item.rb +++ b/app/models/examination_item.rb @@ -1,6 +1,27 @@ class ExaminationItem < ApplicationRecord + enum item_type: { SINGLE: 0, MULTIPLE: 1, JUDGMENT: 2, COMPLETION: 3, SUBJECTIVE: 4, PRACTICAL: 5, PROGRAM: 6 } + belongs_to :examination_bank, touch: true belongs_to :item_bank, optional: true has_many :examination_item_choices, dependent: :destroy + has_one :examination_item_analysis, dependent: :destroy + belongs_to :container, polymorphic: true, optional: true + + def analysis + examination_item_analysis&.analysis + end + + def item_choices + examination_item_choices + end + + def public + 0 + end + + def quotes + 0 + end + end diff --git a/app/models/examination_item_analysis.rb b/app/models/examination_item_analysis.rb new file mode 100644 index 000000000..e96ab769f --- /dev/null +++ b/app/models/examination_item_analysis.rb @@ -0,0 +1,3 @@ +class ExaminationItemAnalysis < ApplicationRecord + belongs_to :examination_item +end diff --git a/app/models/hack.rb b/app/models/hack.rb index 0ca38ef3a..79c7a0f82 100644 --- a/app/models/hack.rb +++ b/app/models/hack.rb @@ -22,6 +22,7 @@ class Hack < ApplicationRecord belongs_to :sub_discipline has_one :item_bank, as: :container, dependent: :destroy + has_one :examination_bank, as: :container, dependent: :destroy scope :published, -> { where(status: 1) } scope :unpublish, -> { where(status: 0) } @@ -56,4 +57,31 @@ class Hack < ApplicationRecord user_id == user.id || user.admin_or_business? end + # 复制fork + def fork + new_hack = Hack.new + new_hack.attributes = self.attributes.dup.except("id", "user_id", "status", "identifier", "comments_count", "praises_count", + "pass_num", "created_at", "updated_at", "hack_user_lastest_codes_count", + "open_or_not", "submit_num") + new_hack.user_id = User.current.id + new_hack.identifier = Util::UUID.generate_identifier(Hack, 8) + new_hack.fork_id = self.id + new_hack.save! + + # 创建测试集与代码 + hack_sets.each do |set| + new_hack.hack_sets.create!(input: set.input, output: set.output, position: set.position) + end + + # 新建知识点 + tag_discipline_containers.each do |tag| + new_hack.tag_discipline_containers.create!(tag_discipline_id: tag.tag_discipline_id) + end + + hack_codes.each do |code| + new_hack.hack_codes.create!(code: code.code, language: code.language, modify_time: Time.now) + end + new_hack + end + end diff --git a/app/services/item_baskets/save_item_basket_service.rb b/app/services/item_baskets/save_item_basket_service.rb index 36ea46cb2..cf6d3738b 100644 --- a/app/services/item_baskets/save_item_basket_service.rb +++ b/app/services/item_baskets/save_item_basket_service.rb @@ -30,9 +30,9 @@ class ItemBaskets::SaveItemBasketService < ApplicationService else score = case item_type - when 0, 1, 2 + when "SINGLE", "MULTIPLE", "JUDGMENT" 5 - when 6 + when "PROGRAM" 10 else 5 diff --git a/app/services/jupyter_service.rb b/app/services/jupyter_service.rb index 2d43a0987..bbe0330bb 100644 --- a/app/services/jupyter_service.rb +++ b/app/services/jupyter_service.rb @@ -8,7 +8,8 @@ module JupyterService uri = "#{shixun_tomcat}/bridge/jupyter/get" tpiID = "tpm#{shixun.id}" mount = shixun.data_sets.present? - params = {tpiID: tpiID, identifier: shixun.identifier, needMount: mount, gitUrl: '', + gitUrl = "#{edu_setting('git_address_domain')}/#{shixun.repo_path}" + params = {tpiID: tpiID, identifier: shixun.identifier, needMount: mount, gitUrl: gitUrl, :containers => "#{Base64.urlsafe_encode64(shixun_container_limit(shixun))}"} logger.info "test_juypter: uri->#{uri}, params->#{params}" diff --git a/app/views/examination_banks/show.json.jbuilder b/app/views/examination_banks/show.json.jbuilder index e69de29bb..2a2da9af9 100644 --- a/app/views/examination_banks/show.json.jbuilder +++ b/app/views/examination_banks/show.json.jbuilder @@ -0,0 +1,51 @@ +json.single_questions do + json.questions @single_questions.each do |question| + json.(question, :id, :position, :score, :item_type) + json.partial! "item_banks/item", locals: {item: question} + end + json.questions_score @single_questions.map(&:score).sum + json.questions_count @single_questions.size +end + +json.multiple_questions do + json.questions @multiple_questions.each do |question| + json.(question, :id, :position, :score, :item_type) + json.partial! "item_banks/item", locals: {item: question} + end + json.questions_score @multiple_questions.map(&:score).sum + json.questions_count @multiple_questions.size +end + +json.judgement_questions do + json.questions @judgement_questions.each do |question| + json.(question, :id, :position, :score, :item_type) + json.partial! "item_banks/item", locals: {item: question} + end + json.questions_score @judgement_questions.map(&:score).sum + json.questions_count @judgement_questions.size +end + +json.program_questions do + json.questions @program_questions.each do |question| + json.(question, :id, :position, :score, :item_type) + json.partial! "item_banks/item", locals: {item: question} + end + json.questions_score @program_questions.map(&:score).sum + json.questions_count @program_questions.size +end + +json.exam do + json.(@exam, :id, :name, :difficulty, :sub_discipline_id, :duration) + json.all_score @items.map(&:score).sum + json.all_questions_count @items.size + + json.discipline do + json.(@exam.sub_discipline&.discipline, :id, :name) + end + json.sub_discipline do + json.(@exam.sub_discipline, :id, :name) + end + json.tag_disciplines @exam.tag_disciplines do |tag| + json.(tag, :id, :name) + end +end \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 77ffdfc2c..c8abff94e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -303,6 +303,8 @@ Rails.application.routes.draw do post :commits post :file_content post :upload_git_file + post :upload_git_folder + delete :delete_git_file post :update_file post :close post :add_file diff --git a/db/migrate/20200102085722_add_fork_id_to_hacks.rb b/db/migrate/20200102085722_add_fork_id_to_hacks.rb new file mode 100644 index 000000000..f664e75ed --- /dev/null +++ b/db/migrate/20200102085722_add_fork_id_to_hacks.rb @@ -0,0 +1,7 @@ +class AddForkIdToHacks < ActiveRecord::Migration[5.2] + def change + add_column :hacks, :fork_id, :integer, default: 0 + add_column :examination_items, :container_id, :integer + add_column :examination_items, :container_type, :string + end +end diff --git a/db/migrate/20200102103810_create_examination_item_analyses.rb b/db/migrate/20200102103810_create_examination_item_analyses.rb new file mode 100644 index 000000000..eb33e8efc --- /dev/null +++ b/db/migrate/20200102103810_create_examination_item_analyses.rb @@ -0,0 +1,10 @@ +class CreateExaminationItemAnalyses < ActiveRecord::Migration[5.2] + def change + create_table :examination_item_analyses do |t| + t.references :examination_item, index: true, unique: true + t.text :analysis + + t.timestamps + end + end +end diff --git a/public/react/src/AppConfig.js b/public/react/src/AppConfig.js index cb5f16055..87ab58f63 100644 --- a/public/react/src/AppConfig.js +++ b/public/react/src/AppConfig.js @@ -123,7 +123,10 @@ export function initAxiosInterceptors(props) { } } if(`${config[0]}`!=`true`){ + let timestamp = Date.parse(new Date())/1000; if (window.location.port === "3007") { + // let timestamp=railsgettimes(proxy); + console.log(timestamp) railsgettimes(`${proxy}/api/main/first_stamp.json`); let newopens=md5(opens+timestamp) config.url = `${proxy}${url}`; diff --git a/public/react/src/common/quillForEditor/ImageBlot.js b/public/react/src/common/quillForEditor/ImageBlot.js index d00f2bafd..5ff84b249 100644 --- a/public/react/src/common/quillForEditor/ImageBlot.js +++ b/public/react/src/common/quillForEditor/ImageBlot.js @@ -4,7 +4,7 @@ * @Github: * @Date: 2019-12-16 15:50:45 * @LastEditors : tangjiang - * @LastEditTime : 2019-12-27 15:07:11 + * @LastEditTime : 2019-12-31 13:59:02 */ import Quill from "quill"; @@ -50,6 +50,7 @@ export default class ImageBlot extends BlockEmbed { return node; } + // 获取节点值 static value (node) { return { diff --git a/public/react/src/common/quillForEditor/index.js b/public/react/src/common/quillForEditor/index.js index cb214465c..f3f359a80 100644 --- a/public/react/src/common/quillForEditor/index.js +++ b/public/react/src/common/quillForEditor/index.js @@ -4,7 +4,7 @@ * @Github: * @Date: 2019-12-18 08:49:30 * @LastEditors : tangjiang - * @LastEditTime : 2019-12-27 16:49:25 + * @LastEditTime : 2019-12-31 15:11:37 */ import './index.scss'; import 'quill/dist/quill.core.css'; // 核心样式 @@ -18,10 +18,18 @@ import deepEqual from './deepEqual.js' import { fetchUploadImage } from '../../services/ojService.js'; import { getImageUrl } from 'educoder' import ImageBlot from './ImageBlot'; +const Size = Quill.import('attributors/style/size'); +const Font = Quill.import('formats/font'); +// const Color = Quill.import('attributes/style/color'); +Size.whitelist = ['12px', '14px', '16px', '18px', '20px', false]; +Font.whitelist = ['SimSun', 'SimHei','Microsoft-YaHei','KaiTi','FangSong','Arial','Times-New-Roman','sans-serif']; window.Quill = Quill; window.katex = katex; Quill.register(ImageBlot); +Quill.register(Size); +Quill.register(Font, true); +// Quill.register(Color); function QuillForEditor ({ placeholder, @@ -38,15 +46,16 @@ function QuillForEditor ({ }) { // toolbar 默认值 const defaultConfig = [ - ['bold', 'italic', 'underline'], - [{align: []}, {list: 'ordered'}, {list: 'bullet'}], // 列表 - [{script: 'sub'}, {script: 'super'}], - [{ 'color': [] }, { 'background': [] }], - [{header: [1,2,3,4,5,false]}], - ['blockquote', 'code-block'], - ['link', 'image', 'video'], - ['formula'], - ['clean'] + 'bold', 'italic', 'underline', + {size: ['12px', '14px', '16px', '18px', '20px']}, + {align: []}, {list: 'ordered'}, {list: 'bullet'}, // 列表 + {script: 'sub'}, {script: 'super'}, + { 'color': [] }, { 'background': [] }, + {header: [1,2,3,4,5,false]}, + 'blockquote', 'code-block', + 'link', 'image', 'video', + 'formula', + 'clean' ]; const editorRef = useRef(null); diff --git a/public/react/src/common/quillForEditor/index.scss b/public/react/src/common/quillForEditor/index.scss index 3e951e2b6..96cd45edd 100644 --- a/public/react/src/common/quillForEditor/index.scss +++ b/public/react/src/common/quillForEditor/index.scss @@ -7,4 +7,107 @@ cursor: pointer; } } + .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="12px"]::before, + .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="12px"]::before { + content: '12px'; + font-size: 12px; + } + .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="14px"]::before, + .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="14px"]::before { + content: '14px'; + font-size: 14px; + } + .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="16px"]::before, + .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="16px"]::before { + content: '16px'; + font-size: 16px; + } + .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="18px"]::before, + .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="18px"]::before { + content: '18px'; + font-size: 18px; + } + .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="20px"]::before, + .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="20px"]::before { + content: '20px'; + font-size: 20px; + } + //默认的样式 + .ql-snow .ql-picker.ql-size .ql-picker-label::before, + .ql-snow .ql-picker.ql-size .ql-picker-item::before { + content: '14px'; + font-size: 14px; + } + + .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimSun]::before, + .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimSun]::before { + content: "宋体"; + font-family: "SimSun"; + } + .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimHei]::before, + .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimHei]::before { + content: "黑体"; + font-family: "SimHei"; + } + .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Microsoft-YaHei]::before, + .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Microsoft-YaHei]::before { + content: "微软雅黑"; + font-family: "Microsoft YaHei"; + } + .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=KaiTi]::before, + .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=KaiTi]::before { + content: "楷体"; + font-family: "KaiTi"; + } + .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=FangSong]::before, + .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=FangSong]::before { + content: "仿宋"; + font-family: "FangSong"; + } + .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Arial]::before, + .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Arial]::before { + content: "Arial"; + font-family: "Arial"; + } + .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Times-New-Roman]::before, + .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Times-New-Roman]::before { + content: "Times New Roman"; + font-family: "Times New Roman"; + } + .ql-snow .ql-picker.ql-font .ql-picker-label[data-value=sans-serif]::before, + .ql-snow .ql-picker.ql-font .ql-picker-item[data-value=sans-serif]::before { + content: "sans-serif"; + font-family: "sans-serif"; + } + + .ql-font-SimSun { + font-family: "SimSun"; + } + .ql-font-SimHei { + font-family: "SimHei"; + } + .ql-font-Microsoft-YaHei { + font-family: "Microsoft YaHei"; + } + .ql-font-KaiTi { + font-family: "KaiTi"; + } + .ql-font-FangSong { + font-family: "FangSong"; + } + .ql-font-Arial { + font-family: "Arial"; + } + .ql-font-Times-New-Roman { + font-family: "Times New Roman"; + } + .ql-font-sans-serif { + font-family: "sans-serif"; + } + + .ql-snow .ql-picker.ql-font .ql-picker-label::before, + .ql-snow .ql-picker.ql-font .ql-picker-item::before { + content: "微软雅黑"; + font-family: "Microsoft YaHei"; + } } \ No newline at end of file diff --git a/public/react/src/constants/index.js b/public/react/src/constants/index.js index 3e7aceab0..e9401e76e 100644 --- a/public/react/src/constants/index.js +++ b/public/react/src/constants/index.js @@ -3,8 +3,8 @@ * @Author: tangjiang * @Github: * @Date: 2019-11-20 23:10:48 - * @LastEditors: tangjiang - * @LastEditTime: 2019-12-06 15:53:27 + * @LastEditors : tangjiang + * @LastEditTime : 2020-01-02 14:57:02 */ const CONST = { jcLabel: { @@ -12,9 +12,11 @@ const CONST = { language: '编程语言', description: '描述', difficult: '难易度', - category: '分类', + category: '课程', openOrNot: '公开程序', - timeLimit: '时间限制' + timeLimit: '时间限制', + knowledge: '知识点', + sub_discipline_id: '课程' }, fontSetting: { title: '代码格式', diff --git a/public/react/src/modules/courses/Resource/Fileslistitem.js b/public/react/src/modules/courses/Resource/Fileslistitem.js index 99888e558..e7e31baab 100644 --- a/public/react/src/modules/courses/Resource/Fileslistitem.js +++ b/public/react/src/modules/courses/Resource/Fileslistitem.js @@ -253,22 +253,22 @@ class Fileslistitem extends Component{ {discussMessage.is_publish===false?:""} {this.props.isAdmin? - this.eventStop(event)}> - + this.eventStop(event)}> + this.settingList()}>设置 :""} {this.props.isStudent===true&&this.props.current_user.login===discussMessage.author.login? - this.eventStop(event)}> + this.eventStop(event)}> - + this.settingList()}>设置 - + this.onDelete(discussMessage.id)}>删除 @@ -329,7 +329,7 @@ class Fileslistitem extends Component{ { discussMessage.publish_time===null?"":discussMessage.is_publish===true?moment(discussMessage.publish_time).fromNow():moment(discussMessage.publish_time).format('YYYY-MM-DD HH:mm')} - {discussMessage&&discussMessage.category_name===null?"":this.props.child===false?
所属目录:{discussMessage&&discussMessage.category_name}
:""}

diff --git a/public/react/src/modules/courses/coursesPublic/SelectSetting.js b/public/react/src/modules/courses/coursesPublic/SelectSetting.js index 1e177be3d..d96ae6f78 100644 --- a/public/react/src/modules/courses/coursesPublic/SelectSetting.js +++ b/public/react/src/modules/courses/coursesPublic/SelectSetting.js @@ -106,12 +106,11 @@ class Selectsetting extends Component{ savecouseShixunModal=()=>{ let {fileList,is_public,description,datatime,Radiovalue}=this.state; - let newfileList=[]; if(fileList!=undefined&&fileList.length>0){ - for(var list of fileList){ + fileList.map((list,key)=>{ newfileList.push(list.response.id) - } + }) } diff --git a/public/react/src/modules/courses/coursesPublic/sendResource.js b/public/react/src/modules/courses/coursesPublic/sendResource.js index d1e935db9..d813b8e69 100644 --- a/public/react/src/modules/courses/coursesPublic/sendResource.js +++ b/public/react/src/modules/courses/coursesPublic/sendResource.js @@ -131,9 +131,9 @@ class Sendresource extends Component{ let newfileList=[]; if(fileList!=undefined&&fileList.length>0){ - for(var list of fileList){ + fileList.map((list,key)=>{ newfileList.push(list.response.id) - } + }) } diff --git a/public/react/src/modules/courses/shixunHomework/ShixunWorkReport.js b/public/react/src/modules/courses/shixunHomework/ShixunWorkReport.js index 90e6e6a4f..3765b2932 100644 --- a/public/react/src/modules/courses/shixunHomework/ShixunWorkReport.js +++ b/public/react/src/modules/courses/shixunHomework/ShixunWorkReport.js @@ -370,11 +370,11 @@ class ShixunWorkReport extends Component { {this.props.isAdmin() ?this.confirmysl(`/student_works/${homeworkid}/export_shixun_work_report.pdf`)} - > 导出实训报告数据: + > 导出实训报告: parseInt(this.props&&this.props.user.user_id)===parseInt(data&&data.user_id)?this.confirmysl(`/student_works/${homeworkid}/export_shixun_work_report.pdf`)} - > 导出实训报告数据:"" + > 导出实训报告:"" } {/*{this.props.isAdmin() ?work_comment_hidden===true? "":
-

{data&&data.username}

+

{data&&data.username}

-

- -

通过关卡: {data&&data.complete_count}/{data&&data.challenges_count}
+

+ +

当前完成关卡: {data&&data.complete_count}/{data&&data.challenges_count}
经验值: {data&&data.myself_experience}/{data&&data.total_experience}
-
课堂最高完成效率: {data&&data.max_efficiency===null?'--':data&&data.max_efficiency}
-
完成效率: {data&&data.efficiency===null?'--':data&&data.efficiency}
+
完成效率: {data&&data.efficiency===null?'--':data&&data.efficiency}
+
课堂最高完成效率: {data&&data.max_efficiency===null?'--':data&&data.max_efficiency}
通关时间: {data&&data.passed_time===null||data&&data.passed_time=== "--"?'--':moment(data&&data.passed_time).format('YYYY-MM-DD HH:mm')}
-
实战耗时: {data&&data.time_consuming===null?'--':data&&data.time_consuming}
+ {/*
实战耗时: {data&&data.time_consuming===null?'--':data&&data.time_consuming}
*/}

diff --git a/public/react/src/modules/courses/shixunHomework/Trainingjobsetting.js b/public/react/src/modules/courses/shixunHomework/Trainingjobsetting.js index d15e8eaab..2ce9c0e53 100644 --- a/public/react/src/modules/courses/shixunHomework/Trainingjobsetting.js +++ b/public/react/src/modules/courses/shixunHomework/Trainingjobsetting.js @@ -2671,14 +2671,14 @@ class Trainingjobsetting extends Component { className="ml15 font-14" style={{textAlign: "left", color: "#FF8204"}}>(总分值 = 效率分+ 关卡任务总分)
-
-
+
+
效率分(选中,则学生最终成绩包含效率分) + className={"font-13 color-grey-c ml15"} + style={{textAlign: "left"}}>(选中,则学生最终成绩包含效率分。效率分在作业截止或者补交结束后由系统自动生成)
@@ -2686,7 +2686,7 @@ class Trainingjobsetting extends Component {
-
+

diff --git a/public/react/src/modules/courses/shixunHomework/shixunreport/ConclusionEvaluation.js b/public/react/src/modules/courses/shixunHomework/shixunreport/ConclusionEvaluation.js index 8d678f335..aa2da8594 100644 --- a/public/react/src/modules/courses/shixunHomework/shixunreport/ConclusionEvaluation.js +++ b/public/react/src/modules/courses/shixunHomework/shixunreport/ConclusionEvaluation.js @@ -66,7 +66,7 @@ class ConclusionEvaluation extends Component { ), },{ - title: '最终成绩', + title: '当前成绩', dataIndex: 'grade', key: 'grade', render: (text, record) => ( @@ -81,7 +81,7 @@ class ConclusionEvaluation extends Component { ), }, { - title: '实战耗时', + title: '实战总耗时', key: 'elapsed', dataIndex: 'elapsed', diff --git a/public/react/src/modules/developer/components/knowledge/index.js b/public/react/src/modules/developer/components/knowledge/index.js new file mode 100644 index 000000000..b287a42c9 --- /dev/null +++ b/public/react/src/modules/developer/components/knowledge/index.js @@ -0,0 +1,127 @@ +/* + * @Description: 知识点 + * @Author: tangjiang + * @Github: + * @Date: 2019-12-30 13:51:19 + * @LastEditors : tangjiang + * @LastEditTime : 2020-01-03 09:32:24 + */ +import './index.scss'; +import React, { useState, useEffect } from 'react'; +import { Select, notification } from 'antd'; + +const { Option } = Select; + +function KnowLedge (props) { + + const { + options = [], // 下拉选项 + values = [], // 已选择的下拉项 + onChange // 获取选择的值 + } = props; + + useEffect(() => { + const _options = []; + const _selects = []; + options.forEach(opt => { + if (!values.includes(opt.id)) { + _options.push(opt); + } else { + _selects.push(opt); + } + }); + setSelectOptions(_options || []); + setSelectValue(_selects || []); + }, [props]); + + // 显示的下拉项 + const [selectOptions, setSelectOptions] = useState(options); + // 已选择的下拉项 + const [selectValue, setSelectValue] = useState([]); + // + const [value] = useState([]); + + // 渲染下拉选项 + const renderOptions = (options = []) => { + return options.map((opt, i) => ( + + )); + } + // 过滤下拉列表项 + const handleSelectChange = (value) => { + value = +value.join(''); + const tempArr = [...selectValue]; + const _result = selectOptions.filter(item => { + if (item.id === value && tempArr.findIndex(t => t.id === value) === -1) { + tempArr.push(item); + } + return item.id !== value; + }); + if (tempArr.length > 50) { + notification.warning({ + message: '提示', + description: '知识点不能超过50个' + }); + return; + } + setSelectValue(tempArr); + setSelectOptions(_result); + // 将选择值返回 + onChange && onChange(tempArr); + } + + // 删除 + const handleRemoveResult = (item) => { + // console.log('点击了删除按钮===>>>>', item); + // 将删除的值重新加入到列表中 + const tempOptions = [...selectOptions]; + const tempValue = selectValue.filter(t => t.id !== item.id); + // console.log(selectValue); + tempOptions.push(item); + setSelectOptions(tempOptions); + setSelectValue(tempValue); + // 将选择值返回 + onChange && onChange(tempValue); + } + + // 渲染下拉结果 + const renderResult = (arrs) => { + return arrs.map((item) => ( + + {item.name} + handleRemoveResult(item)} + className="iconfont icon-roundclose knowledge-close" + > + + )); + } + // 渲染下拉列表 + const renderSelect = (options = []) => { + // console.log('+++++', options); + // setSelectValue(_selects); + return ( + + ) + } + + return ( +

+ { renderSelect(selectOptions) } + {/* 渲染下拉选择项 */} +
+ { renderResult(selectValue) } +
+
+ ); +} + +export default KnowLedge; diff --git a/public/react/src/modules/developer/components/knowledge/index.scss b/public/react/src/modules/developer/components/knowledge/index.scss new file mode 100644 index 000000000..5bd0f5f61 --- /dev/null +++ b/public/react/src/modules/developer/components/knowledge/index.scss @@ -0,0 +1,42 @@ +.knowledge-select-area{ + .ant-select-selection__rendered{ + margin-bottom: 0 !important; + } + .ant-select-search--inline{ + margin-left: 5px; + margin-top: 2px; + } + + .knowledge-result{ + display: flex; + flex-direction: row; + flex-wrap: wrap; + // margin-top: 15px; + + .knowledge-item{ + position: relative; + border: 1px solid #DDDDDD; + border-radius: 3px; + padding: 10px; + background: #fff; + margin-right: 10px; + margin-top: 10px; + // margin-bottom: 10px; + + .knowledge-close{ + display: none; + position: absolute; + right: -10px; + top: -10px; + background-color: rgba(250,250,250,1); + cursor: pointer; + } + + &:hover{ + .knowledge-close{ + display: block; + } + } + } + } +} diff --git a/public/react/src/modules/developer/components/myMonacoEditor/index.js b/public/react/src/modules/developer/components/myMonacoEditor/index.js index b37770328..930561e37 100644 --- a/public/react/src/modules/developer/components/myMonacoEditor/index.js +++ b/public/react/src/modules/developer/components/myMonacoEditor/index.js @@ -4,7 +4,7 @@ * @Github: * @Date: 2019-11-27 15:02:52 * @LastEditors : tangjiang - * @LastEditTime : 2019-12-27 20:49:46 + * @LastEditTime : 2020-01-02 13:59:38 */ import './index.scss'; import React, { useState, useRef, useEffect } from 'react'; diff --git a/public/react/src/modules/developer/newOrEditTask/index.js b/public/react/src/modules/developer/newOrEditTask/index.js index 83200beac..eb894d1d0 100644 --- a/public/react/src/modules/developer/newOrEditTask/index.js +++ b/public/react/src/modules/developer/newOrEditTask/index.js @@ -35,6 +35,7 @@ const NewOrEditTask = (props) => { getUserInfoForNew, handleCancelPublish, validateOjForm, + getQuestion // updateTestAndValidate, } = props; @@ -53,6 +54,10 @@ const NewOrEditTask = (props) => { useEffect(() => { // 获取用户信息 getUserInfoForNew(); + // 获取课程列表 + getQuestion({ + source: 'question' + }); // console.log('获取路由参数: ====', props.match.params); const id = props.match.params.id; // 保存OJForm的id号,指明是编辑还是新增 @@ -273,7 +278,8 @@ const mapDispatchToProps = (dispatch) => ({ startProgramQuestion: (id, props) => dispatch(actions.startProgramQuestion(id, props)), // 新建时获取信息 getUserInfoForNew: () => dispatch(actions.getUserInfoForNew()), - validateOjForm: (props, type, cb) => dispatch(actions.validateOjForm(props, type, cb)) + validateOjForm: (props, type, cb) => dispatch(actions.validateOjForm(props, type, cb)), + getQuestion: (params) => dispatch(actions.getQuestion(params)) }); export default withRouter(connect( diff --git a/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index.js b/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index.js index d4fc12b9c..9c98e4b01 100644 --- a/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index.js +++ b/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index.js @@ -4,12 +4,12 @@ * @Github: * @Date: 2019-11-20 10:35:40 * @LastEditors : tangjiang - * @LastEditTime : 2019-12-27 14:30:55 + * @LastEditTime : 2020-01-03 09:20:12 */ import './index.scss'; // import 'katex/dist/katex.css'; import React from 'react'; -import { Form, Input, Select, InputNumber, Button } from 'antd'; +import { Form, Input, Select, InputNumber, Button, Cascader, notification } from 'antd'; import { connect } from 'react-redux'; import AddTestDemo from './AddTestDemo'; // import QuillEditor from '../../../quillEditor'; @@ -18,6 +18,7 @@ import CONST from '../../../../../constants'; import { toStore } from 'educoder'; // 保存和读取store值 // import Wrapper from '../../../../../common/reactQuill'; import QuillForEditor from '../../../../../common/quillForEditor'; +import KnowLedge from '../../../components/knowledge'; const scrollIntoView = require('scroll-into-view'); const {jcLabel} = CONST; const FormItem = Form.Item; @@ -59,7 +60,9 @@ class EditTab extends React.Component { scrollHeight: 0, // 滚动元素的高度 top: 500, bottom: 20, - offsetTop: 0 + offsetTop: 0, + // knowledges: [], + // coursers: [] // 选中的课程 } } @@ -77,9 +80,17 @@ class EditTab extends React.Component { }, () => { this.state.scrollEl.addEventListener('scroll', this.handleScroll, false); }); + // 获取题库 + // this.props.getQuestion({ + // source: 'question' + // }); } - componentWillUnmount () { + // componentDidUpdate (nextProp) { + // console.log(nextProp); + // } + + componentWillUnmount (nextPro) { this.state.scrollEl.removeEventListener('scroll', this.handleScroll, false); } @@ -125,9 +136,26 @@ class EditTab extends React.Component { handleTimeLimitChange = (value) => { this.props.validateOjTimeLimit(value); } - // 改变分类 - handleChangeCategory = (value) => { - this.props.validateOjCategory(value); + // 改变方向 + handleChangeSubDisciplineId = (value) => { + // 课程下拉值变化时, 同步更新知识点 + const { courseQuestions, saveKnowledge } = this.props; + saveKnowledge([]); + // 获取当前分类下的知识点 + courseQuestions.forEach(item => { + if (value[0] && item.id === value[0]) { + item.sub_disciplines && item.sub_disciplines.forEach(c => { + if (value[1] && c.id === value[1]) { + saveKnowledge(c.tag_disciplines) + console.log(c.tag_disciplines); + } else if (!value[1]) { + saveKnowledge([]); + } + }); + } + }); + // this.props.validateOjCategory(value[1] || ''); + this.props.validateOjSubDisciplineId(value[1] || ''); } // 改变公开程序 handleChangeOpenOrNot = (value) => { @@ -146,13 +174,18 @@ class EditTab extends React.Component { addTestCase, // 添加测试用例 deleteTestCase, // 删除测试用例 testCasesValidate, - openTestCodeIndex = [] + openTestCodeIndex = [], + courseQuestions, + tag_discipline_id, + knowledges } = this.props; + console.log('knowledge======>>>>>>', knowledges); + // const {knowledges} = this.state; // 表单label - const myLabel = (name, subTitle) => { + const myLabel = (name, subTitle, nostar) => { if (subTitle) { return ( - + {name} ({subTitle}) @@ -161,7 +194,7 @@ class EditTab extends React.Component { ) } else { return ( - {name} + {name} ) } }; @@ -197,7 +230,14 @@ class EditTab extends React.Component { }; // 添加测试用例 const handleAddTest = () => { - const {position} = this.props; + const {position, testCases = []} = this.props; + if (testCases.length >= 50) { + notification.warning({ + message: '提示', + description: '测试用例不能超过50个' + }); + return; + } const obj = { // 测试用例参数 input: '', output: '', @@ -235,15 +275,74 @@ class EditTab extends React.Component { } // 编辑器配置信息 const quillConfig = [ - [{ header: [1, 2, 3, 4, 5, 6, false] }], - ['bold', 'italic', 'underline', 'strike'], // 切换按钮 - ['blockquote', 'code-block'], // 代码块 - [{align: []}, { 'list': 'ordered' }, { 'list': 'bullet' }], // 列表 - [{ 'script': 'sub'}, { 'script': 'super' }], - [{ 'color': [] }, { 'background': [] }], // 字体颜色与背景色 - ['image', 'formula'], // 数学公式、图片、视频 - ['clean'], // 清除格式 + { header: 1}, {header: 2}, + // {size: ['12px', '14px', '16px', '18px', '20px', false]}, + 'bold', 'italic', 'underline', 'strike', // 切换按钮 + 'blockquote', 'code-block', // 代码块 + {align: []}, { 'list': 'ordered' }, { 'list': 'bullet' }, // 列表 + { 'script': 'sub'}, { 'script': 'super' }, + { 'color': [] }, { 'background': [] }, // 字体颜色与背景色 + // {font: []}, + 'image', 'formula', // 数学公式、图片、视频 + 'clean', // 清除格式 ]; + + const renderCourseQuestion = (arrs) => { + const tempArr = []; + const sub_id = this.props.ojForm.sub_discipline_id; + function loop (arrs, tempArr) { + arrs.forEach(item => { + const obj = {}; + obj.value = item.id; + obj.label = item.name; + // 当item下还有子元素时,递归调用 + if (item.sub_disciplines) { + arrs = item.sub_disciplines; + obj.children = []; + loop(arrs, obj.children); + } + tempArr.push(obj); + }); + } + loop(arrs, tempArr); + + // 获取选中的下拉值 + let choid_ids = []; + // let tempKnowledges = []; + tempArr.forEach(t => { + // debugger; + if (sub_id && t.children) { + t.children.forEach(c => { + if (c.value === sub_id) { + choid_ids = [t.value, c.value]; + // tempKnowledges = c.children || []; + } + }); + } + }); + + console.log(choid_ids); + return ( + + ) + } + + // 知识点 + const handleKnowledgeChange = (values= []) => { + const _result = []; + values.forEach(v => { + _result.push(v.id); + }); + console.log('下拉选择的值:===>>>', _result); + // 保存选择的知识点 + this.props.saveTagDisciplineId(_result); + } return (
@@ -262,16 +361,34 @@ class EditTab extends React.Component { {myLabel(jcLabel['category'], '合理的分类有利于快速检索')}} - validateStatus={ojFormValidate.category.validateStatus} - help={ojFormValidate.category.errMsg} + label={{myLabel(jcLabel['sub_discipline_id'], '合理的课程分类有利于快速检索')}} + validateStatus={ojFormValidate.sub_discipline_id.validateStatus} + help={ojFormValidate.sub_discipline_id.errMsg} colon={ false } > - {getOptions('category')} - + */} + {/* */} + { renderCourseQuestion(courseQuestions)} + {myLabel(jcLabel['knowledge'], '', 'nostar')}} + > + + + {myLabel(jcLabel['timeLimit'], '程序允许时间限制时长,单位:秒')}} @@ -317,16 +434,13 @@ class EditTab extends React.Component { help={ojFormValidate.description.errMsg} colon={ false } > -
- -
-
{/* { testCases, openTestCodeIndex, testCasesValidate, - ojFormValidate} = ojFormReducer; + ojFormValidate, + courseQuestions, + tag_discipline_id, + knowledges + } = ojFormReducer; return { ojForm, testCases, testCasesValidate, ojFormValidate, position, - openTestCodeIndex + openTestCodeIndex, + courseQuestions, + tag_discipline_id, + knowledges }; }; @@ -386,10 +507,15 @@ const mapDispatchToProps = (dispatch) => ({ validateOjTimeLimit: (value) => dispatch(actions.validateOjTimeLimit(value)), validateOjCategory: (value) => dispatch(actions.validateOjCategory(value)), validateOpenOrNot: (value) => dispatch(actions.validateOpenOrNot(value)), + validateOjSubDisciplineId: (value) => dispatch(actions.validateOjSubDisciplineId(value)), + saveTagDisciplineId: (value) => dispatch(actions.saveTagDisciplineId(value)), // 新增测试用例 addTestCase: (value) => dispatch(actions.addTestCase(value)), // 删除测试用例 deleteTestCase: (value) => dispatch(actions.deleteTestCase(value)), + saveKnowledge: (value) => dispatch(actions.saveKnowledge(value)) + // 获取题库 + // getQuestion: (params) => dispatch(actions.getQuestion(params)) }); export default connect( diff --git a/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index.scss b/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index.scss index 723678bb3..ff5df7090 100644 --- a/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index.scss +++ b/public/react/src/modules/developer/newOrEditTask/leftpane/editorTab/index.scss @@ -18,6 +18,12 @@ line-height: 1; content: '*'; } + + &.nostar{ + &::before { + content: '' + } + } } .input_area{ display: inline-block; diff --git a/public/react/src/modules/developer/recordDetail/index.js b/public/react/src/modules/developer/recordDetail/index.js index fd93b3f2a..6675f066d 100644 --- a/public/react/src/modules/developer/recordDetail/index.js +++ b/public/react/src/modules/developer/recordDetail/index.js @@ -4,7 +4,7 @@ * @Github: * @Date: 2019-12-04 08:36:21 * @LastEditors : tangjiang - * @LastEditTime : 2019-12-27 21:18:39 + * @LastEditTime : 2020-01-02 13:48:02 */ import './index.scss'; import React, { useState, useEffect } from 'react'; @@ -57,7 +57,9 @@ function RecordDetail (props) { const handleReturn = (identifier) => { if (identifier) { saveEditorCodeForDetail(''); - props.history.push(`/myproblems/${identifier}`); + setTimeout(() => { + props.history.push(`/myproblems/${identifier}`); + }, 300); } } diff --git a/public/react/src/modules/developer/split_pane_resizer.scss b/public/react/src/modules/developer/split_pane_resizer.scss index 8fc8b525f..8d4ad171c 100644 --- a/public/react/src/modules/developer/split_pane_resizer.scss +++ b/public/react/src/modules/developer/split_pane_resizer.scss @@ -23,6 +23,8 @@ } .header_title{ + font-size: 16px; + font-weight: bold; text-align: center; } @@ -82,6 +84,8 @@ bottom: 0; top: 0; text-align: center; + font-size: 16px; + font-weight: bold; } } } @@ -158,6 +162,22 @@ -moz-background-clip: padding; -webkit-background-clip: padding; background-clip: padding-box; + + // &::before{ + // position: absolute; + // width: 24px; + // height: 24px; + // border-radius: 50%; + // margin-top: -12px; + // top: 50%; + // right: -12px; + // font-family: 'iconfont'; + // background: gold; + // content: '\e711'; + // font-size: 18px; + // text-align: center; + // line-height: 24px; + // } } .Resizer:hover { @@ -196,4 +216,4 @@ } .Resizer.disabled:hover { border-color: transparent; -} \ No newline at end of file +} diff --git a/public/react/src/modules/developer/studentStudy/leftpane/index.js b/public/react/src/modules/developer/studentStudy/leftpane/index.js index ac8d89321..88c5ee9a0 100644 --- a/public/react/src/modules/developer/studentStudy/leftpane/index.js +++ b/public/react/src/modules/developer/studentStudy/leftpane/index.js @@ -4,7 +4,7 @@ * @Github: * @Date: 2019-11-23 11:33:41 * @LastEditors : tangjiang - * @LastEditTime : 2019-12-27 16:03:04 + * @LastEditTime : 2020-01-02 13:51:22 // */ import './index.scss'; import React, { useState, useEffect, useMemo } from 'react'; @@ -50,8 +50,6 @@ const LeftPane = (props) => { record: (), comment: () }; - - console.log('======>>>>>>>', props); useEffect(() => { setDefaultActiveKey(userCodeTab); diff --git a/public/react/src/modules/developer/studentStudy/rightpane/index.js b/public/react/src/modules/developer/studentStudy/rightpane/index.js index ed500fb3a..bebaea1e8 100644 --- a/public/react/src/modules/developer/studentStudy/rightpane/index.js +++ b/public/react/src/modules/developer/studentStudy/rightpane/index.js @@ -4,7 +4,7 @@ * @Github: * @Date: 2019-11-27 14:59:51 * @LastEditors : tangjiang - * @LastEditTime : 2019-12-27 19:23:46 + * @LastEditTime : 2020-01-02 14:23:43 */ import React, { useState, useEffect } from 'react'; import {connect} from 'react-redux'; @@ -39,7 +39,7 @@ const RightPane = (props) => { changeLoadingState } = props; - const [editorCode, setEditorCode] = useState(editor_code || hack.code); + // const [editorCode, setEditorCode] = useState(editor_code || hack.code); const [noteClazz, setNoteClazz] = useState('editor_nodte_area'); const [noteCount] = useState(5000); // const [code, setCode] = useState(editor_code || hack.code); @@ -65,8 +65,9 @@ const RightPane = (props) => { let timer = null; // 定时器 // 代码块内容变化时 const handleCodeChange = (value) => { + // console.log('编辑器代码 ======》》》》》》》》》++++++++++', value); saveUserInputCode(value); - setEditorCode(value); + // setEditorCode(value); if (!timer) { timer = setInterval(function () { clearInterval(timer); @@ -116,6 +117,7 @@ const RightPane = (props) => { const { getFieldDecorator } = props.form; return (
+ { }); // console.log('+++', userCode); fetchUpdateCode(identifier, { - code: userCode + code: Base64.encode(userCode) }).then(res => { if (res.data.status === 401) { return; }; - dispatch({ - type: types.RESTORE_INITIAL_CODE, - payload: userCode - }); - + // dispatch({ + // type: types.RESTORE_INITIAL_CODE, + // payload: userCode + // }); setTimeout(() => { dispatch({ type: types.AUTO_UPDATE_CODE, @@ -342,7 +341,7 @@ export const getUserCommitRecord = (identifier) => { export const getUserCommitRecordDetail = (identifier) => { return (dispatch) => { fetchUserCommitRecordDetail(identifier).then(res => { - console.log('提交记录详情======》》》》', res); + // console.log('提交记录详情======》》》》', res); const { data } = res; if (data.status === 401) return; dispatch({ diff --git a/public/react/src/redux/actions/ojForm.js b/public/react/src/redux/actions/ojForm.js index 118295802..539798ff4 100644 --- a/public/react/src/redux/actions/ojForm.js +++ b/public/react/src/redux/actions/ojForm.js @@ -4,7 +4,7 @@ * @Github: * @Date: 2019-11-20 16:35:46 * @LastEditors : tangjiang - * @LastEditTime : 2019-12-27 22:19:15 + * @LastEditTime : 2020-01-02 17:38:12 */ import types from './actionTypes'; import CONST from '../../constants'; @@ -12,7 +12,8 @@ import { fetchPostOjForm, fetchGetOjById, publishTask, - cancelPublicTask + cancelPublicTask, + fetchQuestion } from '../../services/ojService'; import { Base64 } from 'js-base64'; import { notification } from 'antd'; @@ -45,6 +46,10 @@ const maps = { label: jcLabel['category'], type: types.VALIDATE_OJ_CATEGORY }, + sub_discipline_id: { + label: jcLabel['sub_discipline_id'], + type: types.VALIDATE_OJ_SUB_DISCIPLINE_ID + }, openOrNot: { label: jcLabel['openOrNot'], type: types.VALIDATE_OJ_OPENORNOT @@ -105,21 +110,24 @@ export const validateOjForm = (props, type, cb) => { const {ojForm, testCases, identifier, code } = getState().ojFormReducer; // console.log('code', code); /** 表单验证开始 */ - let keys = Object.keys(ojForm); + // let keys = Object.keys(ojForm).filter(k => k !== ''); + let keys = Object.keys(ojForm) // 循环判断每个字段是否为空 let hasSuccess = true; keys.forEach(key => { - const value = ojForm[key]; - const validateResult = emptyValidate(key, value); - const errMsg = validateResult[key].errMsg; - if (errMsg) { - hasSuccess = false; - dispatch( - { - type: maps[key].type, - payload: payloadInfo(key, value, errMsg, validateResult[key]) - } - ) + if (!['category'].includes(key)) { + const value = ojForm[key]; + const validateResult = emptyValidate(key, value); + const errMsg = validateResult[key].errMsg; + if (errMsg) { + hasSuccess = false; + dispatch( + { + type: maps[key].type, + payload: payloadInfo(key, value, errMsg, validateResult[key]) + } + ) + } } }); // 验证测试用例中的数组是否都有对应的值 @@ -197,8 +205,8 @@ export const validateOjForm = (props, type, cb) => { if (hasSuccess) { // console.log('表单保存的数据为: ', getState()); const {ojFormReducer} = getState(); - const {code, score, ojForm, testCases = []} = ojFormReducer; - const {category, description, difficult, language, name, openOrNot, timeLimit} = ojForm; + const {code, score, ojForm, testCases = [], tag_discipline_id = []} = ojFormReducer; + const {category, description, difficult, language, name, openOrNot, timeLimit, sub_discipline_id} = ojForm; let paramsObj = {}; const hack = { // 编程题干 name, @@ -207,6 +215,8 @@ export const validateOjForm = (props, type, cb) => { category, 'open_or_not': openOrNot, 'time_limit': timeLimit, + sub_discipline_id, + // tag_discipline_id, score }; @@ -223,7 +233,8 @@ export const validateOjForm = (props, type, cb) => { paramsObj['params'] = { hack, hack_sets: tempTc, - hack_codes + hack_codes, + tags: tag_discipline_id } paramsObj['submitType'] = 'add'; } else { // 存在时调用更新接口 @@ -242,7 +253,8 @@ export const validateOjForm = (props, type, cb) => { hack, hack_sets, hack_codes, - update_hack_sets + update_hack_sets, + tags: tag_discipline_id } paramsObj['submitType'] = 'update'; paramsObj['identifier'] = identifier; @@ -483,6 +495,15 @@ export const validateOjCategory = (value) => { payload: payloadInfo('category', value, errMsg, validate) } }; +// 验证方向 +export const validateOjSubDisciplineId = (value) => { + const validate = emptyValidate('sub_discipline_id', value)['sub_discipline_id']; + const errMsg = validate.errMsg; + return { + type: types.VALIDATE_OJ_SUB_DISCIPLINE_ID, + payload: payloadInfo('sub_discipline_id', value, errMsg, validate) + } +}; // 验证公开程序 export const validateOpenOrNot = (value) => { const validate = emptyValidate('openOrNot', value)['openOrNot']; @@ -492,6 +513,14 @@ export const validateOpenOrNot = (value) => { payload: payloadInfo('openOrNot', value, errMsg, validate) } }; +// 保存知识点 +export const saveTagDisciplineId = (value) => { + // console.log('====????????????', value); + return { + type: types.SAVE_TAG_DISCIPLINE_ID, + payload: value + }; +} // 新增测试用例 export const addTestCase = (obj) => { return { @@ -622,7 +651,6 @@ export const testCaseOutputChange = (value, index) => { // // 调试代码时,更改对应的状态值 // export const changeTestCodeStatus = () => { - // 更新测试用命及验证 export const updateTestAndValidate = (obj) => { return (dispatch) => { @@ -644,3 +672,25 @@ export const updateOpenTestCaseIndex = (value) => { payload: value } } + +// 获取课程题库 +export const getQuestion = (params) => { + return (dispatch) => { + fetchQuestion(params).then(res => { + const { data = {} } = res; + const { disciplines = [] } = data; + dispatch({ + type: types.GET_COURSE_QUESTION, + payload: disciplines + }) + }) + } +} + +// 保存所选择的知识点 +export const saveKnowledge = (values) => { + return { + type: types.CHANGE_KNOWLEDGES, + payload: values + } +} diff --git a/public/react/src/redux/reducers/ojForUserReducer.js b/public/react/src/redux/reducers/ojForUserReducer.js index 721c3e2cc..475d14d79 100644 --- a/public/react/src/redux/reducers/ojForUserReducer.js +++ b/public/react/src/redux/reducers/ojForUserReducer.js @@ -4,7 +4,7 @@ * @Github: * @Date: 2019-11-27 13:41:48 * @LastEditors : tangjiang - * @LastEditTime : 2019-12-27 21:28:28 + * @LastEditTime : 2020-01-02 14:24:09 */ import types from "../actions/actionTypes"; import { Base64 } from 'js-base64'; @@ -94,10 +94,11 @@ const ojForUserReducer = (state = initialState, action) => { pages: Object.assign({}, state.pages, { total: records_count }) } case types.SAVE_USER_CODE: - let curCode = Base64.encode(action.payload); + // console.log('save_user_code: ', action.payload); + // let curCode = Base64.encode(action.payload); return { ...state, - userCode: curCode, + userCode: action.payload, isUpdateCode: true, } case types.IS_UPDATE_CODE: @@ -136,7 +137,6 @@ const ojForUserReducer = (state = initialState, action) => { } else { curHack['code'] = ''; } - console.log(curHack); return { ...state, hack: Object.assign({}, state.hack, curHack), diff --git a/public/react/src/redux/reducers/ojFormReducer.js b/public/react/src/redux/reducers/ojFormReducer.js index 952919aca..d604a6156 100644 --- a/public/react/src/redux/reducers/ojFormReducer.js +++ b/public/react/src/redux/reducers/ojFormReducer.js @@ -4,7 +4,7 @@ * @Github: * @Date: 2019-11-20 16:40:32 * @LastEditors : tangjiang - * @LastEditTime : 2019-12-27 20:00:26 + * @LastEditTime : 2020-01-02 17:36:40 */ import { Base64 } from 'js-base64'; import types from '../actions/actionTypes'; @@ -15,10 +15,12 @@ const init = { language: '', description: '', difficult: '', - category: '', + sub_discipline_id: '', // 方向 + // category: '', // openOrNot: 1, timeLimit: 3 }, + tag_discipline_id: [], // 知识点 ojFormValidate: { name: { validateStatus: '', @@ -36,14 +38,18 @@ const init = { validateStatus: '', errMsg: '' }, - category: { - validateStatus: '', - errMsg: '' - }, + // category: { + // validateStatus: '', + // errMsg: '' + // }, // openOrNot: { // validateStatus: '', // errMsg: '' // }, + sub_discipline_id: { + validateStatus: '', + errMsg: '' + }, timeLimit: { validateStatus: '', errMsg: '' @@ -67,6 +73,8 @@ const init = { testCodeStatus: 'default', // 调试代码状态 default(默认值) | loading(加载中) | loaded(加载完成) | userCase(用户自定义测试用例) | finish(测试完成) openTestCodeIndex: [0], // 展开的测试用例: 数组, 当出错时,展开所有出错的测试用例, 默认展开第一个 isPublish: 0, // 是否是发布状态: 0 未发布 1 已发布 + courseQuestions: [], // 课程题库 + knowledges: [], // 知识点下拉值 } const tcValidateObj = { @@ -127,10 +135,17 @@ const ojFormReducer = (state = initialState, action) => { return returnState(state, ojForm, ojFormValidate); case types.VALIDATE_OJ_CATEGORY: return returnState(state, ojForm, ojFormValidate); + case types.VALIDATE_OJ_SUB_DISCIPLINE_ID: + return returnState(state, ojForm, ojFormValidate); case types.VALIDATE_OJ_OPENORNOT: return returnState(state, ojForm, ojFormValidate); case types.VALIDATE_OJ_TIMELIMIT: return returnState(state, ojForm, ojFormValidate); + case types.SAVE_TAG_DISCIPLINE_ID: + return { + ...state, + tag_discipline_id: action.payload + } case types.ADD_TEST_CASE: const { testCase, tcValidate } = action.payload; const tcArrs = state.testCases.concat([testCase]); @@ -174,7 +189,8 @@ const ojFormReducer = (state = initialState, action) => { * 6. 更改测试用例状态 * 7. 添加测试用例验证 */ - const { code = '', description, language, name, hack_sets = [], time_limit, difficult, category, status } = action.payload; + const { code = '', description, language, name, hack_sets = [], time_limit, difficult, category, status, sub_discipline_id, tag_discipline_id } = action.payload; + const { courseQuestions } = state; let desc = null; try { desc = JSON.parse(description) @@ -188,7 +204,8 @@ const ojFormReducer = (state = initialState, action) => { difficult, category, openOrNot: 1, - timeLimit: time_limit + timeLimit: time_limit, + sub_discipline_id }; // state.code = code; // 保存代码块值 let curPosition = 0; @@ -208,6 +225,18 @@ const ojFormReducer = (state = initialState, action) => { } else if (Array.isArray(code)) { cbcode = Base64.decode(code[code.length - 1]); } + + // console.log('++++>>>>>>>>>>>>>', courseQuestions); + let temp_knowledges = []; + courseQuestions.forEach(c => { + if (sub_discipline_id && c.sub_disciplines) { + c.sub_disciplines.forEach(sub => { + if (+sub.id === sub_discipline_id) { + temp_knowledges = sub.tag_disciplines || []; + } + }); + } + }); // state.position = curPosition; // 计算下一个测试用例的位置值 return { ...state, @@ -218,7 +247,9 @@ const ojFormReducer = (state = initialState, action) => { testCasesValidate: curTcValidates, testCodeStatus: hack_sets.length > 0 ? 'userCase' : 'default', isPublish: status, - showCode: cbcode + showCode: cbcode, + tag_discipline_id, + knowledges: temp_knowledges } case types.CHANGE_PUBLISH_VALUE: return { @@ -301,11 +332,22 @@ const ojFormReducer = (state = initialState, action) => { if (tIndex === -1) { tempArr.push(action.payload); } - console.log(tempArr); + // console.log(tempArr); return { ...state, openTestCodeIndex: tempArr } + case types.GET_COURSE_QUESTION: + return { + ...state, + courseQuestions: action.payload + } + case types.CHANGE_KNOWLEDGES: { + return { + ...state, + knowledges: action.payload + } + } default: return state; } diff --git a/public/react/src/services/ojService.js b/public/react/src/services/ojService.js index 04fde2224..59151e687 100644 --- a/public/react/src/services/ojService.js +++ b/public/react/src/services/ojService.js @@ -4,7 +4,7 @@ * @Github: * @Date: 2019-11-20 10:55:38 * @LastEditors : tangjiang - * @LastEditTime : 2019-12-27 11:06:27 + * @LastEditTime : 2019-12-30 09:44:56 */ import axios from 'axios'; @@ -142,4 +142,10 @@ export async function fetchUploadImageUrl (id) { export async function fetchAddNotes (identifier, params) { const url = `/myproblems/${identifier}/add_notes.json`; return axios.post(url, params); +} + +// 获取课程体系 +export async function fetchQuestion (params) { + const url = `/disciplines.json`; + return axios.get(url, { params }); } \ No newline at end of file diff --git a/spec/models/examination_item_analysis_spec.rb b/spec/models/examination_item_analysis_spec.rb new file mode 100644 index 000000000..2291c099c --- /dev/null +++ b/spec/models/examination_item_analysis_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe ExaminationItemAnalysis, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end