Merge branch 'develop' into dev_cxt2

dev_cs
cxt 5 years ago
commit b579bb9b13

1
.gitignore vendored

@ -37,6 +37,7 @@
/public/react/node_modules/
/public/react/config/stats.json
/public/react/stats.json
/public/react/.idea/*
/public/npm-debug.log

@ -1,5 +1,6 @@
function createMDEditor(element, opts){
var defaults = {
height: 600,
path: '/editormd/lib/',
syncScrolling: "single",
tex: true,

@ -5,6 +5,8 @@ class Admins::BaseController < ApplicationController
layout 'admin'
skip_before_action :verify_authenticity_token
before_action :require_login, :require_admin!
after_action :rebind_event_if_ajax_render_partial

@ -47,7 +47,7 @@ class ApplicationController < ActionController::Base
# 题库的访问权限
def bank_visit_auth
tip_exception("未通过职业认证") if current_user.is_teacher? && !current_user.certification_teacher? && !current_user.admin? && @bank.user_id != current_user.id && @bank.is_public
tip_exception(-2,"未通过职业认证") if current_user.is_teacher? && !current_user.certification_teacher? && !current_user.admin? && @bank.user_id != current_user.id && @bank.is_public
tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin? ||
(current_user.certification_teacher? && @bank.is_public)
end

@ -303,7 +303,7 @@ class CoursesController < ApplicationController
def destroy
if @course.is_delete == 0
@course.delete!
Tiding.create!(user_id: @course.tea_id, trigger_user_id: 1, container_id: @course.id,
Tiding.create!(user_id: @course.tea_id, trigger_user_id: 0, container_id: @course.id,
container_type: 'Course', tiding_type: 'Delete', extra: @course.name)
normal_status(0, "成功")
else

@ -1,7 +1,7 @@
#encoding: UTF-8
class ExerciseBanksController < ApplicationController
before_action :require_login
before_action :find_bank, except: [:choose_shixun]
before_action :find_bank, :bank_visit_auth, except: [:choose_shixun]
before_action :bank_admin, only: [:update]
before_action :commit_shixun_present, only: [:commit_shixun]

@ -67,13 +67,20 @@ class GamesController < ApplicationController
uri = "#{shixun_tomcat}/bridge/vnc/getvnc"
params = {tpiID: @myshixun.id, :containers => "#{Base64.urlsafe_encode64(shixun_container_limit(@shixun))}"}
res = uri_post uri, params
logger.info("###############---- ")
if res && res['code'].to_i != 0
raise("实训云平台繁忙繁忙等级99")
end
# 无域名版本
#@vnc_url = "http://#{service_host}:#{res['port']}/vnc_lite.html?password=headless"
# 有域名版本
@vnc_url = "https://#{res['port']}.#{service_host}/vnc_lite.html?password=headless"
if request.subdomain == "pre-newweb"
# 无域名版本
@vnc_url = "http://#{service_host}:#{res['port']}/vnc_lite.html?password=headless"
else
# 有域名版本
@vnc_url = "https://#{res['port']}.#{service_host}/vnc_lite.html?password=headless"
end
@vnc_evaluate = @shixun.vnc_evaluate
rescue Exception => e
Rails.logger.error(e.message)
end
@ -535,13 +542,14 @@ class GamesController < ApplicationController
end
testCases = Base64.urlsafe_encode64(testSet.to_json) unless testSet.blank?
# 评测类型: 012 用于webssh的评测 3用于vnc
podType = @shixun.vnc_evaluate ? 3 : @shixun.webssh
# 注意:这个地方的参数写的时候不能换行
content_modified = params[:content_modified] # 决定文件内容是否有修改有修改如果中间层pull没有更新则轮询等待更新
br_params = {:tpiID => "#{@myshixun.id}", :tpiGitURL => "#{gitUrl}", :buildID => "#{@game.id}",
:instanceChallenge => "#{step}", :testCases => "#{testCases}", :resubmit => "#{resubmit}",
:times => params[:first].to_i, :podType => @shixun.webssh, :content_modified => content_modified,
:times => params[:first].to_i, :podType => podType, :content_modified => content_modified,
:containers => "#{Base64.urlsafe_encode64(shixun_container_limit(@shixun))}",
:persistenceName => @shixun.identifier, :tpmScript => "#{tpmScript}", :sec_key => sec_key,
:timeLimit => game_challenge.exec_time, :isPublished => (@shixun.status < 2 ? 0 : 1) }

@ -1,6 +1,6 @@
class GtopicBanksController < ApplicationController
before_action :require_login
before_action :find_bank
before_action :find_bank, :bank_visit_auth
before_action :bank_admin, only: [:edit, :update]
def show

@ -0,0 +1,44 @@
class HelpsController < ApplicationController
before_action :require_login, only: [:feedback]
helper_method :current_help
def about
render_ok(content: current_help&.about_us)
end
def contact
@cooperations = Cooperation.all.group(:user_type)
end
def cooperatives
@data = { 'alliance_coop' => [], 'com_coop' => [], 'edu_coop' => [] }
@data = @data.merge CooImg.all.group_by(&:img_type)
end
def agreement
render_ok(content: current_help&.agreement)
end
def help_center
render_ok(content: current_help&.help_center)
end
def feedback
content = "<p>[#{params[:question_kind]}]</p><p>问题页面网址:#{params[:url]}</p>#{params[:description]}"
ActiveRecord::Base.transaction do
attr = { sender_id: User.current.id, receiver_id: 1, content: content, send_time: Time.now }
PrivateMessage.create!(attr.merge(user_id: User.current.id, target_id: 1, status: 1))
PrivateMessage.create!(attr.merge(user_id: 1, target_id: User.current.id, status: 0))
end
render_ok
end
private
def current_help
@_current_help ||= Help.first
end
end

@ -1,6 +1,6 @@
class HomeworkBanksController < ApplicationController
before_action :require_login
before_action :find_bank
before_action :find_bank, :bank_visit_auth
before_action :bank_params, only: [:update]
before_action :bank_admin, only: [:update, :destroy, :set_public]

@ -82,7 +82,9 @@ class HomeworkCommonsController < ApplicationController
end
@task_count = @homework_commons.size
@homework_commons = @homework_commons.order("position DESC").page(page).per(15)
order_str = @homework_type == 4 ? "position DESC" : "IF(ISNULL(homework_commons.publish_time),0,1), homework_commons.publish_time DESC,
homework_commons.created_at DESC"
@homework_commons = @homework_commons.order(order_str).page(page).per(15)
if @homework_type == 4
@homework_commons = @homework_commons.includes(:homework_detail_manual, :published_settings, :shixuns)

@ -266,7 +266,8 @@ class MyshixunsController < ApplicationController
:identifier => @sec_key, :exec_time => exec_time)
uid_logger("-- game build: file update #{@sec_key}, record id is #{record.id}, time is **** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}")
end
unless @hide_code
# 隐藏代码文件 和 VNC的都不需要走版本库
unless @hide_code || @myshixun.shixun&.vnc_evaluate
# 远程版本库文件内容
last_content = GitService.file_content(repo_path: @repo_path, path: path)["content"]
content = params[:content]

@ -55,7 +55,7 @@ class ProjectPackagesController < ApplicationController
package.destroy!
Tiding.create!(user_id: package.creator_id, trigger_user_id: 1, container_id: package.id,
Tiding.create!(user_id: package.creator_id, trigger_user_id: 0, container_id: package.id,
container_type: 'ProjectPackage', tiding_type: 'Destroyed', extra: package.title)
render_ok

@ -265,6 +265,7 @@ class QuestionBanksController < ApplicationController
# exercise.update_column(:quotes, exercise.quotes+1)
# end
new_exercise if new_exercise.save!
exercise.update_column(:quotes, exercise.quotes+1)
end
end
@ -292,6 +293,7 @@ class QuestionBanksController < ApplicationController
# poll.update_column(:quotes, poll.quotes+1)
# end
new_poll if new_poll.save!
poll.update_column(:quotes, poll.quotes+1)
end
end

@ -448,6 +448,7 @@ class ShixunsController < ApplicationController
ShixunMirrorRepository.create(:shixun_id => @shixun.id, :mirror_repository_id => mirror)
end
end
logger.info("#########shixun_params#{shixun_params}")
@shixun.update_attributes(shixun_params)
logger.info("##########shixun_info_params: #{shixun_info_params}")
logger.info("##########params[:shixun_info][:evaluate_script]: #{params[:shixun_info][:evaluate_script]}")
@ -931,7 +932,7 @@ private
raise("实训名称不能为空") if params[:shixun][:name].blank?
params.require(:shixun).permit(:name, :trainee, :webssh, :can_copy, :use_scope, :vnc, :test_set_permission,
:task_pass, :multi_webssh, :opening_time, :mirror_script_id, :code_hidden,
:hide_code, :forbid_copy)
:hide_code, :forbid_copy, :vnc_evaluate)
end
def shixun_info_params

@ -1,6 +1,6 @@
class TaskBanksController < ApplicationController
before_action :require_login
before_action :find_bank
before_action :find_bank, :bank_visit_auth
before_action :bank_visit_auth
before_action :bank_admin, only: [:update]
@ -35,14 +35,17 @@ class TaskBanksController < ApplicationController
def gtask_bank_params
tip_exception("name参数不能为空") if params[:gtask_bank][:name].blank?
tip_exception("description参数不能为空") if params[:gtask_bank][:description].blank?
if @bank.homework_type == 3
if @bank.task_type == 2
tip_exception("base_on_project参数不能为空") if params[:gtask_bank][:base_on_project].nil?
tip_exception("min_num参数不能为空") if params[:gtask_bank][:min_num].blank?
tip_exception("max_num参数不能为空") if params[:gtask_bank][:max_num].blank?
tip_exception("最小人数不能小于1") if params[:gtask_bank][:min_num].to_i < 1
tip_exception("最大人数不能小于最小人数") if params[:gtask_bank][:max_num].to_i < params[:gtask_bank][:min_num].to_i
end
params.require(:gtask_bank).permit(:name, :description) if @bank.task_type == 1
params.require(:gtask_bank).permit(:name, :description, :min_num, :max_num, :base_on_project) if @bank.task_type == 2
if @bank.task_type == 1
params.require(:gtask_bank).permit(:name, :description)
else
params.require(:gtask_bank).permit(:name, :description, :min_num, :max_num, :base_on_project)
end
end
end

@ -1,11 +1,12 @@
class Users::QuestionBanksController < Users::BaseController
before_action :require_login
before_action :private_user_resources!
skip_before_action :check_observed_user_exists!
# before_action :private_user_resources!
before_action :check_query_params!
before_action :check_user_permission!
def index
service = Users::QuestionBankService.new(observed_user, query_params)
service = Users::QuestionBankService.new(User.current, query_params)
question_banks = service.call
@count = question_banks.count
@ -30,7 +31,7 @@ class Users::QuestionBanksController < Users::BaseController
.where(commit_status: 1, exercises: { exercise_bank_id: question_bank_ids })
.group('exercises.exercise_bank_id').count
when 'poll' then
PollUser.joins(:poll).where(polls: { exercise_bank_id: question_bank_ids })
PollUser.joins(:poll).where(commit_status: 1, polls: { exercise_bank_id: question_bank_ids })
.group('polls.exercise_bank_id').count
when 'gtask' then
GraduationWork.has_committed.joins(:graduation_task)
@ -65,7 +66,7 @@ class Users::QuestionBanksController < Users::BaseController
def check_user_permission!
if params[:type] == 'publicly'
render_error("未通过职业认证") unless User.current.admin? || User.current.certification_teacher?
normal_status(-2,"未通过职业认证") unless User.current.admin? || User.current.certification_teacher?
else
render_forbidden unless User.current.admin? || User.current.is_teacher?
end

@ -1,6 +1,6 @@
class Wechats::JsSdkSignaturesController < ApplicationController
def create
timestamp = (Time.now.to_f * 1000).to_i
timestamp = Time.now.to_i
noncestr = ('A'..'z').to_a.sample(8).join
signature = Util::Wechat.js_sdk_signature(params[:url], noncestr, timestamp)

@ -86,7 +86,7 @@ module TidingDecorator
def teacher_join_course_content
name = Course.find_by(id: container_id)&.name
I18n.t(locale_format extra) % [user.show_real_name, name]
I18n.t(locale_format extra) % [trigger_user&.show_real_name, name]
end
def apply_add_department_content
@ -258,7 +258,7 @@ module TidingDecorator
def manager_join_project_content
project = Project.find_by(id: container_id)
I18n.t(locale_format(extra)) % [user&.show_real_name, project.name]
I18n.t(locale_format(extra)) % [trigger_user&.show_real_name, project.name]
end
def reporter_join_project_content

@ -7,7 +7,8 @@ module Util::Wechat
attr_accessor :appid, :secret
def js_sdk_signature(url, noncestr, timestamp)
str = { jsapi_ticket: jsapi_ticket, noncestr: noncestr, timestamp: timestamp, url: url }.to_query
data = { jsapi_ticket: jsapi_ticket, noncestr: noncestr, timestamp: timestamp, url: url }
str = data.map { |k, v| "#{k}=#{v}" }.join('&')
Digest::SHA1.hexdigest(str)
end

@ -4,7 +4,7 @@ class Attachment < ApplicationRecord
include Publishable
include Lockable
belongs_to :container, polymorphic: true, touch: true, optional: true
belongs_to :container, polymorphic: true, optional: true
belongs_to :author, class_name: "User", foreign_key: :author_id
belongs_to :course, foreign_key: :container_id, optional: true
has_many :attachment_group_settings, :dependent => :destroy

@ -337,7 +337,7 @@ class Course < ApplicationRecord
#创建课程后,给该用户发送消息
def send_tiding
self.tidings << Tiding.new(user_id: tea_id, trigger_user_id: 1, belong_container_id: id,
self.tidings << Tiding.new(user_id: tea_id, trigger_user_id: 0, belong_container_id: id,
belong_container_type: 'Course', tiding_type: 'System')
end

@ -28,7 +28,7 @@ class CourseMessage < ApplicationRecord
def send_deal_tiding deal_status
# 发送申请处理结果消息
Tiding.create!(
user_id: course_message_id, trigger_user_id: 1, container_id: course_id, container_type: 'DealCourse',
user_id: course_message_id, trigger_user_id: 0, container_id: course_id, container_type: 'DealCourse',
belong_container: course, extra: content.to_i == 2 ? '9' : '7', tiding_type: 'System', status: deal_status
)
# 将申请消息置为已处理

@ -1,7 +1,7 @@
class GtopicBank < ApplicationRecord
belongs_to :user
belongs_to :graduation_topic
belongs_to :course_list
belongs_to :graduation_topic, optional: true
belongs_to :course_list, optional: true
has_many :attachments, as: :container, dependent: :destroy
has_many :graduation_topics, dependent: :nullify

@ -24,7 +24,7 @@ module Searchable::Course
author_name: teacher&.real_name,
author_school_name: teacher&.school_name,
visits_count: visits,
members_count: members_count,
members_count: course_members_count,
is_public: is_public == 1,
first_category_url: ApplicationController.helpers.module_url(none_hidden_course_modules.first, self)
}

@ -5,6 +5,9 @@ class Shixun < ApplicationRecord
# hide_code 隐藏代码窗口
# code_hidden: 隐藏代码目录
# task_pass: 跳关
# webssh 0不开启webssh1开启练习模式; 2开启评测模式
# trainee 实训的难度
# vnc: VCN实训是否用于评测
has_many :challenges, -> {order("challenges.position asc")}, dependent: :destroy
has_many :challenge_tags, through: :challenges
has_many :myshixuns, :dependent => :destroy

@ -1,6 +1,6 @@
class UserExtension < ApplicationRecord
# identity 0: 教师教授 1: 学生, 2: 专业人士, 3: 开发者
enum identity: { teacher: 0, student: 1, professional: 2, developer: 3 }
enum identity: { teacher: 0, student: 1, professional: 2, developer: 3, cnmooc: 4, unselect: -1 }
belongs_to :user, touch: true
belongs_to :school, optional: true

@ -25,7 +25,7 @@ class Libraries::AgreeApplyService < ApplicationService
private
def notify_library_author!
Tiding.create!(user_id: library.user_id, trigger_user_id: 1,
Tiding.create!(user_id: library.user_id, trigger_user_id: 0,
container_id: library.id, container_type: 'Library',
tiding_type: 'System', status: 1)
end

@ -32,7 +32,7 @@ class Libraries::RefuseApplyService < ApplicationService
private
def notify_library_author!
Tiding.create!(user_id: library.user_id, trigger_user_id: 1,
Tiding.create!(user_id: library.user_id, trigger_user_id: 0,
container_id: library.id, container_type: 'Library',
tiding_type: 'System', status: 2, extra: library_apply.reason)
end

@ -29,7 +29,7 @@ class ProjectPackages::AgreeApplyService < ApplicationService
Tiding.where(container_id: package.id, container_type: 'ProjectPackage',
tiding_type: 'Apply', status: 0).update_all(status: 1)
Tiding.create!(user_id: package.creator_id, trigger_user_id: 1,
Tiding.create!(user_id: package.creator_id, trigger_user_id: 0,
container_id: package.id, container_type: 'ProjectPackage',
tiding_type: 'System', status: 1)
end

@ -16,7 +16,7 @@ class ProjectPackages::EndBiddingService < ApplicationService
private
def send_bidding_end_notify!
Tiding.create!(user_id: package.creator_id, trigger_user_id: 1,
Tiding.create!(user_id: package.creator_id, trigger_user_id: 0,
container_id: package.id, container_type: 'ProjectPackage', tiding_type: 'BiddingEnd')
end

@ -31,7 +31,7 @@ class ProjectPackages::RefuseApplyService < ApplicationService
Tiding.where(container_id: package.id, container_type: 'ProjectPackage',
tiding_type: 'Apply', status: 0).update_all(status: 1)
Tiding.create!(user_id: package.creator_id, trigger_user_id: 1,
Tiding.create!(user_id: package.creator_id, trigger_user_id: 0,
container_id: package.id, container_type: 'ProjectPackage',
tiding_type: 'System', status: 2, extra: apply.reason)
end

@ -71,7 +71,7 @@ class ProjectPackages::SaveService < ApplicationService
end
def send_create_notify!
Tiding.create!(user_id: package.creator_id, trigger_user_id: 1,
Tiding.create!(user_id: package.creator_id, trigger_user_id: 0,
container_id: package.id, container_type: 'ProjectPackage', tiding_type: 'Created')
end

@ -28,7 +28,7 @@ class Videos::AgreeApplyService < ApplicationService
private
def notify_video_author!
Tiding.create!(user_id: video.user_id, trigger_user_id: 1,
Tiding.create!(user_id: video.user_id, trigger_user_id: 0,
container_id: video.id, container_type: 'Video',
tiding_type: 'System', status: 1)
end

@ -31,7 +31,7 @@ class Videos::RefuseApplyService < ApplicationService
private
def notify_video_author!
Tiding.create!(user_id: video.user_id, trigger_user_id: 1,
Tiding.create!(user_id: video.user_id, trigger_user_id: 0,
container_id: video.id, container_type: 'Video',
tiding_type: 'System', status: 2, extra: video_apply.reason)
end

@ -83,6 +83,7 @@ a.edu-txt-w80,.edu-txt-w80{ width:80px; display: inline-block;text-align: center
.font-20{ font-size: 20px!important;}
.font-22{ font-size: 22px!important;}
.font-24{ font-size: 24px!important;}
.font-25{ font-size: 25px!important;}
.font-26{ font-size: 26px!important;}
.font-28{ font-size: 28px!important;}
.font-30{ font-size: 30px!important;}

@ -3,6 +3,7 @@ json.(@base_date, :st, :discusses_count, :game_count, :record_onsume_time, :prev
:shixun, :myshixun, :git_url)
if @shixun.vnc
json.vnc_url @vnc_url
json.vnc_evaluate @vnc_evaluate
end
json.user do
json.partial! 'users/user', user: @user

@ -0,0 +1,8 @@
json.contacts do
json.array! @cooperations.each do |item|
json.extract! item, :name, :qq, :mail
json.type item.user_type_text
end
end
json.address current_help.status

@ -0,0 +1,11 @@
json.data do
json.array! @data.each do |type, objs|
json.name I18n.t("enumerize.coo_img.img_type.#{type}")
json.values do
json.array! objs.sort_by(&:position).each do |obj|
json.img obj.url_states || (Util::FileManage.exist?('CooImg', obj.id) ? Util::FileManage.disk_file_url('CooImg', obj.id) : '')
json.url obj.src_states
end
end
end
end

@ -22,6 +22,7 @@ json.shixun do
json.hide_code @shixun.hide_code # 隐藏代码窗口
json.code_hidden @shixun.code_hidden # 代码目录隐藏
json.vnc @shixun.vnc
json.vnc_evaluate @shixun.vnc_evaluate
#json.exec_time @shixun.exec_time
json.webssh @shixun.webssh
json.multi_webssh @shixun.multi_webssh

@ -1,5 +1,6 @@
json.(@bank, :id, :name, :description, :task_type, :is_public)
# 附件
json.authorize @bank.user_id == current_user.id || current_user.admin?
json.attachments @bank_attachments do |attachment|
json.partial! "attachments/attachment_simple", locals: {attachment: attachment}
end

@ -18,7 +18,7 @@ json.time tiding.how_long_time
json.new_tiding tiding.unread?(@onclick_time)
json.trigger_user do
if tiding.trigger_user_id.zero?
if tiding.trigger_user_id.zero? || (tiding.trigger_user_id == 1 && tiding.tiding_type == 'System')
json.id 0
json.name "系统"
json.login ""

@ -1,35 +1,33 @@
#coding=utf-8
module SessionExtenstions
module EntryExtension
def compressed?
@compressed
end
def value
if @value
begin
Marshal.load(compressed? ? Zlib::Inflate.inflate(@value) : @value)
rescue TypeError
compressed? ? Zlib::Inflate.inflate(@value) : @value
end
end
end
def size
if @value.nil?
0
else
@value.bytesize
end
end
end
end
ActiveSupport::Cache::Entry.const_set("DEFAULT_COMPRESS_LIMIT", 1)
ActiveSupport::Cache::Entry.send(:prepend, SessionExtenstions::EntryExtension)
#coding=utf-8
module SessionExtenstions
module EntryExtension
def compressed?
@compressed
end
def value
if @value
begin
Marshal.load(compressed? ? Zlib::Inflate.inflate(@value) : @value)
rescue TypeError
compressed? ? Zlib::Inflate.inflate(@value) : @value
end
end
end
def size
if @value.nil?
0
else
@value.bytesize
end
end
end
end
ActiveSupport::Cache::Entry.const_set("DEFAULT_COMPRESS_LIMIT", 1)
ActiveSupport::Cache::Entry.send(:prepend, SessionExtenstions::EntryExtension)

@ -55,12 +55,14 @@ Rails.application.routes.draw do
get :homepage_info
end
get :question_banks, on: :collection, to: 'users/question_banks#index'
scope module: :users do
resources :courses, only: [:index]
resources :shixuns, only: [:index]
resources :projects, only: [:index]
resources :subjects, only: [:index]
resources :question_banks, only: [:index]
# resources :question_banks, only: [:index]
resource :experience_records, only: [:show]
resource :grade_records, only: [:show]
resource :watch, only: [:create, :destroy]
@ -783,6 +785,17 @@ Rails.application.routes.draw do
namespace :wechats do
resource :js_sdk_signature, only: [:create]
end
resources :helps, only: [] do
collection do
get :about
get :contact
get :cooperatives
get :agreement
get :help_center
post :feedback
end
end
end
namespace :admins do

@ -0,0 +1,5 @@
class ModifyIsTestForUser < ActiveRecord::Migration[5.2]
def change
change_column :users, :is_test, :integer, :limit => 1
end
end

@ -0,0 +1,5 @@
class GtopicBankIsPublic < ActiveRecord::Migration[5.2]
def change
GtopicBank.where(is_public: nil).update_all(is_public: 0)
end
end

@ -0,0 +1,5 @@
class AddVncEvaluateForShixuns < ActiveRecord::Migration[5.2]
def change
add_column :shixuns, :vnc_evaluate, :boolean, default: false
end
end

@ -0,0 +1,31 @@
class MigrateBankReferenceId < ActiveRecord::Migration[5.2]
def change
HomeworkBank.all.each do |bank|
if bank.homework_common
bank.homework_common.update_column("homework_bank_id", bank.id) if bank.homework_common.homework_bank_id.nil?
end
end
GtopicBank.all.each do |bank|
if bank.graduation_topic
bank.graduation_topic.update_column("gtopic_bank_id", bank.id) if bank.graduation_topic.gtopic_bank_id.nil?
end
end
GtaskBank.all.each do |bank|
if bank.graduation_task
bank.graduation_task.update_column("gtask_bank_id", bank.id) if bank.graduation_task.gtask_bank_id.nil?
end
end
ExerciseBank.all.each do |bank|
if bank.container_type == 'Exercise'
exercise = Exercise.find_by(id: bank.container_id)
exercise.update_column("exercise_bank_id", bank.id) if exercise && exercise.exercise_bank_id.nil?
elsif bank.container_type == 'Poll'
poll = Poll.find_by(id: bank.container_id)
poll.update_column("exercise_bank_id", bank.id) if poll && poll.exercise_bank_id.nil?
end
end
end
end

@ -0,0 +1,28 @@
class MigrateBankQuotes < ActiveRecord::Migration[5.2]
def change
HomeworkBank.all.each do |bank|
task_count = bank.homework_commons.count
bank.update_column("quotes", task_count == 0 ? 1 : task_count)
end
GtopicBank.all.each do |bank|
task_count = bank.graduation_topics.count
bank.update_column("quotes", task_count == 0 ? 1 : task_count)
end
GtaskBank.all.each do |bank|
task_count = bank.graduation_tasks.count
bank.update_column("quotes", task_count == 0 ? 1 : task_count)
end
ExerciseBank.all.each do |bank|
if bank.container_type == 'Exercise'
task_count = bank.exercises.count
bank.update_column("quotes", task_count == 0 ? 1 : task_count)
elsif bank.container_type == 'Poll'
task_count = bank.polls.count
bank.update_column("quotes", task_count == 0 ? 1 : task_count)
end
end
end
end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

After

Width:  |  Height:  |  Size: 180 KiB

File diff suppressed because one or more lines are too long

@ -32,7 +32,7 @@ module.exports = {
// See the discussion in https://github.com/facebookincubator/create-react-app/issues/343.s
// devtool: "cheap-module-eval-source-map",
// 开启调试
//devtool: "source-map", // 开启调试
devtool: "eval", // 开启调试
// These are the "entry points" to our application.
// This means they will be the "root" imports that are included in JS bundle.
// The first two entry points enable "hot" CSS and auto-refreshes for JS.

File diff suppressed because one or more lines are too long

@ -103,6 +103,7 @@ a:hover.link-color-grey03{color:#3498db!important;}
.font-18{ font-size: 18px!important;}
.font-20{ font-size: 20px!important;}
.font-22{ font-size: 22px!important;}
.font-25{ font-size: 25px!important;}
.font-24{ font-size: 24px!important;}
.font-28{ font-size: 28px!important;}
.font-30{ font-size: 30px!important;}

@ -29,7 +29,7 @@
|| navigator.userAgent.indexOf('MSIE 10') != -1 )
&&
location.pathname.indexOf("/compatibility") == -1) {
debugger;
// location.href = './compatibility'
location.href = '/compatibility.html'
}

@ -222,6 +222,12 @@ const InfosIndex = Loadable({
loader: () => import('./modules/user/usersInfo/InfosIndex'),
loading: Loading,
})
// 题库
const BanksIndex = Loadable({
loader: () => import('./modules/user/usersInfo/banks/BanksIndex'),
loading: Loading,
})
// 教学案例
const MoopCases = Loadable({
@ -245,6 +251,17 @@ const Messagerouting= Loadable({
loader: () => import('./modules/message/js/Messagerouting'),
loading: Loading,
})
const Topicbank= Loadable({
loader: () => import('./modules/topic_bank/Topic_bank'),
loading: Loading,
})
const Help = Loadable({
loader: () => import('./modules/help/Help'),
loading: Loading,
})
class App extends Component {
constructor(props) {
super(props)
@ -286,8 +303,9 @@ class App extends Component {
const wx = window.wx
const url = '/wechats/js_sdk_signature.json'
const currentUrl = window.location.href.split('#')[0]
// window.encodeURIComponent()
axios.post(url, {
url: currentUrl,
url: window.__testUrl || currentUrl,
}).then((response) => {
console.log('got res')
const data = response.data;
@ -295,7 +313,7 @@ class App extends Component {
debug: false,
appId: data.appid,
timestamp: data.timestamp,
nonceStr: data.nonceStr,
nonceStr: data.noncestr,
signature: data.signature,
jsApiList: [
'onMenuShareTimeline',//
@ -312,7 +330,8 @@ class App extends Component {
title: 'EduCoder',
desc: '创新源于实践',
link: currentUrl,
imgUrl: currentUrl + '/images/educoder/index/subject/subject15.jpg'
imgUrl: window.__testImageUrl
|| (currentUrl.endsWith('/') ? currentUrl : currentUrl + '/') + 'images/educoder/index/subject/subject15.jpg'
};
wx.onMenuShareAppMessage(shareData);//分享给好友
@ -402,6 +421,22 @@ class App extends Component {
<Router>
<Switch>
{/*题库*/}
<Route path="/topicbank/:username/:topicstype"
render={
(props) => {
return (<Topicbank {...this.props} {...props} {...this.state} />)
}
}></Route>
{/*题库*/}
<Route path="/topicbank/:topicstype"
render={
(props) => {
return (<Topicbank {...this.props} {...props} {...this.state} />)
}
}></Route>
{/*众包创新*/}
<Route path={"/crowdsourcing"} component={ProjectPackages}/>
{/*认证*/}
@ -430,6 +465,13 @@ class App extends Component {
}
}></Route>
<Route path="/banks"
render={
(props) => {
return (<BanksIndex {...this.props} {...props} {...this.state} />)
}
}></Route>
<Route
path="/changepassword" component={EducoderLogin}
/>
@ -485,6 +527,10 @@ class App extends Component {
(props)=>(<Messagerouting {...this.props} {...props} {...this.state}></Messagerouting>)
}
></Route>
<Route path="/help/:type"
render={
(props)=>(<Help {...this.props} {...props} {...this.state}></Help>)
}/>
<Route exact path="/" component={ShixunsHome}/>
<Route component={Shixunnopage}/>

@ -0,0 +1,12 @@
import Loadable from 'react-loadable';
import Loading from "./Loading";
const CustomLoadable = (loader, loading = Loading) => {
return Loadable({
loader,
loading
})
}
export default CustomLoadable

@ -48,7 +48,17 @@ export function SnackbarHOC(options = {}) {
snackbarHorizontal: horizontal,
})
}
//个别情况需要走
showNotification = (description, message = "提示", icon) => {
const data = {
message,
description
}
if (icon) {
data.icon = icon;
}
notification.open(data);
}
render() {
const { snackbarOpen, snackbarText, snackbarHorizontal, snackbarVertical } = this.state;
@ -70,7 +80,7 @@ export function SnackbarHOC(options = {}) {
resumeHideDuration={2000}
message={<span id="message-id">{this.state.snackbarText}</span>}
/>
<WrappedComponent {...this.props} showSnackbar={ this.showSnackbar } >
<WrappedComponent {...this.props} showSnackbar={ this.showSnackbar } showNotification= { this.showNotification } >
</WrappedComponent>
</React.Fragment>

@ -56,7 +56,7 @@ export function getUploadActionUrl(path, goTest) {
return `${getUrl()}/api/attachments.json${isDev ? `?debug=${window._debugType || 'admin'}` : ''}`
}
export function getUploadActionUrlOfAuth(id) {
return `${getUrl()}/api/users/accounts/${id}/auth_attachments.json${isDev ? `?debug=${window._debugType || 'admin'}` : ''}`
return `${getUrl()}/api/users/accounts/${id}/auth_attachment.json${isDev ? `?debug=${window._debugType || 'admin'}` : ''}`
}
export function test(path) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 661 KiB

After

Width:  |  Height:  |  Size: 180 KiB

@ -232,7 +232,7 @@ const GraduationTasksquestions= Loadable({
//毕设任务列表
const GraduationTaskssettinglist= Loadable({
loader: () => import('./graduation/tasks/GraduationTaskssettinglist'),
loader: () => import('./graduation/tasks/GraduationTaskDetail'),
loading: Loading,
})
@ -264,34 +264,12 @@ const GraduationTasksSubmiteditApp=Loadable({
loader: () => import('./graduation/tasks/GraduationTasksSubmitedit'),
loading: Loading,
})
//普通作业题库详情
const Generaljobbankdetails =Loadable({
loader: () => import('../../modules/courses/questionbank/Generaljobbankdetails'),
loading: Loading,
});
//分组作业题库详情
const GroupjobbankPage =Loadable({
loader: () => import('../../modules/courses/groupjobbank/GroupjobbankPage'),
loading: Loading,
});
//毕设选题详情
const CompletetopicdePage =Loadable({
loader: () => import('../../modules/courses/comtopicdetails/CompletetopicdePage'),
loading: Loading,
});
//毕设任务详情
const Completetaskpage =Loadable({
loader: () => import('../../modules/courses/completetaskdetails/Completetaskpage'),
loading: Loading,
});
//排序
const Ordering=Loadable({
loader: () => import('../../modules/courses/ordering/Ordering'),
loading: Loading,
});
class CoursesIndex extends Component{
constructor(props) {
super(props)
@ -478,37 +456,12 @@ class CoursesIndex extends Component{
// console.log(commons)
return (
<Switch {...this.props}>
{/*排序*/}
<Route path="/courses/:coursesId/ordering/:ordering_type/:main_id"
render={
(props) => (<Ordering {...this.props} {...props} {...this.state} />)
}
></Route>
{/*毕设任务题库详情*/}
<Route path="/banks/gtopic_topics/:workid"
render={
(props) => (<Completetaskpage {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*毕设内容题库详情*/}
<Route path="/banks/gtask_topics/:workid"
render={
(props) => (<CompletetopicdePage {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*分组作业题库详情*/}
<Route path="/banks/group_topics/:workid"
render={
(props) => (<GroupjobbankPage {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/* 普通作业题库详情*/}
<Route path="/banks/normal_topics/:workid"
render={
(props) => (<Generaljobbankdetails {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/*排序*/}
<Route path="/courses/:coursesId/ordering/:ordering_type/:main_id"
render={
(props) => (<Ordering {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 资源列表页 */}
<Route path="/courses/:coursesId/file/:Id" exact
render={
@ -589,7 +542,7 @@ class CoursesIndex extends Component{
{/* 设置毕设任务 https://www.trustie.net/issues/19981 */}
<Route path="/courses/:coursesId/graduation_tasks/:category_id/:task_Id/setting"
{/* <Route path="/courses/:coursesId/graduation_tasks/:category_id/:task_Id/setting"
render={
(props) => (<GraduationTaskssetting {...this.props} {...props} {...this.state} {...common}/>)
}
@ -597,19 +550,18 @@ class CoursesIndex extends Component{
<Route path="/courses/:coursesId/graduation_tasks/:category_id/:task_Id/questions"
render={
(props) => (<GraduationTasksquestions {...this.props} {...props} {...this.state} {...common}/>)
}></Route>
render={
(props) => (<GraduationTasksquestions {...this.props} {...props} {...this.state} />)
}></Route> */}
<Route path="/courses/:coursesId/graduation_tasks/:category_id/:task_Id/list"
<Route path="/courses/:coursesId/graduation_tasks/:category_id/detail/:task_Id"
render={
(props) => (<GraduationTaskssettinglist {...this.props} {...props} {...this.state} {...common}/>)
}
></Route>
{/* 修改毕设任务 https://www.trustie.net/issues/19981 */}
<Route path="/courses/:coursesId/graduation_tasks/:category_id/edit"
render={

@ -4,250 +4,125 @@ import axios from 'axios'
import '../css/busyWork.css'
import '../css/Courses.css'
import { WordsBtn, getUrl, ConditionToolTip, appendFileSizeToUploadFile, appendFileSizeToUploadFileAll
, getUploadActionUrl } from 'educoder'
, getUploadActionUrl } from 'educoder'
import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor';
import CBreadcrumb from '../common/CBreadcrumb'
import NewWorkForm from './NewWorkForm'
const confirm = Modal.confirm;
const $ = window.$
const MAX_TITLE_LENGTH = 60;
class NewWork extends Component{
constructor(props){
super(props);
this.contentMdRef = React.createRef();
this.answerMdRef = React.createRef();
this.state={
title_value:"",
title_num: 0,
contentFileList: [],
answerFileList: [],
workLoaded: false,
base_on_project: true,
category: {},
min_num: 2,
max_num: 10,
}
}
componentDidMount () {
let {typeId, coursesId, pageType, workId}=this.props.match.params;
const isEdit = pageType === "edit"
this.isEdit = isEdit;
if (isEdit) {
this.fetchWork(workId)
} else {
this.fetchCourseData(coursesId)
}
}
fetchCourseData = (courseId) => {
const isGroup = this.props.isGroup()
const url = `/courses/${courseId}/homework_commons/new.json?type=${isGroup ? 3 : 1}`
axios.get(url, {
})
.then((response) => {
if (response.data.course_name) {
const data = response.data;
this.setState({
course_id: data.course_id,
course_name: data.course_name,
category: data.category,
})
}
})
.catch(function (error) {
console.log(error);
});
}
fetchWork = (workId) => {
const url = `/homework_commons/${workId}/edit.json`
axios.get(url, {
})
.then((response) => {
if (response.data.name) {
const data = response.data;
const contentFileList = data.attachments.map(item => {
return {
id: item.id,
uid: item.id,
name: appendFileSizeToUploadFile(item),
url: item.url,
filesize: item.filesize,
status: 'done'
}
})
const answerFileList = data.ref_attachments.map(item => {
return {
id: item.id,
uid: item.id,
name: appendFileSizeToUploadFile(item),
url: item.url,
filesize: item.filesize,
status: 'done'
}
})
this.setState({
...data,
// course_id: data.course_id,
// course_name: data.course_name,
// category: data.category,
title_num: parseInt(data.name.length),
workLoaded: true,
init_min_num: data.min_num,
init_max_num: data.max_num,
// description: data.description,
reference_answer: data.reference_answer,
contentFileList,
answerFileList,
}, () => {
setTimeout(() => {
this.contentMdRef.current.setValue(data.description || '')
this.answerMdRef.current.setValue(data.reference_answer || '')
}, 2000)
this.props.form.setFieldsValue({
title: data.name,
description: data.description || '',
reference_answer: data.reference_answer || '',
});
})
}
})
.catch(function (error) {
console.log(error);
});
}
// 输入title
changeTitle=(e)=>{
console.log(e.target.value.length);
this.setState({
title_num: parseInt(e.target.value.length)
})
}
handleSubmit = () => {
const courseId = this.state.course_id || this.props.match.params.coursesId ;
this.props.form.validateFieldsAndScroll((err, values) => {
console.log(values)
const mdContnet = this.contentMdRef.current.getValue().trim();
console.log(mdContnet)
values.description = mdContnet;
// return;
{/* max={has_commit ? init_min_num : null } */}
{/* min={has_commit ? init_max_num : (min_num == undefined ? 2 : min_num + 1) } */}
// 已有提交作品,人数范围只能扩大
const { has_commit, max_num, init_max_num, min_num, init_min_num } = this.state;
if (has_commit) {
if (max_num < init_max_num || min_num > init_min_num) {
this.props.showNotification(`已有提交作品,人数范围只能扩大(原设置为:${init_min_num} - ${init_max_num})`)
return;
}
}
// const errKeys = Object.keys(err); // || errKeys.length == 1 && errKeys[0] == 'content' && mdContnet
if (!err) {
if (this.isEdit) {
this.doEdit(courseId, values)
} else {
this.doNew(courseId, values)
}
} else {
$("html").animate({ scrollTop: $('html').scrollTop() - 100 })
}
})
}
doEdit = (courseId, values) => {
const workId = this.props.match.params.workId
const newUrl = `/homework_commons/${workId}.json`
let attachment_ids = this.state.contentFileList.map(item => {
return item.response ? item.response.id : item.id
})
let reference_attachment_ids = this.state.answerFileList.map(item => {
return item.response ? item.response.id : item.id
})
const { min_num, max_num, base_on_project, category } = this.state
const isGroup = this.props.isGroup()
axios.put(newUrl, {
type: isGroup ? 3 : 1,
name: values.title,
description: values.description,
reference_answer: values.reference_answer,
attachment_ids,
reference_attachment_ids,
min_num,
max_num,
base_on_project
})
.then((response) => {
if (response.data.status == 0) {
this.props.showNotification('保存成功')
this.props.toWorkListPage(this.props.match.params, workId)
}
})
.catch(function (error) {
console.log(error);
});
}
doNew = (courseId, values) => {
const newUrl = `/courses/${courseId}/homework_commons.json`
let attachment_ids = this.state.contentFileList.map(item => {
return item.response ? item.response.id : item.id
})
let reference_attachment_ids = this.state.answerFileList.map(item => {
return item.response ? item.response.id : item.id
})
const isGroup = this.props.isGroup()
const { min_num, max_num, base_on_project, category } = this.state
axios.post(newUrl, {
type: isGroup ? 3 : 1,
name: values.title,
description: values.description,
reference_answer: values.reference_answer,
attachment_ids,
reference_attachment_ids,
constructor(props){
super(props);
this.contentMdRef = React.createRef();
this.answerMdRef = React.createRef();
this.state={
category: {},
course_name: ''
}
}
componentDidMount () {
let {typeId, coursesId, pageType, workId}=this.props.match.params;
const isEdit = pageType === "edit"
this.isEdit = isEdit;
if (isEdit) {
this.fetchWork(workId)
} else {
this.fetchCourseData(coursesId)
}
}
fetchCourseData = (courseId) => {
const isGroup = this.props.isGroup()
const url = `/courses/${courseId}/homework_commons/new.json?type=${isGroup ? 3 : 1}`
axios.get(url, {
})
.then((response) => {
if (response.data.course_name) {
const data = response.data;
this.setState({
course_id: data.course_id,
course_name: data.course_name,
category: data.category,
})
}
})
.catch(function (error) {
console.log(error);
});
}
fetchWork = (workId) => {
const url = `/homework_commons/${workId}/edit.json`
axios.get(url, {
})
.then((response) => {
if (response.data.name) {
const data = response.data;
data.isEdit = this.isEdit
this.setState({
course_id: data.course_id,
course_name: data.course_name,
category: data.category,
})
this.newWorkFormRef.initValue(response.data)
}
})
.catch(function (error) {
console.log(error);
});
}
onCancel = () => {
this.props.toListPage(this.props.match.params, this.state.category.category_id)
}
doEdit = (params) => {
const workId = this.props.match.params.workId
const newUrl = `/homework_commons/${workId}.json`
axios.put(newUrl, params)
.then((response) => {
if (response.data.status == 0) {
this.props.showNotification('保存成功')
this.props.toWorkListPage(this.props.match.params, workId)
}
})
.catch(function (error) {
console.log(error);
});
}
doNew = (params) => {
const coursesId = this.props.match.params.coursesId
const newUrl = `/courses/${coursesId}/homework_commons.json`
axios.post(newUrl, params)
.then((response) => {
if (response.data.status == 0) {
this.props.showNotification('保存成功')
this.props.toWorkListPage(this.props.match.params, response.data.homework_id)
}
})
.catch(function (error) {
console.log(error);
});
}
min_num,
max_num,
base_on_project
})
.then((response) => {
if (response.data.status == 0) {
this.props.showNotification('保存成功')
this.props.toWorkListPage(this.props.match.params, response.data.homework_id)
}
})
.catch(function (error) {
console.log(error);
});
}
handleContentUploadChange = (info) => {
if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
let contentFileList = info.fileList;
this.setState({ contentFileList: appendFileSizeToUploadFileAll(contentFileList) });
}
}
handleAnswerUploadChange = (info) => {
if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
let answerFileList = info.fileList;
this.setState({ answerFileList: appendFileSizeToUploadFileAll(answerFileList) });
}
}
handleContentUploadChange = (info) => {
if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
let contentFileList = info.fileList;
this.setState({ contentFileList: appendFileSizeToUploadFileAll(contentFileList) });
}
}
handleAnswerUploadChange = (info) => {
if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
let answerFileList = info.fileList;
this.setState({ answerFileList: appendFileSizeToUploadFileAll(answerFileList) });
}
}
onAttachmentRemove = (file, stateName) => {
onAttachmentRemove = (file, stateName) => {
if(!file.percent || file.percent == 100){
this.props.confirm({
content: '是否确认删除?',
@ -264,293 +139,69 @@ class NewWork extends Component{
return false;
}
}
deleteAttachment = (file, stateName) => {
// 初次上传不能直接取uid
const url = `/attachments/${file.response ? file.response.id : file.uid}.json`
axios.delete(url, {
})
.then((response) => {
if (response.data) {
const { status } = response.data;
if (status == 0) {
console.log('--- success')
this.setState((state) => {
const index = state[stateName].indexOf(file);
const newFileList = state[stateName].slice();
newFileList.splice(index, 1);
return {
[stateName]: newFileList,
};
});
}
}
})
.catch(function (error) {
console.log(error);
});
}
max_num_change = (val) => {
if (val < 2) {
this.setState({
max_num: 2,
})
return;
}
const { min_num } = this.state;
this.setState({
max_num: val,
min_num: val <= min_num ? val - 1 : min_num
})
}
min_num_change = (val) => {
this.setState({ min_num: val })
}
base_on_project_change = () => {
this.setState({ base_on_project: !this.state.base_on_project })
}
}
render(){
let {typeId,coursesId,pageType}=this.props.match.params;
const { getFieldDecorator } = this.props.form;
const isGroup = this.props.isGroup()
const moduleName = !isGroup? "普通作业":"分组作业";
const moduleEngName = this.props.getModuleName()
let{
title_value, contentFileList, answerFileList, max_num, min_num, base_on_project,
init_max_num, init_min_num,
title_num, course_name, category, has_commit, has_project
}=this.state
const { current_user } = this.props
render(){
let {typeId,coursesId,pageType}=this.props.match.params;
const courseId = this.state.course_id || this.props.match.params.coursesId ;
const isEdit = this.isEdit;
if ((isEdit == undefined || isEdit) && !this.state.workLoaded) {
return ''
}
const uploadProps = {
width: 600,
fileList: contentFileList,
multiple: true,
// https://github.com/ant-design/ant-design/issues/15505
// showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。
// showUploadList: false,
action: `${getUploadActionUrl()}`,
onChange: this.handleContentUploadChange,
onRemove: (file) => this.onAttachmentRemove(file, 'contentFileList'),
beforeUpload: (file) => {
console.log('beforeUpload', file.name);
const isLt150M = file.size / 1024 / 1024 < 150;
if (!isLt150M) {
this.props.showNotification('文件大小必须小于150MB!');
}
return isLt150M;
},
};
const answerUploadProps = {
width: 600,
fileList: answerFileList,
multiple: true,
// https://github.com/ant-design/ant-design/issues/15505
// showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。
// showUploadList: false,
action: `${getUrl()}/api/attachments.json`,
onChange: this.handleAnswerUploadChange,
onRemove: (file) => this.onAttachmentRemove(file, 'answerFileList'),
beforeUpload: (file) => {
console.log('beforeUpload', file.name);
const isLt150M = file.size / 1024 / 1024 < 150;
if (!isLt150M) {
this.props.showNotification('文件大小必须小于150MB!');
}
return isLt150M;
},
};
const isGroup = this.props.isGroup()
const moduleName = !isGroup? "普通作业":"分组作业";
const moduleEngName = this.props.getModuleName()
let{
course_name, category
}=this.state
const { current_user } = this.props
return(
<div className="newMain">
<div className="educontent mt20 mb50">
{/* <p className="clearfix">
<WordsBtn style="grey" className="fl">{course_name}</WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<WordsBtn style="grey" className="fl">{typeId==1 ?"普通作业":"分组作业"}</WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<span>{pageType==="new"?"新建":"编辑"}</span>
</p> */}
<CBreadcrumb items={[
{ to: current_user && current_user.first_category_url, name: this.state.course_name},
{ to: `/courses/${courseId}/${moduleEngName}/${category && category.category_id ? category.category_id : ''}`
, name: category && category.category_name },
{ name: `${ this.isEdit ? '编辑' : '新建'}` }
]}></CBreadcrumb>
<p className="clearfix mt20 mb20">
<span className="fl font-24 color-grey-3">{this.isEdit ?"编辑":"新建"}{ moduleName }</span>
{/* history.goBack()
this.props.toListPage(this.props.match.params, category.category_id)}
*/}
<a href="javascript:void(0)" className="color-grey-6 fr font-16 mr2"
onClick={() => this.props.history.goBack()}>
返回
</a>
</p>
<div>
{/* onSubmit={this.handleSubmit} */}
<style>
{
`
const courseId = this.state.course_id || coursesId ;
const isEdit = this.isEdit;
const common = {
onCancel:this.onCancel,
isGroup: this.props.isGroup,
doNew: this.doNew,
doEdit: this.doEdit,
}
return(
<div className="newMain">
<div className="educontent mt20 mb50">
<CBreadcrumb items={[
{ to: current_user && current_user.first_category_url, name: this.state.course_name},
{ to: `/courses/${courseId}/${moduleEngName}/${category && category.category_id ? category.category_id : ''}`
, name: category && category.category_name },
{ name: `${ this.isEdit ? '编辑' : '新建'}` }
]}></CBreadcrumb>
<p className="clearfix mt20 mb20">
<span className="fl font-24 color-grey-3">{this.isEdit ?"编辑":"新建"}{ moduleName }</span>
<a href="javascript:void(0)" className="color-grey-6 fr font-16 mr2"
onClick={() => this.props.history.goBack()}>
返回
</a>
</p>
<div>
<style>
{
`
.yslnewworkinputaddonAfter .ant-input{
border-right: none !important;
height: 40px !important;
}
`
}
</style>
<Form className="courseForm">
<Form.Item
label="标题"
className="AboutInputForm"
>
{getFieldDecorator('title', {
rules: [{
required: true, message: '请输入标题'
}],
})(
<Input placeholder="请输入作业标题最大限制60个字符" onInput={this.changeTitle} className="searchView yslnewworkinputaddonAfter searchViewAfter" style={{"width":"100%"}} maxLength={MAX_TITLE_LENGTH} addonAfter={`${String(title_num)}/${MAX_TITLE_LENGTH}`}/>
)}
</Form.Item>
<style>{`
.uploadBtn.ant-btn {
border: none;
color: #4CACFF;
box-shadow: none;
background: transparent;
padding: 0 6px;
}
.ant-upload-list-item:hover .ant-upload-list-item-info{
background-color:#fff;
}
.upload_1 .ant-upload-list {
width: 350px;
}
.ant-input-number {
height: 40px;
line-height: 40px;
}
.workContent.AboutInputForm.ant-form-item {
border-bottom: none;
padding-bottom: 0px !important;
}
.newWorkUpload {
padding: 0px 30px 30px 30px!important;
background: #fff;
width: 100%;
display: inline-block;
border-bottom: 1px solid #EDEDED;
}
`}</style>
{/* TpmQuestionEdit 这个没出现抖动 */}
{ <Form.Item
label="内容"
className="AboutInputForm workContent mdInForm"
>
{getFieldDecorator('description', {
rules: [{
required: true, message: '请输入作业内容和要求'
}],
})(
<TPMMDEditor ref={this.contentMdRef} placeholder="请在此输入作业内容和要求,最大限制5000个字符" mdID={'courseContentMD'} refreshTimeout={1500}
className="courseMessageMD" initValue={this.state.description}></TPMMDEditor>
)}
</Form.Item> }
<Upload {...uploadProps} className="upload_1 newWorkUpload">
<Button className="uploadBtn">
<Icon type="upload" /> 上传附件
</Button>
(单个文件150M以内)
</Upload>
{ isGroup &&
<Form.Item
label="分组设置"
className="AboutInputForm"
>
{getFieldDecorator('personNum', {
rules: [{
required: false
// required: true, message: '请输入最小人数和最大人数'
}],
})(
<div>
<p className="clearfix">
<ConditionToolTip condition={has_commit} title={'已有提交作品,人数范围只能扩大'}>
{/* max={has_commit ? init_min_num : null } */}
<InputNumber placeholder="请填写每组最小人数" min={1} className="winput-240-40" value={min_num}
onChange={this.min_num_change} style={{width:'180px'}} />
</ConditionToolTip>
<span className="ml15 mr15">~</span>
{/* min={has_commit ? init_max_num : (min_num == undefined ? 2 : min_num + 1) } */}
<ConditionToolTip condition={has_commit} title={'已有提交作品,人数范围只能扩大'}>
<InputNumber className="winput-240-40" placeholder="请填写每组最大人数" value={max_num} max={10}
onChange={this.max_num_change} style={{width:'180px'}} />
</ConditionToolTip>
<label className="color-grey-9 ml20 font-14">项目管理员角色的成员都可以提交作品提交作品时需要关联同组成员组内成员作品共享</label>
</p>
<p className="mt20">
<ConditionToolTip condition={has_commit || has_project} title={'已有关联项目或作品,不能修改'}>
<Checkbox checked={base_on_project} onChange={this.base_on_project_change}
disabled={has_project || has_commit}
>基于项目实施</Checkbox>
</ConditionToolTip>
<label className="color-grey-9 ml12 font-14">勾选后各小组必须在educoder平台创建项目教师可随时观察平台对各小组最新进展的实时统计</label>
</p>
</div>
)}
</Form.Item>
}
<Form.Item
label="参考答案"
className="AboutInputForm"
style={{"borderBottom":"none"}}
>
{getFieldDecorator('reference_answer', {
rules: [{
required: false
}],
})(
<TPMMDEditor ref={this.answerMdRef} placeholder="请在此输入作业的参考答案,最大限制5000个字符" mdID={'workAnswerMD'}
className="courseMessageMD" refreshTimeout={1500} initValue={this.state.reference_answer || ''}></TPMMDEditor>
)}
<Upload {...answerUploadProps} className="upload_1">
<Button className="uploadBtn">
<Icon type="upload" /> 上传附件
</Button>
(单个文件150M以内)
</Upload>
</Form.Item>
<Form.Item>
<div className="clearfix mt30 mb30">
{/* htmlType="submit" */}
<Button type="primary" onClick={this.handleSubmit} className="defalutSubmitbtn fl mr20">提交</Button>
<a className="defalutCancelbtn fl" onClick={() => this.props.toListPage(this.props.match.params, category.category_id)}>取消</ a>
</div>
</Form.Item>
</Form>
</div>
</div>
</div>
)
}
`
}
</style>
<NewWorkForm
{...this.props}
{...this.state}
{...common}
wrappedComponentRef={(ref) => {this.newWorkFormRef = ref}}
></NewWorkForm>
</div>
</div>
</div>
)
}
}
const WrappedBoardsNew = Form.create({ name: 'NewWork' })(NewWork);
export default WrappedBoardsNew;
export default NewWork;

@ -0,0 +1,529 @@
import React,{ Component } from "react";
import { Input, InputNumber, Form, Button, Checkbox, Upload, Icon, message, Modal } from "antd";
import axios from 'axios'
import '../css/busyWork.css'
import '../css/Courses.css'
import { WordsBtn, getUrl, ConditionToolTip, appendFileSizeToUploadFile, appendFileSizeToUploadFileAll } from 'educoder'
import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor';
import CBreadcrumb from '../common/CBreadcrumb'
const confirm = Modal.confirm;
const $ = window.$
const MAX_TITLE_LENGTH = 60;
/**
需要注意的props
isGroup
*/
class NewWorkForm extends Component{
constructor(props){
super(props);
this.contentMdRef = React.createRef();
this.answerMdRef = React.createRef();
this.state={
title_value:"",
title_num: 0,
contentFileList: [],
answerFileList: [],
workLoaded: false,
base_on_project: true,
category: {},
min_num: 2,
max_num: 10,
}
}
initValue = (data) => {
if (data.isEdit) {
const contentFileList = data.attachments.map(item => {
return {
id: item.id,
uid: item.id,
name: appendFileSizeToUploadFile(item),
url: item.url,
filesize: item.filesize,
status: 'done'
}
})
const answerFileList = data.ref_attachments.map(item => {
return {
id: item.id,
uid: item.id,
name: appendFileSizeToUploadFile(item),
url: item.url,
filesize: item.filesize,
status: 'done'
}
})
this.setState({
...data,
// course_id: data.course_id,
// course_name: data.course_name,
// category: data.category,
title_num: parseInt(data.name.length),
workLoaded: true,
init_min_num: data.min_num,
init_max_num: data.max_num,
// description: data.description,
reference_answer: data.reference_answer,
contentFileList,
answerFileList,
}, () => {
setTimeout(() => {
this.contentMdRef.current.setValue(data.description || '')
this.answerMdRef.current.setValue(data.reference_answer || '')
}, 2000)
this.props.form.setFieldsValue({
title: data.name,
description: data.description || '',
reference_answer: data.reference_answer || '',
});
})
} else { // new
}
}
// 输入title
changeTitle=(e)=>{
console.log(e.target.value.length);
this.setState({
title_num: parseInt(e.target.value.length)
})
}
handleSubmit = () => {
const courseId = this.state.course_id || this.props.match.params.coursesId ;
this.props.form.validateFieldsAndScroll((err, values) => {
if(err && err.personNum) delete err.personNum;
console.log(values)
const mdContnet = this.contentMdRef.current.getValue().trim();
console.log(mdContnet)
values.description = mdContnet;
// return;
{/* max={has_commit ? init_min_num : null } */}
{/* min={has_commit ? init_max_num : (min_num == undefined ? 2 : min_num + 1) } */}
// 已有提交作品,人数范围只能扩大
const { has_commit, max_num, init_max_num, min_num, init_min_num } = this.state;
const isGroup = this.props.isGroup()
if (isGroup) {
if (!min_num) {
this.props.showNotification('最小人数不能为空');
return;
} else if (min_num < 1) {
this.props.showNotification('最小人数不能小于1');
return;
} else if (!max_num) {
this.props.showNotification('最大人数不能为空');
return;
} else if (max_num < min_num) {
this.props.showNotification('最大人数不能小于最小人数');
return;
}
if (has_commit) {
if (max_num < init_max_num || min_num > init_min_num) {
this.props.showNotification(`已有提交作品,人数范围只能扩大(原设置为:${init_min_num} - ${init_max_num})`)
return;
}
}
}
// const errKeys = Object.keys(err); // || errKeys.length == 1 && errKeys[0] == 'content' && mdContnet
if (!err || Object.keys(err).length == 0) {
if (this.state.isEdit) {
this.doEdit(courseId, values)
} else {
this.doNew(courseId, values)
}
} else {
$("html").animate({ scrollTop: $('html').scrollTop() - 100 })
}
})
}
doEdit = (courseId, values) => {
let attachment_ids = this.state.contentFileList.map(item => {
return item.response ? item.response.id : item.id
})
let reference_attachment_ids = this.state.answerFileList.map(item => {
return item.response ? item.response.id : item.id
})
const { min_num, max_num, base_on_project, category } = this.state
const isGroup = this.props.isGroup()
const params = {
type: isGroup ? 3 : 1,
name: values.title,
description: values.description,
reference_answer: values.reference_answer,
attachment_ids,
reference_attachment_ids,
min_num,
max_num,
base_on_project
}
this.props.doEdit && this.props.doEdit(params)
}
doNew = (courseId, values) => {
let attachment_ids = this.state.contentFileList.map(item => {
return item.response ? item.response.id : item.id
})
let reference_attachment_ids = this.state.answerFileList.map(item => {
return item.response ? item.response.id : item.id
})
const isGroup = this.props.isGroup()
const { min_num, max_num, base_on_project, category } = this.state
const params = {
type: isGroup ? 3 : 1,
name: values.title,
description: values.description,
reference_answer: values.reference_answer,
attachment_ids,
reference_attachment_ids,
min_num,
max_num,
base_on_project
}
this.props.doNew && this.props.doNew(params)
}
handleContentUploadChange = (info) => {
if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
let contentFileList = info.fileList;
this.setState({ contentFileList: appendFileSizeToUploadFileAll(contentFileList) });
}
}
handleAnswerUploadChange = (info) => {
if (info.file.status === 'uploading' || info.file.status === 'done' || info.file.status === 'removed') {
let answerFileList = info.fileList;
this.setState({ answerFileList: appendFileSizeToUploadFileAll(answerFileList) });
}
}
onAttachmentRemove = (file, stateName) => {
if(!file.percent || file.percent == 100){
this.props.confirm({
content: '是否确认删除?',
onOk: () => {
this.deleteAttachment(file, stateName)
},
onCancel() {
console.log('Cancel');
},
});
}
return false;
}
deleteAttachment = (file, stateName) => {
// 初次上传不能直接取uid
const url = `/attachments/${file.response ? file.response.id : file.uid}.json`
axios.delete(url, {
})
.then((response) => {
if (response.data) {
const { status } = response.data;
if (status == 0) {
console.log('--- success')
this.setState((state) => {
const index = state[stateName].indexOf(file);
const newFileList = state[stateName].slice();
newFileList.splice(index, 1);
return {
[stateName]: newFileList,
};
});
}
}
})
.catch(function (error) {
console.log(error);
});
}
max_num_change = (val) => {
if (val < 2) {
// this.setState({
// max_num: 2,
// })
return;
}
const { min_num } = this.state;
this.setState({
max_num: val,
min_num: val <= min_num ? val - 1 : min_num
})
}
personNumValidator = (rule, value, callback) => {
const { min_num, max_num } = this.state;
const form = this.props.form;
if (!min_num) {
callback('最小人数不能为空');
} else if (min_num < 1) {
callback('最小人数不能小于1');
} else if (!max_num) {
callback('最大人数不能为空');
} else if (max_num < min_num) {
callback('最大人数不能小于最小人数');
} else {
callback();
}
}
min_num_change = (val) => {
this.setState({ min_num: val })
}
base_on_project_change = () => {
this.setState({ base_on_project: !this.state.base_on_project })
}
componentDidMount() {
window.$('.groupSetting .ant-form-item-label > label').addClass('ant-form-item-required')
}
render(){
let {typeId,coursesId,pageType}=this.props.match.params;
const { getFieldDecorator } = this.props.form;
const isGroup = this.props.isGroup()
let{
title_value, contentFileList, answerFileList, max_num, min_num, base_on_project,
init_max_num, init_min_num,
title_num, course_name, category, has_commit, has_project,
isEdit
}=this.state
const { current_user } = this.props
const courseId = this.state.course_id || this.props.match.params.coursesId ;
if ((isEdit) && !this.state.workLoaded) {
return ''
}
const uploadProps = {
width: 600,
fileList: contentFileList,
multiple: true,
// https://github.com/ant-design/ant-design/issues/15505
// showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。
// showUploadList: false,
action: `${getUrl()}/api/attachments.json`,
onChange: this.handleContentUploadChange,
onRemove: (file) => this.onAttachmentRemove(file, 'contentFileList'),
beforeUpload: (file) => {
console.log('beforeUpload', file.name);
const isLt150M = file.size / 1024 / 1024 < 150;
if (!isLt150M) {
this.props.showNotification('文件大小必须小于150MB!');
}
return isLt150M;
},
};
const answerUploadProps = {
width: 600,
fileList: answerFileList,
multiple: true,
// https://github.com/ant-design/ant-design/issues/15505
// showUploadList={false},然后外部拿到 fileList 数组自行渲染列表。
// showUploadList: false,
action: `${getUrl()}/api/attachments.json`,
onChange: this.handleAnswerUploadChange,
onRemove: (file) => this.onAttachmentRemove(file, 'answerFileList'),
beforeUpload: (file) => {
console.log('beforeUpload', file.name);
const isLt150M = file.size / 1024 / 1024 < 150;
if (!isLt150M) {
this.props.showNotification('文件大小必须小于150MB!');
}
return isLt150M;
},
};
return(
<React.Fragment>
<style>
{
`
.yslnewworkinputaddonAfter .ant-input{
border-right: none !important;
height: 40px !important;
}
`
}
</style>
<Form className="courseForm">
<Form.Item
label="标题"
className="AboutInputForm"
>
{getFieldDecorator('title', {
rules: [{
required: true, message: '请输入标题'
}],
})(
<Input placeholder="请输入作业标题最大限制60个字符" onInput={this.changeTitle} className="searchView yslnewworkinputaddonAfter searchViewAfter" style={{"width":"100%"}} maxLength={MAX_TITLE_LENGTH} addonAfter={`${String(title_num)}/${MAX_TITLE_LENGTH}`}/>
)}
</Form.Item>
<style>{`
.uploadBtn.ant-btn {
border: none;
color: #4CACFF;
box-shadow: none;
background: transparent;
padding: 0 6px;
}
.ant-upload-list-item:hover .ant-upload-list-item-info{
background-color:#fff;
}
.upload_1 .ant-upload-list {
width: 350px;
}
.ant-input-number {
height: 40px;
line-height: 40px;
}
.workContent.AboutInputForm.ant-form-item {
border-bottom: none;
padding-bottom: 0px !important;
}
.newWorkUpload {
padding: 0px 30px 30px 30px!important;
background: #fff;
width: 100%;
display: inline-block;
border-bottom: 1px solid #EDEDED;
}
.courseForm .AboutInputForm.clearPaddingBottom{
padding-bottom:0px!important;
}
.clearPaddingBottom .ant-form-explain{
position:absolute;
bottom:0px;
left:0px
}
.resetNewWorkUpload{
border-bottom:none!important;
}
`}</style>
{ <Form.Item
label="内容"
className="AboutInputForm workContent mdInForm"
>
{getFieldDecorator('description', {
rules: [{
required: true, message: '请输入作业内容和要求',
},{
max: 5000 , message:'最大限制5000个字符'
}],
})(
<TPMMDEditor ref={this.contentMdRef} placeholder="请在此输入作业内容和要求,最大限制5000个字符" mdID={'courseContentMD'} refreshTimeout={1500}
className="courseMessageMD" initValue={this.state.description}></TPMMDEditor>
)}
</Form.Item> }
<Upload {...uploadProps} className="upload_1 newWorkUpload">
<Button className="uploadBtn">
<Icon type="upload" /> 上传附件
</Button>
(单个文件150M以内)
</Upload>
{ isGroup &&
<Form.Item
label="分组设置"
className="AboutInputForm groupSetting"
>
{getFieldDecorator('personNum', {
validateTrigger: 'onNone',
// rules: [{
// required: true,
// message: '人数不能为空'
// validator: this.personNumValidator
// required: true, message: '请输入最小人数和最大人数'
// }],
})(
<div>
<p className="clearfix">
<ConditionToolTip condition={has_commit} title={'已有提交作品,人数范围只能扩大'}>
{/* max={has_commit ? init_min_num : null } */}
<InputNumber placeholder="请填写每组最小人数" min={1} className="winput-240-40" value={min_num}
onChange={this.min_num_change} style={{width:'180px'}} />
</ConditionToolTip>
<span className="ml15 mr15">~</span>
{/* min={has_commit ? init_max_num : (min_num == undefined ? 2 : min_num + 1) } */}
<ConditionToolTip condition={has_commit} title={'已有提交作品,人数范围只能扩大'}>
<InputNumber className="winput-240-40" placeholder="请填写每组最大人数" value={max_num} max={10}
onChange={this.max_num_change} style={{width:'180px'}} />
</ConditionToolTip>
<label className="color-grey-9 ml20 font-14">项目管理员角色的成员都可以提交作品提交作品时需要关联同组成员组内成员作品共享</label>
</p>
<p className="mt20">
<ConditionToolTip condition={has_commit || has_project} title={'已有关联项目或作品,不能修改'}>
<Checkbox checked={base_on_project} onChange={this.base_on_project_change}
disabled={has_project || has_commit}
>基于项目实施</Checkbox>
</ConditionToolTip>
<label className="color-grey-9 ml12 font-14">勾选后各小组必须在educoder平台创建项目教师可随时观察平台对各小组最新进展的实时统计</label>
</p>
</div>
)}
</Form.Item>
}
<div className="edu-back-white">
<Form.Item
label="参考答案"
className="AboutInputForm clearPaddingBottom pr"
style={{"borderBottom":"none"}}
>
{getFieldDecorator('reference_answer', {
rules: [{
max: 5000 , message:'最大限制5000个字符'
}],
})(
<TPMMDEditor ref={this.answerMdRef} placeholder="请在此输入作业的参考答案,最大限制5000个字符" mdID={'workAnswerMD'}
className="courseMessageMD" refreshTimeout={1500} initValue={this.state.reference_answer || ''}></TPMMDEditor>
)}
</Form.Item>
<Upload {...answerUploadProps} className="upload_1 newWorkUpload resetNewWorkUpload">
<Button className="uploadBtn">
<Icon type="upload" /> 上传附件
</Button>
(单个文件150M以内)
</Upload>
</div>
<Form.Item>
<div className="clearfix mt30 mb30">
{/* htmlType="submit" */}
<Button type="primary" onClick={this.handleSubmit} className="defalutSubmitbtn fl mr20">提交</Button>
<a className="defalutCancelbtn fl" onClick={() => this.props.onCancel()}>取消</ a>
</div>
</Form.Item>
</Form>
</React.Fragment>
)
}
}
const WrappedWorkForm = Form.create({ name: 'NewWorkForm' })(NewWorkForm);
export default WrappedWorkForm;

@ -63,7 +63,7 @@ class TabRightComponents extends Component{
})
}
}else {
this.props.slowDownload(url)
this.props.slowDownload(url);
// this.props.showNotification(`正在下载中`);
// window.open("/api"+url, '_blank');
}

@ -76,6 +76,7 @@ class commonWork extends Component{
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.match.path != this.props.match.path) {
this.clearSelection()
this._getList()
}
}
@ -226,7 +227,7 @@ class commonWork extends Component{
}
clearSelection = () => {
this.setState({ checkBoxValues: [] })
this.setState({ checkBoxValues: [], checkAll: false })
}
//
onSetPublic = () => {

@ -53,7 +53,7 @@ class ModalWrapper extends Component{
{this.props.checkBoxValuestype===true?<div className={"mt10 color-red"}>
请先选择课堂
</div>:""}
<div className="mt30 marginauto clearfix edu-txt-center">
<div className="mt20 marginauto clearfix edu-txt-center">
<a onClick={this.onCancel} className="pop_close task-btn mr30">{ cancelText || '取消' }</a>
<a onClick={this.onOk} className="task-btn task-btn-orange" id="submit_send_shixun">{ okText || "确定" }</a>
</div>

@ -1,5 +1,6 @@
import React, {Component} from "react";
import { WordsBtn,on, off, trigger,markdownToHTML,getImageUrl} from 'educoder';
import { WordsBtn,on, off, trigger,MarkdownToHtml,getImageUrl} from 'educoder';
import {
Button,
Checkbox,
@ -13,6 +14,9 @@ import {
import './completetaskdetails.css';
import GroupPackage from "../groupjobbank/GroupPackage";
import GroupPackage2 from "../groupjobbank/GroupPackage2";
import AttachmentsList from "../../../common/components/attachment/AttachmentList";
import NoneData from '../../courses/coursesPublic/NoneData'
class Groupjobbandetails extends Component {
@ -57,20 +61,24 @@ class Groupjobbandetails extends Component {
render() {
let{datas}=this.props;
return (
<div className=" clearfix edu-back-white" ref='targetElementTrainingjobsetting' style={{margin: "auto", minWidth:"1200px"}}>
<div className=" clearfix edu-back-white " ref='targetElementTrainingjobsetting' style={{margin: "auto", minWidth:"1200px"}}>
<div className="yslquestionbank1">
<div id="MakedownHTML"className="markdown-body yslquesHeigth yslquesmarkdowntext" dangerouslySetInnerHTML={{__html: markdownToHTML("C++是C语言的面向对象扩展是C语言的一个超集同时也是历史最悠久、最受欢迎的程序设计语言之一。根据C++创始人Stroustrup的自述C++是一个“更好的C语言”。\n" +
"\n" +
"输入输出是计算机程序的基本功能。程序本质上是对数据进行处理的一系列操作,一般程序都可以分解为:“数据输入”、“数据处理”和“数据输出”三个步骤。标准输入输出(键盘输入和显示器输出)是程序的重要组成部分。\n" +
"\n" +
"本实训项目的主要目标是学习和掌握C++程序的基本结构和基本输入输出主要内容包括标准C语言自有的单个字符的输入输出、格式化的输入输出以及C++扩展的使用流对象的输入输出").replace(/▁/g, "▁▁▁")}}/>
{
datas.description===null||datas.description==="null"||datas.description===""?
""
:
<MarkdownToHtml content={datas.description} selector="work_content" className="mb10 yslquesHeigth"></MarkdownToHtml>
}
<div className="mt24">
{datas.attachments === undefined || datas.attachments === null || datas.attachments === ""? "" :
<AttachmentsList {...this.state} {...this.props} attachments={datas.attachments} ></AttachmentsList>}
<GroupPackage2 datas={datas} bool={false}></GroupPackage2>
</div>
<GroupPackage></GroupPackage>
<GroupPackage2></GroupPackage2>
</div>

@ -1,34 +1,43 @@
import React, {Component} from "react";
import {Link, NavLink} from 'react-router-dom';
import {WordsBtn, ActionBtn} from 'educoder';
import {BrowserRouter as Router,Route,Switch,Link, NavLin} from 'react-router-dom';
import {WordsBtn, ActionBtn,getImageUrl} from 'educoder';
import { Input,Checkbox,Table, Pagination, Modal,Menu, Tooltip,Spin,Button,Form } from "antd";
import axios from 'axios';
import {
notification
} from "antd";
import CoursesListType from '../coursesPublic/CoursesListType';
import Completetaskdetails from './Completetaskdetails';
import BanksMenu from '../../user/usersInfo/banks/banksMenu'
import Loadable from 'react-loadable';
import Loading from '../../../Loading';
import '../css/members.css';
import "../common/formCommon.css";
import '../css/Courses.css';
import '../css/busyWork.css';
import '../poll/pollStyle.css';
const Completetaskdetails = Loadable({
loader: () => import('./Completetaskdetails'),
loading: Loading,
})
class Completetaskpage extends Component {
//分组作业内容详情
constructor(props) {
super(props);
// this.answerMdRef = React.createRef();
this.setState({
this.state={
workid:1,
isSpin:false,
datas:[],
})
visible:false,
banksMenu:undefined
}
}
componentDidMount() {
// console.log("父组件加载框");
this.getonedata();
}
getonedata(){
if( this.props.match.params.workid){
this.setState({
workid: this.props.match.params.workid,
@ -47,14 +56,41 @@ class Completetaskpage extends Component {
this.setState({
isSpin:true,
})
let url = `/homework_banks/${workids}.json`;
let url = `/task_banks/${workids}.json`;
//
axios.get(url).then((response) => {
if(response){
if(response.data){
this.setState({
datas:response.data.informs,
})
datas:response.data,
});
try {
const crumbData={
title:response && response.data && response.data.name,
is_public:response && response.data && response.data.is_public,
crumbArray:[
{content:'详情'}
]
};
const menuData={
tab:'0',//tab选中的index
menuArray:[//tab以及tab路由
{to:`/banks/gtask/${workids}/${this.props.match.params.type}?tab=0`,content:'内容详情'},
],
category:'gtask',//毕设选题
tos:`/banks/gtask/${workids}/edit/${this.props.match.params.type}?tab=0`,
id:workids,
is_public:response && response.data && response.data.is_public,
type:this.props.match.params.type,
authorize:response && response.data && response.data.authorize,
}
this.setState({
banksMenu:menuData
})
this.props.initPublic(crumbData,response.data);
}catch (e) {
}
}else {
this.setState({
datas:[],
@ -79,60 +115,35 @@ class Completetaskpage extends Component {
})
});
}
bindRef = ref => { this.child = ref }
///////////////教师截止
render() {
const isAdmin = this.props.isAdmin();
// console.log(119)
let {datas} = this.state;
let{
banksMenu
}=this.state;
return (
<div className="newMain clearfix ">
<div className={"educontent mt10 mb20"} style={{width: "1200px"}}>
<div className="educontent mb20">
<p className="clearfix mb20 mt10">
<a className="btn colorgrey fl hovercolorblue ">题库</a>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<a
className=" btn colorgrey fl hovercolorblue "
>详情</a>
</p>
</div>
<div className="educontent mb20">
<p className=" fl color-black summaryname" style={{heigth: "33px"}}>
MySQL数据库编程开发实训基础篇
</p>
<CoursesListType
typelist={["公开"]}
/>
</div>
<div className="edu-back-white ">
<div className="stud-class-set bor-bottom-greyE ">
<div className=" clearfix edu-back-white poll_list">
<a className={"active ml12"}>内容详情</a>
<a className="fr color-blue font-16" >发送</a>
<a className="fr color-blue font-16" >编辑</a>
<a className="fr color-blue font-16" >删除</a>
</div>
</div>
</div>
<Completetaskdetails/>
<React.Fragment>
<div className="pd30">
{
banksMenu &&
<BanksMenu
banksMenu={banksMenu}
{...this.props}
{...this.state}
></BanksMenu>
}
<Switch {...this.props}>
<Route path={`/banks/gtask/:workid/${this.props.match.params.type}`}
render={
(props) => {
return (<Completetaskdetails {...this.props} {...props} {...this.state} datas={datas} />)
}
}></Route>
</Switch>
</div>
</div>
</React.Fragment>
)
}
}
export default Completetaskpage;

@ -1,16 +1,18 @@
.yslquestionbank1{
padding-top: 30px !important;
padding-right: 30px !important;
padding-left: 30px !important;
min-height: 500px !important;
}
.yslquesHeigth{
min-height: 500px !important;
width: 100% !important;
}
.yslquesmarkdowntext{
font-size: 16px;
color: #707070;
}
.yslquesmat26{
margin-top: 26px;
@ -55,4 +57,13 @@
.yslboomdivsys{
color: #666666;
font-size: 14px;
}
.pad20{
padding-bottom: 20px !important;
}
.mt24{
margin-top: 24px !important ;
}
.pd30{
margin-bottom: 30px;
}

@ -1,29 +1,32 @@
import React, {Component} from "react";
import {Link, NavLink} from 'react-router-dom';
import {WordsBtn, ActionBtn} from 'educoder';
import {BrowserRouter as Router,Route,Switch,Link, NavLin} from 'react-router-dom';
import {WordsBtn, ActionBtn,getImageUrl} from 'educoder';
import { Input,Checkbox,Table, Pagination, Modal,Menu, Tooltip,Spin,Button,Form } from "antd";
import axios from 'axios';
import {
notification
} from "antd";
import CoursesListType from '../coursesPublic/CoursesListType';
import Completetopicdetails from './Completetopicdetails';
import BanksMenu from '../../user/usersInfo/banks/banksMenu'
import Loadable from 'react-loadable';
import Loading from '../../../Loading';
import '../css/members.css';
import "../common/formCommon.css";
import '../css/Courses.css';
import '../css/busyWork.css';
import '../poll/pollStyle.css';
const Completetopicdetails = Loadable({
loader: () => import('./Completetopicdetails'),
loading: Loading,
})
class CompletetopicdePage extends Component {
//毕设选题内容详情
constructor(props) {
super(props);
// this.answerMdRef = React.createRef();
this.setState({
this.state={
workid:1,
isSpin:false,
datas:[],
})
visible:false,
banksMenu:undefined
}
}
@ -48,14 +51,41 @@ class CompletetopicdePage extends Component {
this.setState({
isSpin:true,
})
let url = `/homework_banks/${workids}.json`;
let url = `/gtopic_banks/${workids}.json`;
//
axios.get(url).then((response) => {
if(response){
if(response.data){
this.setState({
datas:response.data.informs,
})
datas:response.data,
});
try {
const crumbData={
title:response && response.data && response.data.name,
is_public:response && response.data && response.data.is_public,
crumbArray:[
{content:'详情'}
]
}
const menuData={
tab:'0',//tab选中的index
menuArray:[//tab以及tab路由
{to:`/banks/gtopic/${workids}/${this.props.match.params.type}?tab=0`,content:'内容详情'},
],
category:'gtopic',//毕设选题
tos:`/banks/gtopic/${workids}/edit/${this.props.match.params.type}?tab=0`,
id:workids,
is_public:response && response.data && response.data.is_public,
type:this.props.match.params.type,
authorize:response && response.data && response.data.authorize,
}
this.setState({
banksMenu:menuData
})
this.props.initPublic(crumbData,response.data);
}catch (e) {
}
}else {
this.setState({
datas:[],
@ -81,58 +111,37 @@ class CompletetopicdePage extends Component {
});
}
/// 确认是否下载
bindRef = ref => { this.child = ref }
///////////////教师截止
render() {
let {tab,datas,visible} = this.state;
const isAdmin = this.props.isAdmin();
// console.log(119)
let{
banksMenu
}=this.state
//
// const common={
// initPublic:this.initPublic,
// }
return (
<div className="newMain clearfix ">
<div className={"educontent mt10 mb20"} style={{width: "1200px"}}>
<div className="educontent mb20">
<p className="clearfix mb20 mt10">
<a className="btn colorgrey fl hovercolorblue ">题库</a>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<a
className=" btn colorgrey fl hovercolorblue "
>详情</a>
</p>
</div>
<div className="educontent mb20">
<p className=" fl color-black summaryname" style={{heigth: "33px"}}>
MySQL数据库编程开发实训基础篇
</p>
<CoursesListType
typelist={["公开"]}
/>
</div>
<div className="edu-back-white ">
<div className="stud-class-set bor-bottom-greyE ">
<div className=" clearfix edu-back-white poll_list">
<a className="active ml12" >内容详情</a>
<a className="fr color-blue font-16" >发送</a>
<a className="fr color-blue font-16" >编辑</a>
<a className="fr color-blue font-16" >删除</a>
</div>
</div>
</div>
<Completetopicdetails/>
{/*{parseInt(tab) === 1 ? <Completetopicdeswer/>:""}*/}
<React.Fragment>
<div className="pd30">
{
banksMenu &&
<BanksMenu
banksMenu={banksMenu}
{...this.props}
{...this.state}
></BanksMenu>
}
<Switch {...this.props}>
<Route path={`/banks/gtopic/:workid/${this.props.match.params.type}`}
render={
(props) => {
return (<Completetopicdetails {...this.props} {...props} {...this.state} datas={datas} />)
}
}></Route>
</Switch>
</div>
</div>
</React.Fragment>
)
}
}

@ -1,5 +1,5 @@
import React, {Component} from "react";
import { WordsBtn,on, off, trigger,markdownToHTML,getImageUrl} from 'educoder';
import { WordsBtn,on, off, trigger,MarkdownToHtml,getImageUrl} from 'educoder';
import {
Button,
Checkbox,
@ -12,6 +12,8 @@ import {
} from "antd";
import GroupPackage from '../groupjobbank/GroupPackage'
import './completetopicde.css';
import AttachmentsList from "../../../common/components/attachment/AttachmentList";
import NoneData from '../../courses/coursesPublic/NoneData'
class Completetopicdetails extends Component {
@ -56,52 +58,81 @@ class Completetopicdetails extends Component {
render() {
let{datas}=this.props;
return (
<div className=" clearfix edu-back-white " ref='targetElementTrainingjobsetting' style={{margin: "auto", minWidth:"1200px"}}>
<div className="bor-bottom-greyE">
<div className="yslquestionbank1">
<div id="MakedownHTML"className="markdown-body yslquesHeigth yslquesmarkdowntext" dangerouslySetInnerHTML={{__html: markdownToHTML("C++是C语言的面向对象扩展是C语言的一个超集同时也是历史最悠久、最受欢迎的程序设计语言之一。根据C++创始人Stroustrup的自述C++是一个“更好的C语言”。\n" +
"\n" +
"输入输出是计算机程序的基本功能。程序本质上是对数据进行处理的一系列操作,一般程序都可以分解为:“数据输入”、“数据处理”和“数据输出”三个步骤。标准输入输出(键盘输入和显示器输出)是程序的重要组成部分。\n" +
"\n" +
"本实训项目的主要目标是学习和掌握C++程序的基本结构和基本输入输出主要内容包括标准C语言自有的单个字符的输入输出、格式化的输入输出以及C++扩展的使用流对象的输入输出").replace(/▁/g, "▁▁▁")}}/>
<GroupPackage></GroupPackage>
{
datas.description===null?
""
:datas.description==="null"?
""
:
datas.description===""?
""
:
<MarkdownToHtml content={datas.description} selector="work_content" className="mb10 yslquesHeigth"></MarkdownToHtml>
}
{datas.attachment_list === undefined ?
(datas.description===undefined || datas.description===null||datas.description===""?
<NoneData></NoneData>
:
""
)
:
datas.attachment_list === null ?
(datas.description===undefined || datas.description===null||datas.description===""?
<NoneData></NoneData>
:
""
)
:
datas.attachment_list.length === 0 ?
(datas.description===undefined || datas.description===null||datas.description===""?
<NoneData></NoneData>
:
""
)
:
<div className="mb29px">
<AttachmentsList {...this.state} {...this.props} attachments={datas.attachment_list} ></AttachmentsList>
</div>
}
</div>
</div>
<div style={{width:"100%", padding: "36px"}}>
<div className="yslboomdivs">
<p>
<span className="yslboomdivsy">课题类型</span>
<span className="yslboomdivsys">设计</span>
<span className="yslboomdivsys">{datas&&datas.topic_type===1?"设计":datas&&datas.topic_type===2?"论文":datas&&datas.topic_type===3?"创作":"无"}</span>
</p>
<p>
<span className="yslboomdivsy">课题来源</span>
<span className="yslboomdivsys">生产/社会实践</span>
<span className="yslboomdivsys">{datas&&datas.topic_source===1?"生产/社会实际":datas&&datas.topic_source===2?"结合科研":datas&&datas.topic_source===3?"其它":"无"}</span>
</p>
<p>
<span className="yslboomdivsy">课题性质1</span>
<span className="yslboomdivsys">设计</span>
<span className="yslboomdivsys">{datas&&datas.topic_property_first===1?"真题":datas&&datas.topic_property_first===2?"模拟题":"无"}</span>
</p>
<p>
<span className="yslboomdivsy">课题性质2</span>
<span className="yslboomdivsys">设计</span>
<span className="yslboomdivsys">{datas&&datas.topic_property_second===1?"纵向课题":datas&&datas.topic_property_second===2?"横向课题":datas&&datas.topic_property_second===3?"自选":"无"}</span>
</p>
</div>
<div className="yslboomdivs">
<div className="yslboomdivs mt7">
<p>
<span className="yslboomdivsy">课题重复情况 </span>
<span className="yslboomdivsys">新需求</span>
<span className="yslboomdivsys">{datas&&datas.topic_repeat===1?"新题":datas&&datas.topic_repeat===2?"往届题,有新要求":datas&&datas.topic_repeat===3?"往届题,无新要求":"无"}</span>
</p>
<p>
<span className="yslboomdivsy">调研或实习地点</span>
<span className="yslboomdivsys">长沙</span>
<span className="yslboomdivsys">{datas&&datas.province}{datas&&datas.city}</span>
</p>
<p style={{width:"564px"}}>
<span className="yslboomdivsy">课题单位来源</span>
<span className="yslboomdivsys">湖南省据C++创始人Stroustrup有限公司</span>
<span className="yslboomdivsys">{datas&&datas.source_unit===undefined?"无":datas&&datas.source_unit===null?"无":datas&&datas.source_unit===""?"无":datas&&datas.source_unit==="null"?"无":datas.source_unit}</span>
</p>
</div>
</div>

@ -2,10 +2,10 @@
padding-top: 30px !important;
padding-right: 30px !important;
padding-left: 30px !important;
min-height: 500px !important;
}
.yslquesHeigth{
min-height: 500px !important;
width: 100% !important;
}
.yslquesmarkdowntext{
font-size: 16px;
@ -55,4 +55,11 @@
.yslboomdivsys{
color: #666666;
font-size: 14px;
}
.mb29px{
padding-top: 14px !important;
margin-bottom: 29px !important;
}
.pd30{
margin-bottom: 30px;
}

@ -94,7 +94,6 @@ class AppraiseModal extends Component{
width: 48px;
height: 16px;
font-size: 16px;
font-family: PingFangSC;
font-weight: 400;
color: rgba(5,16,26,1);
line-height: 16px;
@ -102,7 +101,6 @@ class AppraiseModal extends Component{
.newfont{
height: 16px;
font-size: 16px;
font-family: PingFangSC;
font-weight: 400;
color: rgba(5,16,26,1);
line-height: 16px;

@ -37,7 +37,7 @@ class ShixunChooseModal extends Component{
hometypepvisible:loading
})
let coursesId=this.props.match.params.coursesId;
let url ="/courses/"+coursesId+"/homework_commons/shixuns.json";
let url = this.props.shixunsUrl || "/courses/"+coursesId+"/homework_commons/shixuns.json";
axios.get(url, {
params: {
@ -76,7 +76,7 @@ class ShixunChooseModal extends Component{
})
let coursesId=this.props.match.params.coursesId;
let url ="/courses/"+coursesId+"/homework_commons/shixuns.json";
let url = this.props.shixunsUrl || "/courses/"+coursesId+"/homework_commons/shixuns.json";
axios.get(url).then((result)=>{
if(result.status===200){

@ -25,7 +25,7 @@ class ShixunModal extends Component{
hometypepvisible:true,
})
let coursesId=this.props.match.params.coursesId;
let url ="/courses/"+coursesId+"/homework_commons/shixuns.json";
let url = this.props.shixunsUrl || "/courses/"+coursesId+"/homework_commons/shixuns.json";
axios.get(url).then((result)=>{
if(result.status===200){
@ -51,7 +51,7 @@ class ShixunModal extends Component{
hometypepvisible:loading
})
let coursesId=this.props.match.params.coursesId;
let url ="/courses/"+coursesId+"/homework_commons/shixuns.json";
let url = this.props.shixunsUrl || "/courses/"+coursesId+"/homework_commons/shixuns.json";
axios.get(url, {
params: {

@ -1220,6 +1220,28 @@ samp {
color: #FE4F4C;
}
/* 毕设任务 */
.graduationTaskMenu>a{
display: block;
position: relative;
line-height: 72px;
font-size: 16px;
margin-right: 30px;
float: left;
}
.graduationTaskMenu>a.active:after{
position: absolute;
left: 0px;
height: 2px;
width: 100%;
content: '';
background: #4CACFF;
bottom: 0px;
font-weight:400;
}
.graduationTaskMenu>a.active{
color: #4CACFF!important;
}
/* end */
/* form表单包含多个item时 */

@ -359,23 +359,23 @@ class Elearning extends Component{
{/*简介*/}
<div className="clearfix pl30 pr30" style={{paddingBottom:"22px"}}>
<div style={{textAlign: "left",marginTop:"10px",paddingBottom: "10px"}}>
<span className=" color-dark-21 " style={{textAlign: "center",fontSize:"19px"}}>简介</span>
</div>
<div className="edu-back-white new_li editormd-html-preview " >
<p className="markdown-body fonttext " dangerouslySetInnerHTML={{__html: markdownToHTML(description).replace(/▁/g,"▁▁▁")}}>
</p>
</div>
{/*<div className="clearfix pl30 pr30" style={{paddingBottom:"22px"}}>*/}
{/* <div style={{textAlign: "left",marginTop:"10px",paddingBottom: "10px"}}>*/}
{/* <span className=" color-dark-21 " style={{textAlign: "center",fontSize:"19px"}}>简介</span>*/}
{/* </div>*/}
{/* <div className="edu-back-white new_li editormd-html-preview " >*/}
{/* <p className="markdown-body fonttext " dangerouslySetInnerHTML={{__html: markdownToHTML(description).replace(/▁/g,"▁▁▁")}}>*/}
{/* </p>*/}
{/* </div>*/}
</div>
{/*</div>*/}
</div>
<Spin size="large" spinning={isSpin} id={"cdiv"}>
<div className=" clearfix" style={{marginTop:"20px"}}>
<div className=" clearfix" style={this.props.isAdmin()===true?{marginTop:"0px"}:{marginTop:"20px"}}>
{
stages===undefined||stages===JSON.stringify("[]")||stages.length===0?
<NoneData></NoneData>

@ -338,7 +338,8 @@ class Exercise extends Component{
checkBoxValues:[],
Modalstypeloding:false
})
this.InitList(type,StudentList_value,page,limit)
this.InitList(type,StudentList_value,page,limit);
this.props.updataleftNavfun();
}
}).catch((error)=>{
console.log(error);

@ -53,11 +53,17 @@ class ExerciseDisplay extends Component{
componentDidMount = () => {
const Id = this.props.match.params.Id
if (Id) {
const url = `/exercises/${Id}.json`
const url = `/${this.props.urlPath || 'exercises'}/${Id}.json`
axios.get(url)
.then((response) => {
if (response.data.status == 0) {
this.setState({...response.data})
if (response.data.exercise) {
response.data.exercise.exercise_description = response.data.exercise.exercise_description || response.data.exercise.description
response.data.exercise.exercise_name = response.data.exercise.exercise_name || response.data.exercise.name
response.data.exercise.exercise_status = response.data.exercise.exercise_status == undefined ? 1 : response.data.exercise.exercise_status
this.setState({...response.data})
this.props.detailFetchCallback && this.props.detailFetchCallback(response);
} else {
this.props.detailFetchCallback && this.props.detailFetchCallback(response);
}
})
.catch(function (error) {

@ -5,440 +5,63 @@ import {
Slider, Button, Upload, Icon, Rate, Checkbox, message,
Row, Col, Select, Modal, Tooltip
} from 'antd';
import { Q_TYPE_SINGLE, Q_TYPE_MULTI, Q_TYPE_JUDGE, Q_TYPE_NULL, Q_TYPE_MAIN, Q_TYPE_SHIXUN } from './new/common'
import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor';
import axios from 'axios'
// import './board.css'
import "../common/formCommon.css"
// import "../common/formCommon.css"
// import { RouteHOC } from './common.js'
import CBreadcrumb from '../common/CBreadcrumb'
import {getUrl, ActionBtn} from 'educoder';
import SingleEditor from './new/SingleEditor'
import SingleDisplay from './new/SingleDisplay'
import JudgeEditor from './new/JudgeEditor'
import JudgeDisplay from './new/JudgeDisplay'
import NullEditor from './new/NullEditor'
import NullDisplay from './new/NullDisplay'
import MainEditor from './new/MainEditor'
import MainDisplay from './new/MainDisplay'
import ShixunEditor from './new/ShixunEditor'
import ShixunDisplay from './new/ShixunDisplay'
// import { Q_TYPE_SINGLE, Q_TYPE_MULTI, Q_TYPE_JUDGE, Q_TYPE_NULL, Q_TYPE_MAIN, Q_TYPE_SHIXUN } from './new/common'
// import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor';
// import CBreadcrumb from '../common/CBreadcrumb'
import {getUrl, ActionBtn, CBreadcrumb} from 'educoder';
// import SingleEditor from './new/SingleEditor'
// import SingleDisplay from './new/SingleDisplay'
// import JudgeEditor from './new/JudgeEditor'
// import JudgeDisplay from './new/JudgeDisplay'
// import NullEditor from './new/NullEditor'
// import NullDisplay from './new/NullDisplay'
// import MainEditor from './new/MainEditor'
// import MainDisplay from './new/MainDisplay'
// import ShixunEditor from './new/ShixunEditor'
// import ShixunDisplay from './new/ShixunDisplay'
import ShixunChooseModal from '../coursesPublic/ShixunChooseModal'
import update from 'immutability-helper'
import './new/common.css'
import '../css/Courses.css'
import ExerciseNewCommon from './ExerciseNewCommon'
const { TextArea } = Input;
const confirm = Modal.confirm;
const $ = window.$
const { Option } = Select;
const TITLE_MAX_LENGTH = 60;
class ExerciceNew extends Component{
constructor(props){
super(props);
this.state = {
exercise_questions: [],
exercise_name: '',
exercise_description: '',
exercise_types: {},
editMode: !this.props.match.params.Id,
}
}
// 已发布试卷编辑保存的确认弹框
changeScore = (question_id,answerArray) =>{
this.props.confirm({
content:'修改了标准答案',
subContent:"是否重新计算学生答题的成绩?",
onOk:()=>{
this.sureChangeScore(question_id,answerArray)
},
onCancel:()=>{
this.addSuccess();
}
})
}
// 已发布试卷修改答案确认修改分数
sureChangeScore = (question_id,answerArray) =>{
let url=`/exercise_questions/${question_id}/update_scores.json`
axios.post((url),{
standard_answers:answerArray
}).then((result)=>{
if(result){
this.props.showNotification(`${result.data.message}`);
this.addSuccess();
}
}).catch((error)=>{
console.log(error);
})
}
fetchExercise = () => {
const Id = this.props.match.params.Id
this.isEdit = !!Id
if (Id) {
const url = `/exercises/${Id}/edit.json`
axios.get(url)
.then((response) => {
if (response.data.status == 0) {
const { exercise, ...others } = response.data
this.setState({
...exercise,
...others,
editMode: false
})
}
})
.catch(function (error) {
console.log(error);
});
} else {
const courseId=this.props.match.params.coursesId;
const newUrl = `/courses/${courseId}/exercises/new.json`
axios.get(newUrl)
.then((response) => {
if (response.data.status == 0) {
this.setState({
...response.data
})
}
})
.catch(function (error) {
console.log(error);
});
}
}
componentDidMount = () => {
this.fetchExercise()
}
handleSubmit = (e) => {
}
onSaveExercise = () => {
const { exercise_name, exercise_description } = this.state;
const exercise_id = this.props.match.params.Id
const courseId = this.props.match.params.coursesId
if (this.isEdit) {
const editUrl = `/exercises/${exercise_id}.json`
axios.put(editUrl, {
exercise_name,
exercise_description
})
.then((response) => {
if (response.data.status == 0) {
this.setState({editMode: false})
this.props.showNotification('试卷编辑成功')
}
})
.catch(function (error) {
console.log(error);
});
} else {
const url = `/courses/${courseId}/exercises.json`
axios.post(url, {
exercise_name,
exercise_description
})
.then((response) => {
if (response.data.status == 0) {
this.setState({editMode: false})
this.props.showNotification('试卷新建成功')
const exercise_id = response.data.data.exercise_id;
this.isEdit = true;
this.props.history.replace(`/courses/${courseId}/exercises/${exercise_id}/edit`);
}
})
.catch(function (error) {
console.log(error);
});
}
}
exercise_name_change = (e) => {
this.setState({exercise_name: e.target.value})
}
exercise_description_change = (e) => {
this.setState({exercise_description: e.target.value})
}
// #问题的类型0为单选题1为多选题2为判断题3为填空题4为主观题5为实训题
_checkIsEditing = () => {
if (this.editingId && $(this.editingId).length ) {
this.props.showNotification('请先保存或取消当前正在编辑的问题。')
$("html").animate({ scrollTop: $(this.editingId).offset().top - 100})
return true
}
return false
}
onEditorCancel = () => {
this.editingId = null;
// 找到编辑或新建的item新建就删掉item编辑就isNew改为false
const { exercise_questions } = this.state
let index = -1;
for(let i = 0; i < exercise_questions.length; i++) {
if (exercise_questions[i].isNew == true) {
index = i;
break;
}
}
if (exercise_questions[index].question_id) { // 编辑
this.setState(
(prevState) => ({
exercise_questions : update(prevState.exercise_questions, {[index]: { isNew: {$set: false}}})
// update(prevState.exercise_questions, {$splice: [[index, 1]]})
})
)
} else { // 新建
this.setState(
(prevState) => ({
exercise_questions : update(prevState.exercise_questions, {$splice: [[index, 1]]})
})
)
}
}
addQuestion = (question_id_to_insert_after, type) => {
if (!this.isEdit) {
this.props.showNotification('请先输入试卷标题,并保存试卷')
return;
}
if (this._checkIsEditing()) {
return;
}
if (type == Q_TYPE_SHIXUN) {
this.addShixun(question_id_to_insert_after)
} else {
this.addEditingQuestion(type, question_id_to_insert_after)
}
}
chooseShixun = (array) => {
this.addEditingQuestion(Q_TYPE_SHIXUN, this.question_id_to_insert_after, {
shixun_id: array[0]
})
}
chooseShixunSuccess = () => {
this.refs.shixunChooseModal.setVisible(false)
}
addShixun = (question_id_to_insert_after) => {
if (!this.isEdit) {
this.props.showNotification('请先输入试卷标题,并保存试卷')
return;
}
// TODO 弹框选择实训
if (this._checkIsEditing()) {
return;
}
this.refs.shixunChooseModal.setVisible(true)
this.question_id_to_insert_after = question_id_to_insert_after;
return;
// 拉取实训items
this.addEditingQuestion(Q_TYPE_SHIXUN, question_id_to_insert_after, {
shixun_id: 50
})
}
editQestion = (index) => {
if (this._checkIsEditing()) {
return;
}
this.editingId = `#question_${index}`
this.setState(
(prevState) => ({
exercise_questions : update(prevState.exercise_questions, {[index]: { isNew: {$set: true}}})
})
)
}
onSort = (index, question_id, isUp) => {
if (this._checkIsEditing()) {
return;
}
const url = `/exercise_questions/${question_id}/up_down.json`
axios.post(url, { opr: isUp ? 'up' : 'down'})
.then((response) => {
if (response.data.status == 0) {
// this.props.showNotification('移动成功')
this.fetchExercise()
}
})
.catch(function (error) {
console.log(error);
});
}
onSortDown = (index, question_id) => {
this.onSort(index, question_id, false)
}
onSortUp = (index, question_id) => {
this.onSort(index, question_id, true)
}
getInitScore = (question_type, question_id_to_insert_after) => {
/**
1.每个题型的首个题目默认值规则如下
选择题5 01
判断题2 2
填空题2 3
简答题10 4
实训题每个关卡5分 5
*/
let init_question_score = 0;
if (question_type == 0 || question_type == 1) {
init_question_score = 5
} else if (question_type == 2) {
init_question_score = 2
} else if (question_type == 3) {
init_question_score = 2
} else if (question_type == 4) {
init_question_score = 10
} else if (question_type == 5) {
init_question_score = 5
}
const _indexBefore = question_id_to_insert_after ? this.findIndexById(question_id_to_insert_after) : this.state.exercise_questions.length - 1
for (let i = _indexBefore; i >= 0; i--) {
if(this.state.exercise_questions[i].question_type == question_type) {
init_question_score = this.state.exercise_questions[i].question_score
break;
}
}
return init_question_score;
}
addEditingQuestion = (question_type, question_id_to_insert_after, otherAttributes) => {
let init_question_score = this.getInitScore(question_type, question_id_to_insert_after)
let questionObj = {
question_type: question_type, // 需要这个通过类型判断
init_question_score: init_question_score,
isNew: true, // 新建或编辑用是否有id区分是新建还是编辑
question_id_to_insert_after,
...otherAttributes
}
const { exercise_questions } = this.state;
let new_exercise_questions = exercise_questions.slice(0)
let newIndex = new_exercise_questions.length;
if (question_id_to_insert_after) {
const _indexBefore = this.findIndexById(question_id_to_insert_after)
new_exercise_questions.splice(_indexBefore + 1, 0, questionObj)
newIndex = _indexBefore + 1
} else {
new_exercise_questions.push(questionObj)
}
this.editingId = `#question_${newIndex}`
this.setState({ exercise_questions: new_exercise_questions }, () => {
setTimeout(() => {
$(this.editingId).length && $("html").animate({ scrollTop: $(this.editingId).offset().top - 100})
}, 500)
})
}
findIndexById = (id) => {
const { exercise_questions } = this.state
for(let i = 0; i < exercise_questions.length; i++) {
if (exercise_questions[i].question_id == id) {
return i;
}
}
}
onQestionDelete = (question_id) => {
this.props.confirm({
content: `确认要删除这个问题吗?`,
onOk: () => {
const url = `/exercise_questions/${question_id}.json`
axios.delete(url)
.then((response) => {
if (response.data.status == 0) {
this.props.showNotification('删除成功')
this.fetchExercise()
// const { exercise_questions } = this.state
// const index = this.findIndexById(question_id)
// this.setState(
// (prevState) => ({
// exercise_questions : update(prevState.exercise_questions, {$splice: [[index, 1]]})
// })
// )
}
})
.catch(function (error) {
console.log(error);
});
}
})
}
addSuccess = () => {
this.editingId = null;
this.fetchExercise()
}
goToPreview = () => {
const exercise_id = this.props.match.params.Id
const courseId = this.props.match.params.coursesId
this.props.history.push(`/courses/${courseId}/exercises/${exercise_id}/student_exercise_list?tab=2`)
initData = (data) => {
this.setState({left_banner_id: data.left_banner_id})
}
render() {
let { exercise_name, exercise_description, course_id, exercise_types,
exercise_questions, left_banner_id } = this.state;
// if (this.isEdit && !exercise_types) {
// return ''
// }
// const { getFieldDecorator } = this.props.form;
const { q_counts, q_scores, q_doubles, q_doubles_scores, q_judges, q_judges_scores,
q_mains, q_mains_scores, q_nulls, q_nulls_scores, q_shixuns, q_shixuns_scores, q_singles, q_singles_scores} = exercise_types;
const formItemLayout = {
labelCol: {
xs: { span: 24 },
// sm: { span: 8 },
sm: { span: 24 },
},
wrapperCol: {
xs: { span: 24 },
// sm: { span: 16 },
sm: { span: 24 },
},
};
let { left_banner_id } = this.state;
const { current_user } = this.props
const isAdmin = this.props.isAdmin()
const courseId=this.props.match.params.coursesId;
const exercise_id = this.props.match.params.Id
const isEdit = this.isEdit
const commonHandler = {
onQestionDelete: this.onQestionDelete,
addSuccess: this.addSuccess,
addQuestion: this.addQuestion,
onEditorCancel: this.onEditorCancel,
changeScore:this.changeScore,
editQestion: this.editQestion,
onSortDown: this.onSortDown,
onSortUp: this.onSortUp,
displayCount: exercise_questions.length,
exercise_status: this.state.exercise_status,
exerciseIsPublish: this.state.exercise_status >= 2
}
return(
<div className="newMain exerciseNew">
<ShixunChooseModal
ref="shixunChooseModal"
chooseShixun={this.chooseShixun}
{...this.props}
singleChoose={true}
></ShixunChooseModal>
<style>{`
.courseForm .formBlock {
padding: 20px 30px 30px 30px;
border-bottom: 1px solid #EDEDED;
margin-bottom: 0px;
background: #fff;
}
.exerciseNew .markdown-body {
max-width: 1128px;
}
`}</style>
<div className="edu-class-container edu-position courseForm">
{ current_user && <CBreadcrumb items={[
{ to: current_user&&current_user.first_category_url, name: this.props.coursedata ? this.props.coursedata.name : ''},
@ -455,160 +78,12 @@ class ExerciceNew extends Component{
</a>
</p>
{!this.state.editMode && <div className="padding20-30" style={{ background: '#fff'}}>
<div className="displayTitle font-16">
<span>{exercise_name}</span>
<a className="fr mr6" onClick={() => { this.setState({editMode: true}) }} style={{ lineHeight: '32px'}}>
<Tooltip title="编辑"><i className="iconfont icon-bianjidaibeijing font-20 color-green"></i></Tooltip>
</a>
</div>
<div className="displayDescription color-grey-9" dangerouslySetInnerHTML={{__html: exercise_description}}
style={{whiteSpace: 'pre-wrap'}}
></div>
</div>}
{this.state.editMode && <Form {...formItemLayout} onSubmit={this.handleSubmit}>
<div className="formBlock" style={{paddingBottom: '2px',borderBottom:"none"}}>
<Form.Item
label="试卷标题"
required
className="topicTitle "
>
{/* {getFieldDecorator('subject', {
rules: [{
required: true, message: '请输入标题',
}, {
max: 20, message: '最大限制为20个字符',
}],
})( */}
<style>
{
`
.exercicenewinputysl .ant-input{
border-right: none !important;
height: 40px !important;
}
`
}
</style>
<Input placeholder={`请输入试卷标题,最大限制${TITLE_MAX_LENGTH}个字符`} maxLength={TITLE_MAX_LENGTH} className="mt5 exercicenewinputysl" value={exercise_name}
onChange={this.exercise_name_change} addonAfter={`${exercise_name ? exercise_name.length : 0}/${TITLE_MAX_LENGTH}`}
/>
{/* )} */}
</Form.Item>
<Form.Item
label="&nbsp;&nbsp;试卷须知"
>
{/* {getFieldDecorator('select_board_id', {
// initialValue: '3779',
})( */}
<TextArea placeholder="请在此输入本次试卷答题的相关说明最大限制100个字符" className="mt5" style={{height:"120px"}} value={exercise_description}
onChange={this.exercise_description_change}
/>
{/* )} */}
</Form.Item>
<Form.Item>
{/* defalutSubmitbtn */}
<a className="task-btn task-btn-orange fr mt4" style={{height: '30px', width: '70px'}}
onClick={this.onSaveExercise}
>保存</a>
{ this.isEdit && <a onClick={() => this.setState({editMode: false})} className="defalutCancelbtn fr mt4"
style={{height: '30px', width: '70px', fontSize: '14px', lineHeight: '30px', marginRight: '16px'}}>取消</a>}
{/* <Button type="primary" onClick={this.onSaveExercise} className="fr">保存</Button> */}
</Form.Item>
</div>
{/* <div className="clearfix mt30 mb30">
<a className="defalutCancelbtn fl" onClick={() => {}}>取消</ a>
</div> */}
</Form>}
<p className="clearfix padding20-30 color-grey-9">
<span className="fl">
{ !!q_singles && <span className="mr20">单选题{q_singles}{q_singles_scores}</span>}
{ !!q_doubles && <span className="mr20">多选题{q_doubles}{q_doubles_scores}</span>}
{ !!q_judges && <span className="mr20">判断题{q_judges}{q_judges_scores}</span>}
{ !!q_nulls && <span className="mr20">填空题{q_nulls}{q_nulls_scores}</span>}
{ !!q_mains && <span className="mr20">简答题{q_mains}{q_mains_scores}</span>}
{ !!q_shixuns && <span className="mr20">实训题{q_shixuns}{q_shixuns_scores}</span> }
</span>
<span className="fr">
{ !!q_counts &&
<span>
合计 <span className="color-blue">{q_counts}</span>
<span className={`${q_scores > 100 ? 'color-red font-bd' : 'color-orange'}`}>{q_scores}</span>
</span>
}
</span>
</p>
<div className="edu-back-white">
{ exercise_questions.map((item, index) => {
if (item.question_type == 0 || item.question_type == 1) {
if (item.isNew) {
return <SingleEditor {...this.props} {...item} index={index} {...commonHandler}></SingleEditor>
} else {
return <SingleDisplay {...this.props} {...item} index={index} {...commonHandler}
displayCount={exercise_questions.length}
></SingleDisplay>
}
} else if (item.question_type == 2) {
if (item.isNew) {
return <JudgeEditor {...this.props} {...item} index={index} {...commonHandler}></JudgeEditor>
} else {
return <JudgeDisplay {...this.props} {...item} index={index} {...commonHandler} ></JudgeDisplay>
}
} else if (item.question_type == 3) {
if (item.isNew) {
return <NullEditor {...this.props} {...item} index={index} {...commonHandler}></NullEditor>
} else {
return <NullDisplay {...this.props} {...item} index={index} {...commonHandler} ></NullDisplay>
}
} else if (item.question_type == 4) {
if (item.isNew) {
return <MainEditor {...this.props} {...item} index={index} {...commonHandler} ></MainEditor>
} else {
return <MainDisplay {...this.props} {...item} index={index} {...commonHandler} ></MainDisplay>
}
} else if (item.question_type == 5) {
if (item.isNew) {
return <ShixunEditor {...this.props} {...item} index={index} {...commonHandler}
chooseShixunSuccess={this.chooseShixunSuccess}
></ShixunEditor>
} else {
return <ShixunDisplay {...this.props} {...item} index={index} {...commonHandler} ></ShixunDisplay>
}
}
return <div></div>
})}
{!commonHandler.exerciseIsPublish && <div className="problemShow padding30">
<ActionBtn style="green" className="mr20" onClick={() => this.addQuestion(null, Q_TYPE_SINGLE)}>
<i className="iconfont icon-tianjiafangda color-white font-14 mr5" style={{ marginTop: '-1px', display: 'inline-block'}}></i>
</ActionBtn>
<ActionBtn style="green" className="mr20" onClick={() => this.addQuestion(null, Q_TYPE_JUDGE)}>
<i className="iconfont icon-tianjiafangda color-white font-14 mr5" style={{ marginTop: '-1px', display: 'inline-block'}}></i>
</ActionBtn>
<ActionBtn style="green" className="mr20" onClick={() => this.addQuestion(null, Q_TYPE_NULL)}>
<i className="iconfont icon-tianjiafangda color-white font-14 mr5" style={{ marginTop: '-1px', display: 'inline-block'}}></i>
</ActionBtn>
<ActionBtn style="green" className="mr20" onClick={() => this.addQuestion(null, Q_TYPE_MAIN)}>
<i className="iconfont icon-tianjiafangda color-white font-14 mr5" style={{ marginTop: '-1px', display: 'inline-block'}}></i>
</ActionBtn>
<ActionBtn style="green" className="mr20" onClick={() => this.addShixun(null)}>
<i className="iconfont icon-tianjiafangda color-white font-14 mr5" style={{ marginTop: '-1px', display: 'inline-block'}}></i>
</ActionBtn>
{exercise_id && <ActionBtn style="blue" className="fr" onClick={() => this.goToPreview()}>
{/* <i className="iconfont icon-tianjiafangda color-white font-14 mr5" style={{ marginTop: '-1px', display: 'inline-block'}}></i> */}
试卷预览
</ActionBtn>}
</div>}
</div>
<ExerciseNewCommon
{...this.props}
{...this.state}
isEdit={this.isEdit}
initData={this.initData}
></ExerciseNewCommon>
</div>
</div>
)

@ -0,0 +1,623 @@
import React,{ Component } from "react";
import {
Form, Input, InputNumber, Switch, Radio,
Slider, Button, Upload, Icon, Rate, Checkbox, message,
Row, Col, Select, Modal, Tooltip
} from 'antd';
import { Q_TYPE_SINGLE, Q_TYPE_MULTI, Q_TYPE_JUDGE, Q_TYPE_NULL, Q_TYPE_MAIN, Q_TYPE_SHIXUN } from './new/common'
import TPMMDEditor from '../../tpm/challengesnew/TPMMDEditor';
import axios from 'axios'
// import './board.css'
import "../common/formCommon.css"
// import { RouteHOC } from './common.js'
import CBreadcrumb from '../common/CBreadcrumb'
import {getUrl, ActionBtn} from 'educoder';
import SingleEditor from './new/SingleEditor'
import SingleDisplay from './new/SingleDisplay'
import JudgeEditor from './new/JudgeEditor'
import JudgeDisplay from './new/JudgeDisplay'
import NullEditor from './new/NullEditor'
import NullDisplay from './new/NullDisplay'
import MainEditor from './new/MainEditor'
import MainDisplay from './new/MainDisplay'
import ShixunEditor from './new/ShixunEditor'
import ShixunDisplay from './new/ShixunDisplay'
import ShixunChooseModal from '../coursesPublic/ShixunChooseModal'
import update from 'immutability-helper'
import './new/common.css'
import '../css/Courses.css'
const { TextArea } = Input;
const confirm = Modal.confirm;
const $ = window.$
const { Option } = Select;
const TITLE_MAX_LENGTH = 60;
class ExerciseNewCommon extends Component{
constructor(props){
super(props);
this.state = {
exercise_questions: [],
exercise_name: '',
exercise_description: '',
exercise_types: {},
editMode: !this.props.match.params.Id,
}
}
// 已发布试卷编辑保存的确认弹框
changeScore = (question_id,answerArray) =>{
this.props.confirm({
content:'修改了标准答案',
subContent:"是否重新计算学生答题的成绩?",
onOk:()=>{
this.sureChangeScore(question_id,answerArray)
},
onCancel:()=>{
this.addSuccess();
}
})
}
// 已发布试卷修改答案确认修改分数
sureChangeScore = (question_id,answerArray) =>{
let url=`/exercise_questions/${question_id}/update_scores.json`
axios.post((url),{
standard_answers:answerArray
}).then((result)=>{
if(result){
this.props.showNotification(`${result.data.message}`);
this.addSuccess();
}
}).catch((error)=>{
console.log(error);
})
}
fetchExercise = () => {
const Id = this.props.match.params.Id
this.isEdit = this.props.isEdit || !!Id
if (this.isEdit) {
const url = this.props.exercise_url ? `/${this.props.exercise_url }/${Id}.json` : `/exercises/${Id}/edit.json`
axios.get(url)
.then((response) => {
if (response.data.exercise) {
const { exercise, ...others } = response.data
exercise.exercise_name = exercise.exercise_name || exercise.name
exercise.exercise_description = exercise.exercise_description || exercise.description
this.setState({
...exercise,
...others,
editMode: false
})
this.props.initData && this.props.initData(response.data)
}
})
.catch(function (error) {
console.log(error);
});
} else {
const courseId=this.props.match.params.coursesId;
const newUrl = `/courses/${courseId}/exercises/new.json`
axios.get(newUrl)
.then((response) => {
if (response.data.status == 0) {
this.setState({
...response.data
})
}
})
.catch(function (error) {
console.log(error);
});
}
}
componentDidMount = () => {
this.fetchExercise()
}
handleSubmit = (e) => {
}
onSaveExercise = () => {
const { exercise_name, exercise_description } = this.state;
const exercise_id = this.props.match.params.Id
const courseId = this.props.match.params.coursesId
if (this.isEdit) {
// /exercise_banks/:id.json
const editUrl = `/${this.props.exercise_url ? this.props.exercise_url : 'exercises'}/${exercise_id}.json`
axios.put(editUrl, {
exercise_name,
exercise_description
})
.then((response) => {
if (response.data.status == 0) {
this.setState({editMode: false})
this.props.showNotification('试卷编辑成功')
}
})
.catch(function (error) {
console.log(error);
});
} else {
const url = `/courses/${courseId}/exercises.json`
axios.post(url, {
exercise_name,
exercise_description
})
.then((response) => {
if (response.data.status == 0) {
this.setState({editMode: false})
this.props.showNotification('试卷新建成功')
const exercise_id = response.data.data.exercise_id;
this.isEdit = true;
this.props.history.replace(`/courses/${courseId}/exercises/${exercise_id}/edit`);
}
})
.catch(function (error) {
console.log(error);
});
}
}
exercise_name_change = (e) => {
this.setState({exercise_name: e.target.value})
}
exercise_description_change = (e) => {
this.setState({exercise_description: e.target.value})
}
// #问题的类型0为单选题1为多选题2为判断题3为填空题4为主观题5为实训题
_checkIsEditing = () => {
if (this.editingId && $(this.editingId).length ) {
this.props.showNotification('请先保存或取消当前正在编辑的问题。')
$("html").animate({ scrollTop: $(this.editingId).offset().top - 100})
return true
}
return false
}
onEditorCancel = () => {
this.editingId = null;
// 找到编辑或新建的item新建就删掉item编辑就isNew改为false
const { exercise_questions } = this.state
let index = -1;
for(let i = 0; i < exercise_questions.length; i++) {
if (exercise_questions[i].isNew == true) {
index = i;
break;
}
}
if (exercise_questions[index].question_id) { // 编辑
this.setState(
(prevState) => ({
exercise_questions : update(prevState.exercise_questions, {[index]: { isNew: {$set: false}}})
// update(prevState.exercise_questions, {$splice: [[index, 1]]})
})
)
} else { // 新建
this.setState(
(prevState) => ({
exercise_questions : update(prevState.exercise_questions, {$splice: [[index, 1]]})
})
)
}
}
addQuestion = (question_id_to_insert_after, type) => {
if (!this.isEdit) {
this.props.showNotification('请先输入试卷标题,并保存试卷')
return;
}
if (this._checkIsEditing()) {
return;
}
if (type == Q_TYPE_SHIXUN) {
this.addShixun(question_id_to_insert_after)
} else {
this.addEditingQuestion(type, question_id_to_insert_after)
}
}
chooseShixun = (array) => {
this.addEditingQuestion(Q_TYPE_SHIXUN, this.question_id_to_insert_after, {
shixun_id: array[0]
})
}
chooseShixunSuccess = () => {
this.refs.shixunChooseModal.setVisible(false)
}
addShixun = (question_id_to_insert_after) => {
if (!this.isEdit) {
this.props.showNotification('请先输入试卷标题,并保存试卷')
return;
}
// TODO 弹框选择实训
if (this._checkIsEditing()) {
return;
}
this.refs.shixunChooseModal.setVisible(true)
this.question_id_to_insert_after = question_id_to_insert_after;
return;
// 拉取实训items
this.addEditingQuestion(Q_TYPE_SHIXUN, question_id_to_insert_after, {
shixun_id: 50
})
}
editQestion = (index) => {
if (this._checkIsEditing()) {
return;
}
this.editingId = `#question_${index}`
this.setState(
(prevState) => ({
exercise_questions : update(prevState.exercise_questions, {[index]: { isNew: {$set: true}}})
})
)
}
onSort = (index, question_id, isUp) => {
if (this._checkIsEditing()) {
return;
}
const url = `/${this.props.exercise_url_questions || 'exercise_questions'}/${question_id}/up_down.json`
axios.post(url, { opr: isUp ? 'up' : 'down'})
.then((response) => {
if (response.data.status == 0) {
// this.props.showNotification('移动成功')
this.fetchExercise()
}
})
.catch(function (error) {
console.log(error);
});
}
onSortDown = (index, question_id) => {
this.onSort(index, question_id, false)
}
onSortUp = (index, question_id) => {
this.onSort(index, question_id, true)
}
getInitScore = (question_type, question_id_to_insert_after) => {
/**
1.每个题型的首个题目默认值规则如下
选择题5 01
判断题2 2
填空题2 3
简答题10 4
实训题每个关卡5分 5
*/
let init_question_score = 0;
if (question_type == 0 || question_type == 1) {
init_question_score = 5
} else if (question_type == 2) {
init_question_score = 2
} else if (question_type == 3) {
init_question_score = 2
} else if (question_type == 4) {
init_question_score = 10
} else if (question_type == 5) {
init_question_score = 5
}
const _indexBefore = question_id_to_insert_after ? this.findIndexById(question_id_to_insert_after) : this.state.exercise_questions.length - 1
for (let i = _indexBefore; i >= 0; i--) {
if(this.state.exercise_questions[i].question_type == question_type) {
init_question_score = this.state.exercise_questions[i].question_score
break;
}
}
return init_question_score;
}
addEditingQuestion = (question_type, question_id_to_insert_after, otherAttributes) => {
let init_question_score = this.getInitScore(question_type, question_id_to_insert_after)
let questionObj = {
question_type: question_type, // 需要这个通过类型判断
init_question_score: init_question_score,
isNew: true, // 新建或编辑用是否有id区分是新建还是编辑
question_id_to_insert_after,
...otherAttributes
}
const { exercise_questions } = this.state;
let new_exercise_questions = exercise_questions.slice(0)
let newIndex = new_exercise_questions.length;
if (question_id_to_insert_after) {
const _indexBefore = this.findIndexById(question_id_to_insert_after)
new_exercise_questions.splice(_indexBefore + 1, 0, questionObj)
newIndex = _indexBefore + 1
} else {
new_exercise_questions.push(questionObj)
}
this.editingId = `#question_${newIndex}`
this.setState({ exercise_questions: new_exercise_questions }, () => {
setTimeout(() => {
$(this.editingId).length && $("html").animate({ scrollTop: $(this.editingId).offset().top - 100})
}, 500)
})
}
findIndexById = (id) => {
const { exercise_questions } = this.state
for(let i = 0; i < exercise_questions.length; i++) {
if (exercise_questions[i].question_id == id) {
return i;
}
}
}
onQestionDelete = (question_id) => {
this.props.confirm({
content: `确认要删除这个问题吗?`,
onOk: () => {
const url = `/${this.props.exercise_url_questions || 'exercise_questions'}/${question_id}.json`
axios.delete(url)
.then((response) => {
if (response.data.status == 0) {
this.props.showNotification('删除成功')
this.fetchExercise()
// const { exercise_questions } = this.state
// const index = this.findIndexById(question_id)
// this.setState(
// (prevState) => ({
// exercise_questions : update(prevState.exercise_questions, {$splice: [[index, 1]]})
// })
// )
}
})
.catch(function (error) {
console.log(error);
});
}
})
}
addSuccess = () => {
this.editingId = null;
this.fetchExercise()
}
goToPreview = () => {
const exercise_id = this.props.match.params.Id
const courseId = this.props.match.params.coursesId
this.props.history.push(`/courses/${courseId}/exercises/${exercise_id}/student_exercise_list?tab=2`)
}
getAddQuestionUrl = () => {
const Id = this.props.match.params.Id
const url = this.props.exercise_url_questions ? `/${this.props.exercise_url_questions}.json` : `/exercises/${Id}/exercise_questions.json`
return url;
}
getEditQuestionUrl = (question_id) => {
const editUrl = this.props.exercise_url_questions ? `/${this.props.exercise_url_questions}/${question_id}.json` : `/exercise_questions/${question_id}.json`
return editUrl;
}
render() {
let { exercise_name, exercise_description, course_id, exercise_types,
exercise_questions, left_banner_id } = this.state;
// if (this.isEdit && !exercise_types) {
// return ''
// }
// const { getFieldDecorator } = this.props.form;
const { q_counts, q_scores, q_doubles, q_doubles_scores, q_judges, q_judges_scores,
q_mains, q_mains_scores, q_nulls, q_nulls_scores, q_shixuns, q_shixuns_scores, q_singles, q_singles_scores} = exercise_types;
const formItemLayout = {
labelCol: {
xs: { span: 24 },
// sm: { span: 8 },
sm: { span: 24 },
},
wrapperCol: {
xs: { span: 24 },
// sm: { span: 16 },
sm: { span: 24 },
},
};
const { current_user } = this.props
const isAdmin = this.props.isAdmin()
const courseId=this.props.match.params.coursesId;
const exercise_id = this.props.match.params.Id
const isEdit = this.isEdit
const commonHandler = {
onQestionDelete: this.onQestionDelete,
addSuccess: this.addSuccess,
addQuestion: this.addQuestion,
onEditorCancel: this.onEditorCancel,
changeScore:this.changeScore,
editQestion: this.editQestion,
onSortDown: this.onSortDown,
onSortUp: this.onSortUp,
displayCount: exercise_questions.length,
exercise_status: this.state.exercise_status,
exerciseIsPublish: this.state.exercise_status >= 2,
getAddQuestionUrl: this.getAddQuestionUrl,
getEditQuestionUrl: this.getEditQuestionUrl,
exercise_url: this.props.exercise_url,
}
return(
<React.Fragment>
<ShixunChooseModal
ref="shixunChooseModal"
chooseShixun={this.chooseShixun}
{...this.props}
singleChoose={true}
shixunsUrl={this.props.shixunsUrl}
></ShixunChooseModal>
<style>{`
.courseForm .formBlock {
padding: 20px 30px 30px 30px;
border-bottom: 1px solid #EDEDED;
margin-bottom: 0px;
background: #fff;
}
.exerciseNew .markdown-body {
max-width: 1128px;
}
`}</style>
{!this.state.editMode && <div className="padding20-30" style={{ background: '#fff'}}>
<div className="displayTitle font-16">
<span>{exercise_name}</span>
<a className="fr mr6" onClick={() => { this.setState({editMode: true}) }} style={{ lineHeight: '32px'}}>
<Tooltip title="编辑"><i className="iconfont icon-bianjidaibeijing font-20 color-green"></i></Tooltip>
</a>
</div>
<div className="displayDescription color-grey-9" dangerouslySetInnerHTML={{__html: exercise_description}}
style={{whiteSpace: 'pre-wrap'}}
></div>
</div>}
{this.state.editMode && <Form {...formItemLayout} onSubmit={this.handleSubmit}>
<div className="formBlock" style={{paddingBottom: '2px',borderBottom:"none"}}>
<Form.Item
label="试卷标题"
required
className="topicTitle "
>
{/* {getFieldDecorator('subject', {
rules: [{
required: true, message: '请输入标题',
}, {
max: 20, message: '最大限制为20个字符',
}],
})( */}
<style>
{
`
.exercicenewinputysl .ant-input{
border-right: none !important;
height: 40px !important;
}
`
}
</style>
<Input placeholder={`请输入试卷标题,最大限制${TITLE_MAX_LENGTH}个字符`} maxLength={TITLE_MAX_LENGTH} className="mt5 exercicenewinputysl" value={exercise_name}
onChange={this.exercise_name_change} addonAfter={`${exercise_name ? exercise_name.length : 0}/${TITLE_MAX_LENGTH}`}
/>
{/* )} */}
</Form.Item>
<Form.Item
label="&nbsp;&nbsp;试卷须知"
>
{/* {getFieldDecorator('select_board_id', {
// initialValue: '3779',
})( */}
<TextArea placeholder="请在此输入本次试卷答题的相关说明最大限制100个字符" className="mt5" style={{height:"120px"}} value={exercise_description}
onChange={this.exercise_description_change}
/>
{/* )} */}
</Form.Item>
<Form.Item>
{/* defalutSubmitbtn */}
<a className="task-btn task-btn-orange fr mt4" style={{height: '30px', width: '70px'}}
onClick={this.onSaveExercise}
>保存</a>
{ this.isEdit && <a onClick={() => this.setState({editMode: false})} className="defalutCancelbtn fr mt4"
style={{height: '30px', width: '70px', fontSize: '14px', lineHeight: '30px', marginRight: '16px'}}>取消</a>}
{/* <Button type="primary" onClick={this.onSaveExercise} className="fr">保存</Button> */}
</Form.Item>
</div>
{/* <div className="clearfix mt30 mb30">
<a className="defalutCancelbtn fl" onClick={() => {}}>取消</ a>
</div> */}
</Form>}
<p className="clearfix padding20-30 color-grey-9">
<span className="fl">
{ !!q_singles && <span className="mr20">单选题{q_singles}{q_singles_scores}</span>}
{ !!q_doubles && <span className="mr20">多选题{q_doubles}{q_doubles_scores}</span>}
{ !!q_judges && <span className="mr20">判断题{q_judges}{q_judges_scores}</span>}
{ !!q_nulls && <span className="mr20">填空题{q_nulls}{q_nulls_scores}</span>}
{ !!q_mains && <span className="mr20">简答题{q_mains}{q_mains_scores}</span>}
{ !!q_shixuns && <span className="mr20">实训题{q_shixuns}{q_shixuns_scores}</span> }
</span>
<span className="fr">
{ !!q_counts &&
<span>
合计 <span className="color-blue">{q_counts}</span>
<span className={`${q_scores > 100 ? 'color-red font-bd' : 'color-orange'}`}>{q_scores}</span>
</span>
}
</span>
</p>
<div className="edu-back-white">
{ exercise_questions.map((item, index) => {
if (item.question_type == 0 || item.question_type == 1) {
if (item.isNew) {
return <SingleEditor {...this.props} {...item} index={index} {...commonHandler}></SingleEditor>
} else {
return <SingleDisplay {...this.props} {...item} index={index} {...commonHandler}
displayCount={exercise_questions.length}
></SingleDisplay>
}
} else if (item.question_type == 2) {
if (item.isNew) {
return <JudgeEditor {...this.props} {...item} index={index} {...commonHandler}></JudgeEditor>
} else {
return <JudgeDisplay {...this.props} {...item} index={index} {...commonHandler} ></JudgeDisplay>
}
} else if (item.question_type == 3) {
if (item.isNew) {
return <NullEditor {...this.props} {...item} index={index} {...commonHandler}></NullEditor>
} else {
return <NullDisplay {...this.props} {...item} index={index} {...commonHandler} ></NullDisplay>
}
} else if (item.question_type == 4) {
if (item.isNew) {
return <MainEditor {...this.props} {...item} index={index} {...commonHandler} ></MainEditor>
} else {
return <MainDisplay {...this.props} {...item} index={index} {...commonHandler} ></MainDisplay>
}
} else if (item.question_type == 5) {
if (item.isNew) {
return <ShixunEditor {...this.props} {...item} index={index} {...commonHandler}
chooseShixunSuccess={this.chooseShixunSuccess}
></ShixunEditor>
} else {
return <ShixunDisplay {...this.props} {...item} index={index} {...commonHandler} ></ShixunDisplay>
}
}
return <div></div>
})}
{!commonHandler.exerciseIsPublish && <div className="problemShow padding30">
<ActionBtn style="green" className="mr20" onClick={() => this.addQuestion(null, Q_TYPE_SINGLE)}>
<i className="iconfont icon-tianjiafangda color-white font-14 mr5" style={{ marginTop: '-1px', display: 'inline-block'}}></i>
</ActionBtn>
<ActionBtn style="green" className="mr20" onClick={() => this.addQuestion(null, Q_TYPE_JUDGE)}>
<i className="iconfont icon-tianjiafangda color-white font-14 mr5" style={{ marginTop: '-1px', display: 'inline-block'}}></i>
</ActionBtn>
<ActionBtn style="green" className="mr20" onClick={() => this.addQuestion(null, Q_TYPE_NULL)}>
<i className="iconfont icon-tianjiafangda color-white font-14 mr5" style={{ marginTop: '-1px', display: 'inline-block'}}></i>
</ActionBtn>
<ActionBtn style="green" className="mr20" onClick={() => this.addQuestion(null, Q_TYPE_MAIN)}>
<i className="iconfont icon-tianjiafangda color-white font-14 mr5" style={{ marginTop: '-1px', display: 'inline-block'}}></i>
</ActionBtn>
<ActionBtn style="green" className="mr20" onClick={() => this.addShixun(null)}>
<i className="iconfont icon-tianjiafangda color-white font-14 mr5" style={{ marginTop: '-1px', display: 'inline-block'}}></i>
</ActionBtn>
{exercise_id && !this.props.hidePreviewButton && <ActionBtn style="blue" className="fr" onClick={() => this.goToPreview()}>
{/* <i className="iconfont icon-tianjiafangda color-white font-14 mr5" style={{ marginTop: '-1px', display: 'inline-block'}}></i> */}
试卷预览
</ActionBtn>}
{this.props.bottomSection && this.props.bottomSection}
</div>}
</div>
</React.Fragment>
)
}
}
// RouteHOC()
export default (ExerciseNewCommon);

@ -38,7 +38,6 @@ class ExerciseReviewAndAnswer extends Component{
super(props);
this.state={
data:undefined,
userName:undefined,
questionPanelFixed:false,
e_ReviewInfo:undefined,
e_AnswerInfo:undefined,
@ -166,7 +165,6 @@ class ExerciseReviewAndAnswer extends Component{
getInfo=()=>{
this.setState({
courseName:this.props.current_user.course_name,
userName:this.props.current_user.username,
isSpin:true
})
@ -493,7 +491,6 @@ class ExerciseReviewAndAnswer extends Component{
let{
data,
questionPanelFixed,
userName,
courseName,
exercise,
exercise_types,
@ -551,7 +548,7 @@ class ExerciseReviewAndAnswer extends Component{
}
.setRadioStyle .ant-radio,.setRadioStyle .ant-checkbox{
height:16px;
margin-top:2px;
margin-top:5px;
}
.standardAnswer.editormd-html-preview,.answerStyle.editormd-html-preview{
width:100%!important

@ -86,7 +86,7 @@ class JudgeDisplay extends Component{
// 单选
return (
<div key={optionIndex} className="fl mr30 df">
<Radio disabled checked={item.standard_boolean}></Radio>
<Radio disabled className="lineh-25" checked={item.standard_boolean}></Radio>
{/* <span>{item.choice_text}</span> */}
<MarkdownToHtml content={item.choice_text} selector={'judge_' + (index + 1) + optionIndex}
className=""

@ -85,7 +85,7 @@ class SingleEditor extends Component{
}*/
const Id = this.props.match.params.Id
if (question_id) {
const editUrl = `/exercise_questions/${question_id}.json`
const editUrl = this.props.getEditQuestionUrl(question_id);
axios.put(editUrl, {
question_title,
question_type: 2,
@ -105,9 +105,10 @@ class SingleEditor extends Component{
console.log(error);
});
} else {
const url = `/exercises/${Id}/exercise_questions.json`
const url = this.props.getAddQuestionUrl();
axios.post(url, {
exercise_bank_id: Id,
question_title,
question_type: 2,
question_score,

@ -70,7 +70,7 @@ class MainEditor extends Component{
}*/
const Id = this.props.match.params.Id
if (question_id) {
const editUrl = `/exercise_questions/${question_id}.json`
const editUrl = this.props.getEditQuestionUrl(question_id);
axios.put(editUrl, {
question_title,
question_type: 4,
@ -88,9 +88,10 @@ class MainEditor extends Component{
console.log(error);
});
} else {
const url = `/exercises/${Id}/exercise_questions.json`
const url = this.props.getAddQuestionUrl();
axios.post(url, {
exercise_bank_id: Id,
question_title,
question_type: 4,
question_score,

@ -92,6 +92,9 @@ class NullDisplay extends Component{
.answerRow {
padding: 1px 0;
}
.answers .markdown-body > p{
line-height:20px;
}
`}</style>
<QestionDisplayHeader {...this.props}></QestionDisplayHeader>

@ -124,7 +124,7 @@ class NullEditor extends Component{
}*/
const Id = this.props.match.params.Id
if (question_id) {
const editUrl = `/exercise_questions/${question_id}.json`
const editUrl = this.props.getEditQuestionUrl(question_id);
axios.put(editUrl, {
question_title,
question_type: 3,
@ -145,9 +145,10 @@ class NullEditor extends Component{
console.log(error);
});
} else {
const url = `/exercises/${Id}/exercise_questions.json`
const url = this.props.getAddQuestionUrl();
axios.post(url, {
exercise_bank_id: Id,
question_title,
question_type: 3,
question_score,

@ -113,7 +113,7 @@ class ShixunEditor extends Component{
}*/
const Id = this.props.match.params.Id
if (question_id) {
const editUrl = `/exercise_questions/${question_id}.json`
const editUrl = this.props.getEditQuestionUrl(question_id);
axios.put(editUrl, {
question_title,
question_type: 5,
@ -131,9 +131,10 @@ class ShixunEditor extends Component{
console.log(error);
});
} else {
const url = `/exercises/${Id}/exercise_questions.json`
const url = this.props.getAddQuestionUrl();
axios.post(url, {
exercise_bank_id: Id,
question_title,
question_type: 5,
question_scores,
@ -155,10 +156,10 @@ class ShixunEditor extends Component{
this.props.onEditorCancel()
}
componentDidMount = () => {
const { shixun_id } = this.props;
const { shixun_id, exercise_url } = this.props;
// shixun_id
const Id = this.props.match.params.Id
const url = `/exercises/${Id}/commit_shixun.json`
const url = `/${exercise_url || 'exercises'}/${Id}/commit_shixun.json`
axios.get(url, {
params: {
shixun_id

@ -99,7 +99,7 @@ class SingleDisplay extends Component{
if (question_type == 0) { // 单选
return (
<div className="mb10 clearfix" key={optionIndex}>
<Radio disabled className="fl lineh-20" checked={item.standard_boolean}>{prefix}</Radio>
<Radio disabled className="fl lineh-25" checked={item.standard_boolean}>{prefix}</Radio>
<MarkdownToHtml content={item.choice_text} selector={'single_' + (index + 1) + '' + (optionIndex + 1)} style={{ float: 'left', display: 'inline-block' }}
></MarkdownToHtml>
@ -110,7 +110,7 @@ class SingleDisplay extends Component{
return (
<div className="mb10 clearfix" key={optionIndex}>
<Checkbox disabled className="fl lineh-20" checked={item.standard_boolean}>{prefix}</Checkbox>
<Checkbox disabled className="fl lineh-25 mr8" checked={item.standard_boolean}>{prefix}</Checkbox>
<MarkdownToHtml content={item.choice_text} selector={'single_' + (index + 1)+ '' + (optionIndex + 1)} style={{ float: 'left', display: 'inline-block' }}
></MarkdownToHtml>

@ -123,7 +123,7 @@ class SingleEditor extends Component{
}*/
const Id = this.props.match.params.Id
if (question_id) {
const editUrl = `/exercise_questions/${question_id}.json`
const editUrl = this.props.getEditQuestionUrl(question_id);
axios.put(editUrl, {
question_title,
question_type: answerArray.length > 1 ? 1 : 0,
@ -143,9 +143,10 @@ class SingleEditor extends Component{
console.log(error);
});
} else {
const url = `/exercises/${Id}/exercise_questions.json`
const url = this.props.getAddQuestionUrl();
axios.post(url, {
exercise_bank_id: Id,
question_title,
question_type: answerArray.length > 1 ? 1 : 0,
question_score,

@ -52,7 +52,7 @@ class Multiple extends Component{
return(
<p className="clearfix mb15 df">
<Checkbox className="lineh-15 df mr8 setRadioStyle" value={item.choice_id}>
<span className="fl mr3 lineh-20">{prefix}</span>
<span className="fl mr3 lineh-25">{prefix}</span>
<MarkdownToHtml content={item.choice_text} selector={'multiple_' + (this.props.index + 1) + (key + 1)}
className="flex1" style={{display:"inline-block"}}
></MarkdownToHtml>

@ -46,10 +46,10 @@ class single extends Component{
let prefix = isJudge ? undefined : `${tagArray[key]}.`
return(
<p className={parseInt(questionType.question_type) == 0 ? "clearfix mb15" : "fl mr40"}>
<Radio className="df lineh-20 setRadioStyle" value={item.choice_id}>
<span className="fl mr3">{prefix}</span>
<Radio className="df lineh-25 setRadioStyle" value={item.choice_id}>
<span className="fl mr3 lineh-25">{prefix}</span>
<MarkdownToHtml content={item.choice_text} selector={'single_' + (this.props.index + 1) + (key + 1)}
className="flex1" style={{display:"inline-block", 'margin-top': '-1px'}}
className="flex1" style={{display:"inline-block"}}
></MarkdownToHtml>
</Radio>
</p>

@ -6,6 +6,7 @@ import axios from 'axios';
import TPMMDEditor from "../../tpm/challengesnew/TPMMDEditor";
import moment from "../new/CoursesNew";
import Fileslistitem from "../Resource/Fileslistitem";
import Modals from "../../modals/Modals";
// 公告栏
class Bullsubdirectory extends Component{
constructor(props){
@ -19,6 +20,10 @@ class Bullsubdirectory extends Component{
addonAfter:0,
eduintits:"",
informs:[],
Modalstype:false,
Modalstopval:"是否确认删除?",
ModalCancel:"",
ModalSave:"",
}
@ -47,6 +52,61 @@ class Bullsubdirectory extends Component{
}
setModeltrue=()=>{
this.setState({
Modalstype:true,
Modalstopval:"是否确认删除?",
ModalCancel:this.cancelmodel,
ModalSave:this.saveonOpen,
})
}
cancelmodel=()=>{
//取消
this.setState({
Modalstype:false,
Modalstopval:"是否确认删除?",
ModalCancel:"",
ModalSave:"",
})
}
saveonOpen=()=>{
//确认
// /
// 删除公告
var id=this.props.match.params.coursesId
const url =`/courses/${id}/delete_informs.json`;
axios.delete(url, { data: {
inform_id: this.props.id
}})
.then((response) => {
if(response){
if(response.data){
if(response.data.status===0){
this.setState({
Modalstype:false,
Modalstopval:"是否确认删除?",
ModalCancel:"",
ModalSave:"",
})
this.props.showNotification(`删除成功`);
this.props.getinputdata();
}else{
this.props.showNotification(`删除失败`);
}
}else{
this.props.showNotification(`删除失败`);
}
}
})
.catch(function (error) {
console.log(error);
this.props.showNotification(`删除失败`);
});
}
bianji = (bians)=>{
this.setState({
@ -113,7 +173,7 @@ class Bullsubdirectory extends Component{
}
var url = `/courses/${id}/update_informs.json`;
axios.post(url,{
inform_id:this.state.id,
inform_id:this.props.id,
name:titname,
description:values.description,
}).then((result) => {
@ -154,12 +214,20 @@ class Bullsubdirectory extends Component{
render(){
let{description,whethertoeditysl,addonAfter,eduintits,informs,isSpinysl} =this.state;
let{myname,mydescription}=this.props;
let{myname,mydescription,id}=this.props;
const {getFieldDecorator} = this.props.form;
// console.log("Bullsubdirectory");
// console.log(this.props.isAdmin());
// console.log(this.props.yslbool);
return(
<React.Fragment >
<div >
{this.state.Modalstype&&this.state.Modalstype===true?<Modals
modalsType={this.state.Modalstype}
modalsTopval={this.state.Modalstopval}
modalCancel={this.state.ModalCancel}
modalSave={this.state.ModalSave}
/>:""}
<Spin size="large" spinning={isSpinysl} >
<div className="edu-back-white ">
{
@ -171,11 +239,30 @@ class Bullsubdirectory extends Component{
<span className="ysltitbt">{myname}</span>
</div>
<div>
<span className="fr pr25 yslbianji">
<span className="fr yslbianji" style={{marginRight:"17px"}}>
{
this.props.isAdmin() === true ?
this.props.isClassManagement() === true ?
(this.props.yslbool===false?
<Tooltip placement="bottom" title={<div>
编辑
</div>}>
<i className="iconfont icon-bianji1 newbianji1" onClick={()=>this.bianji(true)}></i>
</Tooltip>
:
""
)
:""
}
</span>
<span className="fr yslbianji" style={{marginRight:"22px"}}>
{
this.props.isClassManagement() === true ?
(this.props.yslbool===false?
<Tooltip placement="bottom" title={<div>
删除
</div>}>
<i className="iconfont icon-shanchu newbianji1" style={{ color: "#4CACFF"}} onClick={()=>this.setModeltrue(true)}></i>
</Tooltip>
:
""
)

@ -231,7 +231,7 @@ class Eduinforms extends Component{
<p style={{height: '20px'}}>
<span className="font-18 fl color-dark-21">公告栏</span>
{
this.props.isAdmin()===true?
this.props.isClassManagement()===true?
(this.state.yslbool===false?
<li className="btn colorblue font-16 fr bluebkbk pointer"
onClick={() => this.bianji(true)}>

@ -215,14 +215,14 @@ class GraduateTaskItem extends Component{
<h6>
{
this.props.isAdmin?<a href={"/courses/"+coursesId+"/graduation_tasks/"+categoryid+"/"+taskid+"/list"}
this.props.isAdmin?<Link to={"/courses/"+coursesId+"/graduation_tasks/"+categoryid+"/detail/"+taskid+"/list"}
title={discussMessage.name}
className="fl mt3 font-16 font-bd color-dark maxwidth580">{discussMessage.name}</a>:""
className="fl mt3 font-16 font-bd color-dark maxwidth580">{discussMessage.name}</Link>:""
}
{
this.props.isStudent? <a href={"/courses/"+coursesId+"/graduation_tasks/"+categoryid+"/"+taskid+"/list"}
this.props.isStudent? <Link to={"/courses/"+coursesId+"/graduation_tasks/"+categoryid+"/detail/"+taskid+"/list"}
title={discussMessage.name}
className="fl mt3 font-16 font-bd color-dark maxwidth580">{discussMessage.name}</a>:""
className="fl mt3 font-16 font-bd color-dark maxwidth580">{discussMessage.name}</Link>:""
}
{
@ -280,7 +280,7 @@ class GraduateTaskItem extends Component{
{this.props.isAdmin?
<div className="mt13">
<WordsBtn style="blue" to={"/courses/"+coursesId+"/graduation_tasks/"+categoryid+"/"+taskid+"/setting"} className="colorblue font-16 mrf4 fr">
<WordsBtn style="blue" to={"/courses/"+coursesId+"/graduation_tasks/"+categoryid+"/detail/"+taskid+"/setting"} className="colorblue font-16 mrf4 fr">
<a className="btn colorblue">设置</a>
</WordsBtn>
<WordsBtn style="blue" to={"/courses/"+coursesId+"/graduation_tasks/"+taskid+"/edit"} className="colorblue font-16 mr20 fr">

@ -0,0 +1,449 @@
import React, { Component } from 'react';
import {Link} from 'react-router-dom';
import {Tooltip,Menu} from 'antd';
import Loadable from 'react-loadable';
import Loading from '../../../../Loading';
import {BrowserRouter as Router,Route,Switch} from 'react-router-dom';
import axios from 'axios';
import HomeworkModal from "../../coursesPublic/HomeworkModal";
import AccessoryModal from "../../coursesPublic/AccessoryModal";
import Associationmodel from '../../coursesPublic/Associationmodel';
import CoursesListType from '../../coursesPublic/CoursesListType';
import moment from 'moment';
import "../../css/members.css"
import "../../css/Courses.css"
import Modals from '../../../modals/Modals';
//毕设描述
const GraduationTasksquestions= Loadable({
loader: () => import('./GraduationTaskssettingquestions'),
loading: Loading,
})
//毕设任务设置
const GraduationTaskssetting=Loadable({
loader: () => import('./GraduationTaskssetting'),
loading: Loading,
})
//毕设任务列表
const GraduationTaskslist=Loadable({
loader: () => import('./GraduationTaskssettinglist'),
loading: Loading,
})
class GraduationTaskDetail extends Component{
constructor(props){
super(props);
this.state={
modalname:undefined,
visible:false,
Topval:undefined,
starttime:undefined,
starttimes:undefined,
typs:undefined,
endtime:undefined,
Cancelname:undefined,
Savesname:undefined,
Cancel:undefined,
Saves:undefined,
Topvalright:undefined,
Botvalleft:undefined,
course_groupslist:undefined,
course_groups:undefined,
questionslist:undefined,
tab:"list",
visibles:undefined,
Modalstype:undefined,
Modalstopval:undefined,
ModalCancel:undefined,
ModalSave:undefined
}
}
componentDidMount(){
this.getdatas()
}
getdatas=()=>{
const task_Id = this.props.match.params.task_Id;
let url="/graduation_tasks/"+task_Id+".json";
axios.get(url).then((result)=>{
if(result.status===200){
this.setState({
questionslist:result.data
})
}
}).catch((error)=>{
console.log(error)
})
}
//返回
goback=()=>{
// let courseId=this.props.match.params.coursesId;
// let category_id=this.props.match.params.category_id;
// window.location.href="/courses/"+courseId+"/graduation_tasks/"+category_id;
// let courseId = this.props.match.params.coursesId;
// if(courseId===undefined){
// this.props.history.push("/courses");
// }else{
// this.props.history.push(this.props.current_user.first_category_url);
// }
// this.props.history.goBack()
this.props.history.replace(`/courses/${this.state.questionslist.course_id}/graduation_tasks/${this.state.questionslist.graduation_id}`);
}
//立即发布
publish=()=>{
let starttime= this.props.getNowFormatDates(1,1);
let endtime=this.props.getNowFormatDates(2,1);
// this.homeworkstart()
this.setState({
modalname:"立即发布",
visible:true,
Topval:"学生将立即收到毕设任务",
// Botvalleft:"点击修改",
// Botval:`本操作只对"未发布"的分班有效`,
starttime:moment(moment(new Date())).format("YYYY-MM-DD HH:mm") ,
starttimes:this.props.getNowFormatDates(1),
typs:"start",
endtime:endtime,
Cancelname:"暂不发布",
Savesname:"立即发布",
Cancel:this.cancelmodel,
Saves:this.homepublish,
})
}
// 确定立即发布
homepublish=(ids,endtime)=>{
this.cancelmodel();
let task_Id=this.props.match.params.task_Id;
const cid = this.props.match.params.coursesId;
// let url = `/courses/${cid}/graduation_tasks/publish_task.json`;
let url="/courses/"+cid+"/graduation_tasks/publish_task.json"
axios.post(url,{
task_ids:[task_Id],
group_ids: this.state.course_groupslist,
end_time:endtime,
}).then((response)=>{
if (response.data.status == 0) {
this.getdatas()
this.props.showNotification(response.data.message);
this.setState({
// Modalstopval:response.data.message,
// ModalSave:this.cancelmodel,
// Loadtype:true,
course_groupslist:[],
checkAllValue:false
})
}
}).catch((error)=>{
})
}
// 刷新
resetList=()=>{
this.getdatas();
this.child && this.child.searchValue();
}
// 立即截止
end=()=>{
// this.homeworkstart()
this.setState({
modalname:"立即截止",
visible:true,
Topval:"学生将不能再提交作品",
// Botvalleft:"暂不截止",
// Botval:`本操作只对"提交中"的分班有效`,
Cancelname:"暂不截止",
Savesname:"立即截止",
Cancel:this.cancelmodel,
Saves:this.coursetaskend,
typs:"end",
})
}
// 取消
cancelmodel=()=>{
this.setState({
Modalstype:false,
Loadtype:false,
visible:false,
Modulationtype:false,
Allocationtype:false,
Modalstopval:"",
ModalCancel:"",
ModalSave:"",
})
}
getcourse_groupslist=(id)=>{
this.setState({
course_groupslist:id
})
}
setTab = (tab) =>{
this.setState({
tab
})
}
// 关联项目
AssociationItems=()=>{
this.setState({
visibles:true
})
}
Cancel=()=>{
this.setState({
visibles:false
})
}
// 取消关联
cannelAssociation=()=>{
this.setState({
Modalstype:true,
Modalstopval:"确定要取消该项目关联?",
ModalCancel:this.cannerassocition,
ModalSave:this.savetassociton
})
}
savetassociton=()=>{
this.cannerassocition();
let {questionslist}=this.state;
let url = "/graduation_tasks/"+questionslist.task_id+"/graduation_works/cancel_relate_project.json";
console.log(url)
axios.get(url).then((result)=>{
if(result.data.status===0){
this.resetList();
}
}).catch((error)=>{
console.log(error)
})
}
cannerassocition=()=>{
this.setState({
Modalstype:false,
Modalstopval:"",
ModalCancel:"",
ModalSave:"",
loadtype:false,
visibles:false
})
}
// 补交附件
handaccessory=()=>{
// let {taskslistdata}=this.state;
// let courseId=this.props.match.params.coursesId;
//
// let url="/courses/"+courseId+"/graduation_tasks/"+taskslistdata.work_id+"/appraise"
//
// window.location.href=url;
this.setState({
avisible:true
})
}
Cancelvisible=()=>{
this.setState({
avisible:false
})
}
bindRef = ref => { this.child = ref } ;
render(){
let courseId=this.props.match.params.coursesId;
let category_id=this.props.match.params.category_id;
let task_Id=this.props.match.params.task_Id;
let {
questionslist ,
tab ,
visibles ,
Modalstype,
Modalstopval,
ModalCancel,
ModalSave
} = this.state
const commom = {
setTab:this.setTab
}
return(
<div className="newMain clearfix">
{
questionslist &&
<div className={"educontent mb20"}>
<HomeworkModal
starttimes={this.state.starttimes}
typs={this.state.typs}
modalname={this.state.modalname}
visible={this.state.visible}
Topval={this.state.Topval}
Topvalright={this.state.Topvalright}
Botvalleft={this.state.Botvalleft}
Botval={this.state.Botval}
starttime={this.state.starttime}
endtime={this.state.endtime}
Cancelname={this.state.Cancelname}
Savesname={this.state.Savesname}
Cancel={this.state.Cancel}
Saves={this.state.Saves}
course_groups={this.state.course_groups}
modaltype={this.state.modaltype}
getcourse_groupslist={(id) => this.getcourse_groupslist(id)}
/>
{/*关联项目*/}
{visibles===true?
<Associationmodel
modalname={"关联项目"}
visible={visibles}
Cancel={()=>this.Cancel()}
taskid={ questionslist && questionslist.task_id }
funlist={this.resetList}
/>
:""}
{this.state.avisible===true?<AccessoryModal
{...this.props}
modalname={"补交附件"}
visible={this.state.avisible}
Cancelname={"取消"}
Savesname={"确认"}
Cancel={this.Cancelvisible}
categoryid={questionslist.work_id}
setupdate={this.resetList}
/>:""}
{/*提示*/}
<Modals
modalsType={Modalstype}
modalsTopval={Modalstopval}
modalCancel={ModalCancel}
modalSave={ModalSave}
closable={false}
footer={null}
destroyOnClose={true}
centered={true}
/>
<p className="clearfix mt10">
<a onClick={this.goback} className="color-grey-9 fl">{questionslist.course_name}</a>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<Link to={`/courses/${courseId}/graduation_tasks/${category_id}`} className="color-grey-9 fl">{questionslist.graduation_name}</Link>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<span className="color-grey-6">任务详情</span>
</p>
<div className="clearfix mt20 mb20 lineh-25 linbox">
<p className=" fl color-black summaryname">
<Link to={`/courses/${courseId}/graduation_tasks/${category_id}`} className="color-grey-3">{questionslist.task_name}</Link>
</p>
<CoursesListType
typelist={questionslist.task_status}
/>
<a className="color-grey-3 fr font-16 ml30 mr20" onClick={this.goback}>返回</a>
</div>
<div className="stud-class-set bor-bottom-greyE">
<div className="clearfix edu-back-white pl30 pr30 graduationTaskMenu">
<Link className={tab && tab == "list" ? "active" : ""} to={"/courses/"+courseId+"/graduation_tasks/"+category_id+"/detail/"+task_Id+"/list"}>任务列表</Link>
<Link className={tab && tab == "questions" ? "active" : ""} to={"/courses/"+courseId+"/graduation_tasks/"+category_id+"/detail/"+task_Id+"/questions"}>毕设描述</Link>
<Link className={tab && tab == "setting" ? "active" : ""} to={"/courses/"+courseId+"/graduation_tasks/"+category_id+"/detail/"+task_Id+"/setting?tab=3"}>设置</Link>
{/*<a className={"fr color-blue font-16"}>导出成绩</a>*/}
{/*{this.props.isAdmin()?<a href={"/api/graduation_tasks/"+task_Id+"/tasks_list.xls"} className={"fr color-blue font-16"}>导出成绩</a>:""}*/}
{/*{this.props.isAdmin()?<a href={"/api/graduation_tasks/"+task_Id+"/tasks_list.zip"} className={"fr color-blue font-16"}>导出作品附件</a>:""}*/}
<style>
{ `
.drop_down_menu{
height: 118px;
left:0px;
width: 121px;
}
.drop_down_menu li {
overflow: visible;
width: 121px;
}
.drop_down_menu li a{
padding: 0px;
font-size: 14px;
}
.mt19{
margin-top:19px;
}
.drop_down_menu, .drop_down_normal{
padding-top: 10px;
padding-bottom: 8px;
}
.drop_down_menu li .color-dark{
color: #666 !important;
}
.linbox{
height: 26px;
}
`}
</style>
{this.props.isAdmin()? <li className="li_line drop_down fr color-blue font-16 mt20" style={{"paddingLeft":"0px"}}>
导出<i className="iconfont icon-xiajiantou font-12 ml2"></i>
<ul className="drop_down_menu" style={{"right":"-34px","left":"unset","height":"auto"}}>
<li><a onClick={()=>this.confirmysl("/graduation_tasks/"+task_Id+"/tasks_list.xlsx")} className="color-dark">导出成绩</a></li>
<li><a onClick={()=>this.confirmysl("/graduation_tasks/"+task_Id+"/tasks_list.zip")} className="color-dark">导出作品附件</a></li>
</ul>
</li>:""}
{questionslist.work_status===undefined||questionslist.work_status===null||questionslist.work_status.length===0?"":questionslist.work_status.map((item,key)=>{
return(
<span key={key}>
{item==="提交作品"?<a className={"fr color-blue font-16"} href={"/courses/"+courseId+"/graduation_tasks/"+category_id+"/"+task_Id+"/works/new"}>提交作品</a>:""}
{item==="补交作品"?<a className={"fr color-blue font-16"} href={"/courses/"+courseId+"/graduation_tasks/"+category_id+"/"+task_Id+"/works/new"}>补交作品</a>:""}
{item==="修改作品"?<a className={"fr color-blue font-16"} href={"/courses/"+courseId+"/graduation_tasks/"+category_id+"/"+questionslist.work_id+"/works/edit"}>修改作品</a>:""}
{item==="查看作品"?<a className={"fr color-blue font-16"} href={"/courses/"+courseId+"/graduation_tasks/"+category_id+"/"+questionslist.work_id+"/works/edit"}>查看作品</a> :""}
{item==="创建项目"?<a className={"fr color-blue font-16"} href={'/projects/new'} target="_blank">创建项目</a>:""}
{item==="关联项目"?<a className={"fr color-blue font-16"} onClick={this.AssociationItems}>关联项目</a>:""}
{item==="取消关联"?<a className={"fr color-blue font-16"} onClick={this.cannelAssociation}>取消关联</a>:""}
{item==="补交附件"?<a className={"fr color-blue font-16"} onClick={this.handaccessory}>补交附件</a>:""}
</span>
)
})}
{/*<a className={"fr color-blue font-16"}>项目在线质量检测</a>*/}
{ this.props.isAdmin() ? questionslist.status===1 ? <a className={"fr color-blue font-16 mr20"} onClick={() => { this.end()} }>立即截止</a> : "" : "" }
{ this.props.isAdmin() ? questionslist.status===0 ? <a className={"fr color-blue font-16 mr20"} onClick={() => { this.publish()} }>立即发布</a> : "" : "" }
{ this.props.isAdmin() ? <a className={"fr color-blue font-16"} href={"/courses/"+courseId+"/graduation_tasks/"+task_Id+"/edit"}>编辑任务</a> : "" }
</div>
</div>
<Switch {...this.props}>
<Route exact path="/courses/:coursesId/graduation_tasks/:category_id/detail/:task_Id/list"
render={
(props) => (<GraduationTaskslist {...this.props} {...props} {...this.state} {...commom} triggerRef={this.bindRef} tab={`list`}/>)
}
></Route>
<Route exact path="/courses/:coursesId/graduation_tasks/:category_id/detail/:task_Id/setting"
render={
(props) => (<GraduationTaskssetting {...this.props} {...props} {...this.state} {...commom} tab={`setting`}/>)
}
></Route>
<Route exact path="/courses/:coursesId/graduation_tasks/:category_id/detail/:task_Id/questions"
render={
(props) => (<GraduationTasksquestions {...this.props} {...props} {...this.state} {...commom} tab={`questions`}/>)
}></Route>
</Switch>
</div>
}
</div>
)
}
}
// CNotificationHOC() ( SnackbarHOC() ( TPMIndexHOC))
export default (GraduationTaskDetail) ;

@ -570,7 +570,7 @@ class GraduationTasksSubmitedit extends Component{
{/*<span className="color-grey-9 fl ml3 mr3">&gt;</span>*/}
<WordsBtn style="grey" className="fl"> <a onClick={this.goback} className="color-grey-6">毕设任务</a></WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<WordsBtn style="grey" className="fl"> <Link to={"/courses/"+courseId+"/graduation_tasks/"+category_id+"/"+work_Id+"/list"} className="color-grey-6">任务详情</Link></WordsBtn>
<WordsBtn style="grey" className="fl"> <Link to={"/courses/"+courseId+"/graduation_tasks/"+category_id+"/detail/"+work_Id+"/list"} className="color-grey-6">任务详情</Link></WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
{/*<WordsBtn style="grey" className="fl">*/}
{/*<Link to={"/courses/"+courseId+"/graduation/graduation_tasks/"+category_id} className="color-grey-6">{workslist&&workslist.task_name}</Link>*/}

@ -595,7 +595,7 @@ render(){
{/*<span className="color-grey-9 fl ml3 mr3">&gt;</span>*/}
<WordsBtn style="grey" className="fl"> <a onClick={this.goback} className="color-grey-6">毕设任务</a></WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<WordsBtn style="grey" className="fl"> <Link to={"/courses/"+courseId+"/graduation_tasks/"+category_id+"/"+task_Id+"/list"} className="color-grey-6">任务详情</Link></WordsBtn>
<WordsBtn style="grey" className="fl"> <Link to={"/courses/"+courseId+"/graduation_tasks/"+category_id+"/detail/"+task_Id+"/list"} className="color-grey-6">任务详情</Link></WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
{/*<WordsBtn style="grey" className="fl">*/}
{/*<Link to={"/courses/"+courseId+"/graduation/graduation_tasks/"+category_id} className="color-grey-6">{workslist&&workslist.task_name}</Link>*/}

@ -176,7 +176,7 @@ class GraduationTasksappraise extends Component{
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<Link to={"/courses/"+courseId+"/graduation_tasks/"+graduation_id} className="color-grey-9 fl">{datalist&&datalist.graduation_name}</Link>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<Link to={"/courses/"+courseId+"/graduation_tasks/"+graduation_id+"/"+task_id+"/list"} className="color-grey-9 fl">任务详情</Link>
<Link to={"/courses/"+courseId+"/graduation_tasks/"+graduation_id+"/detail/"+task_id+"/list"} className="color-grey-9 fl">任务详情</Link>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<span className="color-grey-6 fl">{datalist&&datalist.author_name}</span>
</p>

@ -28,6 +28,7 @@ class GraduationTasksedit extends Component{
name:"",
description:undefined,
shixunsreplace:false,
graduationtask_id:undefined
}
}
@ -73,7 +74,8 @@ class GraduationTasksedit extends Component{
data:result.data,
title_num:namelength,
attachments:result.data.attachments,
fileList:fileList,
fileList:fileList,
graduationtask_id:result.data.graduation_id
})
@ -290,10 +292,17 @@ class GraduationTasksedit extends Component{
shixunsreplace:false,
})
}
checkContent = (rule, value, callback) => {
if(value.length>5000){
callback('最大限制为5000个字符');
}else{
callback();
}
}
render(){
const { getFieldDecorator } = this.props.form;
let {title_num,pageType,name,description,Loadtype,attachments,fileList,
Modalstype,Modalstopval,ModalCancel,ModalSave,shixunsreplace} =this.state;
Modalstype,Modalstopval,ModalCancel,ModalSave,shixunsreplace ,graduationtask_id} =this.state;
let {coursedata}=this.props;
let courseId=this.props.match.params.coursesId;
@ -354,13 +363,15 @@ class GraduationTasksedit extends Component{
<div className={"educontent mb20"}>
<p className="clearfix mt10">
<WordsBtn style="grey" className="fl"> <a onClick={this.goback} className="color-grey-6">{this.props.current_user&&this.props.current_user.course_name}</a></WordsBtn>
<WordsBtn style="grey" className="fl">
<Link to={`/courses/${graduationtask_id}`} className="color-grey-6">{this.props.current_user&&this.props.current_user.course_name}</Link>
</WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
<WordsBtn style="grey" className="fl"> <Link to={"/courses/"+courseId+"/graduation_tasks/"+category_id} className="color-grey-6">毕设任务</Link></WordsBtn>
<WordsBtn style="grey" className="fl"> <Link to={"/courses/"+courseId+"/graduation_tasks/"+graduationtask_id} className="color-grey-6">毕设任务</Link></WordsBtn>
<span className="color-grey-9 fl ml3 mr3">&gt;</span>
{name===""?"":
<WordsBtn style="grey" className="fl">
<Link to={"/courses/"+courseId+"/graduation_tasks/"+category_id} className="color-grey-6">{name}</Link>
<Link to={"/courses/"+courseId+"/graduation_tasks/"+graduationtask_id+"/detail/"+category_id+"/list"} className="color-grey-6">{name}</Link>
<span className="color-grey-9 ml3 mr3">&gt;</span>
</WordsBtn>
}
@ -437,9 +448,9 @@ class GraduationTasksedit extends Component{
>
{getFieldDecorator('description', {
rules: [{
required: true, message: '请输入帖子内容',
}, {
max: 10000, message: '最大限制为10000个字符',
required: true, message: '请输入内容',
},{
max:5000,message:'最大限制5000个字符'
}],
})(
<TPMMDEditor ref={this.mdRef} placeholder={'请输入任务内容说明最大限制5000个字符'} {...this.state} {...this.props}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save